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