1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2015 Joyent, Inc. All rights reserved. 14 */ 15 16 /* 17 * ndp - display and manipulate Neighbor Cache Entries from NDP 18 */ 19 20 #include <stdio.h> 21 #include <stdarg.h> 22 #include <signal.h> 23 #include <time.h> 24 #include <err.h> 25 #include <errno.h> 26 #include <stdlib.h> 27 #include <strings.h> 28 #include <unistd.h> 29 #include <libgen.h> 30 #include <sys/ioctl.h> 31 #include <sys/types.h> 32 #include <wait.h> 33 #include <sys/mac.h> 34 #include <sys/socket.h> 35 #include <sys/sockio.h> 36 #include <netdb.h> 37 #include <net/if_types.h> 38 #include <netinet/in.h> 39 #include <arpa/inet.h> 40 #include <inet/ip.h> 41 #include <net/if_dl.h> 42 #include <net/route.h> 43 44 typedef struct sockaddr_in6 sin6_t; 45 46 #define BUF_SIZE 2048 47 typedef struct rtmsg_pkt { 48 struct rt_msghdr m_rtm; 49 char m_space[BUF_SIZE]; 50 } rtmsg_pkt_t; 51 52 enum ndp_action { 53 NDP_A_DEFAULT, 54 NDP_A_GET, /* Show a single NDP entry */ 55 NDP_A_GET_ALL, /* Show NDP entries */ 56 NDP_A_GET_FOREVER, /* Repeatedly show entries */ 57 NDP_A_DELETE, /* Delete an NDP entry */ 58 NDP_A_SET_NCE, /* Set NDP entry */ 59 NDP_A_SET_FILE /* Read in & set NDP entries */ 60 }; 61 62 typedef int (ndp_addr_f)(int, struct lifreq *, void *); 63 typedef void (ndp_void_f)(void); 64 65 static void ndp_usage(const char *, ...); 66 static void ndp_fatal(const char *, ...); 67 static void ndp_badflag(enum ndp_action); 68 static void ndp_missingarg(char); 69 70 static void ndp_run_in_child(ndp_void_f *); 71 static void ndp_do_run(void); 72 static void ndp_setup_handler(sigset_t *); 73 static void ndp_start_timer(time_t period); 74 static void ndp_run_periodically(time_t, ndp_void_f *); 75 76 static int ndp_salen(const struct sockaddr *sa); 77 static int ndp_extract_sockaddrs(struct rt_msghdr *, struct sockaddr **, 78 struct sockaddr **, struct sockaddr **, struct sockaddr **, 79 struct sockaddr_dl **); 80 static int ndp_rtmsg_get(int, rtmsg_pkt_t *, struct sockaddr *); 81 static int ndp_find_interface(int, struct sockaddr *, char *, int); 82 83 static int ndp_initialize_lifreq(int, struct lifreq *, struct sockaddr *); 84 static int ndp_host_enumerate(char *, ndp_addr_f *, void *); 85 86 static int ndp_display(struct lifreq *); 87 static int ndp_display_missing(struct lifreq *); 88 static void ndp_lifr2ip(struct lifreq *, char *, int); 89 90 static int ndp_get(int, struct lifreq *, void *); 91 static void ndp_get_all(void); 92 static int ndp_delete(int, struct lifreq *, void *); 93 static int ndp_set(int, struct lifreq *, void *); 94 static int ndp_set_nce(char *, char *, char *[], int); 95 static int ndp_set_file(char *); 96 97 static char *ndp_iface = NULL; 98 static char *netstat_path = "/usr/bin/netstat"; 99 static pid_t ndp_pid; 100 static boolean_t ndp_noresolve = B_FALSE; /* Don't lookup addresses */ 101 static boolean_t ndp_run = B_TRUE; 102 103 #define MAX_ATTEMPTS 5 104 #define MAX_OPTS 5 105 #define WORDSEPS " \t\r\n" 106 107 /* 108 * Macros borrowed from route(8) for working with PF_ROUTE messages 109 */ 110 #define RT_ADVANCE(x, n) ((x) += ndp_salen(n)) 111 #define RT_NEXTADDR(cp, w, u) \ 112 l = ndp_salen(u); \ 113 (void) memmove(cp, u, l); \ 114 cp += l; 115 116 /* 117 * Print an error to stderr and then exit non-zero. 118 */ 119 static void 120 ndp_fatal(const char *format, ...) 121 { 122 va_list ap; 123 124 va_start(ap, format); 125 vwarnx(format, ap); 126 va_end(ap); 127 exit(EXIT_FAILURE); 128 } 129 130 /* 131 * Print out the command usage to stderr, along with any reason why it's being 132 * printed, and then exit non-zero. 133 */ 134 static void 135 ndp_usage(const char *reason, ...) 136 { 137 va_list ap; 138 const char *ndp_progname = getprogname(); 139 140 if (reason != NULL) { 141 va_start(ap, reason); 142 (void) fprintf(stderr, "%s: ", ndp_progname); 143 (void) vfprintf(stderr, reason, ap); 144 (void) fprintf(stderr, "\n"); 145 va_end(ap); 146 } 147 148 (void) fprintf(stderr, 149 "Usage: %s [-n] [-i iface] hostname\n" 150 " %s [-n] [-i iface] -s nodeaddr etheraddr [temp] [proxy]\n" 151 " %s [-n] [-i iface] -d nodeaddr\n" 152 " %s [-n] [-i iface] -f filename\n" 153 " %s [-n] -a\n" 154 " %s [-n] -A period\n", 155 ndp_progname, ndp_progname, ndp_progname, 156 ndp_progname, ndp_progname, ndp_progname); 157 exit(EXIT_FAILURE); 158 } 159 160 static void 161 ndp_badflag(enum ndp_action action) 162 { 163 switch (action) { 164 case NDP_A_DEFAULT: 165 case NDP_A_GET: 166 ndp_usage("Already going to print an entry, " 167 "but extra -%c given", optopt); 168 break; 169 case NDP_A_GET_ALL: 170 ndp_usage("Already going to print all entries (-a), " 171 "but extra -%c given", optopt); 172 break; 173 case NDP_A_GET_FOREVER: 174 ndp_usage("Already going to repeatedly print all entries (-A), " 175 "but extra -%c given", optopt); 176 break; 177 case NDP_A_DELETE: 178 ndp_usage("Already going to delete an entry (-d), " 179 "but extra -%c given", optopt); 180 break; 181 case NDP_A_SET_NCE: 182 ndp_usage("Already going to set an entry (-s), " 183 "but extra -%c given", optopt); 184 break; 185 case NDP_A_SET_FILE: 186 ndp_usage("Already going to set from file (-f), " 187 "but extra -%c given", optopt); 188 break; 189 } 190 } 191 192 static void 193 ndp_missingarg(char flag) 194 { 195 switch (flag) { 196 case 'A': 197 ndp_usage("Missing time period after -%c", flag); 198 break; 199 case 'd': 200 ndp_usage("Missing node name after -%c", flag); 201 break; 202 case 'f': 203 ndp_usage("Missing filename after -%c", flag); 204 break; 205 case 's': 206 ndp_usage("Missing node name after -%c", flag); 207 break; 208 case 'i': 209 ndp_usage("Missing interface name after -%c", flag); 210 break; 211 default: 212 ndp_usage("Missing option argument after -%c", flag); 213 break; 214 } 215 } 216 217 /* 218 * Run a function that's going to exec in a child process, and don't return 219 * until it exits. 220 */ 221 static void 222 ndp_run_in_child(ndp_void_f *func) 223 { 224 pid_t child_pid; 225 int childstat = 0, status = 0; 226 227 child_pid = fork(); 228 if (child_pid == (pid_t)-1) { 229 ndp_fatal("Unable to fork: %s", strerror(errno)); 230 } else if (child_pid == (pid_t)0) { 231 func(); 232 exit(EXIT_FAILURE); 233 } 234 235 while (waitpid(child_pid, &childstat, 0) == -1) { 236 if (errno == EINTR) 237 continue; 238 239 ndp_fatal("Failed to wait on child: %s", strerror(errno)); 240 } 241 242 status = WEXITSTATUS(childstat); 243 if (status != 0) { 244 ndp_fatal("Child process exited with %d", status); 245 } 246 } 247 248 /* 249 * SIGALRM handler to schedule a run. 250 */ 251 static void 252 ndp_do_run(void) 253 { 254 ndp_run = B_TRUE; 255 } 256 257 258 /* 259 * Prepare signal masks, and install the SIGALRM handler. Return old signal 260 * masks through the first argument. 261 */ 262 static void 263 ndp_setup_handler(sigset_t *oset) 264 { 265 struct sigaction sa; 266 267 /* 268 * Mask off SIGALRM so we only trigger the handler when we're ready 269 * using sigsuspend(3C), in case the child process takes longer to 270 * run than the alarm interval. 271 */ 272 if (sigprocmask(0, NULL, oset) != 0) { 273 ndp_fatal("Unable to set signal mask: %s", strerror(errno)); 274 } 275 276 if (sighold(SIGALRM) != 0) { 277 ndp_fatal("Unable to add SIGALRM to signal mask: %s", 278 strerror(errno)); 279 } 280 281 sa.sa_flags = 0; 282 sa.sa_handler = ndp_do_run; 283 284 if (sigemptyset(&sa.sa_mask) != 0) { 285 ndp_fatal("Unable to prepare empty signal set: %s", 286 strerror(errno)); 287 } 288 289 if (sigaction(SIGALRM, &sa, NULL) != 0) { 290 ndp_fatal("Unable to install timer handler: %s", 291 strerror(errno)); 292 } 293 } 294 295 /* 296 * Start the printing timer. 297 */ 298 static void 299 ndp_start_timer(time_t period) 300 { 301 timer_t timer; 302 struct itimerspec interval; 303 interval.it_value.tv_sec = interval.it_interval.tv_sec = period; 304 interval.it_value.tv_nsec = interval.it_interval.tv_nsec = 0; 305 306 if (timer_create(CLOCK_REALTIME, NULL, &timer) != 0) { 307 ndp_fatal("Unable to create timer: %s", strerror(errno)); 308 } 309 310 if (timer_settime(timer, 0, &interval, NULL) != 0) { 311 ndp_fatal("Unable to set time on timer: %s", strerror(errno)); 312 } 313 } 314 315 316 /* 317 * Run a given function forever periodically in a child process. 318 */ 319 static void 320 ndp_run_periodically(time_t period, ndp_void_f *func) 321 { 322 sigset_t oset; 323 324 ndp_setup_handler(&oset); 325 ndp_start_timer(period); 326 327 do { 328 if (ndp_run) { 329 ndp_run = B_FALSE; 330 ndp_run_in_child(func); 331 } 332 (void) sigsuspend(&oset); 333 } while (errno == EINTR); 334 335 /* 336 * Only an EFAULT should get us here. Abort so we get a core dump. 337 */ 338 warnx("Failure while waiting on timer: %s", strerror(errno)); 339 abort(); 340 } 341 342 /* 343 * Given an address, return its size. 344 */ 345 static int 346 ndp_salen(const struct sockaddr *sa) 347 { 348 switch (sa->sa_family) { 349 case AF_INET: 350 return (sizeof (struct sockaddr_in)); 351 case AF_LINK: 352 return (sizeof (struct sockaddr_dl)); 353 case AF_INET6: 354 return (sizeof (struct sockaddr_in6)); 355 default: 356 warnx("Unrecognized sockaddr with address family %d!", 357 sa->sa_family); 358 abort(); 359 } 360 /*NOTREACHED*/ 361 } 362 363 /* 364 * Extract all socket addresses from a routing message, and return them 365 * through the pointers given as arguments to ndp_extract_sockaddrs. None 366 * of the pointers should be null. 367 */ 368 static int 369 ndp_extract_sockaddrs(struct rt_msghdr *rtm, struct sockaddr **dst, 370 struct sockaddr **gate, struct sockaddr **mask, struct sockaddr **src, 371 struct sockaddr_dl **ifp) 372 { 373 struct sockaddr *sa; 374 char *cp; 375 int i; 376 377 if (rtm->rtm_version != RTM_VERSION) { 378 warnx("Routing message version %d not understood", 379 rtm->rtm_version); 380 return (-1); 381 } 382 383 if (rtm->rtm_errno != 0) { 384 warnx("Routing message couldn't be processed: %s", 385 strerror(rtm->rtm_errno)); 386 return (-1); 387 } 388 389 cp = ((char *)(rtm + 1)); 390 if (rtm->rtm_addrs != 0) { 391 for (i = 1; i != 0; i <<= 1) { 392 if ((i & rtm->rtm_addrs) == 0) 393 continue; 394 395 /*LINTED*/ 396 sa = (struct sockaddr *)cp; 397 switch (i) { 398 case RTA_DST: 399 *dst = sa; 400 break; 401 case RTA_GATEWAY: 402 *gate = sa; 403 break; 404 case RTA_NETMASK: 405 *mask = sa; 406 break; 407 case RTA_IFP: 408 if (sa->sa_family == AF_LINK && 409 ((struct sockaddr_dl *)sa)->sdl_nlen != 0) 410 *ifp = (struct sockaddr_dl *)sa; 411 break; 412 case RTA_SRC: 413 *src = sa; 414 break; 415 } 416 RT_ADVANCE(cp, sa); 417 } 418 } 419 420 return (0); 421 } 422 423 /* 424 * Given an IPv6 address, use routing information to look up 425 * the destination and interface it would pass through. 426 */ 427 static int 428 ndp_rtmsg_get(int fd, rtmsg_pkt_t *msg, struct sockaddr *sin6p) 429 { 430 static int seq = 0; 431 struct sockaddr_dl sdl; 432 int mlen, l; 433 char ipaddr[INET6_ADDRSTRLEN]; 434 char *cp = msg->m_space; 435 struct rt_msghdr *m_rtm = &msg->m_rtm; 436 437 bzero(msg, sizeof (rtmsg_pkt_t)); 438 bzero(&sdl, sizeof (struct sockaddr_dl)); 439 440 m_rtm->rtm_type = RTM_GET; 441 m_rtm->rtm_version = RTM_VERSION; 442 m_rtm->rtm_seq = ++seq; 443 m_rtm->rtm_addrs = RTA_DST | RTA_IFP; 444 m_rtm->rtm_msglen = sizeof (rtmsg_pkt_t); 445 446 /* Place the address we're looking up after the header */ 447 RT_NEXTADDR(cp, RTA_DST, sin6p); 448 449 /* Load an empty link-level address, so we get an interface back */ 450 sdl.sdl_family = AF_LINK; 451 RT_NEXTADDR(cp, RTA_IFP, (struct sockaddr *)&sdl); 452 453 m_rtm->rtm_msglen = cp - (char *)msg; 454 455 if ((mlen = write(fd, (char *)msg, m_rtm->rtm_msglen)) < 0) { 456 if (errno == ESRCH) { 457 /*LINTED*/ 458 if (inet_ntop(AF_INET6, &((sin6_t *)sin6p)->sin6_addr, 459 ipaddr, sizeof (ipaddr)) == NULL) { 460 (void) snprintf(ipaddr, sizeof (ipaddr), 461 "(failed to format IP)"); 462 }; 463 warnx("An appropriate interface for the address %s " 464 "is not in the routing table; use -i to force an " 465 "interface", ipaddr); 466 return (-1); 467 } else { 468 warnx("Failed to send routing message: %s", 469 strerror(errno)); 470 return (-1); 471 } 472 } else if (mlen < (int)m_rtm->rtm_msglen) { 473 warnx("Failed to write all bytes to routing socket"); 474 return (-1); 475 } 476 477 /* 478 * Keep reading routing messages until we find the response to the one 479 * we just sent. Note that we depend on the sequence number being unique 480 * to the running program. 481 */ 482 do { 483 mlen = read(fd, (char *)msg, sizeof (rtmsg_pkt_t)); 484 } while (mlen > 0 && 485 (m_rtm->rtm_seq != seq || m_rtm->rtm_pid != ndp_pid)); 486 if (mlen < 0) { 487 warnx("Failed to read from routing socket: %s", 488 strerror(errno)); 489 return (-1); 490 } 491 492 return (0); 493 } 494 495 /* 496 * Find the interface that the IPv6 address would be routed through, and store 497 * the name of the interface in the buffer passed in. 498 */ 499 static int 500 ndp_find_interface(int fd, struct sockaddr *sin6p, char *buf, int buflen) 501 { 502 struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL; 503 struct sockaddr_dl *ifp = NULL; 504 rtmsg_pkt_t msg; 505 506 if (ndp_rtmsg_get(fd, &msg, sin6p) != 0) { 507 return (-1); 508 } 509 510 if (ndp_extract_sockaddrs(&msg.m_rtm, &dst, &gate, 511 &mask, &src, &ifp) != 0) { 512 return (-1); 513 } 514 515 if (ifp == NULL) { 516 warnx("Unable to find appropriate interface for address"); 517 return (-1); 518 } else { 519 if (ifp->sdl_nlen >= buflen) { 520 warnx("The interface name \"%.*s\" is too big for the " 521 "available buffer", ifp->sdl_nlen, ifp->sdl_data); 522 return (-1); 523 } else { 524 (void) snprintf(buf, buflen, "%.*s", ifp->sdl_nlen, 525 ifp->sdl_data); 526 } 527 } 528 529 return (0); 530 } 531 532 /* 533 * Zero out a lifreq struct for a SIOCLIF*ND ioctl, set the address, and fetch 534 * the appropriate interface using the given routing socket. 535 */ 536 static int 537 ndp_initialize_lifreq(int route, struct lifreq *lifrp, struct sockaddr *sap) 538 { 539 struct sockaddr_storage *lnr_addr; 540 /* LINTED E_BAD_PTR_CAST_ALIGN */ 541 struct sockaddr_in6 *sin6p = (sin6_t *)sap; 542 char *lifr_name = lifrp->lifr_name; 543 544 bzero(lifrp, sizeof (struct lifreq)); 545 lnr_addr = &lifrp->lifr_nd.lnr_addr; 546 547 if (ndp_iface != NULL) { 548 (void) strlcpy(lifr_name, ndp_iface, LIFNAMSIZ); 549 } else if (sin6p->sin6_scope_id != 0) { 550 int zone_id = sin6p->sin6_scope_id; 551 if (if_indextoname(zone_id, lifr_name) == NULL) { 552 warnx("Invalid zone identifier: %d", zone_id); 553 return (-1); 554 } 555 } else if (IN6_IS_ADDR_LINKSCOPE(&sin6p->sin6_addr)) { 556 warnx("Link-scope addresses should specify an interface with " 557 "a zone ID, or with -i."); 558 return (-1); 559 } else { 560 if (ndp_find_interface(route, sap, lifr_name, LIFNAMSIZ) != 0) 561 return (-1); 562 } 563 564 (void) memcpy(lnr_addr, sap, sizeof (struct sockaddr_storage)); 565 566 return (0); 567 } 568 569 /* 570 * Take a host identifier, find the corresponding IPv6 addresses and then pass 571 * them to the specified function, along with any desired data. 572 */ 573 static int 574 ndp_host_enumerate(char *host, ndp_addr_f *addr_func, void *data) 575 { 576 struct lifreq lifr; 577 struct addrinfo hints, *serverinfo, *p; 578 int err, attempts = 0; 579 int inet6, route; 580 581 bzero(&hints, sizeof (struct addrinfo)); 582 hints.ai_family = AF_INET6; 583 hints.ai_protocol = IPPROTO_IPV6; 584 585 while (attempts < MAX_ATTEMPTS) { 586 err = getaddrinfo(host, NULL, &hints, &serverinfo); 587 588 if (err == 0) { 589 break; 590 } else if (err == EAI_AGAIN) { 591 attempts++; 592 } else { 593 warnx("Unable to lookup %s: %s", host, 594 gai_strerror(err)); 595 return (-1); 596 } 597 } 598 599 if (attempts == MAX_ATTEMPTS) { 600 warnx("Failed multiple times to lookup %s", host); 601 return (-1); 602 } 603 604 inet6 = socket(PF_INET6, SOCK_DGRAM, 0); 605 if (inet6 < 0) { 606 warnx("Failed to open IPv6 socket: %s", strerror(errno)); 607 err = -1; 608 } 609 610 route = socket(PF_ROUTE, SOCK_RAW, 0); 611 if (route < 0) { 612 warnx("Failed to open routing socket: %s", strerror(errno)); 613 err = -1; 614 } 615 616 if (err == 0) { 617 for (p = serverinfo; p != NULL; p = p->ai_next) { 618 if (ndp_initialize_lifreq(route, &lifr, p->ai_addr) 619 != 0) { 620 err = -1; 621 continue; 622 } 623 624 if (addr_func(inet6, &lifr, data) != 0) { 625 err = -1; 626 continue; 627 } 628 } 629 } 630 631 if (close(route) != 0) { 632 warnx("Failed to close routing socket: %s", strerror(errno)); 633 err = -1; 634 } 635 636 if (close(inet6) != 0) { 637 warnx("Failed to close IPv6 socket: %s", strerror(errno)); 638 err = -1; 639 } 640 641 /* Clean up linked list */ 642 freeaddrinfo(serverinfo); 643 644 return (err); 645 } 646 647 static int 648 ndp_display(struct lifreq *lifrp) 649 { 650 struct sockaddr_in6 *lnr_addr; 651 char ipaddr[INET6_ADDRSTRLEN]; 652 char *lladdr = NULL; 653 char hostname[NI_MAXHOST]; 654 int flags, gni_flags; 655 656 lnr_addr = (struct sockaddr_in6 *)&lifrp->lifr_nd.lnr_addr; 657 flags = lifrp->lifr_nd.lnr_flags; 658 659 if (inet_ntop(AF_INET6, &lnr_addr->sin6_addr, ipaddr, 660 sizeof (ipaddr)) == NULL) { 661 warnx("Couldn't convert IPv6 address to string: %s", 662 strerror(errno)); 663 return (-1); 664 }; 665 666 if ((lladdr = _link_ntoa((uchar_t *)lifrp->lifr_nd.lnr_hdw_addr, 667 NULL, lifrp->lifr_nd.lnr_hdw_len, IFT_ETHER)) == NULL) { 668 warnx("Couldn't convert link-layer address to string: %s", 669 strerror(errno)); 670 return (-1); 671 } 672 673 gni_flags = ndp_noresolve ? NI_NUMERICHOST : 0; 674 675 if (getnameinfo((struct sockaddr *)lnr_addr, sizeof (sin6_t), hostname, 676 sizeof (hostname), NULL, 0, gni_flags) != 0) { 677 warnx("Unable to lookup hostname for %s", ipaddr); 678 free(lladdr); 679 return (-1); 680 } 681 682 (void) printf("%s (%s) at %s", ipaddr, hostname, lladdr); 683 684 if (flags & NDF_ISROUTER_ON) { 685 (void) printf(" router"); 686 } 687 688 if (flags & NDF_ANYCAST_ON) { 689 (void) printf(" any"); 690 } 691 692 if (!(flags & NDF_STATIC)) { 693 (void) printf(" temp"); 694 } 695 696 if (flags & NDF_PROXY_ON) { 697 (void) printf(" proxy"); 698 } 699 700 (void) printf("\n"); 701 702 free(lladdr); 703 return (0); 704 } 705 706 static int 707 ndp_display_missing(struct lifreq *lifrp) 708 { 709 struct sockaddr_in6 *lnr_addr; 710 char ipaddr[INET6_ADDRSTRLEN]; 711 char hostname[NI_MAXHOST]; 712 int flags = ndp_noresolve ? NI_NUMERICHOST : 0; 713 lnr_addr = (struct sockaddr_in6 *)&lifrp->lifr_nd.lnr_addr; 714 715 if (inet_ntop(AF_INET6, &lnr_addr->sin6_addr, ipaddr, 716 sizeof (ipaddr)) == NULL) { 717 warnx("Couldn't convert IPv6 address to string: %s", 718 strerror(errno)); 719 return (-1); 720 }; 721 722 if (getnameinfo((struct sockaddr *)lnr_addr, sizeof (sin6_t), hostname, 723 sizeof (hostname), NULL, 0, flags) != 0) { 724 warnx("Unable to lookup hostname for %s", ipaddr); 725 return (-1); 726 } 727 728 (void) printf("%s (%s) -- no entry\n", ipaddr, hostname); 729 return (0); 730 } 731 732 static void 733 ndp_lifr2ip(struct lifreq *lifrp, char *ipaddr, int buflen) 734 { 735 sin6_t *lnr_addr = (sin6_t *)&lifrp->lifr_nd.lnr_addr; 736 if (inet_ntop(AF_INET6, &lnr_addr->sin6_addr, ipaddr, 737 buflen) == NULL) { 738 (void) snprintf(ipaddr, buflen, "(failed to format IP)"); 739 }; 740 } 741 742 /* 743 * Perform a SIOCLIFGETND and print out information about it 744 */ 745 /*ARGSUSED*/ 746 static int 747 ndp_get(int fd, struct lifreq *lifrp, void *unused) 748 { 749 char ipaddr[INET6_ADDRSTRLEN]; 750 if (ioctl(fd, SIOCLIFGETND, lifrp) < 0) { 751 if (errno == ESRCH) { 752 return (ndp_display_missing(lifrp)); 753 } else { 754 ndp_lifr2ip(lifrp, ipaddr, sizeof (ipaddr)); 755 warnx("Couldn't lookup %s: %s", 756 ipaddr, strerror(errno)); 757 return (-1); 758 } 759 } 760 761 return (ndp_display(lifrp)); 762 } 763 764 /* 765 * Print out all NDP entries 766 */ 767 static void 768 ndp_get_all(void) 769 { 770 (void) execl(netstat_path, "netstat", 771 (ndp_noresolve ? "-np" : "-p"), 772 "-f", "inet6", (char *)0); 773 ndp_fatal("Coudn't exec %s: %s", netstat_path, strerror(errno)); 774 } 775 776 /* 777 * Perform a SIOCLIFDELND ioctl 778 */ 779 /*ARGSUSED*/ 780 static int 781 ndp_delete(int fd, struct lifreq *lifrp, void *unused) 782 { 783 char ipaddr[INET6_ADDRSTRLEN]; 784 785 if (ioctl(fd, SIOCLIFDELND, lifrp) < 0) { 786 ndp_lifr2ip(lifrp, ipaddr, sizeof (ipaddr)); 787 if (errno == ESRCH) { 788 warnx("No entry for %s", ipaddr); 789 return (-1); 790 } else if (errno == EPERM) { 791 warnx("Permission denied, " 792 "could not delete entry for %s", ipaddr); 793 return (-1); 794 } else { 795 warnx("Couldn't delete mapping for %s: %s", 796 ipaddr, strerror(errno)); 797 return (-1); 798 } 799 } 800 801 return (0); 802 } 803 804 /* 805 * Perform a SIOCLIFSETND ioctl using properties from the example structure. 806 */ 807 static int 808 ndp_set(int fd, struct lifreq *lifrp, void *data) 809 { 810 char ipaddr[INET6_ADDRSTRLEN]; 811 const lif_nd_req_t *nd_attrs = data; 812 813 (void) memcpy(lifrp->lifr_nd.lnr_hdw_addr, nd_attrs->lnr_hdw_addr, 814 ND_MAX_HDW_LEN); 815 lifrp->lifr_nd.lnr_hdw_len = nd_attrs->lnr_hdw_len; 816 lifrp->lifr_nd.lnr_flags = nd_attrs->lnr_flags; 817 818 lifrp->lifr_nd.lnr_state_create = nd_attrs->lnr_state_create; 819 lifrp->lifr_nd.lnr_state_same_lla = nd_attrs->lnr_state_same_lla; 820 lifrp->lifr_nd.lnr_state_diff_lla = nd_attrs->lnr_state_diff_lla; 821 822 if (ioctl(fd, SIOCLIFSETND, lifrp) < 0) { 823 ndp_lifr2ip(lifrp, ipaddr, sizeof (ipaddr)); 824 if (errno == EPERM) { 825 warnx("Permission denied, " 826 "could not set entry for %s", ipaddr); 827 return (-1); 828 } else { 829 warnx("Failed to set mapping for %s: %s", 830 ipaddr, strerror(errno)); 831 return (-1); 832 } 833 } 834 835 return (0); 836 } 837 838 /* 839 * Given a host identifier, a link-layer address and possible options, 840 * add/update the NDP mappings. 841 */ 842 static int 843 ndp_set_nce(char *host, char *lladdr, char *opts[], int optlen) 844 { 845 lif_nd_req_t nd_attrs; 846 uchar_t *ea; 847 char *opt; 848 int i; 849 boolean_t temp = B_FALSE; 850 boolean_t any = B_FALSE; 851 boolean_t router = B_FALSE; 852 853 bzero(&nd_attrs, sizeof (lif_nd_req_t)); 854 855 ea = _link_aton(lladdr, &nd_attrs.lnr_hdw_len); 856 857 if (ea == NULL) { 858 warnx("Unable to parse link-layer address \"%s\"", lladdr); 859 return (-1); 860 } 861 862 if (nd_attrs.lnr_hdw_len > sizeof (nd_attrs.lnr_hdw_addr)) { 863 warnx("The size of the link-layer address is " 864 "too large to set\n"); 865 free(ea); 866 return (-1); 867 } 868 869 (void) memcpy(nd_attrs.lnr_hdw_addr, ea, nd_attrs.lnr_hdw_len); 870 871 free(ea); 872 873 nd_attrs.lnr_state_create = ND_REACHABLE; 874 nd_attrs.lnr_state_same_lla = ND_UNCHANGED; 875 nd_attrs.lnr_state_diff_lla = ND_STALE; 876 877 for (i = 0; i < optlen; i++) { 878 opt = opts[i]; 879 if (strcmp(opt, "temp") == 0) { 880 temp = B_TRUE; 881 } else if (strcmp(opt, "any") == 0) { 882 any = B_TRUE; 883 } else if (strcmp(opt, "router") == 0) { 884 router = B_TRUE; 885 } else if (strcmp(opt, "proxy") == 0) { 886 warnx("NDP proxying is currently not supported"); 887 return (-1); 888 } else { 889 warnx("Unrecognized option \"%s\"", opt); 890 return (-1); 891 } 892 } 893 894 if (!temp) { 895 nd_attrs.lnr_flags |= NDF_STATIC; 896 } 897 898 if (any) { 899 nd_attrs.lnr_flags |= NDF_ANYCAST_ON; 900 } else { 901 nd_attrs.lnr_flags |= NDF_ANYCAST_OFF; 902 } 903 904 if (router) { 905 nd_attrs.lnr_flags |= NDF_ISROUTER_OFF; 906 } else { 907 nd_attrs.lnr_flags |= NDF_ISROUTER_OFF; 908 } 909 910 return (ndp_host_enumerate(host, ndp_set, &nd_attrs)); 911 } 912 913 /* 914 * Read in a file and set the mappings from each line. 915 */ 916 static int 917 ndp_set_file(char *filename) 918 { 919 char *line = NULL, *lasts = NULL, *curr; 920 char *host, *lladdr; 921 char *opts[MAX_OPTS]; 922 int optlen = 0, lineno = 0; 923 size_t cap = 0; 924 boolean_t failed_line = B_FALSE; 925 FILE *stream = fopen(filename, "r"); 926 927 if (stream == NULL) { 928 ndp_fatal("Error while opening file %s: %s", 929 filename, strerror(errno)); 930 } 931 932 errno = 0; 933 while (getline(&line, &cap, stream) != -1) { 934 lineno++; 935 936 if (line[0] == '#') 937 continue; 938 939 host = strtok_r(line, WORDSEPS, &lasts); 940 if (host == NULL) { 941 warnx("Line %d incomplete, skipping: " 942 "missing host identifier", lineno); 943 failed_line = B_TRUE; 944 continue; 945 } 946 947 lladdr = strtok_r(NULL, WORDSEPS, &lasts); 948 if (lladdr == NULL) { 949 warnx("Line %d incomplete, skipping: " 950 "missing link-layer address", lineno); 951 failed_line = B_TRUE; 952 continue; 953 } 954 955 for (optlen = 0; optlen < MAX_OPTS; optlen++) { 956 curr = strtok_r(NULL, WORDSEPS, &lasts); 957 if (curr == NULL) 958 break; 959 opts[optlen] = curr; 960 } 961 962 if (ndp_set_nce(host, lladdr, opts, optlen) != 0) { 963 failed_line = B_TRUE; 964 continue; 965 } 966 } 967 968 free(line); 969 970 if (errno != 0 || ferror(stream)) { 971 ndp_fatal("Error while reading from file %s: %s", filename, 972 strerror(errno)); 973 } 974 975 if (fclose(stream) != 0) { 976 ndp_fatal("Error close file %s: %s", filename, strerror(errno)); 977 } 978 979 return (failed_line ? -1 : 0); 980 } 981 982 int 983 main(int argc, char *argv[]) 984 { 985 char *flagarg = NULL, *lladdr = NULL; 986 char **opts; 987 char *endptr; 988 int c, argsleft, optlen = 0, err = 0; 989 long long period; 990 enum ndp_action action = NDP_A_DEFAULT; 991 992 setprogname(basename(argv[0])); 993 994 if (argc < 2) { 995 ndp_usage("No arguments given."); 996 } 997 998 while ((c = getopt(argc, argv, ":naA:d:f:i:s:")) != -1) { 999 switch (c) { 1000 case 'n': 1001 ndp_noresolve = B_TRUE; 1002 break; 1003 case 'i': 1004 ndp_iface = optarg; 1005 break; 1006 case 's': 1007 if (action != NDP_A_DEFAULT) 1008 ndp_badflag(action); 1009 action = NDP_A_SET_NCE; 1010 flagarg = optarg; 1011 1012 if ((argc - optind) < 1) { 1013 ndp_usage("Missing link-layer address after " 1014 "the node address, \"%s\"", flagarg); 1015 } 1016 lladdr = argv[optind++]; 1017 1018 /* 1019 * Grab any following keywords up to the next flag 1020 */ 1021 opts = argv + optind; 1022 while ((argc - optind) > 0) { 1023 if (argv[optind][0] == '-') 1024 ndp_usage("Encountered \"%s\" after " 1025 "flag parsing is done", 1026 argv[optind]); 1027 optind++; 1028 optlen++; 1029 } 1030 break; 1031 case 'a': 1032 if (action != NDP_A_DEFAULT) 1033 ndp_badflag(action); 1034 action = NDP_A_GET_ALL; 1035 break; 1036 case 'A': 1037 if (action != NDP_A_DEFAULT) 1038 ndp_badflag(action); 1039 action = NDP_A_GET_FOREVER; 1040 flagarg = optarg; 1041 break; 1042 case 'd': 1043 if (action != NDP_A_DEFAULT) 1044 ndp_badflag(action); 1045 action = NDP_A_DELETE; 1046 flagarg = optarg; 1047 break; 1048 case 'f': 1049 if (action != NDP_A_DEFAULT) 1050 ndp_badflag(action); 1051 action = NDP_A_SET_FILE; 1052 flagarg = optarg; 1053 break; 1054 case ':': 1055 ndp_missingarg(optopt); 1056 break; 1057 case '?': 1058 ndp_usage("Unrecognized flag \"-%c\"", optopt); 1059 default: 1060 ndp_usage(NULL); 1061 } 1062 } 1063 1064 argsleft = argc - optind; 1065 ndp_pid = getpid(); 1066 1067 if (action != NDP_A_DEFAULT && argsleft != 0) { 1068 ndp_usage("Extra arguments leftover after parsing flags"); 1069 } 1070 1071 switch (action) { 1072 case NDP_A_DEFAULT: 1073 case NDP_A_GET: 1074 if (argsleft != 1) { 1075 ndp_usage("Multiple arguments given without any flags"); 1076 } 1077 err = ndp_host_enumerate(argv[optind], ndp_get, NULL); 1078 break; 1079 case NDP_A_GET_ALL: 1080 ndp_get_all(); 1081 /*NOTREACHED*/ 1082 break; 1083 case NDP_A_GET_FOREVER: 1084 errno = 0; 1085 period = strtoll(flagarg, &endptr, 10); 1086 if ((period == 0 && errno != 0) || 1087 (endptr[0] != '\0') || 1088 (period < 0)) { 1089 ndp_usage("Given period should be a positive integer," 1090 " not \"%s\"", flagarg); 1091 } 1092 if (period > 86400) { 1093 ndp_usage("Given period should be shorter than a day;" 1094 " given \"%s\" seconds", flagarg); 1095 } 1096 ndp_run_periodically(period, ndp_get_all); 1097 /*NOTREACHED*/ 1098 break; 1099 case NDP_A_DELETE: 1100 err = ndp_host_enumerate(flagarg, ndp_delete, NULL); 1101 break; 1102 case NDP_A_SET_NCE: 1103 err = ndp_set_nce(flagarg, lladdr, opts, optlen); 1104 break; 1105 case NDP_A_SET_FILE: 1106 err = ndp_set_file(flagarg); 1107 break; 1108 } 1109 1110 return (err == 0 ? 0 : 1); 1111 } 1112