2 * LTE RLC channel graph info
4 * Originally from tcp_graph.c by Pavel Mores <pvl@uh.cz>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include "tap-rlc-graph.h"
21 #include <epan/epan_dissect.h>
24 /* Return true if the 2 sets of parameters refer to the same channel. */
25 bool compare_rlc_headers(uint8_t rat1
, uint8_t rat2
,
26 uint16_t ueid1
, uint16_t channelType1
, uint16_t channelId1
, uint8_t rlcMode1
, uint8_t direction1
,
27 uint16_t ueid2
, uint16_t channelType2
, uint16_t channelId2
, uint8_t rlcMode2
, uint8_t direction2
,
34 /* Same direction, data - OK. */
35 if (!frameIsControl
) {
36 return (direction1
== direction2
) &&
38 (channelType1
== channelType2
) &&
39 (channelId1
== channelId2
) &&
40 (rlcMode1
== rlcMode2
);
44 if ((rlcMode1
== RLC_AM_MODE
) && (rlcMode2
== RLC_AM_MODE
)) {
45 return ((direction1
!= direction2
) &&
47 (channelType1
== channelType2
) &&
48 (channelId1
== channelId2
));
56 /* This is the tap function used to identify a list of channels found in the current frame. It is only used for the single,
57 currently selected frame. */
58 static tap_packet_status
59 tap_lte_rlc_packet(void *pct
, packet_info
*pinfo _U_
, epan_dissect_t
*edt _U_
, const void *vip
, tap_flags_t flags _U_
)
62 bool is_unique
= true;
63 th_t
*th
= (th_t
*)pct
;
64 const rlc_3gpp_tap_info
*header
= (const rlc_3gpp_tap_info
*)vip
;
66 /* Check new header details against any/all stored ones */
67 for (n
=0; n
< th
->num_hdrs
; n
++) {
68 rlc_3gpp_tap_info
*stored
= th
->rlchdrs
[n
];
70 if (compare_rlc_headers(stored
->rat
, header
->rat
,
71 stored
->ueid
, stored
->channelType
, stored
->channelId
, stored
->rlcMode
, stored
->direction
,
72 header
->ueid
, header
->channelType
, header
->channelId
, header
->rlcMode
, header
->direction
,
73 header
->isControlPDU
)) {
79 /* Add address if unique and have space for it */
80 if (is_unique
&& (th
->num_hdrs
< MAX_SUPPORTED_CHANNELS
)) {
81 /* Copy the tap struct in as next header */
82 /* Need to take a deep copy of the tap struct, it may not be valid
83 to read after this function returns? */
84 th
->rlchdrs
[th
->num_hdrs
] = g_new(rlc_3gpp_tap_info
,1);
85 *(th
->rlchdrs
[th
->num_hdrs
]) = *header
;
87 /* Store in direction of data though... */
88 if (th
->rlchdrs
[th
->num_hdrs
]->isControlPDU
) {
89 th
->rlchdrs
[th
->num_hdrs
]->direction
= !th
->rlchdrs
[th
->num_hdrs
]->direction
;
94 return TAP_PACKET_DONT_REDRAW
; /* i.e. no immediate redraw requested */
97 /* Return an array of tap_info structs that were found while dissecting the current frame
98 * in the packet list. Errors are passed back to the caller, as they will be reported differently
99 * depending upon which GUI toolkit is being used. */
100 rlc_3gpp_tap_info
* select_rlc_lte_session(capture_file
*cf
,
101 struct rlc_segment
*hdrs
,
108 GString
*error_string
;
110 /* Initialised to no known channels */
111 th_t th
= {0, {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
}};
113 if (cf
->state
== FILE_CLOSED
) {
117 /* No real filter yet */
118 if (!dfilter_compile("rlc-lte or rlc-nr", &sfcode
, NULL
)) {
122 /* Dissect the data from the current frame. */
123 if (!cf_read_current_record(cf
)) {
124 dfilter_free(sfcode
);
125 return NULL
; /* error reading the record */
128 fdata
= cf
->current_frame
;
130 /* Set tap listener that will populate th. */
131 error_string
= register_tap_listener("rlc-3gpp", &th
, NULL
, 0, NULL
, tap_lte_rlc_packet
, NULL
, NULL
);
133 fprintf(stderr
, "wireshark: Couldn't register rlc_lte_graph tap: %s\n",
135 g_string_free(error_string
, TRUE
);
136 dfilter_free(sfcode
);
137 exit(1); /* XXX: fix this */
140 epan_dissect_init(&edt
, cf
->epan
, true, false);
141 epan_dissect_prime_with_dfilter(&edt
, sfcode
);
142 epan_dissect_run_with_taps(&edt
, cf
->cd_t
, &cf
->rec
,
143 ws_buffer_start_ptr(&cf
->buf
),
145 rel_ts
= edt
.pi
.rel_ts
;
146 epan_dissect_cleanup(&edt
);
147 remove_tap_listener(&th
);
149 if (th
.num_hdrs
== 0){
150 /* This "shouldn't happen", as the graph menu items won't
151 * even be enabled if the selected packet isn't an RLC PDU.
153 *err_msg
= g_strdup("Selected packet doesn't have an RLC PDU");
156 /* XXX fix this later, we should show a dialog allowing the user
157 * to select which session he wants here */
159 /* Can only handle a single RLC channel yet */
160 *err_msg
= g_strdup("The selected packet has more than one LTE RLC channel in it.");
164 /* For now, still always choose the first/only one */
165 hdrs
->num
= fdata
->num
;
166 hdrs
->rel_secs
= rel_ts
.secs
;
167 hdrs
->rel_usecs
= rel_ts
.nsecs
/1000;
169 hdrs
->rat
= th
.rlchdrs
[0]->rat
;
170 hdrs
->ueid
= th
.rlchdrs
[0]->ueid
;
171 hdrs
->channelType
= th
.rlchdrs
[0]->channelType
;
172 hdrs
->channelId
= th
.rlchdrs
[0]->channelId
;
173 hdrs
->rlcMode
= th
.rlchdrs
[0]->rlcMode
;
174 hdrs
->isControlPDU
= th
.rlchdrs
[0]->isControlPDU
;
175 /* Flip direction if have control PDU */
176 hdrs
->direction
= !hdrs
->isControlPDU
? th
.rlchdrs
[0]->direction
: !th
.rlchdrs
[0]->direction
;
178 return th
.rlchdrs
[0];
181 /* This is the tapping function to update stats when dissecting the whole packet list */
182 static tap_packet_status
rlc_lte_tap_for_graph_data(void *pct
, packet_info
*pinfo
, epan_dissect_t
*edt _U_
, const void *vip
, tap_flags_t flags _U_
)
184 struct rlc_graph
*graph
= (struct rlc_graph
*)pct
;
185 const rlc_3gpp_tap_info
*rlchdr
= (const rlc_3gpp_tap_info
*)vip
;
187 /* See if this one matches graph's channel */
188 if (compare_rlc_headers(graph
->rat
, rlchdr
->rat
,
189 graph
->ueid
, graph
->channelType
, graph
->channelId
, graph
->rlcMode
, graph
->direction
,
190 rlchdr
->ueid
, rlchdr
->channelType
, rlchdr
->channelId
, rlchdr
->rlcMode
, rlchdr
->direction
,
191 rlchdr
->isControlPDU
)) {
193 /* It matches. Copy segment details out of tap struct */
194 struct rlc_segment
*segment
= g_new(struct rlc_segment
, 1);
195 segment
->next
= NULL
;
196 segment
->num
= pinfo
->num
;
197 segment
->rel_secs
= (uint32_t) pinfo
->rel_ts
.secs
;
198 segment
->rel_usecs
= pinfo
->rel_ts
.nsecs
/1000;
200 segment
->rat
= rlchdr
->rat
;
201 segment
->ueid
= rlchdr
->ueid
;
202 segment
->channelType
= rlchdr
->channelType
;
203 segment
->channelId
= rlchdr
->channelId
;
204 segment
->direction
= rlchdr
->direction
;
205 segment
->rlcMode
= rlchdr
->rlcMode
;
206 segment
->sequenceNumberLength
= rlchdr
->sequenceNumberLength
;
208 segment
->isControlPDU
= rlchdr
->isControlPDU
;
210 if (!rlchdr
->isControlPDU
) {
211 if (rlchdr
->sequenceNumberGiven
) {
213 segment
->SN
= rlchdr
->sequenceNumber
;
214 segment
->isResegmented
= rlchdr
->isResegmented
;
215 segment
->pduLength
= rlchdr
->pduLength
;
218 /* No sequence number, so not going to show at all */
220 return TAP_PACKET_DONT_REDRAW
; /* i.e. no immediate redraw requested */
226 segment
->ACKNo
= rlchdr
->ACKNo
;
227 segment
->noOfNACKs
= rlchdr
->noOfNACKs
;
228 for (n
=0; (n
< rlchdr
->noOfNACKs
) && (n
< MAX_NACKs
); n
++) {
229 segment
->NACKs
[n
] = rlchdr
->NACKs
[n
];
233 /* Add segment to end of list */
234 if (graph
->segments
) {
235 /* Add to end of existing last element */
236 graph
->last_segment
->next
= segment
;
238 /* Make this the first (only) segment */
239 graph
->segments
= segment
;
242 /* This one is now the last one */
243 graph
->last_segment
= segment
;
246 return TAP_PACKET_DONT_REDRAW
; /* i.e. no immediate redraw requested */
249 /* If don't have a channel, try to get one from current frame, then read all frames looking for data
250 * for that channel. */
251 bool rlc_graph_segment_list_get(capture_file
*cf
, struct rlc_graph
*g
, bool stream_known
,
254 struct rlc_segment current
;
255 GString
*error_string
;
258 /* Really shouldn't happen */
263 struct rlc_3gpp_tap_info
*header
= select_rlc_lte_session(cf
, ¤t
, err_string
);
265 /* Didn't have a channel, and current frame didn't provide one */
268 g
->channelSet
= true;
270 g
->rat
= header
->rat
;
271 g
->ueid
= header
->ueid
;
272 g
->channelType
= header
->channelType
;
273 g
->channelId
= header
->channelId
;
274 g
->rlcMode
= header
->rlcMode
;
275 g
->direction
= header
->direction
;
279 /* Rescan all the packets and pick up all interesting RLC headers.
280 * We only filter for RLC frames here for speed and do the actual compare
281 * in the tap listener
284 g
->last_segment
= NULL
;
285 error_string
= register_tap_listener("rlc-3gpp", // tap name
287 "rlc-lte or rlc-nr", // filter name
288 0, NULL
, rlc_lte_tap_for_graph_data
, NULL
, NULL
);
290 fprintf(stderr
, "wireshark: Couldn't register rlc_graph tap: %s\n",
292 g_string_free(error_string
, TRUE
);
293 exit(1); /* XXX: fix this */
295 cf_retap_packets(cf
);
296 remove_tap_listener(g
);
298 if (g
->last_segment
== NULL
) {
299 *err_string
= g_strdup("No packets found");
306 /* Free and zero the segments list of an rlc_graph struct */
307 void rlc_graph_segment_list_free(struct rlc_graph
* g
)
309 struct rlc_segment
*segment
;
311 /* Free all segments */
312 while (g
->segments
) {
313 segment
= g
->segments
->next
;
315 g
->segments
= segment
;