1 /* $NetBSD: ndp.c,v 1.58 2020/09/15 10:11:35 roy Exp $ */ 2 /* $KAME: ndp.c,v 1.121 2005/07/13 11:30:13 keiichi 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/param.h> 78 #include <sys/file.h> 79 #include <sys/ioctl.h> 80 #include <sys/socket.h> 81 #include <sys/sysctl.h> 82 #include <sys/time.h> 83 84 #include <net/if.h> 85 #include <net/if_dl.h> 86 #include <net/if_types.h> 87 #include <net/route.h> 88 89 #include <netinet/in.h> 90 91 #include <netinet/icmp6.h> 92 #include <netinet6/in6_var.h> 93 #include <netinet6/nd6.h> 94 95 #include <arpa/inet.h> 96 97 #include <netdb.h> 98 #include <errno.h> 99 #include <nlist.h> 100 #include <stdio.h> 101 #include <string.h> 102 #include <paths.h> 103 #include <err.h> 104 #include <stdlib.h> 105 #include <fcntl.h> 106 #include <unistd.h> 107 108 #include "gmt2local.h" 109 #include "prog_ops.h" 110 111 static pid_t pid; 112 static int nflag; 113 static int tflag; 114 static int32_t thiszone; /* time difference with gmt */ 115 static int my_s = -1; 116 static unsigned int repeat = 0; 117 118 119 static char host_buf[NI_MAXHOST]; /* getnameinfo() */ 120 static char ifix_buf[IFNAMSIZ]; /* if_indextoname() */ 121 122 static void getsocket(void); 123 static int set(int, char **); 124 static void get(char *); 125 static int delete(struct rt_msghdr *, char *); 126 static void delete_one(char *); 127 static void do_foreach(struct in6_addr *, char *, int); 128 static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, unsigned int, int); 129 static char *ether_str(struct sockaddr_dl *); 130 static int ndp_ether_aton(char *, u_char *); 131 __dead static void usage(void); 132 static int rtmsg(int, struct rt_msghdr *); 133 static void ifinfo(char *, int, char **); 134 static const char *sec2str(time_t); 135 static char *ether_str(struct sockaddr_dl *); 136 static void ts_print(const struct timeval *); 137 138 #define NDP_F_CLEAR 1 139 #define NDP_F_DELETE 2 140 141 static int mode = 0; 142 static char *arg = NULL; 143 144 int 145 main(int argc, char **argv) 146 { 147 int ch; 148 149 while ((ch = getopt(argc, argv, "acd:f:i:nstA:")) != -1) 150 switch (ch) { 151 case 'a': 152 case 'c': 153 case 's': 154 case 'd': 155 case 'f': 156 case 'i' : 157 if (mode) { 158 usage(); 159 /*NOTREACHED*/ 160 } 161 mode = ch; 162 arg = optarg; 163 break; 164 case 'n': 165 nflag = 1; 166 break; 167 case 't': 168 tflag = 1; 169 break; 170 case 'A': 171 if (mode) { 172 usage(); 173 /*NOTREACHED*/ 174 } 175 mode = 'a'; 176 repeat = atoi(optarg); 177 break; 178 default: 179 usage(); 180 } 181 182 argc -= optind; 183 argv += optind; 184 185 if (prog_init && prog_init() == -1) 186 err(1, "init failed"); 187 188 pid = prog_getpid(); 189 thiszone = gmt2local(0L); 190 191 switch (mode) { 192 case 'a': 193 case 'c': 194 if (argc != 0) { 195 usage(); 196 /*NOTREACHED*/ 197 } 198 do_foreach(0, NULL, mode == 'c' ? NDP_F_CLEAR : 0); 199 break; 200 case 'd': 201 if (argc != 0) { 202 usage(); 203 /*NOTREACHED*/ 204 } 205 delete_one(arg); 206 break; 207 case 'i': 208 ifinfo(arg, argc, argv); 209 break; 210 case 's': 211 if (argc < 2 || argc > 4) 212 usage(); 213 return(set(argc, argv) ? 1 : 0); 214 case 0: 215 if (argc != 1) { 216 usage(); 217 /*NOTREACHED*/ 218 } 219 get(argv[0]); 220 break; 221 } 222 return(0); 223 } 224 225 static void 226 makeaddr(struct sockaddr_in6 *mysin, const void *resp) 227 { 228 const struct sockaddr_in6 *res = resp; 229 mysin->sin6_addr = res->sin6_addr; 230 mysin->sin6_scope_id = res->sin6_scope_id; 231 inet6_putscopeid(mysin, INET6_IS_ADDR_LINKLOCAL); 232 } 233 234 static void 235 getsocket(void) 236 { 237 if (my_s < 0) { 238 my_s = prog_socket(PF_ROUTE, SOCK_RAW, 0); 239 if (my_s < 0) 240 err(1, "socket"); 241 } 242 } 243 244 #ifdef notdef 245 static struct sockaddr_in6 so_mask = { 246 .sin6_len = sizeof(so_mask), 247 .sin6_family = AF_INET6 248 }; 249 #endif 250 static struct sockaddr_in6 blank_sin = { 251 .sin6_len = sizeof(blank_sin), 252 .sin6_family = AF_INET6 253 }; 254 static struct sockaddr_in6 sin_m; 255 static struct sockaddr_dl blank_sdl = { 256 .sdl_len = sizeof(blank_sdl), 257 .sdl_family = AF_LINK, 258 }; 259 static struct sockaddr_dl sdl_m; 260 static int expire_time, flags, found_entry; 261 static struct { 262 struct rt_msghdr m_rtm; 263 char m_space[512]; 264 } m_rtmsg; 265 266 /* 267 * Set an individual neighbor cache entry 268 */ 269 static int 270 set(int argc, char **argv) 271 { 272 register struct sockaddr_in6 *mysin = &sin_m; 273 register struct sockaddr_dl *sdl; 274 register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); 275 struct addrinfo hints, *res; 276 int gai_error; 277 u_char *ea; 278 char *host = argv[0], *eaddr = argv[1]; 279 280 getsocket(); 281 argc -= 2; 282 argv += 2; 283 sdl_m = blank_sdl; 284 sin_m = blank_sin; 285 286 (void)memset(&hints, 0, sizeof(hints)); 287 hints.ai_family = AF_INET6; 288 gai_error = getaddrinfo(host, NULL, &hints, &res); 289 if (gai_error) { 290 warnx("%s: %s", host, gai_strerror(gai_error)); 291 return 1; 292 } 293 makeaddr(mysin, res->ai_addr); 294 freeaddrinfo(res); 295 ea = (u_char *)LLADDR(&sdl_m); 296 if (ndp_ether_aton(eaddr, ea) == 0) 297 sdl_m.sdl_alen = 6; 298 flags = expire_time = 0; 299 while (argc-- > 0) { 300 if (strncmp(argv[0], "temp", 4) == 0) { 301 struct timeval tim; 302 303 (void)gettimeofday(&tim, 0); 304 expire_time = tim.tv_sec + 20 * 60; 305 } else if (strncmp(argv[0], "proxy", 5) == 0) 306 flags |= RTF_ANNOUNCE; 307 argv++; 308 } 309 if (rtmsg(RTM_GET, NULL) < 0) { 310 errx(1, "RTM_GET(%s) failed", host); 311 /* NOTREACHED */ 312 } 313 mysin = (struct sockaddr_in6 *)(void *)(rtm + 1); 314 sdl = (struct sockaddr_dl *)(void *)(RT_ROUNDUP(mysin->sin6_len) + (char *)(void *)mysin); 315 if (IN6_ARE_ADDR_EQUAL(&mysin->sin6_addr, &sin_m.sin6_addr)) { 316 if (sdl->sdl_family == AF_LINK && 317 !(rtm->rtm_flags & RTF_GATEWAY)) { 318 switch (sdl->sdl_type) { 319 case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: 320 case IFT_ISO88024: case IFT_ISO88025: 321 goto overwrite; 322 } 323 } 324 /* 325 * IPv4 arp command retries with sin_other = SIN_PROXY here. 326 */ 327 (void)fprintf(stderr, "set: cannot configure a new entry\n"); 328 return 1; 329 } 330 331 overwrite: 332 if (sdl->sdl_family != AF_LINK) { 333 warnx("cannot intuit interface index and type for %s", host); 334 return (1); 335 } 336 sdl_m.sdl_type = sdl->sdl_type; 337 sdl_m.sdl_index = sdl->sdl_index; 338 return (rtmsg(RTM_ADD, NULL)); 339 } 340 341 /* 342 * Display an individual neighbor cache entry 343 */ 344 static void 345 get(char *host) 346 { 347 struct sockaddr_in6 *mysin = &sin_m; 348 struct addrinfo hints, *res; 349 int gai_error; 350 351 sin_m = blank_sin; 352 (void)memset(&hints, 0, sizeof(hints)); 353 hints.ai_family = AF_INET6; 354 gai_error = getaddrinfo(host, NULL, &hints, &res); 355 if (gai_error) { 356 warnx("%s: %s", host, gai_strerror(gai_error)); 357 return; 358 } 359 makeaddr(mysin, res->ai_addr); 360 freeaddrinfo(res); 361 do_foreach(&mysin->sin6_addr, host, 0); 362 if (found_entry == 0) { 363 (void)getnameinfo((struct sockaddr *)(void *)mysin, 364 (socklen_t)mysin->sin6_len, 365 host_buf, sizeof(host_buf), NULL ,0, 366 (nflag ? NI_NUMERICHOST : 0)); 367 errx(1, "%s (%s) -- no entry", host, host_buf); 368 } 369 } 370 371 static void 372 delete_one(char *host) 373 { 374 struct sockaddr_in6 *mysin = &sin_m; 375 struct addrinfo hints, *res; 376 int gai_error; 377 378 sin_m = blank_sin; 379 (void)memset(&hints, 0, sizeof(hints)); 380 hints.ai_family = AF_INET6; 381 gai_error = getaddrinfo(host, NULL, &hints, &res); 382 if (gai_error) { 383 warnx("%s: %s", host, gai_strerror(gai_error)); 384 return; 385 } 386 makeaddr(mysin, res->ai_addr); 387 freeaddrinfo(res); 388 do_foreach(&mysin->sin6_addr, host, NDP_F_DELETE); 389 } 390 391 /* 392 * Delete a neighbor cache entry 393 */ 394 static int 395 delete(struct rt_msghdr *rtm, char *host) 396 { 397 char delete_host_buf[NI_MAXHOST]; 398 struct sockaddr_in6 *mysin = &sin_m; 399 struct sockaddr_dl *sdl; 400 401 getsocket(); 402 mysin = (struct sockaddr_in6 *)(void *)(rtm + 1); 403 sdl = (struct sockaddr_dl *)(void *)(RT_ROUNDUP(mysin->sin6_len) + 404 (char *)(void *)mysin); 405 406 if (sdl->sdl_family != AF_LINK) { 407 (void)printf("cannot locate %s\n", host); 408 return (1); 409 } 410 if (rtmsg(RTM_DELETE, rtm) == 0) { 411 struct sockaddr_in6 s6 = *mysin; /* XXX: for safety */ 412 413 s6.sin6_scope_id = 0; 414 inet6_putscopeid(&s6, INET6_IS_ADDR_LINKLOCAL); 415 (void)getnameinfo((struct sockaddr *)(void *)&s6, 416 (socklen_t)s6.sin6_len, delete_host_buf, 417 sizeof(delete_host_buf), NULL, 0, 418 (nflag ? NI_NUMERICHOST : 0)); 419 (void)printf("%s (%s) deleted\n", host, delete_host_buf); 420 } 421 422 return 0; 423 } 424 425 #define W_ADDR (8 * 4 + 7) 426 #define W_LL 17 427 #define W_IF 6 428 429 /* 430 * Iterate on neighbor caches and do 431 * - dump all caches, 432 * - clear all caches (NDP_F_CLEAR) or 433 * - remove matched caches (NDP_F_DELETE) 434 */ 435 static void 436 do_foreach(struct in6_addr *addr, char *host, int _flags) 437 { 438 int mib[6]; 439 size_t needed; 440 char *lim, *buf, *next; 441 struct rt_msghdr *rtm; 442 struct sockaddr_in6 *mysin; 443 struct sockaddr_dl *sdl; 444 struct in6_nbrinfo *nbi; 445 struct timeval tim; 446 int addrwidth; 447 int llwidth; 448 int ifwidth; 449 char flgbuf[8], *fl; 450 const char *ifname; 451 int cflag = _flags == NDP_F_CLEAR; 452 int dflag = _flags == NDP_F_DELETE; 453 454 /* Print header */ 455 if (!tflag && !cflag) 456 (void)printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %2s\n", 457 W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address", 458 W_IF, W_IF, "Netif", "Expire", "S", "Fl"); 459 460 again:; 461 mib[0] = CTL_NET; 462 mib[1] = PF_ROUTE; 463 mib[2] = 0; 464 mib[3] = AF_INET6; 465 mib[4] = NET_RT_FLAGS; 466 mib[5] = RTF_LLDATA; 467 if (prog_sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 468 err(1, "sysctl(PF_ROUTE estimate)"); 469 if (needed > 0) { 470 if ((buf = malloc(needed)) == NULL) 471 err(1, "malloc"); 472 if (prog_sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { 473 free(buf); 474 if (errno == ENOBUFS) 475 goto again; 476 err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)"); 477 } 478 lim = buf + needed; 479 } else 480 buf = lim = NULL; 481 482 for (next = buf; next && next < lim; next += rtm->rtm_msglen) { 483 int isrouter = 0, prbs = 0; 484 485 rtm = (struct rt_msghdr *)(void *)next; 486 mysin = (struct sockaddr_in6 *)(void *)(rtm + 1); 487 sdl = (struct sockaddr_dl *)(void *)((char *)(void *)mysin + RT_ROUNDUP(mysin->sin6_len)); 488 489 /* 490 * Some OSes can produce a route that has the LINK flag but 491 * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD 492 * and BSD/OS, where xx is not the interface identifier on 493 * lo0). Such routes entry would annoy getnbrinfo() below, 494 * so we skip them. 495 * XXX: such routes should have the GATEWAY flag, not the 496 * LINK flag. However, there is rotten routing software 497 * that advertises all routes that have the GATEWAY flag. 498 * Thus, KAME kernel intentionally does not set the LINK flag. 499 * What is to be fixed is not ndp, but such routing software 500 * (and the kernel workaround)... 501 */ 502 if (sdl->sdl_family != AF_LINK) 503 continue; 504 505 if (!(rtm->rtm_flags & RTF_HOST)) 506 continue; 507 508 if (addr) { 509 if (!IN6_ARE_ADDR_EQUAL(addr, &mysin->sin6_addr)) 510 continue; 511 found_entry = 1; 512 } else if (IN6_IS_ADDR_MULTICAST(&mysin->sin6_addr)) 513 continue; 514 if (dflag) { 515 (void)delete(rtm, host_buf); 516 continue; 517 } 518 if (IN6_IS_ADDR_LINKLOCAL(&mysin->sin6_addr) || 519 IN6_IS_ADDR_MC_LINKLOCAL(&mysin->sin6_addr)) { 520 uint16_t scopeid = mysin->sin6_scope_id; 521 inet6_getscopeid(mysin, INET6_IS_ADDR_LINKLOCAL| 522 INET6_IS_ADDR_MC_LINKLOCAL); 523 if (scopeid == 0) 524 mysin->sin6_scope_id = sdl->sdl_index; 525 } 526 (void)getnameinfo((struct sockaddr *)(void *)mysin, 527 (socklen_t)mysin->sin6_len, 528 host_buf, sizeof(host_buf), NULL, 0, 529 (nflag ? NI_NUMERICHOST : 0)); 530 if (cflag) { 531 /* Restore scopeid */ 532 if (IN6_IS_ADDR_LINKLOCAL(&mysin->sin6_addr) || 533 IN6_IS_ADDR_MC_LINKLOCAL(&mysin->sin6_addr)) 534 inet6_putscopeid(mysin, INET6_IS_ADDR_LINKLOCAL| 535 INET6_IS_ADDR_MC_LINKLOCAL); 536 if ((rtm->rtm_flags & RTF_STATIC) == 0) 537 (void)delete(rtm, host_buf); 538 continue; 539 } 540 (void)gettimeofday(&tim, 0); 541 if (tflag) 542 ts_print(&tim); 543 544 addrwidth = strlen(host_buf); 545 if (addrwidth < W_ADDR) 546 addrwidth = W_ADDR; 547 llwidth = strlen(ether_str(sdl)); 548 if (W_ADDR + W_LL - addrwidth > llwidth) 549 llwidth = W_ADDR + W_LL - addrwidth; 550 ifname = if_indextoname((unsigned int)sdl->sdl_index, ifix_buf); 551 if (!ifname) 552 ifname = "?"; 553 ifwidth = strlen(ifname); 554 if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) 555 ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; 556 557 (void)printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, 558 host_buf, llwidth, llwidth, ether_str(sdl), ifwidth, 559 ifwidth, ifname); 560 561 /* Print neighbor discovery specific informations */ 562 nbi = getnbrinfo(&mysin->sin6_addr, 563 (unsigned int)sdl->sdl_index, 1); 564 if (nbi) { 565 if (nbi->expire > tim.tv_sec) { 566 (void)printf(" %-9.9s", 567 sec2str(nbi->expire - tim.tv_sec)); 568 } else if (nbi->expire == 0) 569 (void)printf(" %-9.9s", "permanent"); 570 else 571 (void)printf(" %-9.9s", "expired"); 572 573 switch (nbi->state) { 574 case ND_LLINFO_NOSTATE: 575 (void)printf(" N"); 576 break; 577 case ND_LLINFO_WAITDELETE: 578 (void)printf(" W"); 579 break; 580 case ND_LLINFO_INCOMPLETE: 581 (void)printf(" I"); 582 break; 583 case ND_LLINFO_REACHABLE: 584 (void)printf(" R"); 585 break; 586 case ND_LLINFO_STALE: 587 (void)printf(" S"); 588 break; 589 case ND_LLINFO_DELAY: 590 (void)printf(" D"); 591 break; 592 case ND_LLINFO_PROBE: 593 (void)printf(" P"); 594 break; 595 case ND_LLINFO_UNREACHABLE: 596 (void)printf(" U"); 597 break; 598 default: 599 (void)printf(" ?"); 600 break; 601 } 602 603 isrouter = nbi->isrouter; 604 prbs = nbi->asked; 605 } else { 606 warnx("failed to get neighbor information"); 607 (void)printf(" "); 608 } 609 610 /* 611 * other flags. R: router, P: proxy, W: ?? 612 */ 613 fl = flgbuf; 614 if (isrouter) 615 *fl++ = 'R'; 616 if (rtm->rtm_flags & RTF_ANNOUNCE) 617 *fl++ = 'p'; 618 *fl++ = '\0'; 619 (void)printf(" %s", flgbuf); 620 621 if (prbs) 622 (void)printf(" %d", prbs); 623 624 (void)printf("\n"); 625 } 626 if (buf != NULL) 627 free(buf); 628 629 if (repeat) { 630 (void)printf("\n"); 631 (void)fflush(stdout); 632 (void)sleep(repeat); 633 goto again; 634 } 635 } 636 637 static struct in6_nbrinfo * 638 getnbrinfo(struct in6_addr *addr, unsigned int ifindex, int warning) 639 { 640 static struct in6_nbrinfo nbi; 641 int s; 642 643 if ((s = prog_socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 644 err(1, "socket"); 645 646 (void)memset(&nbi, 0, sizeof(nbi)); 647 (void)if_indextoname(ifindex, nbi.ifname); 648 nbi.addr = *addr; 649 if (prog_ioctl(s, SIOCGNBRINFO_IN6, &nbi) < 0) { 650 if (warning) 651 warn("ioctl(SIOCGNBRINFO_IN6)"); 652 (void)prog_close(s); 653 return(NULL); 654 } 655 656 (void)prog_close(s); 657 return(&nbi); 658 } 659 660 static char * 661 ether_str(struct sockaddr_dl *sdl) 662 { 663 static char hbuf[NI_MAXHOST]; 664 665 if (sdl->sdl_alen) { 666 if (getnameinfo((struct sockaddr *)(void *)sdl, 667 (socklen_t)sdl->sdl_len, 668 hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) 669 (void)snprintf(hbuf, sizeof(hbuf), "<invalid>"); 670 } else 671 (void)snprintf(hbuf, sizeof(hbuf), "(incomplete)"); 672 673 return(hbuf); 674 } 675 676 static int 677 ndp_ether_aton(char *a, u_char *n) 678 { 679 int i, o[6]; 680 681 i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2], 682 &o[3], &o[4], &o[5]); 683 if (i != 6) { 684 warnx("invalid Ethernet address '%s'", a); 685 return (1); 686 } 687 for (i = 0; i < 6; i++) 688 n[i] = o[i]; 689 return (0); 690 } 691 692 static void 693 usage(void) 694 { 695 const char *pn = getprogname(); 696 697 (void)fprintf(stderr, "Usage: %s [-nt] hostname\n", pn); 698 (void)fprintf(stderr, 699 " %s [-nt] -a | -c\n", pn); 700 (void)fprintf(stderr, " %s [-nt] -A wait\n", pn); 701 (void)fprintf(stderr, " %s [-nt] -d hostname\n", pn); 702 (void)fprintf(stderr, " %s [-nt] -f filename\n", pn); 703 (void)fprintf(stderr, " %s [-nt] -i interface [flags...]\n", pn); 704 (void)fprintf(stderr, 705 " %s [-nt] -s nodename etheraddr [temp] [proxy]\n", pn); 706 exit(1); 707 } 708 709 static int 710 rtmsg(int cmd, struct rt_msghdr *_rtm) 711 { 712 static int seq; 713 register struct rt_msghdr *rtm = _rtm; 714 register char *cp = m_rtmsg.m_space; 715 register int l; 716 717 errno = 0; 718 if (rtm != NULL) { 719 memcpy(&m_rtmsg, rtm, rtm->rtm_msglen); 720 rtm = &m_rtmsg.m_rtm; 721 goto doit; 722 } 723 (void)memset(&m_rtmsg, 0, sizeof(m_rtmsg)); 724 rtm = &m_rtmsg.m_rtm; 725 rtm->rtm_flags = flags; 726 rtm->rtm_version = RTM_VERSION; 727 728 switch (cmd) { 729 default: 730 errx(1, "internal wrong cmd"); 731 /*NOTREACHED*/ 732 case RTM_ADD: 733 rtm->rtm_addrs |= RTA_GATEWAY; 734 if (expire_time) { 735 rtm->rtm_rmx.rmx_expire = expire_time; 736 rtm->rtm_inits = RTV_EXPIRE; 737 } 738 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); 739 #ifdef notdef /* we don't support ipv6addr/128 type proxying. */ 740 if (rtm->rtm_flags & RTF_ANNOUNCE) { 741 rtm->rtm_flags &= ~RTF_HOST; 742 rtm->rtm_addrs |= RTA_NETMASK; 743 } 744 #endif 745 rtm->rtm_addrs |= RTA_DST; 746 break; 747 case RTM_GET: 748 rtm->rtm_flags |= RTF_LLDATA; 749 rtm->rtm_addrs |= RTA_DST | RTA_GATEWAY; 750 } 751 #define NEXTADDR(w, s) \ 752 if (rtm->rtm_addrs & (w)) { \ 753 (void)memcpy(cp, &s, sizeof(s)); \ 754 RT_ADVANCE(cp, (struct sockaddr *)(void *)&s); \ 755 } 756 757 NEXTADDR(RTA_DST, sin_m); 758 NEXTADDR(RTA_GATEWAY, sdl_m); 759 #ifdef notdef /* we don't support ipv6addr/128 type proxying. */ 760 (void)memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr)); 761 NEXTADDR(RTA_NETMASK, so_mask); 762 #endif 763 764 rtm->rtm_msglen = cp - (char *)(void *)&m_rtmsg; 765 doit: 766 l = rtm->rtm_msglen; 767 rtm->rtm_seq = ++seq; 768 rtm->rtm_type = cmd; 769 if (prog_write(my_s, &m_rtmsg, (size_t)l) == -1) { 770 if (errno != ESRCH || cmd != RTM_DELETE) 771 err(1, "writing to routing socket"); 772 } 773 do { 774 l = prog_read(my_s, &m_rtmsg, sizeof(m_rtmsg)); 775 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 776 if (l < 0) 777 warn("read from routing socket"); 778 return (0); 779 } 780 781 static void 782 ifinfo(char *ifname, int argc, char **argv) 783 { 784 struct in6_ndireq nd; 785 int i, s; 786 u_int32_t newflags; 787 bool valset = false, flagset = false; 788 789 if ((s = prog_socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 790 err(1, "socket"); 791 (void)memset(&nd, 0, sizeof(nd)); 792 (void)strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); 793 if (prog_ioctl(s, SIOCGIFINFO_IN6, &nd) < 0) 794 err(1, "ioctl(SIOCGIFINFO_IN6)"); 795 #define ND nd.ndi 796 newflags = ND.flags; 797 for (i = 0; i < argc; i++) { 798 int clear = 0; 799 char *cp = argv[i]; 800 801 if (*cp == '-') { 802 clear = 1; 803 cp++; 804 } 805 806 #define SETFLAG(s, f) \ 807 do {\ 808 if (strcmp(cp, (s)) == 0) {\ 809 if (clear)\ 810 newflags &= ~(f);\ 811 else\ 812 newflags |= (f);\ 813 flagset = true; \ 814 }\ 815 } while (/*CONSTCOND*/0) 816 /* 817 * XXX: this macro is not 100% correct, in that it matches "nud" against 818 * "nudbogus". But we just let it go since this is minor. 819 */ 820 #define SETVALUE(f, v) \ 821 do { \ 822 char *valptr; \ 823 unsigned long newval; \ 824 v = 0; /* unspecified */ \ 825 if (strncmp(cp, f, strlen(f)) == 0) { \ 826 valptr = strchr(cp, '='); \ 827 if (valptr == NULL) \ 828 err(1, "syntax error in %s field", (f)); \ 829 errno = 0; \ 830 newval = strtoul(++valptr, NULL, 0); \ 831 if (errno) \ 832 err(1, "syntax error in %s's value", (f)); \ 833 v = newval; \ 834 valset = true; \ 835 } \ 836 } while (/*CONSTCOND*/0) 837 838 #ifdef ND6_IFF_IFDISABLED 839 SETFLAG("disabled", ND6_IFF_IFDISABLED); 840 #endif 841 SETFLAG("nud", ND6_IFF_PERFORMNUD); 842 #ifdef ND6_IFF_ACCEPT_RTADV 843 SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV); 844 #endif 845 #ifdef ND6_IFF_OVERRIDE_RTADV 846 SETFLAG("override_rtadv", ND6_IFF_OVERRIDE_RTADV); 847 #endif 848 #ifdef ND6_IFF_AUTO_LINKLOCAL 849 SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL); 850 #endif 851 #ifdef ND6_IFF_PREFER_SOURCE 852 SETFLAG("prefer_source", ND6_IFF_PREFER_SOURCE); 853 #endif 854 #ifdef ND6_IFF_DONT_SET_IFROUTE 855 SETFLAG("dont_set_ifroute", ND6_IFF_DONT_SET_IFROUTE); 856 #endif 857 SETVALUE("basereachable", ND.basereachable); 858 SETVALUE("retrans", ND.retrans); 859 SETVALUE("curhlim", ND.chlim); 860 861 ND.flags = newflags; 862 #ifdef SIOCSIFINFO_IN6 863 if (valset && prog_ioctl(s, SIOCSIFINFO_IN6, &nd) < 0) 864 err(1, "ioctl(SIOCSIFINFO_IN6)"); 865 #endif 866 if (flagset && prog_ioctl(s, SIOCSIFINFO_FLAGS, &nd) < 0) 867 err(1, "ioctl(SIOCSIFINFO_FLAGS)"); 868 #undef SETFLAG 869 #undef SETVALUE 870 } 871 872 if (prog_ioctl(s, SIOCGIFINFO_IN6, &nd) < 0) 873 err(1, "ioctl(SIOCGIFINFO_IN6)"); 874 (void)printf("curhlim=%d", ND.chlim); 875 (void)printf(", basereachable=%ds%dms", 876 ND.basereachable / 1000, ND.basereachable % 1000); 877 (void)printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000); 878 if (ND.flags) { 879 (void)printf("\nFlags: "); 880 if ((ND.flags & ND6_IFF_PERFORMNUD)) 881 (void)printf("nud "); 882 #ifdef ND6_IFF_IFDISABLED 883 if ((ND.flags & ND6_IFF_IFDISABLED)) 884 (void)printf("disabled "); 885 #endif 886 #ifdef ND6_IFF_AUTO_LINKLOCAL 887 if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL)) 888 (void)printf("auto_linklocal "); 889 #endif 890 #ifdef ND6_IFF_PREFER_SOURCE 891 if ((ND.flags & ND6_IFF_PREFER_SOURCE)) 892 (void)printf("prefer_source "); 893 #endif 894 } 895 (void)putc('\n', stdout); 896 #undef ND 897 898 (void)prog_close(s); 899 } 900 901 static const char * 902 sec2str(time_t total) 903 { 904 static char result[256]; 905 int days, hours, mins, secs; 906 int first = 1; 907 char *p = result; 908 char *ep = &result[sizeof(result)]; 909 int n; 910 911 days = total / 3600 / 24; 912 hours = (total / 3600) % 24; 913 mins = (total / 60) % 60; 914 secs = total % 60; 915 916 if (days) { 917 first = 0; 918 n = snprintf(p, (size_t)(ep - p), "%dd", days); 919 if (n < 0 || n >= ep - p) 920 return "?"; 921 p += n; 922 } 923 if (!first || hours) { 924 first = 0; 925 n = snprintf(p, (size_t)(ep - p), "%dh", hours); 926 if (n < 0 || n >= ep - p) 927 return "?"; 928 p += n; 929 } 930 if (!first || mins) { 931 first = 0; 932 n = snprintf(p, (size_t)(ep - p), "%dm", mins); 933 if (n < 0 || n >= ep - p) 934 return "?"; 935 p += n; 936 } 937 (void)snprintf(p, (size_t)(ep - p), "%ds", secs); 938 939 return(result); 940 } 941 942 /* 943 * Print the timestamp 944 * from tcpdump/util.c 945 */ 946 static void 947 ts_print(const struct timeval *tvp) 948 { 949 int s; 950 951 /* Default */ 952 s = (tvp->tv_sec + thiszone) % 86400; 953 (void)printf("%02d:%02d:%02d.%06u ", 954 s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec); 955 } 956