Ethereal-dev: [Ethereal-dev] nfs filename snooping

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: "Pia Sahlberg" <piabar@xxxxxxxxxxx>
Date: Thu, 09 Aug 2001 12:51:47 +0000
Hi list
 
If you dissect a lot of NFS traffic, doesnt it just suck to try to keep track of fhandles manually, especially since you can not use fhandles in display filters? or to try to see all NFS/MOUNT/NLM packets which touches
a specific file/fhandle?
 
Attached are two files:
one text file that describes the feature that I think would be suitable to put in the manual page and on Richards user guide. It may need to be rewritten since english is not my first language.
 
a patch for nfs:
The attached patch scans the NFSv3 LOOKUP packets to create a mapping between fhandles and filenames. Everytime a fhandle is displayed the corresponding filename will also
be displayed (if known).
 
best regards
   ronnie sahlberg


Get your FREE download of MSN Explorer at http://explorer.msn.com
nfs filename snooping
=====================
[Menu: Edit/Preferences/NFS/snoop fhandle to filename mappings]
When enabled, this option will enable ethereal to snoop all LOOKUP packets
to learn the mapping between fhandles and filenames.
For every fhandle where the corresponding filename is learned an additional
item will be displayed in the tree pane :

Example: "Filename: foo.txt  (snooped from frames 29 and 30)"

This entry use the same "nfs.name" display filter variable as the normal
nfs filenames which means that applying the display filter "nfs.name=='foo.txt'"
will find both where the filename "foo.txt" was found in a packet as well as
all fhandle structures seen which corresponds to that name.

This setting also affects the nfs related protocols, NLM and MOUNT, which use
the same fhandle structures as NFS.


This feature can only be used in ethereal and is not available in tethereal.
By enabling this feature the memory requirement for the ethereal will increase.
Performance can be (but not be noticeably) affected by this feature.


BUG:
This feature will currently only examine NFSv3:LOOKUP packets.
Please expand to other versions of NFS/other commands as required.


diff -u -r -x *.[^ch]|nmake|am ethereal-orig/packet-nfs.c ethereal/packet-nfs.c
--- ethereal-orig/packet-nfs.c	Tue Aug  7 08:45:13 2001
+++ ethereal/packet-nfs.c	Thu Aug  9 10:37:57 2001
@@ -37,7 +37,7 @@

#include <string.h>

-
+#include "prefs.h"
#include "packet-rpc.h"
#include "packet-nfs.h"

@@ -350,6 +350,152 @@
static gint ett_nfs_fs_location4 = -1;
static gint ett_nfs_open4_result_flags = -1;

