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