Ethereal-dev: [ethereal-dev] NFS dissector continuation

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

From: Uwe Girlich <Uwe.Girlich@xxxxxxxxxxx>
Date: Mon, 15 Nov 1999 10:12:28 +0100
Here comes the next part of my NFS dissector. It evolves very slowly but the
NFS protocol is a really big one.

I include some patches for packet-mount.[ch] (new mount protocol) and
packet-portmap.[ch] (new portmapper protocol). To remove all incomplete
dissectors, I provide a start for packet-nlm.[ch] (Network Lock Manager).

roundup() in packet-rpc.c is called rpc_roundup() now.

Then I include a small patch for packet-tcp.c, which may also call
dissect_rpc().

Index: Makefile.am
===================================================================
RCS file: /cvsroot/ethereal/Makefile.am,v
retrieving revision 1.106
diff -u -r1.106 Makefile.am
--- Makefile.am	1999/11/11 23:13:42	1.106
+++ Makefile.am	1999/11/15 09:05:45
@@ -84,6 +84,8 @@
 	packet-netbios.h \
 	packet-nfs.c   \
 	packet-nfs.h   \
+	packet-nlm.c   \
+	packet-nlm.h   \
 	packet-nntp.c  \
 	packet-ntp.c  \
 	packet-ntp.h  \
Index: packet-mount.c
===================================================================
RCS file: /cvsroot/ethereal/packet-mount.c,v
retrieving revision 1.1
diff -u -r1.1 packet-mount.c
--- packet-mount.c	1999/11/11 21:21:59	1.1
+++ packet-mount.c	1999/11/15 09:05:46
@@ -37,11 +37,12 @@
 #include "packet-rpc.h"
 #include "packet-mount.h"
 
+
 static int proto_mount = -1;
 static int hf_mount_path = -1;
+
 
-/* Dissect a unmount call */
-int dissect_unmount_call(const u_char *pd, int offset, frame_data *fd,
+int dissect_mount_dirpath_call(const u_char *pd, int offset, frame_data *fd,
 	proto_tree *tree)
 {
 	if ( tree )
@@ -52,20 +53,45 @@
 	return offset;
 }
 
+
 /* proc number, "proc name", dissect_request, dissect_reply */
 /* NULL as function pointer means: take the generic one. */
-
-const vsff mount_proc[] = {
+/* Mount protocol version 1, RFC 1094 */
+const vsff mount1_proc[] = {
     { 0, "NULL", NULL, NULL },
-    { MOUNTPROC_MOUNT,   "MOUNT",      
+    { MOUNTPROC_MNT,   "MNT",      
+		dissect_mount_dirpath_call, NULL },
+    { MOUNTPROC_DUMP,    "DUMP",
+		NULL, NULL },
+    { MOUNTPROC_UMNT, "UMNT",        
+		dissect_mount_dirpath_call, NULL },
+    { MOUNTPROC_UMNTALL, "UMNTALL",
+		NULL, NULL },
+    { MOUNTPROC_EXPORT, "EXPORT",
 		NULL, NULL },
-    { MOUNTPROC_UNMOUNT, "UNMOUNT",        
-		dissect_unmount_call, NULL },
     { 0, NULL, NULL, NULL }
 };
 /* end of mount version 1 */
 
 
+/* Mount protocol version 3, RFC 1813 */
+const vsff mount3_proc[] = {
+	{ 0, "NULL", NULL, NULL },
+	{ MOUNTPROC_MNT, "MNT",
+		dissect_mount_dirpath_call, NULL },
+	{ MOUNTPROC_DUMP, "DUMP",
+		NULL, NULL },
+	{ MOUNTPROC_UMNT, "UMNT",
+		dissect_mount_dirpath_call, NULL },
+	{ MOUNTPROC_UMNTALL, "UMNTALL",
+		NULL, NULL },
+	{ MOUNTPROC_EXPORT, "EXPORT",
+		NULL, NULL },
+	{ 0, NULL, NULL, NULL }
+};
+/* end of Mount protocol version 3 */
+
+
 void
 proto_register_mount(void)
 {
@@ -81,6 +107,7 @@
 	/* Register the protocol as RPC */
 	rpc_init_prog(proto_mount, MOUNT_PROGRAM, ETT_MOUNT);
 	/* Register the procedure tables */
-	rpc_init_proc_table(MOUNT_PROGRAM, 1, mount_proc);
+	rpc_init_proc_table(MOUNT_PROGRAM, 1, mount1_proc);
+	rpc_init_proc_table(MOUNT_PROGRAM, 3, mount3_proc);
 }
 
Index: packet-mount.h
===================================================================
RCS file: /cvsroot/ethereal/packet-mount.h,v
retrieving revision 1.1
diff -u -r1.1 packet-mount.h
--- packet-mount.h	1999/11/11 21:21:59	1.1
+++ packet-mount.h	1999/11/15 09:05:46
@@ -6,8 +6,11 @@
 
 #define MOUNT_PROGRAM  100005
 
-#define MOUNTPROC_NULL 0
-#define MOUNTPROC_MOUNT 1
-#define MOUNTPROC_UNMOUNT 3
+#define MOUNTPROC_NULL		0
+#define MOUNTPROC_MNT		1
+#define MOUNTPROC_DUMP		2
+#define MOUNTPROC_UMNT		3
+#define MOUNTPROC_UMNTALL	4
+#define MOUNTPROC_EXPORT	5
 
 #endif
Index: packet-nfs.c
===================================================================
RCS file: /cvsroot/ethereal/packet-nfs.c,v
retrieving revision 1.2
diff -u -r1.2 packet-nfs.c
--- packet-nfs.c	1999/11/05 07:16:23	1.2
+++ packet-nfs.c	1999/11/15 09:05:51
@@ -94,7 +94,7 @@
 	
 	if (tree) {
 		proto_tree_add_text(tree, offset, 4,
-			"%s (stat): %s (%u)", name, stat_name, stat);
+			"%s: %s (%u)", name, stat_name, stat);
 	}
 
 	offset += 4;
@@ -128,7 +128,7 @@
 	
 	if (tree) {
 		proto_tree_add_text(tree, offset, 4,
-			"%s (ftype): %s (%u)", name, ftype_name, ftype);
+			"%s: %s (%u)", name, ftype_name, ftype);
 	}
 
 	offset += 4;
@@ -145,7 +145,7 @@
 
 	if (tree) {
 		fitem = proto_tree_add_text(tree, offset, FHSIZE,
-			"%s (fhandle)", name);
+			"%s", name);
 		if (fitem)
 			ftree = proto_item_add_subtree(fitem, ETT_NFS_FHANDLE);
 	}