+
+/*
+ * functions to map fhandle<->filename
+ */
+static gboolean nfs_fhandle2name_snooping = FALSE;
+static GHashTable *lookup_trace_table = NULL;
+static GHashTable *fhandle2name_table = NULL;
+static GMemChunk *lookup_trace_key_chunk = NULL;
+static GMemChunk *lookup_trace_data_chunk = NULL;
+static int lookup_trace_init_count = 100;
+
+typedef struct _lookup_trace_key {
+	address src;
+	address dst;
+	guint32 xid;
+} lookup_trace_key;
+
+typedef struct _fhandle_name {
+	int request_frame;
+	int response_frame;
+	int fhandle_len;
+	char *fhandle;
+	int name_len;
+	char *name;
+} fhandle_name;
+
+static gint
+lookup_trace_equal(gconstpointer k1, gconstpointer k2)
+{
+	lookup_trace_key *key1 = (lookup_trace_key *)k1;
+	lookup_trace_key *key2 = (lookup_trace_key *)k2;
+
+	return ( ( (ADDRESSES_EQUAL(&key1->src, &key2->src)) &&
+	           (ADDRESSES_EQUAL(&key1->dst, &key2->dst)) &&
+	           (key1->xid == key2->xid)
+		) ?
+		TRUE : FALSE);
+}
+
+static gint
+fhandle2name_equal(gconstpointer k1, gconstpointer k2)
+{
+	fhandle_name *key1 = (fhandle_name *)k1;
+	fhandle_name *key2 = (fhandle_name *)k2;
+
+	if(key1->fhandle_len!=key2->fhandle_len){
+		return FALSE;
+	}
+
+	return (!memcmp(key1->fhandle, key2->fhandle, key1->fhandle_len));
+}
+
+static guint
+lookup_trace_hash(gconstpointer k)
+{
+	lookup_trace_key *key = (lookup_trace_key *)k;
+
+	return key->xid; /* this is probably good enough*/
+}
+
+static guint
+fhandle2name_hash(gconstpointer k)
+{
+	fhandle_name *key = (fhandle_name *)k;
+	int hash, i;
+
+	hash=0;
+	for(i=0;i<key->fhandle_len;i++){
+		hash|=key->fhandle[i];
+	}
+
+	return hash;
+}
+
+static gboolean
+lookup_trace_free_all(gpointer key_arg, gpointer value, gpointer user_data)
+{
+	lookup_trace_key *key = (lookup_trace_key *)key_arg;
+	fhandle_name *fhn = (fhandle_name *)value;
+
+	g_free((gpointer)key->src.data);
+	g_free((gpointer)key->dst.data);
+
+	if(fhn->fhandle){
+		g_free(fhn->fhandle);
+	}
+	if(fhn->name){
+		g_free(fhn->name);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+fhandle2name_free_all(gpointer key_arg, gpointer value, gpointer user_data)
+{
+	return TRUE;
+}
+
+static void
+lookup_trace_table_init(void)
+{
+	if(lookup_trace_table){
+		g_hash_table_foreach_remove(lookup_trace_table,
+			lookup_trace_free_all, NULL);
+	} else {
+		lookup_trace_table = g_hash_table_new(lookup_trace_hash,
+			lookup_trace_equal);
+	}
+
+	if(fhandle2name_table){
+		g_hash_table_foreach_remove(fhandle2name_table,
+			fhandle2name_free_all, NULL);
+	} else {
+		fhandle2name_table = g_hash_table_new(fhandle2name_hash,
+			fhandle2name_equal);
+	}
+}
+
+void
+lookup_trace_init(void)
+{
+	lookup_trace_table_init();
+
+	if(lookup_trace_key_chunk){
+		g_mem_chunk_destroy(lookup_trace_key_chunk);
+	}
+	lookup_trace_key_chunk = g_mem_chunk_new("lookup_trace_key_chunk",
+		sizeof(lookup_trace_key),
+		lookup_trace_init_count*sizeof(lookup_trace_key),
+		G_ALLOC_ONLY);
+
+	if(lookup_trace_data_chunk){
+		g_mem_chunk_destroy(lookup_trace_data_chunk);
+	}
+	lookup_trace_data_chunk = g_mem_chunk_new("lookup_trace_data_chunk",
+		sizeof(fhandle_name),
+		lookup_trace_init_count*sizeof(fhandle_name),
+		G_ALLOC_ONLY);
+}
+
+
+
+
+
+
/* file handle dissection */

#define FHT_UNKNOWN		0
@@ -1855,6 +2001,48 @@
dissect_filename3(tvbuff_t *tvb, int offset, packet_info *pinfo,
    proto_tree *tree, int hf, char **string_ret)
{
+
+	/* is filehandle->filename snooping activated? (only check new frames)*/
+	if(nfs_fhandle2name_snooping && (!pinfo->fd->flags.visited)){
+		rpc_call_info_value *civ = pinfo->private;
+
+		/* v3 LOOKUP calls will give us a filename */
+		if( (civ->vers==3)
+		  &&(civ->proc==3)
+		  &&(civ->call)){
+			lookup_trace_key ltk;
+			fhandle_name *fhn;
+
+			COPY_ADDRESS(&ltk.src, &pinfo->src);
+			COPY_ADDRESS(&ltk.dst, &pinfo->dst);
+			ltk.xid = civ->xid;
+
+			fhn = g_hash_table_lookup(lookup_trace_table, &ltk);
+			if(!fhn){
+				/* we havnt seen this call before */
+				lookup_trace_key *new_ltk;
+				fhandle_name *new_fhn;
+
+				new_ltk = g_mem_chunk_alloc(lookup_trace_key_chunk);
+				COPY_ADDRESS(&new_ltk->src, &pinfo->src);
+				COPY_ADDRESS(&new_ltk->dst, &pinfo->dst);
+				new_ltk->xid = civ->xid;
+
+				new_fhn = g_mem_chunk_alloc(lookup_trace_data_chunk);
+				new_fhn->request_frame=civ->req_num;
+				new_fhn->response_frame=-1;
+				new_fhn->name_len=tvb_get_ntohl(tvb, offset);
+				new_fhn->name=g_malloc(new_fhn->name_len+1);
+				tvb_memcpy(tvb, new_fhn->name, offset+4, new_fhn->name_len);
+ new_fhn->name[new_fhn->name_len]=0; /* make sure it is null-terminated */
+				new_fhn->fhandle_len=0;
+				new_fhn->fhandle=NULL;
+
+				g_hash_table_insert(lookup_trace_table, new_ltk, new_fhn);
+			}
+		}
+	}
+
	offset = dissect_rpc_string(tvb, pinfo, tree, hf, offset, string_ret);
	return offset;
}
@@ -2086,6 +2274,39 @@
	guint fh3_fill;
	proto_item* fitem;
	proto_tree* ftree = NULL;
+	fhandle_name *fhn = NULL;
+
+	/* is filehandle->filename snooping activated? (only check new frames)*/
+	if(nfs_fhandle2name_snooping && (!pinfo->fd->flags.visited)){
+		rpc_call_info_value *civ = pinfo->private;
+
+		/* v3 LOOKUP replies will give us a filehandle */
+		if( (civ->vers==3)
+		  &&(civ->proc==3)
+		  &&(!civ->call)){
+
+			/* ok so we have got a filehandle in this reply packet.
+			   Do we know the matching filename from the call packet? */
+			lookup_trace_key ltk;
+
+			/* the reply has src/dst swapped compared to the call*/
+			COPY_ADDRESS(&ltk.src, &pinfo->dst);
+			COPY_ADDRESS(&ltk.dst, &pinfo->src);
+			ltk.xid = civ->xid;
+
+			fhn = g_hash_table_lookup(lookup_trace_table, &ltk);
+			if(fhn){
+				/* ok we know the filename from the call and
+				   the filehandle from this packet. store it*/
+				fhn->response_frame=civ->rep_num;
+				fhn->fhandle_len=tvb_get_ntohl(tvb, offset);
+				fhn->fhandle=g_malloc(fhn->fhandle_len);
+				tvb_memcpy(tvb, fhn->fhandle, offset+4, fhn->fhandle_len);
+
+				g_hash_table_insert(fhandle2name_table, fhn, fhn);
+			}
+		}
+	}

	fh3_len = tvb_get_ntohl(tvb, offset+0);
	fh3_len_full = rpc_roundup(fh3_len);
@@ -2102,7 +2323,23 @@
		proto_tree_add_uint(ftree, hf_nfs_fh_length, tvb, offset+0, 4,
				fh3_len);
		dissect_fhandle_data(tvb, offset+4, pinfo, ftree, fh3_len);
+
+	}
+
+	/* is filehandle->filename snooping activated? */
+	if(nfs_fhandle2name_snooping){
+		if(!fhn){
+			static fhandle_name fhn_key;
+			fhn_key.fhandle_len=tvb_get_ntohl(tvb, offset);
+			fhn_key.fhandle=g_malloc(fhn_key.fhandle_len);
+			tvb_memcpy(tvb, fhn_key.fhandle, offset+4, fhn_key.fhandle_len);
+			fhn = g_hash_table_lookup(fhandle2name_table, &fhn_key);
+		}
+		if(fhn){
+ proto_tree_add_string_format(ftree, hf_nfs_name, tvb, 0, 0, fhn->name, "Filename: %s (snooped from frames %d and %d)", fhn->name, fhn->request_frame, fhn->response_frame);
+		}
	}
+
	offset += 4 + fh3_len_full;
	return offset;
}
@@ -6723,9 +6960,20 @@
		&ett_nfs_fs_location4,
		&ett_nfs_open4_result_flags
	};
