2 * Copyright 2011 Martin Mathieson
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include <epan/packet.h>
20 #include <epan/stat_tap_ui.h>
22 #include <epan/dissectors/packet-rlc-lte.h>
23 #include <epan/dissectors/packet-rlc-3gpp-common.h>
26 void register_tap_listener_rlc_lte_stat(void);
46 static const char *ue_titles
[] = { "RAT", " UEId",
47 "UL Frames", "UL Bytes", " UL Mbs", "UL ACKs", "UL NACKs", "UL Missed",
48 "DL Frames", "DL Bytes", " DL Mbs", "DL ACKs", "DL NACKs", "DL Missed"};
50 /* Stats for one UE */
51 typedef struct rlc_lte_row_data
{
52 /* Key for matching this row */
56 bool is_predefined_data
;
59 uint32_t UL_total_bytes
;
60 nstime_t UL_time_start
;
61 nstime_t UL_time_stop
;
62 uint32_t UL_total_acks
;
63 uint32_t UL_total_nacks
;
64 uint32_t UL_total_missing
;
67 uint32_t DL_total_bytes
;
68 nstime_t DL_time_start
;
69 nstime_t DL_time_stop
;
70 uint32_t DL_total_acks
;
71 uint32_t DL_total_nacks
;
72 uint32_t DL_total_missing
;
77 /* Common channel stats (i.e. independent of UEs) */
78 typedef struct rlc_lte_common_stats
{
83 } rlc_lte_common_stats
;
86 /* One row/UE in the UE table */
87 typedef struct rlc_lte_ep
{
88 struct rlc_lte_ep
*next
;
89 struct rlc_lte_row_data stats
;
93 /* Top-level struct for RLC LTE statistics */
94 typedef struct rlc_lte_stat_t
{
95 rlc_lte_ep_t
*ep_list
;
96 uint32_t total_frames
;
99 rlc_lte_common_stats common_stats
;
104 /* Reset RLC stats */
106 rlc_lte_stat_reset(void *phs
)
108 rlc_lte_stat_t
*rlc_lte_stat
= (rlc_lte_stat_t
*)phs
;
109 rlc_lte_ep_t
*list
= rlc_lte_stat
->ep_list
;
111 rlc_lte_stat
->total_frames
= 0;
112 memset(&rlc_lte_stat
->common_stats
, 0, sizeof(rlc_lte_common_stats
));
118 rlc_lte_stat
->ep_list
= NULL
;
122 /* Allocate a rlc_lte_ep_t struct to store info for new UE */
123 static rlc_lte_ep_t
*alloc_rlc_lte_ep(const struct rlc_3gpp_tap_info
*si
, packet_info
*pinfo _U_
)
131 if (!(ep
= g_new(rlc_lte_ep_t
, 1))) {
135 /* Copy SI data into ep->stats */
136 ep
->stats
.rat
= si
->rat
;
137 ep
->stats
.ueid
= si
->ueid
;
139 /* Counts for new UE are all 0 */
140 ep
->stats
.UL_frames
= 0;
141 ep
->stats
.DL_frames
= 0;
142 ep
->stats
.UL_total_bytes
= 0;
143 ep
->stats
.DL_total_bytes
= 0;
144 memset(&ep
->stats
.DL_time_start
, 0, sizeof(nstime_t
));
145 memset(&ep
->stats
.DL_time_stop
, 0, sizeof(nstime_t
));
146 ep
->stats
.UL_total_acks
= 0;
147 ep
->stats
.DL_total_acks
= 0;
148 ep
->stats
.UL_total_nacks
= 0;
149 ep
->stats
.DL_total_nacks
= 0;
150 ep
->stats
.UL_total_missing
= 0;
151 ep
->stats
.DL_total_missing
= 0;
159 /* Process stat struct for a RLC LTE frame */
160 static tap_packet_status
161 rlc_lte_stat_packet(void *phs
, packet_info
*pinfo
, epan_dissect_t
*edt _U_
,
162 const void *phi
, tap_flags_t flags _U_
)
164 /* Get reference to stats struct */
165 rlc_lte_stat_t
*hs
= (rlc_lte_stat_t
*)phs
;
166 rlc_lte_ep_t
*tmp
= NULL
, *te
= NULL
;
168 /* Cast tap info struct */
169 const struct rlc_3gpp_tap_info
*si
= (const struct rlc_3gpp_tap_info
*)phi
;
173 return TAP_PACKET_DONT_REDRAW
;
176 /* Inc top-level frame count */
179 /* Common channel stats */
180 switch (si
->channelType
) {
181 case CHANNEL_TYPE_BCCH_BCH
:
182 case CHANNEL_TYPE_BCCH_DL_SCH
:
183 hs
->common_stats
.bcch_frames
++;
184 hs
->common_stats
.bcch_bytes
+= si
->pduLength
;
185 return TAP_PACKET_REDRAW
;
187 case CHANNEL_TYPE_PCCH
:
188 hs
->common_stats
.pcch_frames
++;
189 hs
->common_stats
.pcch_bytes
+= si
->pduLength
;
190 return TAP_PACKET_REDRAW
;
196 /* For per-UE data, must create a new row if none already existing */
198 /* Allocate new list */
199 hs
->ep_list
= alloc_rlc_lte_ep(si
, pinfo
);
200 /* Make it the first/only entry */
203 /* Look among existing rows for this rat/UEId */
204 /* TODO: with different data structures, could avoid this linear search */
205 for (tmp
= hs
->ep_list
; (tmp
!= NULL
); tmp
= tmp
->next
) {
206 if ((tmp
->stats
.rat
== si
->rat
) &&
207 (tmp
->stats
.ueid
== si
->ueid
))
214 /* Not found among existing, so create a new one now */
216 if ((te
= alloc_rlc_lte_ep(si
, pinfo
))) {
217 /* Add new item to end of list */
218 rlc_lte_ep_t
*p
= hs
->ep_list
;
228 /* Really should have a row pointer by now */
230 return TAP_PACKET_DONT_REDRAW
;
233 /* Update entry with details from si */
234 te
->stats
.ueid
= si
->ueid
;
236 /* Top-level traffic stats */
237 if (si
->direction
== DIRECTION_UPLINK
) {
238 /* Update time range */
239 if (te
->stats
.UL_frames
== 0) {
240 te
->stats
.UL_time_start
= si
->rlc_time
;
242 te
->stats
.UL_time_stop
= si
->rlc_time
;
244 te
->stats
.UL_frames
++;
245 te
->stats
.UL_total_bytes
+= si
->pduLength
;
248 /* Update time range */
249 if (te
->stats
.DL_frames
== 0) {
250 te
->stats
.DL_time_start
= si
->rlc_time
;
252 te
->stats
.DL_time_stop
= si
->rlc_time
;
254 te
->stats
.DL_frames
++;
255 te
->stats
.DL_total_bytes
+= si
->pduLength
;
259 if (si
->direction
== DIRECTION_UPLINK
) {
260 if (si
->isControlPDU
) {
261 te
->stats
.UL_total_acks
++;
263 te
->stats
.UL_total_nacks
+= si
->noOfNACKs
;
264 te
->stats
.UL_total_missing
+= si
->missingSNs
;
267 if (si
->isControlPDU
) {
268 te
->stats
.DL_total_acks
++;
270 te
->stats
.DL_total_nacks
+= si
->noOfNACKs
;
271 te
->stats
.DL_total_missing
+= si
->missingSNs
;
274 return TAP_PACKET_REDRAW
;
278 /* Calculate and return a bandwidth figure, in Mbs */
279 static float calculate_bw(nstime_t
*start_time
, nstime_t
*stop_time
, uint32_t bytes
)
281 /* Can only calculate bandwidth if have time delta */
282 if (memcmp(start_time
, stop_time
, sizeof(nstime_t
)) != 0) {
283 float elapsed_ms
= (((float)stop_time
->secs
- (float)start_time
->secs
) * 1000) +
284 (((float)stop_time
->nsecs
- (float)start_time
->nsecs
) / 1000000);
286 /* Only really meaningful if have a few frames spread over time...
287 For now at least avoid dividing by something very close to 0.0 */
288 if (elapsed_ms
< 2.0) {
291 return ((bytes
* 8) / elapsed_ms
) / 1000;
300 /* (Re)draw RLC stats */
302 rlc_lte_stat_draw(void *phs
)
304 uint16_t number_of_ues
= 0;
307 /* Look up the statistics struct */
308 rlc_lte_stat_t
*hs
= (rlc_lte_stat_t
*)phs
;
309 rlc_lte_ep_t
*list
= hs
->ep_list
, *tmp
= 0;
311 /* Common channel data */
312 printf("Common Data:\n");
313 printf("==============\n");
314 printf("BCCH Frames: %u BCCH Bytes: %u PCCH Frames: %u PCCH Bytes: %u\n\n",
315 hs
->common_stats
.bcch_frames
, hs
->common_stats
.bcch_bytes
,
316 hs
->common_stats
.pcch_frames
, hs
->common_stats
.pcch_bytes
);
318 /* Per-UE table entries */
321 /* Set title that shows how many UEs currently in table */
322 for (tmp
= list
; (tmp
!=NULL
); tmp
=tmp
->next
, number_of_ues
++);
323 printf("Per UE Data - %u UEs (%u frames)\n", number_of_ues
, hs
->total_frames
);
324 printf("==========================================\n");
326 /* Show column titles */
327 for (i
=0; i
< NUM_UE_COLUMNS
; i
++) {
328 printf("%s ", ue_titles
[i
]);
332 /* For each row/UE in the model */
333 for (tmp
= list
; tmp
; tmp
=tmp
->next
) {
334 /* Calculate bandwidth */
335 float UL_bw
= calculate_bw(&tmp
->stats
.UL_time_start
,
336 &tmp
->stats
.UL_time_stop
,
337 tmp
->stats
.UL_total_bytes
);
338 float DL_bw
= calculate_bw(&tmp
->stats
.DL_time_start
,
339 &tmp
->stats
.DL_time_stop
,
340 tmp
->stats
.DL_total_bytes
);
342 printf("%s %5u %10u %9u %10f %8u %9u %10u %10u %9u %10f %8u %9u %10u\n",
343 (tmp
->stats
.rat
== RLC_RAT_LTE
) ? "LTE" : "NR ",
345 tmp
->stats
.UL_frames
,
346 tmp
->stats
.UL_total_bytes
, UL_bw
,
347 tmp
->stats
.UL_total_acks
,
348 tmp
->stats
.UL_total_nacks
,
349 tmp
->stats
.UL_total_missing
,
350 tmp
->stats
.DL_frames
,
351 tmp
->stats
.DL_total_bytes
, DL_bw
,
352 tmp
->stats
.DL_total_acks
,
353 tmp
->stats
.DL_total_nacks
,
354 tmp
->stats
.DL_total_missing
);
361 /* Create a new RLC LTE stats struct */
362 static void rlc_lte_stat_init(const char *opt_arg
, void *userdata _U_
)
365 const char *filter
= NULL
;
366 GString
*error_string
;
368 /* Check for a filter string */
369 if (strncmp(opt_arg
, "rlc-3gpp,stat,", 14) == 0) {
370 /* Skip those characters from filter to display */
371 filter
= opt_arg
+ 14;
378 /* Create top-level struct */
379 hs
= g_new0(rlc_lte_stat_t
, 1);
383 /**********************************************/
384 /* Register the tap listener */
385 /**********************************************/
387 error_string
= register_tap_listener("rlc-3gpp", hs
,
394 g_string_free(error_string
, TRUE
);
402 /* Register this tap listener (need void on own so line register function found) */
403 static stat_tap_ui rlc_lte_stat_ui
= {
404 REGISTER_STAT_GROUP_GENERIC
,
413 register_tap_listener_rlc_lte_stat(void)
415 register_stat_tap_ui(&rlc_lte_stat_ui
, NULL
);