gsmtap: Support dissection of non CS-1 (E)GPRS data blocks

Work based on current code from epan/dissectors/packet-gsm_abis_pgsl.c,
as well as on initial patch from Holger Hans Peter Freyther [1].

Tested with one downlink MCS1 data block generated by osmo-pcu.

[1] https://osmocom.org/issues/1542

Change-Id: I01a8bd1cdb78d1c236a451fbee37854eb688fa14
Reviewed-on: https://code.wireshark.org/review/36489
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Pau Espin 2020-03-18 18:51:43 +01:00 committed by Anders Broman
parent 378ecc27df
commit dcd65a4012
1 changed files with 217 additions and 1 deletions

View File

@ -3,6 +3,7 @@
*
* (C) 2008-2013 by Harald Welte <laforge@gnumonks.org>
* (C) 2011 by Holger Hans Peter Freyther
* (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
@ -34,6 +35,7 @@
#include <epan/packet.h>
#include <epan/conversation.h>
#include <epan/dissectors/packet-gsm_rlcmac.h>
#include "packet-gsmtap.h"
#include "packet-lapdm.h"
@ -555,6 +557,216 @@ handle_tetra(int channel, tvbuff_t *payload_tvb, packet_info *pinfo, proto_tree
tetra_dissect_pdu(tetra_chan, TETRA_DOWNLINK, payload_tvb, tree, pinfo);
}
/* length of an EGPRS RLC data block for given MCS */
static const guint data_block_len_by_mcs[] = {
0, /* MCS0 */
22, /* MCS1 */
28,
37,
44,
56,
74,
56,
68,
74, /* MCS9 */
0, /* MCS_INVALID */
};
/* determine the number of rlc data blocks and their size / offsets */
static void
setup_rlc_mac_priv(RlcMacPrivateData_t *rm, gboolean is_uplink,
guint *n_calls, guint *data_block_bits, guint *data_block_offsets)
{
guint nc, dbl = 0, dbo[2] = {0,0};
dbl = data_block_len_by_mcs[rm->mcs];
switch (rm->block_format) {
case RLCMAC_HDR_TYPE_1:
nc = 3;
dbo[0] = is_uplink ? 5*8 + 6 : 5*8 + 0;
dbo[1] = dbo[0] + dbl * 8 + 2;
break;
case RLCMAC_HDR_TYPE_2:
nc = 2;
dbo[0] = is_uplink ? 4*8 + 5 : 3*8 + 4;
break;
case RLCMAC_HDR_TYPE_3:
nc = 2;
dbo[0] = 3*8 + 7;
break;
default:
nc = 1;
break;
}
*n_calls = nc;
*data_block_bits = dbl * 8 + 2;
data_block_offsets[0] = dbo[0];
data_block_offsets[1] = dbo[1];
}
/* bit-shift the entire 'src' of length 'length_bytes' by 'offset_bits'
* and store the reuslt to caller-allocated 'buffer'. The shifting is
* done lsb-first, unlike tvb_new_octet_aligned() */
static void clone_aligned_buffer_lsbf(guint offset_bits, guint length_bytes,
const guint8 *src, guint8 *buffer)
{
guint hdr_bytes;
guint extra_bits;
guint i;
guint8 c, last_c;
guint8 *dst;
hdr_bytes = offset_bits / 8;
extra_bits = offset_bits % 8;
if (extra_bits == 0) {
/* It is aligned already */
memmove(buffer, src + hdr_bytes, length_bytes);
return;
}
dst = buffer;
src = src + hdr_bytes;
last_c = *(src++);
for (i = 0; i < length_bytes; i++) {
c = src[i];
*(dst++) = (last_c >> extra_bits) | (c << (8 - extra_bits));
last_c = c;
}
}
/* obtain an (aligned) EGPRS data block with given bit-offset and
* bit-length from the parent TVB */
static tvbuff_t *get_egprs_data_block(tvbuff_t *tvb, guint offset_bits,
guint length_bits, packet_info *pinfo)
{
tvbuff_t *aligned_tvb;
const guint initial_spare_bits = 6;
guint8 *aligned_buf;
guint min_src_length_bytes = (offset_bits + length_bits + 7) / 8;
guint length_bytes = (initial_spare_bits + length_bits + 7) / 8;
tvb_ensure_bytes_exist(tvb, 0, min_src_length_bytes);
aligned_buf = (guint8 *) wmem_alloc(pinfo->pool, length_bytes);
/* Copy the data out of the tvb to an aligned buffer */
clone_aligned_buffer_lsbf(
offset_bits - initial_spare_bits, length_bytes,
tvb_get_ptr(tvb, 0, min_src_length_bytes),
aligned_buf);
/* clear spare bits and move block header bits to the right */
aligned_buf[0] = aligned_buf[0] >> initial_spare_bits;
aligned_tvb = tvb_new_child_real_data(tvb, aligned_buf,
length_bytes, length_bytes);
add_new_data_source(pinfo, aligned_tvb, "Aligned EGPRS data bits");
return aligned_tvb;
}
static void tvb_len_get_mcs_and_fmt(guint len, gboolean is_uplink, guint *frm, guint8 *mcs)
{
if (len <= 5 && is_uplink) {
/* Assume random access burst */
*frm = RLCMAC_PRACH;
*mcs = 0;
return;
}
switch (len)
{
case 23: *frm = RLCMAC_CS1; *mcs = 0; break;
case 34: *frm = RLCMAC_CS2; *mcs = 0; break;
case 40: *frm = RLCMAC_CS3; *mcs = 0; break;
case 54: *frm = RLCMAC_CS4; *mcs = 0; break;
case 27: *frm = RLCMAC_HDR_TYPE_3; *mcs = 1; break;
case 33: *frm = RLCMAC_HDR_TYPE_3; *mcs = 2; break;
case 42: *frm = RLCMAC_HDR_TYPE_3; *mcs = 3; break;
case 49: *frm = RLCMAC_HDR_TYPE_3; *mcs = 4; break;
case 60: /* fall through */
case 61: *frm = RLCMAC_HDR_TYPE_2; *mcs = 5; break;
case 78: /* fall through */
case 79: *frm = RLCMAC_HDR_TYPE_2; *mcs = 6; break;
case 118: /* fall through */
case 119: *frm = RLCMAC_HDR_TYPE_1; *mcs = 7; break;
case 142: /* fall through */
case 143: *frm = RLCMAC_HDR_TYPE_1; *mcs = 8; break;
case 154: /* fall through */
case 155: *frm = RLCMAC_HDR_TYPE_1; *mcs = 9; break;
default: *frm = RLCMAC_CS1; *mcs = 0; break; /* TODO: report error instead */
}
}
static void
handle_rlcmac(guint32 frame_nr, tvbuff_t *payload_tvb, packet_info *pinfo, proto_tree *tree)
{
int sub_handle;
RlcMacPrivateData_t rlcmac_data = {0};
tvbuff_t *data_tvb;
guint data_block_bits, data_block_offsets[2];
guint num_calls;
gboolean is_uplink;
if (pinfo->p2p_dir == P2P_DIR_SENT) {
is_uplink = 1;
sub_handle = GSMTAP_SUB_UM_RLC_MAC_UL;
} else {
is_uplink = 0;
sub_handle = GSMTAP_SUB_UM_RLC_MAC_DL;
}
rlcmac_data.magic = GSM_RLC_MAC_MAGIC_NUMBER;
rlcmac_data.frame_number = frame_nr;
tvb_len_get_mcs_and_fmt(tvb_reported_length(payload_tvb), is_uplink,
(guint *) &rlcmac_data.block_format,
(guint8 *) &rlcmac_data.mcs);
switch (rlcmac_data.block_format) {
case RLCMAC_HDR_TYPE_1:
case RLCMAC_HDR_TYPE_2:
case RLCMAC_HDR_TYPE_3:
/* First call of RLC/MAC dissector for header */
call_dissector_with_data(sub_handles[sub_handle], payload_tvb,
pinfo, tree, (void *) &rlcmac_data);
/* now determine how to proceed for data */
setup_rlc_mac_priv(&rlcmac_data, is_uplink,
&num_calls, &data_block_bits, data_block_offsets);
/* and call dissector one or two time for the data blocks */
if (num_calls >= 2) {
rlcmac_data.flags = GSM_RLC_MAC_EGPRS_BLOCK1;
data_tvb = get_egprs_data_block(payload_tvb, data_block_offsets[0],
data_block_bits, pinfo);
call_dissector_with_data(sub_handles[sub_handle], data_tvb, pinfo, tree,
(void *) &rlcmac_data);
}
if (num_calls == 3) {
rlcmac_data.flags = GSM_RLC_MAC_EGPRS_BLOCK2;
data_tvb = get_egprs_data_block(payload_tvb, data_block_offsets[1],
data_block_bits, pinfo);
call_dissector_with_data(sub_handles[sub_handle], data_tvb, pinfo, tree,
(void *) &rlcmac_data);
}
break;
default:
/* regular GPRS CS doesn't need any
* shifting/re-alignment or even separate calls for
* header and data blocks. We simply call the dissector
* as-is */
call_dissector_with_data(sub_handles[sub_handle], payload_tvb, pinfo, tree,
(void *) &rlcmac_data);
}
}
/* dissect a GSMTAP header and hand payload off to respective dissector */
static int
dissect_gsmtap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
@ -565,6 +777,7 @@ dissect_gsmtap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _
tvbuff_t *payload_tvb, *l1h_tvb = NULL;
guint8 hdr_len, type, sub_type, timeslot, subslot;
guint16 arfcn;
guint32 frame_nr;
len = tvb_reported_length(tvb);
@ -572,6 +785,7 @@ dissect_gsmtap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _
type = tvb_get_guint8(tvb, offset + 2);
timeslot = tvb_get_guint8(tvb, offset + 3);
arfcn = tvb_get_ntohs(tvb, offset + 4);
frame_nr = tvb_get_ntohl(tvb, offset + 8);
sub_type = tvb_get_guint8(tvb, offset + 12);
subslot = tvb_get_guint8(tvb, offset + 14);
@ -736,7 +950,6 @@ dissect_gsmtap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _
handle_lapdm(sub_type, payload_tvb, pinfo, tree);
return tvb_captured_length(tvb);
case GSMTAP_CHANNEL_PACCH:
case GSMTAP_CHANNEL_PDTCH:
if (pinfo->p2p_dir == P2P_DIR_SENT) {
sub_handle = GSMTAP_SUB_UM_RLC_MAC_UL;
}
@ -745,6 +958,9 @@ dissect_gsmtap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _
sub_handle = GSMTAP_SUB_UM_RLC_MAC_DL;
}
break;
case GSMTAP_CHANNEL_PDTCH:
handle_rlcmac(frame_nr, payload_tvb, pinfo, tree);
return tvb_captured_length(tvb);
/* See 3GPP TS 45.003, section 5.2 "Packet control channels" */
case GSMTAP_CHANNEL_PTCCH:
/* PTCCH/D carries Timing Advance updates encoded with CS-1 */