replace bool by tristate to avoid unnecessary -install calls
[openwrt.git] / openwrt / package / ether-wake / files / ether-wake.c
1 /* ether-wake.c: Send a magic packet to wake up sleeping machines. */
2
3 static char version_msg[] =
4 "ether-wake.c: v1.09 11/12/2003 Donald Becker, http://www.scyld.com/";
5 static char brief_usage_msg[] =
6 "usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n"
7 " Use '-u' to see the complete set of options.\n";
8 static char usage_msg[] =
9 "usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n"
10 "\n"
11 " This program generates and transmits a Wake-On-LAN (WOL)\n"
12 " \"Magic Packet\", used for restarting machines that have been\n"
13 " soft-powered-down (ACPI D3-warm state).\n"
14 " It currently generates the standard AMD Magic Packet format, with\n"
15 " an optional password appended.\n"
16 "\n"
17 " The single required parameter is the Ethernet MAC (station) address\n"
18 " of the machine to wake or a host ID with known NSS 'ethers' entry.\n"
19 " The MAC address may be found with the 'arp' program while the target\n"
20 " machine is awake.\n"
21 "\n"
22 " Options:\n"
23 " -b Send wake-up packet to the broadcast address.\n"
24 " -D Increase the debug level.\n"
25 " -i ifname Use interface IFNAME instead of the default 'eth0'.\n"
26 " -p <pw> Append the four or six byte password PW to the packet.\n"
27 " A password is only required for a few adapter types.\n"
28 " The password may be specified in ethernet hex format\n"
29 " or dotted decimal (Internet address)\n"
30 " -p 00:22:44:66:88:aa\n"
31 " -p 192.168.1.1\n";
32
33 /*
34 This program generates and transmits a Wake-On-LAN (WOL) "Magic Packet",
35 used for restarting machines that have been soft-powered-down
36 (ACPI D3-warm state). It currently generates the standard AMD Magic Packet
37 format, with an optional password appended.
38
39 This software may be used and distributed according to the terms
40 of the GNU Public License, incorporated herein by reference.
41 Contact the author for use under other terms.
42
43 This source file was originally part of the network tricks package, and
44 is now distributed to support the Scyld Beowulf system.
45 Copyright 1999-2003 Donald Becker and Scyld Computing Corporation.
46
47 The author may be reached as becker@scyld, or C/O
48 Scyld Computing Corporation
49 914 Bay Ridge Road, Suite 220
50 Annapolis MD 21403
51
52 Notes:
53 On some systems dropping root capability allows the process to be
54 dumped, traced or debugged.
55 If someone traces this program, they get control of a raw socket.
56 Linux handles this safely, but beware when porting this program.
57
58 An alternative to needing 'root' is using a UDP broadcast socket, however
59 doing so only works with adapters configured for unicast+broadcast Rx
60 filter. That configuration consumes more power.
61 */
62 \f
63 #include <unistd.h>
64 #include <stdlib.h>
65 #include <stdio.h>
66 #include <errno.h>
67 #include <ctype.h>
68 #include <string.h>
69
70 #if 0 /* Only exists on some versions. */
71 #include <ioctls.h>
72 #endif
73
74 #include <sys/socket.h>
75
76 #include <sys/types.h>
77 #include <sys/ioctl.h>
78 #include <linux/if.h>
79
80 #include <features.h>
81 #if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
82 #include <netpacket/packet.h>
83 #include <net/ethernet.h>
84 #else
85 #include <asm/types.h>
86 #include <linux/if_packet.h>
87 #include <linux/if_ether.h>
88 #endif
89 #include <netdb.h>
90 #include <netinet/ether.h>
91
92 /* Grrr, no consistency between include versions.
93 Enable this if setsockopt() isn't declared with your library. */
94 #if 0
95 extern int setsockopt __P ((int __fd, int __level, int __optname,
96 __ptr_t __optval, int __optlen));
97 #else /* New, correct head files. */
98 #include <sys/socket.h>
99 #endif
100
101 u_char outpack[1000];
102 int outpack_sz = 0;
103 int debug = 0;
104 u_char wol_passwd[6];
105 int wol_passwd_sz = 0;
106
107 static int opt_no_src_addr = 0, opt_broadcast = 0;
108
109 static int get_dest_addr(const char *arg, struct ether_addr *eaddr);
110 static int get_fill(unsigned char *pkt, struct ether_addr *eaddr);
111 static int get_wol_pw(const char *optarg);
112
113 int main(int argc, char *argv[])
114 {
115 char *ifname = "eth0";
116 int one = 1; /* True, for socket options. */
117 int s; /* Raw socket */
118 int errflag = 0, verbose = 0, do_version = 0;
119 int perm_failure = 0;
120 int i, c, pktsize;
121 #if defined(PF_PACKET)
122 struct sockaddr_ll whereto;
123 #else
124 struct sockaddr whereto; /* who to wake up */
125 #endif
126 struct ether_addr eaddr;
127
128 while ((c = getopt(argc, argv, "bDi:p:uvV")) != -1)
129 switch (c) {
130 case 'b': opt_broadcast++; break;
131 case 'D': debug++; break;
132 case 'i': ifname = optarg; break;
133 case 'p': get_wol_pw(optarg); break;
134 case 'u': printf(usage_msg); return 0;
135 case 'v': verbose++; break;
136 case 'V': do_version++; break;
137 case '?':
138 errflag++;
139 }
140 if (verbose || do_version)
141 printf("%s\n", version_msg);
142 if (errflag) {
143 fprintf(stderr, brief_usage_msg);
144 return 3;
145 }
146
147 if (optind == argc) {
148 fprintf(stderr, "Specify the Ethernet address as 00:11:22:33:44:55.\n");
149 return 3;
150 }
151
152 /* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to
153 work as non-root, but we need SOCK_PACKET to specify the Ethernet
154 destination address. */
155 #if defined(PF_PACKET)
156 s = socket(PF_PACKET, SOCK_RAW, 0);
157 #else
158 s = socket(AF_INET, SOCK_PACKET, SOCK_PACKET);
159 #endif
160 if (s < 0) {
161 if (errno == EPERM)
162 fprintf(stderr, "ether-wake: This program must be run as root.\n");
163 else
164 perror("ether-wake: socket");
165 perm_failure++;
166 }
167 /* Don't revert if debugging allows a normal user to get the raw socket. */
168 setuid(getuid());
169
170 /* We look up the station address before reporting failure so that
171 errors may be reported even when run as a normal user.
172 */
173 if (get_dest_addr(argv[optind], &eaddr) != 0)
174 return 3;
175 if (perm_failure && ! debug)
176 return 2;
177
178 pktsize = get_fill(outpack, &eaddr);
179
180 /* Fill in the source address, if possible.
181 The code to retrieve the local station address is Linux specific. */
182 if (! opt_no_src_addr) {
183 struct ifreq if_hwaddr;
184 unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data;
185
186 strcpy(if_hwaddr.ifr_name, ifname);
187 if (ioctl(s, SIOCGIFHWADDR, &if_hwaddr) < 0) {
188 fprintf(stderr, "SIOCGIFHWADDR on %s failed: %s\n", ifname,
189 strerror(errno));
190 /* Magic packets still work if our source address is bogus, but
191 we fail just to be anal. */
192 return 1;
193 }
194 memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6);
195
196 if (verbose) {
197 printf("The hardware address (SIOCGIFHWADDR) of %s is type %d "
198 "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", ifname,
199 if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1],
200 hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
201 }
202 }
203
204 if (wol_passwd_sz > 0) {
205 memcpy(outpack+pktsize, wol_passwd, wol_passwd_sz);
206 pktsize += wol_passwd_sz;
207 }
208
209 if (verbose > 1) {
210 printf("The final packet is: ");
211 for (i = 0; i < pktsize; i++)
212 printf(" %2.2x", outpack[i]);
213 printf(".\n");
214 }
215
216 /* This is necessary for broadcasts to work */
217 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&one, sizeof(one)) < 0)
218 perror("setsockopt: SO_BROADCAST");
219
220 #if defined(PF_PACKET)
221 {
222 struct ifreq ifr;
223 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
224 if (ioctl(s, SIOCGIFINDEX, &ifr) == -1) {
225 fprintf(stderr, "SIOCGIFINDEX on %s failed: %s\n", ifname,
226 strerror(errno));
227 return 1;
228 }
229 memset(&whereto, 0, sizeof(whereto));
230 whereto.sll_family = AF_PACKET;
231 whereto.sll_ifindex = ifr.ifr_ifindex;
232 /* The manual page incorrectly claims the address must be filled.
233 We do so because the code may change to match the docs. */
234 whereto.sll_halen = ETH_ALEN;
235 memcpy(whereto.sll_addr, outpack, ETH_ALEN);
236
237 }
238 #else
239 whereto.sa_family = 0;
240 strcpy(whereto.sa_data, ifname);
241 #endif
242
243 if ((i = sendto(s, outpack, pktsize, 0, (struct sockaddr *)&whereto,
244 sizeof(whereto))) < 0)
245 perror("sendto");
246 else if (debug)
247 printf("Sendto worked ! %d.\n", i);
248
249 #ifdef USE_SEND
250 if (bind(s, (struct sockaddr *)&whereto, sizeof(whereto)) < 0)
251 perror("bind");
252 else if (send(s, outpack, 100, 0) < 0)
253 perror("send");
254 #endif
255 #ifdef USE_SENDMSG
256 {
257 struct msghdr msghdr = { 0,};
258 struct iovec iovector[1];
259 msghdr.msg_name = &whereto;
260 msghdr.msg_namelen = sizeof(whereto);
261 msghdr.msg_iov = iovector;
262 msghdr.msg_iovlen = 1;
263 iovector[0].iov_base = outpack;
264 iovector[0].iov_len = pktsize;
265 if ((i = sendmsg(s, &msghdr, 0)) < 0)
266 perror("sendmsg");
267 else if (debug)
268 printf("sendmsg worked, %d (%d).\n", i, errno);
269 }
270 #endif
271
272 return 0;
273 }
274
275 /* Convert the host ID string to a MAC address.
276 The string may be a
277 Host name
278 IP address string
279 MAC address string
280 */
281
282 static int get_dest_addr(const char *hostid, struct ether_addr *eaddr)
283 {
284 struct ether_addr *eap;
285
286 eap = ether_aton(hostid);
287 if (eap) {
288 *eaddr = *eap;
289 if (debug)
290 fprintf(stderr, "The target station address is %s.\n",
291 ether_ntoa(eaddr));
292 } else if (ether_hostton(hostid, eaddr) == 0) {
293 if (debug)
294 fprintf(stderr, "Station address for hostname %s is %s.\n",
295 hostid, ether_ntoa(eaddr));
296 } else {
297 (void)fprintf(stderr,
298 "ether-wake: The Magic Packet host address must be "
299 "specified as\n"
300 " - a station address, 00:11:22:33:44:55, or\n"
301 " - a hostname with a known 'ethers' entry.\n");
302 return -1;
303 }
304 return 0;
305 }
306
307
308 static int get_fill(unsigned char *pkt, struct ether_addr *eaddr)
309 {
310 int offset, i;
311 unsigned char *station_addr = eaddr->ether_addr_octet;
312
313 if (opt_broadcast)
314 memset(pkt+0, 0xff, 6);
315 else
316 memcpy(pkt, station_addr, 6);
317 memcpy(pkt+6, station_addr, 6);
318 pkt[12] = 0x08; /* Or 0x0806 for ARP, 0x8035 for RARP */
319 pkt[13] = 0x42;
320 offset = 14;
321
322 memset(pkt+offset, 0xff, 6);
323 offset += 6;
324
325 for (i = 0; i < 16; i++) {
326 memcpy(pkt+offset, station_addr, 6);
327 offset += 6;
328 }
329 if (debug) {
330 fprintf(stderr, "Packet is ");
331 for (i = 0; i < offset; i++)
332 fprintf(stderr, " %2.2x", pkt[i]);
333 fprintf(stderr, ".\n");
334 }
335 return offset;
336 }
337
338 static int get_wol_pw(const char *optarg)
339 {
340 int passwd[6];
341 int byte_cnt;
342 int i;
343
344 byte_cnt = sscanf(optarg, "%2x:%2x:%2x:%2x:%2x:%2x",
345 &passwd[0], &passwd[1], &passwd[2],
346 &passwd[3], &passwd[4], &passwd[5]);
347 if (byte_cnt < 4)
348 byte_cnt = sscanf(optarg, "%d.%d.%d.%d",
349 &passwd[0], &passwd[1], &passwd[2], &passwd[3]);
350 if (byte_cnt < 4) {
351 fprintf(stderr, "Unable to read the Wake-On-LAN password.\n");
352 return 0;
353 }
354 printf(" The Magic packet password is %2.2x %2.2x %2.2x %2.2x (%d).\n",
355 passwd[0], passwd[1], passwd[2], passwd[3], byte_cnt);
356 for (i = 0; i < byte_cnt; i++)
357 wol_passwd[i] = passwd[i];
358 return wol_passwd_sz = byte_cnt;
359 }
360
361 #if 0
362 {
363 to = (struct sockaddr_in *)&whereto;
364 to->sin_family = AF_INET;
365 if (inet_aton(target, &to->sin_addr)) {
366 hostname = target;
367 }
368 memset (&sa, 0, sizeof sa);
369 sa.sa_family = AF_INET;
370 strncpy (sa.sa_data, interface, sizeof sa.sa_data);
371 sendto (sock, buf, bufix + len, 0, &sa, sizeof sa);
372 strncpy (sa.sa_data, interface, sizeof sa.sa_data);
373 #if 1
374 sendto (sock, buf, bufix + len, 0, &sa, sizeof sa);
375 #else
376 bind (sock, &sa, sizeof sa);
377 connect();
378 send (sock, buf, bufix + len, 0);
379 #endif
380 }
381 #endif
382
383 \f
384 /*
385 * Local variables:
386 * compile-command: "gcc -O -Wall -o ether-wake ether-wake.c"
387 * c-indent-level: 4
388 * c-basic-offset: 4
389 * c-indent-level: 4
390 * tab-width: 4
391 * End:
392 */
This page took 0.055648 seconds and 5 git commands to generate.