diff options
author | Ronnie Sahlberg <ronnie_sahlberg@ozemail.com.au> | 2002-09-04 09:40:29 +0000 |
---|---|---|
committer | Ronnie Sahlberg <ronnie_sahlberg@ozemail.com.au> | 2002-09-04 09:40:29 +0000 |
commit | 93a649db6f5db64dbdc80eece52f0891c8a72cfb (patch) | |
tree | 90bdf086756c8f41361d49ed02ea7adfa1bbd84a | |
parent | 7876ca4fe0bd8ce1cf1bd16d9e4cbdb8a4a007d3 (diff) | |
download | wireshark-93a649db6f5db64dbdc80eece52f0891c8a72cfb.tar.gz wireshark-93a649db6f5db64dbdc80eece52f0891c8a72cfb.tar.bz2 wireshark-93a649db6f5db64dbdc80eece52f0891c8a72cfb.zip |
Tap api. tap is a simple api that can be used for arbitrary extensions.
One example extension is rpcstat.
Try -Z rpc,rtt,100003,3 as argument to tethereal when reading a capture
containing NFSv3 packets.
tap-rpcstat.[ch] is intended to demonstrate the api and can be used to
base other extensions on.
svn path=/trunk/; revision=6175
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | Makefile.nmake | 4 | ||||
-rw-r--r-- | doc/tethereal.pod.template | 15 | ||||
-rw-r--r-- | epan/epan.c | 4 | ||||
-rw-r--r-- | packet-frame.c | 9 | ||||
-rw-r--r-- | packet-rpc.c | 32 | ||||
-rw-r--r-- | packet-rpc.h | 29 | ||||
-rw-r--r-- | tap-rpcstat.c | 338 | ||||
-rw-r--r-- | tap-rpcstat.h | 28 | ||||
-rw-r--r-- | tap.c | 406 | ||||
-rw-r--r-- | tap.h | 39 | ||||
-rw-r--r-- | tethereal.c | 31 |
12 files changed, 909 insertions, 32 deletions
diff --git a/Makefile.am b/Makefile.am index c668772a7b..14e20c7fd8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ # Makefile.am # Automake file for Ethereal # -# $Id: Makefile.am,v 1.465 2002/09/01 23:54:54 jmayer Exp $ +# $Id: Makefile.am,v 1.466 2002/09/04 09:40:24 sahlberg Exp $ # # Ethereal - Network traffic analyzer # By Gerald Combs <gerald@ethereal.com> @@ -591,6 +591,10 @@ ETHEREAL_COMMON_SRC = \ ringbuffer.h \ rpc_defrag.h \ smb.h \ + tap.c \ + tap.h \ + tap-rpcstat.c \ + tap-rpcstat.h \ util.c \ util.h \ x11-declarations.h \ diff --git a/Makefile.nmake b/Makefile.nmake index 7ce62c7594..70adb76acf 100644 --- a/Makefile.nmake +++ b/Makefile.nmake @@ -1,7 +1,7 @@ ## Makefile for building ethereal.exe with Microsoft C and nmake ## Use: $(MAKE) /$(MAKEFLAGS) -f makefile.nmake # -# $Id: Makefile.nmake,v 1.204 2002/08/29 12:25:42 girlich Exp $ +# $Id: Makefile.nmake,v 1.205 2002/09/04 09:40:24 sahlberg Exp $ include config.nmake include <win32.mak> @@ -304,6 +304,8 @@ ETHEREAL_COMMON_OBJECTS = \ reassemble.obj \ register.obj \ ringbuffer.obj \ + tap.obj \ + tap-rpcstat.obj \ util.obj \ xdlc.obj \ xmlstub.obj \ diff --git a/doc/tethereal.pod.template b/doc/tethereal.pod.template index dc4a26e4f9..062c02b836 100644 --- a/doc/tethereal.pod.template +++ b/doc/tethereal.pod.template @@ -29,6 +29,7 @@ S<[ B<-v> ]> S<[ B<-V> ]> S<[ B<-w> savefile ]> S<[ B<-x> ]> +S<[ B<-Z> statistics-string ]> S<[ filter expression ]> =head1 DESCRIPTION @@ -302,6 +303,20 @@ I<savefile> is "-". Cause B<Tethereal> to print a hex and ASCII dump of the packet data after printing the summary or protocol tree. +=item -Z + +Get B<Tethereal> to collect various types of statistics and display the result +after finishing reading the capture file. +Currently implemented statistics are : + +B<-Z> rpc,rtt,I<program>,I<version> + +Collect call/reply RTT data for I<program>/I<version>. Data collected is +number of calls for each procedure, MinRTT, MaxRTT and AvgRTT. +Example: use -Z rpc,rtt,100003,3 to collect data for NFS v3. This option can +be used multiple times on the command line. + + =back =head1 CAPTURE FILTER SYNTAX diff --git a/epan/epan.c b/epan/epan.c index 0e01ec42f9..91e0ed944f 100644 --- a/epan/epan.c +++ b/epan/epan.c @@ -1,6 +1,6 @@ /* epan.h * - * $Id: epan.c,v 1.19 2002/06/04 07:03:54 guy Exp $ + * $Id: epan.c,v 1.20 2002/09/04 09:40:29 sahlberg Exp $ * * Ethereal Protocol Analyzer Library * @@ -18,6 +18,7 @@ #include "except.h" #include "packet.h" #include "column-utils.h" +#include "../tap.h" /* * XXX - this takes the plugin directory as an argument, because @@ -47,6 +48,7 @@ epan_init(const char *plugin_dir, void (register_all_protocols)(void), except_init(); tvbuff_init(); frame_data_init(); + tap_init(); proto_init(plugin_dir,register_all_protocols,register_all_handoffs); packet_init(); dfilter_init(); diff --git a/packet-frame.c b/packet-frame.c index 5f57732ece..805d6ba529 100644 --- a/packet-frame.c +++ b/packet-frame.c @@ -2,7 +2,7 @@ * * Top-most dissector. Decides dissector based on Wiretap Encapsulation Type. * - * $Id: packet-frame.c,v 1.30 2002/08/28 21:00:13 jmayer Exp $ + * $Id: packet-frame.c,v 1.31 2002/09/04 09:40:24 sahlberg Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -33,6 +33,7 @@ #include <epan/tvbuff.h> #include "packet-frame.h" #include "prefs.h" +#include "tap.h" static int proto_frame = -1; static int hf_frame_arrival_time = -1; @@ -51,6 +52,8 @@ static int proto_unreassembled = -1; static gint ett_frame = -1; +static int frame_tap = -1; + static dissector_handle_t data_handle; static dissector_handle_t docsis_handle; @@ -188,6 +191,8 @@ dissect_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) show_reported_bounds_error(tvb, pinfo, tree); } ENDTRY; + + tap_queue_packet(frame_tap, pinfo, NULL); } void @@ -296,6 +301,8 @@ proto_register_frame(void) "Show File Offset", "Show File Offset", &show_file_off); prefs_register_bool_preference(frame_module, "force_docsis_encap", "Treat all frames as DOCSIS frames", "Treat all frames as DOCSIS Frames", &force_docsis_encap); + + frame_tap=register_tap("frame"); } void diff --git a/packet-rpc.c b/packet-rpc.c index 33c43a5138..363ad3f315 100644 --- a/packet-rpc.c +++ b/packet-rpc.c @@ -2,7 +2,7 @@ * Routines for rpc dissection * Copyright 1999, Uwe Girlich <Uwe.Girlich@philosys.de> * - * $Id: packet-rpc.c,v 1.103 2002/08/28 21:00:29 jmayer Exp $ + * $Id: packet-rpc.c,v 1.104 2002/09/04 09:40:24 sahlberg Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -42,6 +42,7 @@ #include "reassemble.h" #include "rpc_defrag.h" #include "packet-nfs.h" +#include "tap.h" /* * See: @@ -68,6 +69,7 @@ static gboolean rpc_defragment = FALSE; static struct true_false_string yesno = { "Yes", "No" }; +static int rpc_tap = -1; static const value_string rpc_msg_type[] = { { RPC_CALL, "Call" }, @@ -226,32 +228,10 @@ fragment_items rpc_frag_items = { }; /* Hash table with info on RPC program numbers */ -static GHashTable *rpc_progs; +GHashTable *rpc_progs; /* Hash table with info on RPC procedure numbers */ -static GHashTable *rpc_procs; - -typedef struct _rpc_proc_info_key { - guint32 prog; - guint32 vers; - guint32 proc; -} rpc_proc_info_key; - -typedef struct _rpc_proc_info_value { - gchar *name; - dissect_function_t *dissect_call; - dissect_function_t *dissect_reply; -} rpc_proc_info_value; - -typedef struct _rpc_prog_info_key { - guint32 prog; -} rpc_prog_info_key; - -typedef struct _rpc_prog_info_value { - int proto; - int ett; - char* progname; -} rpc_prog_info_value; +GHashTable *rpc_procs; static void dissect_rpc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); static void dissect_rpc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); @@ -2225,6 +2205,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, } } + tap_queue_packet(rpc_tap, pinfo, rpc_call); return TRUE; } @@ -3075,6 +3056,7 @@ proto_register_rpc(void) rpc_handle = find_dissector("rpc"); register_dissector("rpc-tcp", dissect_rpc_tcp, proto_rpc); rpc_tcp_handle = find_dissector("rpc-tcp"); + rpc_tap = register_tap("rpc"); /* * Init the hash tables. Dissectors for RPC protocols must diff --git a/packet-rpc.h b/packet-rpc.h index 60f45b38cf..1c6125f95f 100644 --- a/packet-rpc.h +++ b/packet-rpc.h @@ -1,6 +1,6 @@ /* packet-rpc.h * - * $Id: packet-rpc.h,v 1.37 2002/08/28 21:00:29 jmayer Exp $ + * $Id: packet-rpc.h,v 1.38 2002/09/04 09:40:24 sahlberg Exp $ * * (c) 1999 Uwe Girlich * @@ -137,5 +137,32 @@ extern int dissect_rpc_indir_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, int result_id, int prog_id, int vers_id, int proc_id); + +typedef struct _rpc_prog_info_key { + guint32 prog; +} rpc_prog_info_key; + +typedef struct _rpc_prog_info_value { + int proto; + int ett; + char* progname; +} rpc_prog_info_value; + +extern GHashTable *rpc_progs; + +typedef struct _rpc_proc_info_key { + guint32 prog; + guint32 vers; + guint32 proc; +} rpc_proc_info_key; + +typedef struct _rpc_proc_info_value { + gchar *name; + dissect_function_t *dissect_call; + dissect_function_t *dissect_reply; +} rpc_proc_info_value; + +extern GHashTable *rpc_procs; + #endif /* packet-rpc.h */ diff --git a/tap-rpcstat.c b/tap-rpcstat.c new file mode 100644 index 0000000000..cf80ef73e7 --- /dev/null +++ b/tap-rpcstat.c @@ -0,0 +1,338 @@ +/* tap-rpcstat.c + * rpcstat 2002 Ronnie Sahlberg + * + * $Id: tap-rpcstat.c,v 1.1 2002/09/04 09:40:24 sahlberg Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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 RTT statistics to tethereal. + * It is only used by tethereal and not ethereal + * + * 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 "tap.h" +#include "tap-rpcstat.h" +#include "packet-rpc.h" + + +/* used to keep track of statistics for a specific procedure */ +typedef struct _rpc_procedure_t { + 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 { + char *prog; + guint32 program; + guint32 version; + guint32 num_procedures; + rpc_procedure_t *procedures; +} rpcstat_t; + + + +/* This callback is never used by tethereal but it is here for completeness. + * When registering below, we could just have left this function as NULL. + * + * When used by ethereal, this function will be called whenever we would need + * to reset all state. Such as when ethereal opens a new file, when it + * starts a new capture, when it rescans the packetlist after some prefs have + * changed etc. + * So if your aplication has some state it needs to clean up in those + * situations, here is a good place to put that code. + */ +static void +rpcstat_reset(rpcstat_t *rs) +{ + 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 ethereal dissectors. Try to push as much processing as + * possible into (*draw) instead since that function executes asynchronously + * and does not affect the main threads performance. + * + * If it is possible, try to do all "filtering" explicitely 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 dependant. Since we register this one to the "rpc" + * tap the third parameters 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 didnt 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(rpcstat_t *rs, packet_info *pinfo, rpc_call_info_value *ri) +{ + 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 */ + delta.secs=pinfo->fd->abs_secs-ri->req_time.secs; + delta.nsecs=pinfo->fd->abs_usecs*1000-ri->req_time.nsecs; + if(delta.nsecs<0){ + delta.nsecs+=1000000000; + delta.secs--; + } + + 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>1000000000){ + rp->tot.nsecs-=1000000000; + rp->tot.secs++; + } + rp->num++; + + return 1; +} + +/* This callback is used when tethereal wants us to draw/update our + * data to the output device. Since this is tethereal only output is + * stdout. + * Tethereal will only call this callback once, which is when tethereal has + * finished reading all packets and exists. + * If used with ethereal 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 +rpcstat_draw(rpcstat_t *rs) +{ + guint32 i; +#ifdef G_HAVE_UINT64 + guint64 td; +#else + guint32 td; +#endif + printf("\n"); + printf("===================================================================\n"); + printf("%s Version %d RTT Statistics:\n", rs->prog, rs->version); + printf("Procedure Calls Min RTT Max RTT Avg RTT\n"); + for(i=0;i<rs->num_procedures;i++){ + /* scale it to units of 10us.*/ + /* for long captures with a large tot time, this can overflow on 32bit */ + td=(int)rs->procedures[i].tot.secs; + td=td*100000+(int)rs->procedures[i].tot.nsecs/10000; + if(rs->procedures[i].num){ + td/=rs->procedures[i].num; + } else { + td=0; + } + + printf("%-15s %6d %3d.%05d %3d.%05d %3d.%05d\n", + rs->procedures[i].proc, + rs->procedures[i].num, + (int)rs->procedures[i].min.secs,rs->procedures[i].min.nsecs/10000, + (int)rs->procedures[i].max.secs,rs->procedures[i].max.nsecs/10000, + td/100000, td%100000 + ); + } + 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 whick onc-rpc program/version we want to + * collect statistics for. + * This function is called from tethereal 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. + */ +void +rpcstat_init(guint32 program, guint32 version) +{ + rpcstat_t *rs; + guint32 i; +/* char filter[256];*/ + + + rs=g_malloc(sizeof(rpcstat_t)); + rs->prog=rpc_prog_name(program); + rs->program=program; + rs->version=version; + + 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,"tethereal: Invalid -Z rpc,rrt,%d,%d\n",rpc_program,rpc_version); + fprintf(stderr," Program:%d version:%d is not supported by tethereal.\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 + * we gain performance. Here we just tap everything from rpc without + * filtering and we do the filtering we need explicitely inside the callback + * itself (*packet) instead with a few if()s. + * + * This is what it would look like IF we wanted to use a filter when attaching + * it to the tap. + snprintf(filter, 255, "(rpc.msgtyp==1) && (rpc.program==%d) && (rpc.programversion==%d)",program,version); + register_tap_listener("rpc", rs, filter, (void*)rpcstat_reset, (void*)rpcstat_packet, (void*)rpcstat_draw); + */ + + if(register_tap_listener("rpc", rs, NULL, (void*)rpcstat_reset, (void*)rpcstat_packet, (void*)rpcstat_draw)){ + /* error, we failed to attach to the tap. clean up */ + g_free(rs->procedures); + g_free(rs); + + fprintf(stderr,"tethereal: rpcstat_init() failed to attach to tap.\n"); + exit(1); + } +} + + + + diff --git a/tap-rpcstat.h b/tap-rpcstat.h new file mode 100644 index 0000000000..28463ef6f2 --- /dev/null +++ b/tap-rpcstat.h @@ -0,0 +1,28 @@ +/* tap-rpcstat.h + * rpcstat 2002 Ronnie Sahlberg + * + * $Id: tap-rpcstat.h,v 1.1 2002/09/04 09:40:24 sahlberg Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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. + */ + +void rpcstat_init(guint32 program, guint32 version); + + + @@ -0,0 +1,406 @@ +/* tap.c + * packet tap interface 2002 Ronnie Sahlberg + * + * $Id: tap.c,v 1.1 2002/09/04 09:40:24 sahlberg Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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 + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#include <string.h> +#include "epan/packet_info.h" +#include "epan/dfilter/dfilter.h" +#include "tap.h" + +int tapping_is_active=0; + +typedef struct _tap_dissector_t { + struct _tap_dissector_t *next; + char *name; +} tap_dissector_t; +static tap_dissector_t *tap_dissector_list=NULL; + +/* + * This is the list of free and used packets queued for a tap. + * It is implemented here explicitely instead of using GLib objects + * in order to be as fast as possible as we need to build and tear down the + * queued list at least once for each packet we see, thus we must be able + * to build and tear it down as fast as possible. + */ +typedef struct _tap_packet_t { + struct _tap_packet_t *next; + int tap_id; + packet_info *pinfo; + void *tap_specific_data; +} tap_packet_t; +static tap_packet_t *tap_packet_list_free=NULL; +static tap_packet_t *tap_packet_list_queue=NULL; +#define TAP_PACKET_QUEUE_LEN 100 + + +typedef struct _tap_listener_t { + struct _tap_listener_t *next; + int tap_id; + int needs_redraw; + dfilter_t *code; + void *tapdata; + void (*reset)(void *tapdata); + int (*packet)(void *tapdata, packet_info *pinfo, void *data); + void (*draw)(void *tapdata); +} tap_listener_t; +static tap_listener_t *tap_listener_queue=NULL; + +static union wtap_pseudo_header *l_pseudo_header=NULL; +static const u_char *l_buf=NULL; +static frame_data *l_fdata; + + +/* ********************************************************************** + * Init routine only called from epan at application startup + * ********************************************************************** */ +/* This function is called once when ethereal starts up and is used + to init any data structures we may need later. +*/ +void +tap_init(void) +{ + int i; + tap_packet_t *tpt; + + for(i=0;i<TAP_PACKET_QUEUE_LEN;i++){ + tpt=malloc(sizeof(tap_packet_t)); + tpt->next=tap_packet_list_free; + tap_packet_list_free=tpt; + } + tap_packet_list_queue=NULL; + + return; +} + + + +/* ********************************************************************** + * Functions called from dissector when made tappable + * ********************************************************************** */ +/* the following two functions are used from dissectors to + 1, register the ability to tap packets from this subdissector + 2, push packets encountered by the subdissector to anyone tapping +*/ + +/* This function registers that a dissector has the packet tap ability + available. The name parameter is the name of this tap and extensions can + use open_tap(char *name,... to specify that it wants to receive packets/ + events from this tap. + + This function is only to be called once, when the dissector initializes. + + The return value from this call is later used as a parameter to the + tap_packet(unsinged int *tap_id,... + call so that the tap subsystem knows to which tap point this tapped + packet is associated. +*/ +int +register_tap(char *name) +{ + tap_dissector_t *td, *tdl; + int i; + + td=malloc(sizeof(tap_dissector_t)); + td->next=NULL; + td->name=malloc(strlen(name)); + strcpy(td->name, name); + + if(!tap_dissector_list){ + tap_dissector_list=td; + i=1; + } else { + for(i=2,tdl=tap_dissector_list;tdl->next;i++,tdl=tdl->next) + ; + tdl->next=td; + } + return i; +} + + +/* Everytime the dissector has finished dissecting a packet (and all + subdissectors have returned) and if the dissector has been made "tappable" + it will push some data to everyone tapping this layer by a call + to tap_packet(). + The first parameter is the tap_id returned by the register_tap() + call for this dissector (so the tap system can keep track of who it came + from and who is listening to it) + The second is the packet_info structure which many tap readers will find + interesting. + The third argument is specific to each tap point or NULL if no additional + data is available to this tap. A tap point in say IP will probably want to + push the IP header structure here. Same thing for TCP and ONCRPC. + + The pinfo and the specific pointer are what is supplied to every listener + in the read_callback() call made to every one currently listening to this + tap. + + The tap reader is responsible to know how to parse any structure pointed + to by the tap specific data pointer. +*/ +void +tap_queue_packet(int tap_id, packet_info *pinfo, void *tap_specific_data) +{ + tap_packet_t *tpt; + + if(!tapping_is_active){ + return; + } + +/*printf("tap_queue_packet %d %d\n",pinfo->fd->num,tapping_is_active);*/ + /* get a free tap_packet structure, this is CHEAP */ + tpt=tap_packet_list_free; + tap_packet_list_free=tpt->next; + tpt->next=tap_packet_list_queue; + tap_packet_list_queue=tpt; + + tpt->tap_id=tap_id; + tpt->pinfo=pinfo; + tpt->tap_specific_data=tap_specific_data; +} + + + + + +/* ********************************************************************** + * Functions used by file.c to drive the tap subsystem + * ********************************************************************** */ +/* This function is used to delete/initialize the tap queue. + This is very cheap since we just prepend the used queue to the free queue. +*/ +void +tap_queue_init(union wtap_pseudo_header *pseudo_header, const u_char *buf, frame_data *fdata) +{ + tap_packet_t *tpt; + + l_pseudo_header=pseudo_header; + l_buf=buf; + l_fdata=fdata; + +/*printf("tap_queue_init\n");*/ + tapping_is_active=1; + + tpt=tap_packet_list_queue; + if(!tpt){ + return; + } + + for(;tpt->next;tpt=tpt->next) + ; + + tpt->next=tap_packet_list_free; + tap_packet_list_free=tap_packet_list_queue; + tap_packet_list_queue=NULL; + +} + +/* this function is called after a packet has been fully dissected to push the tapped + data to all extensions that has callbacks registered. +*/ +void +tap_push_tapped_queue(void) +{ + tap_packet_t *tp; + tap_listener_t *tl; + epan_dissect_t *edt; + + tapping_is_active=0; +/*printf("tap_push_tapped_queue\n");*/ + for(tp=tap_packet_list_queue;tp;tp=tp->next){ + for(tl=tap_listener_queue;tl;tl=tl->next){ + if(tp->tap_id==tl->tap_id){ + int passed=TRUE; + if(tl->code){ + edt=epan_dissect_new(TRUE, FALSE); + epan_dissect_prime_dfilter(edt, tl->code); + epan_dissect_run(edt, + l_pseudo_header, + l_buf, + l_fdata, + NULL); + passed=dfilter_apply_edt(tl->code, edt); + epan_dissect_free(edt); + } + if(passed && tl->packet){ + tl->needs_redraw|=tl->packet(tl->tapdata, tp->pinfo, tp->tap_specific_data); + } + } + } + } + + /*draw_tap_listeners();*/ /*XXX until we have a thread for this */ +} + +/* This function is called when we need to reset all tap listeners, for example + when we open/start a new capture or if we need to rescan the packet list. +*/ +void +reset_tap_listeners(void) +{ + tap_listener_t *tl; + +/*printf("reset_tap_listeners\n");*/ + for(tl=tap_listener_queue;tl;tl=tl->next){ + if(tl->reset){ + tl->reset(tl->tapdata); + } + tl->needs_redraw=1; + } +} + +/* This function is called when we need to redraw all tap listeners, for example + when we open/start a new capture or if we need to rescan the packet list. + this one should be called from a low priority thread say once every 3 seconds + + If draw_all is true, redraw all aplications regardless if they have + changed or not. +*/ +void +draw_tap_listeners(gboolean draw_all) +{ + tap_listener_t *tl; + +/*printf("draw_tap_listeners\n");*/ + for(tl=tap_listener_queue;tl;tl=tl->next){ + if(tl->needs_redraw || draw_all){ + if(tl->draw){ + tl->draw(tl->tapdata); + } + } + tl->needs_redraw=0; + } +} + + + +/* ********************************************************************** + * Functions used by the RS (Really Simple api) extension to + * 1, register that a really simple extension is available for use by + * ethereal. + * 2, start tapping from a subdissector + * 3, close an already open tap + * ********************************************************************** */ +/* this function will return the tap_id for the specific protocol tap + or 0 if no such tap was found. + */ +int +find_tap_id(char *name) +{ + tap_dissector_t *td; + int i; + + for(i=1,td=tap_dissector_list;td;i++,td=td->next) { + if(!strcmp(td->name,name)){ + return i; + } + } + return 0; +} + +/* this function attaches the tap_listener to the named tap. + * function returns : + * 0: ok. + * !0: error +*/ +int +register_tap_listener(char *tapname, void *tapdata, char *fstring, void (*reset)(void *tapdata), int (*packet)(void *tapdata, packet_info *pinfo, void *data), void (*draw)(void *tapdata)) +{ + tap_listener_t *tl; + int tap_id; + + tap_id=find_tap_id(tapname); + if(!tap_id){ + fprintf(stderr, "tap not found\n"); + exit(10); + } + + tl=g_malloc(sizeof(tap_listener_t)); + tl->code=NULL; + tl->needs_redraw=1; + if(fstring){ + if(!dfilter_compile(fstring ,&tl->code)){ + g_free(tl); + fprintf(stderr,"register_tap_listener(): %s\n", dfilter_error_msg); + return 1; + } + } + + tl->tap_id=tap_id; + tl->tapdata=tapdata; + tl->reset=reset; + tl->packet=packet; + tl->draw=draw; + tl->next=tap_listener_queue; + + tap_listener_queue=tl; + + return 0; +} + +/* this function removes a tap listener +*/ +void +remove_tap_listener(void *tapdata) +{ + tap_listener_t *tl=NULL,*tl2; + + if(!tap_listener_queue){ + return; + } + + if(tap_listener_queue->tapdata==tapdata){ + tl=tap_listener_queue; + tap_listener_queue=tap_listener_queue->next; + } else { + for(tl2=tap_listener_queue;tl2->next;tl=tl2->next){ + if(tl2->next->tapdata==tapdata){ + tl=tl2->next; + tl2->next=tl2->next->next; + break; + } + + } + } + + if(tl->code){ + dfilter_free(tl->code); + } + g_free(tl); + return; +} + + @@ -0,0 +1,39 @@ +/* tap.h + * packet tap interface 2002 Ronnie Sahlberg + * + * $Id: tap.h,v 1.1 2002/09/04 09:40:24 sahlberg Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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. + */ + + +void tap_init(void); +int register_tap(char *name); +int find_tap_id(char *name); +void tap_queue_packet(int tap_id, packet_info *pinfo, void *tap_specific_data); +void tap_queue_init(union wtap_pseudo_header *pseudo_header, const u_char *buf, frame_data *fdata); +void tap_push_tapped_queue(void); +extern int tapping_is_active; +void reset_tap_listeners(void); +void draw_tap_listeners(gboolean draw_all); +int register_tap_listener(char *tapname, void *tapdata, char *fstring, void (*reset)(void *tapdata), int (*packet)(void *tapdata, packet_info *pinfo, void *data), void (*draw)(void *tapdata)); +void remove_tap_listener(void *tapdata); + + + diff --git a/tethereal.c b/tethereal.c index 22d8a50de2..d7be13a6cb 100644 --- a/tethereal.c +++ b/tethereal.c @@ -1,6 +1,6 @@ /* tethereal.c * - * $Id: tethereal.c,v 1.153 2002/08/28 21:00:41 jmayer Exp $ + * $Id: tethereal.c,v 1.154 2002/09/04 09:40:24 sahlberg Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -95,6 +95,8 @@ #include "capture_stop_conditions.h" #include "ringbuffer.h" #include <epan/epan_dissect.h> +#include "tap.h" +#include "tap-rpcstat.h" #ifdef HAVE_LIBPCAP #include <wiretap/wtap-capture.h> @@ -205,6 +207,7 @@ print_usage(gboolean print_ver) fprintf(stderr, "\t[ -o <preference setting> ] ... [ -r <infile> ] [ -R <read filter> ]\n"); fprintf(stderr, "\t[ -t <time stamp format> ] [ -w <savefile> ] [ -x ]\n"); #endif + fprintf(stderr, "\t[ -Z <statistics string> ]\n"); fprintf(stderr, "Valid file type arguments to the \"-F\" flag:\n"); for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) { if (wtap_dump_can_open(i)) @@ -453,7 +456,7 @@ main(int argc, char *argv[]) #endif /* Now get our args */ - while ((opt = getopt(argc, argv, "a:b:c:Df:F:hi:lnN:o:pqr:R:s:St:vw:Vx")) != -1) { + while ((opt = getopt(argc, argv, "a:b:c:Df:F:hi:lnN:o:pqr:R:s:St:vw:VxZ:")) != -1) { switch (opt) { case 'a': /* autostop criteria */ #ifdef HAVE_LIBPCAP @@ -644,6 +647,25 @@ main(int argc, char *argv[]) case 'x': /* Print packet data in hex (and ASCII) */ print_hex = TRUE; break; + case 'Z': + if(!strncmp(optarg,"rpc,",4)){ + if(!strncmp(optarg,"rpc,rtt,",8)){ + int rpcprogram, rpcversion; + if(sscanf(optarg,"rpc,rtt,%d,%d",&rpcprogram,&rpcversion)==2){ + rpcstat_init(rpcprogram,rpcversion); + } else { + fprintf(stderr, "tethereal: invalid \"-Z rpc,rtt,<program>,<version>\" argument\n"); + exit(1); + } + } else { + fprintf(stderr, "tethereal: invalid -Z argument. Argument must be \"-Z rpc,rtt,...\"\n"); + exit(1); + } + } else { + fprintf(stderr, "tethereal: invalid -Z argument. Argument must be \"-Z rpc,...\"\n"); + exit(1); + } + break; default: case '?': /* Bad flag - print usage message */ arg_error = TRUE; @@ -885,6 +907,7 @@ main(int argc, char *argv[]) #endif } + draw_tap_listeners(TRUE); epan_cleanup(); return 0; @@ -1640,7 +1663,11 @@ wtap_dispatch_cb_print(guchar *user, const struct wtap_pkthdr *phdr, if (cf->rfcode) { epan_dissect_prime_dfilter(edt, cf->rfcode); } + + tap_queue_init(pseudo_header, buf, &fdata); epan_dissect_run(edt, pseudo_header, buf, &fdata, verbose ? NULL : &cf->cinfo); + tap_push_tapped_queue(); + if (cf->rfcode) { passed = dfilter_apply_edt(cf->rfcode, edt); } |