aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@osmocom.org>2023-03-10 10:27:27 +0100
committerAlexis La Goutte <alexis.lagoutte@gmail.com>2023-03-10 19:55:19 +0000
commitae38e9b09216fc48b1d7a48a55035fdec87f6110 (patch)
treea8683de7acaab21082240d92944d65c570935241
parentf64e8f0796c2f343180b851ac6d51bab3f381309 (diff)
New dissector for GSM L2RCOP (3GPP TS 27.002)
This is a dissector for the GSM "Layer 2 Relay Character Oriented Protocol" as used in non-transparent CSD (Circuit Switched Data) calls in GSM and UMTS cellular networks.
-rw-r--r--epan/dissectors/CMakeLists.txt1
-rw-r--r--epan/dissectors/packet-gsm_l2rcop.c167
-rw-r--r--epan/dissectors/packet-gsm_rlp.c17
3 files changed, 184 insertions, 1 deletions
diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt
index 810563546b..55d7c043f5 100644
--- a/epan/dissectors/CMakeLists.txt
+++ b/epan/dissectors/CMakeLists.txt
@@ -1191,6 +1191,7 @@ set(DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-gsm_cbsp.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-gsm_gsup.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-gsm_ipa.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/packet-gsm_l2rcop.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-gsm_osmux.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-gsm_r_uus1.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-gsm_rlcmac.c
diff --git a/epan/dissectors/packet-gsm_l2rcop.c b/epan/dissectors/packet-gsm_l2rcop.c
new file mode 100644
index 0000000000..b9c6fcce3b
--- /dev/null
+++ b/epan/dissectors/packet-gsm_l2rcop.c
@@ -0,0 +1,167 @@
+/* packet-gsm_l2rcop.c
+ * Routines for GSM L2RCOP (3GPP TS 27.002) dissection
+ * (C) 2023 Harald Welte <laforge@osmocom.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include <epan/xdlc.h>
+#include <epan/reassemble.h>
+#include <epan/conversation.h>
+
+void proto_register_gsm_l2rcop(void);
+
+static int proto_l2rcop = -1;
+
+static int hf_l2rcop_sa = -1;
+static int hf_l2rcop_sb = -1;
+static int hf_l2rcop_x = -1;
+static int hf_l2rcop_addr = -1;
+static int hf_l2rcop_break = -1;
+static int hf_l2rcop_break_ack = -1;
+
+static int ett_l2rcop = -1;
+
+static const value_string addr_vals[] = {
+ { 31, "last status change, remainder empty" },
+ { 30, "last status change, remainder full of characters" },
+ { 29, "destructive break signal, remainder empty" },
+ { 28, "destructive break acknowledge, remainder empty" },
+ { 27, "extended address in ext octet" },
+ { 0, NULL }
+};
+
+static void
+add_characters(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, guint offset, guint len)
+{
+ tvbuff_t *next_tvb = tvb_new_subset_length(tvb, offset, len);
+ call_data_dissector(next_tvb, pinfo, tree);
+}
+
+/* Dissect a L2RCOP message as described in 3GPP TS 27.002 */
+static int
+dissect_l2rcop(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+{
+ int reported_len = tvb_reported_length(tvb);
+ guint cur;
+
+ /* we currently support RLP v0 + v1 (first octet is always status octet) */
+
+ for (cur = 0; cur < (guint)reported_len; ) {
+ guint8 oct = tvb_get_guint8(tvb, cur);
+ guint8 addr = oct & 0x1f;
+ proto_tree *l2rcop_tree;
+ proto_item *ti;
+ const gchar *addr_str = val_to_str(addr, addr_vals, "%u characters");
+
+ ti = proto_tree_add_protocol_format(tree, proto_l2rcop, tvb, 0, reported_len,
+ "GSM L2RCOP Chunk Status=0x%02x (Addr: %s)", oct, addr_str);
+ l2rcop_tree = proto_item_add_subtree(ti, ett_l2rcop);
+
+ proto_tree_add_item(l2rcop_tree, hf_l2rcop_sa, tvb, cur, 1, ENC_BIG_ENDIAN);
+ proto_tree_add_item(l2rcop_tree, hf_l2rcop_sb, tvb, cur, 1, ENC_BIG_ENDIAN);
+ proto_tree_add_item(l2rcop_tree, hf_l2rcop_x, tvb, cur, 1, ENC_BIG_ENDIAN);
+ proto_tree_add_item(l2rcop_tree, hf_l2rcop_addr, tvb, cur, 1, ENC_BIG_ENDIAN);
+
+ switch (addr) {
+ case 31: /* last status change, remainder empty */
+ return reported_len;
+ case 30: /* last status change, remainder full of characters */
+ add_characters(l2rcop_tree, pinfo, tvb, cur+1, reported_len-cur-1);
+ return reported_len;
+ case 29: /* destructive break signal, remainder empty */
+ proto_tree_add_item(l2rcop_tree, hf_l2rcop_break, tvb, cur, 1, ENC_BIG_ENDIAN);
+ return reported_len;
+ case 28: /* destructive break acknowledge, remainder empty */
+ proto_tree_add_item(l2rcop_tree, hf_l2rcop_break_ack, tvb, cur, 1, ENC_BIG_ENDIAN);
+ return reported_len;
+ case 27: /* extended address in ext octet */
+ cur++;
+ addr = tvb_get_guint8(tvb, cur) & 0x3f;
+ /* This "cannot happen"; let's abort processing right now. */
+ if (addr == 0)
+ return reported_len;
+ proto_tree_add_uint(l2rcop_tree, hf_l2rcop_addr, tvb, cur, 1, addr);
+ add_characters(l2rcop_tree, pinfo, tvb, cur+1, addr);
+ cur += 1 + addr;
+ break;
+ case 0:
+ /* This "cannot happen"; let's abort processing right now. */
+ return reported_len;
+ default:
+ /* This "cannot happen"; let's abort processing right now. */
+ if (addr == 0)
+ return reported_len;
+ add_characters(l2rcop_tree, pinfo, tvb, cur+1, addr);
+ cur += 1 + addr;
+ break;
+ }
+ }
+
+ return reported_len;
+}
+
+static const true_false_string x_vals = {
+ "flow control ACTIVE", "flow control inactive"
+};
+
+static const true_false_string sab_vals = {
+ "OFF", "ON"
+};
+
+
+void
+proto_register_gsm_l2rcop(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_l2rcop_sa,
+ { "SA", "gsm_l2rcop.sa", FT_BOOLEAN, 8, TFS(&sab_vals), 0x80,
+ NULL, HFILL }},
+ { &hf_l2rcop_sb,
+ { "SB", "gsm_l2rcop.sb", FT_BOOLEAN, 8, TFS(&sab_vals), 0x40,
+ NULL, HFILL }},
+ { &hf_l2rcop_x,
+ { "X", "gsm_l2rcop.x", FT_BOOLEAN, 8, TFS(&x_vals), 0x20,
+ NULL, HFILL }},
+ { &hf_l2rcop_addr,
+ { "Address", "gsm_l2rcop.addr", FT_UINT8, BASE_DEC|BASE_SPECIAL_VALS, VALS(addr_vals), 0x1f,
+ NULL, HFILL }},
+ { &hf_l2rcop_break,
+ { "Break", "gsm_l2rcop.break", FT_UINT8, BASE_DEC, NULL, 0x00,
+ NULL, HFILL }},
+ { &hf_l2rcop_break_ack,
+ { "Break Ack", "gsm_l2rcop.break_ack", FT_UINT8, BASE_DEC, NULL, 0x00,
+ NULL, HFILL }},
+ };
+ static gint *ett[] = {
+ &ett_l2rcop,
+ };
+
+ proto_l2rcop = proto_register_protocol("GSM L2R Character Oriented Protocol (L2RCOP)", "GSM-L2RCOP",
+ "gsm_l2rcop");
+ proto_register_field_array(proto_l2rcop, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+ register_dissector("gsm_l2rcop", dissect_l2rcop, proto_l2rcop);
+}
+
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/epan/dissectors/packet-gsm_rlp.c b/epan/dissectors/packet-gsm_rlp.c
index f4c9a4f3ed..2f0fd7b9ce 100644
--- a/epan/dissectors/packet-gsm_rlp.c
+++ b/epan/dissectors/packet-gsm_rlp.c
@@ -40,6 +40,9 @@ static int ett_gsmrlp_xid = -1;
static expert_field ei_gsmrlp_fcs_bad = EI_INIT;
+static dissector_handle_t l2rcop_handle;
+static gboolean decode_as_l2rcop = true;
+
/* 3GPP TS 24.002 Section 5.2.1 */
enum rlp_ftype {
RLP_FT_U,
@@ -247,7 +250,10 @@ dissect_gsmrlp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _
/* dispatch user data */
data_len = reported_len - 2 /* header */ - 3 /* FCS */;
next_tvb = tvb_new_subset_length(tvb, 2, data_len);
- call_data_dissector(next_tvb, pinfo, rlp_tree);
+ if (decode_as_l2rcop && l2rcop_handle)
+ call_dissector(l2rcop_handle, next_tvb, pinfo, rlp_tree);
+ else
+ call_data_dissector(next_tvb, pinfo, rlp_tree);
}
/* FCS is always the last 3 bytes of the message */
@@ -317,6 +323,15 @@ proto_register_gsmrlp(void)
expert_register_field_array(expert_gsmrlp, ei, array_length(ei));
register_dissector("gsm_rlp", dissect_gsmrlp, proto_gsmrlp);
+
+ rlp_module = prefs_register_protocol(proto_gsmrlp, NULL);
+ prefs_register_bool_preference(rlp_module, "decode_as_l2rcop", "Decode payload as L2RCOP",
+ NULL, &decode_as_l2rcop);
+}
+void
+proto_reg_handoff_gsmrlp(void)
+{
+ l2rcop_handle = find_dissector_add_dependency("gsm_l2rcop", proto_gsmrlp);
}
/*