/* By default don't call PDCP/RRC dissectors for SDU data */
static gboolean global_rlc_lte_call_pdcp_for_srb = FALSE;
-enum pdcp_for_drb { PDCP_drb_off, PDCP_drb_SN_7, PDCP_drb_SN_12};
+enum pdcp_for_drb { PDCP_drb_off, PDCP_drb_SN_7, PDCP_drb_SN_12, PDCP_drb_SN_signalled};
static enum_val_t pdcp_drb_col_vals[] = {
{"pdcp-drb-off", "Off", PDCP_drb_off},
{"pdcp-drb-sn-7", "7-bit SN", PDCP_drb_SN_7},
{"pdcp-drb-sn-12", "12-bit SN", PDCP_drb_SN_12},
+ {"pdcp-drb-sn-signalling", "Use signalled value", PDCP_drb_SN_signalled},
{NULL, NULL, -1}
};
static gint global_rlc_lte_call_pdcp_for_drb = (gint)PDCP_drb_off;
+static gint signalled_pdcp_sn_bits = 12;
static gboolean global_rlc_lte_call_rrc = FALSE;
static int hf_rlc_lte_am_e1 = -1;
static int hf_rlc_lte_am_e2 = -1;
static int hf_rlc_lte_am_nack_sn = -1;
+static int hf_rlc_lte_am_nacks = -1;
static int hf_rlc_lte_am_so_start = -1;
static int hf_rlc_lte_am_so_end = -1;
static int hf_rlc_lte_sequence_analysis_repeated_nack = -1;
static int hf_rlc_lte_sequence_analysis_repeated_nack_original_frame = -1;
+static int hf_rlc_lte_sequence_analysis_ack_out_of_range = -1;
+static int hf_rlc_lte_sequence_analysis_ack_out_of_range_opposite_frame = -1;
+
/* Subtrees. */
static int ett_rlc_lte = -1;
static int ett_rlc_lte_context = -1;
static const value_string rlc_channel_type_vals[] =
{
- { CHANNEL_TYPE_CCCH, "CCCH"},
- { CHANNEL_TYPE_BCCH, "BCCH"},
- { CHANNEL_TYPE_PCCH, "PCCH"},
- { CHANNEL_TYPE_SRB, "SRB"},
- { CHANNEL_TYPE_DRB, "DRB"},
+ { CHANNEL_TYPE_CCCH, "CCCH"},
+ { CHANNEL_TYPE_BCCH_BCH, "BCCH_BCH"},
+ { CHANNEL_TYPE_PCCH, "PCCH"},
+ { CHANNEL_TYPE_SRB, "SRB"},
+ { CHANNEL_TYPE_DRB, "DRB"},
+ { CHANNEL_TYPE_BCCH_DL_SCH, "BCCH_DL_SCH"},
{ 0, NULL }
};
guint16 lastSN;
/* AM/UM */
- enum { SN_OK, SN_Repeated, SN_MAC_Retx, SN_Retx, SN_Missing} state;
+ enum { SN_OK, SN_Repeated, SN_MAC_Retx, SN_Retx, SN_Missing, ACK_Out_of_Window} state;
} state_sequence_analysis_report_in_frame;
case PDCP_drb_SN_12:
p_pdcp_lte_info->seqnum_length = 12;
break;
+ case PDCP_drb_SN_signalled:
+ /* Use whatever was signalled (e.g. in RRC) */
+ p_pdcp_lte_info->seqnum_length = signalled_pdcp_sn_bits;
+ break;
+
default:
DISSECTOR_ASSERT(FALSE);
break;
/* Add to the tree values associated with sequence analysis for this frame */
static void addChannelSequenceInfo(state_sequence_analysis_report_in_frame *p,
+ gboolean isControlFrame,
rlc_lte_info *p_rlc_lte_info,
guint16 sequenceNumber,
gboolean newSegmentStarted,
switch (p->state) {
case SN_OK:
+ if (isControlFrame) {
+ return;
+ }
+
ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok,
tvb, 0, 0, TRUE);
PROTO_ITEM_SET_GENERATED(ti);
break;
case SN_MAC_Retx:
+ if (isControlFrame) {
+ return;
+ }
+
ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok,
tvb, 0, 0, FALSE);
PROTO_ITEM_SET_GENERATED(ti);
tvb, 0, 0, TRUE);
PROTO_ITEM_SET_GENERATED(ti);
expert_add_info_format(pinfo, ti, PI_SEQUENCE, PI_WARN,
- "AM Frame retransmitted for %s on UE %u - due to MAC retx!",
+ "AM Frame retransmitted for %s on UE %u (SN=%u) - due to MAC retx!",
val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"),
- p_rlc_lte_info->ueid);
+ p_rlc_lte_info->ueid,
+ p->firstSN);
break;
case SN_Retx:
+ if (isControlFrame) {
+ return;
+ }
+
ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok,
tvb, 0, 0, FALSE);
PROTO_ITEM_SET_GENERATED(ti);
tvb, 0, 0, TRUE);
PROTO_ITEM_SET_GENERATED(ti);
expert_add_info_format(pinfo, ti, PI_SEQUENCE, PI_WARN,
- "AM Frame retransmitted for %s on UE %u - most likely in response to NACK",
+ "AM Frame retransmitted for %s on UE %u (SN=%u) - most likely in response to NACK",
val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"),
- p_rlc_lte_info->ueid);
+ p_rlc_lte_info->ueid,
+ p->firstSN);
proto_item_append_text(seqnum_ti, " - SN %u retransmitted", p->firstSN);
break;
case SN_Repeated:
+ if (isControlFrame) {
+ return;
+ }
+
ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok,
tvb, 0, 0, FALSE);
PROTO_ITEM_SET_GENERATED(ti);
break;
case SN_Missing:
+ if (isControlFrame) {
+ return;
+ }
+
ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok,
tvb, 0, 0, FALSE);
PROTO_ITEM_SET_GENERATED(ti);
tap_info->missingSNs = 1;
}
break;
+
+ case ACK_Out_of_Window:
+ if (!isControlFrame) {
+ return;
+ }
+
+
+ /* Not OK */
+ ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok,
+ tvb, 0, 0, FALSE);
+ /* Out of range */
+ PROTO_ITEM_SET_GENERATED(ti);
+ ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ack_out_of_range,
+ tvb, 0, 0, TRUE);
+ PROTO_ITEM_SET_GENERATED(ti);
+
+ /* Link back to last seen SN in other direction */
+ ti = proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_ack_out_of_range_opposite_frame,
+ tvb, 0, 0, p->previousFrameNum);
+ PROTO_ITEM_SET_GENERATED(ti);
+
+ /* Expert error */
+ expert_add_info_format(pinfo, ti, PI_SEQUENCE, PI_ERROR,
+ "AM ACK for SN %u - but last received SN in other direction is %u for UE %u",
+ p->firstSN, p->sequenceExpected,
+ p_rlc_lte_info->ueid);
+ proto_item_append_text(seqnum_ti, "- ACK SN %u Outside Rx Window - last received SN is %u",
+ p->firstSN, p->sequenceExpected);
+
+ break;
}
break;
p_rlc_lte_info->ueid);
break;
+ case SN_OK:
+ ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok,
+ tvb, 0, 0, TRUE);
+ PROTO_ITEM_SET_GENERATED(ti);
+ proto_item_append_text(seqnum_ti, " - OK");
+ break;
+
default:
/* Incorrect sequence number */
expert_add_info_format(pinfo, ti, PI_SEQUENCE, PI_WARN,
/* Update the channel status and set report for this frame */
static void checkChannelSequenceInfo(packet_info *pinfo, tvbuff_t *tvb,
rlc_lte_info *p_rlc_lte_info,
+ gboolean isControlFrame,
guint16 sequenceNumber,
gboolean first_includes_start, gboolean last_includes_end,
gboolean is_resegmented _U_,
p_report_in_frame = (state_sequence_analysis_report_in_frame*)g_hash_table_lookup(rlc_lte_frame_sequence_analysis_report_hash,
&pinfo->fd->num);
if (p_report_in_frame != NULL) {
- addChannelSequenceInfo(p_report_in_frame, p_rlc_lte_info,
+ addChannelSequenceInfo(p_report_in_frame, isControlFrame, p_rlc_lte_info,
sequenceNumber, first_includes_start,
tap_info, pinfo, tree, tvb);
return;
if (p_channel_status == NULL) {
createdChannel = TRUE;
- /* Allocate a new key and value */
- p_channel_key = se_alloc(sizeof(rlc_channel_hash_key));
+ /* Allocate a new value and duplicate key contents */
p_channel_status = se_alloc0(sizeof(rlc_channel_sequence_analysis_status));
-
- /* Copy key contents */
- memcpy(p_channel_key, &channel_key, sizeof(rlc_channel_hash_key));
+ p_channel_key = se_memdup(&channel_key, sizeof(rlc_channel_hash_key));
/* Set mode */
p_channel_status->rlcMode = p_rlc_lte_info->rlcMode;
if (!createdChannel) {
expectedSequenceNumber = (p_channel_status->previousSequenceNumber + 1) % snLimit;
}
+ else {
+ /* Whatever we got is fine.. */
+ expectedSequenceNumber = sequenceNumber;
+ }
/* Set report for this frame */
/* For UM, sequence number is always expectedSequence number */
/* Don't get confused by MAC (HARQ) retx */
if (is_mac_lte_frame_retx(pinfo, p_rlc_lte_info->direction)) {
p_report_in_frame->state = SN_MAC_Retx;
+ p_report_in_frame->firstSN = sequenceNumber;
}
/* Frames are not missing if we get an earlier sequence number again */
- else if (((snLimit + expectedSequenceNumber - sequenceNumber) % snLimit) > 40) {
- if (!createdChannel) {
- p_report_in_frame->state = SN_Missing;
- tap_info->missingSNs = (snLimit + sequenceNumber - expectedSequenceNumber) % snLimit;
- p_report_in_frame->firstSN = expectedSequenceNumber;
- p_report_in_frame->lastSN = (snLimit + sequenceNumber - 1) % snLimit;
+ /* TODO: taking time into account would give better idea of whether missing or repeated... */
+ else if (((snLimit + sequenceNumber - expectedSequenceNumber) % snLimit) < 10) {
+ p_report_in_frame->state = SN_Missing;
+ tap_info->missingSNs = (snLimit + sequenceNumber - expectedSequenceNumber) % snLimit;
+ p_report_in_frame->firstSN = expectedSequenceNumber;
+ p_report_in_frame->lastSN = (snLimit + sequenceNumber - 1) % snLimit;
- p_report_in_frame->sequenceExpected = expectedSequenceNumber;
- p_report_in_frame->previousFrameNum = p_channel_status->previousFrameNum;
- p_report_in_frame->previousSegmentIncomplete = p_channel_status->previousSegmentIncomplete;
- }
- else {
- /* The log may not contain the very first SNs for this channel, so be forgiving... */
- p_report_in_frame->state = SN_OK;
- }
+ p_report_in_frame->sequenceExpected = expectedSequenceNumber;
+ p_report_in_frame->previousFrameNum = p_channel_status->previousFrameNum;
+ p_report_in_frame->previousSegmentIncomplete = p_channel_status->previousSegmentIncomplete;
/* Update channel status to remember *this* frame */
p_channel_status->previousFrameNum = pinfo->fd->num;
if (is_mac_lte_frame_retx(pinfo, p_rlc_lte_info->direction)) {
/* Just report that this is a MAC Retx */
p_report_in_frame->state = SN_MAC_Retx;
+ p_report_in_frame->firstSN = sequenceNumber;
/* No channel state to update */
}
-
/* Expected? */
else if (sequenceNumber == expectedSequenceNumber) {
g_hash_table_insert(rlc_lte_frame_sequence_analysis_report_hash, &pinfo->fd->num, p_report_in_frame);
/* Add state report for this frame into tree */
- addChannelSequenceInfo(p_report_in_frame, p_rlc_lte_info, sequenceNumber,
+ addChannelSequenceInfo(p_report_in_frame, isControlFrame, p_rlc_lte_info, sequenceNumber,
first_includes_start, tap_info, pinfo, tree, tvb);
}
p_channel_status->frameNum = pinfo->fd->num;
}
+/* Check that the ACK is consistent with data the expected sequence number
+ in the other direction */
+static void checkChannelACKWindow(guint16 ack_sn,
+ packet_info *pinfo,
+ rlc_lte_info *p_rlc_lte_info,
+ rlc_lte_tap_info *tap_info,
+ proto_tree *tree,
+ tvbuff_t *tvb)
+{
+ rlc_channel_hash_key channel_key;
+ rlc_channel_sequence_analysis_status *p_channel_status;
+ state_sequence_analysis_report_in_frame *p_report_in_frame = NULL;
+
+ /* If find stat_report_in_frame already, use that and get out */
+ if (pinfo->fd->flags.visited) {
+ p_report_in_frame = (state_sequence_analysis_report_in_frame*)g_hash_table_lookup(rlc_lte_frame_sequence_analysis_report_hash,
+ &pinfo->fd->num);
+ if (p_report_in_frame != NULL) {
+ /* Add any info to tree */
+ addChannelSequenceInfo(p_report_in_frame, TRUE, p_rlc_lte_info,
+ 0, FALSE,
+ tap_info, pinfo, tree, tvb);
+ return;
+ }
+ else {
+ /* Give up - we must have tried already... */
+ return;
+ }
+ }
+
+ /*******************************************************************/
+ /* Find an entry for this channel state (in the opposite direction */
+ channel_key.ueId = p_rlc_lte_info->ueid;
+ channel_key.channelType = p_rlc_lte_info->channelType;
+ channel_key.channelId = p_rlc_lte_info->channelId;
+ channel_key.direction =
+ (p_rlc_lte_info->direction == DIRECTION_UPLINK) ? DIRECTION_DOWNLINK : DIRECTION_UPLINK;
+
+ /* Do the table lookup */
+ p_channel_status = (rlc_channel_sequence_analysis_status*)g_hash_table_lookup(rlc_lte_sequence_analysis_channel_hash, &channel_key);
+
+ /* Create table entry if necessary */
+ if (p_channel_status == NULL) {
+ return;
+ }
+
+ /* Is it in the rx window? This test will catch if its ahead, but we don't
+ really know what the back of the tx window is... */
+ if (((1024 + p_channel_status->previousSequenceNumber+1 - ack_sn) % 1024) > 512) {
+
+ /* Set result */
+ p_report_in_frame = se_alloc(sizeof(state_sequence_analysis_report_in_frame));
+ p_report_in_frame->state = ACK_Out_of_Window;
+ p_report_in_frame->previousFrameNum = p_channel_status->previousFrameNum;
+ p_report_in_frame->sequenceExpected = p_channel_status->previousSequenceNumber;
+ p_report_in_frame->firstSN = ack_sn;
+
+ /* Associate with this frame number */
+ g_hash_table_insert(rlc_lte_frame_sequence_analysis_report_hash,
+ &pinfo->fd->num, p_report_in_frame);
+
+ /* Add state report for this frame into tree */
+ addChannelSequenceInfo(p_report_in_frame, TRUE, p_rlc_lte_info, 0,
+ FALSE, tap_info, pinfo, tree, tvb);
+ }
+}
+
+
/***************************************************/
/* Create hidden TM root */
tm_ti = proto_tree_add_string_format(tree, hf_rlc_lte_tm,
- tvb, offset, 0, "", "UM");
+ tvb, offset, 0, "", "TM");
PROTO_ITEM_SET_HIDDEN(tm_ti);
/* Remaining bytes are all data */
}
break;
- case CHANNEL_TYPE_BCCH:
- /* TODO: Problem is don't know which transport channel... */
- return;
-
+ case CHANNEL_TYPE_BCCH_BCH:
+ protocol_handle = find_dissector("lte_rrc.bcch_bch");
+ break;
+ case CHANNEL_TYPE_BCCH_DL_SCH:
+ protocol_handle = find_dissector("lte_rrc.bcch_dl_sch");
+ break;
case CHANNEL_TYPE_PCCH:
protocol_handle = find_dissector("lte-rrc.pcch");
break;
tap_info->sequenceNumber = (guint16)sn;
/* Show SN in info column */
- write_pdu_label_and_info(top_ti, NULL, pinfo, " SN=%-4u", (guint16)sn);
+ write_pdu_label_and_info(top_ti, um_header_ti, pinfo, " SN=%-4u", (guint16)sn);
proto_item_set_len(um_header_ti, offset-start_offset);
(p_get_proto_data(pinfo->fd, proto_mac_lte) == NULL))) {
checkChannelSequenceInfo(pinfo, tvb, p_rlc_lte_info,
+ FALSE,
(guint16)sn, first_includes_start, last_includes_end,
FALSE, /* UM doesn't re-segment */
tap_info, um_header_tree);
} while (e1 || e2);
if (nack_count > 0) {
+ proto_item *count_ti = proto_tree_add_uint(tree, hf_rlc_lte_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;
}
/* Set selected length of control tree */
proto_item_set_len(status_ti, offset);
- /* Repeated NACK analysis */
+ /* Repeated NACK analysis & check ACK-SN is in range */
if (((global_rlc_lte_am_sequence_analysis == SEQUENCE_ANALYSIS_MAC_ONLY) &&
(p_get_proto_data(pinfo->fd, proto_mac_lte) != NULL)) ||
((global_rlc_lte_am_sequence_analysis == SEQUENCE_ANALYSIS_RLC_ONLY) &&
if (!is_mac_lte_frame_retx(pinfo, p_rlc_lte_info->direction)) {
checkChannelRepeatedNACKInfo(pinfo, p_rlc_lte_info, tap_info, tree, tvb);
+ checkChannelACKWindow((guint16)ack_sn, pinfo, p_rlc_lte_info, tap_info, tree, tvb);
}
}
}
/* SO */
segmentOffset = tvb_get_ntohs(tvb, offset) & 0x7fff;
proto_tree_add_item(am_header_tree, hf_rlc_lte_am_segment_so, tvb, offset, 2, FALSE);
- write_pdu_label_and_info(top_ti, NULL, pinfo, " SO=%u ", segmentOffset);
+ write_pdu_label_and_info(top_ti, am_header_ti, pinfo, " SO=%u ", segmentOffset);
offset += 2;
}
((global_rlc_lte_am_sequence_analysis == SEQUENCE_ANALYSIS_RLC_ONLY) &&
(p_get_proto_data(pinfo->fd, proto_mac_lte) == NULL))) {
- checkChannelSequenceInfo(pinfo, tvb, p_rlc_lte_info, (guint16)sn,
+ checkChannelSequenceInfo(pinfo, tvb, p_rlc_lte_info, FALSE, (guint16)sn,
first_includes_start, last_includes_end,
is_resegmented, tap_info, tree);
}
}
+/* Configure number of PDCP SN bits to use for DRB channels.
+ TODO: currently assume all UEs/Channels will use the same length... */
+void set_rlc_lte_drb_pdcp_seqnum_length(guint16 ueid _U_, guint8 drbid _U_, guint8 userplane_seqnum_length)
+{
+ signalled_pdcp_sn_bits = userplane_seqnum_length;
+}
+
void proto_register_rlc_lte(void)
NULL, HFILL
}
},
+ { &hf_rlc_lte_am_nacks,
+ { "Number of NACKs",
+ "rlc-lte.am.nacks", FT_UINT16, BASE_DEC, 0, 0x0,
+ "Number of NACKs in this status PDU", HFILL
+ }
+ },
{ &hf_rlc_lte_am_nack_sn,
{ "NACK Sequence Number",
"rlc-lte.am.nack-sn", FT_UINT16, BASE_DEC, 0, 0x0,
}
},
+ { &hf_rlc_lte_sequence_analysis_ack_out_of_range,
+ { "Out of range ACK",
+ "rlc-lte.sequence-analysis.ack-out-of-range", FT_BOOLEAN, BASE_NONE, 0, 0x0,
+ NULL, HFILL
+ }
+ },
+ { &hf_rlc_lte_sequence_analysis_ack_out_of_range_opposite_frame,
+ { "Frame with most recent SN",
+ "rlc-lte.sequence-analysis.ack-out-of-range.last-sn-frame", FT_FRAMENUM, BASE_NONE, 0, 0x0,
+ NULL, HFILL
+ }
+ },
+
{ &hf_rlc_lte_header_only,
{ "RLC PDU Header only",
"rlc-lte.header-only", FT_UINT8, BASE_DEC, VALS(header_only_vals), 0x0,