summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/arp/arp.833
-rw-r--r--usr.sbin/arp/arp.c202
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);
+}