From Hannes Gredler: make the IS-IS dissector more verbose in the INFO
[obnox/wireshark/wip.git] / packet-mount.c
1 /* packet-mount.c
2  * Routines for mount dissection
3  *
4  * $Id: packet-mount.c,v 1.33 2002/08/28 21:00:22 jmayer Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * Copied from packet-smb.c
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31
32 #include <string.h>
33
34 #include "packet-rpc.h"
35 #include "packet-mount.h"
36 #include "packet-nfs.h"
37
38
39 static int proto_mount = -1;
40 static int hf_mount_path = -1;
41 static int hf_mount3_status = -1;
42 static int hf_mount_mountlist_hostname = -1;
43 static int hf_mount_mountlist_directory = -1;
44 static int hf_mount_mountlist = -1;
45 static int hf_mount_groups_group = -1;
46 static int hf_mount_groups = -1;
47 static int hf_mount_exportlist_directory = -1;
48 static int hf_mount_exportlist = -1;
49 static int hf_mount_pathconf_link_max = -1;
50 static int hf_mount_pathconf_max_canon = -1;
51 static int hf_mount_pathconf_max_input = -1;
52 static int hf_mount_pathconf_name_max = -1;
53 static int hf_mount_pathconf_path_max = -1;
54 static int hf_mount_pathconf_pipe_buf = -1;
55 static int hf_mount_pathconf_vdisable = -1;
56 static int hf_mount_pathconf_mask = -1;
57 static int hf_mount_pathconf_error_all = -1;
58 static int hf_mount_pathconf_error_link_max = -1;
59 static int hf_mount_pathconf_error_max_canon = -1;
60 static int hf_mount_pathconf_error_max_input = -1;
61 static int hf_mount_pathconf_error_name_max = -1;
62 static int hf_mount_pathconf_error_path_max = -1;
63 static int hf_mount_pathconf_error_pipe_buf = -1;
64 static int hf_mount_pathconf_chown_restricted = -1;
65 static int hf_mount_pathconf_no_trunc = -1;
66 static int hf_mount_pathconf_error_vdisable = -1;
67 static int hf_mount_flavors = -1;
68 static int hf_mount_flavor = -1;
69
70 static gint ett_mount = -1;
71 static gint ett_mount_mountlist = -1;
72 static gint ett_mount_groups = -1;
73 static gint ett_mount_exportlist = -1;
74 static gint ett_mount_pathconf_mask = -1;
75
76 #define MAX_GROUP_NAME_LIST 128
77 static char group_name_list[MAX_GROUP_NAME_LIST];
78 static int  group_names_len;
79
80 /* RFC 1094, Page 24 */
81 /* This function dissects fhstatus for v1 and v2 of the mount protocol.
82  * Formally, hf_mount3_status only define the status codes returned by version
83  * 3 of the protocol.
84  * Though not formally defined in the standard, we use the same
85  * value-to-string mappings as version 3 since we belive that this mapping
86  * is consistant with most v1 and v2 implementations.
87  */
88 static int
89 dissect_fhstatus(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
90 {
91         gint32 status;
92
93         status=tvb_get_ntohl(tvb,offset);
94         offset = dissect_rpc_uint32(tvb,tree,hf_mount3_status,offset);
95
96         switch (status) {
97                 case 0:
98                         offset = dissect_fhandle(tvb,offset,pinfo,tree,"fhandle");
99                 break;
100                 default:
101                         /* void */
102                 break;
103         }
104
105         return offset;
106 }
107
108
109 static int
110 dissect_mount_dirpath_call(tvbuff_t *tvb, int offset, packet_info *pinfo,
111                 proto_tree *tree)
112 {
113         if((!pinfo->fd->flags.visited) && nfs_file_name_snooping){
114                 rpc_call_info_value *civ=pinfo->private_data;
115
116                 if(civ->request && (civ->proc==1)){
117                         unsigned char *host, *name;
118                         unsigned const char *dir;
119                         int len;
120
121                         host=ip_to_str(pinfo->dst.data);
122                         len=tvb_get_ntohl(tvb, offset);
123
124                         dir=tvb_get_ptr(tvb, offset+4, len);
125                         if(dir){
126                                 unsigned char *ptr;
127                                 name=g_malloc(strlen(host)+1+len+1+200);
128                                 ptr=name;
129                                 memcpy(ptr, host, strlen(host));
130                                 ptr+=strlen(host);
131                                 *ptr++=':';
132                                 memcpy(ptr, dir, len);
133                                 ptr+=len;
134                                 *ptr=0;
135
136                                 nfs_name_snoop_add_name(civ->xid, tvb, -1, strlen(name), 0, 0, name);
137                         }
138                 }
139         }
140
141         if ( tree )
142         {
143                 offset = dissect_rpc_string(tvb,tree,hf_mount_path,offset,NULL);
144         }
145
146         return offset;
147 }
148
149
150 /* RFC 1094, Page 25,26 */
151 static int
152 dissect_mount1_mnt_reply(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
153 {
154         offset = dissect_fhstatus(tvb,offset,pinfo,tree);
155
156         return offset;
157 }
158
159
160
161 /* RFC 1094, Page 26 */
162 /* RFC 1813, Page 110 */
163 static int
164 dissect_mountlist(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
165 {
166         proto_item* lock_item = NULL;
167         proto_tree* lock_tree = NULL;
168         int old_offset = offset;
169         char* hostname;
170         char* directory;
171
172         if (tree) {
173                 lock_item = proto_tree_add_item(tree, hf_mount_mountlist, tvb,
174                                         offset, -1, FALSE);
175                 if (lock_item)
176                         lock_tree = proto_item_add_subtree(lock_item, ett_mount_mountlist);
177         }
178
179         offset = dissect_rpc_string(tvb, lock_tree,
180                         hf_mount_mountlist_hostname, offset, &hostname);
181         offset = dissect_rpc_string(tvb, lock_tree,
182                         hf_mount_mountlist_directory, offset, &directory);
183
184         if (lock_item) {
185                 /* now we have a nicer string */
186                 proto_item_set_text(lock_item, "Mount List Entry: %s:%s", hostname, directory);
187                 /* now we know, that mountlist is shorter */
188                 proto_item_set_len(lock_item, offset - old_offset);
189         }
190         g_free(hostname);
191         g_free(directory);
192
193         return offset;
194 }
195
196
197 /* RFC 1094, Page 26 */
198 /* RFC 1813, Page 110 */
199 static int
200 dissect_mount_dump_reply(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
201 {
202         offset = dissect_rpc_list(tvb, pinfo, tree, offset, dissect_mountlist);
203
204         return offset;
205 }
206
207
208
209 /* RFC 1094, Page 26 */
210 /* RFC 1813, Page 110 */
211 static int
212 dissect_group(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
213 {
214         int len,str_len;
215         len=tvb_get_ntohl(tvb,offset);
216         str_len=tvb_get_nstringz(tvb,offset+4,
217                 MAX_GROUP_NAME_LIST-5-group_names_len,
218                 group_name_list+group_names_len);
219         if((group_names_len>=(MAX_GROUP_NAME_LIST-5))||(str_len<0)){
220                 strcpy(group_name_list+(MAX_GROUP_NAME_LIST-5),"...");
221                 group_names_len=MAX_GROUP_NAME_LIST-1;
222         } else {
223                 group_names_len+=str_len;
224                 group_name_list[group_names_len++]=' ';
225         }
226         group_name_list[group_names_len]=0;
227
228         offset = dissect_rpc_string(tvb, tree,
229                         hf_mount_groups_group, offset, NULL);
230
231         return offset;
232 }
233
234
235 /* RFC 1094, Page 26 */
236 /* RFC 1813, Page 113 */
237 static int
238 dissect_exportlist(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
239 {
240         proto_item* exportlist_item = NULL;
241         proto_tree* exportlist_tree = NULL;
242         int old_offset = offset;
243         int groups_offset;
244         proto_item* groups_item = NULL;
245         proto_item* groups_tree = NULL;
246         char* directory;
247
248         group_name_list[0]=0;
249         group_names_len=0;
250         if (tree) {
251                 exportlist_item = proto_tree_add_item(tree, hf_mount_exportlist, tvb,
252                                         offset, -1, FALSE);
253                 if (exportlist_item)
254                         exportlist_tree = proto_item_add_subtree(exportlist_item, ett_mount_exportlist);
255         }
256
257         offset = dissect_rpc_string(tvb, exportlist_tree,
258                         hf_mount_exportlist_directory, offset, &directory);
259         groups_offset = offset;
260
261         if (tree) {
262                 groups_item = proto_tree_add_item(exportlist_tree, hf_mount_groups, tvb,
263                                         offset, -1, FALSE);
264                 if (groups_item)
265                         groups_tree = proto_item_add_subtree(groups_item, ett_mount_groups);
266         }
267
268         offset = dissect_rpc_list(tvb, pinfo, groups_tree, offset, dissect_group);
269         if (groups_item) {
270                 /* mark empty lists */
271                 if (offset - groups_offset == 4) {
272                         proto_item_set_text(groups_item, "Groups: empty");
273                 }
274
275                 /* now we know, that groups is shorter */
276                 proto_item_set_len(groups_item, offset - groups_offset);
277         }
278
279         if (exportlist_item) {
280                 /* now we have a nicer string */
281                 proto_item_set_text(exportlist_item, "Export List Entry: %s -> %s", directory,group_name_list);
282                 /* now we know, that exportlist is shorter */
283                 proto_item_set_len(exportlist_item, offset - old_offset);
284         }
285         g_free(directory);
286
287         return offset;
288 }
289
290
291 /* RFC 1094, Page 26 */
292 /* RFC 1813, Page 113 */
293 static int
294 dissect_mount_export_reply(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
295 {
296         offset = dissect_rpc_list(tvb, pinfo, tree, offset, dissect_exportlist);
297
298         return offset;
299 }
300
301
302 #define OFFS_MASK       32      /* offset of the "pc_mask" field */
303
304 #define PC_ERROR_ALL            0x0001
305 #define PC_ERROR_LINK_MAX       0x0002
306 #define PC_ERROR_MAX_CANON      0x0004
307 #define PC_ERROR_MAX_INPUT      0x0008
308 #define PC_ERROR_NAME_MAX       0x0010
309 #define PC_ERROR_PATH_MAX       0x0020
310 #define PC_ERROR_PIPE_BUF       0x0040
311 #define PC_CHOWN_RESTRICTED     0x0080
312 #define PC_NO_TRUNC             0x0100
313 #define PC_ERROR_VDISABLE       0x0200
314
315 static const true_false_string tos_error_all = {
316   "All info invalid",
317   "Some or all info valid"
318 };
319
320 static const true_false_string tos_error_link_max = {
321   "LINK_MAX invalid",
322   "LINK_MAX valid"
323 };
324
325 static const true_false_string tos_error_max_canon = {
326   "MAX_CANON invalid",
327   "MAX_CANON valid"
328 };
329
330 static const true_false_string tos_error_max_input = {
331   "MAX_INPUT invalid",
332   "MAX_INPUT valid"
333 };
334
335 static const true_false_string tos_error_name_max = {
336   "NAME_MAX invalid",
337   "NAME_MAX valid"
338 };
339
340 static const true_false_string tos_error_path_max = {
341   "PATH_MAX invalid",
342   "PATH_MAX valid"
343 };
344
345 static const true_false_string tos_error_pipe_buf = {
346   "PIPE_BUF invalid",
347   "PIPE_BUF valid"
348 };
349
350 static const true_false_string tos_chown_restricted = {
351   "Only a privileged user can change the ownership of a file",
352   "Users may give away their own files"
353 };
354
355 static const true_false_string tos_no_trunc = {
356   "File names that are too long will get an error",
357   "File names that are too long will be truncated"
358 };
359
360 static const true_false_string tos_error_vdisable = {
361   "VDISABLE invalid",
362   "VDISABLE valid"
363 };
364
365
366 static int
367 dissect_mount_pathconf_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
368 {
369         int saved_offset;
370         guint32 pc_mask;
371         proto_item *lock_item;
372         proto_tree *lock_tree;
373
374         saved_offset=offset;
375         /*
376          * Extract the mask first, so we know which other fields the
377          * server was able to return to us.
378          */
379         pc_mask = tvb_get_ntohl(tvb, offset+OFFS_MASK) & 0xffff;
380         if (!(pc_mask & (PC_ERROR_LINK_MAX|PC_ERROR_ALL))) {
381                 if (tree) {
382                         dissect_rpc_uint32(tvb,tree,hf_mount_pathconf_link_max,offset);
383                 }
384         }
385         offset += 4;
386
387         if (!(pc_mask & (PC_ERROR_MAX_CANON|PC_ERROR_ALL))) {
388                 if (tree) {
389                         proto_tree_add_item(tree,
390                                 hf_mount_pathconf_max_canon,tvb,offset+2,2,
391                                 tvb_get_ntohs(tvb,offset)&0xffff);
392                 }
393         }
394         offset += 4;
395
396         if (!(pc_mask & (PC_ERROR_MAX_INPUT|PC_ERROR_ALL))) {
397                 if (tree) {
398                         proto_tree_add_item(tree,
399                                 hf_mount_pathconf_max_input,tvb,offset+2,2,
400                                 tvb_get_ntohs(tvb,offset)&0xffff);
401                 }
402         }
403         offset += 4;
404
405         if (!(pc_mask & (PC_ERROR_NAME_MAX|PC_ERROR_ALL))) {
406                 if (tree) {
407                         proto_tree_add_item(tree,
408                                 hf_mount_pathconf_name_max,tvb,offset+2,2,
409                                 tvb_get_ntohs(tvb,offset)&0xffff);
410                 }
411         }
412         offset += 4;
413
414         if (!(pc_mask & (PC_ERROR_PATH_MAX|PC_ERROR_ALL))) {
415                 if (tree) {
416                         proto_tree_add_item(tree,
417                                 hf_mount_pathconf_path_max,tvb,offset+2,2,
418                                 tvb_get_ntohs(tvb,offset)&0xffff);
419                 }
420         }
421         offset += 4;
422
423         if (!(pc_mask & (PC_ERROR_PIPE_BUF|PC_ERROR_ALL))) {
424                 if (tree) {
425                         proto_tree_add_item(tree,
426                                 hf_mount_pathconf_pipe_buf,tvb,offset+2,2,
427                                 tvb_get_ntohs(tvb,offset)&0xffff);
428                 }
429         }
430         offset += 4;
431
432         offset += 4;    /* skip "pc_xxx" pad field */
433
434         if (!(pc_mask & (PC_ERROR_VDISABLE|PC_ERROR_ALL))) {
435                 if (tree) {
436                         proto_tree_add_item(tree,
437                                 hf_mount_pathconf_vdisable,tvb,offset+3,1,
438                                 tvb_get_ntohs(tvb,offset)&0xffff);
439                 }
440         }
441         offset += 4;
442
443
444         if (tree) {
445                 lock_item = proto_tree_add_item(tree, hf_mount_pathconf_mask, tvb,
446                                         offset+2, 2, FALSE);
447
448                 lock_tree = proto_item_add_subtree(lock_item, ett_mount_pathconf_mask);
449                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_all, tvb,
450                     offset + 2, 2, pc_mask);
451
452                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_link_max, tvb,
453                     offset + 2, 2, pc_mask);
454                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_max_canon, tvb,
455                     offset + 2, 2, pc_mask);
456                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_max_input, tvb,
457                     offset + 2, 2, pc_mask);
458                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_name_max, tvb,
459                     offset + 2, 2, pc_mask);
460                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_path_max, tvb,
461                     offset + 2, 2, pc_mask);
462                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_pipe_buf, tvb,
463                     offset + 2, 2, pc_mask);
464                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_chown_restricted, tvb,
465                     offset + 2, 2, pc_mask);
466                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_no_trunc, tvb,
467                     offset + 2, 2, pc_mask);
468                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_vdisable, tvb,
469                     offset + 2, 2, pc_mask);
470         }
471
472         offset += 8;
473         return offset;
474 }
475
476 /* RFC 1813, Page 107 */
477 static const value_string mount3_mountstat3[] =
478 {
479         {       0,      "OK" },
480         {       1,      "ERR_PERM" },
481         {       2,      "ERR_NOENT" },
482         {       5,      "ERR_IO" },
483         {       13,     "ERR_ACCESS" },
484         {       20,     "ERR_NOTDIR" },
485         {       22,     "ERR_INVAL" },
486         {       63,     "ERR_NAMETOOLONG" },
487         {       10004,  "ERR_NOTSUPP" },
488         {       10006,  "ERR_SERVERFAULT" },
489         {       0,      NULL }
490 };
491
492
493 /* RFC 1813, Page 107 */
494 static int
495 dissect_mountstat3(tvbuff_t *tvb, proto_tree *tree, int offset, int hfindex, guint32 *status)
496 {
497         guint32 mountstat3;
498
499         mountstat3 = tvb_get_ntohl(tvb, offset);
500
501         offset = dissect_rpc_uint32(tvb,tree,hfindex,offset);
502         *status = mountstat3;
503         return offset;
504 }
505
506 /* RFC 1831, Page 109 */
507 static int
508 dissect_mount3_mnt_reply(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
509 {
510         guint32 status;
511         guint32 auth_flavors;
512         guint32 auth_flavor;
513         guint32 auth_flavor_i;
514
515         offset = dissect_mountstat3(tvb,tree,offset,hf_mount3_status,&status);
516
517         switch (status) {
518                 case 0:
519                         offset = dissect_nfs_fh3(tvb,offset,pinfo,tree,"fhandle");
520
521                         auth_flavors = tvb_get_ntohl(tvb, offset);
522                         proto_tree_add_uint(tree,hf_mount_flavors, tvb,
523                                 offset, 4, auth_flavors);
524                         offset += 4;
525                         for (auth_flavor_i = 0 ; auth_flavor_i < auth_flavors ; auth_flavor_i++) {
526                                 auth_flavor = tvb_get_ntohl(tvb, offset);
527                                 proto_tree_add_uint(tree,hf_mount_flavor, tvb,
528                                         offset, 4, auth_flavor);
529                                 offset += 4;
530                         }
531                 break;
532                 default:
533                         /* void */
534                 break;
535         }
536
537         return offset;
538 }
539
540
541 /* proc number, "proc name", dissect_request, dissect_reply */
542 /* NULL as function pointer means: type of arguments is "void". */
543
544 /* Mount protocol version 1, RFC 1094 */
545 static const vsff mount1_proc[] = {
546     { 0, "NULL", NULL, NULL },
547     { MOUNTPROC_MNT,        "MNT",
548                 dissect_mount_dirpath_call, dissect_mount1_mnt_reply },
549     { MOUNTPROC_DUMP,       "DUMP",
550                 NULL, dissect_mount_dump_reply },
551     { MOUNTPROC_UMNT,      "UMNT",
552                 dissect_mount_dirpath_call, NULL },
553     { MOUNTPROC_UMNTALL,   "UMNTALL",
554                 NULL, NULL },
555     { MOUNTPROC_EXPORT,    "EXPORT",
556                 NULL, dissect_mount_export_reply },
557     { MOUNTPROC_EXPORTALL, "EXPORTALL",
558                 NULL, dissect_mount_export_reply },
559     { 0, NULL, NULL, NULL }
560 };
561 /* end of mount version 1 */
562
563
564 /* Mount protocol version 2, private communication from somebody at Sun;
565    mount V2 is V1 plus MOUNTPROC_PATHCONF to fetch information for the
566    POSIX "pathconf()" call. */
567 static const vsff mount2_proc[] = {
568     { 0, "NULL", NULL, NULL },
569     { MOUNTPROC_MNT,        "MNT",
570                 dissect_mount_dirpath_call, dissect_mount1_mnt_reply },
571     { MOUNTPROC_DUMP,       "DUMP",
572                 NULL, dissect_mount_dump_reply },
573     { MOUNTPROC_UMNT,      "UMNT",
574                 dissect_mount_dirpath_call, NULL },
575     { MOUNTPROC_UMNTALL,   "UMNTALL",
576                 NULL, NULL },
577     { MOUNTPROC_EXPORT,    "EXPORT",
578                 NULL, dissect_mount_export_reply },
579     { MOUNTPROC_EXPORTALL, "EXPORTALL",
580                 NULL, dissect_mount_export_reply },
581     { MOUNTPROC_PATHCONF,  "PATHCONF",
582                 dissect_mount_dirpath_call, dissect_mount_pathconf_reply },
583     { 0, NULL, NULL, NULL }
584 };
585 /* end of mount version 2 */
586
587
588 /* Mount protocol version 3, RFC 1813 */
589 static const vsff mount3_proc[] = {
590         { 0, "NULL", NULL, NULL },
591         { MOUNTPROC_MNT, "MNT",
592                 dissect_mount_dirpath_call, dissect_mount3_mnt_reply },
593         { MOUNTPROC_DUMP, "DUMP",
594                 NULL, dissect_mount_dump_reply },
595         { MOUNTPROC_UMNT, "UMNT",
596                 dissect_mount_dirpath_call, NULL },
597         { MOUNTPROC_UMNTALL, "UMNTALL",
598                 NULL, NULL },
599         { MOUNTPROC_EXPORT, "EXPORT",
600                 NULL, dissect_mount_export_reply },
601         { 0, NULL, NULL, NULL }
602 };
603 /* end of Mount protocol version 3 */
604
605
606 void
607 proto_register_mount(void)
608 {
609         static hf_register_info hf[] = {
610                 { &hf_mount_path, {
611                         "Path", "mount.path", FT_STRING, BASE_DEC,
612                         NULL, 0, "Path", HFILL }},
613                 { &hf_mount3_status, {
614                         "Status", "mount.status", FT_UINT32, BASE_DEC,
615                         VALS(mount3_mountstat3), 0, "Status", HFILL }},
616                 { &hf_mount_mountlist_hostname, {
617                         "Hostname", "mount.dump.hostname", FT_STRING, BASE_DEC,
618                         NULL, 0, "Hostname", HFILL }},
619                 { &hf_mount_mountlist_directory, {
620                         "Directory", "mount.dump.directory", FT_STRING, BASE_DEC,
621                         NULL, 0, "Directory", HFILL }},
622                 { &hf_mount_mountlist, {
623                         "Mount List Entry", "mount.dump.entry", FT_NONE, 0,
624                         NULL, 0, "Mount List Entry", HFILL }},
625                 { &hf_mount_groups_group, {
626                         "Group", "mount.export.group", FT_STRING, BASE_DEC,
627                         NULL, 0, "Group", HFILL }},
628                 { &hf_mount_groups, {
629                         "Groups", "mount.export.groups", FT_NONE, 0,
630                         NULL, 0, "Groups", HFILL }},
631                 { &hf_mount_exportlist_directory, {
632                         "Directory", "mount.export.directory", FT_STRING, BASE_DEC,
633                         NULL, 0, "Directory", HFILL }},
634                 { &hf_mount_exportlist, {
635                         "Export List Entry", "mount.export.entry", FT_NONE, 0,
636                         NULL, 0, "Export List Entry", HFILL }},
637                 { &hf_mount_pathconf_link_max, {
638                         "Maximum number of links to a file", "mount.pathconf.link_max",
639                         FT_UINT32, BASE_DEC,
640                         NULL, 0, "Maximum number of links allowed to a file", HFILL }},
641                 { &hf_mount_pathconf_max_canon, {
642                         "Maximum terminal input line length", "mount.pathconf.max_canon",
643                         FT_UINT16, BASE_DEC,
644                         NULL, 0, "Max tty input line length", HFILL }},
645                 { &hf_mount_pathconf_max_input, {
646                         "Terminal input buffer size", "mount.pathconf.max_input",
647                         FT_UINT16, BASE_DEC,
648                         NULL, 0, "Terminal input buffer size", HFILL }},
649                 { &hf_mount_pathconf_name_max, {
650                         "Maximum file name length", "mount.pathconf.name_max",
651                         FT_UINT16, BASE_DEC,
652                         NULL, 0, "Maximum file name length", HFILL }},
653                 { &hf_mount_pathconf_path_max, {
654                         "Maximum path name length", "mount.pathconf.path_max",
655                         FT_UINT16, BASE_DEC,
656                         NULL, 0, "Maximum path name length", HFILL }},
657                 { &hf_mount_pathconf_pipe_buf, {
658                         "Pipe buffer size", "mount.pathconf.pipe_buf",
659                         FT_UINT16, BASE_DEC,
660                         NULL, 0, "Maximum amount of data that can be written atomically to a pipe", HFILL }},
661                 { &hf_mount_pathconf_vdisable, {
662                         "VDISABLE character", "mount.pathconf.vdisable_char",
663                         FT_UINT8, BASE_HEX,
664                         NULL, 0, "Character value to disable a terminal special character", HFILL }},
665                 { &hf_mount_pathconf_mask, {
666                         "Reply error/status bits", "mount.pathconf.mask",
667                         FT_UINT16, BASE_HEX,
668                         NULL, 0, "Bit mask with error and status bits", HFILL }},
669                 { &hf_mount_pathconf_error_all, {
670                         "ERROR_ALL",    "mount.pathconf.mask.error_all",
671                         FT_BOOLEAN, 16, TFS(&tos_error_all),
672                         PC_ERROR_ALL, "", HFILL }},
673                 { &hf_mount_pathconf_error_link_max, {
674                         "ERROR_LINK_MAX", "mount.pathconf.mask.error_link_max",
675                         FT_BOOLEAN, 16, TFS(&tos_error_link_max),
676                         PC_ERROR_LINK_MAX, "", HFILL }},
677                 { &hf_mount_pathconf_error_max_canon, {
678                         "ERROR_MAX_CANON", "mount.pathconf.mask.error_max_canon",
679                         FT_BOOLEAN, 16, TFS(&tos_error_max_canon),
680                         PC_ERROR_MAX_CANON, "", HFILL }},
681                 { &hf_mount_pathconf_error_max_input, {
682                         "ERROR_MAX_INPUT", "mount.pathconf.mask.error_max_input",
683                         FT_BOOLEAN, 16, TFS(&tos_error_max_input),
684                         PC_ERROR_MAX_INPUT, "", HFILL }},
685                 { &hf_mount_pathconf_error_name_max, {
686                         "ERROR_NAME_MAX", "mount.pathconf.mask.error_name_max",
687                         FT_BOOLEAN, 16, TFS(&tos_error_name_max),
688                         PC_ERROR_NAME_MAX, "", HFILL }},
689                 { &hf_mount_pathconf_error_path_max, {
690                         "ERROR_PATH_MAX", "mount.pathconf.mask.error_path_max",
691                         FT_BOOLEAN, 16, TFS(&tos_error_path_max),
692                         PC_ERROR_PATH_MAX, "", HFILL }},
693                 { &hf_mount_pathconf_error_pipe_buf, {
694                         "ERROR_PIPE_BUF", "mount.pathconf.mask.error_pipe_buf",
695                         FT_BOOLEAN, 16, TFS(&tos_error_pipe_buf),
696                         PC_ERROR_PIPE_BUF, "", HFILL }},
697                 { &hf_mount_pathconf_chown_restricted, {
698                         "CHOWN_RESTRICTED", "mount.pathconf.mask.chown_restricted",
699                         FT_BOOLEAN, 16, TFS(&tos_chown_restricted),
700                         PC_CHOWN_RESTRICTED, "", HFILL }},
701                 { &hf_mount_pathconf_no_trunc, {
702                         "NO_TRUNC", "mount.pathconf.mask.no_trunc",
703                         FT_BOOLEAN, 16, TFS(&tos_no_trunc),
704                         PC_NO_TRUNC, "", HFILL }},
705                 { &hf_mount_pathconf_error_vdisable, {
706                         "ERROR_VDISABLE", "mount.pathconf.mask.error_vdisable",
707                         FT_BOOLEAN, 16, TFS(&tos_error_vdisable),
708                         PC_ERROR_VDISABLE, "", HFILL }},
709                 { &hf_mount_flavors, {
710                         "Flavors", "mount.flavors", FT_UINT32, BASE_DEC,
711                         NULL, 0, "Flavors", HFILL }},
712                 { &hf_mount_flavor, {
713                         "Flavor", "mount.flavor", FT_UINT32, BASE_DEC,
714                         VALS(rpc_auth_flavor), 0, "Flavor", HFILL }},
715         };
716         static gint *ett[] = {
717                 &ett_mount,
718                 &ett_mount_mountlist,
719                 &ett_mount_groups,
720                 &ett_mount_exportlist,
721                 &ett_mount_pathconf_mask,
722         };
723
724         proto_mount = proto_register_protocol("Mount Service", "MOUNT", "mount");
725         proto_register_field_array(proto_mount, hf, array_length(hf));
726         proto_register_subtree_array(ett, array_length(ett));
727 }
728
729 void
730 proto_reg_handoff_mount(void)
731 {
732         /* Register the protocol as RPC */
733         rpc_init_prog(proto_mount, MOUNT_PROGRAM, ett_mount);
734         /* Register the procedure tables */
735         rpc_init_proc_table(MOUNT_PROGRAM, 1, mount1_proc);
736         rpc_init_proc_table(MOUNT_PROGRAM, 2, mount2_proc);
737         rpc_init_proc_table(MOUNT_PROGRAM, 3, mount3_proc);
738 }