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