aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-rlc-nr.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-rlc-nr.c')
-rw-r--r--epan/dissectors/packet-rlc-nr.c457
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);