diff options
author | Martin Mathieson <martin.r.mathieson@googlemail.com> | 2019-05-02 14:12:18 +0100 |
---|---|---|
committer | Martin Mathieson <martin.r.mathieson@googlemail.com> | 2019-05-13 17:43:28 +0000 |
commit | 736052b3f1dae0732c0eae3dc88e37e2462c623b (patch) | |
tree | 6828450ad1215351bfe8ddf477e861e502c73dd4 | |
parent | ccf9820ea7575edf1d4192b3f119769a6a6819dd (diff) |
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>
-rw-r--r-- | epan/dissectors/packet-rlc-nr.c | 224 |
1 files changed, 209 insertions, 15 deletions
diff --git a/epan/dissectors/packet-rlc-nr.c b/epan/dissectors/packet-rlc-nr.c index eb1dd20d04..2f4fccf1bd 100644 --- a/epan/dissectors/packet-rlc-nr.c +++ b/epan/dissectors/packet-rlc-nr.c @@ -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) |