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