wireshark/epan/dissectors/asn1/tetra/packet-tetra-template.c

634 lines
17 KiB
C

/* packet-tetra.c
* Routines for TETRA packet dissection
*
* Copyright (c) 2007 - 2011 Professional Mobile Communication Research Group,
* Beijing Institute of Technology, China
* Copyright (c) 2011 Holger Hans Peter Freyther
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* REF: ETSI EN 300 392-2 V3.2.1
*/
#include "config.h"
#include <epan/packet.h>
#include <epan/expert.h>
#include <epan/prefs.h>
#include <epan/oids.h>
#include <epan/conversation.h>
#include <epan/asn1.h>
#include "packet-per.h"
#include "packet-tetra.h"
#define PROTO_TAG_tetra "TETRA"
void proto_register_tetra(void);
void proto_reg_handoff_tetra(void);
/* Wireshark ID of the tetra protocol */
static int proto_tetra = -1;
static dissector_handle_t tetra_handle;
#define TETRA_UDP_PORT 7074 /* Not IANA assigned */
/* Whether the capture data include carrier numbers */
static gboolean include_carrier_number = TRUE;
/* The following hf_* variables are used to hold the Wireshark IDs of
* our header fields; they are filled out when we call
* proto_register_field_array() in proto_register_tetra()
*/
/** Kts attempt at defining the protocol */
static gint hf_tetra = -1;
static gint hf_tetra_header = -1;
static gint hf_tetra_channels = -1;
static gint hf_tetra_channel1 = -1;
static gint hf_tetra_channel2 = -1;
static gint hf_tetra_channel3 = -1;
static gint hf_tetra_txreg = -1;
static gint hf_tetra_timer = -1;
static gint hf_tetra_pdu = -1;
static gint hf_tetra_rvstr = -1;
static gint hf_tetra_carriernumber = -1;
static gint hf_tetra_rxchannel1 = -1;
static gint hf_tetra_rxchannel2 = -1;
static gint hf_tetra_rxchannel3 = -1;
static gint hf_tetra_crc = -1;
static gint hf_tetra_len0 = -1;
#include "packet-tetra-hf.c"
/* Initialize the subtree pointers */
/* These are the ids of the subtrees that we may be creating */
static gint ett_tetra = -1;
static gint ett_tetra_header = -1;
static gint ett_tetra_length = -1;
static gint ett_tetra_txreg = -1;
static gint ett_tetra_text = -1;
#include "packet-tetra-ett.c"
static expert_field ei_tetra_channels_incorrect = EI_INIT;
#include "packet-tetra-fn.c"
static const value_string channeltypenames[] = {
{ 0, "Reserved" },
{ 1, "AACH" },
{ 2, "SCH/F" },
{ 3, "SCH/HD" },
{ 4, "Unknown" },
{ 5, "BSCH" },
{ 6, "BNCH" },
{ 7, "TCH/F" },
{ 8, "TCH/H" },
{ 9, "TCH4.8"},
{ 10, "TCH7.2"},
{ 11, "STCH"},
{ 0, NULL }
};
static const value_string recvchanneltypenames[] = {
{ 0, "Reserved" },
{ 1, "AACH" },
{ 2, "SCH/F" },
{ 3, "SCH/HD" },
{ 4, "Unknown" },
{ 5, "BSCH" },
{ 6, "BNCH" },
{ 7, "TCH/F" },
{ 8, "TCH/H" },
{ 9, "TCH4.8"},
{ 10, "TCH7.2"},
{ 11, "STCH"},
{ 15, "SCH/HU"},
{ 0, NULL }
};
/* Get the length of received pdu */
static gint get_rx_pdu_length(guint32 channel_type)
{
gint len = 0;
switch(channel_type) {
case TETRA_CHAN_AACH:
len = 14;
break;
case TETRA_CHAN_SCH_F:
len = 268;
break;
case TETRA_CHAN_SCH_D:
len = 124; ;
break;
case TETRA_CHAN_BSCH:
len = 60;
break;
case TETRA_CHAN_BNCH:
len = 124;
break;
case TETRA_CHAN_TCH_F:
len = 274;
break;
case TETRA_CHAN_TCH_H:
len = 137;
break;
case TETRA_CHAN_TCH_2_4:
len = 144;
break;
case TETRA_CHAN_TCH_4_8:
len = 288;
break;
case TETRA_CHAN_STCH:
len = 124;
break;
case TETRA_CHAN_SCH_HU:
len = 92;
break;
default:
len = 0;
break;
}
return len;
}
/* Get the length of transmitted pdu */
static gint get_tx_pdu_length(guint32 channel_type)
{
gint len = 0;
switch(channel_type) {
case TETRA_CHAN_AACH:
len = 14;
break;
case TETRA_CHAN_SCH_F:
len = 268;
break;
case TETRA_CHAN_SCH_D:
len = 124;
break;
case TETRA_CHAN_BSCH:
len = 60;
break;
case TETRA_CHAN_BNCH:
len = 124;
break;
case TETRA_CHAN_TCH_F:
len = 274;
break;
case TETRA_CHAN_TCH_H:
len = 137;
break;
case TETRA_CHAN_TCH_2_4:
len = 144;
break;
case TETRA_CHAN_TCH_4_8:
len = 288;
break;
case TETRA_CHAN_STCH:
len = 124;
break;
}
return len;
}
void tetra_dissect_pdu(int channel_type, int dir, tvbuff_t *pdu, proto_tree *tree, packet_info *pinfo)
{
proto_item *tetra_sub_item;
proto_tree *tetra_sub_tree;
guint8 p;
tetra_sub_item = proto_tree_add_item(tree, hf_tetra_pdu,
pdu, 0, tvb_captured_length(pdu), ENC_NA);
tetra_sub_tree = proto_item_add_subtree(tetra_sub_item, ett_tetra);
switch(channel_type) {
case TETRA_CHAN_AACH:
dissect_AACH_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
case TETRA_CHAN_SCH_F:
p = tvb_get_guint8(pdu, 0);
switch(p >> 6) {
case 0:
if (dir == TETRA_DOWNLINK)
dissect_MAC_RESOURCE_PDU(pdu, pinfo, tetra_sub_tree, NULL);
else
dissect_MAC_DATA_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
case 1: /* MAC-FRAG or MAC-END */
if((p >> 5) == 3) {
if (dir == TETRA_DOWNLINK)
dissect_MAC_END_DOWNLINK_PDU(pdu, pinfo, tetra_sub_tree, NULL);
else
dissect_MAC_END_UPLINK_PDU(pdu, pinfo, tetra_sub_tree, NULL);
} else
dissect_MAC_FRAG_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
case 2:
dissect_MAC_ACCESS_DEFINE_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
}
break;
case TETRA_CHAN_SCH_D:
p = tvb_get_guint8(pdu, 0);
switch(p >> 6) {
case 0:
dissect_MAC_RESOURCE_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
case 1: /* MAC-FRAG or MAC-END */
if((p >> 5) == 3)
dissect_MAC_END_DOWN111_PDU(pdu, pinfo, tetra_sub_tree, NULL);
else
dissect_MAC_FRAG120_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
case 2:
dissect_MAC_ACCESS_DEFINE_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
}
break;
case TETRA_CHAN_SCH_HU:
p = tvb_get_guint8(pdu, 0);
switch(p >> 7) {
case 0: /* MAC-ACCESS */
dissect_MAC_ACCESS_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
case 1: /* MAC-END-HU */
dissect_MAC_END_HU_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
}
break;
case TETRA_CHAN_BSCH:
col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "BSCH");
dissect_BSCH_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
case TETRA_CHAN_BNCH:
col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "BNCH");
dissect_BNCH_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
case TETRA_CHAN_STCH:
p = tvb_get_guint8(pdu, 0);
switch(p >> 6) {
case 0:
dissect_MAC_RESOURCE_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
case 1: /* MAC-FRAG or MAC-END */
if((p >> 5) == 3) {
if (dir == TETRA_DOWNLINK)
dissect_MAC_END_DOWN111_PDU(pdu, pinfo, tetra_sub_tree, NULL);
else
dissect_MAC_END_UP114_PDU(pdu, pinfo, tetra_sub_tree, NULL);
} else
dissect_MAC_FRAG120_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
case 2:
dissect_MAC_ACCESS_DEFINE_PDU(pdu, pinfo, tetra_sub_tree, NULL);
break;
}
break;
case TETRA_CHAN_TCH_F:
col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Voice");
break;
}
}
static void dissect_tetra_UNITDATA_IND(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tetra_tree, int offset)
{
guint32 rxreg = 0;
guint32 channels = 0, i;
guint32 channel_type;
gint pdu_offset = 0;
proto_item *tetra_sub_item;
proto_tree *tetra_header_tree = NULL;
tvbuff_t *payload_tvb;
/* Length */
rxreg = tvb_get_letohl(tvb, offset);
proto_tree_add_uint(tetra_tree, hf_tetra_len0, tvb, offset, 4, rxreg);
/* RvSteR */
offset += 4;
rxreg = tvb_get_letohl(tvb, offset);
proto_tree_add_uint(tetra_tree, hf_tetra_rvstr, tvb, offset, 4, rxreg);
/* Logical channels */
channels = rxreg & 0x3;
tetra_sub_item = proto_tree_add_uint( tetra_tree, hf_tetra_channels, tvb, offset, 4, channels );
tetra_header_tree = proto_item_add_subtree(tetra_sub_item, ett_tetra);
if (channels > 3) {
expert_add_info(pinfo, tetra_sub_item, &ei_tetra_channels_incorrect);
channels = 3;
}
pdu_offset = offset + 4;
for(i = 0; i < channels; i++) {
gint byte_len, bits_len, remaining_bits;
gint hf_channel[3];
hf_channel[0] = hf_tetra_rxchannel1;
hf_channel[1] = hf_tetra_rxchannel2;
hf_channel[2] = hf_tetra_rxchannel3;
/* Channel type */
channel_type = (rxreg >> ((i + 1) * 4) ) & 0xf;
proto_tree_add_uint( tetra_header_tree, hf_channel[i], tvb, offset, 4, channel_type);
/* CRC */
proto_tree_add_boolean( tetra_header_tree, hf_tetra_crc, tvb, offset, 4, !(rxreg >> (i + 2) & 0x01));
/* PDU */
bits_len = get_rx_pdu_length(channel_type);
byte_len = bits_len >> 3;
remaining_bits = bits_len % 8;
if ((remaining_bits)!=0)
byte_len++;
payload_tvb = tvb_new_subset_length(tvb, pdu_offset, byte_len);
tetra_dissect_pdu(channel_type, TETRA_UPLINK, payload_tvb, tetra_header_tree, pinfo);
if ((remaining_bits)!=0)
byte_len--;
pdu_offset += byte_len;
}
}
static void dissect_tetra_UNITDATA_REQ(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tetra_tree, int offset)
{
guint32 txreg = 0;
guint32 channels = 0, i;
guint32 channel_type;
gint pdu_offset = 0;
proto_item *tetra_sub_item = NULL;
proto_tree *tetra_header_tree = NULL;
tvbuff_t *payload_tvb;
/* TxR */
txreg = tvb_get_letohl(tvb, offset);
proto_tree_add_uint(tetra_tree, hf_tetra_txreg, tvb, offset, 4, txreg);
/* Logical channels */
channels = (txreg & 0x3) + 1;
tetra_sub_item = proto_tree_add_uint( tetra_tree, hf_tetra_channels, tvb, offset, 4, channels );
tetra_header_tree = proto_item_add_subtree(tetra_sub_item, ett_tetra);
txreg >>= 2;
/* Skip 0000B */
if(channels == 2)
txreg >>= 4;
if (channels > 3) {
expert_add_info(pinfo, tetra_sub_item, &ei_tetra_channels_incorrect);
channels = 3;
}
pdu_offset = offset + 4;
for(i = 0; i < channels; i++) {
gint byte_len, bits_len, remaining_bits;
gint hf_channel[3];
hf_channel[0] = hf_tetra_channel1;
hf_channel[1] = hf_tetra_channel2;
hf_channel[2] = hf_tetra_channel3;
channel_type = txreg & 0xf;
proto_tree_add_uint( tetra_header_tree, hf_channel[i], tvb, offset, 4, channel_type);
txreg >>= 4;
/* PDU */
bits_len = get_tx_pdu_length(channel_type);
byte_len = bits_len >> 3;
remaining_bits = bits_len % 8;
if ((remaining_bits)!=0)
byte_len++;
payload_tvb = tvb_new_subset_length(tvb, pdu_offset, byte_len);
tetra_dissect_pdu(channel_type, TETRA_DOWNLINK, payload_tvb, tetra_header_tree, pinfo);
pdu_offset += byte_len;
}
}
static int
dissect_tetra(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
proto_item *tetra_item = NULL;
proto_item *tetra_sub_item = NULL;
proto_tree *tetra_tree = NULL;
proto_tree *tetra_header_tree = NULL;
guint16 type = 0;
guint8 carriernumber = -1;
col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTO_TAG_tetra);
/* Clear out stuff in the info column */
col_clear(pinfo->cinfo,COL_INFO);
/*
* This is not a good way of dissecting packets. The tvb length should
* be sanity checked so we aren't going past the actual size of the buffer.
*/
type = tvb_get_guint8(tvb, 0);
if(include_carrier_number) {
carriernumber = tvb_get_guint8(tvb, 1);
}
switch(type) {
case 1:
if(include_carrier_number)
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-REQ, Carrier: %d",
carriernumber);
else
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-REQ");
break;
case 2:
if(include_carrier_number)
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-IND, Carrier: %d",
carriernumber);
else
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-IND");
break;
case 3:
if(include_carrier_number)
col_add_fstr(pinfo->cinfo, COL_INFO, "MAC-Timer, Carrier: %d",
carriernumber);
else
col_add_fstr(pinfo->cinfo, COL_INFO, "MAC-Timer");
break;
case 127:
if(include_carrier_number)
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-IND Done, Carrier: %d",
carriernumber);
else
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-IND Done");
break;
case 128:
if(include_carrier_number)
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-REQ Done, Carrier: %d",
carriernumber);
else
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-REQ Done");
break;
default:
col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown command: %d", type);
break;
}
/* if (tree) */ { /* we are being asked for details */
guint32 offset = 0;
guint32 txtimer = 0;
guint32 tslot = 0;
tetra_item = proto_tree_add_item(tree, proto_tetra, tvb, 0, -1, ENC_NA);
tetra_tree = proto_item_add_subtree(tetra_item, ett_tetra);
offset ++;
/* Carrier number */
if(include_carrier_number) {
proto_tree_add_uint(tetra_tree, hf_tetra_carriernumber, tvb, offset, 1, carriernumber);
offset ++;
}
/* Registers */
tetra_sub_item = proto_tree_add_item( tetra_tree, hf_tetra_header, tvb, offset, -1, ENC_NA );
tetra_header_tree = proto_item_add_subtree(tetra_sub_item, ett_tetra);
/* Timer */
txtimer = tvb_get_letohl(tvb, offset);
tetra_sub_item = proto_tree_add_item(tetra_header_tree, hf_tetra_timer, tvb, offset, 4, ENC_LITTLE_ENDIAN);
tslot = ((txtimer & 0x7800) >> 11);
if(tslot==4)
tslot = 3;
if(tslot==8)
tslot = 4;
proto_item_append_text(tetra_sub_item, " (Multiple frame: %d, Frame: %d, Slot: %d)",
txtimer & 0x3F, (txtimer & 0x7c0) >> 6,
tslot);
offset += 4;
switch(type) {
case 1: /* tetra-UNITDATA-REQ */
case 128: /* tetra-UNITDATA-REQ Done */
dissect_tetra_UNITDATA_REQ(tvb, pinfo, tetra_header_tree, offset);
break;
case 2: /* tetra-UNITDATA-IND */
case 127: /* tetra-UNITDATA-IND Done */
dissect_tetra_UNITDATA_IND(tvb, pinfo, tetra_header_tree, offset);
break;
case 3: /* MAC-Timer */
break;
default:
break;
}
}
return tvb_captured_length(tvb);
}
void proto_reg_handoff_tetra(void)
{
tetra_handle = create_dissector_handle(dissect_tetra, proto_tetra);
dissector_add_uint_with_preference("udp.port", TETRA_UDP_PORT, tetra_handle);
}
void proto_register_tetra (void)
{
module_t *tetra_module;
expert_module_t* expert_tetra;
/*
* A header field is something you can search/filter on.
*
* We create a structure to register our fields. It consists of an
* array of hf_register_info structures, each of which are of the format
* {&(field id), {name, abbrev, type, display, strings, bitmask, blurb, HFILL}}.
*/
static hf_register_info hf[] = {
{ &hf_tetra,
{ "Data", "tetra.data", FT_NONE, BASE_NONE, NULL, 0x0,
"tetra PDU", HFILL }},
{ &hf_tetra_header,
{ "Registers", "tetra.header", FT_NONE, BASE_NONE, NULL, 0x0,
"TETRA Registers", HFILL }},
{ &hf_tetra_channels,
{ "Logical Channels", "tetra.channels", FT_UINT8, BASE_DEC, NULL, 0x0,
"The amount of logical channels", HFILL }},
{ &hf_tetra_channel1,
{ "Channel 1", "tetra.txchannel1", FT_UINT8, BASE_DEC, VALS(channeltypenames), 0x0,
"Logical channels type", HFILL }},
{ &hf_tetra_channel2,
{ "Channel 2", "tetra.txchannel2", FT_UINT8, BASE_DEC, VALS(channeltypenames), 0x0,
"Logical channels type", HFILL }},
{ &hf_tetra_channel3,
{ "Channel 3", "tetra.txchannel3", FT_UINT8, BASE_DEC, VALS(channeltypenames), 0x0,
"Logical channels type", HFILL }},
{ &hf_tetra_txreg,
{ "TxR", "tetra.txreg", FT_UINT16, BASE_HEX, NULL, 0x0,
"TX Register", HFILL }},
{ &hf_tetra_rvstr,
{ "RvSteR", "tetra.rvster", FT_UINT16, BASE_HEX, NULL, 0x0,
"Receive Status Register", HFILL }},
{ &hf_tetra_carriernumber,
{ "Carrier Number", "tetra.carrier", FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_tetra_rxchannel1,
{ "Channel 1", "tetra.rxchannel1", FT_UINT8, BASE_DEC, VALS(recvchanneltypenames), 0x0,
"Logical channels type", HFILL }},
{ &hf_tetra_rxchannel2,
{ "Channel 2", "tetra.rxchannel2", FT_UINT8, BASE_DEC, VALS(recvchanneltypenames), 0x0,
"Logical channels type", HFILL }},
{ &hf_tetra_rxchannel3,
{ "Channel 3", "tetra.rxchannel3", FT_UINT8, BASE_DEC, VALS(recvchanneltypenames), 0x0,
"Logical channels type", HFILL }},
{ &hf_tetra_timer,
{ "Timer", "tetra.timer", FT_UINT32, BASE_HEX, NULL, 0x0,
"Timer Register", HFILL }},
{ &hf_tetra_crc,
{ "CRC", "tetra.crc", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"CRC result", HFILL }},
{ &hf_tetra_len0,
{ "Length", "tetra.len0", FT_UINT16, BASE_DEC, NULL, 0x0,
"Length of the PDU", HFILL }},
{ &hf_tetra_pdu,
{ "PDU", "tetra.pdu", FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }} ,
#include "packet-tetra-hfarr.c"
};
/* List of subtrees */
static gint *ett[] = {
&ett_tetra,
&ett_tetra_header,
&ett_tetra_length,
&ett_tetra_txreg,
&ett_tetra_text,
#include "packet-tetra-ettarr.c"
};
static ei_register_info ei[] = {
{ &ei_tetra_channels_incorrect, { "tetra.channels.incorrect", PI_MALFORMED, PI_WARN, "Channel count incorrect, must be <= 3", EXPFILL }},
};
proto_tetra = proto_register_protocol("TETRA Protocol", "TETRA", "tetra");
proto_register_field_array (proto_tetra, hf, array_length (hf));
proto_register_subtree_array (ett, array_length (ett));
register_dissector("tetra", dissect_tetra, proto_tetra);
expert_tetra = expert_register_protocol(proto_tetra);
expert_register_field_array(expert_tetra, ei, array_length(ei));
tetra_module = prefs_register_protocol(proto_tetra, NULL);
prefs_register_bool_preference(tetra_module, "include_carrier_number",
"The data include carrier numbers",
"Whether the captured data include carrier number",
&include_carrier_number);
}