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