1 /* $NetBSD: arp.c,v 1.24 2000/07/12 22:36:12 mason Exp $ */ 2 3 /* 4 * Copyright (c) 1984, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Sun Microsystems, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 __COPYRIGHT("@(#) Copyright (c) 1984, 1993\n\ 42 The Regents of the University of California. All rights reserved.\n"); 43 #endif /* not lint */ 44 45 #ifndef lint 46 #if 0 47 static char sccsid[] = "@(#)arp.c 8.3 (Berkeley) 4/28/95"; 48 #else 49 __RCSID("$NetBSD: arp.c,v 1.24 2000/07/12 22:36:12 mason Exp $"); 50 #endif 51 #endif /* not lint */ 52 53 /* 54 * arp - display, set, and delete arp table entries 55 */ 56 57 #include <sys/param.h> 58 #include <sys/file.h> 59 #include <sys/socket.h> 60 #include <sys/sysctl.h> 61 62 #include <net/if.h> 63 #include <net/if_dl.h> 64 #include <net/if_ether.h> 65 #include <net/if_types.h> 66 #include <net/route.h> 67 #include <netinet/in.h> 68 #include <netinet/if_inarp.h> 69 #include <arpa/inet.h> 70 71 #include <err.h> 72 #include <errno.h> 73 #include <netdb.h> 74 #include <nlist.h> 75 #include <paths.h> 76 #include <stdio.h> 77 #include <stdlib.h> 78 #include <string.h> 79 #include <unistd.h> 80 81 int delete __P((const char *, const char *)); 82 void dump __P((u_long)); 83 void sdl_print __P((const struct sockaddr_dl *)); 84 int atosdl __P((const char *s, struct sockaddr_dl *sdl)); 85 int file __P((char *)); 86 void get __P((const char *)); 87 int getinetaddr __P((const char *, struct in_addr *)); 88 void getsocket __P((void)); 89 int main __P((int, char **)); 90 int rtmsg __P((int)); 91 int set __P((int, char **)); 92 void usage __P((void)); 93 94 static int pid; 95 static int nflag, vflag; 96 static int s = -1; 97 98 int 99 main(argc, argv) 100 int argc; 101 char **argv; 102 { 103 int ch; 104 int op = 0; 105 106 pid = getpid(); 107 108 while ((ch = getopt(argc, argv, "andsfv")) != -1) 109 switch((char)ch) { 110 case 'a': 111 case 'd': 112 case 's': 113 case 'f': 114 if (op) 115 usage(); 116 op = ch; 117 break; 118 case 'n': 119 nflag = 1; 120 break; 121 case 'v': 122 vflag = 1; 123 break; 124 default: 125 usage(); 126 } 127 argc -= optind; 128 argv += optind; 129 130 switch((char)op) { 131 case 'a': 132 dump(0); 133 break; 134 case 'd': 135 if (argc < 1 || argc > 2) 136 usage(); 137 (void)delete(argv[0], argv[1]); 138 break; 139 case 's': 140 if (argc < 2 || argc > 5) 141 usage(); 142 return (set(argc, argv) ? 1 : 0); 143 case 'f': 144 if (argc != 1) 145 usage(); 146 return (file(argv[0])); 147 default: 148 if (argc != 1) 149 usage(); 150 get(argv[0]); 151 break; 152 } 153 return (0); 154 } 155 156 /* 157 * Process a file to set standard arp entries 158 */ 159 int 160 file(name) 161 char *name; 162 { 163 char line[100], arg[5][50], *args[5]; 164 int i, retval; 165 FILE *fp; 166 167 if ((fp = fopen(name, "r")) == NULL) 168 err(1, "cannot open %s", name); 169 args[0] = &arg[0][0]; 170 args[1] = &arg[1][0]; 171 args[2] = &arg[2][0]; 172 args[3] = &arg[3][0]; 173 args[4] = &arg[4][0]; 174 retval = 0; 175 while (fgets(line, 100, fp) != NULL) { 176 i = sscanf(line, "%s %s %s %s %s", arg[0], arg[1], arg[2], 177 arg[3], arg[4]); 178 if (i < 2) { 179 warnx("bad line: %s", line); 180 retval = 1; 181 continue; 182 } 183 if (set(i, args)) 184 retval = 1; 185 } 186 fclose(fp); 187 return (retval); 188 } 189 190 void 191 getsocket() 192 { 193 if (s >= 0) 194 return; 195 s = socket(PF_ROUTE, SOCK_RAW, 0); 196 if (s < 0) 197 err(1, "socket"); 198 } 199 200 struct sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}}; 201 struct sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }, sin_m; 202 struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m; 203 int expire_time, flags, export_only, doing_proxy, found_entry; 204 struct { 205 struct rt_msghdr m_rtm; 206 char m_space[512]; 207 } m_rtmsg; 208 209 /* 210 * Set an individual arp entry 211 */ 212 int 213 set(argc, argv) 214 int argc; 215 char **argv; 216 { 217 struct sockaddr_inarp *sin; 218 struct sockaddr_dl *sdl; 219 struct rt_msghdr *rtm; 220 char *host = argv[0], *eaddr; 221 int rval; 222 223 sin = &sin_m; 224 rtm = &(m_rtmsg.m_rtm); 225 eaddr = argv[1]; 226 227 getsocket(); 228 argc -= 2; 229 argv += 2; 230 sdl_m = blank_sdl; /* struct copy */ 231 sin_m = blank_sin; /* struct copy */ 232 if (getinetaddr(host, &sin->sin_addr) == -1) 233 return (1); 234 if (atosdl(eaddr, &sdl_m)) 235 warnx("invalid link-level address '%s'", eaddr); 236 doing_proxy = flags = export_only = expire_time = 0; 237 while (argc-- > 0) { 238 if (strncmp(argv[0], "temp", 4) == 0) { 239 struct timeval time; 240 (void)gettimeofday(&time, 0); 241 expire_time = time.tv_sec + 20 * 60; 242 } 243 else if (strncmp(argv[0], "pub", 3) == 0) { 244 flags |= RTF_ANNOUNCE; 245 doing_proxy = SIN_PROXY; 246 } else if (strncmp(argv[0], "trail", 5) == 0) { 247 (void)printf( 248 "%s: Sending trailers is no longer supported\n", 249 host); 250 } 251 argv++; 252 } 253 tryagain: 254 if (rtmsg(RTM_GET) < 0) { 255 warn("%s", host); 256 return (1); 257 } 258 sin = (struct sockaddr_inarp *)(rtm + 1); 259 sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin); 260 if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { 261 if (sdl->sdl_family == AF_LINK && 262 (rtm->rtm_flags & RTF_LLINFO) && 263 !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { 264 case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: 265 case IFT_ISO88024: case IFT_ISO88025: case IFT_ARCNET: 266 goto overwrite; 267 } 268 if (doing_proxy == 0) { 269 (void)printf("set: can only proxy for %s\n", host); 270 return (1); 271 } 272 if (sin_m.sin_other & SIN_PROXY) { 273 (void)printf( 274 "set: proxy entry exists for non 802 device\n"); 275 return (1); 276 } 277 sin_m.sin_other = SIN_PROXY; 278 export_only = 1; 279 goto tryagain; 280 } 281 overwrite: 282 if (sdl->sdl_family != AF_LINK) { 283 (void)printf("cannot intuit interface index and type for %s\n", 284 host); 285 return (1); 286 } 287 sdl_m.sdl_type = sdl->sdl_type; 288 sdl_m.sdl_index = sdl->sdl_index; 289 rval = rtmsg(RTM_ADD); 290 if (vflag) 291 (void)printf("%s (%s) added\n", host, eaddr); 292 return (rval); 293 } 294 295 /* 296 * Display an individual arp entry 297 */ 298 void 299 get(host) 300 const char *host; 301 { 302 struct sockaddr_inarp *sin; 303 304 sin = &sin_m; 305 sin_m = blank_sin; /* struct copy */ 306 if (getinetaddr(host, &sin->sin_addr) == -1) 307 exit(1); 308 dump(sin->sin_addr.s_addr); 309 if (found_entry == 0) { 310 (void)printf("%s (%s) -- no entry\n", host, 311 inet_ntoa(sin->sin_addr)); 312 exit(1); 313 } 314 } 315 316 /* 317 * Delete an arp entry 318 */ 319 int 320 delete(host, info) 321 const char *host; 322 const char *info; 323 { 324 struct sockaddr_inarp *sin; 325 struct rt_msghdr *rtm; 326 struct sockaddr_dl *sdl; 327 328 sin = &sin_m; 329 rtm = &m_rtmsg.m_rtm; 330 331 if (info && strncmp(info, "pro", 3) ) 332 export_only = 1; 333 getsocket(); 334 sin_m = blank_sin; /* struct copy */ 335 if (getinetaddr(host, &sin->sin_addr) == -1) 336 return (1); 337 tryagain: 338 if (rtmsg(RTM_GET) < 0) { 339 warn("%s", host); 340 return (1); 341 } 342 sin = (struct sockaddr_inarp *)(rtm + 1); 343 sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin); 344 if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { 345 if (sdl->sdl_family == AF_LINK && 346 (rtm->rtm_flags & RTF_LLINFO) && 347 !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { 348 case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: 349 case IFT_ISO88024: case IFT_ISO88025: case IFT_ARCNET: 350 goto delete; 351 } 352 } 353 if (sin_m.sin_other & SIN_PROXY) { 354 warnx("delete: can't locate %s", host); 355 return (1); 356 } else { 357 sin_m.sin_other = SIN_PROXY; 358 goto tryagain; 359 } 360 delete: 361 if (sdl->sdl_family != AF_LINK) { 362 (void)printf("cannot locate %s\n", host); 363 return (1); 364 } 365 if (rtmsg(RTM_DELETE)) 366 return (1); 367 if (vflag) 368 (void)printf("%s (%s) deleted\n", host, 369 inet_ntoa(sin->sin_addr)); 370 return (0); 371 } 372 373 /* 374 * Dump the entire arp table 375 */ 376 void 377 dump(addr) 378 u_long addr; 379 { 380 int mib[6]; 381 size_t needed; 382 char *host, *lim, *buf, *next; 383 struct rt_msghdr *rtm; 384 struct sockaddr_inarp *sin; 385 struct sockaddr_dl *sdl; 386 extern int h_errno; 387 struct hostent *hp; 388 389 mib[0] = CTL_NET; 390 mib[1] = PF_ROUTE; 391 mib[2] = 0; 392 mib[3] = AF_INET; 393 mib[4] = NET_RT_FLAGS; 394 mib[5] = RTF_LLINFO; 395 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 396 err(1, "route-sysctl-estimate"); 397 if (needed == 0) 398 return; 399 if ((buf = malloc(needed)) == NULL) 400 err(1, "malloc"); 401 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 402 err(1, "actual retrieval of routing table"); 403 lim = buf + needed; 404 for (next = buf; next < lim; next += rtm->rtm_msglen) { 405 rtm = (struct rt_msghdr *)next; 406 sin = (struct sockaddr_inarp *)(rtm + 1); 407 sdl = (struct sockaddr_dl *)(sin + 1); 408 if (addr) { 409 if (addr != sin->sin_addr.s_addr) 410 continue; 411 found_entry = 1; 412 } 413 if (nflag == 0) 414 hp = gethostbyaddr((caddr_t)&(sin->sin_addr), 415 sizeof sin->sin_addr, AF_INET); 416 else 417 hp = 0; 418 if (hp) 419 host = hp->h_name; 420 else { 421 host = "?"; 422 if (h_errno == TRY_AGAIN) 423 nflag = 1; 424 } 425 (void)printf("%s (%s) at ", host, inet_ntoa(sin->sin_addr)); 426 if (sdl->sdl_alen) 427 sdl_print(sdl); 428 else 429 (void)printf("(incomplete)"); 430 if (rtm->rtm_rmx.rmx_expire == 0) 431 (void)printf(" permanent"); 432 if (sin->sin_other & SIN_PROXY) 433 (void)printf(" published (proxy only)"); 434 if (rtm->rtm_addrs & RTA_NETMASK) { 435 sin = (struct sockaddr_inarp *) 436 (sdl->sdl_len + (char *)sdl); 437 if (sin->sin_addr.s_addr == 0xffffffff) 438 (void)printf(" published"); 439 if (sin->sin_len != 8) 440 (void)printf("(wierd)"); 441 } 442 (void)printf("\n"); 443 } 444 } 445 446 void 447 sdl_print(sdl) 448 const struct sockaddr_dl *sdl; 449 { 450 int i; 451 u_int8_t *p; 452 const char *hexfmt = "%x"; 453 454 if (sdl->sdl_type == IFT_ETHER || sdl->sdl_type == IFT_FDDI) 455 hexfmt = "%02x"; 456 457 i = sdl->sdl_alen; 458 p = LLADDR(sdl); 459 460 (void)printf(hexfmt, *p); 461 while (--i > 0) { 462 putchar(':'); 463 (void)printf(hexfmt, *++p); 464 } 465 } 466 467 int 468 atosdl(s, sdl) 469 const char *s; 470 struct sockaddr_dl *sdl; 471 { 472 int i; 473 long b; 474 caddr_t endp; 475 caddr_t p; 476 char *t, *r; 477 478 p = LLADDR(sdl); 479 endp = ((caddr_t)sdl) + sdl->sdl_len; 480 i = 0; 481 482 b = strtol(s, &t, 16); 483 if (t == s) 484 return 1; 485 486 *p++ = b; 487 ++i; 488 while ((p < endp) && (*t++ == ':')) { 489 b = strtol(t, &r, 16); 490 if (r == t) 491 break; 492 *p++ = b; 493 ++i; 494 t = r; 495 } 496 sdl->sdl_alen = i; 497 498 return 0; 499 } 500 501 void 502 usage() 503 { 504 extern char *__progname; 505 506 (void)fprintf(stderr, "usage: %s [-n] hostname\n", __progname); 507 (void)fprintf(stderr, "usage: %s [-n] -a\n", __progname); 508 (void)fprintf(stderr, "usage: %s -d hostname\n", __progname); 509 (void)fprintf(stderr, 510 "usage: %s -s hostname ether_addr [temp] [pub]\n", __progname); 511 (void)fprintf(stderr, "usage: %s -f filename\n", __progname); 512 exit(1); 513 } 514 515 int 516 rtmsg(cmd) 517 int cmd; 518 { 519 static int seq; 520 int rlen; 521 struct rt_msghdr *rtm; 522 char *cp; 523 int l; 524 525 rtm = &m_rtmsg.m_rtm; 526 cp = m_rtmsg.m_space; 527 errno = 0; 528 529 if (cmd == RTM_DELETE) 530 goto doit; 531 (void)memset(&m_rtmsg, 0, sizeof(m_rtmsg)); 532 rtm->rtm_flags = flags; 533 rtm->rtm_version = RTM_VERSION; 534 535 switch (cmd) { 536 default: 537 errx(1, "internal wrong cmd"); 538 /*NOTREACHED*/ 539 case RTM_ADD: 540 rtm->rtm_addrs |= RTA_GATEWAY; 541 rtm->rtm_rmx.rmx_expire = expire_time; 542 rtm->rtm_inits = RTV_EXPIRE; 543 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); 544 sin_m.sin_other = 0; 545 if (doing_proxy) { 546 if (export_only) 547 sin_m.sin_other = SIN_PROXY; 548 else { 549 rtm->rtm_addrs |= RTA_NETMASK; 550 rtm->rtm_flags &= ~RTF_HOST; 551 } 552 } 553 /* FALLTHROUGH */ 554 case RTM_GET: 555 rtm->rtm_addrs |= RTA_DST; 556 } 557 #define NEXTADDR(w, s) \ 558 if (rtm->rtm_addrs & (w)) { \ 559 (void)memcpy(cp, &s, sizeof(s)); cp += sizeof(s);} 560 561 NEXTADDR(RTA_DST, sin_m); 562 NEXTADDR(RTA_GATEWAY, sdl_m); 563 NEXTADDR(RTA_NETMASK, so_mask); 564 565 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 566 doit: 567 l = rtm->rtm_msglen; 568 rtm->rtm_seq = ++seq; 569 rtm->rtm_type = cmd; 570 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 571 if (errno != ESRCH || cmd != RTM_DELETE) { 572 warn("writing to routing socket"); 573 return (-1); 574 } 575 } 576 do { 577 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 578 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 579 if (l < 0) 580 warn("read from routing socket"); 581 return (0); 582 } 583 584 int 585 getinetaddr(host, inap) 586 const char *host; 587 struct in_addr *inap; 588 { 589 struct hostent *hp; 590 591 if (inet_aton(host, inap) == 1) 592 return (0); 593 if ((hp = gethostbyname(host)) == NULL) { 594 warnx("%s: %s\n", host, hstrerror(h_errno)); 595 return (-1); 596 } 597 (void)memcpy(inap, hp->h_addr, sizeof(*inap)); 598 return (0); 599 } 600