LATER... ei_kerberos_kdc_session_key ...
[wireshark-sm.git] / ui / cli / tap-rlcltestat.c
blob9ebf051b3e60b427a1573767156c7cb740b32ff6
1 /* tap-rlclte_stat.c
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
9 */
12 #include "config.h"
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
18 #include <epan/packet.h>
19 #include <epan/tap.h>
20 #include <epan/stat_tap_ui.h>
22 #include <epan/dissectors/packet-rlc-lte.h>
23 #include <epan/dissectors/packet-rlc-3gpp-common.h>
26 void register_tap_listener_rlc_lte_stat(void);
28 enum {
29 RAT_COLUMN,
30 UEID_COLUMN,
31 UL_FRAMES_COLUMN,
32 UL_BYTES_COLUMN,
33 UL_BW_COLUMN,
34 UL_ACKS_COLUMN,
35 UL_NACKS_COLUMN,
36 UL_MISSING_COLUMN,
37 DL_FRAMES_COLUMN,
38 DL_BYTES_COLUMN,
39 DL_BW_COLUMN,
40 DL_ACKS_COLUMN,
41 DL_NACKS_COLUMN,
42 DL_MISSING_COLUMN,
43 NUM_UE_COLUMNS
46 static const char *ue_titles[] = { "RAT", " UEId",
47 "UL Frames", "UL Bytes", " UL Mbs", "UL ACKs", "UL NACKs", "UL Missed",
48 "DL Frames", "DL Bytes", " DL Mbs", "DL ACKs", "DL NACKs", "DL Missed"};
50 /* Stats for one UE */
51 typedef struct rlc_lte_row_data {
52 /* Key for matching this row */
53 uint8_t rat;
54 uint16_t ueid;
56 bool is_predefined_data;
58 uint32_t UL_frames;
59 uint32_t UL_total_bytes;
60 nstime_t UL_time_start;
61 nstime_t UL_time_stop;
62 uint32_t UL_total_acks;
63 uint32_t UL_total_nacks;
64 uint32_t UL_total_missing;
66 uint32_t DL_frames;
67 uint32_t DL_total_bytes;
68 nstime_t DL_time_start;
69 nstime_t DL_time_stop;
70 uint32_t DL_total_acks;
71 uint32_t DL_total_nacks;
72 uint32_t DL_total_missing;
74 } rlc_lte_row_data;
77 /* Common channel stats (i.e. independent of UEs) */
78 typedef struct rlc_lte_common_stats {
79 uint32_t bcch_frames;
80 uint32_t bcch_bytes;
81 uint32_t pcch_frames;
82 uint32_t pcch_bytes;
83 } rlc_lte_common_stats;
86 /* One row/UE in the UE table */
87 typedef struct rlc_lte_ep {
88 struct rlc_lte_ep *next;
89 struct rlc_lte_row_data stats;
90 } rlc_lte_ep_t;
93 /* Top-level struct for RLC LTE statistics */
94 typedef struct rlc_lte_stat_t {
95 rlc_lte_ep_t *ep_list;
96 uint32_t total_frames;
98 /* Common stats */
99 rlc_lte_common_stats common_stats;
100 } rlc_lte_stat_t;
104 /* Reset RLC stats */
105 static void
106 rlc_lte_stat_reset(void *phs)
108 rlc_lte_stat_t *rlc_lte_stat = (rlc_lte_stat_t *)phs;
109 rlc_lte_ep_t *list = rlc_lte_stat->ep_list;
111 rlc_lte_stat->total_frames = 0;
112 memset(&rlc_lte_stat->common_stats, 0, sizeof(rlc_lte_common_stats));
114 if (!list) {
115 return;
118 rlc_lte_stat->ep_list = NULL;
122 /* Allocate a rlc_lte_ep_t struct to store info for new UE */
123 static rlc_lte_ep_t *alloc_rlc_lte_ep(const struct rlc_3gpp_tap_info *si, packet_info *pinfo _U_)
125 rlc_lte_ep_t *ep;
127 if (!si) {
128 return NULL;
131 if (!(ep = g_new(rlc_lte_ep_t, 1))) {
132 return NULL;
135 /* Copy SI data into ep->stats */
136 ep->stats.rat = si->rat;
137 ep->stats.ueid = si->ueid;
139 /* Counts for new UE are all 0 */
140 ep->stats.UL_frames = 0;
141 ep->stats.DL_frames = 0;
142 ep->stats.UL_total_bytes = 0;
143 ep->stats.DL_total_bytes = 0;
144 memset(&ep->stats.DL_time_start, 0, sizeof(nstime_t));
145 memset(&ep->stats.DL_time_stop, 0, sizeof(nstime_t));
146 ep->stats.UL_total_acks = 0;
147 ep->stats.DL_total_acks = 0;
148 ep->stats.UL_total_nacks = 0;
149 ep->stats.DL_total_nacks = 0;
150 ep->stats.UL_total_missing = 0;
151 ep->stats.DL_total_missing = 0;
153 ep->next = NULL;
155 return ep;
159 /* Process stat struct for a RLC LTE frame */
160 static tap_packet_status
161 rlc_lte_stat_packet(void *phs, packet_info *pinfo, epan_dissect_t *edt _U_,
162 const void *phi, tap_flags_t flags _U_)
164 /* Get reference to stats struct */
165 rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs;
166 rlc_lte_ep_t *tmp = NULL, *te = NULL;
168 /* Cast tap info struct */
169 const struct rlc_3gpp_tap_info *si = (const struct rlc_3gpp_tap_info *)phi;
171 /* Need this */
172 if (!hs) {
173 return TAP_PACKET_DONT_REDRAW;
176 /* Inc top-level frame count */
177 hs->total_frames++;
179 /* Common channel stats */
180 switch (si->channelType) {
181 case CHANNEL_TYPE_BCCH_BCH:
182 case CHANNEL_TYPE_BCCH_DL_SCH:
183 hs->common_stats.bcch_frames++;
184 hs->common_stats.bcch_bytes += si->pduLength;
185 return TAP_PACKET_REDRAW;
187 case CHANNEL_TYPE_PCCH:
188 hs->common_stats.pcch_frames++;
189 hs->common_stats.pcch_bytes += si->pduLength;
190 return TAP_PACKET_REDRAW;
192 default:
193 break;
196 /* For per-UE data, must create a new row if none already existing */
197 if (!hs->ep_list) {
198 /* Allocate new list */
199 hs->ep_list = alloc_rlc_lte_ep(si, pinfo);
200 /* Make it the first/only entry */
201 te = hs->ep_list;
202 } else {
203 /* Look among existing rows for this rat/UEId */
204 /* TODO: with different data structures, could avoid this linear search */
205 for (tmp = hs->ep_list; (tmp != NULL); tmp = tmp->next) {
206 if ((tmp->stats.rat == si->rat) &&
207 (tmp->stats.ueid == si->ueid))
209 te = tmp;
210 break;
214 /* Not found among existing, so create a new one now */
215 if (te == NULL) {
216 if ((te = alloc_rlc_lte_ep(si, pinfo))) {
217 /* Add new item to end of list */
218 rlc_lte_ep_t *p = hs->ep_list;
219 while (p->next) {
220 p = p->next;
222 p->next = te;
223 te->next = NULL;
228 /* Really should have a row pointer by now */
229 if (!te) {
230 return TAP_PACKET_DONT_REDRAW;
233 /* Update entry with details from si */
234 te->stats.ueid = si->ueid;
236 /* Top-level traffic stats */
237 if (si->direction == DIRECTION_UPLINK) {
238 /* Update time range */
239 if (te->stats.UL_frames == 0) {
240 te->stats.UL_time_start = si->rlc_time;
242 te->stats.UL_time_stop = si->rlc_time;
244 te->stats.UL_frames++;
245 te->stats.UL_total_bytes += si->pduLength;
247 else {
248 /* Update time range */
249 if (te->stats.DL_frames == 0) {
250 te->stats.DL_time_start = si->rlc_time;
252 te->stats.DL_time_stop = si->rlc_time;
254 te->stats.DL_frames++;
255 te->stats.DL_total_bytes += si->pduLength;
259 if (si->direction == DIRECTION_UPLINK) {
260 if (si->isControlPDU) {
261 te->stats.UL_total_acks++;
263 te->stats.UL_total_nacks += si->noOfNACKs;
264 te->stats.UL_total_missing += si->missingSNs;
266 else {
267 if (si->isControlPDU) {
268 te->stats.DL_total_acks++;
270 te->stats.DL_total_nacks += si->noOfNACKs;
271 te->stats.DL_total_missing += si->missingSNs;
274 return TAP_PACKET_REDRAW;
278 /* Calculate and return a bandwidth figure, in Mbs */
279 static float calculate_bw(nstime_t *start_time, nstime_t *stop_time, uint32_t bytes)
281 /* Can only calculate bandwidth if have time delta */
282 if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) {
283 float elapsed_ms = (((float)stop_time->secs - (float)start_time->secs) * 1000) +
284 (((float)stop_time->nsecs - (float)start_time->nsecs) / 1000000);
286 /* Only really meaningful if have a few frames spread over time...
287 For now at least avoid dividing by something very close to 0.0 */
288 if (elapsed_ms < 2.0) {
289 return 0.0f;
291 return ((bytes * 8) / elapsed_ms) / 1000;
293 else {
294 return 0.0f;
300 /* (Re)draw RLC stats */
301 static void
302 rlc_lte_stat_draw(void *phs)
304 uint16_t number_of_ues = 0;
305 int i;
307 /* Look up the statistics struct */
308 rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs;
309 rlc_lte_ep_t *list = hs->ep_list, *tmp = 0;
311 /* Common channel data */
312 printf("Common Data:\n");
313 printf("==============\n");
314 printf("BCCH Frames: %u BCCH Bytes: %u PCCH Frames: %u PCCH Bytes: %u\n\n",
315 hs->common_stats.bcch_frames, hs->common_stats.bcch_bytes,
316 hs->common_stats.pcch_frames, hs->common_stats.pcch_bytes);
318 /* Per-UE table entries */
321 /* Set title that shows how many UEs currently in table */
322 for (tmp = list; (tmp!=NULL); tmp=tmp->next, number_of_ues++);
323 printf("Per UE Data - %u UEs (%u frames)\n", number_of_ues, hs->total_frames);
324 printf("==========================================\n");
326 /* Show column titles */
327 for (i=0; i < NUM_UE_COLUMNS; i++) {
328 printf("%s ", ue_titles[i]);
330 printf("\n");
332 /* For each row/UE in the model */
333 for (tmp = list; tmp; tmp=tmp->next) {
334 /* Calculate bandwidth */
335 float UL_bw = calculate_bw(&tmp->stats.UL_time_start,
336 &tmp->stats.UL_time_stop,
337 tmp->stats.UL_total_bytes);
338 float DL_bw = calculate_bw(&tmp->stats.DL_time_start,
339 &tmp->stats.DL_time_stop,
340 tmp->stats.DL_total_bytes);
342 printf("%s %5u %10u %9u %10f %8u %9u %10u %10u %9u %10f %8u %9u %10u\n",
343 (tmp->stats.rat == RLC_RAT_LTE) ? "LTE" : "NR ",
344 tmp->stats.ueid,
345 tmp->stats.UL_frames,
346 tmp->stats.UL_total_bytes, UL_bw,
347 tmp->stats.UL_total_acks,
348 tmp->stats.UL_total_nacks,
349 tmp->stats.UL_total_missing,
350 tmp->stats.DL_frames,
351 tmp->stats.DL_total_bytes, DL_bw,
352 tmp->stats.DL_total_acks,
353 tmp->stats.DL_total_nacks,
354 tmp->stats.DL_total_missing);
361 /* Create a new RLC LTE stats struct */
362 static void rlc_lte_stat_init(const char *opt_arg, void *userdata _U_)
364 rlc_lte_stat_t *hs;
365 const char *filter = NULL;
366 GString *error_string;
368 /* Check for a filter string */
369 if (strncmp(opt_arg, "rlc-3gpp,stat,", 14) == 0) {
370 /* Skip those characters from filter to display */
371 filter = opt_arg + 14;
373 else {
374 /* No filter */
375 filter = NULL;
378 /* Create top-level struct */
379 hs = g_new0(rlc_lte_stat_t, 1);
380 hs->ep_list = NULL;
383 /**********************************************/
384 /* Register the tap listener */
385 /**********************************************/
387 error_string = register_tap_listener("rlc-3gpp", hs,
388 filter, 0,
389 rlc_lte_stat_reset,
390 rlc_lte_stat_packet,
391 rlc_lte_stat_draw,
392 NULL);
393 if (error_string) {
394 g_string_free(error_string, TRUE);
395 g_free(hs);
396 exit(1);
402 /* Register this tap listener (need void on own so line register function found) */
403 static stat_tap_ui rlc_lte_stat_ui = {
404 REGISTER_STAT_GROUP_GENERIC,
405 NULL,
406 "rlc-3gpp,stat",
407 rlc_lte_stat_init,
409 NULL
412 void
413 register_tap_listener_rlc_lte_stat(void)
415 register_stat_tap_ui(&rlc_lte_stat_ui, NULL);