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:
Martin Mathieson 2019-05-02 14:12:18 +01:00
parent ccf9820ea7
commit 736052b3f1
1 changed files with 209 additions and 15 deletions

View File

@ -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)