/* packet-catapult-dct2000.c * Routines for Catapult DCT2000 packet stub header disassembly * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "packet-umts_fp.h" #include "packet-rlc.h" #include "packet-mac-lte.h" #include "packet-rlc-lte.h" #include "packet-pdcp-lte.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_lte_ueid = -1; static int hf_catapult_dct2000_lte_srbid = -1; static int hf_catapult_dct2000_lte_drbid = -1; static int hf_catapult_dct2000_lte_cellid = -1; static int hf_catapult_dct2000_lte_bcch_transport = -1; static int hf_catapult_dct2000_lte_rlc_op = -1; static int hf_catapult_dct2000_lte_rlc_channel_type = -1; static int hf_catapult_dct2000_lte_rlc_mui = -1; static int hf_catapult_dct2000_lte_rlc_cnf = -1; static int hf_catapult_dct2000_lte_rlc_discard_req = -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; /* UMTS RLC fields */ static int hf_catapult_dct2000_ueid = -1; 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; /* 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_lte_s1ap = TRUE; static gboolean catapult_dct2000_dissect_mac_lte_oob_messages = TRUE; /* 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 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 ccpri_status_vals[] = { { 0, "OK"}, { 1, "ERROR"}, { 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 tx_priority_vals[] = { { 0, "Normal"}, { 1, "High"}, { 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 MAX_OUTHDR_VALUES 32 static guint outhdr_values[MAX_OUTHDR_VALUES]; static guint outhdr_values_found = 0; extern int proto_fp; extern int proto_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 look_for_dissector(const char *protocol_name); static void parse_outhdr_string(const guchar *outhdr_string, gint outhdr_length); static void attach_fp_info(packet_info *pinfo, gboolean received, const char *protocol_name, int variant); static void attach_rlc_info(packet_info *pinfo, guint32 urnti, guint8 rbid, gboolean is_sent); static void attach_mac_lte_info(packet_info *pinfo); static void attach_rlc_lte_info(packet_info *pinfo); static void attach_pdcp_lte_info(packet_info *pinfo); /* 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 */ *dest_port_offset = offset; 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) { 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); proto_tree_add_item(tree, hf_catapult_dct2000_ueid, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; 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); /* 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); } } } /* Dissect an RRC LTE frame by first parsing the header entries then passing the data to the RRC dissector, according to direction and channel type. TODO: factor out common code between this function and dissect_pdcp_lte() */ static void dissect_rrc_lte(tvbuff_t *tvb, gint offset, packet_info *pinfo, proto_tree *tree) { guint8 tag; dissector_handle_t protocol_handle = 0; gboolean isUplink = FALSE; LogicalChannelType logicalChannelType; guint16 cell_id; guint8 bcch_transport = 0; tvbuff_t *rrc_tvb; /* Top-level opcode */ tag = tvb_get_guint8(tvb, offset++); switch (tag) { case 0x00: /* Data_Req_UE */ case 0x04: /* Data_Ind_eNodeB */ isUplink = TRUE; break; case 0x02: /* Data_Req_eNodeB */ case 0x03: /* Data_Ind_UE */ 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 */ /* Length will fit in one byte here */ offset++; logicalChannelType = Channel_DCCH; /* UEId */ proto_tree_add_item(tree, hf_catapult_dct2000_lte_ueid, tvb, offset, 2, ENC_BIG_ENDIAN); 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_lte_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_lte_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_lte_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_lte_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_lte_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_lte_ueid, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; break; default: break; } break; default: /* Unexpected tag */ return; } /* Data tag should follow */ 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: protocol_handle = find_dissector("lte_rrc.ul_dcch"); break; case Channel_CCCH: protocol_handle = find_dissector("lte_rrc.ul_ccch"); break; default: /* Unknown Uplink channel type */ break; } } else { /* Downlink channel types */ switch (logicalChannelType) { case Channel_DCCH: protocol_handle = find_dissector("lte_rrc.dl_dcch"); break; case Channel_CCCH: protocol_handle = find_dissector("lte_rrc.dl_ccch"); break; case Channel_PCCH: protocol_handle = find_dissector("lte_rrc.pcch"); break; case Channel_BCCH: if (bcch_transport == 1) { protocol_handle = find_dissector("lte_rrc.bcch_bch"); } else { protocol_handle = find_dissector("lte_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)) { 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_lte_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 = NULL; 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_lte_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_lte_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_lte_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_lte_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_lte_cellid, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; /* Logical channel type */ proto_tree_add_item(tree, hf_catapult_dct2000_lte_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_lte_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_lte_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_lte_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_lte_rlc_cnf, tvb, offset, 1, ENC_NA); offset++; } } else if (tag == 0x45) { /* Discard Req */ offset++; proto_tree_add_item(tree, hf_catapult_dct2000_lte_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) { /* Use known aliases and protocol name prefixes */ if (strcmp(protocol_name, "tbcp") == 0) { return find_dissector("rtcp"); } else if (strncmp(protocol_name, "diameter", strlen("diameter")) == 0) { return find_dissector("diameter"); } 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 ((strcmp(protocol_name, "fp") == 0) || (strncmp(protocol_name, "fp_r", 4) == 0) || (strcmp(protocol_name, "fpiur_r5") == 0)) { return find_dissector("fp"); } else if ((strcmp(protocol_name, "iuup_rtp_r5") == 0) || (strcmp(protocol_name, "iuup_rtp_r6") == 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 (strncmp(protocol_name, "gtp", strlen("gtp")) == 0) { return find_dissector("gtp"); } else if (strcmp(protocol_name, "dhcpv4") == 0) { return find_dissector("bootp"); } 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 /* Only match with s1ap if preference turned on */ if (catapult_dct2000_dissect_lte_s1ap && strncmp(protocol_name, "s1ap", strlen("s1ap")) == 0) { return find_dissector("s1ap"); } else /* Always try lookup for now */ if ((strncmp(protocol_name, "x2ap_r8_lte", strlen("x2ap_r8_lte")) == 0) || (strncmp(protocol_name, "x2ap_r9_lte", strlen("x2ap_r9_lte")) == 0)) { return find_dissector("x2ap"); } else if ((strcmp(protocol_name, "gtpv2_r8_lte") == 0) || (strcmp(protocol_name, "gtpv2_r9_lte") == 0)) { return find_dissector("gtpv2"); } /* Try for an exact match */ else { return find_dissector(protocol_name); } } /* Populate outhdr_values array with numbers found in outhdr_string */ static void parse_outhdr_string(const guchar *outhdr_string, gint outhdr_string_len) { int n = 0; /* 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++; } } /* 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) { int i=0; int chan; int 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++) { p_fp_info->chan_tf_size[chan] = outhdr_values[tf_start+chan]; } /* 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++) { p_fp_info->chan_num_tbs[chan] = outhdr_values[num_chans_start+chan]; } } /* 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++) { p_fp_info->edch_ddi[n] = outhdr_values[i++]; } /* Corresponding MAC-d sizes */ for (n=0; n < p_fp_info->no_ddi_entries; n++) { p_fp_info->edch_macd_pdu_size[n] = outhdr_values[i++]; } if (strcmp(protocol_name, "fp_r8") == 0) { p_fp_info->edch_type = outhdr_values[i]; } 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) { /* 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_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_new(wmem_file_scope(), struct fp_info); /* Fill in struct fields for first (only) PDU in this frame */ /* Urnti. Just use UEId */ p_rlc_info->urnti[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_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) { 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; p_mac_lte_info->rntiType = outhdr_values[i++]; p_mac_lte_info->direction = outhdr_values[i++]; /* 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->subframeNumber = outhdr_values[i++]; p_mac_lte_info->isPredefinedData = outhdr_values[i++]; p_mac_lte_info->rnti = outhdr_values[i++]; p_mac_lte_info->ueid = outhdr_values[i++]; p_mac_lte_info->length = outhdr_values[i++]; if (outhdr_values_found > 8) { p_mac_lte_info->reTxCount = outhdr_values[i++]; } 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++]; } else { i++; } } 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++]; p_mac_lte_info->detailed_phy_info.dl_info.dci_format = outhdr_values[i++]; p_mac_lte_info->detailed_phy_info.dl_info.resource_allocation_type = outhdr_values[i++]; p_mac_lte_info->detailed_phy_info.dl_info.aggregation_level = outhdr_values[i++]; p_mac_lte_info->detailed_phy_info.dl_info.mcs_index = outhdr_values[i++]; p_mac_lte_info->detailed_phy_info.dl_info.redundancy_version_index = outhdr_values[i++]; if (outhdr_values[i++]) { p_mac_lte_info->dl_retx = dl_retx_yes; } else { p_mac_lte_info->dl_retx = dl_retx_no; } p_mac_lte_info->detailed_phy_info.dl_info.resource_block_length = outhdr_values[i++]; p_mac_lte_info->crcStatusValid = TRUE; 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++]; p_mac_lte_info->detailed_phy_info.dl_info.ndi = outhdr_values[i++]; } if (outhdr_values_found > 20) { p_mac_lte_info->detailed_phy_info.dl_info.transport_block = outhdr_values[i++]; } } else { /* Uplink */ p_mac_lte_info->detailed_phy_info.ul_info.present = outhdr_values[i++]; p_mac_lte_info->detailed_phy_info.ul_info.modulation_type = outhdr_values[i++]; p_mac_lte_info->detailed_phy_info.ul_info.tbs_index = outhdr_values[i++]; p_mac_lte_info->detailed_phy_info.ul_info.resource_block_length = outhdr_values[i++]; p_mac_lte_info->detailed_phy_info.ul_info.resource_block_start = outhdr_values[i++]; /* Skip retx flag */ i++; if (outhdr_values_found == 16) { p_mac_lte_info->subframeNumberOfGrantPresent = TRUE; p_mac_lte_info->subframeNumberOfGrant = outhdr_values[i++]; } if (outhdr_values_found > 16) { p_mac_lte_info->detailed_phy_info.ul_info.harq_id = outhdr_values[i++]; p_mac_lte_info->detailed_phy_info.ul_info.ndi = outhdr_values[i++]; p_mac_lte_info->subframeNumberOfGrantPresent = TRUE; p_mac_lte_info->subframeNumberOfGrant = outhdr_values[i++]; } } } /* 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; } /* 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) { 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) { 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(wmem_packet_scope(), 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(wmem_packet_scope(), hex_string_length); idx = g_snprintf(hex_string, hex_string_length, "$"); /* Write hex out to new string */ for (n=0; n < tty_string_length; n++) { idx += g_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; /* Look for strings matching expected formats */ if (sscanf(string, ">> RACH Preamble Request[UE = %u] [RAPID = %u] [Attempt = %u]", &ueids[0], &rapid, &rach_attempt_number) == 3) { oob_event = ltemac_send_preamble; } else if (sscanf(string, ">> Schedule Requests (%u) [UE=%u][RNTI=%u]", &number_of_ues, &ueids[0], &rntis[0]) == 3) { 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; } } } } else /* Support both old and new formats of SR failure */ if ((sscanf(string, ">> INFO (inst %u) MAC: [UE = %u] SR failed (CRNTI=%u)", &temp, &ueids[0], &rntis[0]) == 3) || (sscanf(string, ">> INFO MAC: SR failed for UE %u (CRNTI=%u", &ueids[0], &rntis[0]) == 2)) { oob_event = ltemac_sr_failure; } else { /* No events found */ return; } /* We have an event */ /* Only need to set info once per session. */ p_mac_lte_info = get_mac_lte_proto_data(pinfo); if (p_mac_lte_info == NULL) { /* 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->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); } /*****************************************/ /* 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; /* 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_const_stringz(tvb, offset, &context_length); if (dct2000_tree) { proto_tree_add_item(dct2000_tree, hf_catapult_dct2000_context, tvb, offset, context_length, ENC_ASCII|ENC_NA); } 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_const_stringz(tvb, offset, ×tamp_length); if (dct2000_tree) { /* TODO: this is *very* slow, but float version adds trailing digits when displayed as a custom column... */ proto_tree_add_double(dct2000_tree, hf_catapult_dct2000_timestamp, tvb, offset, timestamp_length, g_ascii_strtod(timestamp_string, NULL)); } offset += timestamp_length; /* DCT2000 protocol name */ protocol_name = tvb_get_const_stringz(tvb, offset, &protocol_length); if (dct2000_tree) { proto_tree_add_item(dct2000_tree, hf_catapult_dct2000_protocol, tvb, offset, protocol_length, ENC_ASCII|ENC_NA); } 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_const_stringz(tvb, offset, &variant_length); if (!is_comment && !is_sprint) { proto_tree_add_item(dct2000_tree, hf_catapult_dct2000_variant, tvb, offset, variant_length, ENC_ASCII|ENC_NA); } offset += variant_length; /* Outhdr (shown as string) */ outhdr_string = tvb_get_const_stringz(tvb, offset, &outhdr_length); if (!is_comment && !is_sprint && (outhdr_length > 1)) { proto_tree_add_item(dct2000_tree, hf_catapult_dct2000_outhdr, tvb, offset, outhdr_length, ENC_ASCII|ENC_NA); } 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); /* 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)) { parse_outhdr_string(outhdr_string, outhdr_length); if (ws_strtou32(variant_string, NULL, &variant)) attach_fp_info(pinfo, direction, protocol_name, variant); 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)) { parse_outhdr_string(outhdr_string, outhdr_length); /* 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)) { parse_outhdr_string(outhdr_string, outhdr_length); attach_mac_lte_info(pinfo); } /* 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)) { parse_outhdr_string(outhdr_string, outhdr_length); attach_rlc_lte_info(pinfo); } /* 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)) { parse_outhdr_string(outhdr_string, outhdr_length); attach_pdcp_lte_info(pinfo); } 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)) { 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_lte_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_lte_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_lte_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 if looks right */ if (nas_body_found) { offset += 2; /* L3 tag + len */ protocol_handle = find_dissector("nas-eps"); } } /* 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: protocol_handle = find_dissector("lapd"); pinfo->p2p_dir = pinfo->pseudo_header->isdn.uton; 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); 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, wmem_packet_scope(), &string); col_append_fstr(pinfo->cinfo, COL_INFO, "%s", 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); } 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, wmem_packet_scope(), &string); col_append_fstr(pinfo->cinfo, COL_INFO, "%s", string); return tvb_captured_length(tvb); } 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))) { /* Dissect proprietary header, then pass remainder to RRC (depending upon direction and channel type) */ dissect_rrc_lte(tvb, offset, pinfo, tree); 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; /* Will give up if couldn't match protocol anyway... */ heur_protocol_handle = look_for_dissector(protocol_name); if ((heur_protocol_handle != 0) && 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. */ 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, (source_addr_length == 4) ? ENC_BIG_ENDIAN : 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, (source_addr_length == 4) ? ENC_BIG_ENDIAN : 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, (dest_addr_length == 4) ? ENC_BIG_ENDIAN : 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, (dest_addr_length == 4) ? ENC_BIG_ENDIAN : 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)) : "" ) : "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, (dest_addr_length == 4) ? ENC_BIG_ENDIAN : 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, (dest_addr_length == 4) ? ENC_BIG_ENDIAN : 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); } } /* Last chance: is there a (private) registered protocol of the form "dct2000.protocol" ? */ if (protocol_handle == 0) { /* TODO: only look inside preference? */ char dotted_protocol_name[64+128]; g_snprintf(dotted_protocol_name, 64+128, "dct2000.%s", protocol_name); protocol_handle = find_dissector(dotted_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"); } /****************************************/ /* 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_lte_ueid, { "UE Id", "dct2000.lte.ueid", FT_UINT16, BASE_DEC, NULL, 0x0, "User Equipment Identifier", HFILL } }, { &hf_catapult_dct2000_lte_srbid, { "srbid", "dct2000.lte.srbid", FT_UINT8, BASE_DEC, NULL, 0x0, "Signalling Radio Bearer Identifier", HFILL } }, { &hf_catapult_dct2000_lte_drbid, { "drbid", "dct2000.lte.drbid", FT_UINT8, BASE_DEC, NULL, 0x0, "Data Radio Bearer Identifier", HFILL } }, { &hf_catapult_dct2000_lte_cellid, { "Cell-Id", "dct2000.lte.cellid", FT_UINT16, BASE_DEC, NULL, 0x0, "Cell Identifier", HFILL } }, { &hf_catapult_dct2000_lte_bcch_transport, { "BCCH Transport", "dct2000.lte.bcch-transport", FT_UINT16, BASE_DEC, VALS(bcch_transport_vals), 0x0, "BCCH Transport Channel", HFILL } }, { &hf_catapult_dct2000_lte_rlc_op, { "RLC Op", "dct2000.lte.rlc-op", FT_UINT8, BASE_DEC, VALS(rlc_op_vals), 0x0, "RLC top-level op", HFILL } }, { &hf_catapult_dct2000_lte_rlc_channel_type, { "RLC Logical Channel Type", "dct2000.lte.rlc-logchan-type", FT_UINT8, BASE_DEC, VALS(rlc_logical_channel_vals), 0x0, NULL, HFILL } }, { &hf_catapult_dct2000_lte_rlc_mui, { "MUI", "dct2000.lte.rlc-mui", FT_UINT16, BASE_DEC, NULL, 0x0, "RLC MUI", HFILL } }, { &hf_catapult_dct2000_lte_rlc_cnf, { "CNF", "dct2000.lte.rlc-cnf", FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0x0, "RLC CNF", HFILL } }, { &hf_catapult_dct2000_lte_rlc_discard_req, { "Discard Req", "dct2000.lte.rlc-discard-req", FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0x0, "RLC Discard Req", 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_UINT8, BASE_DEC, VALS(ccpri_status_vals), 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_ueid, { "UE Id", "dct2000.ueid", FT_UINT32, BASE_DEC, NULL, 0x0, "User Equipment Identifier", 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_UINT8, BASE_DEC, VALS(tx_priority_vals), 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 } }, }; static gint *ett[] = { &ett_catapult_dct2000, &ett_catapult_dct2000_ipprim, &ett_catapult_dct2000_sctpprim, &ett_catapult_dct2000_tty }; 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"); /* 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 LTE S1AP messages should be dissected */ prefs_register_bool_preference(catapult_dct2000_module, "decode_lte_s1ap", "Attempt to decode LTE S1AP frames", "When set, attempt to decode LTE S1AP frames. " "Note that this won't affect other protocols " "that also call the LTE S1AP dissector", &catapult_dct2000_dissect_lte_s1ap); /* 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); } /* * Editor modelines - http://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: */