xref: /freebsd/contrib/tcpdump/print-rx.c (revision 9247238cc4b8835892a47701136b0fd073f8d67c)
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 *, const 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; char str[256]; \
798 			_t = (time_t) GET_BE_S_4(bp); \
799 			bp += sizeof(int32_t); \
800 			ND_PRINT(" %s", \
801 			    nd_format_time(str, sizeof(str), \
802 			      "%Y/%m/%d %H:%M:%S", localtime(&_t))); \
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 XSTRINGIFY(x) #x
1144 #define NUMSTRINGIFY(x)	XSTRINGIFY(x)
1145 
1146 static void
1147 acl_print(netdissect_options *ndo,
1148           u_char *s, const u_char *end)
1149 {
1150 	int pos, neg, acl;
1151 	int n, i;
1152 	char user[USERNAMEMAX+1];
1153 
1154 	if (sscanf((char *) s, "%d %d\n%n", &pos, &neg, &n) != 2)
1155 		return;
1156 
1157 	s += n;
1158 
1159 	if (s > end)
1160 		return;
1161 
1162 	/*
1163 	 * This wacky order preserves the order used by the "fs" command
1164 	 */
1165 
1166 #define ACLOUT(acl) \
1167 	ND_PRINT("%s%s%s%s%s%s%s", \
1168 	          acl & PRSFS_READ       ? "r" : "", \
1169 	          acl & PRSFS_LOOKUP     ? "l" : "", \
1170 	          acl & PRSFS_INSERT     ? "i" : "", \
1171 	          acl & PRSFS_DELETE     ? "d" : "", \
1172 	          acl & PRSFS_WRITE      ? "w" : "", \
1173 	          acl & PRSFS_LOCK       ? "k" : "", \
1174 	          acl & PRSFS_ADMINISTER ? "a" : "");
1175 
1176 	for (i = 0; i < pos; i++) {
1177 		if (sscanf((char *) s, "%" NUMSTRINGIFY(USERNAMEMAX) "s %d\n%n", user, &acl, &n) != 2)
1178 			return;
1179 		s += n;
1180 		ND_PRINT(" +{");
1181 		fn_print_str(ndo, (u_char *)user);
1182 		ND_PRINT(" ");
1183 		ACLOUT(acl);
1184 		ND_PRINT("}");
1185 		if (s > end)
1186 			return;
1187 	}
1188 
1189 	for (i = 0; i < neg; i++) {
1190 		if (sscanf((char *) s, "%" NUMSTRINGIFY(USERNAMEMAX) "s %d\n%n", user, &acl, &n) != 2)
1191 			return;
1192 		s += n;
1193 		ND_PRINT(" -{");
1194 		fn_print_str(ndo, (u_char *)user);
1195 		ND_PRINT(" ");
1196 		ACLOUT(acl);
1197 		ND_PRINT("}");
1198 		if (s > end)
1199 			return;
1200 	}
1201 }
1202 
1203 #undef ACLOUT
1204 
1205 /*
1206  * Handle calls to the AFS callback service
1207  */
1208 
1209 static void
1210 cb_print(netdissect_options *ndo,
1211          const u_char *bp, u_int length)
1212 {
1213 	uint32_t cb_op;
1214 	uint32_t i;
1215 
1216 	if (length <= sizeof(struct rx_header))
1217 		return;
1218 
1219 	/*
1220 	 * Print out the afs call we're invoking.  The table used here was
1221 	 * gleaned from fsint/afscbint.xg
1222 	 */
1223 
1224 	cb_op = GET_BE_U_4(bp + sizeof(struct rx_header));
1225 
1226 	ND_PRINT(" cb call %s", tok2str(cb_req, "op#%u", cb_op));
1227 
1228 	bp += sizeof(struct rx_header) + 4;
1229 
1230 	/*
1231 	 * Print out the afs call we're invoking.  The table used here was
1232 	 * gleaned from fsint/afscbint.xg
1233 	 */
1234 
1235 	switch (cb_op) {
1236 		case 204:		/* Callback */
1237 		{
1238 			uint32_t j, t;
1239 			j = GET_BE_U_4(bp);
1240 			bp += sizeof(uint32_t);
1241 
1242 			for (i = 0; i < j; i++) {
1243 				FIDOUT();
1244 				if (i != j - 1)
1245 					ND_PRINT(",");
1246 			}
1247 
1248 			if (j == 0)
1249 				ND_PRINT(" <none!>");
1250 
1251 			j = GET_BE_U_4(bp);
1252 			bp += sizeof(uint32_t);
1253 
1254 			if (j != 0)
1255 				ND_PRINT(";");
1256 
1257 			for (i = 0; i < j; i++) {
1258 				ND_PRINT(" ver");
1259 				INTOUT();
1260 				ND_PRINT(" expires");
1261 				DATEOUT();
1262 				t = GET_BE_U_4(bp);
1263 				bp += sizeof(uint32_t);
1264 				tok2str(cb_types, "type %u", t);
1265 			}
1266 			break;
1267 		}
1268 		case 214: {
1269 			ND_PRINT(" afsuuid");
1270 			AFSUUIDOUT();
1271 			break;
1272 		}
1273 		default:
1274 			;
1275 	}
1276 
1277 	return;
1278 
1279 trunc:
1280 	ND_PRINT(" [|cb]");
1281 }
1282 
1283 /*
1284  * Handle replies to the AFS Callback Service
1285  */
1286 
1287 static void
1288 cb_reply_print(netdissect_options *ndo,
1289                const u_char *bp, u_int length, uint32_t opcode)
1290 {
1291 	const struct rx_header *rxh;
1292 	uint8_t type;
1293 
1294 	if (length <= sizeof(struct rx_header))
1295 		return;
1296 
1297 	rxh = (const struct rx_header *) bp;
1298 
1299 	/*
1300 	 * Print out the afs call we're invoking.  The table used here was
1301 	 * gleaned from fsint/afscbint.xg
1302 	 */
1303 
1304 	ND_PRINT(" cb reply %s", tok2str(cb_req, "op#%u", opcode));
1305 
1306 	type = GET_U_1(rxh->type);
1307 	bp += sizeof(struct rx_header);
1308 
1309 	/*
1310 	 * If it was a data packet, interpret the response.
1311 	 */
1312 
1313 	if (type == RX_PACKET_TYPE_DATA)
1314 		switch (opcode) {
1315 		case 213:	/* InitCallBackState3 */
1316 			AFSUUIDOUT();
1317 			break;
1318 		default:
1319 		;
1320 		}
1321 	else {
1322 		/*
1323 		 * Otherwise, just print out the return code
1324 		 */
1325 		ND_PRINT(" errcode");
1326 		INTOUT();
1327 	}
1328 
1329 	return;
1330 
1331 trunc:
1332 	ND_PRINT(" [|cb]");
1333 }
1334 
1335 /*
1336  * Handle calls to the AFS protection database server
1337  */
1338 
1339 static void
1340 prot_print(netdissect_options *ndo,
1341            const u_char *bp, u_int length)
1342 {
1343 	uint32_t i;
1344 	uint32_t pt_op;
1345 
1346 	if (length <= sizeof(struct rx_header))
1347 		return;
1348 
1349 	/*
1350 	 * Print out the afs call we're invoking.  The table used here was
1351 	 * gleaned from ptserver/ptint.xg
1352 	 */
1353 
1354 	pt_op = GET_BE_U_4(bp + sizeof(struct rx_header));
1355 
1356 	ND_PRINT(" pt");
1357 
1358 	if (is_ubik(pt_op)) {
1359 		ubik_print(ndo, bp);
1360 		return;
1361 	}
1362 
1363 	ND_PRINT(" call %s", tok2str(pt_req, "op#%u", pt_op));
1364 
1365 	/*
1366 	 * Decode some of the arguments to the PT calls
1367 	 */
1368 
1369 	bp += sizeof(struct rx_header) + 4;
1370 
1371 	switch (pt_op) {
1372 		case 500:	/* I New User */
1373 			STROUT(PRNAMEMAX);
1374 			ND_PRINT(" id");
1375 			INTOUT();
1376 			ND_PRINT(" oldid");
1377 			INTOUT();
1378 			break;
1379 		case 501:	/* Where is it */
1380 		case 506:	/* Delete */
1381 		case 508:	/* Get CPS */
1382 		case 512:	/* List entry */
1383 		case 514:	/* List elements */
1384 		case 517:	/* List owned */
1385 		case 518:	/* Get CPS2 */
1386 		case 519:	/* Get host CPS */
1387 		case 530:	/* List super groups */
1388 			ND_PRINT(" id");
1389 			INTOUT();
1390 			break;
1391 		case 502:	/* Dump entry */
1392 			ND_PRINT(" pos");
1393 			INTOUT();
1394 			break;
1395 		case 503:	/* Add to group */
1396 		case 507:	/* Remove from group */
1397 		case 515:	/* Is a member of? */
1398 			ND_PRINT(" uid");
1399 			INTOUT();
1400 			ND_PRINT(" gid");
1401 			INTOUT();
1402 			break;
1403 		case 504:	/* Name to ID */
1404 		{
1405 			uint32_t j;
1406 			j = GET_BE_U_4(bp);
1407 			bp += sizeof(uint32_t);
1408 
1409 			/*
1410 			 * Who designed this chicken-shit protocol?
1411 			 *
1412 			 * Each character is stored as a 32-bit
1413 			 * integer!
1414 			 */
1415 
1416 			for (i = 0; i < j; i++) {
1417 				VECOUT(PRNAMEMAX);
1418 			}
1419 			if (j == 0)
1420 				ND_PRINT(" <none!>");
1421 		}
1422 			break;
1423 		case 505:	/* Id to name */
1424 		{
1425 			uint32_t j;
1426 			ND_PRINT(" ids:");
1427 			i = GET_BE_U_4(bp);
1428 			bp += sizeof(uint32_t);
1429 			for (j = 0; j < i; j++)
1430 				INTOUT();
1431 			if (j == 0)
1432 				ND_PRINT(" <none!>");
1433 		}
1434 			break;
1435 		case 509:	/* New entry */
1436 			STROUT(PRNAMEMAX);
1437 			ND_PRINT(" flag");
1438 			INTOUT();
1439 			ND_PRINT(" oid");
1440 			INTOUT();
1441 			break;
1442 		case 511:	/* Set max */
1443 			ND_PRINT(" id");
1444 			INTOUT();
1445 			ND_PRINT(" gflag");
1446 			INTOUT();
1447 			break;
1448 		case 513:	/* Change entry */
1449 			ND_PRINT(" id");
1450 			INTOUT();
1451 			STROUT(PRNAMEMAX);
1452 			ND_PRINT(" oldid");
1453 			INTOUT();
1454 			ND_PRINT(" newid");
1455 			INTOUT();
1456 			break;
1457 		case 520:	/* Update entry */
1458 			ND_PRINT(" id");
1459 			INTOUT();
1460 			STROUT(PRNAMEMAX);
1461 			break;
1462 		default:
1463 			;
1464 	}
1465 
1466 
1467 	return;
1468 
1469 trunc:
1470 	ND_PRINT(" [|pt]");
1471 }
1472 
1473 /*
1474  * Handle replies to the AFS protection service
1475  */
1476 
1477 static void
1478 prot_reply_print(netdissect_options *ndo,
1479                  const u_char *bp, u_int length, uint32_t opcode)
1480 {
1481 	const struct rx_header *rxh;
1482 	uint8_t type;
1483 	uint32_t i;
1484 
1485 	if (length < sizeof(struct rx_header))
1486 		return;
1487 
1488 	rxh = (const struct rx_header *) bp;
1489 
1490 	/*
1491 	 * Print out the afs call we're invoking.  The table used here was
1492 	 * gleaned from ptserver/ptint.xg.  Check to see if it's a
1493 	 * Ubik call, however.
1494 	 */
1495 
1496 	ND_PRINT(" pt");
1497 
1498 	if (is_ubik(opcode)) {
1499 		ubik_reply_print(ndo, bp, length, opcode);
1500 		return;
1501 	}
1502 
1503 	ND_PRINT(" reply %s", tok2str(pt_req, "op#%u", opcode));
1504 
1505 	type = GET_U_1(rxh->type);
1506 	bp += sizeof(struct rx_header);
1507 
1508 	/*
1509 	 * If it was a data packet, interpret the response
1510 	 */
1511 
1512 	if (type == RX_PACKET_TYPE_DATA)
1513 		switch (opcode) {
1514 		case 504:		/* Name to ID */
1515 		{
1516 			uint32_t j;
1517 			ND_PRINT(" ids:");
1518 			i = GET_BE_U_4(bp);
1519 			bp += sizeof(uint32_t);
1520 			for (j = 0; j < i; j++)
1521 				INTOUT();
1522 			if (j == 0)
1523 				ND_PRINT(" <none!>");
1524 		}
1525 			break;
1526 		case 505:		/* ID to name */
1527 		{
1528 			uint32_t j;
1529 			j = GET_BE_U_4(bp);
1530 			bp += sizeof(uint32_t);
1531 
1532 			/*
1533 			 * Who designed this chicken-shit protocol?
1534 			 *
1535 			 * Each character is stored as a 32-bit
1536 			 * integer!
1537 			 */
1538 
1539 			for (i = 0; i < j; i++) {
1540 				VECOUT(PRNAMEMAX);
1541 			}
1542 			if (j == 0)
1543 				ND_PRINT(" <none!>");
1544 		}
1545 			break;
1546 		case 508:		/* Get CPS */
1547 		case 514:		/* List elements */
1548 		case 517:		/* List owned */
1549 		case 518:		/* Get CPS2 */
1550 		case 519:		/* Get host CPS */
1551 		{
1552 			uint32_t j;
1553 			j = GET_BE_U_4(bp);
1554 			bp += sizeof(uint32_t);
1555 			for (i = 0; i < j; i++) {
1556 				INTOUT();
1557 			}
1558 			if (j == 0)
1559 				ND_PRINT(" <none!>");
1560 		}
1561 			break;
1562 		case 510:		/* List max */
1563 			ND_PRINT(" maxuid");
1564 			INTOUT();
1565 			ND_PRINT(" maxgid");
1566 			INTOUT();
1567 			break;
1568 		default:
1569 			;
1570 		}
1571 	else {
1572 		/*
1573 		 * Otherwise, just print out the return code
1574 		 */
1575 		ND_PRINT(" errcode");
1576 		INTOUT();
1577 	}
1578 
1579 	return;
1580 
1581 trunc:
1582 	ND_PRINT(" [|pt]");
1583 }
1584 
1585 /*
1586  * Handle calls to the AFS volume location database service
1587  */
1588 
1589 static void
1590 vldb_print(netdissect_options *ndo,
1591            const u_char *bp, u_int length)
1592 {
1593 	uint32_t vldb_op;
1594 	uint32_t i;
1595 
1596 	if (length <= sizeof(struct rx_header))
1597 		return;
1598 
1599 	/*
1600 	 * Print out the afs call we're invoking.  The table used here was
1601 	 * gleaned from vlserver/vldbint.xg
1602 	 */
1603 
1604 	vldb_op = GET_BE_U_4(bp + sizeof(struct rx_header));
1605 
1606 	ND_PRINT(" vldb");
1607 
1608 	if (is_ubik(vldb_op)) {
1609 		ubik_print(ndo, bp);
1610 		return;
1611 	}
1612 	ND_PRINT(" call %s", tok2str(vldb_req, "op#%u", vldb_op));
1613 
1614 	/*
1615 	 * Decode some of the arguments to the VLDB calls
1616 	 */
1617 
1618 	bp += sizeof(struct rx_header) + 4;
1619 
1620 	switch (vldb_op) {
1621 		case 501:	/* Create new volume */
1622 		case 517:	/* Create entry N */
1623 			VECOUT(VLNAMEMAX);
1624 			break;
1625 		case 502:	/* Delete entry */
1626 		case 503:	/* Get entry by ID */
1627 		case 507:	/* Update entry */
1628 		case 508:	/* Set lock */
1629 		case 509:	/* Release lock */
1630 		case 518:	/* Get entry by ID N */
1631 			ND_PRINT(" volid");
1632 			INTOUT();
1633 			i = GET_BE_U_4(bp);
1634 			bp += sizeof(uint32_t);
1635 			if (i <= 2)
1636 				ND_PRINT(" type %s", voltype[i]);
1637 			break;
1638 		case 504:	/* Get entry by name */
1639 		case 519:	/* Get entry by name N */
1640 		case 524:	/* Update entry by name */
1641 		case 527:	/* Get entry by name U */
1642 			STROUT(VLNAMEMAX);
1643 			break;
1644 		case 505:	/* Get new vol id */
1645 			ND_PRINT(" bump");
1646 			INTOUT();
1647 			break;
1648 		case 506:	/* Replace entry */
1649 		case 520:	/* Replace entry N */
1650 			ND_PRINT(" volid");
1651 			INTOUT();
1652 			i = GET_BE_U_4(bp);
1653 			bp += sizeof(uint32_t);
1654 			if (i <= 2)
1655 				ND_PRINT(" type %s", voltype[i]);
1656 			VECOUT(VLNAMEMAX);
1657 			break;
1658 		case 510:	/* List entry */
1659 		case 521:	/* List entry N */
1660 			ND_PRINT(" index");
1661 			INTOUT();
1662 			break;
1663 		default:
1664 			;
1665 	}
1666 
1667 	return;
1668 
1669 trunc:
1670 	ND_PRINT(" [|vldb]");
1671 }
1672 
1673 /*
1674  * Handle replies to the AFS volume location database service
1675  */
1676 
1677 static void
1678 vldb_reply_print(netdissect_options *ndo,
1679                  const u_char *bp, u_int length, uint32_t opcode)
1680 {
1681 	const struct rx_header *rxh;
1682 	uint8_t type;
1683 	uint32_t i;
1684 
1685 	if (length < sizeof(struct rx_header))
1686 		return;
1687 
1688 	rxh = (const struct rx_header *) bp;
1689 
1690 	/*
1691 	 * Print out the afs call we're invoking.  The table used here was
1692 	 * gleaned from vlserver/vldbint.xg.  Check to see if it's a
1693 	 * Ubik call, however.
1694 	 */
1695 
1696 	ND_PRINT(" vldb");
1697 
1698 	if (is_ubik(opcode)) {
1699 		ubik_reply_print(ndo, bp, length, opcode);
1700 		return;
1701 	}
1702 
1703 	ND_PRINT(" reply %s", tok2str(vldb_req, "op#%u", opcode));
1704 
1705 	type = GET_U_1(rxh->type);
1706 	bp += sizeof(struct rx_header);
1707 
1708 	/*
1709 	 * If it was a data packet, interpret the response
1710 	 */
1711 
1712 	if (type == RX_PACKET_TYPE_DATA)
1713 		switch (opcode) {
1714 		case 510:	/* List entry */
1715 			ND_PRINT(" count");
1716 			INTOUT();
1717 			ND_PRINT(" nextindex");
1718 			INTOUT();
1719 			ND_FALL_THROUGH;
1720 		case 503:	/* Get entry by id */
1721 		case 504:	/* Get entry by name */
1722 		{	uint32_t nservers, j;
1723 			VECOUT(VLNAMEMAX);
1724 			ND_TCHECK_4(bp);
1725 			bp += sizeof(uint32_t);
1726 			ND_PRINT(" numservers");
1727 			nservers = GET_BE_U_4(bp);
1728 			bp += sizeof(uint32_t);
1729 			ND_PRINT(" %u", nservers);
1730 			ND_PRINT(" servers");
1731 			for (i = 0; i < 8; i++) {
1732 				ND_TCHECK_4(bp);
1733 				if (i < nservers)
1734 					ND_PRINT(" %s",
1735 					   intoa(GET_IPV4_TO_NETWORK_ORDER(bp)));
1736 				bp += sizeof(nd_ipv4);
1737 			}
1738 			ND_PRINT(" partitions");
1739 			for (i = 0; i < 8; i++) {
1740 				j = GET_BE_U_4(bp);
1741 				if (i < nservers && j <= 26)
1742 					ND_PRINT(" %c", 'a' + j);
1743 				else if (i < nservers)
1744 					ND_PRINT(" %u", j);
1745 				bp += sizeof(uint32_t);
1746 			}
1747 			ND_TCHECK_LEN(bp, 8 * sizeof(uint32_t));
1748 			bp += 8 * sizeof(uint32_t);
1749 			ND_PRINT(" rwvol");
1750 			UINTOUT();
1751 			ND_PRINT(" rovol");
1752 			UINTOUT();
1753 			ND_PRINT(" backup");
1754 			UINTOUT();
1755 		}
1756 			break;
1757 		case 505:	/* Get new volume ID */
1758 			ND_PRINT(" newvol");
1759 			UINTOUT();
1760 			break;
1761 		case 521:	/* List entry */
1762 		case 529:	/* List entry U */
1763 			ND_PRINT(" count");
1764 			INTOUT();
1765 			ND_PRINT(" nextindex");
1766 			INTOUT();
1767 			ND_FALL_THROUGH;
1768 		case 518:	/* Get entry by ID N */
1769 		case 519:	/* Get entry by name N */
1770 		{	uint32_t nservers, j;
1771 			VECOUT(VLNAMEMAX);
1772 			ND_PRINT(" numservers");
1773 			nservers = GET_BE_U_4(bp);
1774 			bp += sizeof(uint32_t);
1775 			ND_PRINT(" %u", nservers);
1776 			ND_PRINT(" servers");
1777 			for (i = 0; i < 13; i++) {
1778 				ND_TCHECK_4(bp);
1779 				if (i < nservers)
1780 					ND_PRINT(" %s",
1781 					   intoa(GET_IPV4_TO_NETWORK_ORDER(bp)));
1782 				bp += sizeof(nd_ipv4);
1783 			}
1784 			ND_PRINT(" partitions");
1785 			for (i = 0; i < 13; i++) {
1786 				j = GET_BE_U_4(bp);
1787 				if (i < nservers && j <= 26)
1788 					ND_PRINT(" %c", 'a' + j);
1789 				else if (i < nservers)
1790 					ND_PRINT(" %u", j);
1791 				bp += sizeof(uint32_t);
1792 			}
1793 			ND_TCHECK_LEN(bp, 13 * sizeof(uint32_t));
1794 			bp += 13 * sizeof(uint32_t);
1795 			ND_PRINT(" rwvol");
1796 			UINTOUT();
1797 			ND_PRINT(" rovol");
1798 			UINTOUT();
1799 			ND_PRINT(" backup");
1800 			UINTOUT();
1801 		}
1802 			break;
1803 		case 526:	/* Get entry by ID U */
1804 		case 527:	/* Get entry by name U */
1805 		{	uint32_t nservers, j;
1806 			VECOUT(VLNAMEMAX);
1807 			ND_PRINT(" numservers");
1808 			nservers = GET_BE_U_4(bp);
1809 			bp += sizeof(uint32_t);
1810 			ND_PRINT(" %u", nservers);
1811 			ND_PRINT(" servers");
1812 			for (i = 0; i < 13; i++) {
1813 				if (i < nservers) {
1814 					ND_PRINT(" afsuuid");
1815 					AFSUUIDOUT();
1816 				} else {
1817 					ND_TCHECK_LEN(bp, 44);
1818 					bp += 44;
1819 				}
1820 			}
1821 			ND_TCHECK_LEN(bp, 4 * 13);
1822 			bp += 4 * 13;
1823 			ND_PRINT(" partitions");
1824 			for (i = 0; i < 13; i++) {
1825 				j = GET_BE_U_4(bp);
1826 				if (i < nservers && j <= 26)
1827 					ND_PRINT(" %c", 'a' + j);
1828 				else if (i < nservers)
1829 					ND_PRINT(" %u", j);
1830 				bp += sizeof(uint32_t);
1831 			}
1832 			ND_TCHECK_LEN(bp, 13 * sizeof(uint32_t));
1833 			bp += 13 * sizeof(uint32_t);
1834 			ND_PRINT(" rwvol");
1835 			UINTOUT();
1836 			ND_PRINT(" rovol");
1837 			UINTOUT();
1838 			ND_PRINT(" backup");
1839 			UINTOUT();
1840 		}
1841 		default:
1842 			;
1843 		}
1844 
1845 	else {
1846 		/*
1847 		 * Otherwise, just print out the return code
1848 		 */
1849 		ND_PRINT(" errcode");
1850 		INTOUT();
1851 	}
1852 
1853 	return;
1854 
1855 trunc:
1856 	ND_PRINT(" [|vldb]");
1857 }
1858 
1859 /*
1860  * Handle calls to the AFS Kerberos Authentication service
1861  */
1862 
1863 static void
1864 kauth_print(netdissect_options *ndo,
1865             const u_char *bp, u_int length)
1866 {
1867 	uint32_t kauth_op;
1868 
1869 	if (length <= sizeof(struct rx_header))
1870 		return;
1871 
1872 	/*
1873 	 * Print out the afs call we're invoking.  The table used here was
1874 	 * gleaned from kauth/kauth.rg
1875 	 */
1876 
1877 	kauth_op = GET_BE_U_4(bp + sizeof(struct rx_header));
1878 
1879 	ND_PRINT(" kauth");
1880 
1881 	if (is_ubik(kauth_op)) {
1882 		ubik_print(ndo, bp);
1883 		return;
1884 	}
1885 
1886 
1887 	ND_PRINT(" call %s", tok2str(kauth_req, "op#%u", kauth_op));
1888 
1889 	/*
1890 	 * Decode some of the arguments to the KA calls
1891 	 */
1892 
1893 	bp += sizeof(struct rx_header) + 4;
1894 
1895 	switch (kauth_op) {
1896 		case 1:		/* Authenticate old */
1897 		case 21:	/* Authenticate */
1898 		case 22:	/* Authenticate-V2 */
1899 		case 2:		/* Change PW */
1900 		case 5:		/* Set fields */
1901 		case 6:		/* Create user */
1902 		case 7:		/* Delete user */
1903 		case 8:		/* Get entry */
1904 		case 14:	/* Unlock */
1905 		case 15:	/* Lock status */
1906 			ND_PRINT(" principal");
1907 			STROUT(KANAMEMAX);
1908 			STROUT(KANAMEMAX);
1909 			break;
1910 		case 3:		/* GetTicket-old */
1911 		case 23:	/* GetTicket */
1912 		{
1913 			uint32_t i;
1914 			ND_PRINT(" kvno");
1915 			INTOUT();
1916 			ND_PRINT(" domain");
1917 			STROUT(KANAMEMAX);
1918 			i = GET_BE_U_4(bp);
1919 			bp += sizeof(uint32_t);
1920 			ND_TCHECK_LEN(bp, i);
1921 			bp += i;
1922 			ND_PRINT(" principal");
1923 			STROUT(KANAMEMAX);
1924 			STROUT(KANAMEMAX);
1925 			break;
1926 		}
1927 		case 4:		/* Set Password */
1928 			ND_PRINT(" principal");
1929 			STROUT(KANAMEMAX);
1930 			STROUT(KANAMEMAX);
1931 			ND_PRINT(" kvno");
1932 			INTOUT();
1933 			break;
1934 		case 12:	/* Get password */
1935 			ND_PRINT(" name");
1936 			STROUT(KANAMEMAX);
1937 			break;
1938 		default:
1939 			;
1940 	}
1941 
1942 	return;
1943 
1944 trunc:
1945 	ND_PRINT(" [|kauth]");
1946 }
1947 
1948 /*
1949  * Handle replies to the AFS Kerberos Authentication Service
1950  */
1951 
1952 static void
1953 kauth_reply_print(netdissect_options *ndo,
1954                   const u_char *bp, u_int length, uint32_t opcode)
1955 {
1956 	const struct rx_header *rxh;
1957 	uint8_t type;
1958 
1959 	if (length <= sizeof(struct rx_header))
1960 		return;
1961 
1962 	rxh = (const struct rx_header *) bp;
1963 
1964 	/*
1965 	 * Print out the afs call we're invoking.  The table used here was
1966 	 * gleaned from kauth/kauth.rg
1967 	 */
1968 
1969 	ND_PRINT(" kauth");
1970 
1971 	if (is_ubik(opcode)) {
1972 		ubik_reply_print(ndo, bp, length, opcode);
1973 		return;
1974 	}
1975 
1976 	ND_PRINT(" reply %s", tok2str(kauth_req, "op#%u", opcode));
1977 
1978 	type = GET_U_1(rxh->type);
1979 	bp += sizeof(struct rx_header);
1980 
1981 	/*
1982 	 * If it was a data packet, interpret the response.
1983 	 */
1984 
1985 	if (type == RX_PACKET_TYPE_DATA)
1986 		/* Well, no, not really.  Leave this for later */
1987 		;
1988 	else {
1989 		/*
1990 		 * Otherwise, just print out the return code
1991 		 */
1992 		ND_PRINT(" errcode");
1993 		INTOUT();
1994 	}
1995 }
1996 
1997 /*
1998  * Handle calls to the AFS Volume location service
1999  */
2000 
2001 static void
2002 vol_print(netdissect_options *ndo,
2003           const u_char *bp, u_int length)
2004 {
2005 	uint32_t vol_op;
2006 
2007 	if (length <= sizeof(struct rx_header))
2008 		return;
2009 
2010 	/*
2011 	 * Print out the afs call we're invoking.  The table used here was
2012 	 * gleaned from volser/volint.xg
2013 	 */
2014 
2015 	vol_op = GET_BE_U_4(bp + sizeof(struct rx_header));
2016 
2017 	ND_PRINT(" vol call %s", tok2str(vol_req, "op#%u", vol_op));
2018 
2019 	bp += sizeof(struct rx_header) + 4;
2020 
2021 	switch (vol_op) {
2022 		case 100:	/* Create volume */
2023 			ND_PRINT(" partition");
2024 			UINTOUT();
2025 			ND_PRINT(" name");
2026 			STROUT(AFSNAMEMAX);
2027 			ND_PRINT(" type");
2028 			UINTOUT();
2029 			ND_PRINT(" parent");
2030 			UINTOUT();
2031 			break;
2032 		case 101:	/* Delete volume */
2033 		case 107:	/* Get flags */
2034 			ND_PRINT(" trans");
2035 			UINTOUT();
2036 			break;
2037 		case 102:	/* Restore */
2038 			ND_PRINT(" totrans");
2039 			UINTOUT();
2040 			ND_PRINT(" flags");
2041 			UINTOUT();
2042 			break;
2043 		case 103:	/* Forward */
2044 			ND_PRINT(" fromtrans");
2045 			UINTOUT();
2046 			ND_PRINT(" fromdate");
2047 			DATEOUT();
2048 			DESTSERVEROUT();
2049 			ND_PRINT(" desttrans");
2050 			INTOUT();
2051 			break;
2052 		case 104:	/* End trans */
2053 			ND_PRINT(" trans");
2054 			UINTOUT();
2055 			break;
2056 		case 105:	/* Clone */
2057 			ND_PRINT(" trans");
2058 			UINTOUT();
2059 			ND_PRINT(" purgevol");
2060 			UINTOUT();
2061 			ND_PRINT(" newtype");
2062 			UINTOUT();
2063 			ND_PRINT(" newname");
2064 			STROUT(AFSNAMEMAX);
2065 			break;
2066 		case 106:	/* Set flags */
2067 			ND_PRINT(" trans");
2068 			UINTOUT();
2069 			ND_PRINT(" flags");
2070 			UINTOUT();
2071 			break;
2072 		case 108:	/* Trans create */
2073 			ND_PRINT(" vol");
2074 			UINTOUT();
2075 			ND_PRINT(" partition");
2076 			UINTOUT();
2077 			ND_PRINT(" flags");
2078 			UINTOUT();
2079 			break;
2080 		case 109:	/* Dump */
2081 		case 655537:	/* Get size */
2082 			ND_PRINT(" fromtrans");
2083 			UINTOUT();
2084 			ND_PRINT(" fromdate");
2085 			DATEOUT();
2086 			break;
2087 		case 110:	/* Get n-th volume */
2088 			ND_PRINT(" index");
2089 			UINTOUT();
2090 			break;
2091 		case 111:	/* Set forwarding */
2092 			ND_PRINT(" tid");
2093 			UINTOUT();
2094 			ND_PRINT(" newsite");
2095 			UINTOUT();
2096 			break;
2097 		case 112:	/* Get name */
2098 		case 113:	/* Get status */
2099 			ND_PRINT(" tid");
2100 			break;
2101 		case 114:	/* Signal restore */
2102 			ND_PRINT(" name");
2103 			STROUT(AFSNAMEMAX);
2104 			ND_PRINT(" type");
2105 			UINTOUT();
2106 			ND_PRINT(" pid");
2107 			UINTOUT();
2108 			ND_PRINT(" cloneid");
2109 			UINTOUT();
2110 			break;
2111 		case 116:	/* List volumes */
2112 			ND_PRINT(" partition");
2113 			UINTOUT();
2114 			ND_PRINT(" flags");
2115 			UINTOUT();
2116 			break;
2117 		case 117:	/* Set id types */
2118 			ND_PRINT(" tid");
2119 			UINTOUT();
2120 			ND_PRINT(" name");
2121 			STROUT(AFSNAMEMAX);
2122 			ND_PRINT(" type");
2123 			UINTOUT();
2124 			ND_PRINT(" pid");
2125 			UINTOUT();
2126 			ND_PRINT(" clone");
2127 			UINTOUT();
2128 			ND_PRINT(" backup");
2129 			UINTOUT();
2130 			break;
2131 		case 119:	/* Partition info */
2132 			ND_PRINT(" name");
2133 			STROUT(AFSNAMEMAX);
2134 			break;
2135 		case 120:	/* Reclone */
2136 			ND_PRINT(" tid");
2137 			UINTOUT();
2138 			break;
2139 		case 121:	/* List one volume */
2140 		case 122:	/* Nuke volume */
2141 		case 124:	/* Extended List volumes */
2142 		case 125:	/* Extended List one volume */
2143 		case 65536:	/* Convert RO to RW volume */
2144 			ND_PRINT(" partid");
2145 			UINTOUT();
2146 			ND_PRINT(" volid");
2147 			UINTOUT();
2148 			break;
2149 		case 123:	/* Set date */
2150 			ND_PRINT(" tid");
2151 			UINTOUT();
2152 			ND_PRINT(" date");
2153 			DATEOUT();
2154 			break;
2155 		case 126:	/* Set info */
2156 			ND_PRINT(" tid");
2157 			UINTOUT();
2158 			break;
2159 		case 128:	/* Forward multiple */
2160 			ND_PRINT(" fromtrans");
2161 			UINTOUT();
2162 			ND_PRINT(" fromdate");
2163 			DATEOUT();
2164 			{
2165 				uint32_t i, j;
2166 				j = GET_BE_U_4(bp);
2167 				bp += sizeof(uint32_t);
2168 				for (i = 0; i < j; i++) {
2169 					DESTSERVEROUT();
2170 					if (i != j - 1)
2171 						ND_PRINT(",");
2172 				}
2173 				if (j == 0)
2174 					ND_PRINT(" <none!>");
2175 			}
2176 			break;
2177 		case 65538:	/* Dump version 2 */
2178 			ND_PRINT(" fromtrans");
2179 			UINTOUT();
2180 			ND_PRINT(" fromdate");
2181 			DATEOUT();
2182 			ND_PRINT(" flags");
2183 			UINTOUT();
2184 			break;
2185 		default:
2186 			;
2187 	}
2188 	return;
2189 
2190 trunc:
2191 	ND_PRINT(" [|vol]");
2192 }
2193 
2194 /*
2195  * Handle replies to the AFS Volume Service
2196  */
2197 
2198 static void
2199 vol_reply_print(netdissect_options *ndo,
2200                 const u_char *bp, u_int length, uint32_t opcode)
2201 {
2202 	const struct rx_header *rxh;
2203 	uint8_t type;
2204 
2205 	if (length <= sizeof(struct rx_header))
2206 		return;
2207 
2208 	rxh = (const struct rx_header *) bp;
2209 
2210 	/*
2211 	 * Print out the afs call we're invoking.  The table used here was
2212 	 * gleaned from volser/volint.xg
2213 	 */
2214 
2215 	ND_PRINT(" vol reply %s", tok2str(vol_req, "op#%u", opcode));
2216 
2217 	type = GET_U_1(rxh->type);
2218 	bp += sizeof(struct rx_header);
2219 
2220 	/*
2221 	 * If it was a data packet, interpret the response.
2222 	 */
2223 
2224 	if (type == RX_PACKET_TYPE_DATA) {
2225 		switch (opcode) {
2226 			case 100:	/* Create volume */
2227 				ND_PRINT(" volid");
2228 				UINTOUT();
2229 				ND_PRINT(" trans");
2230 				UINTOUT();
2231 				break;
2232 			case 104:	/* End transaction */
2233 				UINTOUT();
2234 				break;
2235 			case 105:	/* Clone */
2236 				ND_PRINT(" newvol");
2237 				UINTOUT();
2238 				break;
2239 			case 107:	/* Get flags */
2240 				UINTOUT();
2241 				break;
2242 			case 108:	/* Transaction create */
2243 				ND_PRINT(" trans");
2244 				UINTOUT();
2245 				break;
2246 			case 110:	/* Get n-th volume */
2247 				ND_PRINT(" volume");
2248 				UINTOUT();
2249 				ND_PRINT(" partition");
2250 				UINTOUT();
2251 				break;
2252 			case 112:	/* Get name */
2253 				STROUT(AFSNAMEMAX);
2254 				break;
2255 			case 113:	/* Get status */
2256 				ND_PRINT(" volid");
2257 				UINTOUT();
2258 				ND_PRINT(" nextuniq");
2259 				UINTOUT();
2260 				ND_PRINT(" type");
2261 				UINTOUT();
2262 				ND_PRINT(" parentid");
2263 				UINTOUT();
2264 				ND_PRINT(" clone");
2265 				UINTOUT();
2266 				ND_PRINT(" backup");
2267 				UINTOUT();
2268 				ND_PRINT(" restore");
2269 				UINTOUT();
2270 				ND_PRINT(" maxquota");
2271 				UINTOUT();
2272 				ND_PRINT(" minquota");
2273 				UINTOUT();
2274 				ND_PRINT(" owner");
2275 				UINTOUT();
2276 				ND_PRINT(" create");
2277 				DATEOUT();
2278 				ND_PRINT(" access");
2279 				DATEOUT();
2280 				ND_PRINT(" update");
2281 				DATEOUT();
2282 				ND_PRINT(" expire");
2283 				DATEOUT();
2284 				ND_PRINT(" backup");
2285 				DATEOUT();
2286 				ND_PRINT(" copy");
2287 				DATEOUT();
2288 				break;
2289 			case 115:	/* Old list partitions */
2290 				break;
2291 			case 116:	/* List volumes */
2292 			case 121:	/* List one volume */
2293 				{
2294 					uint32_t i, j;
2295 					j = GET_BE_U_4(bp);
2296 					bp += sizeof(uint32_t);
2297 					for (i = 0; i < j; i++) {
2298 						ND_PRINT(" name");
2299 						VECOUT(32);
2300 						ND_PRINT(" volid");
2301 						UINTOUT();
2302 						ND_PRINT(" type");
2303 						bp += sizeof(uint32_t) * 21;
2304 						if (i != j - 1)
2305 							ND_PRINT(",");
2306 					}
2307 					if (j == 0)
2308 						ND_PRINT(" <none!>");
2309 				}
2310 				break;
2311 
2312 
2313 			default:
2314 				;
2315 		}
2316 	} else {
2317 		/*
2318 		 * Otherwise, just print out the return code
2319 		 */
2320 		ND_PRINT(" errcode");
2321 		INTOUT();
2322 	}
2323 
2324 	return;
2325 
2326 trunc:
2327 	ND_PRINT(" [|vol]");
2328 }
2329 
2330 /*
2331  * Handle calls to the AFS BOS service
2332  */
2333 
2334 static void
2335 bos_print(netdissect_options *ndo,
2336           const u_char *bp, u_int length)
2337 {
2338 	uint32_t bos_op;
2339 
2340 	if (length <= sizeof(struct rx_header))
2341 		return;
2342 
2343 	/*
2344 	 * Print out the afs call we're invoking.  The table used here was
2345 	 * gleaned from bozo/bosint.xg
2346 	 */
2347 
2348 	bos_op = GET_BE_U_4(bp + sizeof(struct rx_header));
2349 
2350 	ND_PRINT(" bos call %s", tok2str(bos_req, "op#%u", bos_op));
2351 
2352 	/*
2353 	 * Decode some of the arguments to the BOS calls
2354 	 */
2355 
2356 	bp += sizeof(struct rx_header) + 4;
2357 
2358 	switch (bos_op) {
2359 		case 80:	/* Create B node */
2360 			ND_PRINT(" type");
2361 			STROUT(BOSNAMEMAX);
2362 			ND_PRINT(" instance");
2363 			STROUT(BOSNAMEMAX);
2364 			break;
2365 		case 81:	/* Delete B node */
2366 		case 83:	/* Get status */
2367 		case 85:	/* Get instance info */
2368 		case 87:	/* Add super user */
2369 		case 88:	/* Delete super user */
2370 		case 93:	/* Set cell name */
2371 		case 96:	/* Add cell host */
2372 		case 97:	/* Delete cell host */
2373 		case 104:	/* Restart */
2374 		case 106:	/* Uninstall */
2375 		case 108:	/* Exec */
2376 		case 112:	/* Getlog */
2377 		case 114:	/* Get instance strings */
2378 			STROUT(BOSNAMEMAX);
2379 			break;
2380 		case 82:	/* Set status */
2381 		case 98:	/* Set T status */
2382 			STROUT(BOSNAMEMAX);
2383 			ND_PRINT(" status");
2384 			INTOUT();
2385 			break;
2386 		case 86:	/* Get instance parm */
2387 			STROUT(BOSNAMEMAX);
2388 			ND_PRINT(" num");
2389 			INTOUT();
2390 			break;
2391 		case 84:	/* Enumerate instance */
2392 		case 89:	/* List super users */
2393 		case 90:	/* List keys */
2394 		case 91:	/* Add key */
2395 		case 92:	/* Delete key */
2396 		case 95:	/* Get cell host */
2397 			INTOUT();
2398 			break;
2399 		case 105:	/* Install */
2400 			STROUT(BOSNAMEMAX);
2401 			ND_PRINT(" size");
2402 			INTOUT();
2403 			ND_PRINT(" flags");
2404 			INTOUT();
2405 			ND_PRINT(" date");
2406 			INTOUT();
2407 			break;
2408 		default:
2409 			;
2410 	}
2411 
2412 	return;
2413 
2414 trunc:
2415 	ND_PRINT(" [|bos]");
2416 }
2417 
2418 /*
2419  * Handle replies to the AFS BOS Service
2420  */
2421 
2422 static void
2423 bos_reply_print(netdissect_options *ndo,
2424                 const u_char *bp, u_int length, uint32_t opcode)
2425 {
2426 	const struct rx_header *rxh;
2427 	uint8_t type;
2428 
2429 	if (length <= sizeof(struct rx_header))
2430 		return;
2431 
2432 	rxh = (const struct rx_header *) bp;
2433 
2434 	/*
2435 	 * Print out the afs call we're invoking.  The table used here was
2436 	 * gleaned from volser/volint.xg
2437 	 */
2438 
2439 	ND_PRINT(" bos reply %s", tok2str(bos_req, "op#%u", opcode));
2440 
2441 	type = GET_U_1(rxh->type);
2442 	bp += sizeof(struct rx_header);
2443 
2444 	/*
2445 	 * If it was a data packet, interpret the response.
2446 	 */
2447 
2448 	if (type == RX_PACKET_TYPE_DATA)
2449 		/* Well, no, not really.  Leave this for later */
2450 		;
2451 	else {
2452 		/*
2453 		 * Otherwise, just print out the return code
2454 		 */
2455 		ND_PRINT(" errcode");
2456 		INTOUT();
2457 	}
2458 }
2459 
2460 /*
2461  * Check to see if this is a Ubik opcode.
2462  */
2463 
2464 static int
2465 is_ubik(uint32_t opcode)
2466 {
2467 	if ((opcode >= VOTE_LOW && opcode <= VOTE_HIGH) ||
2468 	    (opcode >= DISK_LOW && opcode <= DISK_HIGH))
2469 		return(1);
2470 	else
2471 		return(0);
2472 }
2473 
2474 /*
2475  * Handle Ubik opcodes to any one of the replicated database services
2476  */
2477 
2478 static void
2479 ubik_print(netdissect_options *ndo,
2480            const u_char *bp)
2481 {
2482 	uint32_t ubik_op;
2483 	uint32_t temp;
2484 
2485 	/*
2486 	 * Print out the afs call we're invoking.  The table used here was
2487 	 * gleaned from ubik/ubik_int.xg
2488 	 */
2489 
2490 	/* Every function that calls this function first makes a bounds check
2491 	 * for (sizeof(rx_header) + 4) bytes, so long as it remains this way
2492 	 * the line below will not over-read.
2493 	 */
2494 	ubik_op = GET_BE_U_4(bp + sizeof(struct rx_header));
2495 
2496 	ND_PRINT(" ubik call %s", tok2str(ubik_req, "op#%u", ubik_op));
2497 
2498 	/*
2499 	 * Decode some of the arguments to the Ubik calls
2500 	 */
2501 
2502 	bp += sizeof(struct rx_header) + 4;
2503 
2504 	switch (ubik_op) {
2505 		case 10000:		/* Beacon */
2506 			temp = GET_BE_U_4(bp);
2507 			bp += sizeof(uint32_t);
2508 			ND_PRINT(" syncsite %s", temp ? "yes" : "no");
2509 			ND_PRINT(" votestart");
2510 			DATEOUT();
2511 			ND_PRINT(" dbversion");
2512 			UBIK_VERSIONOUT();
2513 			ND_PRINT(" tid");
2514 			UBIK_VERSIONOUT();
2515 			break;
2516 		case 10003:		/* Get sync site */
2517 			ND_PRINT(" site");
2518 			UINTOUT();
2519 			break;
2520 		case 20000:		/* Begin */
2521 		case 20001:		/* Commit */
2522 		case 20007:		/* Abort */
2523 		case 20008:		/* Release locks */
2524 		case 20010:		/* Writev */
2525 			ND_PRINT(" tid");
2526 			UBIK_VERSIONOUT();
2527 			break;
2528 		case 20002:		/* Lock */
2529 			ND_PRINT(" tid");
2530 			UBIK_VERSIONOUT();
2531 			ND_PRINT(" file");
2532 			INTOUT();
2533 			ND_PRINT(" pos");
2534 			INTOUT();
2535 			ND_PRINT(" length");
2536 			INTOUT();
2537 			temp = GET_BE_U_4(bp);
2538 			bp += sizeof(uint32_t);
2539 			tok2str(ubik_lock_types, "type %u", temp);
2540 			break;
2541 		case 20003:		/* Write */
2542 			ND_PRINT(" tid");
2543 			UBIK_VERSIONOUT();
2544 			ND_PRINT(" file");
2545 			INTOUT();
2546 			ND_PRINT(" pos");
2547 			INTOUT();
2548 			break;
2549 		case 20005:		/* Get file */
2550 			ND_PRINT(" file");
2551 			INTOUT();
2552 			break;
2553 		case 20006:		/* Send file */
2554 			ND_PRINT(" file");
2555 			INTOUT();
2556 			ND_PRINT(" length");
2557 			INTOUT();
2558 			ND_PRINT(" dbversion");
2559 			UBIK_VERSIONOUT();
2560 			break;
2561 		case 20009:		/* Truncate */
2562 			ND_PRINT(" tid");
2563 			UBIK_VERSIONOUT();
2564 			ND_PRINT(" file");
2565 			INTOUT();
2566 			ND_PRINT(" length");
2567 			INTOUT();
2568 			break;
2569 		case 20012:		/* Set version */
2570 			ND_PRINT(" tid");
2571 			UBIK_VERSIONOUT();
2572 			ND_PRINT(" oldversion");
2573 			UBIK_VERSIONOUT();
2574 			ND_PRINT(" newversion");
2575 			UBIK_VERSIONOUT();
2576 			break;
2577 		default:
2578 			;
2579 	}
2580 
2581 	return;
2582 
2583 trunc:
2584 	ND_PRINT(" [|ubik]");
2585 }
2586 
2587 /*
2588  * Handle Ubik replies to any one of the replicated database services
2589  */
2590 
2591 static void
2592 ubik_reply_print(netdissect_options *ndo,
2593                  const u_char *bp, u_int length, uint32_t opcode)
2594 {
2595 	const struct rx_header *rxh;
2596 	uint8_t type;
2597 
2598 	if (length < sizeof(struct rx_header))
2599 		return;
2600 
2601 	rxh = (const struct rx_header *) bp;
2602 
2603 	/*
2604 	 * Print out the ubik call we're invoking.  This table was gleaned
2605 	 * from ubik/ubik_int.xg
2606 	 */
2607 
2608 	ND_PRINT(" ubik reply %s", tok2str(ubik_req, "op#%u", opcode));
2609 
2610 	type = GET_U_1(rxh->type);
2611 	bp += sizeof(struct rx_header);
2612 
2613 	/*
2614 	 * If it was a data packet, print out the arguments to the Ubik calls
2615 	 */
2616 
2617 	if (type == RX_PACKET_TYPE_DATA)
2618 		switch (opcode) {
2619 		case 10000:		/* Beacon */
2620 			ND_PRINT(" vote no");
2621 			break;
2622 		case 20004:		/* Get version */
2623 			ND_PRINT(" dbversion");
2624 			UBIK_VERSIONOUT();
2625 			break;
2626 		default:
2627 			;
2628 		}
2629 
2630 	/*
2631 	 * Otherwise, print out "yes" if it was a beacon packet (because
2632 	 * that's how yes votes are returned, go figure), otherwise
2633 	 * just print out the error code.
2634 	 */
2635 
2636 	else
2637 		switch (opcode) {
2638 		case 10000:		/* Beacon */
2639 			ND_PRINT(" vote yes until");
2640 			DATEOUT();
2641 			break;
2642 		default:
2643 			ND_PRINT(" errcode");
2644 			INTOUT();
2645 		}
2646 
2647 	return;
2648 
2649 trunc:
2650 	ND_PRINT(" [|ubik]");
2651 }
2652 
2653 /*
2654  * Handle RX ACK packets.
2655  */
2656 
2657 static void
2658 rx_ack_print(netdissect_options *ndo,
2659              const u_char *bp, u_int length)
2660 {
2661 	const struct rx_ackPacket *rxa;
2662 	uint8_t nAcks;
2663 	int i, start, last;
2664 	uint32_t firstPacket;
2665 
2666 	if (length < sizeof(struct rx_header))
2667 		return;
2668 
2669 	bp += sizeof(struct rx_header);
2670 
2671 	ND_TCHECK_LEN(bp, sizeof(struct rx_ackPacket));
2672 
2673 	rxa = (const struct rx_ackPacket *) bp;
2674 	bp += sizeof(struct rx_ackPacket);
2675 
2676 	/*
2677 	 * Print out a few useful things from the ack packet structure
2678 	 */
2679 
2680 	if (ndo->ndo_vflag > 2)
2681 		ND_PRINT(" bufspace %u maxskew %u",
2682 		       GET_BE_U_2(rxa->bufferSpace),
2683 		       GET_BE_U_2(rxa->maxSkew));
2684 
2685 	firstPacket = GET_BE_U_4(rxa->firstPacket);
2686 	ND_PRINT(" first %u serial %u reason %s",
2687 	       firstPacket, GET_BE_U_4(rxa->serial),
2688 	       tok2str(rx_ack_reasons, "#%u", GET_U_1(rxa->reason)));
2689 
2690 	/*
2691 	 * Okay, now we print out the ack array.  The way _this_ works
2692 	 * is that we start at "first", and step through the ack array.
2693 	 * If we have a contiguous range of acks/nacks, try to
2694 	 * collapse them into a range.
2695 	 *
2696 	 * If you're really clever, you might have noticed that this
2697 	 * doesn't seem quite correct.  Specifically, due to structure
2698 	 * padding, sizeof(struct rx_ackPacket) - RX_MAXACKS won't actually
2699 	 * yield the start of the ack array (because RX_MAXACKS is 255
2700 	 * and the structure will likely get padded to a 2 or 4 byte
2701 	 * boundary).  However, this is the way it's implemented inside
2702 	 * of AFS - the start of the extra fields are at
2703 	 * sizeof(struct rx_ackPacket) - RX_MAXACKS + nAcks, which _isn't_
2704 	 * the exact start of the ack array.  Sigh.  That's why we aren't
2705 	 * using bp, but instead use rxa->acks[].  But nAcks gets added
2706 	 * to bp after this, so bp ends up at the right spot.  Go figure.
2707 	 */
2708 
2709 	nAcks = GET_U_1(rxa->nAcks);
2710 	if (nAcks != 0) {
2711 
2712 		ND_TCHECK_LEN(bp, nAcks);
2713 
2714 		/*
2715 		 * Sigh, this is gross, but it seems to work to collapse
2716 		 * ranges correctly.
2717 		 */
2718 
2719 		for (i = 0, start = last = -2; i < nAcks; i++)
2720 			if (GET_U_1(bp + i) == RX_ACK_TYPE_ACK) {
2721 
2722 				/*
2723 				 * I figured this deserved _some_ explanation.
2724 				 * First, print "acked" and the packet seq
2725 				 * number if this is the first time we've
2726 				 * seen an acked packet.
2727 				 */
2728 
2729 				if (last == -2) {
2730 					ND_PRINT(" acked %u", firstPacket + i);
2731 					start = i;
2732 				}
2733 
2734 				/*
2735 				 * Otherwise, if there is a skip in
2736 				 * the range (such as an nacked packet in
2737 				 * the middle of some acked packets),
2738 				 * then print the current packet number
2739 				 * separated from the last number by
2740 				 * a comma.
2741 				 */
2742 
2743 				else if (last != i - 1) {
2744 					ND_PRINT(",%u", firstPacket + i);
2745 					start = i;
2746 				}
2747 
2748 				/*
2749 				 * We always set last to the value of
2750 				 * the last ack we saw.  Conversely, start
2751 				 * is set to the value of the first ack
2752 				 * we saw in a range.
2753 				 */
2754 
2755 				last = i;
2756 
2757 				/*
2758 				 * Okay, this bit a code gets executed when
2759 				 * we hit a nack ... in _this_ case we
2760 				 * want to print out the range of packets
2761 				 * that were acked, so we need to print
2762 				 * the _previous_ packet number separated
2763 				 * from the first by a dash (-).  Since we
2764 				 * already printed the first packet above,
2765 				 * just print the final packet.  Don't
2766 				 * do this if there will be a single-length
2767 				 * range.
2768 				 */
2769 			} else if (last == i - 1 && start != last)
2770 				ND_PRINT("-%u", firstPacket + i - 1);
2771 
2772 		/*
2773 		 * So, what's going on here?  We ran off the end of the
2774 		 * ack list, and if we got a range we need to finish it up.
2775 		 * So we need to determine if the last packet in the list
2776 		 * was an ack (if so, then last will be set to it) and
2777 		 * we need to see if the last range didn't start with the
2778 		 * last packet (because if it _did_, then that would mean
2779 		 * that the packet number has already been printed and
2780 		 * we don't need to print it again).
2781 		 */
2782 
2783 		if (last == i - 1 && start != last)
2784 			ND_PRINT("-%u", firstPacket + i - 1);
2785 
2786 		/*
2787 		 * Same as above, just without comments
2788 		 */
2789 
2790 		for (i = 0, start = last = -2; i < nAcks; i++)
2791 			if (GET_U_1(bp + i) == RX_ACK_TYPE_NACK) {
2792 				if (last == -2) {
2793 					ND_PRINT(" nacked %u", firstPacket + i);
2794 					start = i;
2795 				} else if (last != i - 1) {
2796 					ND_PRINT(",%u", firstPacket + i);
2797 					start = i;
2798 				}
2799 				last = i;
2800 			} else if (last == i - 1 && start != last)
2801 				ND_PRINT("-%u", firstPacket + i - 1);
2802 
2803 		if (last == i - 1 && start != last)
2804 			ND_PRINT("-%u", firstPacket + i - 1);
2805 
2806 		bp += nAcks;
2807 	}
2808 
2809 	/* Padding. */
2810 	bp += 3;
2811 
2812 	/*
2813 	 * These are optional fields; depending on your version of AFS,
2814 	 * you may or may not see them
2815 	 */
2816 
2817 #define TRUNCRET(n)	if (ndo->ndo_snapend - bp + 1 <= n) return;
2818 
2819 	if (ndo->ndo_vflag > 1) {
2820 		TRUNCRET(4);
2821 		ND_PRINT(" ifmtu");
2822 		UINTOUT();
2823 
2824 		TRUNCRET(4);
2825 		ND_PRINT(" maxmtu");
2826 		UINTOUT();
2827 
2828 		TRUNCRET(4);
2829 		ND_PRINT(" rwind");
2830 		UINTOUT();
2831 
2832 		TRUNCRET(4);
2833 		ND_PRINT(" maxpackets");
2834 		UINTOUT();
2835 	}
2836 
2837 	return;
2838 
2839 trunc:
2840 	ND_PRINT(" [|ack]");
2841 }
2842 #undef TRUNCRET
2843