@@ -175,7 +175,7 @@
 	
 	if (tree) {
 		time_item = proto_tree_add_text(tree, offset, 8,
-			"%s (timeval): %u.%06u", name, seconds, mseconds);
+			"%s: %u.%06u", name, seconds, mseconds);
 		if (time_item)
 			time_tree = proto_item_add_subtree(time_item, ETT_NFS_TIMEVAL);
 	}
@@ -214,7 +214,7 @@
 	
 	if (tree) {
 		mode_item = proto_tree_add_text(tree, offset, 4,
-			"%s (mode): 0%o", name, mode);
+			"%s: 0%o", name, mode);
 		if (mode_item)
 			mode_tree = proto_item_add_subtree(mode_item, ETT_NFS_MODE);
 	}
@@ -264,7 +264,7 @@
 
 	if (tree) {
 		fattr_item = proto_tree_add_text(tree, offset,
-			END_OF_FRAME, "%s (fattr)", name);
+			END_OF_FRAME, "%s", name);
 		if (fattr_item)
 			fattr_tree = proto_item_add_subtree(fattr_item, ETT_NFS_FATTR);
 	}
@@ -293,6 +293,38 @@
 }
 
 
+/* RFC 1094, Page 17 */
+int
+dissect_sattr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
+{
+	proto_item* sattr_item = NULL;
+	proto_tree* sattr_tree = NULL;
+	int old_offset = offset;
+
+	if (tree) {
+		sattr_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s", name);
+		if (sattr_item)
+			sattr_tree = proto_item_add_subtree(sattr_item, ETT_NFS_SATTR);
+	}
+
+	/* some how we should indicate here, that -1 means "do not set" */
+	offset = dissect_mode         (pd,offset,fd,sattr_tree,"mode");
+	offset = dissect_unsigned_int (pd,offset,fd,sattr_tree,"uid");
+	offset = dissect_unsigned_int (pd,offset,fd,sattr_tree,"gid");
+	offset = dissect_unsigned_int (pd,offset,fd,sattr_tree,"size");
+	offset = dissect_timeval      (pd,offset,fd,sattr_tree,"atime");
+	offset = dissect_timeval      (pd,offset,fd,sattr_tree,"mtime");
+
+	/* now we know, that sattr is shorter */
+	if (sattr_item) {
+		proto_item_set_len(sattr_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
 /* generic NFS2 call dissector */
 int
 dissect_nfs2_any_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
@@ -345,6 +377,39 @@
 	return offset;
 }
 
+
+/* RFC 1094, Page 6 */
+int
+dissect_nfs2_setattr_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
+{
+	offset = dissect_fhandle(pd, offset, fd, tree, "file"      );
+	offset = dissect_sattr  (pd, offset, fd, tree, "attributes");
+
+	return offset;
+}
+
+
+/* RFC 1094, Page 6 */
+int
+dissect_nfs2_setattr_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
+{
+	guint32 status;
+
+	/* attrstat: RFC 1094, Page 17 */
+	offset = dissect_stat(pd, offset, fd, tree, "status", &status);
+	switch (status) {
+		case 0:
+			offset = dissect_fattr(pd, offset, fd, tree, "attributes");
+		break;
+		default:
+			/* do nothing */
+		break;
+	}
+
+	return offset;
+}
+
+
 /* more to come here */
 
 
@@ -353,7 +418,7 @@
 const vsff nfs2_proc[] = {
 	{ 0,	"NULL",		NULL,				NULL },
 	{ 1,	"GETATTR",	dissect_nfs2_getattr_call,	dissect_nfs2_getattr_reply },
-	{ 2,	"SETATTR",	dissect_nfs2_any_call,		dissect_nfs2_any_reply },
+	{ 2,	"SETATTR",	dissect_nfs2_any_call,		dissect_nfs2_setattr_reply },
 	{ 3,	"ROOT",		NULL,				NULL },
 	{ 4,	"LOOKUP",	dissect_nfs2_any_call,		dissect_nfs2_any_reply },
 	{ 5,	"READLINK",	dissect_nfs2_any_call,		dissect_nfs2_any_reply },
@@ -453,7 +518,7 @@
 	
 	if (tree) {
 		mode3_item = proto_tree_add_text(tree, offset, 4,
-			"%s (mode3): 0%o", name, mode3);
+			"%s: 0%o", name, mode3);
 		if (mode3_item)
 			mode3_tree = proto_item_add_subtree(mode3_item, ETT_NFS_MODE3);
 	}
@@ -550,7 +615,7 @@
 	
 	if (tree) {
 		proto_tree_add_text(tree, offset, 4,
-			"%s (nfsstat3): %s (%u)", name, nfsstat3_name, nfsstat3);
+			"%s: %s (%u)", name, nfsstat3_name, nfsstat3);
 	}
 
 	offset += 4;
@@ -588,7 +653,7 @@
 	
 	if (tree) {
 		proto_tree_add_text(tree, offset, 4,
-			"%s (ftype3): %s (%u)", name, ftype3_name, ftype3);
+			"%s: %s (%u)", name, ftype3_name, ftype3);
 	}
 
 	offset += 4;
