Compare commits

...

3 Commits

Author SHA1 Message Date
Pau Espin 3c341df6d2 WIP: gm: Decode GSM GM MS RA Capabilities IE with RLCMAC CSN1 decoder
Tested with a sample pcap file containing an GSM GM packet (Attach
Request)  with an MS Radio Access Capabilities IE containing several entries.

TODO:
* Check if by dropping the ad-hoc decoder we lose support/features.
* Drop de_gmm_ms_radio_acc_cap and replace it with
  de_gmm_ms_radio_acc_cap_rlcmac(), since there are other users of that
  function in other protocol dissectors. Then all benefit from the
  change. More than 1k lines of code can be dropped.
* Some general clean up required

Change-Id: I096eafcb5ca31d0ad1fa63561f43853ee4e7a40f
2021-10-20 17:55:39 +02:00
Pau Espin 902531bd81 csn1: Avoid storing existence bit as true if content was actually NULL
If we decode Exist bit as "1" but we are at the end of the message, and
all the Next items we'd read are expected to be possibly NULL, then swap
the Exist bit in the decoded structure as "0" in order to tell the
decoder user that the related information structure is actually unset,
as if "0" was received.

This patch is a port from patch fixing same issue in the osmo-pcu.git copy of
csn1 decoder:
https://git.osmocom.org/osmo-pcu/commit/?id=1859ec38cc4f4e3788e495a100fdec3787d25020
And fixup patch for that one:
https://git.osmocom.org/osmo-pcu/commit/?id=9ecdc11eb6b983748ae2fd6a1d07849c8106826f
2021-10-20 17:55:28 +02:00
Pau Espin 23289dc2f7 csn1: Avoid failing if optional DownlinkDualCarrierCapability_r7 is missing
All additional release fields in RadioAccesCapabilities are considered
optional, and the CSN_DESCR for Content_t already marks almost all as such,
except DownlinkDualCarrierCapability_r7.

It has been found that some MS transmits a MS RA Capability with a Length=61 bits
where the last bit in the buffer is setting the Exist bit for
DownlinkDualCarrierCapability_r7 as 1. Hence, the CSN1 decoder failed to
decode the whole message because it expected to keep reading there
despite there's no more bytes to read.

While this is could actually be considered an MS bug, let's relax our
expectancies and simply consider the case { 1 <end> } as it was { 0 },
and mark skip decoding DownlinkDualCarrierCapability_r7. That what
wireshark (packet-gsm_a_gsm.c) or pycrate do for instance.

This patch itself doesn't fix the problem where actually the Exist bit
is stored as 1 in the output decoded structure, but simply allows keep
ongoing with decoding until the end. This issue will be fixed in a
follow-up patch.

This patch is a port from patch fixing same issue in the osmo-pcu.git copy of
csn1 decoder:
https://git.osmocom.org/osmo-pcu/commit/?id=ebdc0d8c170ee2dbf23b19056d6c2d0ef316b3c2
2021-10-20 17:45:32 +02:00
4 changed files with 112 additions and 44 deletions

View File

