diff options
Diffstat (limited to 'epan/dissectors/packet-catapult-dct2000.c')
-rw-r--r-- | epan/dissectors/packet-catapult-dct2000.c | 4068 |
1 files changed, 4068 insertions, 0 deletions
diff --git a/epan/dissectors/packet-catapult-dct2000.c b/epan/dissectors/packet-catapult-dct2000.c new file mode 100644 index 00000000..16c07fa0 --- /dev/null +++ b/epan/dissectors/packet-catapult-dct2000.c @@ -0,0 +1,4068 @@ +/* packet-catapult-dct2000.c + * Routines for Catapult DCT2000 packet stub header disassembly + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include <stdio.h> /* for sscanf() */ + +#include <epan/packet.h> +#include <epan/conversation.h> +#include <epan/expert.h> +#include <epan/prefs.h> +#include <epan/addr_resolv.h> +#include <epan/proto_data.h> + +#include <wsutil/strtoi.h> + +#include <wiretap/catapult_dct2000.h> +#include "packet-umts_fp.h" +#include "packet-umts_rlc.h" + +#include "packet-mac-lte.h" +#include "packet-rlc-lte.h" +#include "packet-pdcp-lte.h" + +#include "packet-mac-nr.h" +#include "packet-pdcp-nr.h" + +void proto_reg_handoff_catapult_dct2000(void); +void proto_register_catapult_dct2000(void); + +/* Protocol and registered fields. */ +static int proto_catapult_dct2000 = -1; + +static int hf_catapult_dct2000_context = -1; +static int hf_catapult_dct2000_port_number = -1; +static int hf_catapult_dct2000_timestamp = -1; +static int hf_catapult_dct2000_protocol = -1; +static int hf_catapult_dct2000_variant = -1; +static int hf_catapult_dct2000_outhdr = -1; +static int hf_catapult_dct2000_direction = -1; +static int hf_catapult_dct2000_encap = -1; +static int hf_catapult_dct2000_unparsed_data = -1; +static int hf_catapult_dct2000_comment = -1; +static int hf_catapult_dct2000_sprint = -1; +static int hf_catapult_dct2000_error_comment = -1; +static int hf_catapult_dct2000_tty = -1; +static int hf_catapult_dct2000_tty_line = -1; +static int hf_catapult_dct2000_dissected_length = -1; + +static int hf_catapult_dct2000_ipprim_addresses = -1; +static int hf_catapult_dct2000_ipprim_src_addr_v4 = -1; +static int hf_catapult_dct2000_ipprim_src_addr_v6 = -1; +static int hf_catapult_dct2000_ipprim_dst_addr_v4 = -1; +static int hf_catapult_dct2000_ipprim_dst_addr_v6 = -1; +static int hf_catapult_dct2000_ipprim_addr_v4 = -1; +static int hf_catapult_dct2000_ipprim_addr_v6 = -1; +static int hf_catapult_dct2000_ipprim_udp_src_port = -1; +static int hf_catapult_dct2000_ipprim_udp_dst_port = -1; +static int hf_catapult_dct2000_ipprim_udp_port = -1; +static int hf_catapult_dct2000_ipprim_tcp_src_port = -1; +static int hf_catapult_dct2000_ipprim_tcp_dst_port = -1; +static int hf_catapult_dct2000_ipprim_tcp_port = -1; +static int hf_catapult_dct2000_ipprim_conn_id = -1; + +static int hf_catapult_dct2000_sctpprim_addresses = -1; +static int hf_catapult_dct2000_sctpprim_dst_addr_v4 = -1; +static int hf_catapult_dct2000_sctpprim_dst_addr_v6 = -1; +static int hf_catapult_dct2000_sctpprim_addr_v4 = -1; +static int hf_catapult_dct2000_sctpprim_addr_v6 = -1; +static int hf_catapult_dct2000_sctpprim_dst_port = -1; + +static int hf_catapult_dct2000_ueid = -1; +static int hf_catapult_dct2000_srbid = -1; +static int hf_catapult_dct2000_drbid = -1; +static int hf_catapult_dct2000_cellid = -1; +static int hf_catapult_dct2000_bcch_transport = -1; +static int hf_catapult_dct2000_rlc_op = -1; +static int hf_catapult_dct2000_rlc_channel_type = -1; +static int hf_catapult_dct2000_rlc_mui = -1; +static int hf_catapult_dct2000_rlc_cnf = -1; +static int hf_catapult_dct2000_rlc_discard_req = -1; +static int hf_catapult_dct2000_carrier_type = -1; +static int hf_catapult_dct2000_cell_group = -1; +static int hf_catapult_dct2000_carrier_id = -1; + +static int hf_catapult_dct2000_security_mode_params = -1; +static int hf_catapult_dct2000_uplink_sec_mode = -1; +static int hf_catapult_dct2000_downlink_sec_mode = -1; +static int hf_catapult_dct2000_ciphering_algorithm = -1; +static int hf_catapult_dct2000_ciphering_key = -1; +static int hf_catapult_dct2000_integrity_algorithm = -1; +static int hf_catapult_dct2000_integrity_key = -1; + +static int hf_catapult_dct2000_lte_ccpri_opcode = -1; +static int hf_catapult_dct2000_lte_ccpri_status = -1; +static int hf_catapult_dct2000_lte_ccpri_channel = -1; + +static int hf_catapult_dct2000_lte_nas_rrc_opcode = -1; +static int hf_catapult_dct2000_lte_nas_rrc_establish_cause = -1; +static int hf_catapult_dct2000_lte_nas_rrc_priority = -1; +static int hf_catapult_dct2000_lte_nas_rrc_release_cause = -1; + +static int hf_catapult_dct2000_nr_nas_s1ap_opcode = -1; + +/* UMTS RLC fields */ +static int hf_catapult_dct2000_rbid = -1; +static int hf_catapult_dct2000_ccch_id = -1; +static int hf_catapult_dct2000_no_crc_error = -1; +static int hf_catapult_dct2000_crc_error = -1; +static int hf_catapult_dct2000_clear_tx_buffer = -1; +static int hf_catapult_dct2000_buffer_occupancy = -1; +static int hf_catapult_dct2000_pdu_size = -1; +static int hf_catapult_dct2000_ueid_type = -1; +static int hf_catapult_dct2000_tx_priority = -1; +static int hf_catapult_dct2000_last_in_seg_set = -1; +static int hf_catapult_dct2000_rx_timing_deviation = -1; +static int hf_catapult_dct2000_transport_channel_type = -1; +static int hf_catapult_dct2000_no_padding_bits = -1; + +static int hf_catapult_dct2000_rawtraffic_interface = -1; +static int hf_catapult_dct2000_rawtraffic_direction = -1; +static int hf_catapult_dct2000_rawtraffic_pdu = -1; + + +/* Variables used for preferences */ +static gboolean catapult_dct2000_try_ipprim_heuristic = TRUE; +static gboolean catapult_dct2000_try_sctpprim_heuristic = TRUE; +static gboolean catapult_dct2000_dissect_lte_rrc = TRUE; +static gboolean catapult_dct2000_dissect_mac_lte_oob_messages = TRUE; +static gboolean catapult_dct2000_dissect_old_protocol_names = FALSE; +static gboolean catapult_dct2000_use_protocol_name_as_dissector_name = FALSE; + +/* Protocol subtree. */ +static int ett_catapult_dct2000 = -1; +static int ett_catapult_dct2000_ipprim = -1; +static int ett_catapult_dct2000_sctpprim = -1; +static int ett_catapult_dct2000_tty = -1; +static int ett_catapult_dct2000_security_mode_params = -1; + +static expert_field ei_catapult_dct2000_lte_ccpri_status_error = EI_INIT; +static expert_field ei_catapult_dct2000_error_comment_expert = EI_INIT; +static expert_field ei_catapult_dct2000_string_invalid = EI_INIT; + +static const value_string direction_vals[] = { + { 0, "Sent" }, + { 1, "Received" }, + { 0, NULL }, +}; + +static const value_string encap_vals[] = { + { WTAP_ENCAP_RAW_IP, "Raw IP" }, + { WTAP_ENCAP_ETHERNET, "Ethernet" }, + { WTAP_ENCAP_ISDN, "LAPD" }, + { WTAP_ENCAP_ATM_PDUS_UNTRUNCATED, "ATM (PDUs untruncated)" }, + { WTAP_ENCAP_PPP, "PPP" }, + { DCT2000_ENCAP_SSCOP, "SSCOP" }, + { WTAP_ENCAP_FRELAY, "Frame Relay" }, + { WTAP_ENCAP_MTP2, "MTP2" }, + { DCT2000_ENCAP_NBAP, "NBAP" }, + { DCT2000_ENCAP_UNHANDLED, "No Direct Encapsulation" }, + { 0, NULL }, +}; + +static const value_string bcch_transport_vals[] = { + { BCH_TRANSPORT, "BCH" }, + { DLSCH_TRANSPORT, "DLSCH" }, + { 0, NULL }, +}; + + +#define RLC_MGMT_ASSIGN 0x41 +#define RLC_AM_DATA_REQ 0x60 +#define RLC_AM_DATA_IND 0x61 +#define RLC_AM_DATA_CONF 0x62 +#define RLC_UM_DATA_REQ 0x70 +#define RLC_UM_DATA_IND 0x71 +#define RLC_UM_DATA_CONF 0x74 +#define RLC_TR_DATA_REQ 0x80 +#define RLC_TR_DATA_IND 0x81 +#define RLC_TR_DATA_CONF 0x83 + +static const value_string rlc_op_vals[] = { + { RLC_AM_DATA_REQ, "[UL] [AM]" }, + { RLC_AM_DATA_IND, "[DL] [AM]" }, + { RLC_UM_DATA_REQ, "[UL] [UM]"}, + { RLC_UM_DATA_IND, "[DL] [UM]"}, + { RLC_TR_DATA_REQ, "[UL] [TM]"}, + { RLC_TR_DATA_IND, "[DL] [TM]"}, + { 0, NULL } +}; + + +static const value_string rlc_logical_channel_vals[] = { + { Channel_DCCH, "DCCH"}, + { Channel_BCCH, "BCCH"}, + { Channel_CCCH, "CCCH"}, + { Channel_PCCH, "PCCH"}, + { 0, NULL} +}; + + +#define CCPRI_REQ 1 +#define CCPRI_IND 2 + +static const value_string ccpri_opcode_vals[] = { + { CCPRI_REQ, "REQUEST"}, + { CCPRI_IND, "INDICATION"}, + { 0, NULL} +}; + +static const value_string rlc_rbid_vals[] = { + { 1, "DCH1"}, + { 2, "DCH2"}, + { 3, "DCH3"}, + { 4, "DCH4"}, + { 5, "DCH5"}, + { 6, "DCH6"}, + { 7, "DCH7"}, + { 8, "DCH8"}, + { 9, "DCH9"}, + { 10, "DCH10"}, + { 11, "DCH11"}, + { 12, "DCH12"}, + { 13, "DCH13"}, + { 14, "DCH14"}, + { 15, "DCH15"}, + { 17, "BCCH"}, + { 18, "CCCH"}, + { 19, "PCCH"}, + { 20, "SHCCH"}, + { 21, "CTCH"}, + { 23, "MCCH"}, + { 24, "MSCH"}, + { 25, "MTCH"}, + { 0, NULL} +}; +static value_string_ext rlc_rbid_vals_ext = VALUE_STRING_EXT_INIT(rlc_rbid_vals); + +static const value_string ueid_type_vals[] = { + { 0, "URNTI"}, + { 1, "CRNTI"}, + { 0, NULL} +}; + +static const value_string transport_channel_type_vals[] = { + { 1, "RACH"}, + { 2, "FACH"}, + { 3, "BCH"}, + { 4, "PCH"}, + { 6, "USCH"}, + { 7, "DSCH"}, + { 8, "DCH"}, + { 9, "HSDSCH"}, + { 10, "EDCH"}, + { 0, NULL} +}; + +#define LTE_NAS_RRC_DATA_IND 0x02 +#define LTE_NAS_RRC_DATA_REQ 0x03 +#define LTE_NAS_RRC_ESTABLISH_REQ 0x06 +#define LTE_NAS_RRC_RELEASE_IND 0x08 + +static const value_string lte_nas_rrc_opcode_vals[] = { + { LTE_NAS_RRC_DATA_IND, "Data-Ind"}, + { LTE_NAS_RRC_DATA_REQ, "Data-Req"}, + { LTE_NAS_RRC_ESTABLISH_REQ, "Establish-Req"}, + { LTE_NAS_RRC_RELEASE_IND, "Release-Ind"}, + { 0, NULL} +}; + + +#define NAS_S1AP_DATA_REQ 0x00 +#define NAS_S1AP_DATA_IND 0x01 + +static const value_string nas_s1ap_opcode_vals[] = { + { NAS_S1AP_DATA_REQ, "Data-Req"}, + { NAS_S1AP_DATA_IND, "Data-Ind"}, + { 0, NULL} +}; + + +/* Distinguish between similar 4G or 5G protocols */ +enum LTE_or_NR { + LTE, + NR +}; + +static const value_string carrier_type_vals[] = { + { 0, "LTE"}, + { 1, "CatM"}, + { 2, "NBIoT"}, + { 3, "NR"}, + { 0, NULL} +}; + + +static const value_string security_mode_vals[] = { + { 0, "None"}, + { 1, "Integrity only"}, + { 2, "Ciphering and Integrity"}, + { 0, NULL} +}; + +static const value_string ciphering_algorithm_vals[] = { + { 0, "EEA0"}, + { 1, "EEA1"}, + { 2, "EEA2"}, + { 3, "EEA3"}, + { 0, NULL} +}; + +static const value_string integrity_algorithm_vals[] = { + { 0, "EIA0"}, + { 1, "EIA1"}, + { 2, "EIA2"}, + { 3, "EIA3"}, + { 0, NULL} +}; + + +#define MAX_OUTHDR_VALUES 32 + +extern int proto_fp; +extern int proto_umts_rlc; + +extern int proto_rlc_lte; +extern int proto_pdcp_lte; + + +static dissector_handle_t mac_lte_handle; +static dissector_handle_t rlc_lte_handle; +static dissector_handle_t pdcp_lte_handle; +static dissector_handle_t catapult_dct2000_handle; +static dissector_handle_t nrup_handle; + +static dissector_handle_t mac_nr_handle; + +static dissector_handle_t eth_handle; + +static dissector_handle_t look_for_dissector(const char *protocol_name); +static guint parse_outhdr_string(const guchar *outhdr_string, gint outhdr_length, guint *outhdr_values); + +static void attach_fp_info(packet_info *pinfo, gboolean received, + const char *protocol_name, int variant, + guint *outhdr_values, guint outhdr_values_found); +static void attach_rlc_info(packet_info *pinfo, guint32 urnti, guint8 rbid, + gboolean is_sent, guint *outhdr_values, + guint outhdr_values_found); + +static void attach_mac_lte_info(packet_info *pinfo, guint *outhdr_values, + guint outhdr_values_found); +static void attach_rlc_lte_info(packet_info *pinfo, guint *outhdr_values, + guint outhdr_values_found); +static void attach_pdcp_lte_info(packet_info *pinfo, guint *outhdr_values, + guint outhdr_values_found); + + +/* Return the number of bytes used to encode the length field + (we're not interested in the length value itself) */ +static int skipASNLength(guint8 value) +{ + if ((value & 0x80) == 0) + { + return 1; + } + else + { + return ((value & 0x03) == 1) ? 2 : 3; + } +} + + +/* Look for the protocol data within an ipprim packet. + Only set *data_offset if data field found. */ +static gboolean find_ipprim_data_offset(tvbuff_t *tvb, int *data_offset, guint8 direction, + guint32 *source_addr_offset, guint8 *source_addr_length, + guint32 *dest_addr_offset, guint8 *dest_addr_length, + guint32 *source_port_offset, guint32 *dest_port_offset, + port_type *type_of_port, + guint16 *conn_id_offset) +{ + guint8 length; + int offset = *data_offset; + + /* Get the ipprim command code. */ + guint8 tag = tvb_get_guint8(tvb, offset++); + + /* Only accept UDP or TCP data request or indication */ + switch (tag) { + case 0x23: /* UDP data request */ + case 0x24: /* UDP data indication */ + *type_of_port = PT_UDP; + break; + case 0x45: /* TCP data request */ + case 0x46: /* TCP data indication */ + *type_of_port = PT_TCP; + break; + default: + return FALSE; + } + + /* Skip any other TLC fields before reach payload */ + while (tvb_reported_length_remaining(tvb, offset) > 2) { + /* Look at next tag */ + tag = tvb_get_guint8(tvb, offset++); + + /* Is this the data payload we're expecting? */ + if (((tag == 0x34) && (*type_of_port == PT_UDP)) || + ((tag == 0x48) && (*type_of_port == PT_TCP))) { + + *data_offset = offset; + return TRUE; + } + else { + /* Read length in next byte */ + length = tvb_get_guint8(tvb, offset++); + + if (tag == 0x31 && length >=4) { + /* Remote IP address */ + if (direction == 0) { + /* Sent *to* remote, so dest */ + *dest_addr_offset = offset; + *dest_addr_length = (length/4) * 4; + } + else { + *source_addr_offset = offset; + *source_addr_length = (length/4) * 4; + } + + /* Remote port follows (if present) */ + if ((length % 4) == 2) { + if (direction == 0) { + *dest_port_offset = offset + *dest_addr_length; + } + else { + *source_port_offset = offset + *source_addr_length; + } + } + } + else + if (tag == 0x32) { + if (length == 4 || length == 16) { + /* Local IP address */ + if (direction == 0) { + /* Sent *from* local, so source */ + *source_addr_offset = offset; + *source_addr_length = length; + } + else { + *dest_addr_offset = offset; + *dest_addr_length = length; + } + } + } + else + if (tag == 0x33 && length == 2) { + /* Get local port */ + if (direction == 0) { + /* Sent from local, so source */ + *source_port_offset = offset; + } + else { + *dest_port_offset = offset; + } + } + else + if (tag == 0x36 && length == 2) { + /* Get conn_id */ + *conn_id_offset = offset; + } + + /* Skip the length of the indicated value */ + offset += length; + } + } + + /* No data found... */ + return FALSE; +} + + + +/* Look for the protocol data within an sctpprim (variant 1 or 2...) packet. + Only set *data_offset if data field found. */ +static gboolean find_sctpprim_variant1_data_offset(tvbuff_t *tvb, int *data_offset, + guint32 *dest_addr_offset, + guint16 *dest_addr_length, + guint32 *dest_port_offset) +{ + int offset = *data_offset; + + /* Get the sctpprim command code. */ + guint8 first_tag = tvb_get_guint8(tvb, offset++); + guint8 tag; + guint8 first_length_byte; + + /* Only accept interested in data requests or indications */ + switch (first_tag) { + case 0x04: /* data request */ + case 0x62: /* data indication */ + break; + default: + return FALSE; + } + + first_length_byte = tvb_get_guint8(tvb, offset); + offset += skipASNLength(first_length_byte); + + /* Skip any other fields before reach payload */ + while (tvb_reported_length_remaining(tvb, offset) > 2) { + /* Look at next tag */ + tag = tvb_get_guint8(tvb, offset++); + + /* Is this the data payload we're expecting? */ + if (tag == 0x19) { + *data_offset = offset; + return TRUE; + } + else { + /* Skip length field */ + offset++; + switch (tag) { + case 0x0a: /* destPort */ + *dest_port_offset = offset; + offset += 2; + break; + + case 0x01: /* sctpInstanceNum */ + case 0x1e: /* strseqnum */ + case 0x0d: /* streamnum */ + offset += 2; + continue; + + case 0x09: /* ipv4Address */ + *dest_addr_offset = offset; + *dest_addr_length = 4; + offset += 4; + break; + + case 0x1d: + case 0x0c: /* payloadType */ + offset += 4; + continue; + + default: + /* Fail if not a known header field */ + return FALSE; + } + } + } + + /* No data found... */ + return FALSE; +} + +/* Look for the protocol data within an sctpprim (variant 3) packet. + Return value indicates whether this header found. + Only set *data_offset if data field found. */ +static gboolean find_sctpprim_variant3_data_offset(tvbuff_t *tvb, int *data_offset, + guint32 *dest_addr_offset, + guint16 *dest_addr_length, + guint32 *dest_port_offset) +{ + guint16 tag = 0; + guint16 length = 0; + int offset = *data_offset; + + /* Get the sctpprim (2 byte) command code. */ + guint16 top_tag = tvb_get_ntohs(tvb, offset); + offset += 2; + + /* Only interested in data requests or indications */ + if ((top_tag != 0x0400) && /* SendDataReq */ + (top_tag != 0x6200)) { /* DataInd */ + return FALSE; + } + + /* Overall length field is next 2 bytes */ + offset += 2; + + /* Rx/Tx ops have different formats */ + + /*****************/ + /* DataInd */ + if (top_tag == 0x6200) { + /* Next 2 bytes are associate ID */ + offset += 2; + + /* Next 2 bytes are destination port */ + *dest_port_offset = offset; + offset += 2; + + /* Destination address should follow - check tag */ + tag = tvb_get_ntohs(tvb, offset); + if (tag != 0x0900) { + return FALSE; + } + else { + /* Skip tag */ + offset += 2; + + /* Length field */ + length = tvb_get_ntohs(tvb, offset) / 2; + if ((length != 4) && (length != 16)) + { + return FALSE; + } + offset += 2; + + /* Address data is here */ + *dest_addr_offset = offset; + *dest_addr_length = length; + + offset += length; + } + + /* Not interested in remaining (fixed) fields */ + if (tvb_reported_length_remaining(tvb, offset) > (4 + 2 + 2 + 4)) { + offset += (4 + 2 + 2 + 4); + } + else { + return FALSE; + } + + /* Data should now be here */ + tag = tvb_get_ntohs(tvb, offset); + offset += 2; + if (tag == 0x1900) { + /* 2-byte length field */ + offset += 2; + + /* Data is here!!! */ + *data_offset = offset; + return TRUE; + } + else { + return FALSE; + } + } + + /***********************************/ + /* SendDataReq (top_tag == 0x0400) */ + else { + /* AssociateId should follow - check tag */ + tag = tvb_get_ntohs(tvb, offset); + if (tag != 0x2400) { + return FALSE; + } + else { + /* Skip tag */ + offset += 2; + + /* Skip 2-byte value */ + offset += 2; + } + + /* Get tag */ + tag = tvb_get_ntohs(tvb, offset); + offset += 2; + + /* Some optional params */ + while ((tag != 0x0c00) && (tvb_reported_length_remaining(tvb, offset) > 4)) { + switch (tag) { + case 0x0900: /* Dest address */ + /* Length field */ + length = tvb_get_ntohs(tvb, offset) / 2; + if ((length != 4) && (length != 16)) { + return FALSE; + } + offset += 2; + + /* Address data is here */ + *dest_addr_offset = offset; + *dest_addr_length = length; + + offset += length; + break; + + case 0x0a00: /* Dest port number */ + *dest_port_offset = offset; + offset += 2; + break; + + case 0x0d00: /* StreamNum */ + offset += 2; + break; + + + default: + return FALSE; + } + + /* Get the next tag */ + tag = tvb_get_ntohs(tvb, offset); + offset += 2; + } + + + /* Mandatory payload type */ + if (tag != 0x0c00) { + return FALSE; + } + length = tvb_get_ntohs(tvb, offset) / 2; + offset += 2; + offset += length; + + + /* Optional options */ + tag = tvb_get_ntohs(tvb, offset); + offset += 2; + if (tag == 0x0b00) { + length = tvb_get_ntohs(tvb, offset) / 2; + offset += 2; + + offset += length; + + /* Get next tag */ + tag = tvb_get_ntohs(tvb, offset); + offset += 2; + } + + + /* Data should now be here!! */ + if (tag == 0x1900) { + /* 2-byte length field */ + offset += 2; + + /* Data is here!!! */ + *data_offset = offset; + return TRUE; + } + else { + return FALSE; + } + } +} + + +/* Dissect a UMTS RLC frame by: + - parsing the primitive header + - passing those values + outhdeader to dissector + - calling the UMTS RLC dissector */ +static void dissect_rlc_umts(tvbuff_t *tvb, gint offset, + packet_info *pinfo, proto_tree *tree, + gboolean is_sent, guint *outhdr_values, + guint outhdr_values_found) +{ + guint8 tag; + gboolean ueid_set = FALSE, rbid_set=FALSE; + guint32 ueid = 0; + guint8 rbid = 0; + guint8 length; + tvbuff_t *rlc_tvb; + dissector_handle_t rlc_umts_handle = 0; + + /* Top-level opcode */ + tag = tvb_get_guint8(tvb, offset++); + switch (tag) { + case 0xc0: /* mac data request */ + case 0xc1: /* mac data indication */ + break; + + default: + /* No data to dissect */ + return; + } + + /* Keep going until reach data tag or end of frame */ + while ((tag != 0x41) && tvb_reported_length_remaining(tvb, offset)) { /* i.e. Data */ + tag = tvb_get_guint8(tvb, offset++); + switch (tag) { + case 0x72: /* UE Id */ + ueid = tvb_get_ntohl(tvb, offset); + offset += 2; + proto_tree_add_item(tree, hf_catapult_dct2000_ueid, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + ueid_set = TRUE; + break; + case 0xa2: /* RBID */ + offset++; /* skip length */ + rbid = tvb_get_guint8(tvb, offset); + proto_tree_add_item(tree, hf_catapult_dct2000_rbid, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + rbid_set = TRUE; + break; + case 0x22: /* CCCH-id setting rbid to CCCH! */ + offset++; /* skip length */ + proto_tree_add_item(tree, hf_catapult_dct2000_ccch_id, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + rbid = 18; + break; + case 0xc4: /* No CRC error */ + proto_tree_add_item(tree, hf_catapult_dct2000_no_crc_error, tvb, offset-1, 1, ENC_NA); + break; + case 0xc5: /* CRC error */ + proto_tree_add_item(tree, hf_catapult_dct2000_crc_error, tvb, offset-1, 1, ENC_NA); + break; + case 0xf7: /* Clear Tx Buffer */ + proto_tree_add_item(tree, hf_catapult_dct2000_clear_tx_buffer, tvb, offset-1, 1, ENC_NA); + break; + + case 0x41: /* Data !!! */ + offset += skipASNLength(tvb_get_guint8(tvb, offset)); + break; + + default: + /* For other fields, just skip length and following data */ + length = tvb_get_guint8(tvb, offset++); + switch (tag) { + case 0x42: /* Buffer Occupancy */ + proto_tree_add_item(tree, hf_catapult_dct2000_buffer_occupancy, tvb, offset, length, ENC_BIG_ENDIAN); + break; + case 0x49: /* PDU Size */ + proto_tree_add_item(tree, hf_catapult_dct2000_pdu_size, tvb, offset, 2, ENC_LITTLE_ENDIAN); + break; + case 0x47: /* UEId type */ + proto_tree_add_item(tree, hf_catapult_dct2000_ueid_type, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + case 0x4e: /* Tx Priority */ + proto_tree_add_item(tree, hf_catapult_dct2000_tx_priority, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + case 0x4c: /* Last in seg set */ + proto_tree_add_item(tree, hf_catapult_dct2000_last_in_seg_set, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + case 0x43: /* Rx timing deviation */ + proto_tree_add_item(tree, hf_catapult_dct2000_rx_timing_deviation, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + case 0x46: /* Transport channel type */ + proto_tree_add_item(tree, hf_catapult_dct2000_transport_channel_type, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + case 0xc2: /* Number of padding bits */ + proto_tree_add_item(tree, hf_catapult_dct2000_no_padding_bits, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + + default: + break; + + + } + offset += length; + } + } + + /* Have we got enough info to call dissector */ + if ((tag == 0x41) && ueid_set && rbid_set) { + attach_rlc_info(pinfo, ueid, rbid, is_sent, outhdr_values, + outhdr_values_found); + + /* Set appropriate RLC dissector handle */ + switch (rbid) { + case 1: case 2: case 3: case 4: case 5: + case 6: case 7: case 8: case 9: case 10: + case 11: case 12: case 13: case 14: case 15: + /* DCH channels. */ + /* TODO: can't really tell if these are control or transport... + maybe control with preferences (UAT?) between "rlc.ps_dtch" and "rlc.dcch" ? */ + rlc_umts_handle = find_dissector("rlc.dch_unknown"); + break; + case 18: + rlc_umts_handle = find_dissector("rlc.ccch"); + break; + case 21: + rlc_umts_handle = find_dissector("rlc.ctch"); + break; + + default: + /* Give up here */ + return; + } + + /* Call UMTS RLC dissector */ + if (rlc_umts_handle != 0) { + rlc_tvb = tvb_new_subset_remaining(tvb, offset); + call_dissector_only(rlc_umts_handle, rlc_tvb, pinfo, tree, NULL); + } + } +} + +static char* get_key(tvbuff_t*tvb, gint offset) +{ + static gchar key[33]; + for (int n=0; n < 16; n++) { + snprintf(&key[n*2], 33-(n*2), "%02x", tvb_get_guint8(tvb, offset+n)); + } + return key; +} + + +/* Dissect an RRC LTE or NR frame by first parsing the header entries then passing + the data to the RRC dissector, according to direction and channel type. */ +static void dissect_rrc_lte_nr(tvbuff_t *tvb, gint offset, + packet_info *pinfo, proto_tree *tree, + enum LTE_or_NR lte_or_nr) +{ + guint8 opcode, tag; + dissector_handle_t protocol_handle = 0; + gboolean isUplink = FALSE; + LogicalChannelType logicalChannelType; + guint16 cell_id; + guint8 bcch_transport = 0; + guint32 ueid = 0; + tvbuff_t *rrc_tvb; + + /* Top-level opcode */ + opcode = tvb_get_guint8(tvb, offset++); + switch (opcode) { + case 0x00: /* Data_Req_UE */ + case 0x05: /* Data_Req_UE_SM */ + case 0x04: /* Data_Ind_eNodeB */ + isUplink = TRUE; + break; + + case 0x02: /* Data_Req_eNodeB */ + case 0x03: /* Data_Ind_UE */ + case 0x07: /* Data_Ind_UE_SM */ + isUplink = FALSE; + break; + + default: + /* Unexpected opcode tag! */ + return; + } + + /* Skip length */ + offset += skipASNLength(tvb_get_guint8(tvb, offset)); + + /* Get next tag */ + tag = tvb_get_guint8(tvb, offset++); + switch (tag) { + case 0x12: /* UE_Id_LCId */ + { + /* Dedicated channel info */ + + /* Skip length */ + offset++; + + logicalChannelType = Channel_DCCH; + + /* UEId */ + proto_tree_add_item_ret_uint(tree, hf_catapult_dct2000_ueid, tvb, offset, 2, ENC_BIG_ENDIAN, &ueid); + offset += 2; + + /* Get tag of channel type */ + tag = tvb_get_guint8(tvb, offset++); + + switch (tag) { + case 0: + offset++; + col_append_fstr(pinfo->cinfo, COL_INFO, " SRB:%u", + tvb_get_guint8(tvb, offset)); + proto_tree_add_item(tree, hf_catapult_dct2000_srbid, + tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + break; + case 1: + offset++; + col_append_fstr(pinfo->cinfo, COL_INFO, " DRB:%u", + tvb_get_guint8(tvb, offset)); + proto_tree_add_item(tree, hf_catapult_dct2000_drbid, + tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + break; + + default: + /* Unexpected channel type */ + return; + } + + break; + } + + case 0x1a: /* Cell_LCId */ + + /* Common channel info */ + + /* Skip length */ + offset++; + + /* Cell-id */ + proto_tree_add_item(tree, hf_catapult_dct2000_cellid, + tvb, offset, 2, ENC_BIG_ENDIAN); + cell_id = tvb_get_ntohs(tvb, offset); + offset += 2; + + /* Logical channel type */ + proto_tree_add_item(tree, hf_catapult_dct2000_rlc_channel_type, + tvb, offset, 1, ENC_BIG_ENDIAN); + logicalChannelType = (LogicalChannelType)tvb_get_guint8(tvb, offset); + offset++; + + /* Won't be seen if RRC decoder is called... */ + col_append_fstr(pinfo->cinfo, COL_INFO, " cell-id=%u %s", + cell_id, + val_to_str_const(logicalChannelType, rlc_logical_channel_vals, + "UNKNOWN-CHANNEL")); + + + switch (logicalChannelType) { + case Channel_BCCH: + /* Skip length */ + offset++; + + /* Transport channel type */ + bcch_transport = tvb_get_guint8(tvb, offset); + proto_tree_add_item(tree, hf_catapult_dct2000_bcch_transport, + tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + break; + + case Channel_CCCH: + /* Skip length */ + offset++; + + /* UEId */ + proto_tree_add_item(tree, hf_catapult_dct2000_ueid, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + break; + + default: + break; + } + break; + + default: + /* Unexpected tag */ + return; + } + + /* Optional Carrier Id */ + if (tvb_get_guint8(tvb, offset)==0x1e) { + offset += 2; /* tag + len of 1 */ + proto_tree_add_item(tree, hf_catapult_dct2000_carrier_id, + tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } + + /* Optional Carrier Type */ + if (tvb_get_guint8(tvb, offset)==0x20) { + offset += 2; + proto_tree_add_item(tree, hf_catapult_dct2000_carrier_type, + tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } + + /* Optional Cell Group */ + if (tvb_get_guint8(tvb, offset)==0x22) { + offset += 2; + proto_tree_add_item(tree, hf_catapult_dct2000_cell_group, + tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } + + if (opcode == 0x07) { + /* Data_Ind_UE_SM - 1 byte MAC */ + offset++; + } + else if (opcode == 0x05) { + /* Data_Req_UE_SM - SecurityMode Params */ + /* N.B. DRB keys do not get configured here.. */ + offset++; /* tag */ + guint8 len = tvb_get_guint8(tvb, offset++); /* length */ + + /* Uplink Sec Mode */ + proto_item *sc_ti; + proto_tree *sc_tree; + sc_ti = proto_tree_add_item(tree, hf_catapult_dct2000_security_mode_params, tvb, offset, len, ENC_NA); + sc_tree = proto_item_add_subtree(sc_ti, ett_catapult_dct2000_security_mode_params); + + guint32 uplink_sec_mode; + proto_tree_add_item_ret_uint(sc_tree, hf_catapult_dct2000_uplink_sec_mode, + tvb, offset++, 1, ENC_BIG_ENDIAN, &uplink_sec_mode); + + /* Downlink Sec Mode */ + guint32 downlink_sec_mode; + proto_tree_add_item_ret_uint(sc_tree, hf_catapult_dct2000_downlink_sec_mode, + tvb, offset++, 1, ENC_BIG_ENDIAN, &downlink_sec_mode); + + if (len > 2) { + offset++; /* tag Should be 0x21 */ + offset++; /* len */ + + tag = tvb_get_guint8(tvb, offset++); + if (tag == 0x25) { + /* Cell Group Id */ + offset++; + proto_tree_add_item(sc_tree, hf_catapult_dct2000_cell_group, + tvb, offset, 1, ENC_BIG_ENDIAN); + } + + /* Optional cryptParams */ + if (tag == 0x2) { + guint32 cipher_algorithm; + + len = tvb_get_guint8(tvb, offset++); + + /* Cipher algorithm (required) */ + offset += 2; /* Skip tag and length */ + proto_tree_add_item_ret_uint(sc_tree, hf_catapult_dct2000_ciphering_algorithm, + tvb, offset++, 1, ENC_BIG_ENDIAN, &cipher_algorithm); + + /* Ciphering key (optional) */ + if (len > 3) { + /* Skip tag and length */ + offset += 2; + proto_tree_add_item(sc_tree, hf_catapult_dct2000_ciphering_key, + tvb, offset, 16, ENC_NA); + gchar *key = get_key(tvb, offset); + + if (!PINFO_FD_VISITED(pinfo)) { + if (lte_or_nr == NR) { + set_pdcp_nr_rrc_ciphering_key(ueid, key, pinfo->num); + } + else { + set_pdcp_lte_rrc_ciphering_key(ueid, key, pinfo->num); + } + } + offset += 16; + } + } + else { + offset--; + } + + /* Now should be Auth params (required) */ + guint32 integrity_algorithm; + /* Skip tag */ + offset++; + + len = tvb_get_guint8(tvb, offset++); + + /* Integrity algorithm (required) */ + offset += 2; /* Skip tag and length */ + proto_tree_add_item_ret_uint(sc_tree, hf_catapult_dct2000_integrity_algorithm, + tvb, offset++, 1, ENC_BIG_ENDIAN, &integrity_algorithm); + + /* Integrity key (optional */ + if (len > 3) { + /* Skip tag and length */ + offset += 2; + proto_tree_add_item(sc_tree, hf_catapult_dct2000_integrity_key, + tvb, offset, 16, ENC_NA); + gchar *key = get_key(tvb, offset); + + if (!PINFO_FD_VISITED(pinfo)) { + if (lte_or_nr == NR) { + set_pdcp_nr_rrc_integrity_key(ueid, key, pinfo->num); + } + else { + set_pdcp_lte_rrc_integrity_key(ueid, key, pinfo->num); + } + } + offset += 16; + } + } + } + + /* Optional data tag may follow */ + if (!tvb_reported_length_remaining(tvb, offset)) { + return; + } + tag = tvb_get_guint8(tvb, offset++); + if (tag != 0xaa) { + return; + } + + /* Skip length */ + offset += skipASNLength(tvb_get_guint8(tvb, offset)); + + /* Look up dissector handle corresponding to direction and channel type */ + if (isUplink) { + + /* Uplink channel types */ + switch (logicalChannelType) { + case Channel_DCCH: + if (lte_or_nr == LTE) { + protocol_handle = find_dissector("lte_rrc.ul_dcch"); + } + else { + protocol_handle = find_dissector("nr-rrc.ul.dcch"); + } + break; + case Channel_CCCH: + if (lte_or_nr == LTE) { + protocol_handle = find_dissector("lte_rrc.ul_ccch"); + } + else { + if (tvb_captured_length_remaining(tvb, offset) == 6) { + protocol_handle = find_dissector("nr-rrc.ul.ccch"); + } + else { + /* Should be 8 bytes.. */ + protocol_handle = find_dissector("nr-rrc.ul.ccch1"); + } + } + break; + + default: + /* Unknown Uplink channel type */ + break; + } + } else { + + /* Downlink channel types */ + switch (logicalChannelType) { + case Channel_DCCH: + if (lte_or_nr == LTE) { + protocol_handle = find_dissector("lte_rrc.dl_dcch"); + } + else { + protocol_handle = find_dissector("nr-rrc.dl.dcch"); + } + break; + case Channel_CCCH: + if (lte_or_nr == LTE) { + protocol_handle = find_dissector("lte_rrc.dl_ccch"); + } + else { + protocol_handle = find_dissector("nr-rrc.dl.ccch"); + } + break; + case Channel_PCCH: + if (lte_or_nr == LTE) { + protocol_handle = find_dissector("lte_rrc.pcch"); + } + else { + protocol_handle = find_dissector("nr-rrc.pcch"); + } + break; + case Channel_BCCH: + if (bcch_transport == 1) { + if (lte_or_nr == LTE) { + protocol_handle = find_dissector("lte_rrc.bcch_bch"); + } + else { + protocol_handle = find_dissector("nr-rrc.bcch.bch"); + } + } + else { + if (lte_or_nr == LTE) { + protocol_handle = find_dissector("lte_rrc.bcch_dl_sch"); + } + else { + protocol_handle = find_dissector("nr-rrc.bcch.dl.sch"); + } + } + break; + + default: + /* Unknown Downlink channel type */ + break; + } + } + + /* Send to RRC dissector, if got here, have sub-dissector and some data left */ + if ((protocol_handle != NULL) && (tvb_reported_length_remaining(tvb, offset) > 0)) { + + /* Set MAC-NR info for this PDU. Needed so that UEId can be found for this frame, + * as used by MAC/RLC/PDCP configuration from RRC dissector */ + if (ueid) { + struct mac_nr_info *p_mac_nr_info; + p_mac_nr_info = wmem_new0(wmem_file_scope(), struct mac_nr_info); + p_mac_nr_info->ueid = ueid; + p_mac_nr_info->direction = (isUplink) ? DIRECTION_UPLINK : DIRECTION_DOWNLINK; + /* Store info in packet */ + set_mac_nr_proto_data(pinfo, p_mac_nr_info); + } + + rrc_tvb = tvb_new_subset_remaining(tvb, offset); + call_dissector_only(protocol_handle, rrc_tvb, pinfo, tree, NULL); + } +} + + +/* Dissect an CCPRI LTE frame by first parsing the header entries then passing + the data to the CPRI C&M dissector + + XXX - is this the Common Public Radio Interface? If so, what's the extra + "C" in "CCPRI"? And why is the LAPB dissector involved here? The CPRI + spec just speaks of HDLC; LAPB is certainly a HDLC-based protocol, but + that doesn't mean every HDLC-based protocol is LAPB. */ +static void dissect_ccpri_lte(tvbuff_t *tvb, gint offset, + packet_info *pinfo, proto_tree *tree) +{ + guint8 opcode; + guint8 tag; + tvbuff_t *ccpri_tvb; + dissector_handle_t protocol_handle = 0; + guint16 length; + + /* Top-level opcode */ + proto_tree_add_item(tree, hf_catapult_dct2000_lte_ccpri_opcode, tvb, offset, 1, ENC_BIG_ENDIAN); + opcode = tvb_get_guint8(tvb, offset++); + + /* Skip 2-byte length field */ + offset += 2; + + /* Cell-id */ + proto_tree_add_item(tree, hf_catapult_dct2000_cellid, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + /* Status (ind only) */ + if (opcode == 2) { + proto_item *ti; + guint8 status = tvb_get_guint8(tvb, offset); + ti = proto_tree_add_item(tree, hf_catapult_dct2000_lte_ccpri_status, + tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + if (status != 0) { + expert_add_info(pinfo, ti, &ei_catapult_dct2000_lte_ccpri_status_error); + + } + } + + /* Channel ID */ + proto_tree_add_item(tree, hf_catapult_dct2000_lte_ccpri_channel, + tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + /* Data tag must follow */ + tag = tvb_get_guint8(tvb, offset++); + if (tag != 2) { + return; + } + + /* Skip length */ + length = tvb_get_ntohs(tvb, offset); + offset += 2; + + /* Send remainder to lapb dissector; I assume "CPRI" is the Common + Public Radio Interface, in which case the "lapb" dissector is + being used to dissect the HDLC used by CPRI, and in which case + we should really have a CPRI dissector that dissects its HDLC + and then hands off to a CPRI C&M dissector. */ + protocol_handle = find_dissector("lapb"); + if ((protocol_handle != NULL) && (tvb_reported_length_remaining(tvb, offset) > 0)) { + ccpri_tvb = tvb_new_subset_length(tvb, offset, length); + call_dissector_only(protocol_handle, ccpri_tvb, pinfo, tree, NULL); + } +} + + + + +/* Dissect a PDCP LTE frame by first parsing the RLCPrim header then passing + the data to the PDCP LTE dissector */ +static void dissect_pdcp_lte(tvbuff_t *tvb, gint offset, + packet_info *pinfo, proto_tree *tree) +{ + guint8 opcode; + guint8 tag; + struct pdcp_lte_info *p_pdcp_lte_info; + tvbuff_t *pdcp_lte_tvb; + guint16 ueid; + guint8 channelId; + + /* Look this up so can update channel info */ + p_pdcp_lte_info = (struct pdcp_lte_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_pdcp_lte, 0); + if (p_pdcp_lte_info == NULL) { + /* This really should be set...can't dissect anything without it */ + return; + } + + /* Top-level opcode */ + opcode = tvb_get_guint8(tvb, offset); + if (tree) { + proto_tree_add_item(tree, hf_catapult_dct2000_rlc_op, tvb, offset, 1, ENC_BIG_ENDIAN); + } + offset++; + + col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(opcode, rlc_op_vals, "Unknown")); + + /* Assume UE side, so REQ is UL, IND is DL */ + switch (opcode) { + case RLC_AM_DATA_REQ: + case RLC_UM_DATA_REQ: + case RLC_TR_DATA_REQ: + p_pdcp_lte_info->direction = DIRECTION_UPLINK; + break; + + default: + p_pdcp_lte_info->direction = DIRECTION_DOWNLINK; + } + + /* Parse header */ + switch (opcode) { + case RLC_AM_DATA_REQ: + case RLC_AM_DATA_IND: + case RLC_UM_DATA_REQ: + case RLC_UM_DATA_IND: + case RLC_TR_DATA_REQ: + case RLC_TR_DATA_IND: + + /* Get next tag */ + tag = tvb_get_guint8(tvb, offset++); + switch (tag) { + case 0x10: /* UE_Id_LCId */ + + /* Dedicated channel info */ + + /* Length will fit in one byte here */ + offset++; + + p_pdcp_lte_info->channelType = Channel_DCCH; + + /* UEId */ + ueid = tvb_get_ntohs(tvb, offset); + proto_tree_add_item(tree, hf_catapult_dct2000_ueid, tvb, offset, 2, ENC_BIG_ENDIAN); + col_append_fstr(pinfo->cinfo, COL_INFO, + " UEId=%u", ueid); + p_pdcp_lte_info->ueid = ueid; + offset += 2; + + /* Get tag of channel type */ + tag = tvb_get_guint8(tvb, offset++); + + switch (tag) { + case 0: + offset++; + channelId = tvb_get_guint8(tvb, offset); + col_append_fstr(pinfo->cinfo, COL_INFO, " SRB:%u", + channelId); + proto_tree_add_item(tree, hf_catapult_dct2000_srbid, + tvb, offset++, 1, ENC_BIG_ENDIAN); + p_pdcp_lte_info->channelId = channelId; + break; + case 1: + offset++; + channelId = tvb_get_guint8(tvb, offset); + col_append_fstr(pinfo->cinfo, COL_INFO, " DRB:%u", + channelId); + proto_tree_add_item(tree, hf_catapult_dct2000_drbid, + tvb, offset++, 1, ENC_BIG_ENDIAN); + p_pdcp_lte_info->channelId = channelId; + break; + + default: + /* Unexpected channel type */ + return; + } + break; + + case 0x1a: /* Cell_LCId */ + + /* Common channel info */ + + /* Skip length */ + offset++; + + /* Cell-id */ + proto_tree_add_item(tree, hf_catapult_dct2000_cellid, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + /* Logical channel type */ + proto_tree_add_item(tree, hf_catapult_dct2000_rlc_channel_type, + tvb, offset, 1, ENC_BIG_ENDIAN); + p_pdcp_lte_info->channelType = (LogicalChannelType)tvb_get_guint8(tvb, offset++); + col_append_fstr(pinfo->cinfo, COL_INFO, " %s", + val_to_str_const(p_pdcp_lte_info->channelType, rlc_logical_channel_vals, + "UNKNOWN-CHANNEL")); + + switch (p_pdcp_lte_info->channelType) { + case Channel_BCCH: + /* Skip length */ + offset++; + + /* Transport channel type */ + p_pdcp_lte_info->BCCHTransport = (BCCHTransportType)tvb_get_guint8(tvb, offset); + proto_tree_add_item(tree, hf_catapult_dct2000_bcch_transport, + tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + break; + + case Channel_CCCH: + /* Skip length */ + offset++; + + /* UEId */ + proto_tree_add_item(tree, hf_catapult_dct2000_ueid, + tvb, offset, 2, ENC_BIG_ENDIAN); + ueid = tvb_get_ntohs(tvb, offset); + offset += 2; + + col_append_fstr(pinfo->cinfo, COL_INFO, " UEId=%u", ueid); + break; + + default: + break; + } + break; + + default: + /* Unexpected tag */ + return; + } + + /* Other optional fields may follow */ + tag = tvb_get_guint8(tvb, offset++); + while ((tag != 0x41) && (tvb_reported_length_remaining(tvb, offset) > 2)) { + + if (tag == 0x35) { + /* This is MUI */ + offset++; + proto_tree_add_item(tree, hf_catapult_dct2000_rlc_mui, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + /* CNF follows MUI in AM */ + if ((opcode == RLC_AM_DATA_REQ) || (opcode == RLC_AM_DATA_IND)) { + proto_tree_add_item(tree, hf_catapult_dct2000_rlc_cnf, + tvb, offset, 1, ENC_NA); + offset++; + } + } + else if (tag == 0x45) { + /* Discard Req */ + offset++; + proto_tree_add_item(tree, hf_catapult_dct2000_rlc_discard_req, + tvb, offset, 1, ENC_NA); + offset++; + } + + tag = tvb_get_guint8(tvb, offset++); + } + + + /********************************/ + /* Should be at data tag now */ + + /* Call PDCP LTE dissector */ + pdcp_lte_tvb = tvb_new_subset_remaining(tvb, offset); + call_dissector_only(pdcp_lte_handle, pdcp_lte_tvb, pinfo, tree, NULL); + + break; + + default: + return; + } +} + + + + + +/* Look up dissector by protocol name. Fix up known name mis-matches. + This includes exact matches and prefixes (e.g. "diameter_rx" -> "diameter") */ +static dissector_handle_t look_for_dissector(const char *protocol_name) +{ + if (strncmp(protocol_name, "diameter", strlen("diameter")) == 0) { + return find_dissector("diameter"); + } + else + if (strncmp(protocol_name, "gtpv2_r", 7) == 0) { + return find_dissector("gtpv2"); + } + else + if (strncmp(protocol_name, "s1ap", 4) == 0) { + return find_dissector("s1ap"); + } + else + if (strncmp(protocol_name, "x2ap_r", 6) == 0) { + return find_dissector("x2ap"); + } + else + if (strncmp(protocol_name, "xnap_r1", 7) == 0) { + return find_dissector("xnap"); + } + else + if (strncmp(protocol_name, "ngap_r1", 7) == 0) { + return find_dissector("ngap"); + } + + /* Only check really old names to convert if preference is checked */ + else if (catapult_dct2000_dissect_old_protocol_names) { + /* Use known aliases and protocol name prefixes */ + if (strcmp(protocol_name, "tbcp") == 0) { + return find_dissector("rtcp"); + } + else + if ((strcmp(protocol_name, "xcap_caps") == 0) || + (strcmp(protocol_name, "soap") == 0) || + (strcmp(protocol_name, "mm1") == 0) || + (strcmp(protocol_name, "mm3") == 0) || + (strcmp(protocol_name, "mm7") == 0)) { + + return find_dissector("http"); + } + else + if ((strncmp(protocol_name, "fp_r", 4) == 0) || + (strcmp(protocol_name, "fpiur_r5") == 0)) { + + return find_dissector("fp"); + } + else + if (strncmp(protocol_name, "iuup_rtp_r", strlen("iuup_rtp_r")) == 0) { + return find_dissector("rtp"); + } + else + if (strcmp(protocol_name, "sipt") == 0) { + return find_dissector("sip"); + } + else + if (strncmp(protocol_name, "nbap_sctp", strlen("nbap_sctp")) == 0) { + return find_dissector("nbap"); + } + else + if (strcmp(protocol_name, "dhcpv4") == 0) { + return find_dissector("dhcp"); + } + else + if (strcmp(protocol_name, "wimax") == 0) { + return find_dissector("wimaxasncp"); + } + else + if (strncmp(protocol_name, "sabp", strlen("sabp")) == 0) { + return find_dissector("sabp"); + } + else + if (strcmp(protocol_name, "wtp") == 0) { + return find_dissector("wtp-udp"); + } + else + if (strncmp(protocol_name, "gtp", strlen("gtp")) == 0) { + return find_dissector("gtp"); + } + } + + /* Try for an exact match */ + return find_dissector(protocol_name); +} + + +/* Populate outhdr_values array with numbers found in outhdr_string */ +static guint parse_outhdr_string(const guchar *outhdr_string, gint outhdr_string_len, guint *outhdr_values) +{ + int n = 0; + int outhdr_values_found; + + /* Populate values array */ + for (outhdr_values_found=0; outhdr_values_found < MAX_OUTHDR_VALUES; ) { + + guint digit_array[MAX_OUTHDR_VALUES]; + guint number_digits = 0; + + guint number = 0; + guint multiplier = 1; + guint d; + + /* Find digits */ + for ( ; (n < outhdr_string_len) && (number_digits < MAX_OUTHDR_VALUES); n++) { + if (!g_ascii_isdigit(outhdr_string[n])) { + break; + } + else { + digit_array[number_digits++] = outhdr_string[n] - '0'; + } + } + + if (number_digits == 0) { + /* No more numbers left */ + break; + } + + /* Convert digits into value (much faster than format_text() + atoi()) */ + for (d=number_digits; d > 0; d--) { + number += ((digit_array[d-1]) * multiplier); + multiplier *= 10; + } + outhdr_values[outhdr_values_found++] = number; + + /* Skip comma */ + n++; + } + return outhdr_values_found; +} + + + +/* Fill in an FP packet info struct and attach it to the packet for the FP + dissector to use */ +static void attach_fp_info(packet_info *pinfo, gboolean received, + const char *protocol_name, int variant, + guint *outhdr_values, guint outhdr_values_found) +{ + guint i = 0; + int chan; + guint tf_start, num_chans_start; + gint node_type; + int calculated_variant; + + /* Only need to set info once per session. */ + struct fp_info *p_fp_info = (struct fp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp, 0); + if (p_fp_info != NULL) { + return; + } + + /* Allocate struct */ + p_fp_info = wmem_new0(wmem_file_scope(), struct fp_info); + + /* Check that the number of outhdr values looks sensible */ + if (((strcmp(protocol_name, "fpiur_r5") == 0) && (outhdr_values_found != 2)) || + (outhdr_values_found < 5)) { + + return; + } + + /* 3gpp release (99, 4, 5, 6, 7) */ + if (strcmp(protocol_name, "fp") == 0) { + p_fp_info->release = 99; + } + else if (strcmp(protocol_name, "fp_r4") == 0) { + p_fp_info->release = 4; + } + else if (strcmp(protocol_name, "fp_r5") == 0) { + p_fp_info->release = 5; + } + else if (strcmp(protocol_name, "fp_r6") == 0) { + p_fp_info->release = 6; + } + else if (strcmp(protocol_name, "fp_r7") == 0) { + p_fp_info->release = 7; + } + else if (strcmp(protocol_name, "fp_r8") == 0) { + p_fp_info->release = 8; + } + else if (strcmp(protocol_name, "fpiur_r5") == 0) { + p_fp_info->release = 5; + } + else { + /* Really shouldn't get here */ + DISSECTOR_ASSERT_NOT_REACHED(); + return; + } + + /* Release date is derived from variant number */ + /* Only R6 sub-versions currently influence format within a release */ + switch (p_fp_info->release) { + case 6: + if (variant < 256) { + calculated_variant = variant; + } + else { + calculated_variant = variant / 256; + } + + switch (calculated_variant) { + case 1: + p_fp_info->release_year = 2005; + p_fp_info->release_month = 6; + break; + case 2: + p_fp_info->release_year = 2005; + p_fp_info->release_month = 9; + break; + case 3: + default: + p_fp_info->release_year = 2006; + p_fp_info->release_month = 3; + break; + } + break; + case 7: + p_fp_info->release_year = 2008; + p_fp_info->release_month = 3; + break; + + case 8: + p_fp_info->release_year = 2010; + p_fp_info->release_month = 6; + break; + + + default: + p_fp_info->release_year = 0; + p_fp_info->release_month = 0; + } + + + /* Channel type */ + p_fp_info->channel = outhdr_values[i++]; + /* Sad hack until this value is filled in properly */ + if (p_fp_info->channel == 0) { + p_fp_info->channel = CHANNEL_DCH; + } + + /* Derive direction from node type/side */ + node_type = outhdr_values[i++]; + p_fp_info->is_uplink = (( received && (node_type == 2)) || + (!received && (node_type == 1))); + + /* Division type introduced for R7 */ + if ((p_fp_info->release == 7) || + (p_fp_info->release == 8)) { + p_fp_info->division = (enum division_type)outhdr_values[i++]; + } + + /* HS-DSCH config */ + if (p_fp_info->channel == CHANNEL_HSDSCH) { + if ((p_fp_info->release == 7) || + (p_fp_info->release == 8)) { + /* Entity (MAC-hs or MAC-ehs) used */ + if (outhdr_values[i++]) { + p_fp_info->hsdsch_entity = ehs; + } + } + else { + /* This is the pre-R7 default */ + p_fp_info->hsdsch_entity = hs; + } + } + + + /* IUR only uses the above... */ + if (strcmp(protocol_name, "fpiur_r5") == 0) { + /* Store info in packet */ + p_fp_info->iface_type = IuR_Interface; + p_add_proto_data(wmem_file_scope(), pinfo, proto_fp, 0, p_fp_info); + return; + } + + /* DCH CRC present... */ + p_fp_info->dch_crc_present = outhdr_values[i++]; + + /* ... but don't trust for edch */ + if (p_fp_info->channel == CHANNEL_EDCH) { + p_fp_info->dch_crc_present = 2; /* unknown */ + } + + /* How many paging indications (if PCH data) */ + p_fp_info->paging_indications = outhdr_values[i++]; + + /* Number of channels (for coordinated channels) */ + p_fp_info->num_chans = outhdr_values[i++]; + if (p_fp_info->num_chans > MAX_FP_CHANS) { + p_fp_info->num_chans = MAX_FP_CHANS; + } + + /* EDCH-Common is always T2 */ + if (p_fp_info->channel == CHANNEL_EDCH_COMMON) { + p_fp_info->edch_type = 1; + } + + if (p_fp_info->channel != CHANNEL_EDCH) { + /* TF size for each channel */ + tf_start = i; + for (chan=0; chan < p_fp_info->num_chans; chan++) { + if (outhdr_values_found > tf_start+chan) { + p_fp_info->chan_tf_size[chan] = outhdr_values[tf_start+chan]; + } else { + p_fp_info->chan_tf_size[chan] = 0; + } + } + + /* Number of TBs for each channel */ + num_chans_start = tf_start + p_fp_info->num_chans; + for (chan=0; chan < p_fp_info->num_chans; chan++) { + if (outhdr_values_found > num_chans_start+chan) { + p_fp_info->chan_num_tbs[chan] = outhdr_values[num_chans_start+chan]; + } else { + p_fp_info->chan_num_tbs[chan] = 0; + } + } + } + /* EDCH info */ + else { + int n; + + p_fp_info->no_ddi_entries = outhdr_values[i++]; + + /* DDI values */ + for (n=0; n < p_fp_info->no_ddi_entries; n++) { + if (outhdr_values_found > i) { + p_fp_info->edch_ddi[n] = outhdr_values[i++]; + } else { + p_fp_info->edch_ddi[n] = 0; + } + } + + /* Corresponding MAC-d sizes */ + for (n=0; n < p_fp_info->no_ddi_entries; n++) { + if (outhdr_values_found > i) { + p_fp_info->edch_macd_pdu_size[n] = outhdr_values[i++]; + } else { + p_fp_info->edch_macd_pdu_size[n] = 0; + } + } + + if (strcmp(protocol_name, "fp_r8") == 0) { + if (outhdr_values_found > i) { + p_fp_info->edch_type = outhdr_values[i]; + } else { + p_fp_info->edch_type = 0; + } + } + else { + p_fp_info->edch_type = 0; + } + } + + /* Interface must be IuB */ + p_fp_info->iface_type = IuB_Interface; + + /* Store info in packet */ + p_add_proto_data(wmem_file_scope(), pinfo, proto_fp, 0, p_fp_info); +} + + +/* Fill in an RLC packet info struct and attach it to the packet for the RLC + dissector to use */ +static void attach_rlc_info(packet_info *pinfo, guint32 urnti, guint8 rbid, + gboolean is_sent, guint *outhdr_values, + guint outhdr_values_found) +{ + /* Only need to set info once per session. */ + struct fp_info *p_fp_info; + struct rlc_info *p_rlc_info = (struct rlc_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0); + + if (p_rlc_info != NULL) { + return; + } + + /* Check that the number of outhdr values looks correct */ + if (outhdr_values_found != 2) { + return; + } + + /* Allocate structs */ + p_rlc_info = wmem_new(wmem_file_scope(), struct rlc_info); + p_fp_info = wmem_new0(wmem_file_scope(), struct fp_info); + + /* Fill in struct fields for first (only) PDU in this frame */ + + /* Urnti. Just use UEId */ + p_rlc_info->ueid[0] = urnti; + + /* ciphered (off by default) */ + p_rlc_info->ciphered[0] = FALSE; + + /* deciphered (off by default) */ + p_rlc_info->deciphered[0] = FALSE; + + /* Mode. */ + switch (outhdr_values[1]) { + case 1: + p_rlc_info->mode[0] = RLC_TM; + break; + case 2: + p_rlc_info->mode[0] = RLC_UM; + break; + case 3: + p_rlc_info->mode[0] = RLC_AM; + break; + case 4: + p_rlc_info->mode[0] = RLC_UM; + p_rlc_info->ciphered[0] = TRUE; + break; + case 5: + p_rlc_info->mode[0] = RLC_AM; + p_rlc_info->ciphered[0] = TRUE; + break; + default: + return; + } + + /* rbid. TODO: does this need conversion? */ + p_rlc_info->rbid[0] = rbid; + + /* li_size */ + p_rlc_info->li_size[0] = (enum rlc_li_size)outhdr_values[0]; + + /* Store info in packet */ + p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0, p_rlc_info); + + /* Also store minimal FP info consulted by RLC dissector + TODO: Don't really know direction, but use S/R flag to make + logs in same context consistent. Will be correct for NodeB logs, + but RLC dissector seems to not use anyway... */ + p_fp_info->is_uplink = is_sent; + p_fp_info->cur_tb = 0; /* Always the first/only one */ + p_add_proto_data(wmem_file_scope(), pinfo, proto_fp, 0, p_fp_info); +} + + +/* Fill in a MAC LTE packet info struct and attach it to the packet for that + dissector to use */ +static void attach_mac_lte_info(packet_info *pinfo, guint *outhdr_values, guint outhdr_values_found) +{ + struct mac_lte_info *p_mac_lte_info; + unsigned int i = 0; + + /* Only need to set info once per session. */ + p_mac_lte_info = get_mac_lte_proto_data(pinfo); + if (p_mac_lte_info != NULL) { + return; + } + + /* Allocate & zero struct */ + p_mac_lte_info = wmem_new0(wmem_file_scope(), struct mac_lte_info); + + /* Populate the struct from outhdr values */ + p_mac_lte_info->crcStatusValid = FALSE; /* not set yet */ + + p_mac_lte_info->radioType = outhdr_values[i++] + 1; // 1 + p_mac_lte_info->rntiType = outhdr_values[i++]; // 2 + p_mac_lte_info->direction = outhdr_values[i++]; // 3 + /* Set these extra PHY present flags to FALSE by default */ + 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->sfnSfInfoPresent = TRUE; + p_mac_lte_info->subframeNumber = outhdr_values[i++]; // 4 + p_mac_lte_info->isPredefinedData = outhdr_values[i++]; // 5 + p_mac_lte_info->rnti = outhdr_values[i++]; // 6 + p_mac_lte_info->ueid = outhdr_values[i++]; // 7 + p_mac_lte_info->length = outhdr_values[i++]; // 8 + if (outhdr_values_found > 8) { + p_mac_lte_info->reTxCount = outhdr_values[i++]; // 9 + } + /* TODO: delete if won't see this special case anymore? */ + if (outhdr_values_found == 10) { + /* CRC only valid for Downlink */ + if (p_mac_lte_info->direction == DIRECTION_DOWNLINK) { + p_mac_lte_info->crcStatusValid = TRUE; + p_mac_lte_info->crcStatus = (mac_lte_crc_status)outhdr_values[i++]; // 10 + } + else { + i++; // 10 (ignoring for UL) + } + } + + p_mac_lte_info->dl_retx = dl_retx_unknown; + + if (outhdr_values_found > 10) { + /* Extra PHY parameters */ + if (p_mac_lte_info->direction == DIRECTION_DOWNLINK) { + p_mac_lte_info->detailed_phy_info.dl_info.present = outhdr_values[i++]; // 10 + p_mac_lte_info->detailed_phy_info.dl_info.dci_format = outhdr_values[i++]; // 11 + p_mac_lte_info->detailed_phy_info.dl_info.resource_allocation_type = outhdr_values[i++]; // 12 + p_mac_lte_info->detailed_phy_info.dl_info.aggregation_level = outhdr_values[i++]; // 13 + p_mac_lte_info->detailed_phy_info.dl_info.mcs_index = outhdr_values[i++]; // 14 + p_mac_lte_info->detailed_phy_info.dl_info.redundancy_version_index = outhdr_values[i++]; // 15 + p_mac_lte_info->dl_retx = (outhdr_values[i++]) ? dl_retx_yes : dl_retx_no; // 16 + + p_mac_lte_info->detailed_phy_info.dl_info.resource_block_length = outhdr_values[i++]; // 17 + p_mac_lte_info->crcStatusValid = TRUE; // 18 + p_mac_lte_info->crcStatus = (mac_lte_crc_status)outhdr_values[i++]; + if (outhdr_values_found > 18) { + p_mac_lte_info->detailed_phy_info.dl_info.harq_id = outhdr_values[i++]; // 19 + p_mac_lte_info->detailed_phy_info.dl_info.ndi = outhdr_values[i++]; // 20 + } + if (outhdr_values_found > 20) { + p_mac_lte_info->detailed_phy_info.dl_info.transport_block = outhdr_values[i++]; // 21 + } + } + else { + /* Uplink */ + p_mac_lte_info->detailed_phy_info.ul_info.present = outhdr_values[i++]; // 10 + p_mac_lte_info->detailed_phy_info.ul_info.modulation_type = outhdr_values[i++]; // 11 + p_mac_lte_info->detailed_phy_info.ul_info.tbs_index = outhdr_values[i++]; // 12 + p_mac_lte_info->detailed_phy_info.ul_info.resource_block_length = outhdr_values[i++]; // 13 + p_mac_lte_info->detailed_phy_info.ul_info.resource_block_start = outhdr_values[i++]; // 14 + /* Skip retx flag */ + i++; // 15 + + /* TODO: delete if won't see this special case anymore? */ + if (outhdr_values_found == 16) { + p_mac_lte_info->subframeNumberOfGrantPresent = TRUE; + p_mac_lte_info->subframeNumberOfGrant = outhdr_values[i++]; // 16 + } + if (outhdr_values_found > 16) { + p_mac_lte_info->detailed_phy_info.ul_info.harq_id = outhdr_values[i++]; // 16 + p_mac_lte_info->detailed_phy_info.ul_info.ndi = outhdr_values[i++]; // 17 + + p_mac_lte_info->subframeNumberOfGrantPresent = TRUE; + p_mac_lte_info->subframeNumberOfGrant = outhdr_values[i++]; // 18 + } + } + } + + /* System frame number */ + if (i < outhdr_values_found) { + p_mac_lte_info->sysframeNumber = outhdr_values[i++]; + } + + if ((p_mac_lte_info->direction == DIRECTION_UPLINK) && + (i < outhdr_values_found)) { + + p_mac_lte_info->isPHICHNACK = outhdr_values[i++]; + } + + if (p_mac_lte_info->direction == DIRECTION_UPLINK) { + /* R10 parameter not set yet */ + p_mac_lte_info->isExtendedBSRSizes = FALSE; + } + + if (i < outhdr_values_found) { + /* Carrier ID */ + p_mac_lte_info->carrierId = (mac_lte_carrier_id)outhdr_values[i++]; + } + + /* Remaining fields not (yet?) supported in + the mac-lte dissector. */ + if (i++ < outhdr_values_found) { + /* Serving cell index */ + } + if (i < outhdr_values_found) { + /* UE Type */ + } + + /* Store info in packet */ + set_mac_lte_proto_data(pinfo, p_mac_lte_info); +} + + +/* Fill in a RLC LTE packet info struct and attach it to the packet for that + dissector to use */ +static void attach_rlc_lte_info(packet_info *pinfo, guint *outhdr_values, + guint outhdr_values_found _U_) +{ + struct rlc_lte_info *p_rlc_lte_info; + unsigned int i = 0; + + /* Only need to set info once per session. */ + p_rlc_lte_info = (rlc_lte_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_rlc_lte, 0); + if (p_rlc_lte_info != NULL) { + return; + } + + /* Allocate & zero struct */ + p_rlc_lte_info = wmem_new0(wmem_file_scope(), rlc_lte_info); + + p_rlc_lte_info->rlcMode = outhdr_values[i++]; + p_rlc_lte_info->direction = outhdr_values[i++]; + p_rlc_lte_info->priority = outhdr_values[i++]; + p_rlc_lte_info->sequenceNumberLength = outhdr_values[i++]; + p_rlc_lte_info->channelId = outhdr_values[i++]; + p_rlc_lte_info->channelType = outhdr_values[i++]; + p_rlc_lte_info->ueid = outhdr_values[i++]; + p_rlc_lte_info->pduLength = outhdr_values[i]; + + /* Store info in packet */ + p_add_proto_data(wmem_file_scope(), pinfo, proto_rlc_lte, 0, p_rlc_lte_info); +} + +/* Fill in a PDCP LTE packet info struct and attach it to the packet for the PDCP LTE + dissector to use */ +static void attach_pdcp_lte_info(packet_info *pinfo, guint *outhdr_values, + guint outhdr_values_found _U_) +{ + struct pdcp_lte_info *p_pdcp_lte_info; + unsigned int i = 0; + + /* Only need to set info once per session. */ + p_pdcp_lte_info = (pdcp_lte_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_pdcp_lte, 0); + if (p_pdcp_lte_info != NULL) { + return; + } + + /* Allocate & zero struct */ + p_pdcp_lte_info = wmem_new0(wmem_file_scope(), pdcp_lte_info); + + p_pdcp_lte_info->no_header_pdu = outhdr_values[i++]; + p_pdcp_lte_info->plane = (enum pdcp_plane)outhdr_values[i++]; + if (p_pdcp_lte_info->plane != USER_PLANE) { + p_pdcp_lte_info->plane = SIGNALING_PLANE; + } + p_pdcp_lte_info->seqnum_length = outhdr_values[i++]; + + p_pdcp_lte_info->rohc.rohc_compression = outhdr_values[i++]; + p_pdcp_lte_info->rohc.rohc_ip_version = outhdr_values[i++]; + p_pdcp_lte_info->rohc.cid_inclusion_info = outhdr_values[i++]; + p_pdcp_lte_info->rohc.large_cid_present = outhdr_values[i++]; + p_pdcp_lte_info->rohc.mode = (enum rohc_mode)outhdr_values[i++]; + p_pdcp_lte_info->rohc.rnd = outhdr_values[i++]; + p_pdcp_lte_info->rohc.udp_checksum_present = outhdr_values[i++]; + p_pdcp_lte_info->rohc.profile = outhdr_values[i]; + + /* Remaining 2 (fixed) fields are ah_length and gre_checksum */ + + /* Store info in packet */ + p_add_proto_data(wmem_file_scope(), pinfo, proto_pdcp_lte, 0, p_pdcp_lte_info); +} + + +/* Attempt to show tty (raw character messages) as text lines. */ +static void dissect_tty_lines(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset) +{ + gint next_offset; + proto_tree *tty_tree; + proto_item *ti; + int lines = 0; + + /* Create tty tree. */ + ti = proto_tree_add_item(tree, hf_catapult_dct2000_tty, tvb, offset, -1, ENC_NA); + tty_tree = proto_item_add_subtree(ti, ett_catapult_dct2000_tty); + + /* Show the tty lines one at a time. */ + while (tvb_offset_exists(tvb, offset)) { + /* Find the end of the line. */ + int linelen = tvb_find_line_end_unquoted(tvb, offset, -1, &next_offset); + + /* Extract & add the string. */ + char *string = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, linelen, ENC_ASCII); + if (g_ascii_isprint(string[0])) { + /* If the first byte of the string is printable ASCII treat as string... */ + proto_tree_add_string_format(tty_tree, hf_catapult_dct2000_tty_line, + tvb, offset, + linelen, string, + "%s", string); + } + else { + /* Otherwise show as $hex */ + int n, idx; + char *hex_string; + int tty_string_length = tvb_reported_length_remaining(tvb, offset); + int hex_string_length = 1+(2*tty_string_length)+1; + hex_string = (char *)wmem_alloc(pinfo->pool, hex_string_length); + + idx = snprintf(hex_string, hex_string_length, "$"); + + /* Write hex out to new string */ + for (n=0; n < tty_string_length; n++) { + idx += snprintf(hex_string+idx, 3, "%02x", + tvb_get_guint8(tvb, offset+n)); + } + string = hex_string; + } + lines++; + + /* Show first line in info column */ + if (lines == 1) { + col_append_fstr(pinfo->cinfo, COL_INFO, "tty (%s", string); + proto_item_append_text(ti, " (%s)", string); + } + + /* Move onto next line. */ + offset = next_offset; + } + + /* Close off summary of tty message in info column */ + if (lines != 0) { + col_append_str(pinfo->cinfo, COL_INFO, (lines > 1) ? "...)" : ")"); + } +} + + +/* Scan the log comment looking for notable out-of-band MAC events that should + be sent to the MAC dissector */ +static void check_for_oob_mac_lte_events(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree, + const char *string) +{ + guint number_of_ues; + guint ueids[MAX_SRs]; + guint rntis[MAX_SRs]; + guint rapid; + guint rach_attempt_number; + guint temp; + mac_lte_oob_event oob_event; + struct mac_lte_info *p_mac_lte_info; + guint16 n; + + /* Current strings of interest begin with ">> ", so if don't see, avoid sscanf() calls. */ + if (strncmp(string, ">> ", 3) != 0) { + return; + } + + /*********************************************/ + /* Look for strings matching formats */ + + /* RACH Preamble request */ + if (sscanf(string, ">> RACH Preamble Request [CarrierId=%u] [LTE UE = %u] [RAPID = %u] [Attempt = %u", + &temp, &ueids[0], &rapid, &rach_attempt_number) == 4) { + oob_event = ltemac_send_preamble; + } + + /* Scheduling Requests */ + else if (sscanf(string, ">> Schedule Requests (%u) [CarrierId=%u][UE=%u][RNTI=%u]", + &number_of_ues, &temp, &ueids[0], &rntis[0]) == 4) { + const char *current_position; + + /* Newer, multi-UE format */ + oob_event = ltemac_send_sr; + + /* Parse other ueid/rnti pairs */ + number_of_ues = MIN(number_of_ues, MAX_SRs); + if (number_of_ues > 1) { + current_position = string; + + for (n=1; n < number_of_ues; n++) { + + /* Find the start of the next entry */ + current_position = strstr(current_position, "] "); + if (current_position != NULL) { + current_position += 2; + } + else { + /* This is an error - shouldn't happen */ + return; + } + + /* Read this entry */ + if (sscanf(current_position, "[UE=%u][RNTI=%u]", &ueids[n], &rntis[n]) != 2) { + /* Assuming that if we can't read this one there is no point trying others */ + number_of_ues = n; + break; + } + } + } + } + + /* SR failures */ + else if (sscanf(string, ">> INFO (inst %u) MAC: [UE = %u] SR failed (CRNTI=%u)", + &temp, &ueids[0], &rntis[0]) == 3) { + oob_event = ltemac_sr_failure; + } + + /* No events found */ + else return; + + + /********************************************/ + /* We have an event. Allocate & zero struct */ + p_mac_lte_info = wmem_new0(wmem_file_scope(), mac_lte_info); + + /* This indicates to MAC dissector that it has an oob event */ + p_mac_lte_info->length = 0; + + switch (oob_event) { + case ltemac_send_preamble: + p_mac_lte_info->ueid = ueids[0]; + p_mac_lte_info->rapid = rapid; + p_mac_lte_info->rach_attempt_number = rach_attempt_number; + p_mac_lte_info->direction = DIRECTION_UPLINK; + break; + case ltemac_send_sr: + for (n=0; n < number_of_ues; n++) { + p_mac_lte_info->oob_ueid[n] = ueids[n]; + p_mac_lte_info->oob_rnti[n] = rntis[n]; + } + p_mac_lte_info->number_of_srs = number_of_ues; + p_mac_lte_info->direction = DIRECTION_UPLINK; + break; + case ltemac_sr_failure: + p_mac_lte_info->rnti = rntis[0]; + p_mac_lte_info->ueid = ueids[0]; + p_mac_lte_info->direction = DIRECTION_DOWNLINK; + break; + } + + p_mac_lte_info->radioType = FDD_RADIO; /* TODO: will be the same as rest of log... */ + p_mac_lte_info->sfnSfInfoPresent = FALSE; /* We don't have this */ + p_mac_lte_info->oob_event = oob_event; + + /* Store info in packet */ + set_mac_lte_proto_data(pinfo, p_mac_lte_info); + + /* Call MAC dissector */ + call_dissector_only(mac_lte_handle, tvb, pinfo, tree, NULL); +} + +static guint8 +hex_from_char(gchar c) +{ + if ((c >= '0') && (c <= '9')) { + return c - '0'; + } + + if ((c >= 'a') && (c <= 'f')) { + return 0x0a + (c - 'a'); + } + + /* Not a valid hex string character */ + return 0xff; +} + + +/*****************************************/ +/* Main dissection function. */ +/*****************************************/ +static int +dissect_catapult_dct2000(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + proto_tree *dct2000_tree = NULL; + proto_item *ti = NULL; + gint offset = 0; + gint context_length; + const char *context_name; + guint8 port_number; + gint protocol_length; + gint timestamp_length; + const char *timestamp_string; + gint variant_length; + const char *variant_string; + guint32 variant; + gint outhdr_length; + const char *outhdr_string; + guint8 direction; + tvbuff_t *next_tvb; + int encap; + dissector_handle_t protocol_handle = 0; + dissector_handle_t heur_protocol_handle = 0; + void *protocol_data = 0; + int sub_dissector_result = 0; + const char *protocol_name; + gboolean is_comment, is_sprint = FALSE; + guint outhdr_values[MAX_OUTHDR_VALUES]; + guint outhdr_values_found; + + /* Set Protocol */ + col_set_str(pinfo->cinfo, COL_PROTOCOL, "DCT2000"); + + /* Clear Info */ + col_clear(pinfo->cinfo, COL_INFO); + + /* Create root (protocol) tree. */ + if (tree) { + ti = proto_tree_add_item(tree, proto_catapult_dct2000, tvb, offset, -1, ENC_NA); + dct2000_tree = proto_item_add_subtree(ti, ett_catapult_dct2000); + } + + /*********************************************************************/ + /* Note that these are the fields of the stub header as written out */ + /* by the wiretap module */ + + /* Context Name */ + context_name = tvb_get_stringz_enc(pinfo->pool, tvb, offset, &context_length, ENC_ASCII); + if (dct2000_tree) { + proto_tree_add_string(dct2000_tree, hf_catapult_dct2000_context, tvb, + offset, context_length, context_name); + } + offset += context_length; + + /* Context port number */ + port_number = tvb_get_guint8(tvb, offset); + if (dct2000_tree) { + proto_tree_add_item(dct2000_tree, hf_catapult_dct2000_port_number, tvb, + offset, 1, ENC_BIG_ENDIAN); + } + offset++; + + /* Timestamp in file */ + timestamp_string = tvb_get_stringz_enc(pinfo->pool, tvb, offset, ×tamp_length, ENC_ASCII); + if (dct2000_tree) { + /* g_ascii_strtod(timestamp_string, NULL)) is much simpler, but *very* slow.. + There will be seconds, a dot, and 4 decimal places. + N.B. timestamp_length also includes following NULL character */ + if (timestamp_length < 7) { + /* Can't properly parse, but leave showing how far we got */ + return offset; + } + + /* Seconds. */ + int seconds = 0; + int multiplier = 1; + for (int d=timestamp_length-7; d >= 0; d--) { + seconds += ((timestamp_string[d]-'0') * multiplier); + multiplier *= 10; + } + + /* Subseconds (4 digits). N.B. trailing zeros are written out by wiretap module. */ + int subseconds = 0; + subseconds += (timestamp_string[timestamp_length-2]-'0'); + subseconds += (timestamp_string[timestamp_length-3]-'0')*10; + subseconds += (timestamp_string[timestamp_length-4]-'0')*100; + subseconds += (timestamp_string[timestamp_length-5]-'0')*1000; + + proto_tree_add_double(dct2000_tree, hf_catapult_dct2000_timestamp, tvb, + offset, timestamp_length, + seconds+(subseconds/10000.0)); + } + offset += timestamp_length; + + + /* DCT2000 protocol name */ + protocol_name = tvb_get_stringz_enc(pinfo->pool, tvb, offset, &protocol_length, ENC_ASCII); + if (dct2000_tree) { + proto_tree_add_string(dct2000_tree, hf_catapult_dct2000_protocol, tvb, + offset, protocol_length, protocol_name); + } + is_comment = (strcmp(protocol_name, "comment") == 0); + if (!is_comment) { + is_sprint = (strcmp(protocol_name, "sprint") == 0); + } + offset += protocol_length; + + + /* Protocol Variant */ + variant_string = tvb_get_stringz_enc(pinfo->pool, tvb, offset, &variant_length, ENC_ASCII); + if (!is_comment && !is_sprint) { + proto_tree_add_string(dct2000_tree, hf_catapult_dct2000_variant, tvb, + offset, variant_length, variant_string); + } + offset += variant_length; + + /* Outhdr (shown as string) */ + outhdr_string = tvb_get_stringz_enc(pinfo->pool, tvb, offset, &outhdr_length, ENC_ASCII); + if (!is_comment && !is_sprint && (outhdr_length > 1)) { + proto_tree_add_string(dct2000_tree, hf_catapult_dct2000_outhdr, tvb, + offset, outhdr_length, outhdr_string); + } + offset += outhdr_length; + + + /* Direction */ + direction = tvb_get_guint8(tvb, offset); + if (dct2000_tree) { + proto_tree_add_item(dct2000_tree, hf_catapult_dct2000_direction, tvb, + offset, 1, ENC_BIG_ENDIAN); + } + offset++; + + /* Read frame encapsulation set by wiretap */ + if (!is_comment && !is_sprint) { + proto_tree_add_item(dct2000_tree, hf_catapult_dct2000_encap, tvb, offset, 1, ENC_BIG_ENDIAN); + } + encap = tvb_get_guint8(tvb, offset); + offset++; + + /* Add useful details to protocol tree label */ + proto_item_append_text(ti, " context=%s.%u t=%s %c prot=%s (v=%s)", + context_name, + port_number, + timestamp_string, + (direction == 0) ? 'S' : 'R', + protocol_name, + variant_string); + + + memset(outhdr_values, 0, sizeof outhdr_values); + outhdr_values_found = 0; + + /* FP protocols need info from outhdr attached */ + if ((strcmp(protocol_name, "fp") == 0) || + (strncmp(protocol_name, "fp_r", 4) == 0) || + (strcmp(protocol_name, "fpiur_r5") == 0)) { + + outhdr_values_found = parse_outhdr_string(outhdr_string, outhdr_length, + outhdr_values); + if (ws_strtou32(variant_string, NULL, &variant)) + attach_fp_info(pinfo, direction, protocol_name, variant, + outhdr_values, outhdr_values_found); + else + expert_add_info(pinfo, ti, &ei_catapult_dct2000_string_invalid); + } + + /* RLC protocols need info from outhdr attached */ + else if ((strcmp(protocol_name, "rlc") == 0) || + (strcmp(protocol_name, "rlc_r4") == 0) || + (strcmp(protocol_name, "rlc_r5") == 0) || + (strcmp(protocol_name, "rlc_r6") == 0) || + (strcmp(protocol_name, "rlc_r7") == 0) || + (strcmp(protocol_name, "rlc_r8") == 0) || + (strcmp(protocol_name, "rlc_r9") == 0)) { + + outhdr_values_found = parse_outhdr_string(outhdr_string, outhdr_length, + outhdr_values); + /* Can't attach info yet. Need combination of outheader values + and fields parsed from primitive header... */ + } + + /* LTE MAC needs info attached */ + else if ((strcmp(protocol_name, "mac_r8_lte") == 0) || + (strcmp(protocol_name, "mac_r9_lte") == 0) || + (strcmp(protocol_name, "mac_r10_lte") == 0)) { + outhdr_values_found = parse_outhdr_string(outhdr_string, outhdr_length, + outhdr_values); + attach_mac_lte_info(pinfo, outhdr_values, outhdr_values_found); + } + + /* LTE RLC needs info attached */ + else if ((strcmp(protocol_name, "rlc_r8_lte") == 0) || + (strcmp(protocol_name, "rlc_r9_lte") == 0) || + (strcmp(protocol_name, "rlc_r10_lte") == 0)) { + outhdr_values_found = parse_outhdr_string(outhdr_string, outhdr_length, + outhdr_values); + attach_rlc_lte_info(pinfo, outhdr_values, outhdr_values_found); + } + + /* LTE PDCP needs info attached */ + else if ((strcmp(protocol_name, "pdcp_r8_lte") == 0) || + (strcmp(protocol_name, "pdcp_r9_lte") == 0) || + (strcmp(protocol_name, "pdcp_r10_lte") == 0)) { + outhdr_values_found = parse_outhdr_string(outhdr_string, outhdr_length, + outhdr_values); + attach_pdcp_lte_info(pinfo, outhdr_values, outhdr_values_found); + } + + else if ((strcmp(protocol_name, "nas_rrc_r8_lte") == 0) || + (strcmp(protocol_name, "nas_rrc_r9_lte") == 0) || + (strcmp(protocol_name, "nas_rrc_r10_lte") == 0) || + (strcmp(protocol_name, "nas_rrc_r13_lte") == 0) || + (strcmp(protocol_name, "nas_rrc_r15_5gnr") == 0)) { + gboolean nas_body_found = TRUE; + guint8 opcode = tvb_get_guint8(tvb, offset); + proto_tree_add_item(tree, hf_catapult_dct2000_lte_nas_rrc_opcode, + tvb, offset++, 1, ENC_BIG_ENDIAN); + + offset++; /* Skip overall length */ + + switch (opcode) { + case LTE_NAS_RRC_DATA_IND: + case LTE_NAS_RRC_DATA_REQ: + /* UEId */ + offset++; /* tag */ + offset += 2; /* 2 wasted bytes of UEId*/ + proto_tree_add_item(tree, hf_catapult_dct2000_ueid, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + break; + case LTE_NAS_RRC_ESTABLISH_REQ: + /* UEId */ + offset++; /* tag */ + offset += 2; /* 2 wasted bytes of UEId*/ + proto_tree_add_item(tree, hf_catapult_dct2000_ueid, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + /* Establish cause. TODO: value_string */ + offset += 2; /* tag + length */ + proto_tree_add_item(tree, hf_catapult_dct2000_lte_nas_rrc_establish_cause, + tvb, offset++, 1, ENC_BIG_ENDIAN); + + /* Priority. TODO: Vals are low | high */ + offset += 2; /* tag + length */ + proto_tree_add_item(tree, hf_catapult_dct2000_lte_nas_rrc_priority, + tvb, offset++, 1, ENC_BIG_ENDIAN); + break; + case LTE_NAS_RRC_RELEASE_IND: + /* UEId */ + offset++; /* tag */ + offset += 2; /* 2 wasted bytes of UEId*/ + proto_tree_add_item(tree, hf_catapult_dct2000_ueid, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + /* Release cause. TODO: value_string */ + offset += 2; /* tag + length */ + proto_tree_add_item(tree, hf_catapult_dct2000_lte_nas_rrc_release_cause, + tvb, offset++, 1, ENC_BIG_ENDIAN); + break; + + default: + nas_body_found = FALSE; + break; + } + + /* Look up dissector if it looks right */ + if (nas_body_found) { + offset += 2; /* L3 tag + len */ + if (strcmp(protocol_name, "nas_rrc_r15_5gnr") == 0) { + protocol_handle = find_dissector("nas-5gs"); + } + else { + protocol_handle = find_dissector("nas-eps"); + } + } + } + + /* NR NAS for S1AP */ + else if (strcmp(protocol_name, "nas_s1ap_r15_5gnr") == 0) { + guint8 opcode = tvb_get_guint8(tvb, offset); + if (opcode <= NAS_S1AP_DATA_IND) { + /* Opcode tag (only interested in ones that carry NAS PDU) */ + proto_tree_add_item(tree, hf_catapult_dct2000_nr_nas_s1ap_opcode, + tvb, offset++, 1, ENC_BIG_ENDIAN); + + /* Skip overall length */ + offset += skipASNLength(tvb_get_guint8(tvb, offset)); + + /* UE Id. Skip tag and fixed length */ + offset += 2; + proto_tree_add_item(tree, hf_catapult_dct2000_ueid, + tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + + /* NAS PDU tag is 2 bytes */ + guint16 data_tag = tvb_get_ntohs(tvb, offset); + if (data_tag == 0x0021) { + offset += 2; + /* Also skip length */ + offset += 2; + protocol_handle = find_dissector("nas-5gs"); + + /* N.B. Ignoring some optional fields after the NAS PDU */ + } + } + } + + + /* Note that the first item of pinfo->pseudo_header->dct2000 will contain + the pseudo-header needed (in some cases) by the Wireshark dissector that + this packet data will be handed off to. */ + + + /***********************************************************************/ + /* Now hand off to the dissector of intended packet encapsulation type */ + + /* Get protocol handle, and set p2p_dir where necessary. + (packet-frame.c won't copy it from pseudo-header because it doesn't + know about Catapult DCT2000 encap type...) + */ + switch (encap) { + case WTAP_ENCAP_RAW_IP: + protocol_handle = find_dissector("ip"); +#if 0 + /* TODO: this doesn't work yet. + pseudo_header isn't copied from wtap to pinfo... */ + if ((pinfo->pseudo_header != NULL) && + (pinfo->pseudo_header->dct2000.inner_pseudo_header.pdcp.ueid != 0)) { + + proto_item *ti; + + /* Add PDCP thread info as generated fields */ + ti = proto_tree_add_uint(dct2000_tree, hf_catapult_dct2000_lte_ueid, tvb, 0, 0, + pinfo->pseudo_header->dct2000.inner_pseudo_header.pdcp.ueid); + proto_item_set_generated(ti); + ti = proto_tree_add_uint(dct2000_tree, hf_catapult_dct2000_lte_drbid, tvb, 0, 0, + pinfo->pseudo_header->dct2000.inner_pseudo_header.pdcp.drbid); + proto_item_set_generated(ti); + } +#endif + break; + case WTAP_ENCAP_ETHERNET: + protocol_handle = find_dissector("eth_withoutfcs"); + break; + case WTAP_ENCAP_ISDN: + /* + * XXX - if the file can handle B-channel traffic as well + * as D-channel traffic, have the libwiretap code fill + * in the channel, and call the ISDN dissector rather + * than the LAPD-with-pseudoheader dissector. + */ + protocol_handle = find_dissector("lapd-phdr"); + protocol_data = &pinfo->pseudo_header->dct2000.inner_pseudo_header.isdn; + break; + case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED: + protocol_handle = find_dissector("atm_untruncated"); + protocol_data = &pinfo->pseudo_header->dct2000.inner_pseudo_header.atm; + break; + case WTAP_ENCAP_PPP: + protocol_handle = find_dissector("ppp_hdlc"); + pinfo->p2p_dir = pinfo->pseudo_header->p2p.sent; + break; + case DCT2000_ENCAP_SSCOP: + protocol_handle = find_dissector("sscop"); + break; + case WTAP_ENCAP_FRELAY: + protocol_handle = find_dissector("fr"); + break; + case DCT2000_ENCAP_MTP2: + protocol_handle = find_dissector("mtp2"); + break; + case DCT2000_ENCAP_NBAP: + protocol_handle = find_dissector("nbap"); + break; + + case DCT2000_ENCAP_UNHANDLED: + /**********************************************************/ + /* The wiretap module wasn't able to set an encapsulation */ + /* type, but it may still be possible to dissect the data */ + /* if we know about the protocol or if we can recognise */ + /* and parse or skip a primitive header */ + /**********************************************************/ + + /* Show context.port in src or dest column as appropriate */ + if (direction == 0) { + col_add_fstr(pinfo->cinfo, COL_DEF_SRC, + "%s.%u", + context_name, + port_number); + } + else + if (direction == 1) { + col_add_fstr(pinfo->cinfo, COL_DEF_DST, + "%s.%u", + context_name, + port_number); + } + + + /**************************************************************************/ + /* These protocols have no encapsulation type, just look them up directly */ + + if ((strcmp(protocol_name, "rlc") == 0) || + (strcmp(protocol_name, "rlc_r4") == 0) || + (strcmp(protocol_name, "rlc_r5") == 0) || + (strcmp(protocol_name, "rlc_r6") == 0) || + (strcmp(protocol_name, "rlc_r7") == 0) || + (strcmp(protocol_name, "rlc_r8") == 0) || + (strcmp(protocol_name, "rlc_r9") == 0)) { + + dissect_rlc_umts(tvb, offset, pinfo, tree, direction, + outhdr_values, outhdr_values_found); + return tvb_captured_length(tvb); + } + + else + if ((strcmp(protocol_name, "mac_r8_lte") == 0) || + (strcmp(protocol_name, "mac_r9_lte") == 0) || + (strcmp(protocol_name, "mac_r10_lte") == 0)) { + protocol_handle = mac_lte_handle; + } + + else + if ((strcmp(protocol_name, "rlc_r8_lte") == 0) || + (strcmp(protocol_name, "rlc_r9_lte") == 0) || + (strcmp(protocol_name, "rlc_r10_lte") == 0)) { + protocol_handle = rlc_lte_handle; + } + + else + if ((strcmp(protocol_name, "pdcp_r8_lte") == 0) || + (strcmp(protocol_name, "pdcp_r9_lte") == 0) || + (strcmp(protocol_name, "pdcp_r10_lte") == 0)) { + /* Dissect proprietary header, then pass remainder to PDCP */ + dissect_pdcp_lte(tvb, offset, pinfo, tree); + return tvb_captured_length(tvb); + } + + + /* Work with generic XML protocol. */ + else + if (strcmp(protocol_name, "xml") == 0) { + protocol_handle = find_dissector("xml"); + } + + + /* Attempt to show tty messages as raw text */ + else + if (strcmp(protocol_name, "tty") == 0) { + dissect_tty_lines(tvb, pinfo, dct2000_tree, offset); + return tvb_captured_length(tvb); + } + + else + if (strcmp(protocol_name, "sipprim") == 0) { + protocol_handle = find_dissector("sipprim"); + } + + else + if (strcmp(protocol_name, "comment") == 0) { + /* Extract & add the string. */ + proto_item *string_ti; + const guint8 *string; + + /* Show comment string */ + string_ti = proto_tree_add_item_ret_string(dct2000_tree, hf_catapult_dct2000_comment, tvb, + offset, tvb_reported_length_remaining(tvb, offset), ENC_ASCII|ENC_NA, pinfo->pool, &string); + col_append_str(pinfo->cinfo, COL_INFO, string); + + if (catapult_dct2000_dissect_mac_lte_oob_messages) { + /* Look into string for out-of-band MAC events, such as SRReq, SRInd */ + check_for_oob_mac_lte_events(pinfo, tvb, tree, string); + } + + /* Look for and flag generic error messages */ + if (strncmp(string, ">> ERR", 6) == 0) { + proto_item *error_ti = proto_tree_add_item(dct2000_tree, hf_catapult_dct2000_error_comment, tvb, + offset, -1, ENC_NA); + proto_item_set_generated(error_ti); + expert_add_info_format(pinfo, string_ti, &ei_catapult_dct2000_error_comment_expert, + "%s", string); + } + + + /* Look for logged MAC-NR PDU */ + /* Example contents would be: + $Debug - NRMAC PDU: direction=0 rntiType=3 rnti=8495 ueid=1 SN=0 SFN=0 length=22 $40111212121212121212121212121212121212121212 + */ + int dir, rntiType, rnti, ueid, sn, sfn, length; + + if ((sscanf(string, "L1_App: NRMAC PDU: direction=%d rntiType=%d rnti=%d ueid=%d SN=%d SFN=%d length=%d $", + &dir, &rntiType, &rnti, &ueid, &sn, &sfn, &length) == 7) || + (sscanf(string, "NRMAC PDU: direction=%d rntiType=%d rnti=%d ueid=%d SN=%d SFN=%d length=%d $", + &dir, &rntiType, &rnti, &ueid, &sn, &sfn, &length) == 7)) + { + struct mac_nr_info *p_mac_nr_info; + + /* Only need to set info once per session? */ + /* p_mac_nr_info = get_mac_nr_proto_data(pinfo); */ + + /* Allocate & zero struct */ + p_mac_nr_info = wmem_new0(wmem_file_scope(), struct mac_nr_info); + + /* Populate the struct from outhdr values */ + p_mac_nr_info->radioType = FDD_RADIO; + + /* Map internal RNTI type -> Wireshark #defines from packet-mac-nr.h */ + switch (rntiType) { + case 2: + p_mac_nr_info->rntiType = P_RNTI; + break; + case 3: + p_mac_nr_info->rntiType = RA_RNTI; + break; + case 4: + p_mac_nr_info->rntiType = C_RNTI; /* temp C-RNTI */ + break; + case 5: + p_mac_nr_info->rntiType = C_RNTI; + break; + default: + p_mac_nr_info->rntiType = NO_RNTI; + break; + } + + p_mac_nr_info->direction = dir; + p_mac_nr_info->rnti = rnti; + // 0xFFFF trumps logged rntiType... + if (rnti == 65535) { + p_mac_nr_info->rntiType = SI_RNTI; + } + p_mac_nr_info->ueid = ueid; + + p_mac_nr_info->phr_type2_othercell = FALSE; + + p_mac_nr_info->length = length; + + /* Store info in packet */ + set_mac_nr_proto_data(pinfo, p_mac_nr_info); + + /* Payload is from $ to end of string */ + int data_offset = 0; + for (unsigned int n=0; n < strlen(string); n++) { + if (string[n] == '$') { + data_offset = n; + break; + } + } + + /* Convert data to hex. */ + char *mac_data = (char *)wmem_alloc(pinfo->pool, 2 + (strlen(string)-data_offset)/2); + int idx, m; + for (idx=0, m=data_offset+1; string[m] != '\0'; m+=2, idx++) { + mac_data[idx] = (hex_from_char(string[m]) << 4) + hex_from_char(string[m+1]); + } + + /* Create tvb */ + tvbuff_t *mac_nr_tvb = tvb_new_real_data(mac_data, idx, idx); + add_new_data_source(pinfo, mac_nr_tvb, "MAC-NR Payload"); + /* Call the dissector! */ + call_dissector_only(mac_nr_handle, mac_nr_tvb, pinfo, tree, NULL); + } + + /* Look for logged NRUP PDU */ + const char *nrup_pattern = "NRUP PDU: "; + char *start = strstr(string, nrup_pattern); + if (start) { + int off = 0; + + while (start[off] && start[off] != '$') { + off++; + } + + const char *payload = &start[off+1]; + + /* Pad out to nearest 4 bytes if necessary. */ + /* Convert data to hex. */ + #define MAX_NRUP_DATA_LENGTH 200 + static guint8 nrup_data[MAX_NRUP_DATA_LENGTH]; + int idx, m; + + /* The rest (or all) is data! */ + length = (int)strlen(payload) / 2; + for (m=0, idx=0; payload[m] != '\0' && idx < MAX_NRUP_DATA_LENGTH-4; m+=2, idx++) { + nrup_data[idx] = (hex_from_char(payload[m]) << 4) + hex_from_char(payload[m+1]); + } + /* Pad out to nearest 4 bytes if necessary. */ + if (length % 4 != 0) { + for (int p=length % 4; p < 4; p++) { + nrup_data[length++] = '\0'; + } + } + + /* Create separate NRUP tvb */ + tvbuff_t *nrup_tvb = tvb_new_real_data(nrup_data, length, length); + add_new_data_source(pinfo, nrup_tvb, "NRUP Payload"); + + /* Call the dissector! */ + call_dissector_only(nrup_handle, nrup_tvb, pinfo, tree, NULL); + } + + /* Read key info from formatted lines */ + /* e.g. NRPDCP: RRCPRIM:ueId= 1;setThreadAuthKey: RRC id=1 alg 2 key: 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 */ + if (strstr(string, "setThreadAuthKey:")) { + guint ue_id, id, alg; + if (!PINFO_FD_VISITED(pinfo) && sscanf(string, "NRPDCP: RRCPRIM:ueId= %u;setThreadAuthKey: RRC id=%u alg %u key: ", &ue_id, &id, &alg) == 3) { + char *key = g_strdup(strstr(string, "key: ")+5); + set_pdcp_nr_rrc_integrity_key(ue_id, key, pinfo->num); + g_free(key); + } + else if (!PINFO_FD_VISITED(pinfo) && sscanf(string, "NRPDCP: RRCPRIM:ueId= %u;setThreadAuthKey: UP id=%u alg %u key: ", &ue_id, &id, &alg) == 3) { + char *key = g_strdup(strstr(string, "key: ")+5); + set_pdcp_nr_up_integrity_key(ue_id, key, pinfo->num); + g_free(key); + } + } + else if (strstr(string, "setThreadCryptKey:")) { + guint ue_id, id, alg; + if (!PINFO_FD_VISITED(pinfo) && sscanf(string, "NRPDCP: RRCPRIM:ueId= %u;setThreadCryptKey: RRC id=%u alg %u key: ", &ue_id, &id, &alg) == 3) { + char *key = g_strdup(strstr(string, "key: ")+5); + set_pdcp_nr_rrc_ciphering_key(ue_id, key, pinfo->num); + g_free(key); + } + else if (!PINFO_FD_VISITED(pinfo) && sscanf(string, "NRPDCP: RRCPRIM:ueId= %u;setThreadCryptKey: UP id=%u alg %u key: ", &ue_id, &id, &alg) == 3) { + char *key = g_strdup(strstr(string, "key: ")+5); + set_pdcp_nr_up_ciphering_key(ue_id, key, pinfo->num); + g_free(key); + } + } + + /* 'raw' (ethernet) frames logged as text comments */ + int raw_interface; + char raw_direction; + if (sscanf(string, "RawTraffic: Interface: %d %c $", + &raw_interface, &raw_direction) == 2) + { + /* Interface */ + proto_tree_add_uint(tree, hf_catapult_dct2000_rawtraffic_interface, + tvb, 0, 0, raw_interface); + + /* Direction */ + proto_tree_add_uint(tree, hf_catapult_dct2000_rawtraffic_direction, + tvb, 0, 0, raw_direction == 'r'); + + /* Payload is from $ to end of string */ + int data_offset = 0; + for (unsigned int n=0; n < strlen(string); n++) { + if (string[n] == '$') { + data_offset = n; + break; + } + } + + /* Convert data to hex. */ + static guint8 eth_data[36000]; + int idx, m; + for (idx=0, m=data_offset+1; idx<36000 && string[m] != '\0'; m+=2, idx++) { + eth_data[idx] = (hex_from_char(string[m]) << 4) + hex_from_char(string[m+1]); + } + + /* Create tvb */ + tvbuff_t *raw_traffic_tvb = tvb_new_real_data(eth_data, idx, idx); + add_new_data_source(pinfo, raw_traffic_tvb, "Raw-Traffic Payload"); + + /* PDU */ + proto_tree_add_item(tree, hf_catapult_dct2000_rawtraffic_pdu, raw_traffic_tvb, + 0, tvb_reported_length(raw_traffic_tvb), ENC_NA); + + /* Call the dissector! */ + call_dissector_only(eth_handle, raw_traffic_tvb, pinfo, tree, NULL); + } + + return tvb_captured_length(tvb); + } + + else + if (strcmp(protocol_name, "sprint") == 0) { + /* Extract & add the string. */ + const guint8 *string; + + /* Show sprint string */ + proto_tree_add_item_ret_string(dct2000_tree, hf_catapult_dct2000_sprint, tvb, + offset, tvb_reported_length_remaining(tvb, offset), ENC_ASCII|ENC_NA, pinfo->pool, &string); + col_append_str(pinfo->cinfo, COL_INFO, string); + + return tvb_captured_length(tvb); + } + + /* RRC (LTE or NR). + Dissect proprietary header, then pass remainder + to RRC dissector (depending upon direction and channel type) */ + else + if (catapult_dct2000_dissect_lte_rrc && + ((strcmp(protocol_name, "rrc_r8_lte") == 0) || + (strcmp(protocol_name, "rrcpdcpprim_r8_lte") == 0) || + (strcmp(protocol_name, "rrc_r9_lte") == 0) || + (strcmp(protocol_name, "rrcpdcpprim_r9_lte") == 0) || + (strcmp(protocol_name, "rrc_r10_lte") == 0) || + (strcmp(protocol_name, "rrc_r11_lte") == 0) || + (strcmp(protocol_name, "rrc_r12_lte") == 0) || + (strcmp(protocol_name, "rrc_r13_lte") == 0) || + (strcmp(protocol_name, "rrc_r15_lte") == 0) || + (strcmp(protocol_name, "rrc_r16_lte") == 0) || + (strcmp(protocol_name, "rrcpdcpprim_r15_lte") == 0))) { + + dissect_rrc_lte_nr(tvb, offset, pinfo, tree, LTE); + return tvb_captured_length(tvb); + } + else if ((strcmp(protocol_name, "rrc_r15_5g") == 0) || + (strcmp(protocol_name, "rrc_r16_5g") == 0)) { + dissect_rrc_lte_nr(tvb, offset, pinfo, tree, NR); + return tvb_captured_length(tvb); + } + + else + if ((strcmp(protocol_name, "ccpri_r8_lte") == 0) || + (strcmp(protocol_name, "ccpri_r9_lte") == 0)) { + + /* Dissect proprietary header, then pass remainder to lapb */ + dissect_ccpri_lte(tvb, offset, pinfo, tree); + return tvb_captured_length(tvb); + } + + /* Many DCT2000 protocols have at least one IPPrim variant. If the + protocol name can be matched to a dissector, try to find the + UDP/TCP data inside and dissect it. + */ + + if (!protocol_handle && catapult_dct2000_try_ipprim_heuristic) { + guint32 source_addr_offset = 0, dest_addr_offset = 0; + guint8 source_addr_length = 0, dest_addr_length = 0; + guint32 source_port_offset = 0, dest_port_offset = 0; + port_type type_of_port = PT_NONE; + guint16 conn_id_offset = 0; + int offset_before_ipprim_header = offset; + + /* For ipprim, want to show ipprim header even if can't find dissector to call for payload.. */ + if (find_ipprim_data_offset(tvb, &offset, direction, + &source_addr_offset, &source_addr_length, + &dest_addr_offset, &dest_addr_length, + &source_port_offset, &dest_port_offset, + &type_of_port, + &conn_id_offset)) { + + proto_tree *ipprim_tree; + proto_item *ipprim_ti; + struct e_in6_addr sourcev6, destv6; + + /* Fetch IPv6 addresses */ + if (source_addr_length != 4) { + tvb_get_ipv6(tvb, source_addr_offset, &sourcev6); + } + if (dest_addr_length != 4) { + tvb_get_ipv6(tvb, dest_addr_offset, &destv6); + } + + + /* Will use this dissector then. */ + heur_protocol_handle = look_for_dissector(protocol_name); + protocol_handle = heur_protocol_handle; + + /* Add address parameters to tree */ + /* Unfortunately can't automatically create a conversation filter for this... + I *could* create a fake IP header from these details, but then it would be tricky + to get the FP dissector called as it has no well-known ports or heuristics... */ + ipprim_ti = proto_tree_add_string_format(dct2000_tree, hf_catapult_dct2000_ipprim_addresses, + tvb, offset_before_ipprim_header, 0, + "", "IPPrim transport (%s): %s:%u -> %s:%u", + (type_of_port == PT_UDP) ? "UDP" : "TCP", + (source_addr_offset) ? + ((source_addr_length == 4) ? + get_hostname(tvb_get_ipv4(tvb, source_addr_offset)) : + get_hostname6(&sourcev6) + ) : + "0.0.0.0", + (source_port_offset) ? + tvb_get_ntohs(tvb, source_port_offset) : + 0, + (dest_addr_offset) ? + ((source_addr_length == 4) ? + get_hostname(tvb_get_ipv4(tvb, dest_addr_offset)) : + get_hostname6(&destv6) + ) : + "0.0.0.0", + (dest_port_offset) ? + tvb_get_ntohs(tvb, dest_port_offset) : + 0); + if ((type_of_port == PT_TCP) && (conn_id_offset != 0)) { + proto_item_append_text(ipprim_ti, " (conn_id=%u)", tvb_get_ntohs(tvb, conn_id_offset)); + } + + /* Add these IPPRIM fields inside an IPPRIM subtree */ + ipprim_tree = proto_item_add_subtree(ipprim_ti, ett_catapult_dct2000_ipprim); + + /* Try to add right stuff to pinfo so conversation stuff works... */ + pinfo->ptype = type_of_port; + + /* Add addresses & ports into ipprim tree. + Also set address info in pinfo for conversations... */ + if (source_addr_offset != 0) { + proto_item *addr_ti; + + set_address_tvb(&pinfo->net_src, + (source_addr_length == 4) ? AT_IPv4 : AT_IPv6, + source_addr_length, tvb, source_addr_offset); + copy_address_shallow(&pinfo->src, &pinfo->net_src); + + proto_tree_add_item(ipprim_tree, + (source_addr_length == 4) ? + hf_catapult_dct2000_ipprim_src_addr_v4 : + hf_catapult_dct2000_ipprim_src_addr_v6, + tvb, source_addr_offset, source_addr_length, + ENC_NA); + + /* Add hidden item for "side-less" addr */ + addr_ti = proto_tree_add_item(ipprim_tree, + (source_addr_length == 4) ? + hf_catapult_dct2000_ipprim_addr_v4 : + hf_catapult_dct2000_ipprim_addr_v6, + tvb, source_addr_offset, source_addr_length, + ENC_NA); + proto_item_set_hidden(addr_ti); + } + if (source_port_offset != 0) { + proto_item *port_ti; + + pinfo->srcport = tvb_get_ntohs(tvb, source_port_offset); + + proto_tree_add_item(ipprim_tree, + (type_of_port == PT_UDP) ? + hf_catapult_dct2000_ipprim_udp_src_port : + hf_catapult_dct2000_ipprim_tcp_src_port, + tvb, source_port_offset, 2, ENC_BIG_ENDIAN); + port_ti = proto_tree_add_item(ipprim_tree, + (type_of_port == PT_UDP) ? + hf_catapult_dct2000_ipprim_udp_port : + hf_catapult_dct2000_ipprim_tcp_port, + tvb, source_port_offset, 2, ENC_BIG_ENDIAN); + proto_item_set_hidden(port_ti); + } + if (dest_addr_offset != 0) { + proto_item *addr_ti; + + set_address_tvb(&pinfo->net_dst, + (dest_addr_length == 4) ? AT_IPv4 : AT_IPv6, + dest_addr_length, tvb, dest_addr_offset); + copy_address_shallow(&pinfo->dst, &pinfo->net_dst); + proto_tree_add_item(ipprim_tree, + (dest_addr_length == 4) ? + hf_catapult_dct2000_ipprim_dst_addr_v4 : + hf_catapult_dct2000_ipprim_dst_addr_v6, + tvb, dest_addr_offset, dest_addr_length, + ENC_NA); + + /* Add hidden item for "side-less" addr */ + addr_ti = proto_tree_add_item(ipprim_tree, + (dest_addr_length == 4) ? + hf_catapult_dct2000_ipprim_addr_v4 : + hf_catapult_dct2000_ipprim_addr_v6, + tvb, dest_addr_offset, dest_addr_length, + ENC_NA); + proto_item_set_hidden(addr_ti); + } + if (dest_port_offset != 0) { + proto_item *port_ti; + + pinfo->destport = tvb_get_ntohs(tvb, dest_port_offset); + + proto_tree_add_item(ipprim_tree, + (type_of_port == PT_UDP) ? + hf_catapult_dct2000_ipprim_udp_dst_port : + hf_catapult_dct2000_ipprim_tcp_dst_port, + tvb, dest_port_offset, 2, ENC_BIG_ENDIAN); + port_ti = proto_tree_add_item(ipprim_tree, + (type_of_port == PT_UDP) ? + hf_catapult_dct2000_ipprim_udp_port : + hf_catapult_dct2000_ipprim_tcp_port, + tvb, dest_port_offset, 2, ENC_BIG_ENDIAN); + proto_item_set_hidden(port_ti); + } + if (conn_id_offset != 0) { + proto_tree_add_item(ipprim_tree, + hf_catapult_dct2000_ipprim_conn_id, + tvb, conn_id_offset, 2, ENC_BIG_ENDIAN); + } + + + /* Set source and dest columns now (will be overwriiten if + src and dst IP addresses set) */ + if (source_addr_offset) { + col_append_fstr(pinfo->cinfo, COL_DEF_SRC, + "(%s:%u)", + get_hostname(tvb_get_ipv4(tvb, source_addr_offset)), + tvb_get_ntohs(tvb, source_port_offset)); + } + if (dest_addr_offset) { + col_append_fstr(pinfo->cinfo, COL_DEF_DST, + "(%s:%u)", + get_hostname(tvb_get_ipv4(tvb, dest_addr_offset)), + tvb_get_ntohs(tvb, dest_port_offset)); + } + + /* Set length for IPPrim tree */ + proto_item_set_len(ipprim_tree, offset - offset_before_ipprim_header); + } + } + + + /* Try SCTP Prim heuristic if configured to */ + if (!protocol_handle && catapult_dct2000_try_sctpprim_heuristic) { + guint32 dest_addr_offset = 0; + guint16 dest_addr_length = 0; + guint32 dest_port_offset = 0; + int offset_before_sctpprim_header = offset; + + heur_protocol_handle = look_for_dissector(protocol_name); + if ((heur_protocol_handle != 0) && + (find_sctpprim_variant1_data_offset(tvb, &offset, + &dest_addr_offset, + &dest_addr_length, + &dest_port_offset) || + find_sctpprim_variant3_data_offset(tvb, &offset, + &dest_addr_offset, + &dest_addr_length, + &dest_port_offset))) { + + proto_tree *sctpprim_tree; + proto_item *ti_local; + + /* Will use this dissector then. */ + protocol_handle = heur_protocol_handle; + + ti_local = proto_tree_add_string_format(dct2000_tree, hf_catapult_dct2000_sctpprim_addresses, + tvb, offset_before_sctpprim_header, 0, + "", "SCTPPrim transport: -> %s:%u", + (dest_addr_offset) ? + ((dest_addr_length == 4) ? + get_hostname(tvb_get_ipv4(tvb, dest_addr_offset)) : + "<ipv6-address>" + ) : + "0.0.0.0", + (dest_port_offset) ? + tvb_get_ntohs(tvb, dest_port_offset) : + 0); + + /* Add these SCTPPRIM fields inside an SCTPPRIM subtree */ + sctpprim_tree = proto_item_add_subtree(ti_local, ett_catapult_dct2000_sctpprim); + + /* Destination address */ + if (dest_addr_offset != 0) { + proto_item *addr_ti; + + set_address_tvb(&pinfo->net_dst, + (dest_addr_length == 4) ? AT_IPv4 : AT_IPv6, + dest_addr_length, tvb, dest_addr_offset); + copy_address_shallow(&pinfo->dst, &pinfo->net_dst); + proto_tree_add_item(sctpprim_tree, + (dest_addr_length == 4) ? + hf_catapult_dct2000_sctpprim_dst_addr_v4 : + hf_catapult_dct2000_sctpprim_dst_addr_v6, + tvb, dest_addr_offset, dest_addr_length, + ENC_NA); + + /* Add hidden item for "side-less" addr */ + addr_ti = proto_tree_add_item(sctpprim_tree, + (dest_addr_length == 4) ? + hf_catapult_dct2000_sctpprim_addr_v4 : + hf_catapult_dct2000_sctpprim_addr_v6, + tvb, dest_addr_offset, dest_addr_length, + ENC_NA); + proto_item_set_hidden(addr_ti); + } + + if (dest_port_offset != 0) { + pinfo->destport = tvb_get_ntohs(tvb, dest_port_offset); + + proto_tree_add_item(sctpprim_tree, + hf_catapult_dct2000_sctpprim_dst_port, + tvb, dest_port_offset, 2, ENC_BIG_ENDIAN); + } + + /* Set length for SCTPPrim tree */ + proto_item_set_len(sctpprim_tree, offset - offset_before_sctpprim_header); + } + } + + /* Next chance: is there a (private) registered protocol of the form + "dct2000.protocol" ? */ + if (protocol_handle == 0) { + /* TODO: only look inside if a preference enabled? */ + char dotted_protocol_name[128]; + /* N.B. avoiding snprintf(), which was slow */ + (void) g_strlcpy(dotted_protocol_name, "dct2000.", 128); + (void) g_strlcpy(dotted_protocol_name+8, protocol_name, 128-8); + protocol_handle = find_dissector(dotted_protocol_name); + } + + /* Last resort: Allow any PDU to be dissected if the protocol matches with + a dissector name */ + if ( !protocol_handle && catapult_dct2000_use_protocol_name_as_dissector_name) { + protocol_handle = find_dissector(protocol_name); + } + + + break; + + default: + /* !! If get here, there is a mismatch between + this dissector and the wiretap module catapult_dct2000.c !! + */ + DISSECTOR_ASSERT_NOT_REACHED(); + return 0; + } + + /* Set selection length of dct2000 tree */ + proto_item_set_len(dct2000_tree, offset); + + /* Try appropriate dissector, if one has been selected */ + if (protocol_handle != 0) { + /* Dissect the remainder of the frame using chosen protocol handle */ + next_tvb = tvb_new_subset_remaining(tvb, offset); + sub_dissector_result = call_dissector_only(protocol_handle, next_tvb, pinfo, tree, protocol_data); + } + + + if (protocol_handle == 0 || sub_dissector_result == 0) { + /* Could get here because: + - encap is DCT2000_ENCAP_UNHANDLED and we still didn't handle it, OR + - desired protocol is unavailable (probably disabled), OR + - protocol rejected our data + Show remaining bytes as unparsed data */ + proto_tree_add_item(dct2000_tree, hf_catapult_dct2000_unparsed_data, + tvb, offset, -1, ENC_NA); + + col_add_fstr(pinfo->cinfo, COL_INFO, + "Not dissected (context=%s.%u t=%s %c prot=%s (v=%s))", + context_name, + port_number, + timestamp_string, + (direction == 0) ? 'S' : 'R', + protocol_name, + variant_string); + } + else { + /* Show number of dissected bytes */ + if (dct2000_tree) { + proto_item *ti_local = proto_tree_add_uint(dct2000_tree, + hf_catapult_dct2000_dissected_length, + tvb, 0, 0, tvb_reported_length(tvb)-offset); + proto_item_set_generated(ti_local); + } + } + + return tvb_captured_length(tvb); +} + + + +/******************************************************************************/ +/* Associate this protocol with the Catapult DCT2000 file encapsulation type. */ +/******************************************************************************/ +void proto_reg_handoff_catapult_dct2000(void) +{ + dissector_add_uint("wtap_encap", WTAP_ENCAP_CATAPULT_DCT2000, catapult_dct2000_handle); + + mac_lte_handle = find_dissector("mac-lte"); + rlc_lte_handle = find_dissector("rlc-lte"); + pdcp_lte_handle = find_dissector("pdcp-lte"); + + mac_nr_handle = find_dissector("mac-nr"); + nrup_handle = find_dissector("nrup"); + eth_handle = find_dissector("eth_withoutfcs"); + nrup_handle = find_dissector("nrup"); +} + +/****************************************/ +/* Register the protocol */ +/****************************************/ +void proto_register_catapult_dct2000(void) +{ + static hf_register_info hf[] = + { + { &hf_catapult_dct2000_context, + { "Context", + "dct2000.context", FT_STRING, BASE_NONE, NULL, 0x0, + "Context name", HFILL + } + }, + { &hf_catapult_dct2000_port_number, + { "Context Port number", + "dct2000.context_port", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_timestamp, + { "Timestamp", + "dct2000.timestamp", FT_DOUBLE, BASE_NONE, NULL, 0x0, + "File timestamp", HFILL + } + }, + { &hf_catapult_dct2000_protocol, + { "DCT2000 protocol", + "dct2000.protocol", FT_STRING, BASE_NONE, NULL, 0x0, + "Original (DCT2000) protocol name", HFILL + } + }, + { &hf_catapult_dct2000_variant, + { "Protocol variant", + "dct2000.variant", FT_STRING, BASE_NONE, NULL, 0x0, + "DCT2000 protocol variant", HFILL + } + }, + { &hf_catapult_dct2000_outhdr, + { "Out-header", + "dct2000.outhdr", FT_STRING, BASE_NONE, NULL, 0x0, + "DCT2000 protocol outhdr", HFILL + } + }, + { &hf_catapult_dct2000_direction, + { "Direction", + "dct2000.direction", FT_UINT8, BASE_DEC, VALS(direction_vals), 0x0, + "Frame direction (Sent or Received)", HFILL + } + }, + { &hf_catapult_dct2000_encap, + { "Wireshark encapsulation", + "dct2000.encapsulation", FT_UINT8, BASE_DEC, VALS(encap_vals), 0x0, + "Wireshark frame encapsulation used", HFILL + } + }, + { &hf_catapult_dct2000_unparsed_data, + { "Unparsed protocol data", + "dct2000.unparsed_data", FT_BYTES, BASE_NONE, NULL, 0x0, + "Unparsed DCT2000 protocol data", HFILL + } + }, + { &hf_catapult_dct2000_comment, + { "Comment", + "dct2000.comment", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_sprint, + { "Sprint text", + "dct2000.sprint", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_error_comment, + { "Error comment", + "dct2000.error-comment", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_dissected_length, + { "Dissected length", + "dct2000.dissected-length", FT_UINT16, BASE_DEC, NULL, 0x0, + "Number of bytes dissected by subdissector(s)", HFILL + } + }, + + { &hf_catapult_dct2000_ipprim_addresses, + { "IPPrim Addresses", + "dct2000.ipprim", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_ipprim_src_addr_v4, + { "Source Address", + "dct2000.ipprim.src", FT_IPv4, BASE_NONE, NULL, 0x0, + "IPPrim IPv4 Source Address", HFILL + } + }, + { &hf_catapult_dct2000_ipprim_src_addr_v6, + { "Source Address", + "dct2000.ipprim.srcv6", FT_IPv6, BASE_NONE, NULL, 0x0, + "IPPrim IPv6 Source Address", HFILL + } + }, + { &hf_catapult_dct2000_ipprim_dst_addr_v4, + { "Destination Address", + "dct2000.ipprim.dst", FT_IPv4, BASE_NONE, NULL, 0x0, + "IPPrim IPv4 Destination Address", HFILL + } + }, + { &hf_catapult_dct2000_ipprim_dst_addr_v6, + { "Destination Address", + "dct2000.ipprim.dstv6", FT_IPv6, BASE_NONE, NULL, 0x0, + "IPPrim IPv6 Destination Address", HFILL + } + }, + { &hf_catapult_dct2000_ipprim_addr_v4, + { "Address", + "dct2000.ipprim.addr", FT_IPv4, BASE_NONE, NULL, 0x0, + "IPPrim IPv4 Address", HFILL + } + }, + { &hf_catapult_dct2000_ipprim_addr_v6, + { "Address", + "dct2000.ipprim.addrv6", FT_IPv6, BASE_NONE, NULL, 0x0, + "IPPrim IPv6 Address", HFILL + } + }, + { &hf_catapult_dct2000_ipprim_udp_src_port, + { "UDP Source Port", + "dct2000.ipprim.udp.srcport", FT_UINT16, BASE_DEC, NULL, 0x0, + "IPPrim UDP Source Port", HFILL + } + }, + { &hf_catapult_dct2000_ipprim_udp_dst_port, + { "UDP Destination Port", + "dct2000.ipprim.udp.dstport", FT_UINT16, BASE_DEC, NULL, 0x0, + "IPPrim UDP Destination Port", HFILL + } + }, + { &hf_catapult_dct2000_ipprim_udp_port, + { "UDP Port", + "dct2000.ipprim.udp.port", FT_UINT16, BASE_DEC, NULL, 0x0, + "IPPrim UDP Port", HFILL + } + }, + { &hf_catapult_dct2000_ipprim_tcp_src_port, + { "TCP Source Port", + "dct2000.ipprim.tcp.srcport", FT_UINT16, BASE_DEC, NULL, 0x0, + "IPPrim TCP Source Port", HFILL + } + }, + { &hf_catapult_dct2000_ipprim_tcp_dst_port, + { "TCP Destination Port", + "dct2000.ipprim.tcp.dstport", FT_UINT16, BASE_DEC, NULL, 0x0, + "IPPrim TCP Destination Port", HFILL + } + }, + { &hf_catapult_dct2000_ipprim_tcp_port, + { "TCP Port", + "dct2000.ipprim.tcp.port", FT_UINT16, BASE_DEC, NULL, 0x0, + "IPPrim TCP Port", HFILL + } + }, + { &hf_catapult_dct2000_ipprim_conn_id, + { "Conn Id", + "dct2000.ipprim.conn-id", FT_UINT16, BASE_DEC, NULL, 0x0, + "IPPrim TCP Connection ID", HFILL + } + }, + + { &hf_catapult_dct2000_sctpprim_addresses, + { "SCTPPrim Addresses", + "dct2000.sctpprim", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_sctpprim_dst_addr_v4, + { "Destination Address", + "dct2000.sctpprim.dst", FT_IPv4, BASE_NONE, NULL, 0x0, + "SCTPPrim IPv4 Destination Address", HFILL + } + }, + { &hf_catapult_dct2000_sctpprim_dst_addr_v6, + { "Destination Address", + "dct2000.sctpprim.dstv6", FT_IPv6, BASE_NONE, NULL, 0x0, + "SCTPPrim IPv6 Destination Address", HFILL + } + }, + { &hf_catapult_dct2000_sctpprim_addr_v4, + { "Address", + "dct2000.sctpprim.addr", FT_IPv4, BASE_NONE, NULL, 0x0, + "SCTPPrim IPv4 Address", HFILL + } + }, + { &hf_catapult_dct2000_sctpprim_addr_v6, + { "Address", + "dct2000.sctpprim.addrv6", FT_IPv6, BASE_NONE, NULL, 0x0, + "SCTPPrim IPv6 Address", HFILL + } + }, + { &hf_catapult_dct2000_sctpprim_dst_port, + { "UDP Destination Port", + "dct2000.sctprim.dstport", FT_UINT16, BASE_DEC, NULL, 0x0, + "SCTPPrim Destination Port", HFILL + } + }, + + { &hf_catapult_dct2000_tty, + { "tty contents", + "dct2000.tty", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_tty_line, + { "tty line", + "dct2000.tty-line", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + + { &hf_catapult_dct2000_ueid, + { "UE Id", + "dct2000.ueid", FT_UINT32, BASE_DEC, NULL, 0x0, + "User Equipment Identifier", HFILL + } + }, + { &hf_catapult_dct2000_srbid, + { "srbid", + "dct2000.srbid", FT_UINT8, BASE_DEC, NULL, 0x0, + "Signalling Radio Bearer Identifier", HFILL + } + }, + { &hf_catapult_dct2000_drbid, + { "drbid", + "dct2000.drbid", FT_UINT8, BASE_DEC, NULL, 0x0, + "Data Radio Bearer Identifier", HFILL + } + }, + { &hf_catapult_dct2000_cellid, + { "Cell-Id", + "dct2000.cellid", FT_UINT16, BASE_DEC, NULL, 0x0, + "Cell Identifier", HFILL + } + }, + { &hf_catapult_dct2000_bcch_transport, + { "BCCH Transport", + "dct2000.bcch-transport", FT_UINT16, BASE_DEC, VALS(bcch_transport_vals), 0x0, + "BCCH Transport Channel", HFILL + } + }, + { &hf_catapult_dct2000_rlc_op, + { "RLC Op", + "dct2000.rlc-op", FT_UINT8, BASE_DEC, VALS(rlc_op_vals), 0x0, + "RLC top-level op", HFILL + } + }, + { &hf_catapult_dct2000_rlc_channel_type, + { "RLC Logical Channel Type", + "dct2000.rlc-logchan-type", FT_UINT8, BASE_DEC, VALS(rlc_logical_channel_vals), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_rlc_mui, + { "MUI", + "dct2000.rlc-mui", FT_UINT16, BASE_DEC, NULL, 0x0, + "RLC MUI", HFILL + } + }, + { &hf_catapult_dct2000_rlc_cnf, + { "CNF", + "dct2000.rlc-cnf", FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0x0, + "RLC CNF", HFILL + } + }, + { &hf_catapult_dct2000_rlc_discard_req, + { "Discard Req", + "dct2000.rlc-discard-req", FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0x0, + "RLC Discard Req", HFILL + } + }, + { &hf_catapult_dct2000_carrier_type, + { "Carrier Type", + "dct2000.carrier-type", FT_UINT8, BASE_DEC, VALS(carrier_type_vals), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_cell_group, + { "Cell Group", + "dct2000.cell-group", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_carrier_id, + { "Carrier Id", + "dct2000.carrier-id", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + + { &hf_catapult_dct2000_security_mode_params, + { "Security Mode Params", + "dct2000.security-mode-params", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_uplink_sec_mode, + { "Uplink Security Mode", + "dct2000.uplink-security-mode", FT_UINT8, BASE_DEC, VALS(security_mode_vals), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_downlink_sec_mode, + { "Downlink Security Mode", + "dct2000.downlink-security-mode", FT_UINT8, BASE_DEC, VALS(security_mode_vals), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_ciphering_algorithm, + { "Ciphering Algorithm", + "dct2000.ciphering-algorithm", FT_UINT8, BASE_DEC, VALS(ciphering_algorithm_vals), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_ciphering_key, + { "Ciphering Key", + "dct2000.ciphering-key", FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_integrity_algorithm, + { "Integrity Algorithm", + "dct2000.integrity-algorithm", FT_UINT8, BASE_DEC, VALS(integrity_algorithm_vals), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_integrity_key, + { "Integrity Key", + "dct2000.integrity-key", FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + + { &hf_catapult_dct2000_lte_ccpri_opcode, + { "CCPRI opcode", + "dct2000.lte.ccpri.opcode", FT_UINT8, BASE_DEC, VALS(ccpri_opcode_vals), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_lte_ccpri_status, + { "Status", + "dct2000.lte.ccpri.status", FT_BOOLEAN, 8, TFS(&tfs_error_ok), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_lte_ccpri_channel, + { "Channel", + "dct2000.lte.ccpri.channel", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + + { &hf_catapult_dct2000_lte_nas_rrc_opcode, + { "NAS RRC Opcode", + "dct2000.lte.nas-rrc.opcode", FT_UINT8, BASE_DEC, VALS(lte_nas_rrc_opcode_vals), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_lte_nas_rrc_establish_cause, + { "Establish Cause", + "dct2000.lte.nas-rrc.establish-cause", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_lte_nas_rrc_priority, + { "Priority", + "dct2000.lte.nas-rrc.priority", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_lte_nas_rrc_release_cause, + { "Priority", + "dct2000.lte.nas-rrc.priority", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_nr_nas_s1ap_opcode, + { "NAS S1AP Opcode", + "dct2000.nas-s1ap.opcode", FT_UINT8, BASE_DEC, VALS(nas_s1ap_opcode_vals), 0x0, + NULL, HFILL + } + }, + + { &hf_catapult_dct2000_rbid, + { "Channel", + "dct2000.rbid", FT_UINT8, BASE_DEC | BASE_EXT_STRING, &rlc_rbid_vals_ext, 0x0, + "Channel (rbid)", HFILL + } + }, + { &hf_catapult_dct2000_ccch_id, + { "CCCH Id", + "dct2000.ccch-id", FT_UINT8, BASE_DEC, NULL, 0x0, + "CCCH Identifier", HFILL + } + }, + { &hf_catapult_dct2000_no_crc_error, + { "No CRC Error", + "dct2000.no-crc-error", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_crc_error, + { "CRC Error", + "dct2000.crc-error", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_clear_tx_buffer, + { "Clear Tx Buffer", + "dct2000.clear-tx-buffer", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_buffer_occupancy, + { "Buffer Occupancy", + "dct2000.buffer-occupancy", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_pdu_size, + { "PDU Size", + "dct2000.pdu-size", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_ueid_type, + { "UEId Type", + "dct2000.ueid-type", FT_UINT8, BASE_DEC, VALS(ueid_type_vals), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_tx_priority, + { "Tx Priority", + "dct2000.tx-priority", FT_BOOLEAN, 8, TFS(&tfs_high_normal), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_last_in_seg_set, + { "Last in seg set", + "dct2000.last-in-seg-set", FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_rx_timing_deviation, + { "Tx Timing Deviation", + "dct2000.rx-timing-deviation", FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_transport_channel_type, + { "Transport Channel Type", + "dct2000.transport_channel_type", FT_UINT8, BASE_DEC, VALS(transport_channel_type_vals), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_no_padding_bits, + { "Number of padding bits", + "dct2000.number-of-padding-bits", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + + { &hf_catapult_dct2000_rawtraffic_interface, + { "Interface", + "dct2000.rawtraffic.interface", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_rawtraffic_direction, + { "Direction", + "dct2000.rawtraffic.direction", FT_UINT8, BASE_DEC, VALS(direction_vals), 0x0, + NULL, HFILL + } + }, + { &hf_catapult_dct2000_rawtraffic_pdu, + { "PDU", + "dct2000.rawtraffic.pdu", FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + } + }; + + static gint *ett[] = + { + &ett_catapult_dct2000, + &ett_catapult_dct2000_ipprim, + &ett_catapult_dct2000_sctpprim, + &ett_catapult_dct2000_tty, + &ett_catapult_dct2000_security_mode_params + }; + + static ei_register_info ei[] = { + { &ei_catapult_dct2000_lte_ccpri_status_error, { "dct2000.lte.ccpri.status.error", PI_SEQUENCE, PI_ERROR, "CCPRI Indication has error status", EXPFILL }}, + { &ei_catapult_dct2000_error_comment_expert, { "dct2000.error-comment.expert", PI_SEQUENCE, PI_ERROR, "Formatted expert comment", EXPFILL }}, + { &ei_catapult_dct2000_string_invalid, { "dct2000.string.invalid", PI_MALFORMED, PI_ERROR, "String must contain an integer", EXPFILL }} + }; + + module_t *catapult_dct2000_module; + expert_module_t* expert_catapult_dct2000; + + /* Register protocol. */ + proto_catapult_dct2000 = proto_register_protocol("Catapult DCT2000 packet", + "DCT2000", + "dct2000"); + proto_register_field_array(proto_catapult_dct2000, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_catapult_dct2000 = expert_register_protocol(proto_catapult_dct2000); + expert_register_field_array(expert_catapult_dct2000, ei, array_length(ei)); + + /* Allow dissector to find be found by name. */ + catapult_dct2000_handle = register_dissector("dct2000", dissect_catapult_dct2000, proto_catapult_dct2000); + + /* Preferences */ + catapult_dct2000_module = prefs_register_protocol(proto_catapult_dct2000, NULL); + + /* This preference no longer supported (introduces linkage dependency between + dissectors and wiretap) */ + prefs_register_obsolete_preference(catapult_dct2000_module, "board_ports_only"); + prefs_register_obsolete_preference(catapult_dct2000_module, "decode_lte_s1ap"); + + /* Determines whether for not-handled protocols we should try to parse it if: + - it looks like it's embedded in an ipprim message, AND + - the DCT2000 protocol name can be matched to a Wireshark dissector name */ + prefs_register_bool_preference(catapult_dct2000_module, "ipprim_heuristic", + "Use IP Primitive heuristic", + "If a payload looks like it's embedded in an " + "IP primitive message, and there is a Wireshark " + "dissector matching the DCT2000 protocol name, " + "try parsing the payload using that dissector", + &catapult_dct2000_try_ipprim_heuristic); + + /* Determines whether for not-handled protocols we should try to parse it if: + - it looks like it's embedded in an sctpprim message, AND + - the DCT2000 protocol name can be matched to a Wireshark dissector name */ + prefs_register_bool_preference(catapult_dct2000_module, "sctpprim_heuristic", + "Use SCTP Primitive heuristic", + "If a payload looks like it's embedded in an " + "SCTP primitive message, and there is a Wireshark " + "dissector matching the DCT2000 protocol name, " + "try parsing the payload using that dissector", + &catapult_dct2000_try_sctpprim_heuristic); + + /* Determines whether LTE RRC messages should be dissected */ + prefs_register_bool_preference(catapult_dct2000_module, "decode_lte_rrc", + "Attempt to decode LTE RRC frames", + "When set, attempt to decode LTE RRC frames. " + "Note that this won't affect other protocols " + "that also call the LTE RRC dissector", + &catapult_dct2000_dissect_lte_rrc); + + /* Determines whether out-of-band messages should dissected */ + prefs_register_bool_preference(catapult_dct2000_module, "decode_mac_lte_oob_messages", + "Look for out-of-band LTE MAC events messages in comments", + "When set, look for formatted messages indicating " + "specific events. This may be quite slow, so should " + "be disabled if LTE MAC is not being analysed", + &catapult_dct2000_dissect_mac_lte_oob_messages); + + /* Whether old protocol names conversions should be checked */ + prefs_register_bool_preference(catapult_dct2000_module, "convert_old_protocol_names", + "Convert old protocol names to wireshark dissector names", + "When set, look for some older protocol names so that" + "they may be matched with wireshark dissectors.", + &catapult_dct2000_dissect_old_protocol_names); + + /* Determines if the protocol field in the DCT2000 shall be used to lookup for disector*/ + prefs_register_bool_preference(catapult_dct2000_module, "use_protocol_name_as_dissector_name", + "Look for a dissector using the protocol name in the " + "DCT2000 record", + "When set, if there is a Wireshark dissector matching " + "the protocol name, it will parse the PDU using " + "that dissector. This may be slow, so should be " + "disabled unless you are using this feature.", + &catapult_dct2000_use_protocol_name_as_dissector_name); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ |