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>
21 #include <epan/dissectors/packet-mac-lte.h>
23 void register_tap_listener_mac_lte_stat(void);
25 /**********************************************/
26 /* Table column identifiers and title strings */
36 UL_PADDING_PERCENT_COLUMN
,
37 UL_RETX_FRAMES_COLUMN
,
41 DL_PADDING_PERCENT_COLUMN
,
43 DL_RETX_FRAMES_COLUMN
,
48 static const char *ue_titles
[] = { "RAT", " RNTI", " Type", "UEId",
49 "UL Frames", "UL Bytes", "UL Mb/sec", " UL Pad %", "UL ReTX",
50 "DL Frames", "DL Bytes", "DL Mb/sec", " DL Pad %", "DL CRC Fail", "DL ReTX"};
53 /* Stats for one UE */
54 typedef struct mac_lte_nr_row_data
{
55 /* Key for matching this row */
61 bool is_predefined_data
;
64 uint32_t UL_raw_bytes
; /* all bytes */
65 uint32_t UL_total_bytes
; /* payload */
66 nstime_t UL_time_start
;
67 nstime_t UL_time_stop
;
68 uint32_t UL_padding_bytes
;
69 uint32_t UL_CRC_errors
;
70 uint32_t UL_retx_frames
;
73 uint32_t DL_raw_bytes
; /* all bytes */
74 uint32_t DL_total_bytes
;
75 nstime_t DL_time_start
;
76 nstime_t DL_time_stop
;
77 uint32_t DL_padding_bytes
;
79 uint32_t DL_CRC_failures
;
80 uint32_t DL_retx_frames
;
82 } mac_lte_nr_row_data
;
85 /* One row/UE list item in the UE table */
86 typedef struct mac_lte_ep
{
87 struct mac_lte_ep
*next
;
88 struct mac_lte_nr_row_data stats
;
92 /* Common channel stats (i.e. independent of UEs) */
93 typedef struct mac_lte_common_stats
{
100 uint32_t pch_paging_ids
;
102 uint32_t rar_entries
;
104 uint16_t max_ul_ues_in_tti
;
105 uint16_t max_dl_ues_in_tti
;
106 } mac_lte_common_stats
;
109 /* Top-level struct for MAC LTE statistics */
110 typedef struct mac_lte_nr_stat_t
{
112 mac_lte_common_stats common_stats
;
114 /* Keep track of unique rntis & ueids. N.B. only used for counting number of UEs - not for lookup */
115 uint8_t used_ueids
[65535];
116 uint8_t used_rntis
[65535];
117 uint16_t number_of_ueids
;
118 uint16_t number_of_rntis
;
120 /* List of UE entries */
121 mac_lte_ep_t
*ep_list
;
125 /* Reset the statistics window */
127 mac_lte_stat_reset(void *phs
)
129 mac_lte_nr_stat_t
*mac_lte_stat
= (mac_lte_nr_stat_t
*)phs
;
130 mac_lte_ep_t
*list
= mac_lte_stat
->ep_list
;
132 /* Reset counts of unique ueids & rntis */
133 memset(mac_lte_stat
->used_ueids
, 0, 65535);
134 mac_lte_stat
->number_of_ueids
= 0;
135 memset(mac_lte_stat
->used_rntis
, 0, 65535);
136 mac_lte_stat
->number_of_rntis
= 0;
138 /* Zero common stats */
139 memset(&(mac_lte_stat
->common_stats
), 0, sizeof(mac_lte_common_stats
));
145 mac_lte_stat
->ep_list
= NULL
;
149 /* Allocate a mac_lte_ep_t struct to store info for new UE */
150 static mac_lte_ep_t
*alloc_mac_lte_ep(const struct mac_3gpp_tap_info
*si
, packet_info
*pinfo _U_
)
158 if (!(ep
= g_new(mac_lte_ep_t
, 1))) {
162 /* Copy SI data into ep->stats */
163 ep
->stats
.rnti
= si
->rnti
;
164 ep
->stats
.rnti_type
= si
->rntiType
;
165 ep
->stats
.ueid
= si
->ueid
;
167 /* Counts for new UE are all 0 */
168 ep
->stats
.UL_frames
= 0;
169 ep
->stats
.DL_frames
= 0;
170 ep
->stats
.UL_total_bytes
= 0;
171 ep
->stats
.UL_raw_bytes
= 0;
172 ep
->stats
.UL_padding_bytes
= 0;
174 ep
->stats
.DL_total_bytes
= 0;
175 ep
->stats
.DL_raw_bytes
= 0;
176 ep
->stats
.DL_padding_bytes
= 0;
178 ep
->stats
.UL_CRC_errors
= 0;
179 ep
->stats
.DL_CRC_failures
= 0;
180 ep
->stats
.UL_retx_frames
= 0;
181 ep
->stats
.DL_retx_frames
= 0;
189 /* Update counts of unique rntis & ueids */
190 static void update_ueid_rnti_counts(uint16_t rnti
, uint16_t ueid
, mac_lte_nr_stat_t
*hs
)
192 if (hs
->number_of_ueids
== 65535 || hs
->number_of_rntis
== 65535) {
193 /* Arrays are already full! */
197 if (!hs
->used_ueids
[ueid
]) {
198 hs
->used_ueids
[ueid
] = true;
199 hs
->number_of_ueids
++;
201 if (!hs
->used_rntis
[rnti
]) {
202 hs
->used_rntis
[rnti
] = true;
203 hs
->number_of_rntis
++;
208 /* Process stat struct for a MAC LTE frame */
209 static tap_packet_status
210 mac_lte_stat_packet(void *phs
, packet_info
*pinfo
, epan_dissect_t
*edt _U_
,
211 const void *phi
, tap_flags_t flags _U_
)
213 /* Get reference to stat window instance */
214 mac_lte_nr_stat_t
*hs
= (mac_lte_nr_stat_t
*)phs
;
215 mac_lte_ep_t
*tmp
= NULL
, *te
= NULL
;
218 /* Cast tap info struct */
219 const struct mac_3gpp_tap_info
*si
= (const struct mac_3gpp_tap_info
*)phi
;
222 return TAP_PACKET_DONT_REDRAW
;
225 hs
->common_stats
.all_frames
++;
227 /* For common channels, just update global counters */
228 switch (si
->rntiType
) {
230 hs
->common_stats
.pch_frames
++;
231 hs
->common_stats
.pch_bytes
+= si
->single_number_of_bytes
;
232 hs
->common_stats
.pch_paging_ids
+= si
->number_of_paging_ids
;
233 return TAP_PACKET_REDRAW
;
235 hs
->common_stats
.sib_frames
++;
236 hs
->common_stats
.sib_bytes
+= si
->single_number_of_bytes
;
237 return TAP_PACKET_REDRAW
;
239 hs
->common_stats
.mib_frames
++;
240 return TAP_PACKET_REDRAW
;
242 hs
->common_stats
.rar_frames
++;
243 hs
->common_stats
.rar_entries
+= si
->number_of_rars
;
244 return TAP_PACKET_REDRAW
;
247 /* Drop through for per-UE update */
252 return TAP_PACKET_DONT_REDRAW
;
255 /* Check/update max UEs/tti counter */
256 switch (si
->direction
) {
257 case DIRECTION_UPLINK
:
258 hs
->common_stats
.max_ul_ues_in_tti
=
259 MAX(hs
->common_stats
.max_ul_ues_in_tti
, si
->ueInTTI
);
261 case DIRECTION_DOWNLINK
:
262 hs
->common_stats
.max_dl_ues_in_tti
=
263 MAX(hs
->common_stats
.max_dl_ues_in_tti
, si
->ueInTTI
);
267 /* For per-UE data, must create a new row if none already existing */
269 /* Allocate new list */
270 hs
->ep_list
= alloc_mac_lte_ep(si
, pinfo
);
271 /* Make it the first/only entry */
274 /* Update counts of unique ueids & rntis */
275 update_ueid_rnti_counts(si
->rnti
, si
->ueid
, hs
);
277 /* Look among existing rows for this RNTI */
278 /* TODO: with different data structures, could avoid this linear search */
279 for (tmp
= hs
->ep_list
; tmp
!= NULL
; tmp
= tmp
->next
) {
280 /* Match only by RAT, RNTI and UEId together */
281 if ((tmp
->stats
.rat
== si
->rat
) &&
282 (tmp
->stats
.rnti
== si
->rnti
) &&
283 (tmp
->stats
.ueid
== si
->ueid
)) {
289 /* Not found among existing, so create a new one now */
291 if ((te
= alloc_mac_lte_ep(si
, pinfo
))) {
292 /* Add new item to end of list */
293 mac_lte_ep_t
*p
= hs
->ep_list
;
300 /* Update counts of unique ueids & rntis */
301 update_ueid_rnti_counts(si
->rnti
, si
->ueid
, hs
);
306 /* Really should have a row pointer by now */
308 return TAP_PACKET_DONT_REDRAW
;
311 /* Update entry with details from si */
312 te
->stats
.rat
= si
->rat
;
313 te
->stats
.rnti
= si
->rnti
;
314 te
->stats
.is_predefined_data
= si
->isPredefinedData
;
317 if (si
->direction
== DIRECTION_UPLINK
) {
319 te
->stats
.UL_retx_frames
++;
320 return TAP_PACKET_REDRAW
;
323 if (si
->crcStatusValid
&& (si
->crcStatus
!= crc_success
)) {
324 te
->stats
.UL_CRC_errors
++;
325 return TAP_PACKET_REDRAW
;
328 /* Update time range */
329 if (te
->stats
.UL_frames
== 0) {
330 te
->stats
.UL_time_start
= si
->mac_time
;
332 te
->stats
.UL_time_stop
= si
->mac_time
;
334 te
->stats
.UL_frames
++;
336 te
->stats
.UL_raw_bytes
+= si
->raw_length
;
337 te
->stats
.UL_padding_bytes
+= si
->padding_bytes
;
339 if (si
->isPredefinedData
) {
340 te
->stats
.UL_total_bytes
+= si
->single_number_of_bytes
;
343 for (i
= 0; i
< MAC_3GPP_DATA_LCID_COUNT_MAX
; i
++) {
344 te
->stats
.UL_total_bytes
+= si
->bytes_for_lcid
[i
];
352 te
->stats
.DL_retx_frames
++;
353 return TAP_PACKET_REDRAW
;
356 if (si
->crcStatusValid
&& (si
->crcStatus
!= crc_success
)) {
357 te
->stats
.DL_CRC_failures
++;
358 return TAP_PACKET_REDRAW
;
361 /* Update time range */
362 if (te
->stats
.DL_frames
== 0) {
363 te
->stats
.DL_time_start
= si
->mac_time
;
365 te
->stats
.DL_time_stop
= si
->mac_time
;
367 te
->stats
.DL_frames
++;
369 te
->stats
.DL_raw_bytes
+= si
->raw_length
;
370 te
->stats
.DL_padding_bytes
+= si
->padding_bytes
;
372 if (si
->isPredefinedData
) {
373 te
->stats
.DL_total_bytes
+= si
->single_number_of_bytes
;
376 for (i
= 0; i
< MAC_3GPP_DATA_LCID_COUNT_MAX
; i
++) {
377 te
->stats
.DL_total_bytes
+= si
->bytes_for_lcid
[i
];
383 return TAP_PACKET_REDRAW
;
387 /* Calculate and return a bandwidth figure, in Mbs */
388 static float calculate_bw(nstime_t
*start_time
, nstime_t
*stop_time
, uint32_t bytes
)
390 /* Can only calculate bandwidth if have time delta */
391 if (memcmp(start_time
, stop_time
, sizeof(nstime_t
)) != 0) {
392 float elapsed_ms
= (((float)stop_time
->secs
- (float)start_time
->secs
) * 1000) +
393 (((float)stop_time
->nsecs
- (float)start_time
->nsecs
) / 1000000);
395 /* Only really meaningful if have a few frames spread over time...
396 For now at least avoid dividing by something very close to 0.0 */
397 if (elapsed_ms
< 2.0) {
400 return ((bytes
* 8) / elapsed_ms
) / 1000;
409 /* Output the accumulated stats */
411 mac_lte_stat_draw(void *phs
)
414 uint16_t number_of_ues
= 0;
416 /* Deref the struct */
417 mac_lte_nr_stat_t
*hs
= (mac_lte_nr_stat_t
*)phs
;
418 mac_lte_ep_t
*list
= hs
->ep_list
, *tmp
= 0;
421 printf("System data:\n");
422 printf("============\n");
423 printf("Max UL UEs/TTI: %u Max DL UEs/TTI: %u\n\n",
424 hs
->common_stats
.max_ul_ues_in_tti
, hs
->common_stats
.max_dl_ues_in_tti
);
426 /* Common channel data */
427 printf("Common channel data:\n");
428 printf("====================\n");
429 printf("MIBs: %u ", hs
->common_stats
.mib_frames
);
430 printf("SIB Frames: %u ", hs
->common_stats
.sib_frames
);
431 printf("SIB Bytes: %u ", hs
->common_stats
.sib_bytes
);
432 printf("PCH Frames: %u ", hs
->common_stats
.pch_frames
);
433 printf("PCH Bytes: %u ", hs
->common_stats
.pch_bytes
);
434 printf("PCH Paging IDs: %u ", hs
->common_stats
.pch_paging_ids
);
435 printf("RAR Frames: %u ", hs
->common_stats
.rar_frames
);
436 printf("RAR Entries: %u\n\n", hs
->common_stats
.rar_entries
);
439 /* Per-UE table entries */
441 /* Set title to show how many UEs in table */
442 for (tmp
= list
; (tmp
!=NULL
); tmp
=tmp
->next
, number_of_ues
++);
443 printf("UL/DL-SCH data (%u entries - %u unique RNTIs, %u unique UEIds):\n",
444 number_of_ues
, hs
->number_of_rntis
, hs
->number_of_ueids
);
445 printf("==================================================================\n");
447 /* Show column titles */
448 for (i
=0; i
< NUM_UE_COLUMNS
; i
++) {
449 printf("%s ", ue_titles
[i
]);
453 /* Write a row for each UE */
454 for (tmp
= list
; tmp
; tmp
=tmp
->next
) {
455 /* Calculate bandwidth */
456 float UL_bw
= calculate_bw(&tmp
->stats
.UL_time_start
,
457 &tmp
->stats
.UL_time_stop
,
458 tmp
->stats
.UL_total_bytes
);
459 float DL_bw
= calculate_bw(&tmp
->stats
.DL_time_start
,
460 &tmp
->stats
.DL_time_stop
,
461 tmp
->stats
.DL_total_bytes
);
463 printf("%s %5u %7s %5u %10u %9u %10f %10f %8u %10u %9u %10f %10f %12u %8u\n",
464 (tmp
->stats
.rat
== MAC_RAT_LTE
) ? "LTE " : "NR ",
466 (tmp
->stats
.rnti_type
== C_RNTI
) ? "C-RNTI" : "SPS-RNTI",
468 tmp
->stats
.UL_frames
,
469 tmp
->stats
.UL_total_bytes
,
471 tmp
->stats
.UL_raw_bytes
?
472 (((float)tmp
->stats
.UL_padding_bytes
/ (float)tmp
->stats
.UL_raw_bytes
) * 100.0) :
474 tmp
->stats
.UL_retx_frames
,
475 tmp
->stats
.DL_frames
,
476 tmp
->stats
.DL_total_bytes
,
478 tmp
->stats
.DL_raw_bytes
?
479 (((float)tmp
->stats
.DL_padding_bytes
/ (float)tmp
->stats
.DL_raw_bytes
) * 100.0) :
481 tmp
->stats
.DL_CRC_failures
,
482 tmp
->stats
.DL_retx_frames
);
486 /* Create a new MAC LTE stats struct */
487 static void mac_lte_stat_init(const char *opt_arg
, void *userdata _U_
)
489 mac_lte_nr_stat_t
*hs
;
490 const char *filter
= NULL
;
491 GString
*error_string
;
493 /* Check for a filter string */
494 if (strncmp(opt_arg
, "mac-3gpp,stat,", 14) == 0) {
495 /* Skip those characters from filter to display */
496 filter
= opt_arg
+ 14;
504 hs
= g_new0(mac_lte_nr_stat_t
, 1);
507 error_string
= register_tap_listener("mac-3gpp", hs
,
514 g_string_free(error_string
, TRUE
);
520 static stat_tap_ui mac_lte_stat_ui
= {
521 REGISTER_STAT_GROUP_GENERIC
,
529 /* Register this tap listener (need void on own so line register function found) */
531 register_tap_listener_mac_lte_stat(void)
533 register_stat_tap_ui(&mac_lte_stat_ui
, NULL
);