1 /*
   2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 /*
   6  * Copyright (c) 1984 Regents of the University of California.
   7  * All rights reserved.
   8  *
   9  * This code is derived from software contributed to Berkeley by
  10  * Sun Microsystems, Inc.
  11  *
  12  * Redistribution and use in source and binary forms, with or without
  13  * modification, are permitted provided that the following conditions
  14  * are met:
  15  * 1. Redistributions of source code must retain the above copyright
  16  *    notice, this list of conditions and the following disclaimer.
  17  * 2. Redistributions in binary form must reproduce the above copyright
  18  *    notice, this list of conditions and the following disclaimer in the
  19  *    documentation and/or other materials provided with the distribution.
  20  * 3. All advertising materials mentioning features or use of this software
  21  *    must display the following acknowledgement:
  22  *      This product includes software developed by the University of
  23  *      California, Berkeley and its contributors.
  24  * 4. Neither the name of the University nor the names of its contributors
  25  *    may be used to endorse or promote products derived from this software
  26  *    without specific prior written permission.
  27  *
  28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  38  * SUCH DAMAGE.
  39  */
  40 
  41 
  42 /*
  43  * arp - display, set, and delete arp table entries
  44  */
  45 
  46 #include <stdio.h>
  47 #include <sys/types.h>
  48 #include <sys/socket.h>
  49 #include <netinet/in.h>
  50 #include <sys/ioctl.h>
  51 #include <errno.h>
  52 #include <netdb.h>
  53 #include <net/if.h>
  54 #include <net/if_arp.h>
  55 #include <stdlib.h>
  56 #include <unistd.h>
  57 #include <string.h>
  58 #include <arpa/inet.h>
  59 #include <net/if_types.h>
  60 #include <net/if_dl.h>
  61 #include <zone.h>
  62 
  63 static int file(char *);
  64 static int set(int, char *[]);
  65 static void get(char *);
  66 static void delete(char *);
  67 static void usage(void);
  68 
  69 int
  70 main(int argc, char *argv[])
  71 {
  72         int c, nflags = 0, argsleft;
  73         int n_flag, a_flag, d_flag, f_flag, s_flag;
  74 
  75         n_flag = a_flag = d_flag = f_flag = s_flag = 0;
  76 
  77 #define CASE(x, y)                              \
  78                 case x:                         \
  79                         if (nflags > 0) {    \
  80                                 usage();        \
  81                                 exit(1);        \
  82                         } else                  \
  83                                 y##_flag = 1;   \
  84                                 nflags++;       \
  85                         break
  86 
  87         while ((c = getopt(argc, argv, "nadfs")) != EOF) {
  88                 switch (c) {
  89                 case '?':
  90                         usage();
  91                         exit(1);
  92                         /* NOTREACHED */
  93                         break;
  94                 case 'n':
  95                         n_flag = 1;
  96                         break;
  97                 CASE('a', a);
  98                 CASE('d', d);
  99                 CASE('f', f);
 100                 CASE('s', s);
 101                 }
 102         }
 103 
 104 #undef CASE
 105 
 106         /*
 107          * -n only allowed with -a
 108          */
 109         if (n_flag && !a_flag) {
 110                 usage();
 111                 exit(1);
 112         }
 113 
 114         argsleft = argc - optind;
 115 
 116         if (a_flag && (argsleft == 0)) {
 117                 /*
 118                  * the easiest way to get the complete arp table
 119                  * is to let netstat, which prints it as part of
 120                  * the MIB statistics, do it.
 121                  */
 122                 char netstat_path[MAXPATHLEN];
 123                 const char *zroot = zone_get_nroot();
 124                 (void) snprintf(netstat_path, sizeof (netstat_path), "%s%s", zroot != NULL ?
 125                     zroot : "", "/usr/bin/netstat");
 126                 (void) execl(netstat_path, "netstat",
 127                     (n_flag ? "-np" : "-p"),
 128                     "-f", "inet", (char *)0);
 129                 (void) fprintf(stderr, "failed to exec netstat: %s\n",
 130                     strerror(errno));
 131                 exit(1);
 132 
 133         } else if (s_flag && (argsleft >= 2)) {
 134                 if (set(argsleft, &argv[optind]) != 0)
 135                         exit(1);
 136 
 137         } else if (d_flag && (argsleft == 1)) {
 138                 delete(argv[optind]);
 139 
 140         } else if (f_flag && (argsleft == 1)) {
 141                 if (file(argv[optind]) != 0)
 142                         exit(1);
 143 
 144         } else if ((nflags == 0) && (argsleft == 1)) {
 145                 get(argv[optind]);
 146 
 147         } else {
 148                 usage();
 149                 exit(1);
 150         }
 151         return (0);
 152 }
 153 
 154 /*
 155  * Process a file to set standard arp entries
 156  */
 157 static int
 158 file(char *name)
 159 {
 160         /*
 161          * A line of input can be:
 162          * <hostname> <macaddr> ["temp"] ["pub"] ["trail"] ["permanent"]
 163          */
 164 #define MAX_LINE_LEN    (MAXHOSTNAMELEN + \
 165         sizeof (" xx:xx:xx:xx:xx:xx temp pub trail permanent\n"))
 166 #define MIN_ARGS        2
 167 #define MAX_ARGS        5
 168 
 169         FILE *fp;
 170         char line[MAX_LINE_LEN];
 171         int retval;
 172 
 173         if ((fp = fopen(name, "r")) == NULL) {
 174                 (void) fprintf(stderr, "arp: cannot open %s\n", name);
 175                 exit(1);
 176         }
 177 
 178         retval = 0;
 179         while (fgets(line, MAX_LINE_LEN, fp) != NULL) {
 180                 char line_copy[MAX_LINE_LEN];
 181                 char *args[MAX_ARGS];
 182                 char *start;
 183                 int i;
 184 
 185                 /*
 186                  * Keep a copy of the un-altered line for error
 187                  * reporting.
 188                  */
 189                 (void) strlcpy(line_copy, line, MAX_LINE_LEN);
 190 
 191                 start = line_copy;
 192                 for (i = 0; i < MAX_ARGS; i++) {
 193                         if ((args[i] = strtok(start, " \t\n")) == NULL)
 194                                 break;
 195 
 196                         start = NULL;
 197                 }
 198 
 199                 if (i < MIN_ARGS) {
 200                         (void) fprintf(stderr, "arp: bad line: %s\n",
 201                             line);
 202                         retval = 1;
 203                         continue;
 204                 }
 205 
 206                 if (set(i, args) != 0)
 207                         retval = 1;
 208         }
 209 
 210 #undef  MAX_LINE_LEN
 211 #undef  MIN_ARGS
 212 #undef  MAX_ARGS
 213 
 214         (void) fclose(fp);
 215         return (retval);
 216 }
 217 
 218 /*
 219  * Set an individual arp entry
 220  */
 221 static int
 222 set(int argc, char *argv[])
 223 {
 224         struct xarpreq ar;
 225         struct hostent *hp;
 226         struct sockaddr_in *sin;
 227         uchar_t *ea;
 228         int s;
 229         char *host = argv[0], *eaddr = argv[1];
 230 
 231         argc -= 2;
 232         argv += 2;
 233         (void) memset(&ar, 0, sizeof (ar));
 234         sin = (struct sockaddr_in *)&ar.xarp_pa;
 235         sin->sin_family = AF_INET;
 236         sin->sin_addr.s_addr = inet_addr(host);
 237         if (sin->sin_addr.s_addr == (in_addr_t)-1) {
 238                 hp = gethostbyname(host);
 239                 if (hp == NULL) {
 240                         (void) fprintf(stderr, "arp: %s: unknown host\n",
 241                             host);
 242                         return (1);
 243                 }
 244                 (void) memcpy(&sin->sin_addr, hp->h_addr,
 245                     sizeof (sin->sin_addr));
 246         }
 247         ea = _link_aton(eaddr, &s);
 248         if (ea == NULL) {
 249                 if (s == -1) {
 250                         (void) fprintf(stderr,
 251                             "arp: invalid link layer address '%s'\n", eaddr);
 252                         return (1);
 253                 }
 254                 perror("arp: nomem");
 255                 exit(1);
 256         }
 257         ar.xarp_ha.sdl_alen = s;
 258         (void) memcpy(LLADDR(&ar.xarp_ha), ea, ar.xarp_ha.sdl_alen);
 259         free(ea);
 260         ar.xarp_ha.sdl_family = AF_LINK;
 261         ar.xarp_flags = ATF_PERM;
 262         while (argc-- > 0) {
 263                 if (strncmp(argv[0], "temp", 4) == 0) {
 264                         ar.xarp_flags &= ~ATF_PERM;
 265                 } else if (strncmp(argv[0], "pub", 3) == 0) {
 266                         ar.xarp_flags |= ATF_PUBL;
 267                 } else if (strncmp(argv[0], "trail", 5) == 0) {
 268                         ar.xarp_flags |= ATF_USETRAILERS;
 269                 } else if (strcmp(argv[0], "permanent") == 0) {
 270                         ar.xarp_flags |= ATF_AUTHORITY;
 271                 } else {
 272                         (void) fprintf(stderr,
 273                             "arp: unknown keyword '%s'\n", argv[0]);
 274                         return (1);
 275                 }
 276                 argv++;
 277         }
 278 
 279         if ((ar.xarp_flags & (ATF_PERM|ATF_AUTHORITY)) == ATF_AUTHORITY) {
 280                 (void) fprintf(stderr, "arp: 'temp' and 'permanent' flags are "
 281                     "not usable together.\n");
 282                 return (1);
 283         }
 284 
 285         s = socket(AF_INET, SOCK_DGRAM, 0);
 286         if (s < 0) {
 287                 perror("arp: socket");
 288                 exit(1);
 289         }
 290         if (ioctl(s, SIOCSXARP, (caddr_t)&ar) < 0) {
 291                 perror(host);
 292                 exit(1);
 293         }
 294         (void) close(s);
 295         return (0);
 296 }
 297 
 298 /*
 299  * Display an individual arp entry
 300  */
 301 static void
 302 get(char *host)
 303 {
 304         struct xarpreq ar;
 305         struct hostent *hp;
 306         struct sockaddr_in *sin;
 307         uchar_t *ea;
 308         int s;
 309         char *str = NULL;
 310 
 311         (void) memset(&ar, 0, sizeof (ar));
 312         sin = (struct sockaddr_in *)&ar.xarp_pa;
 313         sin->sin_family = AF_INET;
 314         sin->sin_addr.s_addr = inet_addr(host);
 315         if (sin->sin_addr.s_addr == (in_addr_t)-1) {
 316                 hp = gethostbyname(host);
 317                 if (hp == NULL) {
 318                         (void) fprintf(stderr, "arp: %s: unknown host\n",
 319                             host);
 320                         exit(1);
 321                 }
 322                 (void) memcpy(&sin->sin_addr, hp->h_addr,
 323                     sizeof (sin->sin_addr));
 324         }
 325         s = socket(AF_INET, SOCK_DGRAM, 0);
 326         if (s < 0) {
 327                 perror("arp: socket");
 328                 exit(1);
 329         }
 330         ar.xarp_ha.sdl_family = AF_LINK;
 331         if (ioctl(s, SIOCGXARP, (caddr_t)&ar) < 0) {
 332                 if (errno == ENXIO)
 333                         (void) printf("%s (%s) -- no entry\n",
 334                             host, inet_ntoa(sin->sin_addr));
 335                 else
 336                         perror("SIOCGXARP");
 337                 exit(1);
 338         }
 339         (void) close(s);
 340         ea = (uchar_t *)LLADDR(&ar.xarp_ha);
 341         if (ar.xarp_flags & ATF_COM) {
 342                 str = _link_ntoa(ea, str, ar.xarp_ha.sdl_alen, IFT_OTHER);
 343                 if (str != NULL) {
 344                         (void) printf("%s (%s) at %s", host,
 345                             inet_ntoa(sin->sin_addr), str);
 346                         free(str);
 347                 } else {
 348                         perror("arp: nomem");
 349                         exit(1);
 350                 }
 351         } else {
 352                 (void) printf("%s (%s) at (incomplete)", host,
 353                     inet_ntoa(sin->sin_addr));
 354         }
 355         if (!(ar.xarp_flags & ATF_PERM))
 356                 (void) printf(" temp");
 357         if (ar.xarp_flags & ATF_PUBL)
 358                 (void) printf(" pub");
 359         if (ar.xarp_flags & ATF_USETRAILERS)
 360                 (void) printf(" trail");
 361         if (ar.xarp_flags & ATF_AUTHORITY)
 362                 (void) printf(" permanent");
 363         (void) printf("\n");
 364 }
 365 
 366 /*
 367  * Delete an arp entry
 368  */
 369 static void
 370 delete(char *host)
 371 {
 372         struct xarpreq ar;
 373         struct hostent *hp;
 374         struct sockaddr_in *sin;
 375         int s;
 376 
 377         (void) memset(&ar, 0, sizeof (ar));
 378         sin = (struct sockaddr_in *)&ar.xarp_pa;
 379         sin->sin_family = AF_INET;
 380         sin->sin_addr.s_addr = inet_addr(host);
 381         if (sin->sin_addr.s_addr == (in_addr_t)-1) {
 382                 hp = gethostbyname(host);
 383                 if (hp == NULL) {
 384                         (void) fprintf(stderr, "arp: %s: unknown host\n",
 385                             host);
 386                         exit(1);
 387                 }
 388                 (void) memcpy(&sin->sin_addr, hp->h_addr,
 389                     sizeof (sin->sin_addr));
 390         }
 391         s = socket(AF_INET, SOCK_DGRAM, 0);
 392         if (s < 0) {
 393                 perror("arp: socket");
 394                 exit(1);
 395         }
 396         ar.xarp_ha.sdl_family = AF_LINK;
 397         if (ioctl(s, SIOCDXARP, (caddr_t)&ar) < 0) {
 398                 if (errno == ENXIO)
 399                         (void) printf("%s (%s) -- no entry\n",
 400                             host, inet_ntoa(sin->sin_addr));
 401                 else
 402                         perror("SIOCDXARP");
 403                 exit(1);
 404         }
 405         (void) close(s);
 406         (void) printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr));
 407 }
 408 
 409 static void
 410 usage(void)
 411 {
 412         (void) printf("Usage: arp hostname\n");
 413         (void) printf("       arp -a [-n]\n");
 414         (void) printf("       arp -d hostname\n");
 415         (void) printf("       arp -s hostname ether_addr "
 416             "[temp] [pub] [trail] [permanent]\n");
 417         (void) printf("       arp -f filename\n");
 418 }