forked from osmocom/wireshark
RLC-NR: Attempt reassembly of UM PDUs.
Controlled by a preference (off by default). Change-Id: If2fafb1d0b94faf4e42c3e9bb4bef010f1a9be0b Reviewed-on: https://code.wireshark.org/review/33056 Reviewed-by: Martin Mathieson <martin.r.mathieson@googlemail.com>
This commit is contained in:
parent
ccf9820ea7
commit
736052b3f1
|
@ -16,6 +16,8 @@
|
|||
#include <epan/expert.h>
|
||||
#include <epan/prefs.h>
|
||||
#include <epan/proto_data.h>
|
||||
#include <epan/reassemble.h>
|
||||
|
||||
#include "packet-rlc-nr.h"
|
||||
#include "packet-pdcp-nr.h"
|
||||
|
||||
|
@ -26,7 +28,8 @@
|
|||
|
||||
/* TODO:
|
||||
- add sequence analysis
|
||||
- add reassembly
|
||||
- add AM reassembly
|
||||
- take configuration of reordering timer, and stop reassembly if timeout exceeded?
|
||||
- add tap info
|
||||
- call more upper layer dissectors once they appear
|
||||
*/
|
||||
|
@ -58,6 +61,9 @@ static gboolean global_rlc_nr_call_rrc_for_ccch = TRUE;
|
|||
/* Preference to expect RLC headers without payloads */
|
||||
static gboolean global_rlc_nr_headers_expected = FALSE;
|
||||
|
||||
/* Attempt reassembly of UM frames. TODO: if add AM reassembly, might prefer just one preference? */
|
||||
static gboolean global_rlc_nr_reassemble_um_pdus = FALSE;
|
||||
|
||||
/* Tree storing UE related parameters */
|
||||
typedef struct rlc_ue_parameters {
|
||||
guint32 id;
|
||||
|
@ -127,11 +133,46 @@ static int hf_rlc_nr_am_nacks = -1;
|
|||
|
||||
static int hf_rlc_nr_header_only = -1;
|
||||
|
||||
static int hf_rlc_nr_fragments = -1;
|
||||
static int hf_rlc_nr_fragment = -1;
|
||||
static int hf_rlc_nr_fragment_overlap = -1;
|
||||
static int hf_rlc_nr_fragment_overlap_conflict = -1;
|
||||
static int hf_rlc_nr_fragment_multiple_tails = -1;
|
||||
static int hf_rlc_nr_fragment_too_long_fragment = -1;
|
||||
static int hf_rlc_nr_fragment_error = -1;
|
||||
static int hf_rlc_nr_fragment_count = -1;
|
||||
static int hf_rlc_nr_reassembled_in = -1;
|
||||
static int hf_rlc_nr_reassembled_length = -1;
|
||||
static int hf_rlc_nr_reassembled_data = -1;
|
||||
|
||||
|
||||
|
||||
/* Subtrees. */
|
||||
static int ett_rlc_nr = -1;
|
||||
static int ett_rlc_nr_context = -1;
|
||||
static int ett_rlc_nr_um_header = -1;
|
||||
static int ett_rlc_nr_am_header = -1;
|
||||
static int ett_rlc_nr_fragments = -1;
|
||||
static int ett_rlc_nr_fragment = -1;
|
||||
|
||||
|
||||
static const fragment_items rlc_nr_frag_items = {
|
||||
&ett_rlc_nr_fragment,
|
||||
&ett_rlc_nr_fragments,
|
||||
&hf_rlc_nr_fragments,
|
||||
&hf_rlc_nr_fragment,
|
||||
&hf_rlc_nr_fragment_overlap,
|
||||
&hf_rlc_nr_fragment_overlap_conflict,
|
||||
&hf_rlc_nr_fragment_multiple_tails,
|
||||
&hf_rlc_nr_fragment_too_long_fragment,
|
||||
&hf_rlc_nr_fragment_error,
|
||||
&hf_rlc_nr_fragment_count,
|
||||
&hf_rlc_nr_reassembled_in,
|
||||
&hf_rlc_nr_reassembled_length,
|
||||
&hf_rlc_nr_reassembled_data,
|
||||
"RLC PDU fragments"
|
||||
};
|
||||
|
||||
|
||||
static expert_field ei_rlc_nr_context_mode = EI_INIT;
|
||||
static expert_field ei_rlc_nr_am_nack_sn = EI_INIT;
|
||||
|
@ -236,6 +277,49 @@ static const true_false_string header_only_vals =
|
|||
"RLC PDU Headers and body present"
|
||||
};
|
||||
|
||||
/* Reassembly state */
|
||||
static reassembly_table pdu_reassembly_table;
|
||||
|
||||
static guint pdu_hash(gconstpointer k _U_)
|
||||
{
|
||||
return GPOINTER_TO_UINT(k);
|
||||
}
|
||||
|
||||
static gint pdu_equal(gconstpointer k1, gconstpointer k2)
|
||||
{
|
||||
return k1 == k2;
|
||||
}
|
||||
|
||||
static gpointer pdu_temporary_key(const packet_info *pinfo _U_, const guint32 id _U_, const void *data _U_)
|
||||
{
|
||||
return (gpointer)data;
|
||||
}
|
||||
|
||||
static gpointer pdu_persistent_key(const packet_info *pinfo _U_, const guint32 id _U_,
|
||||
const void *data)
|
||||
{
|
||||
return (gpointer)data;
|
||||
}
|
||||
|
||||
static void pdu_free_temporary_key(gpointer ptr _U_)
|
||||
{
|
||||
}
|
||||
|
||||
static void pdu_free_persistent_key(gpointer ptr _U_)
|
||||
{
|
||||
}
|
||||
|
||||
reassembly_table_functions pdu_reassembly_table_functions =
|
||||
{
|
||||
pdu_hash,
|
||||
pdu_equal,
|
||||
pdu_temporary_key,
|
||||
pdu_persistent_key,
|
||||
pdu_free_temporary_key,
|
||||
pdu_free_persistent_key
|
||||
};
|
||||
|
||||
|
||||
/********************************************************/
|
||||
/* Forward declarations & functions */
|
||||
static void dissect_rlc_nr_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean is_udp_framing);
|
||||
|
@ -305,21 +389,21 @@ static void show_PDU_in_info(packet_info *pinfo,
|
|||
|
||||
|
||||
/* Show an SDU. If configured, pass to PDCP/RRC dissector */
|
||||
static void show_PDU_in_tree(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, gint offset, gint length,
|
||||
rlc_nr_info *rlc_info, guint32 seg_info _U_)
|
||||
static void show_PDU_in_tree(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, gint offset, gint length,
|
||||
rlc_nr_info *rlc_info, guint32 seg_info, gboolean is_reassembled)
|
||||
{
|
||||
wmem_tree_key_t key[3];
|
||||
guint32 id;
|
||||
rlc_ue_parameters *params;
|
||||
|
||||
/* Add raw data (according to mode) */
|
||||
proto_tree_add_item(tree, (rlc_info->rlcMode == RLC_AM_MODE) ?
|
||||
hf_rlc_nr_am_data : hf_rlc_nr_um_data,
|
||||
tvb, offset, length, ENC_NA);
|
||||
if (!is_reassembled) {
|
||||
proto_tree_add_item(tree, (rlc_info->rlcMode == RLC_AM_MODE) ?
|
||||
hf_rlc_nr_am_data : hf_rlc_nr_um_data,
|
||||
tvb, offset, length, ENC_NA);
|
||||
}
|
||||
|
||||
/* TODO: handle reassembled PDCP PDUs. */
|
||||
/* For now only dissect complete PDUs */
|
||||
if (seg_info == 0) { /* i.e. contains whole SDU */
|
||||
if ((seg_info == 0) || is_reassembled) { /* i.e. contains whole SDU */
|
||||
if ((global_rlc_nr_call_pdcp_for_srb && (rlc_info->bearerType == BEARER_TYPE_SRB)) ||
|
||||
((rlc_info->bearerType == BEARER_TYPE_DRB) &&
|
||||
(((rlc_info->direction == PDCP_NR_DIRECTION_UPLINK) && (global_rlc_nr_call_pdcp_for_ul_drb != PDCP_drb_off)) ||
|
||||
|
@ -487,6 +571,7 @@ static void dissect_rlc_nr_um(tvbuff_t *tvb, packet_info *pinfo,
|
|||
proto_item *truncated_ti;
|
||||
proto_item *reserved_ti;
|
||||
int start_offset = offset;
|
||||
guint32 so = 0;
|
||||
|
||||
/* Hidden UM root */
|
||||
um_ti = proto_tree_add_string_format(tree, hf_rlc_nr_um,
|
||||
|
@ -500,7 +585,7 @@ static void dissect_rlc_nr_um(tvbuff_t *tvb, packet_info *pinfo,
|
|||
um_header_tree = proto_item_add_subtree(um_header_ti,
|
||||
ett_rlc_nr_um_header);
|
||||
|
||||
|
||||
/* Segmentation Info */
|
||||
proto_tree_add_item_ret_uint(um_header_tree, hf_rlc_nr_um_si, tvb, offset, 1, ENC_BIG_ENDIAN, &seg_info);
|
||||
if (seg_info == 0) {
|
||||
reserved_ti = proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_nr_um_reserved,
|
||||
|
@ -530,8 +615,6 @@ static void dissect_rlc_nr_um(tvbuff_t *tvb, packet_info *pinfo,
|
|||
return;
|
||||
}
|
||||
if (seg_info >= 2) {
|
||||
guint32 so;
|
||||
|
||||
proto_tree_add_item_ret_uint(um_header_tree, hf_rlc_nr_um_so, tvb, offset, 2, ENC_BIG_ENDIAN, &so);
|
||||
offset += 2;
|
||||
write_pdu_label_and_info(top_ti, um_header_ti, pinfo, " SN=%-6u SO=%-4u", sn, so);
|
||||
|
@ -557,10 +640,45 @@ static void dissect_rlc_nr_um(tvbuff_t *tvb, packet_info *pinfo,
|
|||
}
|
||||
}
|
||||
|
||||
tvbuff_t *next_tvb = NULL;
|
||||
/* Handle reassembly. */
|
||||
if (global_rlc_nr_reassemble_um_pdus && seg_info && tvb_reported_length_remaining(tvb, offset) > 0) {
|
||||
// Set fragmented flag.
|
||||
gboolean save_fragmented = pinfo->fragmented;
|
||||
pinfo->fragmented = TRUE;
|
||||
fragment_head *fh;
|
||||
gboolean more_frags = seg_info & 0x01;
|
||||
/* TODO: This should be unique enough, but is there a way to get frame number of first frame in reassembly table? */
|
||||
guint32 id = p_rlc_nr_info->direction + /* 1 bit */
|
||||
(p_rlc_nr_info->ueid<<1) + /* 7 bits */
|
||||
(p_rlc_nr_info->bearerId<<8) + /* 5 bits */
|
||||
(sn<<13); /* Leave 19 bits for SN - overlaps with other fields but room to overflow into msb */
|
||||
|
||||
fh = fragment_add(&pdu_reassembly_table, tvb, offset, pinfo,
|
||||
id, /* id */
|
||||
GUINT_TO_POINTER(id), /* data */
|
||||
so, /* frag_offset */
|
||||
tvb_reported_length_remaining(tvb, offset), /* frag_data_len */
|
||||
more_frags /* more_frags */
|
||||
);
|
||||
|
||||
gboolean update_col_info = TRUE;
|
||||
next_tvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled RLC SDU",
|
||||
fh, &rlc_nr_frag_items,
|
||||
&update_col_info, tree);
|
||||
pinfo->fragmented = save_fragmented;
|
||||
}
|
||||
|
||||
if (tvb_reported_length_remaining(tvb, offset) > 0) {
|
||||
show_PDU_in_tree(pinfo, tree, tvb, offset, tvb_reported_length_remaining(tvb, offset),
|
||||
p_rlc_nr_info, seg_info);
|
||||
p_rlc_nr_info, seg_info, FALSE);
|
||||
show_PDU_in_info(pinfo, top_ti, tvb_reported_length_remaining(tvb, offset), seg_info);
|
||||
/* Also add reassembled PDU */
|
||||
if (next_tvb) {
|
||||
add_new_data_source(pinfo, next_tvb, "Reassembled RLC-NR PDU");
|
||||
show_PDU_in_tree(pinfo, tree, next_tvb, 0, tvb_captured_length(next_tvb),
|
||||
p_rlc_nr_info, seg_info, TRUE);
|
||||
}
|
||||
} else if (!global_rlc_nr_headers_expected) {
|
||||
/* Report that expected data was missing (unless we know it might happen) */
|
||||
expert_add_info(pinfo, um_header_ti, &ei_rlc_nr_um_data_no_data);
|
||||
|
@ -877,7 +995,7 @@ static void dissect_rlc_nr_am(tvbuff_t *tvb, packet_info *pinfo,
|
|||
/* Data */
|
||||
if (tvb_reported_length_remaining(tvb, offset) > 0) {
|
||||
show_PDU_in_tree(pinfo, tree, tvb, offset, tvb_reported_length_remaining(tvb, offset),
|
||||
p_rlc_nr_info, seg_info);
|
||||
p_rlc_nr_info, seg_info, FALSE);
|
||||
show_PDU_in_info(pinfo, top_ti, tvb_reported_length_remaining(tvb, offset), seg_info);
|
||||
} else if (!global_rlc_nr_headers_expected) {
|
||||
/* Report that expected data was missing (unless we know it might happen) */
|
||||
|
@ -1396,6 +1514,71 @@ void proto_register_rlc_nr(void)
|
|||
NULL, HFILL
|
||||
}
|
||||
},
|
||||
|
||||
{ &hf_rlc_nr_fragment,
|
||||
{ "RLC-NR fragment",
|
||||
"rlc-nr.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_rlc_nr_fragments,
|
||||
{ "RLC-NR fragments",
|
||||
"rlc-nr.fragments", FT_BYTES, BASE_NONE, NULL, 0x0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_rlc_nr_fragment_overlap,
|
||||
{ "Fragment overlap",
|
||||
"rlc-nr.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
|
||||
"Fragment overlaps with other fragments", HFILL }
|
||||
},
|
||||
{ &hf_rlc_nr_fragment_overlap_conflict,
|
||||
{ "Conflicting data in fragment overlap",
|
||||
"rlc-nr.fragment.overlap.conflict",
|
||||
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
|
||||
"Overlapping fragments contained conflicting data", HFILL }
|
||||
},
|
||||
{ &hf_rlc_nr_fragment_multiple_tails,
|
||||
{ "Multiple tail fragments found",
|
||||
"rlc-nr.fragment.multipletails",
|
||||
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
|
||||
"Several tails were found when defragmenting the packet", HFILL }
|
||||
},
|
||||
{ &hf_rlc_nr_fragment_too_long_fragment,
|
||||
{ "Fragment too long",
|
||||
"rlc-nr.fragment.toolongfragment",
|
||||
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
|
||||
"Fragment contained data past end of packet", HFILL }
|
||||
},
|
||||
{ &hf_rlc_nr_fragment_error,
|
||||
{ "Defragmentation error",
|
||||
"rlc-nr.fragment.error",
|
||||
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
|
||||
"Defragmentation error due to illegal fragments", HFILL }
|
||||
},
|
||||
{ &hf_rlc_nr_fragment_count,
|
||||
{ "Fragment count",
|
||||
"rlc-nr.fragment.count",
|
||||
FT_UINT32, BASE_DEC, NULL, 0x0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_rlc_nr_reassembled_in,
|
||||
{ "Reassembled RLC-NR in frame",
|
||||
"rlc-nr.reassembled_in",
|
||||
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
|
||||
"This RLC-NR packet is reassembled in this frame", HFILL }
|
||||
},
|
||||
{ &hf_rlc_nr_reassembled_length,
|
||||
{ "Reassembled RLC-NR length",
|
||||
"rlc-nr.reassembled.length",
|
||||
FT_UINT32, BASE_DEC, NULL, 0x0,
|
||||
"The total length of the reassembled payload", HFILL }
|
||||
},
|
||||
{ &hf_rlc_nr_reassembled_data,
|
||||
{ "Reassembled payload",
|
||||
"rlc-nr.reassembled.data",
|
||||
FT_BYTES, BASE_NONE, NULL, 0x0,
|
||||
"The reassembled payload", HFILL }
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static gint *ett[] =
|
||||
|
@ -1403,7 +1586,9 @@ void proto_register_rlc_nr(void)
|
|||
&ett_rlc_nr,
|
||||
&ett_rlc_nr_context,
|
||||
&ett_rlc_nr_um_header,
|
||||
&ett_rlc_nr_am_header
|
||||
&ett_rlc_nr_am_header,
|
||||
&ett_rlc_nr_fragment,
|
||||
&ett_rlc_nr_fragments
|
||||
};
|
||||
|
||||
static ei_register_info ei[] = {
|
||||
|
@ -1470,7 +1655,16 @@ void proto_register_rlc_nr(void)
|
|||
"add expert info to indicate that headers were omitted",
|
||||
&global_rlc_nr_headers_expected);
|
||||
|
||||
prefs_register_bool_preference(rlc_nr_module, "reassemble_um_frames",
|
||||
"Try to reassemble UM frames",
|
||||
"N.B. This should be considered experimental/incomplete, in that it doesn't try to discard reassembled state "
|
||||
"when reestablishmenment happens, or in certain packet-loss cases",
|
||||
&global_rlc_nr_reassemble_um_pdus);
|
||||
|
||||
ue_parameters_tree = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
|
||||
|
||||
/* Register reassembly table. */
|
||||
reassembly_table_register(&pdu_reassembly_table, &pdu_reassembly_table_functions);
|
||||
}
|
||||
|
||||
void proto_reg_handoff_rlc_nr(void)
|
||||
|
|
Loading…
Reference in New Issue