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