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