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