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