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