aboutsummaryrefslogtreecommitdiffstats
path: root/epan
diff options
context:
space:
mode:
authorAnders Broman <anders.broman@ericsson.com>2009-10-09 06:37:48 +0000
committerAnders Broman <anders.broman@ericsson.com>2009-10-09 06:37:48 +0000
commit46aa5c44de5b62cc061840b29903a63bc3790445 (patch)
tree64ab8ab30cfec2b204cd625f538cf1345b443aee /epan
parent0659b21e03316a01da5b2ed845064f57d76380f4 (diff)
From Tobias Witek:
New protocols: UMTS RLC (ETSI TS 125 322), UMTS MAC (ETSI TS 125 321). This does not currently compile cleanly but checking it in to be worked on. https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=3495 svn path=/trunk/; revision=30414
Diffstat (limited to 'epan')
-rw-r--r--epan/dissectors/packet-fp_hint.c586
-rw-r--r--epan/dissectors/packet-meta.c534
-rw-r--r--epan/dissectors/packet-meta.h69
-rw-r--r--epan/dissectors/packet-rlc.c1384
-rw-r--r--epan/dissectors/packet-rlc.h38
-rw-r--r--epan/dissectors/packet-umts_mac.c609
-rw-r--r--epan/dissectors/packet-umts_mac.h50
7 files changed, 3270 insertions, 0 deletions
diff --git a/epan/dissectors/packet-fp_hint.c b/epan/dissectors/packet-fp_hint.c
new file mode 100644
index 0000000000..d8cf8e865b
--- /dev/null
+++ b/epan/dissectors/packet-fp_hint.c
@@ -0,0 +1,586 @@
+/* Routines for UMTS FP Hint protocol disassembly
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <epan/packet.h>
+#include "packet-umts_fp.h"
+#include "packet-umts_mac.h"
+#include "packet-rlc.h"
+#include "wiretap/erf.h"
+
+static int proto_fp_hint = -1;
+extern int proto_fp;
+extern int proto_umts_mac;
+extern int proto_rlc;
+
+static int ett_fph = -1;
+static int ett_fph_rb = -1;
+static int ett_fph_ddi_entry = -1;
+static int ett_fph_tf = -1;
+
+static int hf_fph_frametype = -1;
+static int hf_fph_channeltype = -1;
+static int hf_fph_chcnt = -1;
+static int hf_fph_dchid = -1;
+static int hf_fph_urnti = -1;
+static int hf_fph_rlcmode = -1;
+static int hf_fph_content = -1;
+static int hf_fph_rbid = -1;
+static int hf_fph_ctmux = -1;
+static int hf_fph_ciphered = -1;
+static int hf_fph_deciphered = -1;
+static int hf_fph_macdflowid = -1;
+static int hf_fph_rb = -1;
+static int hf_fph_ddi_entry = -1;
+static int hf_fph_ddi_size = -1;
+static int hf_fph_ddi_logical = -1;
+static int hf_fph_ddi_value = -1;
+static int hf_fph_tf = -1;
+static int hf_fph_tf_n = -1;
+static int hf_fph_tf_size = -1;
+
+static dissector_handle_t data_handle;
+static dissector_handle_t ethwithfcs_handle;
+static dissector_handle_t atm_untrunc_handle;
+static dissector_handle_t erf_handle;
+
+enum fph_ctype {
+ FPH_CHANNEL_PCH,
+ FPH_CHANNEL_RACH,
+ FPH_CHANNEL_FACH,
+ FPH_CHANNEL_DCH,
+ FPH_CHANNEL_HSDSCH,
+ FPH_CHANNEL_EDCH
+};
+
+enum fph_frame {
+ FPH_FRAME_ERF_AAL2,
+ FPH_FRAME_ETHERNET
+};
+
+enum fph_pich {
+ FPH_PICH18,
+ FPH_PICH36,
+ FPH_PICH72,
+ FPH_PICH144
+};
+
+enum fph_content {
+ FPH_CONTENT_UNKNOWN,
+ FPH_CONTENT_DCCH,
+ FPH_CONTENT_PS_DTCH,
+ FPH_CONTENT_CS_DTCH
+};
+
+static const value_string fph_frametype_vals[] = {
+ { FPH_FRAME_ERF_AAL2, "ERF AAL2" },
+ { FPH_FRAME_ETHERNET, "Ethernet" },
+ { 0, NULL }
+};
+
+static const value_string fph_channeltype_vals[] = {
+ { FPH_CHANNEL_PCH, "PCH" },
+ { FPH_CHANNEL_RACH, "RACH" },
+ { FPH_CHANNEL_FACH, "FACH" },
+ { FPH_CHANNEL_DCH, "DCH" },
+ { FPH_CHANNEL_HSDSCH, "HSDSCH" },
+ { FPH_CHANNEL_EDCH, "E-DCH" },
+ { 0, NULL }
+};
+
+static const value_string fph_rlcmode_vals[] = {
+ { RLC_TM, "Transparent Mode" },
+ { RLC_UM, "Unacknowledged Mode" },
+ { RLC_AM, "Acknowledged Mode" },
+ { 0, NULL }
+};
+
+static const value_string fph_content_vals[] = {
+ { FPH_CONTENT_UNKNOWN, "Unknown" },
+ { FPH_CONTENT_DCCH, "DCCH" },
+ { FPH_CONTENT_PS_DTCH, "PS DTCH" },
+ { FPH_CONTENT_CS_DTCH, "PS DTCH" },
+ { 0, NULL }
+};
+
+static const true_false_string fph_ctmux_vals = {
+ "C/T Mux field present", "C/T Mux field not present"
+};
+
+static const true_false_string fph_ciphered_vals = {
+ "Ciphered", "Not ciphered"
+};
+
+static const true_false_string fph_deciphered_vals = {
+ "Deciphered", "Not deciphered"
+};
+
+static guint16 assign_rb_info(tvbuff_t *tvb, packet_info *pinfo, guint16 offset, guint8 rbcnt, proto_tree *tree)
+{
+ guint8 i = 0, next_byte;
+ guint8 rlc_mode, content, rb_id, ctmux, ciphered, deciphered;
+ guint32 urnti;
+ struct umts_mac_info *macinf;
+ struct rlc_info *rlcinf;
+
+ macinf = p_get_proto_data(pinfo->fd, proto_umts_mac);
+ rlcinf = p_get_proto_data(pinfo->fd, proto_rlc);
+ if (!macinf) {
+ macinf = se_alloc0(sizeof(struct umts_mac_info));
+ p_add_proto_data(pinfo->fd, proto_umts_mac, macinf);
+ }
+ if (!rlcinf) {
+ rlcinf = se_alloc0(sizeof(struct rlc_info));
+ p_add_proto_data(pinfo->fd, proto_rlc, rlcinf);
+ }
+
+ while (i < rbcnt) {
+ urnti = tvb_get_letohl(tvb, offset);
+ next_byte = tvb_get_guint8(tvb, offset + 4);
+ rlc_mode = next_byte & 0x3;
+ content = (next_byte >> 2) & 0x3;
+ rb_id = next_byte >> 4;
+ next_byte = tvb_get_guint8(tvb, offset + 5);
+ rb_id |= (next_byte & 0x01) << 4;
+ ctmux = (next_byte >> 1) & 0x1;
+ ciphered = (next_byte >> 2) & 0x1;
+ deciphered = (next_byte >> 3) & 0x1;
+
+ if (i >= MAX_RLC_CHANS) {
+ proto_item *pi;
+ pi = proto_tree_add_text(tree, tvb, offset, -1,
+ "Frame contains more Radio Bearers than currently supported (%u present, %u supported)",
+ rbcnt, MAX_RLC_CHANS);
+ return -1;
+ }
+ if (i >= MAX_MAC_FRAMES) {
+ proto_item *pi;
+ pi = proto_tree_add_text(tree, tvb, offset, -1,
+ "Frame contains more MAC Frames than currently supported (%u present, %u supported)",
+ rbcnt, MAX_MAC_FRAMES);
+ return -1;
+ }
+
+ rlcinf->mode[i] = rlc_mode;
+ rlcinf->rbid[i] = rb_id;
+ rlcinf->urnti[i] = urnti;
+ rlcinf->ciphered[i] = ciphered;
+ rlcinf->deciphered[i] = deciphered;
+
+ macinf->ctmux[i] = ctmux ? TRUE : FALSE;
+ switch (content) {
+ case FPH_CONTENT_DCCH:
+ macinf->content[i] = MAC_CONTENT_DCCH;
+ break;
+ case FPH_CONTENT_PS_DTCH:
+ macinf->content[i] = MAC_CONTENT_PS_DTCH;
+ break;
+ case FPH_CONTENT_CS_DTCH:
+ macinf->content[i] = MAC_CONTENT_CS_DTCH;
+ break;
+ default:
+ macinf->content[i] = MAC_CONTENT_UNKNOWN;
+ }
+
+ if (tree) {
+ proto_tree *subtree;
+ proto_item *pi;
+
+ pi = proto_tree_add_item(tree, hf_fph_rb, tvb, offset, 8, TRUE);
+ subtree = proto_item_add_subtree(pi, ett_fph_rb);
+
+ if (urnti)
+ proto_tree_add_uint(subtree, hf_fph_urnti, tvb, offset, 4, urnti);
+ pi = proto_tree_add_bits_item(subtree, hf_fph_content, tvb, (offset+4)*8+4, 2, TRUE);
+ pi = proto_tree_add_bits_item(subtree, hf_fph_rlcmode, tvb, (offset+4)*8+6, 2, TRUE);
+ proto_tree_add_item(subtree, hf_fph_rbid, tvb, (offset+4), 2, TRUE);
+ proto_tree_add_boolean(subtree, hf_fph_ctmux, tvb, offset+5, 1, ctmux);
+ proto_tree_add_boolean(subtree, hf_fph_ciphered, tvb, offset+5, 1, ciphered);
+ proto_tree_add_boolean(subtree, hf_fph_deciphered, tvb, offset+5, 1, deciphered);
+ }
+ offset += 8;
+ i++;
+ }
+ return offset;
+}
+
+static void assign_fph_pch(tvbuff_t *tvb, packet_info *pinfo _U_, guint16 offset, fp_info *fpi, proto_tree *tree _U_)
+{
+ guint8 pich;
+ guint16 blkcnt, blksz;
+ const guint8 *hdr;
+
+ fpi->channel = CHANNEL_PCH;
+
+ hdr = tvb_get_ptr(tvb, offset, 4);
+ blkcnt = hdr[0] | ((hdr[1] & 0x01) << 8);
+ blksz = (hdr[1] >> 1) | ((hdr[2] & 0x3f) << 7);
+ pich = (hdr[2] >> 6) | ((hdr[3] & 0x01) << 2);
+
+ switch (pich) {
+ case FPH_PICH18:
+ fpi->paging_indications = 18;
+ break;
+ case FPH_PICH36:
+ fpi->paging_indications = 36;
+ break;
+ case FPH_PICH72:
+ fpi->paging_indications = 72;
+ break;
+ case FPH_PICH144:
+ fpi->paging_indications = 144;
+ break;
+ default:
+ fpi->paging_indications = 0;
+ }
+ fpi->num_chans = 1;
+ fpi->chan_tf_size[0] = blksz;
+ fpi->chan_num_tbs[0] = blkcnt;
+}
+
+static void assign_fph_rach(tvbuff_t *tvb, packet_info *pinfo _U_, guint16 offset, fp_info *fpi, proto_tree *tree)
+{
+ const guint8 *hdr;
+ guint8 rbcnt;
+ guint16 blkcnt, blksz;
+
+ fpi->channel = CHANNEL_RACH_FDD;
+
+ hdr = tvb_get_ptr(tvb, offset, 4);
+ blkcnt = hdr[0] | ((hdr[1] & 0x01) << 8);
+ blksz = (hdr[1] >> 1) | ((hdr[2] & 0x3f) << 7);
+
+ fpi->num_chans = 1;
+ fpi->chan_tf_size[0] = blksz;
+ fpi->chan_num_tbs[0] = blkcnt;
+
+ offset += 4;
+ rbcnt = tvb_get_guint8(tvb, offset); offset++;
+ if (rbcnt > 0)
+ offset = assign_rb_info(tvb, pinfo, offset, rbcnt, tree);
+}
+
+static void assign_fph_dch(tvbuff_t *tvb, packet_info *pinfo, guint16 offset, fp_info *fpi, proto_tree *tree)
+{
+ guint8 dch_id, rbcnt;
+ guint16 N, size;
+ guint32 cnt, i = 0;
+ const guint8 *hdr;
+ proto_tree *subtree;
+ proto_item *pi;
+
+ fpi->channel = CHANNEL_DCH;
+ cnt = tvb_get_guint8(tvb, offset); offset++;
+
+ if (tree)
+ proto_tree_add_uint(tree, hf_fph_chcnt, tvb, offset-1, 1, cnt);
+
+ fpi->num_chans = cnt;
+ fpi->dch_crc_present = 1;
+ while (i < cnt) {
+ pi = proto_tree_add_item(tree, hf_fph_tf, tvb, offset, 4, TRUE);
+ subtree = proto_item_add_subtree(pi, ett_fph_rb);
+ hdr = tvb_get_ptr(tvb, offset, 4);
+ dch_id = hdr[0] & 0x1f;
+
+ N = ((hdr[1] & 0x3f)<<3) | (hdr[0] >> 5);
+ size = ((hdr[3] & 0x07)<<10) | (hdr[2] << 2) | ((hdr[1] & 0xc0)>>6);
+ size = size == 0x1fff ? 0 : size;
+
+ fpi->chan_tf_size[i] = size;
+ fpi->chan_num_tbs[i] = N;
+
+ if (subtree) {
+ proto_tree_add_bits_item(subtree, hf_fph_dchid, tvb, offset*8+3, 5, TRUE);
+ proto_tree_add_uint(subtree, hf_fph_tf_n, tvb, offset, 2, N);
+ if (size)
+ proto_tree_add_uint(subtree, hf_fph_tf_size, tvb, offset + 1, 3, size);
+ }
+ offset += 4;
+ if (i > MAX_FP_CHANS) {
+ proto_item *pi;
+ pi = proto_tree_add_text(tree, tvb, offset, -1,
+ "Frame contains more FP channels than currently supported (%u supported)",
+ MAX_FP_CHANS);
+ return;
+ }
+ i++;
+ }
+ rbcnt = tvb_get_guint8(tvb, offset); offset++;
+ if (rbcnt > 0)
+ offset = assign_rb_info(tvb, pinfo, offset, rbcnt, tree);
+}
+
+static void assign_fph_fach(tvbuff_t *tvb, packet_info *pinfo, guint16 offset, fp_info *fpi, proto_tree *tree)
+{
+ const guint8 *hdr;
+ guint8 rbcnt;
+ guint16 blkcnt, blksz;
+
+ fpi->channel = CHANNEL_FACH_FDD;
+
+ hdr = tvb_get_ptr(tvb, offset, 4);
+ blkcnt = hdr[0] | ((hdr[1] & 0x01) << 8);
+ blksz = (hdr[1] >> 1) | ((hdr[2] & 0x3f) << 7);
+
+ fpi->num_chans = 1;
+ fpi->chan_tf_size[0] = blksz;
+ fpi->chan_num_tbs[0] = blkcnt;
+
+ offset += 4;
+ rbcnt = tvb_get_guint8(tvb, offset); offset++;
+ if (rbcnt > 0)
+ offset = assign_rb_info(tvb, pinfo, offset, rbcnt, tree);
+}
+
+static void assign_fph_hsdsch(tvbuff_t *tvb, packet_info *pinfo, guint16 offset, fp_info *fpi, proto_tree *tree)
+{
+ guint8 rbcnt, macdflow_id;
+
+ fpi->channel = CHANNEL_HSDSCH;
+ macdflow_id = tvb_get_guint8(tvb, offset);
+
+ if (tree)
+ proto_tree_add_uint(tree, hf_fph_macdflowid, tvb, offset, 1, macdflow_id);
+
+ offset++;
+ rbcnt = tvb_get_guint8(tvb, offset); offset++;
+ if (rbcnt > 0)
+ offset = assign_rb_info(tvb, pinfo, offset, rbcnt, tree);
+}
+
+static void assign_fph_edch(tvbuff_t *tvb, packet_info *pinfo, guint16 offset, fp_info *fpi, proto_tree *tree)
+{
+ guint8 rbcnt, macdflow_id, maces_cnt, i = 0;
+ guint8 logical, ddi;
+ guint16 maces_size;
+ proto_item *pi;
+ proto_tree *subtree = NULL;
+
+ fpi->channel = CHANNEL_EDCH;
+ macdflow_id = tvb_get_guint8(tvb, offset);
+
+ if (tree) {
+ proto_tree_add_uint(tree, hf_fph_macdflowid, tvb, offset, 1, macdflow_id);
+ }
+
+ offset++;
+ maces_cnt = tvb_get_guint8(tvb, offset); offset++;
+
+ fpi->no_ddi_entries = maces_cnt;
+ while (i < maces_cnt) {
+ ddi = tvb_get_guint8(tvb, offset++);
+ logical = tvb_get_guint8(tvb, offset++);
+ maces_size = tvb_get_letohs(tvb, offset);
+ offset += 2;
+ fpi->edch_ddi[i] = ddi;
+ fpi->edch_macd_pdu_size[i] = maces_size;
+ if (tree) {
+ pi = proto_tree_add_item(tree, hf_fph_ddi_entry, tvb, offset - 4, 4, TRUE);
+ subtree = proto_item_add_subtree(pi, ett_fph_ddi_entry);
+ proto_tree_add_uint(subtree, hf_fph_ddi_value, tvb, offset - 4, 1, ddi);
+ proto_tree_add_uint(subtree, hf_fph_ddi_logical, tvb, offset - 3, 1, logical);
+ proto_tree_add_uint(subtree, hf_fph_ddi_size, tvb, offset - 2, 2, maces_size);
+ }
+ i++;
+ if (i >= MAX_EDCH_DDIS) {
+ proto_item *pi;
+ pi = proto_tree_add_text(tree, tvb, offset, -1,
+ "Frame contains more FP channels than currently supported (%u supported)",
+ MAX_FP_CHANS);
+ return;
+ }
+ }
+
+
+ rbcnt = tvb_get_guint8(tvb, offset); offset++;
+ if (rbcnt > 0)
+ offset = assign_rb_info(tvb, pinfo, offset, rbcnt, tree);
+}
+
+static void attach_info(tvbuff_t *tvb, packet_info *pinfo, guint16 offset, guint8 channel_type, guint8 frame_type, proto_tree *tree)
+{
+ fp_info *fpi;
+
+ fpi = p_get_proto_data(pinfo->fd, proto_fp);
+ if (!fpi) {
+ fpi = se_alloc0(sizeof(fp_info));
+ p_add_proto_data(pinfo->fd, proto_fp, fpi);
+ }
+
+ fpi->is_uplink = pinfo->p2p_dir == P2P_DIR_RECV;
+ /* TODO make this configurable */
+ fpi->release = 6;
+ fpi->release_year = 2006;
+ fpi->release_month = 9;
+ fpi->dch_crc_present = 1;
+
+ switch (frame_type) {
+ case FPH_FRAME_ERF_AAL2:
+ fpi->link_type = FP_Link_ATM;
+ break;
+ case FPH_FRAME_ETHERNET:
+ fpi->link_type = FP_Link_Ethernet;
+ break;
+ default:
+ fpi->link_type = FP_Link_Unknown;
+ }
+
+ /* at the moment, only IuB is supported */
+ fpi->iface_type = IuB_Interface;
+ /* at the moment, only FDD is supported */
+ fpi->division = Division_FDD;
+
+ switch (channel_type) {
+ case FPH_CHANNEL_PCH:
+ assign_fph_pch(tvb, pinfo, offset, fpi, tree);
+ break;
+ case FPH_CHANNEL_RACH:
+ assign_fph_rach(tvb, pinfo, offset, fpi, tree);
+ break;
+ case FPH_CHANNEL_FACH:
+ assign_fph_fach(tvb, pinfo, offset, fpi, tree);
+ break;
+ case FPH_CHANNEL_DCH:
+ assign_fph_dch(tvb, pinfo, offset, fpi, tree);
+ break;
+ case FPH_CHANNEL_HSDSCH:
+ assign_fph_hsdsch(tvb, pinfo, offset, fpi, tree);
+ break;
+ case FPH_CHANNEL_EDCH:
+ assign_fph_edch(tvb, pinfo, offset, fpi, tree);
+ break;
+ default:
+ fpi->channel = 0;
+ }
+}
+
+static void dissect_fp_hint(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ guint8 frame_type, channel_type;
+ guint16 hdrlen;
+#if 0
+ guint32 atm_aal2_ext, atm_hdr;
+#endif
+ tvbuff_t *next_tvb;
+ dissector_handle_t *next_dissector;
+ proto_item *ti;
+ proto_tree *fph_tree = NULL;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "FP Hint");
+
+ hdrlen = tvb_get_letohs(tvb, 0);
+ frame_type = tvb_get_guint8(tvb, 2);
+ channel_type = tvb_get_guint8(tvb, 3);
+
+ if (tree) {
+ ti = proto_tree_add_item(tree, proto_fp_hint, tvb, 0, hdrlen, TRUE);
+ fph_tree = proto_item_add_subtree(ti, ett_fph);
+ proto_tree_add_uint(fph_tree, hf_fph_frametype, tvb, 2, 1, frame_type);
+ proto_tree_add_uint(fph_tree, hf_fph_channeltype, tvb, 3, 1, channel_type);
+ }
+
+ /* attach FP, MAC, RLC information */
+ attach_info(tvb, pinfo, 4, channel_type, frame_type, fph_tree);
+ switch (frame_type) {
+ case FPH_FRAME_ERF_AAL2:
+ memset(&pinfo->pseudo_header->erf, 0, sizeof(pinfo->pseudo_header->erf));
+ pinfo->pseudo_header->erf.phdr.type = ERF_TYPE_AAL2;
+ /* store p2p direction in ERF flags */
+ pinfo->pseudo_header->erf.phdr.flags |= pinfo->p2p_dir;
+ /* set ATM properties */
+ pinfo->pseudo_header->atm.type = TRAF_UMTS_FP;
+ pinfo->pseudo_header->atm.subtype = TRAF_UNKNOWN;
+ next_dissector = &erf_handle;
+ break;
+ case FPH_FRAME_ETHERNET:
+ next_dissector = &ethwithfcs_handle;
+ break;
+ default:
+ next_dissector = &data_handle;
+ }
+
+ next_tvb = tvb_new_subset(tvb, hdrlen, -1, -1);
+ call_dissector(*next_dissector, next_tvb, pinfo, tree);
+}
+
+void
+proto_register_fp_hint(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_fph_frametype, { "Frame Type", "fp_hint.frame_type", FT_UINT8, BASE_HEX, VALS(fph_frametype_vals), 0x0, "Frame Type", HFILL } },
+ { &hf_fph_channeltype, { "Channel Type", "fp_hint.channel_type", FT_UINT8, BASE_HEX, VALS(fph_channeltype_vals), 0x0, "Channel Type", HFILL } },
+ { &hf_fph_chcnt, { "Number of Channels", "fp_hint.num_chan", FT_UINT8, BASE_DEC, NULL, 0, "Number of Channels", HFILL } },
+ { &hf_fph_dchid, { "DCH ID", "fp_hint.dchid", FT_UINT8, BASE_DEC, NULL, 0, "DCH ID", HFILL } },
+ { &hf_fph_macdflowid, { "MACd Flow ID", "fp_hint.macdflowid", FT_UINT8, BASE_DEC, NULL, 0, "MACd Flow ID", HFILL } },
+ /* traffic format details */
+ { &hf_fph_tf, { "Traffic Format", "fp_hint.tf", FT_NONE, BASE_NONE, NULL, 0, "", HFILL } },
+ { &hf_fph_tf_n, { "N", "fp_hint.tf.n", FT_UINT16, BASE_DEC, NULL, 0, "", HFILL } },
+ { &hf_fph_tf_size, { "Size", "fp_hintf.tf.size", FT_UINT32, BASE_DEC, NULL, 0, "", HFILL } },
+ /* DDI information for E-DCH */
+ { &hf_fph_ddi_entry, { "DDI Entry", "fp_hint.ddi", FT_NONE, BASE_NONE, NULL, 0, "", HFILL } },
+ { &hf_fph_ddi_value, { "DDI", "fp_hint.ddi.value", FT_UINT8, BASE_DEC, NULL, 0, "", HFILL } },
+ { &hf_fph_ddi_logical, { "Logical Channel ID", "fp_hint.ddi.logical", FT_UINT8, BASE_DEC, NULL, 0, "", HFILL } },
+ { &hf_fph_ddi_size, { "Size", "fp_hint.ddi.size", FT_UINT16, BASE_DEC, NULL, 0, "", HFILL } },
+ /* radio bearer details */
+ { &hf_fph_rb, { "Radio Bearer", "fp_hint.rb", FT_NONE, BASE_NONE, NULL, 0, "", HFILL } },
+ { &hf_fph_urnti, { "U-RNTI", "fp_hint.rb.urnti", FT_UINT32, BASE_HEX, NULL, 0x0, "U-RNTI", HFILL } },
+ { &hf_fph_content, { "Content", "fp_hint.rb.content", FT_UINT8, BASE_DEC, VALS(fph_content_vals), 0, "Content", HFILL } },
+ { &hf_fph_rlcmode, { "RLC Mode", "fp_hint.rb.rlc_mode", FT_UINT8, BASE_DEC, VALS(fph_rlcmode_vals), 0, "RLC Mode", HFILL } },
+ { &hf_fph_rbid, { "Radio Bearer ID", "fp_hint.rb.rbid", FT_UINT16, BASE_DEC, NULL, 0x01f0, "Radio Bearer ID", HFILL } },
+ { &hf_fph_ctmux, { "C/T Mux", "fp_hint.rb.ctmux", FT_BOOLEAN, BASE_NONE, TFS(&fph_ctmux_vals), 0, "C/T Mux field", HFILL } },
+ { &hf_fph_ciphered, { "Ciphered", "fp_hint.rb.ciphered", FT_BOOLEAN, BASE_NONE, TFS(&fph_ciphered_vals), 0, "Ciphered flag", HFILL } },
+ { &hf_fph_deciphered, { "Deciphered", "fp_hint.rb.deciphered", FT_BOOLEAN, BASE_NONE, TFS(&fph_deciphered_vals), 0, "Deciphered flag", HFILL } }
+ };
+
+ static gint *ett[] = {
+ &ett_fph,
+ &ett_fph_rb,
+ &ett_fph_ddi_entry,
+ &ett_fph_tf
+ };
+
+ proto_fp_hint = proto_register_protocol("FP Hint", "FP Hint", "fp_hint");
+ register_dissector("fp_hint", dissect_fp_hint, proto_fp_hint);
+
+ proto_register_field_array(proto_fp_hint, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_fp_hint(void)
+{
+ atm_untrunc_handle = find_dissector("atm_untruncated");
+ data_handle = find_dissector("data");
+ ethwithfcs_handle = find_dissector("eth_withfcs");
+ erf_handle = find_dissector("erf");
+}
diff --git a/epan/dissectors/packet-meta.c b/epan/dissectors/packet-meta.c
new file mode 100644
index 0000000000..154ff4e8df
--- /dev/null
+++ b/epan/dissectors/packet-meta.c
@@ -0,0 +1,534 @@
+/* Routines for 'Metadata' disassembly
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <epan/packet.h>
+#include <epan/expert.h>
+
+#include "packet-meta.h"
+#include "packet-sscop.h"
+#include "wiretap/erf.h"
+
+int proto_meta = -1;
+extern int proto_sscop;
+extern int proto_malformed;
+
+/* fields */
+static int hf_meta_schema = -1;
+static int hf_meta_hdrlen = -1;
+static int hf_meta_proto = -1;
+static int hf_meta_reserved = -1;
+static int hf_meta_item = -1;
+static int hf_meta_item_id = -1;
+static int hf_meta_item_type = -1;
+static int hf_meta_item_len = -1;
+static int hf_meta_item_data = -1;
+/* specific fields */
+static int hf_meta_item_direction = -1;
+static int hf_meta_item_ts = -1;
+static int hf_meta_item_phylinkid = -1;
+static int hf_meta_item_nsapi = -1;
+static int hf_meta_item_imsi = -1;
+static int hf_meta_item_imei = -1;
+static int hf_meta_item_signaling = -1;
+static int hf_meta_item_incomplete = -1;
+static int hf_meta_item_apn = -1;
+static int hf_meta_item_rat = -1;
+static int hf_meta_item_aal5proto = -1;
+static int hf_meta_item_cell = -1;
+
+/* subtrees */
+static gint ett_meta = -1;
+static gint ett_meta_item = -1;
+
+/* default handle */
+static dissector_handle_t data_handle;
+static dissector_handle_t atm_untrunc_handle;
+static dissector_handle_t sscf_nni_handle;
+static dissector_handle_t alcap_handle;
+static dissector_handle_t nbap_handle;
+static dissector_handle_t ethwithfcs_handle;
+static dissector_handle_t ethwithoutfcs_handle;
+static dissector_handle_t fphint_handle;
+static dissector_handle_t erf_handle;
+
+static const value_string meta_schema_vals[] = {
+ { META_SCHEMA_PCAP, "PCAP" },
+ { META_SCHEMA_DXT, "DXT" },
+ { 0, NULL }
+};
+
+static const value_string meta_proto_vals[] = {
+ { META_PROTO_DXT_ETHERNET, "Ethernet without FCS" },
+ { META_PROTO_DXT_ETHERNET_CRC, "Ethernet with FCS" },
+ { META_PROTO_DXT_FP_HINT, "FP Hint" },
+ { META_PROTO_DXT_ERF_AAL5, "ERF AAL5" },
+ { META_PROTO_DXT_ERF_AAL2, "ERF AAL2" },
+ { META_PROTO_DXT_ATM, "ATM" },
+ { 0, NULL }
+};
+
+static const value_string meta_type_vals[] = {
+ { META_TYPE_NONE, "NONE" },
+ { META_TYPE_BOOLEAN, "BOOLEAN" },
+ { META_TYPE_UINT8, "UINT8" },
+ { META_TYPE_UINT16, "UINT16" },
+ { META_TYPE_UINT32, "UINT32" },
+ { META_TYPE_UINT64, "UINT64" },
+ { META_TYPE_STRING, "STRING" },
+ { 0, NULL }
+};
+
+/* TODO: this must be on a per-schema basis! */
+static const value_string meta_id_vals[] = {
+ { META_ID_NULL, "NULL" },
+ { META_ID_DIRECTION, "Direction" },
+ { META_ID_SIGNALING, "Signaling" },
+ { META_ID_INCOMPLETE, "Incomplete" },
+ { META_ID_DECIPHERED, "Deciphered" },
+ { META_ID_PAYLOADCUT, "Payload cutted" },
+ { META_ID_TIMESTAMP64, "Timestamp" },
+ { META_ID_AAL5PROTO, "AAL5 Protocol Type" },
+ { META_ID_PHYLINKID, "Physical Link ID" },
+ { META_ID_LOCALDEVID, "Local Device ID" },
+ { META_ID_REMOTEDEVID, "Remote Device ID" },
+ { META_ID_TAPGROUPID, "Tap Group ID" },
+ { META_ID_IMSI, "IMSI" },
+ { META_ID_IMEI, "IMEI" },
+ { META_ID_CELL, "Cell" },
+ { META_ID_TLLI, "TLLI" },
+ { META_ID_NSAPI, "NSAPI" },
+ { META_ID_APN, "APN" },
+ { META_ID_RAT, "RAT" },
+ { 0, NULL }
+};
+
+#define META_AAL5PROTO_MTP3 2
+#define META_AAL5PROTO_ALCAP 5
+#define META_AAL5PROTO_NBAP 6
+static const value_string meta_aal5proto_vals[] = {
+ { META_AAL5PROTO_MTP3, "SSCOP MTP3" },
+ { META_AAL5PROTO_ALCAP, "SSCOP ALCAP" },
+ { META_AAL5PROTO_NBAP, "SSCOP NBAP" },
+ { 0, NULL }
+};
+
+static const value_string meta_direction_vals[] = {
+ { 0, "Up" },
+ { 1, "Down" },
+ { 0, NULL }
+};
+
+static guint16 skip_item(proto_tree *meta_tree, tvbuff_t *tvb, packet_info *pinfo _U_, guint16 offs)
+{
+ guint16 id;
+ guint8 type, len, aligned_len, total_len;
+ proto_tree *item_tree;
+ proto_item *subti;
+
+ id = tvb_get_letohs(tvb, offs); offs += 2;
+ type = tvb_get_guint8(tvb, offs); offs++;
+ len = tvb_get_guint8(tvb, offs); offs++;
+ aligned_len = (len + 3) & 0xfffc;
+ total_len = aligned_len + 4; /* 4: id, type, len fields */
+
+ subti = proto_tree_add_item(meta_tree, hf_meta_item, tvb, offs - 4,
+ aligned_len + 4, FALSE);
+ item_tree = proto_item_add_subtree(subti, ett_meta_item);
+ proto_tree_add_uint(item_tree, hf_meta_item_id, tvb, offs - 4, 2, id);
+ proto_tree_add_uint(item_tree, hf_meta_item_type, tvb, offs - 2, 1, type);
+ proto_tree_add_uint(item_tree, hf_meta_item_len,
+ tvb, offs - 1, 1, len);
+ if (len > 0)
+ proto_tree_add_bytes(item_tree, hf_meta_item_data,
+ tvb, offs, len, tvb_get_ptr(tvb, offs, len));
+
+ return total_len;
+}
+
+/*
+* offs: current offset in tvb
+*/
+static guint16 evaluate_meta_item_pcap(proto_tree *meta_tree, tvbuff_t *tvb, packet_info *pinfo, guint16 offs)
+{
+ guint16 id;
+ guint8 type, len, aligned_len, total_len;
+ proto_tree *item_tree;
+ proto_item *subti;
+ /* field values */
+ guint8 dir;
+ guint64 ts;
+
+ id = tvb_get_letohs(tvb, offs); offs += 2;
+ type = tvb_get_guint8(tvb, offs); offs++;
+ len = tvb_get_guint8(tvb, offs); offs++;
+ aligned_len = (len + 3) & 0xfffc;
+ total_len = aligned_len + 4; /* 4: id, type, len fields */
+
+ switch (id) {
+ case META_ID_DIRECTION:
+ dir = tvb_get_guint8(tvb, offs);
+ pinfo->p2p_dir = dir == META_DIR_UP ? P2P_DIR_RECV : P2P_DIR_SENT;
+ proto_tree_add_uint(meta_tree, hf_meta_item_direction, tvb, offs, 1, dir);
+ break;
+ case META_ID_TIMESTAMP64:
+ ts = tvb_get_letoh64(tvb, offs);
+ proto_tree_add_uint64(meta_tree, hf_meta_item_ts, tvb, offs, 8, ts);
+ break;
+ case META_ID_SIGNALING:
+ proto_tree_add_boolean(meta_tree, hf_meta_item_signaling, tvb,
+ offs, 0, 1);
+ break;
+ case META_ID_INCOMPLETE:
+ proto_tree_add_boolean(meta_tree, hf_meta_item_incomplete, tvb,
+ offs, 0, 1);
+ break;
+ default:
+ subti = proto_tree_add_item(meta_tree, hf_meta_item, tvb, offs - 4,
+ aligned_len + 4, FALSE);
+ item_tree = proto_item_add_subtree(subti, ett_meta_item);
+ proto_tree_add_uint(item_tree, hf_meta_item_id, tvb, offs - 4, 2, id);
+ proto_tree_add_uint(item_tree, hf_meta_item_type, tvb, offs - 2, 1, type);
+ proto_tree_add_uint(item_tree, hf_meta_item_len,
+ tvb, offs - 1, 1, len);
+ if (len > 0)
+ proto_tree_add_bytes(item_tree, hf_meta_item_data,
+ tvb, offs, len, tvb_get_ptr(tvb, offs, len));
+ }
+ return total_len;
+}
+
+/*
+* offs: current offset in tvb
+*/
+static guint16 evaluate_meta_item_dxt(proto_tree *meta_tree, tvbuff_t *tvb, packet_info *pinfo, guint16 offs)
+{
+ guint16 id;
+ guint8 type, len, aligned_len, total_len;
+ proto_tree *item_tree;
+ proto_item *subti;
+ /* field values */
+ guint8 dir, nsapi, rat, aal5proto, *apn;
+ guint16 phylinkid;
+ guint64 ts, imsi, imei, cell;
+ sscop_payload_info *p_sscop_info;
+
+ id = tvb_get_letohs(tvb, offs); offs += 2;
+ type = tvb_get_guint8(tvb, offs); offs++;
+ len = tvb_get_guint8(tvb, offs); offs++;
+ aligned_len = (len + 3) & 0xfffc;
+ total_len = aligned_len + 4; /* 4: id, type, len fields */
+
+ switch (id) {
+ case META_ID_DIRECTION:
+ dir = tvb_get_guint8(tvb, offs);
+ pinfo->p2p_dir = dir == META_DIR_UP ? P2P_DIR_RECV : P2P_DIR_SENT;
+ proto_tree_add_uint(meta_tree, hf_meta_item_direction, tvb, offs, 1, dir);
+ break;
+ case META_ID_TIMESTAMP64:
+ ts = tvb_get_letoh64(tvb, offs);
+ proto_tree_add_uint64(meta_tree, hf_meta_item_ts, tvb, offs, 8, ts);
+ break;
+ case META_ID_PHYLINKID:
+ phylinkid = tvb_get_letohs(tvb, offs);
+ pinfo->link_number = phylinkid;
+ proto_tree_add_uint(meta_tree, hf_meta_item_phylinkid, tvb,
+ offs, 2, phylinkid);
+ break;
+ case META_ID_NSAPI:
+ nsapi = tvb_get_guint8(tvb, offs);
+ proto_tree_add_uint(meta_tree, hf_meta_item_nsapi, tvb,
+ offs, 1, nsapi);
+ break;
+ case META_ID_IMSI:
+ imsi = tvb_get_letoh64(tvb, offs);
+ proto_tree_add_uint64(meta_tree, hf_meta_item_imsi, tvb,
+ offs, 8, imsi);
+ break;
+ case META_ID_IMEI:
+ imei = tvb_get_letoh64(tvb, offs);
+ proto_tree_add_uint64(meta_tree, hf_meta_item_imei, tvb,
+ offs, 8, imei);
+ break;
+ case META_ID_APN:
+ apn = tvb_get_string(tvb, offs, len);
+ proto_tree_add_string(meta_tree, hf_meta_item_apn, tvb,
+ offs, len, apn);
+ break;
+ case META_ID_RAT:
+ rat = tvb_get_guint8(tvb, offs);
+ proto_tree_add_uint(meta_tree, hf_meta_item_rat, tvb,
+ offs, 1, rat);
+ break;
+ case META_ID_CELL:
+ cell = tvb_get_ntoh64(tvb, offs);
+ proto_tree_add_uint64(meta_tree, hf_meta_item_cell, tvb,
+ offs, 8, cell);
+ break;
+ case META_ID_SIGNALING:
+ proto_tree_add_boolean(meta_tree, hf_meta_item_signaling, tvb,
+ offs, 0, 1);
+ break;
+ case META_ID_INCOMPLETE:
+ proto_tree_add_boolean(meta_tree, hf_meta_item_incomplete, tvb,
+ offs, 0, 1);
+ break;
+ case META_ID_AAL5PROTO:
+ aal5proto = tvb_get_guint8(tvb, offs);
+ p_sscop_info = p_get_proto_data(pinfo->fd, proto_sscop);
+ if (!p_sscop_info) {
+ p_sscop_info = se_alloc0(sizeof(sscop_payload_info));
+ p_add_proto_data(pinfo->fd, proto_sscop, p_sscop_info);
+ }
+ switch (aal5proto) {
+ case META_AAL5PROTO_MTP3:
+ p_sscop_info->subdissector = sscf_nni_handle;
+ break;
+ case META_AAL5PROTO_ALCAP:
+ p_sscop_info->subdissector = alcap_handle;
+ break;
+ case META_AAL5PROTO_NBAP:
+ p_sscop_info->subdissector = nbap_handle;
+ break;
+ /* TODO: check for additional protos on Iu 802 LLC/SNAP ... */
+ default:
+ /* TODO: add warning */
+ p_remove_proto_data(pinfo->fd, proto_sscop);
+ }
+ proto_tree_add_uint(meta_tree, hf_meta_item_aal5proto, tvb,
+ offs, 1, aal5proto);
+ break;
+ default:
+ subti = proto_tree_add_item(meta_tree, hf_meta_item, tvb, offs - 4,
+ aligned_len + 4, FALSE);
+ item_tree = proto_item_add_subtree(subti, ett_meta_item);
+ proto_tree_add_uint(item_tree, hf_meta_item_id, tvb, offs - 4, 2, id);
+ proto_tree_add_uint(item_tree, hf_meta_item_type, tvb, offs - 2, 1, type);
+ proto_tree_add_uint(item_tree, hf_meta_item_len,
+ tvb, offs - 1, 1, len);
+ if (len > 0)
+ proto_tree_add_bytes(item_tree, hf_meta_item_data,
+ tvb, offs, len, tvb_get_ptr(tvb, offs, len));
+ }
+ return total_len;
+}
+
+/*
+ * offs: current offset within tvb
+ * header_length: length of meta header
+ */
+static gint32 evaluate_meta_items(guint16 schema, tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *meta_tree, guint16 offs, gint32 header_length)
+{
+ gint16 item_len;
+ gint32 total_len = 0;
+ while (total_len < header_length) {
+ switch (schema) {
+ case META_SCHEMA_DXT:
+ item_len = evaluate_meta_item_dxt(meta_tree, tvb, pinfo, offs + total_len);
+ break;
+ case META_SCHEMA_PCAP:
+ item_len = evaluate_meta_item_pcap(meta_tree, tvb, pinfo, offs + total_len);
+ break;
+ default:
+ item_len = skip_item(meta_tree, tvb, pinfo, offs + total_len);
+ }
+ if (item_len < 4) { /* 4 is the minimum length of an item: id + type + length field */
+ proto_item *malformed;
+ malformed = proto_tree_add_protocol_format(meta_tree,
+ proto_malformed, tvb, offs, -1, "[Malformed Packet: %s]", pinfo->current_proto);
+ expert_add_info_format(pinfo, malformed, PI_MALFORMED, PI_ERROR,
+ "Malformed Packet (wrong item encoding)");
+ return -1;
+ }
+ total_len += item_len;
+ }
+ return total_len;
+}
+
+static dissector_handle_t *dxt_get_dissector(guint16 proto, packet_info *pinfo)
+{
+ dissector_handle_t *next_dissector = &data_handle;
+ switch (proto) {
+ case META_PROTO_DXT_ETHERNET:
+ next_dissector = &ethwithoutfcs_handle;
+ break;
+ case META_PROTO_DXT_ETHERNET_CRC:
+ next_dissector = &ethwithfcs_handle;
+ break;
+ case META_PROTO_DXT_FP_HINT:
+ next_dissector = &fphint_handle;
+ break;
+ case META_PROTO_DXT_ATM:
+ next_dissector = &atm_untrunc_handle;
+ pinfo->pseudo_header->atm.aal = AAL_OAMCELL;
+ pinfo->pseudo_header->atm.type = TRAF_UNKNOWN;
+ break;
+ case META_PROTO_DXT_ERF_AAL2:
+ /* fake erf pseudo header */
+ memset(&pinfo->pseudo_header->erf, 0, sizeof(pinfo->pseudo_header->erf));
+ pinfo->pseudo_header->erf.phdr.type = ERF_TYPE_AAL2;
+ /* store p2p direction in ERF flags */
+ pinfo->pseudo_header->erf.phdr.flags |= pinfo->p2p_dir;
+ next_dissector = &erf_handle;
+ break;
+ case META_PROTO_DXT_ERF_AAL5:
+ /* fake erf pseudo header */
+ memset(&pinfo->pseudo_header->erf, 0, sizeof(pinfo->pseudo_header->erf));
+ pinfo->pseudo_header->erf.phdr.type = ERF_TYPE_AAL5;
+ /* store p2p direction in ERF flags */
+ pinfo->pseudo_header->erf.phdr.flags |= pinfo->p2p_dir;
+ next_dissector = &erf_handle;
+ break;
+ default:
+ next_dissector = &data_handle;
+ }
+ return next_dissector;
+}
+
+static void
+dissect_meta(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+#define META_HEADER_SIZE 8
+ guint16 schema, proto, hdrlen, reserved;
+ gint32 item_len;
+ proto_tree *meta_tree = NULL;
+ proto_item *ti = NULL;
+ tvbuff_t *next_tvb;
+ dissector_handle_t *next_dissector = &data_handle;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "META");
+
+ schema = tvb_get_letohs(tvb, 0);
+ hdrlen = tvb_get_letohs(tvb, 2);
+ proto = tvb_get_letohs(tvb, 4);
+ reserved = tvb_get_letohs(tvb, 6);
+
+ if (tree) {
+ ti = proto_tree_add_item(tree, proto_meta, tvb, 0, hdrlen + 4, FALSE);
+ meta_tree = proto_item_add_subtree(ti, ett_meta);
+ proto_tree_add_uint(meta_tree, hf_meta_schema, tvb, 0, 2, schema);
+ proto_tree_add_uint(meta_tree, hf_meta_hdrlen, tvb, 2, 2, hdrlen);
+ proto_tree_add_uint(meta_tree, hf_meta_proto, tvb, 4, 2, proto);
+ proto_tree_add_uint(meta_tree, hf_meta_reserved, tvb, 6, 2, reserved);
+ }
+ item_len = evaluate_meta_items(schema, tvb, pinfo, meta_tree, META_HEADER_SIZE, hdrlen);
+
+ if (item_len < 0) {
+ /* evaluate_meta_items signalled an error */
+ return; /* stop parsing */
+ }
+
+ if (hdrlen != item_len) {
+ expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "Invalid Header Length");
+ proto_tree_add_text(tree, tvb, hdrlen+4, -1, "[Malformed Packet]");
+ return;
+ }
+
+ /* find next subdissector based on the chosen schema */
+ switch (schema) {
+ case META_SCHEMA_PCAP:
+ /* TODO */
+ break;
+ case META_SCHEMA_DXT:
+ next_dissector = dxt_get_dissector(proto, pinfo);
+ break;
+ default:
+ next_dissector = &data_handle;
+ }
+ next_tvb = tvb_new_subset(tvb, item_len + META_HEADER_SIZE, -1, -1);
+ call_dissector(*next_dissector, next_tvb, pinfo, tree);
+}
+
+void
+proto_register_meta(void)
+{
+ static hf_register_info hf[] = {
+ /* metadata header */
+ { &hf_meta_schema, { "Schema", "meta.schema", FT_UINT16, BASE_DEC, VALS(meta_schema_vals), 0, "", HFILL } },
+ { &hf_meta_hdrlen, { "Header Length", "meta.hdrlen", FT_UINT16, BASE_DEC, NULL, 0, "", HFILL } },
+ { &hf_meta_proto, { "Protocol", "meta.proto", FT_UINT16, BASE_DEC, VALS(meta_proto_vals), 0, "", HFILL } },
+ { &hf_meta_reserved, { "Reserved", "meta.reserved", FT_UINT16, BASE_HEX, NULL, 0, "", HFILL } },
+
+ /* general meta item */
+ { &hf_meta_item, { "Unknown Item", "meta.item", FT_NONE, BASE_NONE, NULL, 0, "", HFILL } },
+ { &hf_meta_item_id, { "Item ID", "meta.item.id", FT_UINT16, BASE_HEX, VALS(meta_id_vals), 0x0, "", HFILL } },
+ { &hf_meta_item_type, { "Item Type", "meta.item.type", FT_UINT8, BASE_HEX, VALS(meta_type_vals), 0x0, "", HFILL } },
+ { &hf_meta_item_len, { "Item Length", "meta.item.len", FT_UINT8, BASE_DEC, NULL, 0, "", HFILL } },
+ { &hf_meta_item_data, { "Item Data", "meta.item.data", FT_BYTES, BASE_HEX, NULL, 0x0, "", HFILL } },
+
+ /* specific meta items */
+ { &hf_meta_item_direction, { "Direction", "meta.direction", FT_UINT8, BASE_DEC, VALS(meta_direction_vals), 0, "", HFILL } },
+ { &hf_meta_item_ts, { "Timestamp", "meta.timestamp", FT_UINT64, BASE_DEC, NULL, 0, "", HFILL } },
+ { &hf_meta_item_phylinkid, { "Physical Link ID", "meta.phylinkid", FT_UINT16, BASE_DEC, NULL, 0, "", HFILL } },
+ { &hf_meta_item_nsapi, { "NSAPI", "meta.nsapi", FT_UINT8, BASE_DEC, NULL, 0, "", HFILL } },
+ { &hf_meta_item_imsi, { "IMSI", "meta.imsi", FT_UINT64, BASE_HEX, NULL, 0, "", HFILL } },
+ { &hf_meta_item_imei, { "IMEI", "meta.imei", FT_UINT64, BASE_HEX, NULL, 0, "", HFILL } },
+ { &hf_meta_item_signaling, { "Signaling", "meta.signaling", FT_BOOLEAN, BASE_NONE, NULL, 0, "", HFILL } },
+ { &hf_meta_item_incomplete, { "Incomplete", "meta.incomplete", FT_BOOLEAN, BASE_NONE, NULL, 0, "", HFILL } },
+ { &hf_meta_item_apn, { "APN", "meta.apn", FT_STRINGZ, BASE_NONE, NULL, 0, "", HFILL } },
+ { &hf_meta_item_rat, { "RAT", "meta.rat", FT_UINT8, BASE_DEC, NULL, 0, "", HFILL } },
+ { &hf_meta_item_aal5proto, { "AAL5 Protocol Type", "meta.aal5proto", FT_UINT8, BASE_DEC, VALS(meta_aal5proto_vals), 0, "", HFILL } },
+ { &hf_meta_item_cell, { "Cell", "meta.cell", FT_UINT64, BASE_HEX, NULL, 0, "", HFILL } },
+ };
+
+ static gint *ett[] = {
+ &ett_meta,
+ &ett_meta_item
+ };
+
+ proto_meta = proto_register_protocol("Metadata", "META", "meta");
+ register_dissector("meta", dissect_meta, proto_meta);
+
+ proto_register_field_array(proto_meta, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_meta(void)
+{
+ dissector_handle_t meta_handle;
+ meta_handle = find_dissector("meta");
+ /* enable once WTAP_ENCAP_META exists
+ dissector_add("wtap_encap", WTAP_ENCAP_META, meta_handle);
+ */
+
+ data_handle = find_dissector("data");
+ alcap_handle = find_dissector("alcap");
+ atm_untrunc_handle = find_dissector("atm_untruncated");
+ nbap_handle = find_dissector("nbap");
+ sscf_nni_handle = find_dissector("sscf-nni");
+ ethwithfcs_handle = find_dissector("eth_withfcs");
+ ethwithoutfcs_handle = find_dissector("eth_withoutfcs");
+ fphint_handle = find_dissector("fp_hint");
+ erf_handle = find_dissector("erf");
+}
diff --git a/epan/dissectors/packet-meta.h b/epan/dissectors/packet-meta.h
new file mode 100644
index 0000000000..e54e585730
--- /dev/null
+++ b/epan/dissectors/packet-meta.h
@@ -0,0 +1,69 @@
+/* Routines for 'Metadata' disassembly
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* schemas */
+#define META_SCHEMA_PCAP 1
+#define META_SCHEMA_DXT 2
+
+/* protocols */
+#define META_PROTO_DXT_ETHERNET 1
+#define META_PROTO_DXT_ETHERNET_CRC 36
+#define META_PROTO_DXT_ATM 41
+#define META_PROTO_DXT_ERF_AAL5 49
+#define META_PROTO_DXT_ERF_AAL2 76
+#define META_PROTO_DXT_FP_HINT 82
+
+/* data types */
+#define META_TYPE_NONE 0
+#define META_TYPE_BOOLEAN 1
+#define META_TYPE_UINT8 2
+#define META_TYPE_UINT16 3
+#define META_TYPE_UINT32 4
+#define META_TYPE_UINT64 5
+#define META_TYPE_STRING 16
+
+/* item ids */
+#define META_ID_NULL 0
+#define META_ID_DIRECTION 1
+#define META_ID_SIGNALING 2
+#define META_ID_INCOMPLETE 3
+#define META_ID_DECIPHERED 4
+#define META_ID_PAYLOADCUT 5
+#define META_ID_TIMESTAMP64 6
+#define META_ID_AAL5PROTO 7
+#define META_ID_PHYLINKID 256
+#define META_ID_LOCALDEVID 257
+#define META_ID_REMOTEDEVID 258
+#define META_ID_TAPGROUPID 259
+#define META_ID_IMSI 1024
+#define META_ID_IMEI 1025
+#define META_ID_CELL 1026
+#define META_ID_TLLI 1027
+#define META_ID_NSAPI 1028
+#define META_ID_APN 1029
+#define META_ID_RAT 1030
+
+enum meta_direction {
+ META_DIR_UP,
+ META_DIR_DOWN
+};
diff --git a/epan/dissectors/packet-rlc.c b/epan/dissectors/packet-rlc.c
new file mode 100644
index 0000000000..4ac5acd830
--- /dev/null
+++ b/epan/dissectors/packet-rlc.c
@@ -0,0 +1,1384 @@
+/* Routines for UMTS RLC disassembly
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <epan/packet.h>
+#include <epan/asn1.h>
+#include <epan/expert.h>
+#include "packet-umts_fp.h"
+#include "packet-umts_mac.h"
+#include "packet-rlc.h"
+#include "packet-rrc.h"
+
+/* TODO:
+ * - 15 bit Length Identifiers
+ * - AM SEQ wrap case
+ * - UM/AM 'real' reordering (final packet must appear in-order right now)
+ * - decode CW values in RLIST SUFI
+ * - decode RESET & RESET ACK
+ * - use sub_num in fragment identification?
+ */
+
+#define DEBUG_FRAME(number, msg) {if (pinfo->fd->num == number) printf("%u: %s\n", number, msg);}
+
+int proto_rlc = -1;
+extern int proto_fp;
+extern int proto_malformed;
+
+/* fields */
+static int hf_rlc_seq = -1;
+static int hf_rlc_ext = -1;
+static int hf_rlc_pad = -1;
+static int hf_rlc_frags = -1;
+static int hf_rlc_frag = -1;
+static int hf_rlc_duplicate_of = -1;
+static int hf_rlc_reassembled_in = -1;
+static int hf_rlc_he = -1;
+static int hf_rlc_dc = -1;
+static int hf_rlc_p = -1;
+static int hf_rlc_li = -1;
+static int hf_rlc_li_value = -1;
+static int hf_rlc_li_ext = -1;
+static int hf_rlc_li_data = -1;
+static int hf_rlc_data = -1;
+static int hf_rlc_ctrl_type = -1;
+static int hf_rlc_sufi = -1;
+static int hf_rlc_sufi_type = -1;
+static int hf_rlc_sufi_lsn = -1;
+static int hf_rlc_sufi_wsn = -1;
+static int hf_rlc_sufi_sn = -1;
+static int hf_rlc_sufi_l = -1;
+static int hf_rlc_sufi_fsn = -1;
+static int hf_rlc_sufi_len = -1;
+static int hf_rlc_sufi_bitmap = -1;
+static int hf_rlc_sufi_cw = -1;
+static int hf_rlc_sufi_n = -1;
+static int hf_rlc_sufi_sn_ack = -1;
+static int hf_rlc_sufi_sn_mrw = -1;
+
+/* subtrees */
+static int ett_rlc = -1;
+static int ett_rlc_frag = -1;
+static int ett_rlc_fragments = -1;
+static int ett_rlc_sdu = -1;
+static int ett_rlc_sufi = -1;
+
+static dissector_handle_t ip_handle;
+static dissector_handle_t rrc_handle;
+
+enum channel_type {
+ PCCH,
+ UL_CCCH,
+ DL_CCCH,
+ UL_DCCH,
+ DL_DCCH,
+ PS_DTCH,
+};
+
+static const true_false_string rlc_ext_val = {
+ "Next field is Length Indicator and E Bit", "Next field is data, piggybacked STATUS PDU or padding"
+};
+
+static const true_false_string rlc_dc_val = {
+ "Data", "Control"
+};
+
+static const true_false_string rlc_p_val = {
+ "Request a status report", "Status report not requested"
+};
+
+static const value_string rlc_he_vals[] = {
+ { 0, "The succeeding octet contains data" },
+ { 1, "The succeeding octet contains a length indicator and E bit" },
+ { 0, NULL }
+};
+
+#define RLC_STATUS 0x0
+#define RLC_RESET 0x1
+#define RLC_RESET_ACK 0x2
+static const value_string rlc_ctrl_vals[] = {
+ { RLC_STATUS, "STATUS" },
+ { RLC_RESET, "RESET" },
+ { RLC_RESET_ACK, "RESET ACK" },
+ { 0, NULL }
+};
+
+#define RLC_SUFI_NOMORE 0x0
+#define RLC_SUFI_WINDOW 0x1
+#define RLC_SUFI_ACK 0x2
+#define RLC_SUFI_LIST 0x3
+#define RLC_SUFI_BITMAP 0x4
+#define RLC_SUFI_RLIST 0x5
+#define RLC_SUFI_MRW 0x6
+#define RLC_SUFI_MRW_ACK 0x7
+static const value_string rlc_sufi_vals[] = {
+ { RLC_SUFI_NOMORE, "No more data" },
+ { RLC_SUFI_WINDOW, "Window size" },
+ { RLC_SUFI_ACK, "Acknowledgement" },
+ { RLC_SUFI_LIST, "List" },
+ { RLC_SUFI_BITMAP, "Bitmap" },
+ { RLC_SUFI_RLIST, "Relative list" },
+ { RLC_SUFI_MRW, "Move receiving window" },
+ { RLC_SUFI_MRW_ACK, "Move receiving window acknowledgement" },
+ { 0, NULL }
+};
+
+/* reassembly related data */
+static GHashTable *fragment_table = NULL; /* maps rlc_channel -> fragmented sdu */
+static GHashTable *reassembled_table = NULL; /* maps fragment -> complete sdu */
+static GHashTable *sequence_table = NULL; /* channel -> seq */
+
+/* identify an RLC channel, using one of two options:
+ * - via Radio Bearer ID and U-RNTI
+ * - via Radio Bearer ID and (VPI/VCI/CID) + Link ID
+ */
+struct rlc_channel {
+ guint32 urnti;
+ guint16 vpi;
+ guint16 vci;
+ guint8 cid;
+ guint16 link; /* link number */
+ guint8 rbid; /* radio bearer ID */
+ guint8 dir; /* direction */
+
+ enum rlc_mode mode;
+};
+
+/* used for duplicate detection */
+struct rlc_seq {
+ guint32 frame_num;
+ nstime_t arrival;
+ guint16 seq;
+ guint16 oc; /* overflow counter */
+};
+
+struct rlc_seqlist {
+ struct rlc_channel ch;
+
+ GList *list;
+};
+
+/* fragment representation */
+struct rlc_frag {
+ guint32 frame_num;
+ struct rlc_channel ch;
+ guint16 seq; /* RLC sequence number */
+ guint16 li; /* LI within current RLC frame */
+ guint16 len; /* length of fragment data */
+ guint8 *data; /* store fragment data here */
+
+ struct rlc_frag *next; /* next fragment */
+};
+
+struct rlc_sdu {
+ tvbuff_t *tvb; /* contains reassembled tvb */
+ guint16 len; /* total length of reassembled SDU */
+ guint16 fragcnt; /* number of fragments within this SDU */
+ guint8 *data; /* reassembled data buffer */
+
+ struct rlc_frag *reassembled_in;
+ struct rlc_frag *frags; /* pointer to list of fragments */
+ struct rlc_frag *last; /* pointer to last fragment */
+};
+
+struct rlc_li {
+ guint16 li; /* original li */
+ guint16 len; /* length of this data fragment */
+ guint8 ext; /* extension bit value */
+
+ proto_tree *tree; /* subtree for this LI */
+};
+
+/* hashtable functions for fragment table
+ * rlc_channel -> SDU
+ */
+static guint rlc_channel_hash(gconstpointer key)
+{
+ const struct rlc_channel *ch = key;
+
+ if (ch->urnti)
+ return ch->urnti | ch->rbid | ch->mode;
+
+ return (ch->vci << 16) | (ch->link << 16) | ch->vpi | ch->vci;
+}
+
+static gboolean rlc_channel_equal(gconstpointer a, gconstpointer b)
+{
+ const struct rlc_channel *x = a, *y = b;
+
+ if (x->urnti || y->urnti)
+ return x->urnti == y->urnti &&
+ x->rbid == y->rbid &&
+ x->mode == y->mode &&
+ x->dir == y->dir ? TRUE : FALSE;
+
+ return x->vpi == y->vpi &&
+ x->vci == y->vci &&
+ x->cid == y->cid &&
+ x->rbid == y->rbid &&
+ x->mode == y->mode &&
+ x->dir == y->dir &&
+ x->link == y->link ? TRUE : FALSE;
+}
+
+static int rlc_channel_assign(struct rlc_channel *ch, enum rlc_mode mode, packet_info *pinfo)
+{
+ struct atm_phdr *atm;
+ rlc_info *rlcinf;
+ fp_info *fpinf;
+
+ atm = &pinfo->pseudo_header->atm;
+ fpinf = p_get_proto_data(pinfo->fd, proto_fp);
+ rlcinf = p_get_proto_data(pinfo->fd, proto_rlc);
+ if (!fpinf || !rlcinf || !atm) return -1;
+
+ if (rlcinf->urnti[fpinf->cur_tb]) {
+ ch->urnti = rlcinf->urnti[fpinf->cur_tb];
+ ch->vpi = ch->vci = ch->link = ch->cid = 0;
+ } else {
+ ch->urnti = 0;
+ ch->vpi = atm->vpi;
+ ch->vci = atm->vci;
+ ch->cid = atm->aal2_cid;
+ ch->link = pinfo->link_number;
+ }
+ ch->rbid = rlcinf->rbid[fpinf->cur_tb];
+ ch->dir = pinfo->p2p_dir;
+ ch->mode = mode;
+
+ return 0;
+}
+
+static struct rlc_channel *rlc_channel_create(enum rlc_mode mode, packet_info *pinfo)
+{
+ struct rlc_channel *ch;
+ int rv;
+
+ ch = g_malloc0(sizeof(struct rlc_channel));
+ rv = rlc_channel_assign(ch, mode, pinfo);
+
+ if (rv != 0) {
+ /* channel assignment failed */
+ g_free(ch);
+ ch = NULL;
+ }
+ return ch;
+}
+
+static void rlc_channel_delete(gpointer data)
+{
+ g_free(data);
+}
+
+/* hashtable functions for reassembled table
+ * fragment -> SDU
+ */
+static guint rlc_frag_hash(gconstpointer key)
+{
+ const struct rlc_frag *frag = key;
+ return rlc_channel_hash(&frag->ch) | frag->li | frag->seq;
+}
+
+static gboolean rlc_frag_equal(gconstpointer a, gconstpointer b)
+{
+ const struct rlc_frag *x = a, *y = b;
+
+ return rlc_channel_equal(&x->ch, &y->ch) &&
+ x->seq == y->seq &&
+ x->frame_num == y->frame_num &&
+ x->li == y->li ? TRUE : FALSE;
+}
+
+
+static struct rlc_sdu *rlc_sdu_create(void)
+{
+ struct rlc_sdu *sdu;
+ sdu = se_alloc0(sizeof(struct rlc_sdu));
+ return sdu;
+}
+
+static void rlc_frag_delete(gpointer data)
+{
+ struct rlc_frag *frag = data;
+ if (frag->data) {
+ g_free(frag->data);
+ frag->data = NULL;
+ }
+}
+
+static void rlc_sdu_frags_delete(gpointer data)
+{
+ struct rlc_sdu *sdu = data;
+ struct rlc_frag *frag;
+
+ frag = sdu->frags;
+ while (frag) {
+ if (frag->data) {
+ g_free(frag->data);
+ }
+ frag->data = NULL;
+ frag = frag->next;
+ }
+}
+
+static int rlc_frag_assign(struct rlc_frag *frag, enum rlc_mode mode, packet_info *pinfo,
+ guint16 seq, guint16 li)
+{
+ frag->frame_num = pinfo->fd->num;
+ frag->seq = seq;
+ frag->li = li;
+ frag->len = 0;
+ frag->data = NULL;
+ rlc_channel_assign(&frag->ch, mode, pinfo);
+
+ return 0;
+}
+
+static int rlc_frag_assign_data(struct rlc_frag *frag, tvbuff_t *tvb,
+ guint16 offset, guint16 length)
+{
+ frag->len = length;
+ frag->data = g_malloc(length);
+ tvb_memcpy(tvb, frag->data, offset, length);
+ return 0;
+}
+
+static struct rlc_frag *rlc_frag_create(tvbuff_t *tvb, enum rlc_mode mode, packet_info *pinfo,
+ guint16 offset, guint16 length, guint16 seq, guint16 li)
+{
+ struct rlc_frag *frag;
+ frag = se_alloc0(sizeof(struct rlc_frag));
+ rlc_frag_assign(frag, mode, pinfo, seq, li);
+ rlc_frag_assign_data(frag, tvb, offset, length);
+
+ return frag;
+}
+
+static int rlc_cmp_seq(gconstpointer a, gconstpointer b)
+{
+ const struct rlc_seq *_a = a, *_b = b;
+
+ return _a->seq < _b->seq ? -1 :
+ _a->seq > _b->seq ? 1 :
+ 0;
+}
+
+static void fragment_table_init(void)
+{
+ if (fragment_table) {
+ g_hash_table_remove_all(fragment_table);
+ g_hash_table_destroy(fragment_table);
+ }
+ if (reassembled_table) {
+ g_hash_table_remove_all(reassembled_table);
+ g_hash_table_destroy(reassembled_table);
+ }
+ if (sequence_table) {
+ g_hash_table_remove_all(sequence_table);
+ g_hash_table_destroy(sequence_table);
+ }
+ fragment_table = g_hash_table_new_full(rlc_channel_hash, rlc_channel_equal,
+ rlc_channel_delete, rlc_sdu_frags_delete);
+ reassembled_table = g_hash_table_new_full(rlc_frag_hash, rlc_frag_equal,
+ rlc_frag_delete, rlc_sdu_frags_delete);
+ sequence_table = g_hash_table_new(rlc_channel_hash, rlc_channel_equal);
+}
+
+/* add the list of fragments for this sdu to 'tree' */
+static void tree_add_fragment_list(struct rlc_sdu *sdu, tvbuff_t *tvb, proto_tree *tree)
+{
+ proto_item *ti;
+ proto_tree *frag_tree;
+ guint16 offset;
+ struct rlc_frag *sdufrag;
+ ti = proto_tree_add_item(tree, hf_rlc_frags, tvb, 0, -1, FALSE);
+ frag_tree = proto_item_add_subtree(ti, ett_rlc_fragments);
+ proto_item_append_text(ti, " (%u bytes, %u fragments): ",
+ sdu->len, sdu->fragcnt);
+ sdufrag = sdu->frags;
+ offset = 0;
+ while (sdufrag) {
+ proto_tree_add_uint_format(frag_tree, hf_rlc_frag, tvb, offset,
+ sdufrag->len, sdufrag->frame_num, "Frame: %u, payload %u-%u (%u bytes) (Seq: %u)",
+ sdufrag->frame_num, offset, offset + sdufrag->len - 1, sdufrag->len, sdufrag->seq);
+ offset += sdufrag->len;
+ sdufrag = sdufrag->next;
+ }
+}
+
+/* add the list of fragments for this sdu to 'tree' */
+static void tree_add_fragment_list_incomplete(struct rlc_sdu *sdu, tvbuff_t *tvb, proto_tree *tree)
+{
+ proto_item *ti;
+ proto_tree *frag_tree;
+ guint16 offset;
+ struct rlc_frag *sdufrag;
+ ti = proto_tree_add_item(tree, hf_rlc_frags, tvb, 0, 0, FALSE);
+ frag_tree = proto_item_add_subtree(ti, ett_rlc_fragments);
+ proto_item_append_text(ti, " (%u bytes, %u fragments): ",
+ sdu->len, sdu->fragcnt);
+ sdufrag = sdu->frags;
+ offset = 0;
+ while (sdufrag) {
+ proto_tree_add_uint_format(frag_tree, hf_rlc_frag, tvb, 0,
+ 0, sdufrag->frame_num, "Frame: %u, payload %u-%u (%u bytes) (Seq: %u)",
+ sdufrag->frame_num, offset, offset + sdufrag->len - 1, sdufrag->len, sdufrag->seq);
+ offset += sdufrag->len;
+ sdufrag = sdufrag->next;
+ }
+}
+
+/* add information for an LI to 'tree' */
+static proto_tree *tree_add_li(struct rlc_li *li, guint8 li_idx, guint8 hdr_offs, tvbuff_t *tvb, proto_tree *tree)
+{
+ proto_item *ti;
+ proto_tree *li_tree;
+ guint8 li_offs;
+
+ if (!tree) return NULL;
+
+ li_offs = hdr_offs + li_idx;
+
+ ti = proto_tree_add_item(tree, hf_rlc_li, tvb, li_offs, 1, FALSE);
+ li_tree = proto_item_add_subtree(ti, ett_rlc_frag);
+ proto_tree_add_bits_item(li_tree, hf_rlc_li_value, tvb, li_offs*8, 7, FALSE);
+ proto_tree_add_item(li_tree, hf_rlc_li_ext, tvb, li_offs, 1, FALSE);
+
+ if (li->len > 0) {
+ if (li->li > tvb_length_remaining(tvb, hdr_offs)) return li_tree;
+ if (li->len > li->li) return li_tree;
+ proto_tree_add_item(li_tree, hf_rlc_li_data, tvb, hdr_offs + li->li - li->len, li->len, FALSE);
+ }
+
+ return li_tree;
+}
+
+/* add a fragment to an SDU */
+static int rlc_sdu_add_fragment(enum rlc_mode mode, struct rlc_sdu *sdu,
+ struct rlc_frag *frag)
+{
+ struct rlc_frag *tmp;
+
+ if (!sdu->frags) {
+ /* insert as first element */
+ sdu->frags = frag;
+ sdu->last = frag;
+ sdu->fragcnt++;
+ sdu->len += frag->len;
+ return 0;
+ }
+ switch (mode) {
+ case RLC_UM:
+ /* insert as last element */
+ sdu->last->next = frag;
+ frag->next = NULL;
+ sdu->last = frag;
+ sdu->len += frag->len;
+ break;
+ case RLC_AM:
+ /* insert ordered */
+ tmp = sdu->frags;
+ if (frag->seq < tmp->seq) {
+ /* insert as first element */
+ frag->next = tmp;
+ sdu->frags = frag;
+ } else {
+ while (tmp->next && tmp->next->seq < frag->seq)
+ tmp = tmp->next;
+ frag->next = tmp->next;
+ tmp->next = frag;
+ if (frag->next == NULL) sdu->last = frag;
+ }
+ sdu->len += frag->len;
+ break;
+ default:
+ return -2;
+ }
+ sdu->fragcnt++;
+ return 0;
+}
+
+static void reassemble_message(struct rlc_channel *ch, struct rlc_sdu *sdu, struct rlc_frag *frag)
+{
+ struct rlc_frag *temp;
+ guint16 offs = 0;
+
+ if (!sdu || !ch || !sdu->frags) return;
+
+ if (sdu->data) return; /* already assembled */
+
+ if (frag)
+ sdu->reassembled_in = frag;
+ else
+ sdu->reassembled_in = sdu->last;
+
+ sdu->data = se_alloc(sdu->len);
+
+ temp = sdu->frags;
+ while (temp) {
+ memcpy(sdu->data + offs, temp->data, temp->len);
+ /* mark this fragment in reassembled table */
+ g_hash_table_insert(reassembled_table, temp, sdu);
+
+ offs += temp->len;
+ temp = temp->next;
+ }
+ g_hash_table_remove(fragment_table, ch);
+}
+
+/* add a new fragment to an SDU
+ * if length == 0, just finalize the specified SDU
+ */
+static struct rlc_frag *add_fragment(enum rlc_mode mode, tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *tree, guint16 offset, guint16 seq, guint16 num_li, guint16 len, gboolean final)
+{
+ struct rlc_channel ch_lookup;
+ struct rlc_frag frag_lookup, *frag = NULL, *tmp;
+ struct rlc_sdu *sdu;
+ gboolean found;
+
+ rlc_channel_assign(&ch_lookup, mode, pinfo);
+ rlc_frag_assign(&frag_lookup, mode, pinfo, seq, num_li);
+
+ /* look for an already assembled SDU */
+ found = g_hash_table_lookup_extended(reassembled_table, &frag_lookup,
+ (gpointer*)&frag, (gpointer*)&sdu);
+ if (found == TRUE) {
+ /* this fragment is already reassembled somewhere */
+ if (tree) {
+ /* mark the fragment, if reassembly happened somewhere else */
+ if (frag->seq != sdu->reassembled_in->seq ||
+ frag->li != sdu->reassembled_in->li)
+ proto_tree_add_uint(tree, hf_rlc_reassembled_in, tvb, 0, 0,
+ sdu->reassembled_in->frame_num);
+ }
+ return frag;
+ }
+
+ /* if not already reassembled, search for a fragment entry */
+ sdu = g_hash_table_lookup(fragment_table, &ch_lookup);
+
+ if (final && len == 0) {
+ /* just finish this SDU */
+ if (sdu) {
+ frag = rlc_frag_create(tvb, mode, pinfo, offset, len, seq, num_li);
+ rlc_sdu_add_fragment(mode, sdu, frag);
+ reassemble_message(&ch_lookup, sdu, frag);
+ }
+ return NULL;
+ }
+ /* create the SDU entry, if it does not already exist */
+ if (!sdu) {
+ /* this the first observed fragment of an SDU */
+ struct rlc_channel *ch;
+ ch = rlc_channel_create(mode, pinfo);
+ sdu = rlc_sdu_create();
+ g_hash_table_insert(fragment_table, ch, sdu);
+ }
+
+ /* check whether we have seen this fragment already */
+ tmp = sdu->frags;
+ while (tmp) {
+ if (rlc_frag_equal(&frag_lookup, tmp) == TRUE)
+ return tmp;
+ tmp = tmp->next;
+ }
+ frag = rlc_frag_create(tvb, mode, pinfo, offset, len, seq, num_li);
+ rlc_sdu_add_fragment(mode, sdu, frag);
+ if (final) {
+ reassemble_message(&ch_lookup, sdu, frag);
+ }
+ return frag;
+}
+
+/* is_data is used to identify rlc data parts that are not identified by an LI, but are at the end of
+ * the RLC frame
+ * these can be valid reassembly points, but only if the LI of the *next* relevant RLC frame is
+ * set to '0' (this is indicated in the reassembled SDU
+ */
+static tvbuff_t *get_reassembled_data(enum rlc_mode mode, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ guint16 seq, guint16 num_li)
+{
+ struct rlc_sdu *sdu;
+ struct rlc_frag lookup, *frag;
+ gboolean found;
+
+ rlc_frag_assign(&lookup, mode, pinfo, seq, num_li);
+
+ found = g_hash_table_lookup_extended(reassembled_table, &lookup, (gpointer*)&frag,
+ (gpointer*)&sdu);
+
+ if (found == FALSE || !sdu || !sdu->data)
+ return NULL;
+
+ /* TODO */
+#if 0
+ if (!rlc_frag_equal(&lookup, sdu->reassembled_in)) return NULL;
+#endif
+
+ frag = sdu->frags;
+ while (frag->next) {
+ if (frag->next->seq - frag->seq > 1) {
+ proto_item *pi = proto_tree_add_text(tree, tvb, 0, 0,
+ "Error: Incomplete sequence");
+ PROTO_ITEM_SET_GENERATED(pi);
+ tree_add_fragment_list_incomplete(sdu, tvb, tree);
+ return NULL;
+ }
+ frag = frag->next;
+ }
+ sdu->tvb = tvb_new_real_data(sdu->data, sdu->len, sdu->len);
+ tvb_set_child_real_data_tvbuff(tvb, sdu->tvb);
+ add_new_data_source(pinfo, sdu->tvb, "Reassembled RLC Message");
+
+ /* reassembly happened here, so create the fragment list */
+ tree_add_fragment_list(sdu, sdu->tvb, tree);
+
+ return sdu->tvb;
+}
+
+#define RLC_RETRANSMISSION_TIMEOUT 5 /* in seconds */
+static gboolean rlc_is_duplicate(enum rlc_mode mode, packet_info *pinfo, guint16 seq, guint32 *original)
+{
+ GList *element;
+ struct rlc_seqlist lookup, *list;
+ struct rlc_seq seq_item, *seq_new;
+
+ rlc_channel_assign(&lookup.ch, mode, pinfo);
+ list = g_hash_table_lookup(sequence_table, &lookup.ch);
+ if (!list) {
+ /* we see this channel for the first time */
+ list = se_alloc0(sizeof(*list));
+ rlc_channel_assign(&list->ch, mode, pinfo);
+ g_hash_table_insert(sequence_table, &list->ch, list);
+ }
+ seq_item.seq = seq;
+ seq_item.frame_num = pinfo->fd->num;
+
+ element = g_list_find_custom(list->list, &seq_item, rlc_cmp_seq);
+ if (element) {
+ seq_new = element->data;
+ if (seq_new->frame_num != seq_item.frame_num) {
+ nstime_t delta;
+ nstime_delta(&delta, &pinfo->fd->abs_ts, &seq_new->arrival);
+ if (delta.secs < RLC_RETRANSMISSION_TIMEOUT) {
+ if (original)
+ *original = seq_new->frame_num;
+ return TRUE;
+ }
+ return FALSE;
+ }
+ return FALSE; /* we revisit the seq that was already seen */
+ }
+ seq_new = se_alloc0(sizeof(struct rlc_seq));
+ *seq_new = seq_item;
+ seq_new->arrival = pinfo->fd->abs_ts;
+ list->list = g_list_insert_sorted(list->list, seq_new, rlc_cmp_seq);
+ return FALSE;
+}
+
+static void rlc_call_subdissector(enum channel_type channel, tvbuff_t *tvb,
+ packet_info *pinfo, proto_tree *tree)
+{
+
+ typedef enum {
+ RRC_MESSAGE_TYPE_INVALID,
+ RRC_MESSAGE_TYPE_UL_CCCH,
+ RRC_MESSAGE_TYPE_DL_CCCH,
+ RRC_MESSAGE_TYPE_UL_DCCH,
+ RRC_MESSAGE_TYPE_DL_DCCH,
+ RRC_MESSAGE_TYPE_PCCH
+ } rrc_message_type_t;
+
+ rrc_message_type_t msgtype;
+
+ switch (channel) {
+ case UL_CCCH:
+ msgtype = RRC_MESSAGE_TYPE_UL_CCCH;
+ break;
+ case DL_CCCH:
+ msgtype = RRC_MESSAGE_TYPE_DL_CCCH;
+ break;
+ case UL_DCCH:
+ msgtype = RRC_MESSAGE_TYPE_UL_DCCH;
+ break;
+ case DL_DCCH:
+ msgtype = RRC_MESSAGE_TYPE_DL_DCCH;
+ break;
+ case PCCH:
+ msgtype = RRC_MESSAGE_TYPE_PCCH;
+ break;
+ case PS_DTCH:
+ msgtype = RRC_MESSAGE_TYPE_INVALID;
+ /* assume transparent PDCP for now */
+ call_dissector(ip_handle, tvb, pinfo, tree);
+ break;
+ default:
+ return; /* abort */
+ }
+ if (msgtype != RRC_MESSAGE_TYPE_INVALID) {
+ struct rrc_info *rrcinf;
+ fp_info *fpinf;
+ fpinf = p_get_proto_data(pinfo->fd, proto_fp);
+ rrcinf = p_get_proto_data(pinfo->fd, proto_rrc);
+ if (!rrcinf) {
+ rrcinf = se_alloc0(sizeof(struct rrc_info));
+ p_add_proto_data(pinfo->fd, proto_rrc, rrcinf);
+ }
+ rrcinf->msgtype[fpinf->cur_tb] = msgtype;
+ call_dissector(rrc_handle, tvb, pinfo, tree);
+ /* once the packet has been dissected, protect it from further changes */
+ col_set_writable(pinfo->cinfo, FALSE);
+ }
+}
+
+static void dissect_rlc_tm(enum channel_type channel, tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *top_level, proto_tree *tree _U_)
+{
+ rlc_call_subdissector(channel, tvb, pinfo, top_level);
+}
+
+
+static void rlc_um_reassemble(tvbuff_t *tvb, guint8 offs, packet_info *pinfo, proto_tree *tree,
+ proto_tree *top_level, enum channel_type channel, guint16 seq, struct rlc_li *li, guint16 num_li)
+{
+ guint8 i;
+ gboolean dissected = FALSE;
+ tvbuff_t *next_tvb = NULL;
+ /* perform reassembly now */
+ for (i = 0; i < num_li; i++) {
+ switch (li[i].li) {
+ case 0x7f: /* padding, must be last LI */
+ if (tree)
+ proto_tree_add_item(tree, hf_rlc_pad, tvb, offs, -1, FALSE);
+ offs += tvb_length_remaining(tvb, offs);
+ break;
+ case 0x7c: /* a new SDU starts here */
+ break; /* nothing to do, really */
+ case 0x00: /* previous segment was the last of an SDU */
+ default:
+ add_fragment(RLC_UM, tvb, pinfo, li[i].tree, offs, seq, i, li[i].len, TRUE);
+ next_tvb = get_reassembled_data(RLC_UM, tvb, pinfo, li[i].tree, seq, i);
+ }
+ if (next_tvb) {
+ dissected = TRUE;
+ rlc_call_subdissector(channel, next_tvb, pinfo, top_level);
+ next_tvb = NULL;
+ }
+ offs += li[i].len;
+ }
+
+ /* is there data left? */
+ if (tvb_length_remaining(tvb, offs) > 0) {
+ if (tree) {
+ proto_item *ti;
+ ti = proto_tree_add_item(tree, hf_rlc_data, tvb, offs, -1, FALSE);
+ }
+ /* add remaining data as fragment */
+ add_fragment(RLC_UM, tvb, pinfo, tree, offs, seq, i, tvb_length_remaining(tvb, offs), FALSE);
+ if (dissected == FALSE && check_col(pinfo->cinfo, COL_INFO))
+ col_set_str(pinfo->cinfo, COL_INFO, "[RLC UM Fragment]");
+ }
+}
+
+static gint16 rlc_decode_li(enum rlc_mode mode, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ struct rlc_li *li, guint8 max_li)
+{
+ guint8 ext, next_byte, hdr_len, offs = 0, num_li = 0, li_offs;
+ guint16 prev_li = 0;
+ proto_item *malformed;
+ guint16 total_len;
+
+ switch (mode) {
+ case RLC_AM: offs = 1; break;
+ case RLC_UM: offs = 0; break;
+ case RLC_TM: return -1;
+ }
+ hdr_len = offs;
+ /* calculate header length */
+ ext = tvb_get_guint8(tvb, offs) & 0x01;
+ while (ext) {
+ next_byte = tvb_get_guint8(tvb, hdr_len);
+ ext = next_byte & 0x01;
+ hdr_len++;
+ }
+ total_len = tvb_length_remaining(tvb, hdr_len);
+
+ /* do actual evaluation of LIs */
+ ext = tvb_get_guint8(tvb, offs++) & 0x01;
+ li_offs = offs;
+ while (ext) {
+ next_byte = tvb_get_guint8(tvb, offs++);
+ ext = next_byte & 0x01;
+ li[num_li].ext = ext;
+ li[num_li].li = next_byte >> 1;
+
+ switch (li[num_li].li) {
+ case 0x00: /* previous segment was the last one */
+ case 0x7e: /* contains piggybacked STATUS */
+ case 0x7f: /* padding */
+ li[num_li].len = 0;
+ break;
+ case 0x7c: /* start of a new PDU, UM only */
+ case 0x7d: /* contains exactly one PDU, UM only */
+ if (mode == RLC_UM) {
+ /* valid for UM */
+ li[num_li].len = 0;
+ break;
+ }
+ /*invalid for AM */
+ /* add malformed LI for investigation */
+ tree_add_li(&li[num_li], num_li, li_offs, tvb, tree);
+ malformed = proto_tree_add_protocol_format(tree,
+ proto_malformed, tvb, 0, 0, "[Malformed Packet: %s]", pinfo->current_proto);
+ expert_add_info_format(pinfo, malformed, PI_MALFORMED, PI_ERROR,
+ "Malformed Packet (Uses reserved LI)");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO, "[Malformed Packet]");
+ return -1; /* just give up on this */
+ default:
+ /* since the LI is an offset (from the end of the header), it
+ * may not be larger than the total remaining length and no
+ * LI may be smaller than its preceding one
+ */
+ if (li[num_li].li > total_len || li[num_li].li < prev_li) {
+ /* add malformed LI for investigation */
+ tree_add_li(&li[num_li], num_li, li_offs, tvb, tree);
+ malformed = proto_tree_add_protocol_format(tree,
+ proto_malformed, tvb, 0, 0, "[Malformed Packet: %s]", pinfo->current_proto);
+ expert_add_info_format(pinfo, malformed, PI_MALFORMED, PI_ERROR,
+ "Malformed Packet (incorrect LI value)");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO, "[Malformed Packet]");
+ return -1; /* just give up on this */
+ }
+ li[num_li].len = li[num_li].li - prev_li;
+ prev_li = li[num_li].li;
+ }
+ li[num_li].tree = tree_add_li(&li[num_li], num_li, li_offs, tvb, tree);
+ num_li++;
+
+ if (num_li > max_li) {
+ /* OK, so this is not really a malformed packet, but for now,
+ * we will treat it as such, so that it is marked in some way */
+ malformed = proto_tree_add_protocol_format(tree,
+ proto_malformed, tvb, 0, 0, "[Dissector Problem: %s]", pinfo->current_proto);
+ expert_add_info_format(pinfo, malformed, PI_MALFORMED, PI_ERROR,
+ "Too many LI entries");
+ return -1;
+ }
+ }
+ return num_li;
+}
+
+static void dissect_rlc_um(enum channel_type channel, tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *top_level, proto_tree *tree)
+{
+#define MAX_LI 16
+ struct rlc_li li[MAX_LI];
+ fp_info *fpinf;
+ rlc_info *rlcinf;
+ guint32 orig_num;
+ guint8 seq, ext;
+ guint8 next_byte, offs = 0;
+ gint16 pos, num_li = 0;
+
+ next_byte = tvb_get_guint8(tvb, offs++);
+ seq = next_byte >> 1;
+ ext = next_byte & 0x01;
+
+ /* show sequence number and extension bit */
+ if (tree) {
+ proto_tree_add_bits_item(tree, hf_rlc_seq, tvb, 0, 7, FALSE);
+ proto_tree_add_item(tree, hf_rlc_ext, tvb, 0, 1, FALSE);
+ }
+
+ fpinf = p_get_proto_data(pinfo->fd, proto_fp);
+ rlcinf = p_get_proto_data(pinfo->fd, proto_rlc);
+ if (!fpinf || !rlcinf) {
+ proto_tree_add_text(tree, tvb, 0, -1,
+ "Cannot dissect RLC frame because per-frame info is missing");
+ return;
+ }
+ pos = fpinf->cur_tb;
+ if (rlcinf->ciphered[pos] == TRUE && rlcinf->deciphered[pos] == FALSE) {
+ proto_tree_add_text(tree, tvb, 0, -1,
+ "Cannot dissect RLC frame because it is ciphered");
+ return;
+ }
+
+ num_li = rlc_decode_li(RLC_UM, tvb, pinfo, tree, li, MAX_LI);
+ if (num_li == -1) return; /* something went wrong */
+ offs += num_li;
+
+
+ /* do not detect duplicates or reassemble, if prefiltering is done */
+ if (pinfo->fd->num == 0) return;
+ /* check for duplicates */
+ if (rlc_is_duplicate(RLC_UM, pinfo, seq, &orig_num) == TRUE) {
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_set_str(pinfo->cinfo, COL_INFO, "[RLC UM Fragment] [Duplicate]");
+ proto_tree_add_uint(tree, hf_rlc_duplicate_of, tvb, 0, 0, orig_num);
+ return;
+ }
+ rlc_um_reassemble(tvb, offs, pinfo, tree, top_level, channel, seq, li, num_li);
+}
+
+static void dissect_rlc_status(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint8 offset)
+{
+ guint8 sufi_type, len;
+ gint bit_offset;
+ proto_tree *sufi_tree;
+ proto_item *sufi_item, *malformed;
+
+ bit_offset = offset*8 + 4; /* first SUFI type is always 4 bit shifted */
+
+ while (tvb_length_remaining(tvb, bit_offset/8) > 0) {
+ sufi_type = tvb_get_bits8(tvb, bit_offset, 4);
+ sufi_item = proto_tree_add_item(tree, hf_rlc_sufi, tvb, 0, 0, FALSE);
+ sufi_tree = proto_item_add_subtree(sufi_item, ett_rlc_sufi);
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_type, tvb, bit_offset, 4, FALSE);
+ bit_offset += 4;
+ switch (sufi_type) {
+ case RLC_SUFI_NOMORE:
+ return; /* must be last SUFI */
+ case RLC_SUFI_ACK:
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_lsn, tvb, bit_offset, 12, FALSE);
+ return; /* must be last SUFI */
+ case RLC_SUFI_WINDOW:
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_wsn, tvb, bit_offset, 12, FALSE);
+ bit_offset += 12;
+ break;
+ case RLC_SUFI_LIST:
+ len = tvb_get_bits8(tvb, bit_offset, 4);
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_len, tvb, bit_offset, 4, FALSE);
+ bit_offset += 4;
+ while (len) {
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_sn, tvb, bit_offset, 12, FALSE);
+ bit_offset += 12;
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_l, tvb, bit_offset, 4, FALSE);
+ bit_offset += 4;
+ len--;
+ }
+ break;
+ case RLC_SUFI_BITMAP:
+ len = tvb_get_bits8(tvb, bit_offset, 4);
+ len++; /* bitmap is len + 1 */
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_len, tvb, bit_offset, 4, FALSE);
+ bit_offset += 4;
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_fsn, tvb, bit_offset, 12, FALSE);
+ bit_offset += 12;
+ proto_tree_add_item(sufi_tree, hf_rlc_sufi_bitmap, tvb, bit_offset/8, len, FALSE);
+ bit_offset += len*8;
+ break;
+ case RLC_SUFI_RLIST:
+ len = tvb_get_bits8(tvb, bit_offset, 4);
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_len, tvb, bit_offset, 4, FALSE);
+ bit_offset += 4;
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_fsn, tvb, bit_offset, 12, FALSE);
+ bit_offset += 12;
+ while (len) {
+ /* TODO: decode CW values */
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_cw, tvb, bit_offset, 4, FALSE);
+ bit_offset += 4;
+ len--;
+ }
+ break;
+ case RLC_SUFI_MRW_ACK:
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_n, tvb, bit_offset, 4, FALSE);
+ bit_offset += 4;
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_sn_ack, tvb, bit_offset, 12, FALSE);
+ bit_offset += 12;
+ break;
+ case RLC_SUFI_MRW:
+ len = tvb_get_bits8(tvb, bit_offset, 4);
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_len, tvb, bit_offset, 4, FALSE);
+ bit_offset += 4;
+
+ while (len) {
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_sn_mrw, tvb, bit_offset, 12, FALSE);
+ bit_offset += 12;
+ len--;
+ }
+ proto_tree_add_bits_item(sufi_tree, hf_rlc_sufi_n, tvb, bit_offset, 4, FALSE);
+ bit_offset += 4;
+ break;
+ default:
+ malformed = proto_tree_add_protocol_format(tree,
+ proto_malformed, tvb, 0, 0, "[Malformed Packet: %s]", pinfo->current_proto);
+ expert_add_info_format(pinfo, malformed, PI_MALFORMED, PI_ERROR,
+ "Malformed Packet (invalid SUFI type)");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO, " [Malformed Packet]");
+ return; /* invalid value, ignore the rest */
+ }
+ }
+}
+
+static void dissect_rlc_control(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ guint8 type, next_byte;
+ proto_item *malformed;
+
+ next_byte = tvb_get_guint8(tvb, 0);
+ type = (next_byte >> 4) & 0x07;
+
+ proto_tree_add_uint(tree, hf_rlc_ctrl_type, tvb, 0, 1, next_byte);
+ switch (type) {
+ case RLC_STATUS:
+ dissect_rlc_status(tvb, pinfo, tree, 0);
+ break;
+ case RLC_RESET:
+ case RLC_RESET_ACK:
+ /* TODO */
+ break;
+ default:
+ malformed = proto_tree_add_protocol_format(tree,
+ proto_malformed, tvb, 0, 0, "[Malformed Packet: %s]", pinfo->current_proto);
+ expert_add_info_format(pinfo, malformed, PI_MALFORMED, PI_ERROR,
+ "Malformed Packet (invalid RLC AM control type %u)", type);
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO, " [Malformed Packet]");
+ return; /* invalid */
+ }
+}
+
+static void rlc_am_reassemble(tvbuff_t *tvb, guint8 offs, packet_info *pinfo, proto_tree *tree,
+ proto_tree *top_level, enum channel_type channel, guint16 seq, struct rlc_li *li, guint16 num_li)
+{
+ guint8 i;
+ gboolean piggyback = FALSE, dissected = FALSE;
+ tvbuff_t *next_tvb = NULL;
+ /* perform reassembly now */
+ for (i = 0; i < num_li; i++) {
+ switch (li[i].li) {
+ case 0x7e: /* piggybacked status */
+ piggyback = TRUE;
+ break;
+ case 0x7f: /* padding, must be last LI */
+ if (tree && tvb_length_remaining(tvb, offs) > 0)
+ proto_tree_add_item(tree, hf_rlc_pad, tvb, offs, -1, FALSE);
+ offs += tvb_length_remaining(tvb, offs);
+ break;
+ case 0x0: /* previous segment was the last one */
+ default:
+ add_fragment(RLC_AM, tvb, pinfo, li[i].tree, offs, seq, i, li[i].len, TRUE);
+ next_tvb = get_reassembled_data(RLC_AM, tvb, pinfo, li[i].tree, seq, i);
+ }
+ if (next_tvb) {
+ dissected = TRUE;
+ rlc_call_subdissector(channel, next_tvb, pinfo, top_level);
+ next_tvb = NULL;
+ }
+ offs += li[i].len;
+ }
+
+ if (piggyback) {
+ dissect_rlc_status(tvb, pinfo, tree, offs);
+ } else {
+ if (tvb_length_remaining(tvb, offs) > 0) {
+ /* we have remaining data, which we need to mark in the tree */
+ if (tree) {
+ proto_item *ti;
+ ti = proto_tree_add_item(tree, hf_rlc_data, tvb, offs, -1, FALSE);
+ }
+ add_fragment(RLC_AM, tvb, pinfo, tree, offs, seq, i,
+ tvb_length_remaining(tvb,offs), FALSE);
+ }
+ }
+ if (dissected == FALSE && check_col(pinfo->cinfo, COL_INFO))
+ col_set_str(pinfo->cinfo, COL_INFO, "[RLC AM Fragment]");
+}
+
+static void dissect_rlc_am(enum channel_type channel, tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *top_level, proto_tree *tree)
+{
+#define MAX_LI 16
+ struct rlc_li li[MAX_LI];
+ fp_info *fpinf;
+ rlc_info *rlcinf;
+ guint8 ext, dc;
+ guint8 next_byte, offs = 0;
+ guint32 orig_num = 0;
+ gint16 num_li = 0, seq, pos;
+
+ next_byte = tvb_get_guint8(tvb, offs++);
+ dc = next_byte >> 7;
+ if (tree)
+ proto_tree_add_item(tree, hf_rlc_dc, tvb, 0, 1, FALSE);
+ if (dc == 0) {
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_set_str(pinfo->cinfo, COL_INFO, "RLC Control Frame");
+ dissect_rlc_control(tvb, pinfo, tree);
+ return;
+ }
+
+ seq = next_byte & 0x7f;
+ seq <<= 5;
+ next_byte = tvb_get_guint8(tvb, offs++);
+ seq |= (next_byte >> 3);
+
+ ext = next_byte & 0x03;
+ /* show header fields */
+ if (tree) {
+ proto_tree_add_bits_item(tree, hf_rlc_seq, tvb, 1, 12, FALSE);
+ proto_tree_add_item(tree, hf_rlc_p, tvb, 1, 1, FALSE);
+ proto_tree_add_bits_item(tree, hf_rlc_he, tvb, 14, 2, FALSE);
+ }
+
+ /* header extension may only be 00 or 01 */
+ if (ext > 1) {
+ proto_item *malformed;
+ malformed = proto_tree_add_protocol_format(tree,
+ proto_malformed, tvb, 0, 0, "[Malformed Packet: %s]", pinfo->current_proto);
+ expert_add_info_format(pinfo, malformed, PI_MALFORMED, PI_ERROR,
+ "Malformed Packet (incorrect HE value)");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO, "[Malformed Packet]");
+ return;
+ }
+
+ fpinf = p_get_proto_data(pinfo->fd, proto_fp);
+ rlcinf = p_get_proto_data(pinfo->fd, proto_rlc);
+ if (!rlcinf) {
+ proto_tree_add_text(tree, tvb, 0, -1,
+ "Cannot dissect RLC frame because per-frame info is missing");
+ return;
+ }
+ pos = fpinf->cur_tb;
+ if (rlcinf->ciphered[pos] == TRUE && rlcinf->deciphered[pos] == FALSE) {
+ proto_tree_add_text(tree, tvb, 0, -1,
+ "Cannot dissect RLC frame because it is ciphered");
+ return;
+ }
+
+ num_li = rlc_decode_li(RLC_AM, tvb, pinfo, tree, li, MAX_LI);
+ if (num_li == -1) return; /* something went wrong */
+ offs += num_li;
+
+ /* do not detect duplicates or reassemble, if prefiltering is done */
+ if (pinfo->fd->num == 0) return;
+ /* check for duplicates */
+ if (rlc_is_duplicate(RLC_AM, pinfo, seq, &orig_num) == TRUE) {
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_set_str(pinfo->cinfo, COL_INFO, "[RLC AM Fragment] [Duplicate]");
+ proto_tree_add_uint(tree, hf_rlc_duplicate_of, tvb, 0, 0, orig_num);
+ return;
+ }
+ rlc_am_reassemble(tvb, offs, pinfo, tree, top_level, channel, seq, li, num_li);
+}
+
+/* dissect entry functions */
+static void dissect_rlc_pcch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_tree *subtree = NULL;
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "RLC");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ /* PCCH is always RLC UM */
+ if (tree) {
+ proto_item *ti;
+ ti = proto_tree_add_item(tree, proto_rlc, tvb, 0, -1, FALSE);
+ subtree = proto_item_add_subtree(ti, ett_rlc);
+ proto_item_append_text(ti, " TM (PCCH)");
+ }
+ dissect_rlc_tm(PCCH, tvb, pinfo, tree, subtree);
+}
+
+static void dissect_rlc_ccch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ fp_info *fpi;
+ proto_item *ti = NULL;
+ proto_tree *subtree = NULL;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "RLC");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ fpi = p_get_proto_data(pinfo->fd, proto_fp);
+ if (!fpi) return; /* dissection failure */
+
+ if (tree) {
+ ti = proto_tree_add_item(tree, proto_rlc, tvb, 0, -1, FALSE);
+ subtree = proto_item_add_subtree(ti, ett_rlc);
+ }
+
+ if (fpi->is_uplink) {
+ /* UL CCCH is always RLC TM */
+ proto_item_append_text(ti, " TM (CCCH)");
+ dissect_rlc_tm(UL_CCCH, tvb, pinfo, tree, subtree);
+ } else {
+ /* DL CCCH is always UM */
+ proto_item_append_text(ti, " UM (CCCH)");
+ dissect_rlc_um(DL_CCCH, tvb, pinfo, tree, subtree);
+ }
+}
+
+static void dissect_rlc_dcch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_item *ti = NULL;
+ proto_tree *subtree = NULL;
+ fp_info *fpi;
+ rlc_info *rlci;
+ enum channel_type channel;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "RLC");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ fpi = p_get_proto_data(pinfo->fd, proto_fp);
+ rlci = p_get_proto_data(pinfo->fd, proto_rlc);
+
+ if (!fpi || !rlci) return;
+
+ if (tree) {
+ ti = proto_tree_add_item(tree, proto_rlc, tvb, 0, -1, FALSE);
+ subtree = proto_item_add_subtree(ti, ett_rlc);
+ }
+
+ channel = fpi->is_uplink ? UL_DCCH : DL_DCCH;
+
+ switch (rlci->mode[fpi->cur_tb]) {
+ case RLC_UM:
+ proto_item_append_text(ti, " UM (DCCH)");
+ dissect_rlc_um(channel, tvb, pinfo, tree, subtree);
+ break;
+ case RLC_AM:
+ proto_item_append_text(ti, " AM (DCCH)");
+ dissect_rlc_am(channel, tvb, pinfo, tree, subtree);
+ break;
+ }
+}
+
+static void dissect_rlc_ps_dtch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_item *ti = NULL;
+ proto_tree *subtree = NULL;
+ fp_info *fpi;
+ rlc_info *rlci;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "RLC");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ fpi = p_get_proto_data(pinfo->fd, proto_fp);
+ rlci = p_get_proto_data(pinfo->fd, proto_rlc);
+
+ if (!fpi || !rlci) return;
+
+ if (tree) {
+ ti = proto_tree_add_item(tree, proto_rlc, tvb, 0, -1, FALSE);
+ subtree = proto_item_add_subtree(ti, ett_rlc);
+ }
+
+ switch (rlci->mode[fpi->cur_tb]) {
+ case RLC_UM:
+ proto_item_append_text(ti, " UM (PS DTCH)");
+ dissect_rlc_um(PS_DTCH, tvb, pinfo, tree, subtree);
+ break;
+ case RLC_AM:
+ proto_item_append_text(ti, " AM (PS DTCH)");
+ dissect_rlc_am(PS_DTCH, tvb, pinfo, tree, subtree);
+ break;
+ case RLC_TM:
+ proto_item_append_text(ti, " TM (PS DTCH)");
+ dissect_rlc_tm(PS_DTCH, tvb, pinfo, tree, subtree);
+ break;
+ }
+}
+
+void
+proto_register_rlc(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_rlc_dc, { "D/C Bit", "rlc.dc", FT_BOOLEAN, 8, TFS(&rlc_dc_val), 0x80, "D/C Bit", HFILL } },
+ { &hf_rlc_ctrl_type, { "Control PDU Type", "rlc.ctrl_pdu_type", FT_UINT8, BASE_DEC, VALS(rlc_ctrl_vals), 0x70, "PDU Type", HFILL } },
+ { &hf_rlc_seq, { "Sequence Number", "rlc.seq", FT_UINT8, BASE_DEC, NULL, 0, "Sequence Number", HFILL } },
+ { &hf_rlc_ext, { "Extension Bit", "rlc.ext", FT_BOOLEAN, BASE_DEC, TFS(&rlc_ext_val), 0x01, "Extension Bit", HFILL } },
+ { &hf_rlc_he, { "Header Extension Type", "rlc.he", FT_UINT8, BASE_DEC, VALS(rlc_he_vals), 0, "Header Extension Type", HFILL } },
+ { &hf_rlc_p, { "Polling Bit", "rlc.p", FT_BOOLEAN, 8, TFS(&rlc_p_val), 0x04, "Polling Bit", HFILL } },
+ { &hf_rlc_pad, { "Padding", "rlc.padding", FT_BYTES, BASE_HEX, NULL, 0x0, "Padding", HFILL } },
+ { &hf_rlc_frags, { "Reassembled Fragments", "rlc.fragments", FT_NONE, BASE_NONE, NULL, 0, "Fragments", HFILL } },
+ { &hf_rlc_frag, { "RLC Fragment", "rlc.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0, "", HFILL } },
+ { &hf_rlc_duplicate_of, { "Duplicate of", "rlc.duplicate_of", FT_FRAMENUM, BASE_NONE, NULL, 0, "", HFILL } },
+ { &hf_rlc_reassembled_in, { "Reassembled Message in frame", "rlc.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0, "", HFILL } },
+ { &hf_rlc_data, { "Data", "rlc.data", FT_NONE, BASE_NONE, NULL, 0, "Data", HFILL } },
+ /* LI information */
+ { &hf_rlc_li, { "LI", "rlc.li", FT_NONE, BASE_NONE, NULL, 0, "Length Indicator", HFILL } },
+ { &hf_rlc_li_value, { "LI value", "rlc.li.value", FT_UINT16, BASE_DEC, NULL, 0, "LI Value", HFILL } },
+ { &hf_rlc_li_ext, { "LI extension bit", "rlc.li.ext", FT_BOOLEAN, BASE_DEC, TFS(&rlc_ext_val), 0x01, "LI Extension Bit", HFILL } },
+ { &hf_rlc_li_data, { "LI Data", "rlc.li.data", FT_NONE, BASE_NONE, NULL, 0x0, "LI Data", HFILL } },
+ /* SUFI information */
+ { &hf_rlc_sufi, { "SUFI", "rlc.sufi", FT_NONE, BASE_NONE, NULL, 0, "SUFI", HFILL } },
+ { &hf_rlc_sufi_type, { "SUFI Type", "rlc.sufi.type", FT_UINT8, BASE_DEC, VALS(rlc_sufi_vals), 0, "SUFI Type", HFILL } },
+ { &hf_rlc_sufi_lsn, { "LSN", "rlc.sufi.lsn", FT_UINT16, BASE_DEC, NULL, 0, "LSN", HFILL } },
+ { &hf_rlc_sufi_wsn, { "WSN", "rlc.sufi.wsn", FT_UINT16, BASE_DEC, NULL, 0, "WSN", HFILL } },
+ { &hf_rlc_sufi_sn, { "SN", "rlc.sufi.sn", FT_UINT16, BASE_DEC, NULL, 0, "SN", HFILL } },
+ { &hf_rlc_sufi_l, { "L", "rlc.sufi.l", FT_UINT8, BASE_DEC, NULL, 0, "L", HFILL } },
+ { &hf_rlc_sufi_len, { "Length", "rlc.sufi.len", FT_UINT8, BASE_DEC, NULL, 0, "Length", HFILL } },
+ { &hf_rlc_sufi_fsn, { "FSN", "rlc.sufi.fsn", FT_UINT16, BASE_DEC, NULL, 0, "FSN", HFILL } },
+ { &hf_rlc_sufi_bitmap, { "Bitmap", "rlc.sufi.bitmap", FT_BYTES, BASE_HEX, NULL, 0x0, "Bitmap", HFILL } },
+ { &hf_rlc_sufi_cw, { "CW", "rlc.sufi.cw", FT_UINT8, BASE_DEC, NULL, 0, "CW", HFILL } },
+ { &hf_rlc_sufi_n, { "N", "rlc.sufi.n", FT_UINT8, BASE_DEC, NULL, 0, "N", HFILL } },
+ { &hf_rlc_sufi_sn_ack, { "SN ACK", "rlc.sufi.sn_ack", FT_UINT16, BASE_DEC, NULL, 0, "SN ACK", HFILL } },
+ { &hf_rlc_sufi_sn_mrw, { "SN MRW", "rlc.sufi.sn_mrw", FT_UINT16, BASE_DEC, NULL, 0, "SN MRW", HFILL } },
+ };
+ static gint *ett[] = {
+ &ett_rlc,
+ &ett_rlc_frag,
+ &ett_rlc_fragments,
+ &ett_rlc_sdu,
+ &ett_rlc_sufi,
+ };
+ proto_rlc = proto_register_protocol("RLC", "RLC", "rlc");
+ register_dissector("rlc.pcch", dissect_rlc_pcch, proto_rlc);
+ register_dissector("rlc.ccch", dissect_rlc_ccch, proto_rlc);
+ register_dissector("rlc.dcch", dissect_rlc_dcch, proto_rlc);
+ register_dissector("rlc.ps_dtch", dissect_rlc_ps_dtch, proto_rlc);
+
+ proto_register_field_array(proto_rlc, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ register_init_routine(fragment_table_init);
+}
+
+void
+proto_reg_handoff_rlc(void)
+{
+ rrc_handle = find_dissector("rrc");
+ ip_handle = find_dissector("ip");
+}
diff --git a/epan/dissectors/packet-rlc.h b/epan/dissectors/packet-rlc.h
new file mode 100644
index 0000000000..875f0b5bd8
--- /dev/null
+++ b/epan/dissectors/packet-rlc.h
@@ -0,0 +1,38 @@
+/* Routines for UMTS RLC disassembly
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+enum rlc_mode {
+ RLC_TM,
+ RLC_UM,
+ RLC_AM
+};
+
+#define MAX_RLC_CHANS 64
+typedef struct rlc_info
+{
+ guint32 urnti[MAX_RLC_CHANS];
+ guint8 mode[MAX_RLC_CHANS];
+ guint8 rbid[MAX_RLC_CHANS];
+ gboolean ciphered[MAX_RLC_CHANS];
+ gboolean deciphered[MAX_RLC_CHANS];
+} rlc_info;
diff --git a/epan/dissectors/packet-umts_mac.c b/epan/dissectors/packet-umts_mac.c
new file mode 100644
index 0000000000..c59aa1f5ce
--- /dev/null
+++ b/epan/dissectors/packet-umts_mac.c
@@ -0,0 +1,609 @@
+/* Routines for UMTS FP disassembly
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <epan/packet.h>
+
+#include "packet-umts_fp.h"
+#include "packet-umts_mac.h"
+
+int proto_umts_mac = -1;
+extern int proto_fp;
+
+/* dissector fields */
+static int hf_mac_fach_fdd_tctf = -1;
+static int hf_mac_rach_fdd_tctf = -1;
+static int hf_mac_ct = -1;
+static int hf_mac_ueid_type = -1;
+static int hf_mac_crnti = -1;
+static int hf_mac_urnti = -1;
+static int hf_mac_channel = -1;
+
+/* subtrees */
+static int ett_mac = -1;
+static int ett_mac_fach = -1;
+static int ett_mac_rach = -1;
+static int ett_mac_dch = -1;
+static int ett_mac_pch = -1;
+static int ett_mac_edch = -1;
+static int ett_mac_hsdsch = -1;
+
+static dissector_handle_t rlc_pcch_handle;
+static dissector_handle_t rlc_ccch_handle;
+static dissector_handle_t rlc_dcch_handle;
+static dissector_handle_t rlc_ps_dtch_handle;
+
+static const value_string rach_fdd_tctf_vals[] = {
+ { TCTF_CCCH_RACH_FDD, "CCCH over RACH (FDD)" },
+ { TCTF_DCCH_DTCH_RACH_FDD, "DCCH/DTCH over RACH (FDD)" },
+ { 0, NULL }
+};
+
+static const value_string fach_fdd_tctf_vals[] = {
+ { TCTF_BCCH_FACH_FDD, "BCCH over FACH (FDD)" },
+ { TCTF_DCCH_DTCH_FACH_FDD, "DCCH/DTCH over FACH (FDD)" },
+ { TCTF_MTCH_FACH_FDD, "MTCH over FACH (FDD)" },
+ { TCTF_CCCH_FACH_FDD, "CCCH over FACH (FDD)" },
+ { TCTF_MCCH_FACH_FDD, "MCCH over FACH (FDD)" },
+ { TCTF_MSCH_FACH_FDD, "MSCH over FACH (FDD)" },
+ { TCTF_CTCH_FACH_FDD, "CTCH over FACH (FDD)" },
+ { 0, NULL }
+};
+
+static const value_string ueid_type_vals[] = {
+ { MAC_UEID_TYPE_URNTI, "U-RNTI" },
+ { MAC_UEID_TYPE_CRNTI, "C-RNTI" },
+ { 0, NULL }
+};
+
+#define MAC_PCCH 0
+#define MAC_CCCH 1
+#define MAC_CTCH 2
+#define MAC_DCCH 3
+#define MAC_DTCH 4
+#define MAC_BCCH 5
+#define MAC_MCCH 6
+#define MAC_MSCH 7
+#define MAC_MTCH 8
+static const value_string mac_logical_channel_vals[] = {
+ { MAC_PCCH, "PCCH" },
+ { MAC_CCCH, "CCCH" },
+ { MAC_CTCH, "CTCH" },
+ { MAC_DCCH, "DCCH" },
+ { MAC_DTCH, "DTCH" },
+ { MAC_BCCH, "BCCH" },
+ { MAC_MCCH, "MCCH" },
+ { MAC_MSCH, "MSCH" },
+ { MAC_MTCH, "MTCH" },
+ { 0, NULL }
+};
+
+static tvbuff_t *
+tvb_new_octet_aligned(tvbuff_t *tvb, guint32 bit_offset, gint32 no_of_bits)
+{
+ tvbuff_t *sub_tvb = NULL;
+ guint32 byte_offset;
+ gint32 datalen, i;
+ guint8 left, right, *buf;
+ const guint8 *data;
+
+ byte_offset = bit_offset >> 3;
+ left = bit_offset % 8; /* for left-shifting */
+ right = 8 - left; /* for right-shifting */
+
+ if (no_of_bits == -1) {
+ datalen = tvb_length_remaining(tvb, byte_offset);
+ } else {
+ datalen = no_of_bits >> 3;
+ if (no_of_bits % 8) datalen++;
+ }
+
+ /* already aligned -> shortcut */
+ if (left == 0) {
+ return tvb_new_subset(tvb, byte_offset, datalen, -1);
+ }
+
+ buf = ep_alloc0(datalen);
+
+ /* if at least one trailing byte is available, we must use the content
+ * of that byte for the last shift (i.e. tvb_get_ptr() must use datalen + 1
+ * if non extra byte is available, the last shifted byte requires
+ * special treatment
+ */
+ if (tvb_length_remaining(tvb, byte_offset) > datalen) {
+ data = tvb_get_ptr(tvb, byte_offset, datalen + 1);
+ } else {
+ data = tvb_get_ptr(tvb, byte_offset, datalen);
+ datalen--; /* correct 'datalen' for 'for' loop */
+ buf[datalen] = data[datalen] << left; /* set last octet */
+ }
+ /* shift tvb data bit_offset bits to the left */
+ for (i = 0; i < datalen; i++)
+ buf[i] = (data[i] << left) | (data[i+1] >> right);
+
+ sub_tvb = tvb_new_real_data(buf, datalen, datalen);
+ tvb_set_child_real_data_tvbuff(tvb, sub_tvb);
+
+ return sub_tvb;
+}
+
+static guint8 fach_fdd_tctf(guint8 hdr, guint16 *bit_offs)
+{
+ guint8 tctf;
+ /* first, test for valid 2-bit combinations */
+ tctf = hdr >> 6;
+ switch (tctf) {
+ case TCTF_BCCH_FACH_FDD:
+ case TCTF_DCCH_DTCH_FACH_FDD:
+ *bit_offs = 2;
+ return tctf;
+ }
+ /* 4-bit combinations */
+ tctf = hdr >> 4;
+ switch (tctf) {
+ case TCTF_MTCH_FACH_FDD:
+ *bit_offs = 4;
+ return tctf;
+ }
+ /* just return the 8-bit combination */
+ *bit_offs = 8;
+ tctf = hdr;
+ switch (tctf) {
+ case TCTF_CCCH_FACH_FDD:
+ case TCTF_MCCH_FACH_FDD:
+ case TCTF_MSCH_FACH_FDD:
+ case TCTF_CTCH_FACH_FDD:
+ return tctf;
+ default:
+ return tctf; /* TODO */
+ }
+}
+
+static guint16 tree_add_common_dcch_dtch_fields(tvbuff_t *tvb, packet_info *pinfo _U_,
+ proto_tree *tree, guint16 bitoffs, fp_info *fpinf, umts_mac_info *macinf)
+{
+ guint8 ueid_type;
+
+ ueid_type = tvb_get_bits8(tvb, bitoffs, 2);
+ proto_tree_add_bits_item(tree, hf_mac_ueid_type, tvb, bitoffs, 2, FALSE);
+ bitoffs += 2;
+ if (ueid_type == MAC_UEID_TYPE_URNTI) {
+ proto_tree_add_bits_item(tree, hf_mac_urnti, tvb, bitoffs, 32, FALSE);
+ bitoffs += 32;
+ } else if (ueid_type == MAC_UEID_TYPE_CRNTI) {
+ proto_tree_add_bits_item(tree, hf_mac_crnti, tvb, 4, 16, FALSE);
+ bitoffs += 16;
+ }
+
+ if (macinf->ctmux[fpinf->cur_tb]) {
+ proto_tree_add_bits_item(tree, hf_mac_ct, tvb, bitoffs, 4, FALSE);
+ bitoffs += 4;
+ }
+ return bitoffs;
+}
+
+static void dissect_mac_fdd_pch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_tree *pch_tree = NULL;
+ proto_item *channel_type;
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "MAC");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_set_str(pinfo->cinfo, COL_INFO, "PCCH");
+
+ if (tree) {
+ proto_item *ti;
+ ti = proto_tree_add_item(tree, proto_umts_mac, tvb, 0, -1, FALSE);
+ pch_tree = proto_item_add_subtree(ti, ett_mac_pch);
+ proto_item_append_text(ti, " (PCCH)");
+ channel_type = proto_tree_add_uint(pch_tree, hf_mac_channel, tvb, 0, 0, MAC_PCCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ }
+ call_dissector(rlc_pcch_handle, tvb, pinfo, tree);
+}
+
+static void dissect_mac_fdd_rach(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ guint8 tctf;
+ guint8 chan;
+ guint16 bitoffs = 0;
+ tvbuff_t *next_tvb;
+ proto_tree *rach_tree = NULL;
+ proto_item *channel_type;
+ umts_mac_info *macinf;
+ fp_info *fpinf;
+ proto_item *ti = NULL;
+
+ /* RACH TCTF is always 2 bit */
+ tctf = tvb_get_bits8(tvb, 0, 2);
+ bitoffs += 2;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "MAC");
+
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_add_str(pinfo->cinfo, COL_INFO,
+ val_to_str(tctf, rach_fdd_tctf_vals, "Unknown TCTF"));
+
+ ti = proto_tree_add_item(tree, proto_umts_mac, tvb, 0, -1, FALSE);
+ rach_tree = proto_item_add_subtree(ti, ett_mac_rach);
+
+ macinf = p_get_proto_data(pinfo->fd, proto_umts_mac);
+ fpinf = p_get_proto_data(pinfo->fd, proto_fp);
+ if (!macinf || !fpinf) {
+ proto_tree_add_text(rach_tree, tvb, 0, -1,
+ "Cannot dissect MAC frame because per-frame info is missing");
+ return;
+ }
+
+ proto_tree_add_bits_item(rach_tree, hf_mac_rach_fdd_tctf, tvb, 0, 2, FALSE);
+ if (tctf == TCTF_DCCH_DTCH_RACH_FDD) {
+ macinf->ctmux[fpinf->cur_tb] = 1; /* DCCH/DTCH on RACH *always* has a C/T */
+ bitoffs = tree_add_common_dcch_dtch_fields(tvb, pinfo, rach_tree, bitoffs, fpinf, macinf);
+ }
+
+ chan = fpinf->cur_chan;
+ /* handoff to next dissector */
+ switch (tctf) {
+ case TCTF_CCCH_RACH_FDD:
+ proto_item_append_text(ti, " (CCCH)");
+ channel_type = proto_tree_add_uint(rach_tree, hf_mac_channel, tvb, 0, 0, MAC_CCCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ next_tvb = tvb_new_octet_aligned(tvb, bitoffs, fpinf->chan_tf_size[chan] - bitoffs);
+ add_new_data_source(pinfo, next_tvb, "Octet-Aligned CCCH Data");
+ call_dissector(rlc_ccch_handle, next_tvb, pinfo, tree);
+ break;
+ case TCTF_DCCH_DTCH_RACH_FDD:
+ switch (macinf->content[chan]) {
+ case MAC_CONTENT_DCCH:
+ proto_item_append_text(ti, " (DCCH)");
+ channel_type = proto_tree_add_uint(rach_tree, hf_mac_channel, tvb, 0, 0, MAC_DCCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ next_tvb = tvb_new_octet_aligned(tvb, bitoffs, fpinf->chan_tf_size[chan] - bitoffs);
+ add_new_data_source(pinfo, next_tvb, "Octet-Aligned DCCH Data");
+ call_dissector(rlc_dcch_handle, next_tvb, pinfo, tree);
+ break;
+ case MAC_CONTENT_PS_DTCH:
+ proto_item_append_text(ti, " (PS DTCH)");
+ channel_type = proto_tree_add_uint(rach_tree, hf_mac_channel, tvb, 0, 0, MAC_DTCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ next_tvb = tvb_new_octet_aligned(tvb, bitoffs, fpinf->chan_tf_size[chan] - bitoffs);
+ add_new_data_source(pinfo, next_tvb, "Octet-Aligned DTCH Data");
+ call_dissector(rlc_ps_dtch_handle, next_tvb, pinfo, tree);
+ break;
+ case MAC_CONTENT_CS_DTCH:
+ proto_item_append_text(ti, " (CS DTCH)");
+ /* TODO */
+ break;
+ default:
+ proto_item_append_text(ti, " (Unknown RACH DCCH/DTCH Content)");
+ }
+ break;
+ default:
+ proto_item_append_text(ti, " (Unknown RACH TCTF)");
+ }
+}
+
+static void dissect_mac_fdd_fach(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ guint8 hdr, tctf;
+ guint16 bitoffs = 0;
+ guint16 tctf_len, chan;
+ proto_tree *fach_tree = NULL;
+ proto_item *channel_type;
+ umts_mac_info *macinf;
+ fp_info *fpinf;
+ proto_item *ti = NULL;
+
+ hdr = tvb_get_guint8(tvb, 0);
+
+ /* get target channel type field */
+ tctf = fach_fdd_tctf(hdr, &bitoffs);
+ tctf_len = bitoffs;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "MAC");
+
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_add_str(pinfo->cinfo, COL_INFO,
+ val_to_str(tctf, fach_fdd_tctf_vals, "Unknown TCTF"));
+
+ ti = proto_tree_add_item(tree, proto_umts_mac, tvb, 0, -1, FALSE);
+ fach_tree = proto_item_add_subtree(ti, ett_mac_fach);
+
+ macinf = p_get_proto_data(pinfo->fd, proto_umts_mac);
+ fpinf = p_get_proto_data(pinfo->fd, proto_fp);
+ if (!macinf || !fpinf) {
+ proto_tree_add_text(fach_tree, tvb, 0, -1,
+ "Cannot dissect MAC frame because per-frame info is missing");
+ return;
+ }
+
+ proto_tree_add_bits_item(fach_tree, hf_mac_fach_fdd_tctf, tvb, 0, tctf_len, FALSE);
+ if (tctf == TCTF_DCCH_DTCH_FACH_FDD) {
+ macinf->ctmux[fpinf->cur_tb] = 1; /* DCCH/DTCH on FACH *always* has a C/T */
+ bitoffs = tree_add_common_dcch_dtch_fields(tvb, pinfo, fach_tree, bitoffs, fpinf, macinf);
+ }
+
+ chan = fpinf->cur_chan;
+ switch (tctf) {
+ tvbuff_t *next_tvb;
+ case TCTF_CCCH_FACH_FDD:
+ proto_item_append_text(ti, " (CCCH)");
+ channel_type = proto_tree_add_uint(fach_tree, hf_mac_channel, tvb, 0, 0, MAC_CCCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ /* CCCH over FACH is always octet aligned */
+ next_tvb = tvb_new_subset(tvb, 1, tvb_length_remaining(tvb, 1), -1);
+ call_dissector(rlc_ccch_handle, next_tvb, pinfo, tree);
+ break;
+ case TCTF_DCCH_DTCH_FACH_FDD:
+ switch (macinf->content[fpinf->cur_tb]) {
+ case MAC_CONTENT_DCCH:
+ proto_item_append_text(ti, " (DCCH)");
+ channel_type = proto_tree_add_uint(fach_tree, hf_mac_channel, tvb, 0, 0, MAC_DCCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ next_tvb = tvb_new_octet_aligned(tvb, bitoffs, fpinf->chan_tf_size[chan] - bitoffs);
+ add_new_data_source(pinfo, next_tvb, "Octet-Aligned DCCH Data");
+ call_dissector(rlc_dcch_handle, next_tvb, pinfo, tree);
+ break;
+ case MAC_CONTENT_PS_DTCH:
+ proto_item_append_text(ti, " (PS DTCH)");
+ channel_type = proto_tree_add_uint(fach_tree, hf_mac_channel, tvb, 0, 0, MAC_DTCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ next_tvb = tvb_new_octet_aligned(tvb, bitoffs, fpinf->chan_tf_size[chan] - bitoffs);
+ add_new_data_source(pinfo, next_tvb, "Octet-Aligned DCCH Data");
+ call_dissector(rlc_ps_dtch_handle, next_tvb, pinfo, tree);
+ break;
+ case MAC_CONTENT_CS_DTCH:
+ proto_item_append_text(ti, " (CS DTCH)");
+ /* TODO */
+ break;
+ default:
+ proto_item_append_text(ti, " (Unknown FACH Content");
+ }
+ break;
+ default:
+ proto_item_append_text(ti, " (Unknown FACH Content");
+ }
+}
+
+static void dissect_mac_fdd_dch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ guint16 pos;
+ guint8 bitoffs = 0;
+ umts_mac_info *macinf;
+ fp_info *fpinf;
+ proto_tree *dch_tree = NULL;
+ proto_item *channel_type;
+ tvbuff_t *next_tvb;
+ proto_item *ti = NULL;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "MAC");
+
+ ti = proto_tree_add_item(tree, proto_umts_mac, tvb, 0, -1, FALSE);
+ dch_tree = proto_item_add_subtree(ti, ett_mac_dch);
+
+ macinf = p_get_proto_data(pinfo->fd, proto_umts_mac);
+ fpinf = p_get_proto_data(pinfo->fd, proto_fp);
+ pos = fpinf->cur_tb;
+ if (!macinf || !fpinf) {
+ proto_tree_add_text(dch_tree, tvb, 0, -1,
+ "Cannot dissect MAC frame because per-frame info is missing");
+ return;
+ }
+ if (macinf->ctmux[pos]) {
+ proto_tree_add_bits_item(dch_tree, hf_mac_ct, tvb, 0, 4, FALSE);
+ bitoffs = 4;
+ }
+
+ if (bitoffs) {
+ next_tvb = tvb_new_octet_aligned(tvb, bitoffs, fpinf->chan_tf_size[pos] - bitoffs);
+ add_new_data_source(pinfo, next_tvb, "Octet-Aligned DCCH Data");
+ } else
+ next_tvb = tvb;
+ switch (macinf->content[pos]) {
+ case MAC_CONTENT_DCCH:
+ proto_item_append_text(ti, " (DCCH)");
+ channel_type = proto_tree_add_uint(dch_tree, hf_mac_channel, tvb, 0, 0, MAC_DCCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ call_dissector(rlc_dcch_handle, next_tvb, pinfo, tree);
+ break;
+ case MAC_CONTENT_PS_DTCH:
+ proto_item_append_text(ti, " (PS DTCH)");
+ channel_type = proto_tree_add_uint(dch_tree, hf_mac_channel, tvb, 0, 0, MAC_DTCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ call_dissector(rlc_ps_dtch_handle, next_tvb, pinfo, tree);
+ break;
+ case MAC_CONTENT_CS_DTCH:
+ proto_item_append_text(ti, " (CS DTCH)");
+ /* TODO */
+ break;
+ default:
+ proto_item_append_text(ti, " (Unknown DCH Content)");
+ ;
+ }
+}
+
+static void dissect_mac_fdd_edch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_tree *edch_tree = NULL;
+ proto_item *channel_type;
+ umts_mac_info *macinf;
+ fp_info *fpinf;
+ guint16 pos;
+ proto_item *ti = NULL;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "MAC");
+
+ ti = proto_tree_add_item(tree, proto_umts_mac, tvb, 0, -1, FALSE);
+ edch_tree = proto_item_add_subtree(ti, ett_mac_edch);
+
+ fpinf = p_get_proto_data(pinfo->fd, proto_fp);
+ macinf = p_get_proto_data(pinfo->fd, proto_umts_mac);
+ pos = fpinf->cur_tb;
+ if (!macinf) {
+ proto_tree_add_text(edch_tree, tvb, 0, -1,
+ "Cannot dissect MAC frame because per-frame info is missing");
+ return;
+ }
+
+ switch (macinf->content[pos]) {
+ case MAC_CONTENT_DCCH:
+ proto_item_append_text(ti, " (DCCH)");
+ channel_type = proto_tree_add_uint(edch_tree, hf_mac_channel, tvb, 0, 0, MAC_DCCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ call_dissector(rlc_dcch_handle, tvb, pinfo, tree);
+ break;
+ case MAC_CONTENT_PS_DTCH:
+ proto_item_append_text(ti, " (PS DTCH)");
+ channel_type = proto_tree_add_uint(edch_tree, hf_mac_channel, tvb, 0, 0, MAC_DTCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ call_dissector(rlc_ps_dtch_handle, tvb, pinfo, tree);
+ break;
+ case MAC_CONTENT_CS_DTCH:
+ proto_item_append_text(ti, " (CS DTCH)");
+ /* TODO */
+ break;
+ default:
+ proto_item_append_text(ti, " (Unknown EDCH Content)");
+ }
+}
+
+/* to avoid unnecessary re-alignment, the 4 bit padding prepended to the HSDSCH in FP
+ * are handled in the MAC layer
+ * If the C/T field is present, 'bitoffs' will be 8 (4 bit padding and 4 bit C/T) and
+ * no re-alignment is necessary
+ * If no C/T is present, the whole payload will be left-shifted by 4 bit
+ */
+static void dissect_mac_fdd_hsdsch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_tree *hsdsch_tree = NULL;
+ proto_item *channel_type;
+ fp_info *fpinf;
+ umts_mac_info *macinf;
+ guint16 pos;
+ guint8 bitoffs = 4;
+ tvbuff_t *next_tvb;
+ proto_item *ti = NULL;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "MAC");
+
+ ti = proto_tree_add_item(tree, proto_umts_mac, tvb, 0, -1, FALSE);
+ hsdsch_tree = proto_item_add_subtree(ti, ett_mac_hsdsch);
+
+ fpinf = p_get_proto_data(pinfo->fd, proto_fp);
+ macinf = p_get_proto_data(pinfo->fd, proto_umts_mac);
+ pos = fpinf->cur_tb;
+
+ if (!macinf) {
+ proto_tree_add_text(hsdsch_tree, tvb, 0, -1,
+ "Cannot dissect MAC frame because per-frame info is missing");
+ return;
+ }
+ if (macinf->ctmux[pos]) {
+ proto_tree_add_bits_item(hsdsch_tree, hf_mac_ct, tvb, 0, 4, FALSE);
+ bitoffs += 4;
+ }
+
+ if ((bitoffs % 8) == 0) {
+ next_tvb = tvb_new_subset(tvb, bitoffs/8, -1, -1);
+ } else {
+ next_tvb = tvb_new_octet_aligned(tvb, bitoffs, -1);
+ add_new_data_source(pinfo, next_tvb, "Octet-Aligned HSDSCH Data");
+ }
+ switch (macinf->content[pos]) {
+ case MAC_CONTENT_DCCH:
+ proto_item_append_text(ti, " (DCCH)");
+ channel_type = proto_tree_add_uint(hsdsch_tree, hf_mac_channel, tvb, 0, 0, MAC_DCCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ call_dissector(rlc_dcch_handle, next_tvb, pinfo, tree);
+ break;
+ case MAC_CONTENT_PS_DTCH:
+ proto_item_append_text(ti, " (PS DTCH)");
+ channel_type = proto_tree_add_uint(hsdsch_tree, hf_mac_channel, tvb, 0, 0, MAC_DTCH);
+ PROTO_ITEM_SET_GENERATED(channel_type);
+ call_dissector(rlc_ps_dtch_handle, next_tvb, pinfo, tree);
+ break;
+ case MAC_CONTENT_CS_DTCH:
+ proto_item_append_text(ti, " (CS DTCH)");
+ break;
+ default:
+ proto_item_append_text(ti, " (Unknown HSDSCH Content)");
+ }
+}
+
+void
+proto_register_umts_mac(void)
+{
+ static gint *ett[] = {
+ &ett_mac,
+ &ett_mac_fach,
+ &ett_mac_rach,
+ &ett_mac_dch,
+ &ett_mac_pch,
+ &ett_mac_edch,
+ &ett_mac_hsdsch,
+ };
+ static hf_register_info hf[] = {
+ { &hf_mac_rach_fdd_tctf, { "Target Channel Type Field", "mac.tctf", FT_UINT8, BASE_HEX, VALS(rach_fdd_tctf_vals), 0, "Target Channel Type Field", HFILL } },
+ { &hf_mac_fach_fdd_tctf, { "Target Channel Type Field", "mac.tctf", FT_UINT8, BASE_HEX, VALS(fach_fdd_tctf_vals), 0, "Target Channel Type Field", HFILL } },
+ { &hf_mac_ct, { "C/T", "mac.ct", FT_UINT8, BASE_HEX, NULL, 0, "C/T", HFILL } },
+ { &hf_mac_ueid_type, { "UEID Type", "mac.ueid_type", FT_UINT8, BASE_DEC, VALS(ueid_type_vals), 0, "UEID Type", HFILL } },
+ { &hf_mac_crnti, { "C-RNTI (UEID)", "mac.ueid", FT_UINT16, BASE_HEX, NULL, 0x0, "C-RNTI (UEID)", HFILL } },
+ { &hf_mac_urnti, { "U-RNTI (UEID)", "mac.ueid", FT_UINT32, BASE_HEX, NULL, 0x0, "U-RNTI (UEID)", HFILL } },
+ { &hf_mac_channel, { "Logical Channel", "mac.logical_channel", FT_UINT16, BASE_DEC, VALS(mac_logical_channel_vals), 0, "Logical Channel", HFILL } },
+ };
+
+ proto_umts_mac = proto_register_protocol("MAC", "MAC", "mac");
+ proto_register_field_array(proto_umts_mac, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ register_dissector("mac.fdd.rach", dissect_mac_fdd_rach, proto_umts_mac);
+ register_dissector("mac.fdd.fach", dissect_mac_fdd_fach, proto_umts_mac);
+ register_dissector("mac.fdd.pch", dissect_mac_fdd_pch, proto_umts_mac);
+ register_dissector("mac.fdd.dch", dissect_mac_fdd_dch, proto_umts_mac);
+ register_dissector("mac.fdd.edch", dissect_mac_fdd_edch, proto_umts_mac);
+ register_dissector("mac.fdd.hsdsch", dissect_mac_fdd_hsdsch, proto_umts_mac);
+}
+
+void
+proto_reg_handoff_umts_mac(void)
+{
+ rlc_pcch_handle = find_dissector("rlc.pcch");
+ rlc_ccch_handle = find_dissector("rlc.ccch");
+ rlc_dcch_handle = find_dissector("rlc.dcch");
+ rlc_ps_dtch_handle = find_dissector("rlc.ps_dtch");
+}
+
diff --git a/epan/dissectors/packet-umts_mac.h b/epan/dissectors/packet-umts_mac.h
new file mode 100644
index 0000000000..a377ecc787
--- /dev/null
+++ b/epan/dissectors/packet-umts_mac.h
@@ -0,0 +1,50 @@
+/* Routines for UMTS FP disassembly
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Target Channel Type Field (TCTF) values */
+#define TCTF_CCCH_RACH_FDD 0x0
+#define TCTF_DCCH_DTCH_RACH_FDD 0x1
+
+#define TCTF_BCCH_FACH_FDD 0x0
+#define TCTF_DCCH_DTCH_FACH_FDD 0x3
+#define TCTF_MTCH_FACH_FDD 0x6
+#define TCTF_CCCH_FACH_FDD 0x40
+#define TCTF_MCCH_FACH_FDD 0x50
+#define TCTF_MSCH_FACH_FDD 0x5f
+#define TCTF_CTCH_FACH_FDD 0x80
+
+/* UeID Type values */
+#define MAC_UEID_TYPE_URNTI 0x0
+#define MAC_UEID_TYPE_CRNTI 0x1
+
+#define MAC_CONTENT_UNKNOWN 0
+#define MAC_CONTENT_DCCH 1
+#define MAC_CONTENT_PS_DTCH 2
+#define MAC_CONTENT_CS_DTCH 3
+
+#define MAX_MAC_FRAMES 64
+typedef struct umts_mac_info
+{
+ gboolean ctmux[MAX_MAC_FRAMES];
+ guint8 content[MAX_MAC_FRAMES];
+} umts_mac_info;