1 /* $OpenBSD: arp.c,v 1.56 2014/03/18 14:18:22 mikeb 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, delete arp table entries and wake up hosts. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/file.h> 42 #include <sys/socket.h> 43 #include <sys/sysctl.h> 44 #include <sys/ioctl.h> 45 #include <net/bpf.h> 46 #include <net/if.h> 47 #include <net/if_dl.h> 48 #include <net/if_types.h> 49 #include <net/route.h> 50 #include <netinet/in.h> 51 #include <netinet/if_ether.h> 52 #include <arpa/inet.h> 53 54 #include <netdb.h> 55 #include <errno.h> 56 #include <err.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <paths.h> 61 #include <unistd.h> 62 #include <ifaddrs.h> 63 64 int delete(const char *, const char *); 65 void search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl, 66 struct sockaddr_inarp *sin, struct rt_msghdr *rtm)); 67 void print_entry(struct sockaddr_dl *sdl, 68 struct sockaddr_inarp *sin, struct rt_msghdr *rtm); 69 void nuke_entry(struct sockaddr_dl *sdl, 70 struct sockaddr_inarp *sin, struct rt_msghdr *rtm); 71 int wake(const char *ether_addr, const char *iface); 72 void ether_print(const char *); 73 int file(char *); 74 int get(const char *); 75 int getinetaddr(const char *, struct in_addr *); 76 void getsocket(void); 77 int rtmsg(int); 78 int set(int, char **); 79 void usage(void); 80 81 static pid_t pid; 82 static int replace; /* replace entries when adding */ 83 static int nflag; /* no reverse dns lookups */ 84 static int aflag; /* do it for all entries */ 85 static int s = -1; 86 static int rdomain; 87 88 extern int h_errno; 89 90 /* ROUNDUP() is nasty, but it is identical to what's in the kernel. */ 91 #define ROUNDUP(a) \ 92 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 93 94 /* which function we're supposed to do */ 95 #define F_GET 1 96 #define F_SET 2 97 #define F_FILESET 3 98 #define F_DELETE 4 99 #define F_WAKE 5 100 101 int 102 main(int argc, char *argv[]) 103 { 104 int ch, func = 0, rtn; 105 const char *errstr; 106 107 pid = getpid(); 108 opterr = 0; 109 rdomain = getrtable(); 110 while ((ch = getopt(argc, argv, "andsFfV:W")) != -1) { 111 switch (ch) { 112 case 'a': 113 aflag = 1; 114 break; 115 case 'n': 116 nflag = 1; 117 break; 118 case 'd': 119 if (func) 120 usage(); 121 func = F_DELETE; 122 break; 123 case 's': 124 if (func) 125 usage(); 126 func = F_SET; 127 break; 128 case 'F': 129 replace = 1; 130 break; 131 case 'f': 132 if (func) 133 usage(); 134 func = F_FILESET; 135 break; 136 case 'V': 137 rdomain = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr); 138 if (errstr != NULL) { 139 warn("bad rdomain: %s", errstr); 140 usage(); 141 } 142 break; 143 case 'W': 144 if (func) 145 usage(); 146 func = F_WAKE; 147 break; 148 default: 149 usage(); 150 break; 151 } 152 } 153 argc -= optind; 154 argv += optind; 155 156 if (!func) 157 func = F_GET; 158 rtn = 0; 159 160 switch (func) { 161 case F_GET: 162 if (aflag && argc == 0) 163 search(0, print_entry); 164 else if (!aflag && argc == 1) 165 rtn = get(argv[0]); 166 else 167 usage(); 168 break; 169 case F_SET: 170 if (argc < 2 || argc > 5) 171 usage(); 172 if (replace) 173 delete(argv[0], NULL); 174 rtn = set(argc, argv) ? 1 : 0; 175 break; 176 case F_DELETE: 177 if (aflag && argc == 0) 178 search(0, nuke_entry); 179 else if (!aflag && argc == 1) 180 rtn = delete(argv[0], argv[1]); 181 else 182 usage(); 183 break; 184 case F_FILESET: 185 if (argc != 1) 186 usage(); 187 rtn = file(argv[0]); 188 break; 189 case F_WAKE: 190 if (aflag || nflag || replace || rdomain > 0) 191 usage(); 192 if (argc == 1) 193 rtn = wake(argv[0], NULL); 194 else if (argc == 2) 195 rtn = wake(argv[0], argv[1]); 196 else 197 usage(); 198 break; 199 } 200 return (rtn); 201 } 202 203 /* 204 * Process a file to set standard arp entries 205 */ 206 int 207 file(char *name) 208 { 209 char line[100], arg[5][50], *args[5]; 210 int i, retval; 211 FILE *fp; 212 213 if ((fp = fopen(name, "r")) == NULL) 214 err(1, "cannot open %s", name); 215 args[0] = &arg[0][0]; 216 args[1] = &arg[1][0]; 217 args[2] = &arg[2][0]; 218 args[3] = &arg[3][0]; 219 args[4] = &arg[4][0]; 220 retval = 0; 221 while (fgets(line, sizeof(line), fp) != NULL) { 222 i = sscanf(line, "%49s %49s %49s %49s %49s", arg[0], arg[1], 223 arg[2], arg[3], arg[4]); 224 if (i < 2) { 225 warnx("bad line: %s", line); 226 retval = 1; 227 continue; 228 } 229 if (replace) 230 delete(arg[0], NULL); 231 if (set(i, args)) 232 retval = 1; 233 } 234 fclose(fp); 235 return (retval); 236 } 237 238 void 239 getsocket(void) 240 { 241 socklen_t len = sizeof(rdomain); 242 243 if (s >= 0) 244 return; 245 s = socket(PF_ROUTE, SOCK_RAW, 0); 246 if (s < 0) 247 err(1, "socket"); 248 if (setsockopt(s, PF_ROUTE, ROUTE_TABLEFILTER, &rdomain, len) < 0) 249 err(1, "ROUTE_TABLEFILTER"); 250 } 251 252 struct sockaddr_in so_mask = { 8, 0, 0, { 0xffffffff } }; 253 struct sockaddr_inarp blank_sin = { sizeof(blank_sin), AF_INET }, sin_m; 254 struct sockaddr_dl blank_sdl = { sizeof(blank_sdl), AF_LINK }, sdl_m; 255 time_t expire_time; 256 int flags, export_only, doing_proxy, found_entry; 257 struct { 258 struct rt_msghdr m_rtm; 259 char m_space[512]; 260 } m_rtmsg; 261 262 /* 263 * Set an individual arp entry 264 */ 265 int 266 set(int argc, char *argv[]) 267 { 268 struct sockaddr_inarp *sin; 269 struct sockaddr_dl *sdl; 270 struct rt_msghdr *rtm; 271 char *eaddr = argv[1], *host = argv[0]; 272 struct ether_addr *ea; 273 274 sin = &sin_m; 275 rtm = &(m_rtmsg.m_rtm); 276 277 getsocket(); 278 argc -= 2; 279 argv += 2; 280 sdl_m = blank_sdl; /* struct copy */ 281 sin_m = blank_sin; /* struct copy */ 282 if (getinetaddr(host, &sin->sin_addr) == -1) 283 return (1); 284 ea = ether_aton(eaddr); 285 if (ea == NULL) 286 errx(1, "invalid ethernet address: %s", eaddr); 287 memcpy(LLADDR(&sdl_m), ea, sizeof(*ea)); 288 sdl_m.sdl_alen = 6; 289 expire_time = 0; 290 doing_proxy = flags = export_only = 0; 291 while (argc-- > 0) { 292 if (strncmp(argv[0], "temp", 4) == 0) { 293 struct timeval now; 294 295 gettimeofday(&now, 0); 296 expire_time = now.tv_sec + 20 * 60; 297 if (flags & RTF_PERMANENT_ARP) { 298 /* temp or permanent, not both */ 299 usage(); 300 return (0); 301 } 302 } else if (strncmp(argv[0], "pub", 3) == 0) { 303 flags |= RTF_ANNOUNCE; 304 doing_proxy = SIN_PROXY; 305 } else if (strncmp(argv[0], "permanent", 9) == 0) { 306 flags |= RTF_PERMANENT_ARP; 307 if (expire_time != 0) { 308 /* temp or permanent, not both */ 309 usage(); 310 return (0); 311 } 312 } else if (strncmp(argv[0], "trail", 5) == 0) 313 printf("%s: Sending trailers is no longer supported\n", 314 host); 315 316 argv++; 317 } 318 319 tryagain: 320 if (rtmsg(RTM_GET) < 0) { 321 warn("%s", host); 322 return (1); 323 } 324 sin = (struct sockaddr_inarp *)((char *)rtm + rtm->rtm_hdrlen); 325 sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin_len) + (char *)sin); 326 if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { 327 if (sdl->sdl_family == AF_LINK && 328 (rtm->rtm_flags & RTF_LLINFO) && 329 !(rtm->rtm_flags & RTF_GATEWAY)) 330 switch (sdl->sdl_type) { 331 case IFT_ETHER: 332 case IFT_FDDI: 333 case IFT_ISO88023: 334 case IFT_ISO88024: 335 case IFT_ISO88025: 336 case IFT_CARP: 337 goto overwrite; 338 } 339 340 if (doing_proxy == 0) { 341 printf("set: can only proxy for %s\n", host); 342 return (1); 343 } 344 if (sin_m.sin_other & SIN_PROXY) { 345 printf("set: proxy entry exists for non 802 device\n"); 346 return (1); 347 } 348 sin_m.sin_other = SIN_PROXY; 349 export_only = 1; 350 goto tryagain; 351 } 352 353 overwrite: 354 if (sdl->sdl_family != AF_LINK) { 355 printf("cannot intuit interface index and type for %s\n", host); 356 return (1); 357 } 358 sdl_m.sdl_type = sdl->sdl_type; 359 sdl_m.sdl_index = sdl->sdl_index; 360 return (rtmsg(RTM_ADD)); 361 } 362 363 /* 364 * Display an individual arp entry 365 */ 366 int 367 get(const char *host) 368 { 369 struct sockaddr_inarp *sin; 370 371 sin = &sin_m; 372 sin_m = blank_sin; /* struct copy */ 373 if (getinetaddr(host, &sin->sin_addr) == -1) 374 exit(1); 375 search(sin->sin_addr.s_addr, print_entry); 376 if (found_entry == 0) { 377 printf("%s (%s) -- no entry\n", host, inet_ntoa(sin->sin_addr)); 378 return (1); 379 } 380 return (0); 381 } 382 383 /* 384 * Delete an arp entry 385 */ 386 int 387 delete(const char *host, const char *info) 388 { 389 struct sockaddr_inarp *sin; 390 struct rt_msghdr *rtm; 391 struct sockaddr_dl *sdl; 392 393 sin = &sin_m; 394 rtm = &m_rtmsg.m_rtm; 395 396 if (info && strncmp(info, "pro", 3) ) 397 export_only = 1; 398 getsocket(); 399 sin_m = blank_sin; /* struct copy */ 400 if (getinetaddr(host, &sin->sin_addr) == -1) 401 return (1); 402 tryagain: 403 if (rtmsg(RTM_GET) < 0) { 404 warn("%s", host); 405 return (1); 406 } 407 sin = (struct sockaddr_inarp *)((char *)rtm + rtm->rtm_hdrlen); 408 sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin_len) + (char *)sin); 409 if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) 410 if (sdl->sdl_family == AF_LINK && 411 (rtm->rtm_flags & RTF_LLINFO) && 412 !(rtm->rtm_flags & RTF_GATEWAY)) 413 switch (sdl->sdl_type) { 414 case IFT_ETHER: 415 case IFT_FDDI: 416 case IFT_ISO88023: 417 case IFT_ISO88024: 418 case IFT_ISO88025: 419 case IFT_CARP: 420 goto delete; 421 } 422 423 if (sin_m.sin_other & SIN_PROXY) { 424 warnx("delete: can't locate %s", host); 425 return (1); 426 } else { 427 sin_m.sin_other = SIN_PROXY; 428 goto tryagain; 429 } 430 delete: 431 if (sdl->sdl_family != AF_LINK) { 432 printf("cannot locate %s\n", host); 433 return (1); 434 } 435 if (rtmsg(RTM_DELETE)) 436 return (1); 437 printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr)); 438 return (0); 439 } 440 441 /* 442 * Search the entire arp table, and do some action on matching entries. 443 */ 444 void 445 search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl, 446 struct sockaddr_inarp *sin, struct rt_msghdr *rtm)) 447 { 448 int mib[7]; 449 size_t needed; 450 char *lim, *buf = NULL, *next; 451 struct rt_msghdr *rtm; 452 struct sockaddr_inarp *sin; 453 struct sockaddr_dl *sdl; 454 455 mib[0] = CTL_NET; 456 mib[1] = PF_ROUTE; 457 mib[2] = 0; 458 mib[3] = AF_INET; 459 mib[4] = NET_RT_FLAGS; 460 mib[5] = RTF_LLINFO; 461 mib[6] = rdomain; 462 while (1) { 463 if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1) 464 err(1, "route-sysctl-estimate"); 465 if (needed == 0) 466 return; 467 if ((buf = realloc(buf, needed)) == NULL) 468 err(1, "malloc"); 469 if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1) { 470 if (errno == ENOMEM) 471 continue; 472 err(1, "actual retrieval of routing table"); 473 } 474 lim = buf + needed; 475 break; 476 } 477 for (next = buf; next < lim; next += rtm->rtm_msglen) { 478 rtm = (struct rt_msghdr *)next; 479 if (rtm->rtm_version != RTM_VERSION) 480 continue; 481 sin = (struct sockaddr_inarp *)(next + rtm->rtm_hdrlen); 482 sdl = (struct sockaddr_dl *)(sin + 1); 483 if (addr) { 484 if (addr != sin->sin_addr.s_addr) 485 continue; 486 found_entry = 1; 487 } 488 (*action)(sdl, sin, rtm); 489 } 490 free(buf); 491 } 492 493 /* 494 * Display an arp entry 495 */ 496 void 497 print_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin, 498 struct rt_msghdr *rtm) 499 { 500 char ifname[IFNAMSIZ], *host; 501 struct hostent *hp; 502 503 if (nflag == 0) 504 hp = gethostbyaddr((caddr_t)&(sin->sin_addr), 505 sizeof(sin->sin_addr), AF_INET); 506 else 507 hp = 0; 508 if (hp) 509 host = hp->h_name; 510 else { 511 host = "?"; 512 if (h_errno == TRY_AGAIN) 513 nflag = 1; 514 } 515 printf("%s (%s) at ", host, inet_ntoa(sin->sin_addr)); 516 if (sdl->sdl_alen) 517 ether_print(LLADDR(sdl)); 518 else 519 printf("(incomplete)"); 520 if (if_indextoname(sdl->sdl_index, ifname) != NULL) 521 printf(" on %s", ifname); 522 if (rtm->rtm_flags & RTF_PERMANENT_ARP) 523 printf(" permanent"); 524 if (rtm->rtm_rmx.rmx_expire == 0) 525 printf(" static"); 526 if (sin->sin_other & SIN_PROXY) 527 printf(" published (proxy only)"); 528 if (rtm->rtm_addrs & RTA_NETMASK) { 529 sin = (struct sockaddr_inarp *) 530 (ROUNDUP(sdl->sdl_len) + (char *)sdl); 531 if (sin->sin_addr.s_addr == 0xffffffff) 532 printf(" published"); 533 if (sin->sin_len != 8) 534 printf("(weird %d)", sin->sin_len); 535 } 536 printf("\n"); 537 } 538 539 /* 540 * Nuke an arp entry 541 */ 542 void 543 nuke_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin, 544 struct rt_msghdr *rtm) 545 { 546 char ip[20]; 547 548 strlcpy(ip, inet_ntoa(sin->sin_addr), sizeof(ip)); 549 delete(ip, NULL); 550 } 551 552 void 553 ether_print(const char *scp) 554 { 555 const u_char *cp = (u_char *)scp; 556 557 printf("%02x:%02x:%02x:%02x:%02x:%02x", 558 cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); 559 } 560 561 void 562 usage(void) 563 { 564 fprintf(stderr, "usage: arp [-adn] [-V rdomain] hostname\n"); 565 fprintf(stderr, " arp [-F] [-f file] [-V rdomain] " 566 "-s hostname ether_addr\n" 567 " [temp | permanent] [pub]\n"); 568 fprintf(stderr, " arp -W ether_addr [iface]\n"); 569 exit(1); 570 } 571 572 int 573 rtmsg(int cmd) 574 { 575 static int seq; 576 struct rt_msghdr *rtm; 577 char *cp; 578 int l; 579 580 rtm = &m_rtmsg.m_rtm; 581 cp = m_rtmsg.m_space; 582 errno = 0; 583 584 if (cmd == RTM_DELETE) 585 goto doit; 586 memset(&m_rtmsg, 0, sizeof(m_rtmsg)); 587 rtm->rtm_flags = flags; 588 rtm->rtm_version = RTM_VERSION; 589 rtm->rtm_hdrlen = sizeof(*rtm); 590 rtm->rtm_tableid = rdomain; 591 592 switch (cmd) { 593 default: 594 errx(1, "internal wrong cmd"); 595 /*NOTREACHED*/ 596 case RTM_ADD: 597 rtm->rtm_addrs |= RTA_GATEWAY; 598 rtm->rtm_rmx.rmx_expire = expire_time; 599 rtm->rtm_inits = RTV_EXPIRE; 600 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); 601 sin_m.sin_other = 0; 602 if (doing_proxy) { 603 if (export_only) 604 sin_m.sin_other = SIN_PROXY; 605 else { 606 rtm->rtm_addrs |= RTA_NETMASK; 607 rtm->rtm_flags &= ~RTF_HOST; 608 } 609 } 610 /* FALLTHROUGH */ 611 case RTM_GET: 612 rtm->rtm_addrs |= RTA_DST; 613 } 614 615 #define NEXTADDR(w, s) \ 616 if (rtm->rtm_addrs & (w)) { \ 617 memcpy(cp, &s, sizeof(s)); \ 618 cp += ROUNDUP(sizeof(s)); \ 619 } 620 621 NEXTADDR(RTA_DST, sin_m); 622 NEXTADDR(RTA_GATEWAY, sdl_m); 623 NEXTADDR(RTA_NETMASK, so_mask); 624 625 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 626 doit: 627 l = rtm->rtm_msglen; 628 rtm->rtm_seq = ++seq; 629 rtm->rtm_type = cmd; 630 if (write(s, (char *)&m_rtmsg, l) < 0) 631 if (errno != ESRCH || cmd != RTM_DELETE) { 632 warn("writing to routing socket"); 633 return (-1); 634 } 635 636 do { 637 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 638 } while (l > 0 && (rtm->rtm_version != RTM_VERSION || 639 rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 640 641 if (l < 0) 642 warn("read from routing socket"); 643 return (0); 644 } 645 646 int 647 getinetaddr(const char *host, struct in_addr *inap) 648 { 649 struct hostent *hp; 650 651 if (inet_aton(host, inap) == 1) 652 return (0); 653 if ((hp = gethostbyname(host)) == NULL) { 654 warnx("%s: %s", host, hstrerror(h_errno)); 655 return (-1); 656 } 657 memcpy(inap, hp->h_addr, sizeof(*inap)); 658 return (0); 659 } 660 661 /* 662 * Copyright (c) 2011 Jasper Lievisse Adriaanse <jasper@openbsd.org> 663 * Copyright (C) 2006,2007,2008,2009 Marc Balmer <mbalmer@openbsd.org> 664 * Copyright (C) 2000 Eugene M. Kim. All rights reserved. 665 * 666 * Redistribution and use in source and binary forms, with or without 667 * modification, are permitted provided that the following conditions 668 * are met: 669 * 670 * 1. Redistributions of source code must retain the above copyright 671 * notice, this list of conditions and the following disclaimer. 672 * 2. Author's name may not be used endorse or promote products derived 673 * from this software without specific prior written permission. 674 * 675 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 676 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 677 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 678 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 679 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 680 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 681 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 682 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 683 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 684 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 685 * POSSIBILITY OF SUCH DAMAGE. 686 */ 687 688 #ifndef BPF_PATH_FORMAT 689 #define BPF_PATH_FORMAT "/dev/bpf%u" 690 #endif 691 692 int do_wakeup(const char *, const char *, int); 693 int get_bpf(void); 694 int bind_if_to_bpf(const char *, int); 695 int get_ether(const char *, struct ether_addr *); 696 int send_frame(int, const struct ether_addr *); 697 698 int 699 wake(const char *ether_addr, const char *iface) 700 { 701 struct ifaddrs *ifa, *ifap; 702 char *pname = NULL; 703 int bpf; 704 705 bpf = get_bpf(); 706 if (bpf == -1) 707 errx(1, "Failed to bind to bpf."); 708 709 if (iface == NULL) { 710 if (getifaddrs(&ifa) == -1) 711 errx(1, "Could not get interface addresses."); 712 713 for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next){ 714 if (pname && !strcmp(pname, ifap->ifa_name)) 715 continue; 716 pname = ifap->ifa_name; 717 718 /* 719 * We're only interested in sending the WoL frame on 720 * certain interfaces. So skip the loopback interface, 721 * as well as point-to-point and down interfaces. 722 */ 723 if ((ifap->ifa_flags & IFF_LOOPBACK) || 724 (ifap->ifa_flags & IFF_POINTOPOINT) || 725 (!(ifap->ifa_flags & IFF_UP)) || 726 (!(ifap->ifa_flags & IFF_BROADCAST))) 727 continue; 728 729 do_wakeup(ether_addr, ifap->ifa_name, bpf); 730 } 731 freeifaddrs(ifa); 732 } else { 733 do_wakeup(ether_addr, iface, bpf); 734 } 735 736 (void)close(bpf); 737 738 return 0; 739 } 740 741 int 742 do_wakeup(const char *eaddr, const char *iface, int bpf) 743 { 744 struct ether_addr macaddr; 745 746 if (get_ether(eaddr, &macaddr) != 0) 747 errx(1, "Invalid Ethernet address: %s", eaddr); 748 if (bind_if_to_bpf(iface, bpf) != 0) 749 errx(1, "Failed to bind %s to bpf.", iface); 750 if (send_frame(bpf, &macaddr) != 0) 751 errx(1, "Failed to send WoL frame on %s", iface); 752 return 0; 753 } 754 755 int 756 get_bpf(void) 757 { 758 char path[MAXPATHLEN]; 759 int i, fd; 760 761 for (i = 0; ; i++) { 762 if (snprintf(path, sizeof(path), BPF_PATH_FORMAT, i) == -1) 763 return -1; 764 fd = open(path, O_RDWR); 765 if (fd != -1) 766 return fd; 767 if (errno == EBUSY) 768 continue; 769 break; 770 } 771 return -1; 772 } 773 774 int 775 bind_if_to_bpf(const char *ifname, int bpf) 776 { 777 struct ifreq ifr; 778 u_int dlt; 779 780 if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= 781 sizeof(ifr.ifr_name)) 782 return -1; 783 if (ioctl(bpf, BIOCSETIF, &ifr) == -1) 784 return -1; 785 if (ioctl(bpf, BIOCGDLT, &dlt) == -1) 786 return -1; 787 if (dlt != DLT_EN10MB) 788 return -1; 789 return 0; 790 } 791 792 int 793 get_ether(const char *text, struct ether_addr *addr) 794 { 795 struct ether_addr *eaddr; 796 797 eaddr = ether_aton(text); 798 799 if (eaddr == NULL) { 800 if (ether_hostton(text, addr)) 801 return -1; 802 } else { 803 *addr = *eaddr; 804 return 0; 805 } 806 807 return 0; 808 } 809 810 #define SYNC_LEN 6 811 #define DESTADDR_COUNT 16 812 813 int 814 send_frame(int bpf, const struct ether_addr *addr) 815 { 816 struct { 817 struct ether_header hdr; 818 u_char sync[SYNC_LEN]; 819 u_char dest[ETHER_ADDR_LEN * DESTADDR_COUNT]; 820 } __packed pkt; 821 u_char *p; 822 int i; 823 824 (void)memset(&pkt, 0, sizeof(pkt)); 825 (void)memset(&pkt.hdr.ether_dhost, 0xff, sizeof(pkt.hdr.ether_dhost)); 826 pkt.hdr.ether_type = htons(0); 827 (void)memset(pkt.sync, 0xff, SYNC_LEN); 828 for (p = pkt.dest, i = 0; i < DESTADDR_COUNT; p += ETHER_ADDR_LEN, i++) 829 bcopy(addr->ether_addr_octet, p, ETHER_ADDR_LEN); 830 if (write(bpf, &pkt, sizeof(pkt)) != sizeof(pkt)) 831 return (errno); 832 return (0); 833 } 834