1 /* $OpenBSD: ndp.c,v 1.107 2022/12/28 21:30:17 jmc 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 /* 461 * strlen("2001:0db8:3333:4444:5555:6666:7777:8888") == 39 462 */ 463 #define W_ADDR 39 464 #define W_LL 17 465 #define W_IF 7 466 467 /* 468 * Dump the entire neighbor cache 469 */ 470 void 471 dump(struct sockaddr_in6 *addr, int cflag) 472 { 473 int mib[7]; 474 size_t needed; 475 char *lim, *buf = NULL, *next; 476 struct rt_msghdr *rtm; 477 struct sockaddr_in6 *sin; 478 struct sockaddr_dl *sdl; 479 struct in6_nbrinfo *nbi; 480 struct timeval now; 481 int addrwidth; 482 int llwidth; 483 int ifwidth; 484 char *ifname; 485 486 /* Print header */ 487 if (!tflag && !cflag) 488 printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %5s\n", 489 W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address", 490 W_IF, W_IF, "Netif", "Expire", "S", "Flags"); 491 492 again:; 493 lim = NULL; 494 mib[0] = CTL_NET; 495 mib[1] = PF_ROUTE; 496 mib[2] = 0; 497 mib[3] = AF_INET6; 498 mib[4] = NET_RT_FLAGS; 499 mib[5] = RTF_LLINFO; 500 mib[6] = rdomain; 501 while (1) { 502 if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1) 503 err(1, "sysctl(PF_ROUTE estimate)"); 504 if (needed == 0) 505 break; 506 if ((buf = realloc(buf, needed)) == NULL) 507 err(1, "realloc"); 508 if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1) { 509 if (errno == ENOMEM) 510 continue; 511 err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)"); 512 } 513 lim = buf + needed; 514 break; 515 } 516 517 for (next = buf; next && lim && next < lim; next += rtm->rtm_msglen) { 518 int isrouter = 0, prbs = 0; 519 520 rtm = (struct rt_msghdr *)next; 521 if (rtm->rtm_version != RTM_VERSION) 522 continue; 523 sin = (struct sockaddr_in6 *)(next + rtm->rtm_hdrlen); 524 #ifdef __KAME__ 525 { 526 struct in6_addr *in6 = &sin->sin6_addr; 527 if ((IN6_IS_ADDR_LINKLOCAL(in6) || 528 IN6_IS_ADDR_MC_LINKLOCAL(in6) || 529 IN6_IS_ADDR_MC_INTFACELOCAL(in6)) && 530 sin->sin6_scope_id == 0) { 531 sin->sin6_scope_id = (u_int32_t) 532 ntohs(*(u_short *)&in6->s6_addr[2]); 533 *(u_short *)&in6->s6_addr[2] = 0; 534 } 535 } 536 #endif 537 sdl = (struct sockaddr_dl *)((char *)sin + ROUNDUP(sin->sin6_len)); 538 539 /* 540 * Some OSes can produce a route that has the LINK flag but 541 * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD 542 * and BSD/OS, where xx is not the interface identifier on 543 * lo0). Such routes entry would annoy getnbrinfo() below, 544 * so we skip them. 545 * XXX: such routes should have the GATEWAY flag, not the 546 * LINK flag. However, there is rotten routing software 547 * that advertises all routes that have the GATEWAY flag. 548 * Thus, KAME kernel intentionally does not set the LINK flag. 549 * What is to be fixed is not ndp, but such routing software 550 * (and the kernel workaround)... 551 */ 552 if (sdl->sdl_family != AF_LINK) 553 continue; 554 555 if (!(rtm->rtm_flags & RTF_HOST)) 556 continue; 557 558 if (addr) { 559 if (!IN6_ARE_ADDR_EQUAL(&addr->sin6_addr, 560 &sin->sin6_addr) || addr->sin6_scope_id != 561 sin->sin6_scope_id) 562 continue; 563 found_entry = 1; 564 } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr)) 565 continue; 566 getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, 567 sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0)); 568 if (cflag) { 569 if (rtm->rtm_flags & RTF_CLONED) 570 delete(host_buf); 571 continue; 572 } 573 gettimeofday(&now, 0); 574 if (tflag) { 575 char buf[sizeof("00:00:00")]; 576 struct tm *tm; 577 578 tm = localtime(&now.tv_sec); 579 if (tm != NULL) { 580 strftime(buf, sizeof(buf), "%H:%M:%S", tm); 581 printf("%s.%06ld ", buf, now.tv_usec); 582 } 583 } 584 585 addrwidth = strlen(host_buf); 586 if (addrwidth < W_ADDR) 587 addrwidth = W_ADDR; 588 llwidth = strlen(ether_str(sdl)); 589 if (W_ADDR + W_LL - addrwidth > llwidth) 590 llwidth = W_ADDR + W_LL - addrwidth; 591 ifname = if_indextoname(sdl->sdl_index, ifix_buf); 592 if (!ifname) 593 ifname = "?"; 594 ifwidth = strlen(ifname); 595 if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) 596 ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; 597 598 printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf, 599 llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname); 600 601 /* Print neighbor discovery specific information */ 602 nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index, 1); 603 if (nbi) { 604 if (nbi->expire > now.tv_sec) { 605 printf(" %-9.9s", 606 sec2str(nbi->expire - now.tv_sec)); 607 } else if (nbi->expire == 0) 608 printf(" %-9.9s", "permanent"); 609 else 610 printf(" %-9.9s", "expired"); 611 612 switch (nbi->state) { 613 case ND6_LLINFO_NOSTATE: 614 printf(" N"); 615 break; 616 case ND6_LLINFO_INCOMPLETE: 617 printf(" I"); 618 break; 619 case ND6_LLINFO_REACHABLE: 620 printf(" R"); 621 break; 622 case ND6_LLINFO_STALE: 623 printf(" S"); 624 break; 625 case ND6_LLINFO_DELAY: 626 printf(" D"); 627 break; 628 case ND6_LLINFO_PROBE: 629 printf(" P"); 630 break; 631 default: 632 printf(" ?"); 633 break; 634 } 635 636 isrouter = nbi->isrouter; 637 prbs = nbi->asked; 638 } else { 639 warnx("failed to get neighbor information"); 640 printf(" "); 641 } 642 643 printf(" %s%s%s", 644 (rtm->rtm_flags & RTF_LOCAL) ? "l" : "", 645 isrouter ? "R" : "", 646 (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); 647 648 if (prbs) 649 printf(" %d", prbs); 650 651 printf("\n"); 652 } 653 654 if (repeat) { 655 printf("\n"); 656 fflush(stdout); 657 sleep(repeat); 658 goto again; 659 } 660 661 free(buf); 662 } 663 664 static struct in6_nbrinfo * 665 getnbrinfo(struct in6_addr *addr, int ifindex, int warning) 666 { 667 static struct in6_nbrinfo nbi; 668 int s; 669 670 if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) 671 err(1, "socket"); 672 673 bzero(&nbi, sizeof(nbi)); 674 if_indextoname(ifindex, nbi.ifname); 675 nbi.addr = *addr; 676 if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) == -1) { 677 if (warning) 678 warn("ioctl(SIOCGNBRINFO_IN6)"); 679 close(s); 680 return(NULL); 681 } 682 683 close(s); 684 return(&nbi); 685 } 686 687 static char * 688 ether_str(struct sockaddr_dl *sdl) 689 { 690 static char hbuf[NI_MAXHOST]; 691 u_char *cp; 692 693 if (sdl->sdl_alen) { 694 cp = (u_char *)LLADDR(sdl); 695 snprintf(hbuf, sizeof(hbuf), "%02x:%02x:%02x:%02x:%02x:%02x", 696 cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); 697 } else 698 snprintf(hbuf, sizeof(hbuf), "(incomplete)"); 699 700 return(hbuf); 701 } 702 703 int 704 ndp_ether_aton(const char *a, u_char *n) 705 { 706 int i, o[6]; 707 708 i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2], 709 &o[3], &o[4], &o[5]); 710 if (i != 6) { 711 warnx("invalid Ethernet address '%s'", a); 712 return (1); 713 } 714 for (i = 0; i < 6; i++) 715 n[i] = o[i]; 716 return (0); 717 } 718 719 void 720 usage(void) 721 { 722 printf("usage: ndp [-acnt] "); 723 printf("[-A wait] [-d hostname] [-f filename] [-i interface]\n"); 724 printf("\t[-s nodename ether_addr [temp] [proxy]] "); 725 printf("[-V rdomain] [hostname]\n"); 726 exit(1); 727 } 728 729 int 730 rtmsg(int cmd) 731 { 732 static int seq; 733 int rlen; 734 struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 735 char *cp = m_rtmsg.m_space; 736 int l; 737 738 errno = 0; 739 if (cmd == RTM_DELETE) 740 goto doit; 741 bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 742 rtm->rtm_flags = flags; 743 rtm->rtm_version = RTM_VERSION; 744 rtm->rtm_tableid = rdomain; 745 746 switch (cmd) { 747 default: 748 errx(1, "internal wrong cmd"); 749 case RTM_ADD: 750 rtm->rtm_addrs |= RTA_GATEWAY; 751 if (expire_time) { 752 rtm->rtm_rmx.rmx_expire = expire_time; 753 rtm->rtm_inits = RTV_EXPIRE; 754 } 755 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); 756 #if 0 /* we don't support ipv6addr/128 type proxying. */ 757 if (rtm->rtm_flags & RTF_ANNOUNCE) { 758 rtm->rtm_flags &= ~RTF_HOST; 759 rtm->rtm_addrs |= RTA_NETMASK; 760 } 761 #endif 762 /* FALLTHROUGH */ 763 case RTM_GET: 764 rtm->rtm_addrs |= (RTA_DST | RTA_IFP); 765 } 766 767 #define NEXTADDR(w, s) \ 768 if (rtm->rtm_addrs & (w)) { \ 769 memcpy(cp, &(s), sizeof(s)); \ 770 ADVANCE(cp, (struct sockaddr *)&(s)); \ 771 } 772 773 #ifdef __KAME__ 774 { 775 struct sockaddr_in6 sin6 = sin_m; 776 struct in6_addr *in6 = &sin6.sin6_addr; 777 if (IN6_IS_ADDR_LINKLOCAL(in6) || 778 IN6_IS_ADDR_MC_LINKLOCAL(in6) || 779 IN6_IS_ADDR_MC_INTFACELOCAL(in6)) { 780 *(u_int16_t *)& in6->s6_addr[2] = 781 htons(sin6.sin6_scope_id); 782 sin6.sin6_scope_id = 0; 783 } 784 NEXTADDR(RTA_DST, sin6); 785 } 786 #else 787 NEXTADDR(RTA_DST, sin_m); 788 #endif 789 NEXTADDR(RTA_GATEWAY, sdl_m); 790 #if 0 /* we don't support ipv6addr/128 type proxying. */ 791 memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr)); 792 NEXTADDR(RTA_NETMASK, so_mask); 793 #endif 794 NEXTADDR(RTA_IFP, ifp_m); 795 796 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 797 doit: 798 l = rtm->rtm_msglen; 799 rtm->rtm_seq = ++seq; 800 rtm->rtm_type = cmd; 801 if ((rlen = write(rtsock, (char *)&m_rtmsg, l)) == -1) { 802 if (errno != ESRCH || cmd != RTM_DELETE) { 803 err(1, "writing to routing socket"); 804 } 805 } 806 do { 807 l = read(rtsock, (char *)&m_rtmsg, sizeof(m_rtmsg)); 808 } while (l > 0 && (rtm->rtm_version != RTM_VERSION || 809 rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 810 if (l == -1) 811 warn("read from routing socket"); 812 return (0); 813 } 814 815 int 816 rtget(struct sockaddr_in6 **sinp, struct sockaddr_dl **sdlp) 817 { 818 struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); 819 struct sockaddr_in6 *sin = NULL; 820 struct sockaddr_dl *sdl = NULL; 821 struct sockaddr *sa; 822 char *cp; 823 unsigned int i; 824 825 if (rtmsg(RTM_GET) < 0) 826 return (1); 827 828 if (rtm->rtm_addrs) { 829 cp = ((char *)rtm + rtm->rtm_hdrlen); 830 for (i = 1; i; i <<= 1) { 831 if (i & rtm->rtm_addrs) { 832 sa = (struct sockaddr *)cp; 833 switch (i) { 834 case RTA_DST: 835 sin = (struct sockaddr_in6 *)sa; 836 break; 837 case RTA_IFP: 838 sdl = (struct sockaddr_dl *)sa; 839 break; 840 default: 841 break; 842 } 843 ADVANCE(cp, sa); 844 } 845 } 846 } 847 848 if (sin == NULL || sdl == NULL) 849 return (1); 850 851 #ifdef __KAME__ 852 { 853 static struct sockaddr_in6 ksin; 854 struct in6_addr *in6; 855 856 /* do not damage the route message, we need it for delete */ 857 ksin = *sin; 858 sin = &ksin; 859 in6 = &sin->sin6_addr; 860 861 if ((IN6_IS_ADDR_LINKLOCAL(in6) || 862 IN6_IS_ADDR_MC_LINKLOCAL(in6) || 863 IN6_IS_ADDR_MC_INTFACELOCAL(in6)) && 864 sin->sin6_scope_id == 0) { 865 sin->sin6_scope_id = (u_int32_t)ntohs(*(u_short *) 866 &in6->s6_addr[2]); 867 *(u_short *)&in6->s6_addr[2] = 0; 868 } 869 } 870 #endif 871 *sinp = sin; 872 *sdlp = sdl; 873 874 return (0); 875 } 876 877 void 878 ifinfo(const char *ifname) 879 { 880 struct in6_ndireq nd; 881 int s; 882 883 if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { 884 err(1, "socket"); 885 } 886 bzero(&nd, sizeof(nd)); 887 strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); 888 if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) == -1) 889 err(1, "ioctl(SIOCGIFINFO_IN6)"); 890 891 printf("reachable=%ds\n", nd.ndi.reachable); 892 893 close(s); 894 } 895 896 static char * 897 sec2str(time_t total) 898 { 899 static char result[256]; 900 int days, hours, mins, secs; 901 int first = 1; 902 char *p = result; 903 char *ep = &result[sizeof(result)]; 904 int n; 905 906 days = total / 3600 / 24; 907 hours = (total / 3600) % 24; 908 mins = (total / 60) % 60; 909 secs = total % 60; 910 911 if (days) { 912 first = 0; 913 n = snprintf(p, ep - p, "%dd", days); 914 if (n < 0 || n >= ep - p) 915 return "?"; 916 p += n; 917 } 918 if (!first || hours) { 919 first = 0; 920 n = snprintf(p, ep - p, "%dh", hours); 921 if (n < 0 || n >= ep - p) 922 return "?"; 923 p += n; 924 } 925 if (!first || mins) { 926 first = 0; 927 n = snprintf(p, ep - p, "%dm", mins); 928 if (n < 0 || n >= ep - p) 929 return "?"; 930 p += n; 931 } 932 snprintf(p, ep - p, "%ds", secs); 933 934 return(result); 935 } 936