xref: /dragonfly/contrib/tcpdump/print-rx.c (revision 6f5ec8b5)
1 /*
2  * Copyright: (c) 2000 United States Government as represented by the
3  *	Secretary of the Navy. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *   1. Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *   2. Redistributions in binary form must reproduce the above copyright
12  *      notice, this list of conditions and the following disclaimer in
13  *      the documentation and/or other materials provided with the
14  *      distribution.
15  *   3. The names of the authors may not be used to endorse or promote
16  *      products derived from this software without specific prior
17  *      written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22  */
23 
24 /* \summary: AFS RX printer */
25 
26 /*
27  * This code unmangles RX packets.  RX is the mutant form of RPC that AFS
28  * uses to communicate between clients and servers.
29  *
30  * In this code, I mainly concern myself with decoding the AFS calls, not
31  * with the guts of RX, per se.
32  *
33  * Bah.  If I never look at rx_packet.h again, it will be too soon.
34  *
35  * Ken Hornstein <kenh@cmf.nrl.navy.mil>
36  */
37 
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41 
42 #include <stdio.h>
43 #include <string.h>
44 #include "netdissect-stdinc.h"
45 
46 #include "netdissect.h"
47 #include "addrtoname.h"
48 #include "extract.h"
49 
50 #include "ip.h"
51 
52 #define FS_RX_PORT	7000
53 #define CB_RX_PORT	7001
54 #define PROT_RX_PORT	7002
55 #define VLDB_RX_PORT	7003
56 #define KAUTH_RX_PORT	7004
57 #define VOL_RX_PORT	7005
58 #define ERROR_RX_PORT	7006		/* Doesn't seem to be used */
59 #define BOS_RX_PORT	7007
60 
61 #define AFSOPAQUEMAX 1024
62 #define AFSNAMEMAX 256			/* Must be >= PRNAMEMAX + 1, VLNAMEMAX + 1, and 32 + 1 */
63 #define PRNAMEMAX 64
64 #define VLNAMEMAX 65
65 #define KANAMEMAX 64
66 #define BOSNAMEMAX 256
67 #define USERNAMEMAX 1024		/* AFSOPAQUEMAX was used for this; does it need to be this big? */
68 
69 #define	PRSFS_READ		1 /* Read files */
70 #define	PRSFS_WRITE		2 /* Write files */
71 #define	PRSFS_INSERT		4 /* Insert files into a directory */
72 #define	PRSFS_LOOKUP		8 /* Lookup files into a directory */
73 #define	PRSFS_DELETE		16 /* Delete files */
74 #define	PRSFS_LOCK		32 /* Lock files */
75 #define	PRSFS_ADMINISTER	64 /* Change ACL's */
76 
77 struct rx_header {
78 	nd_uint32_t epoch;
79 	nd_uint32_t cid;
80 	nd_uint32_t callNumber;
81 	nd_uint32_t seq;
82 	nd_uint32_t serial;
83 	nd_uint8_t type;
84 #define RX_PACKET_TYPE_DATA		1
85 #define RX_PACKET_TYPE_ACK		2
86 #define RX_PACKET_TYPE_BUSY		3
87 #define RX_PACKET_TYPE_ABORT		4
88 #define RX_PACKET_TYPE_ACKALL		5
89 #define RX_PACKET_TYPE_CHALLENGE	6
90 #define RX_PACKET_TYPE_RESPONSE		7
91 #define RX_PACKET_TYPE_DEBUG		8
92 #define RX_PACKET_TYPE_PARAMS		9
93 #define RX_PACKET_TYPE_VERSION		13
94 	nd_uint8_t flags;
95 #define RX_CLIENT_INITIATED	1
96 #define RX_REQUEST_ACK		2
97 #define RX_LAST_PACKET		4
98 #define RX_MORE_PACKETS		8
99 #define RX_FREE_PACKET		16
100 #define RX_SLOW_START_OK	32
101 #define RX_JUMBO_PACKET		32
102 	nd_uint8_t userStatus;
103 	nd_uint8_t securityIndex;
104 	nd_uint16_t spare;		/* How clever: even though the AFS */
105 	nd_uint16_t serviceId;		/* header files indicate that the */
106 };					/* serviceId is first, it's really */
107 					/* encoded _after_ the spare field */
108 					/* I wasted a day figuring that out! */
109 
110 #define NUM_RX_FLAGS 7
111 
112 #define RX_MAXACKS 255
113 
114 struct rx_ackPacket {
115 	nd_uint16_t bufferSpace;	/* Number of packet buffers available */
116 	nd_uint16_t maxSkew;		/* Max diff between ack'd packet and */
117 					/* highest packet received */
118 	nd_uint32_t firstPacket;	/* The first packet in ack list */
119 	nd_uint32_t previousPacket;	/* Previous packet recv'd (obsolete) */
120 	nd_uint32_t serial;		/* # of packet that prompted the ack */
121 	nd_uint8_t reason;		/* Reason for acknowledgement */
122 	nd_uint8_t nAcks;		/* Number of acknowledgements */
123 	/* Followed by nAcks acknowledgments */
124 #if 0
125 	uint8_t acks[RX_MAXACKS];	/* Up to RX_MAXACKS acknowledgements */
126 #endif
127 };
128 
129 /*
130  * Values for the acks array
131  */
132 
133 #define RX_ACK_TYPE_NACK	0	/* Don't have this packet */
134 #define RX_ACK_TYPE_ACK		1	/* I have this packet */
135 
136 static const struct tok rx_types[] = {
137 	{ RX_PACKET_TYPE_DATA,		"data" },
138 	{ RX_PACKET_TYPE_ACK,		"ack" },
139 	{ RX_PACKET_TYPE_BUSY,		"busy" },
140 	{ RX_PACKET_TYPE_ABORT,		"abort" },
141 	{ RX_PACKET_TYPE_ACKALL,	"ackall" },
142 	{ RX_PACKET_TYPE_CHALLENGE,	"challenge" },
143 	{ RX_PACKET_TYPE_RESPONSE,	"response" },
144 	{ RX_PACKET_TYPE_DEBUG,		"debug" },
145 	{ RX_PACKET_TYPE_PARAMS,	"params" },
146 	{ RX_PACKET_TYPE_VERSION,	"version" },
147 	{ 0,				NULL },
148 };
149 
150 static const struct double_tok {
151 	uint32_t flag;		/* Rx flag */
152 	uint32_t packetType;	/* Packet type */
153 	const char *s;		/* Flag string */
154 } rx_flags[] = {
155 	{ RX_CLIENT_INITIATED,	0,			"client-init" },
156 	{ RX_REQUEST_ACK,	0,			"req-ack" },
157 	{ RX_LAST_PACKET,	0,			"last-pckt" },
158 	{ RX_MORE_PACKETS,	0,			"more-pckts" },
159 	{ RX_FREE_PACKET,	0,			"free-pckt" },
160 	{ RX_SLOW_START_OK,	RX_PACKET_TYPE_ACK,	"slow-start" },
161 	{ RX_JUMBO_PACKET,	RX_PACKET_TYPE_DATA,	"jumbogram" }
162 };
163 
164 static const struct tok fs_req[] = {
165 	{ 130,		"fetch-data" },
166 	{ 131,		"fetch-acl" },
167 	{ 132,		"fetch-status" },
168 	{ 133,		"store-data" },
169 	{ 134,		"store-acl" },
170 	{ 135,		"store-status" },
171 	{ 136,		"remove-file" },
172 	{ 137,		"create-file" },
173 	{ 138,		"rename" },
174 	{ 139,		"symlink" },
175 	{ 140,		"link" },
176 	{ 141,		"makedir" },
177 	{ 142,		"rmdir" },
178 	{ 143,		"oldsetlock" },
179 	{ 144,		"oldextlock" },
180 	{ 145,		"oldrellock" },
181 	{ 146,		"get-stats" },
182 	{ 147,		"give-cbs" },
183 	{ 148,		"get-vlinfo" },
184 	{ 149,		"get-vlstats" },
185 	{ 150,		"set-vlstats" },
186 	{ 151,		"get-rootvl" },
187 	{ 152,		"check-token" },
188 	{ 153,		"get-time" },
189 	{ 154,		"nget-vlinfo" },
190 	{ 155,		"bulk-stat" },
191 	{ 156,		"setlock" },
192 	{ 157,		"extlock" },
193 	{ 158,		"rellock" },
194 	{ 159,		"xstat-ver" },
195 	{ 160,		"get-xstat" },
196 	{ 161,		"dfs-lookup" },
197 	{ 162,		"dfs-flushcps" },
198 	{ 163,		"dfs-symlink" },
199 	{ 220,		"residency" },
200 	{ 65536,        "inline-bulk-status" },
201 	{ 65537,        "fetch-data-64" },
202 	{ 65538,        "store-data-64" },
203 	{ 65539,        "give-up-all-cbs" },
204 	{ 65540,        "get-caps" },
205 	{ 65541,        "cb-rx-conn-addr" },
206 	{ 0,		NULL },
207 };
208 
209 static const struct tok cb_req[] = {
210 	{ 204,		"callback" },
211 	{ 205,		"initcb" },
212 	{ 206,		"probe" },
213 	{ 207,		"getlock" },
214 	{ 208,		"getce" },
215 	{ 209,		"xstatver" },
216 	{ 210,		"getxstat" },
217 	{ 211,		"initcb2" },
218 	{ 212,		"whoareyou" },
219 	{ 213,		"initcb3" },
220 	{ 214,		"probeuuid" },
221 	{ 215,		"getsrvprefs" },
222 	{ 216,		"getcellservdb" },
223 	{ 217,		"getlocalcell" },
224 	{ 218,		"getcacheconf" },
225 	{ 65536,        "getce64" },
226 	{ 65537,        "getcellbynum" },
227 	{ 65538,        "tellmeaboutyourself" },
228 	{ 0,		NULL },
229 };
230 
231 static const struct tok pt_req[] = {
232 	{ 500,		"new-user" },
233 	{ 501,		"where-is-it" },
234 	{ 502,		"dump-entry" },
235 	{ 503,		"add-to-group" },
236 	{ 504,		"name-to-id" },
237 	{ 505,		"id-to-name" },
238 	{ 506,		"delete" },
239 	{ 507,		"remove-from-group" },
240 	{ 508,		"get-cps" },
241 	{ 509,		"new-entry" },
242 	{ 510,		"list-max" },
243 	{ 511,		"set-max" },
244 	{ 512,		"list-entry" },
245 	{ 513,		"change-entry" },
246 	{ 514,		"list-elements" },
247 	{ 515,		"same-mbr-of" },
248 	{ 516,		"set-fld-sentry" },
249 	{ 517,		"list-owned" },
250 	{ 518,		"get-cps2" },
251 	{ 519,		"get-host-cps" },
252 	{ 520,		"update-entry" },
253 	{ 521,		"list-entries" },
254 	{ 530,		"list-super-groups" },
255 	{ 0,		NULL },
256 };
257 
258 static const struct tok vldb_req[] = {
259 	{ 501,		"create-entry" },
260 	{ 502,		"delete-entry" },
261 	{ 503,		"get-entry-by-id" },
262 	{ 504,		"get-entry-by-name" },
263 	{ 505,		"get-new-volume-id" },
264 	{ 506,		"replace-entry" },
265 	{ 507,		"update-entry" },
266 	{ 508,		"setlock" },
267 	{ 509,		"releaselock" },
268 	{ 510,		"list-entry" },
269 	{ 511,		"list-attrib" },
270 	{ 512,		"linked-list" },
271 	{ 513,		"get-stats" },
272 	{ 514,		"probe" },
273 	{ 515,		"get-addrs" },
274 	{ 516,		"change-addr" },
275 	{ 517,		"create-entry-n" },
276 	{ 518,		"get-entry-by-id-n" },
277 	{ 519,		"get-entry-by-name-n" },
278 	{ 520,		"replace-entry-n" },
279 	{ 521,		"list-entry-n" },
280 	{ 522,		"list-attrib-n" },
281 	{ 523,		"linked-list-n" },
282 	{ 524,		"update-entry-by-name" },
283 	{ 525,		"create-entry-u" },
284 	{ 526,		"get-entry-by-id-u" },
285 	{ 527,		"get-entry-by-name-u" },
286 	{ 528,		"replace-entry-u" },
287 	{ 529,		"list-entry-u" },
288 	{ 530,		"list-attrib-u" },
289 	{ 531,		"linked-list-u" },
290 	{ 532,		"regaddr" },
291 	{ 533,		"get-addrs-u" },
292 	{ 534,		"list-attrib-n2" },
293 	{ 0,		NULL },
294 };
295 
296 static const struct tok kauth_req[] = {
297 	{ 1,		"auth-old" },
298 	{ 21,		"authenticate" },
299 	{ 22,		"authenticate-v2" },
300 	{ 2,		"change-pw" },
301 	{ 3,		"get-ticket-old" },
302 	{ 23,		"get-ticket" },
303 	{ 4,		"set-pw" },
304 	{ 5,		"set-fields" },
305 	{ 6,		"create-user" },
306 	{ 7,		"delete-user" },
307 	{ 8,		"get-entry" },
308 	{ 9,		"list-entry" },
309 	{ 10,		"get-stats" },
310 	{ 11,		"debug" },
311 	{ 12,		"get-pw" },
312 	{ 13,		"get-random-key" },
313 	{ 14,		"unlock" },
314 	{ 15,		"lock-status" },
315 	{ 0,		NULL },
316 };
317 
318 static const struct tok vol_req[] = {
319 	{ 100,		"create-volume" },
320 	{ 101,		"delete-volume" },
321 	{ 102,		"restore" },
322 	{ 103,		"forward" },
323 	{ 104,		"end-trans" },
324 	{ 105,		"clone" },
325 	{ 106,		"set-flags" },
326 	{ 107,		"get-flags" },
327 	{ 108,		"trans-create" },
328 	{ 109,		"dump" },
329 	{ 110,		"get-nth-volume" },
330 	{ 111,		"set-forwarding" },
331 	{ 112,		"get-name" },
332 	{ 113,		"get-status" },
333 	{ 114,		"sig-restore" },
334 	{ 115,		"list-partitions" },
335 	{ 116,		"list-volumes" },
336 	{ 117,		"set-id-types" },
337 	{ 118,		"monitor" },
338 	{ 119,		"partition-info" },
339 	{ 120,		"reclone" },
340 	{ 121,		"list-one-volume" },
341 	{ 122,		"nuke" },
342 	{ 123,		"set-date" },
343 	{ 124,		"x-list-volumes" },
344 	{ 125,		"x-list-one-volume" },
345 	{ 126,		"set-info" },
346 	{ 127,		"x-list-partitions" },
347 	{ 128,		"forward-multiple" },
348 	{ 65536,	"convert-ro" },
349 	{ 65537,	"get-size" },
350 	{ 65538,	"dump-v2" },
351 	{ 0,		NULL },
352 };
353 
354 static const struct tok bos_req[] = {
355 	{ 80,		"create-bnode" },
356 	{ 81,		"delete-bnode" },
357 	{ 82,		"set-status" },
358 	{ 83,		"get-status" },
359 	{ 84,		"enumerate-instance" },
360 	{ 85,		"get-instance-info" },
361 	{ 86,		"get-instance-parm" },
362 	{ 87,		"add-superuser" },
363 	{ 88,		"delete-superuser" },
364 	{ 89,		"list-superusers" },
365 	{ 90,		"list-keys" },
366 	{ 91,		"add-key" },
367 	{ 92,		"delete-key" },
368 	{ 93,		"set-cell-name" },
369 	{ 94,		"get-cell-name" },
370 	{ 95,		"get-cell-host" },
371 	{ 96,		"add-cell-host" },
372 	{ 97,		"delete-cell-host" },
373 	{ 98,		"set-t-status" },
374 	{ 99,		"shutdown-all" },
375 	{ 100,		"restart-all" },
376 	{ 101,		"startup-all" },
377 	{ 102,		"set-noauth-flag" },
378 	{ 103,		"re-bozo" },
379 	{ 104,		"restart" },
380 	{ 105,		"start-bozo-install" },
381 	{ 106,		"uninstall" },
382 	{ 107,		"get-dates" },
383 	{ 108,		"exec" },
384 	{ 109,		"prune" },
385 	{ 110,		"set-restart-time" },
386 	{ 111,		"get-restart-time" },
387 	{ 112,		"start-bozo-log" },
388 	{ 113,		"wait-all" },
389 	{ 114,		"get-instance-strings" },
390 	{ 115,		"get-restricted" },
391 	{ 116,		"set-restricted" },
392 	{ 0,		NULL },
393 };
394 
395 static const struct tok ubik_req[] = {
396 	{ 10000,	"vote-beacon" },
397 	{ 10001,	"vote-debug-old" },
398 	{ 10002,	"vote-sdebug-old" },
399 	{ 10003,	"vote-getsyncsite" },
400 	{ 10004,	"vote-debug" },
401 	{ 10005,	"vote-sdebug" },
402 	{ 10006,	"vote-xdebug" },
403 	{ 10007,	"vote-xsdebug" },
404 	{ 20000,	"disk-begin" },
405 	{ 20001,	"disk-commit" },
406 	{ 20002,	"disk-lock" },
407 	{ 20003,	"disk-write" },
408 	{ 20004,	"disk-getversion" },
409 	{ 20005,	"disk-getfile" },
410 	{ 20006,	"disk-sendfile" },
411 	{ 20007,	"disk-abort" },
412 	{ 20008,	"disk-releaselocks" },
413 	{ 20009,	"disk-truncate" },
414 	{ 20010,	"disk-probe" },
415 	{ 20011,	"disk-writev" },
416 	{ 20012,	"disk-interfaceaddr" },
417 	{ 20013,	"disk-setversion" },
418 	{ 0,		NULL },
419 };
420 
421 #define VOTE_LOW	10000
422 #define VOTE_HIGH	10007
423 #define DISK_LOW	20000
424 #define DISK_HIGH	20013
425 
426 static const struct tok cb_types[] = {
427 	{ 1,		"exclusive" },
428 	{ 2,		"shared" },
429 	{ 3,		"dropped" },
430 	{ 0,		NULL },
431 };
432 
433 static const struct tok ubik_lock_types[] = {
434 	{ 1,		"read" },
435 	{ 2,		"write" },
436 	{ 3,		"wait" },
437 	{ 0,		NULL },
438 };
439 
440 static const char *voltype[] = { "read-write", "read-only", "backup" };
441 
442 static const struct tok afs_fs_errors[] = {
443 	{ 101,		"salvage volume" },
444 	{ 102, 		"no such vnode" },
445 	{ 103, 		"no such volume" },
446 	{ 104, 		"volume exist" },
447 	{ 105, 		"no service" },
448 	{ 106, 		"volume offline" },
449 	{ 107, 		"voline online" },
450 	{ 108, 		"diskfull" },
451 	{ 109, 		"diskquota exceeded" },
452 	{ 110, 		"volume busy" },
453 	{ 111, 		"volume moved" },
454 	{ 112, 		"AFS IO error" },
455 	{ 0xffffff9c,	"restarting fileserver" }, /* -100, sic! */
456 	{ 0,		NULL }
457 };
458 
459 /*
460  * Reasons for acknowledging a packet
461  */
462 
463 static const struct tok rx_ack_reasons[] = {
464 	{ 1,		"ack requested" },
465 	{ 2,		"duplicate packet" },
466 	{ 3,		"out of sequence" },
467 	{ 4,		"exceeds window" },
468 	{ 5,		"no buffer space" },
469 	{ 6,		"ping" },
470 	{ 7,		"ping response" },
471 	{ 8,		"delay" },
472 	{ 9,		"idle" },
473 	{ 0,		NULL },
474 };
475 
476 /*
477  * Cache entries we keep around so we can figure out the RX opcode
478  * numbers for replies.  This allows us to make sense of RX reply packets.
479  */
480 
481 struct rx_cache_entry {
482 	uint32_t	callnum;	/* Call number (net order) */
483 	uint32_t	client;		/* client IP address (net order) */
484 	uint32_t	server;		/* server IP address (net order) */
485 	uint16_t	dport;		/* server UDP port (host order) */
486 	uint16_t	serviceId;	/* Service identifier (net order) */
487 	uint32_t	opcode;		/* RX opcode (host order) */
488 };
489 
490 #define RX_CACHE_SIZE	64
491 
492 static struct rx_cache_entry	rx_cache[RX_CACHE_SIZE];
493 
494 static uint32_t	rx_cache_next = 0;
495 static uint32_t	rx_cache_hint = 0;
496 static void	rx_cache_insert(netdissect_options *, const u_char *, const struct ip *, uint16_t);
497 static int	rx_cache_find(netdissect_options *, const struct rx_header *,
498 			      const struct ip *, uint16_t, uint32_t *);
499 
500 static void fs_print(netdissect_options *, const u_char *, u_int);
501 static void fs_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
502 static void acl_print(netdissect_options *, u_char *, u_char *);
503 static void cb_print(netdissect_options *, const u_char *, u_int);
504 static void cb_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
505 static void prot_print(netdissect_options *, const u_char *, u_int);
506 static void prot_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
507 static void vldb_print(netdissect_options *, const u_char *, u_int);
508 static void vldb_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
509 static void kauth_print(netdissect_options *, const u_char *, u_int);
510 static void kauth_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
511 static void vol_print(netdissect_options *, const u_char *, u_int);
512 static void vol_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
513 static void bos_print(netdissect_options *, const u_char *, u_int);
514 static void bos_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
515 static void ubik_print(netdissect_options *, const u_char *);
516 static void ubik_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
517 
518 static void rx_ack_print(netdissect_options *, const u_char *, u_int);
519 
520 static int is_ubik(uint32_t);
521 
522 /*
523  * Handle the rx-level packet.  See if we know what port it's going to so
524  * we can peek at the afs call inside
525  */
526 
527 void
528 rx_print(netdissect_options *ndo,
529          const u_char *bp, u_int length, uint16_t sport, uint16_t dport,
530          const u_char *bp2)
531 {
532 	const struct rx_header *rxh;
533 	uint32_t i;
534 	uint8_t type, flags;
535 	uint32_t opcode;
536 
537 	ndo->ndo_protocol = "rx";
538 	if (!ND_TTEST_LEN(bp, sizeof(struct rx_header))) {
539 		ND_PRINT(" [|rx] (%u)", length);
540 		return;
541 	}
542 
543 	rxh = (const struct rx_header *) bp;
544 
545 	type = GET_U_1(rxh->type);
546 	ND_PRINT(" rx %s", tok2str(rx_types, "type %u", type));
547 
548 	flags = GET_U_1(rxh->flags);
549 	if (ndo->ndo_vflag) {
550 		int firstflag = 0;
551 
552 		if (ndo->ndo_vflag > 1)
553 			ND_PRINT(" cid %08x call# %u",
554 			       GET_BE_U_4(rxh->cid),
555 			       GET_BE_U_4(rxh->callNumber));
556 
557 		ND_PRINT(" seq %u ser %u",
558 		       GET_BE_U_4(rxh->seq),
559 		       GET_BE_U_4(rxh->serial));
560 
561 		if (ndo->ndo_vflag > 2)
562 			ND_PRINT(" secindex %u serviceid %hu",
563 				GET_U_1(rxh->securityIndex),
564 				GET_BE_U_2(rxh->serviceId));
565 
566 		if (ndo->ndo_vflag > 1)
567 			for (i = 0; i < NUM_RX_FLAGS; i++) {
568 				if (flags & rx_flags[i].flag &&
569 				    (!rx_flags[i].packetType ||
570 				     type == rx_flags[i].packetType)) {
571 					if (!firstflag) {
572 						firstflag = 1;
573 						ND_PRINT(" ");
574 					} else {
575 						ND_PRINT(",");
576 					}
577 					ND_PRINT("<%s>", rx_flags[i].s);
578 				}
579 			}
580 	}
581 
582 	/*
583 	 * Try to handle AFS calls that we know about.  Check the destination
584 	 * port and make sure it's a data packet.  Also, make sure the
585 	 * seq number is 1 (because otherwise it's a continuation packet,
586 	 * and we can't interpret that).  Also, seems that reply packets
587 	 * do not have the client-init flag set, so we check for that
588 	 * as well.
589 	 */
590 
591 	if (type == RX_PACKET_TYPE_DATA &&
592 	    GET_BE_U_4(rxh->seq) == 1 &&
593 	    flags & RX_CLIENT_INITIATED) {
594 
595 		/*
596 		 * Insert this call into the call cache table, so we
597 		 * have a chance to print out replies
598 		 */
599 
600 		rx_cache_insert(ndo, bp, (const struct ip *) bp2, dport);
601 
602 		switch (dport) {
603 			case FS_RX_PORT:	/* AFS file service */
604 				fs_print(ndo, bp, length);
605 				break;
606 			case CB_RX_PORT:	/* AFS callback service */
607 				cb_print(ndo, bp, length);
608 				break;
609 			case PROT_RX_PORT:	/* AFS protection service */
610 				prot_print(ndo, bp, length);
611 				break;
612 			case VLDB_RX_PORT:	/* AFS VLDB service */
613 				vldb_print(ndo, bp, length);
614 				break;
615 			case KAUTH_RX_PORT:	/* AFS Kerberos auth service */
616 				kauth_print(ndo, bp, length);
617 				break;
618 			case VOL_RX_PORT:	/* AFS Volume service */
619 				vol_print(ndo, bp, length);
620 				break;
621 			case BOS_RX_PORT:	/* AFS BOS service */
622 				bos_print(ndo, bp, length);
623 				break;
624 			default:
625 				;
626 		}
627 
628 	/*
629 	 * If it's a reply (client-init is _not_ set, but seq is one)
630 	 * then look it up in the cache.  If we find it, call the reply
631 	 * printing functions  Note that we handle abort packets here,
632 	 * because printing out the return code can be useful at times.
633 	 */
634 
635 	} else if (((type == RX_PACKET_TYPE_DATA &&
636 					GET_BE_U_4(rxh->seq) == 1) ||
637 		    type == RX_PACKET_TYPE_ABORT) &&
638 		   (flags & RX_CLIENT_INITIATED) == 0 &&
639 		   rx_cache_find(ndo, rxh, (const struct ip *) bp2,
640 				 sport, &opcode)) {
641 
642 		switch (sport) {
643 			case FS_RX_PORT:	/* AFS file service */
644 				fs_reply_print(ndo, bp, length, opcode);
645 				break;
646 			case CB_RX_PORT:	/* AFS callback service */
647 				cb_reply_print(ndo, bp, length, opcode);
648 				break;
649 			case PROT_RX_PORT:	/* AFS PT service */
650 				prot_reply_print(ndo, bp, length, opcode);
651 				break;
652 			case VLDB_RX_PORT:	/* AFS VLDB service */
653 				vldb_reply_print(ndo, bp, length, opcode);
654 				break;
655 			case KAUTH_RX_PORT:	/* AFS Kerberos auth service */
656 				kauth_reply_print(ndo, bp, length, opcode);
657 				break;
658 			case VOL_RX_PORT:	/* AFS Volume service */
659 				vol_reply_print(ndo, bp, length, opcode);
660 				break;
661 			case BOS_RX_PORT:	/* AFS BOS service */
662 				bos_reply_print(ndo, bp, length, opcode);
663 				break;
664 			default:
665 				;
666 		}
667 
668 	/*
669 	 * If it's an RX ack packet, then use the appropriate ack decoding
670 	 * function (there isn't any service-specific information in the
671 	 * ack packet, so we can use one for all AFS services)
672 	 */
673 
674 	} else if (type == RX_PACKET_TYPE_ACK)
675 		rx_ack_print(ndo, bp, length);
676 
677 
678 	ND_PRINT(" (%u)", length);
679 }
680 
681 /*
682  * Insert an entry into the cache.  Taken from print-nfs.c
683  */
684 
685 static void
686 rx_cache_insert(netdissect_options *ndo,
687                 const u_char *bp, const struct ip *ip, uint16_t dport)
688 {
689 	struct rx_cache_entry *rxent;
690 	const struct rx_header *rxh = (const struct rx_header *) bp;
691 
692 	if (!ND_TTEST_4(bp + sizeof(struct rx_header)))
693 		return;
694 
695 	rxent = &rx_cache[rx_cache_next];
696 
697 	if (++rx_cache_next >= RX_CACHE_SIZE)
698 		rx_cache_next = 0;
699 
700 	rxent->callnum = GET_BE_U_4(rxh->callNumber);
701 	rxent->client = GET_IPV4_TO_NETWORK_ORDER(ip->ip_src);
702 	rxent->server = GET_IPV4_TO_NETWORK_ORDER(ip->ip_dst);
703 	rxent->dport = dport;
704 	rxent->serviceId = GET_BE_U_2(rxh->serviceId);
705 	rxent->opcode = GET_BE_U_4(bp + sizeof(struct rx_header));
706 }
707 
708 /*
709  * Lookup an entry in the cache.  Also taken from print-nfs.c
710  *
711  * Note that because this is a reply, we're looking at the _source_
712  * port.
713  */
714 
715 static int
716 rx_cache_find(netdissect_options *ndo, const struct rx_header *rxh,
717 	      const struct ip *ip, uint16_t sport, uint32_t *opcode)
718 {
719 	uint32_t i;
720 	struct rx_cache_entry *rxent;
721 	uint32_t clip;
722 	uint32_t sip;
723 
724 	clip = GET_IPV4_TO_NETWORK_ORDER(ip->ip_dst);
725 	sip = GET_IPV4_TO_NETWORK_ORDER(ip->ip_src);
726 
727 	/* Start the search where we last left off */
728 
729 	i = rx_cache_hint;
730 	do {
731 		rxent = &rx_cache[i];
732 		if (rxent->callnum == GET_BE_U_4(rxh->callNumber) &&
733 		    rxent->client == clip &&
734 		    rxent->server == sip &&
735 		    rxent->serviceId == GET_BE_U_2(rxh->serviceId) &&
736 		    rxent->dport == sport) {
737 
738 			/* We got a match! */
739 
740 			rx_cache_hint = i;
741 			*opcode = rxent->opcode;
742 			return(1);
743 		}
744 		if (++i >= RX_CACHE_SIZE)
745 			i = 0;
746 	} while (i != rx_cache_hint);
747 
748 	/* Our search failed */
749 	return(0);
750 }
751 
752 /*
753  * These extremely grody macros handle the printing of various AFS stuff.
754  */
755 
756 #define FIDOUT() { uint32_t n1, n2, n3; \
757 			ND_TCHECK_LEN(bp, sizeof(uint32_t) * 3); \
758 			n1 = GET_BE_U_4(bp); \
759 			bp += sizeof(uint32_t); \
760 			n2 = GET_BE_U_4(bp); \
761 			bp += sizeof(uint32_t); \
762 			n3 = GET_BE_U_4(bp); \
763 			bp += sizeof(uint32_t); \
764 			ND_PRINT(" fid %u/%u/%u", n1, n2, n3); \
765 		}
766 
767 #define STROUT(MAX) { uint32_t _i; \
768 			_i = GET_BE_U_4(bp); \
769 			if (_i > (MAX)) \
770 				goto trunc; \
771 			bp += sizeof(uint32_t); \
772 			ND_PRINT(" \""); \
773 			if (nd_printn(ndo, bp, _i, ndo->ndo_snapend)) \
774 				goto trunc; \
775 			ND_PRINT("\""); \
776 			bp += ((_i + sizeof(uint32_t) - 1) / sizeof(uint32_t)) * sizeof(uint32_t); \
777 		}
778 
779 #define INTOUT() { int32_t _i; \
780 			_i = GET_BE_S_4(bp); \
781 			bp += sizeof(int32_t); \
782 			ND_PRINT(" %d", _i); \
783 		}
784 
785 #define UINTOUT() { uint32_t _i; \
786 			_i = GET_BE_U_4(bp); \
787 			bp += sizeof(uint32_t); \
788 			ND_PRINT(" %u", _i); \
789 		}
790 
791 #define UINT64OUT() { uint64_t _i; \
792 			_i = GET_BE_U_8(bp); \
793 			bp += sizeof(uint64_t); \
794 			ND_PRINT(" %" PRIu64, _i); \
795 		}
796 
797 #define DATEOUT() { time_t _t; struct tm *tm; char str[256]; \
798 			_t = (time_t) GET_BE_S_4(bp); \
799 			bp += sizeof(int32_t); \
800 			tm = localtime(&_t); \
801 			strftime(str, 256, "%Y/%m/%d %H:%M:%S", tm); \
802 			ND_PRINT(" %s", str); \
803 		}
804 
805 #define STOREATTROUT() { uint32_t mask, _i; \
806 			ND_TCHECK_LEN(bp, (sizeof(uint32_t) * 6)); \
807 			mask = GET_BE_U_4(bp); bp += sizeof(uint32_t); \
808 			if (mask) ND_PRINT(" StoreStatus"); \
809 		        if (mask & 1) { ND_PRINT(" date"); DATEOUT(); } \
810 			else bp += sizeof(uint32_t); \
811 			_i = GET_BE_U_4(bp); bp += sizeof(uint32_t); \
812 		        if (mask & 2) ND_PRINT(" owner %u", _i);  \
813 			_i = GET_BE_U_4(bp); bp += sizeof(uint32_t); \
814 		        if (mask & 4) ND_PRINT(" group %u", _i); \
815 			_i = GET_BE_U_4(bp); bp += sizeof(uint32_t); \
816 		        if (mask & 8) ND_PRINT(" mode %o", _i & 07777); \
817 			_i = GET_BE_U_4(bp); bp += sizeof(uint32_t); \
818 		        if (mask & 16) ND_PRINT(" segsize %u", _i); \
819 			/* undocumented in 3.3 docu */ \
820 		        if (mask & 1024) ND_PRINT(" fsync");  \
821 		}
822 
823 #define UBIK_VERSIONOUT() {uint32_t epoch; uint32_t counter; \
824 			ND_TCHECK_LEN(bp, sizeof(uint32_t) * 2); \
825 			epoch = GET_BE_U_4(bp); \
826 			bp += sizeof(uint32_t); \
827 			counter = GET_BE_U_4(bp); \
828 			bp += sizeof(uint32_t); \
829 			ND_PRINT(" %u.%u", epoch, counter); \
830 		}
831 
832 #define AFSUUIDOUT() {uint32_t temp; int _i; \
833 			ND_TCHECK_LEN(bp, 11 * sizeof(uint32_t)); \
834 			temp = GET_BE_U_4(bp); \
835 			bp += sizeof(uint32_t); \
836 			ND_PRINT(" %08x", temp); \
837 			temp = GET_BE_U_4(bp); \
838 			bp += sizeof(uint32_t); \
839 			ND_PRINT("%04x", temp); \
840 			temp = GET_BE_U_4(bp); \
841 			bp += sizeof(uint32_t); \
842 			ND_PRINT("%04x", temp); \
843 			for (_i = 0; _i < 8; _i++) { \
844 				temp = GET_BE_U_4(bp); \
845 				bp += sizeof(uint32_t); \
846 				ND_PRINT("%02x", (unsigned char) temp); \
847 			} \
848 		}
849 
850 /*
851  * This is the sickest one of all
852  * MAX is expected to be a constant here
853  */
854 
855 #define VECOUT(MAX) { u_char *sp; \
856 			u_char s[(MAX) + 1]; \
857 			uint32_t k; \
858 			ND_TCHECK_LEN(bp, (MAX) * sizeof(uint32_t)); \
859 			sp = s; \
860 			for (k = 0; k < (MAX); k++) { \
861 				*sp++ = (u_char) GET_BE_U_4(bp); \
862 				bp += sizeof(uint32_t); \
863 			} \
864 			s[(MAX)] = '\0'; \
865 			ND_PRINT(" \""); \
866 			fn_print_str(ndo, s); \
867 			ND_PRINT("\""); \
868 		}
869 
870 #define DESTSERVEROUT() { uint32_t n1, n2, n3; \
871 			ND_TCHECK_LEN(bp, sizeof(uint32_t) * 3); \
872 			n1 = GET_BE_U_4(bp); \
873 			bp += sizeof(uint32_t); \
874 			n2 = GET_BE_U_4(bp); \
875 			bp += sizeof(uint32_t); \
876 			n3 = GET_BE_U_4(bp); \
877 			bp += sizeof(uint32_t); \
878 			ND_PRINT(" server %u:%u:%u", n1, n2, n3); \
879 		}
880 
881 /*
882  * Handle calls to the AFS file service (fs)
883  */
884 
885 static void
886 fs_print(netdissect_options *ndo,
887          const u_char *bp, u_int length)
888 {
889 	uint32_t fs_op;
890 	uint32_t i;
891 
892 	if (length <= sizeof(struct rx_header))
893 		return;
894 
895 	/*
896 	 * Print out the afs call we're invoking.  The table used here was
897 	 * gleaned from fsint/afsint.xg
898 	 */
899 
900 	fs_op = GET_BE_U_4(bp + sizeof(struct rx_header));
901 
902 	ND_PRINT(" fs call %s", tok2str(fs_req, "op#%u", fs_op));
903 
904 	/*
905 	 * Print out arguments to some of the AFS calls.  This stuff is
906 	 * all from afsint.xg
907 	 */
908 
909 	bp += sizeof(struct rx_header) + 4;
910 
911 	/*
912 	 * Sigh.  This is gross.  Ritchie forgive me.
913 	 */
914 
915 	switch (fs_op) {
916 		case 130:	/* Fetch data */
917 			FIDOUT();
918 			ND_PRINT(" offset");
919 			UINTOUT();
920 			ND_PRINT(" length");
921 			UINTOUT();
922 			break;
923 		case 131:	/* Fetch ACL */
924 		case 132:	/* Fetch Status */
925 		case 143:	/* Old set lock */
926 		case 144:	/* Old extend lock */
927 		case 145:	/* Old release lock */
928 		case 156:	/* Set lock */
929 		case 157:	/* Extend lock */
930 		case 158:	/* Release lock */
931 			FIDOUT();
932 			break;
933 		case 135:	/* Store status */
934 			FIDOUT();
935 			STOREATTROUT();
936 			break;
937 		case 133:	/* Store data */
938 			FIDOUT();
939 			STOREATTROUT();
940 			ND_PRINT(" offset");
941 			UINTOUT();
942 			ND_PRINT(" length");
943 			UINTOUT();
944 			ND_PRINT(" flen");
945 			UINTOUT();
946 			break;
947 		case 134:	/* Store ACL */
948 		{
949 			char a[AFSOPAQUEMAX+1];
950 			FIDOUT();
951 			i = GET_BE_U_4(bp);
952 			bp += sizeof(uint32_t);
953 			ND_TCHECK_LEN(bp, i);
954 			i = ND_MIN(AFSOPAQUEMAX, i);
955 			strncpy(a, (const char *) bp, i);
956 			a[i] = '\0';
957 			acl_print(ndo, (u_char *) a, (u_char *) a + i);
958 			break;
959 		}
960 		case 137:	/* Create file */
961 		case 141:	/* MakeDir */
962 			FIDOUT();
963 			STROUT(AFSNAMEMAX);
964 			STOREATTROUT();
965 			break;
966 		case 136:	/* Remove file */
967 		case 142:	/* Remove directory */
968 			FIDOUT();
969 			STROUT(AFSNAMEMAX);
970 			break;
971 		case 138:	/* Rename file */
972 			ND_PRINT(" old");
973 			FIDOUT();
974 			STROUT(AFSNAMEMAX);
975 			ND_PRINT(" new");
976 			FIDOUT();
977 			STROUT(AFSNAMEMAX);
978 			break;
979 		case 139:	/* Symlink */
980 			FIDOUT();
981 			STROUT(AFSNAMEMAX);
982 			ND_PRINT(" link to");
983 			STROUT(AFSNAMEMAX);
984 			break;
985 		case 140:	/* Link */
986 			FIDOUT();
987 			STROUT(AFSNAMEMAX);
988 			ND_PRINT(" link to");
989 			FIDOUT();
990 			break;
991 		case 148:	/* Get volume info */
992 			STROUT(AFSNAMEMAX);
993 			break;
994 		case 149:	/* Get volume stats */
995 		case 150:	/* Set volume stats */
996 			ND_PRINT(" volid");
997 			UINTOUT();
998 			break;
999 		case 154:	/* New get volume info */
1000 			ND_PRINT(" volname");
1001 			STROUT(AFSNAMEMAX);
1002 			break;
1003 		case 155:	/* Bulk stat */
1004 		case 65536:     /* Inline bulk stat */
1005 		{
1006 			uint32_t j;
1007 			j = GET_BE_U_4(bp);
1008 			bp += sizeof(uint32_t);
1009 
1010 			for (i = 0; i < j; i++) {
1011 				FIDOUT();
1012 				if (i != j - 1)
1013 					ND_PRINT(",");
1014 			}
1015 			if (j == 0)
1016 				ND_PRINT(" <none!>");
1017 			break;
1018 		}
1019 		case 65537:	/* Fetch data 64 */
1020 			FIDOUT();
1021 			ND_PRINT(" offset");
1022 			UINT64OUT();
1023 			ND_PRINT(" length");
1024 			UINT64OUT();
1025 			break;
1026 		case 65538:	/* Store data 64 */
1027 			FIDOUT();
1028 			STOREATTROUT();
1029 			ND_PRINT(" offset");
1030 			UINT64OUT();
1031 			ND_PRINT(" length");
1032 			UINT64OUT();
1033 			ND_PRINT(" flen");
1034 			UINT64OUT();
1035 			break;
1036 		case 65541:    /* CallBack rx conn address */
1037 			ND_PRINT(" addr");
1038 			UINTOUT();
1039 		default:
1040 			;
1041 	}
1042 
1043 	return;
1044 
1045 trunc:
1046 	ND_PRINT(" [|fs]");
1047 }
1048 
1049 /*
1050  * Handle replies to the AFS file service
1051  */
1052 
1053 static void
1054 fs_reply_print(netdissect_options *ndo,
1055                const u_char *bp, u_int length, uint32_t opcode)
1056 {
1057 	uint32_t i;
1058 	const struct rx_header *rxh;
1059 	uint8_t type;
1060 
1061 	if (length <= sizeof(struct rx_header))
1062 		return;
1063 
1064 	rxh = (const struct rx_header *) bp;
1065 
1066 	/*
1067 	 * Print out the afs call we're invoking.  The table used here was
1068 	 * gleaned from fsint/afsint.xg
1069 	 */
1070 
1071 	ND_PRINT(" fs reply %s", tok2str(fs_req, "op#%u", opcode));
1072 
1073 	type = GET_U_1(rxh->type);
1074 	bp += sizeof(struct rx_header);
1075 
1076 	/*
1077 	 * If it was a data packet, interpret the response
1078 	 */
1079 
1080 	if (type == RX_PACKET_TYPE_DATA) {
1081 		switch (opcode) {
1082 		case 131:	/* Fetch ACL */
1083 		{
1084 			char a[AFSOPAQUEMAX+1];
1085 			i = GET_BE_U_4(bp);
1086 			bp += sizeof(uint32_t);
1087 			ND_TCHECK_LEN(bp, i);
1088 			i = ND_MIN(AFSOPAQUEMAX, i);
1089 			strncpy(a, (const char *) bp, i);
1090 			a[i] = '\0';
1091 			acl_print(ndo, (u_char *) a, (u_char *) a + i);
1092 			break;
1093 		}
1094 		case 137:	/* Create file */
1095 		case 141:	/* MakeDir */
1096 			ND_PRINT(" new");
1097 			FIDOUT();
1098 			break;
1099 		case 151:	/* Get root volume */
1100 			ND_PRINT(" root volume");
1101 			STROUT(AFSNAMEMAX);
1102 			break;
1103 		case 153:	/* Get time */
1104 			DATEOUT();
1105 			break;
1106 		default:
1107 			;
1108 		}
1109 	} else if (type == RX_PACKET_TYPE_ABORT) {
1110 		/*
1111 		 * Otherwise, just print out the return code
1112 		 */
1113 		int32_t errcode;
1114 
1115 		errcode = GET_BE_S_4(bp);
1116 		bp += sizeof(int32_t);
1117 
1118 		ND_PRINT(" error %s", tok2str(afs_fs_errors, "#%d", errcode));
1119 	} else {
1120 		ND_PRINT(" strange fs reply of type %u", type);
1121 	}
1122 
1123 	return;
1124 
1125 trunc:
1126 	ND_PRINT(" [|fs]");
1127 }
1128 
1129 /*
1130  * Print out an AFS ACL string.  An AFS ACL is a string that has the
1131  * following format:
1132  *
1133  * <positive> <negative>
1134  * <uid1> <aclbits1>
1135  * ....
1136  *
1137  * "positive" and "negative" are integers which contain the number of
1138  * positive and negative ACL's in the string.  The uid/aclbits pair are
1139  * ASCII strings containing the UID/PTS record and an ASCII number
1140  * representing a logical OR of all the ACL permission bits
1141  */
1142 
1143 #define NUMSTRINGIFY(x)	XSTRINGIFY(x)
1144 
1145 static void
1146 acl_print(netdissect_options *ndo,
1147           u_char *s, u_char *end)
1148 {
1149 	int pos, neg, acl;
1150 	int n, i;
1151 	char user[USERNAMEMAX+1];
1152 
1153 	if (sscanf((char *) s, "%d %d\n%n", &pos, &neg, &n) != 2)
1154 		return;
1155 
1156 	s += n;
1157 
1158 	if (s > end)
1159 		return;
1160 
1161 	/*
1162 	 * This wacky order preserves the order used by the "fs" command
1163 	 */
1164 
1165 #define ACLOUT(acl) \
1166 	ND_PRINT("%s%s%s%s%s%s%s", \
1167 	          acl & PRSFS_READ       ? "r" : "", \
1168 	          acl & PRSFS_LOOKUP     ? "l" : "", \
1169 	          acl & PRSFS_INSERT     ? "i" : "", \
1170 	          acl & PRSFS_DELETE     ? "d" : "", \
1171 	          acl & PRSFS_WRITE      ? "w" : "", \
1172 	          acl & PRSFS_LOCK       ? "k" : "", \
1173 	          acl & PRSFS_ADMINISTER ? "a" : "");
1174 
1175 	for (i = 0; i < pos; i++) {
1176 		if (sscanf((char *) s, "%" NUMSTRINGIFY(USERNAMEMAX) "s %d\n%n", user, &acl, &n) != 2)
1177 			return;
1178 		s += n;
1179 		ND_PRINT(" +{");
1180 		fn_print_str(ndo, (u_char *)user);
1181 		ND_PRINT(" ");
1182 		ACLOUT(acl);
1183 		ND_PRINT("}");
1184 		if (s > end)
1185 			return;
1186 	}
1187 
1188 	for (i = 0; i < neg; i++) {
1189 		if (sscanf((char *) s, "%" NUMSTRINGIFY(USERNAMEMAX) "s %d\n%n", user, &acl, &n) != 2)
1190 			return;
1191 		s += n;
1192 		ND_PRINT(" -{");
1193 		fn_print_str(ndo, (u_char *)user);
1194 		ND_PRINT(" ");
1195 		ACLOUT(acl);
1196 		ND_PRINT("}");
1197 		if (s > end)
1198 			return;
1199 	}
1200 }
1201 
1202 #undef ACLOUT
1203 
1204 /*
1205  * Handle calls to the AFS callback service
1206  */
1207 
1208 static void
1209 cb_print(netdissect_options *ndo,
1210          const u_char *bp, u_int length)
1211 {
1212 	uint32_t cb_op;
1213 	uint32_t i;
1214 
1215 	if (length <= sizeof(struct rx_header))
1216 		return;
1217 
1218 	/*
1219 	 * Print out the afs call we're invoking.  The table used here was
1220 	 * gleaned from fsint/afscbint.xg
1221 	 */
1222 
1223 	cb_op = GET_BE_U_4(bp + sizeof(struct rx_header));
1224 
1225 	ND_PRINT(" cb call %s", tok2str(cb_req, "op#%u", cb_op));
1226 
1227 	bp += sizeof(struct rx_header) + 4;
1228 
1229 	/*
1230 	 * Print out the afs call we're invoking.  The table used here was
1231 	 * gleaned from fsint/afscbint.xg
1232 	 */
1233 
1234 	switch (cb_op) {
1235 		case 204:		/* Callback */
1236 		{
1237 			uint32_t j, t;
1238 			j = GET_BE_U_4(bp);
1239 			bp += sizeof(uint32_t);
1240 
1241 			for (i = 0; i < j; i++) {
1242 				FIDOUT();
1243 				if (i != j - 1)
1244 					ND_PRINT(",");
1245 			}
1246 
1247 			if (j == 0)
1248 				ND_PRINT(" <none!>");
1249 
1250 			j = GET_BE_U_4(bp);
1251 			bp += sizeof(uint32_t);
1252 
1253 			if (j != 0)
1254 				ND_PRINT(";");
1255 
1256 			for (i = 0; i < j; i++) {
1257 				ND_PRINT(" ver");
1258 				INTOUT();
1259 				ND_PRINT(" expires");
1260 				DATEOUT();
1261 				t = GET_BE_U_4(bp);
1262 				bp += sizeof(uint32_t);
1263 				tok2str(cb_types, "type %u", t);
1264 			}
1265 			break;
1266 		}
1267 		case 214: {
1268 			ND_PRINT(" afsuuid");
1269 			AFSUUIDOUT();
1270 			break;
1271 		}
1272 		default:
1273 			;
1274 	}
1275 
1276 	return;
1277 
1278 trunc:
1279 	ND_PRINT(" [|cb]");
1280 }
1281 
1282 /*
1283  * Handle replies to the AFS Callback Service
1284  */
1285 
1286 static void
1287 cb_reply_print(netdissect_options *ndo,
1288                const u_char *bp, u_int length, uint32_t opcode)
1289 {
1290 	const struct rx_header *rxh;
1291 	uint8_t type;
1292 
1293 	if (length <= sizeof(struct rx_header))
1294 		return;
1295 
1296 	rxh = (const struct rx_header *) bp;
1297 
1298 	/*
1299 	 * Print out the afs call we're invoking.  The table used here was
1300 	 * gleaned from fsint/afscbint.xg
1301 	 */
1302 
1303 	ND_PRINT(" cb reply %s", tok2str(cb_req, "op#%u", opcode));
1304 
1305 	type = GET_U_1(rxh->type);
1306 	bp += sizeof(struct rx_header);
1307 
1308 	/*
1309 	 * If it was a data packet, interpret the response.
1310 	 */
1311 
1312 	if (type == RX_PACKET_TYPE_DATA)
1313 		switch (opcode) {
1314 		case 213:	/* InitCallBackState3 */
1315 			AFSUUIDOUT();
1316 			break;
1317 		default:
1318 		;
1319 		}
1320 	else {
1321 		/*
1322 		 * Otherwise, just print out the return code
1323 		 */
1324 		ND_PRINT(" errcode");
1325 		INTOUT();
1326 	}
1327 
1328 	return;
1329 
1330 trunc:
1331 	ND_PRINT(" [|cb]");
1332 }
1333 
1334 /*
1335  * Handle calls to the AFS protection database server
1336  */
1337 
1338 static void
1339 prot_print(netdissect_options *ndo,
1340            const u_char *bp, u_int length)
1341 {
1342 	uint32_t i;
1343 	uint32_t pt_op;
1344 
1345 	if (length <= sizeof(struct rx_header))
1346 		return;
1347 
1348 	/*
1349 	 * Print out the afs call we're invoking.  The table used here was
1350 	 * gleaned from ptserver/ptint.xg
1351 	 */
1352 
1353 	pt_op = GET_BE_U_4(bp + sizeof(struct rx_header));
1354 
1355 	ND_PRINT(" pt");
1356 
1357 	if (is_ubik(pt_op)) {
1358 		ubik_print(ndo, bp);
1359 		return;
1360 	}
1361 
1362 	ND_PRINT(" call %s", tok2str(pt_req, "op#%u", pt_op));
1363 
1364 	/*
1365 	 * Decode some of the arguments to the PT calls
1366 	 */
1367 
1368 	bp += sizeof(struct rx_header) + 4;
1369 
1370 	switch (pt_op) {
1371 		case 500:	/* I New User */
1372 			STROUT(PRNAMEMAX);
1373 			ND_PRINT(" id");
1374 			INTOUT();
1375 			ND_PRINT(" oldid");
1376 			INTOUT();
1377 			break;
1378 		case 501:	/* Where is it */
1379 		case 506:	/* Delete */
1380 		case 508:	/* Get CPS */
1381 		case 512:	/* List entry */
1382 		case 514:	/* List elements */
1383 		case 517:	/* List owned */
1384 		case 518:	/* Get CPS2 */
1385 		case 519:	/* Get host CPS */
1386 		case 530:	/* List super groups */
1387 			ND_PRINT(" id");
1388 			INTOUT();
1389 			break;
1390 		case 502:	/* Dump entry */
1391 			ND_PRINT(" pos");
1392 			INTOUT();
1393 			break;
1394 		case 503:	/* Add to group */
1395 		case 507:	/* Remove from group */
1396 		case 515:	/* Is a member of? */
1397 			ND_PRINT(" uid");
1398 			INTOUT();
1399 			ND_PRINT(" gid");
1400 			INTOUT();
1401 			break;
1402 		case 504:	/* Name to ID */
1403 		{
1404 			uint32_t j;
1405 			j = GET_BE_U_4(bp);
1406 			bp += sizeof(uint32_t);
1407 
1408 			/*
1409 			 * Who designed this chicken-shit protocol?
1410 			 *
1411 			 * Each character is stored as a 32-bit
1412 			 * integer!
1413 			 */
1414 
1415 			for (i = 0; i < j; i++) {
1416 				VECOUT(PRNAMEMAX);
1417 			}
1418 			if (j == 0)
1419 				ND_PRINT(" <none!>");
1420 		}
1421 			break;
1422 		case 505:	/* Id to name */
1423 		{
1424 			uint32_t j;
1425 			ND_PRINT(" ids:");
1426 			i = GET_BE_U_4(bp);
1427 			bp += sizeof(uint32_t);
1428 			for (j = 0; j < i; j++)
1429 				INTOUT();
1430 			if (j == 0)
1431 				ND_PRINT(" <none!>");
1432 		}
1433 			break;
1434 		case 509:	/* New entry */
1435 			STROUT(PRNAMEMAX);
1436 			ND_PRINT(" flag");
1437 			INTOUT();
1438 			ND_PRINT(" oid");
1439 			INTOUT();
1440 			break;
1441 		case 511:	/* Set max */
1442 			ND_PRINT(" id");
1443 			INTOUT();
1444 			ND_PRINT(" gflag");
1445 			INTOUT();
1446 			break;
1447 		case 513:	/* Change entry */
1448 			ND_PRINT(" id");
1449 			INTOUT();
1450 			STROUT(PRNAMEMAX);
1451 			ND_PRINT(" oldid");
1452 			INTOUT();
1453 			ND_PRINT(" newid");
1454 			INTOUT();
1455 			break;
1456 		case 520:	/* Update entry */
1457 			ND_PRINT(" id");
1458 			INTOUT();
1459 			STROUT(PRNAMEMAX);
1460 			break;
1461 		default:
1462 			;
1463 	}
1464 
1465 
1466 	return;
1467 
1468 trunc:
1469 	ND_PRINT(" [|pt]");
1470 }
1471 
1472 /*
1473  * Handle replies to the AFS protection service
1474  */
1475 
1476 static void
1477 prot_reply_print(netdissect_options *ndo,
1478                  const u_char *bp, u_int length, uint32_t opcode)
1479 {
1480 	const struct rx_header *rxh;
1481 	uint8_t type;
1482 	uint32_t i;
1483 
1484 	if (length < sizeof(struct rx_header))
1485 		return;
1486 
1487 	rxh = (const struct rx_header *) bp;
1488 
1489 	/*
1490 	 * Print out the afs call we're invoking.  The table used here was
1491 	 * gleaned from ptserver/ptint.xg.  Check to see if it's a
1492 	 * Ubik call, however.
1493 	 */
1494 
1495 	ND_PRINT(" pt");
1496 
1497 	if (is_ubik(opcode)) {
1498 		ubik_reply_print(ndo, bp, length, opcode);
1499 		return;
1500 	}
1501 
1502 	ND_PRINT(" reply %s", tok2str(pt_req, "op#%u", opcode));
1503 
1504 	type = GET_U_1(rxh->type);
1505 	bp += sizeof(struct rx_header);
1506 
1507 	/*
1508 	 * If it was a data packet, interpret the response
1509 	 */
1510 
1511 	if (type == RX_PACKET_TYPE_DATA)
1512 		switch (opcode) {
1513 		case 504:		/* Name to ID */
1514 		{
1515 			uint32_t j;
1516 			ND_PRINT(" ids:");
1517 			i = GET_BE_U_4(bp);
1518 			bp += sizeof(uint32_t);
1519 			for (j = 0; j < i; j++)
1520 				INTOUT();
1521 			if (j == 0)
1522 				ND_PRINT(" <none!>");
1523 		}
1524 			break;
1525 		case 505:		/* ID to name */
1526 		{
1527 			uint32_t j;
1528 			j = GET_BE_U_4(bp);
1529 			bp += sizeof(uint32_t);
1530 
1531 			/*
1532 			 * Who designed this chicken-shit protocol?
1533 			 *
1534 			 * Each character is stored as a 32-bit
1535 			 * integer!
1536 			 */
1537 
1538 			for (i = 0; i < j; i++) {
1539 				VECOUT(PRNAMEMAX);
1540 			}
1541 			if (j == 0)
1542 				ND_PRINT(" <none!>");
1543 		}
1544 			break;
1545 		case 508:		/* Get CPS */
1546 		case 514:		/* List elements */
1547 		case 517:		/* List owned */
1548 		case 518:		/* Get CPS2 */
1549 		case 519:		/* Get host CPS */
1550 		{
1551 			uint32_t j;
1552 			j = GET_BE_U_4(bp);
1553 			bp += sizeof(uint32_t);
1554 			for (i = 0; i < j; i++) {
1555 				INTOUT();
1556 			}
1557 			if (j == 0)
1558 				ND_PRINT(" <none!>");
1559 		}
1560 			break;
1561 		case 510:		/* List max */
1562 			ND_PRINT(" maxuid");
1563 			INTOUT();
1564 			ND_PRINT(" maxgid");
1565 			INTOUT();
1566 			break;
1567 		default:
1568 			;
1569 		}
1570 	else {
1571 		/*
1572 		 * Otherwise, just print out the return code
1573 		 */
1574 		ND_PRINT(" errcode");
1575 		INTOUT();
1576 	}
1577 
1578 	return;
1579 
1580 trunc:
1581 	ND_PRINT(" [|pt]");
1582 }
1583 
1584 /*
1585  * Handle calls to the AFS volume location database service
1586  */
1587 
1588 static void
1589 vldb_print(netdissect_options *ndo,
1590            const u_char *bp, u_int length)
1591 {
1592 	uint32_t vldb_op;
1593 	uint32_t i;
1594 
1595 	if (length <= sizeof(struct rx_header))
1596 		return;
1597 
1598 	/*
1599 	 * Print out the afs call we're invoking.  The table used here was
1600 	 * gleaned from vlserver/vldbint.xg
1601 	 */
1602 
1603 	vldb_op = GET_BE_U_4(bp + sizeof(struct rx_header));
1604 
1605 	ND_PRINT(" vldb");
1606 
1607 	if (is_ubik(vldb_op)) {
1608 		ubik_print(ndo, bp);
1609 		return;
1610 	}
1611 	ND_PRINT(" call %s", tok2str(vldb_req, "op#%u", vldb_op));
1612 
1613 	/*
1614 	 * Decode some of the arguments to the VLDB calls
1615 	 */
1616 
1617 	bp += sizeof(struct rx_header) + 4;
1618 
1619 	switch (vldb_op) {
1620 		case 501:	/* Create new volume */
1621 		case 517:	/* Create entry N */
1622 			VECOUT(VLNAMEMAX);
1623 			break;
1624 		case 502:	/* Delete entry */
1625 		case 503:	/* Get entry by ID */
1626 		case 507:	/* Update entry */
1627 		case 508:	/* Set lock */
1628 		case 509:	/* Release lock */
1629 		case 518:	/* Get entry by ID N */
1630 			ND_PRINT(" volid");
1631 			INTOUT();
1632 			i = GET_BE_U_4(bp);
1633 			bp += sizeof(uint32_t);
1634 			if (i <= 2)
1635 				ND_PRINT(" type %s", voltype[i]);
1636 			break;
1637 		case 504:	/* Get entry by name */
1638 		case 519:	/* Get entry by name N */
1639 		case 524:	/* Update entry by name */
1640 		case 527:	/* Get entry by name U */
1641 			STROUT(VLNAMEMAX);
1642 			break;
1643 		case 505:	/* Get new vol id */
1644 			ND_PRINT(" bump");
1645 			INTOUT();
1646 			break;
1647 		case 506:	/* Replace entry */
1648 		case 520:	/* Replace entry N */
1649 			ND_PRINT(" volid");
1650 			INTOUT();
1651 			i = GET_BE_U_4(bp);
1652 			bp += sizeof(uint32_t);
1653 			if (i <= 2)
1654 				ND_PRINT(" type %s", voltype[i]);
1655 			VECOUT(VLNAMEMAX);
1656 			break;
1657 		case 510:	/* List entry */
1658 		case 521:	/* List entry N */
1659 			ND_PRINT(" index");
1660 			INTOUT();
1661 			break;
1662 		default:
1663 			;
1664 	}
1665 
1666 	return;
1667 
1668 trunc:
1669 	ND_PRINT(" [|vldb]");
1670 }
1671 
1672 /*
1673  * Handle replies to the AFS volume location database service
1674  */
1675 
1676 static void
1677 vldb_reply_print(netdissect_options *ndo,
1678                  const u_char *bp, u_int length, uint32_t opcode)
1679 {
1680 	const struct rx_header *rxh;
1681 	uint8_t type;
1682 	uint32_t i;
1683 
1684 	if (length < sizeof(struct rx_header))
1685 		return;
1686 
1687 	rxh = (const struct rx_header *) bp;
1688 
1689 	/*
1690 	 * Print out the afs call we're invoking.  The table used here was
1691 	 * gleaned from vlserver/vldbint.xg.  Check to see if it's a
1692 	 * Ubik call, however.
1693 	 */
1694 
1695 	ND_PRINT(" vldb");
1696 
1697 	if (is_ubik(opcode)) {
1698 		ubik_reply_print(ndo, bp, length, opcode);
1699 		return;
1700 	}
1701 
1702 	ND_PRINT(" reply %s", tok2str(vldb_req, "op#%u", opcode));
1703 
1704 	type = GET_U_1(rxh->type);
1705 	bp += sizeof(struct rx_header);
1706 
1707 	/*
1708 	 * If it was a data packet, interpret the response
1709 	 */
1710 
1711 	if (type == RX_PACKET_TYPE_DATA)
1712 		switch (opcode) {
1713 		case 510:	/* List entry */
1714 			ND_PRINT(" count");
1715 			INTOUT();
1716 			ND_PRINT(" nextindex");
1717 			INTOUT();
1718 			ND_FALL_THROUGH;
1719 		case 503:	/* Get entry by id */
1720 		case 504:	/* Get entry by name */
1721 		{	uint32_t nservers, j;
1722 			VECOUT(VLNAMEMAX);
1723 			ND_TCHECK_4(bp);
1724 			bp += sizeof(uint32_t);
1725 			ND_PRINT(" numservers");
1726 			nservers = GET_BE_U_4(bp);
1727 			bp += sizeof(uint32_t);
1728 			ND_PRINT(" %u", nservers);
1729 			ND_PRINT(" servers");
1730 			for (i = 0; i < 8; i++) {
1731 				ND_TCHECK_4(bp);
1732 				if (i < nservers)
1733 					ND_PRINT(" %s",
1734 					   intoa(GET_IPV4_TO_NETWORK_ORDER(bp)));
1735 				bp += sizeof(nd_ipv4);
1736 			}
1737 			ND_PRINT(" partitions");
1738 			for (i = 0; i < 8; i++) {
1739 				j = GET_BE_U_4(bp);
1740 				if (i < nservers && j <= 26)
1741 					ND_PRINT(" %c", 'a' + j);
1742 				else if (i < nservers)
1743 					ND_PRINT(" %u", j);
1744 				bp += sizeof(uint32_t);
1745 			}
1746 			ND_TCHECK_LEN(bp, 8 * sizeof(uint32_t));
1747 			bp += 8 * sizeof(uint32_t);
1748 			ND_PRINT(" rwvol");
1749 			UINTOUT();
1750 			ND_PRINT(" rovol");
1751 			UINTOUT();
1752 			ND_PRINT(" backup");
1753 			UINTOUT();
1754 		}
1755 			break;
1756 		case 505:	/* Get new volume ID */
1757 			ND_PRINT(" newvol");
1758 			UINTOUT();
1759 			break;
1760 		case 521:	/* List entry */
1761 		case 529:	/* List entry U */
1762 			ND_PRINT(" count");
1763 			INTOUT();
1764 			ND_PRINT(" nextindex");
1765 			INTOUT();
1766 			ND_FALL_THROUGH;
1767 		case 518:	/* Get entry by ID N */
1768 		case 519:	/* Get entry by name N */
1769 		{	uint32_t nservers, j;
1770 			VECOUT(VLNAMEMAX);
1771 			ND_PRINT(" numservers");
1772 			nservers = GET_BE_U_4(bp);
1773 			bp += sizeof(uint32_t);
1774 			ND_PRINT(" %u", nservers);
1775 			ND_PRINT(" servers");
1776 			for (i = 0; i < 13; i++) {
1777 				ND_TCHECK_4(bp);
1778 				if (i < nservers)
1779 					ND_PRINT(" %s",
1780 					   intoa(GET_IPV4_TO_NETWORK_ORDER(bp)));
1781 				bp += sizeof(nd_ipv4);
1782 			}
1783 			ND_PRINT(" partitions");
1784 			for (i = 0; i < 13; i++) {
1785 				j = GET_BE_U_4(bp);
1786 				if (i < nservers && j <= 26)
1787 					ND_PRINT(" %c", 'a' + j);
1788 				else if (i < nservers)
1789 					ND_PRINT(" %u", j);
1790 				bp += sizeof(uint32_t);
1791 			}
1792 			ND_TCHECK_LEN(bp, 13 * sizeof(uint32_t));
1793 			bp += 13 * sizeof(uint32_t);
1794 			ND_PRINT(" rwvol");
1795 			UINTOUT();
1796 			ND_PRINT(" rovol");
1797 			UINTOUT();
1798 			ND_PRINT(" backup");
1799 			UINTOUT();
1800 		}
1801 			break;
1802 		case 526:	/* Get entry by ID U */
1803 		case 527:	/* Get entry by name U */
1804 		{	uint32_t nservers, j;
1805 			VECOUT(VLNAMEMAX);
1806 			ND_PRINT(" numservers");
1807 			nservers = GET_BE_U_4(bp);
1808 			bp += sizeof(uint32_t);
1809 			ND_PRINT(" %u", nservers);
1810 			ND_PRINT(" servers");
1811 			for (i = 0; i < 13; i++) {
1812 				if (i < nservers) {
1813 					ND_PRINT(" afsuuid");
1814 					AFSUUIDOUT();
1815 				} else {
1816 					ND_TCHECK_LEN(bp, 44);
1817 					bp += 44;
1818 				}
1819 			}
1820 			ND_TCHECK_LEN(bp, 4 * 13);
1821 			bp += 4 * 13;
1822 			ND_PRINT(" partitions");
1823 			for (i = 0; i < 13; i++) {
1824 				j = GET_BE_U_4(bp);
1825 				if (i < nservers && j <= 26)
1826 					ND_PRINT(" %c", 'a' + j);
1827 				else if (i < nservers)
1828 					ND_PRINT(" %u", j);
1829 				bp += sizeof(uint32_t);
1830 			}
1831 			ND_TCHECK_LEN(bp, 13 * sizeof(uint32_t));
1832 			bp += 13 * sizeof(uint32_t);
1833 			ND_PRINT(" rwvol");
1834 			UINTOUT();
1835 			ND_PRINT(" rovol");
1836 			UINTOUT();
1837 			ND_PRINT(" backup");
1838 			UINTOUT();
1839 		}
1840 		default:
1841 			;
1842 		}
1843 
1844 	else {
1845 		/*
1846 		 * Otherwise, just print out the return code
1847 		 */
1848 		ND_PRINT(" errcode");
1849 		INTOUT();
1850 	}
1851 
1852 	return;
1853 
1854 trunc:
1855 	ND_PRINT(" [|vldb]");
1856 }
1857 
1858 /*
1859  * Handle calls to the AFS Kerberos Authentication service
1860  */
1861 
1862 static void
1863 kauth_print(netdissect_options *ndo,
1864             const u_char *bp, u_int length)
1865 {
1866 	uint32_t kauth_op;
1867 
1868 	if (length <= sizeof(struct rx_header))
1869 		return;
1870 
1871 	/*
1872 	 * Print out the afs call we're invoking.  The table used here was
1873 	 * gleaned from kauth/kauth.rg
1874 	 */
1875 
1876 	kauth_op = GET_BE_U_4(bp + sizeof(struct rx_header));
1877 
1878 	ND_PRINT(" kauth");
1879 
1880 	if (is_ubik(kauth_op)) {
1881 		ubik_print(ndo, bp);
1882 		return;
1883 	}
1884 
1885 
1886 	ND_PRINT(" call %s", tok2str(kauth_req, "op#%u", kauth_op));
1887 
1888 	/*
1889 	 * Decode some of the arguments to the KA calls
1890 	 */
1891 
1892 	bp += sizeof(struct rx_header) + 4;
1893 
1894 	switch (kauth_op) {
1895 		case 1:		/* Authenticate old */
1896 		case 21:	/* Authenticate */
1897 		case 22:	/* Authenticate-V2 */
1898 		case 2:		/* Change PW */
1899 		case 5:		/* Set fields */
1900 		case 6:		/* Create user */
1901 		case 7:		/* Delete user */
1902 		case 8:		/* Get entry */
1903 		case 14:	/* Unlock */
1904 		case 15:	/* Lock status */
1905 			ND_PRINT(" principal");
1906 			STROUT(KANAMEMAX);
1907 			STROUT(KANAMEMAX);
1908 			break;
1909 		case 3:		/* GetTicket-old */
1910 		case 23:	/* GetTicket */
1911 		{
1912 			uint32_t i;
1913 			ND_PRINT(" kvno");
1914 			INTOUT();
1915 			ND_PRINT(" domain");
1916 			STROUT(KANAMEMAX);
1917 			i = GET_BE_U_4(bp);
1918 			bp += sizeof(uint32_t);
1919 			ND_TCHECK_LEN(bp, i);
1920 			bp += i;
1921 			ND_PRINT(" principal");
1922 			STROUT(KANAMEMAX);
1923 			STROUT(KANAMEMAX);
1924 			break;
1925 		}
1926 		case 4:		/* Set Password */
1927 			ND_PRINT(" principal");
1928 			STROUT(KANAMEMAX);
1929 			STROUT(KANAMEMAX);
1930 			ND_PRINT(" kvno");
1931 			INTOUT();
1932 			break;
1933 		case 12:	/* Get password */
1934 			ND_PRINT(" name");
1935 			STROUT(KANAMEMAX);
1936 			break;
1937 		default:
1938 			;
1939 	}
1940 
1941 	return;
1942 
1943 trunc:
1944 	ND_PRINT(" [|kauth]");
1945 }
1946 
1947 /*
1948  * Handle replies to the AFS Kerberos Authentication Service
1949  */
1950 
1951 static void
1952 kauth_reply_print(netdissect_options *ndo,
1953                   const u_char *bp, u_int length, uint32_t opcode)
1954 {
1955 	const struct rx_header *rxh;
1956 	uint8_t type;
1957 
1958 	if (length <= sizeof(struct rx_header))
1959 		return;
1960 
1961 	rxh = (const struct rx_header *) bp;
1962 
1963 	/*
1964 	 * Print out the afs call we're invoking.  The table used here was
1965 	 * gleaned from kauth/kauth.rg
1966 	 */
1967 
1968 	ND_PRINT(" kauth");
1969 
1970 	if (is_ubik(opcode)) {
1971 		ubik_reply_print(ndo, bp, length, opcode);
1972 		return;
1973 	}
1974 
1975 	ND_PRINT(" reply %s", tok2str(kauth_req, "op#%u", opcode));
1976 
1977 	type = GET_U_1(rxh->type);
1978 	bp += sizeof(struct rx_header);
1979 
1980 	/*
1981 	 * If it was a data packet, interpret the response.
1982 	 */
1983 
1984 	if (type == RX_PACKET_TYPE_DATA)
1985 		/* Well, no, not really.  Leave this for later */
1986 		;
1987 	else {
1988 		/*
1989 		 * Otherwise, just print out the return code
1990 		 */
1991 		ND_PRINT(" errcode");
1992 		INTOUT();
1993 	}
1994 }
1995 
1996 /*
1997  * Handle calls to the AFS Volume location service
1998  */
1999 
2000 static void
2001 vol_print(netdissect_options *ndo,
2002           const u_char *bp, u_int length)
2003 {
2004 	uint32_t vol_op;
2005 
2006 	if (length <= sizeof(struct rx_header))
2007 		return;
2008 
2009 	/*
2010 	 * Print out the afs call we're invoking.  The table used here was
2011 	 * gleaned from volser/volint.xg
2012 	 */
2013 
2014 	vol_op = GET_BE_U_4(bp + sizeof(struct rx_header));
2015 
2016 	ND_PRINT(" vol call %s", tok2str(vol_req, "op#%u", vol_op));
2017 
2018 	bp += sizeof(struct rx_header) + 4;
2019 
2020 	switch (vol_op) {
2021 		case 100:	/* Create volume */
2022 			ND_PRINT(" partition");
2023 			UINTOUT();
2024 			ND_PRINT(" name");
2025 			STROUT(AFSNAMEMAX);
2026 			ND_PRINT(" type");
2027 			UINTOUT();
2028 			ND_PRINT(" parent");
2029 			UINTOUT();
2030 			break;
2031 		case 101:	/* Delete volume */
2032 		case 107:	/* Get flags */
2033 			ND_PRINT(" trans");
2034 			UINTOUT();
2035 			break;
2036 		case 102:	/* Restore */
2037 			ND_PRINT(" totrans");
2038 			UINTOUT();
2039 			ND_PRINT(" flags");
2040 			UINTOUT();
2041 			break;
2042 		case 103:	/* Forward */
2043 			ND_PRINT(" fromtrans");
2044 			UINTOUT();
2045 			ND_PRINT(" fromdate");
2046 			DATEOUT();
2047 			DESTSERVEROUT();
2048 			ND_PRINT(" desttrans");
2049 			INTOUT();
2050 			break;
2051 		case 104:	/* End trans */
2052 			ND_PRINT(" trans");
2053 			UINTOUT();
2054 			break;
2055 		case 105:	/* Clone */
2056 			ND_PRINT(" trans");
2057 			UINTOUT();
2058 			ND_PRINT(" purgevol");
2059 			UINTOUT();
2060 			ND_PRINT(" newtype");
2061 			UINTOUT();
2062 			ND_PRINT(" newname");
2063 			STROUT(AFSNAMEMAX);
2064 			break;
2065 		case 106:	/* Set flags */
2066 			ND_PRINT(" trans");
2067 			UINTOUT();
2068 			ND_PRINT(" flags");
2069 			UINTOUT();
2070 			break;
2071 		case 108:	/* Trans create */
2072 			ND_PRINT(" vol");
2073 			UINTOUT();
2074 			ND_PRINT(" partition");
2075 			UINTOUT();
2076 			ND_PRINT(" flags");
2077 			UINTOUT();
2078 			break;
2079 		case 109:	/* Dump */
2080 		case 655537:	/* Get size */
2081 			ND_PRINT(" fromtrans");
2082 			UINTOUT();
2083 			ND_PRINT(" fromdate");
2084 			DATEOUT();
2085 			break;
2086 		case 110:	/* Get n-th volume */
2087 			ND_PRINT(" index");
2088 			UINTOUT();
2089 			break;
2090 		case 111:	/* Set forwarding */
2091 			ND_PRINT(" tid");
2092 			UINTOUT();
2093 			ND_PRINT(" newsite");
2094 			UINTOUT();
2095 			break;
2096 		case 112:	/* Get name */
2097 		case 113:	/* Get status */
2098 			ND_PRINT(" tid");
2099 			break;
2100 		case 114:	/* Signal restore */
2101 			ND_PRINT(" name");
2102 			STROUT(AFSNAMEMAX);
2103 			ND_PRINT(" type");
2104 			UINTOUT();
2105 			ND_PRINT(" pid");
2106 			UINTOUT();
2107 			ND_PRINT(" cloneid");
2108 			UINTOUT();
2109 			break;
2110 		case 116:	/* List volumes */
2111 			ND_PRINT(" partition");
2112 			UINTOUT();
2113 			ND_PRINT(" flags");
2114 			UINTOUT();
2115 			break;
2116 		case 117:	/* Set id types */
2117 			ND_PRINT(" tid");
2118 			UINTOUT();
2119 			ND_PRINT(" name");
2120 			STROUT(AFSNAMEMAX);
2121 			ND_PRINT(" type");
2122 			UINTOUT();
2123 			ND_PRINT(" pid");
2124 			UINTOUT();
2125 			ND_PRINT(" clone");
2126 			UINTOUT();
2127 			ND_PRINT(" backup");
2128 			UINTOUT();
2129 			break;
2130 		case 119:	/* Partition info */
2131 			ND_PRINT(" name");
2132 			STROUT(AFSNAMEMAX);
2133 			break;
2134 		case 120:	/* Reclone */
2135 			ND_PRINT(" tid");
2136 			UINTOUT();
2137 			break;
2138 		case 121:	/* List one volume */
2139 		case 122:	/* Nuke volume */
2140 		case 124:	/* Extended List volumes */
2141 		case 125:	/* Extended List one volume */
2142 		case 65536:	/* Convert RO to RW volume */
2143 			ND_PRINT(" partid");
2144 			UINTOUT();
2145 			ND_PRINT(" volid");
2146 			UINTOUT();
2147 			break;
2148 		case 123:	/* Set date */
2149 			ND_PRINT(" tid");
2150 			UINTOUT();
2151 			ND_PRINT(" date");
2152 			DATEOUT();
2153 			break;
2154 		case 126:	/* Set info */
2155 			ND_PRINT(" tid");
2156 			UINTOUT();
2157 			break;
2158 		case 128:	/* Forward multiple */
2159 			ND_PRINT(" fromtrans");
2160 			UINTOUT();
2161 			ND_PRINT(" fromdate");
2162 			DATEOUT();
2163 			{
2164 				uint32_t i, j;
2165 				j = GET_BE_U_4(bp);
2166 				bp += sizeof(uint32_t);
2167 				for (i = 0; i < j; i++) {
2168 					DESTSERVEROUT();
2169 					if (i != j - 1)
2170 						ND_PRINT(",");
2171 				}
2172 				if (j == 0)
2173 					ND_PRINT(" <none!>");
2174 			}
2175 			break;
2176 		case 65538:	/* Dump version 2 */
2177 			ND_PRINT(" fromtrans");
2178 			UINTOUT();
2179 			ND_PRINT(" fromdate");
2180 			DATEOUT();
2181 			ND_PRINT(" flags");
2182 			UINTOUT();
2183 			break;
2184 		default:
2185 			;
2186 	}
2187 	return;
2188 
2189 trunc:
2190 	ND_PRINT(" [|vol]");
2191 }
2192 
2193 /*
2194  * Handle replies to the AFS Volume Service
2195  */
2196 
2197 static void
2198 vol_reply_print(netdissect_options *ndo,
2199                 const u_char *bp, u_int length, uint32_t opcode)
2200 {
2201 	const struct rx_header *rxh;
2202 	uint8_t type;
2203 
2204 	if (length <= sizeof(struct rx_header))
2205 		return;
2206 
2207 	rxh = (const struct rx_header *) bp;
2208 
2209 	/*
2210 	 * Print out the afs call we're invoking.  The table used here was
2211 	 * gleaned from volser/volint.xg
2212 	 */
2213 
2214 	ND_PRINT(" vol reply %s", tok2str(vol_req, "op#%u", opcode));
2215 
2216 	type = GET_U_1(rxh->type);
2217 	bp += sizeof(struct rx_header);
2218 
2219 	/*
2220 	 * If it was a data packet, interpret the response.
2221 	 */
2222 
2223 	if (type == RX_PACKET_TYPE_DATA) {
2224 		switch (opcode) {
2225 			case 100:	/* Create volume */
2226 				ND_PRINT(" volid");
2227 				UINTOUT();
2228 				ND_PRINT(" trans");
2229 				UINTOUT();
2230 				break;
2231 			case 104:	/* End transaction */
2232 				UINTOUT();
2233 				break;
2234 			case 105:	/* Clone */
2235 				ND_PRINT(" newvol");
2236 				UINTOUT();
2237 				break;
2238 			case 107:	/* Get flags */
2239 				UINTOUT();
2240 				break;
2241 			case 108:	/* Transaction create */
2242 				ND_PRINT(" trans");
2243 				UINTOUT();
2244 				break;
2245 			case 110:	/* Get n-th volume */
2246 				ND_PRINT(" volume");
2247 				UINTOUT();
2248 				ND_PRINT(" partition");
2249 				UINTOUT();
2250 				break;
2251 			case 112:	/* Get name */
2252 				STROUT(AFSNAMEMAX);
2253 				break;
2254 			case 113:	/* Get status */
2255 				ND_PRINT(" volid");
2256 				UINTOUT();
2257 				ND_PRINT(" nextuniq");
2258 				UINTOUT();
2259 				ND_PRINT(" type");
2260 				UINTOUT();
2261 				ND_PRINT(" parentid");
2262 				UINTOUT();
2263 				ND_PRINT(" clone");
2264 				UINTOUT();
2265 				ND_PRINT(" backup");
2266 				UINTOUT();
2267 				ND_PRINT(" restore");
2268 				UINTOUT();
2269 				ND_PRINT(" maxquota");
2270 				UINTOUT();
2271 				ND_PRINT(" minquota");
2272 				UINTOUT();
2273 				ND_PRINT(" owner");
2274 				UINTOUT();
2275 				ND_PRINT(" create");
2276 				DATEOUT();
2277 				ND_PRINT(" access");
2278 				DATEOUT();
2279 				ND_PRINT(" update");
2280 				DATEOUT();
2281 				ND_PRINT(" expire");
2282 				DATEOUT();
2283 				ND_PRINT(" backup");
2284 				DATEOUT();
2285 				ND_PRINT(" copy");
2286 				DATEOUT();
2287 				break;
2288 			case 115:	/* Old list partitions */
2289 				break;
2290 			case 116:	/* List volumes */
2291 			case 121:	/* List one volume */
2292 				{
2293 					uint32_t i, j;
2294 					j = GET_BE_U_4(bp);
2295 					bp += sizeof(uint32_t);
2296 					for (i = 0; i < j; i++) {
2297 						ND_PRINT(" name");
2298 						VECOUT(32);
2299 						ND_PRINT(" volid");
2300 						UINTOUT();
2301 						ND_PRINT(" type");
2302 						bp += sizeof(uint32_t) * 21;
2303 						if (i != j - 1)
2304 							ND_PRINT(",");
2305 					}
2306 					if (j == 0)
2307 						ND_PRINT(" <none!>");
2308 				}
2309 				break;
2310 
2311 
2312 			default:
2313 				;
2314 		}
2315 	} else {
2316 		/*
2317 		 * Otherwise, just print out the return code
2318 		 */
2319 		ND_PRINT(" errcode");
2320 		INTOUT();
2321 	}
2322 
2323 	return;
2324 
2325 trunc:
2326 	ND_PRINT(" [|vol]");
2327 }
2328 
2329 /*
2330  * Handle calls to the AFS BOS service
2331  */
2332 
2333 static void
2334 bos_print(netdissect_options *ndo,
2335           const u_char *bp, u_int length)
2336 {
2337 	uint32_t bos_op;
2338 
2339 	if (length <= sizeof(struct rx_header))
2340 		return;
2341 
2342 	/*
2343 	 * Print out the afs call we're invoking.  The table used here was
2344 	 * gleaned from bozo/bosint.xg
2345 	 */
2346 
2347 	bos_op = GET_BE_U_4(bp + sizeof(struct rx_header));
2348 
2349 	ND_PRINT(" bos call %s", tok2str(bos_req, "op#%u", bos_op));
2350 
2351 	/*
2352 	 * Decode some of the arguments to the BOS calls
2353 	 */
2354 
2355 	bp += sizeof(struct rx_header) + 4;
2356 
2357 	switch (bos_op) {
2358 		case 80:	/* Create B node */
2359 			ND_PRINT(" type");
2360 			STROUT(BOSNAMEMAX);
2361 			ND_PRINT(" instance");
2362 			STROUT(BOSNAMEMAX);
2363 			break;
2364 		case 81:	/* Delete B node */
2365 		case 83:	/* Get status */
2366 		case 85:	/* Get instance info */
2367 		case 87:	/* Add super user */
2368 		case 88:	/* Delete super user */
2369 		case 93:	/* Set cell name */
2370 		case 96:	/* Add cell host */
2371 		case 97:	/* Delete cell host */
2372 		case 104:	/* Restart */
2373 		case 106:	/* Uninstall */
2374 		case 108:	/* Exec */
2375 		case 112:	/* Getlog */
2376 		case 114:	/* Get instance strings */
2377 			STROUT(BOSNAMEMAX);
2378 			break;
2379 		case 82:	/* Set status */
2380 		case 98:	/* Set T status */
2381 			STROUT(BOSNAMEMAX);
2382 			ND_PRINT(" status");
2383 			INTOUT();
2384 			break;
2385 		case 86:	/* Get instance parm */
2386 			STROUT(BOSNAMEMAX);
2387 			ND_PRINT(" num");
2388 			INTOUT();
2389 			break;
2390 		case 84:	/* Enumerate instance */
2391 		case 89:	/* List super users */
2392 		case 90:	/* List keys */
2393 		case 91:	/* Add key */
2394 		case 92:	/* Delete key */
2395 		case 95:	/* Get cell host */
2396 			INTOUT();
2397 			break;
2398 		case 105:	/* Install */
2399 			STROUT(BOSNAMEMAX);
2400 			ND_PRINT(" size");
2401 			INTOUT();
2402 			ND_PRINT(" flags");
2403 			INTOUT();
2404 			ND_PRINT(" date");
2405 			INTOUT();
2406 			break;
2407 		default:
2408 			;
2409 	}
2410 
2411 	return;
2412 
2413 trunc:
2414 	ND_PRINT(" [|bos]");
2415 }
2416 
2417 /*
2418  * Handle replies to the AFS BOS Service
2419  */
2420 
2421 static void
2422 bos_reply_print(netdissect_options *ndo,
2423                 const u_char *bp, u_int length, uint32_t opcode)
2424 {
2425 	const struct rx_header *rxh;
2426 	uint8_t type;
2427 
2428 	if (length <= sizeof(struct rx_header))
2429 		return;
2430 
2431 	rxh = (const struct rx_header *) bp;
2432 
2433 	/*
2434 	 * Print out the afs call we're invoking.  The table used here was
2435 	 * gleaned from volser/volint.xg
2436 	 */
2437 
2438 	ND_PRINT(" bos reply %s", tok2str(bos_req, "op#%u", opcode));
2439 
2440 	type = GET_U_1(rxh->type);
2441 	bp += sizeof(struct rx_header);
2442 
2443 	/*
2444 	 * If it was a data packet, interpret the response.
2445 	 */
2446 
2447 	if (type == RX_PACKET_TYPE_DATA)
2448 		/* Well, no, not really.  Leave this for later */
2449 		;
2450 	else {
2451 		/*
2452 		 * Otherwise, just print out the return code
2453 		 */
2454 		ND_PRINT(" errcode");
2455 		INTOUT();
2456 	}
2457 }
2458 
2459 /*
2460  * Check to see if this is a Ubik opcode.
2461  */
2462 
2463 static int
2464 is_ubik(uint32_t opcode)
2465 {
2466 	if ((opcode >= VOTE_LOW && opcode <= VOTE_HIGH) ||
2467 	    (opcode >= DISK_LOW && opcode <= DISK_HIGH))
2468 		return(1);
2469 	else
2470 		return(0);
2471 }
2472 
2473 /*
2474  * Handle Ubik opcodes to any one of the replicated database services
2475  */
2476 
2477 static void
2478 ubik_print(netdissect_options *ndo,
2479            const u_char *bp)
2480 {
2481 	uint32_t ubik_op;
2482 	uint32_t temp;
2483 
2484 	/*
2485 	 * Print out the afs call we're invoking.  The table used here was
2486 	 * gleaned from ubik/ubik_int.xg
2487 	 */
2488 
2489 	/* Every function that calls this function first makes a bounds check
2490 	 * for (sizeof(rx_header) + 4) bytes, so long as it remains this way
2491 	 * the line below will not over-read.
2492 	 */
2493 	ubik_op = GET_BE_U_4(bp + sizeof(struct rx_header));
2494 
2495 	ND_PRINT(" ubik call %s", tok2str(ubik_req, "op#%u", ubik_op));
2496 
2497 	/*
2498 	 * Decode some of the arguments to the Ubik calls
2499 	 */
2500 
2501 	bp += sizeof(struct rx_header) + 4;
2502 
2503 	switch (ubik_op) {
2504 		case 10000:		/* Beacon */
2505 			temp = GET_BE_U_4(bp);
2506 			bp += sizeof(uint32_t);
2507 			ND_PRINT(" syncsite %s", temp ? "yes" : "no");
2508 			ND_PRINT(" votestart");
2509 			DATEOUT();
2510 			ND_PRINT(" dbversion");
2511 			UBIK_VERSIONOUT();
2512 			ND_PRINT(" tid");
2513 			UBIK_VERSIONOUT();
2514 			break;
2515 		case 10003:		/* Get sync site */
2516 			ND_PRINT(" site");
2517 			UINTOUT();
2518 			break;
2519 		case 20000:		/* Begin */
2520 		case 20001:		/* Commit */
2521 		case 20007:		/* Abort */
2522 		case 20008:		/* Release locks */
2523 		case 20010:		/* Writev */
2524 			ND_PRINT(" tid");
2525 			UBIK_VERSIONOUT();
2526 			break;
2527 		case 20002:		/* Lock */
2528 			ND_PRINT(" tid");
2529 			UBIK_VERSIONOUT();
2530 			ND_PRINT(" file");
2531 			INTOUT();
2532 			ND_PRINT(" pos");
2533 			INTOUT();
2534 			ND_PRINT(" length");
2535 			INTOUT();
2536 			temp = GET_BE_U_4(bp);
2537 			bp += sizeof(uint32_t);
2538 			tok2str(ubik_lock_types, "type %u", temp);
2539 			break;
2540 		case 20003:		/* Write */
2541 			ND_PRINT(" tid");
2542 			UBIK_VERSIONOUT();
2543 			ND_PRINT(" file");
2544 			INTOUT();
2545 			ND_PRINT(" pos");
2546 			INTOUT();
2547 			break;
2548 		case 20005:		/* Get file */
2549 			ND_PRINT(" file");
2550 			INTOUT();
2551 			break;
2552 		case 20006:		/* Send file */
2553 			ND_PRINT(" file");
2554 			INTOUT();
2555 			ND_PRINT(" length");
2556 			INTOUT();
2557 			ND_PRINT(" dbversion");
2558 			UBIK_VERSIONOUT();
2559 			break;
2560 		case 20009:		/* Truncate */
2561 			ND_PRINT(" tid");
2562 			UBIK_VERSIONOUT();
2563 			ND_PRINT(" file");
2564 			INTOUT();
2565 			ND_PRINT(" length");
2566 			INTOUT();
2567 			break;
2568 		case 20012:		/* Set version */
2569 			ND_PRINT(" tid");
2570 			UBIK_VERSIONOUT();
2571 			ND_PRINT(" oldversion");
2572 			UBIK_VERSIONOUT();
2573 			ND_PRINT(" newversion");
2574 			UBIK_VERSIONOUT();
2575 			break;
2576 		default:
2577 			;
2578 	}
2579 
2580 	return;
2581 
2582 trunc:
2583 	ND_PRINT(" [|ubik]");
2584 }
2585 
2586 /*
2587  * Handle Ubik replies to any one of the replicated database services
2588  */
2589 
2590 static void
2591 ubik_reply_print(netdissect_options *ndo,
2592                  const u_char *bp, u_int length, uint32_t opcode)
2593 {
2594 	const struct rx_header *rxh;
2595 	uint8_t type;
2596 
2597 	if (length < sizeof(struct rx_header))
2598 		return;
2599 
2600 	rxh = (const struct rx_header *) bp;
2601 
2602 	/*
2603 	 * Print out the ubik call we're invoking.  This table was gleaned
2604 	 * from ubik/ubik_int.xg
2605 	 */
2606 
2607 	ND_PRINT(" ubik reply %s", tok2str(ubik_req, "op#%u", opcode));
2608 
2609 	type = GET_U_1(rxh->type);
2610 	bp += sizeof(struct rx_header);
2611 
2612 	/*
2613 	 * If it was a data packet, print out the arguments to the Ubik calls
2614 	 */
2615 
2616 	if (type == RX_PACKET_TYPE_DATA)
2617 		switch (opcode) {
2618 		case 10000:		/* Beacon */
2619 			ND_PRINT(" vote no");
2620 			break;
2621 		case 20004:		/* Get version */
2622 			ND_PRINT(" dbversion");
2623 			UBIK_VERSIONOUT();
2624 			break;
2625 		default:
2626 			;
2627 		}
2628 
2629 	/*
2630 	 * Otherwise, print out "yes" if it was a beacon packet (because
2631 	 * that's how yes votes are returned, go figure), otherwise
2632 	 * just print out the error code.
2633 	 */
2634 
2635 	else
2636 		switch (opcode) {
2637 		case 10000:		/* Beacon */
2638 			ND_PRINT(" vote yes until");
2639 			DATEOUT();
2640 			break;
2641 		default:
2642 			ND_PRINT(" errcode");
2643 			INTOUT();
2644 		}
2645 
2646 	return;
2647 
2648 trunc:
2649 	ND_PRINT(" [|ubik]");
2650 }
2651 
2652 /*
2653  * Handle RX ACK packets.
2654  */
2655 
2656 static void
2657 rx_ack_print(netdissect_options *ndo,
2658              const u_char *bp, u_int length)
2659 {
2660 	const struct rx_ackPacket *rxa;
2661 	uint8_t nAcks;
2662 	int i, start, last;
2663 	uint32_t firstPacket;
2664 
2665 	if (length < sizeof(struct rx_header))
2666 		return;
2667 
2668 	bp += sizeof(struct rx_header);
2669 
2670 	ND_TCHECK_LEN(bp, sizeof(struct rx_ackPacket));
2671 
2672 	rxa = (const struct rx_ackPacket *) bp;
2673 	bp += sizeof(struct rx_ackPacket);
2674 
2675 	/*
2676 	 * Print out a few useful things from the ack packet structure
2677 	 */
2678 
2679 	if (ndo->ndo_vflag > 2)
2680 		ND_PRINT(" bufspace %u maxskew %u",
2681 		       GET_BE_U_2(rxa->bufferSpace),
2682 		       GET_BE_U_2(rxa->maxSkew));
2683 
2684 	firstPacket = GET_BE_U_4(rxa->firstPacket);
2685 	ND_PRINT(" first %u serial %u reason %s",
2686 	       firstPacket, GET_BE_U_4(rxa->serial),
2687 	       tok2str(rx_ack_reasons, "#%u", GET_U_1(rxa->reason)));
2688 
2689 	/*
2690 	 * Okay, now we print out the ack array.  The way _this_ works
2691 	 * is that we start at "first", and step through the ack array.
2692 	 * If we have a contiguous range of acks/nacks, try to
2693 	 * collapse them into a range.
2694 	 *
2695 	 * If you're really clever, you might have noticed that this
2696 	 * doesn't seem quite correct.  Specifically, due to structure
2697 	 * padding, sizeof(struct rx_ackPacket) - RX_MAXACKS won't actually
2698 	 * yield the start of the ack array (because RX_MAXACKS is 255
2699 	 * and the structure will likely get padded to a 2 or 4 byte
2700 	 * boundary).  However, this is the way it's implemented inside
2701 	 * of AFS - the start of the extra fields are at
2702 	 * sizeof(struct rx_ackPacket) - RX_MAXACKS + nAcks, which _isn't_
2703 	 * the exact start of the ack array.  Sigh.  That's why we aren't
2704 	 * using bp, but instead use rxa->acks[].  But nAcks gets added
2705 	 * to bp after this, so bp ends up at the right spot.  Go figure.
2706 	 */
2707 
2708 	nAcks = GET_U_1(rxa->nAcks);
2709 	if (nAcks != 0) {
2710 
2711 		ND_TCHECK_LEN(bp, nAcks);
2712 
2713 		/*
2714 		 * Sigh, this is gross, but it seems to work to collapse
2715 		 * ranges correctly.
2716 		 */
2717 
2718 		for (i = 0, start = last = -2; i < nAcks; i++)
2719 			if (GET_U_1(bp + i) == RX_ACK_TYPE_ACK) {
2720 
2721 				/*
2722 				 * I figured this deserved _some_ explanation.
2723 				 * First, print "acked" and the packet seq
2724 				 * number if this is the first time we've
2725 				 * seen an acked packet.
2726 				 */
2727 
2728 				if (last == -2) {
2729 					ND_PRINT(" acked %u", firstPacket + i);
2730 					start = i;
2731 				}
2732 
2733 				/*
2734 				 * Otherwise, if there is a skip in
2735 				 * the range (such as an nacked packet in
2736 				 * the middle of some acked packets),
2737 				 * then print the current packet number
2738 				 * separated from the last number by
2739 				 * a comma.
2740 				 */
2741 
2742 				else if (last != i - 1) {
2743 					ND_PRINT(",%u", firstPacket + i);
2744 					start = i;
2745 				}
2746 
2747 				/*
2748 				 * We always set last to the value of
2749 				 * the last ack we saw.  Conversely, start
2750 				 * is set to the value of the first ack
2751 				 * we saw in a range.
2752 				 */
2753 
2754 				last = i;
2755 
2756 				/*
2757 				 * Okay, this bit a code gets executed when
2758 				 * we hit a nack ... in _this_ case we
2759 				 * want to print out the range of packets
2760 				 * that were acked, so we need to print
2761 				 * the _previous_ packet number separated
2762 				 * from the first by a dash (-).  Since we
2763 				 * already printed the first packet above,
2764 				 * just print the final packet.  Don't
2765 				 * do this if there will be a single-length
2766 				 * range.
2767 				 */
2768 			} else if (last == i - 1 && start != last)
2769 				ND_PRINT("-%u", firstPacket + i - 1);
2770 
2771 		/*
2772 		 * So, what's going on here?  We ran off the end of the
2773 		 * ack list, and if we got a range we need to finish it up.
2774 		 * So we need to determine if the last packet in the list
2775 		 * was an ack (if so, then last will be set to it) and
2776 		 * we need to see if the last range didn't start with the
2777 		 * last packet (because if it _did_, then that would mean
2778 		 * that the packet number has already been printed and
2779 		 * we don't need to print it again).
2780 		 */
2781 
2782 		if (last == i - 1 && start != last)
2783 			ND_PRINT("-%u", firstPacket + i - 1);
2784 
2785 		/*
2786 		 * Same as above, just without comments
2787 		 */
2788 
2789 		for (i = 0, start = last = -2; i < nAcks; i++)
2790 			if (GET_U_1(bp + i) == RX_ACK_TYPE_NACK) {
2791 				if (last == -2) {
2792 					ND_PRINT(" nacked %u", firstPacket + i);
2793 					start = i;
2794 				} else if (last != i - 1) {
2795 					ND_PRINT(",%u", firstPacket + i);
2796 					start = i;
2797 				}
2798 				last = i;
2799 			} else if (last == i - 1 && start != last)
2800 				ND_PRINT("-%u", firstPacket + i - 1);
2801 
2802 		if (last == i - 1 && start != last)
2803 			ND_PRINT("-%u", firstPacket + i - 1);
2804 
2805 		bp += nAcks;
2806 	}
2807 
2808 	/* Padding. */
2809 	bp += 3;
2810 
2811 	/*
2812 	 * These are optional fields; depending on your version of AFS,
2813 	 * you may or may not see them
2814 	 */
2815 
2816 #define TRUNCRET(n)	if (ndo->ndo_snapend - bp + 1 <= n) return;
2817 
2818 	if (ndo->ndo_vflag > 1) {
2819 		TRUNCRET(4);
2820 		ND_PRINT(" ifmtu");
2821 		UINTOUT();
2822 
2823 		TRUNCRET(4);
2824 		ND_PRINT(" maxmtu");
2825 		UINTOUT();
2826 
2827 		TRUNCRET(4);
2828 		ND_PRINT(" rwind");
2829 		UINTOUT();
2830 
2831 		TRUNCRET(4);
2832 		ND_PRINT(" maxpackets");
2833 		UINTOUT();
2834 	}
2835 
2836 	return;
2837 
2838 trunc:
2839 	ND_PRINT(" [|ack]");
2840 }
2841 #undef TRUNCRET
2842