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