@@ -612,7 +677,7 @@
 	
 	if (tree) {
 		specdata3_item = proto_tree_add_text(tree, offset, 8,
-			"%s (specdata3) : %u,%u", name, specdata1, specdata2);
+			"%s: %u,%u", name, specdata1, specdata2);
 		if (specdata3_item)
 			specdata3_tree = proto_item_add_subtree(specdata3_item,
 					ETT_NFS_SPECDATA3);
@@ -641,12 +706,12 @@
 	proto_tree* ftree = NULL;
 
 	fh_len = EXTRACT_UINT(pd, offset+0);
-	fh_len_full = roundup(fh_len);
+	fh_len_full = rpc_roundup(fh_len);
 	fh_fill = fh_len_full - fh_len;
 	
 	if (tree) {
 		fitem = proto_tree_add_text(tree, offset, 4+fh_len_full,
-			"%s (nfs_fh3)", name);
+			"%s", name);
 		if (fitem)
 			ftree = proto_item_add_subtree(fitem, ETT_NFS_FH3);
 	}
@@ -667,7 +732,7 @@
 
 /* RFC 1813, Page 21 */
 int
-dissect_nfs3time(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,char* name)
+dissect_nfstime3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,char* name)
 {
 	guint32	seconds;
 	guint32 nseconds;
@@ -681,7 +746,7 @@
 	
 	if (tree) {
 		time_item = proto_tree_add_text(tree, offset, 8,
-			"%s (nfs3time): %u.%09u", name, seconds, nseconds);
+			"%s: %u.%09u", name, seconds, nseconds);
 		if (time_item)
 			time_tree = proto_item_add_subtree(time_item, ETT_NFS_NFSTIME3);
 	}
@@ -707,7 +772,7 @@
 
 	if (tree) {
 		fattr3_item = proto_tree_add_text(tree, offset,
-			END_OF_FRAME, "%s (fattr3)", name);
+			END_OF_FRAME, "%s", name);
 		if (fattr3_item)
 			fattr3_tree = proto_item_add_subtree(fattr3_item, ETT_NFS_FATTR3);
 	}
@@ -722,9 +787,9 @@
 	offset = dissect_specdata3(pd,offset,fd,fattr3_tree,"rdev");
 	offset = dissect_uint64   (pd,offset,fd,fattr3_tree,"fsid");
 	offset = dissect_fileid3  (pd,offset,fd,fattr3_tree,"fileid");
-	offset = dissect_nfs3time (pd,offset,fd,fattr3_tree,"atime");
-	offset = dissect_nfs3time (pd,offset,fd,fattr3_tree,"mtime");
-	offset = dissect_nfs3time (pd,offset,fd,fattr3_tree,"ctime");
+	offset = dissect_nfstime3 (pd,offset,fd,fattr3_tree,"atime");
+	offset = dissect_nfstime3 (pd,offset,fd,fattr3_tree,"mtime");
+	offset = dissect_nfstime3 (pd,offset,fd,fattr3_tree,"ctime");
 
 	/* now we know, that fattr3 is shorter */
 	if (fattr3_item) {
@@ -735,6 +800,476 @@
 }
 
 
