aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRonnie Sahlberg <ronnie_sahlberg@ozemail.com.au>2002-09-04 09:40:29 +0000
committerRonnie Sahlberg <ronnie_sahlberg@ozemail.com.au>2002-09-04 09:40:29 +0000
commit93a649db6f5db64dbdc80eece52f0891c8a72cfb (patch)
tree90bdf086756c8f41361d49ed02ea7adfa1bbd84a
parent7876ca4fe0bd8ce1cf1bd16d9e4cbdb8a4a007d3 (diff)
downloadwireshark-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.am6
-rw-r--r--Makefile.nmake4
-rw-r--r--doc/tethereal.pod.template15
-rw-r--r--epan/epan.c4
-rw-r--r--packet-frame.c9
-rw-r--r--packet-rpc.c32
-rw-r--r--packet-rpc.h29
-rw-r--r--tap-rpcstat.c338
-rw-r--r--tap-rpcstat.h28
-rw-r--r--tap.c406
-rw-r--r--tap.h39
-rw-r--r--tethereal.c31
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);
+
+
+
diff --git a/tap.c b/tap.c
new file mode 100644
index 0000000000..ce57f786ca
--- /dev/null
+++ b/tap.c
@@ -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;
+}
+
+
diff --git a/tap.h b/tap.h
new file mode 100644
index 0000000000..fddcb3e801
--- /dev/null
+++ b/tap.h
@@ -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);
}