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