wireshark/epan/dissectors/packet-mac-lte.c
Guy Harris 659cf0527a UATs could be put into "categories". The categories were defined only
implicitly by the #define name and string they were defined to; not all
UATs neatly fit into any of the categories, so some of them were put
into categories that weren't obviously correct for them, and one - the
display filter macro UAT - wasn't put into any category at all (which
caused crashes when editing them, as the GUI code that handled UAT
changes from a dialog assumed the category field was non-null).

The category was, in practice, used only to decide, in the
aforementioned GUI code, whether the packet summary pane needed to be
updated or not.  It also offered no option of "don't update the packet
summary pane *and* don't redissect anything", which is what would be
appropriate for the display filter macro UAT.

Replace the category with a set of fields indicating what the UAT
affects; we currently offer "dissection", which applies to most UATs
(any UAT in libwireshark presumably affects dissection at a minimum) and
"the set of named fields that exist".  Changing any UAT that affects
dissection requires a redissection; changing any UAT that affects the
set of named fields that exist requires a redissection *and* rebuilding
the packet summary pane.

Perhaps we also need "filtering", so that if you change a display filter
macro, we re-filter, in case the display is currently filtered with a
display filter that uses a macro that changed.

svn path=/trunk/; revision=43603
2012-07-08 01:00:46 +00:00

5809 lines
234 KiB
C

/* Routines for LTE MAC disassembly
*
* Martin Mathieson
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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 <string.h>
#include <epan/packet.h>
#include <epan/expert.h>
#include <epan/prefs.h>
#include <epan/tap.h>
#include <epan/uat.h>
#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 */
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;
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];
/* 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);
/* 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);
/* 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);
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);
}
}
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);
}
}
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);
}
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: <CRC %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);
}
}