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