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