diff options
author | Jörg Mayer <jmayer@loplof.de> | 2012-02-17 12:30:27 +0000 |
---|---|---|
committer | Jörg Mayer <jmayer@loplof.de> | 2012-02-17 12:30:27 +0000 |
commit | 35508464b20bcf32ad548bb9f8c5424eddf5887a (patch) | |
tree | 80f16acb51d81ce613bf7741f6db62b4f73534f3 /ui/cli | |
parent | cdc504ac3fb9121856263c6f2b6e9c6816256ea8 (diff) |
Start moving files to ui/ and ui/cli/
svn path=/trunk/; revision=41047
Diffstat (limited to 'ui/cli')
40 files changed, 12314 insertions, 0 deletions
diff --git a/ui/cli/dftest.c b/ui/cli/dftest.c new file mode 100644 index 0000000000..939f77be11 --- /dev/null +++ b/ui/cli/dftest.c @@ -0,0 +1,199 @@ +/* dftest.c + * Shows display filter byte-code, for debugging dfilter routines. + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <locale.h> +#include <string.h> +#include <errno.h> + +#include <glib.h> +#include <epan/epan.h> + +#include <epan/timestamp.h> +#include <epan/plugins.h> +#include <epan/filesystem.h> +#include <wsutil/privileges.h> +#include <epan/prefs.h> +#include "ui/util.h" +#include "epan/dfilter/dfilter.h" +#include "register.h" + +static void failure_message(const char *msg_format, va_list ap); +static void open_failure_message(const char *filename, int err, + gboolean for_writing); +static void read_failure_message(const char *filename, int err); +static void write_failure_message(const char *filename, int err); + +int +main(int argc, char **argv) +{ + char *init_progfile_dir_error; + char *text; + char *gpf_path, *pf_path; + int gpf_open_errno, gpf_read_errno; + int pf_open_errno, pf_read_errno; + dfilter_t *df; + + /* + * Get credential information for later use. + */ + init_process_policies(); + + /* + * Attempt to get the pathname of the executable file. + */ + init_progfile_dir_error = init_progfile_dir(argv[0], main); + if (init_progfile_dir_error != NULL) { + fprintf(stderr, "dftest: Can't get pathname of dftest program: %s.\n", + init_progfile_dir_error); + } + + timestamp_set_type(TS_RELATIVE); + timestamp_set_seconds_type(TS_SECONDS_DEFAULT); + + /* Register all dissectors; we must do this before checking for the + "-g" flag, as the "-g" flag dumps a list of fields registered + by the dissectors, and we must do it before we read the preferences, + in case any dissectors register preferences. */ + epan_init(register_all_protocols, + register_all_protocol_handoffs, NULL, NULL, + failure_message, open_failure_message, read_failure_message, + write_failure_message); + + /* now register the preferences for any non-dissector modules. + we must do that before we read the preferences as well. */ + prefs_register_modules(); + + /* set the c-language locale to the native environment. */ + setlocale(LC_ALL, ""); + + read_prefs(&gpf_open_errno, &gpf_read_errno, &gpf_path, + &pf_open_errno, &pf_read_errno, &pf_path); + if (gpf_path != NULL) { + if (gpf_open_errno != 0) { + fprintf(stderr, + "can't open global preferences file \"%s\": %s.\n", + pf_path, g_strerror(gpf_open_errno)); + } + if (gpf_read_errno != 0) { + fprintf(stderr, + "I/O error reading global preferences file \"%s\": %s.\n", + pf_path, g_strerror(gpf_read_errno)); + } + } + if (pf_path != NULL) { + if (pf_open_errno != 0) { + fprintf(stderr, + "can't open your preferences file \"%s\": %s.\n", + pf_path, g_strerror(pf_open_errno)); + } + if (pf_read_errno != 0) { + fprintf(stderr, + "I/O error reading your preferences file \"%s\": %s.\n", + pf_path, g_strerror(pf_read_errno)); + } + } + + /* notify all registered modules that have had any of their preferences + changed either from one of the preferences file or from the command + line that its preferences have changed. */ + prefs_apply_all(); + + /* Check for filter on command line */ + if (argc <= 1) { + fprintf(stderr, "Usage: dftest <filter>\n"); + exit(1); + } + + /* Get filter text */ + text = get_args_as_string(argc, argv, 1); + + printf("Filter: \"%s\"\n", text); + + /* Compile it */ + if (!dfilter_compile(text, &df)) { + fprintf(stderr, "dftest: %s\n", dfilter_error_msg); + epan_cleanup(); + exit(2); + } + printf("dfilter ptr = 0x%08x\n", GPOINTER_TO_INT(df)); + + printf("\n\n"); + + if (df == NULL) + printf("Filter is empty\n"); + else + dfilter_dump(df); + + dfilter_free(df); + epan_cleanup(); + exit(0); +} + +/* + * General errors are reported with an console message in "dftest". + */ +static void +failure_message(const char *msg_format, va_list ap) +{ + fprintf(stderr, "dftest: "); + vfprintf(stderr, msg_format, ap); + fprintf(stderr, "\n"); +} + +/* + * Open/create errors are reported with an console message in "dftest". + */ +static void +open_failure_message(const char *filename, int err, gboolean for_writing) +{ + fprintf(stderr, "dftest: "); + fprintf(stderr, file_open_error_message(err, for_writing), filename); + fprintf(stderr, "\n"); +} + +/* + * Read errors are reported with an console message in "dftest". + */ +static void +read_failure_message(const char *filename, int err) +{ + fprintf(stderr, "dftest: An error occurred while reading from the file \"%s\": %s.\n", + filename, g_strerror(err)); +} + +/* + * Write errors are reported with an console message in "dftest". + */ +static void +write_failure_message(const char *filename, int err) +{ + fprintf(stderr, "dftest: An error occurred while writing to the file \"%s\": %s.\n", + filename, g_strerror(err)); +} diff --git a/ui/cli/tap-afpstat.c b/ui/cli/tap-afpstat.c new file mode 100644 index 0000000000..e1960f474d --- /dev/null +++ b/ui/cli/tap-afpstat.c @@ -0,0 +1,164 @@ +/* tap-afpstat.c + * Based on + * smbstat 2003 Ronnie Sahlberg + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include <epan/packet_info.h> +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/value_string.h> +#include <epan/dissectors/packet-afp.h> +#include "timestats.h" + +/* used to keep track of the statistics for an entire program interface */ +typedef struct _afpstat_t { + char *filter; + timestat_t proc[256]; +} afpstat_t; + +static int +afpstat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, const void *prv) +{ + afpstat_t *ss=(afpstat_t *)pss; + const afp_request_val *request_val=prv; + nstime_t t, deltat; + timestat_t *sp=NULL; + + /* if we havnt seen the request, just ignore it */ + if(!request_val){ + return 0; + } + + sp=&(ss->proc[request_val->command]); + + /* calculate time delta between request and reply */ + t=pinfo->fd->abs_ts; + nstime_delta(&deltat, &t, &request_val->req_time); + + if(sp){ + time_stat_update(sp,&deltat, pinfo); + } + + return 1; +} + +static void +afpstat_draw(void *pss) +{ + afpstat_t *ss=(afpstat_t *)pss; + guint32 i; + guint64 td; + printf("\n"); + printf("===================================================================\n"); + printf("AFP SRT Statistics:\n"); + printf("Filter: %s\n",ss->filter?ss->filter:""); + printf("Commands Calls Min SRT Max SRT Avg SRT\n"); + for(i=0;i<256;i++){ + /* nothing seen, nothing to do */ + if(ss->proc[i].num==0){ + continue; + } + + /* scale it to units of 10us.*/ + td=ss->proc[i].tot.secs; + td=td*100000+(int)ss->proc[i].tot.nsecs/10000; + if(ss->proc[i].num){ + td/=ss->proc[i].num; + } else { + td=0; + } + + printf("%-25s %6d %3d.%05d %3d.%05d %3" G_GINT64_MODIFIER "u.%05" G_GINT64_MODIFIER "u\n", + val_to_str_ext(i, &CommandCode_vals_ext, "Unknown (%u)"), + ss->proc[i].num, + (int)ss->proc[i].min.secs,ss->proc[i].min.nsecs/10000, + (int)ss->proc[i].max.secs,ss->proc[i].max.nsecs/10000, + td/100000, td%100000 + ); + } + printf("===================================================================\n"); +} + + +static void +afpstat_init(const char *optarg, void* userdata _U_) +{ + afpstat_t *ss; + guint32 i; + const char *filter=NULL; + GString *error_string; + + if(!strncmp(optarg,"afp,srt,",8)){ + filter=optarg+8; + } else { + filter=NULL; + } + + ss=g_malloc(sizeof(afpstat_t)); + if(filter){ + ss->filter=g_strdup(filter); + } else { + ss->filter=NULL; + } + + for(i=0;i<256;i++){ + ss->proc[i].num=0; + ss->proc[i].min_num=0; + ss->proc[i].max_num=0; + ss->proc[i].min.secs=0; + ss->proc[i].min.nsecs=0; + ss->proc[i].max.secs=0; + ss->proc[i].max.nsecs=0; + ss->proc[i].tot.secs=0; + ss->proc[i].tot.nsecs=0; + } + + error_string=register_tap_listener("afp", ss, filter, 0, NULL, afpstat_packet, afpstat_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(ss->filter); + g_free(ss); + + fprintf(stderr, "tshark: Couldn't register afp,srt tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + +void +register_tap_listener_afpstat(void) +{ + register_stat_cmd_arg("afp,srt", afpstat_init,NULL); +} diff --git a/ui/cli/tap-ansi_astat.c b/ui/cli/tap-ansi_astat.c new file mode 100644 index 0000000000..ee368abb74 --- /dev/null +++ b/ui/cli/tap-ansi_astat.c @@ -0,0 +1,166 @@ +/* tap-ansi_astat.c + * + * Copyright 2003, Michael Lum <mlum [AT] telostech.com> + * In association with Telos Technology Inc. + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * This TAP provides statistics for the ANSI A Interface: + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include "epan/value_string.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/dissectors/packet-bssap.h> +#include <epan/dissectors/packet-ansi_a.h> + + +typedef struct _ansi_a_stat_t { + int bsmap_message_type[0xff]; + int dtap_message_type[0xff]; +} ansi_a_stat_t; + + +static int +ansi_a_stat_packet( + void *tapdata, + packet_info *pinfo _U_, + epan_dissect_t *edt _U_, + const void *data) +{ + ansi_a_stat_t *stat_p = tapdata; + const ansi_a_tap_rec_t *tap_p = data; + + + switch (tap_p->pdu_type) + { + case BSSAP_PDU_TYPE_BSMAP: + stat_p->bsmap_message_type[tap_p->message_type]++; + break; + + case BSSAP_PDU_TYPE_DTAP: + stat_p->dtap_message_type[tap_p->message_type]++; + break; + + default: + /* + * unknown PDU type !!! + */ + return(0); + } + + return(1); +} + + +static void +ansi_a_stat_draw( + void *tapdata) +{ + ansi_a_stat_t *stat_p = tapdata; + guint8 i; + + + printf("\n"); + printf("=========== ANSI A-i/f Statistics ============================\n"); + printf("BSMAP\n"); + printf("Message (ID)Type Number\n"); + + i = 0; + while (ansi_a_ios401_bsmap_strings[i].strptr) + { + if (stat_p->bsmap_message_type[ansi_a_ios401_bsmap_strings[i].value] > 0) + { + printf("0x%02x %-50s%d\n", + ansi_a_ios401_bsmap_strings[i].value, + ansi_a_ios401_bsmap_strings[i].strptr, + stat_p->bsmap_message_type[ansi_a_ios401_bsmap_strings[i].value]); + } + + i++; + } + + printf("\nDTAP\n"); + printf("Message (ID)Type Number\n"); + + i = 0; + while (ansi_a_ios401_dtap_strings[i].strptr) + { + if (stat_p->dtap_message_type[ansi_a_ios401_dtap_strings[i].value] > 0) + { + printf("0x%02x %-50s%d\n", + ansi_a_ios401_dtap_strings[i].value, + ansi_a_ios401_dtap_strings[i].strptr, + stat_p->dtap_message_type[ansi_a_ios401_dtap_strings[i].value]); + } + + i++; + } + + printf("==============================================================\n"); +} + + +static void +ansi_a_stat_init(const char *optarg _U_, void* userdata _U_) +{ + ansi_a_stat_t *stat_p; + GString *err_p; + + stat_p = g_malloc(sizeof(ansi_a_stat_t)); + + memset(stat_p, 0, sizeof(ansi_a_stat_t)); + + err_p = + register_tap_listener("ansi_a", stat_p, NULL, 0, + NULL, + ansi_a_stat_packet, + ansi_a_stat_draw); + + if (err_p != NULL) + { + g_free(stat_p); + g_string_free(err_p, TRUE); + + exit(1); + } +} + + +void +register_tap_listener_ansi_astat(void) +{ + register_stat_cmd_arg("ansi_a,", ansi_a_stat_init,NULL); +} diff --git a/ui/cli/tap-bootpstat.c b/ui/cli/tap-bootpstat.c new file mode 100644 index 0000000000..041364955d --- /dev/null +++ b/ui/cli/tap-bootpstat.c @@ -0,0 +1,186 @@ +/* tap-bootpstat.c + * boop_stat 2003 Jean-Michel FAYARD + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <string.h> + +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> + + +typedef const char* bootp_info_value_t; + +/* used to keep track of the statictics for an entire program interface */ +typedef struct _dhcp_stats_t { + char *filter; + GHashTable *hash; + guint index; /* Number of to display */ +} dhcpstat_t; +/* used to keep track of a single DHCP message type */ +typedef struct _dhcp_message_type_t { + const char *name; + guint32 packets; + dhcpstat_t *sp; /* entire program interface */ +} dhcp_message_type_t; + + +/* Not used anywhere at this moment */ +/* +static void +dhcp_free_hash( gpointer key _U_ , gpointer value, gpointer user_data _U_ ) +{ + g_free(value); +} +*/ + +static void +dhcp_reset_hash(gchar *key _U_ , dhcp_message_type_t *data, gpointer ptr _U_ ) +{ + data->packets = 0; +} + +/* Update the entry corresponding to the number of packets of a special DHCP Message Type + * or create it if it don't exist. + */ +static void +dhcp_draw_message_type(gchar *key _U_, dhcp_message_type_t *data, gchar * format ) +{ + if ((data==NULL) || (data->packets==0)) + return; + printf( format, data->name, data->packets); +} +static void +dhcpstat_reset(void *psp) +{ + dhcpstat_t *sp=psp; + g_hash_table_foreach( sp->hash, (GHFunc)dhcp_reset_hash, NULL); +} +static int +dhcpstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri) +{ + dhcpstat_t *sp=psp; + const bootp_info_value_t value=pri; + dhcp_message_type_t *sc; + + if (sp==NULL) + return 0; + sc = g_hash_table_lookup( + sp->hash, + value); + if (!sc) { + sc = g_malloc( sizeof(dhcp_message_type_t) ); + sc -> packets = 1; + sc -> name = value; + sc -> sp = sp; + g_hash_table_insert( + sp->hash, + (gpointer) value, + sc); + } else { + /*g_warning("sc(%s)->packets++", sc->name);*/ + sc->packets++; + } + return 1; +} + + +static void +dhcpstat_draw(void *psp) +{ + dhcpstat_t *sp=psp; + + printf("\n"); + printf("===================================================================\n"); + + if (sp->filter==NULL) + printf("BOOTP Statistics\n"); + else + printf("BOOTP Statistics with filter %s\n", sp->filter); + printf("BOOTP Option 53: DHCP Messages Types:\n"); + printf("DHCP Message Type Packets nb\n" ); + g_hash_table_foreach( sp->hash, (GHFunc) dhcp_draw_message_type, + "%23s %-9d\n" ); + printf("===================================================================\n"); + +} + + + + +/* When called, this function will create a new instance of tap-boopstat. + */ +static void +dhcpstat_init(const char *optarg, void* userdata _U_) +{ + dhcpstat_t *sp; + const char *filter=NULL; + GString *error_string; + + if (!strncmp (optarg, "bootp,stat,", 11)){ + filter=optarg+11; + } else { + filter=NULL; + } + + sp = g_malloc( sizeof(dhcpstat_t) ); + sp->hash = g_hash_table_new( g_str_hash, g_str_equal); + if(filter){ + sp->filter=g_strdup(filter); + } else { + sp->filter=NULL; + } + sp->index = 0; /* Nothing to display yet */ + + error_string = register_tap_listener( + "bootp", + sp, + filter, + 0, + dhcpstat_reset, + dhcpstat_packet, + dhcpstat_draw); + if (error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(sp->filter); + g_free(sp); + fprintf(stderr, "tshark: Couldn't register dhcp,stat tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + + +void +register_tap_listener_gtkdhcpstat(void) +{ + register_stat_cmd_arg("bootp,stat,", dhcpstat_init,NULL); +} + diff --git a/ui/cli/tap-camelcounter.c b/ui/cli/tap-camelcounter.c new file mode 100644 index 0000000000..c922f19860 --- /dev/null +++ b/ui/cli/tap-camelcounter.c @@ -0,0 +1,133 @@ +/* tap_camelcounter.c + * camel message counter for tshark + * Copyright 2006 Florent DROUIN + * + * $Id$ + * + * This part of code is extracted from tap-h225counter.c from Lars Roland + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet.h" +#include "epan/packet_info.h" +#include "epan/tap.h" +#include "epan/value_string.h" +#include "epan/stat_cmd_args.h" +#include "epan/asn1.h" +#include "epan/camel-persistentdata.h" + +void register_tap_listener_camelcounter(void); + +/* used to keep track of the statistics for an entire program interface */ +struct camelcounter_t { + char *filter; + guint32 camel_msg[camel_MAX_NUM_OPR_CODES]; +}; + + +static void camelcounter_reset(void *phs) +{ + struct camelcounter_t * p_counter= ( struct camelcounter_t *) phs; + memset(p_counter,0,sizeof(struct camelcounter_t)); +} + +static int camelcounter_packet(void *phs, + packet_info *pinfo _U_, + epan_dissect_t *edt _U_, + const void *phi) +{ + struct camelcounter_t * p_counter =(struct camelcounter_t *)phs; + const struct camelsrt_info_t * pi=phi; + if (pi->opcode != 255) + p_counter->camel_msg[pi->opcode]++; + + return 1; +} + + +static void camelcounter_draw(void *phs) +{ + struct camelcounter_t * p_counter= (struct camelcounter_t *)phs; + int i; + printf("\n"); + printf("CAMEL Message and Response Status Counter:\n"); + printf("------------------------------------------\n"); + + for(i=0;i<camel_MAX_NUM_OPR_CODES;i++) { + /* Message counter */ + if(p_counter->camel_msg[i]!=0) { + printf("%30s ", val_to_str(i,camel_opr_code_strings,"Unknown message ")); + printf("%6d\n", p_counter->camel_msg[i]); + } + } /* Message Type */ + printf("------------------------------------------\n"); +} + +static void camelcounter_init(const char *optarg, void* userdata _U_) +{ + struct camelcounter_t *p_camelcounter; + GString *error_string; + + p_camelcounter = g_malloc(sizeof(struct camelcounter_t)); + if(!strncmp(optarg,"camel,counter,",13)){ + p_camelcounter->filter=g_strdup(optarg+13); + } else { + p_camelcounter->filter=NULL; + } + + camelcounter_reset(p_camelcounter); + + error_string=register_tap_listener("CAMEL", + p_camelcounter, + p_camelcounter->filter, + 0, + NULL, + camelcounter_packet, + camelcounter_draw); + + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(p_camelcounter->filter); + g_free(p_camelcounter); + + fprintf(stderr, "tshark: Couldn't register camel,counter tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void /* Next line mandatory */ +register_tap_listener_camelcounter(void) +{ + register_stat_cmd_arg("camel,counter", camelcounter_init, NULL); +} diff --git a/ui/cli/tap-camelsrt.c b/ui/cli/tap-camelsrt.c new file mode 100644 index 0000000000..d4c16589ec --- /dev/null +++ b/ui/cli/tap-camelsrt.c @@ -0,0 +1,255 @@ +/* tap_camelsrt.c + * CAMEL Service Response Time statistics for tshark + * Copyright 2006 Florent Drouin (based on tap_h225rassrt.c from Lars Roland) + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet.h" +#include "epan/packet_info.h" +#include <epan/tap.h> +#include "epan/value_string.h" +#include "epan/asn1.h" +#include "epan/dissectors/packet-camel.h" +#include "epan/camel-persistentdata.h" +#include "timestats.h" +#include "epan/stat_cmd_args.h" + + +void register_tap_listener_camelsrt(void); + +/* Save the 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 int camelsrt_packet(void *phs, + packet_info *pinfo _U_, + epan_dissect_t *edt _U_, + const void *phi) +{ + struct camelsrt_t *hs=(struct camelsrt_t *)phs; + const struct camelsrt_info_t * pi=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 1; +} + + +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]; + + 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; + printf("|%24s |%8u |%8.2f s |%8.2f s |%8.2f s |%10u |%10u |\n", + val_to_str(j,camelSRTtype_naming,"Unknown Message 0x%02x"), + 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 + ); + for(j=2; j<NB_CAMELSRT_CATEGORY; j++) { + if(hs->stats[j].num==0){ + printf("|%24s |%8u |%8.2f ms|%8.2f ms|%8.2f ms|%10u |%10u |\n", + val_to_str(j,camelSRTtype_naming,"Unknown Message 0x%02x"), + 0, 0.0, 0.0, 0.0, 0, 0); + continue; + } + + printf("|%24s |%8u |%8.2f ms|%8.2f ms|%8.2f ms|%10u |%10u |\n", + val_to_str(j,camelSRTtype_naming,"Unknown Message 0x%02x"), + 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 + ); + } /* 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 */ + printf("X%24s |", val_to_str(j, camelSRTtype_naming, "Unknown") ); + for(z=0; z<NB_CRITERIA; z++) printf("%8.2f |", MIN(9999,delay_criteria[z])); + printf("\n"); + } else { /* count */ + printf("X%24s |", val_to_str(j, camelSRTtype_naming, "Unknown") ); + 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 *optarg, void* userdata _U_) +{ + struct camelsrt_t *p_camelsrt; + GString *error_string; + + p_camelsrt = g_malloc(sizeof(struct camelsrt_t)); + if(!strncmp(optarg,"camel,srt,",9)){ + p_camelsrt->filter=g_strdup(optarg+9); + } else { + p_camelsrt->filter=NULL; + } + camelsrt_reset(p_camelsrt); + + error_string=register_tap_listener("CAMEL", + p_camelsrt, + p_camelsrt->filter, + 0, + NULL, + camelsrt_packet, + camelsrt_draw); + + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(p_camelsrt->filter); + g_free(p_camelsrt); + + fprintf(stderr, "tshark: Couldn't register camel,srt tap: %s\n", + 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; +} + + +void /* Next line mandatory */ +register_tap_listener_camelsrt(void) +{ + register_stat_cmd_arg("camel,srt", camelsrt_init, NULL); +} diff --git a/ui/cli/tap-comparestat.c b/ui/cli/tap-comparestat.c new file mode 100644 index 0000000000..81b063b2d9 --- /dev/null +++ b/ui/cli/tap-comparestat.c @@ -0,0 +1,583 @@ +/* tap-comparestat.c + * Compare two capture files + * Copyright 2008 Vincenzo Condoleo, Christophe Dirac, Reto Ruoss + * supported by HSR (Hochschule Rapperswil) + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* This module provides statistics about two merged capture files, to find packet loss, + * time delay, ip header checksum errors and order check to tshark. + * It's also detecting the matching regions of the different files. + * + * The packets are compared by the ip id. MAC or TTL is used to distinct the different files. + * It is only used by tshark and not wireshark + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <math.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/in_cksum.h> +#include <epan/packet.h> +#include <epan/tap.h> +#include <epan/timestamp.h> +#include <epan/stat_cmd_args.h> +#include <epan/dissectors/packet-ip.h> +#include "timestats.h" + + +/* For checksum */ +#define BYTES 8 +#define WRONG_CHKSUM 0 + +#define MERGED_FILES 2 + +#define TTL_SEARCH 5 + +/* information which will be printed */ +typedef struct _for_print { + guint count; + guint16 cksum; + nstime_t predecessor_time; + struct _frame_info *partner; +} for_print; + +/* each tracked packet */ +typedef struct _frame_info { + for_print *fp; + guint32 num; + guint16 id; + guint8 ip_ttl; + address dl_dst; + nstime_t abs_ts, zebra_time, delta; +} frame_info; + +/* used to keep track of the statistics for an entire program interface */ +typedef struct _comparestat_t { + char *filter; + emem_tree_t *packet_tree, *ip_id_tree, *nr_tree; + address eth_dst, eth_src; + nstime_t zebra_time, current_time; + timestat_t stats; + GArray *ip_ttl_list; + gboolean last_hit; + guint32 start_ongoing_hits, stop_ongoing_hits, start_packet_nr_first, start_packet_nr_second, stop_packet_nr_first, stop_packet_nr_second; + guint32 first_file_amount, second_file_amount; +} comparestat_t; + + +/* to call directly _init */ +static gdouble compare_variance=0.0; +static guint8 compare_start, compare_stop; +static gboolean TTL_method=TRUE, ON_method=TRUE; + +/* This callback is never used by tshark but it is here for completeness. */ +static void +comparestat_reset(void *dummy _U_) +{ +} + + +/* This callback is invoked whenever the tap system has seen a packet + * we might be interested in. + * function returns : + * 0: no updates, no need to call (*draw) later + * !0: state has changed, call (*draw) sometime later + */ +static int +comparestat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2) +{ + comparestat_t *cs=arg; + const ws_ip *ci=arg2; + frame_info *fInfo; + vec_t cksum_vec[3]; + guint16 computed_cksum=0; + + /* so this get filled, usually with the first frame */ + if(cs->eth_dst.len==0) { + cs->eth_dst=pinfo->dl_dst; + cs->eth_src=pinfo->dl_src; + } + + /* Set up the fields of the pseudo-header and create checksum */ + cksum_vec[0].ptr=&ci->ip_v_hl; + cksum_vec[0].len=BYTES; + /* skip TTL */ + cksum_vec[1].ptr=&ci->ip_p; + cksum_vec[1].len=1; + /* skip header checksum and ip's (because of NAT)*/ + cksum_vec[2].ptr=ci->ip_dst.data; + cksum_vec[2].ptr=cksum_vec[2].ptr+ci->ip_dst.len; + /* dynamic computation */ + cksum_vec[2].len=pinfo->iphdrlen-20; + computed_cksum=in_cksum(&cksum_vec[0], 3); + + /* collect all packet infos */ + fInfo=(frame_info*)se_alloc(sizeof(frame_info)); + fInfo->fp=(for_print*)se_alloc(sizeof(for_print)); + fInfo->fp->partner=NULL; + fInfo->fp->count=1; + fInfo->fp->cksum=computed_cksum; + fInfo->num=pinfo->fd->num; + fInfo->id=ci->ip_id; + fInfo->ip_ttl=ci->ip_ttl; + fInfo->dl_dst=pinfo->dl_dst; + fInfo->abs_ts=pinfo->fd->abs_ts; + /* clean memory */ + nstime_set_zero(&fInfo->zebra_time); + nstime_set_zero(&fInfo->fp->predecessor_time); + se_tree_insert32(cs->packet_tree, pinfo->fd->num, fInfo); + + return 1; +} + +/* Find equal packets, same IP-Id, count them and make time statistics */ +static gboolean +call_foreach_count_ip_id(gpointer value, gpointer arg) +{ + comparestat_t *cs=(comparestat_t*)arg; + frame_info *fInfo=(frame_info*)value, *fInfoTemp; + nstime_t delta; + guint i; + + /* we only need one value out of pinfo we use a temp one */ + packet_info *pinfo=(packet_info*)ep_alloc(sizeof(packet_info)); + pinfo->fd=(frame_data*)ep_alloc(sizeof(frame_data)); + pinfo->fd->num = fInfo->num; + + fInfoTemp=se_tree_lookup32(cs->ip_id_tree, fInfo->id); + if(fInfoTemp==NULL){ + /* Detect ongoing package loss */ + if((cs->last_hit==FALSE)&&(cs->start_ongoing_hits>compare_start)&&(cs->stop_ongoing_hits<compare_stop)){ + cs->stop_ongoing_hits++; + cs->stop_packet_nr_first=fInfo->num; + } else if(cs->stop_ongoing_hits<compare_stop){ + cs->stop_ongoing_hits=0; + cs->stop_packet_nr_first=G_MAXINT32; + } + cs->last_hit=FALSE; + + fInfo->fp->count=1; + se_tree_insert32(cs->ip_id_tree, fInfo->id, fInfo); + } else { + /* Detect ongoing package hits, special behavior if start is set to 0 */ + if((cs->last_hit||(compare_start==0))&&(cs->start_ongoing_hits<compare_start||(compare_start==0))){ + if((compare_start==0)&&(cs->start_ongoing_hits!=0)){ + /* start from the first packet so allready set */ + } else { + cs->start_ongoing_hits++; + /* Take the lower number */ + cs->start_packet_nr_first=fInfoTemp->num; + cs->start_packet_nr_second=fInfo->num; + } + } else if(cs->start_ongoing_hits<compare_start){ + cs->start_ongoing_hits=0; + cs->start_packet_nr_first=G_MAXINT32; + } + cs->last_hit=TRUE; + + fInfo->fp->count=fInfoTemp->fp->count + 1; + if(fInfoTemp->fp->cksum!=fInfo->fp->cksum){ + fInfo->fp->cksum=WRONG_CHKSUM; + fInfoTemp->fp->cksum=WRONG_CHKSUM; + } + /* Add partner */ + fInfo->fp->partner=fInfoTemp; + /* Create time statistic */ + if(fInfo->fp->count==MERGED_FILES){ + nstime_delta(&delta, &fInfo->abs_ts, &fInfoTemp->abs_ts); + /* Set delta in both packets */ + nstime_set_zero(&fInfoTemp->delta); + nstime_add(&fInfoTemp->delta, &delta); + nstime_set_zero(&fInfo->delta); + nstime_add(&fInfo->delta, &delta); + time_stat_update(&cs->stats, &delta, pinfo); + } + se_tree_insert32(cs->ip_id_tree, fInfo->id, fInfo); + } + + /* collect TTL's */ + if(TTL_method && (fInfo->num<TTL_SEARCH)){ + for(i=0; i < cs->ip_ttl_list->len; i++){ + if(g_array_index(cs->ip_ttl_list, guint8, i) == fInfo->ip_ttl){ + return FALSE; + } + } + g_array_append_val(cs->ip_ttl_list, fInfo->ip_ttl); + } + + return FALSE; +} + +/*Create new numbering */ +static gboolean +call_foreach_new_order(gpointer value, gpointer arg) +{ + comparestat_t *cs=(comparestat_t*)arg; + frame_info *fInfo=(frame_info*)value, *fInfoTemp; + + /* overwrite Info column for new ordering */ + fInfoTemp=se_tree_lookup32(cs->nr_tree, fInfo->id); + if(fInfoTemp==NULL){ + if(TTL_method==FALSE){ + if((ADDRESSES_EQUAL(&cs->eth_dst, &fInfo->dl_dst)) || (ADDRESSES_EQUAL(&cs->eth_src, &fInfo->dl_dst))){ + se_tree_insert32(cs->nr_tree, fInfo->id, fInfo); + fInfo->zebra_time=cs->zebra_time; + cs->zebra_time.nsecs=cs->zebra_time.nsecs + MERGED_FILES; + } else { + cs->zebra_time.nsecs++; + se_tree_insert32(cs->nr_tree, fInfo->id, fInfo); + fInfo->zebra_time=cs->zebra_time; + cs->zebra_time.nsecs++; + } + } else { + if((g_array_index(cs->ip_ttl_list, guint8, 0)==fInfo->ip_ttl) || (g_array_index(cs->ip_ttl_list, guint8, 1)==fInfo->ip_ttl)){ + se_tree_insert32(cs->nr_tree, fInfo->id, fInfo); + fInfo->zebra_time=cs->zebra_time; + cs->zebra_time.nsecs=cs->zebra_time.nsecs + MERGED_FILES; + } else { + cs->zebra_time.nsecs++; + se_tree_insert32(cs->nr_tree, fInfo->id, fInfo); + fInfo->zebra_time=cs->zebra_time; + cs->zebra_time.nsecs++; + } + + } + } else { + if(TTL_method==FALSE){ + if(((ADDRESSES_EQUAL(&cs->eth_dst, &fInfo->dl_dst)) || (ADDRESSES_EQUAL(&cs->eth_src, &fInfo->dl_dst)))&&(!fmod(fInfoTemp->zebra_time.nsecs,MERGED_FILES))){ + fInfo->zebra_time.nsecs=fInfoTemp->zebra_time.nsecs; + } else { + fInfo->zebra_time.nsecs=fInfoTemp->zebra_time.nsecs+1; + } + } else { + if(((g_array_index(cs->ip_ttl_list, guint8, 0)==fInfo->ip_ttl) || (g_array_index(cs->ip_ttl_list, guint8, 1)==fInfo->ip_ttl))&&(!fmod(fInfoTemp->zebra_time.nsecs,MERGED_FILES))){ + fInfo->zebra_time.nsecs=fInfoTemp->zebra_time.nsecs; + } else { + fInfo->zebra_time.nsecs=fInfoTemp->zebra_time.nsecs+1; + } + } + } + + /* count packets of file */ + if(fmod(fInfo->zebra_time.nsecs, MERGED_FILES)){ + cs->first_file_amount++; + } else { + cs->second_file_amount++; + } + + /* ordering */ + if(!nstime_is_unset(&cs->current_time)){ + fInfo->fp->predecessor_time.nsecs=cs->current_time.nsecs; + } + + cs->current_time.nsecs=fInfo->zebra_time.nsecs; + + return FALSE; +} + +/* calculate scopes if not set yet */ +static gboolean +call_foreach_merge_settings(gpointer value, gpointer arg) +{ + comparestat_t *cs=(comparestat_t*)arg; + frame_info *fInfo=(frame_info*)value, *fInfoTemp=NULL; + guint32 tot_packet_amount=cs->first_file_amount+cs->second_file_amount, swap; + + if((fInfo->num==tot_packet_amount)&&(cs->stop_packet_nr_first!=G_MAXINT32)){ + /* calculate missing stop number */ + swap=cs->stop_packet_nr_first; + cs->stop_packet_nr_first=tot_packet_amount-cs->second_file_amount;; + cs->stop_packet_nr_second=swap; + } + + if((fInfo->num==tot_packet_amount)&&(cs->stop_packet_nr_first==G_MAXINT32)&&(cs->start_packet_nr_first!=G_MAXINT32)){ + fInfoTemp=se_tree_lookup32(cs->packet_tree, cs->start_packet_nr_first); + if(fInfoTemp==NULL){ + printf("ERROR: start number not set correctly\n"); + return FALSE; + } + if(fmod(fInfoTemp->zebra_time.nsecs, 2)){ + /*first file*/ + cs->stop_packet_nr_first=cs->start_packet_nr_first+abs(cs->second_file_amount-(cs->start_packet_nr_second-cs->first_file_amount)); + if(cs->stop_packet_nr_first>(tot_packet_amount-cs->second_file_amount)){ + cs->stop_packet_nr_first=tot_packet_amount-cs->second_file_amount; + } + /*this only happens if we have too many MAC's or TTL*/ + if(cs->stop_packet_nr_first>cs->start_packet_nr_second){ + cs->stop_packet_nr_first=cs->start_packet_nr_second-1; + } + fInfoTemp=se_tree_lookup32(cs->packet_tree, cs->stop_packet_nr_first); + while((fInfoTemp!=NULL)?fmod(!fInfoTemp->zebra_time.nsecs, 2):TRUE){ + cs->stop_packet_nr_first--; + fInfoTemp=se_tree_lookup32(cs->packet_tree, cs->stop_packet_nr_first); + } + } else { + /*this only happens if we have too many MAC's or TTL*/ + cs->stop_packet_nr_first=cs->first_file_amount+cs->start_packet_nr_first; + if(cs->stop_packet_nr_first>tot_packet_amount-cs->first_file_amount){ + cs->stop_packet_nr_first=tot_packet_amount-cs->first_file_amount; + } + fInfoTemp=se_tree_lookup32(cs->packet_tree, cs->stop_packet_nr_first); + while((fInfoTemp!=NULL)?fmod(fInfoTemp->zebra_time.nsecs, 2):TRUE){ + cs->stop_packet_nr_first--; + fInfoTemp=se_tree_lookup32(cs->packet_tree, cs->stop_packet_nr_first); + } + } + /* set second stop location */ + cs->stop_packet_nr_second=cs->start_packet_nr_second+abs(cs->stop_packet_nr_first-cs->start_packet_nr_first); + if(cs->stop_packet_nr_second>tot_packet_amount){ + cs->stop_packet_nr_second=tot_packet_amount; + } + } + + /* no start found */ + if(fInfo->num==tot_packet_amount&&compare_start!=0&&compare_stop!=0){ + if(cs->start_packet_nr_first==G_MAXINT32){ + printf("Start point couldn't be set, choose a lower compare start"); + } + } + + return FALSE; +} + +static gboolean +call_foreach_print_ip_tree(gpointer value, gpointer user_data) +{ + frame_info *fInfo=(frame_info*)value; + comparestat_t *cs=(comparestat_t*)user_data; + gdouble delta, average; + gboolean show_it=FALSE; + + delta=fabs(get_average(&fInfo->delta,1)); + average=fabs(get_average(&cs->stats.tot, cs->stats.num)); + + /* special case if both are set to zero ignore start and stop numbering */ + if(compare_start!=0&&compare_stop!=0){ + /* check out if packet is in searched scope */ + if((cs->start_packet_nr_first<fInfo->num)&&(cs->stop_packet_nr_first>fInfo->num)){ + show_it=TRUE; + } else { + /* so we won't miss the other file */ + if((fInfo->num>cs->start_packet_nr_second)&&(fInfo->num<cs->stop_packet_nr_second)){ + show_it=TRUE; + } + } + } else { + show_it=TRUE; + } + + if(show_it){ + if(fInfo->fp->count < MERGED_FILES){ + printf("Packet id :%i, count:%i Problem:", fInfo->id, fInfo->fp->count); + printf("Packet lost\n"); + } + if(fInfo->fp->count > MERGED_FILES){ + printf("Packet id :%i, count:%i Problem:", fInfo->id, fInfo->fp->count); + printf("More than two packets\n"); + if(fInfo->fp->cksum == WRONG_CHKSUM){ + printf("Checksum error over IP header\n"); + } + } + if(fInfo->fp->count == MERGED_FILES){ + if(fInfo->fp->cksum == WRONG_CHKSUM){ + printf("Packet id :%i, count:%i Problem:", fInfo->id, fInfo->fp->count); + printf("Checksum error over IP header\n"); + if(((delta < (average-cs->stats.variance)) || (delta > (average+cs->stats.variance))) && (delta > 0.0) && (cs->stats.variance!=0)){ + printf("Not arrived in time\n"); + } + if((nstime_cmp(&fInfo->fp->predecessor_time, &fInfo->zebra_time)>0||nstime_cmp(&fInfo->fp->partner->fp->predecessor_time, &fInfo->fp->partner->zebra_time)>0) && (fInfo->zebra_time.nsecs!=MERGED_FILES) && ON_method){ + printf("Not correct order\n"); + } + } else if(((delta < (average-cs->stats.variance)) || (delta > (average+cs->stats.variance))) && (delta > 0.0) && (cs->stats.variance!=0)) { + printf("Packet id :%i, count:%i Problem:", fInfo->id, fInfo->fp->count); + printf("Package not arrived in time\n"); + if((nstime_cmp(&fInfo->fp->predecessor_time, &fInfo->zebra_time)>0||nstime_cmp(&fInfo->fp->partner->fp->predecessor_time, &fInfo->fp->partner->zebra_time)>0) && fInfo->zebra_time.nsecs != MERGED_FILES && ON_method){ + printf("Not correct order\n"); + } + } else if((nstime_cmp(&fInfo->fp->predecessor_time, &fInfo->zebra_time)>0||nstime_cmp(&fInfo->fp->partner->fp->predecessor_time, &fInfo->fp->partner->zebra_time)>0) && fInfo->zebra_time.nsecs != MERGED_FILES && ON_method){ + printf("Packet id :%i, count:%i Problem:", fInfo->id, fInfo->fp->count); + printf("Not correct order\n"); + } + } + } + return FALSE; +} + + +/* 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 +comparestat_draw(void *prs) +{ + comparestat_t *cs=prs; + GString *filter_str = g_string_new(""); + const gchar *statis_string; + guint32 first_file_amount, second_file_amount; + + /* inital steps, clear all data before start*/ + cs->zebra_time.secs=0; + cs->zebra_time.nsecs=1; + nstime_set_unset(&cs->current_time); + cs->ip_ttl_list=g_array_new(FALSE, FALSE, sizeof(guint8)); + cs->last_hit=FALSE; + cs->start_ongoing_hits=0; + cs->stop_ongoing_hits=0; + cs->start_packet_nr_first=G_MAXINT32; + cs->start_packet_nr_second=G_MAXINT32; + cs->stop_packet_nr_first=G_MAXINT32; + cs->stop_packet_nr_second=G_MAXINT32; + cs->first_file_amount=0; + cs->second_file_amount=0; + + time_stat_init(&cs->stats); + /* not using g_free, because struct is managed by binarytrees */ + cs->ip_id_tree=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "ip_id_tree"); + emem_tree_foreach(cs->packet_tree, call_foreach_count_ip_id, cs); + + /* set up TTL choice if only one number found */ + if(TTL_method&&cs->ip_ttl_list->len==1){ + g_array_append_val(cs->ip_ttl_list, g_array_index(cs->ip_ttl_list, guint8, 1)); + } + + emem_tree_foreach(cs->packet_tree, call_foreach_new_order,cs); + emem_tree_foreach(cs->packet_tree, call_foreach_merge_settings, cs); + + /* remembering file amounts */ + first_file_amount=cs->first_file_amount; + second_file_amount=cs->second_file_amount; + /* reset after numbering */ + cs->nr_tree=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "nr_tree"); + + /* Variance */ + cs->stats.variance=compare_variance; + + /* add statistic string */ + statis_string=g_strdup_printf("Compare Statistics: \nFilter: %s\nNumber of packets total:%i 1st file:%i, 2nd file:%i\nScopes:\t start:%i stop:%i\nand:\t start:%i stop:%i\nEqual packets: %i \nAllowed variation: %f \nAverage time difference: %f\n", cs->filter ? cs->filter : "", (first_file_amount+second_file_amount), first_file_amount, second_file_amount, cs->start_packet_nr_first, cs->stop_packet_nr_first, cs->start_packet_nr_second, cs->stop_packet_nr_second, cs->stats.num, cs->stats.variance, fabs(get_average(&cs->stats.tot, cs->stats.num))); + + printf("\n"); + printf("===================================================================\n"); + printf("%s", statis_string); + emem_tree_foreach(cs->ip_id_tree, call_foreach_print_ip_tree, cs); + printf("===================================================================\n"); + g_string_free(filter_str, TRUE); + g_array_free(cs->ip_ttl_list, TRUE); +} + +/* When called, this function will create a new instance of comparestat. + * This function is called from tshark when it parses the -z compare, arguments + * and it creates a new instance to store statistics in and registers this + * new instance for the compare tap. + */ +static void +comparestat_init(const char *optarg, void* userdata _U_) +{ + comparestat_t *cs; + const char *filter=NULL; + GString *error_string; + gint start, stop,ttl, order, pos=0; + gdouble variance; + + if(sscanf(optarg,"compare,%d,%d,%d,%d,%lf%n",&start, &stop, &ttl, &order, &variance, &pos)==5){ + if(pos){ + if(*(optarg+pos)==',') + filter=optarg+pos+1; + else + filter=optarg+pos; + } else { + filter=NULL; + } + } else { + fprintf(stderr, "tshark: invalid \"-z compare,<start>,<stop>,<ttl[0|1]>,<order[0|1]>,<variance>[,<filter>]\" argument\n"); + exit(1); + } + + compare_variance=variance; + compare_start=start; + compare_stop=stop; + TTL_method=ttl; + ON_method=order; + + cs=g_malloc(sizeof(comparestat_t)); + nstime_set_unset(&cs->current_time); + cs->ip_ttl_list=g_array_new(FALSE, FALSE, sizeof(guint8)); + cs->last_hit=FALSE; + cs->start_ongoing_hits=0; + cs->stop_ongoing_hits=0; + cs->start_packet_nr_first=G_MAXINT32; + cs->start_packet_nr_second=G_MAXINT32; + cs->stop_packet_nr_first=G_MAXINT32; + cs->stop_packet_nr_second=G_MAXINT32; + cs->first_file_amount=0; + cs->second_file_amount=0; + + cs->zebra_time.secs=0; + cs->zebra_time.nsecs=1; + cs->nr_tree=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "nr_tree"); + /* microsecond precision */ + timestamp_set_precision(TS_PREC_AUTO_NSEC); + + if(filter){ + cs->filter=g_strdup(filter); + } else { + cs->filter=NULL; + } + + /* create a Hash to count the packets with the same ip.id */ + cs->packet_tree=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "Packet_info_tree"); + + error_string=register_tap_listener("ip", cs, filter, 0, comparestat_reset, comparestat_packet, comparestat_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(cs->filter); + g_free(cs); + + fprintf(stderr, "tshark: Couldn't register compare tap: %s\n", error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_comparestat(void) +{ + register_stat_cmd_arg("compare,", comparestat_init,NULL); +} diff --git a/ui/cli/tap-dcerpcstat.c b/ui/cli/tap-dcerpcstat.c new file mode 100644 index 0000000000..5fea8fc085 --- /dev/null +++ b/ui/cli/tap-dcerpcstat.c @@ -0,0 +1,300 @@ +/* tap-dcerpcstat.c + * dcerpcstat 2002 Ronnie Sahlberg + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/dissectors/packet-dcerpc.h> + +#define MICROSECS_PER_SEC 1000000 +#define NANOSECS_PER_SEC 1000000000 + +/* used to keep track of statistics for a specific procedure */ +typedef struct _rpc_procedure_t { + const char *proc; + int num; + nstime_t min; + nstime_t max; + nstime_t tot; +} rpc_procedure_t; + +/* used to keep track of the statistics for an entire program interface */ +typedef struct _rpcstat_t { + const char *prog; + char *filter; + e_uuid_t uuid; + guint16 ver; + guint32 num_procedures; + rpc_procedure_t *procedures; +} rpcstat_t; + + + +static int +dcerpcstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri) +{ + const dcerpc_info *ri=pri; + rpcstat_t *rs=prs; + nstime_t delta; + rpc_procedure_t *rp; + + if(!ri->call_data){ + return 0; + } + if(!ri->call_data->req_frame){ + /* we have not seen the request so we dont know the delta*/ + return 0; + } + if(ri->call_data->opnum>=rs->num_procedures){ + /* dont handle this since its outside of known table */ + return 0; + } + + /* we are only interested in reply packets */ + if(ri->ptype != PDU_RESP){ + return 0; + } + + /* we are only interested in certain program/versions */ + if( (ri->call_data->uuid.Data1!=rs->uuid.Data1) + ||(ri->call_data->uuid.Data2!=rs->uuid.Data2) + ||(ri->call_data->uuid.Data3!=rs->uuid.Data3) + ||(ri->call_data->uuid.Data4[0]!=rs->uuid.Data4[0]) + ||(ri->call_data->uuid.Data4[1]!=rs->uuid.Data4[1]) + ||(ri->call_data->uuid.Data4[2]!=rs->uuid.Data4[2]) + ||(ri->call_data->uuid.Data4[3]!=rs->uuid.Data4[3]) + ||(ri->call_data->uuid.Data4[4]!=rs->uuid.Data4[4]) + ||(ri->call_data->uuid.Data4[5]!=rs->uuid.Data4[5]) + ||(ri->call_data->uuid.Data4[6]!=rs->uuid.Data4[6]) + ||(ri->call_data->uuid.Data4[7]!=rs->uuid.Data4[7]) + ||(ri->call_data->ver!=rs->ver)){ + return 0; + } + + rp=&(rs->procedures[ri->call_data->opnum]); + + /* calculate time delta between request and reply */ + nstime_delta(&delta, &pinfo->fd->abs_ts, &ri->call_data->req_time); + + if(rp->num==0){ + rp->max.secs=delta.secs; + rp->max.nsecs=delta.nsecs; + } + + if(rp->num==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 1; +} + +static void +dcerpcstat_draw(void *prs) +{ + rpcstat_t *rs=prs; + guint32 i; + guint64 td; + printf("\n"); + printf("=======================================================================\n"); + printf("%s Major Version %u SRT Statistics:\n", rs->prog, rs->ver); + printf("Filter: %s\n",rs->filter?rs->filter:""); + printf("Procedure Calls Min SRT Max SRT Avg SRT\n"); + + for(i=0;i<rs->num_procedures;i++){ + /* Only display procs with non-zero calls */ + if(rs->procedures[i].num==0){ + continue; + } + /* Scale the average SRT in units of 1us and round to the nearest us. */ + td = ((guint64)(rs->procedures[i].tot.secs)) * NANOSECS_PER_SEC + rs->procedures[i].tot.nsecs; + td = ((td / rs->procedures[i].num) + 500) / 1000; + + printf("%-31s %6d %3d.%06d %3d.%06d %3" G_GINT64_MODIFIER "u.%06" G_GINT64_MODIFIER "u\n", + rs->procedures[i].proc, + rs->procedures[i].num, + (int)(rs->procedures[i].min.secs),(rs->procedures[i].min.nsecs+500)/1000, + (int)(rs->procedures[i].max.secs),(rs->procedures[i].max.nsecs+500)/1000, + td/MICROSECS_PER_SEC, td%MICROSECS_PER_SEC + ); + } + printf("=======================================================================\n"); +} + + + +static void +dcerpcstat_init(const char *optarg, void* userdata _U_) +{ + rpcstat_t *rs; + guint32 i, max_procs; + dcerpc_sub_dissector *procs; + e_uuid_t uuid; + guint d1,d2,d3,d40,d41,d42,d43,d44,d45,d46,d47; + int major, minor; + guint16 ver; + int pos=0; + const char *filter=NULL; + GString *error_string; + + /* + * XXX - DCE RPC statistics are maintained only by major version, + * not by major and minor version, so the minor version number is + * ignored. + * + * Should we just stop supporting minor version numbers here? + * Or should we allow it to be omitted? Or should we keep + * separate statistics for different minor version numbers, + * and allow the minor version number to be omitted, and + * report aggregate statistics for all minor version numbers + * if it's omitted? + */ + if(sscanf(optarg, + "dcerpc,srt,%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%d.%d%n", + &d1,&d2,&d3,&d40,&d41,&d42,&d43,&d44,&d45,&d46,&d47, + &major,&minor,&pos)==13){ + uuid.Data1=d1; + uuid.Data2=d2; + uuid.Data3=d3; + uuid.Data4[0]=d40; + uuid.Data4[1]=d41; + uuid.Data4[2]=d42; + uuid.Data4[3]=d43; + uuid.Data4[4]=d44; + uuid.Data4[5]=d45; + uuid.Data4[6]=d46; + uuid.Data4[7]=d47; + if(pos){ + filter=optarg+pos; + } else { + filter=NULL; + } + } else { + fprintf(stderr, "tshark: invalid \"-z dcerpc,srt,<uuid>,<major version>.<minor version>[,<filter>]\" argument\n"); + exit(1); + } + if (major < 0 || major > 65535) { + fprintf(stderr,"tshark: dcerpcstat_init() Major version number %d is invalid - must be positive and <= 65535\n", major); + exit(1); + } + if (minor < 0 || minor > 65535) { + fprintf(stderr,"tshark: dcerpcstat_init() Minor version number %d is invalid - must be positive and <= 65535\n", minor); + exit(1); + } + ver = major; + + rs=g_malloc(sizeof(rpcstat_t)); + rs->prog=dcerpc_get_proto_name(&uuid, ver); + if(!rs->prog){ + g_free(rs); + fprintf(stderr,"tshark: dcerpcstat_init() Protocol with uuid:%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x v%u not supported\n",uuid.Data1,uuid.Data2,uuid.Data3,uuid.Data4[0],uuid.Data4[1],uuid.Data4[2],uuid.Data4[3],uuid.Data4[4],uuid.Data4[5],uuid.Data4[6],uuid.Data4[7],ver); + exit(1); + } + procs=dcerpc_get_proto_sub_dissector(&uuid, ver); + rs->uuid=uuid; + rs->ver=ver; + + if(filter){ + rs->filter=g_strdup(filter); + } else { + rs->filter=NULL; + } + + for(i=0,max_procs=0;procs[i].name;i++){ + if(procs[i].num>max_procs){ + max_procs=procs[i].num; + } + } + rs->num_procedures=max_procs+1; + rs->procedures=g_malloc(sizeof(rpc_procedure_t)*(rs->num_procedures+1)); + for(i=0;i<rs->num_procedures;i++){ + int j; + rs->procedures[i].proc="unknown"; + for(j=0;procs[j].name;j++){ + if(procs[j].num==i){ + rs->procedures[i].proc=procs[j].name; + } + } + rs->procedures[i].num=0; + rs->procedures[i].min.secs=0; + rs->procedures[i].min.nsecs=0; + rs->procedures[i].max.secs=0; + rs->procedures[i].max.nsecs=0; + rs->procedures[i].tot.secs=0; + rs->procedures[i].tot.nsecs=0; + } + + error_string=register_tap_listener("dcerpc", rs, filter, 0, NULL, dcerpcstat_packet, dcerpcstat_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(rs->procedures); + g_free(rs->filter); + g_free(rs); + + fprintf(stderr, "tshark: Couldn't register dcerpc,srt tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + +void +register_tap_listener_dcerpcstat(void) +{ + register_stat_cmd_arg("dcerpc,srt,", dcerpcstat_init,NULL); +} diff --git a/ui/cli/tap-diameter-avp.c b/ui/cli/tap-diameter-avp.c new file mode 100644 index 0000000000..002167c31f --- /dev/null +++ b/ui/cli/tap-diameter-avp.c @@ -0,0 +1,286 @@ +/* tap-diameter-avp.c + * Copyright 2010 Andrej Kuehnal <andrejk@freenet.de> + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * This TAP enables extraction of most important diameter fields in text format. + * - much more performance than -T text and -T pdml + * - more powerfull 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) + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/epan_dissect.h> +#include <epan/stat_cmd_args.h> +#include "epan/value_string.h" +#include "epan/nstime.h" +#include "epan/ftypes/ftypes.h" +#include "epan/to_str.h" +#include "epan/dissectors/packet-diameter.h" + + +/* 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",node,data); + return FALSE; + } + fi=node->finfo; + hfi=fi ? fi->hfinfo : NULL; + if(!hfi) { + fprintf(stderr,"traverse end: hfi not found. node='%p'\n",node); + return FALSE; + } + ftype=fi->value.ftype->ftype; + if (ftype!=FT_NONE&&ftype!=FT_PROTOCOL) { + /* convert value to string */ + if(fi->value.ftype->val_to_string_repr) + { + val_tmp=fvalue_to_string_repr(&fi->value,FTREPR_DISPLAY,NULL); + if(val_tmp) + { + val_str=ep_strdup(val_tmp); + g_free(val_tmp); + } + } + if(!val_str) + val_str=ep_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); + } + return FALSE; +} + +static int +diameteravp_packet(void *pds, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pdi) +{ + int ret = 0; + 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=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->fd->num > ds->frame) { + ds->frame=pinfo->fd->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->fd->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='%d' time='%f' src='%s' srcport='%d' dst='%s' dstport='%d' proto='diameter' msgnr='%d' is_request='%d' cmd='%d' req_frame='%d' ans_frame='%d' resp_time='%f' ", + pinfo->fd->num,nstime_to_sec(&pinfo->fd->abs_ts),ep_address_to_str(&pinfo->src),pinfo->srcport,ep_address_to_str(&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 ===\nrequset count:\t%d\nanswer count:\t%d\nreq/ans pairs:\t%d\n",ds->req_count,ds->ans_count,ds->paired_ans_count); +} + + +static void +diameteravp_init(const char *optarg, 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_malloc(sizeof(diameteravp_t)); + 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(optarg,",", 1024); + opt_count=0; + while (tokens[opt_count]) + opt_count++; + if (opt_count>2) + ds->cmd_code=(guint32)atoi(tokens[2]); + + /* 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); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(ds); + + fprintf(stderr, "tshark: Couldn't register diam,csv tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_diameteravp(void) +{ + register_stat_cmd_arg("diameter,avp", diameteravp_init, NULL); +} + diff --git a/ui/cli/tap-expert.c b/ui/cli/tap-expert.c new file mode 100644 index 0000000000..c32e93c97d --- /dev/null +++ b/ui/cli/tap-expert.c @@ -0,0 +1,279 @@ +/* tap-expert.c + * Copyright 2011 Martin Mathieson + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#include <string.h> +#include <epan/packet.h> +#include <epan/packet_info.h> +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/expert.h> + +/* Tap data */ +typedef enum severity_level_t { + chat_level=0, + 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 = chat_level; + +typedef struct expert_entry +{ + guint32 group; + const gchar *protocol; + gchar *summary; + int frequency; +} 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 = 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 int +expert_stat_packet(void *tapdata, packet_info *pinfo _U_, epan_dissect_t *edt _U_, + const void *pointer) +{ + expert_info_t *ei = (expert_info_t *)pointer; + expert_tapdata_t *data = tapdata; + severity_level_t severity_level; + expert_entry tmp_entry; + expert_entry *entry; + guint n; + + switch (ei->severity) { + 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: + g_assert_not_reached(); + return 0; + } + + /* Don't store details at a lesser severity than we are interested in */ + if (severity_level < lowest_report_level) { + return 1; + } + + /* 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 1; + } + } + + /* Else Add new item to end of list for severity level */ + g_array_append_val(data->ei_array[severity_level], tmp_entry); + + /* Get pointer to newly-allocated item */ + entry = &g_array_index(data->ei_array[severity_level], expert_entry, + data->ei_array[severity_level]->len - 1); /* ugly */ + /* 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; + + return 1; +} + +/* 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; + + /* 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 (%u)\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); + printf("%12u %10s %18s %s\n", + ei->frequency, + val_to_str(ei->group, expert_group_vals, "Unknown"), + ei->protocol, ei->summary); + } +} + +/* (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"); +} + +/* Create a new expert stats struct */ +static void expert_stat_init(const char *optarg, 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(optarg, "expert", 6) == 0) { + /* Skip those characters */ + args = optarg + 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; + } + } + + /* Second (optional) arg is a filter string */ + if (args != NULL) { + if (args[0] == ',') { + filter = args+1; + } + } + + + /* Create top-level struct */ + hs = g_malloc(sizeof(expert_tapdata_t)); + memset(hs, 0, sizeof(expert_tapdata_t)); + + /* 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_info_t), 1000); + } + + /**********************************************/ + /* Register the tap listener */ + /**********************************************/ + + error_string = register_tap_listener("expert", hs, + filter, 0, + expert_stat_reset, + expert_stat_packet, + expert_stat_draw); + if (error_string) { + printf("Expert tap error (%s)!\n", error_string->str); + g_string_free(error_string, TRUE); + g_free(hs); + exit(1); + } +} + + +/* Register this tap listener (need void on own so line register function found) */ +void +register_tap_listener_expert_info(void) +{ + register_stat_cmd_arg("expert", expert_stat_init, NULL); +} + diff --git a/ui/cli/tap-follow.c b/ui/cli/tap-follow.c new file mode 100644 index 0000000000..2b4013cc53 --- /dev/null +++ b/ui/cli/tap-follow.c @@ -0,0 +1,869 @@ +/* tap-follow.c + * + * Copyright 2011, QA Cafe <info@qacafe.com> + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* This module provides udp and tcp follow stream capabilities to tshark. + * It is only used by tshark and not wireshark. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <ctype.h> +#include <stdio.h> + +#include <glib.h> +#include <epan/addr_resolv.h> +#include <epan/epan_dissect.h> +#include <epan/follow.h> +#include <epan/stat_cmd_args.h> +#include <epan/tap.h> +#include <epan/tvbuff-int.h> + +#include "wsutil/file_util.h" +#include "tempfile.h" + +WS_VAR_IMPORT FILE *data_out_file; + +typedef enum +{ + type_TCP, + type_UDP +} type_e; + +typedef enum +{ + mode_HEX, + mode_ASCII, + mode_RAW +} mode_e; + +typedef struct +{ + type_e type; + mode_e mode; + + /* filter */ + guint32 index; + address addr[2]; + int port[2]; + guint8 addrBuf[2][16]; + + /* range */ + guint32 chunkMin; + guint32 chunkMax; + + /* stream chunk file */ + FILE * filep; + gchar * filenamep; +} follow_t; + +#define STR_FOLLOW "follow," +#define STR_FOLLOW_TCP STR_FOLLOW "tcp" +#define STR_FOLLOW_UDP STR_FOLLOW "udp" + +#define STR_HEX ",hex" +#define STR_ASCII ",ascii" +#define STR_RAW ",raw" + +static void +followExit( + const char * strp + ) +{ + fprintf(stderr, "tshark: follow - %s\n", strp); + exit(1); +} + +static const char * +followStrType( + const follow_t * fp + ) +{ + switch (fp->type) + { + case type_TCP: return "tcp"; + case type_UDP: return "udp"; + } + + g_assert_not_reached(); + + return "<unknown-type>"; +} + +static const char * +followStrMode( + const follow_t * fp + ) +{ + switch (fp->mode) + { + case mode_HEX: return "hex"; + case mode_ASCII: return "ascii"; + case mode_RAW: return "raw"; + } + + g_assert_not_reached(); + + return "<unknown-mode>"; +} + +static const char * +followStrFilter( + const follow_t * fp + ) +{ + static char filter[512]; + int len = 0; + const gchar * verp; + gchar ip0[MAX_IP6_STR_LEN]; + gchar ip1[MAX_IP6_STR_LEN]; + + if (fp->index != G_MAXUINT32) + { + switch (fp->type) + { + case type_TCP: + len = g_snprintf(filter, sizeof filter, + "tcp.stream eq %d", fp->index); + break; + case type_UDP: + break; + } + } + else + { + verp = fp->addr[0].type == AT_IPv6 ? "v6" : ""; + address_to_str_buf(&fp->addr[0], ip0, sizeof ip0); + address_to_str_buf(&fp->addr[1], ip1, sizeof ip1); + + switch (fp->type) + { + case type_TCP: + len = g_snprintf(filter, sizeof filter, + "((ip%s.src eq %s and tcp.srcport eq %d) and " + "(ip%s.dst eq %s and tcp.dstport eq %d))" + " or " + "((ip%s.src eq %s and tcp.srcport eq %d) and " + "(ip%s.dst eq %s and tcp.dstport eq %d))", + verp, ip0, fp->port[0], + verp, ip1, fp->port[1], + verp, ip1, fp->port[1], + verp, ip0, fp->port[0]); + break; + case type_UDP: + len = g_snprintf(filter, sizeof filter, + "((ip%s.src eq %s and udp.srcport eq %d) and " + "(ip%s.dst eq %s and udp.dstport eq %d))" + " or " + "((ip%s.src eq %s and udp.srcport eq %d) and " + "(ip%s.dst eq %s and udp.dstport eq %d))", + verp, ip0, fp->port[0], + verp, ip1, fp->port[1], + verp, ip1, fp->port[1], + verp, ip0, fp->port[0]); + break; + } + } + + if (len == 0) + { + followExit("Don't know how to create filter."); + } + + if (len == sizeof filter) + { + followExit("Filter buffer overflow."); + } + + return filter; +} + +static void +followFileClose( + follow_t * fp + ) +{ + if (fp->filep != NULL) + { + fclose(fp->filep); + fp->filep = NULL; + if (fp->type == type_TCP) + { + data_out_file = NULL; + } + } + + if (fp->filenamep != NULL) + { + ws_unlink(fp->filenamep); + g_free(fp->filenamep); + fp->filenamep = NULL; + } +} + +static void +followFileOpen( + follow_t * fp + ) +{ + int fd; + char * tempfilep; + + if (fp->type == type_TCP && data_out_file != NULL) + { + followExit("Only one TCP stream can be followed at a time."); + } + + followFileClose(fp); + + fd = create_tempfile(&tempfilep, "follow"); + if (fd == -1) + { + followExit("Error creating temp file."); + } + + fp->filenamep = g_strdup(tempfilep); + if (fp->filenamep == NULL) + { + ws_close(fd); + ws_unlink(tempfilep); + followExit("Error duping temp file name."); + } + + fp->filep = fdopen(fd, "w+b"); + if (fp->filep == NULL) + { + ws_close(fd); + ws_unlink(fp->filenamep); + g_free(fp->filenamep); + fp->filenamep = NULL; + followExit("Error opening temp file stream."); + } + + if (fp->type == type_TCP) + { + data_out_file = fp->filep; + } +} + +static follow_t * +followAlloc( + type_e type + ) +{ + follow_t * fp; + + fp = g_malloc0(sizeof *fp); + + fp->type = type; + SET_ADDRESS(&fp->addr[0], AT_NONE, 0, fp->addrBuf[0]); + SET_ADDRESS(&fp->addr[1], AT_NONE, 0, fp->addrBuf[1]); + + return fp; +} + +static void +followFree( + follow_t * fp + ) +{ + followFileClose(fp); + g_free(fp); +} + +static int +followPacket( + void * contextp, + packet_info * pip, + epan_dissect_t * edp _U_, + const void * datap + ) +{ + follow_t * fp = contextp; + const tvbuff_t * tvbp = datap; + tcp_stream_chunk sc; + size_t size; + + if (tvbp->length > 0) + { + memcpy(sc.src_addr, pip->net_src.data, pip->net_src.len); + sc.src_port = pip->srcport; + sc.dlen = tvbp->length; + + size = fwrite(&sc, 1, sizeof sc, fp->filep); + if (sizeof sc != size) + { + followExit("Error writing stream chunk header."); + } + + size = fwrite(tvbp->real_data, 1, sc.dlen, fp->filep); + if (sc.dlen != size) + { + followExit("Error writing stream chunk data."); + } + } + + return 0; +} + +#define BYTES_PER_LINE 16 +#define OFFSET_START 0 +#define OFFSET_LEN 8 +#define OFFSET_SPACE 2 +#define HEX_START (OFFSET_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 ASCII_SPACE 0 +#define LINE_LEN (ASCII_START + ASCII_LEN + ASCII_SPACE) + +static const char bin2hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; +static void +followPrintHex( + 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 */ + g_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 +followDraw( + void * contextp + ) +{ + static const char seperator[] = + "===================================================================\n"; + + follow_t * fp = contextp; + tcp_stream_chunk sc; + int node; + const address * addr[2]; + int port[2]; + gchar buf[MAX_IP6_STR_LEN]; + guint32 ii; + guint32 jj; + guint32 len; + guint32 chunk; + guint32 offset[2]; + guint8 bin[4096]; + char data[(sizeof bin * 2) + 2]; + + g_assert(sizeof bin % BYTES_PER_LINE == 0); + + if (fp->type == type_TCP) + { + follow_stats_t stats; + address_type type; + int len; + + follow_stats(&stats); + + if (stats.is_ipv6) + { + type = AT_IPv6; + len = 16; + } + else + { + type = AT_IPv4; + len = 4; + } + + for (node = 0; node < 2; node++) + { + memcpy(fp->addrBuf[node], stats.ip_address[node], len); + SET_ADDRESS(&fp->addr[node], type, len, fp->addrBuf[node]); + fp->port[node] = stats.port[node]; + } + } + + /* find first stream chunk */ + rewind(fp->filep); + for (chunk = 0;;) + { + len = (guint32)fread(&sc, 1, sizeof sc, fp->filep); + if (len != sizeof sc) + { + /* no data */ + sc.dlen = 0; + memcpy(sc.src_addr, fp->addr[0].data, fp->addr[0].len) ; + sc.src_port = fp->port[0]; + break; + } + if (sc.dlen > 0) + { + chunk++; + break; + } + } + + /* node 0 is source of first chunk with data */ + if (memcmp(sc.src_addr, fp->addr[0].data, fp->addr[0].len) == 0 && + sc.src_port == fp->port[0]) + { + addr[0] = &fp->addr[0]; + port[0] = fp->port[0]; + addr[1] = &fp->addr[1]; + port[1] = fp->port[1]; + } + else + { + addr[0] = &fp->addr[1]; + port[0] = fp->port[1]; + addr[1] = &fp->addr[0]; + port[1] = fp->port[0]; + } + + printf("\n%s", seperator); + printf("Follow: %s,%s\n", followStrType(fp), followStrMode(fp)); + printf("Filter: %s\n", followStrFilter(fp)); + + for (node = 0; node < 2; node++) + { + address_to_str_buf(addr[node], buf, sizeof buf); + if (addr[node]->type == AT_IPv6) + { + printf("Node %u: [%s]:%d\n", node, buf, port[node]); + } + else + { + printf("Node %u: %s:%d\n", node, buf, port[node]); + } + } + + offset[0] = offset[1] = 0; + + while (chunk <= fp->chunkMax) + { + node = (memcmp(addr[0]->data, sc.src_addr, addr[0]->len) == 0 && + port[0] == sc.src_port) ? 0 : 1; + + if (chunk < fp->chunkMin) + { + while (sc.dlen > 0) + { + len = sc.dlen < sizeof bin ? sc.dlen : sizeof bin; + sc.dlen -= len; + if (fread(bin, 1, len, fp->filep) != len) + { + followExit("Error reading stream chunk data."); + } + offset[node] += len; + } + } + else + { + switch (fp->mode) + { + case mode_HEX: + break; + + case mode_ASCII: + printf("%s%d\n", node ? "\t" : "", sc.dlen); + break; + + case mode_RAW: + if (node) + { + putchar('\t'); + } + break; + } + + while (sc.dlen > 0) + { + len = sc.dlen < sizeof bin ? sc.dlen : sizeof bin; + sc.dlen -= len; + if (fread(bin, 1, len, fp->filep) != len) + { + followExit("Error reading stream chunk data."); + } + + switch (fp->mode) + { + case mode_HEX: + followPrintHex(node ? "\t" : "", offset[node], bin, len); + break; + + case mode_ASCII: + for (ii = 0; ii < len; ii++) + { + switch (bin[ii]) + { + case '\r': + case '\n': + data[ii] = bin[ii]; + break; + default: + data[ii] = isprint(bin[ii]) ? bin[ii] : '.'; + break; + } + } + if (sc.dlen == 0) + { + data[ii++] = '\n'; + } + data[ii] = 0; + printf("%s", data); + break; + + case mode_RAW: + for (ii = 0, jj = 0; ii < len; ii++) + { + data[jj++] = bin2hex[bin[ii] >> 4]; + data[jj++] = bin2hex[bin[ii] & 0xf]; + } + if (sc.dlen == 0) + { + data[jj++] = '\n'; + } + data[jj] = 0; + printf("%s", data); + } + + offset[node] += len; + } + } + + for (;;) + { + len = (guint32)fread(&sc, 1, sizeof sc, fp->filep); + if (len != sizeof sc) + { + /* no more data */ + sc.dlen = 0; + chunk = G_MAXUINT32; + goto done; + } + if (sc.dlen > 0) + { + chunk++; + break; + } + } + } + +done: + + printf("%s", seperator); + + followFileClose(fp); +} + +static gboolean +followArgStrncmp( + const char ** optargp, + const char * strp + ) +{ + int len = (guint32)strlen(strp); + + if (strncmp(*optargp, strp, len) == 0) + { + *optargp += len; + return TRUE; + } + return FALSE; +} + +static void +followArgMode( + const char ** optargp, + follow_t * fp + ) +{ + if (followArgStrncmp(optargp, STR_HEX)) + { + fp->mode = mode_HEX; + } + else if (followArgStrncmp(optargp, STR_ASCII)) + { + fp->mode = mode_ASCII; + } + else if (followArgStrncmp(optargp, STR_RAW)) + { + fp->mode = mode_RAW; + } + else + { + followExit("Invalid display mode."); + } +} + +static void +followArgFilter( + const char ** optargp, + follow_t * fp + ) +{ +#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" + + int len; + unsigned int ii; + char addr[ADDR_LEN]; + + if (sscanf(*optargp, ",%u%n", &fp->index, &len) == 1 && + ((*optargp)[len] == 0 || (*optargp)[len] == ',')) + { + *optargp += len; + } + else + { + for (ii = 0; ii < sizeof fp->addr/sizeof *fp->addr; ii++) + { + if ((sscanf(*optargp, ADDRv6_FMT, addr, &fp->port[ii], &len) != 2 && + sscanf(*optargp, ADDRv4_FMT, addr, &fp->port[ii], &len) != 2) || + fp->port[ii] <= 0 || fp->port[ii] > G_MAXUINT16) + { + followExit("Invalid address:port pair."); + } + + if (strcmp("ip6", host_ip_af(addr)) == 0) + { + if (!get_host_ipaddr6(addr, (struct e_in6_addr *)fp->addrBuf[ii])) + { + followExit("Can't get IPv6 address"); + } + SET_ADDRESS(&fp->addr[ii], AT_IPv6, 16, fp->addrBuf[ii]); + } + else + { + if (!get_host_ipaddr(addr, (guint32 *)fp->addrBuf[ii])) + { + followExit("Can't get IPv4 address"); + } + SET_ADDRESS(&fp->addr[ii], AT_IPv4, 4, fp->addrBuf[ii]); + } + + *optargp += len; + } + + if (fp->addr[0].type != fp->addr[1].type) + { + followExit("Mismatched IP address types."); + } + fp->index = G_MAXUINT32; + } +} + +static void +followArgRange( + const char ** optargp, + follow_t * fp + ) +{ + int len; + + if (**optargp == 0) + { + fp->chunkMin = 1; + fp->chunkMax = G_MAXUINT32; + } + else + { + if (sscanf(*optargp, ",%u-%u%n", &fp->chunkMin, &fp->chunkMax, &len) == 2) + { + *optargp += len; + } + else if (sscanf(*optargp, ",%u%n", &fp->chunkMin, &len) == 1) + { + fp->chunkMax = fp->chunkMin; + *optargp += len; + } + else + { + followExit("Invalid range."); + } + + if (fp->chunkMin < 1 || fp->chunkMin > fp->chunkMax) + { + followExit("Invalid range value."); + } + } +} + +static void +followArgDone( + const char * optarg + ) +{ + if (*optarg != 0) + { + followExit("Invalid parameter."); + } +} + +static void +followTcp( + const char * optarg, + void * userdata _U_ + ) +{ + follow_t * fp; + GString * errp; + + optarg += strlen(STR_FOLLOW_TCP); + + fp = followAlloc(type_TCP); + + followArgMode(&optarg, fp); + followArgFilter(&optarg, fp); + followArgRange(&optarg, fp); + followArgDone(optarg); + + reset_tcp_reassembly(); + if (fp->index != G_MAXUINT32) + { + if (!follow_tcp_index(fp->index)) + { + followExit("Can't follow tcp index."); + } + } + else + { + if (!follow_tcp_addr(&fp->addr[0], fp->port[0], + &fp->addr[1], fp->port[1])) + { + followExit("Can't follow tcp address/port pairs."); + } + } + + followFileOpen(fp); + + errp = register_tap_listener("frame", fp, NULL, 0, + NULL, NULL, followDraw); + if (errp != NULL) + { + followFree(fp); + g_string_free(errp, TRUE); + followExit("Error registering tcp tap listner."); + } +} + +static void +followUdp( + const char * optarg, + void * userdata _U_ + ) +{ + follow_t * fp; + GString * errp; + + optarg += strlen(STR_FOLLOW_UDP); + + fp = followAlloc(type_UDP); + + followArgMode(&optarg, fp); + followArgFilter(&optarg, fp); + followArgRange(&optarg, fp); + followArgDone(optarg); + + if (fp->index != G_MAXUINT32) + { + followExit("UDP does not support index filters."); + } + + followFileOpen(fp); + + errp = register_tap_listener("udp_follow", fp, followStrFilter(fp), 0, + NULL, followPacket, followDraw); + if (errp != NULL) + { + followFree(fp); + g_string_free(errp, TRUE); + followExit("Error registering udp tap listner."); + } +} + +void +register_tap_listener_follow(void) +{ + register_stat_cmd_arg(STR_FOLLOW_TCP, followTcp, NULL); + register_stat_cmd_arg(STR_FOLLOW_UDP, followUdp, NULL); +} diff --git a/ui/cli/tap-funnel.c b/ui/cli/tap-funnel.c new file mode 100644 index 0000000000..9fcd10ed33 --- /dev/null +++ b/ui/cli/tap-funnel.c @@ -0,0 +1,195 @@ +/* + * tap-funnel.c + * + * EPAN's GUI mini-API + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <epan/funnel.h> +#include <stdio.h> +#include <epan/stat_cmd_args.h> + + +struct _funnel_text_window_t { + gchar* title; + GString* text; +}; + +static GPtrArray* text_windows = NULL; + +static funnel_text_window_t* new_text_window(const gchar* title) { + funnel_text_window_t* tw = g_malloc(sizeof(funnel_text_window_t)); + 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; +} + +/* XXX: finish this */ +static void funnel_logger(const gchar *log_domain _U_, + GLogLevelFlags log_level _U_, + const gchar *message, + gpointer user_data _U_) { + fputs(message,stderr); +} + + + +static const funnel_ops_t funnel_ops = { + 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, + funnel_logger, + 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 = 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 *optarg, void* data ) { + gchar** args = g_strsplit(optarg,",",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_malloc(sizeof(menu_cb_t)); + + 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); + + register_stat_cmd_arg(name,init_funnel_cmd,mcb); +} + +void initialize_funnel_ops(void) { + funnel_set_funnel_ops(&funnel_ops); +} + +#endif +void +register_tap_listener_gtkfunnel(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 0000000000..b84e377dfb --- /dev/null +++ b/ui/cli/tap-gsm_astat.c @@ -0,0 +1,353 @@ +/* tap-gsm_astat.c + * + * Copyright 2003, Michael Lum <mlum [AT] telostech.com> + * In association with Telos Technology Inc. + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * This TAP provides statistics for the GSM A Interface: + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include "epan/value_string.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/dissectors/packet-bssap.h> +#include <epan/dissectors/packet-gsm_a_common.h> + + +typedef struct _gsm_a_stat_t { + int bssmap_message_type[0xff]; + int dtap_mm_message_type[0xff]; + int dtap_rr_message_type[0xff]; + int dtap_cc_message_type[0xff]; + int dtap_gmm_message_type[0xff]; + int dtap_sms_message_type[0xff]; + int dtap_sm_message_type[0xff]; + int dtap_ss_message_type[0xff]; + int dtap_tp_message_type[0xff]; + int sacch_rr_message_type[0xff]; +} gsm_a_stat_t; + + +static int +gsm_a_stat_packet( + void *tapdata, + packet_info *pinfo _U_, + epan_dissect_t *edt _U_, + const void *data) +{ + gsm_a_stat_t *stat_p = tapdata; + const gsm_a_tap_rec_t *tap_p = 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(0); + } + 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(0); + } + + return(1); +} + + +static void +gsm_a_stat_draw( + void *tapdata) +{ + gsm_a_stat_t *stat_p = 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 *optarg _U_,void* userdata _U_) +{ + gsm_a_stat_t *stat_p; + GString *err_p; + + stat_p = g_malloc(sizeof(gsm_a_stat_t)); + + 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); + + if (err_p != NULL) + { + g_free(stat_p); + g_string_free(err_p, TRUE); + + exit(1); + } +} + + +void +register_tap_listener_gsm_astat(void) +{ + register_stat_cmd_arg("gsm_a,", gsm_a_stat_init,NULL); +} diff --git a/ui/cli/tap-h225counter.c b/ui/cli/tap-h225counter.c new file mode 100644 index 0000000000..6076f1c225 --- /dev/null +++ b/ui/cli/tap-h225counter.c @@ -0,0 +1,384 @@ +/* tap_h225counter.c + * h225 message counter for wireshark + * Copyright 2003 Lars Roland + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet.h" +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include "epan/value_string.h" +#include <epan/dissectors/packet-h225.h> + +/* following values represent the size of their valuestring arrays */ + +#define RAS_MSG_TYPES 33 +#define CS_MSG_TYPES 13 + +#define GRJ_REASONS 8 +#define RRJ_REASONS 18 +#define URQ_REASONS 6 +#define URJ_REASONS 6 +#define ARJ_REASONS 22 +#define BRJ_REASONS 8 +#define DRQ_REASONS 3 +#define DRJ_REASONS 4 +#define LRJ_REASONS 16 +#define IRQNAK_REASONS 4 +#define REL_CMP_REASONS 26 +#define FACILITY_REASONS 11 + + +/* used to keep track of the statistics for an entire program interface */ +typedef struct _h225counter_t { + char *filter; + guint32 ras_msg[RAS_MSG_TYPES + 1]; + guint32 cs_msg[CS_MSG_TYPES + 1]; + guint32 grj_reason[GRJ_REASONS + 1]; + guint32 rrj_reason[RRJ_REASONS + 1]; + guint32 urq_reason[URQ_REASONS + 1]; + guint32 urj_reason[URJ_REASONS + 1]; + guint32 arj_reason[ARJ_REASONS + 1]; + guint32 brj_reason[BRJ_REASONS + 1]; + guint32 drq_reason[DRQ_REASONS + 1]; + guint32 drj_reason[DRJ_REASONS + 1]; + guint32 lrj_reason[LRJ_REASONS + 1]; + guint32 irqnak_reason[IRQNAK_REASONS + 1]; + guint32 rel_cmp_reason[REL_CMP_REASONS + 1]; + guint32 facility_reason[FACILITY_REASONS + 1]; +} h225counter_t; + + +static void +h225counter_reset(void *phs) +{ + h225counter_t *hs=(h225counter_t *)phs; + char *save_filter = hs->filter; + + memset(hs, 0, sizeof(h225counter_t)); + + hs->filter = save_filter; +} + +static int +h225counter_packet(void *phs, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *phi) +{ + h225counter_t *hs=(h225counter_t *)phs; + const h225_packet_info *pi=phi; + + switch (pi->msg_type) { + + case H225_RAS: + if(pi->msg_tag==-1) { /* uninitialized */ + return 0; + } + else if (pi->msg_tag >= RAS_MSG_TYPES) { /* unknown */ + hs->ras_msg[RAS_MSG_TYPES]++; + } + else { + hs->ras_msg[pi->msg_tag]++; + } + + /* Look for reason tag */ + if(pi->reason==-1) { /* uninitialized */ + break; + } + + switch(pi->msg_tag) { + + case 2: /* GRJ */ + if(pi->reason < GRJ_REASONS) + hs->grj_reason[pi->reason]++; + else + hs->grj_reason[GRJ_REASONS]++; + break; + case 5: /* RRJ */ + if(pi->reason < RRJ_REASONS) + hs->rrj_reason[pi->reason]++; + else + hs->rrj_reason[RRJ_REASONS]++; + break; + case 6: /* URQ */ + if(pi->reason < URQ_REASONS) + hs->urq_reason[pi->reason]++; + else + hs->urq_reason[URQ_REASONS]++; + break; + case 8: /* URJ */ + if(pi->reason < URJ_REASONS) + hs->urj_reason[pi->reason]++; + else + hs->urj_reason[URJ_REASONS]++; + break; + case 11: /* ARJ */ + if(pi->reason < ARJ_REASONS) + hs->arj_reason[pi->reason]++; + else + hs->arj_reason[ARJ_REASONS]++; + break; + case 14: /* BRJ */ + if(pi->reason < BRJ_REASONS) + hs->brj_reason[pi->reason]++; + else + hs->brj_reason[BRJ_REASONS]++; + break; + case 15: /* DRQ */ + if(pi->reason < DRQ_REASONS) + hs->drq_reason[pi->reason]++; + else + hs->drq_reason[DRQ_REASONS]++; + break; + case 17: /* DRJ */ + if(pi->reason < DRJ_REASONS) + hs->drj_reason[pi->reason]++; + else + hs->drj_reason[DRJ_REASONS]++; + break; + case 20: /* LRJ */ + if(pi->reason < LRJ_REASONS) + hs->lrj_reason[pi->reason]++; + else + hs->lrj_reason[LRJ_REASONS]++; + break; + case 29: /* IRQ Nak */ + if(pi->reason < IRQNAK_REASONS) + hs->irqnak_reason[pi->reason]++; + else + hs->irqnak_reason[IRQNAK_REASONS]++; + break; + + default: + /* do nothing */ + break; + } + + break; + + case H225_CS: + if(pi->msg_tag==-1) { /* uninitialized */ + return 0; + } + else if (pi->msg_tag >= CS_MSG_TYPES) { /* unknown */ + hs->cs_msg[CS_MSG_TYPES]++; + } + else { + hs->cs_msg[pi->msg_tag]++; + } + + /* Look for reason tag */ + if(pi->reason==-1) { /* uninitialized */ + break; + } + + switch(pi->msg_tag) { + + case 5: /* ReleaseComplete */ + if(pi->reason < REL_CMP_REASONS) + hs->rel_cmp_reason[pi->reason]++; + else + hs->rel_cmp_reason[REL_CMP_REASONS]++; + break; + case 6: /* Facility */ + if(pi->reason < FACILITY_REASONS) + hs->facility_reason[pi->reason]++; + else + hs->facility_reason[FACILITY_REASONS]++; + break; + default: + /* do nothing */ + break; + } + + break; + + default: + return 0; + } + + return 1; +} + + +static void +h225counter_draw(void *phs) +{ + h225counter_t *hs=(h225counter_t *)phs; + int i,j; + + printf("================== H225 Message and Reason Counter ==================\n"); + printf("RAS-Messages:\n"); + for(i=0;i<=RAS_MSG_TYPES;i++) { + if(hs->ras_msg[i]!=0) { + printf(" %s : %u\n", val_to_str(i,h225_RasMessage_vals,"unknown ras-messages "), hs->ras_msg[i]); + /* reason counter */ + switch(i) { + case 2: /* GRJ */ + for(j=0;j<=GRJ_REASONS;j++) { + if(hs->grj_reason[j]!=0) { + printf(" %s : %u\n", val_to_str(j,GatekeeperRejectReason_vals,"unknown reason "), hs->grj_reason[j]); + } + } + break; + case 5: /* RRJ */ + for(j=0;j<=RRJ_REASONS;j++) { + if(hs->rrj_reason[j]!=0) { + printf(" %s : %u\n", val_to_str(j,RegistrationRejectReason_vals,"unknown reason "), hs->rrj_reason[j]); + } + } + break; + case 6: /* URQ */ + for(j=0;j<=URQ_REASONS;j++) { + if(hs->urq_reason[j]!=0) { + printf(" %s : %u\n", val_to_str(j,UnregRequestReason_vals,"unknown reason "), hs->urq_reason[j]); + } + } + break; + case 8: /* URJ */ + for(j=0;j<=URJ_REASONS;j++) { + if(hs->urj_reason[j]!=0) { + printf(" %s : %u\n", val_to_str(j,UnregRejectReason_vals,"unknown reason "), hs->urj_reason[j]); + } + } + break; + case 11: /* ARJ */ + for(j=0;j<=ARJ_REASONS;j++) { + if(hs->arj_reason[j]!=0) { + printf(" %s : %u\n", val_to_str(j,AdmissionRejectReason_vals,"unknown reason "), hs->arj_reason[j]); + } + } + break; + case 14: /* BRJ */ + for(j=0;j<=BRJ_REASONS;j++) { + if(hs->brj_reason[j]!=0) { + printf(" %s : %u\n", val_to_str(j,BandRejectReason_vals,"unknown reason "), hs->brj_reason[j]); + } + } + break; + case 15: /* DRQ */ + for(j=0;j<=DRQ_REASONS;j++) { + if(hs->drq_reason[j]!=0) { + printf(" %s : %u\n", val_to_str(j,DisengageReason_vals,"unknown reason "), hs->drq_reason[j]); + } + } + break; + case 17: /* DRJ */ + for(j=0;j<=DRJ_REASONS;j++) { + if(hs->drj_reason[j]!=0) { + printf(" %s : %u\n", val_to_str(j,DisengageRejectReason_vals,"unknown reason "), hs->drj_reason[j]); + } + } + break; + case 20: /* LRJ */ + for(j=0;j<=LRJ_REASONS;j++) { + if(hs->lrj_reason[j]!=0) { + printf(" %s : %u\n", val_to_str(j,LocationRejectReason_vals,"unknown reason "), hs->lrj_reason[j]); + } + } + break; + case 29: /* IRQNak */ + for(j=0;j<=IRQNAK_REASONS;j++) { + if(hs->irqnak_reason[j]!=0) { + printf(" %s : %u\n", val_to_str(j,InfoRequestNakReason_vals,"unknown reason "), hs->irqnak_reason[j]); + } + } + break; + default: + break; + } + /* end of reason counter*/ + } + } + printf("Call Signalling:\n"); + for(i=0;i<=CS_MSG_TYPES;i++) { + if(hs->cs_msg[i]!=0) { + printf(" %s : %u\n", val_to_str(i,T_h323_message_body_vals,"unknown cs-messages "), hs->cs_msg[i]); + /* reason counter */ + switch(i) { + case 5: /* ReleaseComplete */ + for(j=0;j<=REL_CMP_REASONS;j++) { + if(hs->rel_cmp_reason[j]!=0) { + printf(" %s : %u\n", val_to_str(j,h225_ReleaseCompleteReason_vals,"unknown reason "), hs->rel_cmp_reason[j]); + } + } + break; + case 6: /* Facility */ + for(j=0;j<=FACILITY_REASONS;j++) { + if(hs->facility_reason[j]!=0) { + printf(" %s : %u\n", val_to_str(j,FacilityReason_vals,"unknown reason "), hs->facility_reason[j]); + } + } + break; + default: + break; + } + } + } + printf("=====================================================================\n"); +} + + +static void +h225counter_init(const char *optarg, void* userdata _U_) +{ + h225counter_t *hs; + GString *error_string; + + hs = g_malloc(sizeof(h225counter_t)); + if(!strncmp(optarg,"h225,counter,",13)){ + hs->filter=g_strdup(optarg+13); + } else { + hs->filter=NULL; + } + + h225counter_reset(hs); + + error_string=register_tap_listener("h225", hs, hs->filter, 0, NULL, h225counter_packet, h225counter_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(hs->filter); + g_free(hs); + + fprintf(stderr, "tshark: Couldn't register h225,counter tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_h225counter(void) +{ + register_stat_cmd_arg("h225,counter", h225counter_init,NULL); +} diff --git a/ui/cli/tap-h225rassrt.c b/ui/cli/tap-h225rassrt.c new file mode 100644 index 0000000000..a0c6d8e37e --- /dev/null +++ b/ui/cli/tap-h225rassrt.c @@ -0,0 +1,246 @@ +/* tap_h225rassrt.c + * h225 RAS Service Response Time statistics for wireshark + * Copyright 2003 Lars Roland + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet.h" +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include "epan/value_string.h" +#include <epan/dissectors/packet-h225.h> +#include "timestats.h" + +/* following values represent the size of their valuestring arrays */ +#define NUM_RAS_STATS 7 + +static const value_string ras_message_category[] = { + { 0, "Gatekeeper "}, + { 1, "Registration "}, + { 2, "UnRegistration"}, + { 3, "Admission "}, + { 4, "Bandwidth "}, + { 5, "Disengage "}, + { 6, "Location "}, + { 0, NULL } +}; + +typedef enum _ras_type { + RAS_REQUEST, + RAS_CONFIRM, + RAS_REJECT, + RAS_OTHER +}ras_type; + +typedef enum _ras_category { + RAS_GATEKEEPER, + RAS_REGISTRATION, + RAS_UNREGISTRATION, + RAS_ADMISSION, + RAS_BANDWIDTH, + RAS_DISENGAGE, + RAS_LOCATION, + RAS_OTHERS +}ras_category; + +/* Summary of response-time calculations*/ +typedef struct _h225_rtd_t { + guint32 open_req_num; + guint32 disc_rsp_num; + guint32 req_dup_num; + guint32 rsp_dup_num; + timestat_t stats; +} h225_rtd_t; + +/* used to keep track of the statistics for an entire program interface */ +typedef struct _h225rassrt_t { + char *filter; + h225_rtd_t ras_rtd[NUM_RAS_STATS]; +} h225rassrt_t; + + +static void +h225rassrt_reset(void *phs) +{ + h225rassrt_t *hs=(h225rassrt_t *)phs; + int i; + + for(i=0;i<NUM_RAS_STATS;i++) { + hs->ras_rtd[i].stats.num = 0; + hs->ras_rtd[i].stats.min_num = 0; + hs->ras_rtd[i].stats.max_num = 0; + hs->ras_rtd[i].stats.min.secs = 0; + hs->ras_rtd[i].stats.min.nsecs = 0; + hs->ras_rtd[i].stats.max.secs = 0; + hs->ras_rtd[i].stats.max.nsecs = 0; + hs->ras_rtd[i].stats.tot.secs = 0; + hs->ras_rtd[i].stats.tot.nsecs = 0; + hs->ras_rtd[i].open_req_num = 0; + hs->ras_rtd[i].disc_rsp_num = 0; + hs->ras_rtd[i].req_dup_num = 0; + hs->ras_rtd[i].rsp_dup_num = 0; + } + +} + +static int +h225rassrt_packet(void *phs, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *phi) +{ + h225rassrt_t *hs=(h225rassrt_t *)phs; + const h225_packet_info *pi=phi; + + ras_type rasmsg_type = RAS_OTHER; + ras_category rascategory = RAS_OTHERS; + + if (pi->msg_type != H225_RAS || pi->msg_tag == -1) { + /* No RAS Message or uninitialized msg_tag -> return */ + return 0; + } + + if (pi->msg_tag < 21) { + /* */ + rascategory = pi->msg_tag / 3; + rasmsg_type = pi->msg_tag % 3; + } + else { + /* No SRT yet (ToDo) */ + return 0; + } + + switch(rasmsg_type) { + + case RAS_REQUEST: + if(pi->is_duplicate){ + hs->ras_rtd[rascategory].req_dup_num++; + } + else { + hs->ras_rtd[rascategory].open_req_num++; + } + break; + + case RAS_CONFIRM: + /* no break - delay calculation is identical for Confirm and Reject */ + case RAS_REJECT: + if(pi->is_duplicate){ + /* Duplicate is ignored */ + hs->ras_rtd[rascategory].rsp_dup_num++; + } + else if (!pi->request_available) { + /* no request was seen, ignore response */ + hs->ras_rtd[rascategory].disc_rsp_num++; + } + else { + hs->ras_rtd[rascategory].open_req_num--; + time_stat_update(&(hs->ras_rtd[rascategory].stats),&(pi->delta_time), pinfo); + } + break; + + default: + return 0; + } + return 1; +} + +static void +h225rassrt_draw(void *phs) +{ + h225rassrt_t *hs=(h225rassrt_t *)phs; + int i; + timestat_t *rtd_temp; + + printf("======================================== H225 RAS Service Response Time ========================================\n"); + printf("H225 RAS Service Response Time (SRT) Statistics:\n"); + printf("RAS-Messages | Measurements | Min SRT | Max SRT | Avg SRT | Min in Frame | Max in Frame |\n"); + for(i=0;i<NUM_RAS_STATS;i++) { + rtd_temp = &(hs->ras_rtd[i].stats); + if(rtd_temp->num){ + printf("%s | %10u | %9.2f msec | %9.2f msec | %9.2f msec | %10u | %10u |\n", + val_to_str(i,ras_message_category,"Unknown "),rtd_temp->num, + nstime_to_msec(&(rtd_temp->min)), nstime_to_msec(&(rtd_temp->max)), + get_average(&(rtd_temp->tot), rtd_temp->num), + rtd_temp->min_num, rtd_temp->max_num + ); + } + } + printf("================================================================================================================\n"); + printf("RAS-Messages | Open REQ | Discarded RSP | Repeated REQ | Repeated RSP |\n"); + for(i=0;i<NUM_RAS_STATS;i++) { + rtd_temp = &(hs->ras_rtd[i].stats); + if(rtd_temp->num){ + printf("%s | %10u | %10u | %10u | %10u |\n", + val_to_str(i,ras_message_category,"Unknown "), + hs->ras_rtd[i].open_req_num, hs->ras_rtd[i].disc_rsp_num, + hs->ras_rtd[i].req_dup_num, hs->ras_rtd[i].rsp_dup_num + ); + } + } + printf("================================================================================================================\n"); + +} + + +static void +h225rassrt_init(const char *optarg, void* userdata _U_) +{ + h225rassrt_t *hs; + GString *error_string; + + hs = g_malloc(sizeof(h225rassrt_t)); + if(!strncmp(optarg,"h225,srt,",9)){ + hs->filter=g_strdup(optarg+9); + } else { + hs->filter=NULL; + } + + h225rassrt_reset(hs); + + error_string=register_tap_listener("h225", hs, hs->filter, 0, NULL, h225rassrt_packet, h225rassrt_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(hs->filter); + g_free(hs); + + fprintf(stderr, "tshark: Couldn't register h225,srt tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_h225rassrt(void) +{ + register_stat_cmd_arg("h225,srt", h225rassrt_init,NULL); +} diff --git a/ui/cli/tap-hosts.c b/ui/cli/tap-hosts.c new file mode 100644 index 0000000000..a840932782 --- /dev/null +++ b/ui/cli/tap-hosts.c @@ -0,0 +1,183 @@ +/* tap-hosts.c + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Dump our collected IPv4- and IPv6-to-hostname mappings */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> + +#include "globals.h" + +#include <epan/packet.h> +#include <cfile.h> +#include <epan/proto.h> +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/addr_resolv.h> + +/* Needed for addrinfo */ +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#ifdef HAVE_NETDB_H +# include <netdb.h> +#endif + +#ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +#endif + +#if defined(_WIN32) && defined(INET6) +# include <ws2tcpip.h> +#endif + +#ifdef NEED_INET_V6DEFS_H +# include "wsutil/inet_v6defs.h" +#endif + + +gboolean dump_v4 = FALSE; +gboolean dump_v6 = FALSE; + +#define TAP_NAME "hosts" + +#define HOSTNAME_POS 48 +#define ADDRSTRLEN 46 /* Covers IPv4 & IPv6 */ +static void +hosts_draw(void *dummy _U_) +{ + struct addrinfo *ai; + struct sockaddr_in *sa4; + struct sockaddr_in6 *sa6; + char addr_str[ADDRSTRLEN]; + int i, tab_count; + + printf("# TShark hosts output\n"); + printf("#\n"); + printf("# Host data gathered from %s\n", cfile.filename); + printf("\n"); + + /* Dump the v4 addresses first, then v6 */ + for (ai = get_addrinfo_list(); ai; ai = ai->ai_next) { + if (!dump_v4 || ai->ai_family != AF_INET) { + continue; + } + + sa4 = (struct sockaddr_in *)(void *)ai->ai_addr; + if (inet_ntop(AF_INET, &(sa4->sin_addr.s_addr), addr_str, ADDRSTRLEN)) { + tab_count = (HOSTNAME_POS - (int)strlen(addr_str)) / 8; + printf("%s", addr_str); + for (i = 0; i < tab_count; i++) + printf("\t"); + printf("%s\n", ai->ai_canonname); + } + } + + + for (ai = get_addrinfo_list(); ai; ai = ai->ai_next) { + if (!dump_v6 || ai->ai_family != AF_INET6) { + continue; + } + + sa6 = (struct sockaddr_in6 *)(void *)ai->ai_addr; + if (inet_ntop(AF_INET6, sa6->sin6_addr.s6_addr, addr_str, ADDRSTRLEN)) { + tab_count = (HOSTNAME_POS - (int)strlen(addr_str)) / 8; + printf("%s", addr_str); + for (i = 0; i < tab_count; i++) + printf("\t"); + printf("%s\n", ai->ai_canonname); + } + } +} + + +static void +hosts_init(const char *optarg, void* userdata _U_) +{ + GString *error_string; + gchar **tokens; + gint opt_count; + + dump_v4 = FALSE; + dump_v6 = FALSE; + + if(strcmp(TAP_NAME, optarg)==0) { + /* No arguments; dump everything */ + dump_v4 = TRUE; + dump_v6 = TRUE; + } else { + tokens = g_strsplit(optarg,",", 0); + opt_count=0; + while (tokens[opt_count]) { + if (strcmp("ipv4", tokens[opt_count]) == 0) { + dump_v4 = TRUE; + } else if (strcmp("ipv6", tokens[opt_count]) == 0) { + dump_v6 = TRUE; + } else if (opt_count > 0) { + fprintf(stderr, "tshark: invalid \"-z " TAP_NAME "[,ipv4|ipv6]\" argument\n"); + exit(1); + } + opt_count++; + } + g_strfreev(tokens); + } + + error_string=register_tap_listener("frame", NULL, NULL, TL_REQUIRES_PROTO_TREE, + NULL, NULL, hosts_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + fprintf(stderr, "tshark: Couldn't register " TAP_NAME " tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + +void +register_tap_listener_hosts(void) +{ + register_stat_cmd_arg(TAP_NAME, hosts_init, NULL); +} + diff --git a/ui/cli/tap-httpstat.c b/ui/cli/tap-httpstat.c new file mode 100644 index 0000000000..fc12fcd9b2 --- /dev/null +++ b/ui/cli/tap-httpstat.c @@ -0,0 +1,329 @@ +/* tap-httpstat.c + * tap-httpstat 2003 Jean-Michel FAYARD + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <string.h> + +#include "epan/packet_info.h" +#include "epan/value_string.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/dissectors/packet-http.h> + + +/* 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; + + +static const value_string vals_status_code[] = { + { 100, "Continue" }, + { 101, "Switching Protocols" }, + { 199, "Informational - Others" }, + + { 200, "OK"}, + { 201, "Created"}, + { 202, "Accepted"}, + { 203, "Non-authoritative Information"}, + { 204, "No Content"}, + { 205, "Reset Content"}, + { 206, "Partial Content"}, + { 299, "Success - Others"}, /* used to keep track of others Success packets */ + + { 300, "Multiple Choices"}, + { 301, "Moved Permanently"}, + { 302, "Moved Temporarily"}, + { 303, "See Other"}, + { 304, "Not Modified"}, + { 305, "Use Proxy"}, + { 399, "Redirection - Others"}, + + { 400, "Bad Request"}, + { 401, "Unauthorized"}, + { 402, "Payment Required"}, + { 403, "Forbidden"}, + { 404, "Not Found"}, + { 405, "Method Not Allowed"}, + { 406, "Not Acceptable"}, + { 407, "Proxy Authentication Required"}, + { 408, "Request Time-out"}, + { 409, "Conflict"}, + { 410, "Gone"}, + { 411, "Length Required"}, + { 412, "Precondition Failed"}, + { 413, "Request Entity Too Large"}, + { 414, "Request-URI Too Large"}, + { 415, "Unsupported Media Type"}, + { 499, "Client Error - Others"}, + + { 500, "Internal Server Error"}, + { 501, "Not Implemented"}, + { 502, "Bad Gateway"}, + { 503, "Service Unavailable"}, + { 504, "Gateway Time-out"}, + { 505, "HTTP Version not supported"}, + { 599, "Server Error - Others"}, + + { 0, NULL} +} ; + +/* insert some entries */ +static void +http_init_hash( httpstat_t *sp) +{ + int i; + + sp->hash_responses = g_hash_table_new( g_int_hash, g_int_equal); + + for (i=0 ; vals_status_code[i].strptr ; i++ ) + { + gint *key = g_malloc (sizeof(gint)); + http_response_code_t *sc = g_malloc (sizeof(http_response_code_t)); + *key = vals_status_code[i].value; + sc->packets=0; + sc->response_code = *key; + sc->name=vals_status_code[i].strptr; + sc->sp = sp; + g_hash_table_insert( sc->sp->hash_responses, key, sc); + } + sp->hash_requests = g_hash_table_new( g_str_hash, g_str_equal); +} +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) { + g_warning("No data available, key=%d\n", *key); + exit(EXIT_FAILURE); + } + if (data->packets==0) + return; + /* " HTTP %3d %-35s %9d packets", */ + 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=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 int +httpstat_packet(void *psp , packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri) +{ + const http_info_value_t *value=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) { + guint *key=g_malloc( sizeof(guint) ); + http_response_code_t *sc; + + *key=value->response_code; + sc = 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) + */ + int i = value->response_code; + if ((i<100) || (i>=600)) { + return 0; + } + 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 = g_hash_table_lookup( + sp->hash_responses, + key); + if (sc==NULL) + return 0; + } + sc->packets++; + } + else if (value->request_method){ + http_request_methode_t *sc; + + sc = g_hash_table_lookup( + sp->hash_requests, + value->request_method); + if (sc==NULL){ + sc=g_malloc( sizeof(http_request_methode_t) ); + 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 0; + } + return 1; +} + + +static void +httpstat_draw(void *psp ) +{ + httpstat_t *sp=psp; + printf("\n"); + printf("===================================================================\n"); + if (! sp->filter[0]) + printf("HTTP Statistics\n"); + else + printf("HTTP Statistics with filter %s\n", sp->filter); + + printf( "* HTTP Status Codes in reply packets\n"); + g_hash_table_foreach( sp->hash_responses, (GHFunc)http_draw_hash_responses, + " HTTP %3d %s\n"); + printf("* List of HTTP Request methods\n"); + g_hash_table_foreach( sp->hash_requests, (GHFunc)http_draw_hash_requests, + " %9s %d \n"); + printf("===================================================================\n"); +} + + + +/* When called, this function will create a new instance of gtk_httpstat. + */ +static void +gtk_httpstat_init(const char *optarg,void* userdata _U_) +{ + httpstat_t *sp; + const char *filter=NULL; + GString *error_string; + + if (!strncmp (optarg, "http,stat,", 10)){ + filter=optarg+10; + } else { + filter=NULL; + } + + sp = g_malloc( sizeof(httpstat_t) ); + if(filter){ + sp->filter=g_strdup(filter); + } else { + sp->filter=NULL; + } + /*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); + if (error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(sp->filter); + g_free(sp); + fprintf (stderr, "tshark: Couldn't register http,stat tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } + + http_init_hash(sp); +} + +void +register_tap_listener_gtkhttpstat(void) +{ + register_stat_cmd_arg("http,stat,", gtk_httpstat_init,NULL); +} diff --git a/ui/cli/tap-icmpstat.c b/ui/cli/tap-icmpstat.c new file mode 100644 index 0000000000..b8abbb2a5c --- /dev/null +++ b/ui/cli/tap-icmpstat.c @@ -0,0 +1,319 @@ +/* tap-icmpstat.c + * icmpstat 2011 Christopher Maynard + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/dissectors/packet-icmp.h> +#include <math.h> + +/* 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 = 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 = *(double *)a; + bd = *(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 : + * 0: no updates, no need to call (*draw) later + * !0: state has changed, call (*draw) sometime later + */ +static int +icmpstat_packet(void *tapdata, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *data) +{ + icmpstat_t *icmpstat = tapdata; + const icmp_transaction_t *trans = data; + double *rt; + + if (trans == NULL) + return 0; + + if (trans->resp_frame) { + rt = g_malloc(sizeof(double)); + if (rt == NULL) + return 0; + *rt = trans->resp_time; + icmpstat->rt_list = g_slist_insert_sorted(icmpstat->rt_list, rt, compare_doubles); + icmpstat->num_resps++; + if (icmpstat->min_msecs > trans->resp_time) { + icmpstat->min_frame = trans->resp_frame; + icmpstat->min_msecs = trans->resp_time; + } + if (icmpstat->max_msecs < trans->resp_time) { + icmpstat->max_frame = trans->resp_frame; + icmpstat->max_msecs = trans->resp_time; + } + icmpstat->tot_msecs += trans->resp_time; + } else if (trans->rqst_frame) + icmpstat->num_rqsts++; + else + return 0; + + return 1; +} + + +/* + * Compute the mean, median and standard deviation. + */ +static void compute_stats(icmpstat_t *icmpstat, double *mean, double *med, double *sdev) +{ + GSList *slist = icmpstat->rt_list; + double diff; + double sq_diff_sum = 0.0; + + 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 = 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 *optarg, void* userdata _U_) +{ + icmpstat_t *icmpstat; + const char *filter = NULL; + GString *error_string; + + if (strstr(optarg, "icmp,srt,")) + filter = optarg + strlen("icmp,srt,"); + + icmpstat = g_try_malloc(sizeof(icmpstat_t)); + if (icmpstat == NULL) { + fprintf(stderr, "tshark: g_try_malloc() fatal error.\n"); + exit(1); + } + memset(icmpstat, 0, sizeof(icmpstat_t)); + icmpstat->min_msecs = 1.0 * G_MAXUINT; + + if (filter) + 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); + if (error_string) { + /* error, we failed to attach to the tap. clean up */ + if (icmpstat->filter) + g_free(icmpstat->filter); + g_free(icmpstat); + + fprintf(stderr, "tshark: Couldn't register icmp,srt tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_icmpstat(void) +{ + register_stat_cmd_arg("icmp,srt", icmpstat_init, NULL); +} + diff --git a/ui/cli/tap-icmpv6stat.c b/ui/cli/tap-icmpv6stat.c new file mode 100644 index 0000000000..12659b26a7 --- /dev/null +++ b/ui/cli/tap-icmpv6stat.c @@ -0,0 +1,320 @@ +/* tap-icmpv6stat.c + * icmpv6stat 2011 Christopher Maynard + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/dissectors/packet-icmp.h> +#include <math.h> + +/* 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 = 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 = *(double *)a; + bd = *(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 : + * 0: no updates, no need to call (*draw) later + * !0: state has changed, call (*draw) sometime later + */ +static int +icmpv6stat_packet(void *tapdata, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *data) +{ + icmpv6stat_t *icmpv6stat = tapdata; + const icmp_transaction_t *trans = data; + double *rt; + + if (trans == NULL) + return 0; + + if (trans->resp_frame) { + rt = g_malloc(sizeof(double)); + if (rt == NULL) + return 0; + *rt = trans->resp_time; + icmpv6stat->rt_list = g_slist_insert_sorted(icmpv6stat->rt_list, rt, compare_doubles); + icmpv6stat->num_resps++; + if (icmpv6stat->min_msecs > trans->resp_time) { + icmpv6stat->min_frame = trans->resp_frame; + icmpv6stat->min_msecs = trans->resp_time; + } + if (icmpv6stat->max_msecs < trans->resp_time) { + icmpv6stat->max_frame = trans->resp_frame; + icmpv6stat->max_msecs = trans->resp_time; + } + icmpv6stat->tot_msecs += trans->resp_time; + } else if (trans->rqst_frame) + icmpv6stat->num_rqsts++; + else + return 0; + + return 1; +} + + +/* + * Compute the mean, median and standard deviation. + */ +static void compute_stats(icmpv6stat_t *icmpv6stat, double *mean, double *med, double *sdev) +{ + GSList *slist = icmpv6stat->rt_list; + double diff; + double sq_diff_sum = 0.0; + + 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 = 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 *optarg, void* userdata _U_) +{ + icmpv6stat_t *icmpv6stat; + const char *filter = NULL; + GString *error_string; + + if (strstr(optarg, "icmpv6,srt,")) + filter = optarg + strlen("icmpv6,srt,"); + + icmpv6stat = g_try_malloc(sizeof(icmpv6stat_t)); + if (icmpv6stat == NULL) { + fprintf(stderr, "tshark: g_try_malloc() fatal error.\n"); + exit(1); + } + memset(icmpv6stat, 0, sizeof(icmpv6stat_t)); + icmpv6stat->min_msecs = 1.0 * G_MAXUINT; + + if (filter) + 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); + if (error_string) { + /* error, we failed to attach to the tap. clean up */ + if (icmpv6stat->filter) + g_free(icmpv6stat->filter); + g_free(icmpv6stat); + + fprintf(stderr, "tshark: Couldn't register icmpv6,srt tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_icmpv6stat(void) +{ + register_stat_cmd_arg("icmpv6,srt", icmpv6stat_init, NULL); +} + diff --git a/ui/cli/tap-iostat.c b/ui/cli/tap-iostat.c new file mode 100644 index 0000000000..1104edd6d6 --- /dev/null +++ b/ui/cli/tap-iostat.c @@ -0,0 +1,984 @@ +/* tap-iostat.c + * iostat 2002 Ronnie Sahlberg + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/epan_dissect.h" +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/strutil.h> + + +typedef struct _io_stat_t { + gint64 interval; /* unit is us */ + guint32 num_items; + struct _io_stat_item_t *items; + const char **filters; +} io_stat_t; + +#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 + +typedef struct _io_stat_item_t { + io_stat_t *parent; + struct _io_stat_item_t *next; + struct _io_stat_item_t *prev; + gint64 time; /* unit is us since start of capture */ + int calc_type; + int hf_index; + guint64 frames; + guint64 num; + guint64 counter; + gfloat float_counter; + gdouble double_counter; +} io_stat_item_t; + +#define NANOSECS_PER_SEC 1000000000 + +static int +iostat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_) +{ + io_stat_item_t *mit = arg; + io_stat_item_t *it; + gint64 current_time, ct; + GPtrArray *gp; + guint i; + + current_time = (gint64)(pinfo->fd->rel_ts.secs*1000000) + (pinfo->fd->rel_ts.nsecs+500)/1000; + + /* the prev item before the main one is always the last interval we saw packets for */ + it=mit->prev; + + /* XXX for the time being, just ignore all frames that are in the past. + should be fixed in the future but hopefully it is uncommon */ + if(current_time<it->time){ + return FALSE; + } + + /* we have moved into a new interval, we need to create a new struct */ + ct = current_time; + while(ct >= (it->time + mit->parent->interval)){ + it->next=g_malloc(sizeof(io_stat_item_t)); + it->next->prev=it; + it->next->next=NULL; + it=it->next; + mit->prev=it; + + it->time = it->prev->time + mit->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 will now give us the current structure to use to store the data in */ + it->frames++; + + switch(it->calc_type){ + case CALC_TYPE_BYTES: + case CALC_TYPE_FRAMES: + 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; + nstime_t *new_time; + + 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_UINT64: + it->counter+=fvalue_get_integer64(&((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_INT64: + it->counter+=(gint64)fvalue_get_integer64(&((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(&((field_info *)gp->pdata[i])->value); + val=(guint64)(new_time->secs) * NANOSECS_PER_SEC + new_time->nsecs; + it->counter += val; + break; + } + } + } + break; + case CALC_TYPE_MIN: + gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index); + if(gp){ + int type; + guint64 val; + gfloat float_val; + gdouble double_val; + nstime_t *new_time; + + type=proto_registrar_get_ftype(it->hf_index); + for(i=0;i<gp->len;i++){ + switch(type){ + 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)){ + it->counter=val; + } else if(val<it->counter){ + it->counter=val; + } + break; + case FT_UINT64: + val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value); + if((it->frames==1)&&(i==0)){ + it->counter=val; + } else 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((it->frames==1)&&(i==0)){ + it->counter=val; + } else if((gint32)val<(gint32)(it->counter)){ + it->counter=val; + } + break; + case FT_INT64: + val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value); + if((it->frames==1)&&(i==0)){ + it->counter=val; + } else 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((it->frames==1)&&(i==0)){ + it->float_counter=float_val; + } else 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((it->frames==1)&&(i==0)){ + it->double_counter=double_val; + } else if(double_val<it->double_counter){ + it->double_counter=double_val; + } + break; + case FT_RELATIVE_TIME: + new_time=fvalue_get(&((field_info *)gp->pdata[i])->value); + val=(guint64)(new_time->secs) * NANOSECS_PER_SEC + new_time->nsecs; + if((it->frames==1)&&(i==0)){ + it->counter=val; + } else if(val<it->counter){ + it->counter=val; + } + break; + } + } + } + break; + case CALC_TYPE_MAX: + gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index); + if(gp){ + int type; + guint64 val; + gfloat float_val; + gdouble double_val; + nstime_t *new_time; + + type=proto_registrar_get_ftype(it->hf_index); + for(i=0;i<gp->len;i++){ + switch(type){ + 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)){ + it->counter=val; + } else if(val>it->counter){ + it->counter=val; + } + break; + case FT_UINT64: + val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value); + if((it->frames==1)&&(i==0)){ + it->counter=val; + } else 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((it->frames==1)&&(i==0)){ + it->counter=val; + } else if((gint32)val>(gint32)(it->counter)){ + it->counter=val; + } + break; + case FT_INT64: + val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value); + if((it->frames==1)&&(i==0)){ + it->counter=val; + } else 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((it->frames==1)&&(i==0)){ + it->float_counter=float_val; + } else 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((it->frames==1)&&(i==0)){ + it->double_counter=double_val; + } else if(double_val>it->double_counter){ + it->double_counter=double_val; + } + break; + case FT_RELATIVE_TIME: + new_time=fvalue_get(&((field_info *)gp->pdata[i])->value); + val=(guint64)(new_time->secs) * NANOSECS_PER_SEC + new_time->nsecs; + if((it->frames==1)&&(i==0)){ + it->counter=val; + } else if(val>it->counter){ + it->counter=val; + } + break; + } + } + } + break; + case CALC_TYPE_AVG: + gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index); + if(gp){ + int type; + guint64 val; + nstime_t *new_time; + + type=proto_registrar_get_ftype(it->hf_index); + for(i=0;i<gp->len;i++){ + it->num++; + switch(type){ + 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_UINT64: + case FT_INT64: + val=fvalue_get_integer64(&((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_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(&((field_info *)gp->pdata[i])->value); + val=(guint64)(new_time->secs) * NANOSECS_PER_SEC + new_time->nsecs; + it->counter+=val; + break; + } + } + } + break; + case CALC_TYPE_LOAD: + gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index); + if(gp){ + int type; + + type=proto_registrar_get_ftype(it->hf_index); + if (type != FT_RELATIVE_TIME) { + fprintf(stderr, + "\ntshark: LOAD() is only supported for relative-time fiels such as smb.time\n" + ); + exit(10); + } + for(i=0;i<gp->len;i++){ + guint64 val; + int tival; + nstime_t *new_time; + io_stat_item_t *pit; + + new_time=fvalue_get(&((field_info *)gp->pdata[i])->value); + val=(guint64)(new_time->secs)*1000000 + new_time->nsecs/1000; + tival = (int)(val % mit->parent->interval); + it->counter += tival; + val -= tival; + pit = it->prev; + while (val > 0) { + if (val < (guint64)mit->parent->interval) { + pit->counter += val; + val = 0; + break; + } + + pit->counter += mit->parent->interval; + val -= mit->parent->interval; + pit = pit->prev; + + } + } + } + break; + } + + return TRUE; +} + +static void +iostat_draw(void *arg) +{ + io_stat_item_t *mit = arg; + io_stat_t *iot; + io_stat_item_t **items; + guint64 *frames; + guint64 *counters; + gfloat *float_counters; + gdouble *double_counters; + guint64 *num; + guint32 i; + guint32 borderLen=68; + gboolean more_items; + gint64 t; + + iot=mit->parent; + + printf("\n"); + + /* Display the table border */ + for(i=0;i<iot->num_items;i++){ + if(iot->items[i].calc_type==CALC_TYPE_FRAMES_AND_BYTES) + borderLen+=17; + } + if(iot->interval!=G_MAXINT32) + borderLen+=8; + if(iot->num_items>3) + borderLen+=(iot->num_items-3)*17; + for(i=0;i<borderLen;i++){ + printf("="); + } + printf("\n"); + + + printf("IO Statistics\n"); + if(iot->interval!=G_MAXINT32) + printf("Interval: %3" G_GINT64_MODIFIER "u.%06" G_GINT64_MODIFIER "u secs\n", + iot->interval/1000000, iot->interval%1000000); + + for(i=0;i<iot->num_items;i++){ + printf("Column #%u: %s\n",i,iot->filters[i]?iot->filters[i]:""); + } + if(iot->interval==G_MAXINT32){ + printf(" |"); + } else { + printf(" |"); + } + for(i=0;i<iot->num_items;i++){ + if(iot->items[i].calc_type==CALC_TYPE_FRAMES_AND_BYTES){ + printf(" Column #%-2u |",i); + } else { + printf(" Column #%-2u |",i); + } + } + printf("\n"); + + if(iot->interval==G_MAXINT32) { + printf("Time |"); + } else { + printf("Time |"); + } + for(i=0;i<iot->num_items;i++){ + switch(iot->items[i].calc_type){ + case CALC_TYPE_FRAMES: + printf(" FRAMES |"); + break; + case CALC_TYPE_BYTES: + printf(" BYTES |"); + break; + case CALC_TYPE_FRAMES_AND_BYTES: + printf(" Frames | Bytes |"); + break; + case CALC_TYPE_COUNT: + printf(" COUNT |"); + break; + case CALC_TYPE_SUM: + printf(" SUM |"); + break; + case CALC_TYPE_MIN: + printf(" MIN |"); + break; + case CALC_TYPE_MAX: + printf(" MAX |"); + break; + case CALC_TYPE_AVG: + printf(" AVG |"); + break; + case CALC_TYPE_LOAD: + printf(" LOAD |"); + break; + } + } + printf("\n"); + + items=g_malloc(sizeof(io_stat_item_t *)*iot->num_items); + frames=g_malloc(sizeof(guint64)*iot->num_items); + counters=g_malloc(sizeof(guint64)*iot->num_items); + float_counters=g_malloc(sizeof(gfloat)*iot->num_items); + double_counters=g_malloc(sizeof(gdouble)*iot->num_items); + num=g_malloc(sizeof(guint64)*iot->num_items); + /* preset all items at the first interval */ + for(i=0;i<iot->num_items;i++){ + items[i]=&iot->items[i]; + } + + /* loop the items until we run out of them all */ + t=0; + do { + more_items=FALSE; + for(i=0;i<iot->num_items;i++){ + frames[i]=0; + counters[i]=0; + float_counters[i]=0; + double_counters[i]=0; + num[i]=0; + } + for(i=0;i<iot->num_items;i++){ + if(items[i] && (t>=(items[i]->time+iot->interval))){ + items[i]=items[i]->next; + } + + if(items[i] && (t<(items[i]->time+iot->interval)) && (t>=items[i]->time) ){ + frames[i]=items[i]->frames; + counters[i]=items[i]->counter; + float_counters[i]=items[i]->float_counter; + double_counters[i]=items[i]->double_counter; + num[i]=items[i]->num; + } + + if(items[i]){ + more_items=TRUE; + } + } + + if(more_items){ + if(iot->interval==G_MAXINT32) { + printf("000.000- "); + } else { + printf("%04u.%06u-%04u.%06u ", + (int)(t/1000000),(int)(t%1000000), + (int)((t+iot->interval)/1000000), + (int)((t+iot->interval)%1000000)); + } + for(i=0;i<iot->num_items;i++){ + switch(iot->items[i].calc_type){ + case CALC_TYPE_FRAMES: + printf(" %15" G_GINT64_MODIFIER "u ", frames[i]); + break; + case CALC_TYPE_BYTES: + printf(" %15" G_GINT64_MODIFIER "u ", counters[i]); + break; + case CALC_TYPE_FRAMES_AND_BYTES: + printf(" %15" G_GINT64_MODIFIER "u %15" G_GINT64_MODIFIER "u ", frames[i], counters[i]); + break; + case CALC_TYPE_COUNT: + printf(" %15" G_GINT64_MODIFIER "u ", counters[i]); + break; + case CALC_TYPE_SUM: + switch(proto_registrar_get_ftype(iot->items[i].hf_index)){ + 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: + printf(" %15" G_GINT64_MODIFIER "u ", counters[i]); + break; + case FT_FLOAT: + printf(" %f ", float_counters[i]); + break; + case FT_DOUBLE: + printf(" %f ", double_counters[i]); + break; + case FT_RELATIVE_TIME: + counters[i] = (counters[i]+500)/1000; + printf(" %8u.%06u ", + (int)(counters[i]/1000000), (int)(counters[i]%1000000)); + break; + } + break; + case CALC_TYPE_MIN: + switch(proto_registrar_get_ftype(iot->items[i].hf_index)){ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_UINT64: + printf(" %15" G_GINT64_MODIFIER "u ", counters[i]); + break; + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_INT64: + printf(" %15" G_GINT64_MODIFIER "d ", counters[i]); + break; + case FT_FLOAT: + printf(" %f ", float_counters[i]); + break; + case FT_DOUBLE: + printf(" %f ", double_counters[i]); + break; + case FT_RELATIVE_TIME: + counters[i]=(counters[i]+500)/1000; + printf(" %8u.%06u ", + (int)(counters[i]/1000000), (int)(counters[i]%1000000)); + break; + } + break; + case CALC_TYPE_MAX: + switch(proto_registrar_get_ftype(iot->items[i].hf_index)){ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_UINT64: + printf(" %15u ", (int)(counters[i])); + break; + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_INT64: + printf(" %15" G_GINT64_MODIFIER "d ", counters[i]); + break; + case FT_FLOAT: + printf(" %f ", float_counters[i]); + break; + case FT_DOUBLE: + printf(" %f ", double_counters[i]); + break; + case FT_RELATIVE_TIME: + counters[i]=(counters[i]+500)/1000; + printf(" %8u.%06u ", + (int)(counters[i]/1000000), (int)(counters[i]%1000000)); + break; + } + break; + case CALC_TYPE_AVG: + if(num[i]==0){ + num[i]=1; + } + switch(proto_registrar_get_ftype(iot->items[i].hf_index)){ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_UINT64: + printf(" %15" G_GINT64_MODIFIER "u ", counters[i]/num[i]); + break; + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_INT64: + printf(" %15" G_GINT64_MODIFIER "d ", counters[i]/num[i]); + break; + case FT_FLOAT: + printf(" %f ", float_counters[i]/num[i]); + break; + case FT_DOUBLE: + printf(" %f ", double_counters[i]/num[i]); + break; + case FT_RELATIVE_TIME: + counters[i]=((counters[i]/num[i])+500)/1000; + printf(" %8u.%06u ", + (int)(counters[i]/1000000), (int)(counters[i]%1000000)); + break; + } + break; + + case CALC_TYPE_LOAD: + switch(proto_registrar_get_ftype(iot->items[i].hf_index)){ + case FT_RELATIVE_TIME: + printf("%8u.%06u ", + (int)(counters[i]/iot->interval), (int)((counters[i]%iot->interval)*1000000/iot->interval)); + break; + } + break; + + } + } + printf("\n"); + } + + t+=iot->interval; + } while(more_items); + + for(i=0;i<borderLen;i++){ + printf("="); + } + printf("\n"); + + g_free(items); + g_free(frames); + g_free(counters); + g_free(float_counters); + g_free(double_counters); + g_free(num); +} + + +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 }, + { "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 } +}; + +static void +register_io_tap(io_stat_t *io, 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].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; + 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 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=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=""; + } + } + 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); + 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 *optarg, void* userdata _U_) +{ + gdouble interval_float; + gint64 interval; + int idx=0; + io_stat_t *io; + const char *filter=NULL; + + if(sscanf(optarg,"io,stat,%lf,%n",&interval_float,&idx)==1){ + if(idx){ + if(*(optarg+idx)==',') + filter=optarg+idx+1; + else + filter=optarg+idx; + } else { + filter=NULL; + } + } else { + fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>]\" argument\n"); + exit(1); + } + + /* if interval is 0, calculate statistics over the whole file + * by setting the interval to G_MAXINT32 + */ + if(interval_float==0) { + interval=G_MAXINT32; + } else { + /* make interval be number of us rounded to the nearest integer*/ + interval=(gint64)(interval_float*1000000.0+0.5); + } + + if(interval<1){ + fprintf(stderr, + "\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n"); + exit(10); + } + + io=g_malloc(sizeof(io_stat_t)); + io->interval=interval; + if((!filter)||(filter[0]==0)){ + io->num_items=1; + io->items=g_malloc(sizeof(io_stat_item_t)*io->num_items); + io->filters=g_malloc(sizeof(char *)*io->num_items); + + register_io_tap(io, 0, NULL); + } else { + const char *str,*pos; + char *tmp; + int i; + /* find how many ',' separated filters we have */ + str=filter; + io->num_items=1; + while((str=strchr(str,','))){ + io->num_items++; + str++; + } + + io->items=g_malloc(sizeof(io_stat_item_t)*io->num_items); + io->filters=g_malloc(sizeof(char *)*io->num_items); + + /* for each filter, register a tap listener */ + i=0; + str=filter; + do{ + pos=strchr(str,','); + if(pos==str){ + register_io_tap(io, i, NULL); + } else if(pos==NULL) { + tmp=g_strdup(str); + register_io_tap(io, i, tmp); + } else { + tmp=g_malloc((pos-str)+1); + g_strlcpy(tmp,str,(pos-str)+1); + register_io_tap(io, i, tmp); + } + str=pos+1; + i++; + } while(pos); + } +} + +void +register_tap_listener_iostat(void) +{ + register_stat_cmd_arg("io,stat,", iostat_init, NULL); +} diff --git a/ui/cli/tap-iousers.c b/ui/cli/tap-iousers.c new file mode 100644 index 0000000000..175ec28155 --- /dev/null +++ b/ui/cli/tap-iousers.c @@ -0,0 +1,780 @@ +/* tap-iousers.c + * iostat 2003 Ronnie Sahlberg + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include <epan/packet_info.h> +#include <epan/packet.h> +#include <epan/addr_resolv.h> +#include <epan/tap.h> +#include <epan/conversation.h> +#include <epan/stat_cmd_args.h> +#include <epan/dissectors/packet-ip.h> +#include <epan/dissectors/packet-ipv6.h> +#include <epan/dissectors/packet-ipx.h> +#include <epan/dissectors/packet-tcp.h> +#include <epan/dissectors/packet-udp.h> +#include <epan/dissectors/packet-eth.h> +#include <epan/dissectors/packet-sctp.h> +#include <epan/dissectors/packet-tr.h> +#include <epan/dissectors/packet-scsi.h> +#include <epan/dissectors/packet-fc.h> +#include <epan/dissectors/packet-fddi.h> + +typedef struct _io_users_t { + const char *type; + char *filter; + struct _io_users_item_t *items; +} io_users_t; + +typedef struct _io_users_item_t { + struct _io_users_item_t *next; + char *name1; + char *name2; + address addr1; + address addr2; + guint32 frames1; + guint32 frames2; + guint64 bytes1; + guint64 bytes2; +} io_users_item_t; + + +static int +iousers_udpip_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vudph) +{ + io_users_t *iu=arg; + const e_udphdr *udph=vudph; + char name1[256],name2[256]; + io_users_item_t *iui; + int direction=0; + + if(udph->uh_sport>udph->uh_dport){ + direction=0; + g_snprintf(name1,256,"%s:%s",ep_address_to_str(&udph->ip_src),get_udp_port(udph->uh_sport)); + g_snprintf(name2,256,"%s:%s",ep_address_to_str(&udph->ip_dst),get_udp_port(udph->uh_dport)); + } else if(udph->uh_sport<udph->uh_dport){ + direction=1; + g_snprintf(name2,256,"%s:%s",ep_address_to_str(&udph->ip_src),get_udp_port(udph->uh_sport)); + g_snprintf(name1,256,"%s:%s",ep_address_to_str(&udph->ip_dst),get_udp_port(udph->uh_dport)); + } else if(CMP_ADDRESS(&udph->ip_src, &udph->ip_dst)>0){ + direction=0; + g_snprintf(name1,256,"%s:%s",ep_address_to_str(&udph->ip_src),get_udp_port(udph->uh_sport)); + g_snprintf(name2,256,"%s:%s",ep_address_to_str(&udph->ip_dst),get_udp_port(udph->uh_dport)); + } else { + direction=1; + g_snprintf(name2,256,"%s:%s",ep_address_to_str(&udph->ip_src),get_udp_port(udph->uh_sport)); + g_snprintf(name1,256,"%s:%s",ep_address_to_str(&udph->ip_dst),get_udp_port(udph->uh_dport)); + } + + for(iui=iu->items;iui;iui=iui->next){ + if((!strcmp(iui->name1, name1)) + && (!strcmp(iui->name2, name2)) ){ + break; + } + } + + if(!iui){ + iui=g_malloc(sizeof(io_users_item_t)); + iui->next=iu->items; + iu->items=iui; +/* iui->addr1=NULL;*/ + iui->name1=g_strdup(name1); +/* iui->addr2=NULL;*/ + iui->name2=g_strdup(name2); + iui->frames1=0; + iui->frames2=0; + iui->bytes1=0; + iui->bytes2=0; + } + + if(direction){ + iui->frames1++; + iui->bytes1+=pinfo->fd->pkt_len; + } else { + iui->frames2++; + iui->bytes2+=pinfo->fd->pkt_len; + } + + return 1; +} + + +static int +iousers_sctp_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vsctp) +{ + io_users_t *iu=arg; + const struct _sctp_info* sctph = vsctp; + char name1[256],name2[256], s_sport[10], s_dport[10]; + io_users_item_t *iui; + int direction=0; + + g_snprintf(s_sport, sizeof s_sport, "%d",sctph->sport); + g_snprintf(s_dport, sizeof s_dport, "%d",sctph->dport); + + if(sctph->sport > sctph->dport) { + direction=0; + g_snprintf(name1,256,"%s:%s",ep_address_to_str(&sctph->ip_src),s_sport); + g_snprintf(name2,256,"%s:%s",ep_address_to_str(&sctph->ip_dst),s_dport); + } else if(sctph->sport < sctph->dport) { + direction=1; + g_snprintf(name1,256,"%s:%s",ep_address_to_str(&sctph->ip_src),s_sport); + g_snprintf(name2,256,"%s:%s",ep_address_to_str(&sctph->ip_dst),s_dport); + } else { + direction=0; + g_snprintf(name1,256,"%s:%s",ep_address_to_str(&sctph->ip_src),s_sport); + g_snprintf(name2,256,"%s:%s",ep_address_to_str(&sctph->ip_dst),s_dport); + } + + for(iui=iu->items;iui;iui=iui->next){ + if((!strcmp(iui->name1, name1)) + && (!strcmp(iui->name2, name2)) ){ + break; + } + } + + if(!iui){ + iui=g_malloc(sizeof(io_users_item_t)); + iui->next=iu->items; + iu->items=iui; +/* iui->addr1=NULL;*/ + iui->name1=g_strdup(name1); +/* iui->addr2=NULL;*/ + iui->name2=g_strdup(name2); + iui->frames1=0; + iui->frames2=0; + iui->bytes1=0; + iui->bytes2=0; + } + + if(direction){ + iui->frames1++; + iui->bytes1+=pinfo->fd->pkt_len; + } else { + iui->frames2++; + iui->bytes2+=pinfo->fd->pkt_len; + } + + return 1; +} + + +static int +iousers_tcpip_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vtcph) +{ + io_users_t *iu=arg; + const struct tcpheader *tcph=vtcph; + char name1[256],name2[256]; + io_users_item_t *iui; + int direction=0; + + if(tcph->th_sport>tcph->th_dport){ + direction=0; + g_snprintf(name1,256,"%s:%s",ep_address_to_str(&tcph->ip_src),get_tcp_port(tcph->th_sport)); + g_snprintf(name2,256,"%s:%s",ep_address_to_str(&tcph->ip_dst),get_tcp_port(tcph->th_dport)); + } else if(tcph->th_sport<tcph->th_dport){ + direction=1; + g_snprintf(name2,256,"%s:%s",ep_address_to_str(&tcph->ip_src),get_tcp_port(tcph->th_sport)); + g_snprintf(name1,256,"%s:%s",ep_address_to_str(&tcph->ip_dst),get_tcp_port(tcph->th_dport)); + } else if(CMP_ADDRESS(&tcph->ip_src, &tcph->ip_dst)>0){ + direction=0; + g_snprintf(name1,256,"%s:%s",ep_address_to_str(&tcph->ip_src),get_tcp_port(tcph->th_sport)); + g_snprintf(name2,256,"%s:%s",ep_address_to_str(&tcph->ip_dst),get_tcp_port(tcph->th_dport)); + } else { + direction=1; + g_snprintf(name2,256,"%s:%s",ep_address_to_str(&tcph->ip_src),get_tcp_port(tcph->th_sport)); + g_snprintf(name1,256,"%s:%s",ep_address_to_str(&tcph->ip_dst),get_tcp_port(tcph->th_dport)); + } + + for(iui=iu->items;iui;iui=iui->next){ + if((!strcmp(iui->name1, name1)) + && (!strcmp(iui->name2, name2)) ){ + break; + } + } + + if(!iui){ + iui=g_malloc(sizeof(io_users_item_t)); + iui->next=iu->items; + iu->items=iui; +/* iui->addr1=NULL;*/ + iui->name1=g_strdup(name1); +/* iui->addr2=NULL;*/ + iui->name2=g_strdup(name2); + iui->frames1=0; + iui->frames2=0; + iui->bytes1=0; + iui->bytes2=0; + } + + if(direction){ + iui->frames1++; + iui->bytes1+=pinfo->fd->pkt_len; + } else { + iui->frames2++; + iui->bytes2+=pinfo->fd->pkt_len; + } + + return 1; +} + + +static int +iousers_ip_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip) +{ + io_users_t *iu=arg; + const ws_ip *iph=vip; + const address *addr1, *addr2; + io_users_item_t *iui; + + if(CMP_ADDRESS(&iph->ip_src, &iph->ip_dst)>0){ + addr1=&iph->ip_src; + addr2=&iph->ip_dst; + } else { + addr2=&iph->ip_src; + addr1=&iph->ip_dst; + } + + for(iui=iu->items;iui;iui=iui->next){ + if((!CMP_ADDRESS(&iui->addr1, addr1)) + &&(!CMP_ADDRESS(&iui->addr2, addr2)) ){ + break; + } + } + + if(!iui){ + iui=g_malloc(sizeof(io_users_item_t)); + iui->next=iu->items; + iu->items=iui; + COPY_ADDRESS(&iui->addr1, addr1); + iui->name1=g_strdup(ep_address_to_str(addr1)); + COPY_ADDRESS(&iui->addr2, addr2); + iui->name2=g_strdup(ep_address_to_str(addr2)); + iui->frames1=0; + iui->frames2=0; + iui->bytes1=0; + iui->bytes2=0; + } + + if(!CMP_ADDRESS(&iph->ip_dst, &iui->addr1)){ + iui->frames1++; + iui->bytes1+=pinfo->fd->pkt_len; + } else { + iui->frames2++; + iui->bytes2+=pinfo->fd->pkt_len; + } + + return 1; +} + +static int +iousers_ipv6_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip) +{ + io_users_t *iu=arg; + const struct ip6_hdr *ip6h=vip; + address src, dst; + const address *addr1, *addr2; + io_users_item_t *iui; + + /* Addresses aren't implemented as 'address' type in struct ip6_hdr */ + src.type = dst.type = AT_IPv6; + src.len = dst.len = sizeof(struct e_in6_addr); + src.data = &ip6h->ip6_src; + dst.data = &ip6h->ip6_dst; + + if(CMP_ADDRESS(&src, &dst)>0){ + addr1=&src; + addr2=&dst; + } else { + addr2=&src; + addr1=&dst; + } + + for(iui=iu->items;iui;iui=iui->next){ + if((!CMP_ADDRESS(&iui->addr1, addr1)) + &&(!CMP_ADDRESS(&iui->addr2, addr2)) ){ + break; + } + } + + if(!iui){ + iui=g_malloc(sizeof(io_users_item_t)); + iui->next=iu->items; + iu->items=iui; + COPY_ADDRESS(&iui->addr1, addr1); + iui->name1=g_strdup(ep_address_to_str(addr1)); + COPY_ADDRESS(&iui->addr2, addr2); + iui->name2=g_strdup(ep_address_to_str(addr2)); + iui->frames1=0; + iui->frames2=0; + iui->bytes1=0; + iui->bytes2=0; + } + + if(!CMP_ADDRESS(&dst, &iui->addr1)){ + iui->frames1++; + iui->bytes1+=pinfo->fd->pkt_len; + } else { + iui->frames2++; + iui->bytes2+=pinfo->fd->pkt_len; + } + + return 1; +} + +static int +iousers_ipx_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vipx) +{ + io_users_t *iu=arg; + const ipxhdr_t *ipxh=vipx; + const address *addr1, *addr2; + io_users_item_t *iui; + + if(CMP_ADDRESS(&ipxh->ipx_src, &ipxh->ipx_dst)>0){ + addr1=&ipxh->ipx_src; + addr2=&ipxh->ipx_dst; + } else { + addr2=&ipxh->ipx_src; + addr1=&ipxh->ipx_dst; + } + + for(iui=iu->items;iui;iui=iui->next){ + if((!CMP_ADDRESS(&iui->addr1, addr1)) + &&(!CMP_ADDRESS(&iui->addr2, addr2)) ){ + break; + } + } + + if(!iui){ + iui=g_malloc(sizeof(io_users_item_t)); + iui->next=iu->items; + iu->items=iui; + COPY_ADDRESS(&iui->addr1, addr1); + iui->name1=g_strdup(ep_address_to_str(addr1)); + COPY_ADDRESS(&iui->addr2, addr2); + iui->name2=g_strdup(ep_address_to_str(addr2)); + iui->frames1=0; + iui->frames2=0; + iui->bytes1=0; + iui->bytes2=0; + } + + if(!CMP_ADDRESS(&ipxh->ipx_dst, &iui->addr1)){ + iui->frames1++; + iui->bytes1+=pinfo->fd->pkt_len; + } else { + iui->frames2++; + iui->bytes2+=pinfo->fd->pkt_len; + } + + return 1; +} + +static int +iousers_fc_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vfc) +{ + io_users_t *iu=arg; + const fc_hdr *fchdr=vfc; + const address *addr1, *addr2; + io_users_item_t *iui; + + if(CMP_ADDRESS(&fchdr->s_id, &fchdr->d_id)<0){ + addr1=&fchdr->s_id; + addr2=&fchdr->d_id; + } else { + addr2=&fchdr->s_id; + addr1=&fchdr->d_id; + } + + for(iui=iu->items;iui;iui=iui->next){ + if((!CMP_ADDRESS(&iui->addr1, addr1)) + &&(!CMP_ADDRESS(&iui->addr2, addr2)) ){ + break; + } + } + + if(!iui){ + iui=g_malloc(sizeof(io_users_item_t)); + iui->next=iu->items; + iu->items=iui; + COPY_ADDRESS(&iui->addr1, addr1); + iui->name1=g_strdup(ep_address_to_str(addr1)); + COPY_ADDRESS(&iui->addr2, addr2); + iui->name2=g_strdup(ep_address_to_str(addr2)); + iui->frames1=0; + iui->frames2=0; + iui->bytes1=0; + iui->bytes2=0; + } + + if(!CMP_ADDRESS(&fchdr->d_id,&iui->addr1)){ + iui->frames1++; + iui->bytes1+=pinfo->fd->pkt_len; + } else { + iui->frames2++; + iui->bytes2+=pinfo->fd->pkt_len; + } + + return 1; +} + +static int +iousers_eth_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *veth) +{ + io_users_t *iu=arg; + const eth_hdr *ehdr=veth; + const address *addr1, *addr2; + io_users_item_t *iui; + + if(CMP_ADDRESS(&ehdr->src, &ehdr->dst)<0){ + addr1=&ehdr->src; + addr2=&ehdr->dst; + } else { + addr2=&ehdr->src; + addr1=&ehdr->dst; + } + + for(iui=iu->items;iui;iui=iui->next){ + if((!CMP_ADDRESS(&iui->addr1, addr1)) + &&(!CMP_ADDRESS(&iui->addr2, addr2)) ){ + break; + } + } + + if(!iui){ + iui=g_malloc(sizeof(io_users_item_t)); + iui->next=iu->items; + iu->items=iui; + COPY_ADDRESS(&iui->addr1, addr1); + iui->name1=g_strdup(ep_address_to_str(addr1)); + COPY_ADDRESS(&iui->addr2, addr2); + iui->name2=g_strdup(ep_address_to_str(addr2)); + iui->frames1=0; + iui->frames2=0; + iui->bytes1=0; + iui->bytes2=0; + } + + if(!CMP_ADDRESS(&ehdr->dst,&iui->addr1)){ + iui->frames1++; + iui->bytes1+=pinfo->fd->pkt_len; + } else { + iui->frames2++; + iui->bytes2+=pinfo->fd->pkt_len; + } + + return 1; +} + +static int +iousers_fddi_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *veth) +{ + io_users_t *iu=arg; + const fddi_hdr *ehdr=veth; + const address *addr1, *addr2; + io_users_item_t *iui; + + if(CMP_ADDRESS(&ehdr->src, &ehdr->dst)<0){ + addr1=&ehdr->src; + addr2=&ehdr->dst; + } else { + addr2=&ehdr->src; + addr1=&ehdr->dst; + } + + for(iui=iu->items;iui;iui=iui->next){ + if((!CMP_ADDRESS(&iui->addr1, addr1)) + &&(!CMP_ADDRESS(&iui->addr2, addr2)) ){ + break; + } + } + + if(!iui){ + iui=g_malloc(sizeof(io_users_item_t)); + iui->next=iu->items; + iu->items=iui; + COPY_ADDRESS(&iui->addr1, addr1); + iui->name1=g_strdup(ep_address_to_str(addr1)); + COPY_ADDRESS(&iui->addr2, addr2); + iui->name2=g_strdup(ep_address_to_str(addr2)); + iui->frames1=0; + iui->frames2=0; + iui->bytes1=0; + iui->bytes2=0; + } + + if(!CMP_ADDRESS(&ehdr->dst,&iui->addr1)){ + iui->frames1++; + iui->bytes1+=pinfo->fd->pkt_len; + } else { + iui->frames2++; + iui->bytes2+=pinfo->fd->pkt_len; + } + + return 1; +} + +static int +iousers_tr_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vtr) +{ + io_users_t *iu=arg; + const tr_hdr *trhdr=vtr; + const address *addr1, *addr2; + io_users_item_t *iui; + + if(CMP_ADDRESS(&trhdr->src, &trhdr->dst)<0){ + addr1=&trhdr->src; + addr2=&trhdr->dst; + } else { + addr2=&trhdr->src; + addr1=&trhdr->dst; + } + + for(iui=iu->items;iui;iui=iui->next){ + if((!CMP_ADDRESS(&iui->addr1, addr1)) + &&(!CMP_ADDRESS(&iui->addr2, addr2)) ){ + break; + } + } + + if(!iui){ + iui=g_malloc(sizeof(io_users_item_t)); + iui->next=iu->items; + iu->items=iui; + COPY_ADDRESS(&iui->addr1, addr1); + iui->name1=g_strdup(ep_address_to_str(addr1)); + COPY_ADDRESS(&iui->addr2, addr2); + iui->name2=g_strdup(ep_address_to_str(addr2)); + iui->frames1=0; + iui->frames2=0; + iui->bytes1=0; + iui->bytes2=0; + } + + if(!CMP_ADDRESS(&trhdr->dst,&iui->addr1)){ + iui->frames1++; + iui->bytes1+=pinfo->fd->pkt_len; + } else { + iui->frames2++; + iui->bytes2+=pinfo->fd->pkt_len; + } + + return 1; +} + +static void +iousers_draw(void *arg) +{ + io_users_t *iu = arg; + io_users_item_t *iui; + guint32 last_frames, max_frames; + + printf("================================================================================\n"); + printf("%s Conversations\n",iu->type); + printf("Filter:%s\n",iu->filter?iu->filter:"<No Filter>"); + printf(" | <- | | -> | | Total |\n"); + printf(" | Frames Bytes | | Frames Bytes | | Frames Bytes |\n"); + max_frames=0xffffffff; + do { + last_frames=0; + for(iui=iu->items;iui;iui=iui->next){ + guint32 tot_frames; + tot_frames=iui->frames1+iui->frames2; + + if((tot_frames>last_frames) + &&(tot_frames<max_frames)){ + last_frames=tot_frames; + } + } + for(iui=iu->items;iui;iui=iui->next){ + guint32 tot_frames; + tot_frames=iui->frames1+iui->frames2; + + if(tot_frames==last_frames){ + printf("%-20s <-> %-20s %6d %9" G_GINT64_MODIFIER "d %6d %9" G_GINT64_MODIFIER "d %6d %9" G_GINT64_MODIFIER "d\n", + iui->name1, iui->name2, + iui->frames1, iui->bytes1, + iui->frames2, iui->bytes2, + iui->frames1+iui->frames2, + iui->bytes1+iui->bytes2 + ); + } + } + max_frames=last_frames; + } while(last_frames); + printf("================================================================================\n"); +} + +static void +iousers_init(const char *optarg, void* userdata _U_) +{ + const char *filter=NULL; + const char *tap_type, *tap_type_name; + tap_packet_cb packet_func; + io_users_t *iu=NULL; + GString *error_string; + + if(!strncmp(optarg,"conv,eth",8)){ + if(optarg[8]==','){ + filter=optarg+9; + } else { + filter=NULL; + } + tap_type="eth"; + tap_type_name="Ethernet"; + packet_func=iousers_eth_packet; + } else if(!strncmp(optarg,"conv,fc",7)){ + if(optarg[7]==','){ + filter=optarg+8; + } else { + filter=NULL; + } + tap_type="fc"; + tap_type_name="Fibre Channel"; + packet_func=iousers_fc_packet; + } else if(!strncmp(optarg,"conv,fddi",9)){ + if(optarg[9]==','){ + filter=optarg+10; + } else { + filter=NULL; + } + tap_type="fddi"; + tap_type_name="FDDI"; + packet_func=iousers_fddi_packet; + } else if(!strncmp(optarg,"conv,tcp",8)){ + if(optarg[8]==','){ + filter=optarg+9; + } else { + filter=NULL; + } + tap_type="tcp"; + tap_type_name="TCP"; + packet_func=iousers_tcpip_packet; + } else if(!strncmp(optarg,"conv,udp",8)){ + if(optarg[8]==','){ + filter=optarg+9; + } else { + filter=NULL; + } + tap_type="udp"; + tap_type_name="UDP"; + packet_func=iousers_udpip_packet; + } else if(!strncmp(optarg,"conv,tr",7)){ + if(optarg[7]==','){ + filter=optarg+8; + } else { + filter=NULL; + } + tap_type="tr"; + tap_type_name="Token Ring"; + packet_func=iousers_tr_packet; + } else if(!strncmp(optarg,"conv,ipx",8)){ + if(optarg[8]==','){ + filter=optarg+9; + } else { + filter=NULL; + } + tap_type="ipx"; + tap_type_name="IPX"; + packet_func=iousers_ipx_packet; + } else if(!strncmp(optarg,"conv,ipv6",9)){ + if(optarg[9]==','){ + filter=optarg+10; + } else { + filter=NULL; + } + tap_type="ipv6"; + tap_type_name="IPv6"; + packet_func=iousers_ipv6_packet; + } else if(!strncmp(optarg,"conv,ip",7)){ + if(optarg[7]==','){ + filter=optarg+8; + } else { + filter=NULL; + } + tap_type="ip"; + tap_type_name="IPv4"; + packet_func=iousers_ip_packet; + } else if(!strncmp(optarg,"conv,sctp",9)) { + if(optarg[9]==','){ + filter=optarg+10; + } else { + filter=NULL; + } + tap_type="sctp"; + tap_type_name="SCTP"; + packet_func=iousers_sctp_packet; + } else { + fprintf(stderr, "tshark: invalid \"-z conv,<type>[,<filter>]\" argument\n"); + fprintf(stderr," <type> must be one of\n"); + fprintf(stderr," \"eth\"\n"); + fprintf(stderr," \"fc\"\n"); + fprintf(stderr," \"fddi\"\n"); + fprintf(stderr," \"ip\"\n"); + fprintf(stderr," \"ipx\"\n"); + fprintf(stderr," \"sctp\"\n"); + fprintf(stderr," \"tcp\"\n"); + fprintf(stderr," \"tr\"\n"); + fprintf(stderr," \"udp\"\n"); + exit(1); + } + + + iu=g_malloc(sizeof(io_users_t)); + iu->items=NULL; + iu->type=tap_type_name; + if(filter){ + iu->filter=g_strdup(filter); + } else { + iu->filter=NULL; + } + + error_string=register_tap_listener(tap_type, iu, filter, 0, NULL, packet_func, iousers_draw); + if(error_string){ + if(iu->items){ + g_free(iu->items); + } + g_free(iu); + fprintf(stderr, "tshark: Couldn't register conversations tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } + +} + +void +register_tap_listener_iousers(void) +{ + register_stat_cmd_arg("conv,", iousers_init, NULL); +} diff --git a/ui/cli/tap-macltestat.c b/ui/cli/tap-macltestat.c new file mode 100644 index 0000000000..b91a55396f --- /dev/null +++ b/ui/cli/tap-macltestat.c @@ -0,0 +1,527 @@ +/* tap-macltestat.c + * Copyright 2011 Martin Mathieson + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#include <string.h> +#include <epan/packet.h> +#include <epan/packet_info.h> +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/dissectors/packet-mac-lte.h> + +/**********************************************/ +/* 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_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 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_total_bytes; + nstime_t DL_time_start; + nstime_t DL_time_stop; + 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 bch_frames; + guint32 bch_bytes; + guint32 pch_frames; + guint32 pch_bytes; + 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(struct mac_lte_tap_info *si, packet_info *pinfo _U_) +{ + mac_lte_ep_t* ep; + + if (!si) { + return NULL; + } + + if (!(ep = g_malloc(sizeof(mac_lte_ep_t)))) { + 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.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 int +mac_lte_stat_packet(void *phs, packet_info *pinfo, epan_dissect_t *edt _U_, + const void *phi) +{ + /* Get reference to stat window instance */ + mac_lte_stat_t *hs = (mac_lte_stat_t *)phs; + mac_lte_ep_t *tmp = NULL, *te = NULL; + + /* Cast tap info struct */ + struct mac_lte_tap_info *si = (struct mac_lte_tap_info *)phi; + + if (!hs) { + return 0; + } + + 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; + return 1; + case SI_RNTI: + case NO_RNTI: + hs->common_stats.bch_frames++; + hs->common_stats.bch_bytes += si->single_number_of_bytes; + return 1; + case RA_RNTI: + hs->common_stats.rar_frames++; + hs->common_stats.rar_entries += si->number_of_rars; + return 1; + case C_RNTI: + case SPS_RNTI: + /* Drop through for per-UE update */ + break; + + default: + /* Error */ + return 0; + } + + /* 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 0; + } + + /* 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 1; + } + + if (si->crcStatusValid && (si->crcStatus != crc_success)) { + te->stats.UL_CRC_errors++; + return 1; + } + + /* Update time range */ + if (te->stats.UL_frames == 0) { + te->stats.UL_time_start = si->time; + } + te->stats.UL_time_stop = si->time; + + te->stats.UL_frames++; + + te->stats.UL_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 { + te->stats.UL_total_bytes += si->single_number_of_bytes; + } + } + + /* Downlink */ + else { + if (si->isPHYRetx) { + te->stats.DL_retx_frames++; + return 1; + } + + 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 1; + } + + /* Update time range */ + if (te->stats.DL_frames == 0) { + te->stats.DL_time_start = si->time; + } + te->stats.DL_time_stop = si->time; + + te->stats.DL_frames++; + + if (si->isPredefinedData) { + te->stats.DL_total_bytes += si->single_number_of_bytes; + } + else { + te->stats.DL_total_bytes += si->single_number_of_bytes; + } + + } + + return 1; +} + + +/* Calculate and return a bandwidth figure, in Mbs */ +static float calculate_bw(nstime_t *start_time, nstime_t *stop_time, guint32 bytes) +{ + if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) { + float elapsed_ms = (((float)stop_time->secs - (float)start_time->secs) * 1000) + + (((float)stop_time->nsecs - (float)start_time->nsecs) / 1000000); + return ((bytes * 8) / elapsed_ms) / 1000; + } + else { + return 0.0; + } +} + + +/* 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("BCH Frames: %u ", hs->common_stats.bch_frames); + printf("BCH Bytes: %u ", hs->common_stats.bch_bytes); + printf("PCH Frames: %u ", hs->common_stats.pch_frames); + printf("PCH Bytes: %u ", hs->common_stats.pch_bytes); + 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 %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_total_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_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 *optarg, void *userdata _U_) +{ + mac_lte_stat_t *hs; + const char *filter = NULL; + GString *error_string; + + /* Check for a filter string */ + if (strncmp(optarg, "mac-lte,stat,", 13) == 0) { + /* Skip those characters from filter to display */ + filter = optarg + 13; + } + else { + /* No filter */ + filter = NULL; + } + + /* Create struct */ + hs = g_malloc(sizeof(mac_lte_stat_t)); + 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); + if (error_string) { + g_string_free(error_string, TRUE); + g_free(hs); + exit(1); + } +} + + +/* Register this tap listener (need void on own so line register function found) */ +void +register_tap_listener_mac_lte_stat(void) +{ + register_stat_cmd_arg("mac-lte,stat", mac_lte_stat_init, NULL); +} + diff --git a/ui/cli/tap-megacostat.c b/ui/cli/tap-megacostat.c new file mode 100644 index 0000000000..6e5186c688 --- /dev/null +++ b/ui/cli/tap-megacostat.c @@ -0,0 +1,148 @@ +/* tap-megacostat.c + * mgcpstat 2003 Lars Roland + * Copyright 2008, Ericsson AB + * By Balint Reczey <balint.reczey@ericsson.com> + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include "epan/value_string.h" +#include "epan/gcp.h" +#include "timestats.h" +#include <epan/prefs-int.h> + +#include "tap-megaco-common.h" + + + +static void +megacostat_draw(void *pms) +{ + megacostat_t *ms=(megacostat_t *)pms; + int i; + + /* printing results */ + printf("\n"); + printf("=====================================================================================================\n"); + printf("MEGACO Response Time Delay (RTD) Statistics:\n"); + printf("Filter for statistics: %s\n",ms->filter?ms->filter:""); + printf("Duplicate requests: %u\n",ms->req_dup_num); + printf("Duplicate responses: %u\n",ms->rsp_dup_num); + printf("Open requests: %u\n",ms->open_req_num); + printf("Discarded responses: %u\n",ms->disc_rsp_num); + printf(" Type | Messages | Min RTD | Max RTD | Avg RTD | Min in Frame | Max in Frame |\n"); + for(i=0;i<NUM_TIMESTATS;i++) { + if(ms->rtd[i].num) { + printf("%5s | %7u | %8.2f msec | %8.2f msec | %8.2f msec | %10u | %10u |\n", + val_to_str(i,megaco_message_type,"Other"),ms->rtd[i].num, + nstime_to_msec(&(ms->rtd[i].min)), nstime_to_msec(&(ms->rtd[i].max)), + get_average(&(ms->rtd[i].tot), ms->rtd[i].num), + ms->rtd[i].min_num, ms->rtd[i].max_num + ); + } + } + printf("=====================================================================================================\n"); +} + + +static void +megacostat_init(const char *optarg, void* userdata _U_) +{ + megacostat_t *ms; + int i; + GString *error_string; + pref_t *megaco_ctx_track,*h248_ctx_track; + + megaco_ctx_track = prefs_find_preference(prefs_find_module("megaco"),"ctx_info"); + h248_ctx_track = prefs_find_preference(prefs_find_module("h248"),"ctx_info"); + + if (!megaco_ctx_track || !h248_ctx_track) { + /* No such preferences */ + return; + } + + if (!*megaco_ctx_track->varp.boolp || !*h248_ctx_track->varp.boolp) { + printf("Track Context option at Protocols -> MEGACO and Protocols -> H248 preferences\n"); + printf("has to be set to true to enable measurement of service response times.\n"); + exit(1); + } + + ms=g_malloc(sizeof(megacostat_t)); + if(!strncmp(optarg,"megaco,rtd,",11)){ + ms->filter=g_strdup(optarg+11); + } else { + ms->filter=NULL; + } + + for(i=0;i<NUM_TIMESTATS;i++) { + ms->rtd[i].num=0; + ms->rtd[i].min_num=0; + ms->rtd[i].max_num=0; + ms->rtd[i].min.secs=0; + ms->rtd[i].min.nsecs=0; + ms->rtd[i].max.secs=0; + ms->rtd[i].max.nsecs=0; + ms->rtd[i].tot.secs=0; + ms->rtd[i].tot.nsecs=0; + } + + ms->open_req_num=0; + ms->disc_rsp_num=0; + ms->req_dup_num=0; + ms->rsp_dup_num=0; + + error_string=register_tap_listener("megaco", ms, ms->filter, 0, NULL, megacostat_packet, megacostat_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(ms->filter); + g_free(ms); + + fprintf(stderr, "tshark: Couldn't register megaco,rtd tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_megacostat(void) +{ + /* We don't register this tap, if we don't have the megaco plugin loaded.*/ + if (find_tap_id("megaco")) { + register_stat_cmd_arg("megaco,rtd", megacostat_init, NULL); + } +} + diff --git a/ui/cli/tap-mgcpstat.c b/ui/cli/tap-mgcpstat.c new file mode 100644 index 0000000000..f5b0b1ee79 --- /dev/null +++ b/ui/cli/tap-mgcpstat.c @@ -0,0 +1,230 @@ +/* tap-mgcpstat.c + * mgcpstat 2003 Lars Roland + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include "epan/value_string.h" +#include "epan/dissectors/packet-mgcp.h" +#include "timestats.h" + +#define NUM_TIMESTATS 11 + +/* used to keep track of the statistics for an entire program interface */ +typedef struct _mgcpstat_t { + char *filter; + timestat_t rtd[NUM_TIMESTATS]; + guint32 open_req_num; + guint32 disc_rsp_num; + guint32 req_dup_num; + guint32 rsp_dup_num; +} mgcpstat_t; + +static const value_string mgcp_mesage_type[] = { + { 0, "Overall"}, + { 1, "EPCF "}, + { 2, "CRCX "}, + { 3, "MDCX "}, + { 4, "DLCX "}, + { 5, "RQNT "}, + { 6, "NTFY "}, + { 7, "AUEP "}, + { 8, "AUCX "}, + { 9, "RSIP "}, + { 0, NULL} +}; + +static int +mgcpstat_packet(void *pms, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pmi) +{ + mgcpstat_t *ms=(mgcpstat_t *)pms; + const mgcp_info_t *mi=pmi; + nstime_t delta; + int ret = 0; + + switch (mi->mgcp_type) { + + case MGCP_REQUEST: + if(mi->is_duplicate){ + /* Duplicate is ignored */ + ms->req_dup_num++; + } + else { + ms->open_req_num++; + } + break; + + case MGCP_RESPONSE: + if(mi->is_duplicate){ + /* Duplicate is ignored */ + ms->rsp_dup_num++; + } + else if (!mi->request_available) { + /* no request was seen */ + ms->disc_rsp_num++; + } + else { + ms->open_req_num--; + /* calculate time delta between request and response */ + nstime_delta(&delta, &pinfo->fd->abs_ts, &mi->req_time); + + time_stat_update(&(ms->rtd[0]),&delta, pinfo); + + if (g_ascii_strncasecmp(mi->code, "EPCF", 4) == 0 ) { + time_stat_update(&(ms->rtd[1]),&delta, pinfo); + } + else if (g_ascii_strncasecmp(mi->code, "CRCX", 4) == 0 ) { + time_stat_update(&(ms->rtd[2]),&delta, pinfo); + } + else if (g_ascii_strncasecmp(mi->code, "MDCX", 4) == 0 ) { + time_stat_update(&(ms->rtd[3]),&delta, pinfo); + } + else if (g_ascii_strncasecmp(mi->code, "DLCX", 4) == 0 ) { + time_stat_update(&(ms->rtd[4]),&delta, pinfo); + } + else if (g_ascii_strncasecmp(mi->code, "RQNT", 4) == 0 ) { + time_stat_update(&(ms->rtd[5]),&delta, pinfo); + } + else if (g_ascii_strncasecmp(mi->code, "NTFY", 4) == 0 ) { + time_stat_update(&(ms->rtd[6]),&delta, pinfo); + } + else if (g_ascii_strncasecmp(mi->code, "AUEP", 4) == 0 ) { + time_stat_update(&(ms->rtd[7]),&delta, pinfo); + } + else if (g_ascii_strncasecmp(mi->code, "AUCX", 4) == 0 ) { + time_stat_update(&(ms->rtd[8]),&delta, pinfo); + } + else if (g_ascii_strncasecmp(mi->code, "RSIP", 4) == 0 ) { + time_stat_update(&(ms->rtd[9]),&delta, pinfo); + } + else { + time_stat_update(&(ms->rtd[10]),&delta, pinfo); + } + + ret = 1; + } + break; + + default: + break; + } + + return ret; +} + +static void +mgcpstat_draw(void *pms) +{ + mgcpstat_t *ms=(mgcpstat_t *)pms; + int i; + + /* printing results */ + printf("\n"); + printf("=====================================================================================================\n"); + printf("MGCP Response Time Delay (RTD) Statistics:\n"); + printf("Filter for statistics: %s\n",ms->filter?ms->filter:""); + printf("Duplicate requests: %u\n",ms->req_dup_num); + printf("Duplicate responses: %u\n",ms->rsp_dup_num); + printf("Open requests: %u\n",ms->open_req_num); + printf("Discarded responses: %u\n",ms->disc_rsp_num); + printf("Type | Messages | Min RTD | Max RTD | Avg RTD | Min in Frame | Max in Frame |\n"); + for(i=0;i<NUM_TIMESTATS;i++) { + if(ms->rtd[i].num) { + printf("%s | %7u | %8.2f msec | %8.2f msec | %8.2f msec | %10u | %10u |\n", + val_to_str(i,mgcp_mesage_type,"Other "),ms->rtd[i].num, + nstime_to_msec(&(ms->rtd[i].min)), nstime_to_msec(&(ms->rtd[i].max)), + get_average(&(ms->rtd[i].tot), ms->rtd[i].num), + ms->rtd[i].min_num, ms->rtd[i].max_num + ); + } + } + printf("=====================================================================================================\n"); +} + + +static void +mgcpstat_init(const char *optarg, void* userdata _U_) +{ + mgcpstat_t *ms; + int i; + GString *error_string; + + ms=g_malloc(sizeof(mgcpstat_t)); + if(!strncmp(optarg,"mgcp,rtd,",9)){ + ms->filter=g_strdup(optarg+9); + } else { + ms->filter=NULL; + } + + for(i=0;i<NUM_TIMESTATS;i++) { + ms->rtd[i].num=0; + ms->rtd[i].min_num=0; + ms->rtd[i].max_num=0; + ms->rtd[i].min.secs=0; + ms->rtd[i].min.nsecs=0; + ms->rtd[i].max.secs=0; + ms->rtd[i].max.nsecs=0; + ms->rtd[i].tot.secs=0; + ms->rtd[i].tot.nsecs=0; + } + + ms->open_req_num=0; + ms->disc_rsp_num=0; + ms->req_dup_num=0; + ms->rsp_dup_num=0; + + error_string=register_tap_listener("mgcp", ms, ms->filter, 0, NULL, mgcpstat_packet, mgcpstat_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(ms->filter); + g_free(ms); + + fprintf(stderr, "tshark: Couldn't register mgcp,rtd tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_mgcpstat(void) +{ + /* We don't register this tap, if we don't have the mgcp plugin loaded.*/ + if (find_tap_id("mgcp")) { + register_stat_cmd_arg("mgcp,rtd", mgcpstat_init, NULL); + } +} + diff --git a/ui/cli/tap-protocolinfo.c b/ui/cli/tap-protocolinfo.c new file mode 100644 index 0000000000..a3f2b37666 --- /dev/null +++ b/ui/cli/tap-protocolinfo.c @@ -0,0 +1,144 @@ +/* tap-protocolinfo.c + * protohierstat 2002 Ronnie Sahlberg + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* This module provides Protocol Column Info tap for tshark */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/epan_dissect.h" +#include "epan/column-utils.h" +#include "epan/proto.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/strutil.h> + +typedef struct _pci_t { + char *filter; + int hf_index; +} pci_t; + + +static int +protocolinfo_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_) +{ + pci_t *rs=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. + */ + if (!check_col(pinfo->cinfo, COL_INFO)) { + fprintf(stderr, "tshark: the proto,colinfo tap doesn't work if the INFO column isn't being printed.\n"); + exit(1); + } + gp=proto_get_finfo_ptr_array(edt->tree, rs->hf_index); + if(!gp){ + return 0; + } + + for(i=0;i<gp->len;i++){ + str=proto_construct_match_selected_string(gp->pdata[i], NULL); + if(str){ + col_append_fstr(pinfo->cinfo, COL_INFO, " %s",str); + } + } + return 0; +} + + + +static void +protocolinfo_init(const char *optarg, 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,",optarg,14)){ + filter=optarg+14; + field=strchr(filter,','); + if(field){ + field+=1; /* skip the ',' */ + } + } + if(!field){ + fprintf(stderr, "tshark: invalid \"-z proto,colinfo,<filter>,<field>\" argument\n"); + exit(1); + } + + hfi=proto_registrar_get_byname(field); + if(!hfi){ + fprintf(stderr, "tshark: Field \"%s\" doesn't exist.\n", field); + exit(1); + } + + rs=g_malloc(sizeof(pci_t)); + rs->hf_index=hfi->id; + if((field-filter)>1){ + rs->filter=g_malloc(field-filter); + 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); + if(error_string){ + /* error, we failed to attach to the tap. complain and clean up */ + fprintf(stderr, "tshark: Couldn't register proto,colinfo tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + g_free(rs->filter); + g_free(rs); + + exit(1); + } +} + + +void +register_tap_listener_protocolinfo(void) +{ + register_stat_cmd_arg("proto,colinfo,", protocolinfo_init,NULL); +} + diff --git a/ui/cli/tap-protohierstat.c b/ui/cli/tap-protohierstat.c new file mode 100644 index 0000000000..89eac20d1c --- /dev/null +++ b/ui/cli/tap-protohierstat.c @@ -0,0 +1,220 @@ +/* tap-protohierstat.c + * protohierstat 2002 Ronnie Sahlberg + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* This module provides ProtocolHierarchyStatistics for tshark */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include "epan/epan_dissect.h" +#include "epan/proto.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> + +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; + + +static phs_t * +new_phs_t(phs_t *parent) +{ + phs_t *rs; + rs=g_malloc(sizeof(phs_t)); + rs->sibling=NULL; + rs->child=NULL; + rs->parent=parent; + rs->filter=NULL; + rs->protocol=-1; + rs->proto_name=NULL; + rs->frames=0; + rs->bytes=0; + return rs; +} + + +static int +protohierstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_) +{ + phs_t *rs=prs; + phs_t *tmprs; + proto_node *node; + field_info *fi; + + if(!edt){ + return 0; + } + if(!edt->tree){ + return 0; + } + if(!edt->tree->first_child){ + return 0; + } + + for(node=edt->tree->first_child;node;node=node->next){ + fi=PNODE_FINFO(node); + + /* 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); + 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); + 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); + } + rs=rs->child; + } + return 1; +} + +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+=g_snprintf(str+stroff, MAXPHSLINE-stroff, "..."); + break; + } + stroff+=g_snprintf(str+stroff, MAXPHSLINE-stroff, " "); + } + g_snprintf(str+stroff, MAXPHSLINE-stroff, "%s", rs->proto_name); + printf("%-40s frames:%d bytes:%" G_GINT64_MODIFIER "d\n",str, rs->frames, rs->bytes); + phs_draw(rs->child, indentation+1); + } +} + +static void +protohierstat_draw(void *prs) +{ + phs_t *rs=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 *optarg, void* userdata _U_) +{ + phs_t *rs; + int pos=0; + const char *filter=NULL; + GString *error_string; + + if(strcmp("io,phs",optarg)==0){ + /* No arguments */ + } else if(sscanf(optarg,"io,phs,%n",&pos)==0){ + if(pos){ + filter=optarg+pos; + } + } else { + fprintf(stderr, "tshark: invalid \"-z io,phs[,<filter>]\" argument\n"); + exit(1); + } + + rs=new_phs_t(NULL); + + if(filter){ + rs->filter=g_strdup(filter); + } else { + rs->filter=NULL; + } + + error_string=register_tap_listener("frame", rs, filter, TL_REQUIRES_PROTO_TREE, NULL, protohierstat_packet, protohierstat_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(rs->filter); + g_free(rs); + + fprintf(stderr, "tshark: Couldn't register io,phs tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_protohierstat(void) +{ + register_stat_cmd_arg("io,phs", protohierstat_init, NULL); +} + diff --git a/ui/cli/tap-radiusstat.c b/ui/cli/tap-radiusstat.c new file mode 100644 index 0000000000..dd44e87c24 --- /dev/null +++ b/ui/cli/tap-radiusstat.c @@ -0,0 +1,243 @@ +/* tap-radiusstat.c + * Copyright 2006 Alejandro Vaquero <alejandrovaquero@yahoo.com> + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include "epan/value_string.h" +#include <epan/dissectors/packet-radius.h> +#include "timestats.h" + +typedef enum _radius_category { + RADIUS_CAT_OVERALL = 0, + RADIUS_CAT_ACCESS, + RADIUS_CAT_ACCOUNTING, + RADIUS_CAT_PASSWORD, + RADIUS_CAT_RESOURCE_FREE, + RADIUS_CAT_RESOURCE_QUERY, + RADIUS_CAT_NAS_REBOOT, + RADIUS_CAT_EVENT, + RADIUS_CAT_DISCONNECT, + RADIUS_CAT_COA, + RADIUS_CAT_OTHERS, + RADIUS_CAT_NUM_TIMESTATS +} radius_category; + +/* used to keep track of the statistics for an entire program interface */ +typedef struct _radiusstat_t { + char *filter; + timestat_t rtd[RADIUS_CAT_NUM_TIMESTATS]; + guint32 open_req_num; + guint32 disc_rsp_num; + guint32 req_dup_num; + guint32 rsp_dup_num; +} radiusstat_t; + + + + +static const value_string radius_message_code[] = { + { RADIUS_CAT_OVERALL, "Overall "}, + { RADIUS_CAT_ACCESS, "Access "}, + { RADIUS_CAT_ACCOUNTING, "Accounting "}, + { RADIUS_CAT_PASSWORD, "Password "}, + { RADIUS_CAT_RESOURCE_FREE, "Resource Free "}, + { RADIUS_CAT_RESOURCE_QUERY, "Resource Query"}, + { RADIUS_CAT_NAS_REBOOT, "NAS Reboot "}, + { RADIUS_CAT_EVENT, "Event "}, + { RADIUS_CAT_DISCONNECT, "Disconnect "}, + { RADIUS_CAT_COA, "CoA "}, + { RADIUS_CAT_OTHERS, "Other "}, + { 0, NULL} +}; + +static int +radiusstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri) +{ + radiusstat_t *rs=(radiusstat_t *)prs; + const radius_info_t *ri=pri; + nstime_t delta; + int ret = 0; + + switch (ri->code) { + + case RADIUS_PKT_TYPE_ACCESS_REQUEST: + case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST: + case RADIUS_PKT_TYPE_PASSWORD_REQUEST: + case RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST: + case RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST: + case RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST: + case RADIUS_PKT_TYPE_EVENT_REQUEST: + case RADIUS_PKT_TYPE_DISCONNECT_REQUEST: + case RADIUS_PKT_TYPE_COA_REQUEST: + if(ri->is_duplicate){ + /* Duplicate is ignored */ + rs->req_dup_num++; + } + else { + rs->open_req_num++; + } + break; + + case RADIUS_PKT_TYPE_ACCESS_ACCEPT: + case RADIUS_PKT_TYPE_ACCESS_REJECT: + case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE: + case RADIUS_PKT_TYPE_PASSWORD_ACK: + case RADIUS_PKT_TYPE_PASSWORD_REJECT: + case RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE: + case RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE: + case RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE: + case RADIUS_PKT_TYPE_EVENT_RESPONSE: + case RADIUS_PKT_TYPE_DISCONNECT_ACK: + case RADIUS_PKT_TYPE_DISCONNECT_NAK: + case RADIUS_PKT_TYPE_COA_ACK: + case RADIUS_PKT_TYPE_COA_NAK: + if(ri->is_duplicate){ + /* Duplicate is ignored */ + rs->rsp_dup_num++; + } + else if (!ri->request_available) { + /* no request was seen */ + rs->disc_rsp_num++; + } + else { + rs->open_req_num--; + /* calculate time delta between request and response */ + nstime_delta(&delta, &pinfo->fd->abs_ts, &ri->req_time); + + time_stat_update(&(rs->rtd[RADIUS_CAT_OVERALL]),&delta, pinfo); + + if (ri->code == RADIUS_PKT_TYPE_ACCESS_ACCEPT || ri->code == RADIUS_PKT_TYPE_ACCESS_REJECT) { + time_stat_update(&(rs->rtd[RADIUS_CAT_ACCESS]),&delta, pinfo); + } + else if (ri->code == RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE) { + time_stat_update(&(rs->rtd[RADIUS_CAT_ACCOUNTING]),&delta, pinfo); + } + else { + time_stat_update(&(rs->rtd[RADIUS_CAT_OTHERS]),&delta, pinfo); + } + + ret = 1; + } + break; + + default: + break; + } + + return ret; +} + +static void +radiusstat_draw(void *prs) +{ + radiusstat_t *rs=(radiusstat_t *)prs; + int i; + + /* printing results */ + printf("\n"); + printf("===========================================================================================================\n"); + printf("RADIUS Response Time Delay (RTD) Statistics:\n"); + printf("Filter for statistics: %s\n",rs->filter?rs->filter:""); + printf("Duplicate requests: %u\n",rs->req_dup_num); + printf("Duplicate responses: %u\n",rs->rsp_dup_num); + printf("Open requests: %u\n",rs->open_req_num); + printf("Discarded responses: %u\n",rs->disc_rsp_num); + printf("Type | Messages | Min RTD | Max RTD | Avg RTD | Min in Frame | Max in Frame |\n"); + for(i=0;i<RADIUS_CAT_NUM_TIMESTATS;i++) { + if(rs->rtd[i].num) { + printf("%s | %7u | %8.2f msec | %8.2f msec | %8.2f msec | %10u | %10u |\n", + val_to_str(i,radius_message_code,"Other "),rs->rtd[i].num, + nstime_to_msec(&(rs->rtd[i].min)), nstime_to_msec(&(rs->rtd[i].max)), + get_average(&(rs->rtd[i].tot), rs->rtd[i].num), + rs->rtd[i].min_num, rs->rtd[i].max_num + ); + } + } + printf("===========================================================================================================\n"); +} + + +static void +radiusstat_init(const char *optarg, void* userdata _U_) +{ + radiusstat_t *rs; + int i; + GString *error_string; + + rs=g_malloc(sizeof(radiusstat_t)); + if(!strncmp(optarg,"radius,rtd,",11)){ + rs->filter=g_strdup(optarg+11); + } else { + rs->filter=NULL; + } + + for(i=0;i<RADIUS_CAT_NUM_TIMESTATS;i++) { + rs->rtd[i].num=0; + rs->rtd[i].min_num=0; + rs->rtd[i].max_num=0; + rs->rtd[i].min.secs=0; + rs->rtd[i].min.nsecs=0; + rs->rtd[i].max.secs=0; + rs->rtd[i].max.nsecs=0; + rs->rtd[i].tot.secs=0; + rs->rtd[i].tot.nsecs=0; + } + + rs->open_req_num=0; + rs->disc_rsp_num=0; + rs->req_dup_num=0; + rs->rsp_dup_num=0; + + error_string=register_tap_listener("radius", rs, rs->filter, 0, NULL, radiusstat_packet, radiusstat_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(rs->filter); + g_free(rs); + + fprintf(stderr, "tshark: Couldn't register radius,rtd tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_radiusstat(void) +{ + register_stat_cmd_arg("radius,rtd", radiusstat_init, NULL); +} + diff --git a/ui/cli/tap-rlcltestat.c b/ui/cli/tap-rlcltestat.c new file mode 100644 index 0000000000..d1c3d74999 --- /dev/null +++ b/ui/cli/tap-rlcltestat.c @@ -0,0 +1,411 @@ +/* tap-rlclte_stat.c + * Copyright 2011 Martin Mathieson + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#include <string.h> +#include <epan/packet.h> +#include <epan/packet_info.h> +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/dissectors/packet-rlc-lte.h> + + +enum { + UEID_COLUMN, + UL_FRAMES_COLUMN, + UL_BYTES_COLUMN, + UL_BW_COLUMN, + UL_ACKS_COLUMN, + UL_NACKS_COLUMN, + UL_MISSING_COLUMN, + DL_FRAMES_COLUMN, + DL_BYTES_COLUMN, + DL_BW_COLUMN, + DL_ACKS_COLUMN, + DL_NACKS_COLUMN, + DL_MISSING_COLUMN, + NUM_UE_COLUMNS +}; + +static const gchar *ue_titles[] = { " UEId", + "UL Frames", "UL Bytes", " UL Mbs", "UL ACKs", "UL NACKs", "UL Missed", + "DL Frames", "DL Bytes", " DL Mbs", "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(struct rlc_lte_tap_info *si, packet_info *pinfo _U_) +{ + rlc_lte_ep_t* ep; + + if (!si) { + return NULL; + } + + if (!(ep = g_malloc(sizeof(rlc_lte_ep_t)))) { + return NULL; + } + + /* Copy SI data into ep->stats */ + ep->stats.ueid = si->ueid; + + /* Counts for new UE are all 0 */ + ep->stats.UL_frames = 0; + ep->stats.DL_frames = 0; + ep->stats.UL_total_bytes = 0; + ep->stats.DL_total_bytes = 0; + memset(&ep->stats.DL_time_start, 0, sizeof(nstime_t)); + memset(&ep->stats.DL_time_stop, 0, sizeof(nstime_t)); + ep->stats.UL_total_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 int +rlc_lte_stat_packet(void *phs, packet_info *pinfo, epan_dissect_t *edt _U_, + const void *phi) +{ + /* Get reference to stats struct */ + rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs; + rlc_lte_ep_t *tmp = NULL, *te = NULL; + + /* Cast tap info struct */ + struct rlc_lte_tap_info *si = (struct rlc_lte_tap_info *)phi; + + /* Need this */ + if (!hs) { + return 0; + } + + /* Inc top-level frame count */ + hs->total_frames++; + + /* Common channel stats */ + switch (si->channelType) { + case CHANNEL_TYPE_BCCH_BCH: + case CHANNEL_TYPE_BCCH_DL_SCH: + hs->common_stats.bcch_frames++; + hs->common_stats.bcch_bytes += si->pduLength; + return 1; + + case CHANNEL_TYPE_PCCH: + hs->common_stats.pcch_frames++; + hs->common_stats.pcch_bytes += si->pduLength; + return 1; + + default: + break; + } + + /* For per-UE data, must create a new row if none already existing */ + if (!hs->ep_list) { + /* Allocate new list */ + hs->ep_list = alloc_rlc_lte_ep(si, pinfo); + /* Make it the first/only entry */ + te = hs->ep_list; + } else { + /* Look among existing rows for this UEId */ + for (tmp = hs->ep_list; (tmp != NULL); tmp = tmp->next) { + if (tmp->stats.ueid == si->ueid) { + te = tmp; + break; + } + } + + /* Not found among existing, so create a new one anyway */ + if (te == NULL) { + if ((te = alloc_rlc_lte_ep(si, pinfo))) { + /* Add new item to end of list */ + rlc_lte_ep_t *p = hs->ep_list; + while (p->next) { + p = p->next; + } + p->next = te; + te->next = NULL; + } + } + } + + /* Really should have a row pointer by now */ + if (!te) { + return 0; + } + + /* Update entry with details from si */ + te->stats.ueid = si->ueid; + + /* Top-level traffic stats */ + if (si->direction == DIRECTION_UPLINK) { + /* Update time range */ + if (te->stats.UL_frames == 0) { + te->stats.UL_time_start = si->time; + } + te->stats.UL_time_stop = si->time; + + te->stats.UL_frames++; + te->stats.UL_total_bytes += si->pduLength; + } + else { + /* Update time range */ + if (te->stats.DL_frames == 0) { + te->stats.DL_time_start = si->time; + } + te->stats.DL_time_stop = si->time; + + te->stats.DL_frames++; + te->stats.DL_total_bytes += si->pduLength; + } + + + if (si->direction == DIRECTION_UPLINK) { + if (si->isControlPDU) { + te->stats.UL_total_acks++; + } + te->stats.UL_total_nacks += si->noOfNACKs; + te->stats.UL_total_missing += si->missingSNs; + } + else { + if (si->isControlPDU) { + te->stats.DL_total_acks++; + } + te->stats.DL_total_nacks += si->noOfNACKs; + te->stats.DL_total_missing += si->missingSNs; + } + + return 1; +} + + +/* Calculate and return a bandwidth figure, in Mbs */ +static float calculate_bw(nstime_t *start_time, nstime_t *stop_time, guint32 bytes) +{ + if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) { + float elapsed_ms = (((float)stop_time->secs - (float)start_time->secs) * 1000) + + (((float)stop_time->nsecs - (float)start_time->nsecs) / 1000000); + return ((bytes * 8) / elapsed_ms) / 1000; + } + else { + return 0.0; + } +} + + + + +/* (Re)draw RLC stats */ +static void +rlc_lte_stat_draw(void *phs) +{ + guint16 number_of_ues = 0; + gint i; + + /* Look up the statistics struct */ + rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs; + rlc_lte_ep_t* list = hs->ep_list, *tmp = 0; + + /* Common channel data */ + printf("Common Data:\n"); + printf("==============\n"); + printf("BCCH Frames: %u BCCH Bytes: %u PCCH Frames: %u PCCH Bytes: %u\n\n", + hs->common_stats.bcch_frames, hs->common_stats.bcch_bytes, + hs->common_stats.pcch_frames, hs->common_stats.pcch_bytes); + + /* Per-UE table entries */ + + + /* Set title that shows how many UEs currently in table */ + for (tmp = list; (tmp!=NULL); tmp=tmp->next, number_of_ues++); + printf("Per UE Data - %u UEs (%u frames)\n", number_of_ues, hs->total_frames); + printf("==========================================\n"); + + /* Show column titles */ + for (i=0; i < NUM_UE_COLUMNS; i++) { + printf("%s ", ue_titles[i]); + } + printf("\n"); + + /* For each row/UE in the model */ + for (tmp = list; tmp; tmp=tmp->next) { + /* Calculate bandwidth */ + float UL_bw = calculate_bw(&tmp->stats.UL_time_start, + &tmp->stats.UL_time_stop, + tmp->stats.UL_total_bytes); + float DL_bw = calculate_bw(&tmp->stats.DL_time_start, + &tmp->stats.DL_time_stop, + tmp->stats.DL_total_bytes); + + printf("%5u %10u %9u %10f %8u %9u %10u %10u %9u %10f %8u %9u %10u\n", + tmp->stats.ueid, + tmp->stats.UL_frames, + tmp->stats.UL_total_bytes, UL_bw, + tmp->stats.UL_total_acks, + tmp->stats.UL_total_nacks, + tmp->stats.UL_total_missing, + tmp->stats.DL_frames, + tmp->stats.DL_total_bytes, DL_bw, + tmp->stats.DL_total_acks, + tmp->stats.DL_total_nacks, + tmp->stats.DL_total_missing); + } +} + + + + +/* Create a new RLC LTE stats struct */ +static void rlc_lte_stat_init(const char *optarg, void *userdata _U_) +{ + rlc_lte_stat_t *hs; + const char *filter = NULL; + GString *error_string; + + /* Check for a filter string */ + if (strncmp(optarg, "rlc-lte,stat,", 13) == 0) { + /* Skip those characters from filter to display */ + filter = optarg + 13; + } + else { + /* No filter */ + filter = NULL; + } + + /* Create top-level struct */ + hs = g_malloc(sizeof(rlc_lte_stat_t)); + memset(hs, 0, sizeof(rlc_lte_stat_t)); + hs->ep_list = NULL; + + + /**********************************************/ + /* Register the tap listener */ + /**********************************************/ + + error_string = register_tap_listener("rlc-lte", hs, + filter, 0, + rlc_lte_stat_reset, + rlc_lte_stat_packet, + rlc_lte_stat_draw); + if (error_string) { + g_string_free(error_string, TRUE); + g_free(hs); + exit(1); + } + +} + + +/* Register this tap listener (need void on own so line register function found) */ +void +register_tap_listener_rlc_lte_stat(void) +{ + register_stat_cmd_arg("rlc-lte,stat", rlc_lte_stat_init, NULL); +} + diff --git a/ui/cli/tap-rpcprogs.c b/ui/cli/tap-rpcprogs.c new file mode 100644 index 0000000000..accc64c00d --- /dev/null +++ b/ui/cli/tap-rpcprogs.c @@ -0,0 +1,240 @@ +/* tap-rpcprogs.c + * rpcstat 2002 Ronnie Sahlberg + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* This module provides rpc call/reply SRT statistics to tshark. + * It is only used by tshark and not wireshark + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/dissectors/packet-rpc.h> + +#define MICROSECS_PER_SEC 1000000 +#define NANOSECS_PER_SEC 1000000000 + +/* 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 int +rpcprogs_packet(void *dummy1 _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri) +{ + const rpc_call_info_value *ri=pri; + nstime_t delta; + rpc_program_t *rp=NULL; + + if(!prog_list){ + /* the list was empty */ + rp=g_malloc(sizeof(rpc_program_t)); + 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_malloc(sizeof(rpc_program_t)); + 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_malloc(sizeof(rpc_program_t)); + 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 0; + } + + /* calculate time delta between request and reply */ + nstime_delta(&delta, &pinfo->fd->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 1; +} + + +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; + + g_snprintf(str, sizeof(str), "%s(%d)",rpc_prog_name(rp->program),rp->program); + printf("%-15s %2d %6d %3d.%06d %3d.%06d %3" G_GINT64_MODIFIER "u.%06" G_GINT64_MODIFIER "u\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 *optarg _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); + if(error_string){ + fprintf(stderr,"tshark: Couldn't register rpc,programs tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_rpcprogs(void) +{ + register_stat_cmd_arg("rpc,programs", rpcprogs_init, NULL); +} + + diff --git a/ui/cli/tap-rpcstat.c b/ui/cli/tap-rpcstat.c new file mode 100644 index 0000000000..3f7245f425 --- /dev/null +++ b/ui/cli/tap-rpcstat.c @@ -0,0 +1,355 @@ +/* tap-rpcstat.c + * rpcstat 2002 Ronnie Sahlberg + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* This module provides rpc call/reply SRT statistics to tshark. + * It is only used by tshark and not wireshark. + * + * It serves as an example on how to use the tap api. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/dissectors/packet-rpc.h> + +#define MICROSECS_PER_SEC 1000000 +#define NANOSECS_PER_SEC 1000000000 + +/* used to keep track of statistics for a specific procedure */ +typedef struct _rpc_procedure_t { + const char *proc; + int num; + nstime_t min; + nstime_t max; + nstime_t tot; +} rpc_procedure_t; + +/* used to keep track of the statistics for an entire program interface */ +typedef struct _rpcstat_t { + const char *prog; + char *filter; + guint32 program; + guint32 version; + guint32 num_procedures; + rpc_procedure_t *procedures; +} rpcstat_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 +rpcstat_reset(void *prs) +{ + rpcstat_t *rs=prs; + guint32 i; + + for(i=0;i<rs->num_procedures;i++){ + rs->procedures[i].num=0; + rs->procedures[i].min.secs=0; + rs->procedures[i].min.nsecs=0; + rs->procedures[i].max.secs=0; + rs->procedures[i].max.nsecs=0; + rs->procedures[i].tot.secs=0; + rs->procedures[i].tot.nsecs=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 as we do below in + * this example 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 + * "rpc" tap, the third parameter type is rpc_call_info_value. + * + * The filtering we do is just to check the rpc_call_info_value struct that we + * were called for the proper program and version. We didn't apply a filter + * when we registered so we will be called for ALL rpc packets and not just + * the ones we are collecting stats for. + * + * function returns : + * 0: no updates, no need to call (*draw) later + * !0: state has changed, call (*draw) sometime later + */ +static int +rpcstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri) +{ + rpcstat_t *rs=prs; + const rpc_call_info_value *ri=pri; + nstime_t delta; + rpc_procedure_t *rp; + + if(ri->proc>=rs->num_procedures){ + /* dont handle this since its outside of known table */ + return 0; + } + /* we are only interested in reply packets */ + if(ri->request){ + return 0; + } + /* we are only interested in certain program/versions */ + if( (ri->prog!=rs->program) || (ri->vers!=rs->version) ){ + return 0; + } + + rp=&(rs->procedures[ri->proc]); + + /* calculate time delta between request and reply */ + nstime_delta(&delta, &pinfo->fd->abs_ts, &ri->req_time); + + if(rp->num==0){ + rp->max.secs=delta.secs; + rp->max.nsecs=delta.nsecs; + } + + if(rp->num==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 1; +} + +/* 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 rpcstat_t can thus change + * beneath us. Beware! + */ +static void +rpcstat_draw(void *prs) +{ + rpcstat_t *rs=prs; + guint32 i; + guint64 td; + printf("\n"); + printf("=======================================================\n"); + printf("%s Version %d SRT Statistics:\n", rs->prog, rs->version); + printf("Filter: %s\n",rs->filter?rs->filter:""); + printf("Procedure Calls Min SRT Max SRT Avg SRT\n"); + for(i=0;i<rs->num_procedures;i++){ + if(rs->procedures[i].num==0){ + continue; + } + /* Scale the average SRT in units of 1us and round to the nearest us. */ + td = ((guint64)(rs->procedures[i].tot.secs)) * NANOSECS_PER_SEC + rs->procedures[i].tot.nsecs; + td = ((td / rs->procedures[i].num) + 500) / 1000; + + printf("%-15s %6d %3d.%06d %3d.%06d %3" G_GINT64_MODIFIER "u.%06" G_GINT64_MODIFIER "u\n", + rs->procedures[i].proc, + rs->procedures[i].num, + (int)(rs->procedures[i].min.secs),(rs->procedures[i].min.nsecs+500)/1000, + (int)(rs->procedures[i].max.secs),(rs->procedures[i].max.nsecs+500)/1000, + td/MICROSECS_PER_SEC, td%MICROSECS_PER_SEC + ); + } + printf("=======================================================\n"); +} + +static guint32 rpc_program=0; +static guint32 rpc_version=0; +static gint32 rpc_min_proc=-1; +static gint32 rpc_max_proc=-1; + +static void * +rpcstat_find_procs(gpointer *key, gpointer *value _U_, gpointer *user_data _U_) +{ + rpc_proc_info_key *k=(rpc_proc_info_key *)key; + + if(k->prog!=rpc_program){ + return NULL; + } + if(k->vers!=rpc_version){ + return NULL; + } + if(rpc_min_proc==-1){ + rpc_min_proc=k->proc; + rpc_max_proc=k->proc; + } + if((gint32)k->proc<rpc_min_proc){ + rpc_min_proc=k->proc; + } + if((gint32)k->proc>rpc_max_proc){ + rpc_max_proc=k->proc; + } + + return NULL; +} + + +/* When called, this function will create a new instance of rpcstat. + * + * program and version are which onc-rpc program/version we want to collect + * statistics for. + * + * This function is called from tshark when it parses the -z rpc, arguments and + * it creates a new instance to store statistics in and registers this new + * instance for the rpc tap. + */ +static void +rpcstat_init(const char *optarg, void* userdata _U_) +{ + rpcstat_t *rs; + guint32 i; + int program, version; + int pos=0; + const char *filter=NULL; + GString *error_string; + + if(sscanf(optarg,"rpc,srt,%d,%d,%n",&program,&version,&pos)==2){ + if(pos){ + filter=optarg+pos; + } else { + filter=NULL; + } + } else { + fprintf(stderr, "tshark: invalid \"-z rpc,srt,<program>,<version>[,<filter>]\" argument\n"); + exit(1); + } + + rs=g_malloc(sizeof(rpcstat_t)); + rs->prog=rpc_prog_name(program); + rs->program=program; + rs->version=version; + if(filter){ + rs->filter=g_strdup(filter); + } else { + rs->filter=NULL; + } + rpc_program=program; + rpc_version=version; + rpc_min_proc=-1; + rpc_max_proc=-1; + g_hash_table_foreach(rpc_procs, (GHFunc)rpcstat_find_procs, NULL); + if(rpc_min_proc==-1){ + fprintf(stderr,"tshark: Invalid -z rpc,srt,%d,%d\n",rpc_program,rpc_version); + fprintf(stderr," Program:%d version:%d isn't supported by tshark.\n", rpc_program, rpc_version); + exit(1); + } + + + rs->num_procedures=rpc_max_proc+1; + rs->procedures=g_malloc(sizeof(rpc_procedure_t)*(rs->num_procedures+1)); + for(i=0;i<rs->num_procedures;i++){ + rs->procedures[i].proc=rpc_proc_name(program, version, i); + rs->procedures[i].num=0; + rs->procedures[i].min.secs=0; + rs->procedures[i].min.nsecs=0; + rs->procedures[i].max.secs=0; + rs->procedures[i].max.nsecs=0; + rs->procedures[i].tot.secs=0; + rs->procedures[i].tot.nsecs=0; + } + +/* 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. + * (Perhaps the user only wants the stats for nis+ traffic for certain objects?) + */ + + error_string=register_tap_listener("rpc", rs, filter, 0, rpcstat_reset, rpcstat_packet, rpcstat_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(rs->procedures); + g_free(rs->filter); + g_free(rs); + + fprintf(stderr, "tshark: Couldn't register rpc,srt tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_rpcstat(void) +{ + register_stat_cmd_arg("rpc,srt,", rpcstat_init,NULL); +} + diff --git a/ui/cli/tap-rtp.c b/ui/cli/tap-rtp.c new file mode 100644 index 0000000000..2741ea84b7 --- /dev/null +++ b/ui/cli/tap-rtp.c @@ -0,0 +1,162 @@ +/* tap-rtp.c + * RTP TAP for tshark + * + * $Id$ + * + * 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 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * This TAP provides statistics for RTP streams + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include <locale.h> +#include "epan/packet_info.h" +#include "epan/value_string.h" +#include <epan/tap.h> +#include <epan/rtp_pt.h> +#include <epan/stat_cmd_args.h> +#include <epan/addr_resolv.h> +#include "tap-rtp-common.h" + +/* The one and only global rtpstream_tapinfo_t structure for tshark and wireshark. + */ +static rtpstream_tapinfo_t the_tapinfo_struct = + {0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE}; + +static void +rtp_streams_stat_draw(void *arg _U_) +{ + + + GList *list; + rtp_stream_info_t* strinfo; + gchar *payload_type; + guint32 expected; + gint32 lost; + double perc; + char *savelocale; + + printf("========================= RTP Streams ========================\n"); + printf("%15s %5s %15s %5s %10s %16s %5s %12s %15s %15s %15s %s\n","Src IP addr", "Port", "Dest IP addr", "Port", "SSRC", "Payload", "Pkts", "Lost", "Max Delta(ms)", "Max Jitter(ms)", "Mean Jitter(ms)", "Problems?"); + + /* save the current locale */ + savelocale = setlocale(LC_NUMERIC, NULL); + /* switch to "C" locale to avoid problems with localized decimal separators + in g_snprintf("%f") functions */ + setlocale(LC_NUMERIC, "C"); + + list = the_tapinfo_struct.strinfo_list; + + list = g_list_first(list); + while (list) + { + strinfo = (rtp_stream_info_t*)(list->data); + + /* payload type */ + if(strinfo->pt>95){ + if(strinfo->info_payload_type_str != NULL){ + payload_type = g_strdup(strinfo->info_payload_type_str); + }else{ + payload_type = g_strdup_printf("Unknown(%u)",strinfo->pt); + } + + }else{ + payload_type = g_strdup(val_to_str_ext(strinfo->pt, &rtp_payload_type_vals_ext, + "Unknown (%u)")); + } + + /* packet count, lost packets */ + expected = (strinfo->rtp_stats.stop_seq_nr + strinfo->rtp_stats.cycles*65536) + - strinfo->rtp_stats.start_seq_nr + 1; + lost = expected - strinfo->rtp_stats.total_nr; + if (expected){ + perc = (double)(lost*100)/(double)expected; + } else { + perc = 0; + } + + printf("%15s %5u %15s %5u 0x%08X %16s %5u %5d (%.1f%%) %15.2f %15.2f %15.2f %s\n", + get_addr_name(&(strinfo->src_addr)), + strinfo->src_port, + get_addr_name(&(strinfo->dest_addr)), + strinfo->dest_port, + strinfo->ssrc, + payload_type, + strinfo->npackets, + lost, perc, + strinfo->rtp_stats.max_delta, + strinfo->rtp_stats.max_jitter, + strinfo->rtp_stats.mean_jitter, + (strinfo->problem)?"X":""); + + list = g_list_next(list); + + + } + + printf("==============================================================\n"); + /* restore previous locale setting */ + setlocale(LC_NUMERIC, savelocale); +} + + +static void +rtp_streams_stat_init(const char *optarg _U_, void* userdata _U_) +{ + GString *err_p; + + err_p = + register_tap_listener("rtp", &the_tapinfo_struct, NULL, 0, + rtpstream_reset_cb, + rtpstream_packet, + rtp_streams_stat_draw); + + if (err_p != NULL) + { + g_string_free(err_p, TRUE); + + exit(1); + } +} + + +void +register_tap_listener_rtp_streams(void) +{ + register_stat_cmd_arg("rtp,streams", rtp_streams_stat_init,NULL); +} diff --git a/ui/cli/tap-rtspstat.c b/ui/cli/tap-rtspstat.c new file mode 100644 index 0000000000..feb8c8bd8e --- /dev/null +++ b/ui/cli/tap-rtspstat.c @@ -0,0 +1,282 @@ +/* tap-rtspstat.c + * tap-rtspstat March 2011 + * + * Stephane GORSE (Orange Labs / France Telecom) + * Copied from Jean-Michel FAYARD's works (HTTP) + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <string.h> + +#include "epan/packet_info.h" +#include "epan/value_string.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include "register.h" +#include <epan/dissectors/packet-rtsp.h> + + +/* 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_int_hash, g_int_equal); + + for (i=0 ; rtsp_status_code_vals[i].strptr ; i++ ) + { + gint *key = g_malloc (sizeof(gint)); + rtsp_response_code_t *sc = g_malloc (sizeof(rtsp_response_code_t)); + *key = rtsp_status_code_vals[i].value; + sc->packets=0; + sc->response_code = *key; + sc->name=rtsp_status_code_vals[i].strptr; + sc->sp = sp; + g_hash_table_insert( sc->sp->hash_responses, key, 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( gint * key _U_ , rtsp_response_code_t *data, char * format) +{ + if (data==NULL) { + g_warning("No data available, key=%d\n", *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=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 int +rtspstat_packet(void *psp , packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri) +{ + const rtsp_info_value_t *value=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) { + guint *key=g_malloc( sizeof(guint) ); + rtsp_response_code_t *sc; + + *key=value->response_code; + sc = 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) + */ + int i = value->response_code; + if ((i<100) || (i>=600)) { + return 0; + } + 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 = g_hash_table_lookup( + sp->hash_responses, + key); + if (sc==NULL) + return 0; + } + sc->packets++; + } + else if (value->request_method){ + rtsp_request_methode_t *sc; + + sc = g_hash_table_lookup( + sp->hash_requests, + value->request_method); + if (sc==NULL){ + sc=g_malloc( sizeof(rtsp_request_methode_t) ); + 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 0; + } + return 1; +} + + +static void +rtspstat_draw(void *psp ) +{ + rtspstat_t *sp=psp; + printf("\n"); + printf("===================================================================\n"); + if (! sp->filter[0]) + printf("RTSP Statistics\n"); + else + printf("RTSP Statistics with filter %s\n", sp->filter); + + printf( "* RTSP Status Codes in reply packets\n"); + g_hash_table_foreach( sp->hash_responses, (GHFunc)rtsp_draw_hash_responses, + " RTSP %3d %s\n"); + printf("* List of RTSP Request methods\n"); + g_hash_table_foreach( sp->hash_requests, (GHFunc)rtsp_draw_hash_requests, + " %9s %d \n"); + printf("===================================================================\n"); +} + + + +/* When called, this function will create a new instance of gtk_rtspstat. + */ +static void +gtk_rtspstat_init(const char *optarg,void* userdata _U_) +{ + rtspstat_t *sp; + const char *filter=NULL; + GString *error_string; + + if (!strncmp (optarg, "rtsp,stat,", 10)){ + filter=optarg+10; + } else { + filter=NULL; + } + + sp = g_malloc( sizeof(rtspstat_t) ); + if(filter){ + sp->filter=g_strdup(filter); + } else { + sp->filter=NULL; + } + /*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); + if (error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(sp->filter); + g_free(sp); + fprintf (stderr, "tshark: Couldn't register rtsp,stat tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } + + rtsp_init_hash(sp); +} + +void +register_tap_listener_gtkrtspstat(void) +{ + register_stat_cmd_arg("rtsp,stat,", gtk_rtspstat_init,NULL); +} diff --git a/ui/cli/tap-scsistat.c b/ui/cli/tap-scsistat.c new file mode 100644 index 0000000000..c12d78cb72 --- /dev/null +++ b/ui/cli/tap-scsistat.c @@ -0,0 +1,264 @@ +/* tap-scsistat.c 2010 Chris Costa and Cal Turney + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include <epan/packet_info.h> +#include <epan/epan.h> +#include <epan/stat_cmd_args.h> +#include <epan/tap.h> +#include <epan/conversation.h> +#include <epan/dissectors/packet-scsi.h> +#include <epan/dissectors/packet-fc.h> +#include <epan/dissectors/packet-scsi-sbc.h> +#include <epan/dissectors/packet-scsi-ssc.h> +#include <epan/dissectors/packet-scsi-smc.h> +#include <epan/dissectors/packet-scsi-osd.h> + +static guint8 scsi_program=0; + +/* used to keep track of statistics for a specific procedure */ +typedef struct _scsi_procedure_t { + const char *proc; + int num; + nstime_t min; + nstime_t max; + nstime_t tot; +} scsi_procedure_t; + +/* used to keep track of the statistics for an entire program interface */ +typedef struct _scsistat_t { + guint8 cmdset; + char *filter; + const value_string *cdbnames; + const char *prog; +#define MAX_PROCEDURES 256 + scsi_procedure_t *procedures; +} scsistat_t; + +#define NANOSECS_PER_SEC 1000000000 + +static void +scsistat_reset(void *prs) +{ + scsistat_t *rs=prs; + guint32 i; + + for(i=0; i < MAX_PROCEDURES; i++) { + rs->procedures[i].num=0; + rs->procedures[i].min.secs=0; + rs->procedures[i].min.nsecs=0; + rs->procedures[i].max.secs=0; + rs->procedures[i].max.nsecs=0; + rs->procedures[i].tot.secs=0; + rs->procedures[i].tot.nsecs=0; + } +} + +static int +scsistat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri) +{ + scsistat_t *rs = prs; + const scsi_task_data_t *ri = pri; + nstime_t delta; + scsi_procedure_t *rp; + + /* we are only interested in response packets */ + if(ri->type!=SCSI_PDU_TYPE_RSP) { + return 0; + } + /* we are only interested in a specific commandset */ + if( (!ri->itl) || ((ri->itl->cmdset&SCSI_CMDSET_MASK)!=rs->cmdset) ) { + return 0; + } + /* check that the opcode looks sane */ + if( (!ri->itlq) || (ri->itlq->scsi_opcode > 255) ) { + return 0; + } + + rp=&(rs->procedures[ri->itlq->scsi_opcode]); + + /* calculate time delta between request and reply */ + nstime_delta(&delta, &pinfo->fd->abs_ts, &ri->itlq->fc_time); + + if(rp->num==0) { + rp->max.secs=delta.secs; + rp->max.nsecs=delta.nsecs; + } + if(rp->num==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 1; +} + +static void +scsistat_draw(void *prs) +{ + scsistat_t *rs=prs; + guint32 i; + guint64 td; + + printf("\n"); + printf("===========================================================\n"); + printf("SCSI %s SRT Statistics:\n", rs->prog); + printf("Filter: %s\n", rs->filter?rs->filter:""); + printf("Procedure Calls Min SRT Max SRT Avg SRT\n"); + for(i=0; i < MAX_PROCEDURES; i++) { + if(rs->procedures[i].num==0) { + continue; + } + /* scale it to units of 1us.*/ + td = ((guint64)(rs->procedures[i].tot.secs)) * NANOSECS_PER_SEC + rs->procedures[i].tot.nsecs; + td = ((td / rs->procedures[i].num) + 500) / 1000; + + printf("%-19s %6d %3d.%06u %3d.%06u %3d.%06u \n", + rs->procedures[i].proc, + rs->procedures[i].num, + (int)(rs->procedures[i].min.secs), + (rs->procedures[i].min.nsecs+500)/1000, + (int)(rs->procedures[i].max.secs), + (rs->procedures[i].max.nsecs+500)/1000, + (int)(td/1000000), (int)(td%1000000) + ); + } + printf("===========================================================\n"); +} + +static void +scsistat_init(const char *optarg, void* userdata _U_) +{ + scsistat_t *rs; + guint32 i; + int program, pos; + const char *filter=NULL; + GString *error_string; + + pos=0; + if(sscanf(optarg, "scsi,srt,%d,%n", &program, &pos)==1) { + if(pos) { + filter=optarg+pos; + } else { + filter=NULL; + } + } else { + fprintf(stderr, "tshark: invalid \"-z scsi,srt,<cmdset>[,<filter>]\" argument\n"); + exit(1); + } + + scsi_program=program; + rs=g_malloc(sizeof(scsistat_t)); + if(filter) { + rs->filter=g_strdup(filter); + } else { + rs->filter=NULL; + } + rs->cmdset=program; + + switch(program) { + case SCSI_DEV_SBC: + rs->prog="SBC (disk)"; + rs->cdbnames=scsi_sbc_vals; + break; + case SCSI_DEV_SSC: + rs->prog="SSC (tape)"; + rs->cdbnames=scsi_ssc_vals; + break; + case SCSI_DEV_CDROM: + rs->prog="MMC (cd/dvd)"; + rs->cdbnames=scsi_mmc_vals; + break; + case SCSI_DEV_SMC: + rs->prog="SMC (tape robot)"; + rs->cdbnames=scsi_smc_vals; + break; + case SCSI_DEV_OSD: + rs->prog="OSD (object based)"; + rs->cdbnames=scsi_osd_vals; + break; + default: + /* Default to the SBC (disk), since this is what EMC SCSI seem to always be */ + rs->cmdset=0; + rs->prog="SBC (disk)"; + rs->cdbnames=scsi_sbc_vals; + break; + } + rs->procedures=g_malloc(sizeof(scsi_procedure_t)*MAX_PROCEDURES); + for(i=0; i < MAX_PROCEDURES; i++) { + rs->procedures[i].proc=val_to_str(i, rs->cdbnames, "Unknown-0x%02x"); + rs->procedures[i].num=0; + rs->procedures[i].min.secs=0; + rs->procedures[i].min.nsecs=0; + rs->procedures[i].max.secs=0; + rs->procedures[i].max.nsecs=0; + rs->procedures[i].tot.secs=0; + rs->procedures[i].tot.nsecs=0; + } + error_string=register_tap_listener("scsi", rs, filter, 0, scsistat_reset, scsistat_packet, scsistat_draw); + if(error_string) { + /* error, we failed to attach to the tap. clean up */ + g_free(rs->procedures); + g_free(rs->filter); + g_free(rs); + + fprintf(stderr, "tshark: Couldn't register scsi,srt tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + +void +register_tap_listener_scsistat(void) +{ + register_stat_cmd_arg("scsi,srt,", scsistat_init, NULL); +} + diff --git a/ui/cli/tap-sctpchunkstat.c b/ui/cli/tap-sctpchunkstat.c new file mode 100644 index 0000000000..fcb8285240 --- /dev/null +++ b/ui/cli/tap-sctpchunkstat.c @@ -0,0 +1,254 @@ +/* tap_sctpchunkstat.c + * SCTP chunk counter for wireshark + * Copyright 2005 Oleg Terletsky <oleg.terletsky@comverse.com> + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include "epan/addr_resolv.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include "epan/value_string.h" +#include <epan/dissectors/packet-sctp.h> +#include <epan/to_str.h> + +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 SCTP_DATA_CHUNK_ID 0 +#define SCTP_INIT_CHUNK_ID 1 +#define SCTP_INIT_ACK_CHUNK_ID 2 +#define SCTP_SACK_CHUNK_ID 3 +#define SCTP_HEARTBEAT_CHUNK_ID 4 +#define SCTP_HEARTBEAT_ACK_CHUNK_ID 5 +#define SCTP_ABORT_CHUNK_ID 6 +#define SCTP_SHUTDOWN_CHUNK_ID 7 +#define SCTP_SHUTDOWN_ACK_CHUNK_ID 8 +#define SCTP_ERROR_CHUNK_ID 9 +#define SCTP_COOKIE_ECHO_CHUNK_ID 10 +#define SCTP_COOKIE_ACK_CHUNK_ID 11 +#define SCTP_ECNE_CHUNK_ID 12 +#define SCTP_CWR_CHUNK_ID 13 +#define SCTP_SHUTDOWN_COMPLETE_CHUNK_ID 14 +#define SCTP_AUTH_CHUNK_ID 0x16 +#define SCTP_ASCONF_ACK_CHUNK_ID 0x80 +#define SCTP_PKTDROP_CHUNK_ID 0x81 +#define SCTP_FORWARD_TSN_CHUNK_ID 0xC0 +#define SCTP_ASCONF_CHUNK_ID 0xC1 +#define SCTP_IETF_EXT 0xFF + +#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_malloc(sizeof(sctp_ep_t)))) + 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 int +sctpstat_packet(void *phs, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *phi) +{ + + 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 (0); + + 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 (0); + + + 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 (1); +} + + +static void +sctpstat_draw(void *phs) +{ + sctpstat_t *hs=(sctpstat_t *)phs; + sctp_ep_t* list = hs->ep_list, *tmp; + + 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) { + printf("|%15s|%5u|%15s|%5u|%8u|%8u|%8u|%8u|%8u|%8u|%8u|%8u|%8u|%8u|\n", + ep_address_to_str(&tmp->src),tmp->sport, + ep_address_to_str(&tmp->dst),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]); + } + printf("---------------------------------------------------------------------------------------------------------------------------------------\n"); +} + + +static void +sctpstat_init(const char *optarg, void* userdata _U_) +{ + sctpstat_t *hs; + GString *error_string; + + hs = (sctpstat_t *)g_malloc(sizeof(sctpstat_t)); + if(!strncmp(optarg,"sctp,stat,",11)){ + hs->filter=g_strdup(optarg+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); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(hs->filter); + g_free(hs); + + fprintf(stderr, "tshark: Couldn't register sctp,stat tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_sctpstat(void) +{ + register_stat_cmd_arg("sctp,stat", sctpstat_init,NULL); +} diff --git a/ui/cli/tap-sipstat.c b/ui/cli/tap-sipstat.c new file mode 100644 index 0000000000..b8273219ba --- /dev/null +++ b/ui/cli/tap-sipstat.c @@ -0,0 +1,443 @@ +/* tap_sipstat.c + * sip message counter for wireshark + * + * $Id$ + * 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 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include "epan/value_string.h" +#include <epan/dissectors/packet-sip.h> + +/* 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; + +/* TODO: extra codes to be added from SIP extensions? */ +static const value_string vals_status_code[] = { + { 100, "Trying"}, + { 180, "Ringing"}, + { 181, "Call Is Being Forwarded"}, + { 182, "Queued"}, + { 183, "Session Progress"}, + { 199, "Informational - Others" }, + + { 200, "OK"}, + { 202, "Accepted"}, + { 204, "No Notification"}, + { 299, "Success - Others"}, /* used to keep track of other Success packets */ + + { 300, "Multiple Choices"}, + { 301, "Moved Permanently"}, + { 302, "Moved Temporarily"}, + { 305, "Use Proxy"}, + { 380, "Alternative Service"}, + { 399, "Redirection - Others"}, + + { 400, "Bad Request"}, + { 401, "Unauthorized"}, + { 402, "Payment Required"}, + { 403, "Forbidden"}, + { 404, "Not Found"}, + { 405, "Method Not Allowed"}, + { 406, "Not Acceptable"}, + { 407, "Proxy Authentication Required"}, + { 408, "Request Timeout"}, + { 410, "Gone"}, + { 412, "Conditional Request Failed"}, + { 413, "Request Entity Too Large"}, + { 414, "Request-URI Too Long"}, + { 415, "Unsupported Media Type"}, + { 416, "Unsupported URI Scheme"}, + { 420, "Bad Extension"}, + { 421, "Extension Required"}, + { 422, "Session Timer Too Small"}, + { 423, "Interval Too Brief"}, + { 428, "Use Identity Header"}, + { 429, "Provide Referrer Identity"}, + { 430, "Flow Failed"}, + { 433, "Anonymity Disallowed"}, + { 436, "Bad Identity-Info"}, + { 437, "Unsupported Certificate"}, + { 438, "Invalid Identity Header"}, + { 439, "First Hop Lacks Outbound Support"}, + { 440, "Max-Breadth Exceeded"}, + { 470, "Consent Needed"}, + { 480, "Temporarily Unavailable"}, + { 481, "Call/Transaction Does Not Exist"}, + { 482, "Loop Detected"}, + { 483, "Too Many Hops"}, + { 484, "Address Incomplete"}, + { 485, "Ambiguous"}, + { 486, "Busy Here"}, + { 487, "Request Terminated"}, + { 488, "Not Acceptable Here"}, + { 489, "Bad Event"}, + { 491, "Request Pending"}, + { 493, "Undecipherable"}, + { 494, "Security Agreement Required"}, + { 499, "Client Error - Others"}, + + { 500, "Server Internal Error"}, + { 501, "Not Implemented"}, + { 502, "Bad Gateway"}, + { 503, "Service Unavailable"}, + { 504, "Server Time-out"}, + { 505, "Version Not Supported"}, + { 513, "Message Too Large"}, + { 599, "Server Error - Others"}, + + { 600, "Busy Everywhere"}, + { 603, "Decline"}, + { 604, "Does Not Exist Anywhere"}, + { 606, "Not Acceptable"}, + { 699, "Global Failure - Others"}, + + { 0, NULL} +}; + +/* 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 ; vals_status_code[i].strptr ; i++) + { + gint *key = g_malloc (sizeof(gint)); + sip_response_code_t *sc = g_malloc (sizeof(sip_response_code_t)); + *key = vals_status_code[i].value; + sc->packets=0; + sc->response_code = *key; + sc->name=vals_status_code[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) { + g_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=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 int +sipstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri) +{ + const sip_info_value_t *value=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 = g_malloc(sizeof(guint)); + sip_response_code_t *sc; + + /* Look up response code in hash table */ + *key = value->response_code; + sc = 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 0; + } + 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 = g_hash_table_lookup(sp->hash_responses, key); + if (sc==NULL) + { + return 0; + } + } + sc->packets++; + } + else if (value->request_method) + { + /* Requests */ + sip_request_method_t *sc; + + /* Look up the request method in the table */ + sc = g_hash_table_lookup(sp->hash_requests, value->request_method); + if (sc == NULL) + { + /* First of this type. Create structure and initialise */ + sc=g_malloc(sizeof(sip_request_method_t)); + 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 0; + } + + return 1; +} + +static void +sipstat_draw(void *psp ) +{ + sipstat_t *sp=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: %d", sp->packets); + printf("\nNumber of resent SIP messages: %d\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, + " 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, + " %-15s : %5d Packets\n"); + printf( "\n* Average setup time %d ms\n Min %d ms\n Max %d ms\n", sp->average_setup_time, sp->min_setup_time, sp->max_setup_time); + printf("===================================================================\n"); +} + +static void +sipstat_init(const char *optarg, void* userdata _U_) +{ + sipstat_t *sp; + const char *filter=NULL; + GString *error_string; + + if (strncmp (optarg, "sip,stat,", 9) == 0){ + filter=optarg+9; + } else { + filter=NULL; + } + + sp = g_malloc( sizeof(sipstat_t) ); + if(filter){ + sp->filter=g_strdup(filter); + } else { + sp->filter=NULL; + } + /*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); + if (error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(sp->filter); + g_free(sp); + fprintf (stderr, "tshark: Couldn't register sip,stat tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } + + sp->packets = 0; + sp->resent_packets = 0; + sip_init_hash(sp); +} + +void +register_tap_listener_sipstat(void) +{ + register_stat_cmd_arg("sip,stat", sipstat_init,NULL); +} diff --git a/ui/cli/tap-smbsids.c b/ui/cli/tap-smbsids.c new file mode 100644 index 0000000000..2955bcfbed --- /dev/null +++ b/ui/cli/tap-smbsids.c @@ -0,0 +1,100 @@ +/* tap-smbsids.c + * smbstat 2003 Ronnie Sahlberg + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/dissectors/packet-smb-sidsnooping.h> +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include "epan/value_string.h" +#include <epan/dissectors/packet-smb.h> + + +static int +smbsids_packet(void *pss _U_, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *psi _U_) +{ + return 1; +} + +static void +enum_sids(gpointer key _U_, gpointer value, gpointer userdata _U_) +{ + sid_name *sn=(sid_name *)value; + + printf("%-60s %s\n", sn->sid, sn->name); + return; +} + +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 *optarg _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); + if(error_string){ + fprintf(stderr, "tshark: Couldn't register smb,sids tap:%s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_smbsids(void) +{ + register_stat_cmd_arg("smb,sids", smbsids_init,NULL); +} + diff --git a/ui/cli/tap-smbstat.c b/ui/cli/tap-smbstat.c new file mode 100644 index 0000000000..2a3439e6e1 --- /dev/null +++ b/ui/cli/tap-smbstat.c @@ -0,0 +1,260 @@ +/* tap-smbstat.c + * smbstat 2003 Ronnie Sahlberg + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include "epan/value_string.h" +#include <epan/dissectors/packet-smb.h> +#include "timestats.h" + +#define MICROSECS_PER_SEC 1000000 +#define NANOSECS_PER_SEC 1000000000 + +/* used to keep track of the statistics for an entire program interface */ +typedef struct _smbstat_t { + char *filter; + timestat_t proc[256]; + timestat_t trans2[256]; + timestat_t nt_trans[256]; +} smbstat_t; + + + +static int +smbstat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, const void *psi) +{ + smbstat_t *ss=(smbstat_t *)pss; + const smb_info_t *si=psi; + nstime_t t, deltat; + timestat_t *sp=NULL; + + /* we are only interested in reply packets */ + if(si->request){ + return 0; + } + /* if we havnt seen the request, just ignore it */ + if(!si->sip){ + return 0; + } + + if(si->cmd==0xA0 && si->sip->extra_info_type == SMB_EI_NTI){ + smb_nt_transact_info_t *sti=(smb_nt_transact_info_t *)si->sip->extra_info; + + /*nt transaction*/ + if(sti){ + sp=&(ss->nt_trans[sti->subcmd]); + } + } else if(si->cmd==0x32 && si->sip->extra_info_type == SMB_EI_T2I){ + smb_transact2_info_t *st2i=(smb_transact2_info_t *)si->sip->extra_info; + + /*transaction2*/ + if(st2i){ + sp=&(ss->trans2[st2i->subcmd]); + } + } else { + sp=&(ss->proc[si->cmd]); + } + + /* calculate time delta between request and reply */ + t=pinfo->fd->abs_ts; + nstime_delta(&deltat, &t, &si->sip->req_time); + + if(sp){ + time_stat_update(sp,&deltat, pinfo); + } + + return 1; +} + +static void +smbstat_draw(void *pss) +{ + smbstat_t *ss=(smbstat_t *)pss; + guint32 i; + guint64 td; + printf("\n"); + printf("=================================================================\n"); + printf("SMB SRT Statistics:\n"); + printf("Filter: %s\n",ss->filter?ss->filter:""); + printf("Commands Calls Min SRT Max SRT Avg SRT\n"); + for(i=0;i<256;i++){ + /* nothing seen, nothing to do */ + if(ss->proc[i].num==0){ + continue; + } + + /* we deal with transaction2 later */ + if(i==0x32){ + continue; + } + + /* we deal with nt transaction later */ + if(i==0xA0){ + continue; + } + + /* Scale the average SRT in units of 1us and round to the nearest us. */ + td = ((guint64)(ss->proc[i].tot.secs)) * NANOSECS_PER_SEC + ss->proc[i].tot.nsecs; + + td = ((td / ss->proc[i].num) + 500) / 1000; + + printf("%-25s %6d %3d.%06d %3d.%06d %3" G_GINT64_MODIFIER "u.%06" G_GINT64_MODIFIER "u\n", + val_to_str_ext(i, &smb_cmd_vals_ext, "Unknown (0x%02x)"), + ss->proc[i].num, + (int)(ss->proc[i].min.secs),(ss->proc[i].min.nsecs+500)/1000, + (int)(ss->proc[i].max.secs),(ss->proc[i].max.nsecs+500)/1000, + td/MICROSECS_PER_SEC, td%MICROSECS_PER_SEC + ); + } + + printf("\n"); + printf("Transaction2 Commands Calls Min SRT Max SRT Avg SRT\n"); + for(i=0;i<256;i++){ + /* nothing seen, nothing to do */ + if(ss->trans2[i].num==0){ + continue; + } + + /* Scale the average SRT in units of 1us and round to the nearest us. */ + td = ((guint64)(ss->trans2[i].tot.secs)) * NANOSECS_PER_SEC + ss->trans2[i].tot.nsecs; + td = ((td / ss->trans2[i].num) + 500) / 1000; + + printf("%-25s %6d %3d.%06d %3d.%06d %3" G_GINT64_MODIFIER "u.%06" G_GINT64_MODIFIER "u\n", + val_to_str_ext(i, &trans2_cmd_vals_ext, "Unknown (0x%02x)"), + ss->trans2[i].num, + (int)(ss->trans2[i].min.secs),(ss->trans2[i].min.nsecs+500)/1000, + (int)(ss->trans2[i].max.secs),(ss->trans2[i].max.nsecs+500)/1000, + td/MICROSECS_PER_SEC, td%MICROSECS_PER_SEC + ); + } + + printf("\n"); + printf("NT Transaction Commands Calls Min SRT Max SRT Avg SRT\n"); + for(i=0;i<256;i++){ + /* nothing seen, nothing to do */ + if(ss->nt_trans[i].num==0){ + continue; + } + /* Scale the average SRT in units of 1us and round to the nearest us. */ + td = ((guint64)(ss->nt_trans[i].tot.secs)) * NANOSECS_PER_SEC + ss->nt_trans[i].tot.nsecs; + td = ((td / ss->nt_trans[i].num) + 500) / 1000; + + printf("%-25s %6d %3d.%06d %3d.%06d %3" G_GINT64_MODIFIER "u.%06" G_GINT64_MODIFIER "u\n", + val_to_str_ext(i, &nt_cmd_vals_ext, "Unknown (0x%02x)"), + ss->nt_trans[i].num, + (int)(ss->nt_trans[i].min.secs),(ss->nt_trans[i].min.nsecs+500)/1000, + (int)(ss->nt_trans[i].max.secs),(ss->nt_trans[i].max.nsecs+500)/1000, + td/MICROSECS_PER_SEC, td%MICROSECS_PER_SEC + ); + } + + printf("=================================================================\n"); +} + + +static void +smbstat_init(const char *optarg,void* userdata _U_) +{ + smbstat_t *ss; + guint32 i; + const char *filter=NULL; + GString *error_string; + + if(!strncmp(optarg,"smb,srt,",8)){ + filter=optarg+8; + } else { + filter=NULL; + } + + ss=g_malloc(sizeof(smbstat_t)); + if(filter){ + ss->filter=g_strdup(filter); + } else { + ss->filter=NULL; + } + + for(i=0;i<256;i++){ + ss->proc[i].num=0; + ss->proc[i].min_num=0; + ss->proc[i].max_num=0; + ss->proc[i].min.secs=0; + ss->proc[i].min.nsecs=0; + ss->proc[i].max.secs=0; + ss->proc[i].max.nsecs=0; + ss->proc[i].tot.secs=0; + ss->proc[i].tot.nsecs=0; + + ss->trans2[i].num=0; + ss->trans2[i].min_num=0; + ss->trans2[i].max_num=0; + ss->trans2[i].min.secs=0; + ss->trans2[i].min.nsecs=0; + ss->trans2[i].max.secs=0; + ss->trans2[i].max.nsecs=0; + ss->trans2[i].tot.secs=0; + ss->trans2[i].tot.nsecs=0; + + ss->nt_trans[i].num=0; + ss->nt_trans[i].min_num=0; + ss->nt_trans[i].max_num=0; + ss->nt_trans[i].min.secs=0; + ss->nt_trans[i].min.nsecs=0; + ss->nt_trans[i].max.secs=0; + ss->nt_trans[i].max.nsecs=0; + ss->nt_trans[i].tot.secs=0; + ss->nt_trans[i].tot.nsecs=0; + } + + error_string=register_tap_listener("smb", ss, filter, 0, NULL, smbstat_packet, smbstat_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(ss->filter); + g_free(ss); + + fprintf(stderr, "tshark: Couldn't register smb,srt tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_smbstat(void) +{ + register_stat_cmd_arg("smb,srt", smbstat_init,NULL); +} + diff --git a/ui/cli/tap-stats_tree.c b/ui/cli/tap-stats_tree.c new file mode 100644 index 0000000000..e9568b306c --- /dev/null +++ b/ui/cli/tap-stats_tree.c @@ -0,0 +1,145 @@ +/* tap-stats_tree.c + * tshark's tap implememntation of stats_tree + * 2005, Luis E. G. Ontanon + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <stdio.h> +#include <glib.h> +#include <epan/stats_tree_priv.h> +#include <epan/stat_cmd_args.h> +#include <epan/report_err.h> + +/* 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; + gchar *fmt; + stat_node *child; + + s = g_string_new("\n===================================================================\n"); + fmt = g_strdup_printf(" %%s%%-%us%%12s\t%%12s\t%%12s\n",stats_tree_branch_max_namelen(&st->root,0)); + g_string_append_printf(s,fmt,"",st->cfg->name,"value","rate","percent"); + g_free(fmt); + g_string_append_printf(s,"-------------------------------------------------------------------\n"); + + for (child = st->root.children; child; child = child->next ) { + stats_tree_branch_to_str(child,s,0); + } + + s = g_string_append(s,"\n===================================================================\n"); + + printf("%s",s->str); + +} + +static void +init_stats_tree(const char *optarg, void *userdata _U_) +{ + char *abbr = stats_tree_get_abbr(optarg); + GString *error_string; + stats_tree_cfg *cfg = NULL; + stats_tree *st = NULL; + + if (abbr) { + cfg = stats_tree_get_cfg_by_abbr(abbr); + + if (cfg != NULL) { + if (strncmp (optarg, cfg->pr->init_string, strlen(cfg->pr->init_string)) == 0){ + st = stats_tree_new(cfg,NULL,optarg+strlen(cfg->pr->init_string)); + } 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 abbr (%s) from arg '%s'",abbr,optarg); + return; + } + + error_string = register_tap_listener(st->cfg->tapname, + st, + st->filter, + st->cfg->flags, + stats_tree_reset, + stats_tree_packet, + draw_stats_tree); + + 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); + +} + +void +register_stats_tree_tap (gpointer k _U_, gpointer v, gpointer p _U_) +{ + stats_tree_cfg *cfg = (stats_tree_cfg *)v; + + cfg->pr = (tree_cfg_pres *)g_malloc(sizeof(tree_cfg_pres)); + cfg->pr->init_string = g_strdup_printf("%s,tree", cfg->abbr); + + register_stat_cmd_arg(cfg->pr->init_string, init_stats_tree, 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, NULL, NULL, NULL, + NULL, free_tree_presentation, NULL, NULL, NULL); +} diff --git a/ui/cli/tap-sv.c b/ui/cli/tap-sv.c new file mode 100644 index 0000000000..db3f84f8f8 --- /dev/null +++ b/ui/cli/tap-sv.c @@ -0,0 +1,87 @@ +/* tap-sv.c + * Copyright 2008 Michael Bernhard + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include <epan/packet_info.h> +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include <epan/nstime.h> +#include <epan/dissectors/packet-sv.h> + +static int +sv_packet(void *prs _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri) +{ + int i; + const sv_frame_data * sv_data = pri; + + printf("%f %u ", nstime_to_sec(&pinfo->fd->rel_ts), sv_data->smpCnt); + + for(i = 0; i < sv_data->num_phsMeas; i++) { + printf("%d ", sv_data->phsMeas[i].value); + } + + printf("\n"); + + return 0; +} + +static void +svstat_init(const char *optarg _U_, void* userdata _U_) +{ + GString *error_string; + + error_string = register_tap_listener( + "sv", + NULL, + NULL, + 0, + NULL, + sv_packet, + NULL); + if (error_string){ + /* error, we failed to attach to the tap. clean up */ + fprintf(stderr, "tshark: Couldn't register sv,stat tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + +void +register_tap_listener_sv(void) +{ + register_stat_cmd_arg("sv", svstat_init, NULL); +} diff --git a/ui/cli/tap-wspstat.c b/ui/cli/tap-wspstat.c new file mode 100644 index 0000000000..40bce6cefc --- /dev/null +++ b/ui/cli/tap-wspstat.c @@ -0,0 +1,286 @@ +/* tap-rpcstat.c + * wspstat 2003 Jean-Michel FAYARD + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* This module provides WSP statistics to tshark. + * It is only used by tshark and not wireshark + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include <epan/tap.h> +#include <epan/stat_cmd_args.h> +#include "epan/value_string.h" +#include <epan/dissectors/packet-wsp.h> + +/* 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(gint *key, wsp_status_code_t *data, char* format) +{ + if (data && (data->packets!=0)) + printf(format, *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=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 didnt 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 int +wspstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri) +{ + wspstat_t *sp=psp; + const wsp_info_value_t *value=pri; + gint idx = pdut2index(value->pdut); + int retour=0; + + if (value->status_code != 0) { + gint *key=g_malloc( sizeof(gint) ); + wsp_status_code_t *sc; + *key=value->status_code ; + sc = g_hash_table_lookup( + sp->hash, + key); + if (!sc) { + sc = g_malloc( sizeof(wsp_status_code_t) ); + sc -> packets = 1; + sc -> name = NULL; + g_hash_table_insert( + sp->hash, + key, + sc); + } else { + sc->packets++; + } + retour=1; + } + + + + if (idx!=0) { + sp->pdu_stats[ idx ].packets++; + retour = 1; + } + 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=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 %9d", sp->pdu_stats[i ].type, sp->pdu_stats[i ].packets); + printf(" || "); + if (ii< (sp->num_pdus) ) + printf("%-23s %9d\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, + " 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 *optarg, 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 (optarg, "wsp,stat," , 9)){ + filter=optarg+9; + } else { + filter=NULL; + } + + + sp = g_malloc( sizeof(wspstat_t) ); + sp->hash = g_hash_table_new( g_int_hash, g_int_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_malloc( sizeof(wsp_status_code_t) ); + key=g_malloc( sizeof(gint) ); + 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_malloc( (sp->num_pdus+1) * sizeof( wsp_pdu_t) ); + if(filter){ + sp->filter=g_strdup(filter); + } else { + sp->filter=NULL; + } + for (i=0;i<sp->num_pdus; i++) + { + sp->pdu_stats[i].packets=0; + sp->pdu_stats[i].type = match_strval_ext( index2pdut( i ), &wsp_vals_pdu_type_ext) ; + } + + error_string = register_tap_listener( + "wsp", + sp, + filter, + 0, + wspstat_reset, + wspstat_packet, + wspstat_draw); + if (error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(sp->pdu_stats); + g_free(sp->filter); + g_free(sp); + g_hash_table_foreach( sp->hash, (GHFunc) wsp_free_hash_table, NULL ) ; + g_hash_table_destroy( sp->hash ); + fprintf(stderr, "tshark: Couldn't register wsp,stat tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} +void +register_tap_listener_wspstat(void) +{ + register_stat_cmd_arg("wsp,stat,", wspstat_init,NULL); +} |