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 
  62 static int file(char *);
  63 static int set(int, char *[]);
  64 static void get(char *);
  65 static void delete(char *);
  66 static void usage(void);
  67 
  68 int
  69 main(int argc, char *argv[])
  70 {
  71         int c, nflags = 0, argsleft;
  72         int n_flag, a_flag, d_flag, f_flag, s_flag;
  73 
  74         n_flag = a_flag = d_flag = f_flag = s_flag = 0;
  75 
  76 #define CASE(x, y)                              \
  77                 case x:                         \
  78                         if (nflags > 0) {    \
  79                                 usage();        \
  80                                 exit(1);        \
  81                         } else                  \
  82                                 y##_flag = 1;   \
  83                                 nflags++;       \
  84                         break
  85 
  86         while ((c = getopt(argc, argv, "nadfs")) != EOF) {
  87                 switch (c) {
  88                 case '?':
  89                         usage();
  90                         exit(1);
  91                         /* NOTREACHED */
  92                         break;
  93                 case 'n':
  94                         n_flag = 1;
  95                         break;
  96                 CASE('a', a);
  97                 CASE('d', d);
  98                 CASE('f', f);
  99                 CASE('s', s);
 100                 }
 101         }
 102 
 103 #undef CASE
 104 
 105         /*
 106          * -n only allowed with -a
 107          */
 108         if (n_flag && !a_flag) {
 109                 usage();
 110                 exit(1);
 111         }
 112 
 113         argsleft = argc - optind;
 114 
 115         if (a_flag && (argsleft == 0)) {
 116                 /*
 117                  * the easiest way to get the complete arp table
 118                  * is to let netstat, which prints it as part of
 119                  * the MIB statistics, do it.
 120                  */
 121                 (void) execl("/usr/bin/netstat", "netstat",
 122                     (n_flag ? "-np" : "-p"),
 123                     "-f", "inet", (char *)0);
 124                 (void) fprintf(stderr, "failed to exec netstat: %s\n",
 125                     strerror(errno));
 126                 exit(1);
 127 
 128         } else if (s_flag && (argsleft >= 2)) {
 129                 if (set(argsleft, &argv[optind]) != 0)
 130                         exit(1);
 131 
 132         } else if (d_flag && (argsleft == 1)) {
 133                 delete(argv[optind]);
 134 
 135         } else if (f_flag && (argsleft == 1)) {
 136                 if (file(argv[optind]) != 0)
 137                         exit(1);
 138 
 139         } else if ((nflags == 0) && (argsleft == 1)) {
 140                 get(argv[optind]);
 141 
 142         } else {
 143                 usage();
 144                 exit(1);
 145         }
 146         return (0);
 147 }
 148 
 149 /*
 150  * Process a file to set standard arp entries
 151  */
 152 static int
 153 file(char *name)
 154 {
 155         /*
 156          * A line of input can be:
 157          * <hostname> <macaddr> ["temp"] ["pub"] ["trail"] ["permanent"]
 158          */
 159 #define MAX_LINE_LEN    (MAXHOSTNAMELEN + \
 160         sizeof (" xx:xx:xx:xx:xx:xx temp pub trail permanent\n"))
 161 #define MIN_ARGS        2
 162 #define MAX_ARGS        5
 163 
 164         FILE *fp;
 165         char line[MAX_LINE_LEN];
 166         int retval;
 167 
 168         if ((fp = fopen(name, "r")) == NULL) {
 169                 (void) fprintf(stderr, "arp: cannot open %s\n", name);
 170                 exit(1);
 171         }
 172 
 173         retval = 0;
 174         while (fgets(line, MAX_LINE_LEN, fp) != NULL) {
 175                 char line_copy[MAX_LINE_LEN];
 176                 char *args[MAX_ARGS];
 177                 char *start;
 178                 int i;
 179 
 180                 /*
 181                  * Keep a copy of the un-altered line for error
 182                  * reporting.
 183                  */
 184                 (void) strlcpy(line_copy, line, MAX_LINE_LEN);
 185 
 186                 start = line_copy;
 187                 for (i = 0; i < MAX_ARGS; i++) {
 188                         if ((args[i] = strtok(start, " \t\n")) == NULL)
 189                                 break;
 190 
 191                         start = NULL;
 192                 }
 193 
 194                 if (i < MIN_ARGS) {
 195                         (void) fprintf(stderr, "arp: bad line: %s\n",
 196                             line);
 197                         retval = 1;
 198                         continue;
 199                 }
 200 
 201                 if (set(i, args) != 0)
 202                         retval = 1;
 203         }
 204 
 205 #undef  MAX_LINE_LEN
 206 #undef  MIN_ARGS
 207 #undef  MAX_ARGS
 208 
 209         (void) fclose(fp);
 210         return (retval);
 211 }
 212 
 213 /*
 214  * Set an individual arp entry
 215  */
 216 static int
 217 set(int argc, char *argv[])
 218 {
 219         struct xarpreq ar;
 220         struct hostent *hp;
 221         struct sockaddr_in *sin;
 222         uchar_t *ea;
 223         int s;
 224         char *host = argv[0], *eaddr = argv[1];
 225 
 226         argc -= 2;
 227         argv += 2;
 228         (void) memset(&ar, 0, sizeof (ar));
 229         sin = (struct sockaddr_in *)&ar.xarp_pa;
 230         sin->sin_family = AF_INET;
 231         sin->sin_addr.s_addr = inet_addr(host);
 232         if (sin->sin_addr.s_addr == (in_addr_t)-1) {
 233                 hp = gethostbyname(host);
 234                 if (hp == NULL) {
 235                         (void) fprintf(stderr, "arp: %s: unknown host\n",
 236                             host);
 237                         return (1);
 238                 }
 239                 (void) memcpy(&sin->sin_addr, hp->h_addr,
 240                     sizeof (sin->sin_addr));
 241         }
 242         ea = _link_aton(eaddr, &s);
 243         if (ea == NULL) {
 244                 if (s == -1) {
 245                         (void) fprintf(stderr,
 246                             "arp: invalid link layer address '%s'\n", eaddr);
 247                         return (1);
 248                 }
 249                 perror("arp: nomem");
 250                 exit(1);
 251         }
 252         ar.xarp_ha.sdl_alen = s;
 253         (void) memcpy(LLADDR(&ar.xarp_ha), ea, ar.xarp_ha.sdl_alen);
 254         free(ea);
 255         ar.xarp_ha.sdl_family = AF_LINK;
 256         ar.xarp_flags = ATF_PERM;
 257         while (argc-- > 0) {
 258                 if (strncmp(argv[0], "temp", 4) == 0) {
 259                         ar.xarp_flags &= ~ATF_PERM;
 260                 } else if (strncmp(argv[0], "pub", 3) == 0) {
 261                         ar.xarp_flags |= ATF_PUBL;
 262                 } else if (strncmp(argv[0], "trail", 5) == 0) {
 263                         ar.xarp_flags |= ATF_USETRAILERS;
 264                 } else if (strcmp(argv[0], "permanent") == 0) {
 265                         ar.xarp_flags |= ATF_AUTHORITY;
 266                 } else {
 267                         (void) fprintf(stderr,
 268                             "arp: unknown keyword '%s'\n", argv[0]);
 269                         return (1);
 270                 }
 271                 argv++;
 272         }
 273 
 274         if ((ar.xarp_flags & (ATF_PERM|ATF_AUTHORITY)) == ATF_AUTHORITY) {
 275                 (void) fprintf(stderr, "arp: 'temp' and 'permanent' flags are "
 276                     "not usable together.\n");
 277                 return (1);
 278         }
 279 
 280         s = socket(AF_INET, SOCK_DGRAM, 0);
 281         if (s < 0) {
 282                 perror("arp: socket");
 283                 exit(1);
 284         }
 285         if (ioctl(s, SIOCSXARP, (caddr_t)&ar) < 0) {
 286                 perror(host);
 287                 exit(1);
 288         }
 289         (void) close(s);
 290         return (0);
 291 }
 292 
 293 /*
 294  * Display an individual arp entry
 295  */
 296 static void
 297 get(char *host)
 298 {
 299         struct xarpreq ar;
 300         struct hostent *hp;
 301         struct sockaddr_in *sin;
 302         uchar_t *ea;
 303         int s;
 304         char *str = NULL;
 305 
 306         (void) memset(&ar, 0, sizeof (ar));
 307         sin = (struct sockaddr_in *)&ar.xarp_pa;
 308         sin->sin_family = AF_INET;
 309         sin->sin_addr.s_addr = inet_addr(host);
 310         if (sin->sin_addr.s_addr == (in_addr_t)-1) {
 311                 hp = gethostbyname(host);
 312                 if (hp == NULL) {
 313                         (void) fprintf(stderr, "arp: %s: unknown host\n",
 314                             host);
 315                         exit(1);
 316                 }
 317                 (void) memcpy(&sin->sin_addr, hp->h_addr,
 318                     sizeof (sin->sin_addr));
 319         }
 320         s = socket(AF_INET, SOCK_DGRAM, 0);
 321         if (s < 0) {
 322                 perror("arp: socket");
 323                 exit(1);
 324         }
 325         ar.xarp_ha.sdl_family = AF_LINK;
 326         if (ioctl(s, SIOCGXARP, (caddr_t)&ar) < 0) {
 327                 if (errno == ENXIO)
 328                         (void) printf("%s (%s) -- no entry\n",
 329                             host, inet_ntoa(sin->sin_addr));
 330                 else
 331                         perror("SIOCGXARP");
 332                 exit(1);
 333         }
 334         (void) close(s);
 335         ea = (uchar_t *)LLADDR(&ar.xarp_ha);
 336         if (ar.xarp_flags & ATF_COM) {
 337                 str = _link_ntoa(ea, str, ar.xarp_ha.sdl_alen, IFT_OTHER);
 338                 if (str != NULL) {
 339                         (void) printf("%s (%s) at %s", host,
 340                             inet_ntoa(sin->sin_addr), str);
 341                         free(str);
 342                 } else {
 343                         perror("arp: nomem");
 344                         exit(1);
 345                 }
 346         } else {
 347                 (void) printf("%s (%s) at (incomplete)", host,
 348                     inet_ntoa(sin->sin_addr));
 349         }
 350         if (!(ar.xarp_flags & ATF_PERM))
 351                 (void) printf(" temp");
 352         if (ar.xarp_flags & ATF_PUBL)
 353                 (void) printf(" pub");
 354         if (ar.xarp_flags & ATF_USETRAILERS)
 355                 (void) printf(" trail");
 356         if (ar.xarp_flags & ATF_AUTHORITY)
 357                 (void) printf(" permanent");
 358         (void) printf("\n");
 359 }
 360 
 361 /*
 362  * Delete an arp entry
 363  */
 364 static void
 365 delete(char *host)
 366 {
 367         struct xarpreq ar;
 368         struct hostent *hp;
 369         struct sockaddr_in *sin;
 370         int s;
 371 
 372         (void) memset(&ar, 0, sizeof (ar));
 373         sin = (struct sockaddr_in *)&ar.xarp_pa;
 374         sin->sin_family = AF_INET;
 375         sin->sin_addr.s_addr = inet_addr(host);
 376         if (sin->sin_addr.s_addr == (in_addr_t)-1) {
 377                 hp = gethostbyname(host);
 378                 if (hp == NULL) {
 379                         (void) fprintf(stderr, "arp: %s: unknown host\n",
 380                             host);
 381                         exit(1);
 382                 }
 383                 (void) memcpy(&sin->sin_addr, hp->h_addr,
 384                     sizeof (sin->sin_addr));
 385         }
 386         s = socket(AF_INET, SOCK_DGRAM, 0);
 387         if (s < 0) {
 388                 perror("arp: socket");
 389                 exit(1);
 390         }
 391         ar.xarp_ha.sdl_family = AF_LINK;
 392         if (ioctl(s, SIOCDXARP, (caddr_t)&ar) < 0) {
 393                 if (errno == ENXIO)
 394                         (void) printf("%s (%s) -- no entry\n",
 395                             host, inet_ntoa(sin->sin_addr));
 396                 else
 397                         perror("SIOCDXARP");
 398                 exit(1);
 399         }
 400         (void) close(s);
 401         (void) printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr));
 402 }
 403 
 404 static void
 405 usage(void)
 406 {
 407         (void) printf("Usage: arp hostname\n");
 408         (void) printf("       arp -a [-n]\n");
 409         (void) printf("       arp -d hostname\n");
 410         (void) printf("       arp -s hostname ether_addr "
 411             "[temp] [pub] [trail] [permanent]\n");
 412         (void) printf("       arp -f filename\n");
 413 }