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