1 /* Routines for LTE RLC disassembly
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 #include <epan/packet.h>
33 #include <epan/expert.h>
34 #include <epan/prefs.h>
36 #include "packet-rlc-lte.h"
40 * 3GPP TS 36.322 Evolved Universal Terrestial Radio Access (E-UTRA)
41 * Radio Link Control (RLC) Protocol specification
45 - AM sequence analysis/re-assembly?
49 /* By default try to analyse the sequence of messages for UM channels */
50 static gboolean global_rlc_lte_sequence_analysis = TRUE;
54 /* Initialize the protocol and registered fields. */
55 int proto_rlc_lte = -1;
57 /* Decoding context */
58 static int hf_rlc_lte_context_mode = -1;
59 static int hf_rlc_lte_context_direction = -1;
60 static int hf_rlc_lte_context_priority = -1;
61 static int hf_rlc_lte_context_ueid = -1;
62 static int hf_rlc_lte_context_channel_type = -1;
63 static int hf_rlc_lte_context_channel_id = -1;
64 static int hf_rlc_lte_context_pdu_length = -1;
65 static int hf_rlc_lte_context_um_sn_length = -1;
67 /* Transparent mode fields */
68 static int hf_rlc_lte_tm_data = -1;
70 /* Unacknowledged mode fields */
71 static int hf_rlc_lte_um_header = -1;
72 static int hf_rlc_lte_um_fi = -1;
73 static int hf_rlc_lte_um_fixed_e = -1;
74 static int hf_rlc_lte_um_sn = -1;
75 static int hf_rlc_lte_um_fixed_reserved = -1;
76 static int hf_rlc_lte_um_data = -1;
77 static int hf_rlc_lte_extension_part = -1;
79 /* Extended header (common to UM and AM) */
80 static int hf_rlc_lte_extension_e = -1;
81 static int hf_rlc_lte_extension_li = -1;
82 static int hf_rlc_lte_extension_padding = -1;
85 /* Acknowledged mode fields */
86 static int hf_rlc_lte_am_header = -1;
87 static int hf_rlc_lte_am_data_control = -1;
88 static int hf_rlc_lte_am_rf = -1;
89 static int hf_rlc_lte_am_p = -1;
90 static int hf_rlc_lte_am_fi = -1;
91 static int hf_rlc_lte_am_fixed_e = -1;
92 static int hf_rlc_lte_am_fixed_sn = -1;
93 static int hf_rlc_lte_am_segment_lsf = -1;
94 static int hf_rlc_lte_am_segment_so = -1;
95 static int hf_rlc_lte_am_data = -1;
98 static int hf_rlc_lte_am_cpt = -1;
99 static int hf_rlc_lte_am_ack_sn = -1;
100 static int hf_rlc_lte_am_e1 = -1;
101 static int hf_rlc_lte_am_e2 = -1;
102 static int hf_rlc_lte_am_nack_sn = -1;
103 static int hf_rlc_lte_am_so_start = -1;
104 static int hf_rlc_lte_am_so_end = -1;
106 static int hf_rlc_lte_predefined_pdu = -1;
108 /* Sequence Analysis */
109 static int hf_rlc_lte_sequence_analysis = -1;
110 static int hf_rlc_lte_sequence_analysis_previous_frame = -1;
111 static int hf_rlc_lte_sequence_analysis_expected_sn = -1;
112 static int hf_rlc_lte_sequence_analysis_framing_info_correct = -1;
116 static int ett_rlc_lte = -1;
117 static int ett_rlc_lte_um_header = -1;
118 static int ett_rlc_lte_am_header = -1;
119 static int ett_rlc_lte_extension_part = -1;
120 static int ett_rlc_lte_sequence_analysis = -1;
123 static const value_string direction_vals[] =
125 { DIRECTION_UPLINK, "Uplink"},
126 { DIRECTION_DOWNLINK, "Downlink"},
130 static const value_string rlc_mode_short_vals[] =
132 { RLC_TM_MODE, "TM"},
133 { RLC_UM_MODE, "UM"},
134 { RLC_AM_MODE, "AM"},
135 { RLC_PREDEF, "PREDEFINED"},
139 static const value_string rlc_mode_vals[] =
141 { RLC_TM_MODE, "Transparent Mode"},
142 { RLC_UM_MODE, "Unacknowledged Mode"},
143 { RLC_AM_MODE, "Acknowledged Mode"},
148 static const value_string rlc_channel_type_vals[] =
150 { CHANNEL_TYPE_CCCH, "CCCH"},
151 { CHANNEL_TYPE_BCCH, "BCCH"},
152 { CHANNEL_TYPE_PCCH, "PCCH"},
153 { CHANNEL_TYPE_SRB, "SRB"},
154 { CHANNEL_TYPE_DRB, "DRB"},
159 static const value_string framing_info_vals[] =
161 { 0, "First byte begins an RLC SDU and last byte ends an RLC SDU"},
162 { 1, "First byte begins an RLC SDU and last byte does not end an RLC SDU"},
163 { 2, "First byte does not begin an RLC SDU and last byte ends an RLC SDU"},
164 { 3, "First byte does not begin an RLC SDU and last byte does not end an RLC SDU"},
168 static const value_string fixed_extension_vals[] =
170 { 0, "Data field follows from the octet following the fixed part of the header"},
171 { 1, "A set of E field and LI field follows from the octet following the fixed part of the header"},
175 static const value_string extension_extension_vals[] =
177 { 0, "Data field follows from the octet following the LI field following this E field"},
178 { 1, "A set of E field and LI field follows from the bit following the LI field following this E field"},
182 static const value_string data_or_control_vals[] =
189 static const value_string resegmentation_flag_vals[] =
192 { 1, "AND PDU segment"},
196 static const value_string polling_bit_vals[] =
198 { 0, "Status report not requested"},
199 { 1, "Status report is requested"},
204 static const value_string lsf_vals[] =
206 { 0, "Last byte of the AMD PDU segment does not correspond to the last byte of an AMD PDU"},
207 { 1, "Last byte of the AMD PDU segment corresponds to the last byte of an AND PDU"},
212 static const value_string control_pdu_type_vals[] =
218 static const value_string am_e1_vals[] =
220 { 0, "A set of NACK_SN, E1 and E2 does not follow"},
221 { 1, "A set of NACK_SN, E1 and E2 follows"},
225 static const value_string am_e2_vals[] =
227 { 0, "A set of SOstart and SOend does not follow for this NACK_SN"},
228 { 1, "A set of SOstart and SOend follows for this NACK_SN"},
234 /**********************************************************************************/
235 /* These are for keeping track of UM/AM extension headers, and the lengths found */
237 guint8 s_number_of_extensions = 0;
238 #define MAX_RLC_SDUS 64
239 guint16 s_lengths[MAX_RLC_SDUS];
242 /* Dissect extension headers (common to both UM and AM) */
243 static int dissect_rlc_lte_extension_header(tvbuff_t *tvb, packet_info *pinfo,
248 guint64 extension = 1;
251 /* Reset this count */
252 s_number_of_extensions = 0;
254 while (extension && (s_number_of_extensions < MAX_RLC_SDUS)) {
255 proto_tree *extension_part_tree;
256 proto_item *extension_part_ti;
258 isOdd = (s_number_of_extensions % 2);
260 /* Extension part subtree */
261 extension_part_ti = proto_tree_add_string_format(tree,
262 hf_rlc_lte_extension_part,
266 extension_part_tree = proto_item_add_subtree(extension_part_ti,
267 ett_rlc_lte_extension_part);
269 /* Read next extension */
270 proto_tree_add_bits_ret_val(extension_part_tree, hf_rlc_lte_extension_e, tvb,
271 (offset*8) + ((isOdd) ? 4 : 0),
275 /* Read length field */
276 proto_tree_add_bits_ret_val(extension_part_tree, hf_rlc_lte_extension_li, tvb,
277 (offset*8) + ((isOdd) ? 5 : 1),
281 proto_item_append_text(extension_part_tree, " (length=%u)", (guint16)length);
283 /* Move on to byte of next extension */
290 s_lengths[s_number_of_extensions++] = (guint16)length;
293 /* May need to skip padding after last extension part */
294 isOdd = (s_number_of_extensions % 2);
299 padding = tvb_get_guint8(tvb, offset) & 0x0f;
300 ti = proto_tree_add_item(tree, hf_rlc_lte_extension_padding,
301 tvb, offset, 1, FALSE);
303 expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR,
304 "Extension Header padding not zero (found 0x%x)", padding);
313 /* Show in the info column how many bytes are in the UM/AM PDU, and indicate
314 whether or not the beginning and end are included in this packet */
315 static void show_PDU_in_info(packet_info *pinfo,
318 gboolean first_includes_start,
319 gboolean last_includes_end)
321 /* Reflect this PDU in the info column */
322 col_append_fstr(pinfo->cinfo, COL_INFO, " %s%u-byte%s%s",
323 (first_includes_start) ? "[" : "..",
325 (length > 1) ? "s" : "",
326 (last_includes_end) ? "]" : "..");
328 proto_item_append_text(top_ti, " %s%u-byte%s%s",
329 (first_includes_start) ? "[" : "..",
331 (length > 1) ? "s" : "",
332 (last_includes_end) ? "]" : "..");
337 /*********************************************************************/
338 /* UM/AM sequence analysis */
340 /* Types for RLC channel hash table */
341 /* This table is maintained during initial dissection of RLC */
342 /* frames, mapping from rlc_channel_hash_key -> rlc_channel_status */
351 } rlc_channel_hash_key;
353 /* Conversation-type status for channel */
356 guint16 previousSequenceNumber;
357 guint32 previousFrameNum;
358 gboolean previousSegmentIncomplete;
359 } rlc_channel_status;
362 /* Hash table functions for RLC channels */
365 static gint rlc_channel_equal(gconstpointer v, gconstpointer v2)
367 const rlc_channel_hash_key* val1 = v;
368 const rlc_channel_hash_key* val2 = v2;
370 /* All fields must match */
371 return ((val1->ueId == val2->ueId) &&
372 (val1->channelType == val2->channelType) &&
373 (val1->channelId == val2->channelId) &&
374 (val1->direction == val2->direction));
377 /* Compute a hash value for a given key. */
378 static guint rlc_channel_hash_func(gconstpointer v)
380 const rlc_channel_hash_key* val1 = v;
382 /* TODO: check/reduce multipliers */
383 return ((val1->ueId * 1024) + (val1->channelType*64) + (val1->channelId*2) + val1->direction);
386 /* The channel hash table instance itself */
387 static GHashTable *rlc_lte_channel_hash = NULL;
392 /* Types for frame report hash table */
393 /* This is a table from framenum -> state_report_in_frame */
394 /* This is necessary because the per-packet info is already being used */
395 /* for conext information before the dissector is called */
397 /* Info to attach to frame when first read, recording what to show about sequence */
400 guint8 sequenceExpectedCorrect;
401 guint16 sequenceExpected;
402 guint32 previousFrameNum;
403 guint8 previousSegmentIncomplete;
404 } state_report_in_frame;
407 /* Hash table functions for frame reports */
410 static gint rlc_frame_equal(gconstpointer v, gconstpointer v2)
415 /* Compute a hash value for a given key. */
416 static guint rlc_frame_hash_func(gconstpointer v)
418 return GPOINTER_TO_UINT(v);
421 /* The frame report hash table instance itself */
422 static GHashTable *rlc_lte_frame_report_hash = NULL;
426 /* Add to the tree values associated with sequence analysis for this frame */
427 static void addChannelSequenceInfo(state_report_in_frame *p,
428 guint16 sequenceNumber,
429 gboolean newSegmentStarted,
430 packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb)
432 proto_tree *seqnum_tree;
433 proto_item *seqnum_ti;
437 seqnum_ti = proto_tree_add_string_format(tree,
438 hf_rlc_lte_sequence_analysis,
441 "Sequence Analysis");
442 seqnum_tree = proto_item_add_subtree(seqnum_ti,
443 ett_rlc_lte_sequence_analysis);
444 PROTO_ITEM_SET_GENERATED(seqnum_ti);
446 /* Previous channel frame */
447 if (p->previousFrameNum != 0) {
448 proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_previous_frame,
449 tvb, 0, 0, p->previousFrameNum);
452 /* Expected sequence number */
453 ti = proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_expected_sn,
454 tvb, 0, 0, p->sequenceExpected);
455 PROTO_ITEM_SET_GENERATED(ti);
456 if (!p->sequenceExpectedCorrect) {
457 /* Incorrect sequence number */
458 expert_add_info_format(pinfo, ti, PI_SEQUENCE, PI_WARN,
459 "Wrong Sequence Number - got %u, expected %u",
460 sequenceNumber, p->sequenceExpected);
463 /* Correct sequence number, so check frame indication bits consistent */
464 if (p->previousSegmentIncomplete) {
465 /* Previous segment was incomplete, so this PDU should continue it */
466 if (newSegmentStarted) {
467 ti = proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_framing_info_correct,
469 if (!p->sequenceExpectedCorrect) {
470 expert_add_info_format(pinfo, ti, PI_SEQUENCE, PI_WARN,
471 "Last segment of previous PDU was not continued");
475 ti = proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_framing_info_correct,
480 /* Previous segment was complete, so this PDU should start a new one */
481 if (!newSegmentStarted) {
482 ti = proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_framing_info_correct,
484 if (!p->sequenceExpectedCorrect) {
485 expert_add_info_format(pinfo, ti, PI_SEQUENCE, PI_WARN,
486 "Last segment of previous PDU was complete, but new segmeng was not started");
490 ti = proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_framing_info_correct,
495 PROTO_ITEM_SET_GENERATED(ti);
499 /* Update the channel status and set report for this frame */
500 static void checkChannelSequenceInfo(packet_info *pinfo, tvbuff_t *tvb,
501 rlc_lte_info *p_rlc_lte_info,
502 guint16 sequenceNumber,
503 gboolean first_includes_start, gboolean last_includes_end,
506 rlc_channel_hash_key channel_key;
507 rlc_channel_hash_key *p_channel_key;
508 rlc_channel_status *p_channel_status;
509 state_report_in_frame *p_report_in_frame = NULL;
510 guint8 createdChannel = FALSE;
511 guint16 expectedSequenceNumber;
513 /* If find stat_report_in_frame already, use that and get out */
514 if (pinfo->fd->flags.visited) {
515 p_report_in_frame = (state_report_in_frame*)g_hash_table_lookup(rlc_lte_frame_report_hash,
517 if (p_report_in_frame != NULL) {
518 addChannelSequenceInfo(p_report_in_frame, sequenceNumber, first_includes_start,
523 /* Give up - we must have tried already... */
529 /**************************************************/
530 /* Create or find an entry for this channel state */
531 channel_key.ueId = p_rlc_lte_info->ueid;
532 channel_key.channelType = p_rlc_lte_info->channelType;
533 channel_key.channelId = p_rlc_lte_info->channelId;
534 channel_key.direction = p_rlc_lte_info->direction;
536 /* Do the table lookup */
537 p_channel_status = (rlc_channel_status*)g_hash_table_lookup(rlc_lte_channel_hash, &channel_key);
539 /* Create table entry if necessary */
540 if (p_channel_status == NULL) {
541 createdChannel = TRUE;
543 /* Allocate a new key and value */
544 p_channel_key = se_alloc(sizeof(rlc_channel_hash_key));
545 p_channel_status = se_alloc0(sizeof(rlc_channel_status));
547 /* Just give up if allocations failed */
548 if (!p_channel_key || !p_channel_status) {
552 /* Copy key contents */
553 memcpy(p_channel_key, &channel_key, sizeof(rlc_channel_hash_key));
556 g_hash_table_insert(rlc_lte_channel_hash, p_channel_key, p_channel_status);
559 /* Create space for frame state_report */
560 p_report_in_frame = se_alloc(sizeof(state_report_in_frame));
562 /* Set expected sequence number.
563 Wrap according to number of bits in SN */
564 if (!createdChannel) {
565 guint16 snLimit = 4096; /* AM default */
566 if (p_rlc_lte_info->rlcMode == RLC_UM_MODE) {
567 if (p_rlc_lte_info->UMSequenceNumberLength == 5) {
574 expectedSequenceNumber = (p_channel_status->previousSequenceNumber + 1) % snLimit;
577 expectedSequenceNumber = 0;
580 /* Set report info regarding sequence number */
581 if (sequenceNumber == expectedSequenceNumber) {
582 p_report_in_frame->sequenceExpectedCorrect = TRUE;
585 p_report_in_frame->sequenceExpectedCorrect = FALSE;
587 p_report_in_frame->sequenceExpected = expectedSequenceNumber;
588 p_report_in_frame->previousFrameNum = p_channel_status->previousFrameNum;
589 p_report_in_frame->previousSegmentIncomplete = p_channel_status->previousSegmentIncomplete;
591 /* Associate with this frame number */
592 g_hash_table_insert(rlc_lte_frame_report_hash, &pinfo->fd->num, p_report_in_frame);
594 /* Update channel status to remember *this* frame */
595 p_channel_status->previousFrameNum = pinfo->fd->num;
596 p_channel_status->previousSequenceNumber = sequenceNumber;
597 p_channel_status->previousSegmentIncomplete = !last_includes_end;
599 /* Add state report for this frame into tree */
600 addChannelSequenceInfo(p_report_in_frame, sequenceNumber, first_includes_start,
608 /***************************************************/
609 /* Unacknowledged mode PDU */
610 static void dissect_rlc_lte_um(tvbuff_t *tvb, packet_info *pinfo,
613 rlc_lte_info *p_rlc_lte_info,
616 guint64 framing_info;
617 gboolean first_includes_start;
618 gboolean last_includes_end;
619 guint64 fixed_extension;
621 gint start_offset = offset;
622 proto_tree *um_header_tree;
623 proto_item *um_header_ti;
625 /* Add UM header subtree */
626 um_header_ti = proto_tree_add_string_format(tree,
627 hf_rlc_lte_um_header,
631 um_header_tree = proto_item_add_subtree(um_header_ti,
632 ett_rlc_lte_um_header);
635 /*******************************/
636 /* Fixed UM header */
637 if (p_rlc_lte_info->UMSequenceNumberLength == UM_SN_LENGTH_5_BITS) {
638 /* Framing info (2 bits) */
639 proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_lte_um_fi,
641 &framing_info, FALSE);
643 /* Extension (1 bit) */
644 proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_lte_um_fixed_e, tvb,
646 &fixed_extension, FALSE);
648 /* Sequence Number (5 bit) */
649 proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_lte_um_sn, tvb,
654 else if (p_rlc_lte_info->UMSequenceNumberLength == UM_SN_LENGTH_10_BITS) {
658 /* Check 3 Reserved bits */
659 reserved = (tvb_get_guint8(tvb, offset) & 0xe0) >> 5;
660 ti = proto_tree_add_item(um_header_tree, hf_rlc_lte_um_fixed_reserved, tvb, offset, 1, FALSE);
662 expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR,
663 "RLC UM Fixed header Reserved bits not zero (found 0x%x)", reserved);
666 /* Framing info (2 bits) */
667 proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_lte_um_fi,
668 tvb, (offset*8)+3, 2,
669 &framing_info, FALSE);
671 /* Extension (1 bit) */
672 proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_lte_um_fixed_e, tvb,
674 &fixed_extension, FALSE);
676 /* Sequence Number (10 bits) */
677 proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_lte_um_sn, tvb,
683 /* Invalid length of sequence number */
685 ti = proto_tree_add_text(um_header_tree, tvb, 0, 0, "Invalid sequence number length (%u bits)",
686 p_rlc_lte_info->UMSequenceNumberLength);
687 expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR,
688 "Invalid sequence number length (%u bits)",
689 p_rlc_lte_info->UMSequenceNumberLength);
693 /* Show SN in info column */
694 col_append_fstr(pinfo->cinfo, COL_INFO, " SN=%04u", (guint16)sn);
696 /* Show SN in UM header root */
697 proto_item_append_text(um_header_ti, " (SN=%u)", (guint16)sn);
698 proto_item_set_len(um_header_ti, offset-start_offset);
701 /*************************************/
702 /* UM header extension */
703 if (fixed_extension) {
704 offset = dissect_rlc_lte_extension_header(tvb, pinfo, tree, offset);
708 /* Extract these 2 flags from framing_info */
709 first_includes_start = ((guint8)framing_info & 0x02) == 0;
710 last_includes_end = ((guint8)framing_info & 0x01) == 0;
713 /* Call sequence analysis function now */
714 if (global_rlc_lte_sequence_analysis) {
715 checkChannelSequenceInfo(pinfo, tvb, p_rlc_lte_info,
716 (guint16)sn, first_includes_start, last_includes_end,
721 /*************************************/
723 if (s_number_of_extensions > 0) {
724 /* Show each data segment separately */
726 for (n=0; n < s_number_of_extensions; n++) {
727 proto_tree_add_item(tree, hf_rlc_lte_um_data, tvb, offset, s_lengths[n], FALSE);
728 show_PDU_in_info(pinfo, top_ti, s_lengths[n],
729 (n==0) ? first_includes_start : TRUE,
731 tvb_ensure_bytes_exist(tvb, offset, s_lengths[n]);
732 offset += s_lengths[n];
736 /* Final data element */
737 proto_tree_add_item(tree, hf_rlc_lte_um_data, tvb, offset, -1, FALSE);
738 show_PDU_in_info(pinfo, top_ti, (guint16)tvb_length_remaining(tvb, offset),
739 (s_number_of_extensions == 0) ? first_includes_start : TRUE,
746 /* Dissect an AM STATUS PDU */
747 static void dissect_rlc_lte_am_status_pdu(tvbuff_t *tvb,
750 proto_item *status_ti,
755 guint64 ack_sn, nack_sn;
756 guint64 e1 = 0, e2 = 0;
757 guint64 so_start, so_end;
758 int bit_offset = offset * 8;
761 /****************************************************************/
762 /* Part of RLC control PDU header */
764 /* Control PDU Type (CPT) */
765 cpt = (tvb_get_guint8(tvb, offset) & 0xf0) >> 4;
766 ti = proto_tree_add_item(tree, hf_rlc_lte_am_cpt, tvb, offset, 1, FALSE);
768 /* Protest and stop - only know about STATUS PDUs */
769 expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR,
770 "RLC Control frame type %u not handled", cpt);
775 /*****************************************************************/
778 /* The PDU itself starts 4 bits into the byte */
782 proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_ack_sn, tvb,
783 bit_offset, 10, &ack_sn, FALSE);
785 col_append_fstr(pinfo->cinfo, COL_INFO, " ACK_SN=%u", (guint16)ack_sn);
786 proto_item_append_text(top_ti, " ACK_SN=%u", (guint16)ack_sn);
789 proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_e1, tvb,
790 bit_offset, 1, &e1, FALSE);
792 /* Skip another bit to byte-align the next bit... */
795 /* Optional, extra fields */
800 /****************************/
801 /* Read NACK_SN, E1, E2 */
804 nack_ti = proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_nack_sn, tvb,
805 bit_offset, 10, &nack_sn, FALSE);
807 col_append_fstr(pinfo->cinfo, COL_INFO, " NACK_SN=%u", (guint16)nack_sn);
808 proto_item_append_text(top_ti, " NACK_SN=%u", (guint16)nack_sn);
809 expert_add_info_format(pinfo, nack_ti, PI_SEQUENCE, PI_WARN,
810 "Status PDU reports NACK for SN=%u", (guint16)nack_sn);
814 proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_e1, tvb,
815 bit_offset, 1, &e1, FALSE);
819 proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_e2, tvb,
820 bit_offset, 1, &e2, FALSE);
825 /* Read SOstart, SOend */
826 proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_so_start, tvb,
827 bit_offset, 15, &so_start, FALSE);
830 proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_so_end, tvb,
831 bit_offset, 15, &so_end, FALSE);
835 if ((guint16)so_end == 0x7fff) {
836 col_append_fstr(pinfo->cinfo, COL_INFO, " (SOstart=%u SOend=<END-OF_PDU>)",
840 col_append_fstr(pinfo->cinfo, COL_INFO, " (SOstart=%u SOend=%u)",
841 (guint16)so_start, (guint16)so_end);
844 /* Reset this flag here */
849 /* Check that we've reached the end of the PDU. If not, show malformed */
850 offset = (bit_offset+7) / 8;
851 if (tvb_length_remaining(tvb, offset) > 0) {
852 expert_add_info_format(pinfo, status_ti, PI_MALFORMED, PI_ERROR,
853 "%u bytes remaining after Status PDU complete",
854 tvb_length_remaining(tvb, offset));
857 /* Set selected length of control tree */
858 proto_item_set_len(status_ti, offset);
862 /***************************************************/
863 /* Acknowledged mode PDU */
864 static void dissect_rlc_lte_am(tvbuff_t *tvb, packet_info *pinfo,
867 rlc_lte_info *p_rlc_lte_info _U_,
873 guint8 fixed_extension;
875 gboolean first_includes_start;
876 gboolean last_includes_end;
877 proto_tree *am_header_tree;
878 proto_item *am_header_ti;
879 gint start_offset = offset;
882 /* Add UM header subtree */
883 am_header_ti = proto_tree_add_string_format(tree,
884 hf_rlc_lte_am_header,
888 am_header_tree = proto_item_add_subtree(am_header_ti,
889 ett_rlc_lte_am_header);
892 /*******************************************/
893 /* First bit is Data/Control flag */
894 is_data = (tvb_get_guint8(tvb, offset) & 0x80) >> 7;
895 proto_tree_add_item(am_header_tree, hf_rlc_lte_am_data_control, tvb, offset, 1, FALSE);
897 /**************************************************/
899 col_append_str(pinfo->cinfo, COL_INFO, " [CONTROL]");
900 proto_item_append_text(top_ti, " [CONTROL]");
902 /* Control PDUs are a completely separate format */
903 dissect_rlc_lte_am_status_pdu(tvb, pinfo, am_header_tree, am_header_ti, offset, top_ti);
907 /******************************/
908 /* Data PDU fixed header */
910 /* Re-segmentation Flag (RF) field */
911 is_segment = (tvb_get_guint8(tvb, offset) & 0x40) >> 6;
912 proto_tree_add_item(am_header_tree, hf_rlc_lte_am_rf, tvb, offset, 1, FALSE);
914 col_append_str(pinfo->cinfo, COL_INFO, (is_segment) ? " [DATA-SEGMENT]" : " [DATA]");
915 proto_item_append_text(top_ti, (is_segment) ? " [DATA-SEGMENT]" : " [DATA]");
919 polling = (tvb_get_guint8(tvb, offset) & 0x20) >> 5;
920 proto_tree_add_item(am_header_tree, hf_rlc_lte_am_p, tvb, offset, 1, FALSE);
921 col_append_str(pinfo->cinfo, COL_INFO, (polling) ? " (P) " : " ");
922 proto_item_append_text(top_ti, (polling) ? " (P) " : " ");
924 proto_item_append_text(am_header_ti, " (P)");
928 framing_info = (tvb_get_guint8(tvb, offset) & 0x18) >> 3;
929 proto_tree_add_item(am_header_tree, hf_rlc_lte_am_fi, tvb, offset, 1, FALSE);
932 fixed_extension = (tvb_get_guint8(tvb, offset) & 0x04) >> 2;
933 proto_tree_add_item(am_header_tree, hf_rlc_lte_am_fixed_e, tvb, offset, 1, FALSE);
935 /* Sequence Number */
936 sn = tvb_get_ntohs(tvb, offset) & 0x03ff;
937 proto_tree_add_item(am_header_tree, hf_rlc_lte_am_fixed_sn, tvb, offset, 2, FALSE);
940 col_append_fstr(pinfo->cinfo, COL_INFO, "sn=%u", sn);
941 proto_item_append_text(top_ti, " (SN=%u)", sn);
943 /* Show SN in AM header root */
944 proto_item_append_text(am_header_ti, " (SN=%u)", sn);
945 proto_item_set_len(am_header_ti, offset-start_offset);
947 /***************************************/
948 /* Dissect extra segment header fields */
950 guint16 segmentOffset;
952 /* Last Segment Field (LSF) */
953 proto_tree_add_item(am_header_tree, hf_rlc_lte_am_segment_lsf, tvb, offset, 1, FALSE);
956 segmentOffset = tvb_get_ntohs(tvb, offset) & 0x7fff;
957 proto_tree_add_item(am_header_tree, hf_rlc_lte_am_segment_so, tvb, offset, 2, FALSE);
958 col_append_fstr(pinfo->cinfo, COL_INFO, " SO=%u ", segmentOffset);
959 proto_item_append_text(top_ti, " SO=%u ", segmentOffset);
964 /*************************************/
965 /* AM header extension */
966 if (fixed_extension) {
967 offset = dissect_rlc_lte_extension_header(tvb, pinfo, tree, offset);
971 /* Extract these 2 flags from framing_info */
972 first_includes_start = (framing_info & 0x02) == 0;
973 last_includes_end = (framing_info & 0x01) == 0;
976 /* Call sequence analysis function now (pretty limited for AM) */
978 if (global_rlc_lte_sequence_analysis) {
979 checkChannelSequenceInfo(pinfo, tvb, p_rlc_lte_info, (guint16)sn,
980 first_includes_start, last_includes_end,
986 /*************************************/
988 if (s_number_of_extensions > 0) {
989 /* Show each data segment separately */
991 for (n=0; n < s_number_of_extensions; n++) {
992 proto_tree_add_item(tree, hf_rlc_lte_am_data, tvb, offset, s_lengths[n], FALSE);
993 show_PDU_in_info(pinfo, top_ti, s_lengths[n],
994 (n==0) ? first_includes_start : TRUE,
996 tvb_ensure_bytes_exist(tvb, offset, s_lengths[n]);
997 offset += s_lengths[n];
1001 /* Final data element */
1002 if (tvb_length_remaining(tvb, offset) > 0) {
1003 proto_tree_add_item(tree, hf_rlc_lte_am_data, tvb, offset, -1, FALSE);
1004 show_PDU_in_info(pinfo, top_ti, (guint16)tvb_length_remaining(tvb, offset),
1005 (s_number_of_extensions == 0) ? first_includes_start : TRUE,
1009 expert_add_info_format(pinfo, am_header_ti, PI_MALFORMED, PI_WARN,
1010 "AM data PDU doesn't contain any data");
1017 /*****************************/
1018 /* Main dissection function. */
1019 /*****************************/
1021 void dissect_rlc_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1023 proto_tree *rlc_lte_tree;
1026 proto_item *mode_ti;
1028 struct rlc_lte_info *p_rlc_lte_info = NULL;
1030 /* Set protocol name */
1031 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RLC-LTE");
1033 /* Create protocol tree. */
1034 top_ti = proto_tree_add_item(tree, proto_rlc_lte, tvb, offset, -1, FALSE);
1035 rlc_lte_tree = proto_item_add_subtree(top_ti, ett_rlc_lte);
1038 /* Look for packet info! */
1039 p_rlc_lte_info = p_get_proto_data(pinfo->fd, proto_rlc_lte);
1041 /* Can't dissect anything without it... */
1042 if (p_rlc_lte_info == NULL) {
1044 proto_tree_add_text(rlc_lte_tree, tvb, offset, -1,
1045 "Can't dissect LTE RLC frame because no per-frame info was attached!");
1046 PROTO_ITEM_SET_GENERATED(ti);
1050 /*****************************************/
1051 /* Show context information */
1052 /* TODO: hide inside own tree? */
1054 ti = proto_tree_add_uint(rlc_lte_tree, hf_rlc_lte_context_direction,
1055 tvb, 0, 0, p_rlc_lte_info->direction);
1056 PROTO_ITEM_SET_GENERATED(ti);
1058 mode_ti = proto_tree_add_uint(rlc_lte_tree, hf_rlc_lte_context_mode,
1059 tvb, 0, 0, p_rlc_lte_info->rlcMode);
1060 PROTO_ITEM_SET_GENERATED(mode_ti);
1062 if (p_rlc_lte_info->ueid != 0) {
1063 ti = proto_tree_add_uint(rlc_lte_tree, hf_rlc_lte_context_ueid,
1064 tvb, 0, 0, p_rlc_lte_info->ueid);
1065 PROTO_ITEM_SET_GENERATED(ti);
1068 ti = proto_tree_add_uint(rlc_lte_tree, hf_rlc_lte_context_priority,
1069 tvb, 0, 0, p_rlc_lte_info->priority);
1070 PROTO_ITEM_SET_GENERATED(ti);
1072 ti = proto_tree_add_uint(rlc_lte_tree, hf_rlc_lte_context_channel_type,
1073 tvb, 0, 0, p_rlc_lte_info->channelType);
1074 PROTO_ITEM_SET_GENERATED(ti);
1076 if ((p_rlc_lte_info->channelType == CHANNEL_TYPE_SRB) ||
1077 (p_rlc_lte_info->channelType == CHANNEL_TYPE_DRB)) {
1078 ti = proto_tree_add_uint(rlc_lte_tree, hf_rlc_lte_context_channel_id,
1079 tvb, 0, 0, p_rlc_lte_info->channelId);
1080 PROTO_ITEM_SET_GENERATED(ti);
1083 ti = proto_tree_add_uint(rlc_lte_tree, hf_rlc_lte_context_pdu_length,
1084 tvb, 0, 0, p_rlc_lte_info->pduLength);
1085 PROTO_ITEM_SET_GENERATED(ti);
1087 if (p_rlc_lte_info->rlcMode == RLC_UM_MODE) {
1088 ti = proto_tree_add_uint(rlc_lte_tree, hf_rlc_lte_context_um_sn_length,
1089 tvb, 0, 0, p_rlc_lte_info->UMSequenceNumberLength);
1090 PROTO_ITEM_SET_GENERATED(ti);
1093 /* Append highlights to top-level item */
1094 if (p_rlc_lte_info->ueid != 0) {
1095 proto_item_append_text(top_ti, " UEId=%u", p_rlc_lte_info->ueid);
1098 if (p_rlc_lte_info->channelId == 0) {
1099 proto_item_append_text(top_ti, " (%s) ",
1100 val_to_str(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"));
1103 proto_item_append_text(top_ti, " (%s:%u) ",
1104 val_to_str(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"),
1105 p_rlc_lte_info->channelId);
1107 proto_item_append_text(top_ti, "[%s] ",
1108 val_to_str(p_rlc_lte_info->rlcMode, rlc_mode_short_vals, "Unknown"));
1111 /* Append context highlights to info column */
1112 col_add_fstr(pinfo->cinfo, COL_INFO,
1114 (p_rlc_lte_info->direction == 0) ? "UL" : "DL",
1115 val_to_str(p_rlc_lte_info->rlcMode, rlc_mode_short_vals, "Unknown"));
1116 if (p_rlc_lte_info->ueid != 0) {
1117 col_append_fstr(pinfo->cinfo, COL_INFO, "UEId=%u ", p_rlc_lte_info->ueid);
1119 if (p_rlc_lte_info->channelId == 0) {
1120 col_append_fstr(pinfo->cinfo, COL_INFO, "%s",
1121 val_to_str(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"));
1124 col_append_fstr(pinfo->cinfo, COL_INFO, "%s:%u",
1125 val_to_str(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"),
1126 p_rlc_lte_info->channelId);
1129 /* Reset this count */
1130 s_number_of_extensions = 0;
1132 /* Dissect the RLC PDU itself. Format depends upon mode... */
1133 switch (p_rlc_lte_info->rlcMode) {
1136 /* Remaining bytes are all data */
1137 proto_tree_add_item(rlc_lte_tree, hf_rlc_lte_tm_data, tvb, offset, -1, FALSE);
1138 col_append_fstr(pinfo->cinfo, COL_INFO, " [%u-bytes]",
1139 tvb_length_remaining(tvb, offset));
1143 dissect_rlc_lte_um(tvb, pinfo, rlc_lte_tree, offset, p_rlc_lte_info, top_ti);
1147 dissect_rlc_lte_am(tvb, pinfo, rlc_lte_tree, offset, p_rlc_lte_info, top_ti);
1151 /* Predefined data (i.e. not containing a valid RLC header */
1152 proto_tree_add_item(rlc_lte_tree, hf_rlc_lte_predefined_pdu, tvb, offset, -1, FALSE);
1153 col_append_fstr(pinfo->cinfo, COL_INFO, " [%u-bytes]",
1154 tvb_length_remaining(tvb, offset));
1158 /* Error - unrecognised mode */
1159 expert_add_info_format(pinfo, mode_ti, PI_MALFORMED, PI_ERROR,
1160 "Unrecognised RLC Mode set (%u)", p_rlc_lte_info->rlcMode);
1167 /* Initializes the hash table and the mem_chunk area each time a new
1168 * file is loaded or re-loaded in wireshark */
1170 rlc_lte_init_protocol(void)
1172 /* Destroy any existing hashes. */
1173 if (rlc_lte_channel_hash) {
1174 g_hash_table_destroy(rlc_lte_channel_hash);
1177 if (rlc_lte_frame_report_hash) {
1178 g_hash_table_destroy(rlc_lte_frame_report_hash);
1181 /* Now create them over */
1182 rlc_lte_channel_hash = g_hash_table_new(rlc_channel_hash_func, rlc_channel_equal);
1183 rlc_lte_frame_report_hash = g_hash_table_new(rlc_frame_hash_func, rlc_frame_equal);
1189 void proto_register_rlc_lte(void)
1191 static hf_register_info hf[] =
1193 /**********************************/
1194 /* Items for decoding context */
1195 { &hf_rlc_lte_context_mode,
1197 "rlc-lte.mode", FT_UINT8, BASE_DEC, VALS(rlc_mode_vals), 0x0,
1201 { &hf_rlc_lte_context_direction,
1203 "rlc-lte.direction", FT_UINT8, BASE_DEC, VALS(direction_vals), 0x0,
1204 "Direction of message", HFILL
1207 { &hf_rlc_lte_context_priority,
1209 "rlc-lte.priority", FT_UINT8, BASE_DEC, 0, 0x0,
1213 { &hf_rlc_lte_context_ueid,
1215 "rlc-lte.ueid", FT_UINT16, BASE_DEC, 0, 0x0,
1216 "User Equipment Identifier associated with message", HFILL
1219 { &hf_rlc_lte_context_channel_type,
1221 "rlc-lte.channel-type", FT_UINT16, BASE_DEC, VALS(rlc_channel_type_vals), 0x0,
1222 "Channel Type associated with message", HFILL
1225 { &hf_rlc_lte_context_channel_id,
1227 "rlc-lte.channel-id", FT_UINT16, BASE_DEC, 0, 0x0,
1228 "Channel ID associated with message", HFILL
1231 { &hf_rlc_lte_context_pdu_length,
1233 "rlc-lte.pdu_length", FT_UINT16, BASE_DEC, 0, 0x0,
1234 "Length of PDU (in bytes)", HFILL
1237 { &hf_rlc_lte_context_um_sn_length,
1238 { "UM Sequence number length",
1239 "rlc-lte.um-seqnum-length", FT_UINT8, BASE_DEC, 0, 0x0,
1240 "Length of UM sequence number in bits", HFILL
1245 /* Transparent mode fields */
1246 { &hf_rlc_lte_tm_data,
1248 "rlc-lte.tm.data", FT_BYTES, BASE_NONE, 0, 0x0,
1249 "Transparent Mode Data", HFILL
1253 /* Unacknowledged mode fields */
1254 { &hf_rlc_lte_um_header,
1256 "rlc-lte.um.header", FT_STRING, BASE_NONE, NULL, 0x0,
1257 "Unackowledged Mode Header", HFILL
1260 { &hf_rlc_lte_um_fi,
1262 "rlc-lte.um.fi", FT_UINT8, BASE_HEX, VALS(framing_info_vals), 0x0,
1266 { &hf_rlc_lte_um_fixed_e,
1268 "rlc-lte.um.fixed.e", FT_UINT8, BASE_HEX, VALS(fixed_extension_vals), 0x0,
1269 "Extension in fixed part of UM header", HFILL
1272 { &hf_rlc_lte_um_sn,
1273 { "Sequence number",
1274 "rlc-lte.um.sn", FT_UINT8, BASE_DEC, 0, 0x0,
1275 "Unacknowledged Mode Sequence Number", HFILL
1278 { &hf_rlc_lte_um_fixed_reserved,
1280 "rlc-lte.um.reserved", FT_UINT8, BASE_DEC, 0, 0xe0,
1281 "Unacknowledged Mode Fixed header reserved bits", HFILL
1284 { &hf_rlc_lte_um_data,
1286 "rlc-lte.um.data", FT_BYTES, BASE_NONE, 0, 0x0,
1287 "Unacknowledged Mode Data", HFILL
1290 { &hf_rlc_lte_extension_part,
1292 "rlc-lte.extension-part", FT_STRING, BASE_NONE, 0, 0x0,
1298 { &hf_rlc_lte_extension_e,
1300 "rlc-lte.extension.e", FT_UINT8, BASE_HEX, VALS(extension_extension_vals), 0x0,
1301 "Extension in extended part of the header", HFILL
1304 { &hf_rlc_lte_extension_li,
1305 { "Length Indicator",
1306 "rlc-lte.extension.li", FT_UINT16, BASE_DEC, 0, 0x0,
1310 { &hf_rlc_lte_extension_padding,
1312 "rlc-lte.extension.padding", FT_UINT8, BASE_HEX, 0, 0x0f,
1313 "Extension header padding", HFILL
1318 { &hf_rlc_lte_am_header,
1320 "rlc-lte.am.header", FT_STRING, BASE_NONE, NULL, 0x0,
1321 "Ackowledged Mode Header", HFILL
1324 { &hf_rlc_lte_am_data_control,
1326 "rlc-lte.am.frame_type", FT_UINT8, BASE_HEX, VALS(data_or_control_vals), 0x80,
1327 "AM Frame Type (Control or Data)", HFILL
1330 { &hf_rlc_lte_am_rf,
1331 { "Re-segmentation Flag",
1332 "rlc-lte.am.rf", FT_UINT8, BASE_HEX, VALS(resegmentation_flag_vals), 0x40,
1333 "AM Re-segmentation Flag", HFILL
1338 "rlc-lte.am.p", FT_UINT8, BASE_HEX, VALS(polling_bit_vals), 0x20,
1342 { &hf_rlc_lte_am_fi,
1344 "rlc-lte.am.fi", FT_UINT8, BASE_HEX, VALS(framing_info_vals), 0x18,
1345 "AM Framing Info", HFILL
1348 { &hf_rlc_lte_am_fixed_e,
1350 "rlc-lte.am.fixed.e", FT_UINT8, BASE_HEX, VALS(fixed_extension_vals), 0x04,
1351 "Fixed Extension Bit", HFILL
1354 { &hf_rlc_lte_am_fixed_sn,
1355 { "Sequence Number",
1356 "rlc-lte.am.fixed.sn", FT_UINT16, BASE_HEX, 0, 0x03ff,
1357 "AM Fixed Sequence Number", HFILL
1360 { &hf_rlc_lte_am_segment_lsf,
1361 { "Last Segment Flag",
1362 "rlc-lte.am.segment.lsf", FT_UINT8, BASE_HEX, VALS(lsf_vals), 0x80,
1366 { &hf_rlc_lte_am_segment_so,
1368 "rlc-lte.am.segment.offset", FT_UINT16, BASE_DEC, 0, 0x7fff,
1372 { &hf_rlc_lte_am_data,
1374 "rlc-lte.am.data", FT_BYTES, BASE_NONE, 0, 0x0,
1375 "Acknowledged Mode Data", HFILL
1380 { &hf_rlc_lte_am_cpt,
1381 { "Control PDU Type",
1382 "rlc-lte.am.cpt", FT_UINT8, BASE_HEX, VALS(control_pdu_type_vals), 0x70,
1383 "AM Control PDU Type", HFILL
1386 { &hf_rlc_lte_am_ack_sn,
1387 { "ACK Sequence Number",
1388 "rlc-lte.am.ack-sn", FT_UINT16, BASE_DEC, 0, 0x0,
1389 "Sequence Number we're next expecting to receive", HFILL
1392 { &hf_rlc_lte_am_e1,
1393 { "Extension bit 1",
1394 "rlc-lte.am.e1", FT_UINT8, BASE_HEX, VALS(am_e1_vals), 0x0,
1398 { &hf_rlc_lte_am_e2,
1399 { "Extension bit 2",
1400 "rlc-lte.am.e2", FT_UINT8, BASE_HEX, VALS(am_e2_vals), 0x0,
1404 { &hf_rlc_lte_am_nack_sn,
1405 { "NACK Sequence Number",
1406 "rlc-lte.am.nack-sn", FT_UINT16, BASE_DEC, 0, 0x0,
1407 "Negative Acknowledgement Sequence Number", HFILL
1410 { &hf_rlc_lte_am_so_start,
1412 "rlc-lte.am.so-start", FT_UINT16, BASE_DEC, 0, 0x0,
1416 { &hf_rlc_lte_am_so_end,
1418 "rlc-lte.am.so-end", FT_UINT16, BASE_DEC, 0, 0x0,
1423 { &hf_rlc_lte_predefined_pdu,
1424 { "Predefined data",
1425 "rlc-lte.predefined-data", FT_BYTES, BASE_NONE, 0, 0x0,
1426 "Predefined test data", HFILL
1430 { &hf_rlc_lte_sequence_analysis,
1431 { "Sequence Analysis",
1432 "rlc-lte.sequence-analysis", FT_STRING, BASE_NONE, 0, 0x0,
1436 { &hf_rlc_lte_sequence_analysis_previous_frame,
1437 { "Previous frame for channel",
1438 "rlc-lte.sequence-analysis.previous-frame", FT_FRAMENUM, BASE_NONE, 0, 0x0,
1442 { &hf_rlc_lte_sequence_analysis_expected_sn,
1444 "rlc-lte.sequence-analysis.expected-sn", FT_UINT16, BASE_DEC, 0, 0x0,
1448 { &hf_rlc_lte_sequence_analysis_framing_info_correct,
1449 { "Frame info continued correctly",
1450 "rlc-lte.sequence-analysis.framing-info-correct", FT_UINT8, BASE_DEC, 0, 0x0,
1456 static gint *ett[] =
1459 &ett_rlc_lte_um_header,
1460 &ett_rlc_lte_am_header,
1461 &ett_rlc_lte_extension_part,
1462 &ett_rlc_lte_sequence_analysis
1465 module_t *rlc_lte_module;
1467 /* Register protocol. */
1468 proto_rlc_lte = proto_register_protocol("RLC-LTE", "RLC-LTE", "rlc-lte");
1469 proto_register_field_array(proto_rlc_lte, hf, array_length(hf));
1470 proto_register_subtree_array(ett, array_length(ett));
1472 /* Allow other dissectors to find this one by name. */
1473 register_dissector("rlc-lte", dissect_rlc_lte, proto_rlc_lte);
1476 rlc_lte_module = prefs_register_protocol(proto_rlc_lte, NULL);
1478 prefs_register_bool_preference(rlc_lte_module, "do_sequence_analysis",
1479 "Do sequence analysis for UM channels",
1480 "Attempt to keep track of PDUs for UM channels, and point out problems",
1481 &global_rlc_lte_sequence_analysis);
1483 register_init_routine(&rlc_lte_init_protocol);