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