diff options
Diffstat (limited to 'ui/cli/tap-rlcltestat.c')
-rw-r--r-- | ui/cli/tap-rlcltestat.c | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/ui/cli/tap-rlcltestat.c b/ui/cli/tap-rlcltestat.c new file mode 100644 index 00000000..9f4a2cc9 --- /dev/null +++ b/ui/cli/tap-rlcltestat.c @@ -0,0 +1,406 @@ +/* tap-rlclte_stat.c + * Copyright 2011 Martin Mathieson + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <epan/packet.h> +#include <epan/tap.h> +#include <epan/stat_tap_ui.h> +#include <epan/dissectors/packet-rlc-lte.h> + +void register_tap_listener_rlc_lte_stat(void); + +enum { + UEID_COLUMN, + UL_FRAMES_COLUMN, + UL_BYTES_COLUMN, + UL_BW_COLUMN, + UL_ACKS_COLUMN, + UL_NACKS_COLUMN, + UL_MISSING_COLUMN, + DL_FRAMES_COLUMN, + DL_BYTES_COLUMN, + DL_BW_COLUMN, + DL_ACKS_COLUMN, + DL_NACKS_COLUMN, + DL_MISSING_COLUMN, + NUM_UE_COLUMNS +}; + +static const gchar *ue_titles[] = { " UEId", + "UL Frames", "UL Bytes", " UL Mbs", "UL ACKs", "UL NACKs", "UL Missed", + "DL Frames", "DL Bytes", " DL Mbs", "DL ACKs", "DL NACKs", "DL Missed"}; + +/* Stats for one UE */ +typedef struct rlc_lte_row_data { + /* Key for matching this row */ + guint16 ueid; + + gboolean is_predefined_data; + + guint32 UL_frames; + guint32 UL_total_bytes; + nstime_t UL_time_start; + nstime_t UL_time_stop; + guint32 UL_total_acks; + guint32 UL_total_nacks; + guint32 UL_total_missing; + + guint32 DL_frames; + guint32 DL_total_bytes; + nstime_t DL_time_start; + nstime_t DL_time_stop; + guint32 DL_total_acks; + guint32 DL_total_nacks; + guint32 DL_total_missing; + +} rlc_lte_row_data; + + +/* Common channel stats */ +typedef struct rlc_lte_common_stats { + guint32 bcch_frames; + guint32 bcch_bytes; + guint32 pcch_frames; + guint32 pcch_bytes; +} rlc_lte_common_stats; + + +/* One row/UE in the UE table */ +typedef struct rlc_lte_ep { + struct rlc_lte_ep *next; + struct rlc_lte_row_data stats; +} rlc_lte_ep_t; + + +/* Used to keep track of all RLC LTE statistics */ +typedef struct rlc_lte_stat_t { + rlc_lte_ep_t *ep_list; + guint32 total_frames; + + /* Common stats */ + rlc_lte_common_stats common_stats; +} rlc_lte_stat_t; + + + +/* Reset RLC stats */ +static void +rlc_lte_stat_reset(void *phs) +{ + rlc_lte_stat_t *rlc_lte_stat = (rlc_lte_stat_t *)phs; + rlc_lte_ep_t *list = rlc_lte_stat->ep_list; + + rlc_lte_stat->total_frames = 0; + memset(&rlc_lte_stat->common_stats, 0, sizeof(rlc_lte_common_stats)); + + if (!list) { + return; + } + + rlc_lte_stat->ep_list = NULL; +} + + +/* Allocate a rlc_lte_ep_t struct to store info for new UE */ +static rlc_lte_ep_t *alloc_rlc_lte_ep(const struct rlc_lte_tap_info *si, packet_info *pinfo _U_) +{ + rlc_lte_ep_t *ep; + + if (!si) { + return NULL; + } + + if (!(ep = g_new(rlc_lte_ep_t, 1))) { + return NULL; + } + + /* Copy SI data into ep->stats */ + ep->stats.ueid = si->ueid; + + /* Counts for new UE are all 0 */ + ep->stats.UL_frames = 0; + ep->stats.DL_frames = 0; + ep->stats.UL_total_bytes = 0; + ep->stats.DL_total_bytes = 0; + memset(&ep->stats.DL_time_start, 0, sizeof(nstime_t)); + memset(&ep->stats.DL_time_stop, 0, sizeof(nstime_t)); + ep->stats.UL_total_acks = 0; + ep->stats.DL_total_acks = 0; + ep->stats.UL_total_nacks = 0; + ep->stats.DL_total_nacks = 0; + ep->stats.UL_total_missing = 0; + ep->stats.DL_total_missing = 0; + + ep->next = NULL; + + return ep; +} + + +/* Process stat struct for a RLC LTE frame */ +static tap_packet_status +rlc_lte_stat_packet(void *phs, packet_info *pinfo, epan_dissect_t *edt _U_, + const void *phi, tap_flags_t flags _U_) +{ + /* Get reference to stats struct */ + rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs; + rlc_lte_ep_t *tmp = NULL, *te = NULL; + + /* Cast tap info struct */ + const struct rlc_lte_tap_info *si = (const struct rlc_lte_tap_info *)phi; + + /* Need this */ + if (!hs) { + return TAP_PACKET_DONT_REDRAW; + } + + /* Inc top-level frame count */ + hs->total_frames++; + + /* Common channel stats */ + switch (si->channelType) { + case CHANNEL_TYPE_BCCH_BCH: + case CHANNEL_TYPE_BCCH_DL_SCH: + hs->common_stats.bcch_frames++; + hs->common_stats.bcch_bytes += si->pduLength; + return TAP_PACKET_REDRAW; + + case CHANNEL_TYPE_PCCH: + hs->common_stats.pcch_frames++; + hs->common_stats.pcch_bytes += si->pduLength; + return TAP_PACKET_REDRAW; + + default: + break; + } + + /* For per-UE data, must create a new row if none already existing */ + if (!hs->ep_list) { + /* Allocate new list */ + hs->ep_list = alloc_rlc_lte_ep(si, pinfo); + /* Make it the first/only entry */ + te = hs->ep_list; + } else { + /* Look among existing rows for this UEId */ + for (tmp = hs->ep_list; (tmp != NULL); tmp = tmp->next) { + if (tmp->stats.ueid == si->ueid) { + te = tmp; + break; + } + } + + /* Not found among existing, so create a new one anyway */ + if (te == NULL) { + if ((te = alloc_rlc_lte_ep(si, pinfo))) { + /* Add new item to end of list */ + rlc_lte_ep_t *p = hs->ep_list; + while (p->next) { + p = p->next; + } + p->next = te; + te->next = NULL; + } + } + } + + /* Really should have a row pointer by now */ + if (!te) { + return TAP_PACKET_DONT_REDRAW; + } + + /* Update entry with details from si */ + te->stats.ueid = si->ueid; + + /* Top-level traffic stats */ + if (si->direction == DIRECTION_UPLINK) { + /* Update time range */ + if (te->stats.UL_frames == 0) { + te->stats.UL_time_start = si->rlc_lte_time; + } + te->stats.UL_time_stop = si->rlc_lte_time; + + te->stats.UL_frames++; + te->stats.UL_total_bytes += si->pduLength; + } + else { + /* Update time range */ + if (te->stats.DL_frames == 0) { + te->stats.DL_time_start = si->rlc_lte_time; + } + te->stats.DL_time_stop = si->rlc_lte_time; + + te->stats.DL_frames++; + te->stats.DL_total_bytes += si->pduLength; + } + + + if (si->direction == DIRECTION_UPLINK) { + if (si->isControlPDU) { + te->stats.UL_total_acks++; + } + te->stats.UL_total_nacks += si->noOfNACKs; + te->stats.UL_total_missing += si->missingSNs; + } + else { + if (si->isControlPDU) { + te->stats.DL_total_acks++; + } + te->stats.DL_total_nacks += si->noOfNACKs; + te->stats.DL_total_missing += si->missingSNs; + } + + return TAP_PACKET_REDRAW; +} + + +/* Calculate and return a bandwidth figure, in Mbs */ +static float calculate_bw(nstime_t *start_time, nstime_t *stop_time, guint32 bytes) +{ + /* Can only calculate bandwidth if have time delta */ + if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) { + float elapsed_ms = (((float)stop_time->secs - (float)start_time->secs) * 1000) + + (((float)stop_time->nsecs - (float)start_time->nsecs) / 1000000); + + /* Only really meaningful if have a few frames spread over time... + For now at least avoid dividing by something very close to 0.0 */ + if (elapsed_ms < 2.0) { + return 0.0f; + } + return ((bytes * 8) / elapsed_ms) / 1000; + } + else { + return 0.0f; + } +} + + + +/* (Re)draw RLC stats */ +static void +rlc_lte_stat_draw(void *phs) +{ + guint16 number_of_ues = 0; + gint i; + + /* Look up the statistics struct */ + rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs; + rlc_lte_ep_t *list = hs->ep_list, *tmp = 0; + + /* Common channel data */ + printf("Common Data:\n"); + printf("==============\n"); + printf("BCCH Frames: %u BCCH Bytes: %u PCCH Frames: %u PCCH Bytes: %u\n\n", + hs->common_stats.bcch_frames, hs->common_stats.bcch_bytes, + hs->common_stats.pcch_frames, hs->common_stats.pcch_bytes); + + /* Per-UE table entries */ + + + /* Set title that shows how many UEs currently in table */ + for (tmp = list; (tmp!=NULL); tmp=tmp->next, number_of_ues++); + printf("Per UE Data - %u UEs (%u frames)\n", number_of_ues, hs->total_frames); + printf("==========================================\n"); + + /* Show column titles */ + for (i=0; i < NUM_UE_COLUMNS; i++) { + printf("%s ", ue_titles[i]); + } + printf("\n"); + + /* For each row/UE in the model */ + for (tmp = list; tmp; tmp=tmp->next) { + /* Calculate bandwidth */ + float UL_bw = calculate_bw(&tmp->stats.UL_time_start, + &tmp->stats.UL_time_stop, + tmp->stats.UL_total_bytes); + float DL_bw = calculate_bw(&tmp->stats.DL_time_start, + &tmp->stats.DL_time_stop, + tmp->stats.DL_total_bytes); + + printf("%5u %10u %9u %10f %8u %9u %10u %10u %9u %10f %8u %9u %10u\n", + tmp->stats.ueid, + tmp->stats.UL_frames, + tmp->stats.UL_total_bytes, UL_bw, + tmp->stats.UL_total_acks, + tmp->stats.UL_total_nacks, + tmp->stats.UL_total_missing, + tmp->stats.DL_frames, + tmp->stats.DL_total_bytes, DL_bw, + tmp->stats.DL_total_acks, + tmp->stats.DL_total_nacks, + tmp->stats.DL_total_missing); + } +} + + + + +/* Create a new RLC LTE stats struct */ +static void rlc_lte_stat_init(const char *opt_arg, void *userdata _U_) +{ + rlc_lte_stat_t *hs; + const char *filter = NULL; + GString *error_string; + + /* Check for a filter string */ + if (strncmp(opt_arg, "rlc-lte,stat,", 13) == 0) { + /* Skip those characters from filter to display */ + filter = opt_arg + 13; + } + else { + /* No filter */ + filter = NULL; + } + + /* Create top-level struct */ + hs = g_new0(rlc_lte_stat_t, 1); + hs->ep_list = NULL; + + + /**********************************************/ + /* Register the tap listener */ + /**********************************************/ + + error_string = register_tap_listener("rlc-lte", hs, + filter, 0, + rlc_lte_stat_reset, + rlc_lte_stat_packet, + rlc_lte_stat_draw, + NULL); + if (error_string) { + g_string_free(error_string, TRUE); + g_free(hs); + exit(1); + } + +} + + +/* Register this tap listener (need void on own so line register function found) */ +static stat_tap_ui rlc_lte_stat_ui = { + REGISTER_STAT_GROUP_GENERIC, + NULL, + "rlc-lte,stat", + rlc_lte_stat_init, + 0, + NULL +}; + +void +register_tap_listener_rlc_lte_stat(void) +{ + register_stat_tap_ui(&rlc_lte_stat_ui, NULL); +} |