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