+	module_t *nfs_module;
+
+
	proto_nfs = proto_register_protocol("Network File System", "NFS", "nfs");
	proto_register_field_array(proto_nfs, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));
+
+	/* register configuration options */
+	nfs_module = prefs_register_protocol(proto_nfs, NULL);
+	prefs_register_bool_preference(nfs_module, "nfs filename snooping",
+		"fhandle/filename LOOPKUP snooping",
+ "This option will snoop LOOKUP calls to map all fhandles to filenames where possible",
+		&nfs_fhandle2name_snooping);
+	register_init_routine(lookup_trace_init);
}

void
diff -u -r -x *.[^ch]|nmake|am ethereal-orig/packet-rpc.c ethereal/packet-rpc.c
--- ethereal-orig/packet-rpc.c	Tue Aug  7 08:45:14 2001
+++ ethereal/packet-rpc.c	Wed Aug  8 09:21:02 2001
@@ -371,18 +371,6 @@

static GMemChunk *rpc_call_info_key_chunk;

-typedef struct _rpc_call_info_value {
-	guint32	req_num;	/* frame number of first request seen */
-	guint32	rep_num;	/* frame number of first reply seen */
-	guint32	prog;
-	guint32	vers;
-	guint32	proc;
-	guint32 flavor;
-	guint32 gss_proc;
-	guint32 gss_svc;
-	rpc_proc_info_value*	proc_info;
-} rpc_call_info_value;
-
static GMemChunk *rpc_call_info_value_chunk;

