diff options
Diffstat (limited to 'ui/cli')
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__ */ |