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