wireshark/ui/cli/tap-rlcltestat.c

420 lines
11 KiB
C

/* 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)
{
/* 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);
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/