wireshark/epan/dissectors/packet-csn1.c

1628 lines
56 KiB
C
Raw Permalink Normal View History

2011-02-28 09:43:22 +00:00
/* packet-csn1.c
* Routines for CSN1 dissection in wireshark.
* By Vincent Helfre, based on original code by Jari Sassi
* with the gracious authorization of STE
2011-02-28 09:43:22 +00:00
* Copyright (c) 2011 ST-Ericsson
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
2011-02-28 09:43:22 +00:00
*/
#include "config.h"
2011-02-28 09:43:22 +00:00
#include <epan/packet.h>
#include <epan/to_str.h>
2011-02-28 09:43:22 +00:00
#include "packet-csn1.h"
void proto_register_csn1(void);
static int hf_null_data = -1;
static expert_field ei_csn1_more_bits_to_unpack = EI_INIT;
static expert_field ei_csn1_general = EI_INIT;
static expert_field ei_csn1_not_implemented = EI_INIT;
static expert_field ei_csn1_union_index = EI_INIT;
static expert_field ei_csn1_script_error = EI_INIT;
static expert_field ei_csn1_more32bits = EI_INIT;
static expert_field ei_csn1_fixed_not_matched = EI_INIT;
csn1: fix csnStreamDissector(): catch unknown CSN_CHOICE values Some CSN.1 definitions may contain so-called unions that usually combine two or more choices. The exact element to be chosen is determined by the value encoded in one or more bits preceeding it. Here is an example of an identity union: { 0 < Global TFI : < Global TFI IE > > | 10 < TLLI / G-RNTI : bit (32) > | 110 < TQI : bit (16) > } So if a given bitstream starts with '0'B, the Global TFI IE follows. Otherwise either TLLI / G-RNTI or TQI is to be chosen. But what if neither of the choice items matches? For example, what if a given bitstream starts with '111'B? Most likely we should treat the bitstream as malformed, stop further decoding and report an error. And that's how Pycrate's [1] CSN.1 decoder [2] behaves. Hovewer, as it turns out, Wireshark would simply skip the whole choice element and start decoding the next one from the same bit position. Here is an example of a malformed packet: GSM RLC/MAC: PACKET_POLLING_REQUEST (4) (Downlink) 01.. .... = Payload Type (DL): RLC/MAC block contains an RLC/MAC control block that does not include the optional octets of the RLC/MAC control header (1) ..00 .... = RRBP: Reserved Block: (N+13) mod 2715648 (0) .... 1... = S/P: RRBP field is valid .... .001 = USF: 1 PACKET_POLLING_REQUEST (4) (downlink) 0001 00.. = MESSAGE_TYPE (DL): PACKET_POLLING_REQUEST (4) .... ..11 = PAGE_MODE: Same as before (3) ---! ID <--- This is wrong! '111'B is unknown 1... .... = CONTROL_ACK_TYPE: PACKET CONTROL ACKNOWLEDGEMENT message format shall be an RLC/MAC control block Padding Bits .110 0000 0000 1000 0101 0000 1000 1000 = Padding: 1611157640 0100 0000 0001 0011 1010 1000 0000 0100 = Padding: 1075030020 1000 1011 0010 1011 0010 1011 0010 1011 = Padding: 2334862123 0010 1011 0010 1011 0010 1011 0010 1011 = Padding: 724249387 0010 1011 0010 1011 0010 1011 0010 1011 = Padding: 724249387 0010 1011 = Padding: 43 Let's fix this, so after this patch we get: GSM RLC/MAC: PACKET_POLLING_REQUEST (4) (Downlink) 01.. .... = Payload Type (DL): RLC/MAC block contains an RLC/MAC control block that does not include the optional octets of the RLC/MAC control header (1) ..00 .... = RRBP: Reserved Block: (N+13) mod 2715648 (0) .... 1... = S/P: RRBP field is valid .... .001 = USF: 1 PACKET_POLLING_REQUEST (4) (downlink) 0001 00.. = MESSAGE_TYPE (DL): PACKET_POLLING_REQUEST (4) .... ..11 = PAGE_MODE: Same as before (3) ID STREAM NOT SUPPORTED (PacketPollingID) [Expert Info (Warning/Protocol): STREAM NOT SUPPORTED (PacketPollingID)] [STREAM NOT SUPPORTED (PacketPollingID)] [Severity level: Warning] [Group: Protocol] [1] https://github.com/P1sec/pycrate [2] https://github.com/P1sec/pycrate/wiki/Using-the-pycrate-csn1-translator-and-runtime Change-Id: I7096c294e0d04d6afb3414874d3404cbb637fdae Reviewed-on: https://code.wireshark.org/review/36077 Reviewed-by: Pau Espin Pedrol <pespin@sysmocom.de> Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
2020-02-11 08:56:28 +00:00
static expert_field ei_csn1_stream_not_supported = EI_INIT;
2011-02-28 09:43:22 +00:00
#define pvDATA(_pv, _offset) ((void*) ((unsigned char*)_pv + _offset))
#define pui8DATA(_pv, _offset) ((guint8*) pvDATA(_pv, _offset))
#define pui16DATA(_pv, _offset) ((guint16*) pvDATA(_pv, _offset))
#define pui32DATA(_pv, _offset) ((guint32*) pvDATA(_pv, _offset))
/* used to tag existence of next element in variable length lists */
#define STANDARD_TAG 1
2011-02-28 09:43:22 +00:00
#define REVERSED_TAG 0
static const unsigned char ixBitsTab[] = {0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5};
2011-02-28 09:43:22 +00:00
static gint proto_csn1 = -1;
2011-02-28 09:43:22 +00:00
/* Returns no_of_bits (up to 8) masked with 0x2B */
static guint8
tvb_get_masked_bits8(tvbuff_t *tvb, gint bit_offset, const gint no_of_bits)
2011-02-28 09:43:22 +00:00
{
static const guint8 maskBits[] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};
gint byte_offset = bit_offset >> 3; /* divide by 8 */
gint relative_bit_offset = bit_offset & 0x07; /* modulo 8 */
guint8 result;
gint bit_shift = 8 - relative_bit_offset - (gint) no_of_bits;
if (bit_shift >= 0)
{
result = (0x2B ^ tvb_get_guint8(tvb, byte_offset)) >> bit_shift;
result &= maskBits[no_of_bits];
}
else
{
guint8 hight_part = (0x2B ^ tvb_get_guint8(tvb, byte_offset)) & maskBits[8 - relative_bit_offset];
hight_part = (guint8) (hight_part << (-bit_shift));
result = (0x2B ^ tvb_get_guint8(tvb, byte_offset+1)) >> (8 + bit_shift);
result |= hight_part;
}
return result;
}
/**
* ================================================================================================
* set initial/start values in help data structure used for packing/unpacking operation
* ================================================================================================
*/
void
csnStreamInit(csnStream_t* ar, gint bit_offset, gint remaining_bits_len, packet_info* pinfo)
2011-02-28 09:43:22 +00:00
{
ar->remaining_bits_len = remaining_bits_len;
ar->bit_offset = bit_offset;
ar->pinfo = pinfo;
2011-02-28 09:43:22 +00:00
}
static gint16
ProcessError(proto_tree *tree, packet_info* pinfo, tvbuff_t *tvb, gint bit_offset, gint16 err, expert_field* err_field, const CSN_DESCR* pDescr)
2011-02-28 09:43:22 +00:00
{
if (err_field != NULL)
proto_tree_add_expert_format(tree, pinfo, err_field, tvb, bit_offset>>3, 1, "%s (%s)", expert_get_summary(err_field), pDescr?pDescr->sz:"-");
2011-02-28 09:43:22 +00:00
return err;
}
#if 0
2011-02-28 09:43:22 +00:00
static const char* CSN_DESCR_type[]=
{
"CSN_END",
"CSN_BIT",
"CSN_UINT",
"CSN_TYPE",
"CSN_CHOICE",
"CSN_UNION",
"CSN_UNION_LH",
"CSN_UINT_ARRAY",
"CSN_TYPE_ARRAY",
"CSN_BITMAP",
"CSN_VARIABLE_BITMAP",
"CSN_VARIABLE_BITMAP_1",
"CSN_LEFT_ALIGNED_VAR_BMP",
"CSN_LEFT_ALIGNED_VAR_BMP_1",
"CSN_VARIABLE_ARRAY",
"CSN_VARIABLE_TARRAY",
"CSN_VARIABLE_TARRAY_OFFSET",
"CSN_RECURSIVE_ARRAY",
"CSN_RECURSIVE_TARRAY",
"CSN_RECURSIVE_TARRAY_1",
"CSN_RECURSIVE_TARRAY_2",
"CSN_EXIST",
"CSN_EXIST_LH",
"CSN_NEXT_EXIST",
"CSN_NEXT_EXIST_LH",
"CSN_NULL",
"CSN_FIXED",
"CSN_CALLBACK",
"CSN_UINT_OFFSET",
"CSN_UINT_LH",
"CSN_SERIALIZE",
"CSN_TRAP_ERROR"
"CSN_???"
};
#endif
2011-02-28 09:43:22 +00:00
/**
* ================================================================================================
* Return TRUE if tag in bit stream indicates existence of next list element,
* otherwise return FALSE.
* Will work for tag values equal to both 0 and 1.
* ================================================================================================
*/
static gboolean
existNextElement(tvbuff_t *tvb, gint bit_offset, guint8 Tag)
2011-02-28 09:43:22 +00:00
{
guint8 res = tvb_get_bits8(tvb, bit_offset, 1);
if (Tag == STANDARD_TAG)
2011-02-28 09:43:22 +00:00
{
return (res > 0);
}
return (res == 0);
}
gint16
csnStreamDissector(proto_tree *tree, csnStream_t* ar, const CSN_DESCR* pDescr, tvbuff_t *tvb, void* data, int ett_csn1)
2011-02-28 09:43:22 +00:00
{
gint remaining_bits_len = ar->remaining_bits_len;
gint bit_offset = ar->bit_offset;
guint8* pui8 = NULL;
2011-02-28 09:43:22 +00:00
guint16* pui16;
guint32* pui32;
guint8 Tag = STANDARD_TAG;
2011-02-28 09:43:22 +00:00
/* Negative number definitely indicates an error */
if (remaining_bits_len < 0)
2011-02-28 09:43:22 +00:00
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
2011-02-28 09:43:22 +00:00
do
{
switch (pDescr->type)
{
case CSN_BIT:
{
if (remaining_bits_len > 0)
{
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = tvb_get_bits8(tvb, bit_offset, 1);
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, 1, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
/* end add the bit value to protocol tree */
}
else if(pDescr->may_be_null)
{
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = 0;
proto_tree_add_none_format(tree, hf_null_data, tvb, 0, 0, "[NULL data]: %s Not Present", proto_registrar_get_name(*(pDescr->hf_ptr)));
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
}
2011-02-28 09:43:22 +00:00
pDescr++;
2011-02-28 09:43:22 +00:00
remaining_bits_len--;
bit_offset++;
break;
}
case CSN_NULL:
{ /* Empty member! */
bit_offset += pDescr->i;
2011-02-28 09:43:22 +00:00
pDescr++;
break;
}
case CSN_UINT:
{
guint8 no_of_bits = (guint8) pDescr->i;
if (remaining_bits_len >= no_of_bits)
{
if (no_of_bits <= 8)
{
guint8 ui8 = tvb_get_bits8(tvb, bit_offset, no_of_bits);
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = ui8;
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
}
else if (no_of_bits <= 16)
{
guint16 ui16 = tvb_get_bits16(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
pui16 = pui16DATA(data, pDescr->offset);
memcpy(pui16, &ui16, 2);
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
}
else if (no_of_bits <= 32)
{
guint32 ui32 = tvb_get_bits32(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
pui32 = pui32DATA(data, pDescr->offset);
memcpy(pui32, &ui32, 4);
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_GENERAL, &ei_csn1_general, pDescr);
2011-02-28 09:43:22 +00:00
}
remaining_bits_len -= no_of_bits;
bit_offset += no_of_bits;
}
else if(pDescr->may_be_null)
{
if (no_of_bits <= 8)
{
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = 0;
}
else if (no_of_bits <= 16)
{
pui16 = pui16DATA(data, pDescr->offset);
memset(pui16, 0, 2);
}
else if (no_of_bits <= 32)
{
pui32 = pui32DATA(data, pDescr->offset);
memset(pui32, 0, 4);
}
proto_tree_add_none_format(tree, hf_null_data, tvb, 0, 0, "[NULL data]: %s Not Present", proto_registrar_get_name(*(pDescr->hf_ptr)));
2011-02-28 09:43:22 +00:00
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
pDescr++;
break;
}
case CSN_UINT_OFFSET:
{
guint8 no_of_bits = (guint8) pDescr->i;
if (remaining_bits_len >= no_of_bits)
{
if (no_of_bits <= 8)
{
guint8 ui8 = tvb_get_bits8(tvb, bit_offset, no_of_bits);
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = ui8 + (guint8)pDescr->descr.value;
csn1: fix M_UINT_OFFSET: show value after applying the offset Some integer fields in CSN.1 structures can be encoded with an offset. A good example is GPRS Mobile Allocation IE defined in 3GPP TS 44.060, section 12.10a, table 12.10a.1: < GPRS Mobile Allocation IE > ::= < HSN : bit (6) > { 0 | 1 < RFL number list : < RFL number list struct > > } { 0 < MA_LENGTH : bit (6) > < MA_BITMAP : bit (val(MA_LENGTH) + 1) > | 1 { 0 | 1 < ARFCN index list : < ARFCN index list struct > > } } ; so in this case the variable-length MA_BITMAP is defined as follows: < MA_BITMAP : bit (val(MA_LENGTH) + 1) > what basically means that its bit length shall be encoded with a negative offset 1, therefore the following statements apply: MA_LENGTH=0 defines MA_BITMAP of bit length 1 MA_LENGTH=1 defines MA_BITMAP of bit length 2 ... MA_LENGTH=63 defines MA_BITMAP of bit length 64 == What's wrong? == For some reason, Wireshark shows the raw values without applying the offset. Here is an example of GPRS Mobile Allocation IE: GPRS_Mobile_Allocation .... .101 010. .... = HSN: 42 ...0 .... = RFL_NUMBER Exist: 0 .... 0... = Mobile Allocation: (Union) u.MA .... .001 111. .... = Bit length: 15 ...0 .... = Bitmap: 0 // 1st .... 1... = Bitmap: 1 .... .0.. = Bitmap: 0 .... ..1. = Bitmap: 1 .... ...0 = Bitmap: 0 1... .... = Bitmap: 1 .0.. .... = Bitmap: 0 ..1. .... = Bitmap: 1 // 8th ...0 .... = Bitmap: 0 .... 1... = Bitmap: 1 .... .0.. = Bitmap: 0 .... ..1. = Bitmap: 1 .... ...0 = Bitmap: 0 1... .... = Bitmap: 1 .0.. .... = Bitmap: 0 ..1. .... = Bitmap: 1 // 16th == Solution == Let's use proto_tree_add_uint_bits_format_value(), so we can print the final value with the offset applied, as well as the original one and the offset itself: GPRS_Mobile_Allocation .... .101 010. .... = HSN: 42 ...0 .... = RFL_NUMBER Exist: 0 .... 0... = Mobile Allocation: (Union) u.MA .... .001 111. .... = Bit length: 16 (Raw 15 + Offset 1) Change-Id: Ic4eaf2d8a3c2fedca855726e4175ddf47d16c5af Reviewed-on: https://code.wireshark.org/review/37931 Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
2020-07-22 19:54:20 +00:00
proto_tree_add_uint_bits_format_value(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits,
*pui8, ENC_BIG_ENDIAN, "%u (Raw %u + Offset %u)", *pui8, ui8,
csn1: fix M_UINT_OFFSET: show value after applying the offset Some integer fields in CSN.1 structures can be encoded with an offset. A good example is GPRS Mobile Allocation IE defined in 3GPP TS 44.060, section 12.10a, table 12.10a.1: < GPRS Mobile Allocation IE > ::= < HSN : bit (6) > { 0 | 1 < RFL number list : < RFL number list struct > > } { 0 < MA_LENGTH : bit (6) > < MA_BITMAP : bit (val(MA_LENGTH) + 1) > | 1 { 0 | 1 < ARFCN index list : < ARFCN index list struct > > } } ; so in this case the variable-length MA_BITMAP is defined as follows: < MA_BITMAP : bit (val(MA_LENGTH) + 1) > what basically means that its bit length shall be encoded with a negative offset 1, therefore the following statements apply: MA_LENGTH=0 defines MA_BITMAP of bit length 1 MA_LENGTH=1 defines MA_BITMAP of bit length 2 ... MA_LENGTH=63 defines MA_BITMAP of bit length 64 == What's wrong? == For some reason, Wireshark shows the raw values without applying the offset. Here is an example of GPRS Mobile Allocation IE: GPRS_Mobile_Allocation .... .101 010. .... = HSN: 42 ...0 .... = RFL_NUMBER Exist: 0 .... 0... = Mobile Allocation: (Union) u.MA .... .001 111. .... = Bit length: 15 ...0 .... = Bitmap: 0 // 1st .... 1... = Bitmap: 1 .... .0.. = Bitmap: 0 .... ..1. = Bitmap: 1 .... ...0 = Bitmap: 0 1... .... = Bitmap: 1 .0.. .... = Bitmap: 0 ..1. .... = Bitmap: 1 // 8th ...0 .... = Bitmap: 0 .... 1... = Bitmap: 1 .... .0.. = Bitmap: 0 .... ..1. = Bitmap: 1 .... ...0 = Bitmap: 0 1... .... = Bitmap: 1 .0.. .... = Bitmap: 0 ..1. .... = Bitmap: 1 // 16th == Solution == Let's use proto_tree_add_uint_bits_format_value(), so we can print the final value with the offset applied, as well as the original one and the offset itself: GPRS_Mobile_Allocation .... .101 010. .... = HSN: 42 ...0 .... = RFL_NUMBER Exist: 0 .... 0... = Mobile Allocation: (Union) u.MA .... .001 111. .... = Bit length: 16 (Raw 15 + Offset 1) Change-Id: Ic4eaf2d8a3c2fedca855726e4175ddf47d16c5af Reviewed-on: https://code.wireshark.org/review/37931 Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
2020-07-22 19:54:20 +00:00
(guint8) pDescr->descr.value);
2011-02-28 09:43:22 +00:00
}
else if (no_of_bits <= 16)
{
guint16 ui16 = tvb_get_bits16(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN) + (guint16)pDescr->descr.value;
2011-02-28 09:43:22 +00:00
pui16 = pui16DATA(data, pDescr->offset);
memcpy(pui16, &ui16, 2);
csn1: fix M_UINT_OFFSET: show value after applying the offset Some integer fields in CSN.1 structures can be encoded with an offset. A good example is GPRS Mobile Allocation IE defined in 3GPP TS 44.060, section 12.10a, table 12.10a.1: < GPRS Mobile Allocation IE > ::= < HSN : bit (6) > { 0 | 1 < RFL number list : < RFL number list struct > > } { 0 < MA_LENGTH : bit (6) > < MA_BITMAP : bit (val(MA_LENGTH) + 1) > | 1 { 0 | 1 < ARFCN index list : < ARFCN index list struct > > } } ; so in this case the variable-length MA_BITMAP is defined as follows: < MA_BITMAP : bit (val(MA_LENGTH) + 1) > what basically means that its bit length shall be encoded with a negative offset 1, therefore the following statements apply: MA_LENGTH=0 defines MA_BITMAP of bit length 1 MA_LENGTH=1 defines MA_BITMAP of bit length 2 ... MA_LENGTH=63 defines MA_BITMAP of bit length 64 == What's wrong? == For some reason, Wireshark shows the raw values without applying the offset. Here is an example of GPRS Mobile Allocation IE: GPRS_Mobile_Allocation .... .101 010. .... = HSN: 42 ...0 .... = RFL_NUMBER Exist: 0 .... 0... = Mobile Allocation: (Union) u.MA .... .001 111. .... = Bit length: 15 ...0 .... = Bitmap: 0 // 1st .... 1... = Bitmap: 1 .... .0.. = Bitmap: 0 .... ..1. = Bitmap: 1 .... ...0 = Bitmap: 0 1... .... = Bitmap: 1 .0.. .... = Bitmap: 0 ..1. .... = Bitmap: 1 // 8th ...0 .... = Bitmap: 0 .... 1... = Bitmap: 1 .... .0.. = Bitmap: 0 .... ..1. = Bitmap: 1 .... ...0 = Bitmap: 0 1... .... = Bitmap: 1 .0.. .... = Bitmap: 0 ..1. .... = Bitmap: 1 // 16th == Solution == Let's use proto_tree_add_uint_bits_format_value(), so we can print the final value with the offset applied, as well as the original one and the offset itself: GPRS_Mobile_Allocation .... .101 010. .... = HSN: 42 ...0 .... = RFL_NUMBER Exist: 0 .... 0... = Mobile Allocation: (Union) u.MA .... .001 111. .... = Bit length: 16 (Raw 15 + Offset 1) Change-Id: Ic4eaf2d8a3c2fedca855726e4175ddf47d16c5af Reviewed-on: https://code.wireshark.org/review/37931 Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
2020-07-22 19:54:20 +00:00
proto_tree_add_uint_bits_format_value(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits,
*pui16, ENC_BIG_ENDIAN, "%u (Raw %u + Offset %u)", *pui16, ui16,
csn1: fix M_UINT_OFFSET: show value after applying the offset Some integer fields in CSN.1 structures can be encoded with an offset. A good example is GPRS Mobile Allocation IE defined in 3GPP TS 44.060, section 12.10a, table 12.10a.1: < GPRS Mobile Allocation IE > ::= < HSN : bit (6) > { 0 | 1 < RFL number list : < RFL number list struct > > } { 0 < MA_LENGTH : bit (6) > < MA_BITMAP : bit (val(MA_LENGTH) + 1) > | 1 { 0 | 1 < ARFCN index list : < ARFCN index list struct > > } } ; so in this case the variable-length MA_BITMAP is defined as follows: < MA_BITMAP : bit (val(MA_LENGTH) + 1) > what basically means that its bit length shall be encoded with a negative offset 1, therefore the following statements apply: MA_LENGTH=0 defines MA_BITMAP of bit length 1 MA_LENGTH=1 defines MA_BITMAP of bit length 2 ... MA_LENGTH=63 defines MA_BITMAP of bit length 64 == What's wrong? == For some reason, Wireshark shows the raw values without applying the offset. Here is an example of GPRS Mobile Allocation IE: GPRS_Mobile_Allocation .... .101 010. .... = HSN: 42 ...0 .... = RFL_NUMBER Exist: 0 .... 0... = Mobile Allocation: (Union) u.MA .... .001 111. .... = Bit length: 15 ...0 .... = Bitmap: 0 // 1st .... 1... = Bitmap: 1 .... .0.. = Bitmap: 0 .... ..1. = Bitmap: 1 .... ...0 = Bitmap: 0 1... .... = Bitmap: 1 .0.. .... = Bitmap: 0 ..1. .... = Bitmap: 1 // 8th ...0 .... = Bitmap: 0 .... 1... = Bitmap: 1 .... .0.. = Bitmap: 0 .... ..1. = Bitmap: 1 .... ...0 = Bitmap: 0 1... .... = Bitmap: 1 .0.. .... = Bitmap: 0 ..1. .... = Bitmap: 1 // 16th == Solution == Let's use proto_tree_add_uint_bits_format_value(), so we can print the final value with the offset applied, as well as the original one and the offset itself: GPRS_Mobile_Allocation .... .101 010. .... = HSN: 42 ...0 .... = RFL_NUMBER Exist: 0 .... 0... = Mobile Allocation: (Union) u.MA .... .001 111. .... = Bit length: 16 (Raw 15 + Offset 1) Change-Id: Ic4eaf2d8a3c2fedca855726e4175ddf47d16c5af Reviewed-on: https://code.wireshark.org/review/37931 Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
2020-07-22 19:54:20 +00:00
(guint16) pDescr->descr.value);
2011-02-28 09:43:22 +00:00
}
else if (no_of_bits <= 32)
{
guint32 ui32 = tvb_get_bits32(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN) + (guint16)pDescr->descr.value;
2011-02-28 09:43:22 +00:00
pui32 = pui32DATA(data, pDescr->offset);
memcpy(pui32, &ui32, 4);
csn1: fix M_UINT_OFFSET: show value after applying the offset Some integer fields in CSN.1 structures can be encoded with an offset. A good example is GPRS Mobile Allocation IE defined in 3GPP TS 44.060, section 12.10a, table 12.10a.1: < GPRS Mobile Allocation IE > ::= < HSN : bit (6) > { 0 | 1 < RFL number list : < RFL number list struct > > } { 0 < MA_LENGTH : bit (6) > < MA_BITMAP : bit (val(MA_LENGTH) + 1) > | 1 { 0 | 1 < ARFCN index list : < ARFCN index list struct > > } } ; so in this case the variable-length MA_BITMAP is defined as follows: < MA_BITMAP : bit (val(MA_LENGTH) + 1) > what basically means that its bit length shall be encoded with a negative offset 1, therefore the following statements apply: MA_LENGTH=0 defines MA_BITMAP of bit length 1 MA_LENGTH=1 defines MA_BITMAP of bit length 2 ... MA_LENGTH=63 defines MA_BITMAP of bit length 64 == What's wrong? == For some reason, Wireshark shows the raw values without applying the offset. Here is an example of GPRS Mobile Allocation IE: GPRS_Mobile_Allocation .... .101 010. .... = HSN: 42 ...0 .... = RFL_NUMBER Exist: 0 .... 0... = Mobile Allocation: (Union) u.MA .... .001 111. .... = Bit length: 15 ...0 .... = Bitmap: 0 // 1st .... 1... = Bitmap: 1 .... .0.. = Bitmap: 0 .... ..1. = Bitmap: 1 .... ...0 = Bitmap: 0 1... .... = Bitmap: 1 .0.. .... = Bitmap: 0 ..1. .... = Bitmap: 1 // 8th ...0 .... = Bitmap: 0 .... 1... = Bitmap: 1 .... .0.. = Bitmap: 0 .... ..1. = Bitmap: 1 .... ...0 = Bitmap: 0 1... .... = Bitmap: 1 .0.. .... = Bitmap: 0 ..1. .... = Bitmap: 1 // 16th == Solution == Let's use proto_tree_add_uint_bits_format_value(), so we can print the final value with the offset applied, as well as the original one and the offset itself: GPRS_Mobile_Allocation .... .101 010. .... = HSN: 42 ...0 .... = RFL_NUMBER Exist: 0 .... 0... = Mobile Allocation: (Union) u.MA .... .001 111. .... = Bit length: 16 (Raw 15 + Offset 1) Change-Id: Ic4eaf2d8a3c2fedca855726e4175ddf47d16c5af Reviewed-on: https://code.wireshark.org/review/37931 Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
2020-07-22 19:54:20 +00:00
proto_tree_add_uint_bits_format_value(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits,
*pui32, ENC_BIG_ENDIAN, "%u (Raw %u + Offset %u)", *pui32, ui32,
csn1: fix M_UINT_OFFSET: show value after applying the offset Some integer fields in CSN.1 structures can be encoded with an offset. A good example is GPRS Mobile Allocation IE defined in 3GPP TS 44.060, section 12.10a, table 12.10a.1: < GPRS Mobile Allocation IE > ::= < HSN : bit (6) > { 0 | 1 < RFL number list : < RFL number list struct > > } { 0 < MA_LENGTH : bit (6) > < MA_BITMAP : bit (val(MA_LENGTH) + 1) > | 1 { 0 | 1 < ARFCN index list : < ARFCN index list struct > > } } ; so in this case the variable-length MA_BITMAP is defined as follows: < MA_BITMAP : bit (val(MA_LENGTH) + 1) > what basically means that its bit length shall be encoded with a negative offset 1, therefore the following statements apply: MA_LENGTH=0 defines MA_BITMAP of bit length 1 MA_LENGTH=1 defines MA_BITMAP of bit length 2 ... MA_LENGTH=63 defines MA_BITMAP of bit length 64 == What's wrong? == For some reason, Wireshark shows the raw values without applying the offset. Here is an example of GPRS Mobile Allocation IE: GPRS_Mobile_Allocation .... .101 010. .... = HSN: 42 ...0 .... = RFL_NUMBER Exist: 0 .... 0... = Mobile Allocation: (Union) u.MA .... .001 111. .... = Bit length: 15 ...0 .... = Bitmap: 0 // 1st .... 1... = Bitmap: 1 .... .0.. = Bitmap: 0 .... ..1. = Bitmap: 1 .... ...0 = Bitmap: 0 1... .... = Bitmap: 1 .0.. .... = Bitmap: 0 ..1. .... = Bitmap: 1 // 8th ...0 .... = Bitmap: 0 .... 1... = Bitmap: 1 .... .0.. = Bitmap: 0 .... ..1. = Bitmap: 1 .... ...0 = Bitmap: 0 1... .... = Bitmap: 1 .0.. .... = Bitmap: 0 ..1. .... = Bitmap: 1 // 16th == Solution == Let's use proto_tree_add_uint_bits_format_value(), so we can print the final value with the offset applied, as well as the original one and the offset itself: GPRS_Mobile_Allocation .... .101 010. .... = HSN: 42 ...0 .... = RFL_NUMBER Exist: 0 .... 0... = Mobile Allocation: (Union) u.MA .... .001 111. .... = Bit length: 16 (Raw 15 + Offset 1) Change-Id: Ic4eaf2d8a3c2fedca855726e4175ddf47d16c5af Reviewed-on: https://code.wireshark.org/review/37931 Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
2020-07-22 19:54:20 +00:00
(guint16) pDescr->descr.value);
2011-02-28 09:43:22 +00:00
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_GENERAL, &ei_csn1_general, pDescr);
2011-02-28 09:43:22 +00:00
}
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
remaining_bits_len -= no_of_bits;
bit_offset += no_of_bits;
pDescr++;
break;
}
case CSN_UINT_LH:
{
guint8 no_of_bits = (guint8) pDescr->i;
2011-02-28 09:43:22 +00:00
if (remaining_bits_len >= no_of_bits)
{
if (no_of_bits <= 8)
{
guint8 ui8 = tvb_get_masked_bits8(tvb, bit_offset, no_of_bits);
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = ui8;
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
}
else
{/* Maybe we should support more than 8 bits ? */
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_GENERAL, &ei_csn1_general, pDescr);
2011-02-28 09:43:22 +00:00
}
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
remaining_bits_len -= no_of_bits;
bit_offset += no_of_bits;
pDescr++;
break;
}
case CSN_SPLIT_BITS:
{
guint8 no_of_value_bits = (guint8) pDescr->i;
guint64 value;
proto_tree_add_split_bits_item_ret_val(tree, *pDescr->hf_ptr, tvb, bit_offset, pDescr->descr.crumb_spec, &value);
if (no_of_value_bits <= 8)
{
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = (guint8)value;
}
else if (no_of_value_bits <= 16)
{
guint16 ui16 = (guint16) value;
pui16 = pui16DATA(data, pDescr->offset);
memcpy(pui16, &ui16, 2);
}
else if (no_of_value_bits <= 32)
{
guint32 ui32 = (guint32) value;
pui32 = pui32DATA(data, pDescr->offset);
memcpy(pui32, &ui32, 4);
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_GENERAL, &ei_csn1_general, pDescr);
}
pDescr++;
break;
}
case CSN_SPLIT_BITS_CRUMB:
{
if (remaining_bits_len >= pDescr->descr.crumb_spec[pDescr->i].crumb_bit_length)
{
proto_tree_add_split_bits_crumb(tree, *pDescr->hf_ptr, tvb, bit_offset,
pDescr->descr.crumb_spec, pDescr->i);
remaining_bits_len -= pDescr->descr.crumb_spec[pDescr->i].crumb_bit_length;
bit_offset += pDescr->descr.crumb_spec[pDescr->i].crumb_bit_length;
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
}
pDescr++;
break;
}
2011-02-28 09:43:22 +00:00
case CSN_UINT_ARRAY:
{
guint8 no_of_bits = (guint8) pDescr->i;
guint16 nCount = (guint16)pDescr->descr.value; /* nCount supplied by value i.e. M_UINT_ARRAY(...) */
int i = 0;
2011-02-28 09:43:22 +00:00
if (pDescr->value != 0)
2011-02-28 09:43:22 +00:00
{ /* nCount specified by a reference to field holding value i.e. M_VAR_UINT_ARRAY(...) */
memcpy(&nCount, pui16DATA(data, nCount), 2);
2011-02-28 09:43:22 +00:00
}
if (remaining_bits_len >= (no_of_bits * nCount))
2011-02-28 09:43:22 +00:00
{
remaining_bits_len -= (no_of_bits * nCount);
2011-02-28 09:43:22 +00:00
if (no_of_bits <= 8)
{
pui8 = pui8DATA(data, pDescr->offset);
do
{
*pui8++ = tvb_get_bits8(tvb, bit_offset, no_of_bits);
proto_tree_add_uint_bits_format_value(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, *pui8, ENC_BIG_ENDIAN, " (Count %d)", i++);
2011-02-28 09:43:22 +00:00
bit_offset += no_of_bits;
} while (--nCount > 0);
}
else if (no_of_bits <= 16)
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, 999, &ei_csn1_not_implemented, pDescr);
2011-02-28 09:43:22 +00:00
}
else if (no_of_bits <= 32)
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, 999, &ei_csn1_not_implemented, pDescr);
2011-02-28 09:43:22 +00:00
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_GENERAL, &ei_csn1_general, pDescr);
2011-02-28 09:43:22 +00:00
}
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
pDescr++;
break;
}
case CSN_VARIABLE_TARRAY_OFFSET:
case CSN_VARIABLE_TARRAY:
case CSN_TYPE_ARRAY:
{
gint16 Status;
csnStream_t arT = *ar;
gint16 nCount = pDescr->i;
guint16 nSize = (guint16)(gint32)pDescr->value;
2011-02-28 09:43:22 +00:00
int i =0;
pui8 = pui8DATA(data, pDescr->offset);
if (pDescr->type == CSN_VARIABLE_TARRAY)
{ /* Count specified in field */
nCount = *pui8DATA(data, pDescr->i);
2011-02-28 09:43:22 +00:00
}
else if (pDescr->type == CSN_VARIABLE_TARRAY_OFFSET)
{ /* Count specified in field */
nCount = *pui8DATA(data, pDescr->i);
/* nCount--; the 1 offset is already taken into account in CSN_UINT_OFFSET */
2011-02-28 09:43:22 +00:00
}
while (nCount > 0)
{ /* resulting array of length 0 is possible
2011-02-28 09:43:22 +00:00
* but no bits shall be read from bitstream
*/
proto_item *ti;
proto_tree *test_tree;
test_tree = proto_tree_add_subtree_format(tree, tvb, bit_offset>>3, 1, ett_csn1, &ti, "%s[%d]",pDescr->sz, i++);
2011-02-28 09:43:22 +00:00
csnStreamInit(&arT, bit_offset, remaining_bits_len, ar->pinfo);
Status = csnStreamDissector(test_tree, &arT, (const CSN_DESCR*)pDescr->descr.ptr, tvb, pui8, ett_csn1);
2011-02-28 09:43:22 +00:00
if (Status >= 0)
{
pui8 += nSize;
proto_item_set_len(ti,((arT.bit_offset-1)>>3) - (bit_offset>>3)+1);
2011-02-28 09:43:22 +00:00
remaining_bits_len = arT.remaining_bits_len;
bit_offset = arT.bit_offset;
}
else
{
return Status;
}
nCount--;
}
pDescr++;
break;
}
case CSN_BITMAP:
{ /* bitmap with given length. The result is left aligned! */
guint8 no_of_bits = (guint8) pDescr->i; /* length of bitmap */
if (no_of_bits > 0)
{
if (no_of_bits > remaining_bits_len)
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
}
if (no_of_bits <= 32)
{
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
}
else if (no_of_bits <= 64)
{
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, 999, &ei_csn1_not_implemented, pDescr);
}
2011-02-28 09:43:22 +00:00
remaining_bits_len -= no_of_bits;
bit_offset += no_of_bits;
}
/* bitmap was successfully extracted or it was empty */
pDescr++;
break;
}
case CSN_TYPE:
{
gint16 Status;
csnStream_t arT = *ar;
proto_item *ti;
proto_tree *test_tree;
if (pDescr->may_be_null && remaining_bits_len == 0)
2011-02-28 09:43:22 +00:00
{
proto_tree_add_none_format(tree, hf_null_data, tvb, 0, 0, "[NULL data]: %s Not Present", pDescr->sz);
} else {
test_tree = proto_tree_add_subtree_format(tree, tvb, bit_offset>>3, 1, ett_csn1, &ti, "%s", pDescr->sz);
csnStreamInit(&arT, bit_offset, remaining_bits_len, ar->pinfo);
Status = csnStreamDissector(test_tree, &arT, (const CSN_DESCR*)pDescr->descr.ptr, tvb, pvDATA(data, pDescr->offset), ett_csn1);
if (Status >= 0)
{
proto_item_set_len(ti,((arT.bit_offset-1)>>3) - (bit_offset>>3)+1);
remaining_bits_len = arT.remaining_bits_len;
bit_offset = arT.bit_offset;
}
else
{
/* Has already been processed: ProcessError("csnStreamDissector", Status, pDescr); */
return Status;
}
2011-02-28 09:43:22 +00:00
}
pDescr++;
2011-02-28 09:43:22 +00:00
break;
}
case CSN_CHOICE:
{
gint16 count = pDescr->i;
guint8 i = 0;
const CSN_ChoiceElement_t* pChoice = (const CSN_ChoiceElement_t*) pDescr->descr.ptr;
2011-02-28 09:43:22 +00:00
csn1: fix csnStreamDissector(): catch unknown CSN_CHOICE values Some CSN.1 definitions may contain so-called unions that usually combine two or more choices. The exact element to be chosen is determined by the value encoded in one or more bits preceeding it. Here is an example of an identity union: { 0 < Global TFI : < Global TFI IE > > | 10 < TLLI / G-RNTI : bit (32) > | 110 < TQI : bit (16) > } So if a given bitstream starts with '0'B, the Global TFI IE follows. Otherwise either TLLI / G-RNTI or TQI is to be chosen. But what if neither of the choice items matches? For example, what if a given bitstream starts with '111'B? Most likely we should treat the bitstream as malformed, stop further decoding and report an error. And that's how Pycrate's [1] CSN.1 decoder [2] behaves. Hovewer, as it turns out, Wireshark would simply skip the whole choice element and start decoding the next one from the same bit position. Here is an example of a malformed packet: GSM RLC/MAC: PACKET_POLLING_REQUEST (4) (Downlink) 01.. .... = Payload Type (DL): RLC/MAC block contains an RLC/MAC control block that does not include the optional octets of the RLC/MAC control header (1) ..00 .... = RRBP: Reserved Block: (N+13) mod 2715648 (0) .... 1... = S/P: RRBP field is valid .... .001 = USF: 1 PACKET_POLLING_REQUEST (4) (downlink) 0001 00.. = MESSAGE_TYPE (DL): PACKET_POLLING_REQUEST (4) .... ..11 = PAGE_MODE: Same as before (3) ---! ID <--- This is wrong! '111'B is unknown 1... .... = CONTROL_ACK_TYPE: PACKET CONTROL ACKNOWLEDGEMENT message format shall be an RLC/MAC control block Padding Bits .110 0000 0000 1000 0101 0000 1000 1000 = Padding: 1611157640 0100 0000 0001 0011 1010 1000 0000 0100 = Padding: 1075030020 1000 1011 0010 1011 0010 1011 0010 1011 = Padding: 2334862123 0010 1011 0010 1011 0010 1011 0010 1011 = Padding: 724249387 0010 1011 0010 1011 0010 1011 0010 1011 = Padding: 724249387 0010 1011 = Padding: 43 Let's fix this, so after this patch we get: GSM RLC/MAC: PACKET_POLLING_REQUEST (4) (Downlink) 01.. .... = Payload Type (DL): RLC/MAC block contains an RLC/MAC control block that does not include the optional octets of the RLC/MAC control header (1) ..00 .... = RRBP: Reserved Block: (N+13) mod 2715648 (0) .... 1... = S/P: RRBP field is valid .... .001 = USF: 1 PACKET_POLLING_REQUEST (4) (downlink) 0001 00.. = MESSAGE_TYPE (DL): PACKET_POLLING_REQUEST (4) .... ..11 = PAGE_MODE: Same as before (3) ID STREAM NOT SUPPORTED (PacketPollingID) [Expert Info (Warning/Protocol): STREAM NOT SUPPORTED (PacketPollingID)] [STREAM NOT SUPPORTED (PacketPollingID)] [Severity level: Warning] [Group: Protocol] [1] https://github.com/P1sec/pycrate [2] https://github.com/P1sec/pycrate/wiki/Using-the-pycrate-csn1-translator-and-runtime Change-Id: I7096c294e0d04d6afb3414874d3404cbb637fdae Reviewed-on: https://code.wireshark.org/review/36077 Reviewed-by: Pau Espin Pedrol <pespin@sysmocom.de> Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
2020-02-11 08:56:28 +00:00
/* Make sure that the list of choice items is not empty */
if (!count)
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_IN_SCRIPT, &ei_csn1_script_error, pDescr);
2011-02-28 09:43:22 +00:00
while (count > 0)
{
guint8 no_of_bits = pChoice->bits;
guint8 value = tvb_get_bits8(tvb, bit_offset, no_of_bits);
if (value == pChoice->value)
{
CSN_DESCR descr[2];
gint16 Status;
csnStream_t arT = *ar;
proto_item *ti = NULL;
2011-02-28 09:43:22 +00:00
proto_tree *test_tree;
descr[0] = pChoice->descr;
memset(&descr[1], 0x00, sizeof(CSN_DESCR));
descr[1].type = CSN_END;
2011-02-28 09:43:22 +00:00
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = i;
if (pDescr->sz)
{
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
}
if (!pChoice->keep_bits)
{
bit_offset += no_of_bits;
remaining_bits_len -= no_of_bits;
}
if (pDescr->sz)
{
test_tree = proto_tree_add_subtree(tree, tvb, bit_offset>>3, 1, ett_csn1, &ti, pDescr->sz);
} else {
test_tree = tree;
}
2011-02-28 09:43:22 +00:00
csnStreamInit(&arT, bit_offset, remaining_bits_len, ar->pinfo);
Status = csnStreamDissector(test_tree, &arT, (const CSN_DESCR*)descr, tvb, data, ett_csn1);
2011-02-28 09:43:22 +00:00
if (Status >= 0)
{
if (ti)
proto_item_set_len(ti,((arT.bit_offset-1)>>3) - (bit_offset>>3)+1);
2011-02-28 09:43:22 +00:00
remaining_bits_len = arT.remaining_bits_len;
bit_offset = arT.bit_offset;
}
else
{
return Status;
}
break;
}
count--;
pChoice++;
i++;
}
csn1: fix csnStreamDissector(): catch unknown CSN_CHOICE values Some CSN.1 definitions may contain so-called unions that usually combine two or more choices. The exact element to be chosen is determined by the value encoded in one or more bits preceeding it. Here is an example of an identity union: { 0 < Global TFI : < Global TFI IE > > | 10 < TLLI / G-RNTI : bit (32) > | 110 < TQI : bit (16) > } So if a given bitstream starts with '0'B, the Global TFI IE follows. Otherwise either TLLI / G-RNTI or TQI is to be chosen. But what if neither of the choice items matches? For example, what if a given bitstream starts with '111'B? Most likely we should treat the bitstream as malformed, stop further decoding and report an error. And that's how Pycrate's [1] CSN.1 decoder [2] behaves. Hovewer, as it turns out, Wireshark would simply skip the whole choice element and start decoding the next one from the same bit position. Here is an example of a malformed packet: GSM RLC/MAC: PACKET_POLLING_REQUEST (4) (Downlink) 01.. .... = Payload Type (DL): RLC/MAC block contains an RLC/MAC control block that does not include the optional octets of the RLC/MAC control header (1) ..00 .... = RRBP: Reserved Block: (N+13) mod 2715648 (0) .... 1... = S/P: RRBP field is valid .... .001 = USF: 1 PACKET_POLLING_REQUEST (4) (downlink) 0001 00.. = MESSAGE_TYPE (DL): PACKET_POLLING_REQUEST (4) .... ..11 = PAGE_MODE: Same as before (3) ---! ID <--- This is wrong! '111'B is unknown 1... .... = CONTROL_ACK_TYPE: PACKET CONTROL ACKNOWLEDGEMENT message format shall be an RLC/MAC control block Padding Bits .110 0000 0000 1000 0101 0000 1000 1000 = Padding: 1611157640 0100 0000 0001 0011 1010 1000 0000 0100 = Padding: 1075030020 1000 1011 0010 1011 0010 1011 0010 1011 = Padding: 2334862123 0010 1011 0010 1011 0010 1011 0010 1011 = Padding: 724249387 0010 1011 0010 1011 0010 1011 0010 1011 = Padding: 724249387 0010 1011 = Padding: 43 Let's fix this, so after this patch we get: GSM RLC/MAC: PACKET_POLLING_REQUEST (4) (Downlink) 01.. .... = Payload Type (DL): RLC/MAC block contains an RLC/MAC control block that does not include the optional octets of the RLC/MAC control header (1) ..00 .... = RRBP: Reserved Block: (N+13) mod 2715648 (0) .... 1... = S/P: RRBP field is valid .... .001 = USF: 1 PACKET_POLLING_REQUEST (4) (downlink) 0001 00.. = MESSAGE_TYPE (DL): PACKET_POLLING_REQUEST (4) .... ..11 = PAGE_MODE: Same as before (3) ID STREAM NOT SUPPORTED (PacketPollingID) [Expert Info (Warning/Protocol): STREAM NOT SUPPORTED (PacketPollingID)] [STREAM NOT SUPPORTED (PacketPollingID)] [Severity level: Warning] [Group: Protocol] [1] https://github.com/P1sec/pycrate [2] https://github.com/P1sec/pycrate/wiki/Using-the-pycrate-csn1-translator-and-runtime Change-Id: I7096c294e0d04d6afb3414874d3404cbb637fdae Reviewed-on: https://code.wireshark.org/review/36077 Reviewed-by: Pau Espin Pedrol <pespin@sysmocom.de> Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
2020-02-11 08:56:28 +00:00
/* Neither of the choice items matched => unknown value */
if (!count) {
return ProcessError(tree, ar->pinfo, tvb, bit_offset,
CSN_ERROR_STREAM_NOT_SUPPORTED,
&ei_csn1_stream_not_supported, pDescr);
}
2011-02-28 09:43:22 +00:00
pDescr++;
break;
}
case CSN_SERIALIZE:
{
StreamSerializeFcn_t serialize = (StreamSerializeFcn_t)pDescr->aux_fn;
2011-02-28 09:43:22 +00:00
csnStream_t arT = *ar;
guint length_len = pDescr->i;
2011-02-28 09:43:22 +00:00
gint16 Status = -1;
proto_item *ti;
proto_tree *test_tree;
guint8 length = 0;
if (length_len)
{
length = tvb_get_bits8(tvb, bit_offset, length_len);
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, length_len, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
bit_offset += length_len;
remaining_bits_len -= length_len;
2011-02-28 09:43:22 +00:00
test_tree = proto_tree_add_subtree(tree, tvb, bit_offset>>3, ((bit_offset+length-1)>>3)-(bit_offset>>3) + 1, ett_csn1, &ti, pDescr->sz);
} else {
test_tree = proto_tree_add_subtree(tree, tvb, bit_offset>>3, 1, ett_csn1, &ti, pDescr->sz);
}
csnStreamInit(&arT, bit_offset, length > 0 ? length : remaining_bits_len, ar->pinfo);
2011-02-28 09:43:22 +00:00
Status = serialize(test_tree, &arT, tvb, pvDATA(data, pDescr->offset), ett_csn1);
if (Status >= 0)
{
if (length > 0) {
remaining_bits_len -= length;
bit_offset += length;
} else {
proto_item_set_len(ti,((arT.bit_offset - bit_offset)>>3)+1);
remaining_bits_len = arT.remaining_bits_len;
bit_offset = arT.bit_offset;
}
2011-02-28 09:43:22 +00:00
pDescr++;
}
else
{
/* Has already been processed: */
return Status;
}
break;
}
case CSN_UNION_LH:
case CSN_UNION:
{
gint16 Bits;
guint8 t_index;
2011-02-28 09:43:22 +00:00
gint16 count = pDescr->i;
const CSN_DESCR* pDescrNext = pDescr;
pDescrNext += count + 1; /* now this is next after the union */
if ((count <= 0) || (count > 16))
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_INVALID_UNION_INDEX, &ei_csn1_union_index, pDescr);
2011-02-28 09:43:22 +00:00
}
/* Now get the bits to extract the index */
Bits = ixBitsTab[count];
proto_tree_add_uint_bits_format_value(tree, *(pDescr->hf_ptr), tvb, bit_offset, Bits, tvb_get_bits8(tvb, bit_offset, Bits), ENC_BIG_ENDIAN, " (Union)");
t_index = 0;
2011-02-28 09:43:22 +00:00
while (Bits > 0)
{
t_index <<= 1;
2011-02-28 09:43:22 +00:00
if (CSN_UNION_LH == pDescr->type)
{
t_index |= tvb_get_masked_bits8(tvb, bit_offset, 1);
2011-02-28 09:43:22 +00:00
}
else
{
t_index |= tvb_get_bits8(tvb, bit_offset, 1);
2011-02-28 09:43:22 +00:00
}
remaining_bits_len--;
bit_offset++;
Bits--;
}
/* Assign UnionType */
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = t_index;
2011-02-28 09:43:22 +00:00
/* script index to continue on, limited in case we do not have a power of 2 */
pDescr += (MIN(t_index + 1, count));
2011-02-28 09:43:22 +00:00
switch (pDescr->type)
{ /* get the right element of the union based on computed index */
case CSN_BIT:
{
pui8 = pui8DATA(data, pDescr->offset);
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, 1, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
*pui8 = 0x00;
if (tvb_get_bits8(tvb, bit_offset, 1) > 0)
{
*pui8 = 0x01;
}
remaining_bits_len --;
2011-02-28 09:43:22 +00:00
bit_offset++;
pDescr++;
2011-02-28 09:43:22 +00:00
break;
}
case CSN_NULL:
{ /* Empty member! */
bit_offset += pDescr->i;
2011-02-28 09:43:22 +00:00
pDescr++;
break;
}
case CSN_UINT:
{
guint8 no_of_bits = (guint8) pDescr->i;
if (remaining_bits_len >= no_of_bits)
{
if (no_of_bits <= 8)
{
guint8 ui8 = tvb_get_bits8(tvb, bit_offset, no_of_bits);
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = ui8;
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
}
else if (no_of_bits <= 16)
{
guint16 ui16 = tvb_get_bits16(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
pui16 = pui16DATA(data, pDescr->offset);
memcpy(pui16, &ui16, 2);
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
}
else if (no_of_bits <= 32)
{
guint32 ui32 = tvb_get_bits32(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
pui32 = pui32DATA(data, pDescr->offset);
memcpy(pui32, &ui32, 4);
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_GENERAL, &ei_csn1_general, pDescr);
2011-02-28 09:43:22 +00:00
}
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_GENERAL, &ei_csn1_general, pDescr);
2011-02-28 09:43:22 +00:00
}
remaining_bits_len -= no_of_bits;
2011-02-28 09:43:22 +00:00
bit_offset += no_of_bits;
pDescr++;
break;
}
case CSN_UINT_OFFSET:
{
guint8 no_of_bits = (guint8) pDescr->i;
if (remaining_bits_len >= no_of_bits)
{
if (no_of_bits <= 8)
{
guint8 ui8 = tvb_get_bits8(tvb, bit_offset, no_of_bits);
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = ui8 + (guint8)pDescr->descr.value;
proto_tree_add_uint_bits_format_value(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ui8, ENC_BIG_ENDIAN, "%d", ui8);
}
else if (no_of_bits <= 16)
{
guint16 ui16 = tvb_get_bits16(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN) + (guint16)pDescr->descr.value;
pui16 = pui16DATA(data, pDescr->offset);
memcpy(pui16, &ui16, 2);
proto_tree_add_uint_bits_format_value(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ui16, ENC_BIG_ENDIAN, "%d", ui16);
}
else if (no_of_bits <= 32)
{
guint32 ui32 = tvb_get_bits32(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN) + (guint16)pDescr->descr.value;
pui32 = pui32DATA(data, pDescr->offset);
memcpy(pui32, &ui32, 4);
proto_tree_add_uint_bits_format_value(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ui32, ENC_BIG_ENDIAN, "%d", ui32);
2011-02-28 09:43:22 +00:00
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_GENERAL, &ei_csn1_general, pDescr);
2011-02-28 09:43:22 +00:00
}
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
remaining_bits_len -= no_of_bits;
2011-02-28 09:43:22 +00:00
bit_offset += no_of_bits;
pDescr++;
break;
}
case CSN_UINT_LH:
{
guint8 no_of_bits = (guint8) pDescr->i;
if (remaining_bits_len >= no_of_bits)
{
if (no_of_bits <= 8)
{
guint8 ui8 = tvb_get_masked_bits8(tvb, bit_offset, no_of_bits);
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = ui8;
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
}
else
{ /* Maybe we should support more than 8 bits ? */
ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_GENERAL, &ei_csn1_general, pDescr);
2011-02-28 09:43:22 +00:00
}
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
remaining_bits_len -= no_of_bits;
2011-02-28 09:43:22 +00:00
bit_offset += no_of_bits;
pDescr++;
break;
}
case CSN_UINT_ARRAY:
{
guint8 no_of_bits = (guint8) pDescr->i;
guint16 nCount = (guint16)pDescr->descr.value; /* nCount supplied by value i.e. M_UINT_ARRAY(...) */
2011-02-28 09:43:22 +00:00
gint i = 0;
if (pDescr->value != 0)
2011-02-28 09:43:22 +00:00
{ /* nCount specified by a reference to field holding value i.e. M_VAR_UINT_ARRAY(...) */
memcpy(&nCount, pui16DATA(data, nCount), 2);
2011-02-28 09:43:22 +00:00
}
if (remaining_bits_len >= (no_of_bits * nCount))
2011-02-28 09:43:22 +00:00
{
remaining_bits_len -= (no_of_bits * nCount);
2011-02-28 09:43:22 +00:00
if (no_of_bits <= 8)
{
pui8 = pui8DATA(data, pDescr->offset);
2011-02-28 09:43:22 +00:00
while (nCount > 0)
{
*pui8 = tvb_get_bits8(tvb, bit_offset, no_of_bits);
proto_tree_add_uint_bits_format_value(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, *pui8, ENC_BIG_ENDIAN, " (Count %d)", i++);
2011-02-28 09:43:22 +00:00
pui8++;
bit_offset += no_of_bits;
nCount--;
}
}
else if (no_of_bits <= 16)
{
pui16 = pui16DATA(data, pDescr->offset);
2011-02-28 09:43:22 +00:00
while (nCount > 0)
{
guint16 ui16;
ui16 = tvb_get_bits16(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
proto_tree_add_uint_bits_format_value(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ui16, ENC_BIG_ENDIAN, " (Count %d)", i++);
memcpy(pui16++, &ui16, sizeof(ui16));
2011-02-28 09:43:22 +00:00
bit_offset += no_of_bits;
nCount--;
}
}
else if (no_of_bits <= 32)
{ /* not supported */
return ProcessError(tree, ar->pinfo, tvb, bit_offset, 999, &ei_csn1_not_implemented, pDescr);
2011-02-28 09:43:22 +00:00
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_GENERAL, &ei_csn1_general, pDescr);
2011-02-28 09:43:22 +00:00
}
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
pDescr++;
break;
}
case CSN_VARIABLE_TARRAY_OFFSET:
case CSN_VARIABLE_TARRAY:
case CSN_TYPE_ARRAY:
{
gint16 Status;
csnStream_t arT = *ar;
guint16 nCount = (guint16) pDescr->i;
guint16 nSize = (guint16)(guint32)pDescr->value;
2011-02-28 09:43:22 +00:00
gint i = 0;
pui8 = pui8DATA(data, pDescr->offset);
2011-02-28 09:43:22 +00:00
if (CSN_VARIABLE_TARRAY == pDescr->type)
{ /* Count specified in field */
nCount = *pui8DATA(data, pDescr->i);
}
else if (CSN_VARIABLE_TARRAY_OFFSET == pDescr->type)
{ /* Count specified in field */
nCount = *pui8DATA(data, pDescr->i);
nCount--; /* Offset 1 */
}
2011-02-28 09:43:22 +00:00
while (nCount--) /* Changed to handle length = 0. */
{
proto_item *ti;
proto_tree *test_tree;
test_tree = proto_tree_add_subtree_format(tree, tvb, bit_offset>>3, 1, ett_csn1, &ti, "%s[%d]",pDescr->sz, i++);
2011-02-28 09:43:22 +00:00
csnStreamInit(&arT, bit_offset, remaining_bits_len, ar->pinfo);
Status = csnStreamDissector(test_tree, &arT, (const CSN_DESCR *)pDescr->descr.ptr, tvb, pui8, ett_csn1);
2011-02-28 09:43:22 +00:00
if (Status >= 0)
{
pui8 += nSize;
proto_item_set_len(ti,((arT.bit_offset-1)>>3) - (bit_offset>>3)+1);
2011-02-28 09:43:22 +00:00
remaining_bits_len = arT.remaining_bits_len;
bit_offset = arT.bit_offset;
}
else
{
return Status;
}
}
pDescr++;
break;
}
case CSN_BITMAP:
{ /* bitmap with given length. The result is left aligned! */
guint8 no_of_bits = (guint8) pDescr->i; /* length of bitmap */
if (no_of_bits > 0)
{ /* a non empty bitmap */
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
remaining_bits_len -= no_of_bits;
bit_offset += no_of_bits;
2011-02-28 09:43:22 +00:00
if (remaining_bits_len < 0)
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
}
/* bitmap was successfully extracted or it was empty */
pDescr++;
break;
}
case CSN_TYPE:
{
gint16 Status;
csnStream_t arT = *ar;
proto_item *ti;
proto_tree *test_tree;
if (pDescr->may_be_null && remaining_bits_len == 0)
2011-02-28 09:43:22 +00:00
{
proto_tree_add_none_format(tree, hf_null_data, tvb, 0, 0, "[NULL data]: %s Not Present", pDescr->sz);
} else {
test_tree = proto_tree_add_subtree(tree, tvb, bit_offset>>3, 1, ett_csn1, &ti, pDescr->sz);
csnStreamInit(&arT, bit_offset, remaining_bits_len, ar->pinfo);
Status = csnStreamDissector(test_tree, &arT, (const CSN_DESCR *)pDescr->descr.ptr, tvb, pvDATA(data, pDescr->offset), ett_csn1);
if (Status >= 0)
{
proto_item_set_len(ti,((arT.bit_offset-1)>>3) - (bit_offset>>3)+1);
remaining_bits_len = arT.remaining_bits_len;
bit_offset = arT.bit_offset;
}
else
{ /* return error code Has already been processed: */
return Status;
}
2011-02-28 09:43:22 +00:00
}
pDescr++;
2011-02-28 09:43:22 +00:00
break;
}
2011-02-28 09:43:22 +00:00
default:
{ /* descriptions of union elements other than above are illegal */
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_IN_SCRIPT, &ei_csn1_script_error, pDescr);
2011-02-28 09:43:22 +00:00
}
}
pDescr = pDescrNext;
break;
}
case CSN_EXIST:
case CSN_EXIST_LH:
{
guint8 fExist;
pui8 = pui8DATA(data, pDescr->offset);
if (CSN_EXIST_LH == pDescr->type)
{
fExist = tvb_get_masked_bits8(tvb, bit_offset, 1);
proto_tree_add_uint(tree, *(pDescr->hf_ptr), tvb, bit_offset>>3, 1, fExist);
2011-02-28 09:43:22 +00:00
}
else
{
fExist = tvb_get_bits8(tvb, bit_offset, 1);
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, 1, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
}
*pui8 = fExist;
remaining_bits_len --;
bit_offset++;
2011-02-28 09:43:22 +00:00
pDescr++;
if (!fExist)
{
ar->remaining_bits_len = remaining_bits_len;
ar->bit_offset = bit_offset;
return remaining_bits_len;
}
2011-02-28 09:43:22 +00:00
break;
}
case CSN_NEXT_EXIST:
{
guint8 isnull;
2011-02-28 09:43:22 +00:00
pui8 = pui8DATA(data, pDescr->offset);
/* this if-statement represents the M_NEXT_EXIST_OR_NULL description element */
if ((pDescr->may_be_null) && (remaining_bits_len == 0))
2011-02-28 09:43:22 +00:00
{ /* no more bits to decode is fine here - end of message detected and allowed */
2011-02-28 09:43:22 +00:00
/* Skip i entries + this entry */
pDescr += pDescr->i + 1;
2011-02-28 09:43:22 +00:00
/* Set the data member to "not exist" */
*pui8 = 0;
break;
}
2011-02-28 09:43:22 +00:00
/* the "regular" M_NEXT_EXIST description element */
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, 1, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
isnull = 1;
2011-02-28 09:43:22 +00:00
if (tvb_get_bits8(tvb, bit_offset, 1))
{
if (remaining_bits_len == 1)
{
/* If { 1 < end > } and all next items may be null, store it as { 0 } */
const CSN_DESCR* pDescrNext = pDescr + 1;
guint8 i;
for (i = 0; i < pDescr->i; i++, pDescrNext++)
{
if (!pDescrNext->may_be_null)
isnull = 0;
}
} else {
isnull = 0;
}
2011-02-28 09:43:22 +00:00
}
*pui8 = isnull ? 0 : 1;
2011-02-28 09:43:22 +00:00
remaining_bits_len --;
bit_offset++;
2011-02-28 09:43:22 +00:00
if (isnull)
2011-02-28 09:43:22 +00:00
{ /* Skip 'i' entries */
pDescr += pDescr->i;
}
pDescr++;
break;
}
case CSN_NEXT_EXIST_LH:
{
guint8 isnull;
2011-02-28 09:43:22 +00:00
pui8 = pui8DATA(data, pDescr->offset);
/* this if-statement represents the M_NEXT_EXIST_OR_NULL_LH description element */
if ((pDescr->descr.ptr != NULL) && (remaining_bits_len == 0))
2011-02-28 09:43:22 +00:00
{ /* no more bits to decode is fine here - end of message detected and allowed */
2011-02-28 09:43:22 +00:00
/* skip 'i' entries + this entry */
pDescr += pDescr->i + 1;
/* set the data member to "not exist" */
*pui8 = 0;
break;
}
/* the "regular" M_NEXT_EXIST_LH description element */
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, 1, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
isnull = 1;
if (tvb_get_masked_bits8(tvb, bit_offset, 1))
{
if (remaining_bits_len == 1) {
/* If { 1 < end > } and all next items may be null, store it as { 0 } */
const CSN_DESCR* pDescrNext = pDescr + 1;
guint8 i;
for (i = 0; i < pDescr->i; i++, pDescrNext++)
{
if (!pDescrNext->may_be_null)
isnull = 0;
}
} else {
isnull = 0;
}
}
2011-02-28 09:43:22 +00:00
*pui8++ = isnull ? 0 : 1;
2011-02-28 09:43:22 +00:00
remaining_bits_len --;
2011-02-28 09:43:22 +00:00
bit_offset++;
if (isnull)
2011-02-28 09:43:22 +00:00
{ /* Skip 'i' entries */
pDescr += pDescr->i;
}
pDescr++;
break;
}
case CSN_VARIABLE_BITMAP_1:
{ /* Bitmap from here and to the end of message */
*pui8DATA(data, (gint16)pDescr->descr.value) = (guint8) remaining_bits_len; /* length of bitmap == remaining bits */
2011-02-28 09:43:22 +00:00
/*no break -
2011-02-28 09:43:22 +00:00
* with a length set we have a regular variable length bitmap so we continue */
}
/* FALL THROUGH */
2011-02-28 09:43:22 +00:00
case CSN_VARIABLE_BITMAP:
{ /* {CSN_VARIABLE_BITMAP, 0, offsetof(_STRUCT, _ElementCountField), offsetof(_STRUCT, _MEMBER), #_MEMBER}
* <N: bit (5)> <bitmap: bit(N + offset)>
* Bit array with length (in bits) specified in parameter (pDescr->descr)
* The result is right aligned!
*/
gint16 no_of_bits = *pui8DATA(data, (gint16)pDescr->descr.value);
2011-02-28 09:43:22 +00:00
no_of_bits += pDescr->i; /* adjusted by offset */
while (no_of_bits > 0)
2011-02-28 09:43:22 +00:00
{
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, 1, ENC_BIG_ENDIAN);
bit_offset++;
no_of_bits--;
remaining_bits_len--;
2011-02-28 09:43:22 +00:00
if (remaining_bits_len < 0)
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
}
pDescr++;
break;
}
case CSN_LEFT_ALIGNED_VAR_BMP_1:
{ /* Bitmap from here and to the end of message */
*pui8DATA(data, (gint16)pDescr->descr.value) = (guint8) remaining_bits_len; /* length of bitmap == remaining bits */
/* no break -
2011-02-28 09:43:22 +00:00
* with a length set we have a regular left aligned variable length bitmap so we continue
*/
}
/* FALL THROUGH */
2011-02-28 09:43:22 +00:00
case CSN_LEFT_ALIGNED_VAR_BMP:
{ /* {CSN_LEFT_ALIGNED_VAR_BMP, _OFFSET, (void*)offsetof(_STRUCT, _ElementCountField), offsetof(_STRUCT, _MEMBER), #_MEMBER}
* <N: bit (5)> <bitmap: bit(N + offset)>
* bit array with length (in bits) specified in parameter (pDescr->descr)
*/
gint16 no_of_bits = *pui8DATA(data, (gint16)pDescr->descr.value);/* Size of bitmap */
2011-02-28 09:43:22 +00:00
no_of_bits += pDescr->i;/* size adjusted by offset */
if (no_of_bits > 0)
{
if (no_of_bits <= 32)
{
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
}
else if (no_of_bits <= 64)
{
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
}
else
{
proto_tree_add_uint64_format_value(tree, *(pDescr->hf_ptr), tvb, bit_offset>>3, ((bit_offset+no_of_bits-1)>>3)-(bit_offset>>3)+1, no_of_bits, "%u bits",
no_of_bits);
}
bit_offset += no_of_bits;
2011-02-28 09:43:22 +00:00
remaining_bits_len -= no_of_bits;
if (remaining_bits_len < 0)
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
}
/* bitmap was successfully extracted or it was empty */
pDescr++;
break;
}
case CSN_PADDING_BITS:
{ /* Padding from here and to the end of message */
if (remaining_bits_len > 0)
{
proto_tree *padding_tree = proto_tree_add_subtree(tree, tvb, bit_offset>>3, -1, ett_csn1, NULL, "Padding Bits");
while (remaining_bits_len > 0)
{
gint bits_to_handle = remaining_bits_len + (bit_offset%8);
if (bits_to_handle > 32)
{
bits_to_handle = 32 - (bit_offset%8);
}
else
{
bits_to_handle -= (bit_offset%8);
}
proto_tree_add_bits_item(padding_tree, *(pDescr->hf_ptr), tvb, bit_offset, bits_to_handle, ENC_BIG_ENDIAN);
bit_offset += bits_to_handle;
remaining_bits_len -= bits_to_handle;
}
}
if (remaining_bits_len < 0)
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
}
/* Padding was successfully extracted or it was empty */
pDescr++;
break;
}
2011-02-28 09:43:22 +00:00
case CSN_VARIABLE_ARRAY:
{ /* {int type; int i; void* descr; int offset; const char* sz; } CSN_DESCR;
* {CSN_VARIABLE_ARRAY, _OFFSET, (void*)offsetof(_STRUCT, _ElementCountField), offsetof(_STRUCT, _MEMBER), #_MEMBER}
* Array with length specified in parameter:
* <count: bit (x)>
* <list: octet(count + offset)>
*/
gint16 count = *pui8DATA(data, (gint16)pDescr->descr.value);
2011-02-28 09:43:22 +00:00
count += pDescr->i; /* Adjusted by offset */
if (count > 0)
{
pui8 = pui8DATA(data, pDescr->offset);
while (count > 0)
{
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, 8, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
*pui8++ = tvb_get_bits8(tvb, bit_offset, 8);
bit_offset += 8;
count--;
remaining_bits_len -= 8;
if (remaining_bits_len < 0)
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
}
2011-02-28 09:43:22 +00:00
}
}
pDescr++;
break;
}
case CSN_RECURSIVE_ARRAY:
{ /* Recursive way to specify an array: <list> ::= {1 <number: bit (4)> <list> | 0}
2011-02-28 09:43:22 +00:00
* or more generally: <list> ::= { <tag> <element> <list> | <EndTag> }
* where <element> ::= bit(value)
* <tag> ::= 0 | 1
* <EndTag> ::= reversed tag i.e. tag == 1 -> EndTag == 0 and vice versa
* {CSN_RECURSIVE_ARRAY, _BITS, (void*)offsetof(_STRUCT, _ElementCountField), offsetof(_STRUCT, _MEMBER), #_MEMBER}
* REMARK: recursive way to specify an array but an iterative implementation!
*/
gint16 no_of_bits = pDescr->i;
guint8 ElementCount = 0;
pui8 = pui8DATA(data, pDescr->offset);
while (existNextElement(tvb, bit_offset, Tag))
{ /* tag control shows existence of next list elements */
proto_tree_add_bits_item(tree, *(pDescr->hf_exist_ptr), tvb, bit_offset, 1, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
bit_offset++;
remaining_bits_len--;
/* extract and store no_of_bits long element from bitstream */
*pui8++ = tvb_get_bits8(tvb, bit_offset, no_of_bits);
ElementCount++;
if (remaining_bits_len < 0)
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
bit_offset += no_of_bits;
remaining_bits_len -= no_of_bits;
2011-02-28 09:43:22 +00:00
}
proto_tree_add_bits_item(tree, *(pDescr->hf_exist_ptr), tvb, bit_offset, 1, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
/* existNextElement() returned FALSE, 1 bit consumed */
bit_offset++;
remaining_bits_len--;
2011-02-28 09:43:22 +00:00
/* Store the counted number of elements of the array */
*pui8DATA(data, (gint16)pDescr->descr.value) = ElementCount;
2011-02-28 09:43:22 +00:00
pDescr++;
break;
}
case CSN_RECURSIVE_TARRAY:
{ /* Recursive way to specify an array of type: <lists> ::= { 1 <type> } ** 0 ;
* M_REC_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)
* {t, offsetof(_STRUCT, _ElementCountField), (void*)CSNDESCR_##_MEMBER_TYPE, offsetof(_STRUCT, _MEMBER), #_MEMBER, (StreamSerializeFcn_t)sizeof(_MEMBER_TYPE), NULL, NULL, (void_fn_t)ElementsOf(((_STRUCT*)0)->_MEMBER)}
2011-02-28 09:43:22 +00:00
*/
gint16 nSizeElement = (gint16)(gint32)pDescr->value;
guint32 nSizeArray = (guint32)((guintptr)pDescr->aux_fn);
2011-02-28 09:43:22 +00:00
guint8 ElementCount = 0;
while (existNextElement(tvb, bit_offset, Tag))
{ /* tag control shows existence of next list elements */
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, 1, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
/* existNextElement() returned TRUE, 1 bit consumed */
bit_offset++;
remaining_bits_len--;
ElementCount++;
if (ElementCount > nSizeArray)
{
/* error: too many elements in recursive array. Increase its size! */
return ProcessError(tree , ar->pinfo, tvb, bit_offset, CSN_ERROR_STREAM_NOT_SUPPORTED, &ei_csn1_stream_not_supported, pDescr);
}
2011-02-28 09:43:22 +00:00
{ /* unpack the following data structure */
csnStream_t arT = *ar;
gint16 Status;
proto_item *ti;
proto_tree *test_tree;
test_tree = proto_tree_add_subtree(tree, tvb, bit_offset>>3, 1, ett_csn1, &ti, pDescr->sz);
2011-02-28 09:43:22 +00:00
csnStreamInit(&arT, bit_offset, remaining_bits_len, ar->pinfo);
Status = csnStreamDissector(test_tree, &arT, (const CSN_DESCR *)pDescr->descr.ptr, tvb, pvDATA(data, pDescr->offset), ett_csn1);
2011-02-28 09:43:22 +00:00
if (Status >= 0)
{ /* successful completion */
pui8 += nSizeElement; /* -> to next data element */
proto_item_set_len(ti,((arT.bit_offset-1)>>3) - (bit_offset>>3)+1);
2011-02-28 09:43:22 +00:00
remaining_bits_len = arT.remaining_bits_len;
bit_offset = arT.bit_offset;
}
else
{ /* something went awry */
return Status;
}
}
if (remaining_bits_len < 0)
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
}
2011-02-28 09:43:22 +00:00
/* existNextElement() returned FALSE, 1 bit consumed */
bit_offset++;
remaining_bits_len --;
2011-02-28 09:43:22 +00:00
/* Store the counted number of elements of the array */
*pui8DATA(data, (gint16)(gint32)pDescr->i) = ElementCount;
pDescr++;
break;
}
case CSN_RECURSIVE_TARRAY_2:
2011-02-28 09:43:22 +00:00
{ /* Recursive way to specify an array of type: <list> ::= <type> { 0 <type> } ** 1 ; */
Tag = REVERSED_TAG;
/* NO break -
2011-02-28 09:43:22 +00:00
* handling is exactly the same as for CSN_RECURSIVE_TARRAY_1 so we continue
*/
}
/* FALL THROUGH */
case CSN_RECURSIVE_TARRAY_1:
2011-02-28 09:43:22 +00:00
{ /* Recursive way to specify an array of type: <lists> ::= <type> { 1 <type> } ** 0 ;
* M_REC_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)
* {t, offsetof(_STRUCT, _ElementCountField), (void*)CSNDESCR_##_MEMBER_TYPE, offsetof(_STRUCT, _MEMBER), #_MEMBER, (StreamSerializeFcn_t)sizeof(_MEMBER_TYPE), NULL, NULL, (void_fn_t)ElementsOf(((_STRUCT*)0)->_MEMBER)}
2011-02-28 09:43:22 +00:00
*/
gint16 nSizeElement = (gint16)(gint32)pDescr->value;
guint32 nSizeArray = (guint32)((guintptr)pDescr->aux_fn);
2011-02-28 09:43:22 +00:00
guint8 ElementCount = 0;
csnStream_t arT = *ar;
gboolean EndOfList = FALSE;
gint16 Status;
proto_item *ti;
proto_tree *test_tree;
do
{ /* get data element */
ElementCount++;
if (ElementCount >= nSizeArray)
{
/* error: too many elements in recursive array. Increase its size! */
return ProcessError(tree , ar->pinfo, tvb, bit_offset, CSN_ERROR_STREAM_NOT_SUPPORTED, &ei_csn1_stream_not_supported, pDescr);
}
test_tree = proto_tree_add_subtree_format(tree, tvb, bit_offset>>3, 1, ett_csn1, &ti, "%s[%d]", pDescr->sz, ElementCount-1);
csnStreamInit(&arT, bit_offset, remaining_bits_len, ar->pinfo);
Status = csnStreamDissector(test_tree, &arT, (const CSN_DESCR *)pDescr->descr.ptr, tvb, pvDATA(data, pDescr->offset), ett_csn1);
2011-02-28 09:43:22 +00:00
if (Status >= 0)
{ /* successful completion */
pui8 += nSizeElement; /* -> to next */
proto_item_set_len(ti,((arT.bit_offset-1)>>3) - (bit_offset>>3)+1);
2011-02-28 09:43:22 +00:00
remaining_bits_len = arT.remaining_bits_len;
bit_offset = arT.bit_offset;
}
else
{ /* something went awry */
return Status;
}
if (remaining_bits_len < 0)
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
2011-02-28 09:43:22 +00:00
}
/* control of next element's tag */
proto_tree_add_uint_bits_format_value(tree, *(pDescr->hf_ptr), tvb, bit_offset, 1, tvb_get_bits8(tvb, bit_offset, 1), ENC_BIG_ENDIAN, "%s[%d]",
proto_registrar_get_name(*(pDescr->hf_ptr)), ElementCount);
2011-02-28 09:43:22 +00:00
EndOfList = !(existNextElement(tvb, bit_offset, Tag));
bit_offset++;
remaining_bits_len--; /* 1 bit consumed (tag) */
} while (!EndOfList);
/* Store the count of the array */
*pui8DATA(data, pDescr->i) = ElementCount;
Tag = STANDARD_TAG; /* in case it was set to "reversed" */
pDescr++;
break;
}
case CSN_FIXED:
{ /* Verify the fixed bits */
guint8 no_of_bits = (guint8) pDescr->i;
guint32 ui32;
if (no_of_bits <= 8)
{
ui32 = tvb_get_bits8(tvb, bit_offset, no_of_bits);
}
else if (no_of_bits <= 16)
{
ui32 = tvb_get_bits16(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
}
else if (no_of_bits <= 32)
{
ui32 = tvb_get_bits32(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
}
else
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, -1, &ei_csn1_more32bits, pDescr);
}
2011-02-28 09:43:22 +00:00
if (ui32 != (unsigned)(gint32)pDescr->offset)
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, -1, &ei_csn1_fixed_not_matched, pDescr);
2011-02-28 09:43:22 +00:00
}
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN);
2011-02-28 09:43:22 +00:00
remaining_bits_len -= no_of_bits;
bit_offset += no_of_bits;
pDescr++;
break;
}
case CSN_CALLBACK:
{
guint16 no_of_bits;
DissectorCallbackFcn_t callback = (DissectorCallbackFcn_t)pDescr->aux_fn;
no_of_bits = callback(tree, tvb, pvDATA(data, pDescr->i), pvDATA(data, pDescr->offset), bit_offset, ett_csn1, ar->pinfo);
bit_offset += no_of_bits;
remaining_bits_len -= no_of_bits;
pDescr++;
2011-02-28 09:43:22 +00:00
break;
}
case CSN_TRAP_ERROR:
{
return ProcessError(tree, ar->pinfo, tvb, bit_offset, -1, pDescr->error, pDescr);
2011-02-28 09:43:22 +00:00
}
case CSN_END:
{
ar->remaining_bits_len = remaining_bits_len;
ar->bit_offset = bit_offset;
return remaining_bits_len;
}
default:
{
DISSECTOR_ASSERT(0);
}
}
} while (remaining_bits_len >= 0);
return ProcessError(tree, ar->pinfo, tvb, bit_offset, CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, &ei_csn1_more_bits_to_unpack, pDescr);
}
void
proto_register_csn1(void)
{
static hf_register_info hf[] = {
{ &hf_null_data,
{ "NULL data", "csn1.null_data",
FT_NONE, BASE_NONE, NULL, 0x00,
NULL, HFILL }
},
};
static ei_register_info ei[] = {
{ &ei_csn1_more_bits_to_unpack, { "csn1.more_bits_to_unpack", PI_MALFORMED, PI_ERROR, "NEED_MORE BITS TO UNPACK", EXPFILL }},
{ &ei_csn1_general, { "csn1.general_error", PI_PROTOCOL, PI_WARN, "General -1", EXPFILL }},
{ &ei_csn1_not_implemented, { "csn1.not_implemented", PI_UNDECODED, PI_WARN, "NOT IMPLEMENTED", EXPFILL }},
{ &ei_csn1_union_index, { "csn1.union_index_invalid", PI_PROTOCOL, PI_WARN, "INVALID UNION INDEX", EXPFILL }},
{ &ei_csn1_script_error, { "csn1.script_error", PI_MALFORMED, PI_ERROR, "ERROR IN SCRIPT", EXPFILL }},
{ &ei_csn1_more32bits, { "csn1.more32bits", PI_PROTOCOL, PI_WARN, "no_of_bits > 32", EXPFILL }},
{ &ei_csn1_fixed_not_matched, { "csn1.fixed_not_matched", PI_PROTOCOL, PI_WARN, "FIXED value does not match", EXPFILL }},
csn1: fix csnStreamDissector(): catch unknown CSN_CHOICE values Some CSN.1 definitions may contain so-called unions that usually combine two or more choices. The exact element to be chosen is determined by the value encoded in one or more bits preceeding it. Here is an example of an identity union: { 0 < Global TFI : < Global TFI IE > > | 10 < TLLI / G-RNTI : bit (32) > | 110 < TQI : bit (16) > } So if a given bitstream starts with '0'B, the Global TFI IE follows. Otherwise either TLLI / G-RNTI or TQI is to be chosen. But what if neither of the choice items matches? For example, what if a given bitstream starts with '111'B? Most likely we should treat the bitstream as malformed, stop further decoding and report an error. And that's how Pycrate's [1] CSN.1 decoder [2] behaves. Hovewer, as it turns out, Wireshark would simply skip the whole choice element and start decoding the next one from the same bit position. Here is an example of a malformed packet: GSM RLC/MAC: PACKET_POLLING_REQUEST (4) (Downlink) 01.. .... = Payload Type (DL): RLC/MAC block contains an RLC/MAC control block that does not include the optional octets of the RLC/MAC control header (1) ..00 .... = RRBP: Reserved Block: (N+13) mod 2715648 (0) .... 1... = S/P: RRBP field is valid .... .001 = USF: 1 PACKET_POLLING_REQUEST (4) (downlink) 0001 00.. = MESSAGE_TYPE (DL): PACKET_POLLING_REQUEST (4) .... ..11 = PAGE_MODE: Same as before (3) ---! ID <--- This is wrong! '111'B is unknown 1... .... = CONTROL_ACK_TYPE: PACKET CONTROL ACKNOWLEDGEMENT message format shall be an RLC/MAC control block Padding Bits .110 0000 0000 1000 0101 0000 1000 1000 = Padding: 1611157640 0100 0000 0001 0011 1010 1000 0000 0100 = Padding: 1075030020 1000 1011 0010 1011 0010 1011 0010 1011 = Padding: 2334862123 0010 1011 0010 1011 0010 1011 0010 1011 = Padding: 724249387 0010 1011 0010 1011 0010 1011 0010 1011 = Padding: 724249387 0010 1011 = Padding: 43 Let's fix this, so after this patch we get: GSM RLC/MAC: PACKET_POLLING_REQUEST (4) (Downlink) 01.. .... = Payload Type (DL): RLC/MAC block contains an RLC/MAC control block that does not include the optional octets of the RLC/MAC control header (1) ..00 .... = RRBP: Reserved Block: (N+13) mod 2715648 (0) .... 1... = S/P: RRBP field is valid .... .001 = USF: 1 PACKET_POLLING_REQUEST (4) (downlink) 0001 00.. = MESSAGE_TYPE (DL): PACKET_POLLING_REQUEST (4) .... ..11 = PAGE_MODE: Same as before (3) ID STREAM NOT SUPPORTED (PacketPollingID) [Expert Info (Warning/Protocol): STREAM NOT SUPPORTED (PacketPollingID)] [STREAM NOT SUPPORTED (PacketPollingID)] [Severity level: Warning] [Group: Protocol] [1] https://github.com/P1sec/pycrate [2] https://github.com/P1sec/pycrate/wiki/Using-the-pycrate-csn1-translator-and-runtime Change-Id: I7096c294e0d04d6afb3414874d3404cbb637fdae Reviewed-on: https://code.wireshark.org/review/36077 Reviewed-by: Pau Espin Pedrol <pespin@sysmocom.de> Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
2020-02-11 08:56:28 +00:00
{ &ei_csn1_stream_not_supported, { "csn1.stream_not_supported", PI_PROTOCOL, PI_WARN, "STREAM NOT SUPPORTED", EXPFILL }},
};
expert_module_t* expert_csn1;
proto_csn1 = proto_register_protocol("CSN.1", "CSN1", "csn1");
proto_register_field_array(proto_csn1, hf, array_length(hf));
expert_csn1 = expert_register_protocol(proto_csn1);
expert_register_field_array(expert_csn1, ei, array_length(ei));
proto_set_cant_toggle(proto_csn1);
2011-02-28 09:43:22 +00:00
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local Variables:
* c-basic-offset: 2
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=2 tabstop=8 expandtab:
* :indentSize=2:tabSize=8:noTabs=true:
*/