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