@ -531,25 +531,26 @@ csnStreamDissector(proto_tree *tree, csnStream_t* ar, const CSN_DESCR* pDescr, t
csnStream_t arT = *ar;
proto_item *ti;
proto_tree *test_tree;
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)
if (pDescr->may_be_null && remaining_bits_len == 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;
pDescr++;
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;
}
}
else
{
/* Has already been processed: ProcessError("csnStreamDissector", Status, pDescr); */
return Status;
}
pDescr++;
break;
}
@ -1002,22 +1003,25 @@ csnStreamDissector(proto_tree *tree, csnStream_t* ar, const CSN_DESCR* pDescr, t
proto_item *ti;
proto_tree *test_tree;
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)
if (pDescr->may_be_null && remaining_bits_len == 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;
pDescr++;
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;
}
}
else
{ /* return error code Has already been processed: */
return Status;
}
pDescr++;
break;
}
@ -1066,7 +1070,7 @@ csnStreamDissector(proto_tree *tree, csnStream_t* ar, const CSN_DESCR* pDescr, t
case CSN_NEXT_EXIST:
{
guint8 fExist;
guint8 isnull;
pui8 = pui8DATA(data, pDescr->offset);
@ -1085,18 +1089,30 @@ csnStreamDissector(proto_tree *tree, csnStream_t* ar, const CSN_DESCR* pDescr, t
/* the "regular" M_NEXT_EXIST description element */
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, 1, ENC_BIG_ENDIAN);
fExist = 0x00;
isnull = 1;
if (tvb_get_bits8(tvb, bit_offset, 1))
{
fExist = 0x01;
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;
}
}
*pui8 = fExist;
*pui8 = !isnull;
remaining_bits_len --;
bit_offset++;
if (fExist == 0)
if (isnull)
{ /* Skip 'i' entries */
pDescr += pDescr->i;
}
@ -1107,7 +1123,7 @@ csnStreamDissector(proto_tree *tree, csnStream_t* ar, const CSN_DESCR* pDescr, t
case CSN_NEXT_EXIST_LH:
{
guint8 fExist;
guint8 isnull;
pui8 = pui8DATA(data, pDescr->offset);
/* this if-statement represents the M_NEXT_EXIST_OR_NULL_LH description element */
@ -1125,14 +1141,29 @@ csnStreamDissector(proto_tree *tree, csnStream_t* ar, const CSN_DESCR* pDescr, t
/* the "regular" M_NEXT_EXIST_LH description element */
proto_tree_add_bits_item(tree, *(pDescr->hf_ptr), tvb, bit_offset, 1, ENC_BIG_ENDIAN);
fExist = tvb_get_masked_bits8(tvb, bit_offset, 1);
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;
}
}
*pui8++ = fExist;
*pui8++ = !isnull;
remaining_bits_len --;
bit_offset++;
if (fExist == 0)
if (isnull)
{ /* Skip 'i' entries */
pDescr += pDescr->i;
}

View File

@ -477,6 +477,17 @@ gint16 csnStreamDissector(proto_tree *tree, csnStream_t* ar, const CSN_DESCR* pD
#define M_TYPE(_STRUCT, _MEMBER, _MEMBER_TYPE)\
{CSN_TYPE, 0, {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, NULL, 0, NULL, NULL, NULL}
/******************************************************************************
* M_TYPE_OR_NULL(Par1, Par2, Par3)
* Similar to the M_TYPE except that not only the request set of bits but also the
* end of the message may be encountered when looking for the next element in
* the message.
* Covers the case {null | 0 | 1 < IE >}
*****************************************************************************/
#define M_TYPE_OR_NULL(_STRUCT, _MEMBER, _MEMBER_TYPE)\
{CSN_TYPE, 0, {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, NULL, 0, NULL, NULL, NULL}
/******************************************************************************
* M_TYPE_LABEL(Par1, Par2, Par3, Par4)
* Same as M_TYPE but allows to define a custom string for the subtree

View File

@ -91,6 +91,7 @@
#include <epan/expert.h>
#include <epan/ipproto.h>
#include <epan/etypes.h>
#include "packet-gsm_rlcmac.h"
#include "packet-ber.h"
#include "packet-gsm_a_common.h"
#include "packet-e212.h"
@ -609,6 +610,7 @@ static expert_field ei_gsm_a_gm_missing_mandatory_element = EI_INIT;
static dissector_handle_t rrc_irat_ho_info_handle;
static dissector_handle_t lte_rrc_ue_eutra_cap_handle;
static dissector_handle_t nbifom_handle;
static dissector_handle_t rlcmac_racap_handle;
static dissector_table_t gprs_sm_pco_subdissector_table; /* GPRS SM PCO PPP Protocols */
@ -1663,6 +1665,16 @@ static const value_string gsm_a_gm_ec_pch_mon_support_vals[] = {
{0, NULL}
};
guint16
de_gmm_ms_radio_acc_cap_rlcmac(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_)
{
tvbuff_t *payload_tvb;
payload_tvb = tvb_new_subset_length(tvb, offset, len);
call_dissector(rlcmac_racap_handle, payload_tvb, pinfo, tree);
return len;
}
guint16
de_gmm_ms_radio_acc_cap(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_)
{
@ -6372,7 +6384,7 @@ guint16 (*gm_elem_fcn[])(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_
de_gmm_imeisv_req, /* IMEISV Request */
de_gmm_rec_npdu_lst, /* Receive N-PDU Numbers List */
de_gmm_ms_net_cap, /* MS Network Capability */
de_gmm_ms_radio_acc_cap, /* MS Radio Access Capability */
de_gmm_ms_radio_acc_cap_rlcmac, /* MS Radio Access Capability */
de_gmm_cause, /* GMM Cause */
de_gmm_rai, /* Routing Area Identification */
de_gmm_rai2, /* Routing Area Identification 2 */
@ -9681,6 +9693,7 @@ proto_reg_handoff_gsm_a_gm(void)
rrc_irat_ho_info_handle = find_dissector_add_dependency("rrc.irat.irat_ho_info", proto_a_gm);
lte_rrc_ue_eutra_cap_handle = find_dissector_add_dependency("lte-rrc.ue_eutra_cap", proto_a_gm);
nbifom_handle = find_dissector_add_dependency("nbifom", proto_a_gm);
rlcmac_racap_handle = find_dissector_add_dependency("gsm_rlcmac_racap", proto_a_gm);
}
/*

View File

@ -2750,7 +2750,7 @@ CSN_DESCR_BEGIN (Content_t)
/* additions in release 7 */
M_UINT_OR_NULL (Content_t, DTM_Handover_Capability, 1, &hf_content_dtm_handover_capability),
M_NEXT_EXIST_OR_NULL(Content_t, Exist_DownlinkDualCarrierCapability_r7, 1, &hf_content_multislot_capability_reduction_for_dl_dual_carrier_exist),
M_TYPE (Content_t, DownlinkDualCarrierCapability_r7, DownlinkDualCarrierCapability_r7_t),
M_TYPE_OR_NULL (Content_t, DownlinkDualCarrierCapability_r7, DownlinkDualCarrierCapability_r7_t),
M_UINT_OR_NULL (Content_t, FlexibleTimeslotAssignment, 1, &hf_content_flexible_timeslot_assignment),
M_UINT_OR_NULL (Content_t, GAN_PS_HandoverCapability, 1, &hf_content_gan_ps_handover_capability),
@ -2819,13 +2819,11 @@ CSN_DESCR_END (MS_RA_capability_value_t)
* This one would be used to decode for instance MS RA Capabilities IE SGSN->MS on the PCU.
* However, an ad-hoc decoder is used in this scenario in wireshark: See packet-gsm_a_gm.c de_gmm_ms_radio_acc_cap().
*/
#if 0
static const
CSN_DESCR_BEGIN (MS_Radio_Access_capability_t)
M_REC_TARRAY_1(MS_Radio_Access_capability_t, MS_RA_capability_value, MS_RA_capability_value_t, Count_MS_RA_capability_value, &hf_ms_ra_capability_value),
M_PADDING_BITS(MS_Radio_Access_capability_t, &hf_padding),
CSN_DESCR_END (MS_Radio_Access_capability_t)
#endif
/* TS44.060 section 12.30 "MS Radio Access Capability 2". Same as above but without spare bits */
static const
@ -9903,6 +9901,20 @@ dissect_gsm_ec_rlcmac_uplink(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree
return dissect_gsm_rlcmac_uplink(tvb, pinfo, tree, &rlc_mac);
}
static int
dissect_gsm_rlcmac_racap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
csnStream_t ar;
MS_Radio_Access_capability_t racap = {0};
int len = tvb_reported_length(tvb);
guint16 bit_length = len * 8;
csnStreamInit(&ar, 0, bit_length, pinfo);
csnStreamDissector(tree, &ar, CSNDESCR(MS_Radio_Access_capability_t), tvb, &racap, ett_gsm_rlcmac);
return len;
}
void
proto_register_gsm_rlcmac(void)
{
@ -18620,6 +18632,7 @@ proto_register_gsm_rlcmac(void)
proto_register_subtree_array(ett, array_length(ett));
expert_gsm_rlcmac = expert_register_protocol(proto_gsm_rlcmac);
expert_register_field_array(expert_gsm_rlcmac, ei, array_length(ei));
register_dissector("gsm_rlcmac_racap", dissect_gsm_rlcmac_racap, proto_gsm_rlcmac);
register_dissector("gsm_rlcmac_ul", dissect_gsm_rlcmac_uplink, proto_gsm_rlcmac);
register_dissector("gsm_rlcmac_dl", dissect_gsm_rlcmac_downlink, proto_gsm_rlcmac);
register_dissector("gsm_ec_rlcmac_ul", dissect_gsm_ec_rlcmac_uplink, proto_gsm_rlcmac);