summaryrefslogtreecommitdiffstats
path: root/ui/cli
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /ui/cli
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--ui/cli/.editorconfig83
-rw-r--r--ui/cli/simple_dialog.c60
-rw-r--r--ui/cli/tap-camelsrt.c268
-rw-r--r--ui/cli/tap-credentials.c117
-rw-r--r--ui/cli/tap-diameter-avp.c298
-rw-r--r--ui/cli/tap-endpoints.c134
-rw-r--r--ui/cli/tap-expert.c287
-rw-r--r--ui/cli/tap-exportobject.c190
-rw-r--r--ui/cli/tap-exportobject.h41
-rw-r--r--ui/cli/tap-flow.c147
-rw-r--r--ui/cli/tap-follow.c639
-rw-r--r--ui/cli/tap-funnel.c187
-rw-r--r--ui/cli/tap-gsm_astat.c346
-rw-r--r--ui/cli/tap-hosts.c157
-rw-r--r--ui/cli/tap-httpstat.c297
-rw-r--r--ui/cli/tap-icmpstat.c315
-rw-r--r--ui/cli/tap-icmpv6stat.c315
-rw-r--r--ui/cli/tap-iostat.c1541
-rw-r--r--ui/cli/tap-iousers.c267
-rw-r--r--ui/cli/tap-macltestat.c551
-rw-r--r--ui/cli/tap-protocolinfo.c150
-rw-r--r--ui/cli/tap-protohierstat.c244
-rw-r--r--ui/cli/tap-protohierstat.h51
-rw-r--r--ui/cli/tap-rlcltestat.c406
-rw-r--r--ui/cli/tap-rpcprogs.c246
-rw-r--r--ui/cli/tap-rtd.c163
-rw-r--r--ui/cli/tap-rtp.c128
-rw-r--r--ui/cli/tap-rtspstat.c289
-rw-r--r--ui/cli/tap-sctpchunkstat.c243
-rw-r--r--ui/cli/tap-simple_stattable.c169
-rw-r--r--ui/cli/tap-sipstat.c370
-rw-r--r--ui/cli/tap-smbsids.c106
-rw-r--r--ui/cli/tap-srt.c196
-rw-r--r--ui/cli/tap-stats_tree.c149
-rw-r--r--ui/cli/tap-sv.c95
-rw-r--r--ui/cli/tap-voip.c75
-rw-r--r--ui/cli/tap-voip.h45
-rw-r--r--ui/cli/tap-wspstat.c293
-rw-r--r--ui/cli/tshark-tap.h22
39 files changed, 9680 insertions, 0 deletions
diff --git a/ui/cli/.editorconfig b/ui/cli/.editorconfig
new file mode 100644
index 00000000..43fe1d71
--- /dev/null
+++ b/ui/cli/.editorconfig
@@ -0,0 +1,83 @@
+#
+# Editor configuration
+#
+# https://editorconfig.org
+#
+
+[tap-camelsrt.[ch]]
+indent_size = 2
+
+[tap-diameter-avp.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-endpoints.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-follow.[ch]]
+indent_size = 2
+
+[tap-hosts.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-httpstat.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-iousers.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-protocolinfo.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-protohierstat.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-rpcprogs.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-rtd.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-rtspstat.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-sctpchunkstat.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-simple_stattable.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-sipstat.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-smbsids.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-srt.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-stats_tree.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-sv.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tap-wspstat.[ch]]
+indent_style = tab
+indent_size = tab
diff --git a/ui/cli/simple_dialog.c b/ui/cli/simple_dialog.c
new file mode 100644
index 00000000..5807c8b3
--- /dev/null
+++ b/ui/cli/simple_dialog.c
@@ -0,0 +1,60 @@
+/* simple_dialog.c
+ * simple_dialog 2023 Niels Widger
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* This module provides a minimal cli implementation of simple dialogs.
+ * It is only used by tshark and not wireshark.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <stdio.h>
+#include <ui/simple_dialog.h>
+#include "ws_attributes.h"
+
+gpointer
+simple_dialog(
+ ESD_TYPE_E type _U_,
+ gint btn_mask _U_,
+ const gchar * msg_format,
+ ...
+ )
+{
+ va_list ap;
+
+ va_start(ap, msg_format);
+ vfprintf(stderr, msg_format, ap);
+ va_end(ap);
+
+ return NULL;
+}
+
+void
+simple_message_box(ESD_TYPE_E type _U_, gboolean *notagain _U_,
+ const char *secondary_msg, const char *msg_format, ...)
+{
+ va_list ap;
+ va_start(ap, msg_format);
+ vfprintf(stderr, msg_format, ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s\n", secondary_msg);
+}
+
+/*
+ * Error alert box, taking a format and a va_list argument.
+ */
+void
+vsimple_error_message_box(const char *msg_format, va_list ap)
+{
+ vfprintf(stderr, msg_format, ap);
+}
diff --git a/ui/cli/tap-camelsrt.c b/ui/cli/tap-camelsrt.c
new file mode 100644
index 00000000..935731ac
--- /dev/null
+++ b/ui/cli/tap-camelsrt.c
@@ -0,0 +1,268 @@
+/* tap_camelsrt.c
+ * CAMEL Service Response Time statistics for tshark
+ * Copyright 2006 Florent Drouin (based on tap_h225rassrt.c from Lars Roland)
+ *
+ * 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/value_string.h"
+#include "epan/asn1.h"
+#include "epan/dissectors/packet-camel.h"
+#include "epan/dissectors/packet-tcap.h"
+#include "epan/timestats.h"
+#include "epan/stat_tap_ui.h"
+
+#include <wsutil/cmdarg_err.h>
+
+void register_tap_listener_camelsrt(void);
+
+/* Save the first NUM_RAS_STATS stats in the array to calculate percentile */
+#define NUM_RAS_STATS 500000
+
+/* Number of couple message Request/Response to analyze*/
+#define NB_CRITERIA 7
+
+/* used to keep track of the statistics for an entire program interface */
+struct camelsrt_t {
+ char *filter;
+ guint32 count[NB_CAMELSRT_CATEGORY];
+ timestat_t stats[NB_CAMELSRT_CATEGORY];
+ nstime_t delta_time[NB_CAMELSRT_CATEGORY][NUM_RAS_STATS];
+};
+
+/* Reset the counter */
+static void camelsrt_reset(void *phs)
+{
+ struct camelsrt_t *hs = (struct camelsrt_t *)phs;
+ memset(hs, 0, sizeof(struct camelsrt_t));
+}
+
+
+static tap_packet_status camelsrt_packet(void *phs,
+ packet_info *pinfo _U_,
+ epan_dissect_t *edt _U_,
+ const void *phi, tap_flags_t flags _U_)
+{
+ struct camelsrt_t *hs = (struct camelsrt_t *)phs;
+ const struct camelsrt_info_t * pi = (const struct camelsrt_info_t *)phi;
+ int i;
+
+ for (i=0; i<NB_CAMELSRT_CATEGORY; i++) {
+ if (pi->bool_msginfo[i] &&
+ pi->msginfo[i].is_delta_time
+ && pi->msginfo[i].request_available
+ && !pi->msginfo[i].is_duplicate ) {
+
+ time_stat_update(&(hs->stats[i]),
+ &(pi->msginfo[i].delta_time),
+ pinfo);
+
+ if (hs->count[i] < NUM_RAS_STATS) {
+ hs->delta_time[i][hs->count[i]++]
+ = pi->msginfo[i].delta_time;
+ }
+ }
+ }
+ return TAP_PACKET_REDRAW;
+}
+
+
+static void camelsrt_draw(void *phs)
+{
+ struct camelsrt_t *hs = (struct camelsrt_t *)phs;
+ guint j, z;
+ guint32 li;
+ int somme, iteration = 0;
+ timestat_t *rtd_temp;
+ double x, delay, delay_max, delay_min, delta;
+ double criteria[NB_CRITERIA] = { 5.0, 10.0, 75.0, 90.0, 95.0, 99.0, 99.90 };
+ double delay_criteria[NB_CRITERIA];
+ gchar* tmp_str;
+
+ printf("\n");
+ printf("Camel Service Response Time (SRT) Statistics:\n");
+ printf("=================================================================================================\n");
+ printf("| Category | Measure | Min SRT | Max SRT | Avg SRT | Min frame | Max frame |\n");
+ printf("|-------------------------|---------|-----------|-----------|-----------|-----------|-----------|\n");
+
+ j = 1;
+ tmp_str = val_to_str_wmem(NULL, j, camelSRTtype_naming, "Unknown Message 0x%02x");
+ printf("|%24s |%8u |%8.2f s |%8.2f s |%8.2f s |%10u |%10u |\n",
+ tmp_str,
+ hs->stats[j].num,
+ nstime_to_sec(&(hs->stats[j].min)),
+ nstime_to_sec(&(hs->stats[j].max)),
+ get_average(&(hs->stats[j].tot), hs->stats[j].num)/1000.0,
+ hs->stats[j].min_num,
+ hs->stats[j].max_num
+ );
+ wmem_free(NULL, tmp_str);
+ for (j=2; j<NB_CAMELSRT_CATEGORY; j++) {
+ if (hs->stats[j].num == 0) {
+ tmp_str = val_to_str_wmem(NULL, j, camelSRTtype_naming, "Unknown Message 0x%02x");
+ printf("|%24s |%8u |%8.2f ms|%8.2f ms|%8.2f ms|%10u |%10u |\n",
+ tmp_str, 0U, 0.0, 0.0, 0.0, 0U, 0U);
+ wmem_free(NULL, tmp_str);
+ continue;
+ }
+
+ tmp_str = val_to_str_wmem(NULL, j, camelSRTtype_naming, "Unknown Message 0x%02x");
+ printf("|%24s |%8u |%8.2f ms|%8.2f ms|%8.2f ms|%10u |%10u |\n",
+ tmp_str,
+ hs->stats[j].num,
+ MIN(9999, nstime_to_msec(&(hs->stats[j].min))),
+ MIN(9999, nstime_to_msec(&(hs->stats[j].max))),
+ MIN(9999, get_average(&(hs->stats[j].tot), hs->stats[j].num)),
+ hs->stats[j].min_num,
+ hs->stats[j].max_num
+ );
+ wmem_free(NULL, tmp_str);
+ } /* j category */
+
+ printf("=================================================================================================\n");
+ /*
+ * Display 95%
+ */
+
+ printf("| Category/Criteria |");
+ for (z=0; z<NB_CRITERIA; z++) printf("%7.2f%% |", criteria[z]);
+ printf("\n");
+
+ printf("|-------------------------|");
+ for (z=0; z<NB_CRITERIA; z++) printf("---------|");
+ printf("\n");
+ /* calculate the delay max to have a given number of messages (in percentage) */
+ for (j=2; j<NB_CAMELSRT_CATEGORY;j++) {
+
+ rtd_temp = &(hs->stats[j]);
+
+ if (hs->count[j] > 0) {
+ /* Calculate the delay to answer to p% of the MS */
+ for (z=0; z<NB_CRITERIA; z++) {
+ iteration = 0;
+ delay_max = (double)rtd_temp->max.secs*1000 +(double)rtd_temp->max.nsecs/1000000;
+ delay_min = (double)rtd_temp->min.secs*1000 +(double)rtd_temp->min.nsecs/1000000;
+ delay = delay_min;
+ delta = delay_max-delay_min;
+ while ( (delta > 0.001) && (iteration < 10000) ) {
+ somme = 0;
+ iteration++;
+
+ for (li=0; li<hs->count[j]; li++) {
+ x = hs->delta_time[j][li].secs*1000
+ + (double)hs->delta_time[j][li].nsecs/1000000;
+ if (x <= delay) somme++;
+ }
+ if ( somme*100 > hs->count[j]*criteria[z] ) { /* trop grand */
+ delay_max = delay;
+ delay = (delay_max+delay_min)/2;
+ delta = delay_max-delay_min;
+ } else { /* trop petit */
+ delay_min = delay;
+ delay = (delay_max+delay_min)/2;
+ delta = delay_max-delay_min;
+ }
+ } /* while */
+ delay_criteria[z] = delay;
+ } /* z criteria */
+ /* Append the result to the table */
+ tmp_str = val_to_str_wmem(NULL, j, camelSRTtype_naming, "Unknown Message 0x%02x");
+ printf("X%24s |", tmp_str);
+ wmem_free(NULL, tmp_str);
+ for (z=0; z<NB_CRITERIA; z++) printf("%8.2f |", MIN(9999, delay_criteria[z]));
+ printf("\n");
+ } else { /* count */
+ tmp_str = val_to_str_wmem(NULL, j, camelSRTtype_naming, "Unknown Message 0x%02x");
+ printf("X%24s |", tmp_str);
+ wmem_free(NULL, tmp_str);
+ for (z=0; z<NB_CRITERIA; z++) printf("%8.2f |", 0.0);
+ printf("\n");
+ } /* count */
+ }/* j category */
+ printf("===========================");
+ for (z=0; z<NB_CRITERIA; z++) printf("==========");
+ printf("\n");
+}
+
+static void camelsrt_init(const char *opt_arg, void *userdata _U_)
+{
+ struct camelsrt_t *p_camelsrt;
+ GString *error_string;
+
+ p_camelsrt = g_new(struct camelsrt_t, 1);
+ camelsrt_reset(p_camelsrt);
+
+ if (!strncmp(opt_arg, "camel,srt,", 10)) {
+ p_camelsrt->filter = g_strdup(opt_arg+10);
+ } else {
+ p_camelsrt->filter = NULL;
+ }
+
+ error_string = register_tap_listener("CAMEL",
+ p_camelsrt,
+ p_camelsrt->filter,
+ 0,
+ NULL,
+ camelsrt_packet,
+ camelsrt_draw,
+ NULL);
+
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ g_free(p_camelsrt->filter);
+ g_free(p_camelsrt);
+
+ cmdarg_err("Couldn't register camel,srt tap: %s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ /*
+ * If we are using tshark, we have to display the stats, even if the stats are not persistent
+ * As the frame are proceeded in the chronological order, we do not need persistent stats
+ * Whereas, with wireshark, it is not possible to have the correct display, if the stats are
+ * not saved along the analyze
+ */
+ gtcap_StatSRT = TRUE;
+ gcamel_StatSRT = TRUE;
+}
+
+static stat_tap_ui camelsrt_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "camel,srt",
+ camelsrt_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_camelsrt(void)
+{
+ register_stat_tap_ui(&camelsrt_ui, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/ui/cli/tap-credentials.c b/ui/cli/tap-credentials.c
new file mode 100644
index 00000000..9a54a7eb
--- /dev/null
+++ b/ui/cli/tap-credentials.c
@@ -0,0 +1,117 @@
+/*
+ * tap-credentials.c
+ * Copyright 2019 Dario Lombardo <lomato@gmail.com>
+ *
+ * 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 <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+
+#include <wsutil/cmdarg_err.h>
+#include <ui/tap-credentials.h>
+
+void register_tap_listener_credentials(void);
+
+wmem_array_t* credentials = NULL;
+
+static tap_credential_t* tap_credential_clone(tap_credential_t* auth)
+{
+ tap_credential_t* clone = wmem_new0(NULL, tap_credential_t);
+ clone->num = auth->num;
+ clone->username_num = auth->username_num;
+ clone->password_hf_id = auth->password_hf_id;
+ if (auth->username)
+ clone->username = wmem_strdup(NULL, auth->username);
+ clone->proto = auth->proto;
+ if (auth->info)
+ clone->info = wmem_strdup(NULL, auth->info);
+ return clone;
+}
+
+static tap_packet_status credentials_packet(void *p _U_, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri, tap_flags_t flags _U_)
+{
+ tap_credential_t* clone = tap_credential_clone((tap_credential_t*)pri);
+ wmem_array_append(credentials, (void*)clone, 1);
+ return TAP_PACKET_REDRAW;
+}
+
+static void credentials_reset(void* p)
+{
+ if (!p)
+ return;
+ tap_credential_t* auth = (tap_credential_t*)p;
+ wmem_free(NULL, auth->username);
+ wmem_free(NULL, auth->info);
+ wmem_free(NULL, auth);
+}
+
+static void credentials_draw(void *p _U_)
+{
+ printf("===================================================================\n");
+ printf("%-10s %-16s %-16s %-16s\n", "Packet", "Protocol", "Username", "Info");
+ printf("------ -------- -------- --------\n");
+ for (guint i = 0; i < wmem_array_get_count(credentials); i++) {
+ tap_credential_t* auth = (tap_credential_t*)wmem_array_index(credentials, i);
+ printf("%-10u %-16s %-16s %-16s\n", auth->num, auth->proto, auth->username, auth->info ? auth->info : "");
+ }
+ printf("===================================================================\n");
+}
+
+static void credentials_init(const char *opt_arg _U_, void *userdata _U_)
+{
+ GString* error_string;
+
+ error_string = register_tap_listener("credentials", NULL, NULL, TL_REQUIRES_NOTHING,
+ credentials_reset, credentials_packet, credentials_draw, NULL);
+
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ cmdarg_err("Couldn't register credentials tap: %s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ credentials = wmem_array_new(wmem_epan_scope(), sizeof(tap_credential_t));
+}
+
+static stat_tap_ui credentials_ui = {
+ REGISTER_TOOLS_GROUP_UNSORTED,
+ "Username and passwords",
+ "credentials",
+ credentials_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_credentials(void)
+{
+ register_stat_tap_ui(&credentials_ui, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-diameter-avp.c b/ui/cli/tap-diameter-avp.c
new file mode 100644
index 00000000..eb990fa9
--- /dev/null
+++ b/ui/cli/tap-diameter-avp.c
@@ -0,0 +1,298 @@
+/* tap-diameter-avp.c
+ * Copyright 2010 Andrej Kuehnal <andrejk@freenet.de>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * This TAP enables extraction of most important diameter fields in text format.
+ * - much more performance than -T text and -T pdml
+ * - more powerful than -T field and -z proto,colinfo
+ * - exacltly one text line per diameter message
+ * - multiple diameter messages in one frame supported
+ * E.g. one device watchdog answer and two credit control answers
+ * in one TCP packet produces 3 text lines.
+ * - several fields with same name within one diameter message supported
+ * E.g. Multiple AVP(444) Subscription-Id-Data once with IMSI once with MSISDN
+ * - several grouped AVPs supported
+ * E.g. Zero or more Multiple-Services-Credit-Control AVPs(456)
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <wsutil/strtoi.h>
+#include <wsutil/cmdarg_err.h>
+
+#include <epan/packet_info.h>
+#include <epan/tap.h>
+#include <epan/epan_dissect.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/value_string.h>
+#include <epan/to_str.h>
+#include <epan/dissectors/packet-diameter.h>
+
+void register_tap_listener_diameteravp(void);
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _diameteravp_t {
+ guint32 frame;
+ guint32 diammsg_toprocess;
+ guint32 cmd_code;
+ guint32 req_count;
+ guint32 ans_count;
+ guint32 paired_ans_count;
+ gchar *filter;
+} diameteravp_t;
+
+/* Copied from proto.c */
+static gboolean
+tree_traverse_pre_order(proto_tree *tree, proto_tree_traverse_func func, gpointer data)
+{
+ proto_node *pnode = tree;
+ proto_node *child;
+ proto_node *current;
+
+ if (func(pnode, data))
+ return TRUE;
+
+ child = pnode->first_child;
+ while (child != NULL) {
+ current = child;
+ child = current->next;
+ if (tree_traverse_pre_order((proto_tree *)current, func, data))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+diam_tree_to_csv(proto_node *node, gpointer data)
+{
+ char *val_str = NULL;
+ char *val_tmp = NULL;
+ ftenum_t ftype;
+ field_info *fi;
+ header_field_info *hfi;
+
+ if (!node) {
+ fprintf(stderr, "traverse end: empty node. node='%p' data='%p'\n", (void *)node, (void *)data);
+ return FALSE;
+ }
+ fi = node->finfo;
+ hfi = fi ? fi->hfinfo : NULL;
+ if (!hfi) {
+ fprintf(stderr, "traverse end: hfi not found. node='%p'\n", (void *)node);
+ return FALSE;
+ }
+ ftype = fvalue_type_ftenum(fi->value);
+ if (ftype != FT_NONE && ftype != FT_PROTOCOL) {
+ /* convert value to string */
+ val_tmp = fvalue_to_string_repr(NULL, fi->value, FTREPR_DISPLAY, hfi->display);
+ if (val_tmp)
+ {
+ val_str = g_strdup(val_tmp);
+ wmem_free(NULL, val_tmp);
+ } else
+ val_str = ws_strdup_printf("unsupported type: %s", ftype_name(ftype));
+
+ /*printf("traverse: name='%s', abbrev='%s',desc='%s', val='%s'\n", hfi->name, hfi->abbrev, ftype_name(hfi->type), val_str);*/
+ printf("%s='%s' ", hfi->name, val_str);
+ g_free(val_str);
+ }
+ return FALSE;
+}
+
+static tap_packet_status
+diameteravp_packet(void *pds, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pdi, tap_flags_t flags _U_)
+{
+ tap_packet_status ret = TAP_PACKET_DONT_REDRAW;
+ double resp_time = 0.;
+ gboolean is_request = TRUE;
+ guint32 cmd_code = 0;
+ guint32 req_frame = 0;
+ guint32 ans_frame = 0;
+ guint32 diam_child_node = 0;
+ proto_node *current = NULL;
+ proto_node *node = NULL;
+ header_field_info *hfi = NULL;
+ field_info *finfo = NULL;
+ const diameter_req_ans_pair_t *dp = (const diameter_req_ans_pair_t *)pdi;
+ diameteravp_t *ds = NULL;
+
+ /* Validate paramerers. */
+ if (!dp || !edt || !edt->tree)
+ return ret;
+
+ /* Several diameter messages within one frame are possible. *
+ * Check if we processing the message in same frame like befor or in new frame.*/
+ ds = (diameteravp_t *)pds;
+ if (pinfo->num > ds->frame) {
+ ds->frame = pinfo->num;
+ ds->diammsg_toprocess = 0;
+ } else {
+ ds->diammsg_toprocess += 1;
+ }
+
+ /* Extract data from request/answer pair provided by diameter dissector.*/
+ is_request = dp->processing_request;
+ cmd_code = dp->cmd_code;
+ req_frame = dp->req_frame;
+ ans_frame = dp->ans_frame;
+ if (!is_request) {
+ nstime_t ns;
+ nstime_delta(&ns, &pinfo->abs_ts, &dp->req_time);
+ resp_time = nstime_to_sec(&ns);
+ resp_time = resp_time < 0. ? 0. : resp_time;
+ }
+
+ /* Check command code provided by command line option.*/
+ if (ds->cmd_code && ds->cmd_code != cmd_code)
+ return ret;
+
+ /* Loop over top level nodes */
+ node = edt->tree->first_child;
+ while (node != NULL) {
+ current = node;
+ node = current->next;
+ finfo = current->finfo;
+ hfi = finfo ? finfo->hfinfo : NULL;
+ /*fprintf(stderr, "DEBUG: diameteravp_packet %d %p %p node=%p abbrev=%s\n", cmd_code, edt, edt->tree, current, hfi->abbrev);*/
+ /* process current diameter subtree in the current frame. */
+ if (hfi && hfi->abbrev && strcmp(hfi->abbrev, "diameter") == 0) {
+ /* Process current diameter message in the frame */
+ if (ds->diammsg_toprocess == diam_child_node) {
+ if (is_request) {
+ ds->req_count++;
+ } else {
+ ds->ans_count++;
+ if (req_frame > 0)
+ ds->paired_ans_count++;
+ }
+ /* Output frame data.*/
+ printf("frame='%u' time='%f' src='%s' srcport='%u' dst='%s' dstport='%u' proto='diameter' msgnr='%u' is_request='%d' cmd='%u' req_frame='%u' ans_frame='%u' resp_time='%f' ",
+ pinfo->num, nstime_to_sec(&pinfo->abs_ts), address_to_str(pinfo->pool, &pinfo->src), pinfo->srcport, address_to_str(pinfo->pool, &pinfo->dst), pinfo->destport, ds->diammsg_toprocess, is_request, cmd_code, req_frame, ans_frame, resp_time);
+ /* Visit selected nodes of one diameter message.*/
+ tree_traverse_pre_order(current, diam_tree_to_csv, &ds);
+ /* End of message.*/
+ printf("\n");
+ /*printf("hfi: name='%s', msg_curr='%d' abbrev='%s',type='%s'\n", hfi->name, diam_child_node, hfi->abbrev, ftype_name(hfi->type));*/
+ }
+ diam_child_node++;
+ }
+ }
+ return ret;
+}
+
+static void
+diameteravp_draw(void *pds)
+{
+ diameteravp_t *ds = (diameteravp_t *)pds;
+ /* printing results */
+ printf("=== Diameter Summary ===\nrequest count:\t%u\nanswer count:\t%u\nreq/ans pairs:\t%u\n", ds->req_count, ds->ans_count, ds->paired_ans_count);
+}
+
+
+static void
+diameteravp_init(const char *opt_arg, void *userdata _U_)
+{
+ diameteravp_t *ds;
+ gchar *field = NULL;
+ gchar **tokens;
+ guint opt_count = 0;
+ guint opt_idx = 0;
+ GString *filter = NULL;
+ GString *error_string = NULL;
+
+ ds = g_new(diameteravp_t, 1);
+ ds->frame = 0;
+ ds->diammsg_toprocess = 0;
+ ds->cmd_code = 0;
+ ds->req_count = 0;
+ ds->ans_count = 0;
+ ds->paired_ans_count = 0;
+ ds->filter = NULL;
+
+ filter = g_string_new("diameter");
+
+ /* Split command line options. */
+ tokens = g_strsplit(opt_arg, ",", 1024);
+ opt_count = 0;
+ while (tokens[opt_count])
+ opt_count++;
+ if (opt_count > 2) {
+ /* if the token is a not-null string and it's not *, the conversion must succeeed */
+ if (strlen(tokens[2]) > 0 && tokens[2][0] != '*') {
+ if (!ws_strtou32(tokens[2], NULL, &ds->cmd_code)) {
+ fprintf(stderr, "Invalid integer token: %s\n", tokens[2]);
+ g_strfreev(tokens);
+ exit(1);
+ }
+ }
+ }
+
+ /* Loop over diameter field names. */
+ for (opt_idx=3; opt_idx<opt_count; opt_idx++)
+ {
+ /* Current field from command line arguments. */
+ field = tokens[opt_idx];
+ /* Connect all requested fields with logical OR. */
+ g_string_append(filter, "||");
+ /* Prefix field name with "diameter." by default. */
+ if (!strchr(field, '.'))
+ g_string_append(filter, "diameter.");
+ /* Append field name to the filter. */
+ g_string_append(filter, field);
+ }
+ g_strfreev(tokens);
+ ds->filter = g_string_free(filter, FALSE);
+
+ error_string = register_tap_listener("diameter", ds, ds->filter, 0, NULL, diameteravp_packet, diameteravp_draw, NULL);
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ g_free(ds);
+
+ cmdarg_err("Couldn't register diam,csv tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static stat_tap_ui diameteravp_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "diameter,avp",
+ diameteravp_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_diameteravp(void)
+{
+ register_stat_tap_ui(&diameteravp_ui, NULL);
+}
+
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-endpoints.c b/ui/cli/tap-endpoints.c
new file mode 100644
index 00000000..e97dcf85
--- /dev/null
+++ b/ui/cli/tap-endpoints.c
@@ -0,0 +1,134 @@
+/* tap-endpoints.c
+ * endpoints 2014 Michael Mann
+ *
+ * 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/conversation_table.h>
+#include <wsutil/cmdarg_err.h>
+#include <ui/cli/tshark-tap.h>
+
+typedef struct _endpoints_t {
+ const char *type;
+ const char *filter;
+ conv_hash_t hash;
+} endpoints_t;
+
+static void
+endpoints_draw(void *arg)
+{
+ conv_hash_t *hash = (conv_hash_t*)arg;
+ endpoints_t *iu = (endpoints_t *)hash->user_data;
+ endpoint_item_t *endpoint;
+ guint64 last_frames, max_frames;
+ guint i;
+ gboolean display_port = (!strncmp(iu->type, "TCP", 3) || !strncmp(iu->type, "UDP", 3) || !strncmp(iu->type, "SCTP", 4)) ? TRUE : FALSE;
+
+ printf("================================================================================\n");
+ printf("%s Endpoints\n", iu->type);
+ printf("Filter:%s\n", iu->filter ? iu->filter : "<No Filter>");
+
+ printf(" | %sPackets | | Bytes | | Tx Packets | | Tx Bytes | | Rx Packets | | Rx Bytes |\n",
+ display_port ? "Port || " : "");
+
+ max_frames = UINT_MAX;
+ do {
+ last_frames = 0;
+ for (i=0; (iu->hash.conv_array && i < iu->hash.conv_array->len); i++) {
+ guint64 tot_frames;
+
+ endpoint = &g_array_index(iu->hash.conv_array, endpoint_item_t, i);
+ tot_frames = endpoint->rx_frames + endpoint->tx_frames;
+
+ if ((tot_frames > last_frames) && (tot_frames < max_frames)) {
+ last_frames = tot_frames;
+ }
+ }
+
+ for (i=0; (iu->hash.conv_array && i < iu->hash.conv_array->len); i++) {
+ guint64 tot_frames;
+ gchar *conversation_str, *port_str;
+
+ endpoint = &g_array_index(iu->hash.conv_array, endpoint_item_t, i);
+ tot_frames = endpoint->rx_frames + endpoint->tx_frames;
+
+ if (tot_frames == last_frames) {
+ /* XXX - TODO: make name resolution configurable (through gbl_resolv_flags?) */
+ conversation_str = get_conversation_address(NULL, &endpoint->myaddress, TRUE);
+ if (display_port) {
+ /* XXX - TODO: make port resolution configurable (through gbl_resolv_flags?) */
+ port_str = get_endpoint_port(NULL, endpoint, TRUE);
+ printf("%-20s %5s %6" PRIu64 " %9" PRIu64
+ " %6" PRIu64 " %9" PRIu64 " %6"
+ PRIu64 " %9" PRIu64 " \n",
+ conversation_str,
+ port_str,
+ endpoint->tx_frames+endpoint->rx_frames, endpoint->tx_bytes+endpoint->rx_bytes,
+ endpoint->tx_frames, endpoint->tx_bytes,
+ endpoint->rx_frames, endpoint->rx_bytes);
+ wmem_free(NULL, port_str);
+ } else {
+ printf("%-20s %6" PRIu64 " %9" PRIu64
+ " %6" PRIu64 " %9" PRIu64 " %6"
+ PRIu64 " %9" PRIu64 " \n",
+ /* XXX - TODO: make name resolution configurable (through gbl_resolv_flags?) */
+ conversation_str,
+ endpoint->tx_frames+endpoint->rx_frames, endpoint->tx_bytes+endpoint->rx_bytes,
+ endpoint->tx_frames, endpoint->tx_bytes,
+ endpoint->rx_frames, endpoint->rx_bytes);
+
+ }
+ wmem_free(NULL, conversation_str);
+ }
+ }
+ max_frames = last_frames;
+ } while (last_frames);
+ printf("================================================================================\n");
+}
+
+void init_endpoints(struct register_ct *ct, const char *filter)
+{
+ endpoints_t *iu;
+ GString *error_string;
+
+ iu = g_new0(endpoints_t, 1);
+ iu->type = proto_get_protocol_short_name(find_protocol_by_id(get_conversation_proto_id(ct)));
+ iu->filter = g_strdup(filter);
+ iu->hash.user_data = iu;
+
+ error_string = register_tap_listener(proto_get_protocol_filter_name(get_conversation_proto_id(ct)), &iu->hash, filter, 0, NULL, get_endpoint_packet_func(ct), endpoints_draw, NULL);
+ if (error_string) {
+ g_free(iu);
+ cmdarg_err("Couldn't register endpoint tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-expert.c b/ui/cli/tap-expert.c
new file mode 100644
index 00000000..3fcf0e7f
--- /dev/null
+++ b/ui/cli/tap-expert.c
@@ -0,0 +1,287 @@
+/* tap-expert.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/expert.h>
+#include <wsutil/ws_assert.h>
+
+void register_tap_listener_expert_info(void);
+
+/* Tap data */
+typedef enum severity_level_t {
+ comment_level = 0,
+ chat_level,
+ note_level,
+ warn_level,
+ error_level,
+ max_level
+} severity_level_t;
+
+/* This variable stores the lowest level that will be displayed.
+ May be changed from the command line */
+static severity_level_t lowest_report_level = comment_level;
+
+typedef struct expert_entry
+{
+ guint32 group;
+ int frequency;
+ const gchar *protocol;
+ gchar *summary;
+} expert_entry;
+
+
+/* Overall struct for storing all data seen */
+typedef struct expert_tapdata_t {
+ GArray *ei_array[max_level]; /* expert info items */
+ GStringChunk *text; /* for efficient storage of summary strings */
+} expert_tapdata_t;
+
+
+/* Reset expert stats */
+static void
+expert_stat_reset(void *tapdata)
+{
+ gint n;
+ expert_tapdata_t *etd = (expert_tapdata_t *)tapdata;
+
+ /* Free & reallocate chunk of strings */
+ g_string_chunk_free(etd->text);
+ etd->text = g_string_chunk_new(100);
+
+ /* Empty each of the arrays */
+ for (n=0; n < max_level; n++) {
+ g_array_set_size(etd->ei_array[n], 0);
+ }
+}
+
+/* Process stat struct for an expert frame */
+static tap_packet_status
+expert_stat_packet(void *tapdata, packet_info *pinfo _U_, epan_dissect_t *edt _U_,
+ const void *pointer, tap_flags_t flags _U_)
+{
+ const expert_info_t *ei = (const expert_info_t *)pointer;
+ expert_tapdata_t *data = (expert_tapdata_t *)tapdata;
+ severity_level_t severity_level;
+ expert_entry tmp_entry;
+ expert_entry *entry;
+ guint n;
+
+ switch (ei->severity) {
+ case PI_COMMENT:
+ severity_level = comment_level;
+ break;
+ case PI_CHAT:
+ severity_level = chat_level;
+ break;
+ case PI_NOTE:
+ severity_level = note_level;
+ break;
+ case PI_WARN:
+ severity_level = warn_level;
+ break;
+ case PI_ERROR:
+ severity_level = error_level;
+ break;
+ default:
+ ws_assert_not_reached();
+ return TAP_PACKET_DONT_REDRAW;
+ }
+
+ /* Don't store details at a lesser severity than we are interested in */
+ if (severity_level < lowest_report_level) {
+ return TAP_PACKET_REDRAW; /* XXX - TAP_PACKET_DONT_REDRAW? */
+ }
+
+ /* If a duplicate just bump up frequency.
+ TODO: could make more efficient by avoiding linear search...*/
+ for (n=0; n < data->ei_array[severity_level]->len; n++) {
+ entry = &g_array_index(data->ei_array[severity_level], expert_entry, n);
+ if ((strcmp(ei->protocol, entry->protocol) == 0) &&
+ (strcmp(ei->summary, entry->summary) == 0)) {
+ entry->frequency++;
+ return TAP_PACKET_REDRAW;
+ }
+ }
+
+ /* Else Add new item to end of list for severity level */
+ entry = &tmp_entry;
+ /* Copy/Store protocol and summary strings efficiently using GStringChunk */
+ entry->protocol = g_string_chunk_insert_const(data->text, ei->protocol);
+ entry->summary = g_string_chunk_insert_const(data->text, ei->summary);
+ entry->group = ei->group;
+ entry->frequency = 1;
+ /* Store a copy of the expert entry */
+ g_array_append_val(data->ei_array[severity_level], tmp_entry);
+
+ return TAP_PACKET_REDRAW;
+}
+
+/* Output for all of the items of one severity */
+static void draw_items_for_severity(GArray *items, const gchar *label)
+{
+ guint n;
+ expert_entry *ei;
+ int total = 0;
+ gchar *tmp_str;
+
+ /* Don't print title if no items */
+ if (items->len == 0) {
+ return;
+ }
+
+ /* Add frequencies together to get total */
+ for (n=0; n < items->len; n++) {
+ ei = &g_array_index(items, expert_entry, n);
+ total += ei->frequency;
+ }
+
+ /* Title */
+ printf("\n%s (%d)\n", label, total);
+ printf("=============\n");
+
+ /* Column headings */
+ printf(" Frequency Group Protocol Summary\n");
+
+ /* Items */
+ for (n=0; n < items->len; n++) {
+ ei = &g_array_index(items, expert_entry, n);
+ tmp_str = val_to_str_wmem(NULL, ei->group, expert_group_vals, "Unknown (%d)");
+ printf("%12d %10s %18s %s\n",
+ ei->frequency,
+ tmp_str,
+ ei->protocol, ei->summary);
+ wmem_free(NULL, tmp_str);
+ }
+}
+
+/* (Re)draw expert stats */
+static void
+expert_stat_draw(void *phs _U_)
+{
+ /* Look up the statistics struct */
+ expert_tapdata_t *hs = (expert_tapdata_t *)phs;
+
+ draw_items_for_severity(hs->ei_array[error_level], "Errors");
+ draw_items_for_severity(hs->ei_array[warn_level], "Warns");
+ draw_items_for_severity(hs->ei_array[note_level], "Notes");
+ draw_items_for_severity(hs->ei_array[chat_level], "Chats");
+ draw_items_for_severity(hs->ei_array[comment_level], "Comments");
+}
+
+static void
+expert_tapdata_free(expert_tapdata_t* hs)
+{
+ for (int n = 0; n < max_level; n++) {
+ g_array_free(hs->ei_array[n], TRUE);
+ }
+ g_string_chunk_free(hs->text);
+ g_free(hs);
+}
+
+/* Create a new expert stats struct */
+static void expert_stat_init(const char *opt_arg, void *userdata _U_)
+{
+ const char *args = NULL;
+ const char *filter = NULL;
+ GString *error_string;
+ expert_tapdata_t *hs;
+ int n;
+
+ /* Check for args. */
+ if (strncmp(opt_arg, "expert", 6) == 0) {
+ /* Skip those characters */
+ args = opt_arg + 6;
+ }
+ else {
+ /* No args. Will show all reports, with no filter */
+ lowest_report_level = max_level;
+ }
+
+ /* First (optional) arg is Error|Warn|Note|Chat */
+ if (args != NULL) {
+ if (g_ascii_strncasecmp(args, ",error", 6) == 0) {
+ lowest_report_level = error_level;
+ args += 6;
+ }
+ else if (g_ascii_strncasecmp(args, ",warn", 5) == 0) {
+ lowest_report_level = warn_level;
+ args += 5;
+ } else if (g_ascii_strncasecmp(args, ",note", 5) == 0) {
+ lowest_report_level = note_level;
+ args += 5;
+ } else if (g_ascii_strncasecmp(args, ",chat", 5) == 0) {
+ lowest_report_level = chat_level;
+ args += 5;
+ } else if (g_ascii_strncasecmp(args, ",comment", 8) == 0) {
+ lowest_report_level = comment_level;
+ args += 8;
+ }
+ }
+
+ /* Second (optional) arg is a filter string */
+ if (args != NULL) {
+ if (args[0] == ',') {
+ filter = args+1;
+ }
+ }
+
+ /* Create top-level struct */
+ hs = g_new0(expert_tapdata_t, 1);
+
+ /* Allocate chunk of strings */
+ hs->text = g_string_chunk_new(100);
+
+ /* Allocate GArray for each severity level */
+ for (n=0; n < max_level; n++) {
+ hs->ei_array[n] = g_array_sized_new(FALSE, FALSE, sizeof(expert_entry), 1000);
+ }
+
+ /**********************************************/
+ /* Register the tap listener */
+ /**********************************************/
+
+ error_string = register_tap_listener("expert", hs,
+ filter, 0,
+ expert_stat_reset,
+ expert_stat_packet,
+ expert_stat_draw,
+ (tap_finish_cb)expert_tapdata_free);
+ if (error_string) {
+ printf("Expert tap error (%s)!\n", error_string->str);
+ g_string_free(error_string, TRUE);
+ expert_tapdata_free(hs);
+ exit(1);
+ }
+}
+
+static stat_tap_ui expert_stat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "expert",
+ expert_stat_init,
+ 0,
+ NULL
+};
+
+/* Register this tap listener (need void on own so line register function found) */
+void
+register_tap_listener_expert_info(void)
+{
+ register_stat_tap_ui(&expert_stat_ui, NULL);
+}
diff --git a/ui/cli/tap-exportobject.c b/ui/cli/tap-exportobject.c
new file mode 100644
index 00000000..4a02155c
--- /dev/null
+++ b/ui/cli/tap-exportobject.c
@@ -0,0 +1,190 @@
+/* tap-exportobject.c
+ *
+ * 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 <glib.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string.h>
+
+#include <wsutil/file_util.h>
+#include <wsutil/filesystem.h>
+#include <wsutil/cmdarg_err.h>
+
+#include <epan/packet_info.h>
+#include <epan/packet.h>
+#include <epan/export_object.h>
+#include "tap-exportobject.h"
+
+typedef struct _export_object_list_gui_t {
+ GSList *entries;
+ register_eo_t* eo;
+} export_object_list_gui_t;
+
+static GHashTable* eo_opts = NULL;
+
+static bool
+list_exportobject_protocol(const void *key, void *value _U_, void *userdata _U_)
+{
+ fprintf(stderr, " %s\n", (const gchar*)key);
+ return FALSE;
+}
+
+void eo_list_object_types(void)
+{
+ eo_iterate_tables(list_exportobject_protocol, NULL);
+}
+
+gboolean eo_tap_opt_add(const char *option_string)
+{
+ gchar** splitted;
+
+ if (!eo_opts)
+ eo_opts = g_hash_table_new(g_str_hash,g_str_equal);
+
+ splitted = g_strsplit(option_string, ",", 2);
+
+ if ((splitted[0] == NULL) || (splitted[1] == NULL) || (get_eo_by_name(splitted[0]) == NULL))
+ {
+ fprintf(stderr, "tshark: \"--export-objects\" are specified as: <protocol>,<destdir>\n");
+ fprintf(stderr, "tshark: The available export object types for the \"--export-objects\" option are:\n");
+ eo_list_object_types();
+ }
+ else
+ {
+ gchar* dir = (gchar*)g_hash_table_lookup(eo_opts, splitted[0]);
+
+ /* Since we're saving all objects from a protocol,
+ it can only be listed once */
+ if (dir == NULL) {
+ g_hash_table_insert(eo_opts, splitted[0], splitted[1]);
+
+ g_free(splitted);
+ return TRUE;
+ }
+ else
+ {
+ cmdarg_err("\"--export-objects\" already specified protocol '%s'", splitted[0]);
+ }
+ }
+
+ g_strfreev(splitted);
+ return FALSE;
+}
+
+static void
+object_list_add_entry(void *gui_data, export_object_entry_t *entry)
+{
+ export_object_list_gui_t *object_list = (export_object_list_gui_t*)gui_data;
+
+ object_list->entries = g_slist_append(object_list->entries, entry);
+}
+
+static export_object_entry_t*
+object_list_get_entry(void *gui_data, int row) {
+ export_object_list_gui_t *object_list = (export_object_list_gui_t*)gui_data;
+
+ return (export_object_entry_t *)g_slist_nth_data(object_list->entries, row);
+}
+
+/* This is just for writing Exported Objects to a file */
+static void
+eo_draw(void *tapdata)
+{
+ export_object_list_t *tap_object = (export_object_list_t *)tapdata;
+ export_object_list_gui_t *object_list = (export_object_list_gui_t*)tap_object->gui_data;
+ GSList *slist = object_list->entries;
+ export_object_entry_t *entry;
+ gchar* save_in_path = (gchar*)g_hash_table_lookup(eo_opts, proto_get_protocol_filter_name(get_eo_proto_id(object_list->eo)));
+ GString *safe_filename = NULL;
+ gchar *save_as_fullpath = NULL;
+ guint count = 0;
+
+ if (!g_file_test(save_in_path, G_FILE_TEST_IS_DIR)) {
+ /* If the destination directory (or its parents) do not exist, create them. */
+ if (g_mkdir_with_parents(save_in_path, 0755) == -1) {
+ fprintf(stderr, "Failed to create export objects output directory \"%s\": %s\n",
+ save_in_path, g_strerror(errno));
+ return;
+ }
+ }
+
+ while (slist) {
+ entry = (export_object_entry_t *)slist->data;
+ do {
+ g_free(save_as_fullpath);
+ if (entry->filename) {
+ safe_filename = eo_massage_str(entry->filename,
+ EXPORT_OBJECT_MAXFILELEN, count);
+ } else {
+ char generic_name[EXPORT_OBJECT_MAXFILELEN+1];
+ const char *ext;
+ ext = eo_ct2ext(entry->content_type);
+ snprintf(generic_name, sizeof(generic_name),
+ "object%u%s%s", entry->pkt_num, ext ? "." : "", ext ? ext : "");
+ safe_filename = eo_massage_str(generic_name,
+ EXPORT_OBJECT_MAXFILELEN, count);
+ }
+ save_as_fullpath = g_build_filename(save_in_path, safe_filename->str, NULL);
+ g_string_free(safe_filename, TRUE);
+ } while (g_file_test(save_as_fullpath, G_FILE_TEST_EXISTS) && ++count < prefs.gui_max_export_objects);
+ count = 0;
+ write_file_binary_mode(save_as_fullpath, entry->payload_data, entry->payload_len);
+ g_free(save_as_fullpath);
+ save_as_fullpath = NULL;
+ slist = slist->next;
+ }
+}
+
+static void
+exportobject_handler(gpointer key, gpointer value _U_, gpointer user_data _U_)
+{
+ GString *error_msg;
+ export_object_list_t *tap_data;
+ export_object_list_gui_t *object_list;
+ register_eo_t* eo;
+
+ eo = get_eo_by_name((const char*)key);
+ if (eo == NULL)
+ {
+ cmdarg_err("\"--export-objects\" INTERNAL ERROR '%s' protocol not found", (const char*)key);
+ return;
+ }
+
+ tap_data = g_new0(export_object_list_t,1);
+ object_list = g_new0(export_object_list_gui_t,1);
+
+ tap_data->add_entry = object_list_add_entry;
+ tap_data->get_entry = object_list_get_entry;
+ tap_data->gui_data = (void*)object_list;
+
+ object_list->eo = eo;
+
+ /* Data will be gathered via a tap callback */
+ error_msg = register_tap_listener(get_eo_tap_listener_name(eo), tap_data, NULL, 0,
+ NULL, get_eo_packet_func(eo), eo_draw, NULL);
+
+ if (error_msg) {
+ cmdarg_err("Can't register %s tap: %s", (const char*)key, error_msg->str);
+ g_string_free(error_msg, TRUE);
+ g_free(tap_data);
+ g_free(object_list);
+ return;
+ }
+}
+
+void start_exportobjects(void)
+{
+ if (eo_opts != NULL)
+ g_hash_table_foreach(eo_opts, exportobject_handler, NULL);
+}
diff --git a/ui/cli/tap-exportobject.h b/ui/cli/tap-exportobject.h
new file mode 100644
index 00000000..49ebc265
--- /dev/null
+++ b/ui/cli/tap-exportobject.h
@@ -0,0 +1,41 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __TAP_EXPORT_OBJECT_H__
+#define __TAP_EXPORT_OBJECT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void eo_list_object_types(void);
+
+/* will be called by main each time a --export-objects option is found */
+gboolean eo_tap_opt_add(const char *ws_optarg);
+
+void start_exportobjects(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TAP_EXPORT_OBJECT_H__ */
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-flow.c b/ui/cli/tap-flow.c
new file mode 100644
index 00000000..32e0a94f
--- /dev/null
+++ b/ui/cli/tap-flow.c
@@ -0,0 +1,147 @@
+/* tap-flow.c
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* This module provides udp and tcp follow stream capabilities to tshark.
+ * It is only used by tshark and not wireshark.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <epan/sequence_analysis.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/tap.h>
+
+void register_tap_listener_flow(void);
+
+#define STR_FLOW "flow,"
+#define STR_STANDARD ",standard"
+#define STR_NETWORK ",network"
+
+WS_NORETURN static void flow_exit(const char *strp)
+{
+ fprintf(stderr, "tshark: flow - %s\n", strp);
+ exit(1);
+}
+
+static void
+flow_draw(void *arg)
+{
+ seq_analysis_info_t* flow_info = (seq_analysis_info_t*)arg;
+
+ sequence_analysis_get_nodes(flow_info);
+
+ sequence_analysis_dump_to_file(stdout, flow_info, 0);
+
+ //clean up the data
+ sequence_analysis_list_free(flow_info);
+ sequence_analysis_info_free(flow_info);
+}
+
+static gboolean flow_arg_strncmp(const char **opt_argp, const char *strp)
+{
+ size_t len = strlen(strp);
+
+ if (strncmp(*opt_argp, strp, len) == 0)
+ {
+ *opt_argp += len;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+flow_arg_mode(const char **opt_argp, seq_analysis_info_t *flow_info)
+{
+ if (flow_arg_strncmp(opt_argp, STR_STANDARD))
+ {
+ flow_info->any_addr = 1;
+ }
+ else if (flow_arg_strncmp(opt_argp, STR_NETWORK))
+ {
+ flow_info->any_addr = 0;
+ }
+ else
+ {
+ flow_exit("Invalid address type.");
+ }
+}
+
+static void
+flow_init(const char *opt_argp, void *userdata)
+{
+ seq_analysis_info_t *flow_info = g_new0(seq_analysis_info_t, 1);
+ GString *errp;
+ register_analysis_t* analysis = (register_analysis_t*)userdata;
+ const char *filter=NULL;
+
+ opt_argp += strlen(STR_FLOW);
+ opt_argp += strlen(sequence_analysis_get_name(analysis));
+
+ flow_arg_mode(&opt_argp, flow_info);
+ if (*opt_argp == ',') {
+ filter = opt_argp + 1;
+ }
+
+ sequence_analysis_list_free(flow_info);
+
+ errp = register_tap_listener(sequence_analysis_get_tap_listener_name(analysis), flow_info, filter, sequence_analysis_get_tap_flags(analysis),
+ NULL, sequence_analysis_get_packet_func(analysis), flow_draw, NULL);
+
+ if (errp != NULL)
+ {
+ sequence_analysis_list_free(flow_info);
+ sequence_analysis_info_free(flow_info);
+ g_string_free(errp, TRUE);
+ flow_exit("Error registering tap listener.");
+ }
+}
+
+static bool
+flow_register(const void *key _U_, void *value, void *userdata _U_)
+{
+ register_analysis_t* analysis = (register_analysis_t*)value;
+ stat_tap_ui flow_ui;
+ GString *cmd_str = g_string_new(STR_FLOW);
+ gchar *cli_string;
+
+ g_string_append(cmd_str, sequence_analysis_get_name(analysis));
+ cli_string = g_string_free(cmd_str, FALSE);
+
+ flow_ui.group = REGISTER_STAT_GROUP_GENERIC;
+ flow_ui.title = NULL; /* construct this from the protocol info? */
+ flow_ui.cli_string = cli_string;
+ flow_ui.tap_init_cb = flow_init;
+ flow_ui.nparams = 0;
+ flow_ui.params = NULL;
+ register_stat_tap_ui(&flow_ui, analysis);
+ g_free(cli_string);
+ return FALSE;
+}
+
+void
+register_tap_listener_flow(void)
+{
+ sequence_analysis_table_iterate_tables(flow_register, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/ui/cli/tap-follow.c b/ui/cli/tap-follow.c
new file mode 100644
index 00000000..51f8f0f7
--- /dev/null
+++ b/ui/cli/tap-follow.c
@@ -0,0 +1,639 @@
+/* tap-follow.c
+ *
+ * Copyright 2011-2013, QA Cafe <info@qacafe.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* This module provides udp and tcp follow stream capabilities to tshark.
+ * It is only used by tshark and not wireshark.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <epan/addr_resolv.h>
+#include <wsutil/str_util.h>
+#include <wsutil/unicode-utils.h>
+#include <epan/follow.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/tap.h>
+#include <wsutil/ws_assert.h>
+
+void register_tap_listener_follow(void);
+
+/* Show Type */
+typedef enum {
+ SHOW_ASCII,
+ SHOW_CARRAY,
+ SHOW_EBCDIC,
+ SHOW_HEXDUMP,
+ SHOW_RAW,
+ SHOW_CODEC, // Ordered to match UTF-8 combobox index
+ SHOW_YAML
+} show_type_t;
+
+typedef struct _cli_follow_info {
+ show_type_t show_type;
+ register_follow_t* follower;
+
+ /* range */
+ guint32 chunkMin;
+ guint32 chunkMax;
+
+ /* filter */
+ int stream_index;
+ int sub_stream_index;
+ int port[2];
+ address addr[2];
+ union {
+ guint32 addrBuf_v4;
+ ws_in6_addr addrBuf_v6;
+ } addrBuf[2];
+} cli_follow_info_t;
+
+
+#define STR_FOLLOW "follow,"
+
+#define STR_HEX ",hex"
+#define STR_ASCII ",ascii"
+#define STR_EBCDIC ",ebcdic"
+#define STR_RAW ",raw"
+#define STR_CODEC ",utf-8"
+#define STR_YAML ",yaml"
+
+WS_NORETURN static void follow_exit(const char *strp)
+{
+ fprintf(stderr, "tshark: follow - %s\n", strp);
+ exit(1);
+}
+
+static const char * follow_str_type(cli_follow_info_t* cli_follow_info)
+{
+ switch (cli_follow_info->show_type)
+ {
+ case SHOW_HEXDUMP: return "hex";
+ case SHOW_ASCII: return "ascii";
+ case SHOW_EBCDIC: return "ebcdic";
+ case SHOW_RAW: return "raw";
+ case SHOW_CODEC: return "utf-8";
+ case SHOW_YAML: return "yaml";
+ default:
+ ws_assert_not_reached();
+ break;
+ }
+
+ ws_assert_not_reached();
+
+ return "<unknown-mode>";
+}
+
+static void
+follow_free(follow_info_t *follow_info)
+{
+ cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
+
+ g_free(cli_follow_info);
+ follow_info_free(follow_info);
+}
+
+#define BYTES_PER_LINE 16
+#define OFFSET_LEN 8
+#define OFFSET_SPACE 2
+#define HEX_START (OFFSET_LEN + OFFSET_SPACE)
+#define HEX_LEN (BYTES_PER_LINE * 3) /* extra space at column 8 */
+#define HEX_SPACE 2
+#define ASCII_START (HEX_START + HEX_LEN + HEX_SPACE)
+#define ASCII_LEN (BYTES_PER_LINE + 1) /* extra space at column 8 */
+#define LINE_LEN (ASCII_START + ASCII_LEN)
+
+static const char bin2hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+static void follow_print_hex(const char *prefixp, guint32 offset, void *datap, int len)
+{
+ int ii;
+ int jj;
+ int kk;
+ guint8 val;
+ char line[LINE_LEN + 1];
+
+ for (ii = 0, jj = 0, kk = 0; ii < len; )
+ {
+ if ((ii % BYTES_PER_LINE) == 0)
+ {
+ /* new line */
+ snprintf(line, LINE_LEN + 1, "%0*X", OFFSET_LEN, offset);
+ memset(line + HEX_START - OFFSET_SPACE, ' ',
+ HEX_LEN + OFFSET_SPACE + HEX_SPACE);
+
+ /* offset of hex */
+ jj = HEX_START;
+
+ /* offset of ascii */
+ kk = ASCII_START;
+ }
+
+ val = ((guint8 *)datap)[ii];
+
+ line[jj++] = bin2hex[val >> 4];
+ line[jj++] = bin2hex[val & 0xf];
+ jj++;
+
+ line[kk++] = val >= ' ' && val < 0x7f ? val : '.';
+
+ /* extra space at column 8 */
+ if (++ii % BYTES_PER_LINE == BYTES_PER_LINE/2)
+ {
+ line[jj++] = ' ';
+ line[kk++] = ' ';
+ }
+
+ if ((ii % BYTES_PER_LINE) == 0 || ii == len)
+ {
+ /* end of line or buffer */
+ if (line[kk - 1] == ' ')
+ {
+ kk--;
+ }
+ line[kk] = 0;
+ printf("%s%s\n", prefixp, line);
+ offset += BYTES_PER_LINE;
+ }
+ }
+}
+
+static void follow_draw(void *contextp)
+{
+ static const char separator[] =
+ "===================================================================\n";
+
+ follow_info_t *follow_info = (follow_info_t*)contextp;
+ cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
+ gchar buf[WS_INET6_ADDRSTRLEN];
+ guint32 global_client_pos = 0, global_server_pos = 0;
+ guint32 *global_pos;
+ guint32 ii, jj;
+ char *buffer;
+ wmem_strbuf_t *strbuf;
+ GList *cur;
+ follow_record_t *follow_record;
+ guint chunk;
+ gchar *b64encoded;
+ const guint32 base64_raw_len = 57; /* Encodes to 76 bytes, common in RFCs */
+
+ /* Print header */
+ switch (cli_follow_info->show_type)
+ {
+ case SHOW_YAML:
+ printf("peers:\n");
+ printf(" - peer: 0\n");
+ address_to_str_buf(&follow_info->client_ip, buf, sizeof buf);
+ printf(" host: %s\n", buf);
+ printf(" port: %d\n", follow_info->client_port);
+ printf(" - peer: 1\n");
+ address_to_str_buf(&follow_info->server_ip, buf, sizeof buf);
+ printf(" host: %s\n", buf);
+ printf(" port: %d\n", follow_info->server_port);
+ printf("packets:\n");
+ break;
+
+ default:
+ printf("\n%s", separator);
+ printf("Follow: %s,%s\n", proto_get_protocol_filter_name(get_follow_proto_id(cli_follow_info->follower)), follow_str_type(cli_follow_info));
+ printf("Filter: %s\n", follow_info->filter_out_filter);
+
+ address_to_str_buf(&follow_info->client_ip, buf, sizeof buf);
+ if (follow_info->client_ip.type == AT_IPv6)
+ printf("Node 0: [%s]:%u\n", buf, follow_info->client_port);
+ else
+ printf("Node 0: %s:%u\n", buf, follow_info->client_port);
+
+ address_to_str_buf(&follow_info->server_ip, buf, sizeof buf);
+ if (follow_info->server_ip.type == AT_IPv6)
+ printf("Node 1: [%s]:%u\n", buf, follow_info->server_port);
+ else
+ printf("Node 1: %s:%u\n", buf, follow_info->server_port);
+ break;
+ }
+
+ for (cur = g_list_last(follow_info->payload), chunk = 1;
+ cur != NULL;
+ cur = g_list_previous(cur), chunk++)
+ {
+ follow_record = (follow_record_t *)cur->data;
+ if (!follow_record->is_server) {
+ global_pos = &global_client_pos;
+ } else {
+ global_pos = &global_server_pos;
+ }
+
+ /* ignore chunks not in range */
+ if ((chunk < cli_follow_info->chunkMin) || (chunk > cli_follow_info->chunkMax)) {
+ (*global_pos) += follow_record->data->len;
+ continue;
+ }
+
+ /* Print start of line */
+ switch (cli_follow_info->show_type)
+ {
+ case SHOW_HEXDUMP:
+ case SHOW_YAML:
+ case SHOW_CODEC: /* The transformation to UTF-8 can change the length */
+ break;
+
+ case SHOW_ASCII:
+ case SHOW_EBCDIC:
+ printf("%s%u\n", follow_record->is_server ? "\t" : "", follow_record->data->len);
+ break;
+
+ case SHOW_RAW:
+ if (follow_record->is_server)
+ {
+ putchar('\t');
+ }
+ break;
+
+ default:
+ ws_assert_not_reached();
+ }
+
+ /* Print data */
+ switch (cli_follow_info->show_type)
+ {
+ case SHOW_HEXDUMP:
+ follow_print_hex(follow_record->is_server ? "\t" : "", *global_pos, follow_record->data->data, follow_record->data->len);
+ (*global_pos) += follow_record->data->len;
+ break;
+
+ case SHOW_ASCII:
+ case SHOW_EBCDIC:
+ buffer = (char *)g_malloc(follow_record->data->len+2);
+
+ for (ii = 0; ii < follow_record->data->len; ii++)
+ {
+ switch (follow_record->data->data[ii])
+ {
+ // XXX: qt/follow_stream_dialog.c sanitize_buffer() also passes
+ // tabs ('\t') through. Should we do that here too?
+ // The Qt code has automatic universal new line handling for reading
+ // so, e.g., \r\n in HTML becomes just \n, but we don't do that here.
+ // (The Qt version doesn't write the file as Text, so all files use
+ // Unix line endings, including on Windows.)
+ case '\r':
+ case '\n':
+ buffer[ii] = follow_record->data->data[ii];
+ break;
+ default:
+ buffer[ii] = g_ascii_isprint(follow_record->data->data[ii]) ? follow_record->data->data[ii] : '.';
+ break;
+ }
+ }
+
+ buffer[ii++] = '\n';
+ buffer[ii] = 0;
+ if (cli_follow_info->show_type == SHOW_EBCDIC) {
+ EBCDIC_to_ASCII(buffer, ii);
+ }
+ printf("%s", buffer);
+ g_free(buffer);
+ break;
+
+ case SHOW_CODEC:
+ // This does the same as the Show As UTF-8 code in the Qt version
+ // (passing through all legal UTF-8, including control codes and
+ // internal NULs, substituting illegal UTF-8 sequences with
+ // REPLACEMENT CHARACTER, and not handling valid UTF-8 sequences
+ // which are split between unreassembled frames), except for the
+ // end of line terminator issue as above.
+ strbuf = ws_utf8_make_valid_strbuf(NULL, follow_record->data->data, follow_record->data->len);
+ printf("%s%zu\n", follow_record->is_server ? "\t" : "", wmem_strbuf_get_len(strbuf));
+ fwrite(wmem_strbuf_get_str(strbuf), 1, wmem_strbuf_get_len(strbuf), stdout);
+ wmem_strbuf_destroy(strbuf);
+ putchar('\n');
+ break;
+
+ case SHOW_RAW:
+ buffer = (char *)g_malloc((follow_record->data->len*2)+2);
+
+ for (ii = 0, jj = 0; ii < follow_record->data->len; ii++)
+ {
+ buffer[jj++] = bin2hex[follow_record->data->data[ii] >> 4];
+ buffer[jj++] = bin2hex[follow_record->data->data[ii] & 0xf];
+ }
+
+ buffer[jj++] = '\n';
+ buffer[jj] = 0;
+ printf("%s", buffer);
+ g_free(buffer);
+ break;
+
+ case SHOW_YAML:
+ printf(" - packet: %d\n", follow_record->packet_num);
+ printf(" peer: %d\n", follow_record->is_server ? 1 : 0);
+ printf(" timestamp: %.9f\n", nstime_to_sec(&follow_record->abs_ts));
+ printf(" data: !!binary |\n");
+ ii = 0;
+ while (ii < follow_record->data->len) {
+ guint32 len = ii + base64_raw_len < follow_record->data->len
+ ? base64_raw_len
+ : follow_record->data->len - ii;
+ b64encoded = g_base64_encode(&follow_record->data->data[ii], len);
+ printf(" %s\n", b64encoded);
+ g_free(b64encoded);
+ ii += len;
+ }
+ break;
+
+ default:
+ ws_assert_not_reached();
+ }
+ }
+
+ /* Print footer */
+ switch (cli_follow_info->show_type)
+ {
+ case SHOW_YAML:
+ break;
+
+ default:
+ printf("%s", separator);
+ break;
+ }
+}
+
+static gboolean follow_arg_strncmp(const char **opt_argp, const char *strp)
+{
+ size_t len = strlen(strp);
+
+ if (strncmp(*opt_argp, strp, len) == 0)
+ {
+ *opt_argp += len;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+follow_arg_mode(const char **opt_argp, follow_info_t *follow_info)
+{
+ cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
+
+ if (follow_arg_strncmp(opt_argp, STR_HEX))
+ {
+ cli_follow_info->show_type = SHOW_HEXDUMP;
+ }
+ else if (follow_arg_strncmp(opt_argp, STR_ASCII))
+ {
+ cli_follow_info->show_type = SHOW_ASCII;
+ }
+ else if (follow_arg_strncmp(opt_argp, STR_EBCDIC))
+ {
+ cli_follow_info->show_type = SHOW_EBCDIC;
+ }
+ else if (follow_arg_strncmp(opt_argp, STR_RAW))
+ {
+ cli_follow_info->show_type = SHOW_RAW;
+ }
+ else if (follow_arg_strncmp(opt_argp, STR_CODEC))
+ {
+ cli_follow_info->show_type = SHOW_CODEC;
+ }
+ else if (follow_arg_strncmp(opt_argp, STR_YAML))
+ {
+ cli_follow_info->show_type = SHOW_YAML;
+ }
+ else
+ {
+ follow_exit("Invalid display mode.");
+ }
+}
+
+#define _STRING(s) # s
+#define STRING(s) _STRING(s)
+
+#define ADDR_CHARS 80
+#define ADDR_LEN (ADDR_CHARS + 1)
+#define ADDRv6_FMT ",[%" STRING(ADDR_CHARS) "[^]]]:%d%n"
+#define ADDRv4_FMT ",%" STRING(ADDR_CHARS) "[^:]:%d%n"
+
+static void
+follow_arg_filter(const char **opt_argp, follow_info_t *follow_info)
+{
+ int len;
+ unsigned int ii;
+ char addr[ADDR_LEN];
+ cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
+ gboolean is_ipv6;
+
+ if (sscanf(*opt_argp, ",%d%n", &cli_follow_info->stream_index, &len) == 1 &&
+ ((*opt_argp)[len] == 0 || (*opt_argp)[len] == ','))
+ {
+ *opt_argp += len;
+
+ /* if it's HTTP2 or QUIC protocol we should read substream id otherwise it's a range parameter from follow_arg_range */
+ if (cli_follow_info->sub_stream_index == -1 && sscanf(*opt_argp, ",%d%n", &cli_follow_info->sub_stream_index, &len) == 1 &&
+ ((*opt_argp)[len] == 0 || (*opt_argp)[len] == ','))
+ {
+ *opt_argp += len;
+ follow_info->substream_id = cli_follow_info->sub_stream_index;
+ }
+ }
+ else
+ {
+ for (ii = 0; ii < sizeof cli_follow_info->addr/sizeof *cli_follow_info->addr; ii++)
+ {
+ if (sscanf(*opt_argp, ADDRv6_FMT, addr, &cli_follow_info->port[ii], &len) == 2)
+ {
+ is_ipv6 = TRUE;
+ }
+ else if (sscanf(*opt_argp, ADDRv4_FMT, addr, &cli_follow_info->port[ii], &len) == 2)
+ {
+ is_ipv6 = FALSE;
+ }
+ else
+ {
+ follow_exit("Invalid address.");
+ }
+
+ if (cli_follow_info->port[ii] <= 0 || cli_follow_info->port[ii] > G_MAXUINT16)
+ {
+ follow_exit("Invalid port.");
+ }
+
+ if (is_ipv6)
+ {
+ if (!get_host_ipaddr6(addr, &cli_follow_info->addrBuf[ii].addrBuf_v6))
+ {
+ follow_exit("Can't get IPv6 address");
+ }
+ set_address(&cli_follow_info->addr[ii], AT_IPv6, 16, (void *)&cli_follow_info->addrBuf[ii].addrBuf_v6);
+ }
+ else
+ {
+ if (!get_host_ipaddr(addr, &cli_follow_info->addrBuf[ii].addrBuf_v4))
+ {
+ follow_exit("Can't get IPv4 address");
+ }
+ set_address(&cli_follow_info->addr[ii], AT_IPv4, 4, (void *)&cli_follow_info->addrBuf[ii].addrBuf_v4);
+ }
+
+ *opt_argp += len;
+ }
+
+ if (cli_follow_info->addr[0].type != cli_follow_info->addr[1].type)
+ {
+ follow_exit("Mismatched IP address types.");
+ }
+ cli_follow_info->stream_index = -1;
+ }
+}
+
+static void follow_arg_range(const char **opt_argp, cli_follow_info_t* cli_follow_info)
+{
+ int len;
+
+ if (**opt_argp == 0)
+ {
+ cli_follow_info->chunkMin = 1;
+ cli_follow_info->chunkMax = G_MAXUINT32;
+ }
+ else
+ {
+ if (sscanf(*opt_argp, ",%u-%u%n", &cli_follow_info->chunkMin, &cli_follow_info->chunkMax, &len) == 2)
+ {
+ *opt_argp += len;
+ }
+ else if (sscanf(*opt_argp, ",%u%n", &cli_follow_info->chunkMin, &len) == 1)
+ {
+ cli_follow_info->chunkMax = cli_follow_info->chunkMin;
+ *opt_argp += len;
+ }
+ else
+ {
+ follow_exit("Invalid range.");
+ }
+
+ if (cli_follow_info->chunkMin < 1 || cli_follow_info->chunkMin > cli_follow_info->chunkMax)
+ {
+ follow_exit("Invalid range value.");
+ }
+ }
+}
+
+static void
+follow_arg_done(const char *opt_argp)
+{
+ if (*opt_argp != 0)
+ {
+ follow_exit("Invalid parameter.");
+ }
+}
+
+static void follow_stream(const char *opt_argp, void *userdata)
+{
+ follow_info_t *follow_info;
+ cli_follow_info_t* cli_follow_info;
+ GString *errp;
+ register_follow_t* follower = (register_follow_t*)userdata;
+ follow_index_filter_func index_filter;
+ follow_address_filter_func address_filter;
+ int proto_id = get_follow_proto_id(follower);
+ const char* proto_filter_name = proto_get_protocol_filter_name(proto_id);
+
+ opt_argp += strlen(STR_FOLLOW);
+ opt_argp += strlen(proto_filter_name);
+
+ cli_follow_info = g_new0(cli_follow_info_t, 1);
+ cli_follow_info->stream_index = -1;
+ /* use second parameter only for followers that have sub streams
+ * (currently HTTP2 or QUIC) */
+ if (get_follow_sub_stream_id_func(follower)) {
+ cli_follow_info->sub_stream_index = -1;
+ } else {
+ cli_follow_info->sub_stream_index = 0;
+ }
+ follow_info = g_new0(follow_info_t, 1);
+ follow_info->gui_data = cli_follow_info;
+ follow_info->substream_id = SUBSTREAM_UNUSED;
+ cli_follow_info->follower = follower;
+
+ follow_arg_mode(&opt_argp, follow_info);
+ follow_arg_filter(&opt_argp, follow_info);
+ follow_arg_range(&opt_argp, cli_follow_info);
+ follow_arg_done(opt_argp);
+
+ if (cli_follow_info->stream_index >= 0)
+ {
+ index_filter = get_follow_index_func(follower);
+ follow_info->filter_out_filter = index_filter(cli_follow_info->stream_index, cli_follow_info->sub_stream_index);
+ if (follow_info->filter_out_filter == NULL || cli_follow_info->sub_stream_index < 0)
+ {
+ follow_exit("Error creating filter for this stream.");
+ }
+ }
+ else
+ {
+ address_filter = get_follow_address_func(follower);
+ follow_info->filter_out_filter = address_filter(&cli_follow_info->addr[0], &cli_follow_info->addr[1], cli_follow_info->port[0], cli_follow_info->port[1]);
+ if (follow_info->filter_out_filter == NULL)
+ {
+ follow_exit("Error creating filter for this address/port pair.\n");
+ }
+ }
+
+ errp = register_tap_listener(get_follow_tap_string(follower), follow_info, follow_info->filter_out_filter, 0,
+ NULL, get_follow_tap_handler(follower), follow_draw, (tap_finish_cb)follow_free);
+
+ if (errp != NULL)
+ {
+ follow_free(follow_info);
+ g_string_free(errp, TRUE);
+ follow_exit("Error registering tap listener.");
+ }
+}
+
+static bool
+follow_register(const void *key _U_, void *value, void *userdata _U_)
+{
+ register_follow_t *follower = (register_follow_t*)value;
+ stat_tap_ui follow_ui;
+ gchar *cli_string;
+
+ cli_string = follow_get_stat_tap_string(follower);
+ follow_ui.group = REGISTER_STAT_GROUP_GENERIC;
+ follow_ui.title = NULL; /* construct this from the protocol info? */
+ follow_ui.cli_string = cli_string;
+ follow_ui.tap_init_cb = follow_stream;
+ follow_ui.nparams = 0;
+ follow_ui.params = NULL;
+ register_stat_tap_ui(&follow_ui, follower);
+ g_free(cli_string);
+ return FALSE;
+}
+
+void
+register_tap_listener_follow(void)
+{
+ follow_iterate_followers(follow_register, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/ui/cli/tap-funnel.c b/ui/cli/tap-funnel.c
new file mode 100644
index 00000000..9adfc296
--- /dev/null
+++ b/ui/cli/tap-funnel.c
@@ -0,0 +1,187 @@
+/*
+ * tap-funnel.c
+ *
+ * EPAN's GUI mini-API
+ *
+ * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org>
+ *
+ * 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 <epan/funnel.h>
+#include <stdio.h>
+
+#include "ws_attributes.h"
+
+#include <wsutil/wslog.h>
+
+void register_tap_listener_funnel(void);
+
+struct _funnel_text_window_t {
+ gchar *title;
+ GString *text;
+};
+
+static GPtrArray *text_windows = NULL;
+
+static funnel_text_window_t *new_text_window(funnel_ops_id_t *ops_id _U_, const gchar *title) {
+ funnel_text_window_t *tw = g_new(funnel_text_window_t, 1);
+ tw->title = g_strdup(title);
+ tw->text = g_string_new("");
+
+ if (!text_windows)
+ text_windows = g_ptr_array_new();
+
+ g_ptr_array_add(text_windows, tw);
+
+ return tw;
+}
+
+static void text_window_clear(funnel_text_window_t *tw) {
+ g_string_free(tw->text, TRUE);
+ tw->text = g_string_new("");
+}
+
+static void text_window_append(funnel_text_window_t *tw, const char *text ) {
+ g_string_append(tw->text, text);
+}
+
+static void text_window_set_text(funnel_text_window_t *tw, const char *text) {
+ g_string_free(tw->text, TRUE);
+ tw->text = g_string_new(text);
+}
+
+static void text_window_prepend(funnel_text_window_t *tw, const char *text) {
+ g_string_prepend(tw->text, text);
+}
+
+static const gchar *text_window_get_text(funnel_text_window_t *tw) {
+ return tw->text->str;
+}
+
+
+static const funnel_ops_t funnel_ops = {
+ NULL,
+ new_text_window,
+ text_window_set_text,
+ text_window_append,
+ text_window_prepend,
+ text_window_clear,
+ text_window_get_text,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ /*...,*/
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+void initialize_funnel_ops(void) {
+ funnel_set_funnel_ops(&funnel_ops);
+}
+
+
+void funnel_dump_all_text_windows(void) {
+ guint i;
+
+ if (!text_windows) return;
+
+ for ( i = 0 ; i < text_windows->len; i++) {
+ funnel_text_window_t *tw = (funnel_text_window_t *)g_ptr_array_index(text_windows, i);
+ printf("\n========================== %s "
+ "==========================\n%s\n", tw->title, tw->text->str);
+
+ g_ptr_array_remove_index(text_windows, i);
+ g_free(tw->title);
+ g_string_free(tw->text, TRUE);
+ g_free(tw);
+ }
+}
+
+#if 0
+
+GHashTable *menus = NULL;
+typedef struct _menu_cb_t {
+ void (*callback)(gpointer);
+ void *callback_data;
+} menu_cb_t;
+
+
+static void init_funnel_cmd(const char *opt_arg, void *data ) {
+ gchar **args = g_strsplit(opt_arg, ",", 0);
+ gchar **arg;
+ menu_cb_t *mcb = data;
+
+ for (arg = args; *arg ; arg++) {
+ g_strstrip(*arg);
+ }
+
+ if (mcb->callback) {
+ mcb->callback(mcb->callback_data);
+ }
+
+}
+
+static void register_menu_cb(const char *name,
+ register_stat_group_t group _U_,
+ void (*callback)(gpointer),
+ gpointer callback_data,
+ gboolean retap _U_) {
+ menu_cb_t* mcb = g_new(menu_cb_t, 1);
+ stat_tap_ui ui_info;
+
+ mcb->callback = callback;
+ mcb->callback_data = callback_data;
+
+ if (!menus)
+ menus = g_hash_table_new(g_str_hash, g_str_equal);
+
+ g_hash_table_insert(menus, g_strdup(name), mcb);
+
+ ui_info.group = REGISTER_STAT_GROUP_GENERIC;
+ ui_info.title = NULL;
+ ui_info.cli_string = name;
+ ui_info.tap_init_cb = init_funnel_cmd;
+ ui_info.nparams = 0;
+ ui_info.params = NULL;
+ register_stat_tap_ui(&ui_info, mcb);
+}
+
+void initialize_funnel_ops(void) {
+ funnel_set_funnel_ops(&funnel_ops);
+}
+
+#endif
+void
+register_tap_listener_funnel(void)
+{
+#if 0
+ /* #if 0 at least since Revision Rev 17396 */
+ funnel_register_all_menus(register_menu_cb);
+#endif
+}
diff --git a/ui/cli/tap-gsm_astat.c b/ui/cli/tap-gsm_astat.c
new file mode 100644
index 00000000..1f0a7924
--- /dev/null
+++ b/ui/cli/tap-gsm_astat.c
@@ -0,0 +1,346 @@
+/* tap-gsm_astat.c
+ *
+ * Copyright 2003, Michael Lum <mlum [AT] telostech.com>
+ * In association with Telos Technology Inc.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * This TAP provides statistics for the GSM A Interface:
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/dissectors/packet-bssap.h>
+#include <epan/dissectors/packet-gsm_a_common.h>
+
+void register_tap_listener_gsm_astat(void);
+
+typedef struct _gsm_a_stat_t {
+ int bssmap_message_type[0x100];
+ int dtap_mm_message_type[0x100];
+ int dtap_rr_message_type[0x100];
+ int dtap_cc_message_type[0x100];
+ int dtap_gmm_message_type[0x100];
+ int dtap_sms_message_type[0x100];
+ int dtap_sm_message_type[0x100];
+ int dtap_ss_message_type[0x100];
+ int dtap_tp_message_type[0x100];
+ int sacch_rr_message_type[0x100];
+} gsm_a_stat_t;
+
+
+static tap_packet_status
+gsm_a_stat_packet(
+ void *tapdata,
+ packet_info *pinfo _U_,
+ epan_dissect_t *edt _U_,
+ const void *data,
+ tap_flags_t flags _U_)
+{
+ gsm_a_stat_t *stat_p = (gsm_a_stat_t *)tapdata;
+ const gsm_a_tap_rec_t *tap_p = (const gsm_a_tap_rec_t *)data;
+
+ switch (tap_p->pdu_type)
+ {
+ case BSSAP_PDU_TYPE_BSSMAP:
+ stat_p->bssmap_message_type[tap_p->message_type]++;
+ break;
+
+ case BSSAP_PDU_TYPE_DTAP:
+ switch (tap_p->protocol_disc)
+ {
+ case PD_CC:
+ stat_p->dtap_cc_message_type[tap_p->message_type]++;
+ break;
+ case PD_MM:
+ stat_p->dtap_mm_message_type[tap_p->message_type]++;
+ break;
+ case PD_RR:
+ stat_p->dtap_rr_message_type[tap_p->message_type]++;
+ break;
+ case PD_GMM:
+ stat_p->dtap_gmm_message_type[tap_p->message_type]++;
+ break;
+ case PD_SMS:
+ stat_p->dtap_sms_message_type[tap_p->message_type]++;
+ break;
+ case PD_SM:
+ stat_p->dtap_sm_message_type[tap_p->message_type]++;
+ break;
+ case PD_SS:
+ stat_p->dtap_ss_message_type[tap_p->message_type]++;
+ break;
+ case PD_TP:
+ stat_p->dtap_tp_message_type[tap_p->message_type]++;
+ break;
+ default:
+ /*
+ * unsupported PD
+ */
+ return(TAP_PACKET_DONT_REDRAW);
+ }
+ break;
+
+ case GSM_A_PDU_TYPE_SACCH:
+ switch (tap_p->protocol_disc)
+ {
+ case 0:
+ stat_p->sacch_rr_message_type[tap_p->message_type]++;
+ break;
+ default:
+ /* unknown Short PD */
+ break;
+ }
+ break;
+
+ default:
+ /*
+ * unknown PDU type !!!
+ */
+ return(TAP_PACKET_DONT_REDRAW);
+ }
+
+ return(TAP_PACKET_REDRAW);
+}
+
+
+static void
+gsm_a_stat_draw(
+ void *tapdata)
+{
+ gsm_a_stat_t *stat_p = (gsm_a_stat_t *)tapdata;
+ guint8 i;
+
+
+ printf("\n");
+ printf("=========== GS=M A-i/f Statistics ============================\n");
+ printf("BSSMAP\n");
+ printf("Message (ID)Type Number\n");
+
+ i = 0;
+ while (gsm_a_bssmap_msg_strings[i].strptr)
+ {
+ if (stat_p->bssmap_message_type[gsm_a_bssmap_msg_strings[i].value] > 0)
+ {
+ printf("0x%02x %-50s%d\n",
+ gsm_a_bssmap_msg_strings[i].value,
+ gsm_a_bssmap_msg_strings[i].strptr,
+ stat_p->bssmap_message_type[gsm_a_bssmap_msg_strings[i].value]);
+ }
+
+ i++;
+ }
+
+ printf("\nDTAP %s\n", gsm_a_pd_str[PD_MM]);
+ printf("Message (ID)Type Number\n");
+
+ i = 0;
+ while (gsm_a_dtap_msg_mm_strings[i].strptr)
+ {
+ if (stat_p->dtap_mm_message_type[gsm_a_dtap_msg_mm_strings[i].value] > 0)
+ {
+ printf("0x%02x %-50s%d\n",
+ gsm_a_dtap_msg_mm_strings[i].value,
+ gsm_a_dtap_msg_mm_strings[i].strptr,
+ stat_p->dtap_mm_message_type[gsm_a_dtap_msg_mm_strings[i].value]);
+ }
+
+ i++;
+ }
+
+ printf("\nDTAP %s\n", gsm_a_pd_str[PD_RR]);
+ printf("Message (ID)Type Number\n");
+
+ i = 0;
+ while (gsm_a_dtap_msg_rr_strings[i].strptr)
+ {
+ if (stat_p->dtap_rr_message_type[gsm_a_dtap_msg_rr_strings[i].value] > 0)
+ {
+ printf("0x%02x %-50s%d\n",
+ gsm_a_dtap_msg_rr_strings[i].value,
+ gsm_a_dtap_msg_rr_strings[i].strptr,
+ stat_p->dtap_rr_message_type[gsm_a_dtap_msg_rr_strings[i].value]);
+ }
+
+ i++;
+ }
+
+ printf("\nDTAP %s\n", gsm_a_pd_str[PD_CC]);
+ printf("Message (ID)Type Number\n");
+
+ i = 0;
+ while (gsm_a_dtap_msg_cc_strings[i].strptr)
+ {
+ if (stat_p->dtap_cc_message_type[gsm_a_dtap_msg_cc_strings[i].value] > 0)
+ {
+ printf("0x%02x %-50s%d\n",
+ gsm_a_dtap_msg_cc_strings[i].value,
+ gsm_a_dtap_msg_cc_strings[i].strptr,
+ stat_p->dtap_cc_message_type[gsm_a_dtap_msg_cc_strings[i].value]);
+ }
+
+ i++;
+ }
+
+ printf("\nDTAP %s\n", gsm_a_pd_str[PD_GMM]);
+ printf("Message (ID)Type Number\n");
+
+ i = 0;
+ while (gsm_a_dtap_msg_gmm_strings[i].strptr)
+ {
+ if (stat_p->dtap_gmm_message_type[gsm_a_dtap_msg_gmm_strings[i].value] > 0)
+ {
+ printf("0x%02x %-50s%d\n",
+ gsm_a_dtap_msg_gmm_strings[i].value,
+ gsm_a_dtap_msg_gmm_strings[i].strptr,
+ stat_p->dtap_gmm_message_type[gsm_a_dtap_msg_gmm_strings[i].value]);
+ }
+
+ i++;
+ }
+
+ printf("\nDTAP %s\n", gsm_a_pd_str[PD_SMS]);
+ printf("Message (ID)Type Number\n");
+
+ i = 0;
+ while (gsm_a_dtap_msg_sms_strings[i].strptr)
+ {
+ if (stat_p->dtap_sms_message_type[gsm_a_dtap_msg_sms_strings[i].value] > 0)
+ {
+ printf("0x%02x %-50s%d\n",
+ gsm_a_dtap_msg_sms_strings[i].value,
+ gsm_a_dtap_msg_sms_strings[i].strptr,
+ stat_p->dtap_sms_message_type[gsm_a_dtap_msg_sms_strings[i].value]);
+ }
+
+ i++;
+ }
+
+ printf("\nDTAP %s\n", gsm_a_pd_str[PD_SM]);
+ printf("Message (ID)Type Number\n");
+
+ i = 0;
+ while (gsm_a_dtap_msg_sm_strings[i].strptr)
+ {
+ if (stat_p->dtap_sm_message_type[gsm_a_dtap_msg_sm_strings[i].value] > 0)
+ {
+ printf("0x%02x %-50s%d\n",
+ gsm_a_dtap_msg_sm_strings[i].value,
+ gsm_a_dtap_msg_sm_strings[i].strptr,
+ stat_p->dtap_sm_message_type[gsm_a_dtap_msg_sm_strings[i].value]);
+ }
+
+ i++;
+ }
+
+ printf("\nDTAP %s\n", gsm_a_pd_str[PD_SS]);
+ printf("Message (ID)Type Number\n");
+
+ i = 0;
+ while (gsm_a_dtap_msg_ss_strings[i].strptr)
+ {
+ if (stat_p->dtap_ss_message_type[gsm_a_dtap_msg_ss_strings[i].value] > 0)
+ {
+ printf("0x%02x %-50s%d\n",
+ gsm_a_dtap_msg_ss_strings[i].value,
+ gsm_a_dtap_msg_ss_strings[i].strptr,
+ stat_p->dtap_ss_message_type[gsm_a_dtap_msg_ss_strings[i].value]);
+ }
+
+ i++;
+ }
+
+ printf("\nDTAP %s\n", gsm_a_pd_str[PD_TP]);
+ printf("Message (ID)Type Number\n");
+
+ i = 0;
+ while (gsm_a_dtap_msg_tp_strings[i].strptr)
+ {
+ if (stat_p->dtap_tp_message_type[gsm_a_dtap_msg_tp_strings[i].value] > 0)
+ {
+ printf("0x%02x %-50s%d\n",
+ gsm_a_dtap_msg_tp_strings[i].value,
+ gsm_a_dtap_msg_tp_strings[i].strptr,
+ stat_p->dtap_tp_message_type[gsm_a_dtap_msg_tp_strings[i].value]);
+ }
+
+ i++;
+ }
+
+ printf("\nSACCH Radio Resources Management messages\n");
+ printf("Message (ID)Type Number\n");
+
+ i = 0;
+ while (gsm_a_rr_short_pd_msg_strings[i].strptr)
+ {
+ if (stat_p->sacch_rr_message_type[gsm_a_rr_short_pd_msg_strings[i].value] > 0)
+ {
+ printf("0x%02x %-50s%d\n",
+ gsm_a_rr_short_pd_msg_strings[i].value,
+ gsm_a_rr_short_pd_msg_strings[i].strptr,
+ stat_p->sacch_rr_message_type[gsm_a_rr_short_pd_msg_strings[i].value]);
+ }
+
+ i++;
+ }
+
+ printf("==============================================================\n");
+}
+
+
+static void
+gsm_a_stat_init(const char *opt_arg _U_, void *userdata _U_)
+{
+ gsm_a_stat_t *stat_p;
+ GString *err_p;
+
+ stat_p = g_new(gsm_a_stat_t, 1);
+
+ memset(stat_p, 0, sizeof(gsm_a_stat_t));
+
+ err_p =
+ register_tap_listener("gsm_a", stat_p, NULL, 0,
+ NULL,
+ gsm_a_stat_packet,
+ gsm_a_stat_draw,
+ NULL);
+
+ if (err_p != NULL)
+ {
+ g_free(stat_p);
+ g_string_free(err_p, TRUE);
+
+ exit(1);
+ }
+}
+
+static stat_tap_ui gsm_a_stat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "gsm_a",
+ gsm_a_stat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_gsm_astat(void)
+{
+ register_stat_tap_ui(&gsm_a_stat_ui, NULL);
+}
diff --git a/ui/cli/tap-hosts.c b/ui/cli/tap-hosts.c
new file mode 100644
index 00000000..c854e9e9
--- /dev/null
+++ b/ui/cli/tap-hosts.c
@@ -0,0 +1,157 @@
+/* tap-hosts.c
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* Dump our collected IPv4- and IPv6-to-hostname mappings */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "globals.h"
+
+#include <epan/packet.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/addr_resolv.h>
+
+#include <wsutil/cmdarg_err.h>
+
+void register_tap_listener_hosts(void);
+
+static gboolean dump_v4 = FALSE;
+static gboolean dump_v6 = FALSE;
+
+#define TAP_NAME "hosts"
+
+static void
+ipv4_hash_table_print_resolved(gpointer key _U_, gpointer value, gpointer user_data _U_)
+{
+ hashipv4_t *ipv4_hash_table_entry = (hashipv4_t *)value;
+
+ if ((ipv4_hash_table_entry->flags & NAME_RESOLVED)) {
+ printf("%s\t%s\n",
+ ipv4_hash_table_entry->ip,
+ ipv4_hash_table_entry->name);
+ }
+}
+
+static void
+ipv6_hash_table_print_resolved(gpointer key _U_, gpointer value, gpointer user_data _U_)
+{
+ hashipv6_t *ipv6_hash_table_entry = (hashipv6_t *)value;
+
+ if ((ipv6_hash_table_entry->flags & NAME_RESOLVED)) {
+ printf("%s\t%s\n",
+ ipv6_hash_table_entry->ip6,
+ ipv6_hash_table_entry->name);
+ }
+}
+
+static void
+hosts_draw(void *dummy _U_)
+{
+
+ wmem_map_t *ipv4_hash_table;
+ wmem_map_t *ipv6_hash_table;
+
+ printf("# TShark hosts output\n");
+ printf("#\n");
+ printf("# Host data gathered from %s\n",
+ cfile.is_tempfile ? "the temporary capture file" : cfile.filename);
+ printf("\n");
+
+ if (dump_v4) {
+ ipv4_hash_table = get_ipv4_hash_table();
+ if (ipv4_hash_table) {
+ wmem_map_foreach( ipv4_hash_table, ipv4_hash_table_print_resolved, NULL);
+ }
+ }
+
+ if (dump_v6) {
+ ipv6_hash_table = get_ipv6_hash_table();
+ if (ipv6_hash_table) {
+ wmem_map_foreach( ipv6_hash_table, ipv6_hash_table_print_resolved, NULL);
+ }
+ }
+
+}
+
+
+static void
+hosts_init(const char *opt_arg, void *userdata _U_)
+{
+ GString *error_string;
+ gchar **tokens;
+ gint opt_count;
+
+ dump_v4 = FALSE;
+ dump_v6 = FALSE;
+
+ if (strcmp(TAP_NAME, opt_arg) == 0) {
+ /* No arguments; dump everything */
+ dump_v4 = TRUE;
+ dump_v6 = TRUE;
+ } else {
+ tokens = g_strsplit(opt_arg, ",", 0);
+ opt_count = 0;
+ while (tokens[opt_count]) {
+ if ((strcmp("ipv4", tokens[opt_count]) == 0) ||
+ (strcmp("ip", tokens[opt_count]) == 0)) {
+ dump_v4 = TRUE;
+ } else if (strcmp("ipv6", tokens[opt_count]) == 0) {
+ dump_v6 = TRUE;
+ } else if (opt_count > 0) {
+ cmdarg_err("invalid \"-z " TAP_NAME "[,ip|ipv4|ipv6]\" argument");
+ exit(1);
+ }
+ opt_count++;
+ }
+ g_strfreev(tokens);
+ }
+
+ error_string = register_tap_listener("frame", NULL, NULL, TL_REQUIRES_PROTO_TREE,
+ NULL, NULL, hosts_draw, NULL);
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ cmdarg_err("Couldn't register " TAP_NAME " tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static stat_tap_ui hosts_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ TAP_NAME,
+ hosts_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_hosts(void)
+{
+ register_stat_tap_ui(&hosts_ui, NULL);
+}
+
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-httpstat.c b/ui/cli/tap-httpstat.c
new file mode 100644
index 00000000..7a5e593e
--- /dev/null
+++ b/ui/cli/tap-httpstat.c
@@ -0,0 +1,297 @@
+/* tap-httpstat.c
+ * tap-httpstat 2003 Jean-Michel FAYARD
+ *
+ * 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 <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/dissectors/packet-http.h>
+
+#include <wsutil/wslog.h>
+
+#include <wsutil/cmdarg_err.h>
+
+void register_tap_listener_httpstat(void);
+
+/* used to keep track of the statictics for an entire program interface */
+typedef struct _http_stats_t {
+ char *filter;
+ GHashTable *hash_responses;
+ GHashTable *hash_requests;
+} httpstat_t;
+
+/* used to keep track of the stats for a specific response code
+ * for example it can be { 3, 404, "Not Found" ,...}
+ * which means we captured 3 reply http/1.1 404 Not Found */
+typedef struct _http_response_code_t {
+ guint32 packets; /* 3 */
+ guint response_code; /* 404 */
+ const gchar *name; /* Not Found */
+ httpstat_t *sp;
+} http_response_code_t;
+
+/* used to keep track of the stats for a specific request string */
+typedef struct _http_request_methode_t {
+ gchar *response; /* eg. : GET */
+ guint32 packets;
+ httpstat_t *sp;
+} http_request_methode_t;
+
+
+/* insert some entries */
+static void
+http_init_hash(httpstat_t *sp)
+{
+ int i;
+
+ sp->hash_responses = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+
+ for (i=0; vals_http_status_code[i].strptr; i++)
+ {
+ http_response_code_t *sc = g_new (http_response_code_t, 1);
+ sc->packets = 0;
+ sc->response_code = vals_http_status_code[i].value;
+ sc->name = vals_http_status_code[i].strptr;
+ sc->sp = sp;
+ g_hash_table_insert(sc->sp->hash_responses, GUINT_TO_POINTER(vals_http_status_code[i].value), sc);
+ }
+ sp->hash_requests = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
+}
+
+static void
+http_draw_hash_requests(gchar *key _U_, http_request_methode_t *data, gchar *format)
+{
+ if (data->packets == 0)
+ return;
+ printf(format, data->response, data->packets);
+}
+
+static void
+http_draw_hash_responses(gint * key _U_, http_response_code_t *data, char *format)
+{
+ if (data == NULL) {
+ ws_warning("No data available, key=%d\n", *key);
+ exit(EXIT_FAILURE);
+ }
+ if (data->packets == 0)
+ return;
+ /* " %3d %-35s %9d packets", */
+ /* The maximum existing response code length is 32 characters */
+ printf(format, data->response_code, data->name, data->packets);
+}
+
+/* NOT USED at this moment */
+/*
+static void
+http_free_hash(gpointer key, gpointer value, gpointer user_data _U_)
+{
+ g_free(key);
+ g_free(value);
+}
+*/
+static void
+http_reset_hash_responses(gchar *key _U_, http_response_code_t *data, gpointer ptr _U_)
+{
+ data->packets = 0;
+}
+static void
+http_reset_hash_requests(gchar *key _U_, http_request_methode_t *data, gpointer ptr _U_)
+{
+ data->packets = 0;
+}
+
+static void
+httpstat_reset(void *psp)
+{
+ httpstat_t *sp = (httpstat_t *)psp;
+
+ g_hash_table_foreach(sp->hash_responses, (GHFunc)http_reset_hash_responses, NULL);
+ g_hash_table_foreach(sp->hash_requests, (GHFunc)http_reset_hash_requests, NULL);
+
+}
+
+static void
+httpstat_finish(void *psp)
+{
+ httpstat_t *sp = (httpstat_t *)psp;
+
+ g_free(sp->filter);
+ g_hash_table_destroy(sp->hash_responses);
+ g_hash_table_destroy(sp->hash_requests);
+ g_free(sp);
+}
+
+static tap_packet_status
+httpstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri, tap_flags_t flags _U_)
+{
+ const http_info_value_t *value = (const http_info_value_t *)pri;
+ httpstat_t *sp = (httpstat_t *)psp;
+
+ /* We are only interested in reply packets with a status code */
+ /* Request or reply packets ? */
+ if (value->response_code != 0) {
+ http_response_code_t *sc;
+ guint key = value->response_code;
+
+ sc = (http_response_code_t *)g_hash_table_lookup(
+ sp->hash_responses,
+ GUINT_TO_POINTER(key));
+ if (sc == NULL) {
+ /* non standard status code ; we classify it as others
+ * in the relevant category (Informational,Success,Redirection,Client Error,Server Error)
+ */
+ int i = value->response_code;
+ if ((i < 100) || (i >= 600)) {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+ else if (i < 200) {
+ key = 199; /* Hopefully, this status code will never be used */
+ }
+ else if (i < 300) {
+ key = 299;
+ }
+ else if (i < 400) {
+ key = 399;
+ }
+ else if (i < 500) {
+ key = 499;
+ }
+ else{
+ key = 599;
+ }
+ sc = (http_response_code_t *)g_hash_table_lookup(
+ sp->hash_responses,
+ GUINT_TO_POINTER(key));
+ if (sc == NULL)
+ return TAP_PACKET_DONT_REDRAW;
+ }
+ sc->packets++;
+ }
+ else if (value->request_method) {
+ http_request_methode_t *sc;
+
+ sc = (http_request_methode_t *)g_hash_table_lookup(
+ sp->hash_requests,
+ value->request_method);
+ if (sc == NULL) {
+ sc = g_new(http_request_methode_t, 1);
+ sc->response = g_strdup(value->request_method);
+ sc->packets = 1;
+ sc->sp = sp;
+ g_hash_table_insert(sp->hash_requests, sc->response, sc);
+ } else {
+ sc->packets++;
+ }
+ } else {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+ return TAP_PACKET_REDRAW;
+}
+
+
+static void
+httpstat_draw(void *psp)
+{
+ httpstat_t *sp = (httpstat_t *)psp;
+ printf("\n");
+ printf("===================================================================\n");
+ if (! sp->filter || ! sp->filter[0])
+ printf("HTTP Statistics\n");
+ else
+ printf("HTTP Statistics with filter %s\n", sp->filter);
+
+ printf("* HTTP Response Status Codes Packets\n");
+ g_hash_table_foreach(sp->hash_responses, (GHFunc)http_draw_hash_responses,
+ (gpointer)" %3d %-35s %9d\n");
+ printf("* HTTP Request Methods Packets\n");
+ g_hash_table_foreach(sp->hash_requests, (GHFunc)http_draw_hash_requests,
+ (gpointer)" %-39s %9d \n");
+ printf("===================================================================\n");
+}
+
+
+
+/* When called, this function will create a new instance of httpstat.
+ */
+static void
+httpstat_init(const char *opt_arg, void *userdata _U_)
+{
+ httpstat_t *sp;
+ const char *filter = NULL;
+ GString *error_string;
+
+ if (!strncmp (opt_arg, "http,stat,", 10)) {
+ filter = opt_arg+10;
+ } else {
+ filter = NULL;
+ }
+
+ sp = g_new(httpstat_t, 1);
+ sp->filter = g_strdup(filter);
+ /*g_hash_table_foreach(http_status, (GHFunc)http_reset_hash_responses, NULL);*/
+
+
+ error_string = register_tap_listener(
+ "http",
+ sp,
+ filter,
+ 0,
+ httpstat_reset,
+ httpstat_packet,
+ httpstat_draw,
+ httpstat_finish);
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ g_free(sp->filter);
+ g_free(sp);
+ cmdarg_err("Couldn't register http,stat tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ http_init_hash(sp);
+}
+
+static stat_tap_ui httpstat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "http,stat",
+ httpstat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_httpstat(void)
+{
+ register_stat_tap_ui(&httpstat_ui, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-icmpstat.c b/ui/cli/tap-icmpstat.c
new file mode 100644
index 00000000..ac0f6423
--- /dev/null
+++ b/ui/cli/tap-icmpstat.c
@@ -0,0 +1,315 @@
+/* tap-icmpstat.c
+ * icmpstat 2011 Christopher Maynard
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* This module provides icmp echo request/reply SRT statistics to tshark.
+ * It is only used by tshark and not wireshark
+ *
+ * It was based on tap-rpcstat.c and doc/README.tapping.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/dissectors/packet-icmp.h>
+
+#include <wsutil/cmdarg_err.h>
+
+void register_tap_listener_icmpstat(void);
+
+/* used to keep track of the ICMP statistics */
+typedef struct _icmpstat_t {
+ char *filter;
+ GSList *rt_list;
+ guint num_rqsts;
+ guint num_resps;
+ guint min_frame;
+ guint max_frame;
+ double min_msecs;
+ double max_msecs;
+ double tot_msecs;
+} icmpstat_t;
+
+
+/* This callback is never used by tshark but it is here for completeness. When
+ * registering below, we could just have left this function as NULL.
+ *
+ * When used by wireshark, this function will be called whenever we would need
+ * to reset all state, such as when wireshark opens a new file, when it starts
+ * a new capture, when it rescans the packetlist after some prefs have changed,
+ * etc.
+ *
+ * So if your application has some state it needs to clean up in those
+ * situations, here is a good place to put that code.
+ */
+static void
+icmpstat_reset(void *tapdata)
+{
+ icmpstat_t *icmpstat = (icmpstat_t *)tapdata;
+
+ g_slist_free(icmpstat->rt_list);
+ memset(icmpstat, 0, sizeof(icmpstat_t));
+ icmpstat->min_msecs = 1.0 * G_MAXUINT;
+}
+
+
+static gint compare_doubles(gconstpointer a, gconstpointer b)
+{
+ double ad, bd;
+
+ ad = *(const double *)a;
+ bd = *(const double *)b;
+
+ if (ad < bd)
+ return -1;
+ if (ad > bd)
+ return 1;
+ return 0;
+}
+
+
+/* This callback is invoked whenever the tap system has seen a packet we might
+ * be interested in. The function is to be used to only update internal state
+ * information in the *tapdata structure, and if there were state changes which
+ * requires the window to be redrawn, return 1 and (*draw) will be called
+ * sometime later.
+ *
+ * This function should be as lightweight as possible since it executes
+ * together with the normal wireshark dissectors. Try to push as much
+ * processing as possible into (*draw) instead since that function executes
+ * asynchronously and does not affect the main thread's performance.
+ *
+ * If it is possible, try to do all "filtering" explicitly since you will get
+ * MUCH better performance than applying a similar display-filter in the
+ * register call.
+ *
+ * The third parameter is tap dependent. Since we register this one to the
+ * "icmp" tap, the third parameter type is icmp_transaction_t.
+ *
+ * function returns :
+ * TAP_PACKET_DONT_REDRAW: no updates, no need to call (*draw) later
+ * TAP_PACKET_REDRAW: state has changed, call (*draw) sometime later
+ */
+static tap_packet_status
+icmpstat_packet(void *tapdata, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *data, tap_flags_t flags _U_)
+{
+ icmpstat_t *icmpstat = (icmpstat_t *)tapdata;
+ const icmp_transaction_t *trans = (const icmp_transaction_t *)data;
+ double resp_time, *rt;
+
+ if (trans == NULL)
+ return TAP_PACKET_DONT_REDRAW;
+
+ if (trans->resp_frame) {
+ resp_time = nstime_to_msec(&trans->resp_time);
+ rt = g_new(double, 1);
+ if (rt == NULL)
+ return TAP_PACKET_DONT_REDRAW;
+ *rt = resp_time;
+ icmpstat->rt_list = g_slist_prepend(icmpstat->rt_list, rt);
+ icmpstat->num_resps++;
+ if (icmpstat->min_msecs > resp_time) {
+ icmpstat->min_frame = trans->resp_frame;
+ icmpstat->min_msecs = resp_time;
+ }
+ if (icmpstat->max_msecs < resp_time) {
+ icmpstat->max_frame = trans->resp_frame;
+ icmpstat->max_msecs = resp_time;
+ }
+ icmpstat->tot_msecs += resp_time;
+ } else if (trans->rqst_frame)
+ icmpstat->num_rqsts++;
+ else
+ return TAP_PACKET_DONT_REDRAW;
+
+ return TAP_PACKET_REDRAW;
+}
+
+
+/*
+ * Compute the mean, median and standard deviation.
+ */
+static void compute_stats(icmpstat_t *icmpstat, double *mean, double *med, double *sdev)
+{
+ GSList *slist;
+ double diff;
+ double sq_diff_sum = 0.0;
+
+ icmpstat->rt_list = g_slist_sort(icmpstat->rt_list, compare_doubles);
+ slist = icmpstat->rt_list;
+
+ if (icmpstat->num_resps == 0 || slist == NULL) {
+ *mean = 0.0;
+ *med = 0.0;
+ *sdev = 0.0;
+ return;
+ }
+
+ /* (arithmetic) mean */
+ *mean = icmpstat->tot_msecs / icmpstat->num_resps;
+
+ /* median: If we have an odd number of elements in our list, then the
+ * median is simply the middle element, otherwise the median is computed by
+ * averaging the 2 elements on either side of the mid-point. */
+ if (icmpstat->num_resps & 1)
+ *med = *(double *)g_slist_nth_data(slist, icmpstat->num_resps / 2);
+ else {
+ *med =
+ (*(double *)g_slist_nth_data(slist, (icmpstat->num_resps - 1) / 2) +
+ *(double *)g_slist_nth_data(slist, icmpstat->num_resps / 2)) / 2;
+ }
+
+ /* (sample) standard deviation */
+ for ( ; slist; slist = g_slist_next(slist)) {
+ diff = *(double *)slist->data - *mean;
+ sq_diff_sum += diff * diff;
+ }
+ if (icmpstat->num_resps > 1)
+ *sdev = sqrt(sq_diff_sum / (icmpstat->num_resps - 1));
+ else
+ *sdev = 0.0;
+}
+
+
+/* This callback is used when tshark wants us to draw/update our data to the
+ * output device. Since this is tshark, the only output is stdout.
+ * TShark will only call this callback once, which is when tshark has finished
+ * reading all packets and exits.
+ * If used with wireshark this may be called any time, perhaps once every 3
+ * seconds or so.
+ * This function may even be called in parallel with (*reset) or (*draw), so
+ * make sure there are no races. The data in the icmpstat_t can thus change
+ * beneath us. Beware!
+ *
+ * How best to display the data? For now, following other tap statistics
+ * output, but here are a few other alternatives we might choose from:
+ *
+ * -> Windows ping output:
+ * Ping statistics for <IP>:
+ * Packets: Sent = <S>, Received = <R>, Lost = <L> (<LP>% loss),
+ * Approximate round trip times in milli-seconds:
+ * Minimum = <m>ms, Maximum = <M>ms, Average = <A>ms
+ *
+ * -> Cygwin ping output:
+ * ----<HOST> PING Statistics----
+ * <S> packets transmitted, <R> packets received, <LP>% packet loss
+ * round-trip (ms) min/avg/max/med = <m>/<M>/<A>/<D>
+ *
+ * -> Linux ping output:
+ * --- <HOST> ping statistics ---
+ * <S> packets transmitted, <R> received, <LP>% packet loss, time <T>ms
+ * rtt min/avg/max/mdev = <m>/<A>/<M>/<D> ms
+ */
+static void
+icmpstat_draw(void *tapdata)
+{
+ icmpstat_t *icmpstat = (icmpstat_t *)tapdata;
+ unsigned int lost;
+ double mean, sdev, med;
+
+ printf("\n");
+ printf("==========================================================================\n");
+ printf("ICMP Service Response Time (SRT) Statistics (all times in ms):\n");
+ printf("Filter: %s\n", icmpstat->filter ? icmpstat->filter : "<none>");
+ printf("\nRequests Replies Lost %% Loss\n");
+
+ if (icmpstat->num_rqsts) {
+ lost = icmpstat->num_rqsts - icmpstat->num_resps;
+ compute_stats(icmpstat, &mean, &med, &sdev);
+
+ printf("%-10u%-10u%-10u%5.1f%%\n\n",
+ icmpstat->num_rqsts, icmpstat->num_resps, lost,
+ 100.0 * lost / icmpstat->num_rqsts);
+ printf("Minimum Maximum Mean Median SDeviation Min Frame Max Frame\n");
+ printf("%-10.3f%-10.3f%-10.3f%-10.3f%-10.3f %-10u%-10u\n",
+ icmpstat->min_msecs >= G_MAXUINT ? 0.0 : icmpstat->min_msecs,
+ icmpstat->max_msecs, mean, med, sdev,
+ icmpstat->min_frame, icmpstat->max_frame);
+ } else {
+ printf("0 0 0 0.0%%\n\n");
+ printf("Minimum Maximum Mean Median SDeviation Min Frame Max Frame\n");
+ printf("0.000 0.000 0.000 0.000 0.000 0 0\n");
+ }
+ printf("==========================================================================\n");
+}
+
+
+/* When called, this function will create a new instance of icmpstat.
+ *
+ * This function is called from tshark when it parses the -z icmp, arguments
+ * and it creates a new instance to store statistics in and registers this new
+ * instance for the icmp tap.
+ */
+static void
+icmpstat_init(const char *opt_arg, void *userdata _U_)
+{
+ icmpstat_t *icmpstat;
+ const char *filter = NULL;
+ GString *error_string;
+
+ if (strstr(opt_arg, "icmp,srt,"))
+ filter = opt_arg + strlen("icmp,srt,");
+
+ icmpstat = (icmpstat_t *)g_try_malloc(sizeof(icmpstat_t));
+ if (icmpstat == NULL) {
+ cmdarg_err("Couldn't register icmp,srt tap: Out of memory");
+ exit(1);
+ }
+ memset(icmpstat, 0, sizeof(icmpstat_t));
+ icmpstat->min_msecs = 1.0 * G_MAXUINT;
+
+ icmpstat->filter = g_strdup(filter);
+
+/* It is possible to create a filter and attach it to the callbacks. Then the
+ * callbacks would only be invoked if the filter matched.
+ *
+ * Evaluating filters is expensive and if we can avoid it and not use them,
+ * then we gain performance.
+ *
+ * In this case we do the filtering for protocol and version inside the
+ * callback itself but use whatever filter the user provided.
+ */
+
+ error_string = register_tap_listener("icmp", icmpstat, icmpstat->filter,
+ TL_REQUIRES_NOTHING, icmpstat_reset, icmpstat_packet, icmpstat_draw,
+ NULL);
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ g_free(icmpstat->filter);
+ g_free(icmpstat);
+
+ cmdarg_err("Couldn't register icmp,srt tap: %s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static stat_tap_ui icmpstat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "icmp,srt",
+ icmpstat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_icmpstat(void)
+{
+ register_stat_tap_ui(&icmpstat_ui, NULL);
+}
diff --git a/ui/cli/tap-icmpv6stat.c b/ui/cli/tap-icmpv6stat.c
new file mode 100644
index 00000000..94f8692a
--- /dev/null
+++ b/ui/cli/tap-icmpv6stat.c
@@ -0,0 +1,315 @@
+/* tap-icmpv6stat.c
+ * icmpv6stat 2011 Christopher Maynard
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* This module provides icmpv6 echo request/reply SRT statistics to tshark.
+ * It is only used by tshark and not wireshark
+ *
+ * It was based on tap-icmptat.c, which itself was based on tap-rpcstat.c and
+ * doc/README.tapping.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/dissectors/packet-icmp.h>
+
+#include <wsutil/cmdarg_err.h>
+
+void register_tap_listener_icmpv6stat(void);
+
+/* used to keep track of the ICMPv6 statistics */
+typedef struct _icmpv6stat_t {
+ char *filter;
+ GSList *rt_list;
+ guint num_rqsts;
+ guint num_resps;
+ guint min_frame;
+ guint max_frame;
+ double min_msecs;
+ double max_msecs;
+ double tot_msecs;
+} icmpv6stat_t;
+
+
+/* This callback is never used by tshark but it is here for completeness. When
+ * registering below, we could just have left this function as NULL.
+ *
+ * When used by wireshark, this function will be called whenever we would need
+ * to reset all state, such as when wireshark opens a new file, when it starts
+ * a new capture, when it rescans the packetlist after some prefs have changed,
+ * etc.
+ *
+ * So if your application has some state it needs to clean up in those
+ * situations, here is a good place to put that code.
+ */
+static void
+icmpv6stat_reset(void *tapdata)
+{
+ icmpv6stat_t *icmpv6stat = (icmpv6stat_t *)tapdata;
+
+ g_slist_free(icmpv6stat->rt_list);
+ memset(icmpv6stat, 0, sizeof(icmpv6stat_t));
+ icmpv6stat->min_msecs = 1.0 * G_MAXUINT;
+}
+
+
+static gint compare_doubles(gconstpointer a, gconstpointer b)
+{
+ double ad, bd;
+
+ ad = *(const double *)a;
+ bd = *(const double *)b;
+
+ if (ad < bd)
+ return -1;
+ if (ad > bd)
+ return 1;
+ return 0;
+}
+
+
+/* This callback is invoked whenever the tap system has seen a packet we might
+ * be interested in. The function is to be used to only update internal state
+ * information in the *tapdata structure, and if there were state changes which
+ * requires the window to be redrawn, return 1 and (*draw) will be called
+ * sometime later.
+ *
+ * This function should be as lightweight as possible since it executes
+ * together with the normal wireshark dissectors. Try to push as much
+ * processing as possible into (*draw) instead since that function executes
+ * asynchronously and does not affect the main thread's performance.
+ *
+ * If it is possible, try to do all "filtering" explicitly since you will get
+ * MUCH better performance than applying a similar display-filter in the
+ * register call.
+ *
+ * The third parameter is tap dependent. Since we register this one to the
+ * "icmpv6" tap, the third parameter type is icmp_transaction_t.
+ *
+ * function returns :
+ * TAP_PACKET_DONT_REDRAW: no updates, no need to call (*draw) later
+ * TAP_PACKET_REDRAW: state has changed, call (*draw) sometime later
+ */
+static tap_packet_status
+icmpv6stat_packet(void *tapdata, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *data, tap_flags_t flags _U_)
+{
+ icmpv6stat_t *icmpv6stat = (icmpv6stat_t *)tapdata;
+ const icmp_transaction_t *trans = (const icmp_transaction_t *)data;
+ double resp_time, *rt;
+
+ if (trans == NULL)
+ return TAP_PACKET_DONT_REDRAW;
+
+ if (trans->resp_frame) {
+ resp_time = nstime_to_msec(&trans->resp_time);
+ rt = g_new(double, 1);
+ if (rt == NULL)
+ return TAP_PACKET_DONT_REDRAW;
+ *rt = resp_time;
+ icmpv6stat->rt_list = g_slist_prepend(icmpv6stat->rt_list, rt);
+ icmpv6stat->num_resps++;
+ if (icmpv6stat->min_msecs > resp_time) {
+ icmpv6stat->min_frame = trans->resp_frame;
+ icmpv6stat->min_msecs = resp_time;
+ }
+ if (icmpv6stat->max_msecs < resp_time) {
+ icmpv6stat->max_frame = trans->resp_frame;
+ icmpv6stat->max_msecs = resp_time;
+ }
+ icmpv6stat->tot_msecs += resp_time;
+ } else if (trans->rqst_frame)
+ icmpv6stat->num_rqsts++;
+ else
+ return TAP_PACKET_DONT_REDRAW;
+
+ return TAP_PACKET_REDRAW;
+}
+
+
+/*
+ * Compute the mean, median and standard deviation.
+ */
+static void compute_stats(icmpv6stat_t *icmpv6stat, double *mean, double *med, double *sdev)
+{
+ GSList *slist;
+ double diff;
+ double sq_diff_sum = 0.0;
+
+ icmpv6stat->rt_list = g_slist_sort(icmpv6stat->rt_list, compare_doubles);
+ slist = icmpv6stat->rt_list;
+
+ if (icmpv6stat->num_resps == 0 || slist == NULL) {
+ *mean = 0.0;
+ *med = 0.0;
+ *sdev = 0.0;
+ return;
+ }
+
+ /* (arithmetic) mean */
+ *mean = icmpv6stat->tot_msecs / icmpv6stat->num_resps;
+
+ /* median: If we have an odd number of elements in our list, then the
+ * median is simply the middle element, otherwise the median is computed by
+ * averaging the 2 elements on either side of the mid-point. */
+ if (icmpv6stat->num_resps & 1)
+ *med = *(double *)g_slist_nth_data(slist, icmpv6stat->num_resps / 2);
+ else {
+ *med =
+ (*(double *)g_slist_nth_data(slist, (icmpv6stat->num_resps - 1) / 2) +
+ *(double *)g_slist_nth_data(slist, icmpv6stat->num_resps / 2)) / 2;
+ }
+
+ /* (sample) standard deviation */
+ for ( ; slist; slist = g_slist_next(slist)) {
+ diff = *(double *)slist->data - *mean;
+ sq_diff_sum += diff * diff;
+ }
+ if (icmpv6stat->num_resps > 1)
+ *sdev = sqrt(sq_diff_sum / (icmpv6stat->num_resps - 1));
+ else
+ *sdev = 0.0;
+}
+
+
+/* This callback is used when tshark wants us to draw/update our data to the
+ * output device. Since this is tshark, the only output is stdout.
+ * TShark will only call this callback once, which is when tshark has finished
+ * reading all packets and exits.
+ * If used with wireshark this may be called any time, perhaps once every 3
+ * seconds or so.
+ * This function may even be called in parallel with (*reset) or (*draw), so
+ * make sure there are no races. The data in the icmpv6stat_t can thus change
+ * beneath us. Beware!
+ *
+ * How best to display the data? For now, following other tap statistics
+ * output, but here are a few other alternatives we might choose from:
+ *
+ * -> Windows ping output:
+ * Ping statistics for <IP>:
+ * Packets: Sent = <S>, Received = <R>, Lost = <L> (<LP>% loss),
+ * Approximate round trip times in milli-seconds:
+ * Minimum = <m>ms, Maximum = <M>ms, Average = <A>ms
+ *
+ * -> Cygwin ping output:
+ * ----<HOST> PING Statistics----
+ * <S> packets transmitted, <R> packets received, <LP>% packet loss
+ * round-trip (ms) min/avg/max/med = <m>/<M>/<A>/<D>
+ *
+ * -> Linux ping output:
+ * --- <HOST> ping statistics ---
+ * <S> packets transmitted, <R> received, <LP>% packet loss, time <T>ms
+ * rtt min/avg/max/mdev = <m>/<A>/<M>/<D> ms
+ */
+static void
+icmpv6stat_draw(void *tapdata)
+{
+ icmpv6stat_t *icmpv6stat = (icmpv6stat_t *)tapdata;
+ unsigned int lost;
+ double mean, sdev, med;
+
+ printf("\n");
+ printf("==========================================================================\n");
+ printf("ICMPv6 Service Response Time (SRT) Statistics (all times in ms):\n");
+ printf("Filter: %s\n", icmpv6stat->filter ? icmpv6stat->filter : "<none>");
+ printf("\nRequests Replies Lost %% Loss\n");
+
+ if (icmpv6stat->num_rqsts) {
+ lost = icmpv6stat->num_rqsts - icmpv6stat->num_resps;
+ compute_stats(icmpv6stat, &mean, &med, &sdev);
+
+ printf("%-10u%-10u%-10u%5.1f%%\n\n",
+ icmpv6stat->num_rqsts, icmpv6stat->num_resps, lost,
+ 100.0 * lost / icmpv6stat->num_rqsts);
+ printf("Minimum Maximum Mean Median SDeviation Min Frame Max Frame\n");
+ printf("%-10.3f%-10.3f%-10.3f%-10.3f%-10.3f %-10u%-10u\n",
+ icmpv6stat->min_msecs >= G_MAXUINT ? 0.0 : icmpv6stat->min_msecs,
+ icmpv6stat->max_msecs, mean, med, sdev,
+ icmpv6stat->min_frame, icmpv6stat->max_frame);
+ } else {
+ printf("0 0 0 0.0%%\n\n");
+ printf("Minimum Maximum Mean Median SDeviation Min Frame Max Frame\n");
+ printf("0.000 0.000 0.000 0.000 0.000 0 0\n");
+ }
+ printf("==========================================================================\n");
+}
+
+
+/* When called, this function will create a new instance of icmpv6stat.
+ *
+ * This function is called from tshark when it parses the -z icmpv6, arguments
+ * and it creates a new instance to store statistics in and registers this new
+ * instance for the icmpv6 tap.
+ */
+static void
+icmpv6stat_init(const char *opt_arg, void *userdata _U_)
+{
+ icmpv6stat_t *icmpv6stat;
+ const char *filter = NULL;
+ GString *error_string;
+
+ if (strstr(opt_arg, "icmpv6,srt,"))
+ filter = opt_arg + strlen("icmpv6,srt,");
+
+ icmpv6stat = (icmpv6stat_t *)g_try_malloc(sizeof(icmpv6stat_t));
+ if (icmpv6stat == NULL) {
+ cmdarg_err("Couldn't register icmpv6,srt tap: Out of memory");
+ exit(1);
+ }
+ memset(icmpv6stat, 0, sizeof(icmpv6stat_t));
+ icmpv6stat->min_msecs = 1.0 * G_MAXUINT;
+
+ icmpv6stat->filter = g_strdup(filter);
+
+/* It is possible to create a filter and attach it to the callbacks. Then the
+ * callbacks would only be invoked if the filter matched.
+ *
+ * Evaluating filters is expensive and if we can avoid it and not use them,
+ * then we gain performance.
+ *
+ * In this case we do the filtering for protocol and version inside the
+ * callback itself but use whatever filter the user provided.
+ */
+
+ error_string = register_tap_listener("icmpv6", icmpv6stat, icmpv6stat->filter,
+ TL_REQUIRES_NOTHING, icmpv6stat_reset, icmpv6stat_packet, icmpv6stat_draw, NULL);
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ g_free(icmpv6stat->filter);
+ g_free(icmpv6stat);
+
+ cmdarg_err("Couldn't register icmpv6,srt tap: %s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static stat_tap_ui icmpv6stat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "icmpv6,srt",
+ icmpv6stat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_icmpv6stat(void)
+{
+ register_stat_tap_ui(&icmpv6stat_ui, NULL);
+}
diff --git a/ui/cli/tap-iostat.c b/ui/cli/tap-iostat.c
new file mode 100644
index 00000000..83810b23
--- /dev/null
+++ b/ui/cli/tap-iostat.c
@@ -0,0 +1,1541 @@
+/* tap-iostat.c
+ * iostat 2002 Ronnie Sahlberg
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include <epan/epan_dissect.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include "globals.h"
+#include <wsutil/ws_assert.h>
+
+#define CALC_TYPE_FRAMES 0
+#define CALC_TYPE_BYTES 1
+#define CALC_TYPE_FRAMES_AND_BYTES 2
+#define CALC_TYPE_COUNT 3
+#define CALC_TYPE_SUM 4
+#define CALC_TYPE_MIN 5
+#define CALC_TYPE_MAX 6
+#define CALC_TYPE_AVG 7
+#define CALC_TYPE_LOAD 8
+
+void register_tap_listener_iostat(void);
+
+typedef struct {
+ const char *func_name;
+ int calc_type;
+} calc_type_ent_t;
+
+static calc_type_ent_t calc_type_table[] = {
+ { "FRAMES", CALC_TYPE_FRAMES },
+ { "BYTES", CALC_TYPE_BYTES },
+ { "FRAMES BYTES", CALC_TYPE_FRAMES_AND_BYTES },
+ { "COUNT", CALC_TYPE_COUNT },
+ { "SUM", CALC_TYPE_SUM },
+ { "MIN", CALC_TYPE_MIN },
+ { "MAX", CALC_TYPE_MAX },
+ { "AVG", CALC_TYPE_AVG },
+ { "LOAD", CALC_TYPE_LOAD },
+ { NULL, 0 }
+};
+
+typedef struct _io_stat_t {
+ guint64 interval; /* The user-specified time interval (us) */
+ guint invl_prec; /* Decimal precision of the time interval (1=10s, 2=100s etc) */
+ unsigned int num_cols; /* The number of columns of stats in the table */
+ struct _io_stat_item_t *items; /* Each item is a single cell in the table */
+ time_t start_time; /* Time of first frame matching the filter */
+ const char **filters; /* 'io,stat' cmd strings (e.g., "AVG(smb.time)smb.time") */
+ guint64 *max_vals; /* The max value sans the decimal or nsecs portion in each stat column */
+ guint32 *max_frame; /* The max frame number displayed in each stat column */
+} io_stat_t;
+
+typedef struct _io_stat_item_t {
+ io_stat_t *parent;
+ struct _io_stat_item_t *next;
+ struct _io_stat_item_t *prev;
+ guint64 start_time; /* Time since start of capture (us)*/
+ int calc_type; /* The statistic type */
+ int colnum; /* Column number of this stat (0 to n) */
+ int hf_index;
+ guint32 frames;
+ guint32 num; /* The sample size of a given statistic (only needed for AVG) */
+ guint64 counter; /* The accumulated data for the calculation of that statistic */
+ gfloat float_counter;
+ gdouble double_counter;
+} io_stat_item_t;
+
+#define NANOSECS_PER_SEC G_GUINT64_CONSTANT(1000000000)
+
+static guint64 last_relative_time;
+
+static tap_packet_status
+iostat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_, tap_flags_t flags _U_)
+{
+ io_stat_t *parent;
+ io_stat_item_t *mit;
+ io_stat_item_t *it;
+ guint64 relative_time, rt;
+ const nstime_t *new_time;
+ GPtrArray *gp;
+ guint i;
+ int ftype;
+
+ mit = (io_stat_item_t *) arg;
+ parent = mit->parent;
+
+ /* If this frame's relative time is negative, set its relative time to last_relative_time
+ rather than disincluding it from the calculations. */
+ if ((pinfo->rel_ts.secs >= 0) && (pinfo->rel_ts.nsecs >= 0)) {
+ relative_time = ((guint64)pinfo->rel_ts.secs * G_GUINT64_CONSTANT(1000000)) +
+ ((guint64)((pinfo->rel_ts.nsecs+500)/1000));
+ last_relative_time = relative_time;
+ } else {
+ relative_time = last_relative_time;
+ }
+
+ if (mit->parent->start_time == 0) {
+ mit->parent->start_time = pinfo->abs_ts.secs - pinfo->rel_ts.secs;
+ }
+
+ /* The prev item is always the last interval in which we saw packets. */
+ it = mit->prev;
+
+ /* If we have moved into a new interval (row), create a new io_stat_item_t struct for every interval
+ * between the last struct and this one. If an item was not found in a previous interval, an empty
+ * struct will be created for it. */
+ rt = relative_time;
+ while (rt >= it->start_time + parent->interval) {
+ it->next = g_new(io_stat_item_t, 1);
+ it->next->prev = it;
+ it->next->next = NULL;
+ it = it->next;
+ mit->prev = it;
+
+ it->start_time = it->prev->start_time + parent->interval;
+ it->frames = 0;
+ it->counter = 0;
+ it->float_counter = 0;
+ it->double_counter = 0;
+ it->num = 0;
+ it->calc_type = it->prev->calc_type;
+ it->hf_index = it->prev->hf_index;
+ it->colnum = it->prev->colnum;
+ }
+
+ /* Store info in the current structure */
+ it->frames++;
+
+ switch (it->calc_type) {
+ case CALC_TYPE_FRAMES:
+ case CALC_TYPE_BYTES:
+ case CALC_TYPE_FRAMES_AND_BYTES:
+ it->counter += pinfo->fd->pkt_len;
+ break;
+ case CALC_TYPE_COUNT:
+ gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
+ if (gp) {
+ it->counter += gp->len;
+ }
+ break;
+ case CALC_TYPE_SUM:
+ gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
+ if (gp) {
+ guint64 val;
+
+ for (i=0; i<gp->len; i++) {
+ switch (proto_registrar_get_ftype(it->hf_index)) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ it->counter += fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_UINT40:
+ case FT_UINT48:
+ case FT_UINT56:
+ case FT_UINT64:
+ it->counter += fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ it->counter += fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_INT40:
+ case FT_INT48:
+ case FT_INT56:
+ case FT_INT64:
+ it->counter += (gint64)fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_FLOAT:
+ it->float_counter +=
+ (gfloat)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_DOUBLE:
+ it->double_counter += fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_RELATIVE_TIME:
+ new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
+ val = ((guint64)new_time->secs * NANOSECS_PER_SEC) + (guint64)new_time->nsecs;
+ it->counter += val;
+ break;
+ default:
+ /*
+ * "Can't happen"; see the checks
+ * in register_io_tap().
+ */
+ ws_assert_not_reached();
+ break;
+ }
+ }
+ }
+ break;
+ case CALC_TYPE_MIN:
+ gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
+ if (gp) {
+ guint64 val;
+ gfloat float_val;
+ gdouble double_val;
+
+ ftype = proto_registrar_get_ftype(it->hf_index);
+ for (i=0; i<gp->len; i++) {
+ switch (ftype) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ val = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
+ if ((it->frames == 1 && i == 0) || (val < it->counter)) {
+ it->counter = val;
+ }
+ break;
+ case FT_UINT40:
+ case FT_UINT48:
+ case FT_UINT56:
+ case FT_UINT64:
+ val = fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
+ if ((it->frames == 1 && i == 0) || (val < it->counter)) {
+ it->counter = val;
+ }
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ val = fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
+ if ((it->frames == 1 && i == 0) || ((gint32)val < (gint32)it->counter)) {
+ it->counter = val;
+ }
+ break;
+ case FT_INT40:
+ case FT_INT48:
+ case FT_INT56:
+ case FT_INT64:
+ val = fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
+ if ((it->frames == 1 && i == 0) || ((gint64)val < (gint64)it->counter)) {
+ it->counter = val;
+ }
+ break;
+ case FT_FLOAT:
+ float_val = (gfloat)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ if ((it->frames == 1 && i == 0) || (float_val < it->float_counter)) {
+ it->float_counter = float_val;
+ }
+ break;
+ case FT_DOUBLE:
+ double_val = fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ if ((it->frames == 1 && i == 0) || (double_val < it->double_counter)) {
+ it->double_counter = double_val;
+ }
+ break;
+ case FT_RELATIVE_TIME:
+ new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
+ val = ((guint64)new_time->secs * NANOSECS_PER_SEC) + (guint64)new_time->nsecs;
+ if ((it->frames == 1 && i == 0) || (val < it->counter)) {
+ it->counter = val;
+ }
+ break;
+ default:
+ /*
+ * "Can't happen"; see the checks
+ * in register_io_tap().
+ */
+ ws_assert_not_reached();
+ break;
+ }
+ }
+ }
+ break;
+ case CALC_TYPE_MAX:
+ gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
+ if (gp) {
+ guint64 val;
+ gfloat float_val;
+ gdouble double_val;
+
+ ftype = proto_registrar_get_ftype(it->hf_index);
+ for (i=0; i<gp->len; i++) {
+ switch (ftype) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ val = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
+ if (val > it->counter)
+ it->counter = val;
+ break;
+ case FT_UINT40:
+ case FT_UINT48:
+ case FT_UINT56:
+ case FT_UINT64:
+ val = fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
+ if (val > it->counter)
+ it->counter = val;
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ val = fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
+ if ((gint32)val > (gint32)it->counter)
+ it->counter = val;
+ break;
+ case FT_INT40:
+ case FT_INT48:
+ case FT_INT56:
+ case FT_INT64:
+ val = fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
+ if ((gint64)val > (gint64)it->counter)
+ it->counter = val;
+ break;
+ case FT_FLOAT:
+ float_val = (gfloat)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ if (float_val > it->float_counter)
+ it->float_counter = float_val;
+ break;
+ case FT_DOUBLE:
+ double_val = fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ if (double_val > it->double_counter)
+ it->double_counter = double_val;
+ break;
+ case FT_RELATIVE_TIME:
+ new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
+ val = ((guint64)new_time->secs * NANOSECS_PER_SEC) + (guint64)new_time->nsecs;
+ if (val > it->counter)
+ it->counter = val;
+ break;
+ default:
+ /*
+ * "Can't happen"; see the checks
+ * in register_io_tap().
+ */
+ ws_assert_not_reached();
+ break;
+ }
+ }
+ }
+ break;
+ case CALC_TYPE_AVG:
+ gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
+ if (gp) {
+ guint64 val;
+
+ ftype = proto_registrar_get_ftype(it->hf_index);
+ for (i=0; i<gp->len; i++) {
+ it->num++;
+ switch (ftype) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ val = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
+ it->counter += val;
+ break;
+ case FT_UINT40:
+ case FT_UINT48:
+ case FT_UINT56:
+ case FT_UINT64:
+ val = fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
+ it->counter += val;
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ val = fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
+ it->counter += val;
+ break;
+ case FT_INT40:
+ case FT_INT48:
+ case FT_INT56:
+ case FT_INT64:
+ val = fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
+ it->counter += val;
+ break;
+ case FT_FLOAT:
+ it->float_counter += (gfloat)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_DOUBLE:
+ it->double_counter += fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_RELATIVE_TIME:
+ new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
+ val = ((guint64)new_time->secs * NANOSECS_PER_SEC) + (guint64)new_time->nsecs;
+ it->counter += val;
+ break;
+ default:
+ /*
+ * "Can't happen"; see the checks
+ * in register_io_tap().
+ */
+ ws_assert_not_reached();
+ break;
+ }
+ }
+ }
+ break;
+ case CALC_TYPE_LOAD:
+ gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
+ if (gp) {
+ ftype = proto_registrar_get_ftype(it->hf_index);
+ if (ftype != FT_RELATIVE_TIME) {
+ fprintf(stderr,
+ "\ntshark: LOAD() is only supported for relative-time fields such as smb.time\n");
+ exit(10);
+ }
+ for (i=0; i<gp->len; i++) {
+ guint64 val;
+ int tival;
+ io_stat_item_t *pit;
+
+ new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
+ val = ((guint64)new_time->secs*G_GUINT64_CONSTANT(1000000)) + (guint64)(new_time->nsecs/1000);
+ tival = (int)(val % parent->interval);
+ it->counter += tival;
+ val -= tival;
+ pit = it->prev;
+ while (val > 0) {
+ if (val < (guint64)parent->interval) {
+ pit->counter += val;
+ break;
+ }
+ pit->counter += parent->interval;
+ val -= parent->interval;
+ pit = pit->prev;
+ }
+ }
+ }
+ break;
+ }
+ /* Store the highest value for this item in order to determine the width of each stat column.
+ * For real numbers we only need to know its magnitude (the value to the left of the decimal point
+ * so round it up before storing it as an integer in max_vals. For AVG of RELATIVE_TIME fields,
+ * calc the average, round it to the next second and store the seconds. For all other calc types
+ * of RELATIVE_TIME fields, store the counters without modification.
+ * fields. */
+ switch (it->calc_type) {
+ case CALC_TYPE_FRAMES:
+ case CALC_TYPE_FRAMES_AND_BYTES:
+ parent->max_frame[it->colnum] =
+ MAX(parent->max_frame[it->colnum], it->frames);
+ if (it->calc_type == CALC_TYPE_FRAMES_AND_BYTES)
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], it->counter);
+ break;
+ case CALC_TYPE_BYTES:
+ case CALC_TYPE_COUNT:
+ case CALC_TYPE_LOAD:
+ parent->max_vals[it->colnum] = MAX(parent->max_vals[it->colnum], it->counter);
+ break;
+ case CALC_TYPE_SUM:
+ case CALC_TYPE_MIN:
+ case CALC_TYPE_MAX:
+ ftype = proto_registrar_get_ftype(it->hf_index);
+ switch (ftype) {
+ case FT_FLOAT:
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], (guint64)(it->float_counter+0.5));
+ break;
+ case FT_DOUBLE:
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], (guint64)(it->double_counter+0.5));
+ break;
+ case FT_RELATIVE_TIME:
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], it->counter);
+ break;
+ default:
+ /* UINT16-64 and INT8-64 */
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], it->counter);
+ break;
+ }
+ break;
+ case CALC_TYPE_AVG:
+ if (it->num == 0) /* avoid division by zero */
+ break;
+ ftype = proto_registrar_get_ftype(it->hf_index);
+ switch (ftype) {
+ case FT_FLOAT:
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], (guint64)it->float_counter/it->num);
+ break;
+ case FT_DOUBLE:
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], (guint64)it->double_counter/it->num);
+ break;
+ case FT_RELATIVE_TIME:
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], ((it->counter/(guint64)it->num) + G_GUINT64_CONSTANT(500000000)) / NANOSECS_PER_SEC);
+ break;
+ default:
+ /* UINT16-64 and INT8-64 */
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], it->counter/it->num);
+ break;
+ }
+ }
+ return TAP_PACKET_REDRAW;
+}
+
+static unsigned int
+magnitude (guint64 val, unsigned int max_w)
+{
+ unsigned int i, mag = 0;
+
+ for (i=0; i<max_w; i++) {
+ mag++;
+ if ((val /= 10) == 0)
+ break;
+ }
+ return(mag);
+}
+
+/*
+* Print the calc_type_table[] function label centered in the column header.
+*/
+static void
+printcenter (const char *label, int lenval, int numpad)
+{
+ int lenlab = (int) strlen(label), len;
+ const char spaces[] = " ", *spaces_ptr;
+
+ len = (int) (strlen(spaces)) - (((lenval-lenlab) / 2) + numpad);
+ if (len > 0 && len < 6) {
+ spaces_ptr = &spaces[len];
+ if ((lenval-lenlab)%2 == 0) {
+ printf("%s%s%s|", spaces_ptr, label, spaces_ptr);
+ } else {
+ printf("%s%s%s|", spaces_ptr-1, label, spaces_ptr);
+ }
+ } else if (len > 0 && len <= 15) {
+ printf("%s|", label);
+ }
+}
+
+typedef struct {
+ int fr; /* Width of this FRAMES column sans padding and border chars */
+ int val; /* Width of this non-FRAMES column sans padding and border chars */
+} column_width;
+
+static void
+iostat_draw(void *arg)
+{
+ guint32 num;
+ guint64 interval, duration, t, invl_end, dv;
+ unsigned int i, j, k, num_cols, num_rows, dur_secs_orig, dur_nsecs_orig, dur_secs, dur_nsecs, dur_mag,
+ invl_mag, invl_prec, tabrow_w, borderlen, invl_col_w, numpad = 1, namelen, len_filt, type,
+ maxfltr_w, ftype;
+ unsigned int fr_mag; /* The magnitude of the max frame number in this column */
+ unsigned int val_mag; /* The magnitude of the max value in this column */
+ char *spaces, *spaces_s, *filler_s = NULL, **fmts, *fmt = NULL;
+ const char *filter;
+ static gchar dur_mag_s[3], invl_prec_s[3], fr_mag_s[3], val_mag_s[3], *invl_fmt, *full_fmt;
+ io_stat_item_t *mit, **stat_cols, *item, **item_in_column;
+ gboolean last_row = FALSE;
+ io_stat_t *iot;
+ column_width *col_w;
+ struct tm *tm_time;
+ time_t the_time;
+
+ mit = (io_stat_item_t *)arg;
+ iot = mit->parent;
+ num_cols = iot->num_cols;
+ col_w = g_new(column_width, num_cols);
+ fmts = (char **)g_malloc(sizeof(char *) * num_cols);
+ duration = ((guint64)cfile.elapsed_time.secs * G_GUINT64_CONSTANT(1000000)) +
+ (guint64)((cfile.elapsed_time.nsecs + 500) / 1000);
+
+ /* Store the pointer to each stat column */
+ stat_cols = (io_stat_item_t **)g_malloc(sizeof(io_stat_item_t *) * num_cols);
+ for (j=0; j<num_cols; j++)
+ stat_cols[j] = &iot->items[j];
+
+ /* The following prevents gross inaccuracies when the user specifies an interval that is greater
+ * than the capture duration. */
+ if (iot->interval > duration || iot->interval == G_MAXUINT64) {
+ interval = duration;
+ iot->interval = G_MAXUINT64;
+ } else {
+ interval = iot->interval;
+ }
+
+ /* Calc the capture duration's magnitude (dur_mag) */
+ dur_secs = (unsigned int)(duration/G_GUINT64_CONSTANT(1000000));
+ dur_secs_orig = dur_secs;
+ dur_nsecs = (unsigned int)(duration%G_GUINT64_CONSTANT(1000000));
+ dur_nsecs_orig = dur_nsecs;
+ dur_mag = magnitude((guint64)dur_secs, 5);
+ snprintf(dur_mag_s, 3, "%u", dur_mag);
+
+ /* Calc the interval's magnitude */
+ invl_mag = magnitude(interval/G_GUINT64_CONSTANT(1000000), 5);
+
+ /* Set or get the interval precision */
+ if (interval == duration) {
+ /*
+ * An interval arg of 0 or an interval size exceeding the capture duration was specified.
+ * Set the decimal precision of duration based on its magnitude. */
+ if (dur_mag >= 2)
+ invl_prec = 1;
+ else if (dur_mag == 1)
+ invl_prec = 3;
+ else
+ invl_prec = 6;
+
+ borderlen = 30 + dur_mag + (invl_prec == 0 ? 0 : invl_prec+1);
+ } else {
+ invl_prec = iot->invl_prec;
+ borderlen = 25 + MAX(invl_mag,dur_mag) + (invl_prec == 0 ? 0 : invl_prec+1);
+ }
+
+ /* Round the duration according to invl_prec */
+ dv = 1000000;
+ for (i=0; i<invl_prec; i++)
+ dv /= 10;
+ if ((duration%dv) > 5*(dv/10)) {
+ duration += 5*(dv/10);
+ duration = (duration/dv) * dv;
+ dur_secs = (unsigned int)(duration/G_GUINT64_CONSTANT(1000000));
+ dur_nsecs = (unsigned int)(duration%G_GUINT64_CONSTANT(1000000));
+ /*
+ * Recalc dur_mag in case rounding has increased its magnitude */
+ dur_mag = magnitude((guint64)dur_secs, 5);
+ }
+ if (iot->interval == G_MAXUINT64)
+ interval = duration;
+
+ /* Calc the width of the time interval column (incl borders and padding). */
+ if (invl_prec == 0)
+ invl_col_w = (2*dur_mag) + 8;
+ else
+ invl_col_w = (2*dur_mag) + (2*invl_prec) + 10;
+
+ /* Update the width of the time interval column if date is shown */
+ switch (timestamp_get_type()) {
+ case TS_ABSOLUTE_WITH_YMD:
+ case TS_ABSOLUTE_WITH_YDOY:
+ case TS_UTC_WITH_YMD:
+ case TS_UTC_WITH_YDOY:
+ invl_col_w = MAX(invl_col_w, 23);
+ break;
+
+ default:
+ invl_col_w = MAX(invl_col_w, 12);
+ break;
+ }
+
+ borderlen = MAX(borderlen, invl_col_w);
+
+ /* Calc the total width of each row in the stats table and build the printf format string for each
+ * column based on its field type, width, and name length.
+ * NOTE: The magnitude of all types including float and double are stored in iot->max_vals which
+ * is an *integer*. */
+ tabrow_w = invl_col_w;
+ for (j=0; j<num_cols; j++) {
+ type = iot->items[j].calc_type;
+ if (type == CALC_TYPE_FRAMES_AND_BYTES) {
+ namelen = 5;
+ } else {
+ namelen = (unsigned int)strlen(calc_type_table[type].func_name);
+ }
+ if (type == CALC_TYPE_FRAMES
+ || type == CALC_TYPE_FRAMES_AND_BYTES) {
+
+ fr_mag = magnitude(iot->max_frame[j], 15);
+ fr_mag = MAX(6, fr_mag);
+ col_w[j].fr = fr_mag;
+ tabrow_w += col_w[j].fr + 3;
+ snprintf(fr_mag_s, 3, "%u", fr_mag);
+
+ if (type == CALC_TYPE_FRAMES) {
+ fmt = g_strconcat(" %", fr_mag_s, "u |", NULL);
+ } else {
+ /* CALC_TYPE_FRAMES_AND_BYTES
+ */
+ val_mag = magnitude(iot->max_vals[j], 15);
+ val_mag = MAX(5, val_mag);
+ col_w[j].val = val_mag;
+ tabrow_w += (col_w[j].val + 3);
+ snprintf(val_mag_s, 3, "%u", val_mag);
+ fmt = g_strconcat(" %", fr_mag_s, "u |", " %", val_mag_s, PRIu64 " |", NULL);
+ }
+ if (fmt)
+ fmts[j] = fmt;
+ continue;
+ }
+ switch (type) {
+ case CALC_TYPE_BYTES:
+ case CALC_TYPE_COUNT:
+
+ val_mag = magnitude(iot->max_vals[j], 15);
+ val_mag = MAX(5, val_mag);
+ col_w[j].val = val_mag;
+ snprintf(val_mag_s, 3, "%u", val_mag);
+ fmt = g_strconcat(" %", val_mag_s, PRIu64 " |", NULL);
+ break;
+
+ default:
+ ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
+ switch (ftype) {
+ case FT_FLOAT:
+ case FT_DOUBLE:
+ val_mag = magnitude(iot->max_vals[j], 15);
+ snprintf(val_mag_s, 3, "%u", val_mag);
+ fmt = g_strconcat(" %", val_mag_s, ".6f |", NULL);
+ col_w[j].val = val_mag + 7;
+ break;
+ case FT_RELATIVE_TIME:
+ /* Convert FT_RELATIVE_TIME field to seconds
+ * CALC_TYPE_LOAD was already converted in iostat_packet() ) */
+ if (type == CALC_TYPE_LOAD) {
+ iot->max_vals[j] /= interval;
+ } else if (type != CALC_TYPE_AVG) {
+ iot->max_vals[j] = (iot->max_vals[j] + G_GUINT64_CONSTANT(500000000)) / NANOSECS_PER_SEC;
+ }
+ val_mag = magnitude(iot->max_vals[j], 15);
+ snprintf(val_mag_s, 3, "%u", val_mag);
+ fmt = g_strconcat(" %", val_mag_s, "u.%06u |", NULL);
+ col_w[j].val = val_mag + 7;
+ break;
+
+ default:
+ val_mag = magnitude(iot->max_vals[j], 15);
+ val_mag = MAX(namelen, val_mag);
+ col_w[j].val = val_mag;
+ snprintf(val_mag_s, 3, "%u", val_mag);
+
+ switch (ftype) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ case FT_UINT64:
+ fmt = g_strconcat(" %", val_mag_s, PRIu64 " |", NULL);
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ case FT_INT64:
+ fmt = g_strconcat(" %", val_mag_s, PRId64 " |", NULL);
+ break;
+ }
+ } /* End of ftype switch */
+ } /* End of calc_type switch */
+ tabrow_w += col_w[j].val + 3;
+ if (fmt)
+ fmts[j] = fmt;
+ } /* End of for loop (columns) */
+
+ borderlen = MAX(borderlen, tabrow_w);
+
+ /* Calc the max width of the list of filters. */
+ maxfltr_w = 0;
+ for (j=0; j<num_cols; j++) {
+ if (iot->filters[j]) {
+ k = (unsigned int) (strlen(iot->filters[j]) + 11);
+ maxfltr_w = MAX(maxfltr_w, k);
+ } else {
+ maxfltr_w = MAX(maxfltr_w, 26);
+ }
+ }
+ /* The stat table is not wrapped (by tshark) but filter is wrapped at the width of the stats table
+ * (which currently = borderlen); however, if the filter width exceeds the table width and the
+ * table width is less than 102 bytes, set borderlen to the lesser of the max filter width and 102.
+ * The filters will wrap at the lesser of borderlen-2 and the last space in the filter.
+ * NOTE: 102 is the typical size of a user window when the font is fixed width (e.g., COURIER 10).
+ * XXX: A pref could be added to change the max width from the default size of 102. */
+ if (maxfltr_w > borderlen && borderlen < 102)
+ borderlen = MIN(maxfltr_w, 102);
+
+ /* Prevent double right border by adding a space */
+ if (borderlen-tabrow_w == 1)
+ borderlen++;
+
+ /* Display the top border */
+ printf("\n");
+ for (i=0; i<borderlen; i++)
+ printf("=");
+
+ spaces = (char *)g_malloc(borderlen+1);
+ for (i=0; i<borderlen; i++)
+ spaces[i] = ' ';
+ spaces[borderlen] = '\0';
+
+ spaces_s = &spaces[16];
+ printf("\n| IO Statistics%s|\n", spaces_s);
+ spaces_s = &spaces[2];
+ printf("|%s|\n", spaces_s);
+
+ if (invl_prec == 0) {
+ invl_fmt = g_strconcat("%", dur_mag_s, "u", NULL);
+ full_fmt = g_strconcat("| Duration: ", invl_fmt, ".%6u secs%s|\n", NULL);
+ spaces_s = &spaces[25 + dur_mag];
+ printf(full_fmt, dur_secs_orig, dur_nsecs_orig, spaces_s);
+ g_free(full_fmt);
+ full_fmt = g_strconcat("| Interval: ", invl_fmt, " secs%s|\n", NULL);
+ spaces_s = &spaces[18 + dur_mag];
+ printf(full_fmt, (guint32)(interval/G_GUINT64_CONSTANT(1000000)), spaces_s);
+ } else {
+ snprintf(invl_prec_s, 3, "%u", invl_prec);
+ invl_fmt = g_strconcat("%", dur_mag_s, "u.%0", invl_prec_s, "u", NULL);
+ full_fmt = g_strconcat("| Duration: ", invl_fmt, " secs%s|\n", NULL);
+ spaces_s = &spaces[19 + dur_mag + invl_prec];
+ printf(full_fmt, dur_secs, dur_nsecs/(int)dv, spaces_s);
+ g_free(full_fmt);
+
+ full_fmt = g_strconcat("| Interval: ", invl_fmt, " secs%s|\n", NULL);
+ spaces_s = &spaces[19 + dur_mag + invl_prec];
+ printf(full_fmt, (guint32)(interval/G_GUINT64_CONSTANT(1000000)),
+ (guint32)((interval%G_GUINT64_CONSTANT(1000000))/dv), spaces_s);
+ }
+ g_free(full_fmt);
+
+ spaces_s = &spaces[2];
+ printf("|%s|\n", spaces_s);
+
+ /* Display the list of filters and their column numbers vertically */
+ printf("| Col");
+ for (j=0; j<num_cols; j++) {
+ printf((j == 0 ? "%2u: " : "| %2u: "), j+1);
+ if (!iot->filters[j]) {
+ /*
+ * An empty (no filter) comma field was specified */
+ spaces_s = &spaces[16 + 10];
+ printf("Frames and bytes%s|\n", spaces_s);
+ } else {
+ filter = iot->filters[j];
+ len_filt = (unsigned int) strlen(filter);
+
+ /* If the width of the widest filter exceeds the width of the stat table, borderlen has
+ * been set to 102 bytes above and filters wider than 102 will wrap at 91 bytes. */
+ if (len_filt+11 <= borderlen) {
+ printf("%s", filter);
+ if (len_filt+11 <= borderlen) {
+ spaces_s = &spaces[len_filt + 10];
+ printf("%s", spaces_s);
+ }
+ printf("|\n");
+ } else {
+ gchar *sfilter1, *sfilter2;
+ const gchar *pos;
+ gsize len;
+ unsigned int next_start, max_w = borderlen-11;
+
+ do {
+ if (len_filt > max_w) {
+ sfilter1 = g_strndup(filter, (gsize) max_w);
+ /*
+ * Find the pos of the last space in sfilter1. If a space is found, set
+ * sfilter2 to the string prior to that space and print it; otherwise, wrap
+ * the filter at max_w. */
+ pos = g_strrstr(sfilter1, " ");
+ if (pos) {
+ len = (gsize)(pos-sfilter1);
+ next_start = (unsigned int) len+1;
+ } else {
+ len = (gsize) strlen(sfilter1);
+ next_start = (unsigned int)len;
+ }
+ sfilter2 = g_strndup(sfilter1, len);
+ printf("%s%s|\n", sfilter2, &spaces[len+10]);
+ g_free(sfilter1);
+ g_free(sfilter2);
+
+ printf("| ");
+ filter = &filter[next_start];
+ len_filt = (unsigned int) strlen(filter);
+ } else {
+ printf("%s%s|\n", filter, &spaces[strlen(filter)+10]);
+ break;
+ }
+ } while (1);
+ }
+ }
+ }
+
+ printf("|-");
+ for (i=0; i<borderlen-3; i++) {
+ printf("-");
+ }
+ printf("|\n");
+
+ /* Display spaces above "Interval (s)" label */
+ spaces_s = &spaces[borderlen-(invl_col_w-2)];
+ printf("|%s|", spaces_s);
+
+ /* Display column number headers */
+ for (j=0; j<num_cols; j++) {
+ item = stat_cols[j];
+ if (item->calc_type == CALC_TYPE_FRAMES_AND_BYTES)
+ spaces_s = &spaces[borderlen - (col_w[j].fr + col_w[j].val)] - 3;
+ else if (item->calc_type == CALC_TYPE_FRAMES)
+ spaces_s = &spaces[borderlen - col_w[j].fr];
+ else
+ spaces_s = &spaces[borderlen - col_w[j].val];
+
+ printf("%-2d%s|", j+1, spaces_s);
+ }
+ if (tabrow_w < borderlen) {
+ filler_s = &spaces[tabrow_w+1];
+ printf("%s|", filler_s);
+ }
+
+ k = 11;
+ switch (timestamp_get_type()) {
+ case TS_ABSOLUTE:
+ printf("\n| Time ");
+ break;
+ case TS_ABSOLUTE_WITH_YMD:
+ case TS_ABSOLUTE_WITH_YDOY:
+ case TS_UTC_WITH_YMD:
+ case TS_UTC_WITH_YDOY:
+ printf("\n| Date and time");
+ k = 16;
+ break;
+ case TS_RELATIVE:
+ case TS_NOT_SET:
+ printf("\n| Interval");
+ break;
+ default:
+ break;
+ }
+
+ spaces_s = &spaces[borderlen-(invl_col_w-k)];
+ printf("%s|", spaces_s);
+
+ /* Display the stat label in each column */
+ for (j=0; j<num_cols; j++) {
+ type = stat_cols[j]->calc_type;
+ if (type == CALC_TYPE_FRAMES) {
+ printcenter (calc_type_table[type].func_name, col_w[j].fr, numpad);
+ } else if (type == CALC_TYPE_FRAMES_AND_BYTES) {
+ printcenter ("Frames", col_w[j].fr, numpad);
+ printcenter ("Bytes", col_w[j].val, numpad);
+ } else {
+ printcenter (calc_type_table[type].func_name, col_w[j].val, numpad);
+ }
+ }
+ if (filler_s)
+ printf("%s|", filler_s);
+ printf("\n|-");
+
+ for (i=0; i<tabrow_w-3; i++)
+ printf("-");
+ printf("|");
+
+ if (tabrow_w < borderlen)
+ printf("%s|", &spaces[tabrow_w+1]);
+
+ printf("\n");
+ t = 0;
+ if (invl_prec == 0 && dur_mag == 1)
+ full_fmt = g_strconcat("| ", invl_fmt, " <> ", invl_fmt, " |", NULL);
+ else
+ full_fmt = g_strconcat("| ", invl_fmt, " <> ", invl_fmt, " |", NULL);
+
+ if (interval == 0 || duration == 0) {
+ num_rows = 0;
+ } else {
+ num_rows = (unsigned int)(duration/interval) + ((unsigned int)(duration%interval) > 0 ? 1 : 0);
+ }
+
+ /* Load item_in_column with the first item in each column */
+ item_in_column = (io_stat_item_t **)g_malloc(sizeof(io_stat_item_t *) * num_cols);
+ for (j=0; j<num_cols; j++) {
+ item_in_column[j] = stat_cols[j];
+ }
+
+ /* Display the table values
+ *
+ * The outer loop is for time interval rows and the inner loop is for stat column items.*/
+ for (i=0; i<num_rows; i++) {
+
+ if (i == num_rows-1)
+ last_row = TRUE;
+
+ /* Compute the interval for this row */
+ if (!last_row) {
+ invl_end = t + interval;
+ } else {
+ invl_end = duration;
+ }
+
+ /* Patch for Absolute Time */
+ /* XXX - has a Y2.038K problem with 32-bit time_t */
+ the_time = (time_t)(iot->start_time + (t/G_GUINT64_CONSTANT(1000000)));
+
+ /* Display the interval for this row */
+ switch (timestamp_get_type()) {
+ case TS_ABSOLUTE:
+ tm_time = localtime(&the_time);
+ if (tm_time != NULL) {
+ printf("| %02d:%02d:%02d |",
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("| XX:XX:XX |");
+ break;
+
+ case TS_ABSOLUTE_WITH_YMD:
+ tm_time = localtime(&the_time);
+ if (tm_time != NULL) {
+ printf("| %04d-%02d-%02d %02d:%02d:%02d |",
+ tm_time->tm_year + 1900,
+ tm_time->tm_mon + 1,
+ tm_time->tm_mday,
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("| XXXX-XX-XX XX:XX:XX |");
+ break;
+
+ case TS_ABSOLUTE_WITH_YDOY:
+ tm_time = localtime(&the_time);
+ if (tm_time != NULL) {
+ printf("| %04d/%03d %02d:%02d:%02d |",
+ tm_time->tm_year + 1900,
+ tm_time->tm_yday + 1,
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("| XXXX/XXX XX:XX:XX |");
+ break;
+
+ case TS_UTC:
+ tm_time = gmtime(&the_time);
+ if (tm_time != NULL) {
+ printf("| %02d:%02d:%02d |",
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("| XX:XX:XX |");
+ break;
+
+ case TS_UTC_WITH_YMD:
+ tm_time = gmtime(&the_time);
+ if (tm_time != NULL) {
+ printf("| %04d-%02d-%02d %02d:%02d:%02d |",
+ tm_time->tm_year + 1900,
+ tm_time->tm_mon + 1,
+ tm_time->tm_mday,
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("| XXXX-XX-XX XX:XX:XX |");
+ break;
+
+ case TS_UTC_WITH_YDOY:
+ tm_time = gmtime(&the_time);
+ if (tm_time != NULL) {
+ printf("| %04d/%03d %02d:%02d:%02d |",
+ tm_time->tm_year + 1900,
+ tm_time->tm_yday + 1,
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("| XXXX/XXX XX:XX:XX |");
+ break;
+
+ case TS_RELATIVE:
+ case TS_NOT_SET:
+ if (invl_prec == 0) {
+ if (last_row) {
+ int maxw;
+ maxw = dur_mag >= 3 ? dur_mag+1 : 3;
+ g_free(full_fmt);
+ snprintf(dur_mag_s, 3, "%u", maxw);
+ full_fmt = g_strconcat( dur_mag == 1 ? "| " : "| ",
+ invl_fmt, " <> ", "%-",
+ dur_mag_s, "s|", NULL);
+ printf(full_fmt, (guint32)(t/G_GUINT64_CONSTANT(1000000)), "Dur");
+ } else {
+ printf(full_fmt, (guint32)(t/G_GUINT64_CONSTANT(1000000)),
+ (guint32)(invl_end/G_GUINT64_CONSTANT(1000000)));
+ }
+ } else {
+ printf(full_fmt, (guint32)(t/G_GUINT64_CONSTANT(1000000)),
+ (guint32)(t%G_GUINT64_CONSTANT(1000000) / dv),
+ (guint32)(invl_end/G_GUINT64_CONSTANT(1000000)),
+ (guint32)(invl_end%G_GUINT64_CONSTANT(1000000) / dv));
+ }
+ break;
+ /* case TS_DELTA:
+ case TS_DELTA_DIS:
+ case TS_EPOCH:
+ are not implemented */
+ default:
+ break;
+ }
+
+ /* Display stat values in each column for this row */
+ for (j=0; j<num_cols; j++) {
+ fmt = fmts[j];
+ item = item_in_column[j];
+
+ if (item) {
+ switch (item->calc_type) {
+ case CALC_TYPE_FRAMES:
+ printf(fmt, item->frames);
+ break;
+ case CALC_TYPE_BYTES:
+ case CALC_TYPE_COUNT:
+ printf(fmt, item->counter);
+ break;
+ case CALC_TYPE_FRAMES_AND_BYTES:
+ printf(fmt, item->frames, item->counter);
+ break;
+
+ case CALC_TYPE_SUM:
+ case CALC_TYPE_MIN:
+ case CALC_TYPE_MAX:
+ ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
+ switch (ftype) {
+ case FT_FLOAT:
+ printf(fmt, item->float_counter);
+ break;
+ case FT_DOUBLE:
+ printf(fmt, item->double_counter);
+ break;
+ case FT_RELATIVE_TIME:
+ item->counter = (item->counter + G_GUINT64_CONSTANT(500)) / G_GUINT64_CONSTANT(1000);
+ printf(fmt,
+ (int)(item->counter/G_GUINT64_CONSTANT(1000000)),
+ (int)(item->counter%G_GUINT64_CONSTANT(1000000)));
+ break;
+ default:
+ printf(fmt, item->counter);
+ break;
+ }
+ break;
+
+ case CALC_TYPE_AVG:
+ num = item->num;
+ if (num == 0)
+ num = 1;
+ ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
+ switch (ftype) {
+ case FT_FLOAT:
+ printf(fmt, item->float_counter/num);
+ break;
+ case FT_DOUBLE:
+ printf(fmt, item->double_counter/num);
+ break;
+ case FT_RELATIVE_TIME:
+ item->counter = ((item->counter / (guint64)num) + G_GUINT64_CONSTANT(500)) / G_GUINT64_CONSTANT(1000);
+ printf(fmt,
+ (int)(item->counter/G_GUINT64_CONSTANT(1000000)),
+ (int)(item->counter%G_GUINT64_CONSTANT(1000000)));
+ break;
+ default:
+ printf(fmt, item->counter / (guint64)num);
+ break;
+ }
+ break;
+
+ case CALC_TYPE_LOAD:
+ ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
+ switch (ftype) {
+ case FT_RELATIVE_TIME:
+ if (!last_row) {
+ printf(fmt,
+ (int) (item->counter/interval),
+ (int)((item->counter%interval)*G_GUINT64_CONSTANT(1000000) / interval));
+ } else {
+ printf(fmt,
+ (int) (item->counter/(invl_end-t)),
+ (int)((item->counter%(invl_end-t))*G_GUINT64_CONSTANT(1000000) / (invl_end-t)));
+ }
+ break;
+ }
+ break;
+ }
+
+ if (last_row) {
+ g_free(fmt);
+ } else {
+ item_in_column[j] = item_in_column[j]->next;
+ }
+ } else {
+ printf(fmt, (guint64)0, (guint64)0);
+ }
+ }
+ if (filler_s)
+ printf("%s|", filler_s);
+ printf("\n");
+ t += interval;
+
+ }
+ for (i=0; i<borderlen; i++) {
+ printf("=");
+ }
+ printf("\n");
+ g_free(iot->items);
+ g_free(iot->max_vals);
+ g_free(iot->max_frame);
+ g_free(iot);
+ g_free(col_w);
+ g_free(invl_fmt);
+ g_free(full_fmt);
+ g_free(fmts);
+ g_free(spaces);
+ g_free(stat_cols);
+ g_free(item_in_column);
+}
+
+
+static void
+register_io_tap(io_stat_t *io, unsigned int i, const char *filter)
+{
+ GString *error_string;
+ const char *flt;
+ int j;
+ size_t namelen;
+ const char *p, *parenp;
+ char *field;
+ header_field_info *hfi;
+
+ io->items[i].prev = &io->items[i];
+ io->items[i].next = NULL;
+ io->items[i].parent = io;
+ io->items[i].start_time = 0;
+ io->items[i].calc_type = CALC_TYPE_FRAMES_AND_BYTES;
+ io->items[i].frames = 0;
+ io->items[i].counter = 0;
+ io->items[i].num = 0;
+
+ io->filters[i] = filter;
+ flt = filter;
+
+ field = NULL;
+ hfi = NULL;
+ for (j=0; calc_type_table[j].func_name; j++) {
+ namelen = strlen(calc_type_table[j].func_name);
+ if (filter && strncmp(filter, calc_type_table[j].func_name, namelen) == 0) {
+ io->items[i].calc_type = calc_type_table[j].calc_type;
+ io->items[i].colnum = i;
+ if (*(filter+namelen) == '(') {
+ p = filter+namelen+1;
+ parenp = strchr(p, ')');
+ if (!parenp) {
+ fprintf(stderr,
+ "\ntshark: Closing parenthesis missing from calculated expression.\n");
+ exit(10);
+ }
+
+ if (io->items[i].calc_type == CALC_TYPE_FRAMES || io->items[i].calc_type == CALC_TYPE_BYTES) {
+ if (parenp != p) {
+ fprintf(stderr,
+ "\ntshark: %s does not require or allow a field name within the parens.\n",
+ calc_type_table[j].func_name);
+ exit(10);
+ }
+ } else {
+ if (parenp == p) {
+ /* bail out if a field name was not specified */
+ fprintf(stderr, "\ntshark: You didn't specify a field name for %s(*).\n",
+ calc_type_table[j].func_name);
+ exit(10);
+ }
+ }
+
+ field = (char *)g_malloc(parenp-p+1);
+ memcpy(field, p, parenp-p);
+ field[parenp-p] = '\0';
+ flt = parenp + 1;
+ if (io->items[i].calc_type == CALC_TYPE_FRAMES || io->items[i].calc_type == CALC_TYPE_BYTES)
+ break;
+ hfi = proto_registrar_get_byname(field);
+ if (!hfi) {
+ fprintf(stderr, "\ntshark: There is no field named '%s'.\n",
+ field);
+ g_free(field);
+ exit(10);
+ }
+
+ io->items[i].hf_index = hfi->id;
+ break;
+ }
+ } else {
+ if (io->items[i].calc_type == CALC_TYPE_FRAMES || io->items[i].calc_type == CALC_TYPE_BYTES)
+ flt = "";
+ io->items[i].colnum = i;
+ }
+ }
+ if (hfi && !(io->items[i].calc_type == CALC_TYPE_BYTES ||
+ io->items[i].calc_type == CALC_TYPE_FRAMES ||
+ io->items[i].calc_type == CALC_TYPE_FRAMES_AND_BYTES)) {
+ /* check that the type is compatible */
+ switch (hfi->type) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ case FT_UINT64:
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ case FT_INT64:
+ /* these types support all calculations */
+ break;
+ case FT_FLOAT:
+ case FT_DOUBLE:
+ /* these types only support SUM, COUNT, MAX, MIN, AVG */
+ switch (io->items[i].calc_type) {
+ case CALC_TYPE_SUM:
+ case CALC_TYPE_COUNT:
+ case CALC_TYPE_MAX:
+ case CALC_TYPE_MIN:
+ case CALC_TYPE_AVG:
+ break;
+ default:
+ fprintf(stderr,
+ "\ntshark: %s is a float field, so %s(*) calculations are not supported on it.",
+ field,
+ calc_type_table[j].func_name);
+ exit(10);
+ }
+ break;
+ case FT_RELATIVE_TIME:
+ /* this type only supports SUM, COUNT, MAX, MIN, AVG, LOAD */
+ switch (io->items[i].calc_type) {
+ case CALC_TYPE_SUM:
+ case CALC_TYPE_COUNT:
+ case CALC_TYPE_MAX:
+ case CALC_TYPE_MIN:
+ case CALC_TYPE_AVG:
+ case CALC_TYPE_LOAD:
+ break;
+ default:
+ fprintf(stderr,
+ "\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
+ field,
+ calc_type_table[j].func_name);
+ exit(10);
+ }
+ break;
+ default:
+ /*
+ * XXX - support all operations on floating-point
+ * numbers?
+ */
+ if (io->items[i].calc_type != CALC_TYPE_COUNT) {
+ fprintf(stderr,
+ "\ntshark: %s doesn't have integral values, so %s(*) "
+ "calculations are not supported on it.\n",
+ field,
+ calc_type_table[j].func_name);
+ exit(10);
+ }
+ break;
+ }
+ }
+ g_free(field);
+
+ error_string = register_tap_listener("frame", &io->items[i], flt, TL_REQUIRES_PROTO_TREE, NULL,
+ iostat_packet, i ? NULL : iostat_draw, NULL);
+ if (error_string) {
+ g_free(io->items);
+ g_free(io);
+ fprintf(stderr, "\ntshark: Couldn't register io,stat tap: %s\n",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static void
+iostat_init(const char *opt_arg, void *userdata _U_)
+{
+ gdouble interval_float;
+ guint32 idx = 0;
+ unsigned int i;
+ io_stat_t *io;
+ const gchar *filters, *str, *pos;
+
+ if ((*(opt_arg+(strlen(opt_arg)-1)) == ',') ||
+ (sscanf(opt_arg, "io,stat,%lf%n", &interval_float, (int *)&idx) != 1) ||
+ (idx < 8)) {
+ fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
+ exit(1);
+ }
+
+ filters = opt_arg+idx;
+ if (*filters) {
+ if (*filters != ',') {
+ /* For locale's that use ',' instead of '.', the comma might
+ * have been consumed during the floating point conversion. */
+ --filters;
+ if (*filters != ',') {
+ fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
+ exit(1);
+ }
+ }
+ } else
+ filters = NULL;
+
+ switch (timestamp_get_type()) {
+ case TS_DELTA:
+ case TS_DELTA_DIS:
+ case TS_EPOCH:
+ fprintf(stderr, "\ntshark: invalid -t operand. io,stat only supports -t <r|a|ad|adoy|u|ud|udoy>\n");
+ exit(1);
+ default:
+ break;
+ }
+
+ io = g_new(io_stat_t, 1);
+
+ /* If interval is 0, calculate statistics over the whole file by setting the interval to
+ * G_MAXUINT64 */
+ if (interval_float == 0) {
+ io->interval = G_MAXUINT64;
+ io->invl_prec = 0;
+ } else {
+ /* Set interval to the number of us rounded to the nearest integer */
+ io->interval = (guint64)(interval_float * 1000000.0 + 0.5);
+ /*
+ * Determine what interval precision the user has specified */
+ io->invl_prec = 6;
+ for (i=10; i<10000000; i*=10) {
+ if (io->interval%i > 0)
+ break;
+ io->invl_prec--;
+ }
+ if (io->invl_prec == 0) {
+ /* The precision is zero but if the user specified one of more zeros after the decimal point,
+ they want that many decimal places shown in the table for all time intervals except
+ response time values such as smb.time which always have 6 decimal places of precision.
+ This feature is useful in cases where for example the duration is 9.1, you specify an
+ interval of 1 and the last interval becomes "9 <> 9". If the interval is instead set to
+ 1.1, the last interval becomes
+ last interval is rounded up to value that is greater than the duration. */
+ const gchar *invl_start = opt_arg+8;
+ gchar *intv_end;
+ int invl_len;
+
+ intv_end = g_strstr_len(invl_start, -1, ",");
+ invl_len = (int)(intv_end - invl_start);
+ invl_start = g_strstr_len(invl_start, invl_len, ".");
+
+ if (invl_start != NULL) {
+ invl_len = (int)(intv_end - invl_start - 1);
+ if (invl_len)
+ io->invl_prec = MIN(invl_len, 6);
+ }
+ }
+ }
+ if (io->interval < 1) {
+ fprintf(stderr,
+ "\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n");
+ exit(10);
+ }
+
+ /* Find how many ',' separated filters we have */
+ io->num_cols = 1;
+ io->start_time = 0;
+
+ if (filters && (*filters != '\0')) {
+ /* Eliminate the first comma. */
+ filters++;
+ str = filters;
+ while ((str = strchr(str, ','))) {
+ io->num_cols++;
+ str++;
+ }
+ }
+
+ io->items = g_new(io_stat_item_t, io->num_cols);
+ io->filters = (const char **)g_malloc(sizeof(char *) * io->num_cols);
+ io->max_vals = g_new(guint64, io->num_cols);
+ io->max_frame = g_new(guint32, io->num_cols);
+
+ for (i=0; i<io->num_cols; i++) {
+ io->max_vals[i] = 0;
+ io->max_frame[i] = 0;
+ }
+
+ /* Register a tap listener for each filter */
+ if ((!filters) || (filters[0] == 0)) {
+ register_io_tap(io, 0, NULL);
+ } else {
+ gchar *filter;
+ i = 0;
+ str = filters;
+ do {
+ pos = (gchar*) strchr(str, ',');
+ if (pos == str) {
+ register_io_tap(io, i, NULL);
+ } else if (pos == NULL) {
+ str = (const char*) g_strstrip((gchar*)str);
+ filter = g_strdup(str);
+ if (*filter)
+ register_io_tap(io, i, filter);
+ else
+ register_io_tap(io, i, NULL);
+ } else {
+ filter = (gchar *)g_malloc((pos-str)+1);
+ (void) g_strlcpy( filter, str, (gsize) ((pos-str)+1));
+ filter = g_strstrip(filter);
+ register_io_tap(io, i, (char *) filter);
+ }
+ str = pos+1;
+ i++;
+ } while (pos);
+ }
+}
+
+static stat_tap_ui iostat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "io,stat",
+ iostat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_iostat(void)
+{
+ register_stat_tap_ui(&iostat_ui, NULL);
+}
diff --git a/ui/cli/tap-iousers.c b/ui/cli/tap-iousers.c
new file mode 100644
index 00000000..742f984c
--- /dev/null
+++ b/ui/cli/tap-iousers.c
@@ -0,0 +1,267 @@
+/* tap-iousers.c
+ * iostat 2003 Ronnie Sahlberg
+ *
+ * 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/timestamp.h>
+#include <wsutil/str_util.h>
+#include <wsutil/cmdarg_err.h>
+#include <ui/cli/tshark-tap.h>
+
+typedef struct _io_users_t {
+ const char *type;
+ const char *filter;
+ conv_hash_t hash;
+} io_users_t;
+
+static void
+iousers_draw(void *arg)
+{
+ conv_hash_t *hash = (conv_hash_t*)arg;
+ io_users_t *iu = (io_users_t *)hash->user_data;
+ conv_item_t *iui;
+ guint64 last_frames, max_frames;
+ struct tm * tm_time;
+ guint i;
+ gboolean display_ports = (!strncmp(iu->type, "TCP", 3) || !strncmp(iu->type, "UDP", 3) || !strncmp(iu->type, "SCTP", 4)) ? TRUE : FALSE;
+
+ printf("================================================================================\n");
+ printf("%s Conversations\n", iu->type);
+ printf("Filter:%s\n", iu->filter ? iu->filter : "<No Filter>");
+
+ switch (timestamp_get_type()) {
+ case TS_ABSOLUTE:
+ case TS_UTC:
+ printf("%s | <- | | -> | | Total | Absolute Time | Duration |\n",
+ display_ports ? " " : "");
+ printf("%s | Frames Size | | Frames Size | | Frames Size | Start | |\n",
+ display_ports ? " " : "");
+ break;
+ case TS_ABSOLUTE_WITH_YMD:
+ case TS_ABSOLUTE_WITH_YDOY:
+ case TS_UTC_WITH_YMD:
+ case TS_UTC_WITH_YDOY:
+ printf("%s | <- | | -> | | Total | Absolute Date | Duration |\n",
+ display_ports ? " " : "");
+ printf("%s | Frames Size | | Frames Size | | Frames Size | Start | |\n",
+ display_ports ? " " : "");
+ break;
+ case TS_EPOCH:
+ printf("%s | <- | | -> | | Total | Relative | Duration |\n",
+ display_ports ? " " : "");
+ printf("%s | Frames Bytes | | Frames Bytes | | Frames Bytes | Start | |\n",
+ display_ports ? " " : "");
+ break;
+ case TS_RELATIVE:
+ case TS_NOT_SET:
+ default:
+ printf("%s | <- | | -> | | Total | Relative | Duration |\n",
+ display_ports ? " " : "");
+ printf("%s | Frames Bytes | | Frames Bytes | | Frames Bytes | Start | |\n",
+ display_ports ? " " : "");
+ break;
+ }
+
+ max_frames = UINT_MAX;
+ do {
+ last_frames = 0;
+ for (i=0; (iu->hash.conv_array && i < iu->hash.conv_array->len); i++) {
+ guint64 tot_frames;
+
+ iui = &g_array_index(iu->hash.conv_array, conv_item_t, i);
+ tot_frames = iui->rx_frames + iui->tx_frames;
+
+ if ((tot_frames > last_frames) && (tot_frames < max_frames)) {
+ last_frames = tot_frames;
+ }
+ }
+
+ for (i=0; (iu->hash.conv_array && i < iu->hash.conv_array->len); i++) {
+ guint64 tot_frames;
+ char *src_addr, *dst_addr;
+
+ iui = &g_array_index(iu->hash.conv_array, conv_item_t, i);
+ tot_frames = iui->rx_frames + iui->tx_frames;
+
+ if (tot_frames == last_frames) {
+ char *rx_bytes, *tx_bytes, *total_bytes;
+
+ rx_bytes = format_size(iui->rx_bytes, FORMAT_SIZE_UNIT_BYTES, 0);
+ tx_bytes = format_size(iui->tx_bytes, FORMAT_SIZE_UNIT_BYTES, 0);
+ total_bytes = format_size(iui->tx_bytes + iui->rx_bytes, FORMAT_SIZE_UNIT_BYTES, 0);
+
+ /* XXX - TODO: make name / port resolution configurable (through gbl_resolv_flags?) */
+ src_addr = get_conversation_address(NULL, &iui->src_address, TRUE);
+ dst_addr = get_conversation_address(NULL, &iui->dst_address, TRUE);
+ if (display_ports) {
+ char *src, *dst, *src_port, *dst_port;
+ src_port = get_conversation_port(NULL, iui->src_port, iui->ctype, TRUE);
+ dst_port = get_conversation_port(NULL, iui->dst_port, iui->ctype, TRUE);
+ src = wmem_strconcat(NULL, src_addr, ":", src_port, NULL);
+ dst = wmem_strconcat(NULL, dst_addr, ":", dst_port, NULL);
+ printf("%-26s <-> %-26s %6" PRIu64 " %-9s"
+ " %6" PRIu64 " %-9s"
+ " %6" PRIu64 " %-9s ",
+ src, dst,
+ iui->rx_frames, rx_bytes,
+ iui->tx_frames, tx_bytes,
+ iui->tx_frames+iui->rx_frames,
+ total_bytes
+ );
+ wmem_free(NULL, src_port);
+ wmem_free(NULL, dst_port);
+ wmem_free(NULL, src);
+ wmem_free(NULL, dst);
+ } else {
+ printf("%-20s <-> %-20s %6" PRIu64 " %-9s"
+ " %6" PRIu64 " %-9s"
+ " %6" PRIu64 " %-9s ",
+ src_addr, dst_addr,
+ iui->rx_frames, rx_bytes,
+ iui->tx_frames, tx_bytes,
+ iui->tx_frames+iui->rx_frames,
+ total_bytes
+ );
+ }
+
+ wmem_free(NULL, src_addr);
+ wmem_free(NULL, dst_addr);
+ wmem_free(NULL, rx_bytes);
+ wmem_free(NULL, tx_bytes);
+ wmem_free(NULL, total_bytes);
+
+ switch (timestamp_get_type()) {
+ case TS_ABSOLUTE:
+ tm_time = localtime(&iui->start_abs_time.secs);
+ if (tm_time != NULL) {
+ printf("%02d:%02d:%02d",
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("XX:XX:XX");
+ break;
+ case TS_ABSOLUTE_WITH_YMD:
+ tm_time = localtime(&iui->start_abs_time.secs);
+ if (tm_time != NULL) {
+ printf("%04d-%02d-%02d %02d:%02d:%02d",
+ tm_time->tm_year + 1900,
+ tm_time->tm_mon + 1,
+ tm_time->tm_mday,
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("XXXX-XX-XX XX:XX:XX");
+ break;
+ case TS_ABSOLUTE_WITH_YDOY:
+ tm_time = localtime(&iui->start_abs_time.secs);
+ if (tm_time != NULL) {
+ printf("%04d/%03d %02d:%02d:%02d",
+ tm_time->tm_year + 1900,
+ tm_time->tm_yday + 1,
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("XXXX/XXX XX:XX:XX");
+ break;
+ case TS_UTC:
+ tm_time = gmtime(&iui->start_abs_time.secs);
+ if (tm_time != NULL) {
+ printf("%02d:%02d:%02d",
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("XX:XX:XX");
+ break;
+ case TS_UTC_WITH_YMD:
+ tm_time = gmtime(&iui->start_abs_time.secs);
+ if (tm_time != NULL) {
+ printf("%04d-%02d-%02d %02d:%02d:%02d",
+ tm_time->tm_year + 1900,
+ tm_time->tm_mon + 1,
+ tm_time->tm_mday,
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("XXXX-XX-XX XX:XX:XX");
+ break;
+ case TS_UTC_WITH_YDOY:
+ tm_time = gmtime(&iui->start_abs_time.secs);
+ if (tm_time != NULL) {
+ printf("%04d/%03d %02d:%02d:%02d",
+ tm_time->tm_year + 1900,
+ tm_time->tm_yday + 1,
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("XXXX/XXX XX:XX:XX");
+ break;
+ case TS_EPOCH:
+ printf("%20.9f", nstime_to_sec(&iui->start_abs_time));
+ break;
+ case TS_RELATIVE:
+ case TS_NOT_SET:
+ default:
+ printf("%14.9f",
+ nstime_to_sec(&iui->start_time));
+ break;
+ }
+ printf(" %12.4f\n",
+ nstime_to_sec(&iui->stop_time) - nstime_to_sec(&iui->start_time));
+ }
+ }
+ max_frames = last_frames;
+ } while (last_frames);
+ printf("================================================================================\n");
+}
+
+void init_iousers(struct register_ct *ct, const char *filter)
+{
+ io_users_t *iu;
+ GString *error_string;
+
+ iu = g_new0(io_users_t, 1);
+ iu->type = proto_get_protocol_short_name(find_protocol_by_id(get_conversation_proto_id(ct)));
+ iu->filter = g_strdup(filter);
+ iu->hash.user_data = iu;
+
+ error_string = register_tap_listener(proto_get_protocol_filter_name(get_conversation_proto_id(ct)), &iu->hash, filter, 0, NULL, get_conversation_packet_func(ct), iousers_draw, NULL);
+ if (error_string) {
+ g_free(iu);
+ cmdarg_err("Couldn't register conversations tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-macltestat.c b/ui/cli/tap-macltestat.c
new file mode 100644
index 00000000..ffe78ad7
--- /dev/null
+++ b/ui/cli/tap-macltestat.c
@@ -0,0 +1,551 @@
+/* tap-macltestat.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-mac-lte.h>
+
+void register_tap_listener_mac_lte_stat(void);
+
+/**********************************************/
+/* Table column identifiers and title strings */
+
+enum {
+ RNTI_COLUMN,
+ RNTI_TYPE_COLUMN,
+ UEID_COLUMN,
+ UL_FRAMES_COLUMN,
+ UL_BYTES_COLUMN,
+ UL_BW_COLUMN,
+ UL_PADDING_PERCENT_COLUMN,
+ UL_RETX_FRAMES_COLUMN,
+ DL_FRAMES_COLUMN,
+ DL_BYTES_COLUMN,
+ DL_BW_COLUMN,
+ DL_PADDING_PERCENT_COLUMN,
+ DL_CRC_FAILED_COLUMN,
+ DL_CRC_HIGH_CODE_RATE_COLUMN,
+ DL_CRC_PDSCH_LOST_COLUMN,
+ DL_CRC_DUPLICATE_NONZERO_RV_COLUMN,
+ DL_RETX_FRAMES_COLUMN,
+ NUM_UE_COLUMNS
+};
+
+
+static const gchar *ue_titles[] = { " RNTI", " Type", "UEId",
+ "UL Frames", "UL Bytes", "UL Mb/sec", " UL Pad %", "UL ReTX",
+ "DL Frames", "DL Bytes", "DL Mb/sec", " DL Pad %", "DL CRC Fail", "DL CRC HCR", "DL CRC PDSCH Lost", "DL CRC DupNonZeroRV", "DL ReTX"};
+
+
+/* Stats for one UE */
+typedef struct mac_lte_row_data {
+ /* Key for matching this row */
+ guint16 rnti;
+ guint8 rnti_type;
+ guint16 ueid;
+
+ gboolean is_predefined_data;
+
+ guint32 UL_frames;
+ guint32 UL_raw_bytes; /* all bytes */
+ guint32 UL_total_bytes; /* payload */
+ nstime_t UL_time_start;
+ nstime_t UL_time_stop;
+ guint32 UL_padding_bytes;
+ guint32 UL_CRC_errors;
+ guint32 UL_retx_frames;
+
+ guint32 DL_frames;
+ guint32 DL_raw_bytes; /* all bytes */
+ guint32 DL_total_bytes;
+ nstime_t DL_time_start;
+ nstime_t DL_time_stop;
+ guint32 DL_padding_bytes;
+
+ guint32 DL_CRC_failures;
+ guint32 DL_CRC_high_code_rate;
+ guint32 DL_CRC_PDSCH_lost;
+ guint32 DL_CRC_Duplicate_NonZero_RV;
+ guint32 DL_retx_frames;
+
+} mac_lte_row_data;
+
+
+/* One row/UE in the UE table */
+typedef struct mac_lte_ep {
+ struct mac_lte_ep *next;
+ struct mac_lte_row_data stats;
+} mac_lte_ep_t;
+
+
+/* Common channel stats */
+typedef struct mac_lte_common_stats {
+ guint32 all_frames;
+ guint32 mib_frames;
+ guint32 sib_frames;
+ guint32 sib_bytes;
+ guint32 pch_frames;
+ guint32 pch_bytes;
+ guint32 pch_paging_ids;
+ guint32 rar_frames;
+ guint32 rar_entries;
+
+ guint16 max_ul_ues_in_tti;
+ guint16 max_dl_ues_in_tti;
+} mac_lte_common_stats;
+
+
+/* Top-level struct for MAC LTE statistics */
+typedef struct mac_lte_stat_t {
+ /* Common stats */
+ mac_lte_common_stats common_stats;
+
+ /* Keep track of unique rntis & ueids */
+ guint8 used_ueids[65535];
+ guint8 used_rntis[65535];
+ guint16 number_of_ueids;
+ guint16 number_of_rntis;
+
+ mac_lte_ep_t *ep_list;
+} mac_lte_stat_t;
+
+
+/* Reset the statistics window */
+static void
+mac_lte_stat_reset(void *phs)
+{
+ mac_lte_stat_t *mac_lte_stat = (mac_lte_stat_t *)phs;
+ mac_lte_ep_t *list = mac_lte_stat->ep_list;
+
+ /* Reset counts of unique ueids & rntis */
+ memset(mac_lte_stat->used_ueids, 0, 65535);
+ mac_lte_stat->number_of_ueids = 0;
+ memset(mac_lte_stat->used_rntis, 0, 65535);
+ mac_lte_stat->number_of_rntis = 0;
+
+ /* Zero common stats */
+ memset(&(mac_lte_stat->common_stats), 0, sizeof(mac_lte_common_stats));
+
+ if (!list) {
+ return;
+ }
+
+ mac_lte_stat->ep_list = NULL;
+}
+
+
+/* Allocate a mac_lte_ep_t struct to store info for new UE */
+static mac_lte_ep_t *alloc_mac_lte_ep(const struct mac_lte_tap_info *si, packet_info *pinfo _U_)
+{
+ mac_lte_ep_t *ep;
+
+ if (!si) {
+ return NULL;
+ }
+
+ if (!(ep = g_new(mac_lte_ep_t, 1))) {
+ return NULL;
+ }
+
+ /* Copy SI data into ep->stats */
+ ep->stats.rnti = si->rnti;
+ ep->stats.rnti_type = si->rntiType;
+ 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.UL_raw_bytes = 0;
+ ep->stats.UL_padding_bytes = 0;
+
+ ep->stats.DL_total_bytes = 0;
+ ep->stats.DL_raw_bytes = 0;
+ ep->stats.DL_padding_bytes = 0;
+
+ ep->stats.UL_CRC_errors = 0;
+ ep->stats.DL_CRC_failures = 0;
+ ep->stats.DL_CRC_high_code_rate = 0;
+ ep->stats.DL_CRC_PDSCH_lost = 0;
+ ep->stats.DL_CRC_Duplicate_NonZero_RV = 0;
+ ep->stats.UL_retx_frames = 0;
+ ep->stats.DL_retx_frames = 0;
+
+ ep->next = NULL;
+
+ return ep;
+}
+
+
+/* Update counts of unique rntis & ueids */
+static void update_ueid_rnti_counts(guint16 rnti, guint16 ueid, mac_lte_stat_t *hs)
+{
+ if (!hs->used_ueids[ueid]) {
+ hs->used_ueids[ueid] = TRUE;
+ hs->number_of_ueids++;
+ }
+ if (!hs->used_rntis[rnti]) {
+ hs->used_rntis[rnti] = TRUE;
+ hs->number_of_rntis++;
+ }
+}
+
+
+/* Process stat struct for a MAC LTE frame */
+static tap_packet_status
+mac_lte_stat_packet(void *phs, packet_info *pinfo, epan_dissect_t *edt _U_,
+ const void *phi, tap_flags_t flags _U_)
+{
+ /* Get reference to stat window instance */
+ mac_lte_stat_t *hs = (mac_lte_stat_t *)phs;
+ mac_lte_ep_t *tmp = NULL, *te = NULL;
+ int i;
+
+ /* Cast tap info struct */
+ const struct mac_lte_tap_info *si = (const struct mac_lte_tap_info *)phi;
+
+ if (!hs) {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+
+ hs->common_stats.all_frames++;
+
+ /* For common channels, just update global counters */
+ switch (si->rntiType) {
+ case P_RNTI:
+ hs->common_stats.pch_frames++;
+ hs->common_stats.pch_bytes += si->single_number_of_bytes;
+ hs->common_stats.pch_paging_ids += si->number_of_paging_ids;
+ return TAP_PACKET_REDRAW;
+ case SI_RNTI:
+ hs->common_stats.sib_frames++;
+ hs->common_stats.sib_bytes += si->single_number_of_bytes;
+ return TAP_PACKET_REDRAW;
+ case NO_RNTI:
+ hs->common_stats.mib_frames++;
+ return TAP_PACKET_REDRAW;
+ case RA_RNTI:
+ hs->common_stats.rar_frames++;
+ hs->common_stats.rar_entries += si->number_of_rars;
+ return TAP_PACKET_REDRAW;
+ case C_RNTI:
+ case SPS_RNTI:
+ /* Drop through for per-UE update */
+ break;
+
+ default:
+ /* Error */
+ return TAP_PACKET_DONT_REDRAW;
+ }
+
+ /* Check max UEs/tti counter */
+ switch (si->direction) {
+ case DIRECTION_UPLINK:
+ hs->common_stats.max_ul_ues_in_tti =
+ MAX(hs->common_stats.max_ul_ues_in_tti, si->ueInTTI);
+ break;
+ case DIRECTION_DOWNLINK:
+ hs->common_stats.max_dl_ues_in_tti =
+ MAX(hs->common_stats.max_dl_ues_in_tti, si->ueInTTI);
+ 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_mac_lte_ep(si, pinfo);
+ /* Make it the first/only entry */
+ te = hs->ep_list;
+
+ /* Update counts of unique ueids & rntis */
+ update_ueid_rnti_counts(si->rnti, si->ueid, hs);
+ } else {
+ /* Look among existing rows for this RNTI */
+ for (tmp = hs->ep_list;(tmp != NULL); tmp = tmp->next) {
+ /* Match only by RNTI and UEId together */
+ if ((tmp->stats.rnti == si->rnti) &&
+ (tmp->stats.ueid == si->ueid)) {
+ te = tmp;
+ break;
+ }
+ }
+
+ /* Not found among existing, so create a new one anyway */
+ if (te == NULL) {
+ if ((te = alloc_mac_lte_ep(si, pinfo))) {
+ /* Add new item to end of list */
+ mac_lte_ep_t *p = hs->ep_list;
+ while (p->next) {
+ p = p->next;
+ }
+ p->next = te;
+ te->next = NULL;
+
+ /* Update counts of unique ueids & rntis */
+ update_ueid_rnti_counts(si->rnti, si->ueid, hs);
+ }
+ }
+ }
+
+ /* Really should have a row pointer by now */
+ if (!te) {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+
+ /* Update entry with details from si */
+ te->stats.rnti = si->rnti;
+ te->stats.is_predefined_data = si->isPredefinedData;
+
+ /* Uplink */
+ if (si->direction == DIRECTION_UPLINK) {
+ if (si->isPHYRetx) {
+ te->stats.UL_retx_frames++;
+ return TAP_PACKET_REDRAW;
+ }
+
+ if (si->crcStatusValid && (si->crcStatus != crc_success)) {
+ te->stats.UL_CRC_errors++;
+ return TAP_PACKET_REDRAW;
+ }
+
+ /* Update time range */
+ if (te->stats.UL_frames == 0) {
+ te->stats.UL_time_start = si->mac_lte_time;
+ }
+ te->stats.UL_time_stop = si->mac_lte_time;
+
+ te->stats.UL_frames++;
+
+ te->stats.UL_raw_bytes += si->raw_length;
+ te->stats.UL_padding_bytes += si->padding_bytes;
+
+ if (si->isPredefinedData) {
+ te->stats.UL_total_bytes += si->single_number_of_bytes;
+ }
+ else {
+ for (i = 0; i < MAC_LTE_DATA_LCID_COUNT_MAX; i++) {
+ te->stats.UL_total_bytes += si->bytes_for_lcid[i];
+ }
+ }
+ }
+
+ /* Downlink */
+ else {
+ if (si->isPHYRetx) {
+ te->stats.DL_retx_frames++;
+ return TAP_PACKET_REDRAW;
+ }
+
+ if (si->crcStatusValid && (si->crcStatus != crc_success)) {
+ switch (si->crcStatus) {
+ case crc_fail:
+ te->stats.DL_CRC_failures++;
+ break;
+ case crc_high_code_rate:
+ te->stats.DL_CRC_high_code_rate++;
+ break;
+ case crc_pdsch_lost:
+ te->stats.DL_CRC_PDSCH_lost++;
+ break;
+ case crc_duplicate_nonzero_rv:
+ te->stats.DL_CRC_Duplicate_NonZero_RV++;
+ break;
+
+ default:
+ /* Something went wrong! */
+ break;
+ }
+ return TAP_PACKET_REDRAW;
+ }
+
+ /* Update time range */
+ if (te->stats.DL_frames == 0) {
+ te->stats.DL_time_start = si->mac_lte_time;
+ }
+ te->stats.DL_time_stop = si->mac_lte_time;
+
+ te->stats.DL_frames++;
+
+ te->stats.DL_raw_bytes += si->raw_length;
+ te->stats.DL_padding_bytes += si->padding_bytes;
+
+ if (si->isPredefinedData) {
+ te->stats.DL_total_bytes += si->single_number_of_bytes;
+ }
+ else {
+ for (i = 0; i < MAC_LTE_DATA_LCID_COUNT_MAX; i++) {
+ te->stats.DL_total_bytes += si->bytes_for_lcid[i];
+ }
+ }
+
+ }
+
+ 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;
+ }
+}
+
+
+
+/* Output the accumulated stats */
+static void
+mac_lte_stat_draw(void *phs)
+{
+ gint i;
+ guint16 number_of_ues = 0;
+
+ /* Deref the struct */
+ mac_lte_stat_t *hs = (mac_lte_stat_t *)phs;
+ mac_lte_ep_t *list = hs->ep_list, *tmp = 0;
+
+ /* System data */
+ printf("System data:\n");
+ printf("============\n");
+ printf("Max UL UEs/TTI: %u Max DL UEs/TTI: %u\n\n",
+ hs->common_stats.max_ul_ues_in_tti, hs->common_stats.max_dl_ues_in_tti);
+
+ /* Common channel data */
+ printf("Common channel data:\n");
+ printf("====================\n");
+ printf("MIBs: %u ", hs->common_stats.mib_frames);
+ printf("SIB Frames: %u ", hs->common_stats.sib_frames);
+ printf("SIB Bytes: %u ", hs->common_stats.sib_bytes);
+ printf("PCH Frames: %u ", hs->common_stats.pch_frames);
+ printf("PCH Bytes: %u ", hs->common_stats.pch_bytes);
+ printf("PCH Paging IDs: %u ", hs->common_stats.pch_paging_ids);
+ printf("RAR Frames: %u ", hs->common_stats.rar_frames);
+ printf("RAR Entries: %u\n\n", hs->common_stats.rar_entries);
+
+
+ /* Per-UE table entries */
+
+ /* Set title to show how many UEs in table */
+ for (tmp = list; (tmp!=NULL); tmp=tmp->next, number_of_ues++);
+ printf("UL/DL-SCH data (%u entries - %u unique RNTIs, %u unique UEIds):\n",
+ number_of_ues, hs->number_of_rntis, hs->number_of_ueids);
+ printf("==================================================================\n");
+
+ /* Show column titles */
+ for (i=0; i < NUM_UE_COLUMNS; i++) {
+ printf("%s ", ue_titles[i]);
+ }
+ printf("\n");
+
+ /* Write a row for each UE */
+ 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 %7s %5u %10u %9u %10f %10f %8u %10u %9u %10f %10f %12u %11u %18u %20u %8u\n",
+ tmp->stats.rnti,
+ (tmp->stats.rnti_type == C_RNTI) ? "C-RNTI" : "SPS-RNTI",
+ tmp->stats.ueid,
+ tmp->stats.UL_frames,
+ tmp->stats.UL_total_bytes,
+ UL_bw,
+ tmp->stats.UL_raw_bytes ?
+ (((float)tmp->stats.UL_padding_bytes / (float)tmp->stats.UL_raw_bytes) * 100.0) :
+ 0.0,
+ tmp->stats.UL_retx_frames,
+ tmp->stats.DL_frames,
+ tmp->stats.DL_total_bytes,
+ DL_bw,
+ tmp->stats.DL_raw_bytes ?
+ (((float)tmp->stats.DL_padding_bytes / (float)tmp->stats.DL_raw_bytes) * 100.0) :
+ 0.0,
+ tmp->stats.DL_CRC_failures,
+ tmp->stats.DL_CRC_high_code_rate,
+ tmp->stats.DL_CRC_PDSCH_lost,
+ tmp->stats.DL_CRC_Duplicate_NonZero_RV,
+ tmp->stats.DL_retx_frames);
+ }
+}
+
+/* Create a new MAC LTE stats struct */
+static void mac_lte_stat_init(const char *opt_arg, void *userdata _U_)
+{
+ mac_lte_stat_t *hs;
+ const char *filter = NULL;
+ GString *error_string;
+
+ /* Check for a filter string */
+ if (strncmp(opt_arg, "mac-lte,stat,", 13) == 0) {
+ /* Skip those characters from filter to display */
+ filter = opt_arg + 13;
+ }
+ else {
+ /* No filter */
+ filter = NULL;
+ }
+
+ /* Create struct */
+ hs = g_new0(mac_lte_stat_t, 1);
+ hs->ep_list = NULL;
+
+ error_string = register_tap_listener("mac-lte", hs,
+ filter, 0,
+ mac_lte_stat_reset,
+ mac_lte_stat_packet,
+ mac_lte_stat_draw,
+ NULL);
+ if (error_string) {
+ g_string_free(error_string, TRUE);
+ g_free(hs);
+ exit(1);
+ }
+}
+
+static stat_tap_ui mac_lte_stat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "mac-lte,stat",
+ mac_lte_stat_init,
+ 0,
+ NULL
+};
+
+/* Register this tap listener (need void on own so line register function found) */
+void
+register_tap_listener_mac_lte_stat(void)
+{
+ register_stat_tap_ui(&mac_lte_stat_ui, NULL);
+}
diff --git a/ui/cli/tap-protocolinfo.c b/ui/cli/tap-protocolinfo.c
new file mode 100644
index 00000000..ea47ae52
--- /dev/null
+++ b/ui/cli/tap-protocolinfo.c
@@ -0,0 +1,150 @@
+/* tap-protocolinfo.c
+ * protohierstat 2002 Ronnie Sahlberg
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* This module provides Protocol Column Info tap for tshark */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "epan/epan_dissect.h"
+#include "epan/column-utils.h"
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+
+#include <wsutil/cmdarg_err.h>
+
+void register_tap_listener_protocolinfo(void);
+
+typedef struct _pci_t {
+ char *filter;
+ int hf_index;
+} pci_t;
+
+
+static tap_packet_status
+protocolinfo_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_, tap_flags_t flags _U_)
+{
+ pci_t *rs = (pci_t *)prs;
+ GPtrArray *gp;
+ guint i;
+ char *str;
+
+ /*
+ * XXX - there needs to be a way for "protocolinfo_init()" to
+ * find out whether the columns are being generated and, if not,
+ * to report an error and exit, as the whole point of this tap
+ * is to modify the columns, and if the columns aren't being
+ * displayed, that makes this tap somewhat pointless.
+ *
+ * To prevent a crash, we check whether INFO column is writable
+ * and, if not, we report that error and exit.
+ *
+ * XXX - report the error and just return TAP_PACKET_FAILED?
+ */
+ if (!col_get_writable(pinfo->cinfo, COL_INFO)) {
+ cmdarg_err("the proto,colinfo tap doesn't work if the INFO column isn't being printed.");
+ exit(1);
+ }
+ gp = proto_get_finfo_ptr_array(edt->tree, rs->hf_index);
+ if (!gp) {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+
+ for (i=0; i<gp->len; i++) {
+ str = (char *)proto_construct_match_selected_string((field_info *)gp->pdata[i], NULL);
+ if (str) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " %s", str);
+ wmem_free(NULL, str);
+ }
+ }
+ return TAP_PACKET_DONT_REDRAW;
+}
+
+
+
+static void
+protocolinfo_init(const char *opt_arg, void *userdata _U_)
+{
+ pci_t *rs;
+ const char *field = NULL;
+ const char *filter = NULL;
+ header_field_info *hfi;
+ GString *error_string;
+
+ if (!strncmp("proto,colinfo,", opt_arg, 14)) {
+ filter = opt_arg+14;
+ field = strchr(filter, ',');
+ if (field) {
+ field += 1; /* skip the ',' */
+ }
+ }
+ if (!field) {
+ cmdarg_err("invalid \"-z proto,colinfo,<filter>,<field>\" argument");
+ exit(1);
+ }
+
+ hfi = proto_registrar_get_byname(field);
+ if (!hfi) {
+ cmdarg_err("Field \"%s\" doesn't exist.", field);
+ exit(1);
+ }
+
+ rs = g_new(pci_t, 1);
+ rs->hf_index = hfi->id;
+ if ((field-filter) > 1) {
+ rs->filter = (char *)g_malloc(field-filter);
+ (void) g_strlcpy(rs->filter, filter, (field-filter));
+ } else {
+ rs->filter = NULL;
+ }
+
+ error_string = register_tap_listener("frame", rs, rs->filter, TL_REQUIRES_PROTO_TREE, NULL, protocolinfo_packet, NULL, NULL);
+ if (error_string) {
+ /* error, we failed to attach to the tap. complain and clean up */
+ cmdarg_err("Couldn't register proto,colinfo tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(rs->filter);
+ g_free(rs);
+
+ exit(1);
+ }
+}
+
+static stat_tap_ui protocolinfo_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "proto,colinfo",
+ protocolinfo_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_protocolinfo(void)
+{
+ register_stat_tap_ui(&protocolinfo_ui, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-protohierstat.c b/ui/cli/tap-protohierstat.c
new file mode 100644
index 00000000..2628fdcf
--- /dev/null
+++ b/ui/cli/tap-protohierstat.c
@@ -0,0 +1,244 @@
+/* tap-protohierstat.c
+ * protohierstat 2002 Ronnie Sahlberg
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* This module provides ProtocolHierarchyStatistics for tshark */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "epan/epan_dissect.h"
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+
+#include <wsutil/cmdarg_err.h>
+#include "tap-protohierstat.h"
+
+int pc_proto_id = -1;
+
+void register_tap_listener_protohierstat(void);
+
+phs_t *
+new_phs_t(phs_t *parent, const char *filter)
+{
+ phs_t *rs;
+ rs = g_new(phs_t, 1);
+ rs->sibling = NULL;
+ rs->child = NULL;
+ rs->parent = parent;
+ rs->filter = NULL;
+ if (filter != NULL) {
+ rs->filter = g_strdup(filter);
+ }
+ rs->protocol = -1;
+ rs->proto_name = NULL;
+ rs->frames = 0;
+ rs->bytes = 0;
+ return rs;
+}
+
+void
+free_phs(phs_t *rs)
+{
+ if (!rs) {
+ return;
+ }
+ if (rs->filter) {
+ g_free(rs->filter);
+ rs->filter = NULL;
+ }
+ if (rs->sibling)
+ {
+ free_phs(rs->sibling);
+ rs->sibling = NULL;
+ }
+ if (rs->child)
+ {
+ free_phs(rs->child);
+ rs->child = NULL;
+ }
+ g_free(rs);
+}
+
+tap_packet_status
+protohierstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_, tap_flags_t flags _U_)
+{
+ phs_t *rs = (phs_t *)prs;
+ phs_t *tmprs;
+ proto_node *node;
+ field_info *fi;
+
+ if (!edt) {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+ if (!edt->tree) {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+ if (!edt->tree->first_child) {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+
+ for (node=edt->tree->first_child; node; node=node->next) {
+ fi = PNODE_FINFO(node);
+
+ /*
+ * If the first child is a tree of comments, skip over it.
+ * This keeps us from having a top-level "pkt_comment"
+ * entry that represents a nonexistent protocol,
+ * and matches how the GUI treats comments.
+ */
+ if (G_UNLIKELY(fi->hfinfo->id == pc_proto_id)) {
+ continue;
+ }
+
+ /* first time we saw a protocol at this leaf */
+ if (rs->protocol == -1) {
+ rs->protocol = fi->hfinfo->id;
+ rs->proto_name = fi->hfinfo->abbrev;
+ rs->frames = 1;
+ rs->bytes = pinfo->fd->pkt_len;
+ rs->child = new_phs_t(rs, NULL);
+ rs = rs->child;
+ continue;
+ }
+
+ /* find this protocol in the list of siblings */
+ for (tmprs=rs; tmprs; tmprs=tmprs->sibling) {
+ if (tmprs->protocol == fi->hfinfo->id) {
+ break;
+ }
+ }
+
+ /* not found, then we must add it to the end of the list */
+ if (!tmprs) {
+ for (tmprs=rs; tmprs->sibling; tmprs=tmprs->sibling)
+ ;
+ tmprs->sibling = new_phs_t(rs->parent, NULL);
+ rs = tmprs->sibling;
+ rs->protocol = fi->hfinfo->id;
+ rs->proto_name = fi->hfinfo->abbrev;
+ } else {
+ rs = tmprs;
+ }
+
+ rs->frames++;
+ rs->bytes += pinfo->fd->pkt_len;
+
+ if (!rs->child) {
+ rs->child = new_phs_t(rs, NULL);
+ }
+ rs = rs->child;
+ }
+ return TAP_PACKET_REDRAW;
+}
+
+static void
+phs_draw(phs_t *rs, int indentation)
+{
+ int i, stroff;
+#define MAXPHSLINE 80
+ char str[MAXPHSLINE];
+ for (;rs;rs = rs->sibling) {
+ if (rs->protocol == -1) {
+ return;
+ }
+ str[0] = 0;
+ stroff = 0;
+ for (i=0; i<indentation; i++) {
+ if (i > 15) {
+ stroff += snprintf(str+stroff, MAXPHSLINE-stroff, "...");
+ break;
+ }
+ stroff += snprintf(str+stroff, MAXPHSLINE-stroff, " ");
+ }
+ snprintf(str+stroff, MAXPHSLINE-stroff, "%s", rs->proto_name);
+ printf("%-40s frames:%u bytes:%" PRIu64 "\n", str, rs->frames, rs->bytes);
+ phs_draw(rs->child, indentation+1);
+ }
+}
+
+static void
+protohierstat_draw(void *prs)
+{
+ phs_t *rs = (phs_t *)prs;
+
+ printf("\n");
+ printf("===================================================================\n");
+ printf("Protocol Hierarchy Statistics\n");
+ printf("Filter: %s\n\n", rs->filter ? rs->filter : "");
+ phs_draw(rs, 0);
+ printf("===================================================================\n");
+}
+
+
+static void
+protohierstat_init(const char *opt_arg, void *userdata _U_)
+{
+ phs_t *rs;
+ int pos = 0;
+ const char *filter = NULL;
+ GString *error_string;
+
+ if (strcmp("io,phs", opt_arg) == 0) {
+ /* No arguments */
+ } else if (sscanf(opt_arg, "io,phs,%n", &pos) == 0) {
+ if (pos) {
+ filter = opt_arg+pos;
+ }
+ } else {
+ cmdarg_err("invalid \"-z io,phs[,<filter>]\" argument");
+ exit(1);
+ }
+
+ pc_proto_id = proto_registrar_get_id_byname("pkt_comment");
+
+ rs = new_phs_t(NULL, filter);
+
+ error_string = register_tap_listener("frame", rs, filter, TL_REQUIRES_PROTO_TREE, NULL, protohierstat_packet, protohierstat_draw, NULL);
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ free_phs(rs);
+
+ cmdarg_err("Couldn't register io,phs tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static stat_tap_ui protohierstat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "io,phs",
+ protohierstat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_protohierstat(void)
+{
+ register_stat_tap_ui(&protohierstat_ui, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-protohierstat.h b/ui/cli/tap-protohierstat.h
new file mode 100644
index 00000000..3a68ff18
--- /dev/null
+++ b/ui/cli/tap-protohierstat.h
@@ -0,0 +1,51 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __TAP_PROTO_HIER_STAT_H__
+#define __TAP_PROTO_HIER_STAT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+extern int pc_proto_id;
+
+typedef struct _phs_t {
+ struct _phs_t *sibling;
+ struct _phs_t *child;
+ struct _phs_t *parent;
+ char *filter;
+ int protocol;
+ const char *proto_name;
+ guint32 frames;
+ guint64 bytes;
+} phs_t;
+
+extern phs_t * new_phs_t(phs_t *parent, const char *filter);
+extern void free_phs(phs_t *rs);
+extern tap_packet_status protohierstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_, tap_flags_t flags _U_);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TAP_PROTO_HIER_STAT_H__ */
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
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);
+}
diff --git a/ui/cli/tap-rpcprogs.c b/ui/cli/tap-rpcprogs.c
new file mode 100644
index 00000000..e64c93bc
--- /dev/null
+++ b/ui/cli/tap-rpcprogs.c
@@ -0,0 +1,246 @@
+/* tap-rpcprogs.c
+ * rpcstat 2002 Ronnie Sahlberg
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* This module provides rpc call/reply SRT statistics to tshark.
+ * It is only used by tshark and not wireshark
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/dissectors/packet-rpc.h>
+
+#include <wsutil/cmdarg_err.h>
+
+#define MICROSECS_PER_SEC 1000000
+#define NANOSECS_PER_SEC 1000000000
+
+void register_tap_listener_rpcprogs(void);
+
+/* used to keep track of statistics for a specific program/version */
+typedef struct _rpc_program_t {
+ struct _rpc_program_t *next;
+ guint32 program;
+ guint32 version;
+ int num;
+ nstime_t min;
+ nstime_t max;
+ nstime_t tot;
+} rpc_program_t;
+
+static rpc_program_t *prog_list = NULL;
+static int already_enabled = 0;
+
+static tap_packet_status
+rpcprogs_packet(void *dummy1 _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri, tap_flags_t flags _U_)
+{
+ const rpc_call_info_value *ri = (const rpc_call_info_value *)pri;
+ nstime_t delta;
+ rpc_program_t *rp = NULL;
+
+ if (!prog_list) {
+ /* the list was empty */
+ rp = g_new(rpc_program_t, 1);
+ rp->next = NULL;
+ rp->program = ri->prog;
+ rp->version = ri->vers;
+ rp->num = 0;
+ rp->min.secs = 0;
+ rp->min.nsecs = 0;
+ rp->max.secs = 0;
+ rp->max.nsecs = 0;
+ rp->tot.secs = 0;
+ rp->tot.nsecs = 0;
+ prog_list = rp;
+ } else if ((ri->prog == prog_list->program)
+ && (ri->vers == prog_list->version)) {
+ rp = prog_list;
+ } else if ( (ri->prog < prog_list->program)
+ || ((ri->prog == prog_list->program) && (ri->vers < prog_list->version))) {
+ /* we should be first entry in list */
+ rp = g_new(rpc_program_t, 1);
+ rp->next = prog_list;
+ rp->program = ri->prog;
+ rp->version = ri->vers;
+ rp->num = 0;
+ rp->min.secs = 0;
+ rp->min.nsecs = 0;
+ rp->max.secs = 0;
+ rp->max.nsecs = 0;
+ rp->tot.secs = 0;
+ rp->tot.nsecs = 0;
+ prog_list = rp;
+ } else {
+ /* we go somewhere else in the list */
+ for (rp=prog_list; rp; rp=rp->next) {
+ if ((rp->next)
+ && (rp->next->program == ri->prog)
+ && (rp->next->version == ri->vers)) {
+ rp = rp->next;
+ break;
+ }
+ if ((!rp->next)
+ || (rp->next->program > ri->prog)
+ || ( (rp->next->program == ri->prog)
+ && (rp->next->version > ri->vers))) {
+ rpc_program_t *trp;
+ trp = g_new(rpc_program_t, 1);
+ trp->next = rp->next;
+ trp->program = ri->prog;
+ trp->version = ri->vers;
+ trp->num = 0;
+ trp->min.secs = 0;
+ trp->min.nsecs = 0;
+ trp->max.secs = 0;
+ trp->max.nsecs = 0;
+ trp->tot.secs = 0;
+ trp->tot.nsecs = 0;
+ rp->next = trp;
+ rp = trp;
+ break;
+ }
+ }
+ }
+
+
+ /* we are only interested in reply packets */
+ if (ri->request || !rp) {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+
+ /* calculate time delta between request and reply */
+ nstime_delta(&delta, &pinfo->abs_ts, &ri->req_time);
+
+ if ((rp->max.secs == 0)
+ && (rp->max.nsecs == 0) ) {
+ rp->max.secs = delta.secs;
+ rp->max.nsecs = delta.nsecs;
+ }
+
+ if ((rp->min.secs == 0)
+ && (rp->min.nsecs == 0) ) {
+ rp->min.secs = delta.secs;
+ rp->min.nsecs = delta.nsecs;
+ }
+
+ if ( (delta.secs < rp->min.secs)
+ || ( (delta.secs == rp->min.secs)
+ && (delta.nsecs < rp->min.nsecs) ) ) {
+ rp->min.secs = delta.secs;
+ rp->min.nsecs = delta.nsecs;
+ }
+
+ if ( (delta.secs > rp->max.secs)
+ || ( (delta.secs == rp->max.secs)
+ && (delta.nsecs > rp->max.nsecs) ) ) {
+ rp->max.secs = delta.secs;
+ rp->max.nsecs = delta.nsecs;
+ }
+
+ rp->tot.secs += delta.secs;
+ rp->tot.nsecs += delta.nsecs;
+ if (rp->tot.nsecs > NANOSECS_PER_SEC) {
+ rp->tot.nsecs -= NANOSECS_PER_SEC;
+ rp->tot.secs++;
+ }
+ rp->num++;
+
+ return TAP_PACKET_REDRAW;
+}
+
+
+static void
+rpcprogs_draw(void *dummy _U_)
+{
+ guint64 td;
+ rpc_program_t *rp;
+ char str[64];
+
+ printf("\n");
+ printf("==========================================================\n");
+ printf("ONC-RPC Program Statistics:\n");
+ printf("Program Version Calls Min SRT Max SRT Avg SRT\n");
+ for (rp = prog_list;rp;rp = rp->next) {
+ /* Only display procs with non-zero calls */
+ if (rp->num == 0) {
+ continue;
+ }
+ /* Scale the average SRT in units of 1us and round to the nearest us. */
+ td = ((guint64)(rp->tot.secs)) * NANOSECS_PER_SEC + rp->tot.nsecs;
+ td = ((td / rp->num) + 500) / 1000;
+
+ snprintf(str, sizeof(str), "%s(%d)", rpc_prog_name(rp->program), rp->program);
+ printf("%-15s %2u %6d %3d.%06d %3d.%06d %3" PRIu64 ".%06" PRIu64 "\n",
+ str,
+ rp->version,
+ rp->num,
+ (int)(rp->min.secs), (rp->min.nsecs+500)/1000,
+ (int)(rp->max.secs), (rp->max.nsecs+500)/1000,
+ td/MICROSECS_PER_SEC, td%MICROSECS_PER_SEC
+ );
+ }
+ printf("===================================================================\n");
+}
+
+
+static void
+rpcprogs_init(const char *opt_arg _U_, void *userdata _U_)
+{
+ GString *error_string;
+
+ if (already_enabled) {
+ return;
+ }
+ already_enabled = 1;
+
+ error_string = register_tap_listener("rpc", NULL, NULL, 0, NULL, rpcprogs_packet, rpcprogs_draw, NULL);
+ if (error_string) {
+ cmdarg_err("Couldn't register rpc,programs tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static stat_tap_ui rpcprogs_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "rpc,programs",
+ rpcprogs_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_rpcprogs(void)
+{
+ register_stat_tap_ui(&rpcprogs_ui, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-rtd.c b/ui/cli/tap-rtd.c
new file mode 100644
index 00000000..ad56c4c5
--- /dev/null
+++ b/ui/cli/tap-rtd.c
@@ -0,0 +1,163 @@
+/* tap-rtd.c
+ *
+ * Based on tap-srt.c
+ *
+ * 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/rtd_table.h>
+#include <epan/timestamp.h>
+#include <epan/stat_tap_ui.h>
+#include <wsutil/cmdarg_err.h>
+#include <ui/cli/tshark-tap.h>
+
+typedef struct _rtd_t {
+ const char *type;
+ const char *filter;
+ const value_string* vs_type;
+ rtd_data_t rtd;
+} rtd_t;
+
+static void
+rtd_draw(void *arg)
+{
+ rtd_data_t* rtd_data = (rtd_data_t*)arg;
+ rtd_t* rtd = (rtd_t*)rtd_data->user_data;
+ gchar* tmp_str;
+ guint i, j;
+
+ /* printing results */
+ printf("\n");
+ printf("=====================================================================================================\n");
+ printf("%s Response Time Delay (RTD) Statistics:\n", rtd->type);
+ printf("Filter for statistics: %s\n", rtd->filter ? rtd->filter : "");
+ if (rtd_data->stat_table.num_rtds == 1)
+ {
+ printf("Duplicate requests: %u\n", rtd_data->stat_table.time_stats[0].req_dup_num);
+ printf("Duplicate responses: %u\n", rtd_data->stat_table.time_stats[0].rsp_dup_num);
+ printf("Open requests: %u\n", rtd_data->stat_table.time_stats[0].open_req_num);
+ printf("Discarded responses: %u\n", rtd_data->stat_table.time_stats[0].disc_rsp_num);
+ printf("Type | Messages | Min RTD | Max RTD | Avg RTD | Min in Frame | Max in Frame |\n");
+ for (i=0; i<rtd_data->stat_table.time_stats[0].num_timestat; i++) {
+ if (rtd_data->stat_table.time_stats[0].rtd[i].num) {
+ tmp_str = val_to_str_wmem(NULL, i, rtd->vs_type, "Other (%d)");
+ printf("%s | %7u | %8.2f msec | %8.2f msec | %8.2f msec | %10u | %10u |\n",
+ tmp_str, rtd_data->stat_table.time_stats[0].rtd[i].num,
+ nstime_to_msec(&(rtd_data->stat_table.time_stats[0].rtd[i].min)), nstime_to_msec(&(rtd_data->stat_table.time_stats[0].rtd[i].max)),
+ get_average(&(rtd_data->stat_table.time_stats[0].rtd[i].tot), rtd_data->stat_table.time_stats[0].rtd[i].num),
+ rtd_data->stat_table.time_stats[0].rtd[i].min_num, rtd_data->stat_table.time_stats[0].rtd[i].max_num
+ );
+ wmem_free(NULL, tmp_str);
+ }
+ }
+ }
+ else
+ {
+ printf("Type | Messages | Min RTD | Max RTD | Avg RTD | Min in Frame | Max in Frame | Open Requests | Discarded responses | Duplicate requests | Duplicate responses\n");
+ for (i=0; i<rtd_data->stat_table.num_rtds; i++) {
+ for (j=0; j<rtd_data->stat_table.time_stats[i].num_timestat; j++) {
+ if (rtd_data->stat_table.time_stats[i].rtd[j].num) {
+ tmp_str = val_to_str_wmem(NULL, i, rtd->vs_type, "Other (%d)");
+ printf("%s | %7u | %8.2f msec | %8.2f msec | %8.2f msec | %10u | %10u | %10u | %10u | %4u (%4.2f%%) | %4u (%4.2f%%) |\n",
+ tmp_str, rtd_data->stat_table.time_stats[i].rtd[j].num,
+ nstime_to_msec(&(rtd_data->stat_table.time_stats[i].rtd[j].min)), nstime_to_msec(&(rtd_data->stat_table.time_stats[i].rtd[j].max)),
+ get_average(&(rtd_data->stat_table.time_stats[i].rtd[j].tot), rtd_data->stat_table.time_stats[i].rtd[j].num),
+ rtd_data->stat_table.time_stats[i].rtd[j].min_num, rtd_data->stat_table.time_stats[i].rtd[j].max_num,
+ rtd_data->stat_table.time_stats[i].open_req_num, rtd_data->stat_table.time_stats[i].disc_rsp_num,
+ rtd_data->stat_table.time_stats[i].req_dup_num,
+ rtd_data->stat_table.time_stats[i].rtd[j].num?((double)rtd_data->stat_table.time_stats[i].req_dup_num*100)/(double)rtd_data->stat_table.time_stats[i].rtd[j].num:0,
+ rtd_data->stat_table.time_stats[i].rsp_dup_num,
+ rtd_data->stat_table.time_stats[i].rtd[j].num?((double)rtd_data->stat_table.time_stats[i].rsp_dup_num*100)/(double)rtd_data->stat_table.time_stats[i].rtd[j].num:0
+ );
+ wmem_free(NULL, tmp_str);
+ }
+ }
+ }
+ }
+ printf("=====================================================================================================\n");
+}
+
+static void
+init_rtd_tables(register_rtd_t* rtd, const char *filter)
+{
+ GString *error_string;
+ rtd_t* ui;
+
+ ui = g_new0(rtd_t, 1);
+ ui->type = proto_get_protocol_short_name(find_protocol_by_id(get_rtd_proto_id(rtd)));
+ ui->filter = g_strdup(filter);
+ ui->vs_type = get_rtd_value_string(rtd);
+ ui->rtd.user_data = ui;
+
+ rtd_table_dissector_init(rtd, &ui->rtd.stat_table, NULL, NULL);
+
+ error_string = register_tap_listener(get_rtd_tap_listener_name(rtd), &ui->rtd, filter, 0, NULL, get_rtd_packet_func(rtd), rtd_draw, NULL);
+ if (error_string) {
+ free_rtd_table(&ui->rtd.stat_table);
+ cmdarg_err("Couldn't register srt tap: %s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static void
+dissector_rtd_init(const char *opt_arg, void* userdata)
+{
+ register_rtd_t *rtd = (register_rtd_t*)userdata;
+ const char *filter=NULL;
+ char* err = NULL;
+
+ rtd_table_get_filter(rtd, opt_arg, &filter, &err);
+ if (err != NULL)
+ {
+ cmdarg_err("%s", err);
+ g_free(err);
+ exit(1);
+ }
+
+ init_rtd_tables(rtd, filter);
+}
+
+/* Set GUI fields for register_rtd list */
+bool
+register_rtd_tables(const void *key _U_, void *value, void *userdata _U_)
+{
+ register_rtd_t *rtd = (register_rtd_t*)value;
+ stat_tap_ui ui_info;
+ gchar *cli_string;
+
+ cli_string = rtd_table_get_tap_string(rtd);
+ ui_info.group = REGISTER_STAT_GROUP_RESPONSE_TIME;
+ ui_info.title = NULL; /* construct this from the protocol info? */
+ ui_info.cli_string = cli_string;
+ ui_info.tap_init_cb = dissector_rtd_init;
+ ui_info.nparams = 0;
+ ui_info.params = NULL;
+ register_stat_tap_ui(&ui_info, rtd);
+ g_free(cli_string);
+ return FALSE;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-rtp.c b/ui/cli/tap-rtp.c
new file mode 100644
index 00000000..b7937cd0
--- /dev/null
+++ b/ui/cli/tap-rtp.c
@@ -0,0 +1,128 @@
+/* tap-rtp.c
+ * RTP TAP for tshark
+ *
+ * Copyright 2008, Ericsson AB
+ * By Balint Reczey <balint.reczey@ericsson.com>
+ *
+ * based on ui/gtk/rtp_stream_dlg.c
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * This TAP provides statistics for RTP streams
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+#include <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/rtp_pt.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/addr_resolv.h>
+
+#include "ui/rtp_stream.h"
+#include "ui/tap-rtp-common.h"
+
+void register_tap_listener_rtpstreams(void);
+static void rtpstreams_stat_draw_cb(rtpstream_tapinfo_t *tapinfo);
+
+/* The one and only global rtpstream_tapinfo_t structure for tshark and wireshark.
+ */
+static rtpstream_tapinfo_t the_tapinfo_struct =
+ { NULL, rtpstreams_stat_draw_cb, NULL,
+ NULL, 0, NULL, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, FALSE, FALSE
+ };
+
+static void
+rtpstreams_stat_draw_cb(rtpstream_tapinfo_t *tapinfo _U_)
+{
+ GList *list;
+ rtpstream_info_t *strinfo;
+ rtpstream_info_calc_t calc;
+ char *savelocale;
+
+ printf("========================= RTP Streams ========================\n");
+ printf("%13s %13s %15s %5s %15s %5s %10s %16s %5s %12s %15s %15s %15s %15s %15s %15s %s\n",
+ "Start time", "End time", "Src IP addr", "Port", "Dest IP addr", "Port", "SSRC", "Payload", "Pkts", "Lost",
+ "Min Delta(ms)", "Mean Delta(ms)", "Max Delta(ms)", "Min Jitter(ms)", "Mean Jitter(ms)", "Max Jitter(ms)", "Problems?");
+
+ /* save the current locale */
+ savelocale = g_strdup(setlocale(LC_NUMERIC, NULL));
+ /* switch to "C" locale to avoid problems with localized decimal separators
+ in snprintf("%f") functions */
+ setlocale(LC_NUMERIC, "C");
+
+ list = the_tapinfo_struct.strinfo_list;
+
+ list = g_list_first(list);
+ while (list)
+ {
+ strinfo = (rtpstream_info_t*)(list->data);
+ rtpstream_info_calculate(strinfo, &calc);
+
+ printf("%13.6f %13.6f %15s %5u %15s %5u 0x%08X %16s %5u %5d (%.1f%%) %15.3f %15.3f %15.3f %15.3f %15.3f %15.3f %s\n",
+ nstime_to_sec(&(strinfo->start_rel_time)),
+ nstime_to_sec(&(strinfo->stop_rel_time)),
+ calc.src_addr_str,
+ calc.src_port,
+ calc.dst_addr_str,
+ calc.dst_port,
+ calc.ssrc,
+ calc.all_payload_type_names,
+ calc.packet_count,
+ calc.lost_num,
+ calc.lost_perc,
+ calc.min_delta,
+ calc.mean_delta,
+ calc.max_delta,
+ calc.min_jitter,
+ calc.mean_jitter,
+ calc.max_jitter,
+ (calc.problem)?"X":"");
+
+ rtpstream_info_calc_free(&calc);
+
+ list = g_list_next(list);
+ }
+
+ printf("==============================================================\n");
+ /* restore previous locale setting */
+ setlocale(LC_NUMERIC, savelocale);
+ g_free(savelocale);
+}
+
+
+static void
+rtpstreams_stat_init(const char *opt_arg _U_, void *userdata _U_)
+{
+ register_tap_listener_rtpstream(&the_tapinfo_struct, NULL, NULL);
+}
+
+static stat_tap_ui rtpstreams_stat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "rtp,streams",
+ rtpstreams_stat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_rtpstreams(void)
+{
+ register_stat_tap_ui(&rtpstreams_stat_ui, NULL);
+}
diff --git a/ui/cli/tap-rtspstat.c b/ui/cli/tap-rtspstat.c
new file mode 100644
index 00000000..a42be606
--- /dev/null
+++ b/ui/cli/tap-rtspstat.c
@@ -0,0 +1,289 @@
+/* tap-rtspstat.c
+ * tap-rtspstat March 2011
+ *
+ * Stephane GORSE (Orange Labs / France Telecom)
+ * Copied from Jean-Michel FAYARD's works (HTTP)
+ *
+ * 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 <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/dissectors/packet-rtsp.h>
+
+#include <wsutil/wslog.h>
+
+#include <wsutil/cmdarg_err.h>
+
+void register_tap_listener_rtspstat(void);
+
+/* used to keep track of the statictics for an entire program interface */
+typedef struct _rtsp_stats_t {
+ char *filter;
+ GHashTable *hash_responses;
+ GHashTable *hash_requests;
+} rtspstat_t;
+
+/* used to keep track of the stats for a specific response code
+ * for example it can be { 3, 404, "Not Found" ,...}
+ * which means we captured 3 reply rtsp/1.1 404 Not Found */
+typedef struct _rtsp_response_code_t {
+ guint32 packets; /* 3 */
+ guint response_code; /* 404 */
+ const gchar *name; /* Not Found */
+ rtspstat_t *sp;
+} rtsp_response_code_t;
+
+/* used to keep track of the stats for a specific request string */
+typedef struct _rtsp_request_methode_t {
+ gchar *response; /* eg. : SETUP */
+ guint32 packets;
+ rtspstat_t *sp;
+} rtsp_request_methode_t;
+
+
+/* insert some entries */
+static void
+rtsp_init_hash( rtspstat_t *sp)
+{
+ int i;
+
+ sp->hash_responses = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ for (i=0 ; rtsp_status_code_vals[i].strptr ; i++ )
+ {
+ rtsp_response_code_t *sc = g_new (rtsp_response_code_t, 1);
+ sc->packets = 0;
+ sc->response_code = rtsp_status_code_vals[i].value;
+ sc->name = rtsp_status_code_vals[i].strptr;
+ sc->sp = sp;
+ g_hash_table_insert( sc->sp->hash_responses, GINT_TO_POINTER(rtsp_status_code_vals[i].value), sc);
+ }
+ sp->hash_requests = g_hash_table_new( g_str_hash, g_str_equal);
+}
+static void
+rtsp_draw_hash_requests( gchar *key _U_ , rtsp_request_methode_t *data, gchar * format)
+{
+ if (data->packets == 0)
+ return;
+ printf( format, data->response, data->packets);
+}
+
+static void
+rtsp_draw_hash_responses( gpointer* key _U_ , rtsp_response_code_t *data, char * format)
+{
+ if (data == NULL) {
+ ws_warning("No data available, key=%d\n", GPOINTER_TO_INT(key));
+ exit(EXIT_FAILURE);
+ }
+ if (data->packets == 0)
+ return;
+ /* " RTSP %3d %-35s %9d packets", */
+ printf(format, data->response_code, data->name, data->packets );
+}
+
+
+
+/* NOT USED at this moment */
+/*
+static void
+rtsp_free_hash( gpointer key, gpointer value, gpointer user_data _U_ )
+{
+ g_free(key);
+ g_free(value);
+}
+*/
+static void
+rtsp_reset_hash_responses(gchar *key _U_ , rtsp_response_code_t *data, gpointer ptr _U_ )
+{
+ data->packets = 0;
+}
+static void
+rtsp_reset_hash_requests(gchar *key _U_ , rtsp_request_methode_t *data, gpointer ptr _U_ )
+{
+ data->packets = 0;
+}
+
+static void
+rtspstat_reset(void *psp )
+{
+ rtspstat_t *sp = (rtspstat_t *)psp;
+
+ g_hash_table_foreach( sp->hash_responses, (GHFunc)rtsp_reset_hash_responses, NULL);
+ g_hash_table_foreach( sp->hash_requests, (GHFunc)rtsp_reset_hash_requests, NULL);
+
+}
+
+static tap_packet_status
+rtspstat_packet(void *psp , packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri, tap_flags_t flags _U_)
+{
+ const rtsp_info_value_t *value = (const rtsp_info_value_t *)pri;
+ rtspstat_t *sp = (rtspstat_t *) psp;
+
+ /* We are only interested in reply packets with a status code */
+ /* Request or reply packets ? */
+ if (value->response_code != 0) {
+ rtsp_response_code_t *sc;
+
+ sc = (rtsp_response_code_t *)g_hash_table_lookup(
+ sp->hash_responses,
+ GINT_TO_POINTER(value->response_code));
+ if (sc == NULL) {
+ gint key;
+ /* non standard status code ; we classify it as others
+ * in the relevant category (Informational,Success,Redirection,Client Error,Server Error)
+ */
+ int i = value->response_code;
+ if ((i < 100) || (i >= 600)) {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+ else if (i < 200) {
+ key = 199; /* Hopefully, this status code will never be used */
+ }
+ else if (i < 300) {
+ key = 299;
+ }
+ else if (i < 400) {
+ key = 399;
+ }
+ else if (i < 500) {
+ key = 499;
+ }
+ else {
+ key = 599;
+ }
+ sc = (rtsp_response_code_t *)g_hash_table_lookup(
+ sp->hash_responses,
+ GINT_TO_POINTER(key));
+ if (sc == NULL)
+ return TAP_PACKET_DONT_REDRAW;
+ }
+ sc->packets++;
+ }
+ else if (value->request_method) {
+ rtsp_request_methode_t *sc;
+
+ sc = (rtsp_request_methode_t *)g_hash_table_lookup(
+ sp->hash_requests,
+ value->request_method);
+ if (sc == NULL) {
+ sc = g_new(rtsp_request_methode_t, 1);
+ sc->response = g_strdup( value->request_method );
+ sc->packets = 1;
+ sc->sp = sp;
+ g_hash_table_insert( sp->hash_requests, sc->response, sc);
+ } else {
+ sc->packets++;
+ }
+ } else {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+ return TAP_PACKET_REDRAW;
+}
+
+
+static void
+rtspstat_draw(void *psp )
+{
+ rtspstat_t *sp = (rtspstat_t *)psp;
+ printf("\n");
+ printf("===================================================================\n");
+ if (!sp->filter || !sp->filter[0])
+ printf("RTSP Statistics\n");
+ else
+ printf("RTSP Statistics with filter %s\n", sp->filter);
+
+ printf("* RTSP Response Status Codes Packets\n");
+ g_hash_table_foreach( sp->hash_responses, (GHFunc)rtsp_draw_hash_responses,
+ (gpointer)" %3d %-35s %9d\n");
+ printf("* RTSP Request Methods Packets\n");
+ g_hash_table_foreach( sp->hash_requests, (GHFunc)rtsp_draw_hash_requests,
+ (gpointer)" %-39s %9d\n");
+ printf("===================================================================\n");
+}
+
+
+
+/* When called, this function will create a new instance of rtspstat.
+ */
+static void
+rtspstat_init(const char *opt_arg, void *userdata _U_)
+{
+ rtspstat_t *sp;
+ const char *filter = NULL;
+ GString *error_string;
+
+ if (!strncmp (opt_arg, "rtsp,stat,", 10)) {
+ filter = opt_arg+10;
+ } else {
+ filter = NULL;
+ }
+
+ sp = g_new(rtspstat_t, 1);
+ sp->filter = g_strdup(filter);
+ /*g_hash_table_foreach( rtsp_status, (GHFunc)rtsp_reset_hash_responses, NULL);*/
+
+
+ error_string = register_tap_listener(
+ "rtsp",
+ sp,
+ filter,
+ 0,
+ rtspstat_reset,
+ rtspstat_packet,
+ rtspstat_draw,
+ NULL);
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ g_free(sp->filter);
+ g_free(sp);
+ cmdarg_err("Couldn't register rtsp,stat tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ rtsp_init_hash(sp);
+}
+
+static stat_tap_ui rtspstat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "rtsp,stat",
+ rtspstat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_rtspstat(void)
+{
+ register_stat_tap_ui(&rtspstat_ui, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-sctpchunkstat.c b/ui/cli/tap-sctpchunkstat.c
new file mode 100644
index 00000000..21e081eb
--- /dev/null
+++ b/ui/cli/tap-sctpchunkstat.c
@@ -0,0 +1,243 @@
+/* tap_sctpchunkstat.c
+ * SCTP chunk counter for wireshark
+ * Copyright 2005 Oleg Terletsky <oleg.terletsky@comverse.com>
+ *
+ * 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 <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/value_string.h>
+#include <epan/dissectors/packet-sctp.h>
+#include <epan/to_str.h>
+
+#include <wsutil/cmdarg_err.h>
+
+void register_tap_listener_sctpstat(void);
+
+typedef struct sctp_ep {
+ struct sctp_ep *next;
+ address src;
+ address dst;
+ guint16 sport;
+ guint16 dport;
+ guint32 chunk_count[256];
+} sctp_ep_t;
+
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _sctpstat_t {
+ char *filter;
+ guint32 number_of_packets;
+ sctp_ep_t *ep_list;
+} sctpstat_t;
+
+#define CHUNK_TYPE_OFFSET 0
+#define CHUNK_TYPE(x)(tvb_get_guint8((x), CHUNK_TYPE_OFFSET))
+
+static void
+sctpstat_reset(void *phs)
+{
+ sctpstat_t *sctp_stat = (sctpstat_t *)phs;
+ sctp_ep_t *list = (sctp_ep_t *)sctp_stat->ep_list;
+ sctp_ep_t *tmp = NULL;
+ guint16 chunk_type;
+
+ if (!list)
+ return;
+
+ for (tmp = list; tmp; tmp = tmp->next)
+ for (chunk_type = 0; chunk_type < 256; chunk_type++)
+ tmp->chunk_count[chunk_type] = 0;
+
+ sctp_stat->number_of_packets = 0;
+}
+
+
+static sctp_ep_t *
+alloc_sctp_ep(const struct _sctp_info *si)
+{
+ sctp_ep_t *ep;
+ guint16 chunk_type;
+
+ if (!si)
+ return NULL;
+
+ if (!(ep = g_new(sctp_ep_t, 1)))
+ return NULL;
+
+ copy_address(&ep->src, &si->ip_src);
+ copy_address(&ep->dst, &si->ip_dst);
+ ep->sport = si->sport;
+ ep->dport = si->dport;
+ ep->next = NULL;
+ for (chunk_type = 0; chunk_type < 256; chunk_type++)
+ ep->chunk_count[chunk_type] = 0;
+ return ep;
+}
+
+
+
+
+static tap_packet_status
+sctpstat_packet(void *phs, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *phi, tap_flags_t flags _U_)
+{
+
+ sctpstat_t *hs = (sctpstat_t *)phs;
+ sctp_ep_t *tmp = NULL, *te = NULL;
+ const struct _sctp_info *si = (const struct _sctp_info *)phi;
+ guint32 tvb_number;
+ guint8 chunk_type;
+
+ if (!hs)
+ return (TAP_PACKET_DONT_REDRAW);
+
+ hs->number_of_packets++;
+
+ if (!hs->ep_list) {
+ hs->ep_list = alloc_sctp_ep(si);
+ te = hs->ep_list;
+ } else {
+ for (tmp = hs->ep_list; tmp; tmp = tmp->next)
+ {
+ if ((!cmp_address(&tmp->src, &si->ip_src)) &&
+ (!cmp_address(&tmp->dst, &si->ip_dst)) &&
+ (tmp->sport == si->sport) &&
+ (tmp->dport == si->dport))
+ {
+ te = tmp;
+ break;
+ }
+ }
+ if (!te) {
+ if ((te = alloc_sctp_ep(si))) {
+ te->next = hs->ep_list;
+ hs->ep_list = te;
+ }
+ }
+ }
+
+ if (!te)
+ return (TAP_PACKET_DONT_REDRAW);
+
+
+ if (si->number_of_tvbs > 0) {
+ chunk_type = CHUNK_TYPE(si->tvb[0]);
+ if ((chunk_type == SCTP_INIT_CHUNK_ID) ||
+ (chunk_type == SCTP_INIT_ACK_CHUNK_ID)) {
+ te->chunk_count[chunk_type]++;
+ } else {
+ for (tvb_number = 0; tvb_number < si->number_of_tvbs; tvb_number++)
+ te->chunk_count[CHUNK_TYPE(si->tvb[tvb_number])]++;
+ }
+ }
+ return (TAP_PACKET_REDRAW);
+}
+
+
+static void
+sctpstat_draw(void *phs)
+{
+ sctpstat_t *hs = (sctpstat_t *)phs;
+ sctp_ep_t *list = hs->ep_list, *tmp;
+ char *src_addr, *dst_addr;
+
+ printf("-------------------------------------------- SCTP Statistics --------------------------------------------------------------------------\n");
+ printf("| Total packets RX/TX %u\n", hs->number_of_packets);
+ printf("---------------------------------------------------------------------------------------------------------------------------------------\n");
+ printf("| Source IP |PortA| Dest. IP |PortB| DATA | SACK | HBEAT |HBEATACK| INIT | INITACK| COOKIE |COOKIACK| ABORT | ERROR |\n");
+ printf("---------------------------------------------------------------------------------------------------------------------------------------\n");
+
+ for (tmp = list; tmp; tmp = tmp->next) {
+ src_addr = (char*)address_to_str(NULL, &tmp->src);
+ dst_addr = (char*)address_to_str(NULL, &tmp->dst);
+ printf("|%15s|%5u|%15s|%5u|%8u|%8u|%8u|%8u|%8u|%8u|%8u|%8u|%8u|%8u|\n",
+ src_addr, tmp->sport,
+ dst_addr, tmp->dport,
+ tmp->chunk_count[SCTP_DATA_CHUNK_ID],
+ tmp->chunk_count[SCTP_SACK_CHUNK_ID],
+ tmp->chunk_count[SCTP_HEARTBEAT_CHUNK_ID],
+ tmp->chunk_count[SCTP_HEARTBEAT_ACK_CHUNK_ID],
+ tmp->chunk_count[SCTP_INIT_CHUNK_ID],
+ tmp->chunk_count[SCTP_INIT_ACK_CHUNK_ID],
+ tmp->chunk_count[SCTP_COOKIE_ECHO_CHUNK_ID],
+ tmp->chunk_count[SCTP_COOKIE_ACK_CHUNK_ID],
+ tmp->chunk_count[SCTP_ABORT_CHUNK_ID],
+ tmp->chunk_count[SCTP_ERROR_CHUNK_ID]);
+ wmem_free(NULL, src_addr);
+ wmem_free(NULL, dst_addr);
+ }
+ printf("---------------------------------------------------------------------------------------------------------------------------------------\n");
+}
+
+
+static void
+sctpstat_init(const char *opt_arg, void *userdata _U_)
+{
+ sctpstat_t *hs;
+ GString *error_string;
+
+ hs = g_new(sctpstat_t, 1);
+ if (!strncmp(opt_arg, "sctp,stat,", 11)) {
+ hs->filter = g_strdup(opt_arg+11);
+ } else {
+ hs->filter = NULL;
+ }
+ hs->ep_list = NULL;
+ hs->number_of_packets = 0;
+
+ sctpstat_reset(hs);
+
+ error_string = register_tap_listener("sctp", hs, hs->filter, 0, NULL, sctpstat_packet, sctpstat_draw, NULL);
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ g_free(hs->filter);
+ g_free(hs);
+
+ cmdarg_err("Couldn't register sctp,stat tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static stat_tap_ui sctpstat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "sctp,stat",
+ sctpstat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_sctpstat(void)
+{
+ register_stat_tap_ui(&sctpstat_ui, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-simple_stattable.c b/ui/cli/tap-simple_stattable.c
new file mode 100644
index 00000000..cca34320
--- /dev/null
+++ b/ui/cli/tap-simple_stattable.c
@@ -0,0 +1,169 @@
+/* tap-simpletable.c
+ *
+ * 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/timestamp.h>
+#include <epan/stat_tap_ui.h>
+#include <wsutil/cmdarg_err.h>
+#include <ui/cli/tshark-tap.h>
+
+typedef struct _table_stat_t {
+ const char *filter;
+ stat_data_t stats;
+} table_stat_t;
+
+static void
+simple_draw(void *arg)
+{
+ stat_data_t* stat_data = (stat_data_t*)arg;
+ table_stat_t* stats = (table_stat_t*)stat_data->user_data;
+ size_t i;
+ guint table_index, element, field_index;
+ stat_tap_table_item* field;
+ stat_tap_table* table;
+ stat_tap_table_item_type* field_data;
+ gchar fmt_string[250];
+
+ /* printing results */
+ printf("\n");
+ printf("=====================================================================================================\n");
+ printf("%s:\n", stat_data->stat_tap_data->title);
+ printf("Filter for statistics: %s\n", stats->filter ? stats->filter : "");
+
+ for (i = 0, field = stat_data->stat_tap_data->fields; i < stat_data->stat_tap_data->nfields; i++, field++)
+ {
+ printf("%s |", field->column_name);
+ }
+ printf("\n");
+
+ /* To iterate is human, and to recurse is divine. I am human */
+ for (table_index = 0; table_index < stat_data->stat_tap_data->tables->len; table_index++)
+ {
+ table = g_array_index(stat_data->stat_tap_data->tables, stat_tap_table*, table_index);
+ printf("%s\n", table->title);
+ for (element = 0; element < table->num_elements; element++)
+ {
+ for (field_index = 0, field = stat_data->stat_tap_data->fields; field_index < table->num_fields; field_index++, field++)
+ {
+ field_data = stat_tap_get_field_data(table, element, field_index);
+ if (field_data->type == TABLE_ITEM_NONE) /* Nothing for us here */
+ break;
+
+ snprintf(fmt_string, sizeof(fmt_string), "%s |", field->field_format);
+ switch(field->type)
+ {
+ case TABLE_ITEM_UINT:
+ printf(fmt_string, field_data->value.uint_value);
+ break;
+ case TABLE_ITEM_INT:
+ printf(fmt_string, field_data->value.int_value);
+ break;
+ case TABLE_ITEM_STRING:
+ printf(fmt_string, field_data->value.string_value);
+ break;
+ case TABLE_ITEM_FLOAT:
+ printf(fmt_string, field_data->value.float_value);
+ break;
+ case TABLE_ITEM_ENUM:
+ printf(fmt_string, field_data->value.enum_value);
+ break;
+ case TABLE_ITEM_NONE:
+ break;
+ }
+ }
+ printf("\n");
+ }
+ }
+ printf("=====================================================================================================\n");
+}
+
+static void simple_finish(void *tapdata)
+{
+ stat_data_t *stat_data = (stat_data_t *)tapdata;
+
+ g_free(stat_data->user_data);
+}
+
+static void
+init_stat_table(stat_tap_table_ui *stat_tap, const char *filter)
+{
+ GString *error_string;
+ table_stat_t* ui;
+
+ ui = g_new0(table_stat_t, 1);
+ ui->filter = g_strdup(filter);
+ ui->stats.stat_tap_data = stat_tap;
+ ui->stats.user_data = ui;
+
+ stat_tap->stat_tap_init_cb(stat_tap);
+
+ error_string = register_tap_listener(stat_tap->tap_name, &ui->stats,
+ filter, 0, NULL, stat_tap->packet_func, simple_draw,
+ simple_finish);
+ if (error_string) {
+/* free_rtd_table(&ui->rtd.stat_table); */
+ cmdarg_err("Couldn't register tap: %s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static void
+simple_stat_init(const char *opt_arg, void* userdata)
+{
+ stat_tap_table_ui *stat_tap = (stat_tap_table_ui*)userdata;
+ const char *filter=NULL;
+ char* err = NULL;
+
+ stat_tap_get_filter(stat_tap, opt_arg, &filter, &err);
+ if (err != NULL)
+ {
+ cmdarg_err("%s", err);
+ g_free(err);
+ exit(1);
+ }
+
+ init_stat_table(stat_tap, filter);
+}
+
+bool
+register_simple_stat_tables(const void *key, void *value, void *userdata _U_)
+{
+ stat_tap_table_ui *stat_tap = (stat_tap_table_ui*)value;
+ stat_tap_ui ui_info;
+
+ ui_info.group = stat_tap->group;
+ ui_info.title = stat_tap->title; /* construct this from the protocol info? */
+ ui_info.cli_string = (const char *)key;
+ ui_info.tap_init_cb = simple_stat_init;
+ ui_info.nparams = stat_tap->nparams;
+ ui_info.params = stat_tap->params;
+
+ register_stat_tap_ui(&ui_info, stat_tap);
+ return FALSE;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-sipstat.c b/ui/cli/tap-sipstat.c
new file mode 100644
index 00000000..92d1463d
--- /dev/null
+++ b/ui/cli/tap-sipstat.c
@@ -0,0 +1,370 @@
+/* tap_sipstat.c
+ * sip message counter for wireshark
+ *
+ * Copied from ui/gtk/sip_stat.c and tap-httpstat.c
+ *
+ * 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 <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/value_string.h>
+#include <epan/dissectors/packet-sip.h>
+
+#include <wsutil/wslog.h>
+
+#include <wsutil/cmdarg_err.h>
+
+void register_tap_listener_sipstat(void);
+
+/* used to keep track of the statictics for an entire program interface */
+typedef struct _sip_stats_t {
+ char *filter;
+ guint32 packets; /* number of sip packets, including continuations */
+ guint32 resent_packets;
+ guint32 average_setup_time;
+ guint32 max_setup_time;
+ guint32 min_setup_time;
+ guint32 no_of_completed_calls;
+ guint64 total_setup_time;
+ GHashTable *hash_responses;
+ GHashTable *hash_requests;
+} sipstat_t;
+
+/* used to keep track of the stats for a specific response code
+ * for example it can be { 3, 404, "Not Found" ,...}
+ * which means we captured 3 reply sip/1.1 404 Not Found */
+typedef struct _sip_response_code_t {
+ guint32 packets; /* 3 */
+ guint response_code; /* 404 */
+ const gchar *name; /* Not Found */
+ sipstat_t *sp;
+} sip_response_code_t;
+
+/* used to keep track of the stats for a specific request string */
+typedef struct _sip_request_method_t {
+ gchar *response; /* eg. : INVITE */
+ guint32 packets;
+ sipstat_t *sp;
+} sip_request_method_t;
+
+
+/* Create tables for responses and requests */
+static void
+sip_init_hash(sipstat_t *sp)
+{
+ int i;
+
+ /* Create responses table */
+ sp->hash_responses = g_hash_table_new(g_int_hash, g_int_equal);
+
+ /* Add all response codes */
+ for (i=0; sip_response_code_vals[i].strptr; i++)
+ {
+ gint *key = g_new (gint, 1);
+ sip_response_code_t *sc = g_new (sip_response_code_t, 1);
+ *key = sip_response_code_vals[i].value;
+ sc->packets = 0;
+ sc->response_code = *key;
+ sc->name = sip_response_code_vals[i].strptr;
+ sc->sp = sp;
+ g_hash_table_insert(sc->sp->hash_responses, key, sc);
+ }
+
+ /* Create empty requests table */
+ sp->hash_requests = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+static void
+sip_draw_hash_requests( gchar *key _U_, sip_request_method_t *data, gchar *format)
+{
+ if (data->packets == 0)
+ return;
+ printf( format, data->response, data->packets);
+}
+
+static void
+sip_draw_hash_responses( gint *key _U_ , sip_response_code_t *data, char *format)
+{
+ if (data == NULL) {
+ ws_warning("C'est quoi ce borderl key=%d\n", *key);
+ exit(EXIT_FAILURE);
+ }
+ if (data->packets == 0)
+ return;
+ printf(format, data->response_code, data->name, data->packets );
+}
+
+/* NOT USED at this moment */
+/*
+static void
+sip_free_hash( gpointer key, gpointer value, gpointer user_data _U_ )
+{
+ g_free(key);
+ g_free(value);
+}
+*/
+
+static void
+sip_reset_hash_responses(gchar *key _U_ , sip_response_code_t *data, gpointer ptr _U_ )
+{
+ data->packets = 0;
+}
+static void
+sip_reset_hash_requests(gchar *key _U_ , sip_request_method_t *data, gpointer ptr _U_ )
+{
+ data->packets = 0;
+}
+
+static void
+sipstat_reset(void *psp )
+{
+ sipstat_t *sp = (sipstat_t *)psp;
+ if (sp) {
+ sp->packets = 0;
+ sp->resent_packets = 0;
+ sp->average_setup_time = 0;
+ sp->max_setup_time = 0;
+ sp->min_setup_time = 0;
+ sp->no_of_completed_calls = 0;
+ sp->total_setup_time = 0;
+
+ g_hash_table_foreach( sp->hash_responses, (GHFunc)sip_reset_hash_responses, NULL);
+ g_hash_table_foreach( sp->hash_requests, (GHFunc)sip_reset_hash_requests, NULL);
+ }
+}
+
+
+/* Main entry point to SIP tap */
+static tap_packet_status
+sipstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri, tap_flags_t flags _U_)
+{
+ const sip_info_value_t *value = (const sip_info_value_t *)pri;
+ sipstat_t *sp = (sipstat_t *)psp;
+
+ /* Total number of packets, including continuation packets */
+ sp->packets++;
+
+ /* Calculate average setup time */
+ if (value->setup_time) {
+ sp->no_of_completed_calls++;
+ /* Check if it's the first value */
+ if ( sp->total_setup_time == 0 ) {
+ sp->average_setup_time = value->setup_time;
+ sp->total_setup_time = value->setup_time;
+ sp->max_setup_time = value->setup_time;
+ sp->min_setup_time = value->setup_time;
+ }else{
+ sp->total_setup_time = sp->total_setup_time + value->setup_time;
+ if (sp->max_setup_time < value->setup_time) {
+ sp->max_setup_time = value->setup_time;
+ }
+ if (sp->min_setup_time > value->setup_time) {
+ sp->min_setup_time = value->setup_time;
+ }
+ /* Calculate average */
+ sp->average_setup_time = (guint32)(sp->total_setup_time / sp->no_of_completed_calls);
+ }
+ }
+
+ /* Update resent count if flag set */
+ if (value->resend)
+ {
+ sp->resent_packets++;
+ }
+
+
+ /* Looking at both requests and responses */
+ if (value->response_code != 0)
+ {
+ /* Responses */
+ guint key;
+ sip_response_code_t *sc;
+
+ /* Look up response code in hash table */
+ key = value->response_code;
+ sc = (sip_response_code_t *)g_hash_table_lookup(sp->hash_responses, &key);
+ if (sc == NULL)
+ {
+ /* Non-standard status code ; we classify it as others
+ * in the relevant category
+ * (Informational,Success,Redirection,Client Error,Server Error,Global Failure)
+ */
+ int i = value->response_code;
+ if ((i < 100) || (i >= 700))
+ {
+ /* Forget about crazy values */
+ return TAP_PACKET_DONT_REDRAW;
+ }
+ else if (i < 200)
+ {
+ key = 199; /* Hopefully, this status code will never be used */
+ }
+ else if (i < 300)
+ {
+ key = 299;
+ }
+ else if (i < 400)
+ {
+ key = 399;
+ }
+ else if (i < 500)
+ {
+ key = 499;
+ }
+ else if (i < 600)
+ {
+ key = 599;
+ }
+ else
+ {
+ key = 699;
+ }
+
+ /* Now look up this fallback code to get its text description */
+ sc = (sip_response_code_t *)g_hash_table_lookup(sp->hash_responses, &key);
+ if (sc == NULL)
+ {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+ }
+ sc->packets++;
+ }
+ else if (value->request_method)
+ {
+ /* Requests */
+ sip_request_method_t *sc;
+
+ /* Look up the request method in the table */
+ sc = (sip_request_method_t *)g_hash_table_lookup(sp->hash_requests, value->request_method);
+ if (sc == NULL)
+ {
+ /* First of this type. Create structure and initialise */
+ sc = g_new(sip_request_method_t, 1);
+ sc->response = g_strdup(value->request_method);
+ sc->packets = 1;
+ sc->sp = sp;
+ /* Insert it into request table */
+ g_hash_table_insert(sp->hash_requests, sc->response, sc);
+ }
+ else
+ {
+ /* Already existed, just update count for that method */
+ sc->packets++;
+ }
+ /* g_free(value->request_method); */
+ }
+ else
+ {
+ /* No request method set. Just ignore */
+ return TAP_PACKET_DONT_REDRAW;
+ }
+
+ return TAP_PACKET_REDRAW;
+}
+
+static void
+sipstat_draw(void *psp )
+{
+ sipstat_t *sp = (sipstat_t *)psp;
+ printf("\n");
+ printf("===================================================================\n");
+ if (sp->filter == NULL)
+ printf("SIP Statistics\n");
+ else
+ printf("SIP Statistics with filter %s\n", sp->filter);
+
+ printf("\nNumber of SIP messages: %u", sp->packets);
+ printf("\nNumber of resent SIP messages: %u\n", sp->resent_packets);
+ printf( "\n* SIP Status Codes in reply packets\n");
+ g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_draw_hash_responses,
+ (gpointer)" SIP %3d %-15s : %5d Packets\n");
+ printf("\n* List of SIP Request methods\n");
+ g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_draw_hash_requests,
+ (gpointer)" %-15s : %5d Packets\n");
+ printf( "\n* Average setup time %u ms\n Min %u ms\n Max %u ms\n", sp->average_setup_time, sp->min_setup_time, sp->max_setup_time);
+ printf("===================================================================\n");
+}
+
+static void
+sipstat_init(const char *opt_arg, void *userdata _U_)
+{
+ sipstat_t *sp;
+ const char *filter = NULL;
+ GString *error_string;
+
+ if (strncmp (opt_arg, "sip,stat,", 9) == 0) {
+ filter = opt_arg+9;
+ } else {
+ filter = NULL;
+ }
+
+ sp = g_new0(sipstat_t, 1);
+ sp->filter = g_strdup(filter);
+ /*g_hash_table_foreach( sip_status, (GHFunc)sip_reset_hash_responses, NULL);*/
+
+
+ error_string = register_tap_listener(
+ "sip",
+ sp,
+ filter,
+ 0,
+ sipstat_reset,
+ sipstat_packet,
+ sipstat_draw,
+ NULL);
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ g_free(sp->filter);
+ g_free(sp);
+ cmdarg_err("Couldn't register sip,stat tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ sp->packets = 0;
+ sp->resent_packets = 0;
+ sip_init_hash(sp);
+}
+
+static stat_tap_ui sipstat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "sip,stat",
+ sipstat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_sipstat(void)
+{
+ register_stat_tap_ui(&sipstat_ui, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-smbsids.c b/ui/cli/tap-smbsids.c
new file mode 100644
index 00000000..f92037c9
--- /dev/null
+++ b/ui/cli/tap-smbsids.c
@@ -0,0 +1,106 @@
+/* tap-smbsids.c
+ * smbstat 2003 Ronnie Sahlberg
+ *
+ * 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 <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/dissectors/packet-smb-sidsnooping.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/value_string.h>
+#include <epan/dissectors/packet-smb.h>
+
+#include <wsutil/cmdarg_err.h>
+
+void register_tap_listener_smbsids(void);
+
+static tap_packet_status
+smbsids_packet(void *pss _U_, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *psi _U_, tap_flags_t flags _U_)
+{
+ return TAP_PACKET_REDRAW;
+}
+
+static void
+enum_sids(gpointer key, gpointer value, gpointer userdata _U_)
+{
+ const char *sid = (const char *)key;
+ const char *name = (const char *)value;
+
+ printf("%-60s %s\n", sid, name);
+}
+
+static void
+smbsids_draw(void *pss _U_)
+{
+ printf("\n");
+ printf("===================================================================\n");
+ printf("SMB SID List:\n");
+ g_hash_table_foreach(sid_name_table, enum_sids, NULL);
+ printf("===================================================================\n");
+}
+
+
+static void
+smbsids_init(const char *opt_arg _U_, void *userdata _U_)
+{
+ GString *error_string;
+
+ if (!sid_name_snooping) {
+ fprintf(stderr, "The -z smb,sids function needs SMB/SID-Snooping to be enabled.\n");
+ fprintf(stderr, "Either enable Edit/Preferences/Protocols/SMB/Snoop SID name mappings in wireshark\n");
+ fprintf(stderr, "or override the preference file by specifying\n");
+ fprintf(stderr, " -o \"smb.sid_name_snooping=TRUE\"\n");
+ fprintf(stderr, "on the tshark command line.\n");
+ exit(1);
+ }
+
+
+ error_string = register_tap_listener("smb", NULL, NULL, 0, NULL, smbsids_packet, smbsids_draw, NULL);
+ if (error_string) {
+ cmdarg_err("Couldn't register smb,sids tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static stat_tap_ui smbsids_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "smb,sids",
+ smbsids_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_smbsids(void)
+{
+ register_stat_tap_ui(&smbsids_ui, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-srt.c b/ui/cli/tap-srt.c
new file mode 100644
index 00000000..62cef3af
--- /dev/null
+++ b/ui/cli/tap-srt.c
@@ -0,0 +1,196 @@
+/* tap-srt.c
+ *
+ * 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/srt_table.h>
+#include <epan/timestamp.h>
+#include <epan/stat_tap_ui.h>
+#include <wsutil/cmdarg_err.h>
+#include <ui/cli/tshark-tap.h>
+
+#define NANOSECS_PER_SEC 1000000000
+
+typedef struct _srt_t {
+ const char *type;
+ const char *filter;
+ srt_data_t data;
+} srt_t;
+
+static void
+draw_srt_table_data(srt_stat_table *rst, gboolean draw_footer, const char *subfilter)
+{
+ int i;
+ guint64 td;
+ guint64 sum;
+
+ if (rst->num_procs > 0) {
+ if (rst->filter_string != NULL && subfilter != NULL) {
+ printf("Filter: %s and (%s)\n", rst->filter_string, subfilter);
+ } else if (subfilter != NULL) {
+ /* Print (subfilter) to disambiguate from just rst->filter_string. */
+ printf("Filter: (%s)\n", subfilter);
+ } else {
+ printf("Filter: %s\n", rst->filter_string ? rst->filter_string : "");
+ }
+ printf("Index %-22s Calls Min SRT Max SRT Avg SRT Sum SRT\n", (rst->proc_column_name != NULL) ? rst->proc_column_name : "Procedure");
+ }
+ for(i=0;i<rst->num_procs;i++){
+ /* ignore procedures with no calls (they don't have rows) */
+ if(rst->procedures[i].stats.num==0){
+ continue;
+ }
+ /* Scale the average SRT in units of 1us and round to the nearest us.
+ tot.secs is a time_t which may be 32 or 64 bits (or even floating)
+ depending uon the platform. After casting tot.secs to 64 bits, it
+ would take a capture with a duration of over 136 *years* to
+ overflow the secs portion of td. */
+ td = ((guint64)(rst->procedures[i].stats.tot.secs))*NANOSECS_PER_SEC + rst->procedures[i].stats.tot.nsecs;
+ sum = (td + 500) / 1000;
+ td = ((td / rst->procedures[i].stats.num) + 500) / 1000;
+
+ printf("%5d %-22s %6u %3d.%06d %3d.%06d %3d.%06d %3d.%06d\n",
+ i, rst->procedures[i].procedure,
+ rst->procedures[i].stats.num,
+ (int)rst->procedures[i].stats.min.secs, (rst->procedures[i].stats.min.nsecs+500)/1000,
+ (int)rst->procedures[i].stats.max.secs, (rst->procedures[i].stats.max.nsecs+500)/1000,
+ (int)(td/1000000), (int)(td%1000000),
+ (int)(sum/1000000), (int)(sum%1000000)
+ );
+ }
+
+ if (draw_footer)
+ printf("==================================================================\n");
+}
+
+static void
+srt_draw(void *arg)
+{
+ guint i = 0;
+ srt_data_t* data = (srt_data_t*)arg;
+ srt_t *ui = (srt_t *)data->user_data;
+ srt_stat_table *srt_table;
+ gboolean need_newline = FALSE;
+
+ printf("\n");
+ printf("===================================================================\n");
+ printf("%s SRT Statistics:\n", ui->type);
+
+ srt_table = g_array_index(data->srt_array, srt_stat_table*, i);
+ draw_srt_table_data(srt_table, data->srt_array->len == 1, ui->filter);
+ if (srt_table->num_procs > 0) {
+ need_newline = TRUE;
+ }
+
+ for (i = 1; i < data->srt_array->len; i++)
+ {
+ if (need_newline)
+ {
+ printf("\n");
+ need_newline = FALSE;
+ }
+ srt_table = g_array_index(data->srt_array, srt_stat_table*, i);
+ draw_srt_table_data(srt_table, i == data->srt_array->len-1, ui->filter);
+ if (srt_table->num_procs > 0) {
+ need_newline = TRUE;
+ }
+ }
+}
+
+static GArray* global_srt_array;
+
+static void
+init_srt_tables(register_srt_t* srt, const char *filter)
+{
+ srt_t *ui;
+ GString *error_string;
+
+ ui = g_new0(srt_t, 1);
+ ui->type = proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt)));
+ ui->filter = g_strdup(filter);
+ ui->data.srt_array = global_srt_array;
+ ui->data.user_data = ui;
+
+ error_string = register_tap_listener(get_srt_tap_listener_name(srt), &ui->data, filter, 0, NULL, get_srt_packet_func(srt), srt_draw, NULL);
+ if (error_string) {
+ free_srt_table(srt, global_srt_array);
+ g_free(ui);
+ cmdarg_err("Couldn't register srt tap: %s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static void
+dissector_srt_init(const char *opt_arg, void* userdata)
+{
+ register_srt_t *srt = (register_srt_t*)userdata;
+ const char *filter=NULL;
+ char* err;
+
+ srt_table_get_filter(srt, opt_arg, &filter, &err);
+ if (err != NULL)
+ {
+ gchar* cmd_str = srt_table_get_tap_string(srt);
+ cmdarg_err("invalid \"-z %s,%s\" argument", cmd_str, err);
+ g_free(cmd_str);
+ g_free(err);
+ exit(1);
+ }
+
+ /* Need to create the SRT array now */
+ global_srt_array = g_array_new(FALSE, TRUE, sizeof(srt_stat_table*));
+
+ srt_table_dissector_init(srt, global_srt_array);
+ init_srt_tables(srt, filter);
+}
+
+/* Set GUI fields for register_srt list */
+bool
+register_srt_tables(const void *key _U_, void *value, void *userdata _U_)
+{
+ register_srt_t *srt = (register_srt_t*)value;
+ const char* short_name = proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt)));
+ stat_tap_ui ui_info;
+ gchar *cli_string;
+
+ /* XXX - CAMEL dissector hasn't been converted over due seemingly different tap packet
+ handling functions. So let the existing TShark CAMEL tap keep its registration */
+ if (strcmp(short_name, "CAMEL") == 0)
+ return FALSE;
+
+ cli_string = srt_table_get_tap_string(srt);
+ ui_info.group = REGISTER_STAT_GROUP_RESPONSE_TIME;
+ ui_info.title = NULL; /* construct this from the protocol info? */
+ ui_info.cli_string = cli_string;
+ ui_info.tap_init_cb = dissector_srt_init;
+ ui_info.nparams = 0;
+ ui_info.params = NULL;
+ register_stat_tap_ui(&ui_info, srt);
+ g_free(cli_string);
+ return FALSE;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-stats_tree.c b/ui/cli/tap-stats_tree.c
new file mode 100644
index 00000000..64557e40
--- /dev/null
+++ b/ui/cli/tap-stats_tree.c
@@ -0,0 +1,149 @@
+/* tap-stats_tree.c
+ * tshark's tap implememntation of stats_tree
+ * 2005, Luis E. G. Ontanon
+ *
+ * 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 <string.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include <wsutil/report_message.h>
+
+#include <epan/stats_tree_priv.h>
+#include <epan/stat_tap_ui.h>
+
+void register_tap_listener_stats_tree_stat(void);
+
+/* actually unused */
+struct _st_node_pres {
+ void *dummy;
+};
+
+struct _tree_pres {
+ void **dummy;
+};
+
+struct _tree_cfg_pres {
+ gchar *init_string;
+};
+
+static void
+draw_stats_tree(void *psp)
+{
+ stats_tree *st = (stats_tree *)psp;
+ GString *s;
+
+ s= stats_tree_format_as_str(st, ST_FORMAT_PLAIN, stats_tree_get_default_sort_col(st),
+ stats_tree_is_default_sort_DESC(st));
+
+ printf("%s", s->str);
+ g_string_free(s, TRUE);
+}
+
+static void
+init_stats_tree(const char *opt_arg, void *userdata _U_)
+{
+ char *abbr = stats_tree_get_abbr(opt_arg);
+ GString *error_string;
+ stats_tree_cfg *cfg = NULL;
+ stats_tree *st = NULL;
+ const char* filter = NULL;
+ size_t len;
+
+ if (abbr) {
+ cfg = stats_tree_get_cfg_by_abbr(abbr);
+
+ if (cfg != NULL) {
+ len = strlen(cfg->pr->init_string);
+ if (strncmp(opt_arg, cfg->pr->init_string, len) == 0) {
+ if (opt_arg[len] == ',') {
+ filter = opt_arg + len + 1;
+ }
+ st = stats_tree_new(cfg, NULL, filter);
+ } else {
+ report_failure("Wrong stats_tree (%s) found when looking at ->init_string", abbr);
+ return;
+ }
+ } else {
+ report_failure("no such stats_tree (%s) found in stats_tree registry", abbr);
+ return;
+ }
+
+ g_free(abbr);
+
+ } else {
+ report_failure("could not obtain stats_tree from arg '%s'", opt_arg);
+ return;
+ }
+
+ error_string = register_tap_listener(st->cfg->tapname,
+ st,
+ st->filter,
+ st->cfg->flags,
+ stats_tree_reset,
+ stats_tree_packet,
+ draw_stats_tree,
+ NULL);
+
+ if (error_string) {
+ report_failure("stats_tree for: %s failed to attach to the tap: %s", cfg->name, error_string->str);
+ return;
+ }
+
+ if (cfg->init) cfg->init(st);
+
+}
+
+static void
+register_stats_tree_tap (gpointer k _U_, gpointer v, gpointer p _U_)
+{
+ stats_tree_cfg *cfg = (stats_tree_cfg *)v;
+ stat_tap_ui ui_info;
+
+ cfg->pr = wmem_new(wmem_epan_scope(), tree_cfg_pres);
+ cfg->pr->init_string = wmem_strdup_printf(wmem_epan_scope(), "%s,tree", cfg->abbr);
+
+ ui_info.group = REGISTER_STAT_GROUP_GENERIC;
+ ui_info.title = NULL;
+ ui_info.cli_string = cfg->pr->init_string;
+ ui_info.tap_init_cb = init_stats_tree;
+ ui_info.nparams = 0;
+ ui_info.params = NULL;
+ register_stat_tap_ui(&ui_info, NULL);
+
+}
+
+static void
+free_tree_presentation(stats_tree *st)
+{
+ g_free(st->pr);
+}
+
+
+void
+register_tap_listener_stats_tree_stat(void)
+{
+ stats_tree_presentation(register_stats_tree_tap, NULL,
+ free_tree_presentation, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-sv.c b/ui/cli/tap-sv.c
new file mode 100644
index 00000000..d724c0d4
--- /dev/null
+++ b/ui/cli/tap-sv.c
@@ -0,0 +1,95 @@
+/* tap-sv.c
+ * Copyright 2008 Michael Bernhard
+ *
+ * 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 <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/dissectors/packet-sv.h>
+
+#include <wsutil/cmdarg_err.h>
+
+void register_tap_listener_sv(void);
+
+static tap_packet_status
+sv_packet(void *prs _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri, tap_flags_t flags _U_)
+{
+ int i;
+ const sv_frame_data * sv_data = (const sv_frame_data *)pri;
+
+ printf("%f %u ", nstime_to_sec(&pinfo->rel_ts), sv_data->smpCnt);
+
+ for (i = 0; i < sv_data->num_phsMeas; i++) {
+ printf("%d ", sv_data->phsMeas[i].value);
+ }
+
+ printf("\n");
+
+ return TAP_PACKET_DONT_REDRAW;
+}
+
+static void
+svstat_init(const char *opt_arg _U_, void *userdata _U_)
+{
+ GString *error_string;
+
+ error_string = register_tap_listener(
+ "sv",
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ sv_packet,
+ NULL,
+ NULL);
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ cmdarg_err("Couldn't register sv,stat tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static stat_tap_ui svstat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "sv",
+ svstat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_sv(void)
+{
+ register_stat_tap_ui(&svstat_ui, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-voip.c b/ui/cli/tap-voip.c
new file mode 100644
index 00000000..72d180e2
--- /dev/null
+++ b/ui/cli/tap-voip.c
@@ -0,0 +1,75 @@
+/* tap-voip.c
+ * voip 2023 Niels Widger
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <glib.h>
+
+#include "epan/packet_info.h"
+#include "epan/value_string.h"
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/addr_resolv.h>
+#include "ui/voip_calls.h"
+#include "ui/rtp_stream.h"
+#include "epan/sequence_analysis.h"
+#include "tap-voip.h"
+
+/* HACKY HACKY
+ *
+ * The cf_retap_packets call doesn't seem to be necessary
+ * when doing VOIP stuff, so it's OK if it's a NOP, it shouldn't get called.
+ *
+ * ... I don't think.
+ */
+#include "file.h"
+cf_read_status_t
+cf_retap_packets(capture_file *cf)
+{
+ (void)cf;
+ return CF_READ_OK;
+}
+
+voip_calls_tapinfo_t tapinfo_;
+int voip_conv_sel[VOIP_CONV_NUM];
+
+void voip_stat_init_tapinfo(void)
+{
+ memset(&tapinfo_, 0, sizeof(tapinfo_));
+ tapinfo_.callsinfos = g_queue_new();
+
+ /* fs_option FLOW_ALL shows the same info as the "SIP Flows" Wireshark tool
+ * FLOW_ONLY_INVITES shows the same thing as "VoIP Flows" in Wireshark.
+ * not totally sure what this really means right now. I believe we want FLOW_ONLY_INVITES?
+ * this matches the Wireshark menu options and shows fewer streams.
+ */
+ tapinfo_.fs_option = FLOW_ONLY_INVITES;
+
+ // add graph analysis
+ tapinfo_.graph_analysis = sequence_analysis_info_new();
+ tapinfo_.graph_analysis->name = "voip";
+}
+
+/*
+ * Editor modelines - http://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:
+ *
+ */
diff --git a/ui/cli/tap-voip.h b/ui/cli/tap-voip.h
new file mode 100644
index 00000000..45c436f5
--- /dev/null
+++ b/ui/cli/tap-voip.h
@@ -0,0 +1,45 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __TAP_VOIP_H__
+#define __TAP_VOIP_H__
+
+#include "ui/voip_calls.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* store the chosen calls in a bit-array */
+#define VOIP_CONV_BITS (sizeof(int) * 8)
+#define VOIP_CONV_NUM ((1<<(sizeof(guint16) * 8))/VOIP_CONV_BITS)
+#define VOIP_CONV_MAX (VOIP_CONV_BITS * VOIP_CONV_NUM)
+
+extern voip_calls_tapinfo_t tapinfo_;
+extern int voip_conv_sel[VOIP_CONV_NUM];
+extern void voip_stat_init_tapinfo(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TAP_VOIP_H__ */
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tap-wspstat.c b/ui/cli/tap-wspstat.c
new file mode 100644
index 00000000..89daed31
--- /dev/null
+++ b/ui/cli/tap-wspstat.c
@@ -0,0 +1,293 @@
+/* tap-wspstat.c
+ * wspstat 2003 Jean-Michel FAYARD
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* This module provides WSP statistics to tshark.
+ * It is only used by tshark and not wireshark
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <epan/packet_info.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/value_string.h>
+#include <epan/dissectors/packet-wsp.h>
+
+#include <wsutil/cmdarg_err.h>
+
+void register_tap_listener_wspstat(void);
+
+/* used to keep track of the stats for a specific PDU type*/
+typedef struct _wsp_pdu_t {
+ const gchar *type;
+ guint32 packets;
+} wsp_pdu_t;
+/* used to keep track of SRT statistics */
+typedef struct _wsp_status_code_t {
+ const gchar *name;
+ guint32 packets;
+} wsp_status_code_t;
+/* used to keep track of the statictics for an entire program interface */
+typedef struct _wsp_stats_t {
+ char *filter;
+ wsp_pdu_t *pdu_stats;
+ guint32 num_pdus;
+ GHashTable *hash;
+} wspstat_t;
+
+static void
+wsp_reset_hash(gchar *key _U_ , wsp_status_code_t *data, gpointer ptr _U_)
+{
+ data->packets = 0;
+}
+static void
+wsp_print_statuscode(gpointer key, wsp_status_code_t *data, char *format)
+{
+ if (data && (data->packets != 0))
+ printf(format, GPOINTER_TO_INT(key), data->packets , data->name);
+}
+static void
+wsp_free_hash_table( gpointer key, gpointer value, gpointer user_data _U_ )
+{
+ g_free(key);
+ g_free(value);
+}
+static void
+wspstat_reset(void *psp)
+{
+ wspstat_t *sp = (wspstat_t *)psp;
+ guint32 i;
+
+ for (i=1; i<=sp->num_pdus; i++)
+ {
+ sp->pdu_stats[i].packets = 0;
+ }
+ g_hash_table_foreach( sp->hash, (GHFunc)wsp_reset_hash, NULL);
+}
+
+
+/* This callback is invoked whenever the tap system has seen a packet
+ * we might be interested in.
+ * The function is to be used to only update internal state information
+ * in the *tapdata structure, and if there were state changes which requires
+ * the window to be redrawn, return 1 and (*draw) will be called sometime
+ * later.
+ *
+ * We didn't apply a filter when we registered so we will be called for
+ * ALL packets and not just the ones we are collecting stats for.
+ *
+ */
+static gint
+pdut2index(gint pdut)
+{
+ if (pdut <= 0x09)
+ return pdut;
+ if (pdut >= 0x40) {
+ if (pdut <= 0x44) {
+ return pdut - 54;
+ } else if (pdut == 0x60 || pdut == 0x61) {
+ return pdut - 81;
+ }
+ }
+ return 0;
+}
+static gint
+index2pdut(gint pdut)
+{
+ if (pdut <= 0x09)
+ return pdut;
+ if (pdut <= 14)
+ return pdut + 54;
+ if (pdut <= 16)
+ return pdut + 81;
+ return 0;
+}
+static tap_packet_status
+wspstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri, tap_flags_t flags _U_)
+{
+ wspstat_t *sp = (wspstat_t *)psp;
+ const wsp_info_value_t *value = (const wsp_info_value_t *)pri;
+ gint idx = pdut2index(value->pdut);
+ tap_packet_status retour = TAP_PACKET_DONT_REDRAW;
+
+ if (value->status_code != 0) {
+ wsp_status_code_t *sc;
+ sc = (wsp_status_code_t *)g_hash_table_lookup(
+ sp->hash,
+ GINT_TO_POINTER(value->status_code));
+ if (!sc) {
+ sc = g_new(wsp_status_code_t, 1);
+ sc -> packets = 1;
+ sc -> name = NULL;
+ g_hash_table_insert(
+ sp->hash,
+ GINT_TO_POINTER(value->status_code),
+ sc);
+ } else {
+ sc->packets++;
+ }
+ retour = TAP_PACKET_REDRAW;
+ }
+
+
+
+ if (idx != 0) {
+ sp->pdu_stats[idx].packets++;
+ retour = TAP_PACKET_REDRAW;
+ }
+ return retour;
+}
+
+
+/* This callback is used when tshark wants us to draw/update our
+ * data to the output device. Since this is tshark only output is
+ * stdout.
+ * TShark will only call this callback once, which is when tshark has
+ * finished reading all packets and exists.
+ * If used with wireshark this may be called any time, perhaps once every 3
+ * seconds or so.
+ * This function may even be called in parallell with (*reset) or (*draw)
+ * so make sure there are no races. The data in the rpcstat_t can thus change
+ * beneath us. Beware.
+ */
+static void
+wspstat_draw(void *psp)
+{
+ wspstat_t *sp = (wspstat_t *)psp;
+ guint32 i;
+
+ printf("\n");
+ printf("===================================================================\n");
+ printf("WSP Statistics:\n");
+ printf("%-23s %9s || %-23s %9s\n", "PDU Type", "Packets", "PDU Type", "Packets");
+ for (i=1; i <= ((sp->num_pdus+1)/2); i++)
+ {
+ guint32 ii = i+sp->num_pdus/2;
+ printf("%-23s %9u", sp->pdu_stats[i ].type, sp->pdu_stats[i ].packets);
+ printf(" || ");
+ if (ii< (sp->num_pdus) )
+ printf("%-23s %9u\n", sp->pdu_stats[ii].type, sp->pdu_stats[ii].packets);
+ else
+ printf("\n");
+ }
+ printf("\nStatus code in reply packets\n");
+ printf( "Status Code Packets Description\n");
+ g_hash_table_foreach( sp->hash, (GHFunc) wsp_print_statuscode,
+ (gpointer)" 0x%02X %9d %s\n" ) ;
+ printf("===================================================================\n");
+}
+
+/* When called, this function will create a new instance of wspstat.
+ * program and version are whick onc-rpc program/version we want to
+ * collect statistics for.
+ * This function is called from tshark when it parses the -z wsp, arguments
+ * and it creates a new instance to store statistics in and registers this
+ * new instance for the wsp tap.
+ */
+static void
+wspstat_init(const char *opt_arg, void *userdata _U_)
+{
+ wspstat_t *sp;
+ const char *filter = NULL;
+ guint32 i;
+ GString *error_string;
+ wsp_status_code_t *sc;
+ const value_string *wsp_vals_status_p;
+
+ if (!strncmp (opt_arg, "wsp,stat,", 9)) {
+ filter = opt_arg+9;
+ } else {
+ filter = NULL;
+ }
+
+
+ sp = g_new(wspstat_t, 1);
+ sp->hash = g_hash_table_new(g_direct_hash, g_direct_equal);
+ wsp_vals_status_p = VALUE_STRING_EXT_VS_P(&wsp_vals_status_ext);
+ for (i=0; wsp_vals_status_p[i].strptr; i++ )
+ {
+ gint *key;
+ sc = g_new(wsp_status_code_t, 1);
+ key = g_new(gint, 1);
+ sc->packets = 0;
+ sc->name = wsp_vals_status_p[i].strptr;
+ *key = wsp_vals_status_p[i].value;
+ g_hash_table_insert(
+ sp->hash,
+ key,
+ sc);
+ }
+ sp->num_pdus = 16;
+ sp->pdu_stats = g_new(wsp_pdu_t, (sp->num_pdus+1));
+ sp->filter = g_strdup(filter);
+
+ for (i=0; i<sp->num_pdus; i++)
+ {
+ sp->pdu_stats[i].packets = 0;
+ sp->pdu_stats[i].type = try_val_to_str_ext( index2pdut( i ), &wsp_vals_pdu_type_ext) ;
+ }
+
+ error_string = register_tap_listener(
+ "wsp",
+ sp,
+ filter,
+ 0,
+ wspstat_reset,
+ wspstat_packet,
+ wspstat_draw,
+ NULL);
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ g_free(sp->pdu_stats);
+ g_free(sp->filter);
+ g_hash_table_foreach( sp->hash, (GHFunc) wsp_free_hash_table, NULL ) ;
+ g_hash_table_destroy( sp->hash );
+ g_free(sp);
+ cmdarg_err("Couldn't register wsp,stat tap: %s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static stat_tap_ui wspstat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "wsp,stat",
+ wspstat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_wspstat(void)
+{
+ register_stat_tap_ui(&wspstat_ui, NULL);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/cli/tshark-tap.h b/ui/cli/tshark-tap.h
new file mode 100644
index 00000000..49b284d1
--- /dev/null
+++ b/ui/cli/tshark-tap.h
@@ -0,0 +1,22 @@
+/** @file
+ *
+ * Registation tap hooks for TShark
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef __TSHARK_TAP_H__
+#define __TSHARK_TAP_H__
+
+#include <epan/conversation_table.h>
+
+extern void init_iousers(struct register_ct* ct, const char *filter);
+extern void init_endpoints(struct register_ct* ct, const char *filter);
+extern bool register_srt_tables(const void *key, void *value, void *userdata);
+extern bool register_rtd_tables(const void *key, void *value, void *userdata);
+extern bool register_simple_stat_tables(const void *key, void *value, void *userdata);
+
+#endif /* __TSHARK_TAP_H__ */