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