/* Routines for LTE MAC disassembly * * Martin Mathieson * * $Id$ * * Wireshark - Network traffic analyzer * By Gerald Combs * 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. * * 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 #include #include #include #include #include "packet-mac-lte.h" /* Described in: * 3GPP TS 36.321 Evolved Universal Terrestrial Radio Access (E-UTRA) * Medium Access Control (MAC) protocol specification (Release 8) */ /* TODO: - more testing of control bodies - TDD mode - add a preference so that padding can be verified against an expected pattern? */ /* Initialize the protocol and registered fields. */ int proto_mac_lte = -1; static int mac_lte_tap = -1; /* Decoding context */ static int hf_mac_lte_context_radio_type = -1; static int hf_mac_lte_context_direction = -1; static int hf_mac_lte_context_rnti = -1; static int hf_mac_lte_context_rnti_type = -1; static int hf_mac_lte_context_ueid = -1; static int hf_mac_lte_context_subframe_number = -1; static int hf_mac_lte_context_predefined_frame = -1; static int hf_mac_lte_context_length = -1; static int hf_mac_lte_context_bch_transport_channel = -1; static int hf_mac_lte_context_retx_count = -1; static int hf_mac_lte_context_crc_status = -1; /* MAC SCH header fields */ static int hf_mac_lte_ulsch_header = -1; static int hf_mac_lte_dlsch_header = -1; static int hf_mac_lte_sch_subheader = -1; static int hf_mac_lte_sch_reserved = -1; static int hf_mac_lte_dlsch_lcid = -1; static int hf_mac_lte_ulsch_lcid = -1; static int hf_mac_lte_sch_extended = -1; static int hf_mac_lte_sch_format = -1; static int hf_mac_lte_sch_length = -1; static int hf_mac_lte_sch_header_only = -1; /* Data */ static int hf_mac_lte_sch_sdu = -1; static int hf_mac_lte_bch_pdu = -1; static int hf_mac_lte_pch_pdu = -1; static int hf_mac_lte_predefined_pdu = -1; static int hf_mac_lte_padding_data = -1; /* RAR fields */ static int hf_mac_lte_rar_headers = -1; static int hf_mac_lte_rar_header = -1; static int hf_mac_lte_rar_extension = -1; static int hf_mac_lte_rar_t = -1; static int hf_mac_lte_rar_bi = -1; static int hf_mac_lte_rar_rapid = -1; static int hf_mac_lte_rar_reserved = -1; static int hf_mac_lte_rar_body = -1; static int hf_mac_lte_rar_reserved2 = -1; static int hf_mac_lte_rar_ta = -1; static int hf_mac_lte_rar_ul_grant = -1; static int hf_mac_lte_rar_temporary_crnti = -1; /* Common channel control values */ static int hf_mac_lte_control_lcg_id = -1; static int hf_mac_lte_control_buffer_size = -1; static int hf_mac_lte_control_buffer_size_1 = -1; static int hf_mac_lte_control_buffer_size_2 = -1; static int hf_mac_lte_control_buffer_size_3 = -1; static int hf_mac_lte_control_buffer_size_4 = -1; static int hf_mac_lte_control_crnti = -1; static int hf_mac_lte_control_timing_advance = -1; static int hf_mac_lte_control_ue_contention_resolution_identity = -1; static int hf_mac_lte_control_power_headroom_reserved = -1; static int hf_mac_lte_control_power_headroom = -1; static int hf_mac_lte_control_padding = -1; /* Subtrees. */ static int ett_mac_lte = -1; static int ett_mac_lte_context = -1; static int ett_mac_lte_ulsch_header = -1; static int ett_mac_lte_dlsch_header = -1; static int ett_mac_lte_sch_subheader = -1; static int ett_mac_lte_rar_headers = -1; static int ett_mac_lte_rar_header = -1; static int ett_mac_lte_rar_body = -1; static int ett_mac_lte_bch = -1; static int ett_mac_lte_pch = -1; /* Constants and value strings */ static const value_string radio_type_vals[] = { { FDD_RADIO, "FDD"}, { TDD_RADIO, "TDD"}, { 0, NULL } }; static const value_string direction_vals[] = { { DIRECTION_UPLINK, "Uplink"}, { DIRECTION_DOWNLINK, "Downlink"}, { 0, NULL } }; static const value_string rnti_type_vals[] = { { NO_RNTI, "NO-RNTI"}, { P_RNTI, "P-RNTI"}, { RA_RNTI, "RA-RNTI"}, { C_RNTI, "C-RNTI"}, { SI_RNTI, "SI-RNTI"}, { 0, NULL } }; static const value_string bch_transport_channel_vals[] = { { SI_RNTI, "DL-SCH"}, { NO_RNTI, "BCH"}, { 0, NULL } }; static const value_string crc_status_vals[] = { { 0, "CRC Status Failed"}, { 1, "CRC Status OK"}, { 0, NULL } }; #define UE_CONTENTION_RESOLUTION_IDENTITY_LCID 0x1c #define TIMING_ADVANCE_LCID 0x1d #define DRX_COMMAND_LCID 0x1e #define PADDING_LCID 0x1f static const value_string dlsch_lcid_vals[] = { { 0, "CCCH"}, { 1, "1"}, { 2, "2"}, { 3, "3"}, { 4, "4"}, { 5, "5"}, { 6, "6"}, { 7, "7"}, { 8, "8"}, { 9, "9"}, { 10, "10"}, { UE_CONTENTION_RESOLUTION_IDENTITY_LCID, "UE Contention Resolution Identity"}, { TIMING_ADVANCE_LCID , "Timing Advance"}, { DRX_COMMAND_LCID , "DRX Command"}, { PADDING_LCID , "Padding" }, { 0, NULL } }; #define POWER_HEADROOM_REPORT_LCID 0x1a #define CRNTI_LCID 0x1b #define TRUNCATED_BSR_LCID 0x1c #define SHORT_BSR_LCID 0x1d #define LONG_BSR_LCID 0x1e static const value_string ulsch_lcid_vals[] = { { 0, "CCCH"}, { 1, "1"}, { 2, "2"}, { 3, "3"}, { 4, "4"}, { 5, "5"}, { 6, "6"}, { 7, "7"}, { 8, "8"}, { 9, "9"}, { 10, "10"}, { POWER_HEADROOM_REPORT_LCID, "Power Headroom Report"}, { CRNTI_LCID, "C-RNTI"}, { TRUNCATED_BSR_LCID, "Truncated BSR"}, { SHORT_BSR_LCID, "Short BSR"}, { LONG_BSR_LCID, "Long BSR"}, { PADDING_LCID, "Padding" }, { 0, NULL } }; static const value_string format_vals[] = { { 0, "Data length is < 128 bytes"}, { 1, "Data length is >= 128 bytes"}, { 0, NULL } }; static const value_string rar_type_vals[] = { { 0, "Backoff Indicator present"}, { 1, "RAPID present"}, { 0, NULL } }; static const value_string rar_bi_vals[] = { { 0, "0"}, { 1, "10"}, { 2, "20"}, { 3, "30"}, { 4, "40"}, { 5, "60"}, { 6, "80"}, { 7, "120"}, { 8, "160"}, { 9, "240"}, { 10, "320"}, { 11, "480"}, { 12, "960"}, { 0, NULL } }; static const value_string buffer_size_vals[] = { { 0, "BS = 0"}, { 1, "0 < BS <= 10"}, { 2, "10 < BS <= 12"}, { 3, "12 < BS <= 14"}, { 4, "14 < BS <= 17"}, { 5, "17 < BS <= 19"}, { 6, "19 < BS <= 22"}, { 7, "22 < BS <= 26"}, { 8, "26 < BS <= 31"}, { 9, "31 < BS <= 36"}, { 10, "36 < BS <= 42"}, { 11, "42 < BS <= 49"}, { 12, "49 < BS <= 57"}, { 13, "47 < BS <= 67"}, { 14, "67 < BS <= 78"}, { 15, "78 < BS <= 91"}, { 16, "91 < BS <= 107"}, { 17, "107 < BS <= 125"}, { 18, "125 < BS <= 146"}, { 19, "146 < BS <= 171"}, { 20, "171 < BS <= 200"}, { 21, "200 < BS <= 234"}, { 22, "234 < BS <= 274"}, { 23, "274 < BS <= 321"}, { 24, "321 < BS <= 376"}, { 25, "376 < BS <= 440"}, { 26, "440 < BS <= 515"}, { 27, "515 < BS <= 603"}, { 28, "603 < BS <= 706"}, { 29, "706 < BS <= 826"}, { 30, "826 < BS <= 967"}, { 31, "967 < BS <= 1132"}, { 32, "1132 < BS <= 1326"}, { 33, "1326 < BS <= 1552"}, { 34, "1552 < BS <= 1817"}, { 35, "1817 < BS <= 2127"}, { 36, "2127 < BS <= 2490"}, { 37, "2490 < BS <= 2915"}, { 38, "2915 < BS <= 3413"}, { 39, "3413 < BS <= 3995"}, { 40, "3995 < BS <= 4677"}, { 41, "4677 < BS <= 5476"}, { 42, "5476 < BS <= 6411"}, { 43, "6411 < BS <= 7505"}, { 44, "7505 < BS <= 8787"}, { 45, "8787 < BS <= 10276"}, { 46, "10287 < BS <= 12043"}, { 47, "12043 < BS <= 14099"}, { 48, "14099 < BS <= 16507"}, { 49, "16507 < BS <= 19325"}, { 50, "19325 < BS <= 22624"}, { 51, "22624 < BS <= 26487"}, { 52, "26487 < BS <= 31009"}, { 53, "31009 < BS <= 36304"}, { 54, "36304 < BS <= 42502"}, { 55, "42502 < BS <= 49759"}, { 56, "49759 < BS <= 58255"}, { 57, "58255 < BS <= 68201"}, { 58, "68201 < BS <= 79846"}, { 59, "79846 < BS <= 93479"}, { 60, "93479 < BS <= 109439"}, { 61, "109439 < BS <= 128125"}, { 62, "128125 < BS <= 150000"}, { 63, "BS > 150000"}, { 0, NULL } }; static const value_string header_only_vals[] = { { 0, "MAC PDU Headers and body present"}, { 1, "MAC PDU Headers only"}, { 0, NULL } }; static const value_string predefined_frame_vals[] = { { 0, "Real MAC PDU present - will dissect"}, { 1, "Predefined frame present - will not dissect"}, { 0, NULL } }; /* By default expect to find complete RAR PDUs for frames received on RA_RNTIs */ static gboolean global_mac_lte_single_rar = FALSE; /* By default check and warn about reserved bits not being zero. December '08 spec says they should be ignored... */ static gboolean global_mac_lte_check_reserved_bits = TRUE; /* If this PDU has been NACK'd (by HARQ) more than a certain number of times, we trigger an expert warning. */ static gint global_mac_lte_retx_counter_trigger = 3; /* Dissect a single Random Access Reponse body */ static gint dissect_rar_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset) { guint8 reserved; guint start_body_offset = offset; proto_item *ti; proto_item *rar_body_ti; proto_tree *rar_body_tree; guint16 timing_advance; guint32 ul_grant; guint16 temp_crnti; /* Create tree for this Body */ rar_body_ti = proto_tree_add_item(tree, hf_mac_lte_rar_body, tvb, offset, 0, FALSE); rar_body_tree = proto_item_add_subtree(rar_body_ti, ett_mac_lte_rar_body); /* Dissect an RAR entry */ /* Check reserved bit */ reserved = (tvb_get_guint8(tvb, offset) & 0x80) >> 7; ti = proto_tree_add_item(rar_body_tree, hf_mac_lte_rar_reserved2, tvb, offset, 1, FALSE); if (global_mac_lte_check_reserved_bits && (reserved != 0)) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "MAC RAR header Reserved bit not zero (found 0x%x)", reserved); } /* Timing Advance */ timing_advance = (tvb_get_ntohs(tvb, offset) & 0x7ff0) >> 4; proto_tree_add_item(rar_body_tree, hf_mac_lte_rar_ta, tvb, offset, 2, FALSE); offset++; /* UL Grant */ ul_grant = (tvb_get_ntohl(tvb, offset) & 0x0fffff00) >> 8; proto_tree_add_item(rar_body_tree, hf_mac_lte_rar_ul_grant, tvb, offset, 3, FALSE); offset += 3; /* Temporary C-RNTI */ temp_crnti = tvb_get_ntohs(tvb, offset); proto_tree_add_item(rar_body_tree, hf_mac_lte_rar_temporary_crnti, tvb, offset, 2, FALSE); offset += 2; proto_item_append_text(rar_body_ti, " (TA=%u, UL-Grant=%u, Temp C-RNTI=%u)", timing_advance, ul_grant, temp_crnti); if (check_col(pinfo->cinfo, COL_INFO)) { col_append_fstr(pinfo->cinfo, COL_INFO, " (TA=%u, UL-Grant=%u, Temp C-RNTI=%u)", timing_advance, ul_grant, temp_crnti); } proto_item_set_len(rar_body_ti, offset-start_body_offset); return offset; } /* Dissect Random Access Reponse (RAR) PDU */ static void dissect_rar(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset) { gint number_of_rars = 0; /* No of RAR bodies expected following headers */ gboolean backoff_indicator_seen = FALSE; guint8 extension; gint n; proto_tree *rar_headers_tree; proto_item *rar_headers_ti; int start_headers_offset = offset; if (check_col(pinfo->cinfo, COL_INFO)) { col_set_str(pinfo->cinfo, COL_INFO, "RAR"); } /* Create headers tree */ rar_headers_ti = proto_tree_add_item(tree, hf_mac_lte_rar_headers, tvb, offset, 0, FALSE); rar_headers_tree = proto_item_add_subtree(rar_headers_ti, ett_mac_lte_rar_headers); /***************************/ /* Read the header entries */ do { int start_header_offset = offset; proto_tree *rar_header_tree; proto_item *rar_header_ti; guint8 type_value; guint8 first_byte = tvb_get_guint8(tvb, offset); /* Create tree for this header */ rar_header_ti = proto_tree_add_item(rar_headers_tree, hf_mac_lte_rar_header, tvb, offset, 0, FALSE); rar_header_tree = proto_item_add_subtree(rar_header_ti, ett_mac_lte_rar_header); /* Extension */ extension = (first_byte & 0x80) >> 7; proto_tree_add_item(rar_header_tree, hf_mac_lte_rar_extension, tvb, offset, 1, FALSE); /* Type */ type_value = (first_byte & 0x40) >> 6; proto_tree_add_item(rar_header_tree, hf_mac_lte_rar_t, tvb, offset, 1, FALSE); if (type_value == 0) { guint8 reserved; guint8 backoff_indicator; proto_item *ti; /* 2 Reserved bits */ reserved = (tvb_get_guint8(tvb, offset) & 0x30) >> 4; ti = proto_tree_add_item(rar_header_tree, hf_mac_lte_rar_reserved, tvb, offset, 1, FALSE); if (global_mac_lte_check_reserved_bits && (reserved != 0)) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "MAC RAR header Reserved bits not zero (found 0x%x)", reserved); } /* Backoff Indicator */ backoff_indicator = tvb_get_guint8(tvb, offset) & 0x0f; proto_tree_add_item(rar_header_tree, hf_mac_lte_rar_bi, tvb, offset, 1, FALSE); backoff_indicator_seen = TRUE; proto_item_append_text(rar_header_ti, " (Backoff Indicator=%sms)", val_to_str(backoff_indicator, rar_bi_vals, "Illegal value")); if (check_col(pinfo->cinfo, COL_INFO)) { col_append_fstr(pinfo->cinfo, COL_INFO, " (Backoff Indicator=%sms)", val_to_str(backoff_indicator, rar_bi_vals, "Illegal value")); } } else { /* RAPID case */ proto_tree_add_item(rar_header_tree, hf_mac_lte_rar_rapid, tvb, offset, 1, FALSE); number_of_rars++; proto_item_append_text(rar_header_ti, " (RAPID=%u)", tvb_get_guint8(tvb, offset) & 0x3f); } offset++; /* Finalise length of header tree selection */ proto_item_set_len(rar_header_ti, offset - start_header_offset); } while (extension); /* Append summary to headers root */ proto_item_append_text(rar_headers_ti, " (%u RARs%s)", number_of_rars, backoff_indicator_seen ? ", BI" : ""); /* Set length for headers root */ proto_item_set_len(rar_headers_ti, offset-start_headers_offset); /***************************/ /* Read any indicated RARs */ for (n=0; n < number_of_rars; n++) { offset = dissect_rar_entry(tvb, pinfo, tree, offset); } } /* Dissect BCH PDU */ static void dissect_bch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, mac_lte_info *p_mac_lte_info) { proto_item *ti; if (check_col(pinfo->cinfo, COL_INFO)) { col_append_fstr(pinfo->cinfo, COL_INFO, "BCH PDU (%u bytes, on %s transport)", tvb_length_remaining(tvb, offset), val_to_str(p_mac_lte_info->rntiType, bch_transport_channel_vals, "Unknown")); } /* Show which transport layer it came in on (inferred from RNTI type) */ ti = proto_tree_add_uint(tree, hf_mac_lte_context_bch_transport_channel, tvb, offset, 0, p_mac_lte_info->rntiType); PROTO_ITEM_SET_GENERATED(ti); /* Whole frame is BCH data */ ti = proto_tree_add_item(tree, hf_mac_lte_bch_pdu, tvb, offset, -1, FALSE); /* Check that this *is* downlink! */ if (p_mac_lte_info->direction == DIRECTION_UPLINK) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "BCH data should not be received in Uplink!"); } } /* Dissect PCH PDU */ static void dissect_pch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint8 direction) { proto_item *ti; if (check_col(pinfo->cinfo, COL_INFO)) { col_append_fstr(pinfo->cinfo, COL_INFO, "PCH PDU (%u bytes)", tvb_length_remaining(tvb, offset)); } ti = proto_tree_add_item(tree, hf_mac_lte_pch_pdu, tvb, offset, -1, FALSE); /* Check that this *is* downlink! */ if (direction == DIRECTION_UPLINK) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "PCH data should not be received in Uplink!"); } } /* Does this header entry correspond to a fixed-sized control element? */ static int is_fixed_sized_control_element(guint8 lcid, guint8 direction) { if (direction == DIRECTION_UPLINK) { /* Uplink */ switch (lcid) { case POWER_HEADROOM_REPORT_LCID: case CRNTI_LCID: case TRUNCATED_BSR_LCID: case SHORT_BSR_LCID: case LONG_BSR_LCID: return TRUE; default: return FALSE; } } else { /* Assume Downlink */ switch (lcid) { case UE_CONTENTION_RESOLUTION_IDENTITY_LCID: case TIMING_ADVANCE_LCID: case DRX_COMMAND_LCID: return TRUE; default: return FALSE; } } } /* Is this a BSR report header? */ static int is_bsr_lcid(guint8 lcid) { return ((lcid == TRUNCATED_BSR_LCID) || (lcid == SHORT_BSR_LCID) || (lcid == LONG_BSR_LCID)); } #define MAX_HEADERS_IN_PDU 1024 /* UL-SCH and DL-SCH formats have much in common, so handle them in a common function */ static void dissect_ulsch_or_dlsch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint8 direction, mac_lte_tap_info *tap_info) { guint8 extension; guint8 n; proto_item *truncated_ti; /* Keep track of LCIDs and lengths as we dissect the header */ guint8 number_of_headers = 0; guint8 lcids[MAX_HEADERS_IN_PDU]; gint16 pdu_lengths[MAX_HEADERS_IN_PDU]; proto_item *pdu_header_ti; proto_tree *pdu_header_tree; gboolean have_seen_data_header = FALSE; gboolean have_seen_bsr = FALSE; if (check_col(pinfo->cinfo, COL_INFO)) { col_append_fstr(pinfo->cinfo, COL_INFO, "%s: ", (direction == DIRECTION_UPLINK) ? "UL-SCH" : "DL-SCH"); } /* Add PDU block header subtree */ pdu_header_ti = proto_tree_add_string_format(tree, (direction == DIRECTION_UPLINK) ? hf_mac_lte_ulsch_header : hf_mac_lte_dlsch_header, tvb, offset, 0, "", "MAC PDU Header"); pdu_header_tree = proto_item_add_subtree(pdu_header_ti, (direction == DIRECTION_UPLINK) ? ett_mac_lte_ulsch_header : ett_mac_lte_dlsch_header); /************************************************************************/ /* Dissect each sub-header. */ do { guint8 reserved; guint64 length = 0; proto_item *pdu_subheader_ti; proto_tree *pdu_subheader_tree; proto_item *lcid_ti; proto_item *ti; gint offset_start_subheader = offset; guint8 first_byte = tvb_get_guint8(tvb, offset); /* Add PDU block header subtree */ pdu_subheader_ti = proto_tree_add_string_format(pdu_header_tree, hf_mac_lte_sch_subheader, tvb, offset, 0, "", "Sub-header"); pdu_subheader_tree = proto_item_add_subtree(pdu_subheader_ti, ett_mac_lte_sch_subheader); /* Check 1st 2 reserved bits */ reserved = (first_byte & 0xc0) >> 6; ti = proto_tree_add_item(pdu_subheader_tree, hf_mac_lte_sch_reserved, tvb, offset, 1, FALSE); if (global_mac_lte_check_reserved_bits && (reserved != 0)) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "MAC U/DL-SCH header Reserved bits not zero"); } /* Extended bit */ extension = (first_byte & 0x20) >> 5; proto_tree_add_item(pdu_subheader_tree, hf_mac_lte_sch_extended, tvb, offset, 1, FALSE); /* LCID. Has different meaning depending upon direction. */ lcids[number_of_headers] = first_byte & 0x1f; if (direction == DIRECTION_UPLINK) { lcid_ti = proto_tree_add_item(pdu_subheader_tree, hf_mac_lte_ulsch_lcid, tvb, offset, 1, FALSE); if (check_col(pinfo->cinfo, COL_INFO)) { col_append_fstr(pinfo->cinfo, COL_INFO, "(%s", val_to_str(lcids[number_of_headers], ulsch_lcid_vals, "(Unknown LCID)")); } } else { lcid_ti = proto_tree_add_item(pdu_subheader_tree, hf_mac_lte_dlsch_lcid, tvb, offset, 1, FALSE); if (check_col(pinfo->cinfo, COL_INFO)) { col_append_fstr(pinfo->cinfo, COL_INFO, "(%s", val_to_str(lcids[number_of_headers], dlsch_lcid_vals, "(Unknown LCID)")); } } offset++; /* Remember if we've seen a data subheader */ if (lcids[number_of_headers] <= 10) { have_seen_data_header = TRUE; } /* Show an expert item if a contol subheader (except Padding) appears *after* a data PDU */ if (have_seen_data_header && (lcids[number_of_headers] > 10) && (lcids[number_of_headers] != PADDING_LCID)) { expert_add_info_format(pinfo, lcid_ti, PI_MALFORMED, PI_ERROR, "Control subheaders should not appear after data subheaders"); } /* Show an expert item if we're seeing more then one BSR in a frame */ if ((direction == DIRECTION_UPLINK) && is_bsr_lcid(lcids[number_of_headers])) { if (have_seen_bsr) { expert_add_info_format(pinfo, lcid_ti, PI_MALFORMED, PI_ERROR, "There shouldn't be > 1 BSR in a frame"); } have_seen_bsr = TRUE; } /********************************************************************/ /* Length field follows if not the last header or for a fixed-sized control element */ if (!extension) { if (is_fixed_sized_control_element(lcids[number_of_headers], direction)) { pdu_lengths[number_of_headers] = 0; } else { pdu_lengths[number_of_headers] = -1; } } else { if (!is_fixed_sized_control_element(lcids[number_of_headers], direction) && (lcids[number_of_headers] != PADDING_LCID)) { guint8 format; /* F(ormat) bit tells us how long the length field is */ format = (tvb_get_guint8(tvb, offset) & 0x80) >> 7; proto_tree_add_item(pdu_subheader_tree, hf_mac_lte_sch_format, tvb, offset, 1, FALSE); /* Now read length field itself */ if (format) { /* >= 128 - use 15 bits */ proto_tree_add_bits_ret_val(pdu_subheader_tree, hf_mac_lte_sch_length, tvb, offset*8 + 1, 15, &length, FALSE); offset += 2; } else { /* Less than 128 - only 7 bits */ proto_tree_add_bits_ret_val(pdu_subheader_tree, hf_mac_lte_sch_length, tvb, offset*8 + 1, 7, &length, FALSE); offset++; } pdu_lengths[number_of_headers] = (gint16)length; } else { pdu_lengths[number_of_headers] = 0; } } /* Close off description in info column */ if (check_col(pinfo->cinfo, COL_INFO)) { switch (pdu_lengths[number_of_headers]) { case 0: col_append_str(pinfo->cinfo, COL_INFO, ") "); break; case -1: col_append_str(pinfo->cinfo, COL_INFO, ":remainder) "); break; default: col_append_fstr(pinfo->cinfo, COL_INFO, ":%u bytes) ", pdu_lengths[number_of_headers]); break; } } /* Append summary to subheader root */ proto_item_append_text(pdu_subheader_ti, " (lcid=%s", val_to_str(lcids[number_of_headers], (direction == DIRECTION_UPLINK) ? ulsch_lcid_vals : dlsch_lcid_vals, "Unknown")); switch (pdu_lengths[number_of_headers]) { case -1: proto_item_append_text(pdu_subheader_ti, ", length is remainder)"); break; case 0: proto_item_append_text(pdu_subheader_ti, ")"); break; default: proto_item_append_text(pdu_subheader_ti, ", length=%u)", pdu_lengths[number_of_headers]); break; } /* Flag unknown lcid values in expert info */ if (strncmp(val_to_str(lcids[number_of_headers], (direction == DIRECTION_UPLINK) ? ulsch_lcid_vals : dlsch_lcid_vals, "Unknown"), "Unknown", sizeof("Unknown")) == 0) { expert_add_info_format(pinfo, pdu_subheader_ti, PI_MALFORMED, PI_ERROR, "Unexpected LCID received (%u)", lcids[number_of_headers]); } /* Set length of this subheader */ proto_item_set_len(pdu_subheader_ti, offset- offset_start_subheader); number_of_headers++; } while (extension); /* Append summary to overall PDU header root */ proto_item_append_text(pdu_header_ti, " (%u subheaders)", number_of_headers); /* And set its length to offset */ proto_item_set_len(pdu_header_ti, offset); /* There might not be any data, if only headers were logged */ truncated_ti = proto_tree_add_uint(tree, hf_mac_lte_sch_header_only, tvb, 0, 0, tvb_length_remaining(tvb, offset) == 0); PROTO_ITEM_SET_GENERATED(truncated_ti); if (tvb_length_remaining(tvb, offset) == 0) { expert_add_info_format(pinfo, truncated_ti, PI_SEQUENCE, PI_NOTE, "MAC PDU SDUs have been ommitted"); return; } /************************************************************************/ /* Dissect SDUs / control elements / padding. */ /************************************************************************/ for (n=0; n < number_of_headers; n++) { /* Data SDUs treated identically for Uplink or downlink channels */ if (lcids[n] <= 10) { guint16 data_length = (pdu_lengths[n] == -1) ? tvb_length_remaining(tvb, offset) : pdu_lengths[n]; /* Dissect SDU */ proto_tree_add_bytes_format(tree, hf_mac_lte_sch_sdu, tvb, offset, pdu_lengths[n], tvb_get_ptr(tvb, offset, pdu_lengths[n]), "SDU (%s, length = %u bytes)", val_to_str(lcids[n], (direction == DIRECTION_UPLINK) ? ulsch_lcid_vals : dlsch_lcid_vals, "Unknown"), data_length); offset += data_length; /* Update tap byte count for this channel */ tap_info->bytes_for_lcid[lcids[n]] += data_length; tap_info->sdus_for_lcid[lcids[n]]++; } else { /* See if its a control PDU type */ if (direction == DIRECTION_DOWNLINK) { /* DL-SCH Control PDUs */ switch (lcids[n]) { case UE_CONTENTION_RESOLUTION_IDENTITY_LCID: proto_tree_add_item(tree, hf_mac_lte_control_ue_contention_resolution_identity, tvb, offset, 6, FALSE); offset += 6; break; case TIMING_ADVANCE_LCID: proto_tree_add_item(tree, hf_mac_lte_control_timing_advance, tvb, offset, 1, FALSE); offset++; break; case DRX_COMMAND_LCID: /* No payload */ break; case PADDING_LCID: /* No payload, unless its the last subheader, in which case it extends to the end of the PDU */ if (n == (number_of_headers-1) && (tvb_length_remaining(tvb, offset) > 0)) { proto_tree_add_item(tree, hf_mac_lte_padding_data, tvb, offset, -1, FALSE); } break; default: break; } } else { /* DL-SCH Control PDUs */ switch (lcids[n]) { case POWER_HEADROOM_REPORT_LCID: { proto_item *ti; guint8 reserved; /* Check 2 Reserved bits */ reserved = (tvb_get_guint8(tvb, offset) & 0xc0) >> 6; ti = proto_tree_add_item(tree, hf_mac_lte_control_power_headroom_reserved, tvb, offset, 1, FALSE); if (global_mac_lte_check_reserved_bits && (reserved != 0)) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "Power Headroom Reserved bits not zero (found 0x%x)", reserved); } } proto_tree_add_item(tree, hf_mac_lte_control_power_headroom, tvb, offset, 1, FALSE); offset++; break; case CRNTI_LCID: proto_tree_add_item(tree, hf_mac_lte_control_crnti, tvb, offset, 2, FALSE); offset += 2; break; case TRUNCATED_BSR_LCID: case SHORT_BSR_LCID: /* LCG ID */ proto_tree_add_item(tree, hf_mac_lte_control_lcg_id, tvb, offset, 1, FALSE); /* Buffer Size */ proto_tree_add_item(tree, hf_mac_lte_control_buffer_size, tvb, offset, 1, FALSE); offset++; break; case LONG_BSR_LCID: proto_tree_add_item(tree, hf_mac_lte_control_buffer_size_1, tvb, offset, 1, FALSE); proto_tree_add_item(tree, hf_mac_lte_control_buffer_size_2, tvb, offset, 1, FALSE); offset++; proto_tree_add_item(tree, hf_mac_lte_control_buffer_size_3, tvb, offset, 1, FALSE); offset++; proto_tree_add_item(tree, hf_mac_lte_control_buffer_size_4, tvb, offset, 1, FALSE); offset++; break; case PADDING_LCID: /* No payload, unless its the last subheader, in which case it extends to the end of the PDU */ if ((n == (number_of_headers-1)) && tvb_length_remaining(tvb, offset) > 0) { proto_tree_add_item(tree, hf_mac_lte_padding_data, tvb, offset, -1, FALSE); } break; default: break; } } } } } /*****************************/ /* Main dissection function. */ void dissect_mac_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_tree *mac_lte_tree; proto_item *ti; gint offset = 0; struct mac_lte_info *p_mac_lte_info = NULL; static mac_lte_tap_info tap_info; memset(&tap_info, 0, sizeof(mac_lte_tap_info)); /* Set protocol name */ if (check_col(pinfo->cinfo, COL_PROTOCOL)) { col_set_str(pinfo->cinfo, COL_PROTOCOL, "MAC-LTE"); } /* Create protocol tree. */ ti = proto_tree_add_item(tree, proto_mac_lte, tvb, offset, -1, FALSE); mac_lte_tree = proto_item_add_subtree(ti, ett_mac_lte); /* Look for packet info! */ p_mac_lte_info = p_get_proto_data(pinfo->fd, proto_mac_lte); /* Can't dissect anything without it... */ if (p_mac_lte_info == NULL) { proto_item *ti = proto_tree_add_text(mac_lte_tree, tvb, offset, -1, "Can't dissect LTE MAC frame because no per-frame info was attached!"); PROTO_ITEM_SET_GENERATED(ti); return; } /*****************************************/ /* Show context information */ ti = proto_tree_add_uint(mac_lte_tree, hf_mac_lte_context_radio_type, tvb, 0, 0, p_mac_lte_info->radioType); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(mac_lte_tree, hf_mac_lte_context_direction, tvb, 0, 0, p_mac_lte_info->direction); PROTO_ITEM_SET_GENERATED(ti); if (p_mac_lte_info->ueid != 0) { ti = proto_tree_add_uint(mac_lte_tree, hf_mac_lte_context_ueid, tvb, 0, 0, p_mac_lte_info->ueid); PROTO_ITEM_SET_GENERATED(ti); } ti = proto_tree_add_uint(mac_lte_tree, hf_mac_lte_context_subframe_number, tvb, 0, 0, p_mac_lte_info->subframeNumber); PROTO_ITEM_SET_GENERATED(ti); if (p_mac_lte_info->rntiType != NO_RNTI) { ti = proto_tree_add_uint(mac_lte_tree, hf_mac_lte_context_rnti, tvb, 0, 0, p_mac_lte_info->rnti); PROTO_ITEM_SET_GENERATED(ti); } ti = proto_tree_add_uint(mac_lte_tree, hf_mac_lte_context_rnti_type, tvb, 0, 0, p_mac_lte_info->rntiType); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(mac_lte_tree, hf_mac_lte_context_predefined_frame, tvb, 0, 0, p_mac_lte_info->isPredefinedData); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(mac_lte_tree, hf_mac_lte_context_length, tvb, 0, 0, p_mac_lte_info->length); PROTO_ITEM_SET_GENERATED(ti); if (p_mac_lte_info->reTxCount) { ti = proto_tree_add_uint(mac_lte_tree, hf_mac_lte_context_retx_count, tvb, 0, 0, p_mac_lte_info->reTxCount); PROTO_ITEM_SET_GENERATED(ti); if (p_mac_lte_info->reTxCount >= global_mac_lte_retx_counter_trigger) { expert_add_info_format(pinfo, ti, PI_SEQUENCE, PI_ERROR, "Frame has now been NACK'd %u times", p_mac_lte_info->reTxCount); } } if (p_mac_lte_info->crcStatusValid) { ti = proto_tree_add_uint(mac_lte_tree, hf_mac_lte_context_crc_status, tvb, 0, 0, p_mac_lte_info->crcStatus); PROTO_ITEM_SET_GENERATED(ti); if (p_mac_lte_info->crcStatus != TRUE) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "Frame has CRC error"); if (check_col(pinfo->cinfo, COL_INFO)) { col_append_fstr(pinfo->cinfo, COL_INFO, ""); } } } /* Set context-info parts of tap struct */ tap_info.rnti = p_mac_lte_info->rnti; tap_info.rntiType = p_mac_lte_info->rntiType; tap_info.isPredefinedData = p_mac_lte_info->isPredefinedData; tap_info.reTxCount = p_mac_lte_info->reTxCount; tap_info.crcStatusValid = p_mac_lte_info->crcStatusValid; tap_info.crcStatus = p_mac_lte_info->crcStatus; tap_info.direction = p_mac_lte_info->direction; /* Also set total number of bytes (won't be used for UL/DL-SCH) */ tap_info.single_number_of_bytes = tvb_length_remaining(tvb, offset); /* If we know its predefined data, don't try to decode any further */ if (p_mac_lte_info->isPredefinedData) { proto_tree_add_item(mac_lte_tree, hf_mac_lte_predefined_pdu, tvb, offset, -1, FALSE); if (check_col(pinfo->cinfo, COL_INFO)) { col_append_fstr(pinfo->cinfo, COL_INFO, "Predefined data (%u bytes)", tvb_length_remaining(tvb, offset)); } /* Queue tap info */ if (!pinfo->in_error_pkt) { tap_queue_packet(mac_lte_tap, pinfo, &tap_info); } return; } /* Dissect the MAC PDU itself. Format depends upon RNTI type. */ switch (p_mac_lte_info->rntiType) { case P_RNTI: /* PCH PDU */ dissect_pch(tvb, pinfo, mac_lte_tree, offset, p_mac_lte_info->direction); break; case RA_RNTI: /* RAR PDU */ if (!global_mac_lte_single_rar) { dissect_rar(tvb, pinfo, mac_lte_tree, offset); } else { dissect_rar_entry(tvb, pinfo, mac_lte_tree, offset); } break; case C_RNTI: /* Can be UL-SCH or DL-SCH */ dissect_ulsch_or_dlsch(tvb, pinfo, mac_lte_tree, offset, p_mac_lte_info->direction, &tap_info); break; case SI_RNTI: /* BCH over DL-SCH */ dissect_bch(tvb, pinfo, mac_lte_tree, offset, p_mac_lte_info); break; case NO_RNTI: /* Must be BCH over BCH... */ dissect_bch(tvb, pinfo, mac_lte_tree, offset, p_mac_lte_info); break; default: break; } /* Queue tap info */ if (!pinfo->in_error_pkt) { tap_queue_packet(mac_lte_tap, pinfo, &tap_info); } } void proto_register_mac_lte(void) { static hf_register_info hf[] = { /**********************************/ /* Items for decoding context */ { &hf_mac_lte_context_radio_type, { "Radio Type", "mac-lte.radio-type", FT_UINT8, BASE_DEC, VALS(radio_type_vals), 0x0, "Radio Type", HFILL } }, { &hf_mac_lte_context_direction, { "Direction", "mac-lte.direction", FT_UINT8, BASE_DEC, VALS(direction_vals), 0x0, "Direction of message", HFILL } }, { &hf_mac_lte_context_rnti, { "RNTI", "mac-lte.rnti", FT_UINT16, BASE_DEC, 0, 0x0, "RNTI associated with message", HFILL } }, { &hf_mac_lte_context_rnti_type, { "RNTI Type", "mac-lte.rnti-type", FT_UINT8, BASE_DEC, VALS(rnti_type_vals), 0x0, "Type of RNTI associated with message", HFILL } }, { &hf_mac_lte_context_ueid, { "UEId", "mac-lte.ueid", FT_UINT16, BASE_DEC, 0, 0x0, "User Equipment Identifier associated with message", HFILL } }, { &hf_mac_lte_context_subframe_number, { "Subframe", "mac-lte.subframe", FT_UINT16, BASE_DEC, 0, 0x0, "Subframe number associate with message", HFILL } }, { &hf_mac_lte_context_predefined_frame, { "Predefined frame", "mac-lte.is-predefined-frame", FT_UINT8, BASE_DEC, VALS(predefined_frame_vals), 0x0, "Predefined test frame (or real MAC PDU)", HFILL } }, { &hf_mac_lte_context_length, { "Length of frame", "mac-lte.length", FT_UINT8, BASE_DEC, 0, 0x0, "Original length of frame (including SDUs and padding)", HFILL } }, { &hf_mac_lte_context_bch_transport_channel, { "Transport channel", "mac-lte.bch-transport-channel", FT_UINT8, BASE_DEC, VALS(bch_transport_channel_vals), 0x0, "Transport channel BCH data was carried on", HFILL } }, { &hf_mac_lte_context_retx_count, { "ReTX count", "mac-lte.retx-count", FT_UINT8, BASE_DEC, 0, 0x0, "Number of times this PDU has been retransmitted", HFILL } }, { &hf_mac_lte_context_crc_status, { "CRC Status", "mac-lte.crc-status", FT_UINT8, BASE_DEC, VALS(crc_status_vals), 0x0, "CRC Status as reported by PHY", HFILL } }, /*******************************************/ /* MAC shared channel header fields */ { &hf_mac_lte_ulsch_header, { "UL-SCH Header", "mac-lte.ulsch.header", FT_STRING, BASE_NONE, NULL, 0x0, "UL-SCH Header", HFILL } }, { &hf_mac_lte_dlsch_header, { "DL-SCH Header", "mac-lte.dlsch.header", FT_STRING, BASE_NONE, NULL, 0x0, "DL-SCH Header", HFILL } }, { &hf_mac_lte_sch_subheader, { "SCH sub-header", "mac-lte.sch.subheader", FT_STRING, BASE_NONE, NULL, 0x0, "SCH sub-header", HFILL } }, { &hf_mac_lte_sch_reserved, { "SCH reserved bits", "mac-lte.sch.reserved", FT_UINT8, BASE_HEX, NULL, 0xc0, "SCH reserved bits", HFILL } }, { &hf_mac_lte_sch_extended, { "Extension", "mac-lte.sch.extended", FT_UINT8, BASE_HEX, 0, 0x20, "Extension - i.e. further headers after this one", HFILL } }, { &hf_mac_lte_dlsch_lcid, { "LCID", "mac-lte.dlsch.lcid", FT_UINT8, BASE_HEX, VALS(dlsch_lcid_vals), 0x1f, "DL-SCH Logical Channel Identifier", HFILL } }, { &hf_mac_lte_ulsch_lcid, { "LCID", "mac-lte.ulsch.lcid", FT_UINT8, BASE_HEX, VALS(ulsch_lcid_vals), 0x1f, "UL-SCH Logical Channel Identifier", HFILL } }, { &hf_mac_lte_sch_format, { "Format", "mac-lte.sch.format", FT_UINT8, BASE_HEX, VALS(format_vals), 0x80, "Format", HFILL } }, { &hf_mac_lte_sch_length, { "Length", "mac-lte.sch.length", FT_UINT16, BASE_DEC, 0, 0x0, "Length of MAC SDU or MAC control element", HFILL } }, { &hf_mac_lte_sch_header_only, { "MAC PDU Header only", "mac-lte.sch.header-only", FT_UINT8, BASE_DEC, VALS(header_only_vals), 0x0, "MAC PDU Header only", HFILL } }, /********************************/ /* Data */ { &hf_mac_lte_sch_sdu, { "SDU", "mac-lte.sch.sdu", FT_BYTES, BASE_HEX, 0, 0x0, "Shared channel SDU", HFILL } }, { &hf_mac_lte_bch_pdu, { "BCH PDU", "mac-lte.bch.pdu", FT_BYTES, BASE_HEX, 0, 0x0, "BCH PDU", HFILL } }, { &hf_mac_lte_pch_pdu, { "PCH PDU", "mac-lte.pch.pdu", FT_BYTES, BASE_HEX, 0, 0x0, "PCH PDU", HFILL } }, { &hf_mac_lte_predefined_pdu, { "Predefined data", "mac-lte.predefined-data", FT_BYTES, BASE_HEX, 0, 0x0, "Predefined test data", HFILL } }, { &hf_mac_lte_padding_data, { "Padding data", "mac-lte.padding-data", FT_BYTES, BASE_HEX, 0, 0x0, "Padding data", HFILL } }, /*********************************/ /* RAR fields */ { &hf_mac_lte_rar_headers, { "RAR Headers", "mac-lte.rar.headers", FT_STRING, BASE_NONE, NULL, 0x0, "RAR Headers", HFILL } }, { &hf_mac_lte_rar_header, { "RAR Header", "mac-lte.rar.header", FT_STRING, BASE_NONE, NULL, 0x0, "RAR Header", HFILL } }, { &hf_mac_lte_rar_extension, { "Extension", "mac-lte.rar.e", FT_UINT8, BASE_HEX, 0, 0x80, "Extension - i.e. further RAR headers after this one", HFILL } }, { &hf_mac_lte_rar_t, { "Type", "mac-lte.rar.t", FT_UINT8, BASE_HEX, VALS(rar_type_vals), 0x40, "Type field indicating whether the payload is RAPID or BI", HFILL } }, { &hf_mac_lte_rar_bi, { "BI", "mac-lte.rar.bi", FT_UINT8, BASE_HEX, VALS(rar_bi_vals), 0x0f, "Backoff Indicator (ms)", HFILL } }, { &hf_mac_lte_rar_rapid, { "RAPID", "mac-lte.rar.rapid", FT_UINT8, BASE_HEX, 0, 0x3f, "Random Access Preamble IDentifier", HFILL } }, { &hf_mac_lte_rar_reserved, { "Reserved", "mac-lte.rar.reserved", FT_UINT8, BASE_HEX, 0, 0x30, "Reserved bits in RAR header - should be 0", HFILL } }, { &hf_mac_lte_rar_body, { "RAR Body", "mac-lte.rar.body", FT_STRING, BASE_NONE, NULL, 0x0, "RAR Body", HFILL } }, { &hf_mac_lte_rar_reserved2, { "Reserved", "mac-lte.rar.reserved2", FT_UINT8, BASE_HEX, 0, 0x80, "Reserved bit in RAR body - should be 0", HFILL } }, { &hf_mac_lte_rar_ta, { "Timing Advance", "mac-lte.rar.ta", FT_UINT16, BASE_DEC, 0, 0x7ff0, "Required adjustment to uplink transmission timing", HFILL } }, { &hf_mac_lte_rar_ul_grant, { "UL Grant", "mac-lte.rar.ul-grant", FT_UINT24, BASE_DEC, 0, 0x0fffff, "Size of UL Grant (length confused in spec!!!!)", HFILL } }, { &hf_mac_lte_rar_temporary_crnti, { "Temporary C-RNTI", "mac-lte.rar.temporary-crnti", FT_UINT16, BASE_DEC, 0, 0x0, "Temporary C-RNTI", HFILL } }, /**********************/ /* Control PDU fields */ { &hf_mac_lte_control_lcg_id, { "Logical Channel Group ID", "mac-lte.control.lcg-id", FT_UINT8, BASE_DEC, 0, 0xc0, "Logical Channel Group ID", HFILL } }, { &hf_mac_lte_control_buffer_size, { "Buffer Size", "mac-lte.control.buffer-size", FT_UINT8, BASE_DEC, VALS(buffer_size_vals), 0x3f, "Buffer Size available in all channels in group", HFILL } }, { &hf_mac_lte_control_buffer_size_1, { "Buffer Size 1", "mac-lte.control.buffer-size-1", FT_UINT8, BASE_DEC, VALS(buffer_size_vals), 0xfc, "Buffer Size available in for 1st channel in group", HFILL } }, { &hf_mac_lte_control_buffer_size_2, { "Buffer Size 2", "mac-lte.control.buffer-size-2", FT_UINT16, BASE_DEC, VALS(buffer_size_vals), 0x03c0, "Buffer Size available in for 2nd channel in group", HFILL } }, { &hf_mac_lte_control_buffer_size_3, { "Buffer Size 3", "mac-lte.control.buffer-size-3", FT_UINT16, BASE_DEC, VALS(buffer_size_vals), 0x0fc0, "Buffer Size available in for 3rd channel in group", HFILL } }, { &hf_mac_lte_control_buffer_size_4, { "Buffer Size 4", "mac-lte.control.buffer-size-4", FT_UINT8, BASE_DEC, VALS(buffer_size_vals), 0x3f, "Buffer Size available in for 4th channel in group", HFILL } }, { &hf_mac_lte_control_crnti, { "C-RNTI", "mac-lte.control.crnti", FT_UINT16, BASE_DEC, 0, 0x0, "C-RNTI for the UE", HFILL } }, { &hf_mac_lte_control_timing_advance, { "Timing Advance", "mac-lte.control.timing-advance", FT_UINT8, BASE_DEC, 0, 0x0, "Timing Advance (TODO: units)", HFILL } }, { &hf_mac_lte_control_ue_contention_resolution_identity, { "UE Contention Resoluation Identity", "mac-lte.control.ue-contention-resoluation-identity", FT_BYTES, BASE_HEX, 0, 0x0, "UE Contention Resoluation Identity", HFILL } }, { &hf_mac_lte_control_power_headroom_reserved, { "Reserved", "mac-lte.control.power-headroom.reserved", FT_UINT8, BASE_DEC, 0, 0xc0, "Reserved bits, should be 0", HFILL } }, { &hf_mac_lte_control_power_headroom, { "Power Headroom", "mac-lte.control.power-headroom", FT_UINT8, BASE_DEC, 0, 0x3f, "Power Headroom", HFILL } }, { &hf_mac_lte_control_padding, { "Padding", "mac-lte.control.padding", FT_NONE, BASE_NONE, 0, 0x0, "Padding", HFILL } }, }; static gint *ett[] = { &ett_mac_lte, &ett_mac_lte_context, &ett_mac_lte_rar_headers, &ett_mac_lte_rar_header, &ett_mac_lte_rar_body, &ett_mac_lte_ulsch_header, &ett_mac_lte_dlsch_header, &ett_mac_lte_sch_subheader, &ett_mac_lte_bch, &ett_mac_lte_pch }; module_t *mac_lte_module; /* Register protocol. */ proto_mac_lte = proto_register_protocol("MAC-LTE", "MAC-LTE", "mac-lte"); proto_register_field_array(proto_mac_lte, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); /* Allow other dissectors to find this one by name. */ register_dissector("mac-lte", dissect_mac_lte, proto_mac_lte); /* Register the tap name */ mac_lte_tap = register_tap("mac-lte"); /* Preferences */ mac_lte_module = prefs_register_protocol(proto_mac_lte, NULL); /* TODO: delete/obselete this preference? */ prefs_register_bool_preference(mac_lte_module, "single_rar", "Expect single RAR bodies", "When dissecting an RA_RNTI frame, expect to find only one RAR body " "instead of a complete RAR PDU complete with headers", &global_mac_lte_single_rar); prefs_register_bool_preference(mac_lte_module, "check_reserved_bits", "Warn if reserved bits are not 0", "When set, an expert warning will indicate if reserved bits are not zero", &global_mac_lte_check_reserved_bits); prefs_register_uint_preference(mac_lte_module, "retx_count_warn", "Number of Re-Transmits before expert warning triggered", "Number of Re-Transmits before expert warning triggered", 10, &global_mac_lte_retx_counter_trigger); }