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