Add LTE RLC stats tap to tshark.
authormartinm <martinm@f5534014-38df-0310-8fa8-9805f1628bb7>
Thu, 14 Jul 2011 17:07:41 +0000 (17:07 +0000)
committermartinm <martinm@f5534014-38df-0310-8fa8-9805f1628bb7>
Thu, 14 Jul 2011 17:07:41 +0000 (17:07 +0000)
git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@38019 f5534014-38df-0310-8fa8-9805f1628bb7

CMakeLists.txt
Makefile.common
doc/tshark.pod
tap-rlcltestat.c [new file with mode: 0644]

index 2c05efde363cdb1c6f7a7810caabb02b2efda199..b52d0d064da36d931c31fb42fe9184225850c203 100644 (file)
@@ -580,6 +580,7 @@ set(TSHARK_TAP_SRC
        tap-protocolinfo.c
        tap-protohierstat.c
        tap-radiusstat.c
+       tap-rlcltestat.c
        tap-rpcstat.c
        tap-rpcprogs.c
        tap-rtp.c
index ce26aafdfe3b8dd59dd12467de4071613ee2735e..cf24b77b0e85053b87f16c5c3aedda4975444fa2 100644 (file)
@@ -127,6 +127,7 @@ TSHARK_TAP_SRC =    \
        tap-protocolinfo.c      \
        tap-protohierstat.c     \
        tap-radiusstat.c        \
+       tap-rlcltestat.c        \
        tap-rpcstat.c   \
        tap-rpcprogs.c  \
        tap-rtp.c       \
index 7c03a190c19e1e5d3bb203cc4bf4dac59073dabb..45efe123194d4b7b0600d4e7ee1431e80743dd0b 100644 (file)
@@ -1210,7 +1210,7 @@ This option will activate a counter for LTE MAC messages. You will get
 information about the maximum number of UEs/TTI, common messages and
 various counters for each UE that appears in the log.
 
-Example: B<-z mac=;te,stat>.
+Example: B<-z mac-lte,stat>.
 
 This option can be used multiple times on the command line.
 
@@ -1219,6 +1219,21 @@ for those frames that match that filter.
 Example: B<-z "mac-lte,stat,mac-lte.rnti>3000"> will only collect stats for
 UEs with an assigned RNTI whose value is more than 3000.
 
+=item B<-z> rlc-lte,stat[I<,filter>]
+
+This option will activate a counter for LTE RLC messages. You will get
+information about common messages and various counters for each UE that appears
+in the log.
+
+Example: B<-z rlc-lte,stat>.
+
+This option can be used multiple times on the command line.
+
+If the optional I<filter> is provided, the stats will only be calculated
+for those frames that match that filter.
+Example: B<-z "rlc-lte,stat,rlc-lte.ueid>3000"> will only collect stats for
+UEs with a UEId of more than 3000.
+
 =back
 
 =back
diff --git a/tap-rlcltestat.c b/tap-rlcltestat.c
new file mode 100644 (file)
index 0000000..bdfcf2a
--- /dev/null
@@ -0,0 +1,406 @@
+/* tap-rlclte_stat.c
+ * Copyright 2011 Martin Mathieson
+ *
+ * $Id: rlc_lte_stat_dlg.c 37996 2011-07-13 08:48:13Z etxrab $
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <string.h>
+#include <epan/packet.h>
+#include <epan/packet_info.h>
+#include <epan/tap.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/dissectors/packet-rlc-lte.h>
+
+
+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", "UL 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(struct rlc_lte_tap_info *si, packet_info *pinfo _U_)
+{
+    rlc_lte_ep_t* ep;
+
+    if (!si) {
+        return NULL;
+    }
+
+    if (!(ep = g_malloc(sizeof(rlc_lte_ep_t)))) {
+        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_nacks = 0;
+    ep->stats.DL_total_nacks = 0;
+    ep->stats.UL_total_missing = 0;
+    ep->stats.DL_total_missing = 0;
+
+    return ep;
+}
+
+
+/* Process stat struct for a RLC LTE frame */
+static int
+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 */
+    struct rlc_lte_tap_info *si = (struct rlc_lte_tap_info *)phi;
+
+    /* Need this */
+    if (!hs) {
+        return 0;
+    }
+
+    /* Inc top-level frame count */
+    hs->total_frames++;
+
+    /* Common channel stats */
+    switch (si->channelType) {
+        case CHANNEL_TYPE_BCCH:
+            hs->common_stats.bcch_frames++;
+            hs->common_stats.bcch_bytes += si->pduLength;
+            return 1;
+
+        case CHANNEL_TYPE_PCCH:
+            hs->common_stats.pcch_frames++;
+            hs->common_stats.pcch_bytes += si->pduLength;
+            return 1;
+
+        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 0;
+    }
+
+    /* 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->time;
+        }
+        te->stats.UL_time_stop = si->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->time;
+        }
+        te->stats.DL_time_stop = si->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 1;
+}
+
+
+/* Calculate and return a bandwidth figure, in Mbs */
+static float calculate_bw(nstime_t *start_time, nstime_t *stop_time, guint32 bytes)
+{
+    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);
+        return ((bytes * 8) / elapsed_ms) / 1000;
+    }
+    else {
+        return 0.0;
+    }
+}
+
+
+
+
+/* (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 *optarg, void *userdata _U_)
+{
+    rlc_lte_stat_t    *hs;
+    const char        *filter = NULL;
+    GString           *error_string;
+
+    /* Check for a filter string */
+    if (strncmp(optarg, "rlc-lte,stat,", 13) == 0) {
+        /* Skip those characters from filter to display */
+        filter = optarg + 13;
+    }
+    else {
+        /* No filter */
+        filter = NULL;
+    }
+
+    /* Create top-level struct */
+    hs = g_malloc(sizeof(rlc_lte_stat_t));
+    memset(hs, 0,  sizeof(rlc_lte_stat_t));
+    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);
+    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) */
+void
+register_tap_listener_rlc_lte_stat(void)
+{
+    register_stat_cmd_arg("rlc-lte,stat", rlc_lte_stat_init, NULL);
+}
+