static GHashTable *rpc_calls;
@@ -1417,6 +1405,10 @@
			   conversation, so it's probably not an RPC reply. */
			return FALSE;
		}
+		/* pass rpc_info to subdissectors */
+		rpc_call->call = FALSE;
+		pinfo->private = rpc_call;
+
		break;

	default:
@@ -1593,6 +1585,7 @@
			rpc_call->prog = prog;
			rpc_call->vers = vers;
			rpc_call->proc = proc;
+			rpc_call->xid = xid;
			rpc_call->flavor = flavor;
			rpc_call->gss_proc = gss_proc;
			rpc_call->gss_svc = gss_svc;
@@ -1606,6 +1599,10 @@

		offset = dissect_rpc_cred(tvb, pinfo, rpc_tree, offset);
		offset = dissect_rpc_verf(tvb, pinfo, rpc_tree, offset, msg_type);
+
+		/* pass rpc_info to subdissectors */
+		rpc_call->call = TRUE;
+		pinfo->private = rpc_call;

		/* go to the next dissector */

diff -u -r -x *.[^ch]|nmake|am ethereal-orig/packet-rpc.h ethereal/packet-rpc.h
--- ethereal-orig/packet-rpc.h	Tue Aug  7 08:45:14 2001
+++ ethereal/packet-rpc.h	Wed Aug  8 09:21:05 2001
@@ -77,6 +77,20 @@
typedef int (old_dissect_function_t)(const u_char* pd, int offset, frame_data* fd, proto_tree* tree); typedef int (dissect_function_t)(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree* tree);

+typedef struct _rpc_call_info_value {
+	guint32 req_num;	/* frame number for first request seen */
+	guint32 rep_num;	/* frame number for first reply seen */
+	guint32 prog;
+	guint32 vers;
+	guint32 proc;
+	guint32 xid;
+	guint32 flavor;
+	guint32 gss_proc;
+	guint32 gss_svc;
+	struct _rpc_proc_info_value *proc_info;
+	gboolean call;
+} rpc_call_info_value;
+
typedef struct _old_vsff {
	guint32	value;
	gchar   *strptr;