/src/wireshark/epan/dissectors/packet-rlc-lte.c
Line | Count | Source |
1 | | /* Routines for LTE RLC disassembly |
2 | | * |
3 | | * Martin Mathieson |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | #include "config.h" |
13 | | |
14 | | #include <epan/packet.h> |
15 | | #include <epan/exceptions.h> |
16 | | #include <epan/expert.h> |
17 | | #include <epan/prefs.h> |
18 | | #include <epan/tap.h> |
19 | | #include <epan/proto_data.h> |
20 | | #include <epan/tfs.h> |
21 | | #include <wsutil/array.h> |
22 | | #include "packet-mac-lte.h" |
23 | | #include "packet-rlc-lte.h" |
24 | | #include "packet-rlc-3gpp-common.h" |
25 | | #include "packet-pdcp-lte.h" |
26 | | |
27 | | |
28 | | /* Described in: |
29 | | * 3GPP TS 36.322 Evolved Universal Terrestrial Radio Access (E-UTRA) |
30 | | * Radio Link Control (RLC) Protocol specification v14.0.0 |
31 | | */ |
32 | | |
33 | | /* TODO: |
34 | | - add intermediate results to segments leading to final reassembly |
35 | | - use multiple active rlc_channel_reassembly_info's per channel |
36 | | - sequence analysis gets confused when we change cells and skip back |
37 | | to SN 0. Maybe add cell-id to context and add to channel/result key? |
38 | | */ |
39 | | |
40 | | void proto_register_rlc_lte(void); |
41 | | void proto_reg_handoff_rlc_lte(void); |
42 | | |
43 | | /********************************/ |
44 | | /* Preference settings */ |
45 | | |
46 | 14 | #define SEQUENCE_ANALYSIS_MAC_ONLY 1 |
47 | 14 | #define SEQUENCE_ANALYSIS_RLC_ONLY 2 |
48 | | |
49 | | /* By default do try to analyse the sequence of messages for AM/UM channels |
50 | | using MAC PDUs */ |
51 | | static int global_rlc_lte_am_sequence_analysis = SEQUENCE_ANALYSIS_MAC_ONLY; |
52 | | static int global_rlc_lte_um_sequence_analysis = SEQUENCE_ANALYSIS_MAC_ONLY; |
53 | | |
54 | | /* By default do call PDCP/RRC dissectors for SDU data */ |
55 | | static bool global_rlc_lte_call_pdcp_for_srb = true; |
56 | | |
57 | | enum pdcp_for_drb { PDCP_drb_off, PDCP_drb_SN_7, PDCP_drb_SN_12, PDCP_drb_SN_signalled, PDCP_drb_SN_15, PDCP_drb_SN_18}; |
58 | | static const enum_val_t pdcp_drb_col_vals[] = { |
59 | | {"pdcp-drb-off", "Off", PDCP_drb_off}, |
60 | | {"pdcp-drb-sn-7", "7-bit SN", PDCP_drb_SN_7}, |
61 | | {"pdcp-drb-sn-12", "12-bit SN", PDCP_drb_SN_12}, |
62 | | {"pdcp-drb-sn-15", "15-bit SN", PDCP_drb_SN_15}, |
63 | | {"pdcp-drb-sn-18", "18-bit SN", PDCP_drb_SN_18}, |
64 | | {"pdcp-drb-sn-signalling", "Use signalled value", PDCP_drb_SN_signalled}, |
65 | | {NULL, NULL, -1} |
66 | | }; |
67 | | static int global_rlc_lte_call_pdcp_for_drb = (int)PDCP_drb_SN_signalled; |
68 | | |
69 | | static bool global_rlc_lte_call_rrc_for_ccch = true; |
70 | | static bool global_rlc_lte_call_rrc_for_mcch; |
71 | | static bool global_rlc_lte_call_ip_for_mtch; |
72 | | |
73 | | /* Preference to expect RLC headers without payloads */ |
74 | | static bool global_rlc_lte_headers_expected; |
75 | | |
76 | | /* Re-assembly of segments */ |
77 | | static bool global_rlc_lte_reassembly = true; |
78 | | |
79 | | /* Tree storing UE related parameters */ |
80 | 0 | #define NO_EXT_LI 0x0 |
81 | 0 | #define UL_EXT_LI 0x1 |
82 | 0 | #define DL_EXT_LI 0x2 |
83 | | typedef struct rlc_ue_parameters { |
84 | | uint32_t id; |
85 | | uint8_t ext_li_field; |
86 | | uint8_t pdcp_sn_bits; |
87 | | } rlc_ue_parameters; |
88 | | static wmem_tree_t *ue_parameters_tree; |
89 | | |
90 | | /**************************************************/ |
91 | | /* Initialize the protocol and registered fields. */ |
92 | | static int proto_rlc_lte; |
93 | | |
94 | | static int proto_mac_lte; |
95 | | static int proto_pdcp_lte; |
96 | | |
97 | | static dissector_handle_t pdcp_lte_handle; |
98 | | static dissector_handle_t ip_handle; |
99 | | static dissector_handle_t lte_rrc_mcch; |
100 | | static dissector_handle_t lte_rrc_ul_ccch; |
101 | | static dissector_handle_t lte_rrc_dl_ccch; |
102 | | static dissector_handle_t lte_rrc_bcch_bch; |
103 | | static dissector_handle_t lte_rrc_bcch_dl_sch; |
104 | | static dissector_handle_t lte_rrc_pcch; |
105 | | static dissector_handle_t lte_rrc_ul_ccch_nb; |
106 | | static dissector_handle_t lte_rrc_dl_ccch_nb; |
107 | | static dissector_handle_t lte_rrc_bcch_bch_nb; |
108 | | static dissector_handle_t lte_rrc_bcch_dl_sch_nb; |
109 | | static dissector_handle_t lte_rrc_pcch_nb; |
110 | | |
111 | | |
112 | | static int rlc_lte_tap; |
113 | | |
114 | | /* Decoding context */ |
115 | | static int hf_rlc_lte_context; |
116 | | static int hf_rlc_lte_context_mode; |
117 | | static int hf_rlc_lte_context_direction; |
118 | | static int hf_rlc_lte_context_priority; |
119 | | static int hf_rlc_lte_context_ueid; |
120 | | static int hf_rlc_lte_context_channel_type; |
121 | | static int hf_rlc_lte_context_channel_id; |
122 | | static int hf_rlc_lte_context_pdu_length; |
123 | | static int hf_rlc_lte_context_um_sn_length; |
124 | | static int hf_rlc_lte_context_am_sn_length; |
125 | | |
126 | | /* Transparent mode fields */ |
127 | | static int hf_rlc_lte_tm; |
128 | | static int hf_rlc_lte_tm_data; |
129 | | |
130 | | /* Unacknowledged mode fields */ |
131 | | static int hf_rlc_lte_um; |
132 | | static int hf_rlc_lte_um_header; |
133 | | static int hf_rlc_lte_um_fi; |
134 | | static int hf_rlc_lte_um_fixed_e; |
135 | | static int hf_rlc_lte_um_sn; |
136 | | static int hf_rlc_lte_um_fixed_reserved; |
137 | | static int hf_rlc_lte_um_data; |
138 | | static int hf_rlc_lte_extension_part; |
139 | | |
140 | | /* Extended header (common to UM and AM) */ |
141 | | static int hf_rlc_lte_extension_e; |
142 | | static int hf_rlc_lte_extension_li; |
143 | | static int hf_rlc_lte_extension_padding; |
144 | | |
145 | | |
146 | | /* Acknowledged mode fields */ |
147 | | static int hf_rlc_lte_am; |
148 | | static int hf_rlc_lte_am_header; |
149 | | static int hf_rlc_lte_am_data_control; |
150 | | static int hf_rlc_lte_am_rf; |
151 | | static int hf_rlc_lte_am_p; |
152 | | static int hf_rlc_lte_am_fi; |
153 | | static int hf_rlc_lte_am_fixed_e; |
154 | | static int hf_rlc_lte_am_fixed_sn; |
155 | | static int hf_rlc_lte_am_fixed_reserved; |
156 | | static int hf_rlc_lte_am_segment_lsf16; |
157 | | static int hf_rlc_lte_am_fixed_reserved2; |
158 | | static int hf_rlc_lte_am_fixed_sn16; |
159 | | static int hf_rlc_lte_am_segment_lsf; |
160 | | static int hf_rlc_lte_am_segment_so; |
161 | | static int hf_rlc_lte_am_segment_so16; |
162 | | static int hf_rlc_lte_am_data; |
163 | | |
164 | | /* Control fields */ |
165 | | static int hf_rlc_lte_am_cpt; |
166 | | static int hf_rlc_lte_am_ack_sn; |
167 | | static int hf_rlc_lte_am_e1; |
168 | | static int hf_rlc_lte_am_e2; |
169 | | static int hf_rlc_lte_am_nack_sn; |
170 | | static int hf_rlc_lte_am_nacks; |
171 | | static int hf_rlc_lte_am_so_start; |
172 | | static int hf_rlc_lte_am_so_end; |
173 | | |
174 | | static int hf_rlc_lte_predefined_pdu; |
175 | | static int hf_rlc_lte_header_only; |
176 | | |
177 | | /* Sequence Analysis */ |
178 | | static int hf_rlc_lte_sequence_analysis; |
179 | | static int hf_rlc_lte_sequence_analysis_ok; |
180 | | static int hf_rlc_lte_sequence_analysis_previous_frame; |
181 | | static int hf_rlc_lte_sequence_analysis_next_frame; |
182 | | static int hf_rlc_lte_sequence_analysis_expected_sn; |
183 | | static int hf_rlc_lte_sequence_analysis_framing_info_correct; |
184 | | |
185 | | static int hf_rlc_lte_sequence_analysis_mac_retx; |
186 | | static int hf_rlc_lte_sequence_analysis_retx; |
187 | | static int hf_rlc_lte_sequence_analysis_repeated; |
188 | | static int hf_rlc_lte_sequence_analysis_skipped; |
189 | | |
190 | | static int hf_rlc_lte_sequence_analysis_repeated_nack; |
191 | | static int hf_rlc_lte_sequence_analysis_repeated_nack_original_frame; |
192 | | |
193 | | static int hf_rlc_lte_sequence_analysis_ack_out_of_range; |
194 | | static int hf_rlc_lte_sequence_analysis_ack_out_of_range_opposite_frame; |
195 | | |
196 | | /* Reassembly */ |
197 | | static int hf_rlc_lte_reassembly_source; |
198 | | static int hf_rlc_lte_reassembly_source_number_of_segments; |
199 | | static int hf_rlc_lte_reassembly_source_total_length; |
200 | | static int hf_rlc_lte_reassembly_source_segment; |
201 | | static int hf_rlc_lte_reassembly_source_segment_sn; |
202 | | static int hf_rlc_lte_reassembly_source_segment_framenum; |
203 | | static int hf_rlc_lte_reassembly_source_segment_length; |
204 | | |
205 | | /* Subtrees. */ |
206 | | static int ett_rlc_lte; |
207 | | static int ett_rlc_lte_context; |
208 | | static int ett_rlc_lte_um_header; |
209 | | static int ett_rlc_lte_am_header; |
210 | | static int ett_rlc_lte_extension_part; |
211 | | static int ett_rlc_lte_sequence_analysis; |
212 | | static int ett_rlc_lte_reassembly_source; |
213 | | static int ett_rlc_lte_reassembly_source_segment; |
214 | | |
215 | | static expert_field ei_rlc_lte_context_mode; |
216 | | static expert_field ei_rlc_lte_am_nack_sn; |
217 | | static expert_field ei_rlc_lte_am_nack_sn_ahead_ack; |
218 | | static expert_field ei_rlc_lte_um_sn_repeated; |
219 | | static expert_field ei_rlc_lte_am_nack_sn_ack_same; |
220 | | static expert_field ei_rlc_lte_am_cpt; |
221 | | static expert_field ei_rlc_lte_am_data_no_data; |
222 | | static expert_field ei_rlc_lte_sequence_analysis_last_segment_complete; |
223 | | static expert_field ei_rlc_lte_sequence_analysis_mac_retx; |
224 | | static expert_field ei_rlc_lte_am_nack_sn_partial; |
225 | | static expert_field ei_rlc_lte_sequence_analysis_repeated_nack; |
226 | | static expert_field ei_rlc_lte_bytes_after_status_pdu_complete; |
227 | | static expert_field ei_rlc_lte_sequence_analysis_repeated; |
228 | | static expert_field ei_rlc_lte_wrong_sequence_number; |
229 | | static expert_field ei_rlc_lte_sequence_analysis_retx; |
230 | | static expert_field ei_rlc_lte_am_sn_missing; |
231 | | static expert_field ei_rlc_lte_um_sn; |
232 | | static expert_field ei_rlc_lte_header_only; |
233 | | static expert_field ei_rlc_lte_am_data_no_data_beyond_extensions; |
234 | | static expert_field ei_rlc_lte_um_sn_missing; |
235 | | static expert_field ei_rlc_lte_sequence_analysis_ack_out_of_range_opposite_frame; |
236 | | static expert_field ei_rlc_lte_sequence_analysis_last_segment_not_continued; |
237 | | static expert_field ei_rlc_lte_reserved_bits_not_zero; |
238 | | static expert_field ei_rlc_lte_no_per_frame_info; |
239 | | static expert_field ei_rlc_lte_unknown_udp_framing_tag; |
240 | | static expert_field ei_rlc_lte_missing_udp_framing_tag; |
241 | | |
242 | | /* Value-strings */ |
243 | | static const value_string direction_vals[] = |
244 | | { |
245 | | { DIRECTION_UPLINK, "Uplink"}, |
246 | | { DIRECTION_DOWNLINK, "Downlink"}, |
247 | | { 0, NULL } |
248 | | }; |
249 | | |
250 | | static const value_string rlc_mode_short_vals[] = |
251 | | { |
252 | | { RLC_TM_MODE, "TM"}, |
253 | | { RLC_UM_MODE, "UM"}, |
254 | | { RLC_AM_MODE, "AM"}, |
255 | | { RLC_PREDEF, "PREDEFINED"}, /* For data testing */ |
256 | | { 0, NULL } |
257 | | }; |
258 | | |
259 | | static const value_string rlc_mode_vals[] = |
260 | | { |
261 | | { RLC_TM_MODE, "Transparent Mode"}, |
262 | | { RLC_UM_MODE, "Unacknowledged Mode"}, |
263 | | { RLC_AM_MODE, "Acknowledged Mode"}, |
264 | | { 0, NULL } |
265 | | }; |
266 | | |
267 | | static const value_string rlc_channel_type_vals[] = |
268 | | { |
269 | | { CHANNEL_TYPE_CCCH, "CCCH"}, |
270 | | { CHANNEL_TYPE_BCCH_BCH, "BCCH_BCH"}, |
271 | | { CHANNEL_TYPE_PCCH, "PCCH"}, |
272 | | { CHANNEL_TYPE_SRB, "SRB"}, |
273 | | { CHANNEL_TYPE_DRB, "DRB"}, |
274 | | { CHANNEL_TYPE_BCCH_DL_SCH, "BCCH_DL_SCH"}, |
275 | | { CHANNEL_TYPE_MCCH, "MCCH"}, |
276 | | { CHANNEL_TYPE_MTCH, "MTCH"}, |
277 | | { 0, NULL } |
278 | | }; |
279 | | |
280 | | static const value_string framing_info_vals[] = |
281 | | { |
282 | | { 0, "First byte begins a RLC SDU and last byte ends a RLC SDU"}, |
283 | | { 1, "First byte begins a RLC SDU and last byte does not end a RLC SDU"}, |
284 | | { 2, "First byte does not begin a RLC SDU and last byte ends a RLC SDU"}, |
285 | | { 3, "First byte does not begin a RLC SDU and last byte does not end a RLC SDU"}, |
286 | | { 0, NULL } |
287 | | }; |
288 | | |
289 | | static const value_string fixed_extension_vals[] = |
290 | | { |
291 | | { 0, "Data field follows from the octet following the fixed part of the header"}, |
292 | | { 1, "A set of E field and LI field follows from the octet following the fixed part of the header"}, |
293 | | { 0, NULL } |
294 | | }; |
295 | | |
296 | | static const value_string extension_extension_vals[] = |
297 | | { |
298 | | { 0, "Data field follows from the octet following the LI field following this E field"}, |
299 | | { 1, "A set of E field and LI field follows from the bit following the LI field following this E field"}, |
300 | | { 0, NULL } |
301 | | }; |
302 | | |
303 | | static const value_string resegmentation_flag_vals[] = |
304 | | { |
305 | | { 0, "AMD PDU"}, |
306 | | { 1, "AMD PDU segment"}, |
307 | | { 0, NULL } |
308 | | }; |
309 | | |
310 | | static const value_string polling_bit_vals[] = |
311 | | { |
312 | | { 0, "Status report not requested"}, |
313 | | { 1, "Status report is requested"}, |
314 | | { 0, NULL } |
315 | | }; |
316 | | |
317 | | static const value_string lsf_vals[] = |
318 | | { |
319 | | { 0, "Last byte of the AMD PDU segment does not correspond to the last byte of an AMD PDU"}, |
320 | | { 1, "Last byte of the AMD PDU segment corresponds to the last byte of an AMD PDU"}, |
321 | | { 0, NULL } |
322 | | }; |
323 | | |
324 | | static const value_string control_pdu_type_vals[] = |
325 | | { |
326 | | { 0, "STATUS PDU"}, |
327 | | { 0, NULL } |
328 | | }; |
329 | | |
330 | | static const value_string am_e1_vals[] = |
331 | | { |
332 | | { 0, "A set of NACK_SN, E1 and E2 does not follow"}, |
333 | | { 1, "A set of NACK_SN, E1 and E2 follows"}, |
334 | | { 0, NULL } |
335 | | }; |
336 | | |
337 | | static const value_string am_e2_vals[] = |
338 | | { |
339 | | { 0, "A set of SOstart and SOend does not follow for this NACK_SN"}, |
340 | | { 1, "A set of SOstart and SOend follows for this NACK_SN"}, |
341 | | { 0, NULL } |
342 | | }; |
343 | | |
344 | | static const value_string header_only_vals[] = |
345 | | { |
346 | | { 0, "RLC PDU Headers and body present"}, |
347 | | { 1, "RLC PDU Headers only"}, |
348 | | { 0, NULL } |
349 | | }; |
350 | | |
351 | | |
352 | | |
353 | | /**********************************************************************************/ |
354 | | /* These are for keeping track of UM/AM extension headers, and the lengths found */ |
355 | | /* in them */ |
356 | | static uint8_t s_number_of_extensions; |
357 | 0 | #define MAX_RLC_SDUS 192 |
358 | | static uint16_t s_lengths[MAX_RLC_SDUS]; |
359 | | |
360 | | |
361 | | /*********************************************************************/ |
362 | | /* UM/AM sequence analysis */ |
363 | | |
364 | | /* Types for RLC channel hash table */ |
365 | | /* This table is maintained during initial dissection of RLC */ |
366 | | /* frames, mapping from channel_hash_key -> sequence_analysis_report */ |
367 | | |
368 | | /* Channel key */ |
369 | | typedef struct |
370 | | { |
371 | | unsigned ueId : 16; |
372 | | unsigned channelType : 3; |
373 | | unsigned channelId : 5; |
374 | | unsigned direction : 1; |
375 | | } channel_hash_key; |
376 | | |
377 | | |
378 | | /******************************************************************/ |
379 | | /* State maintained for AM/UM reassembly */ |
380 | | |
381 | | typedef struct rlc_segment { |
382 | | uint32_t frameNum; |
383 | | uint16_t SN; |
384 | | uint8_t *data; |
385 | | uint16_t length; |
386 | | } rlc_segment; |
387 | | |
388 | | typedef struct rlc_channel_reassembly_info |
389 | | { |
390 | | uint16_t number_of_segments; |
391 | 0 | #define RLC_MAX_SEGMENTS 100 |
392 | | rlc_segment segments[RLC_MAX_SEGMENTS]; |
393 | | } rlc_channel_reassembly_info; |
394 | | |
395 | | |
396 | | |
397 | | |
398 | | /*******************************************************************/ |
399 | | /* Conversation-type status for sequence analysis on channel */ |
400 | | typedef struct |
401 | | { |
402 | | uint8_t rlcMode; |
403 | | |
404 | | /* For UM, we always expect the SN to keep advancing, and these fields |
405 | | keep track of this. |
406 | | For AM, these correspond to new data */ |
407 | | uint16_t previousSequenceNumber; |
408 | | uint32_t previousFrameNum; |
409 | | bool previousSegmentIncomplete; |
410 | | |
411 | | /* Accumulate info about current segmented SDU */ |
412 | | struct rlc_channel_reassembly_info *reassembly_info; |
413 | | } channel_sequence_analysis_status; |
414 | | |
415 | | /* The sequence analysis channel hash table (channel_hash_key* -> channel_sequence_analysis_status*) */ |
416 | | static wmem_map_t *sequence_analysis_channel_hash; |
417 | | |
418 | | |
419 | | /* Types for sequence analysis frame report hash table */ |
420 | | /* This is a table from framenum -> state_report_in_frame */ |
421 | | /* This is necessary because the per-packet info is already being used */ |
422 | | /* for context information before the dissector is called */ |
423 | | |
424 | | /* Info to attach to frame when first read, recording what to show about sequence */ |
425 | | typedef enum { |
426 | | SN_OK, SN_Repeated, SN_MAC_Retx, SN_Retx, SN_Missing, ACK_Out_of_Window, SN_Error |
427 | | } sequence_analysis_state; |
428 | | |
429 | | |
430 | | typedef struct |
431 | | { |
432 | | bool sequenceExpectedCorrect; |
433 | | uint16_t sequenceExpected; |
434 | | uint32_t previousFrameNum; |
435 | | bool previousSegmentIncomplete; |
436 | | uint32_t nextFrameNum; |
437 | | |
438 | | uint16_t firstSN; |
439 | | uint16_t lastSN; |
440 | | |
441 | | /* AM/UM */ |
442 | | sequence_analysis_state state; |
443 | | } sequence_analysis_report; |
444 | | |
445 | | |
446 | | /* The sequence analysis frame report hash table instance itself, lookup when visited */ |
447 | | /* rlc_result_hash_key* -> sequence_analysis_report* */ |
448 | | static wmem_map_t *sequence_analysis_report_hash; |
449 | | |
450 | | |
451 | | static void *get_report_hash_key(uint16_t SN, uint32_t frameNumber, |
452 | | rlc_lte_info *p_rlc_lte_info, |
453 | | bool do_persist); |
454 | | |
455 | | |
456 | | |
457 | | |
458 | | /* The reassembly result hash table */ |
459 | | static wmem_map_t *reassembly_report_hash; |
460 | | |
461 | | |
462 | | /* Create a new struct for reassembly */ |
463 | | static void reassembly_reset(channel_sequence_analysis_status *status) |
464 | 0 | { |
465 | 0 | status->reassembly_info = wmem_new0(wmem_file_scope(), rlc_channel_reassembly_info); |
466 | 0 | } |
467 | | |
468 | | /* Hide previous one */ |
469 | | static void reassembly_destroy(channel_sequence_analysis_status *status) |
470 | 0 | { |
471 | | /* Just "leak" it. There seems to be no way to free this memory... */ |
472 | 0 | status->reassembly_info = NULL; |
473 | 0 | } |
474 | | |
475 | | /* Add a new segment to the accumulating segmented SDU */ |
476 | | static void reassembly_add_segment(channel_sequence_analysis_status *status, |
477 | | uint16_t SN, uint32_t frame, |
478 | | tvbuff_t *tvb, unsigned offset, int length) |
479 | 0 | { |
480 | 0 | int segment_number = status->reassembly_info->number_of_segments; |
481 | 0 | uint8_t *segment_data; |
482 | | |
483 | | /* Give up if reach segment limit */ |
484 | 0 | if (segment_number >= (RLC_MAX_SEGMENTS-1)) { |
485 | 0 | reassembly_destroy(status); |
486 | 0 | return; |
487 | 0 | } |
488 | | |
489 | 0 | segment_data = (uint8_t *)tvb_memdup(wmem_file_scope(),tvb, offset, length); |
490 | | |
491 | | /* Add new segment */ |
492 | 0 | status->reassembly_info->segments[segment_number].frameNum = frame; |
493 | 0 | status->reassembly_info->segments[segment_number].SN = SN; |
494 | 0 | status->reassembly_info->segments[segment_number].data = segment_data; |
495 | 0 | status->reassembly_info->segments[segment_number].length = length; |
496 | |
|
497 | 0 | status->reassembly_info->number_of_segments++; |
498 | 0 | } |
499 | | |
500 | | |
501 | | /* Record the current & complete segmented SDU by mapping from this frame number to |
502 | | struct with segment info. */ |
503 | | static void reassembly_record(channel_sequence_analysis_status *status, packet_info *pinfo, |
504 | | uint16_t SN, rlc_lte_info *p_rlc_lte_info) |
505 | 0 | { |
506 | | /* Just store existing info in hash table */ |
507 | 0 | wmem_map_insert(reassembly_report_hash, |
508 | 0 | get_report_hash_key(SN, pinfo->num, p_rlc_lte_info, true), |
509 | 0 | status->reassembly_info); |
510 | 0 | } |
511 | | |
512 | | /* Create and return a tvb based upon contents of reassembly info */ |
513 | | static tvbuff_t* reassembly_get_reassembled_tvb(rlc_channel_reassembly_info *reassembly_info, |
514 | | tvbuff_t *parent_tvb, packet_info *pinfo) |
515 | 0 | { |
516 | 0 | int n; |
517 | 0 | unsigned combined_length = 0; |
518 | 0 | uint8_t *combined_data; |
519 | 0 | unsigned combined_offset = 0; |
520 | 0 | tvbuff_t *reassembled_tvb; |
521 | | |
522 | | /* Allocate buffer big enough to hold re-assembled data */ |
523 | 0 | for (n=0; n < reassembly_info->number_of_segments; n++) { |
524 | 0 | combined_length += reassembly_info->segments[n].length; |
525 | 0 | } |
526 | 0 | combined_data = (uint8_t *)wmem_alloc(pinfo->pool, combined_length); |
527 | | |
528 | | /* Copy data into contiguous buffer */ |
529 | 0 | for (n=0; n < reassembly_info->number_of_segments; n++) { |
530 | 0 | uint8_t *data = reassembly_info->segments[n].data; |
531 | 0 | int length = reassembly_info->segments[n].length; |
532 | 0 | memcpy(combined_data+combined_offset, data, length); |
533 | 0 | combined_offset += length; |
534 | 0 | } |
535 | | |
536 | | /* Create and return tvb with this data */ |
537 | 0 | reassembled_tvb = tvb_new_child_real_data(parent_tvb, combined_data, combined_offset, combined_offset); |
538 | 0 | add_new_data_source(pinfo, reassembled_tvb, "Reassembled SDU"); |
539 | 0 | return reassembled_tvb; |
540 | 0 | } |
541 | | |
542 | | /* Show where the segments came from for a reassembled SDU */ |
543 | | static void reassembly_show_source(rlc_channel_reassembly_info *reassembly_info, |
544 | | proto_tree *tree, tvbuff_t *tvb, unsigned offset) |
545 | 0 | { |
546 | 0 | int n; |
547 | 0 | proto_item *source_ti, *ti; |
548 | 0 | proto_tree *source_tree; |
549 | 0 | proto_item *segment_ti; |
550 | 0 | proto_tree *segment_tree; |
551 | 0 | unsigned total_length=0; |
552 | | |
553 | | /* Create root of source info */ |
554 | 0 | source_ti = proto_tree_add_item(tree, |
555 | 0 | hf_rlc_lte_reassembly_source, |
556 | 0 | tvb, 0, 0, ENC_ASCII); |
557 | 0 | source_tree = proto_item_add_subtree(source_ti, ett_rlc_lte_reassembly_source); |
558 | 0 | proto_item_set_generated(source_ti); |
559 | |
|
560 | 0 | for (n=0; n < reassembly_info->number_of_segments; n++) { |
561 | 0 | total_length += reassembly_info->segments[n].length; |
562 | 0 | } |
563 | 0 | proto_item_append_text(source_ti, " %u segments, %u bytes", reassembly_info->number_of_segments, |
564 | 0 | total_length); |
565 | | |
566 | | /* Number of segments */ |
567 | 0 | ti = proto_tree_add_uint(source_tree, |
568 | 0 | hf_rlc_lte_reassembly_source_number_of_segments, |
569 | 0 | tvb, 0, 0, reassembly_info->number_of_segments); |
570 | 0 | proto_item_set_generated(ti); |
571 | | |
572 | | /* Total length */ |
573 | 0 | ti = proto_tree_add_uint(source_tree, |
574 | 0 | hf_rlc_lte_reassembly_source_total_length, |
575 | 0 | tvb, 0, 0, total_length); |
576 | 0 | proto_item_set_generated(ti); |
577 | | |
578 | | /* Now add info about each segment in turn */ |
579 | 0 | for (n=0; n < reassembly_info->number_of_segments; n++) { |
580 | | |
581 | | /* Add next segment as a subtree */ |
582 | 0 | rlc_segment *segment = &(reassembly_info->segments[n]); |
583 | 0 | proto_item_append_text(source_ti, " (SN=%u frame=%u len=%u)", |
584 | 0 | segment->SN, segment->frameNum, segment->length); |
585 | | |
586 | | /* N.B. assume last segment from passed-in tvb! */ |
587 | 0 | segment_ti = proto_tree_add_item(source_tree, |
588 | 0 | hf_rlc_lte_reassembly_source_segment, |
589 | 0 | tvb, |
590 | 0 | (n == reassembly_info->number_of_segments-1) ? offset : 0, |
591 | 0 | (n == reassembly_info->number_of_segments-1) ? segment->length : 0, |
592 | 0 | ENC_NA); |
593 | 0 | segment_tree = proto_item_add_subtree(segment_ti, ett_rlc_lte_reassembly_source_segment); |
594 | 0 | proto_item_append_text(segment_ti, " (SN=%u frame=%u length=%u)", |
595 | 0 | segment->SN, segment->frameNum, segment->length); |
596 | 0 | proto_item_set_generated(segment_ti); |
597 | | |
598 | | /* Add details to segment tree */ |
599 | 0 | ti = proto_tree_add_uint(segment_tree, hf_rlc_lte_reassembly_source_segment_sn, |
600 | 0 | tvb, 0, 0, segment->SN); |
601 | 0 | proto_item_set_generated(ti); |
602 | 0 | ti = proto_tree_add_uint(segment_tree, hf_rlc_lte_reassembly_source_segment_framenum, |
603 | 0 | tvb, 0, 0, segment->frameNum); |
604 | 0 | proto_item_set_generated(ti); |
605 | 0 | ti = proto_tree_add_uint(segment_tree, hf_rlc_lte_reassembly_source_segment_length, |
606 | 0 | tvb, 0, 0, segment->length); |
607 | 0 | proto_item_set_generated(ti); |
608 | 0 | } |
609 | 0 | } |
610 | | |
611 | | |
612 | | |
613 | | |
614 | | /******************************************************************/ |
615 | | /* Conversation-type status for repeated NACK checking on channel */ |
616 | | typedef struct |
617 | | { |
618 | | uint16_t noOfNACKs; |
619 | | uint16_t NACKs[MAX_NACKs]; |
620 | | uint32_t frameNum; |
621 | | } channel_repeated_nack_status; |
622 | | |
623 | | static wmem_map_t *repeated_nack_channel_hash; |
624 | | |
625 | | typedef struct { |
626 | | uint16_t noOfNACKsRepeated; |
627 | | uint16_t repeatedNACKs[MAX_NACKs]; |
628 | | uint32_t previousFrameNum; |
629 | | } channel_repeated_nack_report; |
630 | | |
631 | | static wmem_map_t *repeated_nack_report_hash; |
632 | | |
633 | | |
634 | | |
635 | | /********************************************************/ |
636 | | /* Forward declarations & functions */ |
637 | | static void dissect_rlc_lte_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, bool is_udp_framing); |
638 | | |
639 | | |
640 | | /* Write the given formatted text to: |
641 | | - the info column |
642 | | - the top-level RLC PDU item |
643 | | - another subtree item (if supplied) */ |
644 | | static void write_pdu_label_and_info(proto_item *pdu_ti, proto_item *sub_ti, |
645 | | packet_info *pinfo, const char *format, ...) G_GNUC_PRINTF(4, 5); |
646 | | static void write_pdu_label_and_info(proto_item *pdu_ti, proto_item *sub_ti, |
647 | | packet_info *pinfo, const char *format, ...) |
648 | 0 | { |
649 | 0 | #define MAX_INFO_BUFFER 256 |
650 | 0 | static char info_buffer[MAX_INFO_BUFFER]; |
651 | |
|
652 | 0 | va_list ap; |
653 | |
|
654 | 0 | va_start(ap, format); |
655 | 0 | vsnprintf(info_buffer, MAX_INFO_BUFFER, format, ap); |
656 | 0 | va_end(ap); |
657 | | |
658 | | /* Add to indicated places */ |
659 | 0 | col_append_str(pinfo->cinfo, COL_INFO, info_buffer); |
660 | 0 | proto_item_append_text(pdu_ti, "%s", info_buffer); |
661 | 0 | if (sub_ti != NULL) { |
662 | 0 | proto_item_append_text(sub_ti, "%s", info_buffer); |
663 | 0 | } |
664 | 0 | } |
665 | | |
666 | | /* Version of function above, where no vsnprintf() call needed |
667 | | - the info column |
668 | | - the top-level RLC PDU item |
669 | | - another subtree item (if supplied) */ |
670 | | static void write_pdu_label_and_info_literal(proto_item *pdu_ti, proto_item *sub_ti, |
671 | | packet_info *pinfo, const char *info_buffer) |
672 | 0 | { |
673 | | /* Add to indicated places */ |
674 | 0 | col_append_str(pinfo->cinfo, COL_INFO, info_buffer); |
675 | 0 | proto_item_append_text(pdu_ti, "%s", info_buffer); |
676 | 0 | if (sub_ti != NULL) { |
677 | 0 | proto_item_append_text(sub_ti, "%s", info_buffer); |
678 | 0 | } |
679 | 0 | } |
680 | | |
681 | | |
682 | | |
683 | | /* Dissect extension headers (common to both UM and AM) */ |
684 | | static int dissect_rlc_lte_extension_header(tvbuff_t *tvb, packet_info *pinfo _U_, |
685 | | proto_tree *tree, |
686 | | unsigned offset, |
687 | | rlc_lte_info *p_rlc_lte_info) |
688 | 0 | { |
689 | 0 | uint8_t isOdd; |
690 | 0 | uint64_t extension = 1; |
691 | 0 | uint64_t length; |
692 | | |
693 | | /* Reset this count */ |
694 | 0 | s_number_of_extensions = 0; |
695 | |
|
696 | 0 | while (extension && (s_number_of_extensions < MAX_RLC_SDUS)) { |
697 | 0 | proto_tree *extension_part_tree; |
698 | 0 | proto_item *extension_part_ti; |
699 | | |
700 | | /* Extension part subtree */ |
701 | 0 | extension_part_ti = proto_tree_add_string_format(tree, |
702 | 0 | hf_rlc_lte_extension_part, |
703 | 0 | tvb, offset, 2, |
704 | 0 | "", |
705 | 0 | "Extension Part"); |
706 | 0 | extension_part_tree = proto_item_add_subtree(extension_part_ti, |
707 | 0 | ett_rlc_lte_extension_part); |
708 | |
|
709 | 0 | if (p_rlc_lte_info->extendedLiField == false) { |
710 | 0 | isOdd = (s_number_of_extensions % 2); |
711 | | |
712 | | /* Read next extension */ |
713 | 0 | proto_tree_add_bits_ret_val(extension_part_tree, hf_rlc_lte_extension_e, tvb, |
714 | 0 | (offset*8) + ((isOdd) ? 4 : 0), |
715 | 0 | 1, |
716 | 0 | &extension, ENC_BIG_ENDIAN); |
717 | | |
718 | | /* Read length field */ |
719 | 0 | proto_tree_add_bits_ret_val(extension_part_tree, hf_rlc_lte_extension_li, tvb, |
720 | 0 | (offset*8) + ((isOdd) ? 5 : 1), |
721 | 0 | 11, |
722 | 0 | &length, ENC_BIG_ENDIAN); |
723 | | |
724 | | /* Move on to byte of next extension */ |
725 | 0 | if (isOdd) { |
726 | 0 | offset += 2; |
727 | 0 | } else { |
728 | 0 | offset++; |
729 | 0 | } |
730 | 0 | } else { |
731 | | /* Read next extension */ |
732 | 0 | proto_tree_add_bits_ret_val(extension_part_tree, hf_rlc_lte_extension_e, tvb, |
733 | 0 | (offset*8), |
734 | 0 | 1, |
735 | 0 | &extension, ENC_BIG_ENDIAN); |
736 | | |
737 | | /* Read length field */ |
738 | 0 | proto_tree_add_bits_ret_val(extension_part_tree, hf_rlc_lte_extension_li, tvb, |
739 | 0 | (offset*8) + 1, |
740 | 0 | 15, |
741 | 0 | &length, ENC_BIG_ENDIAN); |
742 | | |
743 | | /* Move on to byte of next extension */ |
744 | 0 | offset += 2; |
745 | 0 | } |
746 | |
|
747 | 0 | proto_item_append_text(extension_part_tree, " (length=%u)", (uint16_t)length); |
748 | |
|
749 | 0 | s_lengths[s_number_of_extensions++] = (uint16_t)length; |
750 | 0 | } |
751 | | |
752 | | /* May need to skip padding after last extension part */ |
753 | 0 | isOdd = (s_number_of_extensions % 2); |
754 | 0 | if (isOdd && (p_rlc_lte_info->extendedLiField == false)) { |
755 | 0 | proto_tree_add_item(tree, hf_rlc_lte_extension_padding, |
756 | 0 | tvb, offset++, 1, ENC_BIG_ENDIAN); |
757 | 0 | } |
758 | |
|
759 | 0 | return offset; |
760 | 0 | } |
761 | | |
762 | | |
763 | | /* Show in the info column how many bytes are in the UM/AM PDU, and indicate |
764 | | whether or not the beginning and end are included in this packet */ |
765 | | static void show_PDU_in_info(packet_info *pinfo, |
766 | | proto_item *top_ti, |
767 | | int32_t length, |
768 | | bool first_includes_start, |
769 | | bool last_includes_end) |
770 | 0 | { |
771 | | /* Reflect this PDU in the info column */ |
772 | 0 | if (length > 0) { |
773 | 0 | write_pdu_label_and_info(top_ti, NULL, pinfo, |
774 | 0 | " %s%u-byte%s%s", |
775 | 0 | (first_includes_start) ? "[" : "..", |
776 | 0 | length, |
777 | 0 | (length > 1) ? "s" : "", |
778 | 0 | (last_includes_end) ? "]" : ".."); |
779 | 0 | } |
780 | 0 | else { |
781 | 0 | write_pdu_label_and_info(top_ti, NULL, pinfo, |
782 | 0 | " %sunknown-bytes%s", |
783 | 0 | (first_includes_start) ? "[" : "..", |
784 | 0 | (last_includes_end) ? "]" : ".."); |
785 | 0 | } |
786 | 0 | } |
787 | | |
788 | | |
789 | | /* Show an SDU. If configured, pass to PDCP/RRC/IP dissector */ |
790 | | static void show_PDU_in_tree(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, unsigned offset, int length, |
791 | | rlc_lte_info *rlc_info, bool whole_pdu, rlc_channel_reassembly_info *reassembly_info, |
792 | | sequence_analysis_state state) |
793 | 0 | { |
794 | 0 | wmem_tree_key_t key[3]; |
795 | 0 | uint32_t id; |
796 | 0 | rlc_ue_parameters *params; |
797 | | |
798 | | /* Add raw data (according to mode) */ |
799 | 0 | proto_item *data_ti = proto_tree_add_item(tree, |
800 | 0 | (rlc_info->rlcMode == RLC_AM_MODE) ? |
801 | 0 | hf_rlc_lte_am_data : |
802 | 0 | hf_rlc_lte_um_data, |
803 | 0 | tvb, offset, length, ENC_NA); |
804 | |
|
805 | 0 | if (whole_pdu || (reassembly_info != NULL)) { |
806 | 0 | if (((global_rlc_lte_call_pdcp_for_srb) && (rlc_info->channelType == CHANNEL_TYPE_SRB)) || |
807 | 0 | ((global_rlc_lte_call_pdcp_for_drb != PDCP_drb_off) && (rlc_info->channelType == CHANNEL_TYPE_DRB))) { |
808 | | /* Send whole PDU to PDCP */ |
809 | | |
810 | | /* TODO: made static to avoid compiler warning... */ |
811 | 0 | static tvbuff_t *pdcp_tvb = NULL; |
812 | 0 | struct pdcp_lte_info *p_pdcp_lte_info; |
813 | | |
814 | | /* Get tvb for passing to LTE PDCP dissector */ |
815 | 0 | if (reassembly_info == NULL) { |
816 | 0 | pdcp_tvb = tvb_new_subset_length(tvb, offset, length); |
817 | 0 | } |
818 | 0 | else { |
819 | | /* Get combined tvb. */ |
820 | 0 | pdcp_tvb = reassembly_get_reassembled_tvb(reassembly_info, tvb, pinfo); |
821 | 0 | reassembly_show_source(reassembly_info, tree, tvb, offset); |
822 | 0 | } |
823 | | |
824 | | /* Reuse or allocate struct */ |
825 | 0 | p_pdcp_lte_info = (pdcp_lte_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_pdcp_lte, 0); |
826 | 0 | if (p_pdcp_lte_info == NULL) { |
827 | 0 | p_pdcp_lte_info = wmem_new0(wmem_file_scope(), pdcp_lte_info); |
828 | | /* Store info in packet */ |
829 | 0 | p_add_proto_data(wmem_file_scope(), pinfo, proto_pdcp_lte, 0, p_pdcp_lte_info); |
830 | 0 | } |
831 | |
|
832 | 0 | p_pdcp_lte_info->ueid = rlc_info->ueid; |
833 | 0 | if (rlc_info->nbMode == rlc_nb_mode) { |
834 | 0 | p_pdcp_lte_info->channelType = Channel_DCCH_NB; |
835 | 0 | } else { |
836 | 0 | p_pdcp_lte_info->channelType = Channel_DCCH; |
837 | 0 | } |
838 | 0 | p_pdcp_lte_info->channelId = rlc_info->channelId; |
839 | 0 | p_pdcp_lte_info->direction = rlc_info->direction; |
840 | 0 | p_pdcp_lte_info->is_retx = (state != SN_OK); |
841 | | |
842 | | /* Set plane and sequence number length */ |
843 | 0 | p_pdcp_lte_info->no_header_pdu = false; |
844 | 0 | if (rlc_info->channelType == CHANNEL_TYPE_SRB) { |
845 | 0 | p_pdcp_lte_info->plane = SIGNALING_PLANE; |
846 | 0 | if ((rlc_info->nbMode == rlc_nb_mode) && (rlc_info->channelId == 3)) { |
847 | 0 | p_pdcp_lte_info->no_header_pdu = true; |
848 | 0 | p_pdcp_lte_info->seqnum_length = 0; |
849 | 0 | } else { |
850 | 0 | p_pdcp_lte_info->seqnum_length = 5; |
851 | 0 | } |
852 | 0 | } |
853 | 0 | else { |
854 | 0 | p_pdcp_lte_info->plane = USER_PLANE; |
855 | 0 | switch (global_rlc_lte_call_pdcp_for_drb) { |
856 | 0 | case PDCP_drb_SN_7: |
857 | 0 | p_pdcp_lte_info->seqnum_length = 7; |
858 | 0 | break; |
859 | 0 | case PDCP_drb_SN_12: |
860 | 0 | p_pdcp_lte_info->seqnum_length = 12; |
861 | 0 | break; |
862 | 0 | case PDCP_drb_SN_15: |
863 | 0 | p_pdcp_lte_info->seqnum_length = 15; |
864 | 0 | break; |
865 | 0 | case PDCP_drb_SN_18: |
866 | 0 | p_pdcp_lte_info->seqnum_length = 18; |
867 | 0 | break; |
868 | 0 | case PDCP_drb_SN_signalled: |
869 | | /* Use whatever was signalled (e.g. in RRC) */ |
870 | 0 | id = (rlc_info->channelId << 16) | rlc_info->ueid; |
871 | 0 | key[0].length = 1; |
872 | 0 | key[0].key = &id; |
873 | 0 | key[1].length = 1; |
874 | 0 | key[1].key = &pinfo->num; |
875 | 0 | key[2].length = 0; |
876 | 0 | key[2].key = NULL; |
877 | |
|
878 | 0 | params = (rlc_ue_parameters *)wmem_tree_lookup32_array_le(ue_parameters_tree, key); |
879 | 0 | if (params && (params->id != id)) { |
880 | 0 | params = NULL; |
881 | 0 | } |
882 | 0 | if (params) { |
883 | 0 | p_pdcp_lte_info->seqnum_length = params->pdcp_sn_bits; |
884 | 0 | } else if (rlc_info->nbMode == rlc_nb_mode) { |
885 | 0 | p_pdcp_lte_info->seqnum_length = 7; |
886 | 0 | } else { |
887 | 0 | p_pdcp_lte_info->seqnum_length = 12; |
888 | 0 | } |
889 | 0 | break; |
890 | | |
891 | 0 | default: |
892 | 0 | DISSECTOR_ASSERT(false); |
893 | 0 | break; |
894 | 0 | } |
895 | 0 | } |
896 | | |
897 | 0 | TRY { |
898 | 0 | call_dissector_only(pdcp_lte_handle, pdcp_tvb, pinfo, tree, NULL); |
899 | 0 | } |
900 | 0 | CATCH_ALL { |
901 | 0 | } |
902 | 0 | ENDTRY |
903 | |
|
904 | 0 | proto_item_set_hidden(data_ti); |
905 | 0 | } |
906 | 0 | else if (global_rlc_lte_call_rrc_for_mcch && (rlc_info->channelType == CHANNEL_TYPE_MCCH)) { |
907 | | /* Send whole PDU to RRC */ |
908 | 0 | static tvbuff_t *rrc_tvb = NULL; |
909 | | |
910 | | /* Get tvb for passing to LTE RRC dissector */ |
911 | 0 | if (reassembly_info == NULL) { |
912 | 0 | rrc_tvb = tvb_new_subset_length(tvb, offset, length); |
913 | 0 | } |
914 | 0 | else { |
915 | | /* Get combined tvb. */ |
916 | 0 | rrc_tvb = reassembly_get_reassembled_tvb(reassembly_info, tvb, pinfo); |
917 | 0 | reassembly_show_source(reassembly_info, tree, tvb, offset); |
918 | 0 | } |
919 | |
|
920 | 0 | TRY { |
921 | 0 | call_dissector_only(lte_rrc_mcch, rrc_tvb, pinfo, tree, NULL); |
922 | 0 | } |
923 | 0 | CATCH_ALL { |
924 | 0 | } |
925 | 0 | ENDTRY |
926 | |
|
927 | 0 | proto_item_set_hidden(data_ti); |
928 | 0 | } |
929 | 0 | else if (global_rlc_lte_call_ip_for_mtch && (rlc_info->channelType == CHANNEL_TYPE_MTCH)) { |
930 | | /* Send whole PDU to IP */ |
931 | 0 | static tvbuff_t *ip_tvb = NULL; |
932 | | |
933 | | /* Get tvb for passing to IP dissector */ |
934 | 0 | if (reassembly_info == NULL) { |
935 | 0 | ip_tvb = tvb_new_subset_length(tvb, offset, length); |
936 | 0 | } |
937 | 0 | else { |
938 | | /* Get combined tvb. */ |
939 | 0 | ip_tvb = reassembly_get_reassembled_tvb(reassembly_info, tvb, pinfo); |
940 | 0 | reassembly_show_source(reassembly_info, tree, tvb, offset); |
941 | 0 | } |
942 | |
|
943 | 0 | TRY { |
944 | 0 | call_dissector_only(ip_handle, ip_tvb, pinfo, tree, NULL); |
945 | 0 | } |
946 | 0 | CATCH_ALL { |
947 | 0 | } |
948 | 0 | ENDTRY |
949 | |
|
950 | 0 | proto_item_set_hidden(data_ti); |
951 | 0 | } |
952 | 0 | } |
953 | 0 | } |
954 | | |
955 | | /* Hash table functions for RLC channels */ |
956 | | |
957 | | /* Equal keys */ |
958 | | static int rlc_channel_equal(const void *v, const void *v2) |
959 | 0 | { |
960 | 0 | const channel_hash_key* val1 = (const channel_hash_key *)v; |
961 | 0 | const channel_hash_key* val2 = (const channel_hash_key *)v2; |
962 | | |
963 | | /* All fields must match */ |
964 | | /* N.B. Currently fits into one word, so could return (*v == *v2) |
965 | | if we're sure they're initialised to 0... */ |
966 | 0 | return ((val1->ueId == val2->ueId) && |
967 | 0 | (val1->channelType == val2->channelType) && |
968 | 0 | (val1->channelId == val2->channelId) && |
969 | 0 | (val1->direction == val2->direction)); |
970 | 0 | } |
971 | | |
972 | | /* Compute a hash value for a given key. */ |
973 | | static unsigned rlc_channel_hash_func(const void *v) |
974 | 0 | { |
975 | 0 | const channel_hash_key* val1 = (const channel_hash_key *)v; |
976 | | |
977 | | /* TODO: check/reduce multipliers */ |
978 | 0 | return ((val1->ueId * 1024) + (val1->channelType*64) + (val1->channelId*2) + val1->direction); |
979 | 0 | } |
980 | | |
981 | | |
982 | | /*************************************************************************/ |
983 | | /* Result hash */ |
984 | | |
985 | | typedef struct { |
986 | | uint32_t frameNumber; |
987 | | uint32_t SN : 10; |
988 | | uint32_t channelType : 2; |
989 | | uint32_t channelId: 5; |
990 | | uint32_t direction: 1; |
991 | | } rlc_result_hash_key; |
992 | | |
993 | | /* Compare 2 rlc_result_hash_key structs */ |
994 | | static int rlc_result_hash_equal(const void *v, const void *v2) |
995 | 0 | { |
996 | 0 | const rlc_result_hash_key *val1 = (const rlc_result_hash_key *)v; |
997 | 0 | const rlc_result_hash_key *val2 = (const rlc_result_hash_key *)v2; |
998 | | |
999 | | /* All fields (and any padding...) must match */ |
1000 | 0 | return (memcmp(val1, val2, sizeof(rlc_result_hash_key)) == 0); |
1001 | 0 | } |
1002 | | |
1003 | | /* Compute a hash value for a given key. */ |
1004 | | static unsigned rlc_result_hash_func(const void *v) |
1005 | 0 | { |
1006 | 0 | const rlc_result_hash_key* val1 = (const rlc_result_hash_key *)v; |
1007 | | |
1008 | | /* Got rid of multipliers - no evidence that they reduced collisions */ |
1009 | 0 | return val1->frameNumber + val1->SN + |
1010 | 0 | val1->channelType + |
1011 | 0 | val1->channelId + |
1012 | 0 | val1->direction; |
1013 | 0 | } |
1014 | | |
1015 | | /* Convenience function to get a pointer for the hash_func to work with */ |
1016 | | static void *get_report_hash_key(uint16_t SN, uint32_t frameNumber, |
1017 | | rlc_lte_info *p_rlc_lte_info, |
1018 | | bool do_persist) |
1019 | 0 | { |
1020 | 0 | static rlc_result_hash_key key; |
1021 | 0 | rlc_result_hash_key *p_key; |
1022 | | |
1023 | | /* Only allocate a struct when will be adding entry */ |
1024 | 0 | if (do_persist) { |
1025 | 0 | p_key = wmem_new0(wmem_file_scope(), rlc_result_hash_key); |
1026 | 0 | } |
1027 | 0 | else { |
1028 | 0 | memset(&key, 0, sizeof(rlc_result_hash_key)); |
1029 | 0 | p_key = &key; |
1030 | 0 | } |
1031 | | |
1032 | | /* Fill in details, and return pointer */ |
1033 | 0 | p_key->frameNumber = frameNumber; |
1034 | 0 | p_key->SN = SN; |
1035 | 0 | p_key->channelType = p_rlc_lte_info->channelType; |
1036 | 0 | p_key->channelId = p_rlc_lte_info->channelId; |
1037 | 0 | p_key->direction = p_rlc_lte_info->direction; |
1038 | |
|
1039 | 0 | return p_key; |
1040 | 0 | } |
1041 | | |
1042 | | static void checkFIconsistency(sequence_analysis_report *p, |
1043 | | rlc_lte_info *p_rlc_lte_info, |
1044 | | bool newSegmentStarted, |
1045 | | proto_tree *seqnum_tree, |
1046 | | packet_info *pinfo, tvbuff_t *tvb) |
1047 | 0 | { |
1048 | 0 | proto_item *ti; |
1049 | |
|
1050 | 0 | if (p->previousSegmentIncomplete) { |
1051 | | /* Previous segment was incomplete, so this PDU should continue it */ |
1052 | 0 | if (newSegmentStarted) { |
1053 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_framing_info_correct, |
1054 | 0 | tvb, 0, 0, false); |
1055 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_sequence_analysis_last_segment_not_continued, |
1056 | 0 | "Last segment of previous PDU was not continued for UE %u (%s-%u)", |
1057 | 0 | p_rlc_lte_info->ueid, |
1058 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1059 | 0 | p_rlc_lte_info->channelId); |
1060 | 0 | } |
1061 | 0 | else { |
1062 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_framing_info_correct, |
1063 | 0 | tvb, 0, 0, true); |
1064 | 0 | proto_item_set_hidden(ti); |
1065 | 0 | } |
1066 | 0 | } |
1067 | 0 | else { |
1068 | | /* Previous segment was complete, so this PDU should start a new one */ |
1069 | 0 | if (!newSegmentStarted) { |
1070 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_framing_info_correct, |
1071 | 0 | tvb, 0, 0, false); |
1072 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_sequence_analysis_last_segment_complete, |
1073 | 0 | "Last segment of previous PDU was complete, but new segment was not started on UE %u (%s-%u)", |
1074 | 0 | p_rlc_lte_info->ueid, |
1075 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1076 | 0 | p_rlc_lte_info->channelId); |
1077 | 0 | } |
1078 | 0 | else { |
1079 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_framing_info_correct, |
1080 | 0 | tvb, 0, 0, true); |
1081 | 0 | proto_item_set_hidden(ti); |
1082 | 0 | } |
1083 | 0 | } |
1084 | 0 | proto_item_set_generated(ti); |
1085 | 0 | } |
1086 | | |
1087 | | /* Add to the tree values associated with sequence analysis for this frame */ |
1088 | | static void addChannelSequenceInfo(sequence_analysis_report *p, |
1089 | | bool isControlFrame, |
1090 | | rlc_lte_info *p_rlc_lte_info, |
1091 | | uint16_t sequenceNumber, |
1092 | | bool newSegmentStarted, |
1093 | | rlc_3gpp_tap_info *tap_info, |
1094 | | packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb) |
1095 | 0 | { |
1096 | 0 | proto_tree *seqnum_tree; |
1097 | 0 | proto_item *seqnum_ti; |
1098 | 0 | proto_item *ti; |
1099 | | |
1100 | | /* Create subtree */ |
1101 | 0 | seqnum_ti = proto_tree_add_string_format(tree, |
1102 | 0 | hf_rlc_lte_sequence_analysis, |
1103 | 0 | tvb, 0, 0, |
1104 | 0 | "", "Sequence Analysis"); |
1105 | 0 | seqnum_tree = proto_item_add_subtree(seqnum_ti, |
1106 | 0 | ett_rlc_lte_sequence_analysis); |
1107 | 0 | proto_item_set_generated(seqnum_ti); |
1108 | |
|
1109 | 0 | if (p->previousFrameNum != 0) { |
1110 | 0 | ti = proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_previous_frame, |
1111 | 0 | tvb, 0, 0, p->previousFrameNum); |
1112 | 0 | proto_item_set_generated(ti); |
1113 | 0 | } |
1114 | |
|
1115 | 0 | switch (p_rlc_lte_info->rlcMode) { |
1116 | 0 | case RLC_AM_MODE: |
1117 | | |
1118 | | /********************************************/ |
1119 | | /* AM */ |
1120 | | /********************************************/ |
1121 | |
|
1122 | 0 | switch (p->state) { |
1123 | 0 | case SN_OK: |
1124 | 0 | if (isControlFrame) { |
1125 | 0 | return; |
1126 | 0 | } |
1127 | | |
1128 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok, |
1129 | 0 | tvb, 0, 0, true); |
1130 | 0 | proto_item_set_generated(ti); |
1131 | 0 | proto_item_append_text(seqnum_ti, " - OK"); |
1132 | | |
1133 | | /* Link to next SN in channel (if known) */ |
1134 | 0 | if (p->nextFrameNum != 0) { |
1135 | 0 | proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_next_frame, |
1136 | 0 | tvb, 0, 0, p->nextFrameNum); |
1137 | 0 | } |
1138 | | /* Correct sequence number, so check frame indication bits consistent */ |
1139 | | /* Deactivated for now as it gets confused by resegmentation */ |
1140 | | /* checkFIconsistency(p, p_rlc_lte_info, newSegmentStarted, seqnum_tree, pinfo, tvb); */ |
1141 | 0 | break; |
1142 | | |
1143 | 0 | case SN_MAC_Retx: |
1144 | 0 | if (isControlFrame) { |
1145 | 0 | return; |
1146 | 0 | } |
1147 | | |
1148 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok, |
1149 | 0 | tvb, 0, 0, false); |
1150 | 0 | proto_item_set_generated(ti); |
1151 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_mac_retx, |
1152 | 0 | tvb, 0, 0, true); |
1153 | 0 | proto_item_set_generated(ti); |
1154 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_sequence_analysis_mac_retx, |
1155 | 0 | "AM Frame retransmitted for %s on UE %u - due to MAC retx! (%s-%u)", |
1156 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
1157 | 0 | p_rlc_lte_info->ueid, |
1158 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1159 | 0 | p_rlc_lte_info->channelId); |
1160 | 0 | proto_item_append_text(seqnum_ti, " - MAC retx of SN %u", p->firstSN); |
1161 | 0 | break; |
1162 | | |
1163 | 0 | case SN_Retx: |
1164 | 0 | if (isControlFrame) { |
1165 | 0 | return; |
1166 | 0 | } |
1167 | | |
1168 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok, |
1169 | 0 | tvb, 0, 0, false); |
1170 | 0 | proto_item_set_generated(ti); |
1171 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_retx, |
1172 | 0 | tvb, 0, 0, true); |
1173 | 0 | proto_item_set_generated(ti); |
1174 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_sequence_analysis_retx, |
1175 | 0 | "AM Frame retransmitted for %s on UE %u - most likely in response to NACK (%s-%u)", |
1176 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
1177 | 0 | p_rlc_lte_info->ueid, |
1178 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1179 | 0 | p_rlc_lte_info->channelId); |
1180 | 0 | proto_item_append_text(seqnum_ti, " - SN %u retransmitted", p->firstSN); |
1181 | 0 | break; |
1182 | | |
1183 | 0 | case SN_Repeated: |
1184 | 0 | if (isControlFrame) { |
1185 | 0 | return; |
1186 | 0 | } |
1187 | | |
1188 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok, |
1189 | 0 | tvb, 0, 0, false); |
1190 | 0 | proto_item_set_generated(ti); |
1191 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_repeated, |
1192 | 0 | tvb, 0, 0, true); |
1193 | 0 | proto_item_set_generated(ti); |
1194 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_sequence_analysis_repeated, |
1195 | 0 | "AM SN Repeated for %s for UE %u - probably because didn't receive Status PDU? (%s-%u)", |
1196 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
1197 | 0 | p_rlc_lte_info->ueid, |
1198 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1199 | 0 | p_rlc_lte_info->channelId); |
1200 | 0 | proto_item_append_text(seqnum_ti, "- SN %u Repeated", p->firstSN); |
1201 | 0 | break; |
1202 | | |
1203 | 0 | case SN_Missing: |
1204 | 0 | if (isControlFrame) { |
1205 | 0 | return; |
1206 | 0 | } |
1207 | | |
1208 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok, |
1209 | 0 | tvb, 0, 0, false); |
1210 | 0 | proto_item_set_generated(ti); |
1211 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_skipped, |
1212 | 0 | tvb, 0, 0, true); |
1213 | 0 | proto_item_set_generated(ti); |
1214 | 0 | if (p->lastSN != p->firstSN) { |
1215 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_am_sn_missing, |
1216 | 0 | "AM SNs (%u to %u) missing for %s on UE %u (%s-%u)", |
1217 | 0 | p->firstSN, p->lastSN, |
1218 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
1219 | 0 | p_rlc_lte_info->ueid, |
1220 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1221 | 0 | p_rlc_lte_info->channelId); |
1222 | 0 | proto_item_append_text(seqnum_ti, " - SNs missing (%u to %u)", |
1223 | 0 | p->firstSN, p->lastSN); |
1224 | 0 | if (p_rlc_lte_info->sequenceNumberLength == AM_SN_LENGTH_16_BITS) { |
1225 | 0 | tap_info->missingSNs = ((65536 + (uint32_t)p->lastSN - (uint32_t)p->firstSN) % 65536) + 1; |
1226 | 0 | } else { |
1227 | 0 | tap_info->missingSNs = ((1024 + p->lastSN - p->firstSN) % 1024) + 1; |
1228 | 0 | } |
1229 | 0 | } |
1230 | 0 | else { |
1231 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_am_sn_missing, |
1232 | 0 | "AM SN (%u) missing for %s on UE %u (%s-%u)", |
1233 | 0 | p->firstSN, |
1234 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
1235 | 0 | p_rlc_lte_info->ueid, |
1236 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1237 | 0 | p_rlc_lte_info->channelId); |
1238 | 0 | proto_item_append_text(seqnum_ti, " - SN missing (%u)", p->firstSN); |
1239 | 0 | tap_info->missingSNs = 1; |
1240 | 0 | } |
1241 | 0 | break; |
1242 | | |
1243 | 0 | case ACK_Out_of_Window: |
1244 | 0 | if (!isControlFrame) { |
1245 | 0 | return; |
1246 | 0 | } |
1247 | | |
1248 | | |
1249 | | /* Not OK */ |
1250 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok, |
1251 | 0 | tvb, 0, 0, false); |
1252 | | /* Out of range */ |
1253 | 0 | proto_item_set_generated(ti); |
1254 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ack_out_of_range, |
1255 | 0 | tvb, 0, 0, true); |
1256 | 0 | proto_item_set_generated(ti); |
1257 | | |
1258 | | /* Link back to last seen SN in other direction */ |
1259 | 0 | ti = proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_ack_out_of_range_opposite_frame, |
1260 | 0 | tvb, 0, 0, p->previousFrameNum); |
1261 | 0 | proto_item_set_generated(ti); |
1262 | | |
1263 | | /* Expert error */ |
1264 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_sequence_analysis_ack_out_of_range_opposite_frame, |
1265 | 0 | "AM ACK for SN %u - but last received SN in other direction is %u for UE %u (%s-%u)", |
1266 | 0 | p->firstSN, p->sequenceExpected, |
1267 | 0 | p_rlc_lte_info->ueid, |
1268 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1269 | 0 | p_rlc_lte_info->channelId); |
1270 | 0 | proto_item_append_text(seqnum_ti, "- ACK SN %u Outside Rx Window - last received SN is %u", |
1271 | 0 | p->firstSN, p->sequenceExpected); |
1272 | |
|
1273 | 0 | break; |
1274 | | |
1275 | 0 | default: |
1276 | 0 | return; |
1277 | 0 | } |
1278 | 0 | break; |
1279 | | |
1280 | 0 | case RLC_UM_MODE: |
1281 | | |
1282 | | /********************************************/ |
1283 | | /* UM */ |
1284 | | /********************************************/ |
1285 | | |
1286 | | /* Expected sequence number */ |
1287 | 0 | ti = proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_expected_sn, |
1288 | 0 | tvb, 0, 0, p->sequenceExpected); |
1289 | 0 | proto_item_set_generated(ti); |
1290 | 0 | if (p->sequenceExpectedCorrect) { |
1291 | 0 | proto_item_set_hidden(ti); |
1292 | 0 | } |
1293 | |
|
1294 | 0 | if (!p->sequenceExpectedCorrect) { |
1295 | | /* Work out SN wrap (in case needed below) */ |
1296 | 0 | uint16_t snLimit; |
1297 | 0 | if (p_rlc_lte_info->sequenceNumberLength == UM_SN_LENGTH_5_BITS) { |
1298 | 0 | snLimit = 32; |
1299 | 0 | } |
1300 | 0 | else { |
1301 | 0 | snLimit = 1024; |
1302 | 0 | } |
1303 | |
|
1304 | 0 | switch (p->state) { |
1305 | 0 | case SN_Missing: |
1306 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok, |
1307 | 0 | tvb, 0, 0, false); |
1308 | 0 | proto_item_set_generated(ti); |
1309 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_skipped, |
1310 | 0 | tvb, 0, 0, true); |
1311 | 0 | proto_item_set_generated(ti); |
1312 | 0 | if (p->lastSN != p->firstSN) { |
1313 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_um_sn_missing, |
1314 | 0 | "UM SNs (%u to %u) missing for %s on UE %u (%s-%u)", |
1315 | 0 | p->firstSN, p->lastSN, |
1316 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
1317 | 0 | p_rlc_lte_info->ueid, |
1318 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1319 | 0 | p_rlc_lte_info->channelId); |
1320 | 0 | proto_item_append_text(seqnum_ti, " - SNs missing (%u to %u)", |
1321 | 0 | p->firstSN, p->lastSN); |
1322 | 0 | tap_info->missingSNs = ((snLimit + p->lastSN - p->firstSN) % snLimit) + 1; |
1323 | 0 | } |
1324 | 0 | else { |
1325 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_um_sn_missing, |
1326 | 0 | "UM SN (%u) missing for %s on UE %u (%s-%u)", |
1327 | 0 | p->firstSN, |
1328 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
1329 | 0 | p_rlc_lte_info->ueid, |
1330 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1331 | 0 | p_rlc_lte_info->channelId); |
1332 | 0 | proto_item_append_text(seqnum_ti, " - SN missing (%u)", |
1333 | 0 | p->firstSN); |
1334 | 0 | tap_info->missingSNs = 1; |
1335 | 0 | } |
1336 | 0 | break; |
1337 | | |
1338 | 0 | case SN_Repeated: |
1339 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok, |
1340 | 0 | tvb, 0, 0, false); |
1341 | 0 | proto_item_set_generated(ti); |
1342 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_repeated, |
1343 | 0 | tvb, 0, 0, true); |
1344 | 0 | proto_item_set_generated(ti); |
1345 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_um_sn_repeated, |
1346 | 0 | "UM SN (%u) repeated for %s for UE %u (%s-%u)", |
1347 | 0 | p->firstSN, |
1348 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
1349 | 0 | p_rlc_lte_info->ueid, |
1350 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1351 | 0 | p_rlc_lte_info->channelId); |
1352 | 0 | proto_item_append_text(seqnum_ti, "- SN %u Repeated", |
1353 | 0 | p->firstSN); |
1354 | 0 | break; |
1355 | | |
1356 | 0 | case SN_MAC_Retx: |
1357 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok, |
1358 | 0 | tvb, 0, 0, false); |
1359 | 0 | proto_item_set_generated(ti); |
1360 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_mac_retx, |
1361 | 0 | tvb, 0, 0, true); |
1362 | 0 | proto_item_set_generated(ti); |
1363 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_sequence_analysis_mac_retx, |
1364 | 0 | "UM Frame retransmitted for %s on UE %u - due to MAC retx! (%s-%u)", |
1365 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
1366 | 0 | p_rlc_lte_info->ueid, |
1367 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1368 | 0 | p_rlc_lte_info->channelId); |
1369 | 0 | break; |
1370 | | |
1371 | 0 | default: |
1372 | | /* Incorrect sequence number */ |
1373 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_wrong_sequence_number, |
1374 | 0 | "Wrong Sequence Number for %s on UE %u - got %u, expected %u (%s-%u)", |
1375 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
1376 | 0 | p_rlc_lte_info->ueid, sequenceNumber, p->sequenceExpected, |
1377 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
1378 | 0 | p_rlc_lte_info->channelId); |
1379 | |
|
1380 | 0 | break; |
1381 | 0 | } |
1382 | |
|
1383 | 0 | } |
1384 | 0 | else { |
1385 | | /* Correct sequence number, so check frame indication bits consistent */ |
1386 | 0 | checkFIconsistency(p, p_rlc_lte_info, newSegmentStarted, seqnum_tree, pinfo, tvb); |
1387 | | |
1388 | | /* Set OK here! */ |
1389 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok, |
1390 | 0 | tvb, 0, 0, true); |
1391 | 0 | proto_item_set_generated(ti); |
1392 | 0 | proto_item_append_text(seqnum_ti, " - OK"); |
1393 | 0 | } |
1394 | | |
1395 | | /* Next channel frame */ |
1396 | 0 | if (p->nextFrameNum != 0) { |
1397 | 0 | ti = proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_next_frame, |
1398 | 0 | tvb, 0, 0, p->nextFrameNum); |
1399 | 0 | proto_item_set_generated(ti); |
1400 | 0 | } |
1401 | 0 | } |
1402 | 0 | } |
1403 | | |
1404 | | /* Update the channel status and set report for this frame */ |
1405 | | static sequence_analysis_state checkChannelSequenceInfo(packet_info *pinfo, tvbuff_t *tvb, |
1406 | | rlc_lte_info *p_rlc_lte_info, |
1407 | | bool isControlFrame, |
1408 | | uint8_t number_of_segments, |
1409 | | uint16_t firstSegmentOffset, |
1410 | | uint16_t firstSegmentLength, |
1411 | | uint16_t lastSegmentOffset, |
1412 | | uint16_t sequenceNumber, |
1413 | | bool first_includes_start, bool last_includes_end, |
1414 | | bool is_resegmented _U_, |
1415 | | rlc_3gpp_tap_info *tap_info, |
1416 | | proto_tree *tree) |
1417 | 0 | { |
1418 | 0 | channel_hash_key channel_key; |
1419 | 0 | channel_hash_key *p_channel_key; |
1420 | 0 | channel_sequence_analysis_status *p_channel_status; |
1421 | 0 | sequence_analysis_report *p_report_in_frame = NULL; |
1422 | 0 | bool createdChannel = false; |
1423 | 0 | uint16_t expectedSequenceNumber = 0; |
1424 | 0 | uint32_t snLimit = 0; |
1425 | | |
1426 | | /* If find stat_report_in_frame already, use that and get out */ |
1427 | 0 | if (pinfo->fd->visited) { |
1428 | 0 | p_report_in_frame = (sequence_analysis_report*)wmem_map_lookup(sequence_analysis_report_hash, |
1429 | 0 | get_report_hash_key(sequenceNumber, |
1430 | 0 | pinfo->num, |
1431 | 0 | p_rlc_lte_info, |
1432 | 0 | false)); |
1433 | 0 | if (p_report_in_frame != NULL) { |
1434 | 0 | addChannelSequenceInfo(p_report_in_frame, isControlFrame, p_rlc_lte_info, |
1435 | 0 | sequenceNumber, first_includes_start, |
1436 | 0 | tap_info, pinfo, tree, tvb); |
1437 | 0 | return p_report_in_frame->state; |
1438 | 0 | } |
1439 | | |
1440 | | /* Don't just give up here... */ |
1441 | 0 | } |
1442 | | |
1443 | | |
1444 | | /**************************************************/ |
1445 | | /* Create or find an entry for this channel state */ |
1446 | 0 | channel_key.ueId = p_rlc_lte_info->ueid; |
1447 | 0 | channel_key.channelType = p_rlc_lte_info->channelType; |
1448 | 0 | channel_key.channelId = p_rlc_lte_info->channelId; |
1449 | 0 | channel_key.direction = p_rlc_lte_info->direction; |
1450 | | |
1451 | | /* Do the table lookup */ |
1452 | 0 | p_channel_status = (channel_sequence_analysis_status*)wmem_map_lookup(sequence_analysis_channel_hash, &channel_key); |
1453 | | |
1454 | | /* Create table entry if necessary */ |
1455 | 0 | if (p_channel_status == NULL) { |
1456 | 0 | createdChannel = true; |
1457 | | |
1458 | | /* Allocate a new value and duplicate key contents */ |
1459 | 0 | p_channel_status = wmem_new0(wmem_file_scope(), channel_sequence_analysis_status); |
1460 | 0 | p_channel_key = (channel_hash_key *)wmem_memdup(wmem_file_scope(), &channel_key, sizeof(channel_hash_key)); |
1461 | | |
1462 | | /* Set mode */ |
1463 | 0 | p_channel_status->rlcMode = p_rlc_lte_info->rlcMode; |
1464 | | |
1465 | | /* Add entry */ |
1466 | 0 | wmem_map_insert(sequence_analysis_channel_hash, p_channel_key, p_channel_status); |
1467 | 0 | } |
1468 | | |
1469 | | /* Create space for frame state_report */ |
1470 | 0 | p_report_in_frame = wmem_new0(wmem_file_scope(), sequence_analysis_report); |
1471 | | |
1472 | | |
1473 | | /* Deal with according to channel mode */ |
1474 | 0 | switch (p_channel_status->rlcMode) { |
1475 | 0 | case RLC_UM_MODE: |
1476 | |
|
1477 | 0 | if (p_rlc_lte_info->sequenceNumberLength == UM_SN_LENGTH_5_BITS) { |
1478 | 0 | snLimit = 32; |
1479 | 0 | } |
1480 | 0 | else { |
1481 | 0 | snLimit = 1024; |
1482 | 0 | } |
1483 | | |
1484 | | /* Work out expected sequence number */ |
1485 | 0 | if (!createdChannel) { |
1486 | 0 | expectedSequenceNumber = (p_channel_status->previousSequenceNumber + 1) % snLimit; |
1487 | 0 | } |
1488 | 0 | else { |
1489 | | /* Whatever we got is fine.. */ |
1490 | 0 | expectedSequenceNumber = sequenceNumber; |
1491 | 0 | } |
1492 | |
|
1493 | 0 | if ((sequenceNumber == 0) && |
1494 | 0 | ((p_rlc_lte_info->channelType == CHANNEL_TYPE_MCCH) || (p_rlc_lte_info->channelType == CHANNEL_TYPE_MTCH))) { |
1495 | | /* With eMBMS, SN restarts to 0 at each MCH Scheduling Period so we cannot deduce easily whether |
1496 | | there was a PDU loss or not without analysing the Frame Indicator; assume no loss when seeing 0 */ |
1497 | 0 | expectedSequenceNumber = 0; |
1498 | 0 | } |
1499 | | |
1500 | | /* Set report for this frame */ |
1501 | | /* For UM, sequence number is always expectedSequence number */ |
1502 | 0 | p_report_in_frame->sequenceExpectedCorrect = (sequenceNumber == expectedSequenceNumber); |
1503 | | |
1504 | | /* For wrong sequence number... */ |
1505 | 0 | if (!p_report_in_frame->sequenceExpectedCorrect) { |
1506 | | |
1507 | | /* Don't get confused by MAC (HARQ) retx */ |
1508 | 0 | if (is_mac_lte_frame_retx(pinfo, p_rlc_lte_info->direction)) { |
1509 | 0 | p_report_in_frame->state = SN_MAC_Retx; |
1510 | 0 | p_report_in_frame->firstSN = sequenceNumber; |
1511 | | |
1512 | | /* No channel state to update */ |
1513 | 0 | break; |
1514 | 0 | } |
1515 | | |
1516 | | /* Frames are not missing if we get an earlier sequence number again */ |
1517 | | /* TODO: taking time into account would give better idea of whether missing or repeated... */ |
1518 | 0 | else if ((p_rlc_lte_info->channelType == CHANNEL_TYPE_MCCH) || (p_rlc_lte_info->channelType == CHANNEL_TYPE_MTCH) || |
1519 | 0 | (((snLimit + sequenceNumber - expectedSequenceNumber) % snLimit) < 10)) { |
1520 | 0 | reassembly_destroy(p_channel_status); |
1521 | |
|
1522 | 0 | p_report_in_frame->state = SN_Missing; |
1523 | 0 | tap_info->missingSNs = (snLimit + sequenceNumber - expectedSequenceNumber) % snLimit; |
1524 | 0 | p_report_in_frame->firstSN = expectedSequenceNumber; |
1525 | 0 | p_report_in_frame->lastSN = (snLimit + sequenceNumber - 1) % snLimit; |
1526 | |
|
1527 | 0 | p_report_in_frame->sequenceExpected = expectedSequenceNumber; |
1528 | 0 | p_report_in_frame->previousFrameNum = p_channel_status->previousFrameNum; |
1529 | 0 | p_report_in_frame->previousSegmentIncomplete = p_channel_status->previousSegmentIncomplete; |
1530 | | |
1531 | | /* Update channel status to remember *this* frame */ |
1532 | 0 | p_channel_status->previousFrameNum = pinfo->num; |
1533 | 0 | p_channel_status->previousSequenceNumber = sequenceNumber; |
1534 | 0 | p_channel_status->previousSegmentIncomplete = !last_includes_end; |
1535 | 0 | } |
1536 | 0 | else { |
1537 | | /* An SN has been repeated */ |
1538 | 0 | p_report_in_frame->state = SN_Repeated; |
1539 | 0 | p_report_in_frame->firstSN = sequenceNumber; |
1540 | |
|
1541 | 0 | p_report_in_frame->sequenceExpected = expectedSequenceNumber; |
1542 | 0 | p_report_in_frame->previousFrameNum = p_channel_status->previousFrameNum; |
1543 | 0 | } |
1544 | 0 | } |
1545 | 0 | else { |
1546 | | /* SN was OK */ |
1547 | 0 | p_report_in_frame->sequenceExpected = expectedSequenceNumber; |
1548 | 0 | p_report_in_frame->previousFrameNum = p_channel_status->previousFrameNum; |
1549 | 0 | p_report_in_frame->previousSegmentIncomplete = p_channel_status->previousSegmentIncomplete; |
1550 | | |
1551 | | /* Update channel status to remember *this* frame */ |
1552 | 0 | p_channel_status->previousFrameNum = pinfo->num; |
1553 | 0 | p_channel_status->previousSequenceNumber = sequenceNumber; |
1554 | 0 | p_channel_status->previousSegmentIncomplete = !last_includes_end; |
1555 | |
|
1556 | 0 | if (p_channel_status->reassembly_info) { |
1557 | | /* Add next segment to reassembly info */ |
1558 | 0 | reassembly_add_segment(p_channel_status, sequenceNumber, pinfo->num, |
1559 | 0 | tvb, firstSegmentOffset, firstSegmentLength); |
1560 | | |
1561 | | /* The end of existing reassembly? */ |
1562 | 0 | if (!first_includes_start && |
1563 | 0 | ((number_of_segments > 1) || last_includes_end)) { |
1564 | |
|
1565 | 0 | reassembly_record(p_channel_status, pinfo, sequenceNumber, p_rlc_lte_info); |
1566 | 0 | reassembly_destroy(p_channel_status); |
1567 | 0 | } |
1568 | 0 | } |
1569 | | |
1570 | | /* The start of a new reassembly? */ |
1571 | 0 | if (!last_includes_end && |
1572 | 0 | ((number_of_segments > 1) || first_includes_start)) { |
1573 | |
|
1574 | 0 | uint16_t lastSegmentLength = tvb_reported_length(tvb)-lastSegmentOffset; |
1575 | |
|
1576 | 0 | if (global_rlc_lte_reassembly) { |
1577 | 0 | reassembly_reset(p_channel_status); |
1578 | 0 | reassembly_add_segment(p_channel_status, sequenceNumber, |
1579 | 0 | pinfo->num, |
1580 | 0 | tvb, lastSegmentOffset, lastSegmentLength); |
1581 | 0 | } |
1582 | 0 | } |
1583 | |
|
1584 | 0 | if (p_report_in_frame->previousFrameNum != 0) { |
1585 | | /* Get report for previous frame */ |
1586 | 0 | sequence_analysis_report *p_previous_report; |
1587 | 0 | if (p_rlc_lte_info->sequenceNumberLength == UM_SN_LENGTH_5_BITS) { |
1588 | 0 | snLimit = 32; |
1589 | 0 | } |
1590 | 0 | else { |
1591 | 0 | snLimit = 1024; |
1592 | 0 | } |
1593 | | |
1594 | | /* Look up report for previous SN */ |
1595 | 0 | p_previous_report = (sequence_analysis_report*)wmem_map_lookup(sequence_analysis_report_hash, |
1596 | 0 | get_report_hash_key((sequenceNumber+snLimit-1) % snLimit, |
1597 | 0 | p_report_in_frame->previousFrameNum, |
1598 | 0 | p_rlc_lte_info, |
1599 | 0 | false)); |
1600 | | /* It really shouldn't be NULL... */ |
1601 | 0 | if (p_previous_report != NULL) { |
1602 | | /* Point it forward to this one */ |
1603 | 0 | p_previous_report->nextFrameNum = pinfo->num; |
1604 | 0 | } |
1605 | 0 | } |
1606 | 0 | } |
1607 | | |
1608 | 0 | break; |
1609 | | |
1610 | 0 | case RLC_AM_MODE: |
1611 | |
|
1612 | 0 | if (p_rlc_lte_info->sequenceNumberLength == AM_SN_LENGTH_16_BITS) { |
1613 | 0 | snLimit = 65536; |
1614 | 0 | } else { |
1615 | 0 | snLimit = 1024; |
1616 | 0 | } |
1617 | | |
1618 | | /* Work out expected sequence number */ |
1619 | 0 | if (!createdChannel) { |
1620 | 0 | expectedSequenceNumber = (p_channel_status->previousSequenceNumber + 1) % snLimit; |
1621 | 0 | } |
1622 | 0 | else { |
1623 | | /* Whatever we got is fine.. */ |
1624 | 0 | expectedSequenceNumber = sequenceNumber; |
1625 | 0 | } |
1626 | | |
1627 | | /* For AM, may be: |
1628 | | - expected Sequence number OR |
1629 | | - previous frame repeated |
1630 | | - old SN being sent (in response to NACK) |
1631 | | - new SN, but with frames missed out |
1632 | | Assume window whose front is at expectedSequenceNumber */ |
1633 | | |
1634 | | /* First of all, check to see whether frame is judged to be MAC Retx */ |
1635 | 0 | if (is_mac_lte_frame_retx(pinfo, p_rlc_lte_info->direction)) { |
1636 | | /* Just report that this is a MAC Retx */ |
1637 | 0 | p_report_in_frame->state = SN_MAC_Retx; |
1638 | 0 | p_report_in_frame->firstSN = sequenceNumber; |
1639 | | |
1640 | | /* No channel state to update */ |
1641 | 0 | break; |
1642 | 0 | } |
1643 | | |
1644 | 0 | if (sequenceNumber != expectedSequenceNumber) { |
1645 | | /* Don't trash reassembly info if this looks like a close retx... */ |
1646 | 0 | if (((snLimit + sequenceNumber - expectedSequenceNumber) % snLimit) < 50) { |
1647 | 0 | reassembly_destroy(p_channel_status); |
1648 | 0 | } |
1649 | 0 | } |
1650 | | |
1651 | | /* Expected? */ |
1652 | 0 | if (sequenceNumber == expectedSequenceNumber) { |
1653 | | /* Set report for this frame */ |
1654 | 0 | p_report_in_frame->sequenceExpectedCorrect = true; |
1655 | 0 | p_report_in_frame->sequenceExpected = expectedSequenceNumber; |
1656 | 0 | p_report_in_frame->previousFrameNum = p_channel_status->previousFrameNum; |
1657 | 0 | p_report_in_frame->previousSegmentIncomplete = p_channel_status->previousSegmentIncomplete; |
1658 | 0 | p_report_in_frame->state = SN_OK; |
1659 | | |
1660 | | /* Update channel status */ |
1661 | 0 | p_channel_status->previousSequenceNumber = sequenceNumber; |
1662 | 0 | p_channel_status->previousFrameNum = pinfo->num; |
1663 | 0 | p_channel_status->previousSegmentIncomplete = !last_includes_end; |
1664 | | |
1665 | |
|
1666 | 0 | if (p_channel_status->reassembly_info) { |
1667 | | |
1668 | | /* Add next segment to reassembly info */ |
1669 | 0 | reassembly_add_segment(p_channel_status, sequenceNumber, pinfo->num, |
1670 | 0 | tvb, firstSegmentOffset, firstSegmentLength); |
1671 | | |
1672 | | /* The end of existing reassembly? */ |
1673 | 0 | if (!first_includes_start && |
1674 | 0 | ((number_of_segments > 1) || last_includes_end)) { |
1675 | |
|
1676 | 0 | reassembly_record(p_channel_status, pinfo, |
1677 | 0 | sequenceNumber, p_rlc_lte_info); |
1678 | 0 | reassembly_destroy(p_channel_status); |
1679 | 0 | } |
1680 | 0 | } |
1681 | | |
1682 | | /* The start of a new reassembly? */ |
1683 | 0 | if (!last_includes_end && |
1684 | 0 | ((number_of_segments > 1) || first_includes_start)) { |
1685 | |
|
1686 | 0 | uint16_t lastSegmentLength = tvb_reported_length(tvb)-lastSegmentOffset; |
1687 | 0 | if (global_rlc_lte_reassembly) { |
1688 | 0 | reassembly_reset(p_channel_status); |
1689 | 0 | reassembly_add_segment(p_channel_status, sequenceNumber, |
1690 | 0 | pinfo->num, |
1691 | 0 | tvb, lastSegmentOffset, lastSegmentLength); |
1692 | 0 | } |
1693 | 0 | } |
1694 | |
|
1695 | 0 | if (p_report_in_frame->previousFrameNum != 0) { |
1696 | | /* Get report for previous frame */ |
1697 | 0 | sequence_analysis_report *p_previous_report; |
1698 | 0 | p_previous_report = (sequence_analysis_report*)wmem_map_lookup(sequence_analysis_report_hash, |
1699 | 0 | get_report_hash_key((sequenceNumber+snLimit-1) % snLimit, |
1700 | 0 | p_report_in_frame->previousFrameNum, |
1701 | 0 | p_rlc_lte_info, |
1702 | 0 | false)); |
1703 | | /* It really shouldn't be NULL... */ |
1704 | 0 | if (p_previous_report != NULL) { |
1705 | | /* Point it forward to this one */ |
1706 | 0 | p_previous_report->nextFrameNum = pinfo->num; |
1707 | 0 | } |
1708 | 0 | } |
1709 | |
|
1710 | 0 | } |
1711 | | |
1712 | | /* Previous subframe repeated? */ |
1713 | 0 | else if (((sequenceNumber+1) % snLimit) == expectedSequenceNumber) { |
1714 | 0 | p_report_in_frame->state = SN_Repeated; |
1715 | | |
1716 | | /* Set report for this frame */ |
1717 | 0 | p_report_in_frame->sequenceExpectedCorrect = false; |
1718 | 0 | p_report_in_frame->sequenceExpected = expectedSequenceNumber; |
1719 | 0 | p_report_in_frame->firstSN = sequenceNumber; |
1720 | 0 | p_report_in_frame->previousFrameNum = p_channel_status->previousFrameNum; |
1721 | 0 | p_report_in_frame->previousSegmentIncomplete = p_channel_status->previousSegmentIncomplete; |
1722 | | |
1723 | | |
1724 | | /* Really should be nothing to update... */ |
1725 | 0 | p_channel_status->previousSequenceNumber = sequenceNumber; |
1726 | 0 | p_channel_status->previousFrameNum = pinfo->num; |
1727 | 0 | p_channel_status->previousSegmentIncomplete = !last_includes_end; |
1728 | 0 | } |
1729 | | |
1730 | 0 | else { |
1731 | | /* Need to work out if new (with skips, or likely a retx (due to NACK)) */ |
1732 | 0 | int delta = (snLimit + expectedSequenceNumber - sequenceNumber) % snLimit; |
1733 | | |
1734 | | /* Rx window is 512/32768, so check to see if this is a retx */ |
1735 | 0 | if (delta < (int)(snLimit>>1)) { |
1736 | | /* Probably a retx due to receiving NACK */ |
1737 | 0 | p_report_in_frame->state = SN_Retx; |
1738 | |
|
1739 | 0 | p_report_in_frame->firstSN = sequenceNumber; |
1740 | | /* Don't update anything in channel state */ |
1741 | 0 | } |
1742 | | |
1743 | 0 | else { |
1744 | | /* Ahead of expected SN. Assume frames have been missed */ |
1745 | 0 | p_report_in_frame->state = SN_Missing; |
1746 | |
|
1747 | 0 | p_report_in_frame->firstSN = expectedSequenceNumber; |
1748 | 0 | p_report_in_frame->lastSN = (snLimit + sequenceNumber-1) % snLimit; |
1749 | | |
1750 | | /* Update channel state - forget about missed SNs */ |
1751 | 0 | p_report_in_frame->sequenceExpected = expectedSequenceNumber; |
1752 | 0 | p_channel_status->previousSequenceNumber = sequenceNumber; |
1753 | 0 | p_channel_status->previousFrameNum = pinfo->num; |
1754 | 0 | p_channel_status->previousSegmentIncomplete = !last_includes_end; |
1755 | 0 | } |
1756 | 0 | } |
1757 | 0 | break; |
1758 | | |
1759 | 0 | default: |
1760 | | /* Shouldn't get here! */ |
1761 | 0 | return SN_Error; |
1762 | 0 | } |
1763 | | |
1764 | | /* Associate with this frame number */ |
1765 | 0 | wmem_map_insert(sequence_analysis_report_hash, |
1766 | 0 | get_report_hash_key(sequenceNumber, pinfo->num, p_rlc_lte_info, true), |
1767 | 0 | p_report_in_frame); |
1768 | | |
1769 | | /* Add state report for this frame into tree */ |
1770 | 0 | addChannelSequenceInfo(p_report_in_frame, isControlFrame, p_rlc_lte_info, sequenceNumber, |
1771 | 0 | first_includes_start, tap_info, pinfo, tree, tvb); |
1772 | |
|
1773 | 0 | return p_report_in_frame->state; |
1774 | 0 | } |
1775 | | |
1776 | | |
1777 | | /* Add to the tree values associated with sequence analysis for this frame */ |
1778 | | static void addChannelRepeatedNACKInfo(channel_repeated_nack_report *p, |
1779 | | rlc_lte_info *p_rlc_lte_info, |
1780 | | packet_info *pinfo, proto_tree *tree, |
1781 | | tvbuff_t *tvb) |
1782 | 0 | { |
1783 | 0 | proto_tree *seqnum_tree; |
1784 | 0 | proto_item *seqnum_ti; |
1785 | 0 | proto_item *ti; |
1786 | 0 | int n; |
1787 | | |
1788 | | /* Create subtree */ |
1789 | 0 | seqnum_ti = proto_tree_add_string_format(tree, |
1790 | 0 | hf_rlc_lte_sequence_analysis, |
1791 | 0 | tvb, 0, 0, |
1792 | 0 | "", "Sequence Analysis"); |
1793 | 0 | seqnum_tree = proto_item_add_subtree(seqnum_ti, |
1794 | 0 | ett_rlc_lte_sequence_analysis); |
1795 | 0 | proto_item_set_generated(seqnum_ti); |
1796 | | |
1797 | | /* OK = false */ |
1798 | 0 | ti = proto_tree_add_boolean(seqnum_tree, hf_rlc_lte_sequence_analysis_ok, |
1799 | 0 | tvb, 0, 0, false); |
1800 | 0 | proto_item_set_generated(ti); |
1801 | | |
1802 | | /* Add each repeated NACK as item & expert info */ |
1803 | 0 | for (n=0; n < p->noOfNACKsRepeated; n++) { |
1804 | |
|
1805 | 0 | ti = proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_repeated_nack, |
1806 | 0 | tvb, 0, 0, p->repeatedNACKs[n]); |
1807 | 0 | proto_item_set_generated(ti); |
1808 | |
|
1809 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_sequence_analysis_repeated_nack, |
1810 | 0 | "Same SN (%u) NACKd for %s on UE %u in successive Status PDUs", |
1811 | 0 | p->repeatedNACKs[n], |
1812 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
1813 | 0 | p_rlc_lte_info->ueid); |
1814 | 0 | } |
1815 | | |
1816 | | /* Link back to previous status report */ |
1817 | 0 | ti = proto_tree_add_uint(seqnum_tree, hf_rlc_lte_sequence_analysis_repeated_nack_original_frame, |
1818 | 0 | tvb, 0, 0, p->previousFrameNum); |
1819 | 0 | proto_item_set_generated(ti); |
1820 | | |
1821 | | /* Append count to sequence analysis root */ |
1822 | 0 | proto_item_append_text(seqnum_ti, " - %u SNs repeated from previous Status PDU", |
1823 | 0 | p->noOfNACKsRepeated); |
1824 | 0 | } |
1825 | | |
1826 | | |
1827 | | /* Update the channel repeated NACK status and set report for this frame */ |
1828 | | static void checkChannelRepeatedNACKInfo(packet_info *pinfo, |
1829 | | rlc_lte_info *p_rlc_lte_info, |
1830 | | rlc_3gpp_tap_info *tap_info, |
1831 | | proto_tree *tree, |
1832 | | tvbuff_t *tvb) |
1833 | 0 | { |
1834 | 0 | channel_hash_key channel_key; |
1835 | 0 | channel_hash_key *p_channel_key; |
1836 | 0 | channel_repeated_nack_status *p_channel_status; |
1837 | 0 | channel_repeated_nack_report *p_report_in_frame = NULL; |
1838 | |
|
1839 | 0 | uint16_t noOfNACKsRepeated = 0; |
1840 | 0 | uint16_t repeatedNACKs[MAX_NACKs]; |
1841 | 0 | int n, i, j; |
1842 | | |
1843 | | /* If find state_report_in_frame already, use that and get out */ |
1844 | 0 | if (pinfo->fd->visited) { |
1845 | 0 | p_report_in_frame = (channel_repeated_nack_report*)wmem_map_lookup(repeated_nack_report_hash, |
1846 | 0 | get_report_hash_key(0, pinfo->num, |
1847 | 0 | p_rlc_lte_info, false)); |
1848 | 0 | if (p_report_in_frame != NULL) { |
1849 | 0 | addChannelRepeatedNACKInfo(p_report_in_frame, p_rlc_lte_info, |
1850 | 0 | pinfo, tree, tvb); |
1851 | 0 | return; |
1852 | 0 | } |
1853 | 0 | else { |
1854 | | /* Give up - we must have tried already... */ |
1855 | 0 | return; |
1856 | 0 | } |
1857 | 0 | } |
1858 | | |
1859 | | |
1860 | | /**************************************************/ |
1861 | | /* Create or find an entry for this channel state */ |
1862 | 0 | channel_key.ueId = p_rlc_lte_info->ueid; |
1863 | 0 | channel_key.channelType = p_rlc_lte_info->channelType; |
1864 | 0 | channel_key.channelId = p_rlc_lte_info->channelId; |
1865 | 0 | channel_key.direction = p_rlc_lte_info->direction; |
1866 | 0 | memset(repeatedNACKs, 0, sizeof(repeatedNACKs)); |
1867 | | |
1868 | | /* Do the table lookup */ |
1869 | 0 | p_channel_status = (channel_repeated_nack_status*)wmem_map_lookup(repeated_nack_channel_hash, &channel_key); |
1870 | | |
1871 | | /* Create table entry if necessary */ |
1872 | 0 | if (p_channel_status == NULL) { |
1873 | | |
1874 | | /* Allocate a new key and value */ |
1875 | 0 | p_channel_key = wmem_new(wmem_file_scope(), channel_hash_key); |
1876 | 0 | p_channel_status = wmem_new0(wmem_file_scope(), channel_repeated_nack_status); |
1877 | | |
1878 | | /* Copy key contents */ |
1879 | 0 | memcpy(p_channel_key, &channel_key, sizeof(channel_hash_key)); |
1880 | | |
1881 | | /* Add entry to table */ |
1882 | 0 | wmem_map_insert(repeated_nack_channel_hash, p_channel_key, p_channel_status); |
1883 | 0 | } |
1884 | | |
1885 | | /* Compare NACKs in channel status with NACKs in tap_info. |
1886 | | Note any that are repeated */ |
1887 | 0 | for (i=0; i < p_channel_status->noOfNACKs; i++) { |
1888 | 0 | for (j=0; j < MIN(tap_info->noOfNACKs, MAX_NACKs); j++) { |
1889 | 0 | if (tap_info->NACKs[j] == p_channel_status->NACKs[i]) { |
1890 | | /* Don't add the same repeated NACK twice! */ |
1891 | 0 | if ((noOfNACKsRepeated == 0) || |
1892 | 0 | (repeatedNACKs[noOfNACKsRepeated-1] != p_channel_status->NACKs[i])) { |
1893 | |
|
1894 | 0 | repeatedNACKs[noOfNACKsRepeated++] = p_channel_status->NACKs[i]; |
1895 | 0 | } |
1896 | 0 | } |
1897 | 0 | } |
1898 | 0 | } |
1899 | | |
1900 | | /* Copy NACKs from tap_info into channel status for next time! */ |
1901 | 0 | p_channel_status->noOfNACKs = 0; |
1902 | 0 | for (n=0; n < MIN(tap_info->noOfNACKs, MAX_NACKs); n++) { |
1903 | 0 | p_channel_status->NACKs[p_channel_status->noOfNACKs++] = tap_info->NACKs[n]; |
1904 | 0 | } |
1905 | |
|
1906 | 0 | if (noOfNACKsRepeated >= 1) { |
1907 | | /* Create space for frame state_report */ |
1908 | 0 | p_report_in_frame = wmem_new(wmem_file_scope(), channel_repeated_nack_report); |
1909 | | |
1910 | | /* Copy in found duplicates */ |
1911 | 0 | for (n=0; n < MIN(tap_info->noOfNACKs, MAX_NACKs); n++) { |
1912 | 0 | p_report_in_frame->repeatedNACKs[n] = repeatedNACKs[n]; |
1913 | 0 | } |
1914 | 0 | p_report_in_frame->noOfNACKsRepeated = noOfNACKsRepeated; |
1915 | |
|
1916 | 0 | p_report_in_frame->previousFrameNum = p_channel_status->frameNum; |
1917 | | |
1918 | | /* Associate with this frame number */ |
1919 | 0 | wmem_map_insert(repeated_nack_report_hash, |
1920 | 0 | get_report_hash_key(0, pinfo->num, |
1921 | 0 | p_rlc_lte_info, true), |
1922 | 0 | p_report_in_frame); |
1923 | | |
1924 | | /* Add state report for this frame into tree */ |
1925 | 0 | addChannelRepeatedNACKInfo(p_report_in_frame, p_rlc_lte_info, |
1926 | 0 | pinfo, tree, tvb); |
1927 | 0 | } |
1928 | | |
1929 | | /* Save frame number for next comparison */ |
1930 | 0 | p_channel_status->frameNum = pinfo->num; |
1931 | 0 | } |
1932 | | |
1933 | | /* Check that the ACK is consistent with data the expected sequence number |
1934 | | in the other direction */ |
1935 | | static void checkChannelACKWindow(uint16_t ack_sn, |
1936 | | packet_info *pinfo, |
1937 | | rlc_lte_info *p_rlc_lte_info, |
1938 | | rlc_3gpp_tap_info *tap_info, |
1939 | | proto_tree *tree, |
1940 | | tvbuff_t *tvb) |
1941 | 0 | { |
1942 | 0 | channel_hash_key channel_key; |
1943 | 0 | channel_sequence_analysis_status *p_channel_status; |
1944 | 0 | sequence_analysis_report *p_report_in_frame = NULL; |
1945 | 0 | uint32_t snLimit; |
1946 | | |
1947 | | /* If find stat_report_in_frame already, use that and get out */ |
1948 | 0 | if (pinfo->fd->visited) { |
1949 | 0 | p_report_in_frame = (sequence_analysis_report*)wmem_map_lookup(sequence_analysis_report_hash, |
1950 | 0 | get_report_hash_key(0, pinfo->num, |
1951 | 0 | p_rlc_lte_info, |
1952 | 0 | false)); |
1953 | 0 | if (p_report_in_frame != NULL) { |
1954 | | /* Add any info to tree */ |
1955 | 0 | addChannelSequenceInfo(p_report_in_frame, true, p_rlc_lte_info, |
1956 | 0 | 0, false, |
1957 | 0 | tap_info, pinfo, tree, tvb); |
1958 | 0 | return; |
1959 | 0 | } |
1960 | 0 | else { |
1961 | | /* Give up - we must have tried already... */ |
1962 | 0 | return; |
1963 | 0 | } |
1964 | 0 | } |
1965 | | |
1966 | | /*******************************************************************/ |
1967 | | /* Find an entry for this channel state (in the opposite direction */ |
1968 | 0 | channel_key.ueId = p_rlc_lte_info->ueid; |
1969 | 0 | channel_key.channelType = p_rlc_lte_info->channelType; |
1970 | 0 | channel_key.channelId = p_rlc_lte_info->channelId; |
1971 | 0 | channel_key.direction = |
1972 | 0 | (p_rlc_lte_info->direction == DIRECTION_UPLINK) ? DIRECTION_DOWNLINK : DIRECTION_UPLINK; |
1973 | | |
1974 | | /* Do the table lookup */ |
1975 | 0 | p_channel_status = (channel_sequence_analysis_status*)wmem_map_lookup(sequence_analysis_channel_hash, &channel_key); |
1976 | | |
1977 | | /* Create table entry if necessary */ |
1978 | 0 | if (p_channel_status == NULL) { |
1979 | 0 | return; |
1980 | 0 | } |
1981 | | |
1982 | | /* Is it in the rx window? This test will catch if it's ahead, but we don't |
1983 | | really know what the back of the tx window is... */ |
1984 | 0 | snLimit = (p_rlc_lte_info->sequenceNumberLength == AM_SN_LENGTH_16_BITS) ? 65536 : 1024; |
1985 | 0 | if (((snLimit + (uint32_t)p_channel_status->previousSequenceNumber+1 - ack_sn) % snLimit) > (snLimit>>1)) { |
1986 | | |
1987 | | /* Set result */ |
1988 | 0 | p_report_in_frame = wmem_new0(wmem_file_scope(), sequence_analysis_report); |
1989 | 0 | p_report_in_frame->state = ACK_Out_of_Window; |
1990 | 0 | p_report_in_frame->previousFrameNum = p_channel_status->previousFrameNum; |
1991 | 0 | p_report_in_frame->sequenceExpected = p_channel_status->previousSequenceNumber; |
1992 | 0 | p_report_in_frame->firstSN = ack_sn; |
1993 | | |
1994 | | /* Associate with this frame number */ |
1995 | 0 | wmem_map_insert(sequence_analysis_report_hash, |
1996 | 0 | get_report_hash_key(0, pinfo->num, |
1997 | 0 | p_rlc_lte_info, true), |
1998 | 0 | p_report_in_frame); |
1999 | | |
2000 | | /* Add state report for this frame into tree */ |
2001 | 0 | addChannelSequenceInfo(p_report_in_frame, true, p_rlc_lte_info, 0, |
2002 | 0 | false, tap_info, pinfo, tree, tvb); |
2003 | 0 | } |
2004 | 0 | } |
2005 | | |
2006 | | |
2007 | | |
2008 | | |
2009 | | /***************************************************/ |
2010 | | /* Transparent mode PDU. Call RRC if configured to */ |
2011 | | static void dissect_rlc_lte_tm(tvbuff_t *tvb, packet_info *pinfo, |
2012 | | proto_tree *tree, |
2013 | | unsigned offset, |
2014 | | rlc_lte_info *p_rlc_lte_info, |
2015 | | proto_item *top_ti) |
2016 | 0 | { |
2017 | 0 | proto_item *raw_tm_ti; |
2018 | 0 | proto_item *tm_ti; |
2019 | | |
2020 | | /* Create hidden TM root */ |
2021 | 0 | tm_ti = proto_tree_add_string_format(tree, hf_rlc_lte_tm, |
2022 | 0 | tvb, offset, 0, "", "TM"); |
2023 | 0 | proto_item_set_hidden(tm_ti); |
2024 | | |
2025 | | /* Remaining bytes are all data */ |
2026 | 0 | raw_tm_ti = proto_tree_add_item(tree, hf_rlc_lte_tm_data, tvb, offset, -1, ENC_NA); |
2027 | 0 | if (!global_rlc_lte_call_rrc_for_ccch) { |
2028 | 0 | write_pdu_label_and_info(top_ti, NULL, pinfo, |
2029 | 0 | " [%u-bytes]", tvb_reported_length_remaining(tvb, offset)); |
2030 | 0 | } |
2031 | |
|
2032 | 0 | if (global_rlc_lte_call_rrc_for_ccch) { |
2033 | 0 | tvbuff_t *rrc_tvb = tvb_new_subset_remaining(tvb, offset); |
2034 | 0 | volatile dissector_handle_t protocol_handle; |
2035 | |
|
2036 | 0 | switch (p_rlc_lte_info->channelType) { |
2037 | 0 | case CHANNEL_TYPE_CCCH: |
2038 | 0 | if (p_rlc_lte_info->direction == DIRECTION_UPLINK) { |
2039 | 0 | protocol_handle = (p_rlc_lte_info->nbMode == rlc_nb_mode) ? |
2040 | 0 | lte_rrc_ul_ccch_nb : lte_rrc_ul_ccch; |
2041 | 0 | } |
2042 | 0 | else { |
2043 | 0 | protocol_handle = (p_rlc_lte_info->nbMode == rlc_nb_mode) ? |
2044 | 0 | lte_rrc_dl_ccch_nb : lte_rrc_dl_ccch; |
2045 | 0 | } |
2046 | 0 | break; |
2047 | | |
2048 | 0 | case CHANNEL_TYPE_BCCH_BCH: |
2049 | 0 | protocol_handle = (p_rlc_lte_info->nbMode == rlc_nb_mode) ? |
2050 | 0 | lte_rrc_bcch_bch_nb : lte_rrc_bcch_bch; |
2051 | 0 | break; |
2052 | 0 | case CHANNEL_TYPE_BCCH_DL_SCH: |
2053 | 0 | protocol_handle = (p_rlc_lte_info->nbMode == rlc_nb_mode) ? |
2054 | 0 | lte_rrc_bcch_dl_sch_nb : lte_rrc_bcch_dl_sch; |
2055 | 0 | break; |
2056 | 0 | case CHANNEL_TYPE_PCCH: |
2057 | 0 | protocol_handle = (p_rlc_lte_info->nbMode == rlc_nb_mode) ? |
2058 | 0 | lte_rrc_pcch_nb : lte_rrc_pcch; |
2059 | 0 | break; |
2060 | | |
2061 | 0 | case CHANNEL_TYPE_SRB: |
2062 | 0 | case CHANNEL_TYPE_DRB: |
2063 | 0 | case CHANNEL_TYPE_MCCH: |
2064 | 0 | case CHANNEL_TYPE_MTCH: |
2065 | |
|
2066 | 0 | default: |
2067 | | /* Shouldn't happen, just return... */ |
2068 | 0 | return; |
2069 | 0 | } |
2070 | | |
2071 | | /* Hide raw view of bytes */ |
2072 | 0 | proto_item_set_hidden(raw_tm_ti); |
2073 | | |
2074 | | /* Call it (catch exceptions) */ |
2075 | 0 | TRY { |
2076 | 0 | call_dissector_only(protocol_handle, rrc_tvb, pinfo, tree, NULL); |
2077 | 0 | } |
2078 | 0 | CATCH_ALL { |
2079 | 0 | } |
2080 | 0 | ENDTRY |
2081 | 0 | } |
2082 | 0 | } |
2083 | | |
2084 | | |
2085 | | |
2086 | | /***************************************************/ |
2087 | | /* Unacknowledged mode PDU */ |
2088 | | static void dissect_rlc_lte_um(tvbuff_t *tvb, packet_info *pinfo, |
2089 | | proto_tree *tree, |
2090 | | unsigned offset, |
2091 | | rlc_lte_info *p_rlc_lte_info, |
2092 | | proto_item *top_ti, |
2093 | | rlc_3gpp_tap_info *tap_info) |
2094 | 0 | { |
2095 | 0 | uint64_t framing_info; |
2096 | 0 | bool first_includes_start; |
2097 | 0 | bool last_includes_end; |
2098 | 0 | uint64_t fixed_extension; |
2099 | 0 | uint64_t sn; |
2100 | 0 | unsigned start_offset = offset; |
2101 | 0 | proto_item *um_ti; |
2102 | 0 | proto_tree *um_header_tree; |
2103 | 0 | proto_item *um_header_ti; |
2104 | 0 | bool is_truncated = false; |
2105 | 0 | proto_item *truncated_ti; |
2106 | 0 | rlc_channel_reassembly_info *reassembly_info = NULL; |
2107 | 0 | sequence_analysis_state seq_anal_state = SN_OK; |
2108 | | |
2109 | | /* Hidden UM root */ |
2110 | 0 | um_ti = proto_tree_add_string_format(tree, hf_rlc_lte_um, |
2111 | 0 | tvb, offset, 0, "", "UM"); |
2112 | 0 | proto_item_set_hidden(um_ti); |
2113 | | |
2114 | | /* Add UM header subtree */ |
2115 | 0 | um_header_ti = proto_tree_add_string_format(tree, hf_rlc_lte_um_header, |
2116 | 0 | tvb, offset, 0, |
2117 | 0 | "", "UM header"); |
2118 | 0 | um_header_tree = proto_item_add_subtree(um_header_ti, |
2119 | 0 | ett_rlc_lte_um_header); |
2120 | | |
2121 | | |
2122 | | /*******************************/ |
2123 | | /* Fixed UM header */ |
2124 | 0 | if (p_rlc_lte_info->sequenceNumberLength == UM_SN_LENGTH_5_BITS) { |
2125 | | /* Framing info (2 bits) */ |
2126 | 0 | proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_lte_um_fi, |
2127 | 0 | tvb, offset*8, 2, |
2128 | 0 | &framing_info, ENC_BIG_ENDIAN); |
2129 | | |
2130 | | /* Extension (1 bit) */ |
2131 | 0 | proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_lte_um_fixed_e, tvb, |
2132 | 0 | (offset*8) + 2, 1, |
2133 | 0 | &fixed_extension, ENC_BIG_ENDIAN); |
2134 | | |
2135 | | /* Sequence Number (5 bit) */ |
2136 | 0 | proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_lte_um_sn, tvb, |
2137 | 0 | (offset*8) + 3, 5, |
2138 | 0 | &sn, ENC_BIG_ENDIAN); |
2139 | 0 | offset++; |
2140 | 0 | } |
2141 | 0 | else if (p_rlc_lte_info->sequenceNumberLength == UM_SN_LENGTH_10_BITS) { |
2142 | 0 | uint32_t reserved; |
2143 | 0 | proto_item *ti; |
2144 | | |
2145 | | /* Check 3 Reserved bits */ |
2146 | 0 | ti = proto_tree_add_item_ret_uint(um_header_tree, hf_rlc_lte_um_fixed_reserved, tvb, offset, 1, ENC_BIG_ENDIAN, &reserved); |
2147 | 0 | if (reserved != 0) { |
2148 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_reserved_bits_not_zero, |
2149 | 0 | "RLC UM Fixed header Reserved bits not zero (found 0x%x)", reserved); |
2150 | 0 | } |
2151 | | |
2152 | | /* Framing info (2 bits) */ |
2153 | 0 | proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_lte_um_fi, |
2154 | 0 | tvb, (offset*8)+3, 2, |
2155 | 0 | &framing_info, ENC_BIG_ENDIAN); |
2156 | | |
2157 | | /* Extension (1 bit) */ |
2158 | 0 | proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_lte_um_fixed_e, tvb, |
2159 | 0 | (offset*8) + 5, 1, |
2160 | 0 | &fixed_extension, ENC_BIG_ENDIAN); |
2161 | | |
2162 | | /* Sequence Number (10 bits) */ |
2163 | 0 | proto_tree_add_bits_ret_val(um_header_tree, hf_rlc_lte_um_sn, tvb, |
2164 | 0 | (offset*8) + 6, 10, |
2165 | 0 | &sn, ENC_BIG_ENDIAN); |
2166 | 0 | offset += 2; |
2167 | 0 | } |
2168 | 0 | else { |
2169 | | /* Invalid length of sequence number */ |
2170 | 0 | proto_tree_add_expert_format(um_header_tree, pinfo, &ei_rlc_lte_um_sn, tvb, 0, 0, |
2171 | 0 | "Invalid sequence number length (%u bits)", |
2172 | 0 | p_rlc_lte_info->sequenceNumberLength); |
2173 | 0 | return; |
2174 | 0 | } |
2175 | | |
2176 | 0 | tap_info->sequenceNumberGiven = true; |
2177 | 0 | tap_info->sequenceNumber = (uint16_t)sn; |
2178 | | |
2179 | | /* Show SN in info column */ |
2180 | 0 | if ((p_rlc_lte_info->channelType == CHANNEL_TYPE_MCCH) || (p_rlc_lte_info->channelType == CHANNEL_TYPE_MTCH)) { |
2181 | 0 | write_pdu_label_and_info(top_ti, um_header_ti, pinfo, " sn=%-4u", (uint16_t)sn); |
2182 | 0 | } |
2183 | 0 | else { |
2184 | 0 | write_pdu_label_and_info(top_ti, um_header_ti, pinfo, " sn=%-4u", (uint16_t)sn); |
2185 | 0 | } |
2186 | |
|
2187 | 0 | proto_item_set_len(um_header_ti, offset-start_offset); |
2188 | | |
2189 | | |
2190 | | /*************************************/ |
2191 | | /* UM header extension */ |
2192 | 0 | if (fixed_extension) { |
2193 | 0 | offset = dissect_rlc_lte_extension_header(tvb, pinfo, um_header_tree, offset, p_rlc_lte_info); |
2194 | 0 | } |
2195 | | |
2196 | | /* Extract these 2 flags from framing_info */ |
2197 | 0 | first_includes_start = ((uint8_t)framing_info & 0x02) == 0; |
2198 | 0 | last_includes_end = ((uint8_t)framing_info & 0x01) == 0; |
2199 | |
|
2200 | 0 | if (global_rlc_lte_headers_expected) { |
2201 | | /* There might not be any data, if only headers (plus control data) were logged */ |
2202 | 0 | is_truncated = (tvb_captured_length_remaining(tvb, offset) == 0); |
2203 | 0 | truncated_ti = proto_tree_add_uint(tree, hf_rlc_lte_header_only, tvb, 0, 0, |
2204 | 0 | is_truncated); |
2205 | 0 | if (is_truncated) { |
2206 | 0 | int n; |
2207 | 0 | proto_item_set_generated(truncated_ti); |
2208 | 0 | expert_add_info(pinfo, truncated_ti, &ei_rlc_lte_header_only); |
2209 | | |
2210 | | /* Show in the info column how long the data would be */ |
2211 | 0 | for (n=0; n < s_number_of_extensions; n++) { |
2212 | 0 | show_PDU_in_info(pinfo, top_ti, s_lengths[n], |
2213 | 0 | (n==0) ? first_includes_start : true, |
2214 | 0 | true); |
2215 | 0 | offset += s_lengths[n]; |
2216 | 0 | } |
2217 | | /* Last one */ |
2218 | 0 | show_PDU_in_info(pinfo, top_ti, p_rlc_lte_info->pduLength - offset, |
2219 | 0 | (s_number_of_extensions == 0) ? first_includes_start : true, |
2220 | 0 | last_includes_end); |
2221 | 0 | } |
2222 | 0 | else { |
2223 | 0 | proto_item_set_hidden(truncated_ti); |
2224 | 0 | } |
2225 | 0 | } |
2226 | | |
2227 | | /* Show number of extensions in header root */ |
2228 | 0 | if (s_number_of_extensions > 0) { |
2229 | 0 | proto_item_append_text(um_header_ti, " (%u extensions)", s_number_of_extensions); |
2230 | 0 | } |
2231 | | |
2232 | | /* Call sequence analysis function now */ |
2233 | 0 | if (((global_rlc_lte_um_sequence_analysis == SEQUENCE_ANALYSIS_MAC_ONLY) && |
2234 | 0 | (p_get_proto_data(wmem_file_scope(), pinfo, proto_mac_lte, 0) != NULL)) || |
2235 | 0 | ((global_rlc_lte_um_sequence_analysis == SEQUENCE_ANALYSIS_RLC_ONLY) && |
2236 | 0 | (p_get_proto_data(wmem_file_scope(), pinfo, proto_mac_lte, 0) == NULL))) { |
2237 | |
|
2238 | 0 | uint16_t lastSegmentOffset = offset; |
2239 | 0 | if (s_number_of_extensions >= 1) { |
2240 | 0 | int n; |
2241 | 0 | lastSegmentOffset = offset; |
2242 | 0 | for (n=0; n < s_number_of_extensions; n++) { |
2243 | 0 | lastSegmentOffset += s_lengths[n]; |
2244 | 0 | } |
2245 | 0 | } |
2246 | |
|
2247 | 0 | seq_anal_state = checkChannelSequenceInfo(pinfo, tvb, p_rlc_lte_info, |
2248 | 0 | false, |
2249 | 0 | s_number_of_extensions+1, |
2250 | 0 | offset, |
2251 | 0 | s_number_of_extensions ? |
2252 | 0 | s_lengths[0] : |
2253 | 0 | p_rlc_lte_info->pduLength - offset, |
2254 | 0 | lastSegmentOffset, |
2255 | 0 | (uint16_t)sn, first_includes_start, last_includes_end, |
2256 | 0 | false, /* UM doesn't re-segment */ |
2257 | 0 | tap_info, um_header_tree); |
2258 | 0 | } |
2259 | |
|
2260 | 0 | if (is_truncated) { |
2261 | 0 | return; |
2262 | 0 | } |
2263 | | |
2264 | | /*************************************/ |
2265 | | /* Data */ |
2266 | | |
2267 | 0 | reassembly_info = (rlc_channel_reassembly_info *)wmem_map_lookup(reassembly_report_hash, |
2268 | 0 | get_report_hash_key((uint16_t)sn, pinfo->num, |
2269 | 0 | p_rlc_lte_info, false)); |
2270 | |
|
2271 | 0 | if (s_number_of_extensions > 0) { |
2272 | | /* Show each data segment separately */ |
2273 | 0 | int n; |
2274 | 0 | for (n=0; n < s_number_of_extensions; n++) { |
2275 | 0 | show_PDU_in_tree(pinfo, tree, tvb, offset, s_lengths[n], p_rlc_lte_info, |
2276 | 0 | (n==0) ? first_includes_start : true, |
2277 | 0 | (n==0) ? reassembly_info : NULL, |
2278 | 0 | seq_anal_state); |
2279 | 0 | show_PDU_in_info(pinfo, top_ti, s_lengths[n], |
2280 | 0 | (n==0) ? first_includes_start : true, |
2281 | 0 | true); |
2282 | | /* Make sure we don't lose the summary of this SDU */ |
2283 | 0 | col_append_str(pinfo->cinfo, COL_INFO, " | "); |
2284 | 0 | col_set_fence(pinfo->cinfo, COL_INFO); |
2285 | |
|
2286 | 0 | tvb_ensure_bytes_exist(tvb, offset, s_lengths[n]); |
2287 | 0 | offset += s_lengths[n]; |
2288 | 0 | } |
2289 | 0 | } |
2290 | | |
2291 | | /* Final data element */ |
2292 | 0 | show_PDU_in_tree(pinfo, tree, tvb, offset, tvb_reported_length_remaining(tvb, offset), p_rlc_lte_info, |
2293 | 0 | ((s_number_of_extensions == 0) ? first_includes_start : true) && last_includes_end, |
2294 | 0 | (s_number_of_extensions == 0) ? reassembly_info : NULL, |
2295 | 0 | seq_anal_state); |
2296 | 0 | show_PDU_in_info(pinfo, top_ti, (uint16_t)tvb_reported_length_remaining(tvb, offset), |
2297 | 0 | (s_number_of_extensions == 0) ? first_includes_start : true, |
2298 | 0 | last_includes_end); |
2299 | 0 | } |
2300 | | |
2301 | | |
2302 | | |
2303 | | /* Dissect an AM STATUS PDU */ |
2304 | | static void dissect_rlc_lte_am_status_pdu(tvbuff_t *tvb, |
2305 | | packet_info *pinfo, |
2306 | | proto_tree *tree, |
2307 | | proto_item *status_ti, |
2308 | | unsigned offset, |
2309 | | proto_item *top_ti, |
2310 | | rlc_lte_info *p_rlc_lte_info, |
2311 | | rlc_3gpp_tap_info *tap_info) |
2312 | 0 | { |
2313 | 0 | uint32_t cpt; |
2314 | 0 | uint8_t sn_size, so_size; |
2315 | 0 | uint32_t sn_limit; |
2316 | 0 | uint64_t ack_sn, nack_sn; |
2317 | 0 | uint16_t nack_count = 0, so_end_of_pdu; |
2318 | 0 | uint64_t e1 = 0, e2 = 0; |
2319 | 0 | uint64_t so_start, so_end; |
2320 | 0 | int bit_offset = offset * 8; |
2321 | 0 | proto_item *ti; |
2322 | | |
2323 | | /****************************************************************/ |
2324 | | /* Part of RLC control PDU header */ |
2325 | | |
2326 | | /* Control PDU Type (CPT) */ |
2327 | 0 | ti = proto_tree_add_item_ret_uint(tree, hf_rlc_lte_am_cpt, tvb, offset, 1, ENC_BIG_ENDIAN, &cpt); |
2328 | 0 | if (cpt != 0) { |
2329 | | /* Protest and stop - only know about STATUS PDUs */ |
2330 | 0 | expert_add_info_format(pinfo, ti, &ei_rlc_lte_am_cpt, |
2331 | 0 | "RLC Control frame type %u not handled", cpt); |
2332 | 0 | return; |
2333 | 0 | } |
2334 | | |
2335 | 0 | if (p_rlc_lte_info->sequenceNumberLength == AM_SN_LENGTH_16_BITS) { |
2336 | 0 | sn_size = 16; |
2337 | 0 | sn_limit = 65536; |
2338 | 0 | so_size = 16; |
2339 | 0 | so_end_of_pdu = 0xffff; |
2340 | 0 | } else { |
2341 | 0 | sn_size = 10; |
2342 | 0 | sn_limit = 1024; |
2343 | 0 | so_size = 15; |
2344 | 0 | so_end_of_pdu = 0x7fff; |
2345 | 0 | } |
2346 | | |
2347 | | /* The Status PDU itself starts 4 bits into the byte */ |
2348 | 0 | bit_offset += 4; |
2349 | | |
2350 | | /* ACK SN */ |
2351 | 0 | proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_ack_sn, tvb, |
2352 | 0 | bit_offset, sn_size, &ack_sn, ENC_BIG_ENDIAN); |
2353 | 0 | bit_offset += sn_size; |
2354 | 0 | write_pdu_label_and_info(top_ti, status_ti, pinfo, " ACK_SN=%-4u", (uint16_t)ack_sn); |
2355 | |
|
2356 | 0 | tap_info->ACKNo = (uint16_t)ack_sn; |
2357 | | |
2358 | | /* E1 */ |
2359 | 0 | proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_e1, tvb, |
2360 | 0 | bit_offset, 1, &e1, ENC_BIG_ENDIAN); |
2361 | | |
2362 | | /* Skip another bit to byte-align the next bit... */ |
2363 | 0 | bit_offset++; |
2364 | | |
2365 | | /* Optional, extra fields */ |
2366 | 0 | do { |
2367 | 0 | if (e1) { |
2368 | 0 | proto_item *nack_ti; |
2369 | | |
2370 | | /****************************/ |
2371 | | /* Read NACK_SN, E1, E2 */ |
2372 | | |
2373 | | /* NACK_SN */ |
2374 | 0 | nack_ti = proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_nack_sn, tvb, |
2375 | 0 | bit_offset, sn_size, &nack_sn, ENC_BIG_ENDIAN); |
2376 | 0 | bit_offset += sn_size; |
2377 | 0 | write_pdu_label_and_info(top_ti, NULL, pinfo, " NACK_SN=%-4u", (uint16_t)nack_sn); |
2378 | | |
2379 | | /* We shouldn't NACK the ACK_SN! */ |
2380 | 0 | if (nack_sn == ack_sn) { |
2381 | 0 | expert_add_info_format(pinfo, nack_ti, &ei_rlc_lte_am_nack_sn_ack_same, |
2382 | 0 | "Status PDU shouldn't ACK and NACK the same sequence number (%" PRIu64 ")", |
2383 | 0 | ack_sn); |
2384 | 0 | } |
2385 | | |
2386 | | /* NACK should always be 'behind' the ACK */ |
2387 | 0 | if ((sn_limit + ack_sn - nack_sn) % sn_limit > (sn_limit>>1)) { |
2388 | 0 | expert_add_info(pinfo, nack_ti, &ei_rlc_lte_am_nack_sn_ahead_ack); |
2389 | 0 | } |
2390 | | |
2391 | | /* Copy into struct, but don't exceed buffer */ |
2392 | 0 | if (nack_count < MAX_NACKs) { |
2393 | 0 | tap_info->NACKs[nack_count++] = (uint16_t)nack_sn; |
2394 | 0 | } |
2395 | 0 | else { |
2396 | | /* Let it get bigger than the array for accurate stats... */ |
2397 | 0 | nack_count++; |
2398 | 0 | } |
2399 | | |
2400 | | /* E1 */ |
2401 | 0 | proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_e1, tvb, |
2402 | 0 | bit_offset, 1, &e1, ENC_BIG_ENDIAN); |
2403 | 0 | bit_offset++; |
2404 | | |
2405 | | /* E2 */ |
2406 | 0 | proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_e2, tvb, |
2407 | 0 | bit_offset, 1, &e2, ENC_BIG_ENDIAN); |
2408 | | |
2409 | | /* Report as expert info */ |
2410 | 0 | if (e2) { |
2411 | 0 | expert_add_info_format(pinfo, nack_ti, &ei_rlc_lte_am_nack_sn_partial, |
2412 | 0 | "Status PDU reports NACK (partial) on %s for UE %u", |
2413 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
2414 | 0 | p_rlc_lte_info->ueid); |
2415 | 0 | } |
2416 | 0 | else { |
2417 | 0 | expert_add_info_format(pinfo, nack_ti, &ei_rlc_lte_am_nack_sn, |
2418 | 0 | "Status PDU reports NACK on %s for UE %u", |
2419 | 0 | val_to_str_const(p_rlc_lte_info->direction, direction_vals, "Unknown"), |
2420 | 0 | p_rlc_lte_info->ueid); |
2421 | 0 | } |
2422 | |
|
2423 | 0 | bit_offset++; |
2424 | 0 | } |
2425 | |
|
2426 | 0 | if (e2) { |
2427 | | /* Read SOstart, SOend */ |
2428 | 0 | proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_so_start, tvb, |
2429 | 0 | bit_offset, so_size, &so_start, ENC_BIG_ENDIAN); |
2430 | 0 | bit_offset += so_size; |
2431 | |
|
2432 | 0 | proto_tree_add_bits_ret_val(tree, hf_rlc_lte_am_so_end, tvb, |
2433 | 0 | bit_offset, so_size, &so_end, ENC_BIG_ENDIAN); |
2434 | 0 | bit_offset += so_size; |
2435 | | |
2436 | |
|
2437 | 0 | if ((uint16_t)so_end == so_end_of_pdu) { |
2438 | 0 | write_pdu_label_and_info(top_ti, NULL, pinfo, |
2439 | 0 | " (SOstart=%u SOend=<END-OF_PDU>)", |
2440 | 0 | (uint16_t)so_start); |
2441 | 0 | } |
2442 | 0 | else { |
2443 | 0 | write_pdu_label_and_info(top_ti, NULL, pinfo, |
2444 | 0 | " (SOstart=%u SOend=%u)", |
2445 | 0 | (uint16_t)so_start, (uint16_t)so_end); |
2446 | 0 | } |
2447 | | |
2448 | | /* Reset this flag here */ |
2449 | 0 | e2 = 0; |
2450 | 0 | } |
2451 | 0 | } while (e1); |
2452 | |
|
2453 | 0 | if (nack_count > 0) { |
2454 | 0 | proto_item *count_ti = proto_tree_add_uint(tree, hf_rlc_lte_am_nacks, tvb, 0, 1, nack_count); |
2455 | 0 | proto_item_set_generated(count_ti); |
2456 | 0 | proto_item_append_text(status_ti, " (%u NACKs)", nack_count); |
2457 | 0 | tap_info->noOfNACKs = nack_count; |
2458 | 0 | } |
2459 | | |
2460 | | /* Check that we've reached the end of the PDU. If not, show malformed */ |
2461 | 0 | offset = (bit_offset+7) / 8; |
2462 | 0 | if (tvb_reported_length_remaining(tvb, offset) > 0) { |
2463 | 0 | expert_add_info_format(pinfo, status_ti, &ei_rlc_lte_bytes_after_status_pdu_complete, |
2464 | 0 | "%cL %u bytes remaining after Status PDU complete", |
2465 | 0 | (p_rlc_lte_info->direction == DIRECTION_UPLINK) ? 'U' : 'D', |
2466 | 0 | tvb_reported_length_remaining(tvb, offset)); |
2467 | 0 | } |
2468 | | |
2469 | | /* Set selected length of control tree */ |
2470 | 0 | proto_item_set_len(status_ti, offset); |
2471 | | |
2472 | | /* Repeated NACK analysis & check ACK-SN is in range */ |
2473 | 0 | if (((global_rlc_lte_am_sequence_analysis == SEQUENCE_ANALYSIS_MAC_ONLY) && |
2474 | 0 | (p_get_proto_data(wmem_file_scope(), pinfo, proto_mac_lte, 0) != NULL)) || |
2475 | 0 | ((global_rlc_lte_am_sequence_analysis == SEQUENCE_ANALYSIS_RLC_ONLY) && |
2476 | 0 | (p_get_proto_data(wmem_file_scope(), pinfo, proto_mac_lte, 0) == NULL))) { |
2477 | |
|
2478 | 0 | if (!is_mac_lte_frame_retx(pinfo, p_rlc_lte_info->direction)) { |
2479 | 0 | checkChannelRepeatedNACKInfo(pinfo, p_rlc_lte_info, tap_info, tree, tvb); |
2480 | 0 | checkChannelACKWindow((uint16_t)ack_sn, pinfo, p_rlc_lte_info, tap_info, tree, tvb); |
2481 | 0 | } |
2482 | 0 | } |
2483 | 0 | } |
2484 | | |
2485 | | |
2486 | | /***************************************************/ |
2487 | | /* Acknowledged mode PDU */ |
2488 | | static void dissect_rlc_lte_am(tvbuff_t *tvb, packet_info *pinfo, |
2489 | | proto_tree *tree, |
2490 | | unsigned offset, |
2491 | | rlc_lte_info *p_rlc_lte_info, |
2492 | | proto_item *top_ti, |
2493 | | rlc_3gpp_tap_info *tap_info) |
2494 | 0 | { |
2495 | 0 | bool is_data; |
2496 | 0 | uint32_t is_resegmented; |
2497 | 0 | uint32_t polling; |
2498 | 0 | uint32_t fixed_extension; |
2499 | 0 | uint32_t framing_info; |
2500 | 0 | bool first_includes_start; |
2501 | 0 | bool last_includes_end; |
2502 | 0 | proto_item *am_ti; |
2503 | 0 | proto_tree *am_header_tree; |
2504 | 0 | proto_item *am_header_ti; |
2505 | 0 | int start_offset = offset; |
2506 | 0 | uint32_t sn; |
2507 | 0 | bool is_truncated = false; |
2508 | 0 | proto_item *truncated_ti; |
2509 | 0 | rlc_channel_reassembly_info *reassembly_info = NULL; |
2510 | 0 | sequence_analysis_state seq_anal_state = SN_OK; |
2511 | 0 | uint32_t id; |
2512 | 0 | wmem_tree_key_t key[3]; |
2513 | 0 | rlc_ue_parameters *params; |
2514 | | |
2515 | | /* Hidden AM root */ |
2516 | 0 | am_ti = proto_tree_add_string_format(tree, hf_rlc_lte_am, |
2517 | 0 | tvb, offset, 0, "", "AM"); |
2518 | 0 | proto_item_set_hidden(am_ti); |
2519 | | |
2520 | | /* Add AM header subtree */ |
2521 | 0 | am_header_ti = proto_tree_add_string_format(tree, hf_rlc_lte_am_header, |
2522 | 0 | tvb, offset, 0, |
2523 | 0 | "", "AM Header "); |
2524 | 0 | am_header_tree = proto_item_add_subtree(am_header_ti, |
2525 | 0 | ett_rlc_lte_am_header); |
2526 | | |
2527 | | /* First bit is Data/Control flag */ |
2528 | 0 | proto_tree_add_item_ret_boolean(am_header_tree, hf_rlc_lte_am_data_control, tvb, offset, 1, ENC_BIG_ENDIAN, &is_data); |
2529 | 0 | tap_info->isControlPDU = !is_data; |
2530 | |
|
2531 | 0 | if (!is_data) { |
2532 | | /**********************/ |
2533 | | /* Status PDU */ |
2534 | 0 | write_pdu_label_and_info_literal(top_ti, NULL, pinfo, " [CONTROL]"); |
2535 | | |
2536 | | /* Control PDUs are a completely separate format */ |
2537 | 0 | dissect_rlc_lte_am_status_pdu(tvb, pinfo, am_header_tree, am_header_ti, |
2538 | 0 | offset, top_ti, |
2539 | 0 | p_rlc_lte_info, tap_info); |
2540 | 0 | return; |
2541 | 0 | } |
2542 | | |
2543 | | /******************************/ |
2544 | | /* Data PDU fixed header */ |
2545 | | |
2546 | | /* Re-segmentation Flag (RF) field */ |
2547 | 0 | proto_tree_add_item_ret_uint(am_header_tree, hf_rlc_lte_am_rf, tvb, offset, 1, |
2548 | 0 | ENC_BIG_ENDIAN, &is_resegmented); |
2549 | 0 | tap_info->isResegmented = is_resegmented; |
2550 | |
|
2551 | 0 | write_pdu_label_and_info_literal(top_ti, NULL, pinfo, |
2552 | 0 | (is_resegmented) ? " [DATA-SEGMENT]" : " [DATA]"); |
2553 | | |
2554 | | /* Polling bit */ |
2555 | 0 | proto_tree_add_item_ret_uint(am_header_tree, hf_rlc_lte_am_p, tvb, offset, 1, |
2556 | 0 | ENC_BIG_ENDIAN, &polling); |
2557 | |
|
2558 | 0 | write_pdu_label_and_info_literal(top_ti, NULL, pinfo, (polling) ? " (P) " : " "); |
2559 | 0 | if (polling) { |
2560 | 0 | proto_item_append_text(am_header_ti, " (P) "); |
2561 | 0 | } |
2562 | | |
2563 | | /* Framing Info */ |
2564 | 0 | proto_tree_add_item_ret_uint(am_header_tree, hf_rlc_lte_am_fi, tvb, offset, 1, |
2565 | 0 | ENC_BIG_ENDIAN, &framing_info); |
2566 | | |
2567 | | /* Extension bit */ |
2568 | 0 | proto_tree_add_item_ret_uint(am_header_tree, hf_rlc_lte_am_fixed_e, tvb, offset, 1, |
2569 | 0 | ENC_BIG_ENDIAN, &fixed_extension); |
2570 | | |
2571 | | /* Sequence Number */ |
2572 | 0 | if (p_rlc_lte_info->sequenceNumberLength == AM_SN_LENGTH_16_BITS) { |
2573 | 0 | uint32_t reserved; |
2574 | |
|
2575 | 0 | if (is_resegmented) { |
2576 | | /* Last Segment Field (LSF) */ |
2577 | 0 | proto_tree_add_item(am_header_tree, hf_rlc_lte_am_segment_lsf16, tvb, offset, 1, ENC_BIG_ENDIAN); |
2578 | | /* Reserved (R1) */ |
2579 | 0 | am_ti = proto_tree_add_item_ret_uint(am_header_tree, hf_rlc_lte_am_fixed_reserved2, tvb, offset, 1, ENC_BIG_ENDIAN, &reserved); |
2580 | 0 | } else { |
2581 | | /* Reserved (R1) */ |
2582 | 0 | am_ti = proto_tree_add_item_ret_uint(am_header_tree, hf_rlc_lte_am_fixed_reserved, tvb, offset, 1, ENC_BIG_ENDIAN, &reserved); |
2583 | 0 | } |
2584 | 0 | if (reserved != 0) { |
2585 | 0 | expert_add_info_format(pinfo, am_ti, &ei_rlc_lte_reserved_bits_not_zero, |
2586 | 0 | "RLC AM Fixed header Reserved bits not zero (found 0x02%x)", reserved); |
2587 | 0 | } |
2588 | 0 | offset += 1; |
2589 | 0 | proto_tree_add_item_ret_uint(am_header_tree, hf_rlc_lte_am_fixed_sn16, tvb, offset, 2, ENC_BIG_ENDIAN, &sn); |
2590 | 0 | offset += 2; |
2591 | 0 | } else { |
2592 | 0 | proto_tree_add_item_ret_uint(am_header_tree, hf_rlc_lte_am_fixed_sn, tvb, offset, 2, ENC_BIG_ENDIAN, &sn); |
2593 | 0 | offset += 2; |
2594 | 0 | } |
2595 | 0 | tap_info->sequenceNumberGiven = true; |
2596 | 0 | tap_info->sequenceNumber = sn; |
2597 | |
|
2598 | 0 | write_pdu_label_and_info(top_ti, am_header_ti, pinfo, "sn=%-4u", sn); |
2599 | | |
2600 | | /***************************************/ |
2601 | | /* Dissect extra segment header fields */ |
2602 | 0 | if (is_resegmented) { |
2603 | 0 | uint32_t segmentOffset; |
2604 | |
|
2605 | 0 | if (p_rlc_lte_info->sequenceNumberLength == AM_SN_LENGTH_16_BITS) { |
2606 | | /* SO */ |
2607 | 0 | proto_tree_add_item_ret_uint(am_header_tree, hf_rlc_lte_am_segment_so16, tvb, offset, 2, ENC_BIG_ENDIAN, &segmentOffset); |
2608 | 0 | } else { |
2609 | | /* Last Segment Field (LSF) */ |
2610 | 0 | proto_tree_add_item(am_header_tree, hf_rlc_lte_am_segment_lsf, tvb, offset, 1, ENC_BIG_ENDIAN); |
2611 | | |
2612 | | /* SO */ |
2613 | 0 | proto_tree_add_item_ret_uint(am_header_tree, hf_rlc_lte_am_segment_so, tvb, offset, 2, ENC_BIG_ENDIAN, &segmentOffset); |
2614 | 0 | } |
2615 | 0 | write_pdu_label_and_info(top_ti, am_header_ti, pinfo, " SO=%u ", segmentOffset); |
2616 | 0 | offset += 2; |
2617 | 0 | } |
2618 | | |
2619 | | /*************************************/ |
2620 | | /* AM header extension */ |
2621 | 0 | if (fixed_extension) { |
2622 | 0 | if (!PINFO_FD_VISITED(pinfo)) { |
2623 | 0 | id = (p_rlc_lte_info->channelId << 16) | p_rlc_lte_info->ueid; |
2624 | 0 | key[0].length = 1; |
2625 | 0 | key[0].key = &id; |
2626 | 0 | key[1].length = 1; |
2627 | 0 | key[1].key = &pinfo->num; |
2628 | 0 | key[2].length = 0; |
2629 | 0 | key[2].key = NULL; |
2630 | 0 | params = (rlc_ue_parameters *)wmem_tree_lookup32_array_le(ue_parameters_tree, key); |
2631 | 0 | if (params && (params->id == id)) { |
2632 | 0 | p_rlc_lte_info->extendedLiField = (p_rlc_lte_info->direction == DIRECTION_UPLINK) ? |
2633 | 0 | (params->ext_li_field & UL_EXT_LI): (params->ext_li_field & DL_EXT_LI); |
2634 | 0 | } |
2635 | 0 | } |
2636 | 0 | offset = dissect_rlc_lte_extension_header(tvb, pinfo, am_header_tree, offset, p_rlc_lte_info); |
2637 | 0 | } |
2638 | | |
2639 | | /* Header is now complete */ |
2640 | 0 | proto_item_set_len(am_header_ti, offset-start_offset); |
2641 | | |
2642 | | /* Show number of extensions in header root */ |
2643 | 0 | if (s_number_of_extensions > 0) { |
2644 | 0 | proto_item_append_text(am_header_ti, " (%u extensions)", s_number_of_extensions); |
2645 | 0 | } |
2646 | | |
2647 | | /* Extract these 2 flags from framing_info */ |
2648 | 0 | first_includes_start = (framing_info & 0x02) == 0; |
2649 | 0 | last_includes_end = (framing_info & 0x01) == 0; |
2650 | | |
2651 | | /* There might not be any data, if only headers (plus control data) were logged */ |
2652 | 0 | if (global_rlc_lte_headers_expected) { |
2653 | 0 | is_truncated = (tvb_captured_length_remaining(tvb, offset) == 0); |
2654 | 0 | truncated_ti = proto_tree_add_uint(tree, hf_rlc_lte_header_only, tvb, 0, 0, |
2655 | 0 | is_truncated); |
2656 | 0 | if (is_truncated) { |
2657 | 0 | int n; |
2658 | 0 | proto_item_set_generated(truncated_ti); |
2659 | 0 | expert_add_info(pinfo, truncated_ti, &ei_rlc_lte_header_only); |
2660 | | /* Show in the info column how long the data would be */ |
2661 | 0 | for (n=0; n < s_number_of_extensions; n++) { |
2662 | 0 | show_PDU_in_info(pinfo, top_ti, s_lengths[n], |
2663 | 0 | (n==0) ? first_includes_start : true, |
2664 | 0 | true); |
2665 | 0 | offset += s_lengths[n]; |
2666 | 0 | } |
2667 | | /* Last one */ |
2668 | 0 | show_PDU_in_info(pinfo, top_ti, p_rlc_lte_info->pduLength - offset, |
2669 | 0 | (s_number_of_extensions == 0) ? first_includes_start : true, |
2670 | 0 | last_includes_end); |
2671 | 0 | } |
2672 | 0 | else { |
2673 | 0 | proto_item_set_hidden(truncated_ti); |
2674 | 0 | } |
2675 | 0 | } |
2676 | | |
2677 | | /* Call sequence analysis function now */ |
2678 | 0 | if (((global_rlc_lte_am_sequence_analysis == SEQUENCE_ANALYSIS_MAC_ONLY) && |
2679 | 0 | (p_get_proto_data(wmem_file_scope(), pinfo, proto_mac_lte, 0) != NULL)) || |
2680 | 0 | ((global_rlc_lte_am_sequence_analysis == SEQUENCE_ANALYSIS_RLC_ONLY) && |
2681 | 0 | (p_get_proto_data(wmem_file_scope(), pinfo, proto_mac_lte, 0) == NULL))) { |
2682 | |
|
2683 | 0 | uint16_t firstSegmentLength; |
2684 | 0 | uint16_t lastSegmentOffset = offset; |
2685 | 0 | if (s_number_of_extensions >= 1) { |
2686 | 0 | int n; |
2687 | 0 | for (n=0; n < s_number_of_extensions; n++) { |
2688 | 0 | lastSegmentOffset += s_lengths[n]; |
2689 | 0 | } |
2690 | |
|
2691 | 0 | firstSegmentLength = s_lengths[0]; |
2692 | 0 | } |
2693 | 0 | else { |
2694 | 0 | firstSegmentLength = tvb_reported_length_remaining(tvb, offset); |
2695 | 0 | } |
2696 | |
|
2697 | 0 | seq_anal_state = checkChannelSequenceInfo(pinfo, tvb, p_rlc_lte_info, false, |
2698 | 0 | s_number_of_extensions+1, |
2699 | 0 | offset, firstSegmentLength, |
2700 | 0 | lastSegmentOffset, |
2701 | 0 | (uint16_t)sn, |
2702 | 0 | first_includes_start, last_includes_end, |
2703 | 0 | is_resegmented, tap_info, tree); |
2704 | 0 | } |
2705 | |
|
2706 | 0 | if (is_truncated) { |
2707 | 0 | return; |
2708 | 0 | } |
2709 | | |
2710 | | /*************************************/ |
2711 | | /* Data */ |
2712 | | |
2713 | 0 | if (!first_includes_start) { |
2714 | 0 | reassembly_info = (rlc_channel_reassembly_info *)wmem_map_lookup(reassembly_report_hash, |
2715 | 0 | get_report_hash_key((uint16_t)sn, |
2716 | 0 | pinfo->num, |
2717 | 0 | p_rlc_lte_info, |
2718 | 0 | false)); |
2719 | 0 | } |
2720 | |
|
2721 | 0 | if (s_number_of_extensions > 0) { |
2722 | | /* Show each data segment separately */ |
2723 | 0 | int n; |
2724 | 0 | for (n=0; n < s_number_of_extensions; n++) { |
2725 | 0 | show_PDU_in_tree(pinfo, tree, tvb, offset, s_lengths[n], p_rlc_lte_info, |
2726 | 0 | (n==0) ? first_includes_start : true, |
2727 | 0 | (n==0) ? reassembly_info : NULL, |
2728 | 0 | seq_anal_state); |
2729 | 0 | show_PDU_in_info(pinfo, top_ti, s_lengths[n], |
2730 | 0 | (n==0) ? first_includes_start : true, |
2731 | 0 | true); |
2732 | | /* Make sure we don't lose the summary of this SDU */ |
2733 | 0 | col_append_str(pinfo->cinfo, COL_INFO, " | "); |
2734 | 0 | col_set_fence(pinfo->cinfo, COL_INFO); |
2735 | |
|
2736 | 0 | tvb_ensure_bytes_exist(tvb, offset, s_lengths[n]); |
2737 | 0 | offset += s_lengths[n]; |
2738 | 0 | } |
2739 | 0 | } |
2740 | | |
2741 | | /* Final data element */ |
2742 | 0 | if (tvb_reported_length_remaining(tvb, offset) > 0) { |
2743 | 0 | show_PDU_in_tree(pinfo, tree, tvb, offset, tvb_reported_length_remaining(tvb, offset), p_rlc_lte_info, |
2744 | 0 | ((s_number_of_extensions == 0) ? first_includes_start : true) && last_includes_end, |
2745 | 0 | (s_number_of_extensions == 0) ? reassembly_info : NULL, |
2746 | 0 | seq_anal_state); |
2747 | 0 | show_PDU_in_info(pinfo, top_ti, (uint16_t)tvb_reported_length_remaining(tvb, offset), |
2748 | 0 | (s_number_of_extensions == 0) ? first_includes_start : true, |
2749 | 0 | last_includes_end); |
2750 | 0 | } |
2751 | 0 | else { |
2752 | | /* Report that expected data was missing (unless we know it might happen) */ |
2753 | 0 | if (!global_rlc_lte_headers_expected) { |
2754 | 0 | if (s_number_of_extensions > 0) { |
2755 | 0 | expert_add_info(pinfo, am_header_ti, &ei_rlc_lte_am_data_no_data_beyond_extensions); |
2756 | 0 | } |
2757 | 0 | else { |
2758 | 0 | expert_add_info(pinfo, am_header_ti, &ei_rlc_lte_am_data_no_data); |
2759 | 0 | } |
2760 | 0 | } |
2761 | 0 | } |
2762 | 0 | } |
2763 | | |
2764 | | static void report_heur_error(proto_tree *tree, packet_info *pinfo, expert_field *eiindex, |
2765 | | tvbuff_t *tvb, int start, int length) |
2766 | 0 | { |
2767 | 0 | proto_item *ti; |
2768 | 0 | proto_tree *subtree; |
2769 | |
|
2770 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "RLC-LTE"); |
2771 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
2772 | 0 | ti = proto_tree_add_item(tree, proto_rlc_lte, tvb, 0, -1, ENC_NA); |
2773 | 0 | subtree = proto_item_add_subtree(ti, ett_rlc_lte); |
2774 | 0 | proto_tree_add_expert(subtree, pinfo, eiindex, tvb, start, length); |
2775 | 0 | } |
2776 | | |
2777 | | /* Heuristic dissector looks for supported framing protocol (see wiki page) */ |
2778 | | static bool dissect_rlc_lte_heur(tvbuff_t *tvb, packet_info *pinfo, |
2779 | | proto_tree *tree, void *data _U_) |
2780 | 0 | { |
2781 | 0 | int offset = 0; |
2782 | 0 | struct rlc_lte_info *p_rlc_lte_info; |
2783 | 0 | tvbuff_t *rlc_tvb; |
2784 | 0 | uint8_t tag = 0; |
2785 | 0 | bool seqNumLengthTagPresent = false; |
2786 | | |
2787 | | /* Needs to be at least as long as: |
2788 | | - the signature string |
2789 | | - fixed header bytes |
2790 | | - tag for data |
2791 | | - at least one byte of RLC PDU payload */ |
2792 | 0 | if (tvb_captured_length_remaining(tvb, offset) < (int)(strlen(RLC_LTE_START_STRING)+1+2)) { |
2793 | 0 | return false; |
2794 | 0 | } |
2795 | | |
2796 | | /* OK, compare with signature string */ |
2797 | 0 | if (tvb_strneql(tvb, offset, RLC_LTE_START_STRING, (int)strlen(RLC_LTE_START_STRING)) != 0) { |
2798 | 0 | return false; |
2799 | 0 | } |
2800 | 0 | offset += (int)strlen(RLC_LTE_START_STRING); |
2801 | | |
2802 | | |
2803 | | /* If redissecting, use previous info struct (if available) */ |
2804 | 0 | p_rlc_lte_info = (rlc_lte_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_rlc_lte, 0); |
2805 | 0 | if (p_rlc_lte_info == NULL) { |
2806 | | /* Allocate new info struct for this frame */ |
2807 | 0 | p_rlc_lte_info = wmem_new0(wmem_file_scope(), struct rlc_lte_info); |
2808 | | |
2809 | | /* Read fixed fields */ |
2810 | 0 | p_rlc_lte_info->rlcMode = tvb_get_uint8(tvb, offset++); |
2811 | 0 | if (p_rlc_lte_info->rlcMode == RLC_AM_MODE) { |
2812 | 0 | p_rlc_lte_info->sequenceNumberLength = AM_SN_LENGTH_10_BITS; |
2813 | 0 | } |
2814 | | |
2815 | | /* Read optional fields */ |
2816 | 0 | while (tag != RLC_LTE_PAYLOAD_TAG) { |
2817 | | /* Process next tag */ |
2818 | 0 | tag = tvb_get_uint8(tvb, offset++); |
2819 | 0 | switch (tag) { |
2820 | 0 | case RLC_LTE_SN_LENGTH_TAG: |
2821 | 0 | p_rlc_lte_info->sequenceNumberLength = tvb_get_uint8(tvb, offset); |
2822 | 0 | offset++; |
2823 | 0 | seqNumLengthTagPresent = true; |
2824 | 0 | break; |
2825 | 0 | case RLC_LTE_DIRECTION_TAG: |
2826 | 0 | p_rlc_lte_info->direction = tvb_get_uint8(tvb, offset); |
2827 | 0 | offset++; |
2828 | 0 | break; |
2829 | 0 | case RLC_LTE_PRIORITY_TAG: |
2830 | 0 | p_rlc_lte_info->priority = tvb_get_uint8(tvb, offset); |
2831 | 0 | offset++; |
2832 | 0 | break; |
2833 | 0 | case RLC_LTE_UEID_TAG: |
2834 | 0 | p_rlc_lte_info->ueid = tvb_get_ntohs(tvb, offset); |
2835 | 0 | offset += 2; |
2836 | 0 | break; |
2837 | 0 | case RLC_LTE_CHANNEL_TYPE_TAG: |
2838 | 0 | p_rlc_lte_info->channelType = tvb_get_ntohs(tvb, offset); |
2839 | 0 | offset += 2; |
2840 | 0 | break; |
2841 | 0 | case RLC_LTE_CHANNEL_ID_TAG: |
2842 | 0 | p_rlc_lte_info->channelId = tvb_get_ntohs(tvb, offset); |
2843 | 0 | offset += 2; |
2844 | 0 | break; |
2845 | 0 | case RLC_LTE_EXT_LI_FIELD_TAG: |
2846 | 0 | p_rlc_lte_info->extendedLiField = true; |
2847 | 0 | break; |
2848 | 0 | case RLC_LTE_NB_MODE_TAG: |
2849 | 0 | p_rlc_lte_info->nbMode = |
2850 | 0 | (rlc_lte_nb_mode)tvb_get_uint8(tvb, offset); |
2851 | 0 | offset++; |
2852 | 0 | break; |
2853 | | |
2854 | 0 | case RLC_LTE_PAYLOAD_TAG: |
2855 | | /* Have reached data, so set payload length and get out of loop */ |
2856 | 0 | p_rlc_lte_info->pduLength = tvb_reported_length_remaining(tvb, offset); |
2857 | 0 | continue; |
2858 | | |
2859 | 0 | default: |
2860 | | /* It must be a recognised tag */ |
2861 | 0 | report_heur_error(tree, pinfo, &ei_rlc_lte_unknown_udp_framing_tag, tvb, offset-1, 1); |
2862 | 0 | wmem_free(wmem_file_scope(), p_rlc_lte_info); |
2863 | 0 | return true; |
2864 | 0 | } |
2865 | 0 | } |
2866 | | |
2867 | 0 | if ((p_rlc_lte_info->rlcMode == RLC_UM_MODE) && (seqNumLengthTagPresent == false)) { |
2868 | | /* Conditional field is not present */ |
2869 | 0 | report_heur_error(tree, pinfo, &ei_rlc_lte_missing_udp_framing_tag, tvb, 0, offset); |
2870 | 0 | wmem_free(wmem_file_scope(), p_rlc_lte_info); |
2871 | 0 | return true; |
2872 | 0 | } |
2873 | | |
2874 | | /* Store info in packet */ |
2875 | 0 | p_add_proto_data(wmem_file_scope(), pinfo, proto_rlc_lte, 0, p_rlc_lte_info); |
2876 | 0 | } |
2877 | 0 | else { |
2878 | 0 | offset = tvb_reported_length(tvb) - p_rlc_lte_info->pduLength; |
2879 | 0 | } |
2880 | | |
2881 | | /**************************************/ |
2882 | | /* OK, now dissect as RLC LTE */ |
2883 | | |
2884 | | /* Create tvb that starts at actual RLC PDU */ |
2885 | 0 | rlc_tvb = tvb_new_subset_remaining(tvb, offset); |
2886 | 0 | dissect_rlc_lte_common(rlc_tvb, pinfo, tree, true); |
2887 | 0 | return true; |
2888 | 0 | } |
2889 | | |
2890 | | |
2891 | | |
2892 | | /*****************************/ |
2893 | | /* Main dissection function. */ |
2894 | | /*****************************/ |
2895 | | |
2896 | | static int dissect_rlc_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
2897 | 0 | { |
2898 | 0 | dissect_rlc_lte_common(tvb, pinfo, tree, false); |
2899 | 0 | return tvb_captured_length(tvb); |
2900 | 0 | } |
2901 | | |
2902 | | static void dissect_rlc_lte_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, bool is_udp_framing) |
2903 | 0 | { |
2904 | 0 | proto_tree *rlc_lte_tree; |
2905 | 0 | proto_tree *context_tree; |
2906 | 0 | proto_item *top_ti; |
2907 | 0 | proto_item *context_ti; |
2908 | 0 | proto_item *ti; |
2909 | 0 | proto_item *mode_ti; |
2910 | 0 | int offset = 0; |
2911 | 0 | struct rlc_lte_info *p_rlc_lte_info; |
2912 | | |
2913 | | /* Allocate and Zero tap struct */ |
2914 | 0 | rlc_3gpp_tap_info *tap_info = wmem_new0(pinfo->pool, rlc_3gpp_tap_info); |
2915 | 0 | tap_info->rat = RLC_RAT_LTE; |
2916 | | |
2917 | | /* Set protocol name */ |
2918 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "RLC-LTE"); |
2919 | | |
2920 | | /* Create protocol tree. */ |
2921 | 0 | top_ti = proto_tree_add_item(tree, proto_rlc_lte, tvb, offset, -1, ENC_NA); |
2922 | 0 | rlc_lte_tree = proto_item_add_subtree(top_ti, ett_rlc_lte); |
2923 | | |
2924 | | |
2925 | | /* Look for packet info! */ |
2926 | 0 | p_rlc_lte_info = (rlc_lte_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_rlc_lte, 0); |
2927 | | |
2928 | | /* Can't dissect anything without it... */ |
2929 | 0 | if (p_rlc_lte_info == NULL) { |
2930 | 0 | proto_tree_add_expert_remaining(rlc_lte_tree, pinfo, &ei_rlc_lte_no_per_frame_info, tvb, offset); |
2931 | 0 | return; |
2932 | 0 | } |
2933 | | |
2934 | | /* Clear info column when using UDP framing */ |
2935 | 0 | if (is_udp_framing) { |
2936 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
2937 | 0 | } |
2938 | | |
2939 | | /*****************************************/ |
2940 | | /* Show context information */ |
2941 | | |
2942 | | /* Create context root */ |
2943 | 0 | context_ti = proto_tree_add_string_format(rlc_lte_tree, hf_rlc_lte_context, |
2944 | 0 | tvb, offset, 0, "", "Context"); |
2945 | 0 | context_tree = proto_item_add_subtree(context_ti, ett_rlc_lte_context); |
2946 | 0 | proto_item_set_generated(context_ti); |
2947 | |
|
2948 | 0 | ti = proto_tree_add_uint(context_tree, hf_rlc_lte_context_direction, |
2949 | 0 | tvb, 0, 0, p_rlc_lte_info->direction); |
2950 | 0 | proto_item_set_generated(ti); |
2951 | |
|
2952 | 0 | mode_ti = proto_tree_add_uint(context_tree, hf_rlc_lte_context_mode, |
2953 | 0 | tvb, 0, 0, p_rlc_lte_info->rlcMode); |
2954 | 0 | proto_item_set_generated(mode_ti); |
2955 | |
|
2956 | 0 | if (p_rlc_lte_info->ueid != 0) { |
2957 | 0 | ti = proto_tree_add_uint(context_tree, hf_rlc_lte_context_ueid, |
2958 | 0 | tvb, 0, 0, p_rlc_lte_info->ueid); |
2959 | 0 | proto_item_set_generated(ti); |
2960 | 0 | } |
2961 | |
|
2962 | 0 | if ((p_rlc_lte_info->priority >= 1) && (p_rlc_lte_info->priority <=16)) { |
2963 | 0 | ti = proto_tree_add_uint(context_tree, hf_rlc_lte_context_priority, |
2964 | 0 | tvb, 0, 0, p_rlc_lte_info->priority); |
2965 | 0 | proto_item_set_generated(ti); |
2966 | 0 | } |
2967 | |
|
2968 | 0 | ti = proto_tree_add_uint(context_tree, hf_rlc_lte_context_channel_type, |
2969 | 0 | tvb, 0, 0, p_rlc_lte_info->channelType); |
2970 | 0 | proto_item_set_generated(ti); |
2971 | |
|
2972 | 0 | if ((p_rlc_lte_info->channelType == CHANNEL_TYPE_SRB) || |
2973 | 0 | (p_rlc_lte_info->channelType == CHANNEL_TYPE_DRB) || |
2974 | 0 | (p_rlc_lte_info->channelType == CHANNEL_TYPE_MTCH)) { |
2975 | 0 | ti = proto_tree_add_uint(context_tree, hf_rlc_lte_context_channel_id, |
2976 | 0 | tvb, 0, 0, p_rlc_lte_info->channelId); |
2977 | 0 | proto_item_set_generated(ti); |
2978 | 0 | } |
2979 | |
|
2980 | 0 | ti = proto_tree_add_uint(context_tree, hf_rlc_lte_context_pdu_length, |
2981 | 0 | tvb, 0, 0, p_rlc_lte_info->pduLength); |
2982 | 0 | proto_item_set_generated(ti); |
2983 | |
|
2984 | 0 | if (p_rlc_lte_info->rlcMode == RLC_UM_MODE) { |
2985 | 0 | ti = proto_tree_add_uint(context_tree, hf_rlc_lte_context_um_sn_length, |
2986 | 0 | tvb, 0, 0, p_rlc_lte_info->sequenceNumberLength); |
2987 | 0 | proto_item_set_generated(ti); |
2988 | 0 | } |
2989 | |
|
2990 | 0 | if (p_rlc_lte_info->rlcMode == RLC_AM_MODE) { |
2991 | 0 | if (!p_rlc_lte_info->sequenceNumberLength) { |
2992 | 0 | p_rlc_lte_info->sequenceNumberLength = 10; |
2993 | 0 | } |
2994 | 0 | ti = proto_tree_add_uint(context_tree, hf_rlc_lte_context_am_sn_length, |
2995 | 0 | tvb, 0, 0, p_rlc_lte_info->sequenceNumberLength); |
2996 | 0 | proto_item_set_generated(ti); |
2997 | 0 | } |
2998 | | |
2999 | | /* Append highlights to top-level item */ |
3000 | 0 | if (p_rlc_lte_info->ueid != 0) { |
3001 | 0 | proto_item_append_text(top_ti, " UEId=%u", p_rlc_lte_info->ueid); |
3002 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "UEId=%-4u ", p_rlc_lte_info->ueid); |
3003 | 0 | } |
3004 | | |
3005 | | /* Append context highlights to info column */ |
3006 | 0 | write_pdu_label_and_info(top_ti, NULL, pinfo, |
3007 | 0 | " [%s] [%s] ", |
3008 | 0 | (p_rlc_lte_info->direction == 0) ? "UL" : "DL", |
3009 | 0 | val_to_str_const(p_rlc_lte_info->rlcMode, rlc_mode_short_vals, "Unknown")); |
3010 | |
|
3011 | 0 | if (p_rlc_lte_info->channelId == 0) { |
3012 | 0 | write_pdu_label_and_info(top_ti, NULL, pinfo, "%s ", |
3013 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown")); |
3014 | 0 | } |
3015 | 0 | else { |
3016 | 0 | write_pdu_label_and_info(top_ti, NULL, pinfo, "%s:%-2u", |
3017 | 0 | val_to_str_const(p_rlc_lte_info->channelType, rlc_channel_type_vals, "Unknown"), |
3018 | 0 | p_rlc_lte_info->channelId); |
3019 | 0 | } |
3020 | | |
3021 | | /* Set context-info parts of tap struct */ |
3022 | 0 | tap_info->rlcMode = p_rlc_lte_info->rlcMode; |
3023 | 0 | tap_info->direction = p_rlc_lte_info->direction; |
3024 | 0 | tap_info->priority = p_rlc_lte_info->priority; |
3025 | 0 | tap_info->ueid = p_rlc_lte_info->ueid; |
3026 | 0 | tap_info->channelType = p_rlc_lte_info->channelType; |
3027 | 0 | tap_info->channelId = p_rlc_lte_info->channelId; |
3028 | 0 | tap_info->pduLength = p_rlc_lte_info->pduLength; |
3029 | 0 | tap_info->sequenceNumberLength = p_rlc_lte_info->sequenceNumberLength; |
3030 | 0 | tap_info->loggedInMACFrame = (p_get_proto_data(wmem_file_scope(), pinfo, proto_mac_lte, 0) != NULL); |
3031 | |
|
3032 | 0 | tap_info->rlc_time = pinfo->abs_ts; |
3033 | | |
3034 | | /* Reset this count */ |
3035 | 0 | s_number_of_extensions = 0; |
3036 | | |
3037 | | /* Dissect the RLC PDU itself. Format depends upon mode... */ |
3038 | 0 | switch (p_rlc_lte_info->rlcMode) { |
3039 | | |
3040 | 0 | case RLC_TM_MODE: |
3041 | 0 | dissect_rlc_lte_tm(tvb, pinfo, rlc_lte_tree, offset, p_rlc_lte_info, top_ti); |
3042 | 0 | break; |
3043 | | |
3044 | 0 | case RLC_UM_MODE: |
3045 | 0 | dissect_rlc_lte_um(tvb, pinfo, rlc_lte_tree, offset, p_rlc_lte_info, top_ti, |
3046 | 0 | tap_info); |
3047 | 0 | break; |
3048 | | |
3049 | 0 | case RLC_AM_MODE: |
3050 | 0 | dissect_rlc_lte_am(tvb, pinfo, rlc_lte_tree, offset, p_rlc_lte_info, top_ti, |
3051 | 0 | tap_info); |
3052 | 0 | break; |
3053 | | |
3054 | 0 | case RLC_PREDEF: |
3055 | | /* Predefined data (i.e. not containing a valid RLC header */ |
3056 | 0 | proto_tree_add_item(rlc_lte_tree, hf_rlc_lte_predefined_pdu, tvb, offset, -1, ENC_NA); |
3057 | 0 | write_pdu_label_and_info(top_ti, NULL, pinfo, " [%u-bytes]", |
3058 | 0 | tvb_reported_length_remaining(tvb, offset)); |
3059 | 0 | break; |
3060 | | |
3061 | 0 | default: |
3062 | | /* Error - unrecognised mode */ |
3063 | 0 | expert_add_info_format(pinfo, mode_ti, &ei_rlc_lte_context_mode, |
3064 | 0 | "Unrecognised RLC Mode set (%u)", p_rlc_lte_info->rlcMode); |
3065 | 0 | break; |
3066 | 0 | } |
3067 | | |
3068 | | /* Queue tap info */ |
3069 | 0 | tap_queue_packet(rlc_lte_tap, pinfo, tap_info); |
3070 | 0 | } |
3071 | | |
3072 | | |
3073 | | |
3074 | | /* Configure number of PDCP SN bits to use for DRB channels */ |
3075 | | void set_rlc_lte_drb_pdcp_seqnum_length(packet_info *pinfo, uint16_t ueid, uint8_t drbid, |
3076 | | uint8_t userplane_seqnum_length) |
3077 | 0 | { |
3078 | 0 | wmem_tree_key_t key[3]; |
3079 | 0 | uint32_t id; |
3080 | 0 | rlc_ue_parameters *params; |
3081 | |
|
3082 | 0 | if (PINFO_FD_VISITED(pinfo)) { |
3083 | 0 | return; |
3084 | 0 | } |
3085 | | |
3086 | 0 | id = (drbid << 16) | ueid; |
3087 | 0 | key[0].length = 1; |
3088 | 0 | key[0].key = &id; |
3089 | 0 | key[1].length = 1; |
3090 | 0 | key[1].key = &pinfo->num; |
3091 | 0 | key[2].length = 0; |
3092 | 0 | key[2].key = NULL; |
3093 | |
|
3094 | 0 | params = (rlc_ue_parameters *)wmem_tree_lookup32_array_le(ue_parameters_tree, key); |
3095 | 0 | if (params && (params->id != id)) { |
3096 | 0 | params = NULL; |
3097 | 0 | } |
3098 | 0 | if (params == NULL) { |
3099 | 0 | params = (rlc_ue_parameters *)wmem_new(wmem_file_scope(), rlc_ue_parameters); |
3100 | 0 | params->id = id; |
3101 | 0 | params->ext_li_field = NO_EXT_LI; |
3102 | 0 | wmem_tree_insert32_array(ue_parameters_tree, key, (void *)params); |
3103 | 0 | } |
3104 | 0 | params->pdcp_sn_bits = userplane_seqnum_length; |
3105 | 0 | } |
3106 | | |
3107 | | /*Configure LI field for AM DRB channels */ |
3108 | | void set_rlc_lte_drb_li_field(packet_info *pinfo, uint16_t ueid, uint8_t drbid, |
3109 | | bool ul_ext_li_field, bool dl_ext_li_field) |
3110 | 0 | { |
3111 | 0 | wmem_tree_key_t key[3]; |
3112 | 0 | uint32_t id; |
3113 | 0 | rlc_ue_parameters *params; |
3114 | |
|
3115 | 0 | if (PINFO_FD_VISITED(pinfo)) { |
3116 | 0 | return; |
3117 | 0 | } |
3118 | | |
3119 | 0 | id = (drbid << 16) | ueid; |
3120 | 0 | key[0].length = 1; |
3121 | 0 | key[0].key = &id; |
3122 | 0 | key[1].length = 1; |
3123 | 0 | key[1].key = &pinfo->num; |
3124 | 0 | key[2].length = 0; |
3125 | 0 | key[2].key = NULL; |
3126 | |
|
3127 | 0 | params = (rlc_ue_parameters *)wmem_tree_lookup32_array_le(ue_parameters_tree, key); |
3128 | 0 | if (params && (params->id != id)) { |
3129 | 0 | params = NULL; |
3130 | 0 | } |
3131 | 0 | if (params == NULL) { |
3132 | 0 | params = (rlc_ue_parameters *)wmem_new(wmem_file_scope(), rlc_ue_parameters); |
3133 | 0 | params->id = id; |
3134 | 0 | params->pdcp_sn_bits = 12; |
3135 | 0 | wmem_tree_insert32_array(ue_parameters_tree, key, (void *)params); |
3136 | 0 | } |
3137 | 0 | params->ext_li_field = ul_ext_li_field ? UL_EXT_LI : NO_EXT_LI; |
3138 | 0 | params->ext_li_field |= dl_ext_li_field ? DL_EXT_LI : NO_EXT_LI; |
3139 | 0 | } |
3140 | | |
3141 | | void rlc_lte_reset_ue_bearers(packet_info *pinfo, uint16_t ueid) |
3142 | 0 | { |
3143 | 0 | if (PINFO_FD_VISITED(pinfo)) { |
3144 | 0 | return; |
3145 | 0 | } |
3146 | | |
3147 | | /* Need to reset current state of any entries in sequence_analysis_channel_hash */ |
3148 | 0 | channel_hash_key channel_key; |
3149 | 0 | channel_sequence_analysis_status *p_channel_status; |
3150 | |
|
3151 | 0 | channel_key.ueId = ueid; |
3152 | | |
3153 | | /* SRBs (1-2, both directions) */ |
3154 | 0 | channel_key.channelType = CHANNEL_TYPE_SRB; |
3155 | 0 | for (uint32_t channelId=1; channelId <= 2; ++channelId) { |
3156 | 0 | for (uint32_t direction=0; direction <=1; ++direction) { |
3157 | | /* Update key */ |
3158 | 0 | channel_key.channelId = channelId; |
3159 | 0 | channel_key.direction = direction; |
3160 | | |
3161 | | /* Lookup existing channel status */ |
3162 | 0 | p_channel_status = (channel_sequence_analysis_status*)wmem_map_lookup(sequence_analysis_channel_hash, &channel_key); |
3163 | 0 | if (p_channel_status) { |
3164 | | /* Reset if already exists */ |
3165 | 0 | p_channel_status->previousSequenceNumber = -1; |
3166 | 0 | p_channel_status->previousFrameNum = 0; |
3167 | 0 | p_channel_status->previousSegmentIncomplete = 0; |
3168 | 0 | } |
3169 | 0 | } |
3170 | 0 | } |
3171 | | |
3172 | | /* DRBs (1-32, both directions) */ |
3173 | 0 | channel_key.channelType = CHANNEL_TYPE_DRB; |
3174 | 0 | for (uint32_t channelId=1; channelId <= 32; ++channelId) { |
3175 | 0 | for (uint32_t direction=0; direction <=1; ++direction) { |
3176 | | /* Update key */ |
3177 | 0 | channel_key.channelId = channelId; |
3178 | 0 | channel_key.direction = direction; |
3179 | | |
3180 | | /* Lookup existing channel status */ |
3181 | 0 | p_channel_status = (channel_sequence_analysis_status*)wmem_map_lookup(sequence_analysis_channel_hash, &channel_key); |
3182 | 0 | if (p_channel_status) { |
3183 | | /* Reset if already exists */ |
3184 | 0 | p_channel_status->previousSequenceNumber = -1; |
3185 | 0 | p_channel_status->previousFrameNum = 0; |
3186 | 0 | p_channel_status->previousSegmentIncomplete = 0; |
3187 | 0 | } |
3188 | 0 | } |
3189 | 0 | } |
3190 | 0 | } |
3191 | | |
3192 | | |
3193 | | |
3194 | | void proto_register_rlc_lte(void) |
3195 | 14 | { |
3196 | 14 | static hf_register_info hf[] = |
3197 | 14 | { |
3198 | | /**********************************/ |
3199 | | /* Items for decoding context */ |
3200 | 14 | { &hf_rlc_lte_context, |
3201 | 14 | { "Context", |
3202 | 14 | "rlc-lte.context", FT_STRING, BASE_NONE, NULL, 0x0, |
3203 | 14 | NULL, HFILL |
3204 | 14 | } |
3205 | 14 | }, |
3206 | 14 | { &hf_rlc_lte_context_mode, |
3207 | 14 | { "RLC Mode", |
3208 | 14 | "rlc-lte.mode", FT_UINT8, BASE_DEC, VALS(rlc_mode_vals), 0x0, |
3209 | 14 | NULL, HFILL |
3210 | 14 | } |
3211 | 14 | }, |
3212 | 14 | { &hf_rlc_lte_context_direction, |
3213 | 14 | { "Direction", |
3214 | 14 | "rlc-lte.direction", FT_UINT8, BASE_DEC, VALS(direction_vals), 0x0, |
3215 | 14 | "Direction of message", HFILL |
3216 | 14 | } |
3217 | 14 | }, |
3218 | 14 | { &hf_rlc_lte_context_priority, |
3219 | 14 | { "Priority", |
3220 | 14 | "rlc-lte.priority", FT_UINT8, BASE_DEC, 0, 0x0, |
3221 | 14 | NULL, HFILL |
3222 | 14 | } |
3223 | 14 | }, |
3224 | 14 | { &hf_rlc_lte_context_ueid, |
3225 | 14 | { "UEId", |
3226 | 14 | "rlc-lte.ueid", FT_UINT16, BASE_DEC, 0, 0x0, |
3227 | 14 | "User Equipment Identifier associated with message", HFILL |
3228 | 14 | } |
3229 | 14 | }, |
3230 | 14 | { &hf_rlc_lte_context_channel_type, |
3231 | 14 | { "Channel Type", |
3232 | 14 | "rlc-lte.channel-type", FT_UINT16, BASE_DEC, VALS(rlc_channel_type_vals), 0x0, |
3233 | 14 | "Channel Type associated with message", HFILL |
3234 | 14 | } |
3235 | 14 | }, |
3236 | 14 | { &hf_rlc_lte_context_channel_id, |
3237 | 14 | { "Channel ID", |
3238 | 14 | "rlc-lte.channel-id", FT_UINT16, BASE_DEC, 0, 0x0, |
3239 | 14 | "Channel ID associated with message", HFILL |
3240 | 14 | } |
3241 | 14 | }, |
3242 | 14 | { &hf_rlc_lte_context_pdu_length, |
3243 | 14 | { "PDU Length", |
3244 | 14 | "rlc-lte.pdu-length", FT_UINT16, BASE_DEC, 0, 0x0, |
3245 | 14 | "Length of PDU (in bytes)", HFILL |
3246 | 14 | } |
3247 | 14 | }, |
3248 | 14 | { &hf_rlc_lte_context_um_sn_length, |
3249 | 14 | { "UM Sequence number length", |
3250 | 14 | "rlc-lte.um-seqnum-length", FT_UINT8, BASE_DEC, 0, 0x0, |
3251 | 14 | "Length of UM sequence number in bits", HFILL |
3252 | 14 | } |
3253 | 14 | }, |
3254 | 14 | { &hf_rlc_lte_context_am_sn_length, |
3255 | 14 | { "AM Sequence number length", |
3256 | 14 | "rlc-lte.am-seqnum-length", FT_UINT8, BASE_DEC, 0, 0x0, |
3257 | 14 | "Length of AM sequence number in bits", HFILL |
3258 | 14 | } |
3259 | 14 | }, |
3260 | | |
3261 | | /* Transparent mode fields */ |
3262 | 14 | { &hf_rlc_lte_tm, |
3263 | 14 | { "TM", |
3264 | 14 | "rlc-lte.tm", FT_STRING, BASE_NONE, NULL, 0x0, |
3265 | 14 | "Transparent Mode", HFILL |
3266 | 14 | } |
3267 | 14 | }, |
3268 | 14 | { &hf_rlc_lte_tm_data, |
3269 | 14 | { "TM Data", |
3270 | 14 | "rlc-lte.tm.data", FT_BYTES, BASE_NONE, 0, 0x0, |
3271 | 14 | "Transparent Mode Data", HFILL |
3272 | 14 | } |
3273 | 14 | }, |
3274 | | |
3275 | | /* Unacknowledged mode fields */ |
3276 | 14 | { &hf_rlc_lte_um, |
3277 | 14 | { "UM", |
3278 | 14 | "rlc-lte.um", FT_STRING, BASE_NONE, NULL, 0x0, |
3279 | 14 | "Unacknowledged Mode", HFILL |
3280 | 14 | } |
3281 | 14 | }, |
3282 | 14 | { &hf_rlc_lte_um_header, |
3283 | 14 | { "UM Header", |
3284 | 14 | "rlc-lte.um.header", FT_STRING, BASE_NONE, NULL, 0x0, |
3285 | 14 | "Unacknowledged Mode Header", HFILL |
3286 | 14 | } |
3287 | 14 | }, |
3288 | 14 | { &hf_rlc_lte_um_fi, |
3289 | 14 | { "Framing Info", |
3290 | 14 | "rlc-lte.um.fi", FT_UINT8, BASE_HEX, VALS(framing_info_vals), 0x0, |
3291 | 14 | NULL, HFILL |
3292 | 14 | } |
3293 | 14 | }, |
3294 | 14 | { &hf_rlc_lte_um_fixed_e, |
3295 | 14 | { "Extension", |
3296 | 14 | "rlc-lte.um.fixed.e", FT_UINT8, BASE_HEX, VALS(fixed_extension_vals), 0x0, |
3297 | 14 | "Extension in fixed part of UM header", HFILL |
3298 | 14 | } |
3299 | 14 | }, |
3300 | 14 | { &hf_rlc_lte_um_sn, |
3301 | 14 | { "Sequence number", |
3302 | 14 | "rlc-lte.um.sn", FT_UINT8, BASE_DEC, 0, 0x0, |
3303 | 14 | "Unacknowledged Mode Sequence Number", HFILL |
3304 | 14 | } |
3305 | 14 | }, |
3306 | 14 | { &hf_rlc_lte_um_fixed_reserved, |
3307 | 14 | { "Reserved", |
3308 | 14 | "rlc-lte.um.reserved", FT_UINT8, BASE_DEC, 0, 0xe0, |
3309 | 14 | "Unacknowledged Mode Fixed header reserved bits", HFILL |
3310 | 14 | } |
3311 | 14 | }, |
3312 | 14 | { &hf_rlc_lte_um_data, |
3313 | 14 | { "UM Data", |
3314 | 14 | "rlc-lte.um.data", FT_BYTES, BASE_NONE, 0, 0x0, |
3315 | 14 | "Unacknowledged Mode Data", HFILL |
3316 | 14 | } |
3317 | 14 | }, |
3318 | 14 | { &hf_rlc_lte_extension_part, |
3319 | 14 | { "Extension Part", |
3320 | 14 | "rlc-lte.extension-part", FT_STRING, BASE_NONE, 0, 0x0, |
3321 | 14 | NULL, HFILL |
3322 | 14 | } |
3323 | 14 | }, |
3324 | 14 | { &hf_rlc_lte_extension_e, |
3325 | 14 | { "Extension", |
3326 | 14 | "rlc-lte.extension.e", FT_UINT8, BASE_HEX, VALS(extension_extension_vals), 0x0, |
3327 | 14 | "Extension in extended part of the header", HFILL |
3328 | 14 | } |
3329 | 14 | }, |
3330 | 14 | { &hf_rlc_lte_extension_li, |
3331 | 14 | { "Length Indicator", |
3332 | 14 | "rlc-lte.extension.li", FT_UINT16, BASE_DEC, 0, 0x0, |
3333 | 14 | NULL, HFILL |
3334 | 14 | } |
3335 | 14 | }, |
3336 | 14 | { &hf_rlc_lte_extension_padding, |
3337 | 14 | { "Padding", |
3338 | 14 | "rlc-lte.extension.padding", FT_UINT8, BASE_HEX, 0, 0x0f, |
3339 | 14 | "Extension header padding", HFILL |
3340 | 14 | } |
3341 | 14 | }, |
3342 | | |
3343 | | /* Acknowledged mode fields */ |
3344 | 14 | { &hf_rlc_lte_am, |
3345 | 14 | { "AM", |
3346 | 14 | "rlc-lte.am", FT_STRING, BASE_NONE, NULL, 0x0, |
3347 | 14 | "Acknowledged Mode", HFILL |
3348 | 14 | } |
3349 | 14 | }, |
3350 | 14 | { &hf_rlc_lte_am_header, |
3351 | 14 | { "AM Header", |
3352 | 14 | "rlc-lte.am.header", FT_STRING, BASE_NONE, NULL, 0x0, |
3353 | 14 | "Acknowledged Mode Header", HFILL |
3354 | 14 | } |
3355 | 14 | }, |
3356 | 14 | { &hf_rlc_lte_am_data_control, |
3357 | 14 | { "Frame type", |
3358 | 14 | "rlc-lte.am.frame-type", FT_BOOLEAN, 8, TFS(&tfs_data_pdu_control_pdu), 0x80, |
3359 | 14 | "AM Frame Type (Control or Data)", HFILL |
3360 | 14 | } |
3361 | 14 | }, |
3362 | 14 | { &hf_rlc_lte_am_rf, |
3363 | 14 | { "Re-segmentation Flag", |
3364 | 14 | "rlc-lte.am.rf", FT_UINT8, BASE_HEX, VALS(resegmentation_flag_vals), 0x40, |
3365 | 14 | "AM Re-segmentation Flag", HFILL |
3366 | 14 | } |
3367 | 14 | }, |
3368 | 14 | { &hf_rlc_lte_am_p, |
3369 | 14 | { "Polling Bit", |
3370 | 14 | "rlc-lte.am.p", FT_UINT8, BASE_HEX, VALS(polling_bit_vals), 0x20, |
3371 | 14 | NULL, HFILL |
3372 | 14 | } |
3373 | 14 | }, |
3374 | 14 | { &hf_rlc_lte_am_fi, |
3375 | 14 | { "Framing Info", |
3376 | 14 | "rlc-lte.am.fi", FT_UINT8, BASE_HEX, VALS(framing_info_vals), 0x18, |
3377 | 14 | "AM Framing Info", HFILL |
3378 | 14 | } |
3379 | 14 | }, |
3380 | 14 | { &hf_rlc_lte_am_fixed_e, |
3381 | 14 | { "Extension", |
3382 | 14 | "rlc-lte.am.fixed.e", FT_UINT8, BASE_HEX, VALS(fixed_extension_vals), 0x04, |
3383 | 14 | "Fixed Extension Bit", HFILL |
3384 | 14 | } |
3385 | 14 | }, |
3386 | 14 | { &hf_rlc_lte_am_fixed_sn, |
3387 | 14 | { "Sequence Number", |
3388 | 14 | "rlc-lte.am.fixed.sn", FT_UINT16, BASE_DEC, 0, 0x03ff, |
3389 | 14 | "AM Fixed Sequence Number", HFILL |
3390 | 14 | } |
3391 | 14 | }, |
3392 | 14 | { &hf_rlc_lte_am_fixed_reserved, |
3393 | 14 | { "Reserved", |
3394 | 14 | "rlc-lte.am.reserved", FT_UINT8, BASE_DEC, 0, 0x03, |
3395 | 14 | "Acknowledged Mode Fixed header reserved bits", HFILL |
3396 | 14 | } |
3397 | 14 | }, |
3398 | 14 | { &hf_rlc_lte_am_segment_lsf16, |
3399 | 14 | { "Last Segment Flag", |
3400 | 14 | "rlc-lte.am.segment.lsf", FT_UINT8, BASE_HEX, VALS(lsf_vals), 0x02, |
3401 | 14 | NULL, HFILL |
3402 | 14 | } |
3403 | 14 | }, |
3404 | 14 | { &hf_rlc_lte_am_fixed_reserved2, |
3405 | 14 | { "Reserved", |
3406 | 14 | "rlc-lte.am.reserved", FT_UINT8, BASE_DEC, 0, 0x01, |
3407 | 14 | "Acknowledged Mode Fixed header reserved bit", HFILL |
3408 | 14 | } |
3409 | 14 | }, |
3410 | 14 | { &hf_rlc_lte_am_fixed_sn16, |
3411 | 14 | { "Sequence Number", |
3412 | 14 | "rlc-lte.am.fixed.sn", FT_UINT16, BASE_DEC, 0, 0x0, |
3413 | 14 | "AM Fixed Sequence Number", HFILL |
3414 | 14 | } |
3415 | 14 | }, |
3416 | 14 | { &hf_rlc_lte_am_segment_lsf, |
3417 | 14 | { "Last Segment Flag", |
3418 | 14 | "rlc-lte.am.segment.lsf", FT_UINT8, BASE_HEX, VALS(lsf_vals), 0x80, |
3419 | 14 | NULL, HFILL |
3420 | 14 | } |
3421 | 14 | }, |
3422 | 14 | { &hf_rlc_lte_am_segment_so, |
3423 | 14 | { "Segment Offset", |
3424 | 14 | "rlc-lte.am.segment.offset", FT_UINT16, BASE_DEC, 0, 0x7fff, |
3425 | 14 | NULL, HFILL |
3426 | 14 | } |
3427 | 14 | }, |
3428 | 14 | { &hf_rlc_lte_am_segment_so16, |
3429 | 14 | { "Segment Offset", |
3430 | 14 | "rlc-lte.am.segment.offset", FT_UINT16, BASE_DEC, 0, 0x0, |
3431 | 14 | NULL, HFILL |
3432 | 14 | } |
3433 | 14 | }, |
3434 | 14 | { &hf_rlc_lte_am_data, |
3435 | 14 | { "AM Data", |
3436 | 14 | "rlc-lte.am.data", FT_BYTES, BASE_NONE, 0, 0x0, |
3437 | 14 | "Acknowledged Mode Data", HFILL |
3438 | 14 | } |
3439 | 14 | }, |
3440 | | |
3441 | 14 | { &hf_rlc_lte_am_cpt, |
3442 | 14 | { "Control PDU Type", |
3443 | 14 | "rlc-lte.am.cpt", FT_UINT8, BASE_HEX, VALS(control_pdu_type_vals), 0x70, |
3444 | 14 | "AM Control PDU Type", HFILL |
3445 | 14 | } |
3446 | 14 | }, |
3447 | 14 | { &hf_rlc_lte_am_ack_sn, |
3448 | 14 | { "ACK Sequence Number", |
3449 | 14 | "rlc-lte.am.ack-sn", FT_UINT16, BASE_DEC, 0, 0x0, |
3450 | 14 | "Sequence Number we expect to receive next", HFILL |
3451 | 14 | } |
3452 | 14 | }, |
3453 | 14 | { &hf_rlc_lte_am_e1, |
3454 | 14 | { "Extension bit 1", |
3455 | 14 | "rlc-lte.am.e1", FT_UINT8, BASE_HEX, VALS(am_e1_vals), 0x0, |
3456 | 14 | NULL, HFILL |
3457 | 14 | } |
3458 | 14 | }, |
3459 | 14 | { &hf_rlc_lte_am_e2, |
3460 | 14 | { "Extension bit 2", |
3461 | 14 | "rlc-lte.am.e2", FT_UINT8, BASE_HEX, VALS(am_e2_vals), 0x0, |
3462 | 14 | NULL, HFILL |
3463 | 14 | } |
3464 | 14 | }, |
3465 | 14 | { &hf_rlc_lte_am_nacks, |
3466 | 14 | { "Number of NACKs", |
3467 | 14 | "rlc-lte.am.nacks", FT_UINT16, BASE_DEC, 0, 0x0, |
3468 | 14 | "Number of NACKs in this status PDU", HFILL |
3469 | 14 | } |
3470 | 14 | }, |
3471 | 14 | { &hf_rlc_lte_am_nack_sn, |
3472 | 14 | { "NACK Sequence Number", |
3473 | 14 | "rlc-lte.am.nack-sn", FT_UINT16, BASE_DEC, 0, 0x0, |
3474 | 14 | "Negative Acknowledgement Sequence Number", HFILL |
3475 | 14 | } |
3476 | 14 | }, |
3477 | 14 | { &hf_rlc_lte_am_so_start, |
3478 | 14 | { "SO Start", |
3479 | 14 | "rlc-lte.am.so-start", FT_UINT16, BASE_DEC, 0, 0x0, |
3480 | 14 | "Segment Offset Start byte index", HFILL |
3481 | 14 | } |
3482 | 14 | }, |
3483 | 14 | { &hf_rlc_lte_am_so_end, |
3484 | 14 | { "SO End", |
3485 | 14 | "rlc-lte.am.so-end", FT_UINT16, BASE_DEC, 0, 0x0, |
3486 | 14 | "Segment Offset End byte index", HFILL |
3487 | 14 | } |
3488 | 14 | }, |
3489 | | |
3490 | 14 | { &hf_rlc_lte_predefined_pdu, |
3491 | 14 | { "Predefined data", |
3492 | 14 | "rlc-lte.predefined-data", FT_BYTES, BASE_NONE, 0, 0x0, |
3493 | 14 | "Predefined test data", HFILL |
3494 | 14 | } |
3495 | 14 | }, |
3496 | | |
3497 | | /* Sequence analysis fields */ |
3498 | 14 | { &hf_rlc_lte_sequence_analysis, |
3499 | 14 | { "Sequence Analysis", |
3500 | 14 | "rlc-lte.sequence-analysis", FT_STRING, BASE_NONE, 0, 0x0, |
3501 | 14 | NULL, HFILL |
3502 | 14 | } |
3503 | 14 | }, |
3504 | 14 | { &hf_rlc_lte_sequence_analysis_ok, |
3505 | 14 | { "OK", |
3506 | 14 | "rlc-lte.sequence-analysis.ok", FT_BOOLEAN, BASE_NONE, 0, 0x0, |
3507 | 14 | NULL, HFILL |
3508 | 14 | } |
3509 | 14 | }, |
3510 | 14 | { &hf_rlc_lte_sequence_analysis_previous_frame, |
3511 | 14 | { "Previous frame for channel", |
3512 | 14 | "rlc-lte.sequence-analysis.previous-frame", FT_FRAMENUM, BASE_NONE, 0, 0x0, |
3513 | 14 | NULL, HFILL |
3514 | 14 | } |
3515 | 14 | }, |
3516 | 14 | { &hf_rlc_lte_sequence_analysis_next_frame, |
3517 | 14 | { "Next frame for channel", |
3518 | 14 | "rlc-lte.sequence-analysis.next-frame", FT_FRAMENUM, BASE_NONE, 0, 0x0, |
3519 | 14 | NULL, HFILL |
3520 | 14 | } |
3521 | 14 | }, |
3522 | 14 | { &hf_rlc_lte_sequence_analysis_expected_sn, |
3523 | 14 | { "Expected SN", |
3524 | 14 | "rlc-lte.sequence-analysis.expected-sn", FT_UINT16, BASE_DEC, 0, 0x0, |
3525 | 14 | NULL, HFILL |
3526 | 14 | } |
3527 | 14 | }, |
3528 | 14 | { &hf_rlc_lte_sequence_analysis_framing_info_correct, |
3529 | 14 | { "Frame info continued correctly", |
3530 | 14 | "rlc-lte.sequence-analysis.framing-info-correct", FT_BOOLEAN, BASE_NONE, 0, 0x0, |
3531 | 14 | NULL, HFILL |
3532 | 14 | } |
3533 | 14 | }, |
3534 | 14 | { &hf_rlc_lte_sequence_analysis_mac_retx, |
3535 | 14 | { "Frame retransmitted by MAC", |
3536 | 14 | "rlc-lte.sequence-analysis.mac-retx", FT_BOOLEAN, BASE_NONE, 0, 0x0, |
3537 | 14 | NULL, HFILL |
3538 | 14 | } |
3539 | 14 | }, |
3540 | 14 | { &hf_rlc_lte_sequence_analysis_retx, |
3541 | 14 | { "Retransmitted frame", |
3542 | 14 | "rlc-lte.sequence-analysis.retx", FT_BOOLEAN, BASE_NONE, 0, 0x0, |
3543 | 14 | NULL, HFILL |
3544 | 14 | } |
3545 | 14 | }, |
3546 | 14 | { &hf_rlc_lte_sequence_analysis_skipped, |
3547 | 14 | { "Skipped frames", |
3548 | 14 | "rlc-lte.sequence-analysis.skipped-frames", FT_BOOLEAN, BASE_NONE, 0, 0x0, |
3549 | 14 | NULL, HFILL |
3550 | 14 | } |
3551 | 14 | }, |
3552 | 14 | { &hf_rlc_lte_sequence_analysis_repeated, |
3553 | 14 | { "Repeated frame", |
3554 | 14 | "rlc-lte.sequence-analysis.repeated-frame", FT_BOOLEAN, BASE_NONE, 0, 0x0, |
3555 | 14 | NULL, HFILL |
3556 | 14 | } |
3557 | 14 | }, |
3558 | 14 | { &hf_rlc_lte_sequence_analysis_repeated_nack, |
3559 | 14 | { "Repeated NACK", |
3560 | 14 | "rlc-lte.sequence-analysis.repeated-nack", FT_UINT16, BASE_DEC, 0, 0x0, |
3561 | 14 | NULL, HFILL |
3562 | 14 | } |
3563 | 14 | }, |
3564 | 14 | { &hf_rlc_lte_sequence_analysis_repeated_nack_original_frame, |
3565 | 14 | { "Frame with previous status PDU", |
3566 | 14 | "rlc-lte.sequence-analysis.repeated-nack.original-frame", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_DUP_ACK), 0x0, |
3567 | 14 | NULL, HFILL |
3568 | 14 | } |
3569 | 14 | }, |
3570 | | |
3571 | 14 | { &hf_rlc_lte_sequence_analysis_ack_out_of_range, |
3572 | 14 | { "Out of range ACK", |
3573 | 14 | "rlc-lte.sequence-analysis.ack-out-of-range", FT_BOOLEAN, BASE_NONE, 0, 0x0, |
3574 | 14 | NULL, HFILL |
3575 | 14 | } |
3576 | 14 | }, |
3577 | 14 | { &hf_rlc_lte_sequence_analysis_ack_out_of_range_opposite_frame, |
3578 | 14 | { "Frame with most recent SN", |
3579 | 14 | "rlc-lte.sequence-analysis.ack-out-of-range.last-sn-frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0, |
3580 | 14 | NULL, HFILL |
3581 | 14 | } |
3582 | 14 | }, |
3583 | | |
3584 | | /* Reassembly fields */ |
3585 | 14 | { &hf_rlc_lte_reassembly_source, |
3586 | 14 | { "Reassembly Source", |
3587 | 14 | "rlc-lte.reassembly-info", FT_STRING, BASE_NONE, 0, 0x0, |
3588 | 14 | NULL, HFILL |
3589 | 14 | } |
3590 | 14 | }, |
3591 | 14 | { &hf_rlc_lte_reassembly_source_number_of_segments, |
3592 | 14 | { "Number of segments", |
3593 | 14 | "rlc-lte.reassembly-info.number-of-segments", FT_UINT16, BASE_DEC, 0, 0x0, |
3594 | 14 | NULL, HFILL |
3595 | 14 | } |
3596 | 14 | }, |
3597 | 14 | { &hf_rlc_lte_reassembly_source_total_length, |
3598 | 14 | { "Total length", |
3599 | 14 | "rlc-lte.reassembly-info.total-length", FT_UINT16, BASE_DEC, 0, 0x0, |
3600 | 14 | NULL, HFILL |
3601 | 14 | } |
3602 | 14 | }, |
3603 | 14 | { &hf_rlc_lte_reassembly_source_segment, |
3604 | 14 | { "Segment", |
3605 | 14 | "rlc-lte.reassembly-info.segment", FT_NONE, BASE_NONE, 0, 0x0, |
3606 | 14 | NULL, HFILL |
3607 | 14 | } |
3608 | 14 | }, |
3609 | 14 | { &hf_rlc_lte_reassembly_source_segment_sn, |
3610 | 14 | { "SN", |
3611 | 14 | "rlc-lte.reassembly-info.segment.sn", FT_UINT16, BASE_DEC, 0, 0x0, |
3612 | 14 | NULL, HFILL |
3613 | 14 | } |
3614 | 14 | }, |
3615 | 14 | { &hf_rlc_lte_reassembly_source_segment_framenum, |
3616 | 14 | { "Frame", |
3617 | 14 | "rlc-lte.reassembly-info.segment.frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0, |
3618 | 14 | NULL, HFILL |
3619 | 14 | } |
3620 | 14 | }, |
3621 | 14 | { &hf_rlc_lte_reassembly_source_segment_length, |
3622 | 14 | { "Length", |
3623 | 14 | "rlc-lte.reassembly-info.segment.length", FT_UINT32, BASE_DEC, 0, 0x0, |
3624 | 14 | NULL, HFILL |
3625 | 14 | } |
3626 | 14 | }, |
3627 | | |
3628 | 14 | { &hf_rlc_lte_header_only, |
3629 | 14 | { "RLC PDU Header only", |
3630 | 14 | "rlc-lte.header-only", FT_UINT8, BASE_DEC, VALS(header_only_vals), 0x0, |
3631 | 14 | NULL, HFILL |
3632 | 14 | } |
3633 | 14 | }, |
3634 | 14 | }; |
3635 | | |
3636 | 14 | static int *ett[] = |
3637 | 14 | { |
3638 | 14 | &ett_rlc_lte, |
3639 | 14 | &ett_rlc_lte_context, |
3640 | 14 | &ett_rlc_lte_um_header, |
3641 | 14 | &ett_rlc_lte_am_header, |
3642 | 14 | &ett_rlc_lte_extension_part, |
3643 | 14 | &ett_rlc_lte_sequence_analysis, |
3644 | 14 | &ett_rlc_lte_reassembly_source, |
3645 | 14 | &ett_rlc_lte_reassembly_source_segment |
3646 | 14 | }; |
3647 | | |
3648 | 14 | static ei_register_info ei[] = { |
3649 | 14 | { &ei_rlc_lte_sequence_analysis_last_segment_not_continued, { "rlc-lte.sequence-analysis.last-segment-not-continued", PI_SEQUENCE, PI_WARN, "Last segment of previous PDU was not continued for UE", EXPFILL }}, |
3650 | 14 | { &ei_rlc_lte_sequence_analysis_last_segment_complete, { "rlc-lte.sequence-analysis.last-segment-complete", PI_SEQUENCE, PI_WARN, "Last segment of previous PDU was complete, but new segment was not started on UE", EXPFILL }}, |
3651 | 14 | { &ei_rlc_lte_sequence_analysis_mac_retx, { "rlc-lte.sequence-analysis.mac-retx.expert", PI_SEQUENCE, PI_WARN, "AM Frame retransmitted due to MAC retx!", EXPFILL }}, |
3652 | 14 | { &ei_rlc_lte_sequence_analysis_retx, { "rlc-lte.sequence-analysis.retx.expert", PI_SEQUENCE, PI_WARN, "AM Frame retransmitted most likely in response to NACK", EXPFILL }}, |
3653 | 14 | { &ei_rlc_lte_sequence_analysis_repeated, { "rlc-lte.sequence-analysis.repeated-frame.expert", PI_SEQUENCE, PI_WARN, "AM SN Repeated - probably because didn't receive Status PDU?", EXPFILL }}, |
3654 | 14 | { &ei_rlc_lte_am_sn_missing, { "rlc-lte.sequence-analysis.am-sn.missing", PI_SEQUENCE, PI_WARN, "AM SNs missing", EXPFILL }}, |
3655 | 14 | { &ei_rlc_lte_sequence_analysis_ack_out_of_range_opposite_frame, { "rlc-lte.sequence-analysis.ack-out-of-range.last-sn-frame.expert", PI_SEQUENCE, PI_ERROR, "AM ACK for SN - but last received SN in other direction is X", EXPFILL }}, |
3656 | 14 | { &ei_rlc_lte_um_sn_missing, { "rlc-lte.sequence-analysis.um-sn.missing", PI_SEQUENCE, PI_WARN, "UM SNs missing", EXPFILL }}, |
3657 | 14 | { &ei_rlc_lte_um_sn_repeated, { "rlc-lte.sequence-analysis.um-sn.repeated", PI_SEQUENCE, PI_WARN, "UM SN repeated", EXPFILL }}, |
3658 | 14 | { &ei_rlc_lte_wrong_sequence_number, { "rlc-lte.wrong-sequence-number", PI_SEQUENCE, PI_WARN, "Wrong Sequence Number", EXPFILL }}, |
3659 | 14 | { &ei_rlc_lte_sequence_analysis_repeated_nack, { "rlc-lte.sequence-analysis.repeated-nack.expert", PI_SEQUENCE, PI_ERROR, "Same SN NACKd on successive Status PDUs", EXPFILL }}, |
3660 | 14 | { &ei_rlc_lte_reserved_bits_not_zero, { "rlc-lte.reserved-bits-not-zero", PI_MALFORMED, PI_ERROR, "Reserved bits not zero", EXPFILL }}, |
3661 | 14 | { &ei_rlc_lte_um_sn, { "rlc-lte.um.sn.invalid", PI_MALFORMED, PI_ERROR, "Invalid sequence number length", EXPFILL }}, |
3662 | 14 | { &ei_rlc_lte_header_only, { "rlc-lte.header-only.expert", PI_SEQUENCE, PI_NOTE, "RLC PDU SDUs have been omitted", EXPFILL }}, |
3663 | 14 | { &ei_rlc_lte_am_cpt, { "rlc-lte.am.cpt.invalid", PI_MALFORMED, PI_ERROR, "RLC Control frame type not handled", EXPFILL }}, |
3664 | 14 | { &ei_rlc_lte_am_nack_sn_ack_same, { "rlc-lte.am.nack-sn.ack-same", PI_MALFORMED, PI_ERROR, "Status PDU shouldn't ACK and NACK the same sequence number", EXPFILL }}, |
3665 | 14 | { &ei_rlc_lte_am_nack_sn_ahead_ack, { "rlc-lte.am.nack-sn.ahead-ack", PI_MALFORMED, PI_ERROR, "NACK must not be ahead of ACK in status PDU", EXPFILL }}, |
3666 | 14 | { &ei_rlc_lte_am_nack_sn_partial, { "rlc-lte.am.nack-sn.partial", PI_SEQUENCE, PI_WARN, "Status PDU reports NACK (partial)", EXPFILL }}, |
3667 | 14 | { &ei_rlc_lte_am_nack_sn, { "rlc-lte.am.nack-sn.expert", PI_SEQUENCE, PI_WARN, "Status PDU reports NACK", EXPFILL }}, |
3668 | 14 | { &ei_rlc_lte_bytes_after_status_pdu_complete, { "rlc-lte.bytes-after-status-pdu-complete", PI_MALFORMED, PI_ERROR, "bytes remaining after Status PDU complete", EXPFILL }}, |
3669 | 14 | { &ei_rlc_lte_am_data_no_data_beyond_extensions, { "rlc-lte.am-data.no-data-beyond-extensions", PI_MALFORMED, PI_ERROR, "AM data PDU doesn't contain any data beyond extensions", EXPFILL }}, |
3670 | 14 | { &ei_rlc_lte_am_data_no_data, { "rlc-lte.am-data.no-data", PI_MALFORMED, PI_ERROR, "AM data PDU doesn't contain any data", EXPFILL }}, |
3671 | 14 | { &ei_rlc_lte_context_mode, { "rlc-lte.mode.invalid", PI_MALFORMED, PI_ERROR, "Unrecognised RLC Mode set", EXPFILL }}, |
3672 | 14 | { &ei_rlc_lte_no_per_frame_info, { "rlc-lte.no_per_frame_info", PI_UNDECODED, PI_ERROR, "Can't dissect LTE RLC frame because no per-frame info was attached!", EXPFILL }}, |
3673 | 14 | { &ei_rlc_lte_unknown_udp_framing_tag, { "rlc-lte.unknown-udp-framing-tag", PI_UNDECODED, PI_WARN, "Unknown UDP framing tag, aborting dissection", EXPFILL }}, |
3674 | 14 | { &ei_rlc_lte_missing_udp_framing_tag, { "rlc-lte.missing-udp-framing-tag", PI_UNDECODED, PI_WARN, "Missing UDP framing conditional tag, aborting dissection", EXPFILL }} |
3675 | 14 | }; |
3676 | | |
3677 | 14 | static const enum_val_t sequence_analysis_vals[] = { |
3678 | 14 | {"no-analysis", "No-Analysis", false}, |
3679 | 14 | {"mac-only", "Only-MAC-frames", SEQUENCE_ANALYSIS_MAC_ONLY}, |
3680 | 14 | {"rlc-only", "Only-RLC-frames", SEQUENCE_ANALYSIS_RLC_ONLY}, |
3681 | 14 | {NULL, NULL, -1} |
3682 | 14 | }; |
3683 | | |
3684 | 14 | module_t *rlc_lte_module; |
3685 | 14 | expert_module_t* expert_rlc_lte; |
3686 | | |
3687 | | /* Register protocol. */ |
3688 | 14 | proto_rlc_lte = proto_register_protocol("RLC-LTE", "RLC-LTE", "rlc-lte"); |
3689 | 14 | proto_register_field_array(proto_rlc_lte, hf, array_length(hf)); |
3690 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
3691 | 14 | expert_rlc_lte = expert_register_protocol(proto_rlc_lte); |
3692 | 14 | expert_register_field_array(expert_rlc_lte, ei, array_length(ei)); |
3693 | | |
3694 | | /* Allow other dissectors to find this one by name. */ |
3695 | 14 | register_dissector("rlc-lte", dissect_rlc_lte, proto_rlc_lte); |
3696 | | |
3697 | | /* Register the tap name */ |
3698 | 14 | rlc_lte_tap = register_tap("rlc-3gpp"); |
3699 | | |
3700 | | /* Preferences */ |
3701 | 14 | rlc_lte_module = prefs_register_protocol(proto_rlc_lte, NULL); |
3702 | | |
3703 | 14 | prefs_register_enum_preference(rlc_lte_module, "do_sequence_analysis_am", |
3704 | 14 | "Do sequence analysis for AM channels", |
3705 | 14 | "Attempt to keep track of PDUs for AM channels, and point out problems", |
3706 | 14 | &global_rlc_lte_am_sequence_analysis, sequence_analysis_vals, false); |
3707 | | |
3708 | 14 | prefs_register_enum_preference(rlc_lte_module, "do_sequence_analysis", |
3709 | 14 | "Do sequence analysis for UM channels", |
3710 | 14 | "Attempt to keep track of PDUs for UM channels, and point out problems", |
3711 | 14 | &global_rlc_lte_um_sequence_analysis, sequence_analysis_vals, false); |
3712 | | |
3713 | 14 | prefs_register_bool_preference(rlc_lte_module, "call_pdcp_for_srb", |
3714 | 14 | "Call PDCP dissector for SRB PDUs", |
3715 | 14 | "Call PDCP dissector for signalling PDUs. Note that without reassembly, it can" |
3716 | 14 | "only be called for complete PDUs (i.e. not segmented over RLC)", |
3717 | 14 | &global_rlc_lte_call_pdcp_for_srb); |
3718 | | |
3719 | 14 | prefs_register_enum_preference(rlc_lte_module, "call_pdcp_for_drb", |
3720 | 14 | "Call PDCP dissector for DRB PDUs", |
3721 | 14 | "Call PDCP dissector for user-plane PDUs. Note that without reassembly, it can" |
3722 | 14 | "only be called for complete PDUs (i.e. not segmented over RLC)", |
3723 | 14 | &global_rlc_lte_call_pdcp_for_drb, pdcp_drb_col_vals, false); |
3724 | | |
3725 | | |
3726 | 14 | prefs_register_bool_preference(rlc_lte_module, "call_rrc_for_ccch", |
3727 | 14 | "Call RRC dissector for CCCH PDUs", |
3728 | 14 | "Call RRC dissector for CCCH PDUs", |
3729 | 14 | &global_rlc_lte_call_rrc_for_ccch); |
3730 | | |
3731 | 14 | prefs_register_bool_preference(rlc_lte_module, "call_rrc_for_mcch", |
3732 | 14 | "Call RRC dissector for MCCH PDUs", |
3733 | 14 | "Call RRC dissector for MCCH PDUs Note that without reassembly, it can" |
3734 | 14 | "only be called for complete PDUs (i.e. not segmented over RLC)", |
3735 | 14 | &global_rlc_lte_call_rrc_for_mcch); |
3736 | | |
3737 | 14 | prefs_register_bool_preference(rlc_lte_module, "call_ip_for_mtch", |
3738 | 14 | "Call IP dissector for MTCH PDUs", |
3739 | 14 | "Call ip dissector for MTCH PDUs Note that without reassembly, it can" |
3740 | 14 | "only be called for complete PDUs (i.e. not segmented over RLC)", |
3741 | 14 | &global_rlc_lte_call_ip_for_mtch); |
3742 | | |
3743 | 14 | prefs_register_obsolete_preference(rlc_lte_module, "heuristic_rlc_lte_over_udp"); |
3744 | | |
3745 | 14 | prefs_register_bool_preference(rlc_lte_module, "header_only_mode", |
3746 | 14 | "May see RLC headers only", |
3747 | 14 | "When enabled, if data is not present, don't report as an error, but instead " |
3748 | 14 | "add expert info to indicate that headers were omitted", |
3749 | 14 | &global_rlc_lte_headers_expected); |
3750 | | |
3751 | 14 | prefs_register_bool_preference(rlc_lte_module, "reassembly", |
3752 | 14 | "Attempt SDU reassembly", |
3753 | 14 | "When enabled, attempts to re-assemble upper-layer SDUs that are split over " |
3754 | 14 | "more than one RLC PDU. Note: does not currently support out-of-order or " |
3755 | 14 | "re-segmentation. N.B. sequence analysis must also be turned on in order " |
3756 | 14 | "for reassembly to work", |
3757 | 14 | &global_rlc_lte_reassembly); |
3758 | | |
3759 | 14 | ue_parameters_tree = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); |
3760 | | |
3761 | 14 | sequence_analysis_channel_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), rlc_channel_hash_func, rlc_channel_equal); |
3762 | 14 | sequence_analysis_report_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), rlc_result_hash_func, rlc_result_hash_equal); |
3763 | 14 | repeated_nack_channel_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), rlc_channel_hash_func, rlc_channel_equal); |
3764 | 14 | repeated_nack_report_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), rlc_result_hash_func, rlc_result_hash_equal); |
3765 | 14 | reassembly_report_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), rlc_result_hash_func, rlc_result_hash_equal); |
3766 | 14 | } |
3767 | | |
3768 | | void proto_reg_handoff_rlc_lte(void) |
3769 | 14 | { |
3770 | | /* Add as a heuristic UDP dissector */ |
3771 | 14 | heur_dissector_add("udp", dissect_rlc_lte_heur, "RLC-LTE over UDP", "rlc_lte_udp", proto_rlc_lte, HEURISTIC_DISABLE); |
3772 | | |
3773 | 14 | pdcp_lte_handle = find_dissector_add_dependency("pdcp-lte", proto_rlc_lte); |
3774 | 14 | ip_handle = find_dissector_add_dependency("ip", proto_rlc_lte); |
3775 | 14 | lte_rrc_mcch = find_dissector_add_dependency("lte_rrc.mcch", proto_rlc_lte); |
3776 | 14 | lte_rrc_ul_ccch = find_dissector_add_dependency("lte_rrc.ul_ccch", proto_rlc_lte); |
3777 | 14 | lte_rrc_dl_ccch = find_dissector_add_dependency("lte_rrc.dl_ccch", proto_rlc_lte); |
3778 | 14 | lte_rrc_bcch_bch = find_dissector_add_dependency("lte_rrc.bcch_bch", proto_rlc_lte); |
3779 | 14 | lte_rrc_bcch_dl_sch = find_dissector_add_dependency("lte_rrc.bcch_dl_sch", proto_rlc_lte); |
3780 | 14 | lte_rrc_pcch = find_dissector_add_dependency("lte_rrc.pcch", proto_rlc_lte); |
3781 | 14 | lte_rrc_ul_ccch_nb = find_dissector_add_dependency("lte_rrc.ul_ccch.nb", proto_rlc_lte); |
3782 | 14 | lte_rrc_dl_ccch_nb = find_dissector_add_dependency("lte_rrc.dl_ccch.nb", proto_rlc_lte); |
3783 | 14 | lte_rrc_bcch_bch_nb = find_dissector_add_dependency("lte_rrc.bcch_bch.nb", proto_rlc_lte); |
3784 | 14 | lte_rrc_bcch_dl_sch_nb = find_dissector_add_dependency("lte_rrc.bcch_dl_sch.nb", proto_rlc_lte); |
3785 | 14 | lte_rrc_pcch_nb = find_dissector_add_dependency("lte_rrc.pcch.nb", proto_rlc_lte); |
3786 | | |
3787 | 14 | proto_mac_lte = proto_get_id_by_filter_name("mac-lte"); |
3788 | 14 | proto_pdcp_lte = proto_get_id_by_filter_name("pdcp-lte"); |
3789 | 14 | } |
3790 | | |
3791 | | /* |
3792 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
3793 | | * |
3794 | | * Local variables: |
3795 | | * c-basic-offset: 4 |
3796 | | * tab-width: 8 |
3797 | | * indent-tabs-mode: nil |
3798 | | * End: |
3799 | | * |
3800 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
3801 | | * :indentSize=4:tabSize=8:noTabs=true: |
3802 | | */ |