From 93bad3c334331958b1ff68cb3fc688160aff2701 Mon Sep 17 00:00:00 2001 From: Jasper Lievisse Adriaanse Date: Tue, 11 Jan 2011 16:34:21 +0000 Subject: Add Wake on Lan support to arp(8). This is partly based on the original wake(8) program. One can specify an interface to send on, or simply broadcast on all available interfaces. Initial input from stsp@, further help from claudio@ and deraadt@. ok claudio@ deraadt@ manpage bits ok jmc@ --- usr.sbin/arp/arp.8 | 33 ++++++++- usr.sbin/arp/arp.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 229 insertions(+), 6 deletions(-) (limited to 'usr.sbin/arp') 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 #include #include #include - +#include +#include #include #include #include @@ -58,6 +59,7 @@ #include #include #include +#include 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 + * Copyright (C) 2006,2007,2008,2009 Marc Balmer + * 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); +} -- cgit v1.2.3