/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include "packet-mac-lte.h" #include "packet-rlc-lte.h" /* Described in: * 3GPP TS 36.321 Evolved Universal Terrestrial Radio Access (E-UTRA) * Medium Access Control (MAC) protocol specification (Release 10) */ /* 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 = -1; 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_sysframe_number = -1; static int hf_mac_lte_context_subframe_number = -1; static int hf_mac_lte_context_grant_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_ul_grant_size = -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_retx_reason = -1; static int hf_mac_lte_context_crc_status = -1; static int hf_mac_lte_context_rapid = -1; static int hf_mac_lte_context_rach_attempt_number = -1; /* Inferred context */ static int hf_mac_lte_ues_ul_per_tti = -1; static int hf_mac_lte_ues_dl_per_tti = -1; /* Extra PHY context */ static int hf_mac_lte_context_phy_ul = -1; static int hf_mac_lte_context_phy_ul_modulation_type = -1; static int hf_mac_lte_context_phy_ul_tbs_index = -1; static int hf_mac_lte_context_phy_ul_resource_block_length = -1; static int hf_mac_lte_context_phy_ul_resource_block_start = -1; static int hf_mac_lte_context_phy_ul_harq_id = -1; static int hf_mac_lte_context_phy_ul_ndi = -1; static int hf_mac_lte_context_phy_dl = -1; static int hf_mac_lte_context_phy_dl_dci_format = -1; static int hf_mac_lte_context_phy_dl_resource_allocation_type = -1; static int hf_mac_lte_context_phy_dl_aggregation_level = -1; static int hf_mac_lte_context_phy_dl_mcs_index = -1; static int hf_mac_lte_context_phy_dl_redundancy_version_index = -1; static int hf_mac_lte_context_phy_dl_retx = -1; static int hf_mac_lte_context_phy_dl_resource_block_length = -1; static int hf_mac_lte_context_phy_dl_crc_status = -1; static int hf_mac_lte_context_phy_dl_harq_id = -1; static int hf_mac_lte_context_phy_dl_ndi = -1; static int hf_mac_lte_context_phy_dl_tb = -1; /* Out-of-band events */ static int hf_mac_lte_oob_send_preamble = -1; static int hf_mac_lte_oob_send_sr = -1; static int hf_mac_lte_number_of_srs = -1; static int hf_mac_lte_oob_sr_failure = -1; /* MAC SCH/MCH header fields */ static int hf_mac_lte_ulsch = -1; static int hf_mac_lte_ulsch_header = -1; static int hf_mac_lte_dlsch = -1; static int hf_mac_lte_dlsch_header = -1; static int hf_mac_lte_sch_subheader = -1; static int hf_mac_lte_mch = -1; static int hf_mac_lte_mch_header = -1; static int hf_mac_lte_mch_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_mch_reserved = -1; static int hf_mac_lte_mch_lcid = -1; static int hf_mac_lte_mch_extended = -1; static int hf_mac_lte_mch_format = -1; static int hf_mac_lte_mch_length = -1; static int hf_mac_lte_sch_header_only = -1; static int hf_mac_lte_mch_header_only = -1; /* Data */ static int hf_mac_lte_sch_sdu = -1; static int hf_mac_lte_mch_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_raw_pdu = -1; static int hf_mac_lte_padding_data = -1; static int hf_mac_lte_padding_length = -1; /* RAR fields */ static int hf_mac_lte_rar = -1; 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_ul_grant_hopping = -1; static int hf_mac_lte_rar_ul_grant_fsrba = -1; static int hf_mac_lte_rar_ul_grant_tmcs = -1; static int hf_mac_lte_rar_ul_grant_tcsp = -1; static int hf_mac_lte_rar_ul_grant_ul_delay = -1; static int hf_mac_lte_rar_ul_grant_cqi_request = -1; static int hf_mac_lte_rar_temporary_crnti = -1; /* Common channel control values */ static int hf_mac_lte_control_bsr = -1; static int hf_mac_lte_control_bsr_lcg_id = -1; static int hf_mac_lte_control_short_bsr_buffer_size = -1; static int hf_mac_lte_control_long_bsr_buffer_size_0 = -1; static int hf_mac_lte_control_long_bsr_buffer_size_1 = -1; static int hf_mac_lte_control_long_bsr_buffer_size_2 = -1; static int hf_mac_lte_control_long_bsr_buffer_size_3 = -1; static int hf_mac_lte_control_short_ext_bsr_buffer_size = -1; static int hf_mac_lte_control_long_ext_bsr_buffer_size_0 = -1; static int hf_mac_lte_control_long_ext_bsr_buffer_size_1 = -1; static int hf_mac_lte_control_long_ext_bsr_buffer_size_2 = -1; static int hf_mac_lte_control_long_ext_bsr_buffer_size_3 = -1; static int hf_mac_lte_control_crnti = -1; static int hf_mac_lte_control_timing_advance = -1; static int hf_mac_lte_control_timing_advance_reserved = -1; static int hf_mac_lte_control_ue_contention_resolution = -1; static int hf_mac_lte_control_ue_contention_resolution_identity = -1; static int hf_mac_lte_control_ue_contention_resolution_msg3 = -1; static int hf_mac_lte_control_ue_contention_resolution_msg3_matched = -1; static int hf_mac_lte_control_ue_contention_resolution_time_since_msg3 = -1; static int hf_mac_lte_control_power_headroom = -1; static int hf_mac_lte_control_power_headroom_reserved = -1; static int hf_mac_lte_control_power_headroom_level = -1; static int hf_mac_lte_control_ext_power_headroom = -1; static int hf_mac_lte_control_ext_power_headroom_c7 = -1; static int hf_mac_lte_control_ext_power_headroom_c6 = -1; static int hf_mac_lte_control_ext_power_headroom_c5 = -1; static int hf_mac_lte_control_ext_power_headroom_c4 = -1; static int hf_mac_lte_control_ext_power_headroom_c3 = -1; static int hf_mac_lte_control_ext_power_headroom_c2 = -1; static int hf_mac_lte_control_ext_power_headroom_c1 = -1; static int hf_mac_lte_control_ext_power_headroom_reserved = -1; static int hf_mac_lte_control_ext_power_headroom_power_backoff = -1; static int hf_mac_lte_control_ext_power_headroom_value = -1; static int hf_mac_lte_control_ext_power_headroom_level = -1; static int hf_mac_lte_control_ext_power_headroom_reserved2 = -1; static int hf_mac_lte_control_ext_power_headroom_pcmaxc = -1; static int hf_mac_lte_control_activation_deactivation = -1; static int hf_mac_lte_control_activation_deactivation_c7 = -1; static int hf_mac_lte_control_activation_deactivation_c6 = -1; static int hf_mac_lte_control_activation_deactivation_c5 = -1; static int hf_mac_lte_control_activation_deactivation_c4 = -1; static int hf_mac_lte_control_activation_deactivation_c3 = -1; static int hf_mac_lte_control_activation_deactivation_c2 = -1; static int hf_mac_lte_control_activation_deactivation_c1 = -1; static int hf_mac_lte_control_activation_deactivation_reserved = -1; static int hf_mac_lte_control_mch_scheduling_info = -1; static int hf_mac_lte_control_mch_scheduling_info_lcid = -1; static int hf_mac_lte_control_mch_scheduling_info_stop_mtch = -1; static int hf_mac_lte_dl_harq_resend_original_frame = -1; static int hf_mac_lte_dl_harq_resend_time_since_previous_frame = -1; static int hf_mac_lte_dl_harq_resend_next_frame = -1; static int hf_mac_lte_dl_harq_resend_time_until_next_frame = -1; static int hf_mac_lte_ul_harq_resend_original_frame = -1; static int hf_mac_lte_ul_harq_resend_time_since_previous_frame = -1; static int hf_mac_lte_ul_harq_resend_next_frame = -1; static int hf_mac_lte_ul_harq_resend_time_until_next_frame = -1; static int hf_mac_lte_grant_answering_sr = -1; static int hf_mac_lte_failure_answering_sr = -1; static int hf_mac_lte_sr_leading_to_failure = -1; static int hf_mac_lte_sr_leading_to_grant = -1; static int hf_mac_lte_sr_invalid_event = -1; static int hf_mac_lte_sr_time_since_request = -1; static int hf_mac_lte_sr_time_until_answer = -1; /* Subtrees. */ static int ett_mac_lte = -1; static int ett_mac_lte_context = -1; static int ett_mac_lte_phy_context = -1; static int ett_mac_lte_ulsch_header = -1; static int ett_mac_lte_dlsch_header = -1; static int ett_mac_lte_mch_header = -1; static int ett_mac_lte_sch_subheader = -1; static int ett_mac_lte_mch_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_rar_ul_grant = -1; static int ett_mac_lte_bsr = -1; static int ett_mac_lte_bch = -1; static int ett_mac_lte_pch = -1; static int ett_mac_lte_activation_deactivation = -1; static int ett_mac_lte_contention_resolution = -1; static int ett_mac_lte_power_headroom = -1; static int ett_mac_lte_extended_power_headroom = -1; static int ett_mac_lte_extended_power_headroom_cell = -1; static int ett_mac_lte_mch_scheduling_info = -1; static int ett_mac_lte_oob = -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"}, { SPS_RNTI, "SPS-RNTI"}, { M_RNTI, "M-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[] = { { crc_success, "OK"}, { crc_fail, "Failed"}, { crc_high_code_rate, "High Code Rate"}, { crc_pdsch_lost, "PDSCH Lost"}, { crc_duplicate_nonzero_rv, "Duplicate_nonzero_rv"}, { 0, NULL } }; static const value_string dci_format_vals[] = { { 0, "0"}, { 1, "1"}, { 2, "1A"}, { 3, "1B"}, { 4, "1C"}, { 5, "1D"}, { 6, "2"}, { 7, "2A"}, { 8, "3/3A"}, { 0, NULL } }; static const value_string aggregation_level_vals[] = { { 0, "1"}, { 1, "2"}, { 2, "4"}, { 3, "8"}, { 0, NULL } }; static const value_string modulation_type_vals[] = { { 2, "QPSK"}, { 4, "QAM16"}, { 6, "QAM64"}, { 0, NULL } }; static const true_false_string mac_lte_scell_ph_vals = { "Reported", "Not reported" }; static const true_false_string mac_lte_power_backoff_vals = { "Applied", "Not applied" }; static const true_false_string mac_lte_ph_value_vals = { "Based on reference format", "Based on real transmission" }; static const true_false_string mac_lte_scell_status_vals = { "Activated", "Deactivated" }; #define ACTIVATION_DEACTIVATION_LCID 0x1b #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"}, { ACTIVATION_DEACTIVATION_LCID , "Activation/Deactivation"}, { 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 EXTENDED_POWER_HEADROOM_REPORT_LCID 0x19 #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"}, { EXTENDED_POWER_HEADROOM_REPORT_LCID, "Extended Power Headroom Report"}, { 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 } }; #define MCH_SCHEDULING_INFO_LCID 0x1e static const value_string mch_lcid_vals[] = { { 0, "MCCH"}, { 1, "1"}, { 2, "2"}, { 3, "3"}, { 4, "4"}, { 5, "5"}, { 6, "6"}, { 7, "7"}, { 8, "8"}, { 9, "9"}, { 10, "10"}, { 11, "11"}, { 12, "12"}, { 13, "13"}, { 14, "14"}, { 15, "15"}, { 16, "16"}, { 17, "17"}, { 18, "18"}, { 19, "19"}, { 20, "20"}, { 21, "21"}, { 22, "22"}, { 23, "23"}, { 24, "24"}, { 25, "25"}, { 26, "26"}, { 27, "27"}, { 28, "28"}, { MCH_SCHEDULING_INFO_LCID, "MCH Scheduling Information"}, { 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 value_string_ext buffer_size_vals_ext = VALUE_STRING_EXT_INIT(buffer_size_vals); static const value_string ext_buffer_size_vals[] = { { 0, "BS = 0"}, { 1, "0 < BS <= 10"}, { 2, "10 < BS <= 13"}, { 3, "13 < BS <= 16"}, { 4, "16 < BS <= 19"}, { 5, "19 < BS <= 23"}, { 6, "23 < BS <= 29"}, { 7, "29 < BS <= 35"}, { 8, "35 < BS <= 43"}, { 9, "43 < BS <= 53"}, { 10, "53 < BS <= 65"}, { 11, "65 < BS <= 80"}, { 12, "80 < BS <= 98"}, { 13, "98 < BS <= 120"}, { 14, "120 < BS <= 147"}, { 15, "147 < BS <= 181"}, { 16, "181 < BS <= 223"}, { 17, "223 < BS <= 274"}, { 18, "274 < BS <= 337"}, { 19, "337 < BS <= 414"}, { 20, "414 < BS <= 509"}, { 21, "509 < BS <= 625"}, { 22, "625 < BS <= 769"}, { 23, "769 < BS <= 945"}, { 24, "945 < BS <= 1162"}, { 25, "1162 < BS <= 1429"}, { 26, "1429 < BS <= 1757"}, { 27, "1757 < BS <= 2161"}, { 28, "2161 < BS <= 2657"}, { 29, "2657 < BS <= 3267"}, { 30, "3267 < BS <= 4017"}, { 31, "4017 < BS <= 4940"}, { 32, "4940 < BS <= 6074"}, { 33, "6074 < BS <= 7469"}, { 34, "7469 < BS <= 9185"}, { 35, "9185 < BS <= 11294"}, { 36, "11294 < BS <= 13888"}, { 37, "13888 < BS <= 17077"}, { 38, "17077 < BS <= 20999"}, { 39, "20999 < BS <= 25822"}, { 40, "25822 < BS <= 31752"}, { 41, "31752 < BS <= 39045"}, { 42, "39045 < BS <= 48012"}, { 43, "48012 < BS <= 59039"}, { 44, "59039 < BS <= 72598"}, { 45, "72598 < BS <= 89272"}, { 46, "89272 < BS <= 109774"}, { 47, "109774 < BS <= 134986"}, { 48, "134986 < BS <= 165989"}, { 49, "165989 < BS <= 204111"}, { 50, "204111 < BS <= 250990"}, { 51, "250990 < BS <= 308634"}, { 52, "308634 < BS <= 379519"}, { 53, "379519 < BS <= 466683"}, { 54, "466683 < BS <= 573866"}, { 55, "573866 < BS <= 705666"}, { 56, "705666 < BS <= 867737"}, { 57, "867737 < BS <= 1067031"}, { 58, "1067031 < BS <= 1312097"}, { 59, "1312097 < BS <= 1613447"}, { 60, "1613447 < BS <= 1984009"}, { 61, "1984009 < BS <= 2439678"}, { 62, "2439678 < BS <= 3000000"}, { 63, "BS > 3000000"}, { 0, NULL } }; static value_string_ext ext_buffer_size_vals_ext = VALUE_STRING_EXT_INIT(ext_buffer_size_vals); static const value_string power_headroom_vals[] = { { 0, "-23 <= PH < -22"}, { 1, "-22 <= PH < -21"}, { 2, "-21 <= PH < -20"}, { 3, "-20 <= PH < -19"}, { 4, "-19 <= PH < -18"}, { 5, "-18 <= PH < -17"}, { 6, "-17 <= PH < -16"}, { 7, "-16 <= PH < -15"}, { 8, "-15 <= PH < -14"}, { 9, "-14 <= PH < -13"}, { 10, "-13 <= PH < -12"}, { 11, "-12 <= PH < -11"}, { 12, "-11 <= PH < -10"}, { 13, "-10 <= PH < -9"}, { 14, "-9 <= PH < -8"}, { 15, "-8 <= PH < -7"}, { 16, "-7 <= PH < -6"}, { 17, "-6 <= PH < -5"}, { 18, "-5 <= PH < -4"}, { 19, "-4 <= PH < -3"}, { 20, "-3 <= PH < -2"}, { 21, "-2 <= PH < -1"}, { 22, "-1 <= PH < 0"}, { 23, "0 <= PH < 1"}, { 24, "1 <= PH < 2"}, { 25, "2 <= PH < 3"}, { 26, "3 <= PH < 4"}, { 27, "4 <= PH < 5"}, { 28, "5 <= PH < 6"}, { 29, "6 <= PH < 7"}, { 30, "7 <= PH < 8"}, { 31, "8 <= PH < 9"}, { 32, "9 <= PH < 10"}, { 33, "10 <= PH < 11"}, { 34, "11 <= PH < 12"}, { 35, "12 <= PH < 13"}, { 36, "13 <= PH < 14"}, { 37, "14 <= PH < 15"}, { 38, "15 <= PH < 16"}, { 39, "16 <= PH < 17"}, { 40, "17 <= PH < 18"}, { 41, "18 <= PH < 19"}, { 42, "19 <= PH < 20"}, { 43, "20 <= PH < 21"}, { 44, "21 <= PH < 22"}, { 45, "22 <= PH < 23"}, { 46, "23 <= PH < 24"}, { 47, "24 <= PH < 25"}, { 48, "25 <= PH < 26"}, { 49, "26 <= PH < 27"}, { 50, "27 <= PH < 28"}, { 51, "28 <= PH < 29"}, { 52, "29 <= PH < 30"}, { 53, "30 <= PH < 31"}, { 54, "31 <= PH < 32"}, { 55, "32 <= PH < 33"}, { 56, "33 <= PH < 34"}, { 57, "34 <= PH < 35"}, { 58, "34 <= PH < 36"}, { 59, "36 <= PH < 37"}, { 60, "37 <= PH < 38"}, { 61, "38 <= PH < 39"}, { 62, "39 <= PH < 40"}, { 63, "PH >= 40"}, { 0, NULL } }; static value_string_ext power_headroom_vals_ext = VALUE_STRING_EXT_INIT(power_headroom_vals); static const value_string pcmaxc_vals[] = { { 0, "Pcmax,c < -29"}, { 1, "-29 <= Pcmax,c < -28"}, { 2, "-28 <= Pcmax,c < -27"}, { 3, "-27 <= Pcmax,c < -26"}, { 4, "-26 <= Pcmax,c < -25"}, { 5, "-25 <= Pcmax,c < -24"}, { 6, "-24 <= Pcmax,c < -23"}, { 7, "-23 <= Pcmax,c < -22"}, { 8, "-22 <= Pcmax,c < -21"}, { 9, "-21 <= Pcmax,c < -20"}, { 10, "-20 <= Pcmax,c < -19"}, { 11, "-19 <= Pcmax,c < -18"}, { 12, "-18 <= Pcmax,c < -17"}, { 13, "-17 <= Pcmax,c < -16"}, { 14, "-16 <= Pcmax,c < -15"}, { 15, "-15 <= Pcmax,c < -14"}, { 16, "-14 <= Pcmax,c < -13"}, { 17, "-13 <= Pcmax,c < -12"}, { 18, "-12 <= Pcmax,c < -11"}, { 19, "-11 <= Pcmax,c < -10"}, { 20, "-10 <= Pcmax,c < -9"}, { 21, "-9 <= Pcmax,c < -8"}, { 22, "-8 <= Pcmax,c < -7"}, { 23, "-7 <= Pcmax,c < -6"}, { 24, "-6 <= Pcmax,c < -5"}, { 25, "-5 <= Pcmax,c < -4"}, { 26, "-4 <= Pcmax,c < -3"}, { 27, "-3 <= Pcmax,c < -2"}, { 28, "-2 <= Pcmax,c < -1"}, { 29, "-1 <= Pcmax,c < 0"}, { 30, "0 <= Pcmax,c < 1"}, { 31, "1 <= Pcmax,c < 2"}, { 32, "2 <= Pcmax,c < 3"}, { 33, "3 <= Pcmax,c < 4"}, { 34, "4 <= Pcmax,c < 5"}, { 35, "5 <= Pcmax,c < 6"}, { 36, "6 <= Pcmax,c < 7"}, { 37, "7 <= Pcmax,c < 8"}, { 38, "8 <= Pcmax,c < 9"}, { 39, "9 <= Pcmax,c < 10"}, { 40, "10 <= Pcmax,c < 11"}, { 41, "11 <= Pcmax,c < 12"}, { 42, "12 <= Pcmax,c < 13"}, { 43, "13 <= Pcmax,c < 14"}, { 44, "14 <= Pcmax,c < 15"}, { 45, "15 <= Pcmax,c < 16"}, { 46, "16 <= Pcmax,c < 17"}, { 47, "17 <= Pcmax,c < 18"}, { 48, "18 <= Pcmax,c < 19"}, { 49, "19 <= Pcmax,c < 20"}, { 50, "20 <= Pcmax,c < 21"}, { 51, "21 <= Pcmax,c < 22"}, { 52, "22 <= Pcmax,c < 23"}, { 53, "23 <= Pcmax,c < 24"}, { 54, "24 <= Pcmax,c < 25"}, { 55, "25 <= Pcmax,c < 26"}, { 56, "26 <= Pcmax,c < 27"}, { 57, "27 <= Pcmax,c < 28"}, { 58, "28 <= Pcmax,c < 29"}, { 59, "29 <= Pcmax,c < 30"}, { 60, "30 <= Pcmax,c < 31"}, { 61, "31 <= Pcmax,c < 32"}, { 62, "32 <= Pcmax,c < 33"}, { 63, "33 <= Pcmax,c"}, { 0, NULL } }; static value_string_ext pcmaxc_vals_ext = VALUE_STRING_EXT_INIT(pcmaxc_vals); 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 } }; static const value_string ul_retx_grant_vals[] = { { 0, "PDCCH ReTx"}, { 1, "PHICH NACK"}, { 0, NULL } }; /**************************************************************************/ /* Preferences state */ /**************************************************************************/ /* 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; /* By default try to decode transparent data (BCH, PCH and CCCH) data using LTE RRC dissector */ static gboolean global_mac_lte_attempt_rrc_decode = TRUE; /* Whether should attempt to dissect frames failing CRC check */ static gboolean global_mac_lte_dissect_crc_failures = FALSE; /* Whether should attempt to decode lcid 1&2 SDUs as srb1/2 (i.e. AM RLC) */ static gboolean global_mac_lte_attempt_srb_decode = TRUE; /* Where to take LCID -> DRB mappings from */ enum lcid_drb_source { FromStaticTable, FromConfigurationProtocol }; static gint global_mac_lte_lcid_drb_source = (gint)FromStaticTable; /* Threshold for warning in expert info about high BSR values */ static gint global_mac_lte_bsr_warn_threshold = 50; /* default is 19325 -> 22624 */ /* Whether or not to track SRs and related frames */ static gboolean global_mac_lte_track_sr = TRUE; /* Which layer info to show in the info column */ enum layer_to_show { ShowPHYLayer, ShowMACLayer, ShowRLCLayer }; /* Which layer's details to show in Info column */ static gint global_mac_lte_layer_to_show = (gint)ShowRLCLayer; /* When showing RLC info, count PDUs so can append info column properly */ static guint8 s_number_of_rlc_pdus_shown = 0; /***********************************************************************/ /* How to dissect lcid 3-10 (presume drb logical channels) */ static const value_string drb_lcid_vals[] = { { 3, "LCID 3"}, { 4, "LCID 4"}, { 5, "LCID 5"}, { 6, "LCID 6"}, { 7, "LCID 7"}, { 8, "LCID 8"}, { 9, "LCID 9"}, { 10, "LCID 10"}, { 0, NULL } }; typedef enum rlc_channel_type_t { rlcRaw, rlcTM, rlcUM5, rlcUM10, rlcAM } rlc_channel_type_t; static const value_string rlc_channel_type_vals[] = { { rlcTM, "TM"}, { rlcUM5 , "UM, SN Len=5"}, { rlcUM10, "UM, SN Len=10"}, { rlcAM , "AM"}, { 0, NULL } }; /* Mapping type */ typedef struct lcid_drb_mapping_t { guint16 lcid; gint drbid; rlc_channel_type_t channel_type; } lcid_drb_mapping_t; /* Mapping entity */ static lcid_drb_mapping_t *lcid_drb_mappings = NULL; static guint num_lcid_drb_mappings = 0; UAT_VS_DEF(lcid_drb_mappings, lcid, lcid_drb_mapping_t, 3, "LCID 3") UAT_DEC_CB_DEF(lcid_drb_mappings, drbid, lcid_drb_mapping_t) UAT_VS_DEF(lcid_drb_mappings, channel_type, lcid_drb_mapping_t, 2, "AM") /* UAT object */ static uat_t* lcid_drb_mappings_uat; /* Dynamic mappings (set by configuration protocol) LCID is the index into the array of these */ typedef struct dynamic_lcid_drb_mapping_t { gboolean valid; gint drbid; rlc_channel_type_t channel_type; guint8 ul_priority; } dynamic_lcid_drb_mapping_t; static dynamic_lcid_drb_mapping_t dynamic_lcid_drb_mapping[11]; extern int proto_rlc_lte; /***************************************************************/ /***************************************************************/ /* Keeping track of Msg3 bodies so they can be compared with */ /* Contention Resolution bodies. */ typedef struct Msg3Data { guint8 data[6]; nstime_t msg3Time; guint32 framenum; } Msg3Data; /* This table stores (RNTI -> Msg3Data*). Will be populated when Msg3 frames are first read. */ static GHashTable *mac_lte_msg3_hash = NULL; /* Hash table functions for mac_lte_msg3_hash. Hash is just the (RNTI) key */ static gint mac_lte_rnti_hash_equal(gconstpointer v, gconstpointer v2) { return (v == v2); } static guint mac_lte_rnti_hash_func(gconstpointer v) { return GPOINTER_TO_UINT(v); } typedef enum ContentionResolutionStatus { NoMsg3, Msg3Match, Msg3NoMatch } ContentionResolutionStatus; typedef struct ContentionResolutionResult { ContentionResolutionStatus status; guint msg3FrameNum; guint msSinceMsg3; } ContentionResolutionResult; /* This table stores (CRFrameNum -> CRResult). It is assigned during the first pass and used thereafter */ static GHashTable *mac_lte_cr_result_hash = NULL; /* Hash table functions for mac_lte_cr_result_hash. Hash is just the (framenum) key */ static gint mac_lte_framenum_hash_equal(gconstpointer v, gconstpointer v2) { return (v == v2); } static guint mac_lte_framenum_hash_func(gconstpointer v) { return GPOINTER_TO_UINT(v); } /**************************************************************************/ /****************************************************************/ /* Keeping track of last DL frames per C-RNTI so can guess when */ /* there has been a HARQ retransmission */ /* TODO: this should be simplified now that harq-id & ndi are */ /* being logged! */ /* Could be bigger, but more than enough to flag suspected resends */ #define MAX_EXPECTED_PDU_LENGTH 2048 typedef struct LastFrameData { gboolean inUse; guint32 framenum; gboolean ndi; nstime_t received_time; gint length; guint8 data[MAX_EXPECTED_PDU_LENGTH]; } LastFrameData; typedef struct DLHarqBuffers { LastFrameData harqid[2][15]; /* 2 blocks (1 for each antenna) needed for DL */ } DLHarqBuffers; /* This table stores (RNTI -> DLHARQBuffers*). Will be populated when DL frames are first read. */ static GHashTable *mac_lte_dl_harq_hash = NULL; typedef struct DLHARQResult { gboolean previousSet, nextSet; guint previousFrameNum; guint timeSincePreviousFrame; guint nextFrameNum; guint timeToNextFrame; } DLHARQResult; /* This table stores (FrameNumber -> *DLHARQResult). It is assigned during the first pass and used thereafter */ static GHashTable *mac_lte_dl_harq_result_hash = NULL; /**************************************************************************/ /*****************************************************************/ /* Keeping track of last UL frames per C-RNTI so can verify when */ /* told that a frame is a retx */ typedef struct ULHarqBuffers { LastFrameData harqid[8]; } ULHarqBuffers; /* This table stores (RNTI -> ULHarqBuffers*). Will be populated when UL frames are first read. */ static GHashTable *mac_lte_ul_harq_hash = NULL; typedef struct ULHARQResult { gboolean previousSet, nextSet; guint previousFrameNum; guint timeSincePreviousFrame; guint nextFrameNum; guint timeToNextFrame; } ULHARQResult; /* This table stores (FrameNum -> ULHARQResult). It is assigned during the first pass and used thereafter */ static GHashTable *mac_lte_ul_harq_result_hash = NULL; /**************************************************************************/ /**************************************************************************/ /* Tracking of Scheduling Requests (SRs). */ /* Keep track of: */ /* - last grant before SR */ /* - SR failures following request */ /* - grant following SR */ typedef enum SREvent { SR_Grant, SR_Request, SR_Failure } SREvent; static const value_string sr_event_vals[] = { { SR_Grant, "Grant"}, { SR_Request, "SR Request"}, { SR_Failure, "SR Failure"}, { 0, NULL} }; typedef enum SRStatus { None, SR_Outstanding, SR_Failed } SRStatus; static const value_string sr_status_vals[] = { { None, "Receiving grants"}, { SR_Outstanding, "SR Request outstanding"}, { SR_Failed, "SR has Failed"}, { 0, NULL} }; typedef struct SRState { SRStatus status; guint32 lastSRFramenum; guint32 lastGrantFramenum; nstime_t requestTime; } SRState; /* This table keeps track of the SR state for each UE. (RNTI -> SRState) */ static GHashTable *mac_lte_ue_sr_state = NULL; typedef enum SRResultType { GrantAnsweringSR, FailureAnsweringSR, SRLeadingToGrant, SRLeadingToFailure, InvalidSREvent } SRResultType; typedef struct SRResult { SRResultType type; guint32 frameNum; guint32 timeDifference; /* These 2 are only used with InvalidSREvent */ SRStatus status; SREvent event; } SRResult; /* Entries in this table are created during the first pass It maps (SRFrameNum -> SRResult) */ static GHashTable *mac_lte_sr_request_hash = NULL; /**************************************************************************/ /* Forward declarations */ void proto_reg_handoff_mac_lte(void); void dissect_mac_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); static guint8 get_mac_lte_channel_priority(guint16 ueid _U_, guint8 lcid, guint8 direction); /* Heuristic dissection */ static gboolean global_mac_lte_heur = FALSE; static void call_with_catch_all(dissector_handle_t handle, tvbuff_t* tvb, packet_info *pinfo, proto_tree *tree) { /* Call it (catch exceptions so that stats will be updated) */ TRY { call_dissector_only(handle, tvb, pinfo, tree); } CATCH_ALL { } ENDTRY } /* Dissect context fields in the format described in packet-mac-lte.h. Return TRUE if the necessary information was successfully found */ gboolean dissect_mac_lte_context_fields(struct mac_lte_info *p_mac_lte_info, tvbuff_t *tvb, gint *p_offset) { gint offset = *p_offset; guint8 tag = 0; /* Read fixed fields */ p_mac_lte_info->radioType = tvb_get_guint8(tvb, offset++); p_mac_lte_info->direction = tvb_get_guint8(tvb, offset++); /* TODO: currently no support for detailed PHY info... */ if (p_mac_lte_info->direction == DIRECTION_UPLINK) { p_mac_lte_info->detailed_phy_info.ul_info.present = FALSE; } else { p_mac_lte_info->detailed_phy_info.dl_info.present = FALSE; } p_mac_lte_info->rntiType = tvb_get_guint8(tvb, offset++); /* Initialize RNTI with a default value in case optional field is not present */ switch (p_mac_lte_info->rntiType) { case M_RNTI: p_mac_lte_info->rnti = 0xFFFD; break; case P_RNTI: p_mac_lte_info->rnti = 0xFFFE; break; case SI_RNTI: p_mac_lte_info->rnti = 0xFFFF; break; case RA_RNTI: case C_RNTI: case SPS_RNTI: p_mac_lte_info->rnti = 0x0001; break; default: break; } /* Read optional fields */ while (tag != MAC_LTE_PAYLOAD_TAG) { /* Process next tag */ tag = tvb_get_guint8(tvb, offset++); switch (tag) { case MAC_LTE_RNTI_TAG: p_mac_lte_info->rnti = tvb_get_ntohs(tvb, offset); offset += 2; break; case MAC_LTE_UEID_TAG: p_mac_lte_info->ueid = tvb_get_ntohs(tvb, offset); offset += 2; break; case MAC_LTE_SUBFRAME_TAG: p_mac_lte_info->subframeNumber = tvb_get_ntohs(tvb, offset); offset += 2; break; case MAC_LTE_PREDEFINED_DATA_TAG: p_mac_lte_info->isPredefinedData = tvb_get_guint8(tvb, offset); offset++; break; case MAC_LTE_RETX_TAG: p_mac_lte_info->reTxCount = tvb_get_guint8(tvb, offset); offset++; break; case MAC_LTE_CRC_STATUS_TAG: p_mac_lte_info->crcStatusValid = TRUE; p_mac_lte_info->detailed_phy_info.dl_info.crc_status = tvb_get_guint8(tvb, offset); offset++; break; case MAC_LTE_EXT_BSR_SIZES_TAG: p_mac_lte_info->isExtendedBSRSizes = TRUE; break; case MAC_LTE_PAYLOAD_TAG: /* Have reached data, so set payload length and get out of loop */ /* TODO: this is not correct if there is padding which isn't in frame */ p_mac_lte_info->length= tvb_length_remaining(tvb, offset); continue; default: /* It must be a recognised tag */ return FALSE; } } /* Pass out where offset is now */ *p_offset = offset; return TRUE; } /* Heuristic dissector looks for supported framing protocol (see wiki page) */ static gboolean dissect_mac_lte_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { gint offset = 0; struct mac_lte_info *p_mac_lte_info; tvbuff_t *mac_tvb; gboolean infoAlreadySet = FALSE; /* This is a heuristic dissector, which means we get all the UDP * traffic not sent to a known dissector and not claimed by * a heuristic dissector called before us! */ if (!global_mac_lte_heur) { return FALSE; } /* Do this again on re-dissection to re-discover offset of actual PDU */ /* Needs to be at least as long as: - the signature string - fixed header bytes - tag for data - at least one byte of MAC PDU payload */ if ((size_t)tvb_length_remaining(tvb, offset) < (strlen(MAC_LTE_START_STRING)+3+2)) { return FALSE; } /* OK, compare with signature string */ if (tvb_strneql(tvb, offset, MAC_LTE_START_STRING, strlen(MAC_LTE_START_STRING)) != 0) { return FALSE; } offset += (gint)strlen(MAC_LTE_START_STRING); /* If redissecting, use previous info struct (if available) */ p_mac_lte_info = p_get_proto_data(pinfo->fd, proto_mac_lte); if (p_mac_lte_info == NULL) { /* Allocate new info struct for this frame */ p_mac_lte_info = se_alloc0(sizeof(struct mac_lte_info)); infoAlreadySet = FALSE; } else { infoAlreadySet = TRUE; } /* Dissect the fields to populate p_mac_lte */ if (!dissect_mac_lte_context_fields(p_mac_lte_info, tvb, &offset)) { return FALSE; } if (!infoAlreadySet) { /* Store info in packet */ p_add_proto_data(pinfo->fd, proto_mac_lte, p_mac_lte_info); } /**************************************/ /* OK, now dissect as MAC LTE */ /* Create tvb that starts at actual MAC PDU */ mac_tvb = tvb_new_subset(tvb, offset, -1, tvb_reported_length(tvb)-offset); dissect_mac_lte(mac_tvb, pinfo, tree); return TRUE; } /* Write the given formatted text to: - the info column (if pinfo != NULL) - 1 or 2 other labels (optional) */ static void write_pdu_label_and_info(proto_item *ti1, proto_item *ti2, packet_info *pinfo, const char *format, ...) { #define MAX_INFO_BUFFER 256 static char info_buffer[MAX_INFO_BUFFER]; va_list ap; if ((ti1 == NULL) && (ti2 == NULL) && (pinfo == NULL)) { return; } va_start(ap, format); g_vsnprintf(info_buffer, MAX_INFO_BUFFER, format, ap); va_end(ap); /* Add to indicated places */ if (pinfo != NULL) { col_append_str(pinfo->cinfo, COL_INFO, info_buffer); } if (ti1 != NULL) { proto_item_append_text(ti1, "%s", info_buffer); } if (ti2 != NULL) { proto_item_append_text(ti2, "%s", info_buffer); } } /* Show extra PHY parameters (if present) */ static void show_extra_phy_parameters(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree, struct mac_lte_info *p_mac_lte_info) { proto_item *phy_ti; proto_tree *phy_tree; proto_item *ti; if (global_mac_lte_layer_to_show == ShowPHYLayer) { /* Clear the info column */ col_clear(pinfo->cinfo, COL_INFO); } if (p_mac_lte_info->direction == DIRECTION_UPLINK) { if (p_mac_lte_info->detailed_phy_info.ul_info.present) { /* Create root */ phy_ti = proto_tree_add_string_format(tree, hf_mac_lte_context_phy_ul, tvb, 0, 0, "", "UL PHY Context"); phy_tree = proto_item_add_subtree(phy_ti, ett_mac_lte_phy_context); PROTO_ITEM_SET_GENERATED(phy_ti); /* Add items */ ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_ul_modulation_type, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.ul_info.modulation_type); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_ul_tbs_index, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.ul_info.tbs_index); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_ul_resource_block_length, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.ul_info.resource_block_length); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_ul_resource_block_start, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.ul_info.resource_block_start); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_ul_harq_id, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.ul_info.harq_id); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_ul_ndi, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.ul_info.ndi); PROTO_ITEM_SET_GENERATED(ti); proto_item_append_text(phy_ti, " ("); write_pdu_label_and_info(phy_ti, NULL, (global_mac_lte_layer_to_show == ShowPHYLayer) ? pinfo : NULL, "UL: UEId=%u RNTI=%u %s Tbs_Index=%u RB_len=%u RB_start=%u", p_mac_lte_info->ueid, p_mac_lte_info->rnti, val_to_str_const(p_mac_lte_info->detailed_phy_info.ul_info.modulation_type, modulation_type_vals, "Unknown"), p_mac_lte_info->detailed_phy_info.ul_info.tbs_index, p_mac_lte_info->detailed_phy_info.ul_info.resource_block_length, p_mac_lte_info->detailed_phy_info.ul_info.resource_block_start); proto_item_append_text(phy_ti, ")"); /* Don't want columns to be replaced now */ if (global_mac_lte_layer_to_show == ShowPHYLayer) { col_set_writable(pinfo->cinfo, FALSE); } } } else { if (p_mac_lte_info->detailed_phy_info.dl_info.present) { /* Create root */ phy_ti = proto_tree_add_string_format(tree, hf_mac_lte_context_phy_dl, tvb, 0, 0, "", "DL PHY Context"); phy_tree = proto_item_add_subtree(phy_ti, ett_mac_lte_phy_context); PROTO_ITEM_SET_GENERATED(phy_ti); /* Add items */ ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_dl_dci_format, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.dl_info.dci_format); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_dl_resource_allocation_type, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.dl_info.resource_allocation_type); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_dl_aggregation_level, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.dl_info.aggregation_level); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_dl_mcs_index, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.dl_info.mcs_index); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_dl_redundancy_version_index, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.dl_info.redundancy_version_index); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_boolean(phy_tree, hf_mac_lte_context_phy_dl_retx, tvb, 0, 0, p_mac_lte_info->dl_retx); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_dl_resource_block_length, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.dl_info.resource_block_length); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_dl_crc_status, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.dl_info.crc_status); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_dl_harq_id, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.dl_info.harq_id); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_dl_ndi, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.dl_info.ndi); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(phy_tree, hf_mac_lte_context_phy_dl_tb, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.dl_info.transport_block); PROTO_ITEM_SET_GENERATED(ti); proto_item_append_text(phy_ti, " ("); write_pdu_label_and_info(phy_ti, NULL, (global_mac_lte_layer_to_show == ShowPHYLayer) ? pinfo : NULL, "DL: UEId=%u RNTI=%u DCI_Format=%s Res_Alloc=%u Aggr_Level=%s MCS=%u RV=%u " "Res_Block_len=%u CRC_status=%s HARQ_id=%u NDI=%u", p_mac_lte_info->ueid, p_mac_lte_info->rnti, val_to_str_const(p_mac_lte_info->detailed_phy_info.dl_info.dci_format, dci_format_vals, "Unknown"), p_mac_lte_info->detailed_phy_info.dl_info.resource_allocation_type, val_to_str_const(p_mac_lte_info->detailed_phy_info.dl_info.aggregation_level, aggregation_level_vals, "Unknown"), p_mac_lte_info->detailed_phy_info.dl_info.mcs_index, p_mac_lte_info->detailed_phy_info.dl_info.redundancy_version_index, p_mac_lte_info->detailed_phy_info.dl_info.resource_block_length, val_to_str_const(p_mac_lte_info->detailed_phy_info.dl_info.crc_status, crc_status_vals, "Unknown"), p_mac_lte_info->detailed_phy_info.dl_info.harq_id, p_mac_lte_info->detailed_phy_info.dl_info.ndi); proto_item_append_text(phy_ti, ")"); /* Don't want columns to be replaced now */ if (global_mac_lte_layer_to_show == ShowPHYLayer) { col_set_writable(pinfo->cinfo, FALSE); } } } } /* Dissect a single Random Access Reponse body */ static gint dissect_rar_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item *pdu_ti, gint offset, guint8 rapid) { guint8 reserved; guint start_body_offset = offset; proto_item *ti; proto_item *rar_body_ti; proto_tree *rar_body_tree; proto_tree *ul_grant_tree; proto_item *ul_grant_ti; 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, ENC_ASCII|ENC_NA); 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, ENC_BIG_ENDIAN); if (reserved != 0) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "RAR body Reserved bit not zero (found 0x%x)", reserved); } /* Timing Advance */ timing_advance = (tvb_get_ntohs(tvb, offset) & 0x7ff0) >> 4; ti = proto_tree_add_item(rar_body_tree, hf_mac_lte_rar_ta, tvb, offset, 2, ENC_BIG_ENDIAN); if (timing_advance != 0) { expert_add_info_format(pinfo, ti, PI_SEQUENCE, (timing_advance <= 31) ? PI_NOTE : PI_WARN, "RAR Timing advance not zero (%u)", timing_advance); } offset++; /* UL Grant */ ul_grant = (tvb_get_ntohl(tvb, offset) & 0x0fffff00) >> 8; ul_grant_ti = proto_tree_add_item(rar_body_tree, hf_mac_lte_rar_ul_grant, tvb, offset, 3, ENC_BIG_ENDIAN); /* Break these 20 bits down as described in 36.213, section 6.2 */ /* Create subtree for UL grant break-down */ ul_grant_tree = proto_item_add_subtree(ul_grant_ti, ett_mac_lte_rar_ul_grant); /* Hopping flag (1 bit) */ proto_tree_add_item(ul_grant_tree, hf_mac_lte_rar_ul_grant_hopping, tvb, offset, 1, ENC_BIG_ENDIAN); /* Fixed sized resource block assignment (10 bits) */ proto_tree_add_item(ul_grant_tree, hf_mac_lte_rar_ul_grant_fsrba, tvb, offset, 2, ENC_BIG_ENDIAN); /* Truncated Modulation and coding scheme (4 bits) */ proto_tree_add_item(ul_grant_tree, hf_mac_lte_rar_ul_grant_tmcs, tvb, offset+1, 2, ENC_BIG_ENDIAN); /* TPC command for scheduled PUSCH (3 bits) */ proto_tree_add_item(ul_grant_tree, hf_mac_lte_rar_ul_grant_tcsp, tvb, offset+2, 1, ENC_BIG_ENDIAN); /* UL delay (1 bit) */ proto_tree_add_item(ul_grant_tree, hf_mac_lte_rar_ul_grant_ul_delay, tvb, offset+2, 1, ENC_BIG_ENDIAN); /* CQI request (1 bit) */ proto_tree_add_item(ul_grant_tree, hf_mac_lte_rar_ul_grant_cqi_request, tvb, offset+2, 1, ENC_BIG_ENDIAN); 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, ENC_BIG_ENDIAN); offset += 2; write_pdu_label_and_info(pdu_ti, rar_body_ti, pinfo, "(RAPID=%u: TA=%u, UL-Grant=%u, Temp C-RNTI=%u) ", rapid, timing_advance, ul_grant, temp_crnti); proto_item_set_len(rar_body_ti, offset-start_body_offset); return offset; } #define MAX_RAR_PDUS 64 /* Dissect Random Access Reponse (RAR) PDU */ static void dissect_rar(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item *pdu_ti, gint offset, mac_lte_info *p_mac_lte_info, mac_lte_tap_info *tap_info) { gint number_of_rars = 0; /* No of RAR bodies expected following headers */ guint8 *rapids = ep_alloc(MAX_RAR_PDUS * sizeof(guint8)); gboolean backoff_indicator_seen = FALSE; guint8 backoff_indicator = 0; guint8 extension; gint n; proto_tree *rar_headers_tree; proto_item *ti; proto_item *rar_headers_ti; proto_item *padding_length_ti; int start_headers_offset = offset; write_pdu_label_and_info(pdu_ti, NULL, pinfo, "RAR (RA-RNTI=%u, SF=%u) ", p_mac_lte_info->rnti, p_mac_lte_info->subframeNumber); /* Create hidden 'virtual root' so can filter on mac-lte.rar */ ti = proto_tree_add_item(tree, hf_mac_lte_rar, tvb, offset, -1, ENC_NA); PROTO_ITEM_SET_HIDDEN(ti); /* Create headers tree */ rar_headers_ti = proto_tree_add_item(tree, hf_mac_lte_rar_headers, tvb, offset, 0, ENC_ASCII|ENC_NA); 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, ENC_ASCII|ENC_NA); 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, ENC_BIG_ENDIAN); /* Type */ type_value = (first_byte & 0x40) >> 6; proto_tree_add_item(rar_header_tree, hf_mac_lte_rar_t, tvb, offset, 1, ENC_BIG_ENDIAN); if (type_value == 0) { /* Backoff Indicator (BI) case */ guint8 reserved; proto_item *tii; proto_item *bi_ti; /* 2 Reserved bits */ reserved = (tvb_get_guint8(tvb, offset) & 0x30) >> 4; tii = proto_tree_add_item(rar_header_tree, hf_mac_lte_rar_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); if (reserved != 0) { expert_add_info_format(pinfo, tii, PI_MALFORMED, PI_ERROR, "RAR header Reserved bits not zero (found 0x%x)", reserved); } /* Backoff Indicator */ backoff_indicator = tvb_get_guint8(tvb, offset) & 0x0f; bi_ti = proto_tree_add_item(rar_header_tree, hf_mac_lte_rar_bi, tvb, offset, 1, ENC_BIG_ENDIAN); /* As of March 2009 spec, it must be first, and may only appear once */ if (backoff_indicator_seen) { expert_add_info_format(pinfo, bi_ti, PI_MALFORMED, PI_ERROR, "MAC RAR PDU has > 1 Backoff Indicator subheader present"); } backoff_indicator_seen = TRUE; write_pdu_label_and_info(pdu_ti, rar_header_ti, pinfo, "(Backoff Indicator=%sms)", val_to_str_const(backoff_indicator, rar_bi_vals, "Illegal-value ")); /* If present, it must be the first subheader */ if (number_of_rars > 0) { expert_add_info_format(pinfo, bi_ti, PI_MALFORMED, PI_WARN, "Backoff Indicator must appear as first subheader"); } } else { /* RAPID case */ /* TODO: complain if the same RAPID appears twice in same frame? */ rapids[number_of_rars] = tvb_get_guint8(tvb, offset) & 0x3f; proto_tree_add_item(rar_header_tree, hf_mac_lte_rar_rapid, tvb, offset, 1, ENC_BIG_ENDIAN); proto_item_append_text(rar_header_ti, "(RAPID=%u)", rapids[number_of_rars]); number_of_rars++; } offset++; /* Finalise length of header tree selection */ proto_item_set_len(rar_header_ti, offset - start_header_offset); } while (extension && number_of_rars < MAX_RAR_PDUS); /* Append summary to headers root */ proto_item_append_text(rar_headers_ti, " (%u RARs", number_of_rars); if (backoff_indicator_seen) { proto_item_append_text(rar_headers_ti, ", BI=%sms)", val_to_str_const(backoff_indicator, rar_bi_vals, "Illegal-value ")); } else { proto_item_append_text(rar_headers_ti, ")"); } /* 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, pdu_ti, offset, rapids[n]); } /* Update TAP info */ tap_info->number_of_rars += number_of_rars; /* Padding may follow */ if (tvb_length_remaining(tvb, offset) > 0) { proto_tree_add_item(tree, hf_mac_lte_padding_data, tvb, offset, -1, ENC_NA); } padding_length_ti = proto_tree_add_int(tree, hf_mac_lte_padding_length, tvb, offset, 0, p_mac_lte_info->length - offset); PROTO_ITEM_SET_GENERATED(padding_length_ti); /* Update padding bytes in stats */ tap_info->padding_bytes += (p_mac_lte_info->length - offset); } /* Dissect BCH PDU */ static void dissect_bch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item *pdu_ti, int offset, mac_lte_info *p_mac_lte_info) { proto_item *ti; write_pdu_label_and_info(pdu_ti, NULL, pinfo, "BCH PDU (%u bytes, on %s transport) ", tvb_length_remaining(tvb, offset), val_to_str_const(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 */ /* Raw data */ ti = proto_tree_add_item(tree, hf_mac_lte_bch_pdu, tvb, offset, -1, ENC_NA); if (global_mac_lte_attempt_rrc_decode) { /* Attempt to decode payload using LTE RRC dissector */ tvbuff_t *rrc_tvb = tvb_new_subset(tvb, offset, -1, tvb_length_remaining(tvb, offset)); /* Get appropriate dissector handle */ dissector_handle_t protocol_handle = 0; if (p_mac_lte_info->rntiType == SI_RNTI) { protocol_handle = find_dissector("lte_rrc.bcch_dl_sch"); } else { protocol_handle = find_dissector("lte_rrc.bcch_bch"); } /* Hide raw view of bytes */ PROTO_ITEM_SET_HIDDEN(ti); call_with_catch_all(protocol_handle, rrc_tvb, pinfo, tree); } /* 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, proto_item *pdu_ti, int offset, guint8 direction) { proto_item *ti; write_pdu_label_and_info(pdu_ti, NULL, pinfo, "PCH PDU (%u bytes) ", tvb_length_remaining(tvb, offset)); /****************************************/ /* Whole frame is PCH data */ /* Always show as raw data */ ti = proto_tree_add_item(tree, hf_mac_lte_pch_pdu, tvb, offset, -1, ENC_NA); if (global_mac_lte_attempt_rrc_decode) { /* Attempt to decode payload using LTE RRC dissector */ tvbuff_t *rrc_tvb = tvb_new_subset(tvb, offset, -1, tvb_length_remaining(tvb, offset)); /* Get appropriate dissector handle */ dissector_handle_t protocol_handle = find_dissector("lte-rrc.pcch"); /* Hide raw view of bytes */ PROTO_ITEM_SET_HIDDEN(ti); /* Call it (catch exceptions so that stats will be updated) */ TRY { call_dissector_only(protocol_handle, rrc_tvb, pinfo, tree); } CATCH_ALL { } ENDTRY } /* 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 ACTIVATION_DEACTIVATION_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)); } /* Helper function to call RLC dissector for SDUs (where channel params are known) */ static void call_rlc_dissector(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item *pdu_ti, int offset, guint16 data_length, guint8 mode, guint8 direction, guint16 ueid, guint16 channelType, guint16 channelId, guint8 UMSequenceNumberLength, guint8 priority) { tvbuff_t *srb_tvb = tvb_new_subset(tvb, offset, data_length, data_length); struct rlc_lte_info *p_rlc_lte_info; /* Get RLC dissector handle */ volatile dissector_handle_t protocol_handle = find_dissector("rlc-lte"); /* Resuse or create RLC info */ p_rlc_lte_info = p_get_proto_data(pinfo->fd, proto_rlc_lte); if (p_rlc_lte_info == NULL) { p_rlc_lte_info = se_alloc0(sizeof(struct rlc_lte_info)); } /* Fill in struct details for srb channels */ p_rlc_lte_info->rlcMode = mode; p_rlc_lte_info->direction = direction; p_rlc_lte_info->priority = priority; p_rlc_lte_info->ueid = ueid; p_rlc_lte_info->channelType = channelType; p_rlc_lte_info->channelId = channelId; p_rlc_lte_info->pduLength = data_length; p_rlc_lte_info->UMSequenceNumberLength = UMSequenceNumberLength; /* Store info in packet */ p_add_proto_data(pinfo->fd, proto_rlc_lte, p_rlc_lte_info); if (global_mac_lte_layer_to_show != ShowRLCLayer) { /* Don't want these columns replaced */ col_set_writable(pinfo->cinfo, FALSE); } else { /* Clear info column before first RLC PDU */ if (s_number_of_rlc_pdus_shown == 0) { col_clear(pinfo->cinfo, COL_INFO); } else { /* Add a separator and protect column contents here */ write_pdu_label_and_info(pdu_ti, NULL, pinfo, " || "); col_set_fence(pinfo->cinfo, COL_INFO); } } s_number_of_rlc_pdus_shown++; /* Call it (catch exceptions so that stats will be updated) */ TRY { call_dissector_only(protocol_handle, srb_tvb, pinfo, tree); } CATCH_ALL { } ENDTRY /* Let columns be written to again */ col_set_writable(pinfo->cinfo, TRUE); } /* For DL frames, look for previous Tx. Add link back if found */ static void TrackReportedDLHARQResend(packet_info *pinfo, tvbuff_t *tvb, volatile int length, proto_tree *tree, mac_lte_info *p_mac_lte_info) { DLHARQResult *result = NULL; DLHARQResult *original_result = NULL; /* If don't have detailed DL PHy info, just give up */ if (!p_mac_lte_info->detailed_phy_info.dl_info.present) { return; } /* TDD may not work... */ if (!pinfo->fd->flags.visited) { /* First time, so set result and update DL harq table */ LastFrameData *lastData = NULL; LastFrameData *thisData = NULL; DLHarqBuffers *ueData; /* Read these for convenience */ guint8 harq_id = p_mac_lte_info->detailed_phy_info.dl_info.harq_id; guint8 transport_block = p_mac_lte_info->detailed_phy_info.dl_info.transport_block; /* Check harq-id bounds, give up if invalid */ if ((harq_id >= 15) || (transport_block+1 > 2)) { return; } /* Look up entry for this UE/RNTI */ ueData = g_hash_table_lookup(mac_lte_dl_harq_hash, GUINT_TO_POINTER((guint)p_mac_lte_info->rnti)); if (ueData != NULL) { /* Get previous info for this harq-id */ lastData = &(ueData->harqid[transport_block][harq_id]); if (lastData->inUse) { /* Compare time difference, ndi, data to see if this looks like a retx */ if ((length == lastData->length) && (p_mac_lte_info->detailed_phy_info.dl_info.ndi == lastData->ndi) && tvb_memeql(tvb, 0, lastData->data, MIN(lastData->length, MAX_EXPECTED_PDU_LENGTH)) == 0) { /* Work out gap between frames */ gint seconds_between_packets = (gint) (pinfo->fd->abs_ts.secs - lastData->received_time.secs); gint nseconds_between_packets = pinfo->fd->abs_ts.nsecs - lastData->received_time.nsecs; /* Round difference to nearest millisecond */ gint total_gap = (seconds_between_packets*1000) + ((nseconds_between_packets+500000) / 1000000); /* Expect to be within (say) 8-13 subframes since previous */ if ((total_gap >= 8) && (total_gap <= 13)) { /* Resend detected! Store result pointing back. */ result = se_alloc0(sizeof(DLHARQResult)); result->previousSet = TRUE; result->previousFrameNum = lastData->framenum; result->timeSincePreviousFrame = total_gap; g_hash_table_insert(mac_lte_dl_harq_result_hash, GUINT_TO_POINTER(pinfo->fd->num), result); /* Now make previous frame point forward to here */ original_result = g_hash_table_lookup(mac_lte_dl_harq_result_hash, GUINT_TO_POINTER(lastData->framenum)); if (original_result == NULL) { original_result = se_alloc0(sizeof(ULHARQResult)); g_hash_table_insert(mac_lte_dl_harq_result_hash, GUINT_TO_POINTER(lastData->framenum), original_result); } original_result->nextSet = TRUE; original_result->nextFrameNum = pinfo->fd->num; original_result->timeToNextFrame = total_gap; } } } } else { /* Allocate entry in table for this UE/RNTI */ ueData = se_alloc0(sizeof(DLHarqBuffers)); g_hash_table_insert(mac_lte_dl_harq_hash, GUINT_TO_POINTER((guint)p_mac_lte_info->rnti), ueData); } /* Store this frame's details in table */ thisData = &(ueData->harqid[transport_block][harq_id]); thisData->inUse = TRUE; thisData->length = length; tvb_memcpy(tvb, thisData->data, 0, MIN(thisData->length, MAX_EXPECTED_PDU_LENGTH)); thisData->ndi = p_mac_lte_info->detailed_phy_info.dl_info.ndi; thisData->framenum = pinfo->fd->num; thisData->received_time = pinfo->fd->abs_ts; } else { /* Not first time, so just set whats already stored in result */ result = g_hash_table_lookup(mac_lte_dl_harq_result_hash, GUINT_TO_POINTER(pinfo->fd->num)); } /***************************************************/ /* Show link back to original frame (if available) */ if (result != NULL) { if (result->previousSet) { proto_item *gap_ti; proto_item *original_ti = proto_tree_add_uint(tree, hf_mac_lte_dl_harq_resend_original_frame, tvb, 0, 0, result->previousFrameNum); PROTO_ITEM_SET_GENERATED(original_ti); gap_ti = proto_tree_add_uint(tree, hf_mac_lte_dl_harq_resend_time_since_previous_frame, tvb, 0, 0, result->timeSincePreviousFrame); PROTO_ITEM_SET_GENERATED(gap_ti); } if (result->nextSet) { proto_item *gap_ti; proto_item *next_ti = proto_tree_add_uint(tree, hf_mac_lte_dl_harq_resend_next_frame, tvb, 0, 0, result->nextFrameNum); PROTO_ITEM_SET_GENERATED(next_ti); gap_ti = proto_tree_add_uint(tree, hf_mac_lte_dl_harq_resend_time_until_next_frame, tvb, 0, 0, result->timeToNextFrame); PROTO_ITEM_SET_GENERATED(gap_ti); } } } /* Return TRUE if the given packet is thought to be a retx */ int is_mac_lte_frame_retx(packet_info *pinfo, guint8 direction) { struct mac_lte_info *p_mac_lte_info = p_get_proto_data(pinfo->fd, proto_mac_lte); if (direction == DIRECTION_UPLINK) { /* For UL, retx count is stored in per-packet struct */ return ((p_mac_lte_info != NULL) && (p_mac_lte_info->reTxCount > 0)); } else { /* Use answer if told directly */ if (p_mac_lte_info->dl_retx == dl_retx_yes) { return TRUE; } else { /* Otherwise look up in table */ DLHARQResult *result = g_hash_table_lookup(mac_lte_dl_harq_result_hash, GUINT_TO_POINTER(pinfo->fd->num)); return ((result != NULL) && result->previousSet); } } } /* Track UL frames, so that when a retx is indicated, we can search for the original tx. We will either find it, and provide a link back to it, or flag that we couldn't find as an expert error */ static void TrackReportedULHARQResend(packet_info *pinfo, tvbuff_t *tvb, volatile int offset, proto_tree *tree, mac_lte_info *p_mac_lte_info, proto_item *retx_ti) { ULHARQResult *result = NULL; /* If don't have detailed DL PHY info, just give up */ if (!p_mac_lte_info->detailed_phy_info.ul_info.present) { return; } /* Give up if harqid is out of range */ if (p_mac_lte_info->detailed_phy_info.ul_info.harq_id >= 8) { return; } if (!pinfo->fd->flags.visited) { /* First time, so set result and update UL harq table */ LastFrameData *lastData = NULL; LastFrameData *thisData = NULL; /* Look up entry for this UE/RNTI */ ULHarqBuffers *ueData = g_hash_table_lookup(mac_lte_ul_harq_hash, GUINT_TO_POINTER((guint)p_mac_lte_info->rnti)); if (ueData != NULL) { if (p_mac_lte_info->reTxCount >= 1) { /* Looking for frame previously on this harq-id */ lastData = &(ueData->harqid[p_mac_lte_info->detailed_phy_info.ul_info.harq_id]); if (lastData->inUse) { /* Compare time, sf, data to see if this looks like a retx */ if ((tvb_length_remaining(tvb, offset) == lastData->length) && (p_mac_lte_info->detailed_phy_info.ul_info.ndi == lastData->ndi) && tvb_memeql(tvb, offset, lastData->data, MIN(lastData->length, MAX_EXPECTED_PDU_LENGTH)) == 0) { /* Work out gap between frames */ gint seconds_between_packets = (gint) (pinfo->fd->abs_ts.secs - lastData->received_time.secs); gint nseconds_between_packets = pinfo->fd->abs_ts.nsecs - lastData->received_time.nsecs; /* Round to nearest ms */ gint total_gap = (seconds_between_packets*1000) + ((nseconds_between_packets+500000) / 1000000); /* Could be as many as max-tx (which we don't know) * 8ms ago. 32 is the most I've seen... */ if (total_gap <= 33) { ULHARQResult *original_result = NULL; /* Original detected!!! Store result pointing back */ result = se_alloc0(sizeof(ULHARQResult)); result->previousSet = TRUE; result->previousFrameNum = lastData->framenum; result->timeSincePreviousFrame = total_gap; g_hash_table_insert(mac_lte_ul_harq_result_hash, GUINT_TO_POINTER(pinfo->fd->num), result); /* Now make previous frame point forward to here */ original_result = g_hash_table_lookup(mac_lte_ul_harq_result_hash, GUINT_TO_POINTER(lastData->framenum)); if (original_result == NULL) { original_result = se_alloc0(sizeof(ULHARQResult)); g_hash_table_insert(mac_lte_ul_harq_result_hash, GUINT_TO_POINTER(lastData->framenum), original_result); } original_result->nextSet = TRUE; original_result->nextFrameNum = pinfo->fd->num; original_result->timeToNextFrame = total_gap; } } } } } else { /* Allocate entry in table for this UE/RNTI */ ueData = se_alloc0(sizeof(ULHarqBuffers)); g_hash_table_insert(mac_lte_ul_harq_hash, GUINT_TO_POINTER((guint)p_mac_lte_info->rnti), ueData); } /* Store this frame's details in table */ thisData = &(ueData->harqid[p_mac_lte_info->detailed_phy_info.ul_info.harq_id]); thisData->inUse = TRUE; thisData->length = tvb_length_remaining(tvb, offset); tvb_memcpy(tvb, thisData->data, offset, MIN(thisData->length, MAX_EXPECTED_PDU_LENGTH)); thisData->ndi = p_mac_lte_info->detailed_phy_info.ul_info.ndi; thisData->framenum = pinfo->fd->num; thisData->received_time = pinfo->fd->abs_ts; } else { /* Not first time, so just get whats already stored in result */ result = g_hash_table_lookup(mac_lte_ul_harq_result_hash, GUINT_TO_POINTER(pinfo->fd->num)); } /* Show any link back to previous Tx */ if (retx_ti != NULL) { if (result != NULL) { if (result->previousSet) { proto_item *original_ti, *gap_ti; original_ti = proto_tree_add_uint(tree, hf_mac_lte_ul_harq_resend_original_frame, tvb, 0, 0, result->previousFrameNum); PROTO_ITEM_SET_GENERATED(original_ti); gap_ti = proto_tree_add_uint(tree, hf_mac_lte_ul_harq_resend_time_since_previous_frame, tvb, 0, 0, result->timeSincePreviousFrame); PROTO_ITEM_SET_GENERATED(gap_ti); } } else { expert_add_info_format(pinfo, retx_ti, PI_SEQUENCE, PI_ERROR, "Original Tx of UL frame not found (UE %u) !!", p_mac_lte_info->ueid); } } /* Show link forward to any known next Tx */ if ((result != NULL) && result->nextSet) { proto_item *next_ti, *gap_ti; next_ti = proto_tree_add_uint(tree, hf_mac_lte_ul_harq_resend_next_frame, tvb, 0, 0, result->nextFrameNum); expert_add_info_format(pinfo, next_ti, PI_SEQUENCE, PI_WARN, "UL MAC PDU (UE %u) needed to be retransmitted", p_mac_lte_info->ueid); PROTO_ITEM_SET_GENERATED(next_ti); gap_ti = proto_tree_add_uint(tree, hf_mac_lte_ul_harq_resend_time_until_next_frame, tvb, 0, 0, result->timeToNextFrame); PROTO_ITEM_SET_GENERATED(gap_ti); } } /* Look up SRResult associated with a given frame. Will create one if necessary if can_create is set */ static SRResult *GetSRResult(guint32 frameNum, gboolean can_create) { SRResult *result; result = g_hash_table_lookup(mac_lte_sr_request_hash, GUINT_TO_POINTER(frameNum)); if ((result == NULL) && can_create) { result = se_alloc0(sizeof(SRResult)); g_hash_table_insert(mac_lte_sr_request_hash, GUINT_TO_POINTER((guint)frameNum), result); } return result; } /* Keep track of SR requests, failures and related grants, in order to show them as generated fields in these frames */ static void TrackSRInfo(SREvent event, packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, mac_lte_info *p_mac_lte_info, gint idx, proto_item *event_ti) { SRResult *result = NULL; SRState *state; SRResult *resultForSRFrame = NULL; guint16 rnti; guint16 ueid; proto_item *ti; /* Get appropriate identifiers */ if (event == SR_Request) { rnti = p_mac_lte_info->oob_rnti[idx]; ueid = p_mac_lte_info->oob_ueid[idx]; } else { rnti = p_mac_lte_info->rnti; ueid = p_mac_lte_info->ueid; } /* Create state for this RNTI if necessary */ state = g_hash_table_lookup(mac_lte_ue_sr_state, GUINT_TO_POINTER((guint)rnti)); if (state == NULL) { /* Allocate status for this RNTI */ state = se_alloc(sizeof(SRState)); state->status = None; g_hash_table_insert(mac_lte_ue_sr_state, GUINT_TO_POINTER((guint)rnti), state); } /* First time through - update state with new info */ if (!pinfo->fd->flags.visited) { guint32 timeSinceRequest; /* Store time of request */ if (event == SR_Request) { state->requestTime = pinfo->fd->abs_ts; } switch (state->status) { case None: switch (event) { case SR_Grant: /* Got another grant - fine */ /* update state */ state->lastGrantFramenum = pinfo->fd->num; break; case SR_Request: /* Sent an SR - fine */ /* Update state */ state->status = SR_Outstanding; state->lastSRFramenum = pinfo->fd->num; break; case SR_Failure: /* This is an error, since we hadn't send an SR... */ result = GetSRResult(pinfo->fd->num, TRUE); result->type = InvalidSREvent; result->status = None; result->event = SR_Failure; break; } break; case SR_Outstanding: timeSinceRequest = (guint32)(((pinfo->fd->abs_ts.secs - state->requestTime.secs) * 1000) + ((pinfo->fd->abs_ts.nsecs - state->requestTime.nsecs) / 1000000)); switch (event) { case SR_Grant: /* Got grant we were waiting for, so state goes to None */ /* Update state */ state->status = None; /* Set result info */ result = GetSRResult(pinfo->fd->num, TRUE); result->type = GrantAnsweringSR; result->frameNum = state->lastSRFramenum; result->timeDifference = timeSinceRequest; /* Also set forward link for SR */ resultForSRFrame = GetSRResult(state->lastSRFramenum, TRUE); resultForSRFrame->type = SRLeadingToGrant; resultForSRFrame->frameNum = pinfo->fd->num; resultForSRFrame->timeDifference = timeSinceRequest; break; case SR_Request: /* Another request when already have one pending */ result = GetSRResult(pinfo->fd->num, TRUE); result->type = InvalidSREvent; result->status = SR_Outstanding; result->event = SR_Request; break; case SR_Failure: /* We sent an SR but it failed */ /* Update state */ state->status = SR_Failed; /* Set result info for failure frame */ result = GetSRResult(pinfo->fd->num, TRUE); result->type = FailureAnsweringSR; result->frameNum = state->lastSRFramenum; result->timeDifference = timeSinceRequest; /* Also set forward link for SR */ resultForSRFrame = GetSRResult(state->lastSRFramenum, TRUE); resultForSRFrame->type = SRLeadingToFailure; resultForSRFrame->frameNum = pinfo->fd->num; resultForSRFrame->timeDifference = timeSinceRequest; break; } break; case SR_Failed: switch (event) { case SR_Grant: /* Got a grant, presumably after a subsequent RACH - fine */ /* Update state */ state->status = None; break; case SR_Request: /* Tried another SR after previous one failed. Presumably a subsequent RACH was tried in-between... */ state->status = SR_Outstanding; result = GetSRResult(pinfo->fd->num, TRUE); result->status = SR_Outstanding; result->event = SR_Request; break; case SR_Failure: /* 2 failures in a row.... */ result = GetSRResult(pinfo->fd->num, TRUE); result->type = InvalidSREvent; result->status = SR_Failed; result->event = SR_Failure; break; } break; } } /* Get stored result for this frame */ result = GetSRResult(pinfo->fd->num, FALSE); if (result == NULL) { /* For an SR frame, there should always be either a PDCCH grant or indication that the SR has failed */ if (event == SR_Request) { expert_add_info_format(pinfo, event_ti, PI_SEQUENCE, PI_ERROR, "UE %u: SR results in neither a grant nor a failure indication", ueid); } return; } /* Show result info */ switch (result->type) { case GrantAnsweringSR: ti = proto_tree_add_uint(tree, hf_mac_lte_grant_answering_sr, tvb, 0, 0, result->frameNum); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(tree, hf_mac_lte_sr_time_since_request, tvb, 0, 0, result->timeDifference); PROTO_ITEM_SET_GENERATED(ti); break; case FailureAnsweringSR: ti = proto_tree_add_uint(tree, hf_mac_lte_failure_answering_sr, tvb, 0, 0, result->frameNum); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(tree, hf_mac_lte_sr_time_since_request, tvb, 0, 0, result->timeDifference); PROTO_ITEM_SET_GENERATED(ti); break; case SRLeadingToGrant: ti = proto_tree_add_uint(tree, hf_mac_lte_sr_leading_to_grant, tvb, 0, 0, result->frameNum); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(tree, hf_mac_lte_sr_time_until_answer, tvb, 0, 0, result->timeDifference); PROTO_ITEM_SET_GENERATED(ti); break; case SRLeadingToFailure: ti = proto_tree_add_uint(tree, hf_mac_lte_sr_leading_to_failure, tvb, 0, 0, result->frameNum); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(tree, hf_mac_lte_sr_time_until_answer, tvb, 0, 0, result->timeDifference); PROTO_ITEM_SET_GENERATED(ti); break; case InvalidSREvent: ti = proto_tree_add_none_format(tree, hf_mac_lte_sr_invalid_event, tvb, 0, 0, "UE %u: Invalid SR event - state=%s, event=%s", ueid, val_to_str_const(result->status, sr_status_vals, "Unknown"), val_to_str_const(result->event, sr_event_vals, "Unknown")); PROTO_ITEM_SET_GENERATED(ti); expert_add_info_format(pinfo, ti, PI_SEQUENCE, PI_ERROR, "Invalid SR event for UE %u (C-RNTI %u) - state=%s, event=%s", ueid, rnti, val_to_str_const(result->status, sr_status_vals, "Unknown"), val_to_str_const(result->event, sr_event_vals, "Unknown")); break; } } /********************************************************/ /* Count number of UEs/TTI (in both directions) */ /********************************************************/ /* For keeping track during first pass */ typedef struct tti_info_t { guint16 subframe; nstime_t ttiStartTime; guint ues_in_tti; } tti_info_t; static tti_info_t UL_tti_info; static tti_info_t DL_tti_info; /* For associating with frame and displaying */ typedef struct TTIInfoResult_t { guint ues_in_tti; } TTIInfoResult_t; /* This table stores (FrameNumber -> *TTIInfoResult_t). It is assigned during the first pass and used thereafter */ static GHashTable *mac_lte_tti_info_result_hash = NULL; /* Work out which UE this is within TTI (within direction). Return answer */ static guint16 count_ues_tti(mac_lte_info *p_mac_lte_info, packet_info *pinfo) { gboolean same_tti = FALSE; tti_info_t *tti_info; /* Just return any previous result */ TTIInfoResult_t *result = g_hash_table_lookup(mac_lte_tti_info_result_hash, GUINT_TO_POINTER(pinfo->fd->num)); if (result != NULL) { return result->ues_in_tti; } /* Set tti_info based upon direction */ if (p_mac_lte_info->direction == DIRECTION_UPLINK) { tti_info = &UL_tti_info; } else { tti_info = &DL_tti_info; } /* Work out if we are still in the same tti as before */ if (tti_info->subframe == p_mac_lte_info->subframeNumber) { gint seconds_between_packets = (gint) (pinfo->fd->abs_ts.secs - tti_info->ttiStartTime.secs); gint nseconds_between_packets = pinfo->fd->abs_ts.nsecs - tti_info->ttiStartTime.nsecs; /* Round difference to nearest microsecond */ gint total_us_gap = (seconds_between_packets*1000000) + ((nseconds_between_packets+500) / 1000); if (total_us_gap < 1000) { same_tti = TRUE; } } /* Update global state */ if (!same_tti) { tti_info->subframe = p_mac_lte_info->subframeNumber; tti_info->ttiStartTime = pinfo->fd->abs_ts; tti_info->ues_in_tti = 1; } else { tti_info->ues_in_tti++; } /* Set result state for this frame */ result = se_alloc(sizeof(TTIInfoResult_t)); result->ues_in_tti = tti_info->ues_in_tti; g_hash_table_insert(mac_lte_tti_info_result_hash, GUINT_TO_POINTER(pinfo->fd->num), result); return tti_info->ues_in_tti; } /* Show which UE this is (within direction) for this TTI */ static void show_ues_tti(packet_info *pinfo, mac_lte_info *p_mac_lte_info, tvbuff_t *tvb, proto_tree *context_tree) { /* Look up result */ TTIInfoResult_t *result = g_hash_table_lookup(mac_lte_tti_info_result_hash, GUINT_TO_POINTER(pinfo->fd->num)); if (result != NULL) { proto_item *ti = proto_tree_add_uint(context_tree, (p_mac_lte_info->direction == DIRECTION_UPLINK) ? hf_mac_lte_ues_ul_per_tti : hf_mac_lte_ues_dl_per_tti, tvb, 0, 0, result->ues_in_tti); PROTO_ITEM_SET_GENERATED(ti); } } /* Lookup channel details for lcid */ static void lookup_rlc_channel_from_lcid(guint8 lcid, rlc_channel_type_t *rlc_channel_type, guint8 *UM_seqnum_length, gint *drb_id) { /* Zero params (in case no match is found) */ *rlc_channel_type = rlcRaw; *UM_seqnum_length = 0; *drb_id = 0; if (global_mac_lte_lcid_drb_source == (int)FromStaticTable) { /* Look up in static (UAT) table */ guint m; for (m=0; m < num_lcid_drb_mappings; m++) { if (lcid == lcid_drb_mappings[m].lcid) { *rlc_channel_type = lcid_drb_mappings[m].channel_type; /* Set UM_seqnum_length */ switch (*rlc_channel_type) { case rlcUM5: *UM_seqnum_length = 5; break; case rlcUM10: *UM_seqnum_length = 10; break; default: break; } /* Set drb_id */ *drb_id = lcid_drb_mappings[m].drbid; break; } } } else { /* Look up setting gleaned from configuration protocol */ if (!dynamic_lcid_drb_mapping[lcid].valid) { return; } *rlc_channel_type = dynamic_lcid_drb_mapping[lcid].channel_type; /* Set UM_seqnum_length */ switch (*rlc_channel_type) { case rlcUM5: *UM_seqnum_length = 5; break; case rlcUM10: *UM_seqnum_length = 10; break; default: break; } /* Set drb_id */ *drb_id = dynamic_lcid_drb_mapping[lcid].drbid; } } #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, proto_item *pdu_ti, volatile guint32 offset, guint8 direction, mac_lte_info *p_mac_lte_info, mac_lte_tap_info *tap_info, proto_item *retx_ti, proto_tree *context_tree) { guint8 extension; volatile guint16 n; proto_item *truncated_ti; proto_item *padding_length_ti; proto_item *hidden_root_ti; /* Keep track of LCIDs and lengths as we dissect the header */ volatile guint16 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; guint8 number_of_padding_subheaders = 0; gboolean have_seen_non_padding_control = FALSE; gboolean have_seen_bsr = FALSE; gboolean expecting_body_data = FALSE; volatile guint32 is_truncated = FALSE; /* Maintain/show UEs/TTI count */ tap_info->ueInTTI = count_ues_tti(p_mac_lte_info, pinfo); show_ues_tti(pinfo, p_mac_lte_info, tvb, context_tree); write_pdu_label_and_info(pdu_ti, NULL, pinfo, "%s: (SF=%u) UEId=%-3u ", (direction == DIRECTION_UPLINK) ? "UL-SCH" : "DL-SCH", p_mac_lte_info->subframeNumber, p_mac_lte_info->ueid); tap_info->raw_length = p_mac_lte_info->length; /* For uplink frames, if this is logged as a resend, look for original tx */ if (direction == DIRECTION_UPLINK) { TrackReportedULHARQResend(pinfo, tvb, offset, tree, p_mac_lte_info, retx_ti); } /* For uplink grants, update SR status. N.B. only newTx grant should stop SR */ if ((direction == DIRECTION_UPLINK) && (p_mac_lte_info->reTxCount == 0) && global_mac_lte_track_sr) { TrackSRInfo(SR_Grant, pinfo, tree, tvb, p_mac_lte_info, 0, NULL); } /* Add hidden item to filter on */ hidden_root_ti = proto_tree_add_string_format(tree, (direction == DIRECTION_UPLINK) ? hf_mac_lte_ulsch : hf_mac_lte_dlsch, tvb, offset, 0, "", "Hidden header"); PROTO_ITEM_SET_HIDDEN(hidden_root_ti); /* 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. Default with length of 1 byte. */ pdu_subheader_ti = proto_tree_add_string_format(pdu_header_tree, hf_mac_lte_sch_subheader, tvb, offset, 1, "", "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, ENC_BIG_ENDIAN); if (reserved != 0) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "%cL-SCH header Reserved bits not zero", (p_mac_lte_info->direction == DIRECTION_UPLINK) ? 'U' : 'D'); } /* Extended bit */ extension = (first_byte & 0x20) >> 5; proto_tree_add_item(pdu_subheader_tree, hf_mac_lte_sch_extended, tvb, offset, 1, ENC_BIG_ENDIAN); /* 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, ENC_BIG_ENDIAN); write_pdu_label_and_info(pdu_ti, NULL, pinfo, "(%s", val_to_str_const(lcids[number_of_headers], ulsch_lcid_vals, "(Unknown LCID)")); } else { /* Downlink */ lcid_ti = proto_tree_add_item(pdu_subheader_tree, hf_mac_lte_dlsch_lcid, tvb, offset, 1, ENC_BIG_ENDIAN); write_pdu_label_and_info(pdu_ti, NULL, pinfo, "(%s", val_to_str_const(lcids[number_of_headers], dlsch_lcid_vals, "(Unknown LCID)")); if (lcids[number_of_headers] == DRX_COMMAND_LCID) { expert_add_info_format(pinfo, lcid_ti, PI_SEQUENCE, PI_NOTE, "DRX command received for UE %u (RNTI %u)", p_mac_lte_info->ueid, p_mac_lte_info->rnti); } } offset++; /* Remember if we've seen a data subheader */ if (lcids[number_of_headers] <= 10) { have_seen_data_header = TRUE; expecting_body_data = 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, "%cL-SCH Control subheaders should not appear after data subheaders", (p_mac_lte_info->direction == DIRECTION_UPLINK) ? 'U' : 'D'); return; } /* 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"); return; } have_seen_bsr = TRUE; } /* Should not see padding after non-padding control... */ if ((lcids[number_of_headers] > 10) && (lcids[number_of_headers] == PADDING_LCID) && extension) { number_of_padding_subheaders++; if (number_of_padding_subheaders > 2) { expert_add_info_format(pinfo, lcid_ti, PI_MALFORMED, PI_WARN, "Should not see more than 2 padding subheaders in one frame"); } if (have_seen_non_padding_control) { expert_add_info_format(pinfo, lcid_ti, PI_MALFORMED, PI_ERROR, "Padding should come before other control subheaders!"); } } /* Remember that we've seen non-padding control */ if ((lcids[number_of_headers] > 10) && (lcids[number_of_headers] != PADDING_LCID)) { have_seen_non_padding_control = TRUE; } /********************************************************************/ /* Length field follows if not the last header or for a fixed-sized control element */ if (!extension) { /* Last one... */ 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 { /* Not the last one */ 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, ENC_BIG_ENDIAN); /* 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, ENC_BIG_ENDIAN); 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, ENC_BIG_ENDIAN); offset++; } pdu_lengths[number_of_headers] = (gint16)length; } else { pdu_lengths[number_of_headers] = 0; } } /* Close off description in info column */ switch (pdu_lengths[number_of_headers]) { case 0: write_pdu_label_and_info(pdu_ti, NULL, pinfo, ") "); break; case -1: write_pdu_label_and_info(pdu_ti, NULL, pinfo, ":remainder) "); break; default: write_pdu_label_and_info(pdu_ti, NULL, pinfo, ":%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_const(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)"); proto_item_append_text(pdu_header_ti, " (%s:remainder)", val_to_str_const(lcids[number_of_headers], (direction == DIRECTION_UPLINK) ? ulsch_lcid_vals : dlsch_lcid_vals, "Unknown")); break; case 0: proto_item_append_text(pdu_subheader_ti, ")"); proto_item_append_text(pdu_header_ti, " (%s)", val_to_str_const(lcids[number_of_headers], (direction == DIRECTION_UPLINK) ? ulsch_lcid_vals : dlsch_lcid_vals, "Unknown")); break; default: proto_item_append_text(pdu_subheader_ti, ", length=%u)", pdu_lengths[number_of_headers]); proto_item_append_text(pdu_header_ti, " (%s:%u)", val_to_str_const(lcids[number_of_headers], (direction == DIRECTION_UPLINK) ? ulsch_lcid_vals : dlsch_lcid_vals, "Unknown"), pdu_lengths[number_of_headers]); break; } /* Flag unknown lcid values in expert info */ if (match_strval(lcids[number_of_headers], (direction == DIRECTION_UPLINK) ? ulsch_lcid_vals : dlsch_lcid_vals) == NULL) { expert_add_info_format(pinfo, pdu_subheader_ti, PI_MALFORMED, PI_ERROR, "%cL-SCH: Unexpected LCID received (%u)", (p_mac_lte_info->direction == DIRECTION_UPLINK) ? 'U' : 'D', lcids[number_of_headers]); } /* Set length of this subheader */ proto_item_set_len(pdu_subheader_ti, offset - offset_start_subheader); number_of_headers++; } while ((number_of_headers < MAX_HEADERS_IN_PDU) && extension); /* Check that we didn't reach the end of the subheader array... */ if (number_of_headers >= MAX_HEADERS_IN_PDU) { proto_item *ti = proto_tree_add_text(tree, tvb, offset, 1, "Reached %u subheaders - frame obviously malformed", MAX_HEADERS_IN_PDU); expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "Reached %u subheaders - frame obviously malformed", MAX_HEADERS_IN_PDU); return; } /* 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); /* For DL, see if this is a retx. Use whole PDU present (i.e. ignore padding if not logged) */ if (direction == DIRECTION_DOWNLINK) { /* Result will be added to context tree */ TrackReportedDLHARQResend(pinfo, tvb, tvb_length_remaining(tvb, 0), context_tree, p_mac_lte_info); tap_info->isPHYRetx = (p_mac_lte_info->dl_retx == dl_retx_yes); } /************************************************************************/ /* Dissect SDUs / control elements / padding. */ /************************************************************************/ /* Dissect control element bodies first */ for (n=0; n < number_of_headers; n++) { /* Get out of loop once see any data SDU subheaders */ if (lcids[n] <= 10) { break; } /* Process what should be a valid control PDU type */ if (direction == DIRECTION_DOWNLINK) { /****************************/ /* DL-SCH Control PDUs */ switch (lcids[n]) { case ACTIVATION_DEACTIVATION_LCID: { proto_item *ad_ti; proto_tree *ad_tree; proto_item *ti; guint8 reserved; /* Create AD root */ ad_ti = proto_tree_add_string_format(tree, hf_mac_lte_control_activation_deactivation, tvb, offset, 1, "", "Activation/Deactivation"); ad_tree = proto_item_add_subtree(ad_ti, ett_mac_lte_activation_deactivation); proto_tree_add_item(ad_tree, hf_mac_lte_control_activation_deactivation_c7, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ad_tree, hf_mac_lte_control_activation_deactivation_c6, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ad_tree, hf_mac_lte_control_activation_deactivation_c5, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ad_tree, hf_mac_lte_control_activation_deactivation_c4, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ad_tree, hf_mac_lte_control_activation_deactivation_c3, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ad_tree, hf_mac_lte_control_activation_deactivation_c2, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ad_tree, hf_mac_lte_control_activation_deactivation_c1, tvb, offset, 1, ENC_BIG_ENDIAN); ti = proto_tree_add_item(ad_tree, hf_mac_lte_control_activation_deactivation_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); reserved = tvb_get_guint8(tvb, offset) & 0x01; if (reserved != 0) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "Activation/Deactivation Reserved bit not zero"); } offset++; } break; case UE_CONTENTION_RESOLUTION_IDENTITY_LCID: { proto_item *cr_ti; proto_tree *cr_tree; proto_item *ti; ContentionResolutionResult *crResult; /* Create CR root */ cr_ti = proto_tree_add_string_format(tree, hf_mac_lte_control_ue_contention_resolution, tvb, offset, 6, "", "Contention Resolution"); cr_tree = proto_item_add_subtree(cr_ti, ett_mac_lte_contention_resolution); proto_tree_add_item(cr_tree, hf_mac_lte_control_ue_contention_resolution_identity, tvb, offset, 6, ENC_NA); /* Get pointer to result struct for this frame */ crResult = g_hash_table_lookup(mac_lte_cr_result_hash, GUINT_TO_POINTER(pinfo->fd->num)); if (crResult == NULL) { /* Need to set result by looking for and comparing with Msg3 */ Msg3Data *msg3Data; guint msg3Key = p_mac_lte_info->rnti; /* Allocate result and add it to the table */ crResult = se_alloc(sizeof(ContentionResolutionResult)); g_hash_table_insert(mac_lte_cr_result_hash, GUINT_TO_POINTER(pinfo->fd->num), crResult); /* Look for Msg3 */ msg3Data = g_hash_table_lookup(mac_lte_msg3_hash, GUINT_TO_POINTER(msg3Key)); /* Compare CCCH bytes */ if (msg3Data != NULL) { crResult->msSinceMsg3 = (guint32)(((pinfo->fd->abs_ts.secs - msg3Data->msg3Time.secs) * 1000) + ((pinfo->fd->abs_ts.nsecs - msg3Data->msg3Time.nsecs) / 1000000)); crResult->msg3FrameNum = msg3Data->framenum; /* Compare the 6 bytes */ if (tvb_memeql(tvb, offset, msg3Data->data, 6) == 0) { crResult->status = Msg3Match; } else { crResult->status = Msg3NoMatch; } } else { crResult->status = NoMsg3; } } /* Now show CR result in tree */ switch (crResult->status) { case NoMsg3: proto_item_append_text(cr_ti, " (no corresponding Msg3 found!)"); break; case Msg3Match: ti = proto_tree_add_uint(cr_tree, hf_mac_lte_control_ue_contention_resolution_msg3, tvb, 0, 0, crResult->msg3FrameNum); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(cr_tree, hf_mac_lte_control_ue_contention_resolution_time_since_msg3, tvb, 0, 0, crResult->msSinceMsg3); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_boolean(cr_tree, hf_mac_lte_control_ue_contention_resolution_msg3_matched, tvb, 0, 0, TRUE); PROTO_ITEM_SET_GENERATED(ti); proto_item_append_text(cr_ti, " (matches Msg3 from frame %u, %ums ago)", crResult->msg3FrameNum, crResult->msSinceMsg3); break; case Msg3NoMatch: ti = proto_tree_add_uint(cr_tree, hf_mac_lte_control_ue_contention_resolution_msg3, tvb, 0, 0, crResult->msg3FrameNum); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(cr_tree, hf_mac_lte_control_ue_contention_resolution_time_since_msg3, tvb, 0, 0, crResult->msSinceMsg3); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_boolean(cr_tree, hf_mac_lte_control_ue_contention_resolution_msg3_matched, tvb, 0, 0, FALSE); expert_add_info_format(pinfo, ti, PI_SEQUENCE, PI_WARN, "CR body in Msg4 doesn't match Msg3 CCCH in frame %u", crResult->msg3FrameNum); PROTO_ITEM_SET_GENERATED(ti); proto_item_append_text(cr_ti, " (doesn't match Msg3 from frame %u, %u ago)", crResult->msg3FrameNum, crResult->msSinceMsg3); break; }; offset += 6; } break; case TIMING_ADVANCE_LCID: { proto_item *ta_ti; proto_item *reserved_ti; guint8 reserved; guint8 ta_value; /* Check 2 reserved bits */ reserved = (tvb_get_guint8(tvb, offset) & 0xc0) >> 6; reserved_ti = proto_tree_add_item(tree, hf_mac_lte_control_timing_advance_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); if (reserved != 0) { expert_add_info_format(pinfo, reserved_ti, PI_MALFORMED, PI_ERROR, "Timing Advance Reserved bits not zero (found 0x%x)", reserved); } /* TA value */ ta_value = tvb_get_guint8(tvb, offset) & 0x3f; ta_ti = proto_tree_add_item(tree, hf_mac_lte_control_timing_advance, tvb, offset, 1, ENC_BIG_ENDIAN); if (ta_value == 31) { expert_add_info_format(pinfo, ta_ti, PI_SEQUENCE, PI_NOTE, "Timing Advance control element received (no correction needed)"); } else { expert_add_info_format(pinfo, ta_ti, PI_SEQUENCE, PI_WARN, "Timing Advance control element received (%u) %s correction needed", ta_value, (ta_value < 31) ? "-ve" : "+ve"); } offset++; } break; case DRX_COMMAND_LCID: /* No payload */ break; case PADDING_LCID: /* No payload (in this position) */ tap_info->padding_bytes++; break; default: break; } } else { /**********************************/ /* UL-SCH Control PDUs */ switch (lcids[n]) { case EXTENDED_POWER_HEADROOM_REPORT_LCID: { proto_item *ephr_ti; proto_tree *ephr_tree; proto_item *ti; proto_tree *ephr_cell_tree; proto_item *ephr_cell_ti; guint8 scell_bitmap; guint8 scell_count; guint8 byte; guint i; guint32 curr_offset = offset; guint32 computed_header_offset; if (pdu_lengths[n] == -1) { /* Control Element size is the remaining PDU */ pdu_lengths[n] = (gint16)tvb_length_remaining(tvb, curr_offset); } /* Create EPHR root */ ephr_ti = proto_tree_add_string_format(tree, hf_mac_lte_control_ext_power_headroom, tvb, curr_offset, pdu_lengths[n], "", "Extended Power Headroom"); ephr_tree = proto_item_add_subtree(ephr_ti, ett_mac_lte_extended_power_headroom); scell_bitmap = tvb_get_guint8(tvb, curr_offset); proto_tree_add_item(ephr_tree, hf_mac_lte_control_ext_power_headroom_c7, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ephr_tree, hf_mac_lte_control_ext_power_headroom_c6, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ephr_tree, hf_mac_lte_control_ext_power_headroom_c5, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ephr_tree, hf_mac_lte_control_ext_power_headroom_c4, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ephr_tree, hf_mac_lte_control_ext_power_headroom_c3, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ephr_tree, hf_mac_lte_control_ext_power_headroom_c2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ephr_tree, hf_mac_lte_control_ext_power_headroom_c1, tvb, curr_offset, 1, ENC_BIG_ENDIAN); /* Check Reserved bit */ ti = proto_tree_add_item(ephr_tree, hf_mac_lte_control_ext_power_headroom_reserved, tvb, curr_offset, 1, ENC_BIG_ENDIAN); if (scell_bitmap & 0x01) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "Extended Power Headroom Reserved bit not zero"); } curr_offset++; /* Compute expected header size to deduce if PH Type 2 report is present or not */ /* First count the number of SCells */ for (i = 0, scell_count = 0; i < 7; i++) { if (scell_bitmap & (0x80>>i)) { scell_count++; } } /* Now quickly parse the header */ computed_header_offset = curr_offset; for (i = 0; i < scell_count; i++) { if (tvb_get_guint8(tvb, computed_header_offset) & 0x80) { computed_header_offset++; } computed_header_offset++; } if ((gint16)(computed_header_offset + 1 - curr_offset) != pdu_lengths[n]) { /* PH Type 2 is present */ if (tvb_get_guint8(tvb, computed_header_offset) & 0x80) { computed_header_offset++; } computed_header_offset++; if ((gint16)(computed_header_offset + 1 - curr_offset) != pdu_lengths[n]) { expert_add_info_format(pinfo, ephr_ti, PI_MALFORMED, PI_ERROR, "Control Element has an unexpected size (computed=%d, actual=%d)", computed_header_offset + 1 - curr_offset, pdu_lengths[n]); offset += pdu_lengths[n]; break; } byte = tvb_get_guint8(tvb, curr_offset); ephr_cell_ti = proto_tree_add_text(ephr_tree, tvb, curr_offset, ((byte&0x80)?2:1), "PCell"); ephr_cell_tree = proto_item_add_subtree(ephr_cell_ti, ett_mac_lte_extended_power_headroom_cell); proto_tree_add_item(ephr_cell_tree, hf_mac_lte_control_ext_power_headroom_power_backoff, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ephr_cell_tree, hf_mac_lte_control_ext_power_headroom_value, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ephr_cell_tree, hf_mac_lte_control_ext_power_headroom_level, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_item_append_text(ephr_cell_ti, " (%s)", val_to_str_ext_const((byte&0x3f), &power_headroom_vals_ext, "Unknown")); curr_offset++; if (byte & 0x80) { /* Pcmax,c field is present */ byte = tvb_get_guint8(tvb, curr_offset); /* Check 2 Reserved bits */ ti = proto_tree_add_item(ephr_cell_tree, hf_mac_lte_control_ext_power_headroom_reserved2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); if (byte & 0xc0) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "Extended Power Headroom Reserved bits not zero (found 0x%x)", (byte & 0xc0) >> 6); } proto_tree_add_item(ephr_cell_tree, hf_mac_lte_control_ext_power_headroom_pcmaxc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_item_append_text(ephr_cell_ti, " (%s)", val_to_str_ext_const((byte&0x3f), &pcmaxc_vals_ext, "Unknown")); curr_offset++; } } else { if ((gint16)(computed_header_offset + 1 - curr_offset) != pdu_lengths[n]) { expert_add_info_format(pinfo, ephr_ti, PI_MALFORMED, PI_ERROR, "Control Element has an unexpected size (computed=%d, actual=%d)", computed_header_offset + 1 - curr_offset, pdu_lengths[n]); offset += pdu_lengths[n]; break; } } for (i = 1, scell_bitmap>>=1; i <= 7; i++, scell_bitmap>>=1) { if (scell_bitmap & 0x01) { byte = tvb_get_guint8(tvb, curr_offset); ephr_cell_ti = proto_tree_add_text(ephr_tree, tvb, curr_offset, ((byte&0x80)?2:1), "SCell Index %u", i); ephr_cell_tree = proto_item_add_subtree(ephr_cell_ti, ett_mac_lte_extended_power_headroom_cell); proto_tree_add_item(ephr_cell_tree, hf_mac_lte_control_ext_power_headroom_power_backoff, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ephr_cell_tree, hf_mac_lte_control_ext_power_headroom_value, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ephr_cell_tree, hf_mac_lte_control_ext_power_headroom_level, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_item_append_text(ephr_cell_ti, " (%s)", val_to_str_ext_const((byte&0x3f), &power_headroom_vals_ext, "Unknown")); curr_offset++; if (byte & 0x80) { /* Pcmax,c field is present */ byte = tvb_get_guint8(tvb, curr_offset); /* Check 2 Reserved bits */ ti = proto_tree_add_item(ephr_cell_tree, hf_mac_lte_control_ext_power_headroom_reserved2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); if (byte & 0xc0) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "Extended Power Headroom Reserved bits not zero (found 0x%x)", (byte & 0xc0) >> 6); } proto_tree_add_item(ephr_cell_tree, hf_mac_lte_control_ext_power_headroom_pcmaxc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); proto_item_append_text(ephr_cell_ti, " (%s)", val_to_str_ext_const((byte&0x3f), &pcmaxc_vals_ext, "Unknown")); curr_offset++; } } } offset += pdu_lengths[n]; } break; case POWER_HEADROOM_REPORT_LCID: { proto_item *phr_ti; proto_tree *phr_tree; proto_item *ti; guint8 reserved; guint8 level; /* Create PHR root */ phr_ti = proto_tree_add_string_format(tree, hf_mac_lte_control_power_headroom, tvb, offset, 1, "", "Power Headroom"); phr_tree = proto_item_add_subtree(phr_ti, ett_mac_lte_power_headroom); /* Check 2 Reserved bits */ reserved = (tvb_get_guint8(tvb, offset) & 0xc0) >> 6; ti = proto_tree_add_item(phr_tree, hf_mac_lte_control_power_headroom_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); if (reserved != 0) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "Power Headroom Reserved bits not zero (found 0x%x)", reserved); } /* Level */ level = tvb_get_guint8(tvb, offset) & 0x3f; proto_tree_add_item(phr_tree, hf_mac_lte_control_power_headroom_level, tvb, offset, 1, ENC_BIG_ENDIAN); /* Show value in root label */ proto_item_append_text(phr_ti, " (%s)", val_to_str_ext_const(level, &power_headroom_vals_ext, "Unknown")); offset++; } break; case CRNTI_LCID: proto_tree_add_item(tree, hf_mac_lte_control_crnti, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; break; case TRUNCATED_BSR_LCID: case SHORT_BSR_LCID: { proto_tree *bsr_tree; proto_item *bsr_ti; proto_item *buffer_size_ti; guint8 lcgid; guint8 buffer_size; int hfindex; value_string_ext *p_vs_ext; if (p_mac_lte_info->isExtendedBSRSizes) { hfindex = hf_mac_lte_control_short_ext_bsr_buffer_size; p_vs_ext = &ext_buffer_size_vals_ext; } else { hfindex = hf_mac_lte_control_short_bsr_buffer_size; p_vs_ext = &buffer_size_vals_ext; } bsr_ti = proto_tree_add_string_format(tree, hf_mac_lte_control_bsr, tvb, offset, 1, "", "Short BSR"); bsr_tree = proto_item_add_subtree(bsr_ti, ett_mac_lte_bsr); /* LCG ID */ lcgid = (tvb_get_guint8(tvb, offset) & 0xc0) >> 6; proto_tree_add_item(bsr_tree, hf_mac_lte_control_bsr_lcg_id, tvb, offset, 1, ENC_BIG_ENDIAN); /* Buffer Size */ buffer_size = tvb_get_guint8(tvb, offset) & 0x3f; buffer_size_ti = proto_tree_add_item(bsr_tree, hfindex, tvb, offset, 1, ENC_BIG_ENDIAN); offset++; if (buffer_size >= global_mac_lte_bsr_warn_threshold) { expert_add_info_format(pinfo, buffer_size_ti, PI_SEQUENCE, PI_WARN, "UE %u - BSR for LCG %u exceeds threshold: %u (%s)", p_mac_lte_info->ueid, lcgid, buffer_size, val_to_str_ext_const(buffer_size, p_vs_ext, "Unknown")); } proto_item_append_text(bsr_ti, " (lcgid=%u %s)", lcgid, val_to_str_ext_const(buffer_size, &buffer_size_vals_ext, "Unknown")); } break; case LONG_BSR_LCID: { proto_tree *bsr_tree; proto_item *bsr_ti; proto_item *buffer_size_ti; guint8 buffer_size[4]; int hfindex[4]; value_string_ext *p_vs_ext; if (p_mac_lte_info->isExtendedBSRSizes) { hfindex[0] = hf_mac_lte_control_long_ext_bsr_buffer_size_0; hfindex[1] = hf_mac_lte_control_long_ext_bsr_buffer_size_1; hfindex[2] = hf_mac_lte_control_long_ext_bsr_buffer_size_2; hfindex[3] = hf_mac_lte_control_long_ext_bsr_buffer_size_3; p_vs_ext = &ext_buffer_size_vals_ext; } else { hfindex[0] = hf_mac_lte_control_long_bsr_buffer_size_0; hfindex[1] = hf_mac_lte_control_long_bsr_buffer_size_1; hfindex[2] = hf_mac_lte_control_long_bsr_buffer_size_2; hfindex[3] = hf_mac_lte_control_long_bsr_buffer_size_3; p_vs_ext = &buffer_size_vals_ext; } bsr_ti = proto_tree_add_string_format(tree, hf_mac_lte_control_bsr, tvb, offset, 3, "", "Long BSR"); bsr_tree = proto_item_add_subtree(bsr_ti, ett_mac_lte_bsr); /* LCID Group 0 */ buffer_size_ti = proto_tree_add_item(bsr_tree, hfindex[0], tvb, offset, 1, ENC_BIG_ENDIAN); buffer_size[0] = (tvb_get_guint8(tvb, offset) & 0xfc) >> 2; if (buffer_size[0] >= global_mac_lte_bsr_warn_threshold) { expert_add_info_format(pinfo, buffer_size_ti, PI_SEQUENCE, PI_WARN, "UE %u - BSR for LCG 0 exceeds threshold: %u (%s)", p_mac_lte_info->ueid, buffer_size[0], val_to_str_ext_const(buffer_size[0], p_vs_ext, "Unknown")); } /* LCID Group 1 */ buffer_size_ti = proto_tree_add_item(bsr_tree, hfindex[1], tvb, offset, 2, ENC_BIG_ENDIAN); buffer_size[1] = ((tvb_get_guint8(tvb, offset) & 0x03) << 4) | ((tvb_get_guint8(tvb, offset+1) & 0xf0) >> 4); offset++; if (buffer_size[1] >= global_mac_lte_bsr_warn_threshold) { expert_add_info_format(pinfo, buffer_size_ti, PI_SEQUENCE, PI_WARN, "UE %u - BSR for LCG 1 exceeds threshold: %u (%s)", p_mac_lte_info->ueid, buffer_size[1], val_to_str_ext_const(buffer_size[1], p_vs_ext, "Unknown")); } /* LCID Group 2 */ buffer_size_ti = proto_tree_add_item(bsr_tree, hfindex[2], tvb, offset, 2, ENC_BIG_ENDIAN); buffer_size[2] = ((tvb_get_guint8(tvb, offset) & 0x0f) << 2) | ((tvb_get_guint8(tvb, offset+1) & 0xc0) >> 6); offset++; if (buffer_size[2] >= global_mac_lte_bsr_warn_threshold) { expert_add_info_format(pinfo, buffer_size_ti, PI_SEQUENCE, PI_WARN, "UE %u - BSR for LCG 2 exceeds threshold: %u (%s)", p_mac_lte_info->ueid, buffer_size[2], val_to_str_ext_const(buffer_size[2], p_vs_ext, "Unknown")); } /* LCID Group 3 */ buffer_size_ti = proto_tree_add_item(bsr_tree, hfindex[3], tvb, offset, 1, ENC_BIG_ENDIAN); buffer_size[3] = tvb_get_guint8(tvb, offset) & 0x3f; offset++; if (buffer_size[3] >= global_mac_lte_bsr_warn_threshold) { expert_add_info_format(pinfo, buffer_size_ti, PI_SEQUENCE, PI_WARN, "UE %u - BSR for LCG 3 exceeds threshold: %u (%s)", p_mac_lte_info->ueid, buffer_size[3], val_to_str_ext_const(buffer_size[3], p_vs_ext, "Unknown")); } /* Append summary to parent */ proto_item_append_text(bsr_ti, " 0:(%s) 1:(%s) 2:(%s) 3:(%s)", val_to_str_ext_const(buffer_size[0], p_vs_ext, "Unknown"), val_to_str_ext_const(buffer_size[1], p_vs_ext, "Unknown"), val_to_str_ext_const(buffer_size[2], p_vs_ext, "Unknown"), val_to_str_ext_const(buffer_size[3], p_vs_ext, "Unknown")); } break; case PADDING_LCID: /* No payload, in this position */ tap_info->padding_bytes++; break; default: break; } } } /* There might not be any data, if only headers (plus control data) were logged */ is_truncated = ((tvb_length_remaining(tvb, offset) == 0) && expecting_body_data); truncated_ti = proto_tree_add_uint(tree, hf_mac_lte_sch_header_only, tvb, 0, 0, is_truncated); if (is_truncated) { PROTO_ITEM_SET_GENERATED(truncated_ti); expert_add_info_format(pinfo, truncated_ti, PI_SEQUENCE, PI_NOTE, "MAC PDU SDUs have been omitted"); return; } else { PROTO_ITEM_SET_HIDDEN(truncated_ti); } /* Now process remaining bodies, which should all be data */ for (; n < number_of_headers; n++) { /* Data SDUs treated identically for Uplink or downlink channels */ proto_item *sdu_ti; const guint8 *pdu_data; volatile guint16 data_length; int i; char buff[64]; gboolean rlc_called_for_sdu = FALSE; /* Break out if meet padding */ if (lcids[n] == PADDING_LCID) { break; } /* Work out length */ data_length = (pdu_lengths[n] == -1) ? tvb_length_remaining(tvb, offset) : pdu_lengths[n]; /* Dissect SDU as raw bytes */ sdu_ti = proto_tree_add_bytes_format(tree, hf_mac_lte_sch_sdu, tvb, offset, pdu_lengths[n], NULL, "SDU (%s, length=%u bytes): ", val_to_str_const(lcids[n], (direction == DIRECTION_UPLINK) ? ulsch_lcid_vals : dlsch_lcid_vals, "Unknown"), data_length); /* Look for Msg3 data so that it may be compared with later Contention Resolution body */ if ((lcids[n] == 0) && (direction == DIRECTION_UPLINK) && (data_length == 6)) { if (!pinfo->fd->flags.visited) { guint key = p_mac_lte_info->rnti; Msg3Data *data = g_hash_table_lookup(mac_lte_msg3_hash, GUINT_TO_POINTER(key)); /* Look for previous entry for this UE */ if (data == NULL) { /* Allocate space for data and add to table */ data = se_alloc(sizeof(Msg3Data)); g_hash_table_insert(mac_lte_msg3_hash, GUINT_TO_POINTER(key), data); } /* Fill in data details */ data->framenum = pinfo->fd->num; tvb_memcpy(tvb, data->data, offset, data_length); data->msg3Time = pinfo->fd->abs_ts; } } /* CCCH frames can be dissected directly by LTE RRC... */ if ((lcids[n] == 0) && global_mac_lte_attempt_rrc_decode) { tvbuff_t *rrc_tvb = tvb_new_subset(tvb, offset, data_length, data_length); /* Get appropriate dissector handle */ volatile dissector_handle_t protocol_handle = 0; if (p_mac_lte_info->direction == DIRECTION_UPLINK) { protocol_handle = find_dissector("lte_rrc.ul_ccch"); } else { protocol_handle = find_dissector("lte_rrc.dl_ccch"); } /* Hide raw view of bytes */ PROTO_ITEM_SET_HIDDEN(sdu_ti); rlc_called_for_sdu = TRUE; call_with_catch_all(protocol_handle, rrc_tvb, pinfo, tree); } /* LCID 1 and 2 can be assumed to be srb1&2, so can dissect as RLC AM */ else if ((lcids[n] == 1) || (lcids[n] == 2)) { if (global_mac_lte_attempt_srb_decode) { /* Call RLC dissector */ call_rlc_dissector(tvb, pinfo, tree, pdu_ti, offset, data_length, RLC_AM_MODE, direction, p_mac_lte_info->ueid, CHANNEL_TYPE_SRB, lcids[n], 0, get_mac_lte_channel_priority(p_mac_lte_info->ueid, lcids[n], direction)); /* Hide raw view of bytes */ PROTO_ITEM_SET_HIDDEN(sdu_ti); rlc_called_for_sdu = TRUE; } } else if ((lcids[n] >= 2) && (lcids[n] <= 10)) { /* Look for mapping for this LCID to drb channel set by UAT table */ rlc_channel_type_t rlc_channel_type; guint8 UM_seqnum_length; gint drb_id; guint8 priority = get_mac_lte_channel_priority(p_mac_lte_info->ueid, lcids[n], direction); lookup_rlc_channel_from_lcid(lcids[n], &rlc_channel_type, &UM_seqnum_length, &drb_id); /* Dissect according to channel type */ switch (rlc_channel_type) { case rlcUM5: call_rlc_dissector(tvb, pinfo, tree, pdu_ti, offset, data_length, RLC_UM_MODE, direction, p_mac_lte_info->ueid, CHANNEL_TYPE_DRB, (guint16)drb_id, UM_seqnum_length, priority); break; case rlcUM10: call_rlc_dissector(tvb, pinfo, tree, pdu_ti, offset, data_length, RLC_UM_MODE, direction, p_mac_lte_info->ueid, CHANNEL_TYPE_DRB, (guint16)drb_id, UM_seqnum_length, priority); break; case rlcAM: call_rlc_dissector(tvb, pinfo, tree, pdu_ti, offset, data_length, RLC_AM_MODE, direction, p_mac_lte_info->ueid, CHANNEL_TYPE_DRB, (guint16)drb_id, 0, priority); break; case rlcTM: call_rlc_dissector(tvb, pinfo, tree, pdu_ti, offset, data_length, RLC_TM_MODE, direction, p_mac_lte_info->ueid, CHANNEL_TYPE_DRB, (guint16)drb_id, 0, priority); break; case rlcRaw: /* Nothing to do! */ break; } if (rlc_channel_type != rlcRaw) { /* Hide raw view of bytes */ PROTO_ITEM_SET_HIDDEN(sdu_ti); rlc_called_for_sdu = TRUE; } } /* Show bytes too, if won't be hidden (slow). There must be a nicer way of doing this! */ if (!rlc_called_for_sdu) { pdu_data = tvb_get_ptr(tvb, offset, pdu_lengths[n]); for (i=0; i < data_length; i++) { g_snprintf(buff+(i*2), 3, "%02x", pdu_data[i]); if (i >= 30) { g_snprintf(buff+(i*2), 4, "..."); break; } } proto_item_append_text(sdu_ti, "%s", buff); } 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]]++; } /* Now padding, if present, extends to the end of the PDU */ if (lcids[number_of_headers-1] == PADDING_LCID) { if (tvb_length_remaining(tvb, offset) > 0) { proto_tree_add_item(tree, hf_mac_lte_padding_data, tvb, offset, -1, ENC_NA); } padding_length_ti = proto_tree_add_int(tree, hf_mac_lte_padding_length, tvb, offset, 0, p_mac_lte_info->length - offset); PROTO_ITEM_SET_GENERATED(padding_length_ti); /* Update padding bytes in stats */ tap_info->padding_bytes += (p_mac_lte_info->length - offset); /* Make sure the PDU isn't bigger than reported! */ if (offset > p_mac_lte_info->length) { expert_add_info_format(pinfo, padding_length_ti, PI_MALFORMED, PI_ERROR, "%s MAC PDU is longer than reported length (reported=%u, actual=%u)", (direction == DIRECTION_UPLINK) ? "UL-SCH" : "DL-SCH", p_mac_lte_info->length, offset); } } else { /* There is no padding at the end of the frame */ if (!is_truncated && (offset < p_mac_lte_info->length)) { /* There is a problem if we haven't used all of the PDU */ expert_add_info_format(pinfo, pdu_ti, PI_MALFORMED, PI_ERROR, "%s PDU for UE %u is shorter than reported length (reported=%u, actual=%u)", (direction == DIRECTION_UPLINK) ? "UL-SCH" : "DL-SCH", p_mac_lte_info->ueid, p_mac_lte_info->length, offset); } if (!is_truncated && (offset > p_mac_lte_info->length)) { /* There is a problem if the PDU is longer than rpeported */ expert_add_info_format(pinfo, pdu_ti, PI_MALFORMED, PI_ERROR, "%s PDU for UE %u is longer than reported length (reported=%u, actual=%u)", (direction == DIRECTION_UPLINK) ? "UL-SCH" : "DL-SCH", p_mac_lte_info->ueid, p_mac_lte_info->length, offset); } } } static void dissect_mch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item *pdu_ti, volatile guint32 offset, mac_lte_info *p_mac_lte_info) { guint8 extension; volatile guint16 n; proto_item *truncated_ti; proto_item *padding_length_ti; proto_item *hidden_root_ti; /* Keep track of LCIDs and lengths as we dissect the header */ volatile guint16 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; guint8 number_of_padding_subheaders = 0; gboolean have_seen_non_padding_control = FALSE; gboolean expecting_body_data = FALSE; volatile guint32 is_truncated = FALSE; write_pdu_label_and_info(pdu_ti, NULL, pinfo, "MCH: ", p_mac_lte_info->subframeNumber); /* Add hidden item to filter on */ hidden_root_ti = proto_tree_add_string_format(tree, hf_mac_lte_mch, tvb, offset, 0, "", "Hidden header"); PROTO_ITEM_SET_HIDDEN(hidden_root_ti); /* Add PDU block header subtree */ pdu_header_ti = proto_tree_add_string_format(tree, hf_mac_lte_mch_header, tvb, offset, 0, "", "MAC PDU Header"); pdu_header_tree = proto_item_add_subtree(pdu_header_ti, ett_mac_lte_mch_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. Default with length of 1 byte. */ pdu_subheader_ti = proto_tree_add_string_format(pdu_header_tree, hf_mac_lte_mch_subheader, tvb, offset, 1, "", "Sub-header"); pdu_subheader_tree = proto_item_add_subtree(pdu_subheader_ti, ett_mac_lte_mch_subheader); /* Check 1st 2 reserved bits */ reserved = (first_byte & 0xc0) >> 6; ti = proto_tree_add_item(pdu_subheader_tree, hf_mac_lte_mch_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); if (reserved != 0) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "MCH header Reserved bits not zero"); } /* Extended bit */ extension = (first_byte & 0x20) >> 5; proto_tree_add_item(pdu_subheader_tree, hf_mac_lte_mch_extended, tvb, offset, 1, ENC_BIG_ENDIAN); /* LCID */ lcids[number_of_headers] = first_byte & 0x1f; lcid_ti = proto_tree_add_item(pdu_subheader_tree, hf_mac_lte_mch_lcid, tvb, offset, 1, ENC_BIG_ENDIAN); write_pdu_label_and_info(pdu_ti, NULL, pinfo, "(%s", val_to_str_const(lcids[number_of_headers], mch_lcid_vals, "(Unknown LCID)")); offset++; /* Remember if we've seen a data subheader */ if (lcids[number_of_headers] <= 28) { have_seen_data_header = TRUE; expecting_body_data = 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] > 28) && (lcids[number_of_headers] != PADDING_LCID)) { expert_add_info_format(pinfo, lcid_ti, PI_MALFORMED, PI_ERROR, "MCH Control subheaders should not appear after data subheaders"); return; } /* Should not see padding after non-padding control... */ if ((lcids[number_of_headers] > 28) && (lcids[number_of_headers] == PADDING_LCID) && extension) { number_of_padding_subheaders++; if (number_of_padding_subheaders > 2) { expert_add_info_format(pinfo, lcid_ti, PI_MALFORMED, PI_WARN, "Should not see more than 2 padding subheaders in one frame"); } if (have_seen_non_padding_control) { expert_add_info_format(pinfo, lcid_ti, PI_MALFORMED, PI_ERROR, "Padding should come before other control subheaders!"); } } /* Remember that we've seen non-padding control */ if ((lcids[number_of_headers] > 28) && (lcids[number_of_headers] != PADDING_LCID)) { have_seen_non_padding_control = TRUE; } /********************************************************************/ /* Length field follows if not the last header or for a fixed-sized control element */ if (!extension) { /* Last one... */ pdu_lengths[number_of_headers] = -1; } else { /* Not the last one */ if (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_mch_format, tvb, offset, 1, ENC_BIG_ENDIAN); /* Now read length field itself */ if (format) { /* >= 128 - use 15 bits */ proto_tree_add_bits_ret_val(pdu_subheader_tree, hf_mac_lte_mch_length, tvb, offset*8 + 1, 15, &length, ENC_BIG_ENDIAN); offset += 2; } else { /* Less than 128 - only 7 bits */ proto_tree_add_bits_ret_val(pdu_subheader_tree, hf_mac_lte_mch_length, tvb, offset*8 + 1, 7, &length, ENC_BIG_ENDIAN); offset++; } if ((lcids[number_of_headers] == MCH_SCHEDULING_INFO_LCID) && (length & 0x01)) { expert_add_info_format(pinfo, lcid_ti, PI_MALFORMED, PI_WARN, "MCH Scheduling Information MAC Control Element should have an even size"); } pdu_lengths[number_of_headers] = (gint16)length; } else { pdu_lengths[number_of_headers] = 0; } } /* Close off description in info column */ switch (pdu_lengths[number_of_headers]) { case 0: write_pdu_label_and_info(pdu_ti, NULL, pinfo, ") "); break; case -1: write_pdu_label_and_info(pdu_ti, NULL, pinfo, ":remainder) "); break; default: write_pdu_label_and_info(pdu_ti, NULL, pinfo, ":%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_const(lcids[number_of_headers], mch_lcid_vals, "Unknown")); switch (pdu_lengths[number_of_headers]) { case -1: proto_item_append_text(pdu_subheader_ti, ", length is remainder)"); proto_item_append_text(pdu_header_ti, " (%s:remainder)", val_to_str_const(lcids[number_of_headers], mch_lcid_vals, "Unknown")); break; case 0: proto_item_append_text(pdu_subheader_ti, ")"); proto_item_append_text(pdu_header_ti, " (%s)", val_to_str_const(lcids[number_of_headers], mch_lcid_vals, "Unknown")); break; default: proto_item_append_text(pdu_subheader_ti, ", length=%u)", pdu_lengths[number_of_headers]); proto_item_append_text(pdu_header_ti, " (%s:%u)", val_to_str_const(lcids[number_of_headers], mch_lcid_vals, "Unknown"), pdu_lengths[number_of_headers]); break; } /* Flag unknown lcid values in expert info */ if (match_strval(lcids[number_of_headers],mch_lcid_vals) == NULL) { expert_add_info_format(pinfo, pdu_subheader_ti, PI_MALFORMED, PI_ERROR, "MCH: 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 ((number_of_headers < MAX_HEADERS_IN_PDU) && extension); /* Check that we didn't reach the end of the subheader array... */ if (number_of_headers >= MAX_HEADERS_IN_PDU) { proto_item *ti = proto_tree_add_text(tree, tvb, offset, 1, "Reached %u subheaders - frame obviously malformed", MAX_HEADERS_IN_PDU); expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "Reached %u subheaders - frame obviously malformed", MAX_HEADERS_IN_PDU); return; } /* 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); /************************************************************************/ /* Dissect SDUs / control elements / padding. */ /************************************************************************/ /* Dissect control element bodies first */ for (n=0; n < number_of_headers; n++) { /* Get out of loop once see any data SDU subheaders */ if (lcids[n] <= 28) { break; } /* Process what should be a valid control PDU type */ switch (lcids[n]) { case MCH_SCHEDULING_INFO_LCID: { guint32 curr_offset = offset; gint16 i; guint16 stop_mtch_val; proto_item *mch_sched_info_ti, *ti; proto_tree *mch_sched_info_tree; mch_sched_info_ti = proto_tree_add_string_format(tree, hf_mac_lte_control_mch_scheduling_info, tvb, curr_offset, pdu_lengths[n], "", "MCH Scheduling Information"); mch_sched_info_tree = proto_item_add_subtree(mch_sched_info_ti, ett_mac_lte_mch_scheduling_info); for (i=0; i<(pdu_lengths[n]/2); i++) { proto_tree_add_item(mch_sched_info_tree, hf_mac_lte_control_mch_scheduling_info_lcid, tvb, curr_offset, 1, ENC_BIG_ENDIAN); stop_mtch_val = tvb_get_ntohs(tvb, curr_offset) & 0x7ff; ti = proto_tree_add_item(mch_sched_info_tree, hf_mac_lte_control_mch_scheduling_info_stop_mtch, tvb, curr_offset, 2, ENC_BIG_ENDIAN); if ((stop_mtch_val >= 2043) && (stop_mtch_val <= 2046)) { proto_item_append_text(ti, " (reserved)"); } else if (stop_mtch_val == 2047) { proto_item_append_text(ti, " (MTCH is not scheduled)"); } curr_offset += 2; } offset += pdu_lengths[n]; } break; case PADDING_LCID: /* No payload (in this position) */ break; default: break; } } /* There might not be any data, if only headers (plus control data) were logged */ is_truncated = ((tvb_length_remaining(tvb, offset) == 0) && expecting_body_data); truncated_ti = proto_tree_add_uint(tree, hf_mac_lte_mch_header_only, tvb, 0, 0, is_truncated); if (is_truncated) { PROTO_ITEM_SET_GENERATED(truncated_ti); expert_add_info_format(pinfo, truncated_ti, PI_SEQUENCE, PI_NOTE, "MAC PDU SDUs have been omitted"); return; } else { PROTO_ITEM_SET_HIDDEN(truncated_ti); } /* Now process remaining bodies, which should all be data */ for (; n < number_of_headers; n++) { proto_item *sdu_ti; const guint8 *pdu_data; volatile guint16 data_length; int i; char buff[64]; /* Break out if meet padding */ if (lcids[n] == PADDING_LCID) { break; } /* Work out length */ data_length = (pdu_lengths[n] == -1) ? tvb_length_remaining(tvb, offset) : pdu_lengths[n]; /* Dissect SDU as raw bytes */ sdu_ti = proto_tree_add_bytes_format(tree, hf_mac_lte_mch_sdu, tvb, offset, pdu_lengths[n], NULL, "SDU (%s, length=%u bytes): ", val_to_str_const(lcids[n], mch_lcid_vals, "Unknown"), data_length); /* Show bytes too. There must be a nicer way of doing this! */ pdu_data = tvb_get_ptr(tvb, offset, pdu_lengths[n]); for (i=0; i < data_length; i++) { g_snprintf(buff+(i*2), 3, "%02x", pdu_data[i]); if (i >= 30) { g_snprintf(buff+(i*2), 4, "..."); break; } } proto_item_append_text(sdu_ti, "%s", buff); offset += data_length; } /* Now padding, if present, extends to the end of the PDU */ if (lcids[number_of_headers-1] == PADDING_LCID) { if (tvb_length_remaining(tvb, offset) > 0) { proto_tree_add_item(tree, hf_mac_lte_padding_data, tvb, offset, -1, ENC_NA); } padding_length_ti = proto_tree_add_int(tree, hf_mac_lte_padding_length, tvb, offset, 0, p_mac_lte_info->length - offset); PROTO_ITEM_SET_GENERATED(padding_length_ti); /* Make sure the PDU isn't bigger than reported! */ if (offset > p_mac_lte_info->length) { expert_add_info_format(pinfo, padding_length_ti, PI_MALFORMED, PI_ERROR, "MAC PDU is longer than reported length (reported=%u, actual=%u)", p_mac_lte_info->length, offset); } } else { /* There is no padding at the end of the frame */ if (!is_truncated && (offset < p_mac_lte_info->length)) { /* There is a problem if we haven't used all of the PDU */ expert_add_info_format(pinfo, pdu_ti, PI_MALFORMED, PI_ERROR, "PDU is shorter than reported length (reported=%u, actual=%u)", p_mac_lte_info->length, offset); } if (!is_truncated && (offset > p_mac_lte_info->length)) { /* There is a problem if the PDU is longer than rpeported */ expert_add_info_format(pinfo, pdu_ti, PI_MALFORMED, PI_ERROR, "PDU is longer than reported length (reported=%u, actual=%u)", p_mac_lte_info->length, offset); } } } /*****************************/ /* Main dissection function. */ void dissect_mac_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_tree *mac_lte_tree; proto_item *pdu_ti; proto_tree *context_tree; proto_item *context_ti; proto_item *retx_ti = NULL; proto_item *ti; gint offset = 0; struct mac_lte_info *p_mac_lte_info = NULL; gint n; /* Allocate and zero tap struct */ mac_lte_tap_info *tap_info = ep_alloc0(sizeof(mac_lte_tap_info)); /* Set protocol name */ col_set_str(pinfo->cinfo, COL_PROTOCOL, "MAC-LTE"); /* Create protocol tree. */ pdu_ti = proto_tree_add_item(tree, proto_mac_lte, tvb, offset, -1, ENC_NA); proto_item_append_text(pdu_ti, " "); mac_lte_tree = proto_item_add_subtree(pdu_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 *tii = 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(tii); return; } /* Clear info column */ col_clear(pinfo->cinfo, COL_INFO); /*****************************************/ /* Show context information */ /* Create context root */ context_ti = proto_tree_add_string_format(mac_lte_tree, hf_mac_lte_context, tvb, offset, 0, "", "Context"); context_tree = proto_item_add_subtree(context_ti, ett_mac_lte_context); PROTO_ITEM_SET_GENERATED(context_ti); ti = proto_tree_add_uint(context_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(context_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(context_tree, hf_mac_lte_context_ueid, tvb, 0, 0, p_mac_lte_info->ueid); PROTO_ITEM_SET_GENERATED(ti); } /* There are several out-of-band MAC events that may be indicated in the context info. */ /* Handle them here */ if (p_mac_lte_info->length == 0) { proto_item *preamble_ti; proto_tree *preamble_tree; switch (p_mac_lte_info->oob_event) { case ltemac_send_preamble: preamble_ti = proto_tree_add_item(mac_lte_tree, hf_mac_lte_oob_send_preamble, tvb, 0, 0, ENC_ASCII|ENC_NA); preamble_tree = proto_item_add_subtree(preamble_ti, ett_mac_lte_oob); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(preamble_tree, hf_mac_lte_context_rapid, tvb, 0, 0, p_mac_lte_info->rapid); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(preamble_tree, hf_mac_lte_context_rach_attempt_number, tvb, 0, 0, p_mac_lte_info->rach_attempt_number); PROTO_ITEM_SET_GENERATED(ti); /* Info column */ write_pdu_label_and_info(pdu_ti, preamble_ti, pinfo, "RACH Preamble sent for UE %u (RAPID=%u, attempt=%u)", p_mac_lte_info->ueid, p_mac_lte_info->rapid, p_mac_lte_info->rach_attempt_number); /* Add expert info (a note, unless attempt > 1) */ expert_add_info_format(pinfo, ti, PI_SEQUENCE, (p_mac_lte_info->rach_attempt_number > 1) ? PI_WARN : PI_NOTE, "RACH Preamble sent for UE %u (RAPID=%u, attempt=%u)", p_mac_lte_info->ueid, p_mac_lte_info->rapid, p_mac_lte_info->rach_attempt_number); break; case ltemac_send_sr: /* Count of SRs */ ti = proto_tree_add_uint(mac_lte_tree, hf_mac_lte_number_of_srs, tvb, 0, 0, p_mac_lte_info->number_of_srs); PROTO_ITEM_SET_GENERATED(ti); for (n=0; n < p_mac_lte_info->number_of_srs; n++) { proto_item *sr_ti; proto_tree *sr_tree; /* SR event is subtree */ sr_ti = proto_tree_add_item(mac_lte_tree, hf_mac_lte_oob_send_sr, tvb, 0, 0, ENC_NA); sr_tree = proto_item_add_subtree(sr_ti, ett_mac_lte_oob); PROTO_ITEM_SET_GENERATED(sr_ti); /* RNTI */ ti = proto_tree_add_uint(sr_tree, hf_mac_lte_context_rnti, tvb, 0, 0, p_mac_lte_info->oob_rnti[n]); PROTO_ITEM_SET_GENERATED(ti); /* UEID */ ti = proto_tree_add_uint(sr_tree, hf_mac_lte_context_ueid, tvb, 0, 0, p_mac_lte_info->oob_ueid[n]); PROTO_ITEM_SET_GENERATED(ti); /* Add summary to root. */ proto_item_append_text(sr_ti, " (UE=%u C-RNTI=%u)", p_mac_lte_info->oob_ueid[n], p_mac_lte_info->oob_rnti[n]); /* Info column */ if (n == 0) { write_pdu_label_and_info(pdu_ti, NULL, pinfo, "Scheduling Requests (%u) sent: (UE=%u C-RNTI=%u)", p_mac_lte_info->number_of_srs, p_mac_lte_info->oob_ueid[n], p_mac_lte_info->oob_rnti[n]); } else { write_pdu_label_and_info(pdu_ti, NULL, pinfo, " (UE=%u C-RNTI=%u)", p_mac_lte_info->oob_ueid[n], p_mac_lte_info->oob_rnti[n]); } /* Add expert info (a note) */ expert_add_info_format(pinfo, sr_ti, PI_SEQUENCE, PI_NOTE, "Scheduling Request sent for UE %u (RNTI %u)", p_mac_lte_info->oob_ueid[n], p_mac_lte_info->oob_rnti[n]); /* Update SR status for this UE */ if (global_mac_lte_track_sr) { TrackSRInfo(SR_Request, pinfo, mac_lte_tree, tvb, p_mac_lte_info, n, sr_ti); } } break; case ltemac_sr_failure: ti = proto_tree_add_uint(context_tree, hf_mac_lte_context_rnti, tvb, 0, 0, p_mac_lte_info->rnti); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_item(mac_lte_tree, hf_mac_lte_oob_sr_failure, tvb, 0, 0, ENC_NA); PROTO_ITEM_SET_GENERATED(ti); /* Info column */ write_pdu_label_and_info(pdu_ti, NULL, pinfo, "Scheduling Request FAILED for UE %u (C-RNTI=%u)", p_mac_lte_info->ueid, p_mac_lte_info->rnti); /* Add expert info (an error) */ expert_add_info_format(pinfo, ti, PI_SEQUENCE, PI_ERROR, "Scheduling Request failed for UE %u (RNTI %u)", p_mac_lte_info->ueid, p_mac_lte_info->rnti); /* Update SR status */ if (global_mac_lte_track_sr) { TrackSRInfo(SR_Failure, pinfo, mac_lte_tree, tvb, p_mac_lte_info, 0, ti); } break; } /* Our work here is done */ return; } ti = proto_tree_add_uint(context_tree, hf_mac_lte_context_sysframe_number, tvb, 0, 0, p_mac_lte_info->sysframeNumber); PROTO_ITEM_SET_GENERATED(ti); ti = proto_tree_add_uint(context_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->subframeNumber > 9) { /* N.B. if we set it to valid value, it won't trigger when we rescan (at least with DCT2000 files where the context struct isn't re-read). */ expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "Subframe number (%u) was out of range - valid range is 0-9", p_mac_lte_info->subframeNumber); } if (p_mac_lte_info->subframeNumberOfGrantPresent) { ti = proto_tree_add_uint(context_tree, hf_mac_lte_context_grant_subframe_number, tvb, 0, 0, p_mac_lte_info->subframeNumberOfGrant); PROTO_ITEM_SET_GENERATED(ti); } if (p_mac_lte_info->rntiType != NO_RNTI) { ti = proto_tree_add_uint(context_tree, hf_mac_lte_context_rnti, tvb, 0, 0, p_mac_lte_info->rnti); PROTO_ITEM_SET_GENERATED(ti); proto_item_append_text(context_ti, " (RNTI=%u)", p_mac_lte_info->rnti); } ti = proto_tree_add_uint(context_tree, hf_mac_lte_context_rnti_type, tvb, 0, 0, p_mac_lte_info->rntiType); PROTO_ITEM_SET_GENERATED(ti); /* Check that RNTI value is consistent with given RNTI type */ switch (p_mac_lte_info->rntiType) { case M_RNTI: if (p_mac_lte_info->rnti != 0xFFFD) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "M-RNTI indicated, but value is %u (0x%x) (must be 0x%x)", p_mac_lte_info->rnti, p_mac_lte_info->rnti, 0xFFFD); return; } break; case P_RNTI: if (p_mac_lte_info->rnti != 0xFFFE) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "P-RNTI indicated, but value is %u (0x%x) (must be 0x%x)", p_mac_lte_info->rnti, p_mac_lte_info->rnti, 0xFFFE); return; } break; case SI_RNTI: if (p_mac_lte_info->rnti != 0xFFFF) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "SI-RNTI indicated, but value is %u (0x%x) (must be 0x%x)", p_mac_lte_info->rnti, p_mac_lte_info->rnti, 0xFFFE); return; } break; case RA_RNTI: if ((p_mac_lte_info->rnti < 0x0001) || (p_mac_lte_info->rnti > 0x003C)) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "RA_RNTI indicated, but given value %u (0x%x)is out of range", p_mac_lte_info->rnti, p_mac_lte_info->rnti); return; } break; case C_RNTI: case SPS_RNTI: if ((p_mac_lte_info->rnti < 0x0001) || (p_mac_lte_info->rnti > 0xFFF3)) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "%s indicated, but given value %u (0x%x)is out of range", val_to_str_const(p_mac_lte_info->rntiType, rnti_type_vals, "Unknown"), p_mac_lte_info->rnti, p_mac_lte_info->rnti); return; } break; default: break; } ti = proto_tree_add_uint(context_tree, hf_mac_lte_context_predefined_frame, tvb, 0, 0, p_mac_lte_info->isPredefinedData); if (p_mac_lte_info->isPredefinedData) { PROTO_ITEM_SET_GENERATED(ti); } else { PROTO_ITEM_SET_HIDDEN(ti); } ti = proto_tree_add_uint(context_tree, hf_mac_lte_context_length, tvb, 0, 0, p_mac_lte_info->length); PROTO_ITEM_SET_GENERATED(ti); /* Infer uplink grant size */ if (p_mac_lte_info->direction == DIRECTION_UPLINK) { ti = proto_tree_add_uint(context_tree, hf_mac_lte_context_ul_grant_size, tvb, 0, 0, p_mac_lte_info->length); PROTO_ITEM_SET_GENERATED(ti); } /* Retx count goes in top-level tree to make it more visible */ if (p_mac_lte_info->reTxCount) { proto_item *retx_reason_ti; retx_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(retx_ti); if (p_mac_lte_info->reTxCount >= global_mac_lte_retx_counter_trigger) { expert_add_info_format(pinfo, retx_ti, PI_SEQUENCE, PI_WARN, "UE %u: UL MAC frame ReTX no. %u", p_mac_lte_info->ueid, p_mac_lte_info->reTxCount); } retx_reason_ti = proto_tree_add_uint(mac_lte_tree, hf_mac_lte_context_retx_reason, tvb, 0, 0, p_mac_lte_info->isPHICHNACK); PROTO_ITEM_SET_GENERATED(retx_reason_ti); } if (p_mac_lte_info->crcStatusValid) { /* Set status */ ti = proto_tree_add_uint(context_tree, hf_mac_lte_context_crc_status, tvb, 0, 0, p_mac_lte_info->detailed_phy_info.dl_info.crc_status); PROTO_ITEM_SET_GENERATED(ti); /* Report non-success */ if (p_mac_lte_info->detailed_phy_info.dl_info.crc_status != crc_success) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "%s Frame has CRC error problem (%s)", (p_mac_lte_info->direction == DIRECTION_UPLINK) ? "UL" : "DL", val_to_str_const(p_mac_lte_info->detailed_phy_info.dl_info.crc_status, crc_status_vals, "Unknown")); write_pdu_label_and_info(pdu_ti, NULL, pinfo, "%s: UEId=%u %s=%u ", (p_mac_lte_info->direction == DIRECTION_UPLINK) ? "UL" : "DL", val_to_str_const(p_mac_lte_info->detailed_phy_info.dl_info.crc_status, crc_status_vals, "Unknown"), p_mac_lte_info->ueid, val_to_str_const(p_mac_lte_info->rntiType, rnti_type_vals, "Unknown RNTI type"), p_mac_lte_info->rnti); } } /* May also have extra Physical layer attributes set for this frame */ show_extra_phy_parameters(pinfo, tvb, mac_lte_tree, p_mac_lte_info); /* Set context-info parts of tap struct */ tap_info->rnti = p_mac_lte_info->rnti; tap_info->ueid = p_mac_lte_info->ueid; tap_info->rntiType = p_mac_lte_info->rntiType; tap_info->isPredefinedData = p_mac_lte_info->isPredefinedData; tap_info->isPHYRetx = (p_mac_lte_info->reTxCount >= 1); tap_info->crcStatusValid = p_mac_lte_info->crcStatusValid; tap_info->crcStatus = p_mac_lte_info->detailed_phy_info.dl_info.crc_status; tap_info->direction = p_mac_lte_info->direction; tap_info->time = pinfo->fd->abs_ts; /* 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, ENC_NA); write_pdu_label_and_info(pdu_ti, NULL, pinfo, "Predefined data (%u bytes%s)", p_mac_lte_info->length, (p_mac_lte_info->length > tvb_length_remaining(tvb, offset) ? " - truncated" : "")); /* Queue tap info */ if (!pinfo->flags.in_error_pkt) { tap_queue_packet(mac_lte_tap, pinfo, tap_info); } return; } /* IF CRC status failed, just do decode as raw bytes */ if (!global_mac_lte_dissect_crc_failures && (p_mac_lte_info->crcStatusValid && (p_mac_lte_info->detailed_phy_info.dl_info.crc_status != crc_success))) { proto_tree_add_item(mac_lte_tree, hf_mac_lte_raw_pdu, tvb, offset, -1, ENC_NA); write_pdu_label_and_info(pdu_ti, NULL, pinfo, "Raw data (%u bytes)", tvb_length_remaining(tvb, offset)); /* Queue tap info. TODO: unfortunately DL retx detection won't get done if we return here... */ if (!pinfo->flags.in_error_pkt) { tap_queue_packet(mac_lte_tap, pinfo, tap_info); } return; } /* Reset this counter */ s_number_of_rlc_pdus_shown = 0; /* 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, pdu_ti, offset, p_mac_lte_info->direction); break; case RA_RNTI: /* RAR PDU */ dissect_rar(tvb, pinfo, mac_lte_tree, pdu_ti, offset, p_mac_lte_info, tap_info); break; case C_RNTI: case SPS_RNTI: /* Can be UL-SCH or DL-SCH */ dissect_ulsch_or_dlsch(tvb, pinfo, mac_lte_tree, pdu_ti, offset, p_mac_lte_info->direction, p_mac_lte_info, tap_info, retx_ti, context_tree); break; case SI_RNTI: /* BCH over DL-SCH */ dissect_bch(tvb, pinfo, mac_lte_tree, pdu_ti, offset, p_mac_lte_info); break; case M_RNTI: /* MCH PDU */ dissect_mch(tvb, pinfo, mac_lte_tree, pdu_ti, offset, p_mac_lte_info); break; case NO_RNTI: /* Must be BCH over BCH... */ dissect_bch(tvb, pinfo, mac_lte_tree, pdu_ti, offset, p_mac_lte_info); break; default: break; } /* Queue tap info */ tap_queue_packet(mac_lte_tap, pinfo, tap_info); } /* Initializes the hash tables each time a new * file is loaded or re-loaded in wireshark */ static void mac_lte_init_protocol(void) { /* Destroy any existing tables. */ if (mac_lte_msg3_hash) { g_hash_table_destroy(mac_lte_msg3_hash); } if (mac_lte_cr_result_hash) { g_hash_table_destroy(mac_lte_cr_result_hash); } if (mac_lte_dl_harq_hash) { g_hash_table_destroy(mac_lte_dl_harq_hash); } if (mac_lte_dl_harq_result_hash) { g_hash_table_destroy(mac_lte_dl_harq_result_hash); } if (mac_lte_ul_harq_hash) { g_hash_table_destroy(mac_lte_ul_harq_hash); } if (mac_lte_ul_harq_result_hash) { g_hash_table_destroy(mac_lte_ul_harq_result_hash); } if (mac_lte_ue_sr_state) { g_hash_table_destroy(mac_lte_ue_sr_state); } if (mac_lte_sr_request_hash) { g_hash_table_destroy(mac_lte_sr_request_hash); } if (mac_lte_tti_info_result_hash) { g_hash_table_destroy(mac_lte_tti_info_result_hash); } /* Reset structs */ memset(&UL_tti_info, 0, sizeof(UL_tti_info)); UL_tti_info.subframe = 0xff; /* Invalid value */ memset(&DL_tti_info, 0, sizeof(DL_tti_info)); DL_tti_info.subframe = 0xff; /* Invalid value */ /* Now create them over */ mac_lte_msg3_hash = g_hash_table_new(mac_lte_rnti_hash_func, mac_lte_rnti_hash_equal); mac_lte_cr_result_hash = g_hash_table_new(mac_lte_framenum_hash_func, mac_lte_framenum_hash_equal); mac_lte_dl_harq_hash = g_hash_table_new(mac_lte_rnti_hash_func, mac_lte_rnti_hash_equal); mac_lte_dl_harq_result_hash = g_hash_table_new(mac_lte_framenum_hash_func, mac_lte_framenum_hash_equal); mac_lte_ul_harq_hash = g_hash_table_new(mac_lte_rnti_hash_func, mac_lte_rnti_hash_equal); mac_lte_ul_harq_result_hash = g_hash_table_new(mac_lte_framenum_hash_func, mac_lte_framenum_hash_equal); mac_lte_ue_sr_state = g_hash_table_new(mac_lte_rnti_hash_func, mac_lte_rnti_hash_equal); mac_lte_sr_request_hash = g_hash_table_new(mac_lte_framenum_hash_func, mac_lte_framenum_hash_equal); mac_lte_tti_info_result_hash = g_hash_table_new(mac_lte_framenum_hash_func, mac_lte_framenum_hash_equal); } static void* lcid_drb_mapping_copy_cb(void* dest, const void* orig, size_t len _U_) { const lcid_drb_mapping_t *o = orig; lcid_drb_mapping_t *d = dest; /* Copy all items over */ d->lcid = o->lcid; d->drbid = o->drbid; d->channel_type = o->channel_type; return d; } void proto_register_mac_lte(void) { static hf_register_info hf[] = { /**********************************/ /* Items for decoding context */ { &hf_mac_lte_context, { "Context", "mac-lte.context", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_radio_type, { "Radio Type", "mac-lte.radio-type", FT_UINT8, BASE_DEC, VALS(radio_type_vals), 0x0, NULL, 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_sysframe_number, { "System Frame Number", "mac-lte.sfn", FT_UINT16, BASE_DEC, 0, 0x0, "System Frame Number associated with message", HFILL } }, { &hf_mac_lte_context_subframe_number, { "Subframe", "mac-lte.subframe", FT_UINT16, BASE_DEC, 0, 0x0, "Subframe number associated with message", HFILL } }, { &hf_mac_lte_context_grant_subframe_number, { "Grant Subframe", "mac-lte.grant-subframe", FT_UINT16, BASE_DEC, 0, 0x0, "Subframe when grant for this PDU was received", 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_ul_grant_size, { "Uplink grant size", "mac-lte.ul-grant-size", FT_UINT8, BASE_DEC, 0, 0x0, "Uplink grant size (in bytes)", 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_retx_reason, { "ReTX reason", "mac-lte.retx-reason", FT_UINT8, BASE_DEC, VALS(ul_retx_grant_vals), 0x0, "Type of UL ReTx grant", 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 } }, { &hf_mac_lte_context_rapid, { "RAPID", "mac-lte.preamble-sent.rapid", FT_UINT8, BASE_DEC, 0, 0x0, "RAPID sent in RACH preamble", HFILL } }, { &hf_mac_lte_context_rach_attempt_number, { "RACH Attempt Number", "mac-lte.preamble-sent.attempt", FT_UINT8, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_ues_ul_per_tti, { "UL UE in TTI", "mac-lte.ul-tti-count", FT_UINT8, BASE_DEC, 0, 0x0, "In this TTI, this is the nth UL grant", HFILL } }, { &hf_mac_lte_ues_dl_per_tti, { "DL UE in TTI", "mac-lte.dl-tti-count", FT_UINT8, BASE_DEC, 0, 0x0, "In this TTI, this is the nth DL PDU", HFILL } }, /* Extra PHY context */ { &hf_mac_lte_context_phy_ul, { "UL PHY attributes", "mac-lte.ul-phy", FT_STRING, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_ul_modulation_type, { "Modulation type", "mac-lte.ul-phy.modulation-type", FT_UINT8, BASE_DEC, VALS(modulation_type_vals), 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_ul_tbs_index, { "TBs Index", "mac-lte.ul-phy.tbs-index", FT_UINT8, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_ul_resource_block_length, { "Resource Block Length", "mac-lte.ul-phy.resource-block-length", FT_UINT8, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_ul_resource_block_start, { "Resource Block Start", "mac-lte.ul-phy.resource-block-start", FT_UINT8, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_ul_harq_id, { "HARQ Id", "mac-lte.ul-phy.harq-id", FT_UINT8, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_ul_ndi, { "NDI", "mac-lte.ul-phy.ndi", FT_UINT8, BASE_DEC, 0, 0x0, "UL New Data Indicator", HFILL } }, { &hf_mac_lte_context_phy_dl, { "DL PHY attributes", "mac-lte.dl-phy", FT_STRING, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_dl_dci_format, { "DCI format", "mac-lte.dl-phy.dci-format", FT_UINT8, BASE_DEC, VALS(dci_format_vals), 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_dl_resource_allocation_type, { "Resource Allocation Type", "mac-lte.dl-phy.resource-allocation-type", FT_UINT8, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_dl_aggregation_level, { "Aggregation Level", "mac-lte.dl-phy.aggregation-level", FT_UINT8, BASE_DEC, VALS(aggregation_level_vals), 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_dl_mcs_index, { "MCS Index", "mac-lte.dl-phy.mcs-index", FT_UINT8, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_dl_redundancy_version_index, { "RV Index", "mac-lte.dl-phy.rv-index", FT_UINT8, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_dl_retx, { "DL Retx", "mac-lte.dl-phy.dl-retx", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_dl_resource_block_length, { "RB Length", "mac-lte.dl-phy.rb-length", FT_UINT8, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_dl_crc_status, { "CRC Status", "mac-lte.dl-phy.crc-status", FT_UINT8, BASE_DEC, VALS(crc_status_vals), 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_dl_harq_id, { "HARQ Id", "mac-lte.dl-phy.harq-id", FT_UINT8, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_context_phy_dl_ndi, { "NDI", "mac-lte.dl-phy.ndi", FT_UINT8, BASE_DEC, 0, 0x0, "New Data Indicator", HFILL } }, { &hf_mac_lte_context_phy_dl_tb, { "TB", "mac-lte.dl-phy.tb", FT_UINT8, BASE_DEC, 0, 0x0, "Transport Block (antenna #)", HFILL } }, /* Out-of-band events */ { &hf_mac_lte_oob_send_preamble, { "PRACH", "mac-lte.preamble-sent", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_oob_send_sr, { "Scheduling Request sent", "mac-lte.sr-req", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_number_of_srs, { "Number of SRs", "mac-lte.sr-req.count", FT_UINT32, BASE_DEC, 0, 0x0, "Number of UEs doing SR in this frame", HFILL } }, { &hf_mac_lte_oob_sr_failure, { "Scheduling Request failure", "mac-lte.sr-failure", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, /*******************************************/ /* MAC shared channel header fields */ { &hf_mac_lte_ulsch, { "UL-SCH", "mac-lte.ulsch", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_ulsch_header, { "UL-SCH Header", "mac-lte.ulsch.header", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_dlsch_header, { "DL-SCH Header", "mac-lte.dlsch.header", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_dlsch, { "DL-SCH", "mac-lte.dlsch", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_sch_subheader, { "SCH sub-header", "mac-lte.sch.subheader", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_mch, { "MCH", "mac-lte.mch", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_mch_header, { "MCH Header", "mac-lte.mch.header", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_mch_subheader, { "MCH sub-header", "mac-lte.mch.subheader", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_sch_reserved, { "SCH reserved bits", "mac-lte.sch.reserved", FT_UINT8, BASE_HEX, NULL, 0xc0, NULL, 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, NULL, 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_mch_reserved, { "MCH reserved bits", "mac-lte.mch.reserved", FT_UINT8, BASE_HEX, NULL, 0xc0, NULL, HFILL } }, { &hf_mac_lte_mch_extended, { "Extension", "mac-lte.mch.extended", FT_UINT8, BASE_HEX, 0, 0x20, "Extension - i.e. further headers after this one", HFILL } }, { &hf_mac_lte_mch_lcid, { "LCID", "mac-lte.mch.lcid", FT_UINT8, BASE_HEX, VALS(mch_lcid_vals), 0x1f, "MCH Logical Channel Identifier", HFILL } }, { &hf_mac_lte_mch_format, { "Format", "mac-lte.mch.format", FT_UINT8, BASE_HEX, VALS(format_vals), 0x80, NULL, HFILL } }, { &hf_mac_lte_mch_length, { "Length", "mac-lte.mch.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, NULL, HFILL } }, { &hf_mac_lte_mch_header_only, { "MAC PDU Header only", "mac-lte.mch.header-only", FT_UINT8, BASE_DEC, VALS(header_only_vals), 0x0, NULL, HFILL } }, /********************************/ /* Data */ { &hf_mac_lte_sch_sdu, { "SDU", "mac-lte.sch.sdu", FT_BYTES, BASE_NONE, 0, 0x0, "Shared channel SDU", HFILL } }, { &hf_mac_lte_mch_sdu, { "SDU", "mac-lte.mch.sdu", FT_BYTES, BASE_NONE, 0, 0x0, "Multicast channel SDU", HFILL } }, { &hf_mac_lte_bch_pdu, { "BCH PDU", "mac-lte.bch.pdu", FT_BYTES, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_pch_pdu, { "PCH PDU", "mac-lte.pch.pdu", FT_BYTES, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_predefined_pdu, { "Predefined data", "mac-lte.predefined-data", FT_BYTES, BASE_NONE, 0, 0x0, "Predefined test data", HFILL } }, { &hf_mac_lte_raw_pdu, { "Raw data", "mac-lte.raw-data", FT_BYTES, BASE_NONE, 0, 0x0, "Raw bytes of PDU (e.g. if CRC error)", HFILL } }, { &hf_mac_lte_padding_data, { "Padding data", "mac-lte.padding-data", FT_BYTES, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_padding_length, { "Padding length", "mac-lte.padding-length", FT_INT32, BASE_DEC, 0, 0x0, "Length of padding data not included at end of frame", HFILL } }, /*********************************/ /* RAR fields */ { &hf_mac_lte_rar, { "RAR", "mac-lte.rar", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_rar_headers, { "RAR Headers", "mac-lte.rar.headers", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_mac_lte_rar_header, { "RAR Header", "mac-lte.rar.header", FT_STRING, BASE_NONE, NULL, 0x0, NULL, 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_DEC, 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, NULL, 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", HFILL } }, { &hf_mac_lte_rar_ul_grant_hopping, { "Hopping Flag", "mac-lte.rar.ul-grant.hopping", FT_UINT8, BASE_DEC, 0, 0x08, NULL, HFILL } }, { &hf_mac_lte_rar_ul_grant_fsrba, { "Fixed sized resource block assignment", "mac-lte.rar.ul-grant.fsrba", FT_UINT16, BASE_DEC, 0, 0x07fe, NULL, HFILL } }, { &hf_mac_lte_rar_ul_grant_tmcs, { "Truncated Modulation and coding scheme", "mac-lte.rar.ul-grant.tmcs", FT_UINT16, BASE_DEC, 0, 0x01e0, NULL, HFILL } }, { &hf_mac_lte_rar_ul_grant_tcsp, { "TPC command for scheduled PUSCH", "mac-lte.rar.ul-grant.tcsp", FT_UINT8, BASE_DEC, 0, 0x01c, NULL, HFILL } }, { &hf_mac_lte_rar_ul_grant_ul_delay, { "UL Delay", "mac-lte.rar.ul-grant.ul-delay", FT_UINT8, BASE_DEC, 0, 0x02, NULL, HFILL } }, { &hf_mac_lte_rar_ul_grant_cqi_request, { "CQI Request", "mac-lte.rar.ul-grant.cqi-request", FT_UINT8, BASE_DEC, 0, 0x01, NULL, HFILL } }, { &hf_mac_lte_rar_temporary_crnti, { "Temporary C-RNTI", "mac-lte.rar.temporary-crnti", FT_UINT16, BASE_DEC, 0, 0x0, NULL, HFILL } }, /**********************/ /* Control PDU fields */ { &hf_mac_lte_control_bsr, { "BSR", "mac-lte.control.bsr", FT_STRING, BASE_NONE, 0, 0x0, "Buffer Status Report", HFILL } }, { &hf_mac_lte_control_bsr_lcg_id, { "Logical Channel Group ID", "mac-lte.control.bsr.lcg-id", FT_UINT8, BASE_DEC, 0, 0xc0, NULL, HFILL } }, { &hf_mac_lte_control_short_bsr_buffer_size, { "Buffer Size", "mac-lte.control.bsr.buffer-size", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &buffer_size_vals_ext, 0x3f, "Buffer Size available in all channels in group", HFILL } }, { &hf_mac_lte_control_long_bsr_buffer_size_0, { "Buffer Size 0", "mac-lte.control.bsr.buffer-size-0", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &buffer_size_vals_ext, 0xfc, "Buffer Size available in logical channel group 0", HFILL } }, { &hf_mac_lte_control_long_bsr_buffer_size_1, { "Buffer Size 1", "mac-lte.control.bsr.buffer-size-1", FT_UINT16, BASE_DEC|BASE_EXT_STRING, &buffer_size_vals_ext, 0x03f0, "Buffer Size available in logical channel group 1", HFILL } }, { &hf_mac_lte_control_long_bsr_buffer_size_2, { "Buffer Size 2", "mac-lte.control.bsr.buffer-size-2", FT_UINT16, BASE_DEC|BASE_EXT_STRING, &buffer_size_vals_ext, 0x0fc0, "Buffer Size available in logical channel group 2", HFILL } }, { &hf_mac_lte_control_long_bsr_buffer_size_3, { "Buffer Size 3", "mac-lte.control.bsr.buffer-size-3", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &buffer_size_vals_ext, 0x3f, "Buffer Size available in logical channel group 3", HFILL } }, { &hf_mac_lte_control_short_ext_bsr_buffer_size, { "Buffer Size", "mac-lte.control.bsr.buffer-size", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &ext_buffer_size_vals_ext, 0x3f, "Buffer Size available in all channels in group", HFILL } }, { &hf_mac_lte_control_long_ext_bsr_buffer_size_0, { "Buffer Size 0", "mac-lte.control.bsr.buffer-size-0", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &ext_buffer_size_vals_ext, 0xfc, "Buffer Size available in logical channel group 0", HFILL } }, { &hf_mac_lte_control_long_ext_bsr_buffer_size_1, { "Buffer Size 1", "mac-lte.control.bsr.buffer-size-1", FT_UINT16, BASE_DEC|BASE_EXT_STRING, &ext_buffer_size_vals_ext, 0x03f0, "Buffer Size available in logical channel group 1", HFILL } }, { &hf_mac_lte_control_long_ext_bsr_buffer_size_2, { "Buffer Size 2", "mac-lte.control.bsr.buffer-size-2", FT_UINT16, BASE_DEC|BASE_EXT_STRING, &ext_buffer_size_vals_ext, 0x0fc0, "Buffer Size available in logical channel group 2", HFILL } }, { &hf_mac_lte_control_long_ext_bsr_buffer_size_3, { "Buffer Size 3", "mac-lte.control.bsr.buffer-size-3", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &ext_buffer_size_vals_ext, 0x3f, "Buffer Size available in logical channel group 3", 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, 0x3f, "Timing Advance (0-1282 - see 36.213, 4.2.3)", HFILL } }, { &hf_mac_lte_control_timing_advance_reserved, { "Reserved", "mac-lte.control.timing-advance.reserved", FT_UINT8, BASE_HEX, 0, 0xc0, "Reserved bits", HFILL } }, { &hf_mac_lte_control_ue_contention_resolution, { "UE Contention Resolution", "mac-lte.control.ue-contention-resolution", FT_STRING, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_control_ue_contention_resolution_identity, { "UE Contention Resolution Identity", "mac-lte.control.ue-contention-resolution.identity", FT_BYTES, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_control_ue_contention_resolution_msg3, { "Msg3", "mac-lte.control.ue-contention-resolution.msg3", FT_FRAMENUM, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_control_ue_contention_resolution_msg3_matched, { "UE Contention Resolution Matches Msg3", "mac-lte.control.ue-contention-resolution.matches-msg3", FT_BOOLEAN, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_control_ue_contention_resolution_time_since_msg3, { "Time since Msg3", "mac-lte.control.ue-contention-resolution.time-since-msg3", FT_UINT32, BASE_DEC, 0, 0x0, "Time in ms since corresponding Msg3", HFILL } }, { &hf_mac_lte_control_power_headroom, { "Power Headroom", "mac-lte.control.power-headroom", FT_STRING, BASE_NONE, 0, 0x0, NULL, 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_level, { "Power Headroom Level", "mac-lte.control.power-headroom.level", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &power_headroom_vals_ext, 0x3f, "Power Headroom Level in dB", HFILL } }, { &hf_mac_lte_control_ext_power_headroom, { "Extended Power Headroom", "mac-lte.control.ext-power-headroom", FT_STRING, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_control_ext_power_headroom_c7, { "SCell Index 7 Power Headroom", "mac-lte.control.ext-power-headroom.c7", FT_BOOLEAN, 8, TFS(&mac_lte_scell_ph_vals), 0x80, NULL, HFILL } }, { &hf_mac_lte_control_ext_power_headroom_c6, { "SCell Index 6 Power Headroom", "mac-lte.control.ext-power-headroom.c6", FT_BOOLEAN, 8, TFS(&mac_lte_scell_ph_vals), 0x40, NULL, HFILL } }, { &hf_mac_lte_control_ext_power_headroom_c5, { "SCell Index 5 Power Headroom", "mac-lte.control.ext-power-headroom.c5", FT_BOOLEAN, 8, TFS(&mac_lte_scell_ph_vals), 0x20, NULL, HFILL } }, { &hf_mac_lte_control_ext_power_headroom_c4, { "SCell Index 4 Power Headroom", "mac-lte.control.ext-power-headroom.c4", FT_BOOLEAN, 8, TFS(&mac_lte_scell_ph_vals), 0x10, NULL, HFILL } }, { &hf_mac_lte_control_ext_power_headroom_c3, { "SCell Index 3 Power Headroom", "mac-lte.control.ext-power-headroom.c3", FT_BOOLEAN, 8, TFS(&mac_lte_scell_ph_vals), 0x08, NULL, HFILL } }, { &hf_mac_lte_control_ext_power_headroom_c2, { "SCell Index 2 Power Headroom", "mac-lte.control.ext-power-headroom.c2", FT_BOOLEAN, 8, TFS(&mac_lte_scell_ph_vals), 0x04, NULL, HFILL } }, { &hf_mac_lte_control_ext_power_headroom_c1, { "SCell Index 1 Power Headroom", "mac-lte.control.ext-power-headroom.c1", FT_BOOLEAN, 8, TFS(&mac_lte_scell_ph_vals), 0x02, NULL, HFILL } }, { &hf_mac_lte_control_ext_power_headroom_reserved, { "Reserved", "mac-lte.control.ext-power-headroom.reserved", FT_UINT8, BASE_DEC, 0, 0x01, "Reserved bit, should be 0", HFILL } }, { &hf_mac_lte_control_ext_power_headroom_power_backoff, { "Power Backoff", "mac-lte.control.ext-power-headroom.power-backoff", FT_BOOLEAN, 8, TFS(&mac_lte_power_backoff_vals), 0x80, NULL, HFILL } }, { &hf_mac_lte_control_ext_power_headroom_value, { "Power Headroom Value", "mac-lte.control.ext-power-headroom.power-headroom-value", FT_BOOLEAN, 8, TFS(&mac_lte_ph_value_vals), 0x40, NULL, HFILL } }, { &hf_mac_lte_control_ext_power_headroom_level, { "Power Headroom Level", "mac-lte.control.ext-power-headroom.level", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &power_headroom_vals_ext, 0x3f, "Power Headroom Level in dB", HFILL } }, { &hf_mac_lte_control_ext_power_headroom_reserved2, { "Reserved", "mac-lte.control.ext-power-headroom.reserved2", FT_UINT8, BASE_DEC, 0, 0xc0, "Reserved bits, should be 0", HFILL } }, { &hf_mac_lte_control_ext_power_headroom_pcmaxc, { "Configured UE Transmit Power", "mac-lte.control.ext-power-headroom.pcmaxc", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &pcmaxc_vals_ext, 0x3f, "Pcmax,c in dBm", HFILL } }, { &hf_mac_lte_control_activation_deactivation, { "Activation/Deactivation", "mac-lte.control.activation-deactivation", FT_STRING, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_control_activation_deactivation_c7, { "SCell Index 7 Status", "mac-lte.control.activation-deactivation.c7", FT_BOOLEAN, 8, TFS(&mac_lte_scell_status_vals), 0x80, NULL, HFILL } }, { &hf_mac_lte_control_activation_deactivation_c6, { "SCell Index 6 Status", "mac-lte.control.activation-deactivation.c6", FT_BOOLEAN, 8, TFS(&mac_lte_scell_status_vals), 0x40, NULL, HFILL } }, { &hf_mac_lte_control_activation_deactivation_c5, { "SCell Index 5 Status", "mac-lte.control.activation-deactivation.c5", FT_BOOLEAN, 8, TFS(&mac_lte_scell_status_vals), 0x20, NULL, HFILL } }, { &hf_mac_lte_control_activation_deactivation_c4, { "SCell Index 4 Status", "mac-lte.control.activation-deactivation.c4", FT_BOOLEAN, 8, TFS(&mac_lte_scell_status_vals), 0x10, NULL, HFILL } }, { &hf_mac_lte_control_activation_deactivation_c3, { "SCell Index 3 Status", "mac-lte.control.activation-deactivation.c3", FT_BOOLEAN, 8, TFS(&mac_lte_scell_status_vals), 0x08, NULL, HFILL } }, { &hf_mac_lte_control_activation_deactivation_c2, { "SCell Index 2 Status", "mac-lte.control.activation-deactivation.c2", FT_BOOLEAN, 8, TFS(&mac_lte_scell_status_vals), 0x04, NULL, HFILL } }, { &hf_mac_lte_control_activation_deactivation_c1, { "SCell Index 1 Status", "mac-lte.control.activation-deactivation.c1", FT_BOOLEAN, 8, TFS(&mac_lte_scell_status_vals), 0x02, NULL, HFILL } }, { &hf_mac_lte_control_activation_deactivation_reserved, { "Reserved", "mac-lte.control.activation-deactivation.reserved", FT_UINT8, BASE_DEC, 0, 0x01, "Reserved bit, should be 0", HFILL } }, { &hf_mac_lte_control_mch_scheduling_info, { "MCH Scheduling Information", "mac-lte.control.mch_scheduling_info", FT_STRING, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_control_mch_scheduling_info_lcid, { "LCID", "mac-lte.control.mch_scheduling_info.lcid", FT_UINT8, BASE_HEX, VALS(mch_lcid_vals), 0xf8, "Logical Channel ID of the MTCH", HFILL } }, { &hf_mac_lte_control_mch_scheduling_info_stop_mtch, { "Stop MTCH", "mac-lte.control.mch_scheduling_info.stop_mtch", FT_UINT16, BASE_DEC, 0, 0x07ff, "Ordinal number of the subframe where the corresponding MTCH stops", HFILL } }, /* Generated fields */ { &hf_mac_lte_dl_harq_resend_original_frame, { "Frame with previous tx", "mac-lte.dlsch.retx.original-frame", FT_FRAMENUM, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_dl_harq_resend_time_since_previous_frame, { "Time since previous tx (ms)", "mac-lte.dlsch.retx.time-since-previous", FT_UINT16, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_dl_harq_resend_next_frame, { "Frame with next tx", "mac-lte.dlsch.retx.next-frame", FT_FRAMENUM, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_dl_harq_resend_time_until_next_frame, { "Time until next tx (ms)", "mac-lte.dlsch.retx.time-until-next", FT_UINT16, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_ul_harq_resend_original_frame, { "Frame with previous tx", "mac-lte.ulsch.retx.original-frame", FT_FRAMENUM, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_ul_harq_resend_time_since_previous_frame, { "Time since previous tx (ms)", "mac-lte.ulsch.retx.time-since-previous", FT_UINT16, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_ul_harq_resend_next_frame, { "Frame with next tx", "mac-lte.ulsch.retx.next-frame", FT_FRAMENUM, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_ul_harq_resend_time_until_next_frame, { "Time until next tx (ms)", "mac-lte.ulsch.retx.time-until-next", FT_UINT16, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_grant_answering_sr, { "First Grant Following SR from", "mac-lte.ulsch.grant-answering-sr", FT_FRAMENUM, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_failure_answering_sr, { "SR which failed", "mac-lte.ulsch.failure-answering-sr", FT_FRAMENUM, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_sr_leading_to_failure, { "This SR fails", "mac-lte.ulsch.failure-answering-sr-frame", FT_FRAMENUM, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_sr_leading_to_grant, { "This SR results in a grant here", "mac-lte.ulsch.grant-answering-sr-frame", FT_FRAMENUM, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_sr_invalid_event, { "Invalid event", "mac-lte.ulsch.sr-invalid-event", FT_NONE, BASE_NONE, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_sr_time_since_request, { "Time since SR (ms)", "mac-lte.ulsch.time-since-sr", FT_UINT32, BASE_DEC, 0, 0x0, NULL, HFILL } }, { &hf_mac_lte_sr_time_until_answer, { "Time until answer (ms)", "mac-lte.ulsch.time-until-sr-answer", FT_UINT32, BASE_DEC, 0, 0x0, NULL, HFILL } }, }; static gint *ett[] = { &ett_mac_lte, &ett_mac_lte_context, &ett_mac_lte_phy_context, &ett_mac_lte_rar_headers, &ett_mac_lte_rar_header, &ett_mac_lte_rar_body, &ett_mac_lte_rar_ul_grant, &ett_mac_lte_ulsch_header, &ett_mac_lte_dlsch_header, &ett_mac_lte_mch_header, &ett_mac_lte_sch_subheader, &ett_mac_lte_mch_subheader, &ett_mac_lte_bch, &ett_mac_lte_bsr, &ett_mac_lte_pch, &ett_mac_lte_activation_deactivation, &ett_mac_lte_contention_resolution, &ett_mac_lte_power_headroom, &ett_mac_lte_extended_power_headroom, &ett_mac_lte_extended_power_headroom_cell, &ett_mac_lte_mch_scheduling_info, &ett_mac_lte_oob }; static enum_val_t show_info_col_vals[] = { {"show-phy", "PHY Info", ShowPHYLayer}, {"show-mac", "MAC Info", ShowMACLayer}, {"show-rlc", "RLC Info", ShowRLCLayer}, {NULL, NULL, -1} }; static enum_val_t lcid_drb_source_vals[] = { {"from-static-stable", "From static table", FromStaticTable}, {"from-configuration-protocol", "From configuration protocol", FromConfigurationProtocol}, {NULL, NULL, -1} }; module_t *mac_lte_module; static uat_field_t lcid_drb_mapping_flds[] = { UAT_FLD_VS(lcid_drb_mappings, lcid, "lcid", drb_lcid_vals, "The MAC LCID"), UAT_FLD_DEC(lcid_drb_mappings, drbid,"drb id (1-32)", "Identifier of logical data channel"), UAT_FLD_VS(lcid_drb_mappings, channel_type, "RLC Channel Type", rlc_channel_type_vals, "The MAC LCID"), UAT_END_FIELDS }; /* 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); /* Obsolete preferences */ prefs_register_obsolete_preference(mac_lte_module, "single_rar"); prefs_register_obsolete_preference(mac_lte_module, "check_reserved_bits"); prefs_register_obsolete_preference(mac_lte_module, "decode_rar_ul_grant"); prefs_register_obsolete_preference(mac_lte_module, "show_rlc_info_column"); prefs_register_obsolete_preference(mac_lte_module, "attempt_to_detect_dl_harq_resend"); prefs_register_obsolete_preference(mac_lte_module, "attempt_to_track_ul_harq_resend"); 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); prefs_register_bool_preference(mac_lte_module, "attempt_rrc_decode", "Attempt to decode BCH, PCH and CCCH data using LTE RRC dissector", "Attempt to decode BCH, PCH and CCCH data using LTE RRC dissector", &global_mac_lte_attempt_rrc_decode); prefs_register_bool_preference(mac_lte_module, "attempt_to_dissect_crc_failures", "Dissect frames that have failed CRC check", "Attempt to dissect frames that have failed CRC check", &global_mac_lte_dissect_crc_failures); prefs_register_bool_preference(mac_lte_module, "heuristic_mac_lte_over_udp", "Try Heuristic LTE-MAC over UDP framing", "When enabled, use heuristic dissector to find MAC-LTE frames sent with " "UDP framing", &global_mac_lte_heur); prefs_register_bool_preference(mac_lte_module, "attempt_to_dissect_srb_sdus", "Attempt to dissect LCID 1&2 as srb1&2", "Will call LTE RLC dissector with standard settings as per RRC spec", &global_mac_lte_attempt_srb_decode); prefs_register_enum_preference(mac_lte_module, "lcid_to_drb_mapping_source", "Source of LCID -> drb channel settings", "Set whether LCID -> drb Table is taken from static table (below) or from " "info learned from control protocol (e.g. RRC)", &global_mac_lte_lcid_drb_source, lcid_drb_source_vals, FALSE); lcid_drb_mappings_uat = uat_new("Static LCID -> drb Table", sizeof(lcid_drb_mapping_t), "drb_logchans", TRUE, (void*) &lcid_drb_mappings, &num_lcid_drb_mappings, UAT_AFFECTS_DISSECTION, /* affects dissection of packets, but not set of named fields */ "", /* TODO: is this ref to help manual? */ lcid_drb_mapping_copy_cb, NULL, NULL, NULL, lcid_drb_mapping_flds ); prefs_register_uat_preference(mac_lte_module, "drb_table", "LCID -> DRB Mappings Table", "A table that maps from configurable lcids -> RLC logical channels", lcid_drb_mappings_uat); prefs_register_uint_preference(mac_lte_module, "bsr_warn_threshold", "BSR size when warning should be issued (0 - 63)", "If any BSR report is >= this number, an expert warning will be added", 10, &global_mac_lte_bsr_warn_threshold); prefs_register_bool_preference(mac_lte_module, "track_sr", "Track status of SRs within UEs", "Track status of SRs, providing links between requests, failure indications and grants", &global_mac_lte_track_sr); prefs_register_enum_preference(mac_lte_module, "layer_to_show", "Which layer info to show in Info column", "Can show PHY, MAC or RLC layer info in Info column", &global_mac_lte_layer_to_show, show_info_col_vals, FALSE); register_init_routine(&mac_lte_init_protocol); } /* Set LCID -> RLC channel mappings from signalling protocol (i.e. RRC or similar). TODO: not using UEID yet - assume all UEs configured identically... */ void set_mac_lte_channel_mapping(guint16 ueid _U_, guint8 lcid, guint8 srbid, guint8 drbid, guint8 rlcMode, guint8 um_sn_length, guint8 ul_priority) { /* Don't bother setting srb details - we just assume AM */ if (srbid != 0) { return; } /* Ignore if LCID is out of range */ if ((lcid < 3) || (lcid > 10)) { return; } /* Set array entry */ dynamic_lcid_drb_mapping[lcid].valid = TRUE; dynamic_lcid_drb_mapping[lcid].drbid = drbid; dynamic_lcid_drb_mapping[lcid].ul_priority = ul_priority; switch (rlcMode) { case RLC_AM_MODE: dynamic_lcid_drb_mapping[lcid].channel_type = rlcAM; break; case RLC_UM_MODE: if (um_sn_length == 5) { dynamic_lcid_drb_mapping[lcid].channel_type = rlcUM5; } else { dynamic_lcid_drb_mapping[lcid].channel_type = rlcUM10; } break; default: break; } } /* Return the configured UL priority for the channel */ static guint8 get_mac_lte_channel_priority(guint16 ueid _U_, guint8 lcid, guint8 direction) { /* Priority only affects UL */ if (direction == DIRECTION_DOWNLINK) { return 0; } /* Won't report value if channel not configured */ if (!dynamic_lcid_drb_mapping[lcid].valid) { return 0; } else { return dynamic_lcid_drb_mapping[lcid].ul_priority; } } /* Function to be called from outside this module (e.g. in a plugin) to get per-packet data */ mac_lte_info *get_mac_lte_proto_data(packet_info *pinfo) { return p_get_proto_data(pinfo->fd, proto_mac_lte); } /* Function to be called from outside this module (e.g. in a plugin) to set per-packet data */ void set_mac_lte_proto_data(packet_info *pinfo, mac_lte_info *p_mac_lte_info) { p_add_proto_data(pinfo->fd, proto_mac_lte, p_mac_lte_info); } void proto_reg_handoff_mac_lte(void) { static dissector_handle_t mac_lte_handle; if (!mac_lte_handle) { mac_lte_handle = find_dissector("mac-lte"); /* Add as a heuristic UDP dissector */ heur_dissector_add("udp", dissect_mac_lte_heur, proto_mac_lte); } }