diff options
| -rw-r--r-- | usr.sbin/arp/arp.8 | 33 | ||||
| -rw-r--r-- | usr.sbin/arp/arp.c | 202 |
2 files changed, 229 insertions, 6 deletions
diff --git a/usr.sbin/arp/arp.8 b/usr.sbin/arp/arp.8 index be068b509d5..1f859412b95 100644 --- a/usr.sbin/arp/arp.8 +++ b/usr.sbin/arp/arp.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: arp.8,v 1.26 2009/06/05 06:47:12 jmc Exp $ +.\" $OpenBSD: arp.8,v 1.27 2011/01/11 16:34:20 jasper Exp $ .\" $NetBSD: arp.8,v 1.7 1995/03/01 11:50:59 chopps Exp $ .\" .\" Copyright (c) 1985, 1991, 1993 @@ -30,7 +30,7 @@ .\" .\" from: @(#)arp.8 8.1 (Berkeley) 6/6/93 .\" -.Dd $Mdocdate: June 5 2009 $ +.Dd $Mdocdate: January 11 2011 $ .Dt ARP 8 .Os .Sh NAME @@ -48,6 +48,8 @@ .Fl s Ar hostname ether_addr .Op Cm temp | permanent .Op Cm pub +.Nm +.Fl W Ar ether_addr Op iface .Sh DESCRIPTION The .Nm @@ -62,6 +64,14 @@ when no optional parameters are supplied. may be specified by name or by number, using Internet dot notation. .Pp +.Nm +can also used to send Wake on LAN (WoL) frames over a local +Ethernet network to one or more hosts using their link layer (hardware) +addresses. +WoL functionality is generally enabled in a machine's BIOS +and can be used to power on machines from a remote system without +having physical access to them. +.Pp The options are as follows: .Bl -tag -width Ds .It Fl a @@ -146,6 +156,22 @@ is given. .It Fl V Ar rdomain Select the routing domain. The default is 0. +.It Fl W Ar ether_addr Op Ar iface +Send the Wake on Lan frame from all interfaces on the local machine +that are up, if +.Ar iface +has not been specified. +Otherwise the frame will be sent from +.Ar iface . +.Ar ether_addr +is the Ethernet address of the remote machine or a hostname entry in +.Pa /etc/ethers . +This option cannot be used in combination with any other option. +.El +.Sh FILES +.Bl -tag -width "/etc/ethers" -compact +.It /etc/ethers +Ethernet host name database. .El .Sh EXAMPLES View the current @@ -171,6 +197,7 @@ for IP addresses 204.1.2.3 and 204.1.2.4: .Sh SEE ALSO .Xr inet 3 , .Xr arp 4 , +.Xr ethers 5 , .Xr ifconfig 8 , .Xr ndp 8 .Sh HISTORY @@ -178,3 +205,5 @@ The .Nm command appeared in .Bx 4.3 . +Wake on Lan functionality was added in +.Ox 4.9 . diff --git a/usr.sbin/arp/arp.c b/usr.sbin/arp/arp.c index 20348cce5cf..60abedf482d 100644 --- a/usr.sbin/arp/arp.c +++ b/usr.sbin/arp/arp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: arp.c,v 1.49 2009/09/27 12:07:15 deraadt Exp $ */ +/* $OpenBSD: arp.c,v 1.50 2011/01/11 16:34:20 jasper Exp $ */ /* $NetBSD: arp.c,v 1.12 1995/04/24 13:25:18 cgd Exp $ */ /* @@ -34,14 +34,15 @@ */ /* - * arp - display, set, and delete arp table entries + * arp - display, set, delete arp table entries and wake up hosts. */ #include <sys/param.h> #include <sys/file.h> #include <sys/socket.h> #include <sys/sysctl.h> - +#include <sys/ioctl.h> +#include <net/bpf.h> #include <net/if.h> #include <net/if_dl.h> #include <net/if_types.h> @@ -58,6 +59,7 @@ #include <string.h> #include <paths.h> #include <unistd.h> +#include <ifaddrs.h> int delete(const char *, const char *); void search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl, @@ -66,6 +68,7 @@ void print_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin, struct rt_msghdr *rtm); void nuke_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin, struct rt_msghdr *rtm); +int wake(const char *ether_addr, const char *iface); void ether_print(const char *); int file(char *); int get(const char *); @@ -93,6 +96,7 @@ extern int h_errno; #define F_SET 2 #define F_FILESET 3 #define F_DELETE 4 +#define F_WAKE 5 int main(int argc, char *argv[]) @@ -102,7 +106,7 @@ main(int argc, char *argv[]) pid = getpid(); opterr = 0; - while ((ch = getopt(argc, argv, "andsFfV:")) != -1) { + while ((ch = getopt(argc, argv, "andsFfV:W")) != -1) { switch (ch) { case 'a': aflag = 1; @@ -135,6 +139,11 @@ main(int argc, char *argv[]) usage(); } break; + case 'W': + if (func) + usage(); + func = F_WAKE; + break; default: usage(); break; @@ -176,6 +185,16 @@ main(int argc, char *argv[]) usage(); rtn = file(argv[0]); break; + case F_WAKE: + if (aflag || nflag || replace || rdomain > 0) + usage(); + if (argc == 1) + rtn = wake(argv[0], NULL); + else if (argc == 2) + rtn = wake(argv[0], argv[1]); + else + usage(); + break; } return (rtn); } @@ -534,6 +553,7 @@ usage(void) fprintf(stderr, " arp [-F] [-f file] [-V rdomain] " "-s hostname ether_addr\n" " [temp | permanent] [pub]\n"); + fprintf(stderr, " arp -W ether_addr [iface]\n"); exit(1); } @@ -625,3 +645,177 @@ getinetaddr(const char *host, struct in_addr *inap) memcpy(inap, hp->h_addr, sizeof(*inap)); return (0); } + +/* + * Copyright (c) 2011 Jasper Lievisse Adriaanse <jasper@openbsd.org> + * Copyright (C) 2006,2007,2008,2009 Marc Balmer <mbalmer@openbsd.org> + * Copyright (C) 2000 Eugene M. Kim. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Author's name may not be used endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BPF_PATH_FORMAT +#define BPF_PATH_FORMAT "/dev/bpf%u" +#endif + +int do_wakeup(const char *, const char *, int); +int get_bpf(void); +int bind_if_to_bpf(const char *, int); +int get_ether(const char *, struct ether_addr *); +int send_frame(int, const struct ether_addr *); + +int +wake(const char *ether_addr, const char *iface) +{ + struct ifaddrs *ifa, *ifap; + char *pname = NULL; + int bpf; + + bpf = get_bpf(); + if (bpf == -1) + errx(1, "Failed to bind to bpf."); + + if (iface == NULL) { + if (getifaddrs(&ifa) == -1) + errx(1, "Could not get interface addresses."); + + for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next){ + if (pname && !strcmp(pname, ifap->ifa_name)) + continue; + pname = ifap->ifa_name; + + /* + * We're only interested in sending the WoL frame on + * certain interfaces. So skip the loopback interface, + * as well as point-to-point and down interfaces. + */ + if ((ifap->ifa_flags & IFF_LOOPBACK) || + (ifap->ifa_flags & IFF_POINTOPOINT) || + (!(ifap->ifa_flags & IFF_UP)) || + (!(ifap->ifa_flags & IFF_BROADCAST))) + continue; + + do_wakeup(ether_addr, ifap->ifa_name, bpf); + } + freeifaddrs(ifa); + } else { + do_wakeup(ether_addr, iface, bpf); + } + + (void)close(bpf); + + return 0; +} + +int +do_wakeup(const char *eaddr, const char *iface, int bpf) +{ + struct ether_addr macaddr; + + if (get_ether(eaddr, &macaddr) != 0) + errx(1, "Invalid Ethernet address: %s", eaddr); + if (bind_if_to_bpf(iface, bpf) != 0) + errx(1, "Failed to bind %s to bpf.", iface); + if (send_frame(bpf, &macaddr) != 0) + errx(1, "Failed to send WoL frame on %s", iface); + return 0; +} + +int +get_bpf(void) +{ + char path[MAXPATHLEN]; + int i, fd; + + for (i = 0; ; i++) { + if (snprintf(path, sizeof(path), BPF_PATH_FORMAT, i) == -1) + return -1; + fd = open(path, O_RDWR); + if (fd != -1) + return fd; + if (errno == EBUSY) + continue; + break; + } + return -1; +} + +int +bind_if_to_bpf(const char *ifname, int bpf) +{ + struct ifreq ifr; + u_int dlt; + + if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= + sizeof(ifr.ifr_name)) + return -1; + if (ioctl(bpf, BIOCSETIF, &ifr) == -1) + return -1; + if (ioctl(bpf, BIOCGDLT, &dlt) == -1) + return -1; + if (dlt != DLT_EN10MB) + return -1; + return 0; +} + +int +get_ether(const char *text, struct ether_addr *addr) +{ + struct ether_addr *eaddr; + + eaddr = ether_aton(text); + + if (eaddr == NULL) { + if (ether_hostton(text, addr)) + return -1; + } else { + *addr = *eaddr; + return 0; + } + + return 0; +} + +#define SYNC_LEN 6 +#define DESTADDR_COUNT 16 + +int +send_frame(int bpf, const struct ether_addr *addr) +{ + struct { + struct ether_header hdr; + u_char sync[SYNC_LEN]; + u_char dest[ETHER_ADDR_LEN * DESTADDR_COUNT]; + } __packed pkt; + u_char *p; + int i; + + (void)memset(&pkt, 0, sizeof(pkt)); + (void)memset(&pkt.hdr.ether_dhost, 0xff, sizeof(pkt.hdr.ether_dhost)); + pkt.hdr.ether_type = htons(0); + (void)memset(pkt.sync, 0xff, SYNC_LEN); + for (p = pkt.dest, i = 0; i < DESTADDR_COUNT; p += ETHER_ADDR_LEN, i++) + bcopy(addr->ether_addr_octet, p, ETHER_ADDR_LEN); + if (write(bpf, &pkt, sizeof(pkt)) != sizeof(pkt)) + return (errno); + return (0); +} |