+const value_string value_follows[3] =
+	{
+		{ 0, "no value" },
+		{ 1, "value follows"},
+		{ 0, NULL }
+	};
+
+
+/* RFC 1813, Page 23 */
+int
+dissect_post_op_attr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
+{
+	proto_item* post_op_attr_item = NULL;
+	proto_tree* post_op_attr_tree = NULL;
+	int old_offset = offset;
+	guint32 attributes_follow;
+
+	if (tree) {
+		post_op_attr_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s", name);
+		if (post_op_attr_item)
+			post_op_attr_tree = proto_item_add_subtree(post_op_attr_item, ETT_NFS_POST_OP_ATTR);
+	}
+
+	if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+	attributes_follow = EXTRACT_UINT(pd, offset+0);
+	proto_tree_add_text(post_op_attr_tree, offset, 4,
+		"attributes_follow: %s (%u)", 
+		val_to_str(attributes_follow,value_follows,"Unknown"), attributes_follow);
+	offset += 4;
+	switch (attributes_follow) {
+		case TRUE:
+			offset = dissect_fattr3(pd, offset, fd, post_op_attr_tree,
+					"attributes");
+		break;
+		case FALSE:
+			/* void */
+		break;
+	}
+	
+	/* now we know, that post_op_attr_tree is shorter */
+	if (post_op_attr_item) {
+		proto_item_set_len(post_op_attr_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
+/* RFC 1813, Page 24 */
+int
+dissect_wcc_attr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
+{
+	proto_item* wcc_attr_item = NULL;
+	proto_tree* wcc_attr_tree = NULL;
+	int old_offset = offset;
+
+	if (tree) {
+		wcc_attr_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s", name);
+		if (wcc_attr_item)
+			wcc_attr_tree = proto_item_add_subtree(wcc_attr_item, ETT_NFS_WCC_ATTR);
+	}
+
+	offset = dissect_size3   (pd, offset, fd, wcc_attr_tree, "size" );
+	offset = dissect_nfstime3(pd, offset, fd, wcc_attr_tree, "mtime");
+	offset = dissect_nfstime3(pd, offset, fd, wcc_attr_tree, "ctime");
+	
+	/* now we know, that wcc_attr_tree is shorter */
+	if (wcc_attr_item) {
+		proto_item_set_len(wcc_attr_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
+/* RFC 1813, Page 24 */
+int
+dissect_pre_op_attr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
+{
+	proto_item* pre_op_attr_item = NULL;
+	proto_tree* pre_op_attr_tree = NULL;
+	int old_offset = offset;
+	guint32 attributes_follow;
+
+	if (tree) {
+		pre_op_attr_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s", name);
+		if (pre_op_attr_item)
+			pre_op_attr_tree = proto_item_add_subtree(pre_op_attr_item, ETT_NFS_PRE_OP_ATTR);
+	}
+
+	if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+	attributes_follow = EXTRACT_UINT(pd, offset+0);
+	proto_tree_add_text(pre_op_attr_tree, offset, 4,
+		"attributes_follow: %s (%u)", 
+		val_to_str(attributes_follow,value_follows,"Unknown"), attributes_follow);
+	offset += 4;
+	switch (attributes_follow) {
+		case TRUE:
+			offset = dissect_wcc_attr(pd, offset, fd, pre_op_attr_tree,
+					"attributes");
+		break;
+		case FALSE:
+			/* void */
+		break;
+	}
+	
+	/* now we know, that pre_op_attr_tree is shorter */
+	if (pre_op_attr_item) {
+		proto_item_set_len(pre_op_attr_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
+/* RFC 1813, Page 24 */
+int
+dissect_wcc_data(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
+{
+	proto_item* wcc_data_item = NULL;
+	proto_tree* wcc_data_tree = NULL;
+	int old_offset = offset;
+
+	if (tree) {
+		wcc_data_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s", name);
+		if (wcc_data_item)
+			wcc_data_tree = proto_item_add_subtree(wcc_data_item, ETT_NFS_WCC_DATA);
+	}
+
+	offset = dissect_pre_op_attr (pd, offset, fd, wcc_data_tree, "before");
+	offset = dissect_post_op_attr(pd, offset, fd, wcc_data_tree, "after" );
+
+	/* now we know, that wcc_data is shorter */
+	if (wcc_data_item) {
+		proto_item_set_len(wcc_data_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
+/* RFC 1813, Page 25 */
+int
+dissect_set_mode3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
+{
+	proto_item* set_mode3_item = NULL;
+	proto_tree* set_mode3_tree = NULL;
+	int old_offset = offset;
+	guint32 set_it;
+	char* set_it_name;
+
+	if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+	set_it = EXTRACT_UINT(pd, offset+0);
+	set_it_name = val_to_str(set_it,value_follows,"Unknown");
+
+	if (tree) {
+		set_mode3_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s: %s", name, set_it_name);
+		if (set_mode3_item)
+			set_mode3_tree = proto_item_add_subtree(set_mode3_item, ETT_NFS_SET_MODE3);
+	}
+
+	if (set_mode3_tree)
+		proto_tree_add_text(set_mode3_tree, offset, 4,
+			"set_it: %s (%u)", set_it_name, set_it);
+
+	offset += 4;
+
+	switch (set_it) {
+		case 1:
+			offset = dissect_mode3(pd, offset, fd, set_mode3_tree,
+					"mode");
+		break;
+		default:
+			/* void */
+		break;
+	}
+	
+	/* now we know, that set_mode3 is shorter */
+	if (set_mode3_item) {
+		proto_item_set_len(set_mode3_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
+/* RFC 1813, Page 26 */
+int
+dissect_set_uid3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
+{
+	proto_item* set_uid3_item = NULL;
+	proto_tree* set_uid3_tree = NULL;
+	int old_offset = offset;
+	guint32 set_it;
+	char* set_it_name;
+
+	if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+	set_it = EXTRACT_UINT(pd, offset+0);
+	set_it_name = val_to_str(set_it,value_follows,"Unknown");
+
+	if (tree) {
+		set_uid3_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s: %s", name, set_it_name);
+		if (set_uid3_item)
+			set_uid3_tree = proto_item_add_subtree(set_uid3_item, ETT_NFS_SET_UID3);
+	}
+
+	if (set_uid3_tree)
+		proto_tree_add_text(set_uid3_tree, offset, 4,
+			"set_it: %s (%u)", set_it_name, set_it);
+
+	offset += 4;
+
+	switch (set_it) {
+		case 1:
+			offset = dissect_uid3(pd, offset, fd, set_uid3_tree,
+					"uid");
+		break;
+		default:
+			/* void */
+		break;
+	}
+
+	/* now we know, that set_uid3 is shorter */
+	if (set_uid3_item) {
+		proto_item_set_len(set_uid3_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
+/* RFC 1813, Page 26 */
+int
+dissect_set_gid3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
+{
+	proto_item* set_gid3_item = NULL;
+	proto_tree* set_gid3_tree = NULL;
+	int old_offset = offset;
+	guint32 set_it;
+	char* set_it_name;
+
+	if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+	set_it = EXTRACT_UINT(pd, offset+0);
+	set_it_name = val_to_str(set_it,value_follows,"Unknown");
+
+	if (tree) {
+		set_gid3_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s: %s", name, set_it_name);
+		if (set_gid3_item)
+			set_gid3_tree = proto_item_add_subtree(set_gid3_item, ETT_NFS_SET_GID3);
+	}
+
+	if (set_gid3_tree)
+		proto_tree_add_text(set_gid3_tree, offset, 4,
+			"set_it: %s (%u)", set_it_name, set_it);
+
+	offset += 4;
+
+	switch (set_it) {
+		case 1:
+			offset = dissect_gid3(pd, offset, fd, set_gid3_tree,
+					"gid");
+		break;
+		default:
+			/* void */
+		break;
+	}
+
+	/* now we know, that set_gid3 is shorter */
+	if (set_gid3_item) {
+		proto_item_set_len(set_gid3_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
+/* RFC 1813, Page 26 */
+int
+dissect_set_size3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
+{
+	proto_item* set_size3_item = NULL;
+	proto_tree* set_size3_tree = NULL;
+	int old_offset = offset;
+	guint32 set_it;
+	char* set_it_name;
+
+	if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+	set_it = EXTRACT_UINT(pd, offset+0);
+	set_it_name = val_to_str(set_it,value_follows,"Unknown");
+
+	if (tree) {
+		set_size3_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s: %s", name, set_it_name);
+		if (set_size3_item)
+			set_size3_tree = proto_item_add_subtree(set_size3_item, ETT_NFS_SET_SIZE3);
+	}
+
+	if (set_size3_tree)
+		proto_tree_add_text(set_size3_tree, offset, 4,
+			"set_it: %s (%u)", set_it_name, set_it);
+
+	offset += 4;
+
+	switch (set_it) {
+		case 1:
+			offset = dissect_size3(pd, offset, fd, set_size3_tree,
+					"size");
+		break;
+		default:
+			/* void */
+		break;
+	}
+
+	/* now we know, that set_size3 is shorter */
+	if (set_size3_item) {
+		proto_item_set_len(set_size3_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
+/* RFC 1813, Page 25 */
+#define DONT_CHANGE 0
+#define SET_TO_SERVER_TIME 1
+#define SET_TO_CLIENT_TIME 2
+
+const value_string time_how[] =
+	{
+		{ DONT_CHANGE,	"don't change" },
+		{ SET_TO_SERVER_TIME, "set to server time" },
+		{ SET_TO_CLIENT_TIME, "set to client time" },
+		{ 0, NULL }
+	};
+
+
+/* RFC 1813, Page 26 */
+int
+dissect_set_atime(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
+{
+	proto_item* set_atime_item = NULL;
+	proto_tree* set_atime_tree = NULL;
+	int old_offset = offset;
+	guint32 set_it;
+	char* set_it_name;
+
+	if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+	set_it = EXTRACT_UINT(pd, offset+0);
+	set_it_name = val_to_str(set_it,time_how,"Unknown");
+
+	if (tree) {
+		set_atime_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s: %s",
+			name, set_it_name, set_it);
+		if (set_atime_item)
+			set_atime_tree = proto_item_add_subtree(set_atime_item, ETT_NFS_SET_ATIME);
+	}
+
+	if (set_atime_tree)
+		proto_tree_add_text(set_atime_tree, offset, 4,
+			"set_it: %s (%u)", set_it_name, set_it);
+
+	offset += 4;
+
+	switch (set_it) {
+		case SET_TO_CLIENT_TIME:
+			if (set_atime_item)
+			offset = dissect_nfstime3(pd, offset, fd, set_atime_tree,
+					"atime");
+		break;
+		default:
+			/* void */
+		break;
+	}
+
+	/* now we know, that set_atime is shorter */
+	if (set_atime_item) {
+		proto_item_set_len(set_atime_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
+/* RFC 1813, Page 26 */
+int
+dissect_set_mtime(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
+{
+	proto_item* set_mtime_item = NULL;
+	proto_tree* set_mtime_tree = NULL;
+	int old_offset = offset;
+	guint32 set_it;
+	char* set_it_name;
+
+	if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+	set_it = EXTRACT_UINT(pd, offset+0);
+	set_it_name = val_to_str(set_it,time_how,"Unknown");
+
+	if (tree) {
+		set_mtime_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s: %s",
+			name, set_it_name, set_it);
+		if (set_mtime_item)
+			set_mtime_tree = proto_item_add_subtree(set_mtime_item, ETT_NFS_SET_MTIME);
+	}
+
+	if (set_mtime_tree)
+		proto_tree_add_text(set_mtime_tree, offset, 4,
+				"set_it: %s (%u)", set_it_name, set_it);
+
+	offset += 4;
+
+	switch (set_it) {
+		case SET_TO_CLIENT_TIME:
+			if (set_mtime_item)
+			offset = dissect_nfstime3(pd, offset, fd, set_mtime_tree,
+					"atime");
+		break;
+		default:
+			/* void */
+		break;
+	}
+
+	/* now we know, that set_mtime is shorter */
+	if (set_mtime_item) {
+		proto_item_set_len(set_mtime_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
+/* RFC 1813, Page 26 */
+int
+dissect_sattr3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
+{
+	proto_item* sattr3_item = NULL;
+	proto_tree* sattr3_tree = NULL;
+	int old_offset = offset;
+
+	if (tree) {
+		sattr3_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s", name);
+		if (sattr3_item)
+			sattr3_tree = proto_item_add_subtree(sattr3_item, ETT_NFS_SATTR3);
+	}
+
+	offset = dissect_set_mode3(pd, offset, fd, sattr3_tree, "mode");
+	offset = dissect_set_uid3 (pd, offset, fd, sattr3_tree, "uid");
+	offset = dissect_set_gid3 (pd, offset, fd, sattr3_tree, "gid");
+	offset = dissect_set_size3(pd, offset, fd, sattr3_tree, "size");
+	offset = dissect_set_atime(pd, offset, fd, sattr3_tree, "atime");
+	offset = dissect_set_mtime(pd, offset, fd, sattr3_tree, "mtime");
+
+	/* now we know, that sattr3 is shorter */
+	if (sattr3_item) {
+		proto_item_set_len(sattr3_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
 /* generic NFS3 call dissector */
 int
 dissect_nfs3_any_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
@@ -786,12 +1321,89 @@
 }
 
 
+/* RFC 1813, Page 33 */
+int
+dissect_sattrguard3(const u_char* pd, int offset, frame_data* fd, proto_tree* tree, char *name)
+{
+	proto_item* sattrguard3_item = NULL;
+	proto_tree* sattrguard3_tree = NULL;
+	int old_offset = offset;
+	guint32 check;
+	char* check_name;
+
+	if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+	check = EXTRACT_UINT(pd, offset+0);
+	check_name = val_to_str(check,value_follows,"Unknown");
+
+	if (tree) {
+		sattrguard3_item = proto_tree_add_text(tree, offset,
+			END_OF_FRAME, "%s: %s", name, check_name);
+		if (sattrguard3_item)
+			sattrguard3_tree = proto_item_add_subtree(sattrguard3_item, ETT_NFS_SATTRGUARD3);
+	}
+
+	if (sattrguard3_tree)
+		proto_tree_add_text(sattrguard3_tree, offset, 4,
+			"check: %s (%u)", check_name, check);
+
+	offset += 4;
+
+	switch (check) {
+		case TRUE:
+			offset = dissect_nfstime3(pd, offset, fd, sattrguard3_tree,
+					"obj_ctime");
+		break;
+		case FALSE:
+			/* void */
+		break;
+	}
+
+	/* now we know, that sattrguard3 is shorter */
+	if (sattrguard3_item) {
+		proto_item_set_len(sattrguard3_item, offset - old_offset);
+	}
+
+	return offset;
+}
+
+
+/* RFC 1813, Page 33 */
+int
+dissect_nfs3_setattr_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
+{
+	offset = dissect_nfs_fh3    (pd, offset, fd, tree, "object");
+	offset = dissect_sattr3     (pd, offset, fd, tree, "new_attributes");
+	offset = dissect_sattrguard3(pd, offset, fd, tree, "guard");
+	return offset;
+}
+
+
+/* RFC 1813, Page 33 */
+int
+dissect_nfs3_setattr_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
+{
+	guint32 status;
+
+	offset = dissect_nfsstat3(pd, offset, fd, tree, "status", &status);
+	switch (status) {
+		case 0:
+			offset = dissect_wcc_data(pd, offset, fd, tree, "obj_wcc");
+		break;
+		default:
+			offset = dissect_wcc_data(pd, offset, fd, tree, "obj_wcc");
+		break;
+	}
+		
+	return offset;
+}
+
+
 /* proc number, "proc name", dissect_request, dissect_reply */
 /* NULL as function pointer means: take the generic one. */
 const vsff nfs3_proc[] = {
 	{ 0,	"NULL",		NULL,				NULL },
 	{ 1,	"GETATTR",	dissect_nfs3_getattr_call,	dissect_nfs3_getattr_reply },
-	{ 2,	"SETATTR",	dissect_nfs3_any_call,		dissect_nfs3_any_reply },
+	{ 2,	"SETATTR",	dissect_nfs3_setattr_call,	dissect_nfs3_setattr_reply },
 	{ 3,	"LOOKUP",	dissect_nfs3_any_call,		dissect_nfs3_any_reply },
 	{ 4,	"ACCESS",	dissect_nfs3_any_call,		dissect_nfs3_any_reply },
 	{ 5,	"READLINK",	dissect_nfs3_any_call,		dissect_nfs3_any_reply },
Index: packet-nfs.h
===================================================================
RCS file: /cvsroot/ethereal/packet-nfs.h,v
retrieving revision 1.1
diff -u -r1.1 packet-nfs.h
--- packet-nfs.h	1999/10/29 01:11:23	1.1
+++ packet-nfs.h	1999/11/15 09:05:51
@@ -11,8 +11,8 @@
 #define FHSIZE 32
 
 /* the RPC mount protocol needs both function to decode a MNT reply */
-int dissect_fh2(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
-int dissect_fh3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
+int dissect_fhandle(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name);
+int dissect_nfs_fh3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name);
 
 #endif /* packet-nfs.h */
 
Index: packet-portmap.c
===================================================================
RCS file: /cvsroot/ethereal/packet-portmap.c,v
retrieving revision 1.5
diff -u -r1.5 packet-portmap.c
--- packet-portmap.c	1999/11/11 21:21:59	1.5
+++ packet-portmap.c	1999/11/15 09:05:52
@@ -186,6 +186,63 @@
 /* end of Portmap version 2 */
 
 
+/* Portmapper version 3, RFC 1833, Page 7 */
+const vsff portmap3_proc[] = {
+	{ RPCBPROC_NULL,	"NULL",
+		NULL, NULL },
+	{ RPCBPROC_SET,		"SET",
+		NULL, NULL },
+	{ RPCBPROC_UNSET,	"UNSET",
+		NULL, NULL },
+	{ RPCBPROC_GETADDR,	"GETADDR",
+		NULL, NULL},
+	{ RPCBPROC_DUMP,	"DUMP",
+		NULL, NULL },
+	{ RPCBPROC_CALLIT,	"CALLIT",
+		NULL, NULL },
+	{ RPCBPROC_GETTIME,	"GETTIME",
+		NULL, NULL },
+	{ RPCBPROC_UADDR2TADDR,	"UADDR2TADDR",
+		NULL, NULL },
+	{ RPCBPROC_TADDR2UADDR,	"TADDR2UADDR",
+		NULL, NULL },
+	{ 0, NULL, NULL, NULL }
+};
+/* end of Portmap version 3 */
+
+
+/* Portmapper version 4, RFC 1833, Page 8 */
+const vsff portmap4_proc[] = {
+	{ RPCBPROC_NULL,	"NULL",
+		NULL, NULL },
+	{ RPCBPROC_SET,		"SET",
+		NULL, NULL },
+	{ RPCBPROC_UNSET,	"UNSET",
+		NULL, NULL },
+	{ RPCBPROC_GETADDR,	"GETADDR",
+		NULL, NULL},
+	{ RPCBPROC_DUMP,	"DUMP",
+		NULL, NULL },
+	{ RPCBPROC_BCAST,	"BCAST",
+		NULL, NULL },
+	{ RPCBPROC_GETTIME,	"GETTIME",
+		NULL, NULL },
+	{ RPCBPROC_UADDR2TADDR,	"UADDR2TADDR",
+		NULL, NULL },
+	{ RPCBPROC_TADDR2UADDR,	"TADDR2UADDR",
+		NULL, NULL },
+	{ RPCBPROC_GETVERSADDR,	"GETVERSADDR",
+		NULL, NULL },
+	{ RPCBPROC_INDIRECT,	"INDIRECT",
+		NULL, NULL },
+	{ RPCBPROC_GETADDRLIST,	"GETADDRLIST",
+		NULL, NULL },
+	{ RPCBPROC_GETSTAT,	"GETSTAT",
+		NULL, NULL },
+	{ 0, NULL, NULL, NULL }
+};
+/* end of Portmap version 4 */
+
 void
 proto_register_portmap(void)
 {
@@ -218,5 +275,7 @@
 	/* Register the procedure tables */
 	rpc_init_proc_table(PORTMAP_PROGRAM, 1, portmap1_proc);
 	rpc_init_proc_table(PORTMAP_PROGRAM, 2, portmap2_proc);
+	rpc_init_proc_table(PORTMAP_PROGRAM, 3, portmap3_proc);
+	rpc_init_proc_table(PORTMAP_PROGRAM, 4, portmap4_proc);
 }
 
Index: packet-portmap.h
===================================================================
RCS file: /cvsroot/ethereal/packet-portmap.h,v
retrieving revision 1.2
diff -u -r1.2 packet-portmap.h
--- packet-portmap.h	1999/11/10 21:05:10	1.2
+++ packet-portmap.h	1999/11/15 09:05:52
@@ -13,6 +13,24 @@
 #define PORTMAPPROC_DUMP     4
 #define PORTMAPPROC_CALLIT   5
 
+/* RFC 1833, Page 7 */
+#define RPCBPROC_NULL		0
+#define RPCBPROC_SET		1
+#define RPCBPROC_UNSET		2
+#define RPCBPROC_GETADDR	3
+#define RPCBPROC_DUMP		4
+#define RPCBPROC_CALLIT		5
+#define RPCBPROC_GETTIME	6
+#define RPCBPROC_UADDR2TADDR	7
+#define RPCBPROC_TADDR2UADDR	8
+
+/* RFC 1833, Page 8 */
+#define RPCBPROC_BCAST		RPCBPROC_CALLIT
+#define RPCBPROC_GETVERSADDR	9
+#define RPCBPROC_INDIRECT	10
+#define RPCBPROC_GETADDRLIST	11
+#define RPCBPROC_GETSTAT	12
+
 struct pmap {
         guint32 pm_prog;
         guint32 pm_vers;
Index: packet-rpc.c
===================================================================
RCS file: /cvsroot/ethereal/packet-rpc.c,v
retrieving revision 1.11
diff -u -r1.11 packet-rpc.c
--- packet-rpc.c	1999/11/14 21:16:58	1.11
+++ packet-rpc.c	1999/11/15 09:05:56
@@ -221,22 +221,6 @@
 /*--------------------------------------*/
 
 
-/* Placeholder for future dissectors.
-It should vanish, if they are finally present. Up to this point, this
-minimal variant serves as a detector for RPC services and can even find
-request/reply pairs. */
-
-#define	NLM_PROGRAM	100021
-
-static int proto_nlm = -1;
-
-void init_incomplete_dissect(void)
-{
-	proto_nlm = proto_register_protocol("Network Lock Manager", "NLM");
-	rpc_init_prog(proto_nlm, NLM_PROGRAM, ETT_NLM);
-}
-
-
 /*
  * Init the hash tables. It will be called from ethereal_proto_init().
  * ethereal_proto_init() calls later proto_init(), which calls 
@@ -305,7 +289,7 @@
 
 
 unsigned int
-roundup(unsigned int a)
+rpc_roundup(unsigned int a)
 {
 	unsigned int mod = a % 4;
 	return a + ((mod)? 4-mod : 0);
@@ -323,7 +307,7 @@
 
 	if (tree) {
 		proto_tree_add_text(tree, offset, 4,
-		"%s (%s): %u", name, type, value);
+		"%s: %u", name, value);
 	}
 
 	offset += 4;
@@ -345,10 +329,10 @@
 	if (tree) {
 		if (value_high)
 			proto_tree_add_text(tree, offset, 8,
-				"%s (%s): %x%08x", name, type, value_high, value_low);
+				"%s: %x%08x", name, value_high, value_low);
 		else
 			proto_tree_add_text(tree, offset, 8,
-				"%s (%s): %u", name, type, value_low);
+				"%s: %u", name, value_low);
 	}
 
 	offset += 8;
@@ -373,7 +357,7 @@
 
 	if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
 	string_length = EXTRACT_UINT(pd,offset+0);
-	string_length_full = roundup(string_length);
+	string_length_full = rpc_roundup(string_length);
 	string_fill = string_length_full - string_length;
 	if (!BYTES_ARE_IN_FRAME(offset+4,string_length_full)) return offset;
 	if (string_length>=sizeof(string_buffer)) return offset;
@@ -414,7 +398,7 @@
 
 	if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
 	string_length = EXTRACT_UINT(pd,offset+0);
-	string_length_full = roundup(string_length);
+	string_length_full = rpc_roundup(string_length);
 	string_fill = string_length_full - string_length;
 	if (!BYTES_ARE_IN_FRAME(offset+4,string_length_full)) return offset;
 	if (string_length>=sizeof(string_buffer)) return offset;
@@ -456,7 +440,7 @@
 	/* if (!BYTES_ARE_IN_FRAME(offset,8)) return; */
 	flavor = EXTRACT_UINT(pd,offset+0);
 	length = EXTRACT_UINT(pd,offset+4);
-	length_full = roundup(length);
+	length_full = rpc_roundup(length);
 	/* if (!BYTES_ARE_IN_FRAME(offset+8,full_length)) return; */
 
 	if (tree) {
@@ -553,7 +537,7 @@
 
 	if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
 	length = EXTRACT_UINT(pd,offset+4);
-	length_full = roundup(length);
+	length_full = rpc_roundup(length);
 	if (!BYTES_ARE_IN_FRAME(offset+8,length_full)) return offset;
 
 	if (tree) {
@@ -578,7 +562,7 @@
 
 	if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
 	length = EXTRACT_UINT(pd,offset+4);
-	length_full = roundup(length);
+	length_full = rpc_roundup(length);
 	if (!BYTES_ARE_IN_FRAME(offset+8,length_full)) return offset;
 
 	if (tree) {
@@ -1047,7 +1031,4 @@
 proto_register_rpc(void)
 {
 	proto_rpc = proto_register_protocol("Remote Procedure Call", "rpc");
-
-	/* please remove this, if all specific dissectors are ready */
-	init_incomplete_dissect();
 }
Index: packet-rpc.h
===================================================================
RCS file: /cvsroot/ethereal/packet-rpc.h,v
retrieving revision 1.4
diff -u -r1.4 packet-rpc.h
--- packet-rpc.h	1999/11/11 21:22:00	1.4
+++ packet-rpc.h	1999/11/15 09:05:57
@@ -96,7 +96,7 @@
 extern void init_dissect_rpc();
 extern void cleanup_dissect_rpc();
 
-extern unsigned int roundup(unsigned int a);
+extern unsigned int rpc_roundup(unsigned int a);
 extern int dissect_rpc_string(const u_char *pd, int offset, frame_data *fd,
 	proto_tree *tree, char* name);
 extern int dissect_rpc_string_item(const u_char *pd, int offset, frame_data *fd,
Index: packet-tcp.c
===================================================================
RCS file: /cvsroot/ethereal/packet-tcp.c,v
retrieving revision 1.42
diff -u -r1.42 packet-tcp.c
--- packet-tcp.c	1999/11/11 23:13:43	1.42
+++ packet-tcp.c	1999/11/15 09:05:59
@@ -470,6 +470,13 @@
   /* Check the packet length to see if there's more data
      (it could be an ACK-only packet) */
   if (packet_max > offset) {
+
+    /* ONC RPC.  We can't base this on anything in the UDP header; we have
+       to look at the payload.  If "dissect_rpc()" returns TRUE, it was
+       an RPC packet, otherwise it's some other type of packet. */
+    if (dissect_rpc(pd, offset, fd, tree))
+      goto reas;
+
     /* XXX - this should be handled the way UDP handles this, with a table
        of port numbers to which stuff can be added */
 #define PORT_IS(port)	(th.th_sport == port || th.th_dport == port)
@@ -529,6 +536,8 @@
         }
     }
   }
+
+reas:
  
   if( data_out_file ) {
     reassemble_tcp( th.th_seq,		/* sequence number */
Index: packet.h
===================================================================
RCS file: /cvsroot/ethereal/packet.h,v
retrieving revision 1.140
diff -u -r1.140 packet.h
--- packet.h	1999/11/14 20:44:52	1.140
+++ packet.h	1999/11/15 09:06:03
@@ -455,11 +455,25 @@
 	ETT_NFS_TIMEVAL,
 	ETT_NFS_MODE,
 	ETT_NFS_FATTR,
+	ETT_NFS_SATTR,
 	ETT_NFS_MODE3,
 	ETT_NFS_SPECDATA3,
 	ETT_NFS_FH3,
 	ETT_NFS_NFSTIME3,
 	ETT_NFS_FATTR3,
+	ETT_NFS_POST_OP_ATTR,
+	ETT_NFS_WCC_ATTR,
+	ETT_NFS_PRE_OP_ATTR,
+	ETT_NFS_WCC_DATA,
+	ETT_NFS_SET_MODE3,
+	ETT_NFS_SET_UID3,
+	ETT_NFS_SET_GID3,
+	ETT_NFS_SET_SIZE3,
+	ETT_NFS_SET_ATIME,
+	ETT_NFS_SET_MTIME,
+	ETT_NFS_SATTR3,
+	ETT_NFS_SATTRGUARD3,
+	ETT_BOOT,
 	ETT_NLM,
 	ETT_PORTMAP,
 	ETT_STAT,
/* packet-nlm.c
 * Routines for nlm dissection
 *
 * $Id: $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxx>
 * Copyright 1998 Gerald Combs
 *
 * Copied from packet-mount.c
 *
 * 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


#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif


#include "packet-rpc.h"
#include "packet-nlm.h"


static int proto_nlm = -1;


/* proc number, "proc name", dissect_request, dissect_reply */
/* NULL as function pointer means: take the generic one. */
/* NLM protocol version 3 */
const vsff nlm3_proc[] = {
	{ 0,	"NULL",		NULL,	NULL },
	{ 1,	"TEST",		NULL,	NULL },
	{ 2,	"LOCK",		NULL,	NULL },
	{ 3,	"CANCEL",	NULL,	NULL },
	{ 4,	"UNLOCK",	NULL,	NULL },
	{ 5,	"GRANTED",	NULL,	NULL },
	{ 6,	"TEST_MSG",	NULL,	NULL },
	{ 7,	"LOCK_MSG",	NULL,	NULL },
	{ 8,	"CANCEL_MSG",	NULL,	NULL },
	{ 9,	"UNLOCK_MSG",	NULL,	NULL },
	{ 10,	"GRANTED_MSG",	NULL,	NULL },
	{ 11,	"TEST_RES",	NULL,	NULL },
	{ 12,	"LOCK_RES",	NULL,	NULL },
	{ 13,	"CANCEL_RES",	NULL,	NULL },
	{ 14,	"UNLOCK_RES",	NULL,	NULL },
	{ 15,	"GRANTED_RES",	NULL,	NULL },
	{ 20,	"SHARE",	NULL,	NULL },
	{ 21,	"UNSHARE",	NULL,	NULL },
	{ 22,	"NM_LOCK",	NULL,	NULL },
	{ 23,	"FREE_ALL",	NULL,	NULL },
	{ 0,	NULL,		NULL,	NULL }
};
/* end of NLM protocol version 3 */


void
proto_register_nlm(void)
{
	proto_nlm = proto_register_protocol("Network Lock Manager Protocol", "NLM");

	/* Register the protocol as RPC */
	rpc_init_prog(proto_nlm, NLM_PROGRAM, ETT_NLM);
	/* Register the procedure table */
	rpc_init_proc_table(NLM_PROGRAM, 3, nlm3_proc);
}


/* packet-nlm.h (c) 1999 Uwe Girlich */
/* $Id: $ */

#ifndef __PACKET_NLM_H__
#define __PACKET_NLM_H__

#define NLM_PROGRAM 100021

#endif /* packet-nlm.h */