diff options
author | Anders Broman <anders.broman@ericsson.com> | 2009-10-09 06:37:48 +0000 |
---|---|---|
committer | Anders Broman <anders.broman@ericsson.com> | 2009-10-09 06:37:48 +0000 |
commit | 46aa5c44de5b62cc061840b29903a63bc3790445 (patch) | |
tree | 64ab8ab30cfec2b204cd625f538cf1345b443aee | |
parent | 0659b21e03316a01da5b2ed845064f57d76380f4 (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
-rw-r--r-- | epan/dissectors/packet-fp_hint.c | 586 | ||||
-rw-r--r-- | epan/dissectors/packet-meta.c | 534 | ||||
-rw-r--r-- | epan/dissectors/packet-meta.h | 69 | ||||
-rw-r--r-- | epan/dissectors/packet-rlc.c | 1384 | ||||
-rw-r--r-- | epan/dissectors/packet-rlc.h | 38 | ||||
-rw-r--r-- | epan/dissectors/packet-umts_mac.c | 609 | ||||
-rw-r--r-- | epan/dissectors/packet-umts_mac.h | 50 |
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 = ðwithfcs_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 = ðwithoutfcs_handle; + break; + case META_PROTO_DXT_ETHERNET_CRC: + next_dissector = ðwithfcs_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; |