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