diff options
Diffstat (limited to 'epan/dissectors/packet-rlc-nr.c')
-rw-r--r-- | epan/dissectors/packet-rlc-nr.c | 457 |
1 files changed, 303 insertions, 154 deletions
diff --git a/epan/dissectors/packet-rlc-nr.c b/epan/dissectors/packet-rlc-nr.c index 0a4460885a..3659904f9c 100644 --- a/epan/dissectors/packet-rlc-nr.c +++ b/epan/dissectors/packet-rlc-nr.c @@ -15,10 +15,12 @@ #include <epan/exceptions.h> #include <epan/expert.h> #include <epan/prefs.h> +#include <epan/tap.h> #include <epan/proto_data.h> #include <epan/reassemble.h> #include "packet-rlc-nr.h" +#include "packet-rlc-3gpp-common.h" #include "packet-pdcp-nr.h" @@ -29,8 +31,7 @@ /* TODO: - add sequence analysis - take configuration of reordering timer, and stop reassembly if timeout exceeded? -- add tap info -- call more upper layer dissectors once they appear +- add tap info (for stats / SN graph) */ void proto_register_rlc_nr(void); @@ -67,11 +68,19 @@ static gboolean global_rlc_nr_reassemble_am_pdus = TRUE; /* Tree storing UE related parameters (ueid, drbid) -> pdcp_bearer_parameters */ static wmem_tree_t *ue_parameters_tree; +/* Table storing starting frame for reassembly session during first pass */ +/* Key is (ueId, direction, bearertype, bearerid, sn) */ +static wmem_tree_t *reassembly_start_table; + +/* Table storing starting frame for reassembly session during subsequent passes */ +/* Key is (ueId, direction, bearertype, bearerid, sn, frame) */ +static wmem_tree_t *reassembly_start_table_stored; /**************************************************/ /* Initialize the protocol and registered fields. */ -int proto_rlc_nr = -1; +int proto_rlc_nr; +extern int proto_mac_nr; extern int proto_pdcp_nr; static dissector_handle_t pdcp_nr_handle; @@ -83,77 +92,79 @@ static dissector_handle_t nr_rrc_ul_ccch1; static dissector_handle_t nr_rrc_dl_ccch; +static int rlc_nr_tap = -1; + /* Decoding context */ -static int hf_rlc_nr_context = -1; -static int hf_rlc_nr_context_mode = -1; -static int hf_rlc_nr_context_direction = -1; -static int hf_rlc_nr_context_ueid = -1; -static int hf_rlc_nr_context_bearer_type = -1; -static int hf_rlc_nr_context_bearer_id = -1; -static int hf_rlc_nr_context_pdu_length = -1; -static int hf_rlc_nr_context_sn_length = -1; +static int hf_rlc_nr_context; +static int hf_rlc_nr_context_mode; +static int hf_rlc_nr_context_direction; +static int hf_rlc_nr_context_ueid; +static int hf_rlc_nr_context_bearer_type; +static int hf_rlc_nr_context_bearer_id; +static int hf_rlc_nr_context_pdu_length; +static int hf_rlc_nr_context_sn_length; /* Transparent mode fields */ -static int hf_rlc_nr_tm = -1; -static int hf_rlc_nr_tm_data = -1; +static int hf_rlc_nr_tm; +static int hf_rlc_nr_tm_data; /* Unacknowledged mode fields */ -static int hf_rlc_nr_um = -1; -static int hf_rlc_nr_um_header = -1; -static int hf_rlc_nr_um_si = -1; -static int hf_rlc_nr_um_reserved = -1; -static int hf_rlc_nr_um_sn6 = -1; -static int hf_rlc_nr_um_sn12 = -1; -static int hf_rlc_nr_um_so = -1; -static int hf_rlc_nr_um_data = -1; +static int hf_rlc_nr_um; +static int hf_rlc_nr_um_header; +static int hf_rlc_nr_um_si; +static int hf_rlc_nr_um_reserved; +static int hf_rlc_nr_um_sn6; +static int hf_rlc_nr_um_sn12; +static int hf_rlc_nr_um_so; +static int hf_rlc_nr_um_data; /* Acknowledged mode fields */ -static int hf_rlc_nr_am = -1; -static int hf_rlc_nr_am_header = -1; -static int hf_rlc_nr_am_data_control = -1; -static int hf_rlc_nr_am_p = -1; -static int hf_rlc_nr_am_si = -1; -static int hf_rlc_nr_am_sn12 = -1; -static int hf_rlc_nr_am_sn18 = -1; -static int hf_rlc_nr_am_reserved = -1; -static int hf_rlc_nr_am_so = -1; -static int hf_rlc_nr_am_data = -1; +static int hf_rlc_nr_am; +static int hf_rlc_nr_am_header; +static int hf_rlc_nr_am_data_control; +static int hf_rlc_nr_am_p; +static int hf_rlc_nr_am_si; +static int hf_rlc_nr_am_sn12; +static int hf_rlc_nr_am_sn18; +static int hf_rlc_nr_am_reserved; +static int hf_rlc_nr_am_so; +static int hf_rlc_nr_am_data; /* Control fields */ -static int hf_rlc_nr_am_cpt = -1; -static int hf_rlc_nr_am_ack_sn = -1; -static int hf_rlc_nr_am_e1 = -1; -static int hf_rlc_nr_am_e2 = -1; -static int hf_rlc_nr_am_e3 = -1; -static int hf_rlc_nr_am_nack_sn = -1; -static int hf_rlc_nr_am_so_start = -1; -static int hf_rlc_nr_am_so_end = -1; -static int hf_rlc_nr_am_nack_range = -1; -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; +static int hf_rlc_nr_am_cpt; +static int hf_rlc_nr_am_ack_sn; +static int hf_rlc_nr_am_e1; +static int hf_rlc_nr_am_e2; +static int hf_rlc_nr_am_e3; +static int hf_rlc_nr_am_nack_sn; +static int hf_rlc_nr_am_so_start; +static int hf_rlc_nr_am_so_end; +static int hf_rlc_nr_am_nack_range; +static int hf_rlc_nr_am_nacks; + +static int hf_rlc_nr_header_only; + +static int hf_rlc_nr_fragments; +static int hf_rlc_nr_fragment; +static int hf_rlc_nr_fragment_overlap; +static int hf_rlc_nr_fragment_overlap_conflict; +static int hf_rlc_nr_fragment_multiple_tails; +static int hf_rlc_nr_fragment_too_long_fragment; +static int hf_rlc_nr_fragment_error; +static int hf_rlc_nr_fragment_count; +static int hf_rlc_nr_reassembled_in; +static int hf_rlc_nr_reassembled_length; +static int hf_rlc_nr_reassembled_data; /* 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 int ett_rlc_nr; +static int ett_rlc_nr_context; +static int ett_rlc_nr_um_header; +static int ett_rlc_nr_am_header; +static int ett_rlc_nr_fragments; +static int ett_rlc_nr_fragment; static const fragment_items rlc_nr_frag_items = { @@ -174,22 +185,22 @@ static const fragment_items rlc_nr_frag_items = { }; -static expert_field ei_rlc_nr_context_mode = EI_INIT; -static expert_field ei_rlc_nr_am_nack_sn = EI_INIT; -static expert_field ei_rlc_nr_am_nack_sn_ahead_ack = EI_INIT; -static expert_field ei_rlc_nr_am_nack_sn_ack_same = EI_INIT; -static expert_field ei_rlc_nr_am_nack_range = EI_INIT; -static expert_field ei_rlc_nr_am_cpt = EI_INIT; -static expert_field ei_rlc_nr_um_data_no_data = EI_INIT; -static expert_field ei_rlc_nr_am_data_no_data = EI_INIT; -static expert_field ei_rlc_nr_am_nack_sn_partial = EI_INIT; -static expert_field ei_rlc_nr_bytes_after_status_pdu_complete = EI_INIT; -static expert_field ei_rlc_nr_um_sn = EI_INIT; -static expert_field ei_rlc_nr_am_sn = EI_INIT; -static expert_field ei_rlc_nr_header_only = EI_INIT; -static expert_field ei_rlc_nr_reserved_bits_not_zero = EI_INIT; -static expert_field ei_rlc_nr_no_per_frame_info = EI_INIT; -static expert_field ei_rlc_nr_unknown_udp_framing_tag = EI_INIT; +static expert_field ei_rlc_nr_context_mode; +static expert_field ei_rlc_nr_am_nack_sn; +static expert_field ei_rlc_nr_am_nack_sn_ahead_ack; +static expert_field ei_rlc_nr_am_nack_sn_ack_same; +static expert_field ei_rlc_nr_am_nack_range; +static expert_field ei_rlc_nr_am_cpt; +static expert_field ei_rlc_nr_um_data_no_data; +static expert_field ei_rlc_nr_am_data_no_data; +static expert_field ei_rlc_nr_am_nack_sn_partial; +static expert_field ei_rlc_nr_bytes_after_status_pdu_complete; +static expert_field ei_rlc_nr_um_sn; +static expert_field ei_rlc_nr_am_sn; +static expert_field ei_rlc_nr_header_only; +static expert_field ei_rlc_nr_reserved_bits_not_zero; +static expert_field ei_rlc_nr_no_per_frame_info; +static expert_field ei_rlc_nr_unknown_udp_framing_tag; /* Value-strings */ static const value_string direction_vals[] = @@ -280,7 +291,7 @@ static const true_false_string header_only_vals = /* Reassembly state */ static reassembly_table pdu_reassembly_table; -static guint pdu_hash(gconstpointer k _U_) +static guint pdu_hash(gconstpointer k) { return GPOINTER_TO_UINT(k); } @@ -290,7 +301,7 @@ 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_) +static gpointer pdu_temporary_key(const packet_info *pinfo _U_, const guint32 id _U_, const void *data) { return (gpointer)data; } @@ -330,6 +341,8 @@ static void dissect_rlc_nr_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree - the top-level RLC PDU item - another subtree item (if supplied) */ static void write_pdu_label_and_info(proto_item *pdu_ti, proto_item *sub_ti, + packet_info *pinfo, const char *format, ...) G_GNUC_PRINTF(4, 5); +static void write_pdu_label_and_info(proto_item *pdu_ti, proto_item *sub_ti, packet_info *pinfo, const char *format, ...) { #define MAX_INFO_BUFFER 256 @@ -338,7 +351,7 @@ static void write_pdu_label_and_info(proto_item *pdu_ti, proto_item *sub_ti, va_list ap; va_start(ap, format); - g_vsnprintf(info_buffer, MAX_INFO_BUFFER, format, ap); + vsnprintf(info_buffer, MAX_INFO_BUFFER, format, ap); va_end(ap); /* Add to indicated places */ @@ -349,7 +362,7 @@ static void write_pdu_label_and_info(proto_item *pdu_ti, proto_item *sub_ti, } } -/* Version of function above, where no g_vsnprintf() call needed +/* Version of function above, where no vsnprintf() call needed - the info column - the top-level RLC PDU item - another subtree item (if supplied) */ @@ -366,10 +379,8 @@ static void write_pdu_label_and_info_literal(proto_item *pdu_ti, proto_item *sub /* Show in the info column how many bytes are in the UM/AM PDU, and indicate whether or not the beginning and end are included in this packet */ -static void show_PDU_in_info(packet_info *pinfo, - proto_item *top_ti, - gint32 length, - guint8 seg_info) +static void show_PDU_in_info(packet_info *pinfo, proto_item *top_ti, + gint32 length, guint8 seg_info) { /* Reflect this PDU in the info column */ if (length > 0) { @@ -489,8 +500,6 @@ static void show_PDU_in_tree(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb } p_pdcp_nr_info->bearerId = rlc_info->bearerId; - /* Assume no SDAP present */ - p_pdcp_nr_info->sdap_header = 0; p_pdcp_nr_info->rohc.rohc_compression = FALSE; p_pdcp_nr_info->is_retx = FALSE; p_pdcp_nr_info->pdu_length = length; @@ -570,22 +579,108 @@ static void dissect_rlc_nr_tm(tvbuff_t *tvb, packet_info *pinfo, } } +/* Look up / set the frame thought to be the start segment of this RLC PDU. */ +/* N.B. this algorithm will not be correct in all cases, but is good enough to be useful.. */ +static guint32 get_reassembly_start_frame(packet_info *pinfo, guint32 seg_info, + rlc_nr_info *p_rlc_nr_info, guint32 sn) +{ + guint32 frame_id = 0; + + guint32 key_values[] = { p_rlc_nr_info->ueid, + p_rlc_nr_info->direction, + p_rlc_nr_info->bearerType, + p_rlc_nr_info->bearerId, + sn, + pinfo->num + }; + + /* Is this the first segment of SN? */ + gboolean first_segment = (seg_info & 0x2) == 0; + + /* Set Key. */ + wmem_tree_key_t key[2]; + key[0].length = 5; /* Ignoring this frame num */ + key[0].key = key_values; + key[1].length = 0; + key[1].key = NULL; + + guint32 *p_frame_id = NULL; + + if (!PINFO_FD_VISITED(pinfo)) { + /* On first pass, maintain reassembly_start_table. */ + + /* Look for existing entry. */ + p_frame_id = (guint32*)wmem_tree_lookup32_array(reassembly_start_table, key); + + + if (first_segment) { + /* Let it start from here */ + wmem_tree_insert32_array(reassembly_start_table, key, GUINT_TO_POINTER(pinfo->num)); + frame_id = pinfo->num; + } + else { + if (p_frame_id) { + /* Use existing entry (or zero) if not found */ + frame_id = GPOINTER_TO_UINT(p_frame_id); + } + } + + /* Store this result for subsequent passes. Don't store 0 though. */ + if (frame_id) { + key[0].length = 6; + wmem_tree_insert32_array(reassembly_start_table_stored, key, GUINT_TO_POINTER(frame_id)); + } + } + else { + /* For subsequent passes, use stored value */ + key[0].length = 6; /* i.e. include this framenum in key */ + p_frame_id = (guint32*)wmem_tree_lookup32_array(reassembly_start_table_stored, key); + if (p_frame_id) { + /* Use found value */ + frame_id = GPOINTER_TO_UINT(p_frame_id); + } + } + + return frame_id; +} + +/* On first pass - if this SN is complete, don't try to add any more fragments to it */ +static void reassembly_frame_complete(packet_info *pinfo, + rlc_nr_info *p_rlc_nr_info, guint32 sn) +{ + if (!PINFO_FD_VISITED(pinfo)) { + guint32 key_values[] = { p_rlc_nr_info->ueid, + p_rlc_nr_info->direction, + p_rlc_nr_info->bearerType, + p_rlc_nr_info->bearerId, + sn + }; + + /* Set Key. */ + wmem_tree_key_t key[2]; + key[0].length = 5; + key[0].key = key_values; + key[1].length = 0; + key[1].key = NULL; + + /* Clear entry out */ + wmem_tree_insert32_array(reassembly_start_table, key, GUINT_TO_POINTER(0)); + } +} /***************************************************/ /* Unacknowledged mode PDU */ static void dissect_rlc_nr_um(tvbuff_t *tvb, packet_info *pinfo, - proto_tree *tree, - int offset, - rlc_nr_info *p_rlc_nr_info, - proto_item *top_ti) + proto_tree *tree, int offset, + rlc_nr_info *p_rlc_nr_info, proto_item *top_ti, + rlc_3gpp_tap_info *tap_info) { guint32 seg_info, sn; guint64 reserved; proto_item *um_ti; proto_tree *um_header_tree; proto_item *um_header_ti; - gboolean is_truncated = FALSE; proto_item *truncated_ti; proto_item *reserved_ti; int start_offset = offset; @@ -617,16 +712,20 @@ static void dissect_rlc_nr_um(tvbuff_t *tvb, packet_info *pinfo, } else { /* Add sequence number */ if (p_rlc_nr_info->sequenceNumberLength == UM_SN_LENGTH_6_BITS) { + /* SN */ proto_tree_add_item_ret_uint(um_header_tree, hf_rlc_nr_um_sn6, tvb, offset, 1, ENC_BIG_ENDIAN, &sn); offset++; + tap_info->sequenceNumberGiven = TRUE; } else if (p_rlc_nr_info->sequenceNumberLength == UM_SN_LENGTH_12_BITS) { reserved_ti = proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_nr_um_reserved, tvb, (offset<<3)+2, 2, &reserved, ENC_BIG_ENDIAN); if (reserved) { expert_add_info(pinfo, reserved_ti, &ei_rlc_nr_reserved_bits_not_zero); } + /* SN */ proto_tree_add_item_ret_uint(um_header_tree, hf_rlc_nr_um_sn12, tvb, offset, 2, ENC_BIG_ENDIAN, &sn); offset += 2; + tap_info->sequenceNumberGiven = TRUE; } else { /* Invalid length of sequence number */ proto_tree_add_expert_format(um_header_tree, pinfo, &ei_rlc_nr_um_sn, tvb, 0, 0, @@ -634,6 +733,9 @@ static void dissect_rlc_nr_um(tvbuff_t *tvb, packet_info *pinfo, p_rlc_nr_info->sequenceNumberLength); return; } + + tap_info->sequenceNumber = sn; + if (seg_info >= 2) { /* Segment offset */ proto_tree_add_item_ret_uint(um_header_tree, hf_rlc_nr_um_so, tvb, offset, 2, ENC_BIG_ENDIAN, &so); @@ -649,7 +751,7 @@ static void dissect_rlc_nr_um(tvbuff_t *tvb, packet_info *pinfo, if (global_rlc_nr_headers_expected) { /* There might not be any data, if only headers (plus control data) were logged */ - is_truncated = (tvb_captured_length_remaining(tvb, offset) == 0); + gboolean is_truncated = (tvb_captured_length_remaining(tvb, offset) == 0); truncated_ti = proto_tree_add_boolean(tree, hf_rlc_nr_header_only, tvb, 0, 0, is_truncated); if (is_truncated) { @@ -667,30 +769,27 @@ static void dissect_rlc_nr_um(tvbuff_t *tvb, packet_info *pinfo, /* Handle any reassembly. */ tvbuff_t *next_tvb = NULL; if (global_rlc_nr_reassemble_um_pdus && seg_info && tvb_reported_length_remaining(tvb, offset) > 0) { - // Set fragmented flag. + /* 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; + guint32 id = get_reassembly_start_frame(pinfo, seg_info, p_rlc_nr_info, sn); /* Leave 19 bits for SN - overlaps with other fields but room to overflow into msb */ + if (id != 0) { + 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) { @@ -703,6 +802,9 @@ static void dissect_rlc_nr_um(tvbuff_t *tvb, packet_info *pinfo, 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); + + /* Note that PDU is now completed (so won't try to add to it) */ + reassembly_frame_complete(pinfo, p_rlc_nr_info, sn); } } else if (!global_rlc_nr_headers_expected) { /* Report that expected data was missing (unless we know it might happen) */ @@ -719,7 +821,8 @@ static void dissect_rlc_nr_am_status_pdu(tvbuff_t *tvb, proto_item *status_ti, int offset, proto_item *top_ti, - rlc_nr_info *p_rlc_nr_info) + rlc_nr_info *p_rlc_nr_info, + rlc_3gpp_tap_info *tap_info) { guint8 sn_size, reserved_bits1, reserved_bits2; guint32 cpt, sn_limit, nack_count = 0; @@ -766,6 +869,7 @@ static void dissect_rlc_nr_am_status_pdu(tvbuff_t *tvb, bit_offset, sn_size, &ack_sn, ENC_BIG_ENDIAN); bit_offset += sn_size; write_pdu_label_and_info(top_ti, status_ti, pinfo, " ACK_SN=%-6u", (guint32)ack_sn); + tap_info->ACKNo = (guint32)ack_sn; /* E1 */ proto_tree_add_bits_ret_val(tree, hf_rlc_nr_am_e1, tvb, @@ -796,7 +900,7 @@ static void dissect_rlc_nr_am_status_pdu(tvbuff_t *tvb, /* We shouldn't NACK the ACK_SN! */ if (nack_sn == ack_sn) { expert_add_info_format(pinfo, nack_ti, &ei_rlc_nr_am_nack_sn_ack_same, - "Status PDU shouldn't ACK and NACK the same sequence number (%" G_GINT64_MODIFIER "u)", + "Status PDU shouldn't ACK and NACK the same sequence number (%" PRIu64 ")", ack_sn); } @@ -805,7 +909,15 @@ static void dissect_rlc_nr_am_status_pdu(tvbuff_t *tvb, expert_add_info(pinfo, nack_ti, &ei_rlc_nr_am_nack_sn_ahead_ack); } - nack_count++; + /* Copy single NACK into tap struct, but don't exceed buffer */ + if (nack_count < MAX_NACKs) { + tap_info->NACKs[nack_count++] = (guint32)nack_sn; + } + else { + /* Let it get bigger than the array for accurate stats... */ + nack_count++; + } + /* E1 */ proto_tree_add_bits_ret_val(tree, hf_rlc_nr_am_e1, tvb, @@ -849,6 +961,7 @@ static void dissect_rlc_nr_am_status_pdu(tvbuff_t *tvb, bit_offset>>3, 2, ENC_BIG_ENDIAN, &so_start); bit_offset += 16; + /* N.B., if E3 is set, this refers to a byte offset within the last PDU of the range.. */ proto_tree_add_item_ret_uint(tree, hf_rlc_nr_am_so_end, tvb, bit_offset>>3, 2, ENC_BIG_ENDIAN, &so_end); bit_offset += 16; @@ -866,19 +979,30 @@ static void dissect_rlc_nr_am_status_pdu(tvbuff_t *tvb, } if (e3) { + /* NACK range */ proto_item *nack_range_ti; - - /* Read NACK range */ nack_range_ti = proto_tree_add_item_ret_uint(tree, hf_rlc_nr_am_nack_range, tvb, bit_offset>>3, 1, ENC_BIG_ENDIAN, &nack_range); bit_offset += 8; if (nack_range == 0) { + /* It is the number of PDUs not received, so 0 does not make sense */ expert_add_info(pinfo, nack_range_ti, &ei_rlc_nr_am_nack_range); - } else { - nack_count += nack_range-1; + return; } + proto_item_append_text(nack_range_ti, " (SNs %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT " missing)", + nack_sn, nack_sn+nack_range-1); write_pdu_label_and_info(top_ti, NULL, pinfo," NACK range=%u", nack_range); + + /* Copy NACK SNs into tap_info */ + for (guint nack=0; nack < nack_range-1; nack++) { + if (nack_count+nack < MAX_NACKs) { + /* Guard against wrapping the SN range */ + tap_info->NACKs[nack_count+nack] = (guint32)((nack_sn+nack+1) % sn_limit); + } + } + /* Let it get bigger than the array for accurate stats. Take care not to double-count nack-sn itself. */ + nack_count += (nack_range-1); } } @@ -886,6 +1010,7 @@ static void dissect_rlc_nr_am_status_pdu(tvbuff_t *tvb, proto_item *count_ti = proto_tree_add_uint(tree, hf_rlc_nr_am_nacks, tvb, 0, 1, nack_count); proto_item_set_generated(count_ti); proto_item_append_text(status_ti, " (%u NACKs)", nack_count); + tap_info->noOfNACKs = nack_count; } /* Check that we've reached the end of the PDU. If not, show malformed */ @@ -905,10 +1030,9 @@ static void dissect_rlc_nr_am_status_pdu(tvbuff_t *tvb, /***************************************************/ /* Acknowledged mode PDU */ static void dissect_rlc_nr_am(tvbuff_t *tvb, packet_info *pinfo, - proto_tree *tree, - int offset, - rlc_nr_info *p_rlc_nr_info, - proto_item *top_ti) + proto_tree *tree, int offset, + rlc_nr_info *p_rlc_nr_info, proto_item *top_ti, + rlc_3gpp_tap_info *tap_info _U_) { gboolean dc, polling; guint32 seg_info, sn; @@ -917,7 +1041,6 @@ static void dissect_rlc_nr_am(tvbuff_t *tvb, packet_info *pinfo, proto_tree *am_header_tree; proto_item *am_header_ti; gint start_offset = offset; - gboolean is_truncated = FALSE; proto_item *truncated_ti; proto_item *reserved_ti; guint32 so = 0; @@ -937,6 +1060,7 @@ static void dissect_rlc_nr_am(tvbuff_t *tvb, packet_info *pinfo, /* First bit is Data/Control flag */ proto_tree_add_item_ret_boolean(am_header_tree, hf_rlc_nr_am_data_control, tvb, offset, 1, ENC_BIG_ENDIAN, &dc); + tap_info->isControlPDU = !dc; if (dc == 0) { /**********************/ @@ -945,7 +1069,8 @@ static void dissect_rlc_nr_am(tvbuff_t *tvb, packet_info *pinfo, /* Control PDUs are a completely separate format */ dissect_rlc_nr_am_status_pdu(tvb, pinfo, am_header_tree, am_header_ti, - offset, top_ti, p_rlc_nr_info); + offset, top_ti, + p_rlc_nr_info, tap_info); return; } @@ -988,6 +1113,9 @@ static void dissect_rlc_nr_am(tvbuff_t *tvb, packet_info *pinfo, return; } + tap_info->sequenceNumberGiven = TRUE; + tap_info->sequenceNumber = sn; + /* Segment Offset */ if (seg_info >= 2) { proto_tree_add_item_ret_uint(am_header_tree, hf_rlc_nr_am_so, tvb, @@ -1003,7 +1131,7 @@ static void dissect_rlc_nr_am(tvbuff_t *tvb, packet_info *pinfo, /* There might not be any data, if only headers (plus control data) were logged */ if (global_rlc_nr_headers_expected) { - is_truncated = (tvb_captured_length_remaining(tvb, offset) == 0); + gboolean is_truncated = (tvb_captured_length_remaining(tvb, offset) == 0); truncated_ti = proto_tree_add_boolean(tree, hf_rlc_nr_header_only, tvb, 0, 0, is_truncated); if (is_truncated) { @@ -1021,30 +1149,27 @@ static void dissect_rlc_nr_am(tvbuff_t *tvb, packet_info *pinfo, /* Handle any reassembly. */ tvbuff_t *next_tvb = NULL; if (global_rlc_nr_reassemble_am_pdus && seg_info && tvb_reported_length_remaining(tvb, offset) > 0) { - // Set fragmented flag. + /* 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; + guint32 id = get_reassembly_start_frame(pinfo, seg_info, p_rlc_nr_info, sn); + if (id != 0) { + 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; + } } @@ -1157,7 +1282,7 @@ static gboolean dissect_rlc_nr_heur(tvbuff_t *tvb, packet_info *pinfo, /* Create tvb that starts at actual RLC PDU */ rlc_tvb = tvb_new_subset_remaining(tvb, offset); - dissect_rlc_nr_common(rlc_tvb, pinfo, tree, TRUE); + dissect_rlc_nr_common(rlc_tvb, pinfo, tree, TRUE /* udp framing */); return TRUE; } @@ -1167,7 +1292,7 @@ static gboolean dissect_rlc_nr_heur(tvbuff_t *tvb, packet_info *pinfo, static int dissect_rlc_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { - dissect_rlc_nr_common(tvb, pinfo, tree, FALSE); + dissect_rlc_nr_common(tvb, pinfo, tree, FALSE /* not udp framing */); return tvb_captured_length(tvb); } @@ -1182,6 +1307,10 @@ static void dissect_rlc_nr_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree gint offset = 0; struct rlc_nr_info *p_rlc_nr_info; + /* Allocate and Zero tap struct */ + rlc_3gpp_tap_info *tap_info = wmem_new0(pinfo->pool, rlc_3gpp_tap_info); + tap_info->rat = RLC_RAT_NR; + /* Set protocol name */ col_set_str(pinfo->cinfo, COL_PROTOCOL, "RLC-NR"); @@ -1269,6 +1398,20 @@ static void dissect_rlc_nr_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree p_rlc_nr_info->bearerId); } + /* Set context-info parts of tap struct */ + tap_info->rlcMode = p_rlc_nr_info->rlcMode; + tap_info->direction = p_rlc_nr_info->direction; + /* TODO: p_rlc_nr_info does not have priority. */ + tap_info->ueid = p_rlc_nr_info->ueid; + tap_info->channelType = p_rlc_nr_info->bearerType; + tap_info->channelId = p_rlc_nr_info->bearerId; + tap_info->pduLength = p_rlc_nr_info->pduLength; + tap_info->sequenceNumberLength = p_rlc_nr_info->sequenceNumberLength; + tap_info->loggedInMACFrame = (p_get_proto_data(wmem_file_scope(), pinfo, proto_mac_nr, 0) != NULL); + + tap_info->rlc_time = pinfo->abs_ts; + + /* Dissect the RLC PDU itself. Format depends upon mode... */ switch (p_rlc_nr_info->rlcMode) { @@ -1277,11 +1420,11 @@ static void dissect_rlc_nr_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree break; case RLC_UM_MODE: - dissect_rlc_nr_um(tvb, pinfo, rlc_nr_tree, offset, p_rlc_nr_info, top_ti); + dissect_rlc_nr_um(tvb, pinfo, rlc_nr_tree, offset, p_rlc_nr_info, top_ti, tap_info); break; case RLC_AM_MODE: - dissect_rlc_nr_am(tvb, pinfo, rlc_nr_tree, offset, p_rlc_nr_info, top_ti); + dissect_rlc_nr_am(tvb, pinfo, rlc_nr_tree, offset, p_rlc_nr_info, top_ti, tap_info); break; default: @@ -1290,6 +1433,9 @@ static void dissect_rlc_nr_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree "Unrecognised RLC Mode set (%u)", p_rlc_nr_info->rlcMode); break; } + + /* Queue tap info */ + tap_queue_packet(rlc_nr_tap, pinfo, tap_info); } @@ -1534,7 +1680,6 @@ void proto_register_rlc_nr(void) "Acknowledged Mode Data", HFILL } }, - { &hf_rlc_nr_am_cpt, { "Control PDU Type", "rlc-nr.am.cpt", FT_UINT8, BASE_HEX, VALS(control_pdu_type_vals), 0x70, @@ -1665,8 +1810,7 @@ void proto_register_rlc_nr(void) "rlc-nr.reassembled.data", FT_BYTES, BASE_NONE, NULL, 0x0, "The reassembled payload", HFILL } - }, - + } }; static gint *ett[] = @@ -1711,6 +1855,9 @@ void proto_register_rlc_nr(void) /* Allow other dissectors to find this one by name. */ register_dissector("rlc-nr", dissect_rlc_nr, proto_rlc_nr); + /* Register the tap name */ + rlc_nr_tap = register_tap("rlc-3gpp"); + /* Preferences */ rlc_nr_module = prefs_register_protocol(proto_rlc_nr, NULL); @@ -1756,6 +1903,8 @@ void proto_register_rlc_nr(void) &global_rlc_nr_reassemble_um_pdus); ue_parameters_tree = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); + reassembly_start_table = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); + reassembly_start_table_stored = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); /* Register reassembly table. */ reassembly_table_register(&pdu_reassembly_table, &pdu_reassembly_table_functions); |