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