1 /* $KAME: ndp.c,v 1.104 2003/06/27 07:48:39 itojun Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-3-Clause 5 * 6 * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the project nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 /* 34 * Copyright (c) 1984, 1993 35 * The Regents of the University of California. All rights reserved. 36 * 37 * This code is derived from software contributed to Berkeley by 38 * Sun Microsystems, Inc. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 3. Neither the name of the University nor the names of its contributors 49 * may be used to endorse or promote products derived from this software 50 * without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62 * SUCH DAMAGE. 63 */ 64 65 /* 66 * Based on: 67 * "@(#) Copyright (c) 1984, 1993\n\ 68 * The Regents of the University of California. All rights reserved.\n"; 69 * 70 * "@(#)arp.c 8.2 (Berkeley) 1/2/94"; 71 */ 72 73 /* 74 * ndp - display, set, delete and flush neighbor cache 75 */ 76 77 78 #include <sys/param.h> 79 #include <sys/file.h> 80 #include <sys/ioctl.h> 81 #include <sys/socket.h> 82 #include <sys/sysctl.h> 83 #include <sys/time.h> 84 #include <sys/queue.h> 85 86 #include <net/if.h> 87 #include <net/if_dl.h> 88 #include <net/if_types.h> 89 #include <net/route.h> 90 91 #include <netinet/in.h> 92 #include <netinet/if_ether.h> 93 94 #include <netinet/icmp6.h> 95 #include <netinet6/in6_var.h> 96 #include <netinet6/nd6.h> 97 98 #include <arpa/inet.h> 99 100 #include <assert.h> 101 #include <ctype.h> 102 #include <netdb.h> 103 #include <errno.h> 104 #include <nlist.h> 105 #include <stdbool.h> 106 #include <stdio.h> 107 #include <string.h> 108 #include <paths.h> 109 #include <err.h> 110 #include <stdint.h> 111 #include <stdlib.h> 112 #include <fcntl.h> 113 #include <unistd.h> 114 #include <libxo/xo.h> 115 #include <time.h> 116 117 #include "ndp.h" 118 119 #define NEXTADDR(w, s) \ 120 if (rtm->rtm_addrs & (w)) { \ 121 bcopy((char *)&s, cp, sizeof(s)); \ 122 cp += SA_SIZE(&s); \ 123 } 124 125 static pid_t pid; 126 static int32_t thiszone; /* time difference with gmt */ 127 static int s = -1; 128 static int repeat = 0; 129 130 static char host_buf[NI_MAXHOST]; /* getnameinfo() */ 131 static char ifix_buf[IFNAMSIZ]; /* if_indextoname() */ 132 133 static int file(char *); 134 static int set(int, char **); 135 static void get(char *); 136 static int delete(char *); 137 static int dump(struct sockaddr_in6 *, int); 138 static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int); 139 static int ndp_ether_aton(char *, u_char *); 140 static void usage(void) __dead2; 141 static void ifinfo(char *, int, char **); 142 static void rtrlist(void); 143 static void plist(void); 144 static void pfx_flush(void); 145 static void rtr_flush(void); 146 static void harmonize_rtr(void); 147 #ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */ 148 static void getdefif(void); 149 static void setdefif(char *); 150 #endif 151 152 #ifdef WITHOUT_NETLINK 153 static void getsocket(void); 154 static int rtmsg(int); 155 #endif 156 157 static const char *rtpref_str[] = { 158 "medium", /* 00 */ 159 "high", /* 01 */ 160 "rsv", /* 10 */ 161 "low" /* 11 */ 162 }; 163 164 struct ndp_opts opts = {}; 165 166 #define NDP_XO_VERSION "1" 167 168 bool 169 valid_type(int if_type) 170 { 171 switch (if_type) { 172 case IFT_ETHER: 173 case IFT_FDDI: 174 case IFT_ISO88023: 175 case IFT_ISO88024: 176 case IFT_ISO88025: 177 case IFT_L2VLAN: 178 case IFT_BRIDGE: 179 return (true); 180 break; 181 } 182 return (false); 183 } 184 185 static int32_t 186 utc_offset(void) 187 { 188 time_t t; 189 struct tm *tm; 190 191 t = time(NULL); 192 tm = localtime(&t); 193 194 assert(tm->tm_gmtoff > INT32_MIN && tm->tm_gmtoff < INT32_MAX); 195 196 return (tm->tm_gmtoff); 197 } 198 199 int 200 main(int argc, char **argv) 201 { 202 int ch, mode = 0; 203 char *arg = NULL; 204 int ret = 0; 205 206 pid = getpid(); 207 thiszone = utc_offset(); 208 209 argc = xo_parse_args(argc, argv); 210 if (argc < 0) 211 exit(1); 212 xo_set_version(NDP_XO_VERSION); 213 xo_open_container("ndp"); 214 215 while ((ch = getopt(argc, argv, "acd:f:Ii:nprstA:HPR")) != -1) 216 switch (ch) { 217 case 'a': 218 case 'c': 219 case 'p': 220 case 'r': 221 case 'H': 222 case 'P': 223 case 'R': 224 case 's': 225 case 'I': 226 if (mode) { 227 usage(); 228 /*NOTREACHED*/ 229 } 230 mode = ch; 231 arg = NULL; 232 break; 233 case 'f': 234 exit(file(optarg) ? 1 : 0); 235 case 'd': 236 case 'i': 237 if (mode) { 238 usage(); 239 /*NOTREACHED*/ 240 } 241 mode = ch; 242 arg = optarg; 243 break; 244 case 'n': 245 opts.nflag = true; 246 break; 247 case 't': 248 opts.tflag = true; 249 break; 250 case 'A': 251 if (mode) { 252 usage(); 253 /*NOTREACHED*/ 254 } 255 mode = 'a'; 256 repeat = atoi(optarg); 257 if (repeat < 0) { 258 usage(); 259 /*NOTREACHED*/ 260 } 261 break; 262 default: 263 usage(); 264 } 265 266 argc -= optind; 267 argv += optind; 268 269 switch (mode) { 270 case 'a': 271 case 'c': 272 if (argc != 0) { 273 usage(); 274 /*NOTREACHED*/ 275 } 276 dump(0, mode == 'c'); 277 break; 278 case 'd': 279 if (argc != 0) { 280 usage(); 281 /*NOTREACHED*/ 282 } 283 xo_open_list("neighbor-cache"); 284 ret = delete(arg); 285 xo_close_list("neighbor-cache"); 286 break; 287 case 'I': 288 #ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */ 289 if (argc > 1) { 290 usage(); 291 /*NOTREACHED*/ 292 } else if (argc == 1) { 293 if (strcmp(*argv, "delete") == 0 || 294 if_nametoindex(*argv)) 295 setdefif(*argv); 296 else 297 xo_errx(1, "invalid interface %s", *argv); 298 } 299 getdefif(); /* always call it to print the result */ 300 break; 301 #else 302 xo_errx(1, "not supported yet"); 303 /*NOTREACHED*/ 304 #endif 305 case 'p': 306 if (argc != 0) { 307 usage(); 308 /*NOTREACHED*/ 309 } 310 plist(); 311 break; 312 case 'i': 313 ifinfo(arg, argc, argv); 314 break; 315 case 'r': 316 if (argc != 0) { 317 usage(); 318 /*NOTREACHED*/ 319 } 320 rtrlist(); 321 break; 322 case 's': 323 if (argc < 2 || argc > 4) 324 usage(); 325 exit(set(argc, argv) ? 1 : 0); 326 case 'H': 327 if (argc != 0) { 328 usage(); 329 /*NOTREACHED*/ 330 } 331 harmonize_rtr(); 332 break; 333 case 'P': 334 if (argc != 0) { 335 usage(); 336 /*NOTREACHED*/ 337 } 338 pfx_flush(); 339 break; 340 case 'R': 341 if (argc != 0) { 342 usage(); 343 /*NOTREACHED*/ 344 } 345 rtr_flush(); 346 break; 347 case 0: 348 if (argc != 1) { 349 usage(); 350 /*NOTREACHED*/ 351 } 352 get(argv[0]); 353 break; 354 } 355 xo_close_container("ndp"); 356 xo_finish(); 357 358 return (ret); 359 } 360 361 /* 362 * Process a file to set standard ndp entries 363 */ 364 static int 365 file(char *name) 366 { 367 FILE *fp; 368 int i, retval; 369 char line[100], arg[5][50], *args[5], *p; 370 371 if ((fp = fopen(name, "r")) == NULL) 372 xo_err(1, "cannot open %s", name); 373 args[0] = &arg[0][0]; 374 args[1] = &arg[1][0]; 375 args[2] = &arg[2][0]; 376 args[3] = &arg[3][0]; 377 args[4] = &arg[4][0]; 378 retval = 0; 379 while (fgets(line, sizeof(line), fp) != NULL) { 380 if ((p = strchr(line, '#')) != NULL) 381 *p = '\0'; 382 for (p = line; isblank(*p); p++); 383 if (*p == '\n' || *p == '\0') 384 continue; 385 i = sscanf(line, "%49s %49s %49s %49s %49s", 386 arg[0], arg[1], arg[2], arg[3], arg[4]); 387 if (i < 2) { 388 xo_warnx("bad line: %s", line); 389 retval = 1; 390 continue; 391 } 392 if (set(i, args)) 393 retval = 1; 394 } 395 fclose(fp); 396 return (retval); 397 } 398 399 static void 400 getsocket(void) 401 { 402 if (s < 0) { 403 s = socket(PF_ROUTE, SOCK_RAW, 0); 404 if (s < 0) { 405 xo_err(1, "socket"); 406 /* NOTREACHED */ 407 } 408 } 409 } 410 411 static struct sockaddr_in6 so_mask = { 412 .sin6_len = sizeof(so_mask), 413 .sin6_family = AF_INET6 414 }; 415 static struct sockaddr_in6 blank_sin = { 416 .sin6_len = sizeof(blank_sin), 417 .sin6_family = AF_INET6 418 }; 419 static struct sockaddr_in6 sin_m; 420 static struct sockaddr_dl blank_sdl = { 421 .sdl_len = sizeof(blank_sdl), 422 .sdl_family = AF_LINK 423 }; 424 static struct sockaddr_dl sdl_m; 425 #ifdef WITHOUT_NETLINK 426 static struct { 427 struct rt_msghdr m_rtm; 428 char m_space[512]; 429 } m_rtmsg; 430 #endif 431 432 /* 433 * Set an individual neighbor cache entry 434 */ 435 static int 436 set(int argc, char **argv) 437 { 438 struct sockaddr_in6 *sin = &sin_m; 439 int gai_error; 440 u_char *ea; 441 char *host = argv[0], *eaddr = argv[1]; 442 443 argc -= 2; 444 argv += 2; 445 sdl_m = blank_sdl; 446 sin_m = blank_sin; 447 448 gai_error = getaddr(host, sin); 449 if (gai_error) { 450 xo_warnx("%s: %s", host, gai_strerror(gai_error)); 451 return 1; 452 } 453 454 ea = (u_char *)LLADDR(&sdl_m); 455 if (ndp_ether_aton(eaddr, ea) == 0) 456 sdl_m.sdl_alen = 6; 457 while (argc-- > 0) { 458 if (strncmp(argv[0], "temp", 4) == 0) { 459 struct timeval now; 460 461 gettimeofday(&now, 0); 462 opts.expire_time = now.tv_sec + 20 * 60; 463 } else if (strncmp(argv[0], "proxy", 5) == 0) 464 opts.flags |= RTF_ANNOUNCE; 465 argv++; 466 } 467 468 #ifndef WITHOUT_NETLINK 469 return (set_nl(0, sin, &sdl_m, host)); 470 #else 471 struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); 472 struct sockaddr_dl *sdl; 473 474 getsocket(); 475 476 if (rtmsg(RTM_GET) < 0) { 477 xo_errx(1, "RTM_GET(%s) failed", host); 478 /* NOTREACHED */ 479 } 480 sin = (struct sockaddr_in6 *)(rtm + 1); 481 sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin); 482 if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) { 483 if (sdl->sdl_family == AF_LINK && 484 !(rtm->rtm_flags & RTF_GATEWAY)) { 485 if (valid_type(sdl->sdl_type)) 486 goto overwrite; 487 } 488 xo_warnx("cannot configure a new entry"); 489 return 1; 490 } 491 492 overwrite: 493 if (sdl->sdl_family != AF_LINK) { 494 xo_warnx("cannot intuit interface index and type for %s", host); 495 return (1); 496 } 497 sdl_m.sdl_type = sdl->sdl_type; 498 sdl_m.sdl_index = sdl->sdl_index; 499 return (rtmsg(RTM_ADD)); 500 #endif 501 } 502 503 int 504 getaddr(char *host, struct sockaddr_in6 *sin6) 505 { 506 struct addrinfo hints = { .ai_family = AF_INET6 }; 507 struct addrinfo *res; 508 509 int gai_error = getaddrinfo(host, NULL, &hints, &res); 510 if (gai_error != 0) 511 return (gai_error); 512 sin6->sin6_family = AF_INET6; 513 sin6->sin6_len = sizeof(*sin6); 514 sin6->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; 515 sin6->sin6_scope_id = 516 ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; 517 return (0); 518 } 519 520 /* 521 * Display an individual neighbor cache entry 522 */ 523 static void 524 get(char *host) 525 { 526 struct sockaddr_in6 *sin = &sin_m; 527 int gai_error; 528 529 sin_m = blank_sin; 530 531 gai_error = getaddr(host, sin); 532 if (gai_error) { 533 xo_warnx("%s: %s", host, gai_strerror(gai_error)); 534 return; 535 } 536 if (dump(sin, 0) == 0) { 537 getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, 538 sizeof(host_buf), NULL ,0, 539 (opts.nflag ? NI_NUMERICHOST : 0)); 540 xo_errx(1, "%s (%s) -- no entry", host, host_buf); 541 } 542 } 543 544 #ifdef WITHOUT_NETLINK 545 /* 546 * Delete a neighbor cache entry 547 */ 548 static int 549 delete_rtsock(char *host) 550 { 551 struct sockaddr_in6 *sin = &sin_m; 552 register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 553 register char *cp = m_rtmsg.m_space; 554 struct sockaddr_dl *sdl; 555 int gai_error; 556 557 getsocket(); 558 sin_m = blank_sin; 559 560 gai_error = getaddr(host, sin); 561 if (gai_error) { 562 xo_warnx("%s: %s", host, gai_strerror(gai_error)); 563 return 1; 564 } 565 566 if (rtmsg(RTM_GET) < 0) { 567 xo_errx(1, "RTM_GET(%s) failed", host); 568 /* NOTREACHED */ 569 } 570 sin = (struct sockaddr_in6 *)(rtm + 1); 571 sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin); 572 if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) { 573 if (sdl->sdl_family == AF_LINK && 574 !(rtm->rtm_flags & RTF_GATEWAY)) { 575 goto delete; 576 } 577 xo_warnx("delete: cannot delete non-NDP entry"); 578 return 1; 579 } 580 581 delete: 582 if (sdl->sdl_family != AF_LINK) { 583 xo_warnx("cannot locate %s", host); 584 return (1); 585 } 586 /* 587 * need to reinit the field because it has rt_key 588 * but we want the actual address 589 */ 590 NEXTADDR(RTA_DST, sin_m); 591 rtm->rtm_flags |= RTF_LLDATA; 592 if (rtmsg(RTM_DELETE) == 0) { 593 getnameinfo((struct sockaddr *)sin, 594 sin->sin6_len, host_buf, 595 sizeof(host_buf), NULL, 0, 596 (opts.nflag ? NI_NUMERICHOST : 0)); 597 xo_open_instance("neighbor-cache"); 598 599 char *ifname = if_indextoname(sdl->sdl_index, ifix_buf); 600 if (ifname == NULL) { 601 strlcpy(ifix_buf, "?", sizeof(ifix_buf)); 602 ifname = ifix_buf; 603 } 604 char abuf[INET6_ADDRSTRLEN]; 605 inet_ntop(AF_INET6, &sin->sin6_addr, abuf, sizeof(abuf)); 606 607 xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf); 608 xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname); 609 xo_close_instance("neighbor-cache"); 610 } 611 612 return 0; 613 } 614 615 /* 616 * Dump the entire neighbor cache 617 */ 618 static int 619 dump_rtsock(struct sockaddr_in6 *addr, int cflag) 620 { 621 int mib[6]; 622 size_t needed; 623 char *lim, *buf, *next; 624 struct rt_msghdr *rtm; 625 struct sockaddr_in6 *sin; 626 struct sockaddr_dl *sdl; 627 struct timeval now; 628 time_t expire; 629 int addrwidth; 630 int llwidth; 631 int ifwidth; 632 char flgbuf[8]; 633 char *ifname; 634 635 /* Print header */ 636 if (!opts.tflag && !cflag) { 637 char xobuf[200]; 638 snprintf(xobuf, sizeof(xobuf), 639 "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:%%1s} {T:%%5s}\n", 640 W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF); 641 xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags"); 642 } 643 xo_open_list("neighbor-cache"); 644 again:; 645 mib[0] = CTL_NET; 646 mib[1] = PF_ROUTE; 647 mib[2] = 0; 648 mib[3] = AF_INET6; 649 mib[4] = NET_RT_FLAGS; 650 #ifdef RTF_LLINFO 651 mib[5] = RTF_LLINFO; 652 #else 653 mib[5] = 0; 654 #endif 655 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 656 xo_err(1, "sysctl(PF_ROUTE estimate)"); 657 if (needed > 0) { 658 if ((buf = malloc(needed)) == NULL) 659 xo_err(1, "malloc"); 660 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 661 xo_err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)"); 662 lim = buf + needed; 663 } else 664 buf = lim = NULL; 665 666 int count = 0; 667 for (next = buf; next && next < lim; next += rtm->rtm_msglen) { 668 int isrouter = 0, prbs = 0; 669 670 rtm = (struct rt_msghdr *)next; 671 sin = (struct sockaddr_in6 *)(rtm + 1); 672 sdl = (struct sockaddr_dl *)((char *)sin + 673 ALIGN(sin->sin6_len)); 674 675 /* 676 * Some OSes can produce a route that has the LINK flag but 677 * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD 678 * and BSD/OS, where xx is not the interface identifier on 679 * lo0). Such routes entry would annoy getnbrinfo() below, 680 * so we skip them. 681 * XXX: such routes should have the GATEWAY flag, not the 682 * LINK flag. However, there is rotten routing software 683 * that advertises all routes that have the GATEWAY flag. 684 * Thus, KAME kernel intentionally does not set the LINK flag. 685 * What is to be fixed is not ndp, but such routing software 686 * (and the kernel workaround)... 687 */ 688 if (sdl->sdl_family != AF_LINK) 689 continue; 690 691 if (!(rtm->rtm_flags & RTF_HOST)) 692 continue; 693 694 if (addr) { 695 if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr, 696 &sin->sin6_addr) == 0 || 697 addr->sin6_scope_id != sin->sin6_scope_id) 698 continue; 699 } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr)) 700 continue; 701 count++; 702 if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) || 703 IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) { 704 /* XXX: should scope id be filled in the kernel? */ 705 if (sin->sin6_scope_id == 0) 706 sin->sin6_scope_id = sdl->sdl_index; 707 } 708 getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, 709 sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0)); 710 if (cflag) { 711 #ifdef RTF_WASCLONED 712 if (rtm->rtm_flags & RTF_WASCLONED) 713 delete(host_buf); 714 #elif defined(RTF_CLONED) 715 if (rtm->rtm_flags & RTF_CLONED) 716 delete(host_buf); 717 #else 718 if (rtm->rtm_flags & RTF_PINNED) 719 continue; 720 delete(host_buf); 721 #endif 722 continue; 723 } 724 gettimeofday(&now, 0); 725 if (opts.tflag) 726 ts_print(&now); 727 728 addrwidth = strlen(host_buf); 729 if (addrwidth < W_ADDR) 730 addrwidth = W_ADDR; 731 llwidth = strlen(ether_str(sdl)); 732 if (W_ADDR + W_LL - addrwidth > llwidth) 733 llwidth = W_ADDR + W_LL - addrwidth; 734 ifname = if_indextoname(sdl->sdl_index, ifix_buf); 735 if (ifname == NULL) { 736 strlcpy(ifix_buf, "?", sizeof(ifix_buf)); 737 ifname = ifix_buf; 738 } 739 ifwidth = strlen(ifname); 740 if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) 741 ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; 742 743 xo_open_instance("neighbor-cache"); 744 /* Compose format string for libxo, as it doesn't support *.* */ 745 char xobuf[200]; 746 snprintf(xobuf, sizeof(xobuf), 747 "{:address/%%-%d.%ds/%%s} {:mac-address/%%-%d.%ds/%%s} {:interface/%%%d.%ds/%%s}", 748 addrwidth, addrwidth, llwidth, llwidth, ifwidth, ifwidth); 749 xo_emit(xobuf, host_buf, ether_str(sdl), ifname); 750 751 /* Print neighbor discovery specific information */ 752 expire = rtm->rtm_rmx.rmx_expire; 753 int expire_in = expire - now.tv_sec; 754 if (expire > now.tv_sec) 755 xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", sec2str(expire_in), expire_in); 756 else if (expire == 0) 757 xo_emit("{d:/ %-9.9s}{en:permanent/true}", "permanent"); 758 else 759 xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in); 760 761 char *lle_state = ""; 762 switch (rtm->rtm_rmx.rmx_state) { 763 case ND6_LLINFO_NOSTATE: 764 lle_state = "N"; 765 break; 766 #ifdef ND6_LLINFO_WAITDELETE 767 case ND6_LLINFO_WAITDELETE: 768 lle_state = "W"; 769 break; 770 #endif 771 case ND6_LLINFO_INCOMPLETE: 772 lle_state = "I"; 773 break; 774 case ND6_LLINFO_REACHABLE: 775 lle_state = "R"; 776 break; 777 case ND6_LLINFO_STALE: 778 lle_state = "S"; 779 break; 780 case ND6_LLINFO_DELAY: 781 lle_state = "D"; 782 break; 783 case ND6_LLINFO_PROBE: 784 lle_state = "P"; 785 break; 786 default: 787 lle_state = "?"; 788 break; 789 } 790 xo_emit(" {:neighbor-state/%s}", lle_state); 791 792 isrouter = rtm->rtm_flags & RTF_GATEWAY; 793 prbs = rtm->rtm_rmx.rmx_pksent; 794 795 /* 796 * other flags. R: router, P: proxy, W: ?? 797 */ 798 if ((rtm->rtm_addrs & RTA_NETMASK) == 0) { 799 snprintf(flgbuf, sizeof(flgbuf), "%s%s", 800 isrouter ? "R" : "", 801 (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); 802 } else { 803 #if 0 /* W and P are mystery even for us */ 804 sin = (struct sockaddr_in6 *) 805 (sdl->sdl_len + (char *)sdl); 806 snprintf(flgbuf, sizeof(flgbuf), "%s%s%s%s", 807 isrouter ? "R" : "", 808 !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr) ? "P" : "", 809 (sin->sin6_len != sizeof(struct sockaddr_in6)) ? "W" : "", 810 (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); 811 #else 812 snprintf(flgbuf, sizeof(flgbuf), "%s%s", 813 isrouter ? "R" : "", 814 (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); 815 #endif 816 } 817 xo_emit(" {:nd-flags/%s}", flgbuf); 818 819 if (prbs) 820 xo_emit("{d:/ %d}", prbs); 821 822 xo_emit("\n"); 823 xo_close_instance("neighbor-cache"); 824 } 825 if (buf != NULL) 826 free(buf); 827 828 if (repeat) { 829 xo_emit("\n"); 830 xo_flush(); 831 sleep(repeat); 832 goto again; 833 } 834 835 xo_close_list("neighbor-cache"); 836 837 return (count); 838 } 839 #endif 840 841 842 static int 843 delete(char *host) 844 { 845 #ifndef WITHOUT_NETLINK 846 return (delete_nl(0, host, true)); /* do warn */ 847 #else 848 return (delete_rtsock(host)); 849 #endif 850 } 851 852 static int 853 dump(struct sockaddr_in6 *addr, int cflag) 854 { 855 #ifndef WITHOUT_NETLINK 856 return (print_entries_nl(0, addr, cflag)); 857 #else 858 return (dump_rtsock(addr, cflag)); 859 #endif 860 } 861 862 static struct in6_nbrinfo * 863 getnbrinfo(struct in6_addr *addr, int ifindex, int warning) 864 { 865 static struct in6_nbrinfo nbi; 866 int sock; 867 868 if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 869 xo_err(1, "socket"); 870 871 bzero(&nbi, sizeof(nbi)); 872 if_indextoname(ifindex, nbi.ifname); 873 nbi.addr = *addr; 874 if (ioctl(sock, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) { 875 if (warning) 876 xo_warn("ioctl(SIOCGNBRINFO_IN6)"); 877 close(sock); 878 return(NULL); 879 } 880 881 close(sock); 882 return(&nbi); 883 } 884 885 char * 886 ether_str(struct sockaddr_dl *sdl) 887 { 888 static char hbuf[NI_MAXHOST]; 889 890 if (sdl->sdl_alen == ETHER_ADDR_LEN) { 891 strlcpy(hbuf, ether_ntoa((struct ether_addr *)LLADDR(sdl)), 892 sizeof(hbuf)); 893 } else if (sdl->sdl_alen) { 894 int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; 895 snprintf(hbuf, sizeof(hbuf), "%s", link_ntoa(sdl) + n); 896 } else 897 snprintf(hbuf, sizeof(hbuf), "(incomplete)"); 898 899 return(hbuf); 900 } 901 902 static int 903 ndp_ether_aton(char *a, u_char *n) 904 { 905 int i, o[6]; 906 907 i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2], 908 &o[3], &o[4], &o[5]); 909 if (i != 6) { 910 xo_warnx("invalid Ethernet address '%s'", a); 911 return (1); 912 } 913 for (i = 0; i < 6; i++) 914 n[i] = o[i]; 915 return (0); 916 } 917 918 static void 919 usage(void) 920 { 921 printf("usage: ndp [-nt] hostname\n"); 922 printf(" ndp [-nt] -a | -c | -p | -r | -H | -P | -R\n"); 923 printf(" ndp [-nt] -A wait\n"); 924 printf(" ndp [-nt] -d hostname\n"); 925 printf(" ndp [-nt] -f filename\n"); 926 printf(" ndp [-nt] -i interface [flags...]\n"); 927 #ifdef SIOCSDEFIFACE_IN6 928 printf(" ndp [-nt] -I [interface|delete]\n"); 929 #endif 930 printf(" ndp [-nt] -s nodename etheraddr [temp] [proxy]\n"); 931 exit(1); 932 } 933 934 #ifdef WITHOUT_NETLINK 935 static int 936 rtmsg(int cmd) 937 { 938 static int seq; 939 int rlen; 940 register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 941 register char *cp = m_rtmsg.m_space; 942 register int l; 943 944 errno = 0; 945 if (cmd == RTM_DELETE) 946 goto doit; 947 bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 948 rtm->rtm_flags = opts.flags; 949 rtm->rtm_version = RTM_VERSION; 950 951 switch (cmd) { 952 default: 953 xo_errx(1, "internal wrong cmd"); 954 case RTM_ADD: 955 rtm->rtm_addrs |= RTA_GATEWAY; 956 if (opts.expire_time) { 957 rtm->rtm_rmx.rmx_expire = opts.expire_time; 958 rtm->rtm_inits = RTV_EXPIRE; 959 } 960 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); 961 /* FALLTHROUGH */ 962 case RTM_GET: 963 rtm->rtm_addrs |= RTA_DST; 964 } 965 966 NEXTADDR(RTA_DST, sin_m); 967 NEXTADDR(RTA_GATEWAY, sdl_m); 968 969 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 970 doit: 971 l = rtm->rtm_msglen; 972 rtm->rtm_seq = ++seq; 973 rtm->rtm_type = cmd; 974 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 975 if (errno != ESRCH || cmd != RTM_DELETE) { 976 xo_err(1, "writing to routing socket"); 977 /* NOTREACHED */ 978 } 979 } 980 do { 981 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 982 } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq || 983 rtm->rtm_pid != pid)); 984 if (l < 0) 985 xo_warn("read from routing socket"); 986 return (0); 987 } 988 #endif 989 990 static void 991 ifinfo(char *ifname, int argc, char **argv) 992 { 993 struct in6_ndireq nd; 994 int i, sock; 995 u_int32_t newflags; 996 #ifdef IPV6CTL_USETEMPADDR 997 u_int8_t nullbuf[8]; 998 #endif 999 1000 if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 1001 xo_err(1, "socket"); 1002 /* NOTREACHED */ 1003 } 1004 bzero(&nd, sizeof(nd)); 1005 strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); 1006 if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { 1007 xo_err(1, "ioctl(SIOCGIFINFO_IN6)"); 1008 /* NOTREACHED */ 1009 } 1010 #define ND nd.ndi 1011 newflags = ND.flags; 1012 for (i = 0; i < argc; i++) { 1013 int clear = 0; 1014 char *cp = argv[i]; 1015 1016 if (*cp == '-') { 1017 clear = 1; 1018 cp++; 1019 } 1020 1021 #define SETFLAG(s, f) do { \ 1022 if (strcmp(cp, (s)) == 0) { \ 1023 if (clear) \ 1024 newflags &= ~(f); \ 1025 else \ 1026 newflags |= (f); \ 1027 } \ 1028 } while (0) 1029 /* 1030 * XXX: this macro is not 100% correct, in that it matches "nud" against 1031 * "nudbogus". But we just let it go since this is minor. 1032 */ 1033 #define SETVALUE(f, v) do { \ 1034 char *valptr; \ 1035 unsigned long newval; \ 1036 v = 0; /* unspecified */ \ 1037 if (strncmp(cp, f, strlen(f)) == 0) { \ 1038 valptr = strchr(cp, '='); \ 1039 if (valptr == NULL) \ 1040 xo_err(1, "syntax error in %s field", (f)); \ 1041 errno = 0; \ 1042 newval = strtoul(++valptr, NULL, 0); \ 1043 if (errno) \ 1044 xo_err(1, "syntax error in %s's value", (f)); \ 1045 v = newval; \ 1046 } \ 1047 } while (0) 1048 1049 SETFLAG("disabled", ND6_IFF_IFDISABLED); 1050 SETFLAG("nud", ND6_IFF_PERFORMNUD); 1051 #ifdef ND6_IFF_ACCEPT_RTADV 1052 SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV); 1053 #endif 1054 #ifdef ND6_IFF_AUTO_LINKLOCAL 1055 SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL); 1056 #endif 1057 #ifdef ND6_IFF_NO_PREFER_IFACE 1058 SETFLAG("no_prefer_iface", ND6_IFF_NO_PREFER_IFACE); 1059 #endif 1060 SETVALUE("basereachable", ND.basereachable); 1061 SETVALUE("retrans", ND.retrans); 1062 SETVALUE("curhlim", ND.chlim); 1063 1064 ND.flags = newflags; 1065 if (ioctl(sock, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) { 1066 xo_err(1, "ioctl(SIOCSIFINFO_IN6)"); 1067 /* NOTREACHED */ 1068 } 1069 #undef SETFLAG 1070 #undef SETVALUE 1071 } 1072 1073 if (!ND.initialized) { 1074 xo_errx(1, "%s: not initialized yet", ifname); 1075 /* NOTREACHED */ 1076 } 1077 1078 if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { 1079 xo_err(1, "ioctl(SIOCGIFINFO_IN6)"); 1080 /* NOTREACHED */ 1081 } 1082 xo_open_container("ifinfo"); 1083 1084 xo_emit("{e:interface/%s}", ifname); 1085 xo_emit("linkmtu={:linkmtu/%d}", ND.linkmtu); 1086 xo_emit(", maxmtu={:maxmtu/%d}", ND.maxmtu); 1087 xo_emit(", curhlim={:curhlim/%d}", ND.chlim); 1088 xo_emit("{d:/, basereachable=%ds%dms}{e:basereachable_ms/%u}", 1089 ND.basereachable / 1000, ND.basereachable % 1000, ND.basereachable); 1090 xo_emit("{d:/, reachable=%ds}{e:reachable_ms/%u}", ND.reachable, ND.reachable * 1000); 1091 xo_emit("{d:/, retrans=%ds%dms}{e:retrans_ms/%u}", ND.retrans / 1000, ND.retrans % 1000, 1092 ND.retrans); 1093 #ifdef IPV6CTL_USETEMPADDR 1094 memset(nullbuf, 0, sizeof(nullbuf)); 1095 if (memcmp(nullbuf, ND.randomid, sizeof(nullbuf)) != 0) { 1096 int j; 1097 u_int8_t *rbuf; 1098 1099 for (i = 0; i < 3; i++) { 1100 const char *txt, *field; 1101 switch (i) { 1102 case 0: 1103 txt = "\nRandom seed(0): "; 1104 field = "seed_0"; 1105 rbuf = ND.randomseed0; 1106 break; 1107 case 1: 1108 txt = "\nRandom seed(1): "; 1109 field = "seed_1"; 1110 rbuf = ND.randomseed1; 1111 break; 1112 case 2: 1113 txt = "\nRandom ID: "; 1114 field = "random_id"; 1115 rbuf = ND.randomid; 1116 break; 1117 default: 1118 xo_errx(1, "impossible case for tempaddr display"); 1119 } 1120 char abuf[20], xobuf[200]; 1121 for (j = 0; j < 8; j++) 1122 snprintf(&abuf[j * 2], sizeof(abuf), "%02X", rbuf[j]); 1123 snprintf(xobuf, sizeof(xobuf), "%s{:%s/%%s}", txt, field); 1124 xo_emit(xobuf, abuf); 1125 } 1126 } 1127 #endif /* IPV6CTL_USETEMPADDR */ 1128 if (ND.flags) { 1129 xo_emit("\nFlags: {e:flags/%u}", ND.flags); 1130 xo_open_list("flags_pretty"); 1131 #ifdef ND6_IFF_IFDISABLED 1132 if ((ND.flags & ND6_IFF_IFDISABLED)) 1133 xo_emit("{l:%s} ", "disabled"); 1134 #endif 1135 if ((ND.flags & ND6_IFF_PERFORMNUD)) 1136 xo_emit("{l:%s} ", "nud"); 1137 #ifdef ND6_IFF_ACCEPT_RTADV 1138 if ((ND.flags & ND6_IFF_ACCEPT_RTADV)) 1139 xo_emit("{l:%s} ", "accept_rtadv"); 1140 #endif 1141 #ifdef ND6_IFF_AUTO_LINKLOCAL 1142 if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL)) 1143 xo_emit("{l:%s} ", "auto_linklocal"); 1144 #endif 1145 #ifdef ND6_IFF_NO_PREFER_IFACE 1146 if ((ND.flags & ND6_IFF_NO_PREFER_IFACE)) 1147 xo_emit("{l:%s} ", "no_prefer_iface"); 1148 #endif 1149 xo_close_list("flags"); 1150 } 1151 xo_emit("\n"); 1152 #undef ND 1153 xo_close_container("ifinfo"); 1154 1155 close(sock); 1156 } 1157 1158 #ifndef ND_RA_FLAG_RTPREF_MASK /* XXX: just for compilation on *BSD release */ 1159 #define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ 1160 #endif 1161 1162 static void 1163 rtrlist(void) 1164 { 1165 int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST }; 1166 char *buf; 1167 struct in6_defrouter *p, *ep; 1168 size_t l; 1169 struct timeval now; 1170 1171 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) { 1172 xo_err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)"); 1173 /*NOTREACHED*/ 1174 } 1175 if (l == 0) 1176 return; 1177 buf = malloc(l); 1178 if (!buf) { 1179 xo_err(1, "malloc"); 1180 /*NOTREACHED*/ 1181 } 1182 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { 1183 xo_err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)"); 1184 /*NOTREACHED*/ 1185 } 1186 1187 xo_open_list("router-list"); 1188 1189 ep = (struct in6_defrouter *)(buf + l); 1190 for (p = (struct in6_defrouter *)buf; p < ep; p++) { 1191 int rtpref; 1192 char abuf[INET6_ADDRSTRLEN], *paddr; 1193 1194 if (getnameinfo((struct sockaddr *)&p->rtaddr, 1195 p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0, 1196 (opts.nflag ? NI_NUMERICHOST : 0)) != 0) 1197 strlcpy(host_buf, "?", sizeof(host_buf)); 1198 if (opts.nflag) 1199 paddr = host_buf; 1200 else { 1201 inet_ntop(AF_INET6, &p->rtaddr.sin6_addr, abuf, sizeof(abuf)); 1202 paddr = abuf; 1203 } 1204 1205 xo_open_instance("router-list"); 1206 xo_emit("{:hostname/%s}{e:address/%s} if={:interface/%s}", 1207 host_buf, paddr, 1208 if_indextoname(p->if_index, ifix_buf)); 1209 xo_open_list("flags_pretty"); 1210 char rflags[6] = {}, *pflags = rflags; 1211 if (p->flags & ND_RA_FLAG_MANAGED) { 1212 *pflags++ = 'M'; 1213 xo_emit("{el:%s}", "managed"); 1214 } 1215 if (p->flags & ND_RA_FLAG_OTHER) { 1216 *pflags++ = 'O'; 1217 xo_emit("{el:%s}", "other"); 1218 } 1219 #ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG 1220 if (p->flags & ND_RA_FLAG_IPV6_ONLY) { 1221 *pflags++ = 'S'; 1222 xo_emit("{el:%s}", "ipv6only"); 1223 } 1224 #endif 1225 xo_close_list("flags_pretty"); 1226 xo_emit(", flags={:flags/%s}", rflags); 1227 1228 rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff; 1229 xo_emit(", pref={:preference/%s}", rtpref_str[rtpref]); 1230 1231 gettimeofday(&now, 0); 1232 if (p->expire == 0) 1233 xo_emit(", expire=Never\n{en:permanent/true}"); 1234 else 1235 xo_emit("{d:/, expire=%s\n}{e:expires_sec/%ld}", 1236 sec2str(p->expire - now.tv_sec), 1237 (long)p->expire - now.tv_sec); 1238 xo_close_instance("router-list"); 1239 } 1240 free(buf); 1241 xo_close_list("router-list"); 1242 } 1243 1244 static void 1245 plist(void) 1246 { 1247 int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST }; 1248 char *buf; 1249 struct in6_prefix *p, *ep, *n; 1250 struct sockaddr_in6 *advrtr; 1251 size_t l; 1252 struct timeval now; 1253 const int niflags = NI_NUMERICHOST; 1254 int ninflags = opts.nflag ? NI_NUMERICHOST : 0; 1255 char namebuf[NI_MAXHOST]; 1256 1257 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) { 1258 xo_err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)"); 1259 /*NOTREACHED*/ 1260 } 1261 buf = malloc(l); 1262 if (!buf) { 1263 xo_err(1, "malloc"); 1264 /*NOTREACHED*/ 1265 } 1266 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { 1267 xo_err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)"); 1268 /*NOTREACHED*/ 1269 } 1270 1271 xo_open_list("prefix-list"); 1272 1273 ep = (struct in6_prefix *)(buf + l); 1274 for (p = (struct in6_prefix *)buf; p < ep; p = n) { 1275 advrtr = (struct sockaddr_in6 *)(p + 1); 1276 n = (struct in6_prefix *)&advrtr[p->advrtrs]; 1277 1278 xo_open_instance("prefix-list"); 1279 if (getnameinfo((struct sockaddr *)&p->prefix, 1280 p->prefix.sin6_len, namebuf, sizeof(namebuf), 1281 NULL, 0, niflags) != 0) 1282 strlcpy(namebuf, "?", sizeof(namebuf)); 1283 xo_emit("{:prefix/%s%s%d} if={:interface/%s}\n", namebuf, "/", 1284 p->prefixlen, if_indextoname(p->if_index, ifix_buf)); 1285 1286 gettimeofday(&now, 0); 1287 /* 1288 * meaning of fields, especially flags, is very different 1289 * by origin. notify the difference to the users. 1290 */ 1291 char flags[10] = {}, *pflags = flags; 1292 xo_open_list("flags_pretty"); 1293 if (p->raflags.onlink) { 1294 *pflags++ = 'L'; 1295 xo_emit("{el:%s}", "ra_onlink"); 1296 } 1297 if (p->raflags.autonomous) { 1298 *pflags++ = 'A'; 1299 xo_emit("{el:%s}", "ra_autonomous"); 1300 } 1301 if (p->flags & NDPRF_ONLINK) { 1302 *pflags++ = 'O'; 1303 xo_emit("{el:%s}", "is_onlink"); 1304 } 1305 if (p->flags & NDPRF_DETACHED) { 1306 *pflags++ = 'D'; 1307 xo_emit("{el:%s}", "is_detached"); 1308 } 1309 #ifdef NDPRF_HOME 1310 if (p->flags & NDPRF_HOME) { 1311 *pflags++ = 'H'; 1312 xo_emit("{el:%s}", "is_home"); 1313 } 1314 #endif 1315 xo_close_list("flags_pretty"); 1316 xo_emit("flags={:flags/%s}", flags); 1317 int expire_in = p->expire - now.tv_sec; 1318 1319 if (p->vltime == ND6_INFINITE_LIFETIME) 1320 xo_emit(" vltime=infinity{e:valid-lifetime/%lu}", 1321 (unsigned long)p->vltime); 1322 else 1323 xo_emit(" vltime={:valid-lifetime/%lu}", 1324 (unsigned long)p->vltime); 1325 if (p->pltime == ND6_INFINITE_LIFETIME) 1326 xo_emit(", pltime=infinity{e:preferred-lifetime/%lu}", 1327 (unsigned long)p->pltime); 1328 else 1329 xo_emit(", pltime={:preferred-lifetime/%lu}", 1330 (unsigned long)p->pltime); 1331 if (p->expire == 0) 1332 xo_emit(", expire=Never{en:permanent/true}"); 1333 else if (p->expire >= now.tv_sec) 1334 xo_emit(", expire=%s{e:expires_sec/%d}", 1335 sec2str(expire_in), expire_in); 1336 else 1337 xo_emit(", expired{e:expires_sec/%d}", expire_in); 1338 xo_emit(", ref={:refcount/%d}", p->refcnt); 1339 xo_emit("\n"); 1340 /* 1341 * "advertising router" list is meaningful only if the prefix 1342 * information is from RA. 1343 */ 1344 if (p->advrtrs) { 1345 int j; 1346 struct sockaddr_in6 *sin6; 1347 1348 sin6 = advrtr; 1349 xo_emit(" advertised by\n"); 1350 xo_open_list("advertising-routers"); 1351 for (j = 0; j < p->advrtrs; j++) { 1352 struct in6_nbrinfo *nbi; 1353 1354 xo_open_instance("advertising-routers"); 1355 if (getnameinfo((struct sockaddr *)sin6, 1356 sin6->sin6_len, namebuf, sizeof(namebuf), 1357 NULL, 0, ninflags) != 0) 1358 strlcpy(namebuf, "?", sizeof(namebuf)); 1359 char abuf[INET6_ADDRSTRLEN]; 1360 inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, 1361 sizeof(abuf)); 1362 1363 xo_emit(" {:hostname/%s}{e:address/%s}", 1364 namebuf, abuf); 1365 1366 nbi = getnbrinfo(&sin6->sin6_addr, 1367 p->if_index, 0); 1368 const char *state = ""; 1369 if (nbi) { 1370 switch (nbi->state) { 1371 case ND6_LLINFO_REACHABLE: 1372 case ND6_LLINFO_STALE: 1373 case ND6_LLINFO_DELAY: 1374 case ND6_LLINFO_PROBE: 1375 state = "reachable"; 1376 break; 1377 default: 1378 state = "unreachable"; 1379 } 1380 } else 1381 state = "no neighbor state"; 1382 xo_emit(" ({:state/%s})\n", state); 1383 sin6++; 1384 xo_close_instance("advertising-routers"); 1385 } 1386 xo_close_list("advertising-routers"); 1387 } else 1388 xo_emit(" No advertising router\n"); 1389 xo_close_instance("prefix-list"); 1390 } 1391 free(buf); 1392 1393 xo_close_list("prefix-list"); 1394 } 1395 1396 static void 1397 pfx_flush(void) 1398 { 1399 char dummyif[IFNAMSIZ+8]; 1400 int sock; 1401 1402 if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 1403 xo_err(1, "socket"); 1404 strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */ 1405 if (ioctl(sock, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0) 1406 xo_err(1, "ioctl(SIOCSPFXFLUSH_IN6)"); 1407 1408 close(sock); 1409 } 1410 1411 static void 1412 rtr_flush(void) 1413 { 1414 char dummyif[IFNAMSIZ+8]; 1415 int sock; 1416 1417 if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 1418 xo_err(1, "socket"); 1419 strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */ 1420 if (ioctl(sock, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0) 1421 xo_err(1, "ioctl(SIOCSRTRFLUSH_IN6)"); 1422 1423 close(sock); 1424 } 1425 1426 static void 1427 harmonize_rtr(void) 1428 { 1429 char dummyif[IFNAMSIZ+8]; 1430 int sock; 1431 1432 if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 1433 xo_err(1, "socket"); 1434 strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */ 1435 if (ioctl(sock, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0) 1436 xo_err(1, "ioctl(SIOCSNDFLUSH_IN6)"); 1437 1438 close(sock); 1439 } 1440 1441 #ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */ 1442 static void 1443 setdefif(char *ifname) 1444 { 1445 struct in6_ndifreq ndifreq; 1446 unsigned int ifindex; 1447 int sock; 1448 1449 if (strcasecmp(ifname, "delete") == 0) 1450 ifindex = 0; 1451 else { 1452 if ((ifindex = if_nametoindex(ifname)) == 0) 1453 xo_err(1, "failed to resolve i/f index for %s", ifname); 1454 } 1455 1456 if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 1457 xo_err(1, "socket"); 1458 1459 strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */ 1460 ndifreq.ifindex = ifindex; 1461 1462 if (ioctl(sock, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0) 1463 xo_err(1, "ioctl(SIOCSDEFIFACE_IN6)"); 1464 1465 close(sock); 1466 } 1467 1468 static void 1469 getdefif(void) 1470 { 1471 struct in6_ndifreq ndifreq; 1472 char ifname[IFNAMSIZ+8]; 1473 int sock; 1474 1475 if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 1476 xo_err(1, "socket"); 1477 1478 memset(&ndifreq, 0, sizeof(ndifreq)); 1479 strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */ 1480 1481 if (ioctl(sock, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0) 1482 xo_err(1, "ioctl(SIOCGDEFIFACE_IN6)"); 1483 1484 if (ndifreq.ifindex == 0) 1485 xo_emit("No default interface.\n"); 1486 else { 1487 if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL) 1488 xo_err(1, "failed to resolve ifname for index %lu", 1489 ndifreq.ifindex); 1490 xo_emit("ND default interface = {:default-interface/%s}\n", ifname); 1491 } 1492 1493 close(sock); 1494 } 1495 #endif /* SIOCSDEFIFACE_IN6 */ 1496 1497 char * 1498 sec2str(time_t total) 1499 { 1500 static char result[256]; 1501 int days, hours, mins, secs; 1502 int first = 1; 1503 char *p = result; 1504 char *ep = &result[sizeof(result)]; 1505 int n; 1506 1507 days = total / 3600 / 24; 1508 hours = (total / 3600) % 24; 1509 mins = (total / 60) % 60; 1510 secs = total % 60; 1511 1512 if (days) { 1513 first = 0; 1514 n = snprintf(p, ep - p, "%dd", days); 1515 if (n < 0 || n >= ep - p) 1516 return "?"; 1517 p += n; 1518 } 1519 if (!first || hours) { 1520 first = 0; 1521 n = snprintf(p, ep - p, "%dh", hours); 1522 if (n < 0 || n >= ep - p) 1523 return "?"; 1524 p += n; 1525 } 1526 if (!first || mins) { 1527 first = 0; 1528 n = snprintf(p, ep - p, "%dm", mins); 1529 if (n < 0 || n >= ep - p) 1530 return "?"; 1531 p += n; 1532 } 1533 snprintf(p, ep - p, "%ds", secs); 1534 1535 return(result); 1536 } 1537 1538 /* 1539 * Print the timestamp 1540 * from tcpdump/util.c 1541 */ 1542 void 1543 ts_print(const struct timeval *tvp) 1544 { 1545 int sec; 1546 1547 /* Default */ 1548 sec = (tvp->tv_sec + thiszone) % 86400; 1549 xo_emit("{e:tv_sec/%lld}{e:tv_usec/%lld}{d:/%02d:%02d:%02d.%06u} ", 1550 tvp->tv_sec, tvp->tv_usec, 1551 sec / 3600, (sec % 3600) / 60, sec % 60, (u_int32_t)tvp->tv_usec); 1552 } 1553 1554 #undef NEXTADDR 1555