1 /* $NetBSD: ndp.c,v 1.57 2020/06/12 21:08:02 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 ND6_LLINFO_NOSTATE: 575 (void)printf(" N"); 576 break; 577 #ifdef ND6_LLINFO_WAITDELETE 578 case ND6_LLINFO_WAITDELETE: 579 (void)printf(" W"); 580 break; 581 #endif 582 case ND6_LLINFO_INCOMPLETE: 583 (void)printf(" I"); 584 break; 585 case ND6_LLINFO_REACHABLE: 586 (void)printf(" R"); 587 break; 588 case ND6_LLINFO_STALE: 589 (void)printf(" S"); 590 break; 591 case ND6_LLINFO_DELAY: 592 (void)printf(" D"); 593 break; 594 case ND6_LLINFO_PROBE: 595 (void)printf(" P"); 596 break; 597 default: 598 (void)printf(" ?"); 599 break; 600 } 601 602 isrouter = nbi->isrouter; 603 prbs = nbi->asked; 604 } else { 605 warnx("failed to get neighbor information"); 606 (void)printf(" "); 607 } 608 609 /* 610 * other flags. R: router, P: proxy, W: ?? 611 */ 612 fl = flgbuf; 613 if (isrouter) 614 *fl++ = 'R'; 615 if (rtm->rtm_flags & RTF_ANNOUNCE) 616 *fl++ = 'p'; 617 *fl++ = '\0'; 618 (void)printf(" %s", flgbuf); 619 620 if (prbs) 621 (void)printf(" %d", prbs); 622 623 (void)printf("\n"); 624 } 625 if (buf != NULL) 626 free(buf); 627 628 if (repeat) { 629 (void)printf("\n"); 630 (void)fflush(stdout); 631 (void)sleep(repeat); 632 goto again; 633 } 634 } 635 636 static struct in6_nbrinfo * 637 getnbrinfo(struct in6_addr *addr, unsigned int ifindex, int warning) 638 { 639 static struct in6_nbrinfo nbi; 640 int s; 641 642 if ((s = prog_socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 643 err(1, "socket"); 644 645 (void)memset(&nbi, 0, sizeof(nbi)); 646 (void)if_indextoname(ifindex, nbi.ifname); 647 nbi.addr = *addr; 648 if (prog_ioctl(s, SIOCGNBRINFO_IN6, &nbi) < 0) { 649 if (warning) 650 warn("ioctl(SIOCGNBRINFO_IN6)"); 651 (void)prog_close(s); 652 return(NULL); 653 } 654 655 (void)prog_close(s); 656 return(&nbi); 657 } 658 659 static char * 660 ether_str(struct sockaddr_dl *sdl) 661 { 662 static char hbuf[NI_MAXHOST]; 663 664 if (sdl->sdl_alen) { 665 if (getnameinfo((struct sockaddr *)(void *)sdl, 666 (socklen_t)sdl->sdl_len, 667 hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) 668 (void)snprintf(hbuf, sizeof(hbuf), "<invalid>"); 669 } else 670 (void)snprintf(hbuf, sizeof(hbuf), "(incomplete)"); 671 672 return(hbuf); 673 } 674 675 static int 676 ndp_ether_aton(char *a, u_char *n) 677 { 678 int i, o[6]; 679 680 i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2], 681 &o[3], &o[4], &o[5]); 682 if (i != 6) { 683 warnx("invalid Ethernet address '%s'", a); 684 return (1); 685 } 686 for (i = 0; i < 6; i++) 687 n[i] = o[i]; 688 return (0); 689 } 690 691 static void 692 usage(void) 693 { 694 const char *pn = getprogname(); 695 696 (void)fprintf(stderr, "Usage: %s [-nt] hostname\n", pn); 697 (void)fprintf(stderr, 698 " %s [-nt] -a | -c\n", pn); 699 (void)fprintf(stderr, " %s [-nt] -A wait\n", pn); 700 (void)fprintf(stderr, " %s [-nt] -d hostname\n", pn); 701 (void)fprintf(stderr, " %s [-nt] -f filename\n", pn); 702 (void)fprintf(stderr, " %s [-nt] -i interface [flags...]\n", pn); 703 (void)fprintf(stderr, 704 " %s [-nt] -s nodename etheraddr [temp] [proxy]\n", pn); 705 exit(1); 706 } 707 708 static int 709 rtmsg(int cmd, struct rt_msghdr *_rtm) 710 { 711 static int seq; 712 register struct rt_msghdr *rtm = _rtm; 713 register char *cp = m_rtmsg.m_space; 714 register int l; 715 716 errno = 0; 717 if (rtm != NULL) { 718 memcpy(&m_rtmsg, rtm, rtm->rtm_msglen); 719 rtm = &m_rtmsg.m_rtm; 720 goto doit; 721 } 722 (void)memset(&m_rtmsg, 0, sizeof(m_rtmsg)); 723 rtm = &m_rtmsg.m_rtm; 724 rtm->rtm_flags = flags; 725 rtm->rtm_version = RTM_VERSION; 726 727 switch (cmd) { 728 default: 729 errx(1, "internal wrong cmd"); 730 /*NOTREACHED*/ 731 case RTM_ADD: 732 rtm->rtm_addrs |= RTA_GATEWAY; 733 if (expire_time) { 734 rtm->rtm_rmx.rmx_expire = expire_time; 735 rtm->rtm_inits = RTV_EXPIRE; 736 } 737 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); 738 #ifdef notdef /* we don't support ipv6addr/128 type proxying. */ 739 if (rtm->rtm_flags & RTF_ANNOUNCE) { 740 rtm->rtm_flags &= ~RTF_HOST; 741 rtm->rtm_addrs |= RTA_NETMASK; 742 } 743 #endif 744 rtm->rtm_addrs |= RTA_DST; 745 break; 746 case RTM_GET: 747 rtm->rtm_flags |= RTF_LLDATA; 748 rtm->rtm_addrs |= RTA_DST | RTA_GATEWAY; 749 } 750 #define NEXTADDR(w, s) \ 751 if (rtm->rtm_addrs & (w)) { \ 752 (void)memcpy(cp, &s, sizeof(s)); \ 753 RT_ADVANCE(cp, (struct sockaddr *)(void *)&s); \ 754 } 755 756 NEXTADDR(RTA_DST, sin_m); 757 NEXTADDR(RTA_GATEWAY, sdl_m); 758 #ifdef notdef /* we don't support ipv6addr/128 type proxying. */ 759 (void)memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr)); 760 NEXTADDR(RTA_NETMASK, so_mask); 761 #endif 762 763 rtm->rtm_msglen = cp - (char *)(void *)&m_rtmsg; 764 doit: 765 l = rtm->rtm_msglen; 766 rtm->rtm_seq = ++seq; 767 rtm->rtm_type = cmd; 768 if (prog_write(my_s, &m_rtmsg, (size_t)l) == -1) { 769 if (errno != ESRCH || cmd != RTM_DELETE) 770 err(1, "writing to routing socket"); 771 } 772 do { 773 l = prog_read(my_s, &m_rtmsg, sizeof(m_rtmsg)); 774 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 775 if (l < 0) 776 warn("read from routing socket"); 777 return (0); 778 } 779 780 static void 781 ifinfo(char *ifname, int argc, char **argv) 782 { 783 struct in6_ndireq nd; 784 int i, s; 785 u_int32_t newflags; 786 bool valset = false, flagset = false; 787 788 if ((s = prog_socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 789 err(1, "socket"); 790 (void)memset(&nd, 0, sizeof(nd)); 791 (void)strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); 792 if (prog_ioctl(s, SIOCGIFINFO_IN6, &nd) < 0) 793 err(1, "ioctl(SIOCGIFINFO_IN6)"); 794 #define ND nd.ndi 795 newflags = ND.flags; 796 for (i = 0; i < argc; i++) { 797 int clear = 0; 798 char *cp = argv[i]; 799 800 if (*cp == '-') { 801 clear = 1; 802 cp++; 803 } 804 805 #define SETFLAG(s, f) \ 806 do {\ 807 if (strcmp(cp, (s)) == 0) {\ 808 if (clear)\ 809 newflags &= ~(f);\ 810 else\ 811 newflags |= (f);\ 812 flagset = true; \ 813 }\ 814 } while (/*CONSTCOND*/0) 815 /* 816 * XXX: this macro is not 100% correct, in that it matches "nud" against 817 * "nudbogus". But we just let it go since this is minor. 818 */ 819 #define SETVALUE(f, v) \ 820 do { \ 821 char *valptr; \ 822 unsigned long newval; \ 823 v = 0; /* unspecified */ \ 824 if (strncmp(cp, f, strlen(f)) == 0) { \ 825 valptr = strchr(cp, '='); \ 826 if (valptr == NULL) \ 827 err(1, "syntax error in %s field", (f)); \ 828 errno = 0; \ 829 newval = strtoul(++valptr, NULL, 0); \ 830 if (errno) \ 831 err(1, "syntax error in %s's value", (f)); \ 832 v = newval; \ 833 valset = true; \ 834 } \ 835 } while (/*CONSTCOND*/0) 836 837 #ifdef ND6_IFF_IFDISABLED 838 SETFLAG("disabled", ND6_IFF_IFDISABLED); 839 #endif 840 SETFLAG("nud", ND6_IFF_PERFORMNUD); 841 #ifdef ND6_IFF_ACCEPT_RTADV 842 SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV); 843 #endif 844 #ifdef ND6_IFF_OVERRIDE_RTADV 845 SETFLAG("override_rtadv", ND6_IFF_OVERRIDE_RTADV); 846 #endif 847 #ifdef ND6_IFF_AUTO_LINKLOCAL 848 SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL); 849 #endif 850 #ifdef ND6_IFF_PREFER_SOURCE 851 SETFLAG("prefer_source", ND6_IFF_PREFER_SOURCE); 852 #endif 853 #ifdef ND6_IFF_DONT_SET_IFROUTE 854 SETFLAG("dont_set_ifroute", ND6_IFF_DONT_SET_IFROUTE); 855 #endif 856 SETVALUE("basereachable", ND.basereachable); 857 SETVALUE("retrans", ND.retrans); 858 SETVALUE("curhlim", ND.chlim); 859 860 ND.flags = newflags; 861 #ifdef SIOCSIFINFO_IN6 862 if (valset && prog_ioctl(s, SIOCSIFINFO_IN6, &nd) < 0) 863 err(1, "ioctl(SIOCSIFINFO_IN6)"); 864 #endif 865 if (flagset && prog_ioctl(s, SIOCSIFINFO_FLAGS, &nd) < 0) 866 err(1, "ioctl(SIOCSIFINFO_FLAGS)"); 867 #undef SETFLAG 868 #undef SETVALUE 869 } 870 871 if (prog_ioctl(s, SIOCGIFINFO_IN6, &nd) < 0) 872 err(1, "ioctl(SIOCGIFINFO_IN6)"); 873 (void)printf("curhlim=%d", ND.chlim); 874 (void)printf(", basereachable=%ds%dms", 875 ND.basereachable / 1000, ND.basereachable % 1000); 876 (void)printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000); 877 if (ND.flags) { 878 (void)printf("\nFlags: "); 879 if ((ND.flags & ND6_IFF_PERFORMNUD)) 880 (void)printf("nud "); 881 #ifdef ND6_IFF_IFDISABLED 882 if ((ND.flags & ND6_IFF_IFDISABLED)) 883 (void)printf("disabled "); 884 #endif 885 #ifdef ND6_IFF_AUTO_LINKLOCAL 886 if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL)) 887 (void)printf("auto_linklocal "); 888 #endif 889 #ifdef ND6_IFF_PREFER_SOURCE 890 if ((ND.flags & ND6_IFF_PREFER_SOURCE)) 891 (void)printf("prefer_source "); 892 #endif 893 } 894 (void)putc('\n', stdout); 895 #undef ND 896 897 (void)prog_close(s); 898 } 899 900 static const 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, (size_t)(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, (size_t)(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, (size_t)(ep - p), "%dm", mins); 932 if (n < 0 || n >= ep - p) 933 return "?"; 934 p += n; 935 } 936 (void)snprintf(p, (size_t)(ep - p), "%ds", secs); 937 938 return(result); 939 } 940 941 /* 942 * Print the timestamp 943 * from tcpdump/util.c 944 */ 945 static void 946 ts_print(const struct timeval *tvp) 947 { 948 int s; 949 950 /* Default */ 951 s = (tvp->tv_sec + thiszone) % 86400; 952 (void)printf("%02d:%02d:%02d.%06u ", 953 s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec); 954 } 955