xref: /freebsd/usr.sbin/wake/wake.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
11de7b4b8SPedro F. Giffuni /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
466df920cSMartin Blapp  * Copyright (C) 2006, 2007, 2008, 2009, 2010 Marc Balmer <marc@msys.ch>
558c6a70aSMartin Blapp  * Copyright (C) 2000 Eugene M. Kim.  All rights reserved.
658c6a70aSMartin Blapp  *
758c6a70aSMartin Blapp  * Redistribution and use in source and binary forms, with or without
858c6a70aSMartin Blapp  * modification, are permitted provided that the following conditions
958c6a70aSMartin Blapp  * are met:
1058c6a70aSMartin Blapp  *
1158c6a70aSMartin Blapp  * 1. Redistributions of source code must retain the above copyright
1258c6a70aSMartin Blapp  *    notice, this list of conditions and the following disclaimer.
1358c6a70aSMartin Blapp  * 2. Author's name may not be used endorse or promote products derived
1458c6a70aSMartin Blapp  *    from this software without specific prior written permission.
1558c6a70aSMartin Blapp  *
1658c6a70aSMartin Blapp  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1758c6a70aSMartin Blapp  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1858c6a70aSMartin Blapp  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1958c6a70aSMartin Blapp  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2058c6a70aSMartin Blapp  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2158c6a70aSMartin Blapp  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2258c6a70aSMartin Blapp  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2358c6a70aSMartin Blapp  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2458c6a70aSMartin Blapp  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
2558c6a70aSMartin Blapp  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2658c6a70aSMartin Blapp  * POSSIBILITY OF SUCH DAMAGE.
2758c6a70aSMartin Blapp  */
2858c6a70aSMartin Blapp 
2958c6a70aSMartin Blapp #include <sys/cdefs.h>
30d981a4e2SStanislav Sedov #include <sys/ioctl.h>
31d981a4e2SStanislav Sedov #include <sys/socket.h>
32d981a4e2SStanislav Sedov #include <sys/time.h>
33d981a4e2SStanislav Sedov #include <net/bpf.h>
34d981a4e2SStanislav Sedov #include <net/if.h>
3566df920cSMartin Blapp #include <net/if_dl.h>
3666df920cSMartin Blapp #include <net/if_types.h>
37d981a4e2SStanislav Sedov #include <netinet/in.h>
38d981a4e2SStanislav Sedov #include <netinet/if_ether.h>
39d981a4e2SStanislav Sedov 
4012de41a2SStanislav Sedov #include <err.h>
4112de41a2SStanislav Sedov #include <fcntl.h>
4266df920cSMartin Blapp #include <ifaddrs.h>
4312de41a2SStanislav Sedov #include <stdio.h>
4412de41a2SStanislav Sedov #include <stdlib.h>
4512de41a2SStanislav Sedov #include <string.h>
4612de41a2SStanislav Sedov #include <unistd.h>
4712de41a2SStanislav Sedov 
4858c6a70aSMartin Blapp #define	_PATH_BPF	"/dev/bpf"
4958c6a70aSMartin Blapp 
5058c6a70aSMartin Blapp #ifndef SYNC_LEN
5158c6a70aSMartin Blapp #define	SYNC_LEN	6
5258c6a70aSMartin Blapp #endif
5358c6a70aSMartin Blapp 
5458c6a70aSMartin Blapp #ifndef DESTADDR_COUNT
5558c6a70aSMartin Blapp #define	DESTADDR_COUNT	16
5658c6a70aSMartin Blapp #endif
5758c6a70aSMartin Blapp 
58d981a4e2SStanislav Sedov static int	bind_if_to_bpf(char const *ifname, int bpf);
5966df920cSMartin Blapp static int	find_ether(char *dst, size_t len);
60d981a4e2SStanislav Sedov static int	get_ether(char const *text, struct ether_addr *addr);
61d981a4e2SStanislav Sedov static int	send_wakeup(int bpf, struct ether_addr const *addr);
6212de41a2SStanislav Sedov static void	usage(void);
6366df920cSMartin Blapp static int	wake(int bpf, const char *host);
6458c6a70aSMartin Blapp 
65d981a4e2SStanislav Sedov static void
usage(void)6658c6a70aSMartin Blapp usage(void)
6758c6a70aSMartin Blapp {
68d981a4e2SStanislav Sedov 
6966df920cSMartin Blapp 	(void)fprintf(stderr, "usage: wake [interface] lladdr [lladdr ...]\n");
7012de41a2SStanislav Sedov 	exit(1);
7158c6a70aSMartin Blapp }
7258c6a70aSMartin Blapp 
73d981a4e2SStanislav Sedov static int
wake(int bpf,const char * host)7466df920cSMartin Blapp wake(int bpf, const char *host)
7558c6a70aSMartin Blapp {
7658c6a70aSMartin Blapp 	struct ether_addr macaddr;
7758c6a70aSMartin Blapp 
7866df920cSMartin Blapp 	if (get_ether(host, &macaddr) == -1)
79d981a4e2SStanislav Sedov 		return (-1);
8066df920cSMartin Blapp 
816acc4f15SJaakko Heinonen 	return (send_wakeup(bpf, &macaddr));
8258c6a70aSMartin Blapp }
8358c6a70aSMartin Blapp 
84d981a4e2SStanislav Sedov static int
bind_if_to_bpf(char const * ifname,int bpf)8558c6a70aSMartin Blapp bind_if_to_bpf(char const *ifname, int bpf)
8658c6a70aSMartin Blapp {
8758c6a70aSMartin Blapp 	struct ifreq ifr;
8858c6a70aSMartin Blapp 	u_int dlt;
8958c6a70aSMartin Blapp 
9058c6a70aSMartin Blapp 	if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
9166df920cSMartin Blapp 	    sizeof(ifr.ifr_name))
92d981a4e2SStanislav Sedov 		return (-1);
9366df920cSMartin Blapp 
9466df920cSMartin Blapp 	if (ioctl(bpf, BIOCSETIF, &ifr) == -1)
95d981a4e2SStanislav Sedov 		return (-1);
9666df920cSMartin Blapp 
9766df920cSMartin Blapp 	if (ioctl(bpf, BIOCGDLT, &dlt) == -1)
98d981a4e2SStanislav Sedov 		return (-1);
9966df920cSMartin Blapp 
10066df920cSMartin Blapp 	if (dlt != DLT_EN10MB)
101d981a4e2SStanislav Sedov 		return (-1);
10266df920cSMartin Blapp 
103d981a4e2SStanislav Sedov 	return (0);
10458c6a70aSMartin Blapp }
10558c6a70aSMartin Blapp 
106d981a4e2SStanislav Sedov static int
find_ether(char * dst,size_t len)10766df920cSMartin Blapp find_ether(char *dst, size_t len)
10866df920cSMartin Blapp {
10966df920cSMartin Blapp 	struct ifaddrs *ifap, *ifa;
11066df920cSMartin Blapp 	struct sockaddr_dl *sdl = NULL;
11166df920cSMartin Blapp 	int nifs;
11266df920cSMartin Blapp 
11366df920cSMartin Blapp 	if (dst == NULL || len == 0)
1146acc4f15SJaakko Heinonen 		return (0);
11566df920cSMartin Blapp 
11666df920cSMartin Blapp 	if (getifaddrs(&ifap) != 0)
1176acc4f15SJaakko Heinonen 		return (-1);
11866df920cSMartin Blapp 
11966df920cSMartin Blapp 	/* XXX also check the link state */
12066df920cSMartin Blapp 	for (nifs = 0, ifa = ifap; ifa; ifa = ifa->ifa_next)
12166df920cSMartin Blapp 		if (ifa->ifa_addr->sa_family == AF_LINK &&
12266df920cSMartin Blapp 		    ifa->ifa_flags & IFF_UP && ifa->ifa_flags & IFF_RUNNING) {
12366df920cSMartin Blapp 			sdl = (struct sockaddr_dl *)ifa->ifa_addr;
12466df920cSMartin Blapp 			if (sdl->sdl_type == IFT_ETHER) {
12566df920cSMartin Blapp 				strlcpy(dst, ifa->ifa_name, len);
12666df920cSMartin Blapp 				nifs++;
12766df920cSMartin Blapp 			}
12866df920cSMartin Blapp 		}
12966df920cSMartin Blapp 
13066df920cSMartin Blapp 	freeifaddrs(ifap);
1316acc4f15SJaakko Heinonen 	return (nifs == 1 ? 0 : -1);
13266df920cSMartin Blapp }
13366df920cSMartin Blapp 
13466df920cSMartin Blapp static int
get_ether(char const * text,struct ether_addr * addr)13558c6a70aSMartin Blapp get_ether(char const *text, struct ether_addr *addr)
13658c6a70aSMartin Blapp {
13758c6a70aSMartin Blapp 	struct ether_addr *paddr;
13858c6a70aSMartin Blapp 
13958c6a70aSMartin Blapp 	paddr = ether_aton(text);
14058c6a70aSMartin Blapp 	if (paddr != NULL) {
14158c6a70aSMartin Blapp 		*addr = *paddr;
142d981a4e2SStanislav Sedov 		return (0);
14358c6a70aSMartin Blapp 	}
144d981a4e2SStanislav Sedov 	if (ether_hostton(text, addr)) {
145d981a4e2SStanislav Sedov 		warnx("no match for host %s found", text);
146d981a4e2SStanislav Sedov 		return (-1);
147d981a4e2SStanislav Sedov 	}
148d981a4e2SStanislav Sedov 	return (0);
14958c6a70aSMartin Blapp }
15058c6a70aSMartin Blapp 
151d981a4e2SStanislav Sedov static int
send_wakeup(int bpf,struct ether_addr const * addr)15258c6a70aSMartin Blapp send_wakeup(int bpf, struct ether_addr const *addr)
15358c6a70aSMartin Blapp {
15458c6a70aSMartin Blapp 	struct {
15558c6a70aSMartin Blapp 		struct ether_header hdr;
15658c6a70aSMartin Blapp 		u_char data[SYNC_LEN + ETHER_ADDR_LEN * DESTADDR_COUNT];
157d981a4e2SStanislav Sedov 	} __packed pkt;
15812de41a2SStanislav Sedov 	u_char *p;
15958c6a70aSMartin Blapp 	ssize_t bw;
16058c6a70aSMartin Blapp 	ssize_t len;
161d981a4e2SStanislav Sedov 	int i;
16258c6a70aSMartin Blapp 
16358c6a70aSMartin Blapp 	(void)memset(pkt.hdr.ether_dhost, 0xff, sizeof(pkt.hdr.ether_dhost));
16458c6a70aSMartin Blapp 	pkt.hdr.ether_type = htons(0);
16558c6a70aSMartin Blapp 	(void)memset(pkt.data, 0xff, SYNC_LEN);
16658c6a70aSMartin Blapp 	for (p = pkt.data + SYNC_LEN, i = 0; i < DESTADDR_COUNT;
16758c6a70aSMartin Blapp 	    p += ETHER_ADDR_LEN, i++)
16858c6a70aSMartin Blapp 		bcopy(addr->octet, p, ETHER_ADDR_LEN);
16958c6a70aSMartin Blapp 	p = (u_char *)&pkt;
17058c6a70aSMartin Blapp 	len = sizeof(pkt);
17158c6a70aSMartin Blapp 	bw = 0;
17258c6a70aSMartin Blapp 	while (len) {
173d981a4e2SStanislav Sedov 		if ((bw = write(bpf, p, len)) == -1) {
174d981a4e2SStanislav Sedov 			warn("write()");
175d981a4e2SStanislav Sedov 			return (-1);
176d981a4e2SStanislav Sedov 		}
17758c6a70aSMartin Blapp 		len -= bw;
17858c6a70aSMartin Blapp 		p += bw;
17958c6a70aSMartin Blapp 	}
180d981a4e2SStanislav Sedov 	return (0);
18158c6a70aSMartin Blapp }
18258c6a70aSMartin Blapp 
18358c6a70aSMartin Blapp int
main(int argc,char * argv[])18458c6a70aSMartin Blapp main(int argc, char *argv[])
18558c6a70aSMartin Blapp {
18606eb5e92SJaakko Heinonen 	int bpf, n, rval;
18766df920cSMartin Blapp 	char ifname[IF_NAMESIZE];
18858c6a70aSMartin Blapp 
18966df920cSMartin Blapp 	if (argc < 2)
19058c6a70aSMartin Blapp 		usage();
19158c6a70aSMartin Blapp 
19266df920cSMartin Blapp 	if ((bpf = open(_PATH_BPF, O_RDWR)) == -1)
19366df920cSMartin Blapp 		err(1, "Cannot open bpf interface");
19466df920cSMartin Blapp 
19566df920cSMartin Blapp 	n = 2;
19666df920cSMartin Blapp 	if (bind_if_to_bpf(argv[1], bpf) == -1) {
19766df920cSMartin Blapp 		if (find_ether(ifname, sizeof(ifname)))
19866df920cSMartin Blapp 			err(1, "Failed to determine ethernet interface");
19966df920cSMartin Blapp 		if (bind_if_to_bpf(ifname, bpf) == -1)
20066df920cSMartin Blapp 			err(1, "Cannot bind to interface `%s'", ifname);
20166df920cSMartin Blapp 		--n;
20266df920cSMartin Blapp 	} else
20366df920cSMartin Blapp 		strlcpy(ifname, argv[1], sizeof(ifname));
20466df920cSMartin Blapp 
20566df920cSMartin Blapp 	if (n >= argc)
20666df920cSMartin Blapp 		usage();
20706eb5e92SJaakko Heinonen 	rval = 0;
20806eb5e92SJaakko Heinonen 	for (; n < argc; n++) {
20906eb5e92SJaakko Heinonen 		if (wake(bpf, argv[n]) != 0) {
21006eb5e92SJaakko Heinonen 			rval = 1;
21166df920cSMartin Blapp 			warn("Cannot send Wake on LAN frame over `%s' to `%s'",
21266df920cSMartin Blapp 			    ifname, argv[n]);
21306eb5e92SJaakko Heinonen 		}
21406eb5e92SJaakko Heinonen 	}
21506eb5e92SJaakko Heinonen 	exit(rval);
21658c6a70aSMartin Blapp }
217