1 /* $OpenBSD: ndp.c,v 1.103 2021/03/02 05:34:20 jsg Exp $ */ 2 /* $KAME: ndp.c,v 1.101 2002/07/17 08:46:33 itojun Exp $ */ 3 4 /* 5 * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 /* 33 * Copyright (c) 1984, 1993 34 * The Regents of the University of California. All rights reserved. 35 * 36 * This code is derived from software contributed to Berkeley by 37 * Sun Microsystems, Inc. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 3. Neither the name of the University nor the names of its contributors 48 * may be used to endorse or promote products derived from this software 49 * without specific prior written permission. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 */ 63 64 /* 65 * Based on: 66 * "@(#) Copyright (c) 1984, 1993\n\ 67 * The Regents of the University of California. All rights reserved.\n"; 68 * 69 * "@(#)arp.c 8.2 (Berkeley) 1/2/94"; 70 */ 71 72 /* 73 * ndp - display, set, delete and flush neighbor cache 74 */ 75 76 77 #include <sys/ioctl.h> 78 #include <sys/socket.h> 79 #include <sys/sysctl.h> 80 #include <sys/time.h> 81 #include <sys/queue.h> 82 83 #include <net/if.h> 84 #include <net/if_dl.h> 85 #include <net/if_types.h> 86 #include <net/route.h> 87 88 #include <netinet/in.h> 89 90 #include <netinet/icmp6.h> 91 #include <netinet6/in6_var.h> 92 #include <netinet6/nd6.h> 93 94 #include <arpa/inet.h> 95 96 #include <stdio.h> 97 #include <errno.h> 98 #include <fcntl.h> 99 #include <netdb.h> 100 #include <stdlib.h> 101 #include <string.h> 102 #include <unistd.h> 103 #include <limits.h> 104 #include <err.h> 105 106 /* packing rule for routing socket */ 107 #define ROUNDUP(a) \ 108 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 109 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 110 111 static pid_t pid; 112 static int nflag; 113 static int tflag; 114 static int rtsock = -1; 115 static int repeat = 0; 116 117 char host_buf[NI_MAXHOST]; /* getnameinfo() */ 118 char ifix_buf[IFNAMSIZ]; /* if_indextoname() */ 119 120 int file(char *); 121 void getsocket(void); 122 int parse_host(const char *, struct sockaddr_in6 *); 123 int set(int, char **); 124 void get(const char *); 125 int delete(const char *); 126 void dump(struct sockaddr_in6 *, int); 127 static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int); 128 static char *ether_str(struct sockaddr_dl *); 129 int ndp_ether_aton(const char *, u_char *); 130 void usage(void); 131 int rtmsg(int); 132 int rtget(struct sockaddr_in6 **, struct sockaddr_dl **); 133 void ifinfo(const char *); 134 static char *sec2str(time_t); 135 static int rdomain; 136 137 int 138 main(int argc, char *argv[]) 139 { 140 int ch; 141 int mode = 0; 142 char *arg = NULL; 143 const char *errstr; 144 145 pid = getpid(); 146 rdomain = getrtable(); 147 while ((ch = getopt(argc, argv, "acd:f:i:nstA:V:")) != -1) { 148 switch (ch) { 149 case 'a': 150 case 'c': 151 case 'p': 152 case 'r': 153 case 'P': 154 case 's': 155 if (mode) { 156 usage(); 157 } 158 mode = ch; 159 arg = NULL; 160 break; 161 case 'd': 162 case 'f': 163 case 'i' : 164 if (mode) { 165 usage(); 166 } 167 mode = ch; 168 arg = optarg; 169 break; 170 case 'n': 171 nflag = 1; 172 break; 173 case 't': 174 tflag = 1; 175 break; 176 case 'A': 177 if (mode) { 178 usage(); 179 } 180 mode = 'a'; 181 repeat = strtonum(optarg, 1, INT_MAX, &errstr); 182 if (errstr) { 183 usage(); 184 } 185 break; 186 case 'V': 187 rdomain = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr); 188 if (errstr != NULL) { 189 warn("bad rdomain: %s", errstr); 190 usage(); 191 } 192 break; 193 default: 194 usage(); 195 } 196 } 197 argc -= optind; 198 argv += optind; 199 200 switch (mode) { 201 case 'a': 202 case 'c': 203 if (argc != 0) { 204 usage(); 205 } 206 dump(NULL, mode == 'c'); 207 break; 208 case 'd': 209 if (argc != 0) { 210 usage(); 211 } 212 delete(arg); 213 break; 214 case 'f': 215 if (argc != 0) 216 usage(); 217 file(arg); 218 break; 219 case 'i': 220 if (argc != 0) 221 usage(); 222 ifinfo(arg); 223 break; 224 case 's': 225 if (argc < 2 || argc > 4) 226 usage(); 227 exit(set(argc, argv) ? 1 : 0); 228 case 0: 229 if (argc != 1) { 230 usage(); 231 } 232 get(argv[0]); 233 break; 234 } 235 exit(0); 236 } 237 238 /* 239 * Process a file to set standard ndp entries 240 */ 241 int 242 file(char *name) 243 { 244 FILE *fp; 245 int i, retval; 246 char line[100], arg[5][50], *args[5]; 247 248 if ((fp = fopen(name, "r")) == NULL) { 249 err(1, "cannot open %s", name); 250 } 251 args[0] = &arg[0][0]; 252 args[1] = &arg[1][0]; 253 args[2] = &arg[2][0]; 254 args[3] = &arg[3][0]; 255 args[4] = &arg[4][0]; 256 retval = 0; 257 while (fgets(line, sizeof(line), fp) != NULL) { 258 i = sscanf(line, "%49s %49s %49s %49s %49s", 259 arg[0], arg[1], arg[2], arg[3], arg[4]); 260 if (i < 2) { 261 warnx("bad line: %s", line); 262 retval = 1; 263 continue; 264 } 265 if (set(i, args)) 266 retval = 1; 267 } 268 fclose(fp); 269 return (retval); 270 } 271 272 void 273 getsocket(void) 274 { 275 socklen_t len = sizeof(rdomain); 276 277 if (rtsock >= 0) 278 return; 279 rtsock = socket(AF_ROUTE, SOCK_RAW, 0); 280 if (rtsock == -1) 281 err(1, "routing socket"); 282 if (setsockopt(rtsock, AF_ROUTE, ROUTE_TABLEFILTER, &rdomain, len) == -1) 283 err(1, "ROUTE_TABLEFILTER"); 284 285 if (pledge("stdio dns", NULL) == -1) 286 err(1, "pledge"); 287 } 288 289 int 290 parse_host(const char *host, struct sockaddr_in6 *sin6) 291 { 292 struct addrinfo hints, *res; 293 int gai_error; 294 295 bzero(&hints, sizeof(hints)); 296 hints.ai_family = AF_INET6; 297 if (nflag) 298 hints.ai_flags = AI_NUMERICHOST; 299 300 gai_error = getaddrinfo(host, NULL, &hints, &res); 301 if (gai_error) { 302 warnx("%s: %s", host, gai_strerror(gai_error)); 303 return 1; 304 } 305 *sin6 = *(struct sockaddr_in6 *)res->ai_addr; 306 freeaddrinfo(res); 307 return 0; 308 } 309 310 struct sockaddr_in6 so_mask = {sizeof(so_mask), AF_INET6 }; 311 struct sockaddr_in6 blank_sin = {sizeof(blank_sin), AF_INET6 }, sin_m; 312 struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m; 313 struct sockaddr_dl ifp_m = { sizeof(ifp_m), AF_LINK }; 314 time_t expire_time; 315 int flags, found_entry; 316 struct { 317 struct rt_msghdr m_rtm; 318 char m_space[512]; 319 } m_rtmsg; 320 321 /* 322 * Set an individual neighbor cache entry 323 */ 324 int 325 set(int argc, char *argv[]) 326 { 327 struct sockaddr_in6 *sin = &sin_m; 328 struct sockaddr_dl *sdl; 329 struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); 330 u_char *ea; 331 const char *host = argv[0], *eaddr = argv[1]; 332 333 getsocket(); 334 argc -= 2; 335 argv += 2; 336 sdl_m = blank_sdl; 337 sin_m = blank_sin; 338 339 if (parse_host(host, sin)) 340 return 1; 341 ea = (u_char *)LLADDR(&sdl_m); 342 if (ndp_ether_aton(eaddr, ea) == 0) 343 sdl_m.sdl_alen = 6; 344 expire_time = 0; 345 flags = 0; 346 while (argc-- > 0) { 347 if (strncmp(argv[0], "temp", 4) == 0) { 348 struct timeval now; 349 350 gettimeofday(&now, 0); 351 expire_time = now.tv_sec + 20 * 60; 352 } else if (strncmp(argv[0], "proxy", 5) == 0) 353 flags |= RTF_ANNOUNCE; 354 argv++; 355 } 356 357 if (rtget(&sin, &sdl)) { 358 errx(1, "RTM_GET(%s) failed", host); 359 } 360 361 if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr) && 362 sin->sin6_scope_id == sin_m.sin6_scope_id) { 363 if (sdl->sdl_family == AF_LINK && 364 (rtm->rtm_flags & RTF_LLINFO) && 365 !(rtm->rtm_flags & RTF_GATEWAY)) { 366 switch (sdl->sdl_type) { 367 case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: 368 case IFT_ISO88024: case IFT_ISO88025: 369 goto overwrite; 370 } 371 } 372 /* 373 * IPv4 arp command retries with sin_other = SIN_PROXY here. 374 */ 375 warnx("set: cannot configure a new entry"); 376 return 1; 377 } 378 379 overwrite: 380 if (sdl->sdl_family != AF_LINK) { 381 printf("cannot intuit interface index and type for %s\n", host); 382 return (1); 383 } 384 sdl_m.sdl_type = sdl->sdl_type; 385 sdl_m.sdl_index = sdl->sdl_index; 386 return (rtmsg(RTM_ADD)); 387 } 388 389 /* 390 * Display an individual neighbor cache entry 391 */ 392 void 393 get(const char *host) 394 { 395 struct sockaddr_in6 *sin = &sin_m; 396 397 sin_m = blank_sin; 398 if (parse_host(host, sin)) 399 return; 400 401 dump(sin, 0); 402 if (found_entry == 0) { 403 getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, 404 sizeof(host_buf), NULL ,0, 405 (nflag ? NI_NUMERICHOST : 0)); 406 printf("%s (%s) -- no entry\n", host, host_buf); 407 exit(1); 408 } 409 } 410 411 /* 412 * Delete a neighbor cache entry 413 */ 414 int 415 delete(const char *host) 416 { 417 struct sockaddr_in6 *sin = &sin_m; 418 struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 419 struct sockaddr_dl *sdl; 420 421 getsocket(); 422 sin_m = blank_sin; 423 if (parse_host(host, sin)) 424 return 1; 425 if (rtget(&sin, &sdl)) { 426 errx(1, "RTM_GET(%s) failed", host); 427 } 428 429 if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr) && 430 sin->sin6_scope_id == sin_m.sin6_scope_id) { 431 if (sdl->sdl_family == AF_LINK && rtm->rtm_flags & RTF_LLINFO) { 432 if (rtm->rtm_flags & RTF_LOCAL) 433 return (0); 434 if (!(rtm->rtm_flags & RTF_GATEWAY)) 435 goto delete; 436 } 437 /* 438 * IPv4 arp command retries with sin_other = SIN_PROXY here. 439 */ 440 warnx("delete: cannot delete non-NDP entry"); 441 return 1; 442 } 443 444 delete: 445 if (sdl->sdl_family != AF_LINK) { 446 printf("cannot locate %s\n", host); 447 return (1); 448 } 449 if (rtmsg(RTM_DELETE) == 0) { 450 getnameinfo((struct sockaddr *)sin, 451 sin->sin6_len, host_buf, 452 sizeof(host_buf), NULL, 0, 453 (nflag ? NI_NUMERICHOST : 0)); 454 printf("%s (%s) deleted\n", host, host_buf); 455 } 456 457 return 0; 458 } 459 460 #define W_ADDR 36 461 #define W_LL 17 462 #define W_IF 7 463 464 /* 465 * Dump the entire neighbor cache 466 */ 467 void 468 dump(struct sockaddr_in6 *addr, int cflag) 469 { 470 int mib[7]; 471 size_t needed; 472 char *lim, *buf = NULL, *next; 473 struct rt_msghdr *rtm; 474 struct sockaddr_in6 *sin; 475 struct sockaddr_dl *sdl; 476 struct in6_nbrinfo *nbi; 477 struct timeval now; 478 int addrwidth; 479 int llwidth; 480 int ifwidth; 481 char *ifname; 482 483 /* Print header */ 484 if (!tflag && !cflag) 485 printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %5s\n", 486 W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address", 487 W_IF, W_IF, "Netif", "Expire", "S", "Flags"); 488 489 again:; 490 lim = NULL; 491 mib[0] = CTL_NET; 492 mib[1] = PF_ROUTE; 493 mib[2] = 0; 494 mib[3] = AF_INET6; 495 mib[4] = NET_RT_FLAGS; 496 mib[5] = RTF_LLINFO; 497 mib[6] = rdomain; 498 while (1) { 499 if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1) 500 err(1, "sysctl(PF_ROUTE estimate)"); 501 if (needed == 0) 502 break; 503 if ((buf = realloc(buf, needed)) == NULL) 504 err(1, "realloc"); 505 if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1) { 506 if (errno == ENOMEM) 507 continue; 508 err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)"); 509 } 510 lim = buf + needed; 511 break; 512 } 513 514 for (next = buf; next && lim && next < lim; next += rtm->rtm_msglen) { 515 int isrouter = 0, prbs = 0; 516 517 rtm = (struct rt_msghdr *)next; 518 if (rtm->rtm_version != RTM_VERSION) 519 continue; 520 sin = (struct sockaddr_in6 *)(next + rtm->rtm_hdrlen); 521 #ifdef __KAME__ 522 { 523 struct in6_addr *in6 = &sin->sin6_addr; 524 if ((IN6_IS_ADDR_LINKLOCAL(in6) || 525 IN6_IS_ADDR_MC_LINKLOCAL(in6) || 526 IN6_IS_ADDR_MC_INTFACELOCAL(in6)) && 527 sin->sin6_scope_id == 0) { 528 sin->sin6_scope_id = (u_int32_t) 529 ntohs(*(u_short *)&in6->s6_addr[2]); 530 *(u_short *)&in6->s6_addr[2] = 0; 531 } 532 } 533 #endif 534 sdl = (struct sockaddr_dl *)((char *)sin + ROUNDUP(sin->sin6_len)); 535 536 /* 537 * Some OSes can produce a route that has the LINK flag but 538 * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD 539 * and BSD/OS, where xx is not the interface identifier on 540 * lo0). Such routes entry would annoy getnbrinfo() below, 541 * so we skip them. 542 * XXX: such routes should have the GATEWAY flag, not the 543 * LINK flag. However, there is rotten routing software 544 * that advertises all routes that have the GATEWAY flag. 545 * Thus, KAME kernel intentionally does not set the LINK flag. 546 * What is to be fixed is not ndp, but such routing software 547 * (and the kernel workaround)... 548 */ 549 if (sdl->sdl_family != AF_LINK) 550 continue; 551 552 if (!(rtm->rtm_flags & RTF_HOST)) 553 continue; 554 555 if (addr) { 556 if (!IN6_ARE_ADDR_EQUAL(&addr->sin6_addr, 557 &sin->sin6_addr) || addr->sin6_scope_id != 558 sin->sin6_scope_id) 559 continue; 560 found_entry = 1; 561 } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr)) 562 continue; 563 getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, 564 sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0)); 565 if (cflag) { 566 if (rtm->rtm_flags & RTF_CLONED) 567 delete(host_buf); 568 continue; 569 } 570 gettimeofday(&now, 0); 571 if (tflag) { 572 char buf[sizeof("00:00:00")]; 573 struct tm *tm; 574 575 tm = localtime(&now.tv_sec); 576 if (tm != NULL) { 577 strftime(buf, sizeof(buf), "%H:%M:%S", tm); 578 printf("%s.%06ld ", buf, now.tv_usec); 579 } 580 } 581 582 addrwidth = strlen(host_buf); 583 if (addrwidth < W_ADDR) 584 addrwidth = W_ADDR; 585 llwidth = strlen(ether_str(sdl)); 586 if (W_ADDR + W_LL - addrwidth > llwidth) 587 llwidth = W_ADDR + W_LL - addrwidth; 588 ifname = if_indextoname(sdl->sdl_index, ifix_buf); 589 if (!ifname) 590 ifname = "?"; 591 ifwidth = strlen(ifname); 592 if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) 593 ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; 594 595 printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf, 596 llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname); 597 598 /* Print neighbor discovery specific informations */ 599 nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index, 1); 600 if (nbi) { 601 if (nbi->expire > now.tv_sec) { 602 printf(" %-9.9s", 603 sec2str(nbi->expire - now.tv_sec)); 604 } else if (nbi->expire == 0) 605 printf(" %-9.9s", "permanent"); 606 else 607 printf(" %-9.9s", "expired"); 608 609 switch (nbi->state) { 610 case ND6_LLINFO_NOSTATE: 611 printf(" N"); 612 break; 613 case ND6_LLINFO_INCOMPLETE: 614 printf(" I"); 615 break; 616 case ND6_LLINFO_REACHABLE: 617 printf(" R"); 618 break; 619 case ND6_LLINFO_STALE: 620 printf(" S"); 621 break; 622 case ND6_LLINFO_DELAY: 623 printf(" D"); 624 break; 625 case ND6_LLINFO_PROBE: 626 printf(" P"); 627 break; 628 default: 629 printf(" ?"); 630 break; 631 } 632 633 isrouter = nbi->isrouter; 634 prbs = nbi->asked; 635 } else { 636 warnx("failed to get neighbor information"); 637 printf(" "); 638 } 639 640 printf(" %s%s%s", 641 (rtm->rtm_flags & RTF_LOCAL) ? "l" : "", 642 isrouter ? "R" : "", 643 (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); 644 645 if (prbs) 646 printf(" %d", prbs); 647 648 printf("\n"); 649 } 650 651 if (repeat) { 652 printf("\n"); 653 fflush(stdout); 654 sleep(repeat); 655 goto again; 656 } 657 658 free(buf); 659 } 660 661 static struct in6_nbrinfo * 662 getnbrinfo(struct in6_addr *addr, int ifindex, int warning) 663 { 664 static struct in6_nbrinfo nbi; 665 int s; 666 667 if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) 668 err(1, "socket"); 669 670 bzero(&nbi, sizeof(nbi)); 671 if_indextoname(ifindex, nbi.ifname); 672 nbi.addr = *addr; 673 if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) == -1) { 674 if (warning) 675 warn("ioctl(SIOCGNBRINFO_IN6)"); 676 close(s); 677 return(NULL); 678 } 679 680 close(s); 681 return(&nbi); 682 } 683 684 static char * 685 ether_str(struct sockaddr_dl *sdl) 686 { 687 static char hbuf[NI_MAXHOST]; 688 u_char *cp; 689 690 if (sdl->sdl_alen) { 691 cp = (u_char *)LLADDR(sdl); 692 snprintf(hbuf, sizeof(hbuf), "%02x:%02x:%02x:%02x:%02x:%02x", 693 cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); 694 } else 695 snprintf(hbuf, sizeof(hbuf), "(incomplete)"); 696 697 return(hbuf); 698 } 699 700 int 701 ndp_ether_aton(const char *a, u_char *n) 702 { 703 int i, o[6]; 704 705 i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2], 706 &o[3], &o[4], &o[5]); 707 if (i != 6) { 708 warnx("invalid Ethernet address '%s'", a); 709 return (1); 710 } 711 for (i = 0; i < 6; i++) 712 n[i] = o[i]; 713 return (0); 714 } 715 716 void 717 usage(void) 718 { 719 printf("usage: ndp [-acnt] "); 720 printf("[-A wait] [-d hostname] [-f filename] [-i interface]\n"); 721 printf("\t[-s nodename ether_addr [temp] [proxy]] "); 722 printf("[-V rdomain] [hostname]\n"); 723 exit(1); 724 } 725 726 int 727 rtmsg(int cmd) 728 { 729 static int seq; 730 int rlen; 731 struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 732 char *cp = m_rtmsg.m_space; 733 int l; 734 735 errno = 0; 736 if (cmd == RTM_DELETE) 737 goto doit; 738 bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 739 rtm->rtm_flags = flags; 740 rtm->rtm_version = RTM_VERSION; 741 rtm->rtm_tableid = rdomain; 742 743 switch (cmd) { 744 default: 745 errx(1, "internal wrong cmd"); 746 case RTM_ADD: 747 rtm->rtm_addrs |= RTA_GATEWAY; 748 if (expire_time) { 749 rtm->rtm_rmx.rmx_expire = expire_time; 750 rtm->rtm_inits = RTV_EXPIRE; 751 } 752 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); 753 #if 0 /* we don't support ipv6addr/128 type proxying. */ 754 if (rtm->rtm_flags & RTF_ANNOUNCE) { 755 rtm->rtm_flags &= ~RTF_HOST; 756 rtm->rtm_addrs |= RTA_NETMASK; 757 } 758 #endif 759 /* FALLTHROUGH */ 760 case RTM_GET: 761 rtm->rtm_addrs |= (RTA_DST | RTA_IFP); 762 } 763 764 #define NEXTADDR(w, s) \ 765 if (rtm->rtm_addrs & (w)) { \ 766 memcpy(cp, &(s), sizeof(s)); \ 767 ADVANCE(cp, (struct sockaddr *)&(s)); \ 768 } 769 770 #ifdef __KAME__ 771 { 772 struct sockaddr_in6 sin6 = sin_m; 773 struct in6_addr *in6 = &sin6.sin6_addr; 774 if (IN6_IS_ADDR_LINKLOCAL(in6) || 775 IN6_IS_ADDR_MC_LINKLOCAL(in6) || 776 IN6_IS_ADDR_MC_INTFACELOCAL(in6)) { 777 *(u_int16_t *)& in6->s6_addr[2] = 778 htons(sin6.sin6_scope_id); 779 sin6.sin6_scope_id = 0; 780 } 781 NEXTADDR(RTA_DST, sin6); 782 } 783 #else 784 NEXTADDR(RTA_DST, sin_m); 785 #endif 786 NEXTADDR(RTA_GATEWAY, sdl_m); 787 #if 0 /* we don't support ipv6addr/128 type proxying. */ 788 memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr)); 789 NEXTADDR(RTA_NETMASK, so_mask); 790 #endif 791 NEXTADDR(RTA_IFP, ifp_m); 792 793 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 794 doit: 795 l = rtm->rtm_msglen; 796 rtm->rtm_seq = ++seq; 797 rtm->rtm_type = cmd; 798 if ((rlen = write(rtsock, (char *)&m_rtmsg, l)) == -1) { 799 if (errno != ESRCH || cmd != RTM_DELETE) { 800 err(1, "writing to routing socket"); 801 } 802 } 803 do { 804 l = read(rtsock, (char *)&m_rtmsg, sizeof(m_rtmsg)); 805 } while (l > 0 && (rtm->rtm_version != RTM_VERSION || 806 rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 807 if (l == -1) 808 warn("read from routing socket"); 809 return (0); 810 } 811 812 int 813 rtget(struct sockaddr_in6 **sinp, struct sockaddr_dl **sdlp) 814 { 815 struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); 816 struct sockaddr_in6 *sin = NULL; 817 struct sockaddr_dl *sdl = NULL; 818 struct sockaddr *sa; 819 char *cp; 820 unsigned int i; 821 822 if (rtmsg(RTM_GET) < 0) 823 return (1); 824 825 if (rtm->rtm_addrs) { 826 cp = ((char *)rtm + rtm->rtm_hdrlen); 827 for (i = 1; i; i <<= 1) { 828 if (i & rtm->rtm_addrs) { 829 sa = (struct sockaddr *)cp; 830 switch (i) { 831 case RTA_DST: 832 sin = (struct sockaddr_in6 *)sa; 833 break; 834 case RTA_IFP: 835 sdl = (struct sockaddr_dl *)sa; 836 break; 837 default: 838 break; 839 } 840 ADVANCE(cp, sa); 841 } 842 } 843 } 844 845 if (sin == NULL || sdl == NULL) 846 return (1); 847 848 #ifdef __KAME__ 849 { 850 static struct sockaddr_in6 ksin; 851 struct in6_addr *in6; 852 853 /* do not damage the route message, we need it for delete */ 854 ksin = *sin; 855 sin = &ksin; 856 in6 = &sin->sin6_addr; 857 858 if ((IN6_IS_ADDR_LINKLOCAL(in6) || 859 IN6_IS_ADDR_MC_LINKLOCAL(in6) || 860 IN6_IS_ADDR_MC_INTFACELOCAL(in6)) && 861 sin->sin6_scope_id == 0) { 862 sin->sin6_scope_id = (u_int32_t)ntohs(*(u_short *) 863 &in6->s6_addr[2]); 864 *(u_short *)&in6->s6_addr[2] = 0; 865 } 866 } 867 #endif 868 *sinp = sin; 869 *sdlp = sdl; 870 871 return (0); 872 } 873 874 void 875 ifinfo(const char *ifname) 876 { 877 struct in6_ndireq nd; 878 int s; 879 880 if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { 881 err(1, "socket"); 882 } 883 bzero(&nd, sizeof(nd)); 884 strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); 885 if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) == -1) 886 err(1, "ioctl(SIOCGIFINFO_IN6)"); 887 888 if (!nd.ndi.initialized) 889 errx(1, "%s: not initialized yet", ifname); 890 891 printf("basereachable=%ds%dms", 892 nd.ndi.basereachable / 1000, nd.ndi.basereachable % 1000); 893 printf(", reachable=%ds", nd.ndi.reachable); 894 printf(", retrans=%ds%dms\n", nd.ndi.retrans / 1000, 895 nd.ndi.retrans % 1000); 896 897 close(s); 898 } 899 900 static char * 901 sec2str(time_t total) 902 { 903 static char result[256]; 904 int days, hours, mins, secs; 905 int first = 1; 906 char *p = result; 907 char *ep = &result[sizeof(result)]; 908 int n; 909 910 days = total / 3600 / 24; 911 hours = (total / 3600) % 24; 912 mins = (total / 60) % 60; 913 secs = total % 60; 914 915 if (days) { 916 first = 0; 917 n = snprintf(p, ep - p, "%dd", days); 918 if (n < 0 || n >= ep - p) 919 return "?"; 920 p += n; 921 } 922 if (!first || hours) { 923 first = 0; 924 n = snprintf(p, ep - p, "%dh", hours); 925 if (n < 0 || n >= ep - p) 926 return "?"; 927 p += n; 928 } 929 if (!first || mins) { 930 first = 0; 931 n = snprintf(p, ep - p, "%dm", mins); 932 if (n < 0 || n >= ep - p) 933 return "?"; 934 p += n; 935 } 936 snprintf(p, ep - p, "%ds", secs); 937 938 return(result); 939 } 940