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