]> The Tcpdump Group git mirrors - tcpdump/blob - print-atalk.c
Merge branch 'master' into master
[tcpdump] / print-atalk.c
1 /*
2 * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22 /* \summary: AppleTalk printer */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <netdissect-stdinc.h>
29
30 #include <stdio.h>
31 #include <string.h>
32
33 #include "netdissect.h"
34 #include "addrtoname.h"
35 #include "ethertype.h"
36 #include "extract.h"
37 #include "appletalk.h"
38
39 static const char tstr[] = "[|atalk]";
40
41 static const struct tok type2str[] = {
42 { ddpRTMP, "rtmp" },
43 { ddpRTMPrequest, "rtmpReq" },
44 { ddpECHO, "echo" },
45 { ddpIP, "IP" },
46 { ddpARP, "ARP" },
47 { ddpKLAP, "KLAP" },
48 { 0, NULL }
49 };
50
51 struct aarp {
52 nd_uint16_t htype, ptype;
53 nd_uint8_t halen, palen;
54 nd_uint16_t op;
55 uint8_t hsaddr[6];
56 uint8_t psaddr[4];
57 uint8_t hdaddr[6];
58 uint8_t pdaddr[4];
59 };
60
61 static void atp_print(netdissect_options *, const struct atATP *, u_int);
62 static void atp_bitmap_print(netdissect_options *, u_char);
63 static void nbp_print(netdissect_options *, const struct atNBP *, u_int, u_short, u_char, u_char);
64 static const struct atNBPtuple *nbp_tuple_print(netdissect_options *ndo, const struct atNBPtuple *,
65 const u_char *,
66 u_short, u_char, u_char);
67 static const struct atNBPtuple *nbp_name_print(netdissect_options *, const struct atNBPtuple *,
68 const u_char *);
69 static const char *ataddr_string(netdissect_options *, u_short, u_char);
70 static void ddp_print(netdissect_options *, const u_char *, u_int, int, u_short, u_char, u_char);
71 static const char *ddpskt_string(netdissect_options *, int);
72
73 /*
74 * Print LLAP packets received on a physical LocalTalk interface.
75 */
76 u_int
77 ltalk_if_print(netdissect_options *ndo,
78 const struct pcap_pkthdr *h, const u_char *p)
79 {
80 u_int hdrlen;
81
82 hdrlen = llap_print(ndo, p, h->len);
83 if (hdrlen == 0) {
84 /* Cut short by the snapshot length. */
85 return (h->caplen);
86 }
87 return (hdrlen);
88 }
89
90 /*
91 * Print AppleTalk LLAP packets.
92 */
93 u_int
94 llap_print(netdissect_options *ndo,
95 const u_char *bp, u_int length)
96 {
97 const struct LAP *lp;
98 const struct atDDP *dp;
99 const struct atShortDDP *sdp;
100 u_short snet;
101 u_int hdrlen;
102
103 if (length < sizeof(*lp)) {
104 ND_PRINT((ndo, " [|llap %u]", length));
105 return (length);
106 }
107 if (!ND_TTEST_LEN(bp, sizeof(*lp))) {
108 ND_PRINT((ndo, " [|llap]"));
109 return (0); /* cut short by the snapshot length */
110 }
111 lp = (const struct LAP *)bp;
112 bp += sizeof(*lp);
113 length -= sizeof(*lp);
114 hdrlen = sizeof(*lp);
115 switch (EXTRACT_U_1(lp->type)) {
116
117 case lapShortDDP:
118 if (length < ddpSSize) {
119 ND_PRINT((ndo, " [|sddp %u]", length));
120 return (length);
121 }
122 if (!ND_TTEST_LEN(bp, ddpSSize)) {
123 ND_PRINT((ndo, " [|sddp]"));
124 return (0); /* cut short by the snapshot length */
125 }
126 sdp = (const struct atShortDDP *)bp;
127 ND_PRINT((ndo, "%s.%s",
128 ataddr_string(ndo, 0, EXTRACT_U_1(lp->src)), ddpskt_string(ndo, EXTRACT_U_1(sdp->srcSkt))));
129 ND_PRINT((ndo, " > %s.%s:",
130 ataddr_string(ndo, 0, EXTRACT_U_1(lp->dst)), ddpskt_string(ndo, EXTRACT_U_1(sdp->dstSkt))));
131 bp += ddpSSize;
132 length -= ddpSSize;
133 hdrlen += ddpSSize;
134 ddp_print(ndo, bp, length, EXTRACT_U_1(sdp->type), 0, EXTRACT_U_1(lp->src), EXTRACT_U_1(sdp->srcSkt));
135 break;
136
137 case lapDDP:
138 if (length < ddpSize) {
139 ND_PRINT((ndo, " [|ddp %u]", length));
140 return (length);
141 }
142 if (!ND_TTEST_LEN(bp, ddpSize)) {
143 ND_PRINT((ndo, " [|ddp]"));
144 return (0); /* cut short by the snapshot length */
145 }
146 dp = (const struct atDDP *)bp;
147 snet = EXTRACT_BE_U_2(dp->srcNet);
148 ND_PRINT((ndo, "%s.%s", ataddr_string(ndo, snet, EXTRACT_U_1(dp->srcNode)),
149 ddpskt_string(ndo, EXTRACT_U_1(dp->srcSkt))));
150 ND_PRINT((ndo, " > %s.%s:",
151 ataddr_string(ndo, EXTRACT_BE_U_2(dp->dstNet), EXTRACT_U_1(dp->dstNode)),
152 ddpskt_string(ndo, EXTRACT_U_1(dp->dstSkt))));
153 bp += ddpSize;
154 length -= ddpSize;
155 hdrlen += ddpSize;
156 ddp_print(ndo, bp, length, EXTRACT_U_1(dp->type), snet, EXTRACT_U_1(dp->srcNode), EXTRACT_U_1(dp->srcSkt));
157 break;
158
159 #ifdef notdef
160 case lapKLAP:
161 klap_print(bp, length);
162 break;
163 #endif
164
165 default:
166 ND_PRINT((ndo, "%u > %u at-lap#%u %u",
167 EXTRACT_U_1(lp->src), EXTRACT_U_1(lp->dst), EXTRACT_U_1(lp->type), length));
168 break;
169 }
170 return (hdrlen);
171 }
172
173 /*
174 * Print EtherTalk/TokenTalk packets (or FDDITalk, or whatever it's called
175 * when it runs over FDDI; yes, I've seen FDDI captures with AppleTalk
176 * packets in them).
177 */
178 void
179 atalk_print(netdissect_options *ndo,
180 const u_char *bp, u_int length)
181 {
182 const struct atDDP *dp;
183 u_short snet;
184
185 if(!ndo->ndo_eflag)
186 ND_PRINT((ndo, "AT "));
187
188 if (length < ddpSize) {
189 ND_PRINT((ndo, " [|ddp %u]", length));
190 return;
191 }
192 if (!ND_TTEST_LEN(bp, ddpSize)) {
193 ND_PRINT((ndo, " [|ddp]"));
194 return;
195 }
196 dp = (const struct atDDP *)bp;
197 snet = EXTRACT_BE_U_2(dp->srcNet);
198 ND_PRINT((ndo, "%s.%s", ataddr_string(ndo, snet, EXTRACT_U_1(dp->srcNode)),
199 ddpskt_string(ndo, EXTRACT_U_1(dp->srcSkt))));
200 ND_PRINT((ndo, " > %s.%s: ",
201 ataddr_string(ndo, EXTRACT_BE_U_2(dp->dstNet), EXTRACT_U_1(dp->dstNode)),
202 ddpskt_string(ndo, EXTRACT_U_1(dp->dstSkt))));
203 bp += ddpSize;
204 length -= ddpSize;
205 ddp_print(ndo, bp, length, EXTRACT_U_1(dp->type), snet, EXTRACT_U_1(dp->srcNode), EXTRACT_U_1(dp->srcSkt));
206 }
207
208 /* XXX should probably pass in the snap header and do checks like arp_print() */
209 void
210 aarp_print(netdissect_options *ndo,
211 const u_char *bp, u_int length)
212 {
213 const struct aarp *ap;
214
215 #define AT(member) ataddr_string(ndo, (ap->member[1]<<8)|ap->member[2],ap->member[3])
216
217 ND_PRINT((ndo, "aarp "));
218 ap = (const struct aarp *)bp;
219 if (!ND_TTEST(*ap)) {
220 /* Just bail if we don't have the whole chunk. */
221 ND_PRINT((ndo, " [|aarp]"));
222 return;
223 }
224 if (length < sizeof(*ap)) {
225 ND_PRINT((ndo, " [|aarp %u]", length));
226 return;
227 }
228 if (EXTRACT_BE_U_2(ap->htype) == 1 &&
229 EXTRACT_BE_U_2(ap->ptype) == ETHERTYPE_ATALK &&
230 EXTRACT_U_1(ap->halen) == 6 && EXTRACT_U_1(ap->palen) == 4 )
231 switch (EXTRACT_BE_U_2(ap->op)) {
232
233 case 1: /* request */
234 ND_PRINT((ndo, "who-has %s tell %s", AT(pdaddr), AT(psaddr)));
235 return;
236
237 case 2: /* response */
238 ND_PRINT((ndo, "reply %s is-at %s", AT(psaddr), etheraddr_string(ndo, ap->hsaddr)));
239 return;
240
241 case 3: /* probe (oy!) */
242 ND_PRINT((ndo, "probe %s tell %s", AT(pdaddr), AT(psaddr)));
243 return;
244 }
245 ND_PRINT((ndo, "len %u op %u htype %u ptype %#x halen %u palen %u",
246 length, EXTRACT_BE_U_2(ap->op), EXTRACT_BE_U_2(ap->htype),
247 EXTRACT_BE_U_2(ap->ptype), EXTRACT_U_1(ap->halen), EXTRACT_U_1(ap->palen)));
248 }
249
250 /*
251 * Print AppleTalk Datagram Delivery Protocol packets.
252 */
253 static void
254 ddp_print(netdissect_options *ndo,
255 const u_char *bp, u_int length, int t,
256 u_short snet, u_char snode, u_char skt)
257 {
258
259 switch (t) {
260
261 case ddpNBP:
262 nbp_print(ndo, (const struct atNBP *)bp, length, snet, snode, skt);
263 break;
264
265 case ddpATP:
266 atp_print(ndo, (const struct atATP *)bp, length);
267 break;
268
269 case ddpEIGRP:
270 eigrp_print(ndo, bp, length);
271 break;
272
273 default:
274 ND_PRINT((ndo, " at-%s %d", tok2str(type2str, NULL, t), length));
275 break;
276 }
277 }
278
279 static void
280 atp_print(netdissect_options *ndo,
281 const struct atATP *ap, u_int length)
282 {
283 uint8_t control;
284 uint32_t data;
285
286 if ((const u_char *)(ap + 1) > ndo->ndo_snapend) {
287 /* Just bail if we don't have the whole chunk. */
288 ND_PRINT((ndo, "%s", tstr));
289 return;
290 }
291 if (length < sizeof(*ap)) {
292 ND_PRINT((ndo, " [|atp %u]", length));
293 return;
294 }
295 length -= sizeof(*ap);
296 control = EXTRACT_U_1(ap->control);
297 switch (control & 0xc0) {
298
299 case atpReqCode:
300 ND_PRINT((ndo, " atp-req%s %u",
301 control & atpXO? " " : "*",
302 EXTRACT_BE_U_2(ap->transID)));
303
304 atp_bitmap_print(ndo, EXTRACT_U_1(ap->bitmap));
305
306 if (length != 0)
307 ND_PRINT((ndo, " [len=%u]", length));
308
309 switch (control & (atpEOM|atpSTS)) {
310 case atpEOM:
311 ND_PRINT((ndo, " [EOM]"));
312 break;
313 case atpSTS:
314 ND_PRINT((ndo, " [STS]"));
315 break;
316 case atpEOM|atpSTS:
317 ND_PRINT((ndo, " [EOM,STS]"));
318 break;
319 }
320 break;
321
322 case atpRspCode:
323 ND_PRINT((ndo, " atp-resp%s%d:%u (%u)",
324 control & atpEOM? "*" : " ",
325 EXTRACT_BE_U_2(ap->transID), EXTRACT_U_1(ap->bitmap), length));
326 switch (control & (atpXO|atpSTS)) {
327 case atpXO:
328 ND_PRINT((ndo, " [XO]"));
329 break;
330 case atpSTS:
331 ND_PRINT((ndo, " [STS]"));
332 break;
333 case atpXO|atpSTS:
334 ND_PRINT((ndo, " [XO,STS]"));
335 break;
336 }
337 break;
338
339 case atpRelCode:
340 ND_PRINT((ndo, " atp-rel %u", EXTRACT_BE_U_2(ap->transID)));
341
342 atp_bitmap_print(ndo, EXTRACT_U_1(ap->bitmap));
343
344 /* length should be zero */
345 if (length)
346 ND_PRINT((ndo, " [len=%u]", length));
347
348 /* there shouldn't be any control flags */
349 if (control & (atpXO|atpEOM|atpSTS)) {
350 char c = '[';
351 if (control & atpXO) {
352 ND_PRINT((ndo, "%cXO", c));
353 c = ',';
354 }
355 if (control & atpEOM) {
356 ND_PRINT((ndo, "%cEOM", c));
357 c = ',';
358 }
359 if (control & atpSTS) {
360 ND_PRINT((ndo, "%cSTS", c));
361 }
362 ND_PRINT((ndo, "]"));
363 }
364 break;
365
366 default:
367 ND_PRINT((ndo, " atp-0x%x %d (%u)", control,
368 EXTRACT_BE_U_2(ap->transID), length));
369 break;
370 }
371 data = EXTRACT_BE_U_4(ap->userData);
372 if (data != 0)
373 ND_PRINT((ndo, " 0x%x", data));
374 }
375
376 static void
377 atp_bitmap_print(netdissect_options *ndo,
378 u_char bm)
379 {
380 int i;
381
382 /*
383 * The '& 0xff' below is needed for compilers that want to sign
384 * extend a u_char, which is the case with the Ultrix compiler.
385 * (gcc is smart enough to eliminate it, at least on the Sparc).
386 */
387 if ((bm + 1) & (bm & 0xff)) {
388 char c = '<';
389 for (i = 0; bm; ++i) {
390 if (bm & 1) {
391 ND_PRINT((ndo, "%c%d", c, i));
392 c = ',';
393 }
394 bm >>= 1;
395 }
396 ND_PRINT((ndo, ">"));
397 } else {
398 for (i = 0; bm; ++i)
399 bm >>= 1;
400 if (i > 1)
401 ND_PRINT((ndo, "<0-%d>", i - 1));
402 else
403 ND_PRINT((ndo, "<0>"));
404 }
405 }
406
407 static void
408 nbp_print(netdissect_options *ndo,
409 const struct atNBP *np, u_int length, u_short snet,
410 u_char snode, u_char skt)
411 {
412 const struct atNBPtuple *tp =
413 (const struct atNBPtuple *)((const u_char *)np + nbpHeaderSize);
414 uint8_t control;
415 int i;
416 const u_char *ep;
417
418 if (length < nbpHeaderSize) {
419 ND_PRINT((ndo, " truncated-nbp %u", length));
420 return;
421 }
422
423 length -= nbpHeaderSize;
424 if (length < 8) {
425 /* must be room for at least one tuple */
426 ND_PRINT((ndo, " truncated-nbp %u", length + nbpHeaderSize));
427 return;
428 }
429 /* ep points to end of available data */
430 ep = ndo->ndo_snapend;
431 if ((const u_char *)tp > ep) {
432 ND_PRINT((ndo, "%s", tstr));
433 return;
434 }
435 control = EXTRACT_U_1(np->control);
436 switch (i = (control & 0xf0)) {
437
438 case nbpBrRq:
439 case nbpLkUp:
440 ND_PRINT((ndo, i == nbpLkUp? " nbp-lkup %u:":" nbp-brRq %u:", EXTRACT_U_1(np->id)));
441 if ((const u_char *)(tp + 1) > ep) {
442 ND_PRINT((ndo, "%s", tstr));
443 return;
444 }
445 (void)nbp_name_print(ndo, tp, ep);
446 /*
447 * look for anomalies: the spec says there can only
448 * be one tuple, the address must match the source
449 * address and the enumerator should be zero.
450 */
451 if ((control & 0xf) != 1)
452 ND_PRINT((ndo, " [ntup=%u]", control & 0xf));
453 if (EXTRACT_U_1(tp->enumerator))
454 ND_PRINT((ndo, " [enum=%u]", EXTRACT_U_1(tp->enumerator)));
455 if (EXTRACT_BE_U_2(tp->net) != snet ||
456 EXTRACT_U_1(tp->node) != snode ||
457 EXTRACT_U_1(tp->skt) != skt)
458 ND_PRINT((ndo, " [addr=%s.%d]",
459 ataddr_string(ndo, EXTRACT_BE_U_2(tp->net),
460 EXTRACT_U_1(tp->node)),
461 EXTRACT_U_1(tp->skt)));
462 break;
463
464 case nbpLkUpReply:
465 ND_PRINT((ndo, " nbp-reply %u:", EXTRACT_U_1(np->id)));
466
467 /* print each of the tuples in the reply */
468 for (i = control & 0xf; --i >= 0 && tp; )
469 tp = nbp_tuple_print(ndo, tp, ep, snet, snode, skt);
470 break;
471
472 default:
473 ND_PRINT((ndo, " nbp-0x%x %u (%u)", control, EXTRACT_U_1(np->id), length));
474 break;
475 }
476 }
477
478 /* print a counted string */
479 static const char *
480 print_cstring(netdissect_options *ndo,
481 const char *cp, const u_char *ep)
482 {
483 u_int length;
484
485 if (cp >= (const char *)ep) {
486 ND_PRINT((ndo, "%s", tstr));
487 return (0);
488 }
489 length = EXTRACT_U_1(cp);
490 cp++;
491
492 /* Spec says string can be at most 32 bytes long */
493 if (length > 32) {
494 ND_PRINT((ndo, "[len=%u]", length));
495 return (0);
496 }
497 while (length != 0) {
498 if (cp >= (const char *)ep) {
499 ND_PRINT((ndo, "%s", tstr));
500 return (0);
501 }
502 ND_PRINT((ndo, "%c", EXTRACT_U_1(cp)));
503 cp++;
504 length--;
505 }
506 return (cp);
507 }
508
509 static const struct atNBPtuple *
510 nbp_tuple_print(netdissect_options *ndo,
511 const struct atNBPtuple *tp, const u_char *ep,
512 u_short snet, u_char snode, u_char skt)
513 {
514 const struct atNBPtuple *tpn;
515
516 if ((const u_char *)(tp + 1) > ep) {
517 ND_PRINT((ndo, "%s", tstr));
518 return 0;
519 }
520 tpn = nbp_name_print(ndo, tp, ep);
521
522 /* if the enumerator isn't 1, print it */
523 if (EXTRACT_U_1(tp->enumerator) != 1)
524 ND_PRINT((ndo, "(%u)", EXTRACT_U_1(tp->enumerator)));
525
526 /* if the socket doesn't match the src socket, print it */
527 if (EXTRACT_U_1(tp->skt) != skt)
528 ND_PRINT((ndo, " %u", EXTRACT_U_1(tp->skt)));
529
530 /* if the address doesn't match the src address, it's an anomaly */
531 if (EXTRACT_BE_U_2(tp->net) != snet ||
532 EXTRACT_U_1(tp->node) != snode)
533 ND_PRINT((ndo, " [addr=%s]",
534 ataddr_string(ndo, EXTRACT_BE_U_2(tp->net), EXTRACT_U_1(tp->node))));
535
536 return (tpn);
537 }
538
539 static const struct atNBPtuple *
540 nbp_name_print(netdissect_options *ndo,
541 const struct atNBPtuple *tp, const u_char *ep)
542 {
543 const char *cp = (const char *)tp + nbpTupleSize;
544
545 ND_PRINT((ndo, " "));
546
547 /* Object */
548 ND_PRINT((ndo, "\""));
549 if ((cp = print_cstring(ndo, cp, ep)) != NULL) {
550 /* Type */
551 ND_PRINT((ndo, ":"));
552 if ((cp = print_cstring(ndo, cp, ep)) != NULL) {
553 /* Zone */
554 ND_PRINT((ndo, "@"));
555 if ((cp = print_cstring(ndo, cp, ep)) != NULL)
556 ND_PRINT((ndo, "\""));
557 }
558 }
559 return ((const struct atNBPtuple *)cp);
560 }
561
562
563 #define HASHNAMESIZE 4096
564
565 struct hnamemem {
566 int addr;
567 char *name;
568 struct hnamemem *nxt;
569 };
570
571 static struct hnamemem hnametable[HASHNAMESIZE];
572
573 static const char *
574 ataddr_string(netdissect_options *ndo,
575 u_short atnet, u_char athost)
576 {
577 struct hnamemem *tp, *tp2;
578 int i = (atnet << 8) | athost;
579 char nambuf[256+1];
580 static int first = 1;
581 FILE *fp;
582
583 /*
584 * if this is the first call, see if there's an AppleTalk
585 * number to name map file.
586 */
587 if (first && (first = 0, !ndo->ndo_nflag)
588 && (fp = fopen("/etc/atalk.names", "r"))) {
589 char line[256];
590 int i1, i2;
591
592 while (fgets(line, sizeof(line), fp)) {
593 if (line[0] == '\n' || line[0] == 0 || line[0] == '#')
594 continue;
595 if (sscanf(line, "%d.%d %256s", &i1, &i2, nambuf) == 3)
596 /* got a hostname. */
597 i2 |= (i1 << 8);
598 else if (sscanf(line, "%d %256s", &i1, nambuf) == 2)
599 /* got a net name */
600 i2 = (i1 << 8) | 255;
601 else
602 continue;
603
604 for (tp = &hnametable[i2 & (HASHNAMESIZE-1)];
605 tp->nxt; tp = tp->nxt)
606 ;
607 tp->addr = i2;
608 tp->nxt = newhnamemem(ndo);
609 tp->name = strdup(nambuf);
610 if (tp->name == NULL)
611 (*ndo->ndo_error)(ndo,
612 "ataddr_string: strdup(nambuf)");
613 }
614 fclose(fp);
615 }
616
617 for (tp = &hnametable[i & (HASHNAMESIZE-1)]; tp->nxt; tp = tp->nxt)
618 if (tp->addr == i)
619 return (tp->name);
620
621 /* didn't have the node name -- see if we've got the net name */
622 i |= 255;
623 for (tp2 = &hnametable[i & (HASHNAMESIZE-1)]; tp2->nxt; tp2 = tp2->nxt)
624 if (tp2->addr == i) {
625 tp->addr = (atnet << 8) | athost;
626 tp->nxt = newhnamemem(ndo);
627 (void)snprintf(nambuf, sizeof(nambuf), "%s.%d",
628 tp2->name, athost);
629 tp->name = strdup(nambuf);
630 if (tp->name == NULL)
631 (*ndo->ndo_error)(ndo,
632 "ataddr_string: strdup(nambuf)");
633 return (tp->name);
634 }
635
636 tp->addr = (atnet << 8) | athost;
637 tp->nxt = newhnamemem(ndo);
638 if (athost != 255)
639 (void)snprintf(nambuf, sizeof(nambuf), "%d.%d", atnet, athost);
640 else
641 (void)snprintf(nambuf, sizeof(nambuf), "%d", atnet);
642 tp->name = strdup(nambuf);
643 if (tp->name == NULL)
644 (*ndo->ndo_error)(ndo, "ataddr_string: strdup(nambuf)");
645
646 return (tp->name);
647 }
648
649 static const struct tok skt2str[] = {
650 { rtmpSkt, "rtmp" }, /* routing table maintenance */
651 { nbpSkt, "nis" }, /* name info socket */
652 { echoSkt, "echo" }, /* AppleTalk echo protocol */
653 { zipSkt, "zip" }, /* zone info protocol */
654 { 0, NULL }
655 };
656
657 static const char *
658 ddpskt_string(netdissect_options *ndo,
659 int skt)
660 {
661 static char buf[8];
662
663 if (ndo->ndo_nflag) {
664 (void)snprintf(buf, sizeof(buf), "%d", skt);
665 return (buf);
666 }
667 return (tok2str(skt2str, "%d", skt));
668 }