1 /* tap-macltestat.c
2 * Copyright 2011 Martin Mathieson
3 *
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11
12 #include "config.h"
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17
18 #include <epan/packet.h>
19 #include <epan/tap.h>
20 #include <epan/stat_tap_ui.h>
21 #include <epan/dissectors/packet-mac-lte.h>
22
23 void register_tap_listener_mac_lte_stat(void);
24
25 /**********************************************/
26 /* Table column identifiers and title strings */
27
28 enum {
29 RNTI_COLUMN,
30 RNTI_TYPE_COLUMN,
31 UEID_COLUMN,
32 UL_FRAMES_COLUMN,
33 UL_BYTES_COLUMN,
34 UL_BW_COLUMN,
35 UL_PADDING_PERCENT_COLUMN,
36 UL_RETX_FRAMES_COLUMN,
37 DL_FRAMES_COLUMN,
38 DL_BYTES_COLUMN,
39 DL_BW_COLUMN,
40 DL_PADDING_PERCENT_COLUMN,
41 DL_CRC_FAILED_COLUMN,
42 DL_CRC_HIGH_CODE_RATE_COLUMN,
43 DL_CRC_PDSCH_LOST_COLUMN,
44 DL_CRC_DUPLICATE_NONZERO_RV_COLUMN,
45 DL_RETX_FRAMES_COLUMN,
46 NUM_UE_COLUMNS
47 };
48
49
50 static const gchar *ue_titles[] = { " RNTI", " Type", "UEId",
51 "UL Frames", "UL Bytes", "UL Mb/sec", " UL Pad %", "UL ReTX",
52 "DL Frames", "DL Bytes", "DL Mb/sec", " DL Pad %", "DL CRC Fail", "DL CRC HCR", "DL CRC PDSCH Lost", "DL CRC DupNonZeroRV", "DL ReTX"};
53
54
55 /* Stats for one UE */
56 typedef struct mac_lte_row_data {
57 /* Key for matching this row */
58 guint16 rnti;
59 guint8 rnti_type;
60 guint16 ueid;
61
62 gboolean is_predefined_data;
63
64 guint32 UL_frames;
65 guint32 UL_raw_bytes; /* all bytes */
66 guint32 UL_total_bytes; /* payload */
67 nstime_t UL_time_start;
68 nstime_t UL_time_stop;
69 guint32 UL_padding_bytes;
70 guint32 UL_CRC_errors;
71 guint32 UL_retx_frames;
72
73 guint32 DL_frames;
74 guint32 DL_raw_bytes; /* all bytes */
75 guint32 DL_total_bytes;
76 nstime_t DL_time_start;
77 nstime_t DL_time_stop;
78 guint32 DL_padding_bytes;
79
80 guint32 DL_CRC_failures;
81 guint32 DL_CRC_high_code_rate;
82 guint32 DL_CRC_PDSCH_lost;
83 guint32 DL_CRC_Duplicate_NonZero_RV;
84 guint32 DL_retx_frames;
85
86 } mac_lte_row_data;
87
88
89 /* One row/UE in the UE table */
90 typedef struct mac_lte_ep {
91 struct mac_lte_ep *next;
92 struct mac_lte_row_data stats;
93 } mac_lte_ep_t;
94
95
96 /* Common channel stats */
97 typedef struct mac_lte_common_stats {
98 guint32 all_frames;
99 guint32 mib_frames;
100 guint32 sib_frames;
101 guint32 sib_bytes;
102 guint32 pch_frames;
103 guint32 pch_bytes;
104 guint32 pch_paging_ids;
105 guint32 rar_frames;
106 guint32 rar_entries;
107
108 guint16 max_ul_ues_in_tti;
109 guint16 max_dl_ues_in_tti;
110 } mac_lte_common_stats;
111
112
113 /* Top-level struct for MAC LTE statistics */
114 typedef struct mac_lte_stat_t {
115 /* Common stats */
116 mac_lte_common_stats common_stats;
117
118 /* Keep track of unique rntis & ueids */
119 guint8 used_ueids[65535];
120 guint8 used_rntis[65535];
121 guint16 number_of_ueids;
122 guint16 number_of_rntis;
123
124 mac_lte_ep_t *ep_list;
125 } mac_lte_stat_t;
126
127
128 /* Reset the statistics window */
129 static void
mac_lte_stat_reset(void * phs)130 mac_lte_stat_reset(void *phs)
131 {
132 mac_lte_stat_t *mac_lte_stat = (mac_lte_stat_t *)phs;
133 mac_lte_ep_t *list = mac_lte_stat->ep_list;
134
135 /* Reset counts of unique ueids & rntis */
136 memset(mac_lte_stat->used_ueids, 0, 65535);
137 mac_lte_stat->number_of_ueids = 0;
138 memset(mac_lte_stat->used_rntis, 0, 65535);
139 mac_lte_stat->number_of_rntis = 0;
140
141 /* Zero common stats */
142 memset(&(mac_lte_stat->common_stats), 0, sizeof(mac_lte_common_stats));
143
144 if (!list) {
145 return;
146 }
147
148 mac_lte_stat->ep_list = NULL;
149 }
150
151
152 /* Allocate a mac_lte_ep_t struct to store info for new UE */
alloc_mac_lte_ep(const struct mac_lte_tap_info * si,packet_info * pinfo _U_)153 static mac_lte_ep_t *alloc_mac_lte_ep(const struct mac_lte_tap_info *si, packet_info *pinfo _U_)
154 {
155 mac_lte_ep_t *ep;
156
157 if (!si) {
158 return NULL;
159 }
160
161 if (!(ep = g_new(mac_lte_ep_t, 1))) {
162 return NULL;
163 }
164
165 /* Copy SI data into ep->stats */
166 ep->stats.rnti = si->rnti;
167 ep->stats.rnti_type = si->rntiType;
168 ep->stats.ueid = si->ueid;
169
170 /* Counts for new UE are all 0 */
171 ep->stats.UL_frames = 0;
172 ep->stats.DL_frames = 0;
173 ep->stats.UL_total_bytes = 0;
174 ep->stats.UL_raw_bytes = 0;
175 ep->stats.UL_padding_bytes = 0;
176
177 ep->stats.DL_total_bytes = 0;
178 ep->stats.DL_raw_bytes = 0;
179 ep->stats.DL_padding_bytes = 0;
180
181 ep->stats.UL_CRC_errors = 0;
182 ep->stats.DL_CRC_failures = 0;
183 ep->stats.DL_CRC_high_code_rate = 0;
184 ep->stats.DL_CRC_PDSCH_lost = 0;
185 ep->stats.DL_CRC_Duplicate_NonZero_RV = 0;
186 ep->stats.UL_retx_frames = 0;
187 ep->stats.DL_retx_frames = 0;
188
189 ep->next = NULL;
190
191 return ep;
192 }
193
194
195 /* Update counts of unique rntis & ueids */
update_ueid_rnti_counts(guint16 rnti,guint16 ueid,mac_lte_stat_t * hs)196 static void update_ueid_rnti_counts(guint16 rnti, guint16 ueid, mac_lte_stat_t *hs)
197 {
198 if (!hs->used_ueids[ueid]) {
199 hs->used_ueids[ueid] = TRUE;
200 hs->number_of_ueids++;
201 }
202 if (!hs->used_rntis[rnti]) {
203 hs->used_rntis[rnti] = TRUE;
204 hs->number_of_rntis++;
205 }
206 }
207
208
209 /* Process stat struct for a MAC LTE frame */
210 static tap_packet_status
mac_lte_stat_packet(void * phs,packet_info * pinfo,epan_dissect_t * edt _U_,const void * phi)211 mac_lte_stat_packet(void *phs, packet_info *pinfo, epan_dissect_t *edt _U_,
212 const void *phi)
213 {
214 /* Get reference to stat window instance */
215 mac_lte_stat_t *hs = (mac_lte_stat_t *)phs;
216 mac_lte_ep_t *tmp = NULL, *te = NULL;
217 int i;
218
219 /* Cast tap info struct */
220 const struct mac_lte_tap_info *si = (const struct mac_lte_tap_info *)phi;
221
222 if (!hs) {
223 return TAP_PACKET_DONT_REDRAW;
224 }
225
226 hs->common_stats.all_frames++;
227
228 /* For common channels, just update global counters */
229 switch (si->rntiType) {
230 case P_RNTI:
231 hs->common_stats.pch_frames++;
232 hs->common_stats.pch_bytes += si->single_number_of_bytes;
233 hs->common_stats.pch_paging_ids += si->number_of_paging_ids;
234 return TAP_PACKET_REDRAW;
235 case SI_RNTI:
236 hs->common_stats.sib_frames++;
237 hs->common_stats.sib_bytes += si->single_number_of_bytes;
238 return TAP_PACKET_REDRAW;
239 case NO_RNTI:
240 hs->common_stats.mib_frames++;
241 return TAP_PACKET_REDRAW;
242 case RA_RNTI:
243 hs->common_stats.rar_frames++;
244 hs->common_stats.rar_entries += si->number_of_rars;
245 return TAP_PACKET_REDRAW;
246 case C_RNTI:
247 case SPS_RNTI:
248 /* Drop through for per-UE update */
249 break;
250
251 default:
252 /* Error */
253 return TAP_PACKET_DONT_REDRAW;
254 }
255
256 /* Check max UEs/tti counter */
257 switch (si->direction) {
258 case DIRECTION_UPLINK:
259 hs->common_stats.max_ul_ues_in_tti =
260 MAX(hs->common_stats.max_ul_ues_in_tti, si->ueInTTI);
261 break;
262 case DIRECTION_DOWNLINK:
263 hs->common_stats.max_dl_ues_in_tti =
264 MAX(hs->common_stats.max_dl_ues_in_tti, si->ueInTTI);
265 break;
266 }
267
268 /* For per-UE data, must create a new row if none already existing */
269 if (!hs->ep_list) {
270 /* Allocate new list */
271 hs->ep_list = alloc_mac_lte_ep(si, pinfo);
272 /* Make it the first/only entry */
273 te = hs->ep_list;
274
275 /* Update counts of unique ueids & rntis */
276 update_ueid_rnti_counts(si->rnti, si->ueid, hs);
277 } else {
278 /* Look among existing rows for this RNTI */
279 for (tmp = hs->ep_list;(tmp != NULL); tmp = tmp->next) {
280 /* Match only by RNTI and UEId together */
281 if ((tmp->stats.rnti == si->rnti) &&
282 (tmp->stats.ueid == si->ueid)) {
283 te = tmp;
284 break;
285 }
286 }
287
288 /* Not found among existing, so create a new one anyway */
289 if (te == NULL) {
290 if ((te = alloc_mac_lte_ep(si, pinfo))) {
291 /* Add new item to end of list */
292 mac_lte_ep_t *p = hs->ep_list;
293 while (p->next) {
294 p = p->next;
295 }
296 p->next = te;
297 te->next = NULL;
298
299 /* Update counts of unique ueids & rntis */
300 update_ueid_rnti_counts(si->rnti, si->ueid, hs);
301 }
302 }
303 }
304
305 /* Really should have a row pointer by now */
306 if (!te) {
307 return TAP_PACKET_DONT_REDRAW;
308 }
309
310 /* Update entry with details from si */
311 te->stats.rnti = si->rnti;
312 te->stats.is_predefined_data = si->isPredefinedData;
313
314 /* Uplink */
315 if (si->direction == DIRECTION_UPLINK) {
316 if (si->isPHYRetx) {
317 te->stats.UL_retx_frames++;
318 return TAP_PACKET_REDRAW;
319 }
320
321 if (si->crcStatusValid && (si->crcStatus != crc_success)) {
322 te->stats.UL_CRC_errors++;
323 return TAP_PACKET_REDRAW;
324 }
325
326 /* Update time range */
327 if (te->stats.UL_frames == 0) {
328 te->stats.UL_time_start = si->mac_lte_time;
329 }
330 te->stats.UL_time_stop = si->mac_lte_time;
331
332 te->stats.UL_frames++;
333
334 te->stats.UL_raw_bytes += si->raw_length;
335 te->stats.UL_padding_bytes += si->padding_bytes;
336
337 if (si->isPredefinedData) {
338 te->stats.UL_total_bytes += si->single_number_of_bytes;
339 }
340 else {
341 for (i = 0; i < MAC_LTE_DATA_LCID_COUNT_MAX; i++) {
342 te->stats.UL_total_bytes += si->bytes_for_lcid[i];
343 }
344 }
345 }
346
347 /* Downlink */
348 else {
349 if (si->isPHYRetx) {
350 te->stats.DL_retx_frames++;
351 return TAP_PACKET_REDRAW;
352 }
353
354 if (si->crcStatusValid && (si->crcStatus != crc_success)) {
355 switch (si->crcStatus) {
356 case crc_fail:
357 te->stats.DL_CRC_failures++;
358 break;
359 case crc_high_code_rate:
360 te->stats.DL_CRC_high_code_rate++;
361 break;
362 case crc_pdsch_lost:
363 te->stats.DL_CRC_PDSCH_lost++;
364 break;
365 case crc_duplicate_nonzero_rv:
366 te->stats.DL_CRC_Duplicate_NonZero_RV++;
367 break;
368
369 default:
370 /* Something went wrong! */
371 break;
372 }
373 return TAP_PACKET_REDRAW;
374 }
375
376 /* Update time range */
377 if (te->stats.DL_frames == 0) {
378 te->stats.DL_time_start = si->mac_lte_time;
379 }
380 te->stats.DL_time_stop = si->mac_lte_time;
381
382 te->stats.DL_frames++;
383
384 te->stats.DL_raw_bytes += si->raw_length;
385 te->stats.DL_padding_bytes += si->padding_bytes;
386
387 if (si->isPredefinedData) {
388 te->stats.DL_total_bytes += si->single_number_of_bytes;
389 }
390 else {
391 for (i = 0; i < MAC_LTE_DATA_LCID_COUNT_MAX; i++) {
392 te->stats.DL_total_bytes += si->bytes_for_lcid[i];
393 }
394 }
395
396 }
397
398 return TAP_PACKET_REDRAW;
399 }
400
401
402 /* Calculate and return a bandwidth figure, in Mbs */
calculate_bw(nstime_t * start_time,nstime_t * stop_time,guint32 bytes)403 static float calculate_bw(nstime_t *start_time, nstime_t *stop_time, guint32 bytes)
404 {
405 /* Can only calculate bandwidth if have time delta */
406 if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) {
407 float elapsed_ms = (((float)stop_time->secs - (float)start_time->secs) * 1000) +
408 (((float)stop_time->nsecs - (float)start_time->nsecs) / 1000000);
409
410 /* Only really meaningful if have a few frames spread over time...
411 For now at least avoid dividing by something very close to 0.0 */
412 if (elapsed_ms < 2.0) {
413 return 0.0f;
414 }
415 return ((bytes * 8) / elapsed_ms) / 1000;
416 }
417 else {
418 return 0.0f;
419 }
420 }
421
422
423
424 /* Output the accumulated stats */
425 static void
mac_lte_stat_draw(void * phs)426 mac_lte_stat_draw(void *phs)
427 {
428 gint i;
429 guint16 number_of_ues = 0;
430
431 /* Deref the struct */
432 mac_lte_stat_t *hs = (mac_lte_stat_t *)phs;
433 mac_lte_ep_t *list = hs->ep_list, *tmp = 0;
434
435 /* System data */
436 printf("System data:\n");
437 printf("============\n");
438 printf("Max UL UEs/TTI: %u Max DL UEs/TTI: %u\n\n",
439 hs->common_stats.max_ul_ues_in_tti, hs->common_stats.max_dl_ues_in_tti);
440
441 /* Common channel data */
442 printf("Common channel data:\n");
443 printf("====================\n");
444 printf("MIBs: %u ", hs->common_stats.mib_frames);
445 printf("SIB Frames: %u ", hs->common_stats.sib_frames);
446 printf("SIB Bytes: %u ", hs->common_stats.sib_bytes);
447 printf("PCH Frames: %u ", hs->common_stats.pch_frames);
448 printf("PCH Bytes: %u ", hs->common_stats.pch_bytes);
449 printf("PCH Paging IDs: %u ", hs->common_stats.pch_paging_ids);
450 printf("RAR Frames: %u ", hs->common_stats.rar_frames);
451 printf("RAR Entries: %u\n\n", hs->common_stats.rar_entries);
452
453
454 /* Per-UE table entries */
455
456 /* Set title to show how many UEs in table */
457 for (tmp = list; (tmp!=NULL); tmp=tmp->next, number_of_ues++);
458 printf("UL/DL-SCH data (%u entries - %u unique RNTIs, %u unique UEIds):\n",
459 number_of_ues, hs->number_of_rntis, hs->number_of_ueids);
460 printf("==================================================================\n");
461
462 /* Show column titles */
463 for (i=0; i < NUM_UE_COLUMNS; i++) {
464 printf("%s ", ue_titles[i]);
465 }
466 printf("\n");
467
468 /* Write a row for each UE */
469 for (tmp = list; tmp; tmp=tmp->next) {
470 /* Calculate bandwidth */
471 float UL_bw = calculate_bw(&tmp->stats.UL_time_start,
472 &tmp->stats.UL_time_stop,
473 tmp->stats.UL_total_bytes);
474 float DL_bw = calculate_bw(&tmp->stats.DL_time_start,
475 &tmp->stats.DL_time_stop,
476 tmp->stats.DL_total_bytes);
477
478 printf("%5u %7s %5u %10u %9u %10f %10f %8u %10u %9u %10f %10f %12u %11u %18u %20u %8u\n",
479 tmp->stats.rnti,
480 (tmp->stats.rnti_type == C_RNTI) ? "C-RNTI" : "SPS-RNTI",
481 tmp->stats.ueid,
482 tmp->stats.UL_frames,
483 tmp->stats.UL_total_bytes,
484 UL_bw,
485 tmp->stats.UL_raw_bytes ?
486 (((float)tmp->stats.UL_padding_bytes / (float)tmp->stats.UL_raw_bytes) * 100.0) :
487 0.0,
488 tmp->stats.UL_retx_frames,
489 tmp->stats.DL_frames,
490 tmp->stats.DL_total_bytes,
491 DL_bw,
492 tmp->stats.DL_raw_bytes ?
493 (((float)tmp->stats.DL_padding_bytes / (float)tmp->stats.DL_raw_bytes) * 100.0) :
494 0.0,
495 tmp->stats.DL_CRC_failures,
496 tmp->stats.DL_CRC_high_code_rate,
497 tmp->stats.DL_CRC_PDSCH_lost,
498 tmp->stats.DL_CRC_Duplicate_NonZero_RV,
499 tmp->stats.DL_retx_frames);
500 }
501 }
502
503 /* Create a new MAC LTE stats struct */
mac_lte_stat_init(const char * opt_arg,void * userdata _U_)504 static void mac_lte_stat_init(const char *opt_arg, void *userdata _U_)
505 {
506 mac_lte_stat_t *hs;
507 const char *filter = NULL;
508 GString *error_string;
509
510 /* Check for a filter string */
511 if (strncmp(opt_arg, "mac-lte,stat,", 13) == 0) {
512 /* Skip those characters from filter to display */
513 filter = opt_arg + 13;
514 }
515 else {
516 /* No filter */
517 filter = NULL;
518 }
519
520 /* Create struct */
521 hs = g_new0(mac_lte_stat_t, 1);
522 hs->ep_list = NULL;
523
524 error_string = register_tap_listener("mac-lte", hs,
525 filter, 0,
526 mac_lte_stat_reset,
527 mac_lte_stat_packet,
528 mac_lte_stat_draw,
529 NULL);
530 if (error_string) {
531 g_string_free(error_string, TRUE);
532 g_free(hs);
533 exit(1);
534 }
535 }
536
537 static stat_tap_ui mac_lte_stat_ui = {
538 REGISTER_STAT_GROUP_GENERIC,
539 NULL,
540 "mac-lte,stat",
541 mac_lte_stat_init,
542 0,
543 NULL
544 };
545
546 /* Register this tap listener (need void on own so line register function found) */
547 void
register_tap_listener_mac_lte_stat(void)548 register_tap_listener_mac_lte_stat(void)
549 {
550 register_stat_tap_ui(&mac_lte_stat_ui, NULL);
551 }
552