diff options
author | Mike Frysinger <vapier@gentoo.org> | 2005-05-11 03:59:53 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2005-05-11 03:59:53 +0000 |
commit | b662f0d58ba40cbbc1cca849fb6e8665688deb5e (patch) | |
tree | 9c39ef7e5ff639fac2b67a0b403385c345689c7d /networking/ether-wake.c | |
parent | 348e84c202c4946d3694f130a6f81f61eb1d4b9e (diff) | |
download | busybox-w32-b662f0d58ba40cbbc1cca849fb6e8665688deb5e.tar.gz busybox-w32-b662f0d58ba40cbbc1cca849fb6e8665688deb5e.tar.bz2 busybox-w32-b662f0d58ba40cbbc1cca849fb6e8665688deb5e.zip |
import ether-wake applet by haveaniceday Bug 252
Diffstat (limited to 'networking/ether-wake.c')
-rw-r--r-- | networking/ether-wake.c | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/networking/ether-wake.c b/networking/ether-wake.c new file mode 100644 index 000000000..83ceece80 --- /dev/null +++ b/networking/ether-wake.c | |||
@@ -0,0 +1,300 @@ | |||
1 | /* | ||
2 | * ether-wake.c - Send a magic packet to wake up sleeping machines. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * Author: Donald Becker, http://www.scyld.com/"; http://www.scyld.com/wakeonlan.html | ||
10 | * Busybox port: Christian Volkmann <haveaniceday@online.de> | ||
11 | * Used version of ether-wake.c: v1.09 11/12/2003 Donald Becker, http://www.scyld.com/"; | ||
12 | */ | ||
13 | |||
14 | /* full usage according Donald Becker | ||
15 | * usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n" | ||
16 | * | ||
17 | * This program generates and transmits a Wake-On-LAN (WOL)\n" | ||
18 | * \"Magic Packet\", used for restarting machines that have been\n" | ||
19 | * soft-powered-down (ACPI D3-warm state).\n" | ||
20 | * It currently generates the standard AMD Magic Packet format, with\n" | ||
21 | * an optional password appended.\n" | ||
22 | * | ||
23 | * The single required parameter is the Ethernet MAC (station) address\n" | ||
24 | * of the machine to wake or a host ID with known NSS 'ethers' entry.\n" | ||
25 | * The MAC address may be found with the 'arp' program while the target\n" | ||
26 | * machine is awake.\n" | ||
27 | * | ||
28 | * Options:\n" | ||
29 | * -b Send wake-up packet to the broadcast address.\n" | ||
30 | * -D Increase the debug level.\n" | ||
31 | * -i ifname Use interface IFNAME instead of the default 'eth0'.\n" | ||
32 | * -p <pw> Append the four or six byte password PW to the packet.\n" | ||
33 | * A password is only required for a few adapter types.\n" | ||
34 | * The password may be specified in ethernet hex format\n" | ||
35 | * or dotted decimal (Internet address)\n" | ||
36 | * -p 00:22:44:66:88:aa\n" | ||
37 | * -p 192.168.1.1\n"; | ||
38 | * | ||
39 | * | ||
40 | * This program generates and transmits a Wake-On-LAN (WOL) "Magic Packet", | ||
41 | * used for restarting machines that have been soft-powered-down | ||
42 | * (ACPI D3-warm state). It currently generates the standard AMD Magic Packet | ||
43 | * format, with an optional password appended. | ||
44 | * | ||
45 | * This software may be used and distributed according to the terms | ||
46 | * of the GNU Public License, incorporated herein by reference. | ||
47 | * Contact the author for use under other terms. | ||
48 | * | ||
49 | * This source file was originally part of the network tricks package, and | ||
50 | * is now distributed to support the Scyld Beowulf system. | ||
51 | * Copyright 1999-2003 Donald Becker and Scyld Computing Corporation. | ||
52 | * | ||
53 | * The author may be reached as becker@scyld, or C/O | ||
54 | * Scyld Computing Corporation | ||
55 | * 914 Bay Ridge Road, Suite 220 | ||
56 | * Annapolis MD 21403 | ||
57 | * | ||
58 | * Notes: | ||
59 | * On some systems dropping root capability allows the process to be | ||
60 | * dumped, traced or debugged. | ||
61 | * If someone traces this program, they get control of a raw socket. | ||
62 | * Linux handles this safely, but beware when porting this program. | ||
63 | * | ||
64 | * An alternative to needing 'root' is using a UDP broadcast socket, however | ||
65 | * doing so only works with adapters configured for unicast+broadcast Rx | ||
66 | * filter. That configuration consumes more power. | ||
67 | */ | ||
68 | |||
69 | |||
70 | #include <unistd.h> | ||
71 | #include <stdlib.h> | ||
72 | #include <stdio.h> | ||
73 | #include <errno.h> | ||
74 | #include <ctype.h> | ||
75 | #include <string.h> | ||
76 | |||
77 | #include <sys/socket.h> | ||
78 | #include <sys/types.h> | ||
79 | #include <sys/ioctl.h> | ||
80 | #include <features.h> | ||
81 | #include <netpacket/packet.h> | ||
82 | #include <net/ethernet.h> | ||
83 | #include <netdb.h> | ||
84 | #include <netinet/ether.h> | ||
85 | |||
86 | #ifdef __linux__ | ||
87 | #include <linux/if.h> | ||
88 | #endif | ||
89 | |||
90 | #include "busybox.h" | ||
91 | |||
92 | /* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to | ||
93 | * work as non-root, but we need SOCK_PACKET to specify the Ethernet | ||
94 | * destination address. | ||
95 | */ | ||
96 | #ifdef PF_PACKET | ||
97 | # define whereto_t sockaddr_ll | ||
98 | # define make_socket() socket(PF_PACKET, SOCK_RAW, 0) | ||
99 | #else | ||
100 | # define whereto_t sockaddr | ||
101 | # define make_socket() socket(AF_INET, SOCK_PACKET, SOCK_PACKET) | ||
102 | #endif | ||
103 | |||
104 | #ifdef DEBUG | ||
105 | # define bb_debug_msg(fmt, args...) fprintf(stderr, fmt, ## args) | ||
106 | void bb_debug_dump_packet(unsigned char *outpack, int pktsize) | ||
107 | { | ||
108 | int i; | ||
109 | printf("packet dump:\n"); | ||
110 | for (i = 0; i < pktsize; ++i) { | ||
111 | printf("%2.2x ", outpack[i]); | ||
112 | if (i % 20 == 19) printf("\n"); | ||
113 | } | ||
114 | printf("\n\n"); | ||
115 | } | ||
116 | #else | ||
117 | # define bb_debug_msg(fmt, args...) | ||
118 | # define bb_debug_dump_packet(outpack, pktsize) | ||
119 | #endif | ||
120 | |||
121 | static inline void get_dest_addr(const char *arg, struct ether_addr *eaddr); | ||
122 | static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast); | ||
123 | static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd); | ||
124 | |||
125 | int etherwake_main(int argc, char *argv[]) | ||
126 | { | ||
127 | char *ifname = "eth0", *pass = NULL; | ||
128 | unsigned long flags; | ||
129 | unsigned char wol_passwd[6]; | ||
130 | int wol_passwd_sz = 0; | ||
131 | |||
132 | int s; /* Raw socket */ | ||
133 | int pktsize; | ||
134 | unsigned char outpack[1000]; | ||
135 | |||
136 | struct ether_addr eaddr; | ||
137 | struct whereto_t whereto; /* who to wake up */ | ||
138 | |||
139 | /* handle misc user options */ | ||
140 | flags = bb_getopt_ulflags(argc, argv, "bi:p:", &ifname, &pass); | ||
141 | if (optind == argc) | ||
142 | bb_show_usage(); | ||
143 | if (pass) | ||
144 | wol_passwd_sz = get_wol_pw(pass, wol_passwd); | ||
145 | |||
146 | /* create the raw socket */ | ||
147 | s = make_socket(); | ||
148 | if (s < 0) | ||
149 | bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket); | ||
150 | |||
151 | /* now that we have a raw socket we can drop root */ | ||
152 | setuid(getuid()); | ||
153 | |||
154 | /* look up the dest mac address */ | ||
155 | get_dest_addr(argv[optind], &eaddr); | ||
156 | |||
157 | /* fill out the header of the packet */ | ||
158 | pktsize = get_fill(outpack, &eaddr, flags /*& 1 [OPT_BROADCAST]*/); | ||
159 | |||
160 | bb_debug_dump_packet(outpack, pktsize); | ||
161 | |||
162 | /* Fill in the source address, if possible. */ | ||
163 | #ifdef __linux__ | ||
164 | { | ||
165 | struct ifreq if_hwaddr; | ||
166 | |||
167 | strcpy(if_hwaddr.ifr_name, ifname); | ||
168 | if (ioctl(s, SIOCGIFHWADDR, &if_hwaddr) < 0) | ||
169 | bb_perror_msg_and_die("SIOCGIFHWADDR on %s failed", ifname); | ||
170 | |||
171 | memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6); | ||
172 | |||
173 | # ifdef DEBUG | ||
174 | { | ||
175 | unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data; | ||
176 | printf("The hardware address (SIOCGIFHWADDR) of %s is type %d " | ||
177 | "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n\n", ifname, | ||
178 | if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1], | ||
179 | hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); | ||
180 | } | ||
181 | # endif | ||
182 | } | ||
183 | #endif /* __linux__ */ | ||
184 | |||
185 | bb_debug_dump_packet(outpack, pktsize); | ||
186 | |||
187 | /* append the password if specified */ | ||
188 | if (wol_passwd_sz > 0) { | ||
189 | memcpy(outpack+pktsize, wol_passwd, wol_passwd_sz); | ||
190 | pktsize += wol_passwd_sz; | ||
191 | } | ||
192 | |||
193 | bb_debug_dump_packet(outpack, pktsize); | ||
194 | |||
195 | /* This is necessary for broadcasts to work */ | ||
196 | if (flags /*& 1 [OPT_BROADCAST]*/) { | ||
197 | int one = 1; | ||
198 | if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (void *)&one, sizeof(one)) < 0) | ||
199 | bb_perror_msg("SO_BROADCAST"); | ||
200 | } | ||
201 | |||
202 | #if defined(PF_PACKET) | ||
203 | { | ||
204 | struct ifreq ifr; | ||
205 | strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | ||
206 | if (ioctl(s, SIOCGIFINDEX, &ifr) == -1) | ||
207 | bb_perror_msg_and_die("SIOCGIFINDEX"); | ||
208 | memset(&whereto, 0, sizeof(whereto)); | ||
209 | whereto.sll_family = AF_PACKET; | ||
210 | whereto.sll_ifindex = ifr.ifr_ifindex; | ||
211 | /* The manual page incorrectly claims the address must be filled. | ||
212 | We do so because the code may change to match the docs. */ | ||
213 | whereto.sll_halen = ETH_ALEN; | ||
214 | memcpy(whereto.sll_addr, outpack, ETH_ALEN); | ||
215 | } | ||
216 | #else | ||
217 | whereto.sa_family = 0; | ||
218 | strcpy(whereto.sa_data, ifname); | ||
219 | #endif | ||
220 | |||
221 | if (sendto(s, outpack, pktsize, 0, (struct sockaddr *)&whereto, sizeof(whereto)) < 0) | ||
222 | bb_perror_msg(bb_msg_write_error); | ||
223 | |||
224 | close(s); | ||
225 | |||
226 | return EXIT_SUCCESS; | ||
227 | } | ||
228 | |||
229 | /* Convert the host ID string to a MAC address. | ||
230 | * The string may be a: | ||
231 | * Host name | ||
232 | * IP address string | ||
233 | * MAC address string | ||
234 | */ | ||
235 | static inline void get_dest_addr(const char *hostid, struct ether_addr *eaddr) | ||
236 | { | ||
237 | struct ether_addr *eap; | ||
238 | |||
239 | eap = ether_aton(hostid); | ||
240 | if (eap) { | ||
241 | *eaddr = *eap; | ||
242 | bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eaddr)); | ||
243 | } else if (ether_hostton(hostid, eaddr) == 0) { | ||
244 | bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr)); | ||
245 | } else | ||
246 | bb_show_usage(); | ||
247 | } | ||
248 | |||
249 | static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast) | ||
250 | { | ||
251 | int offset, i; | ||
252 | unsigned char *station_addr = eaddr->ether_addr_octet; | ||
253 | |||
254 | if (broadcast) | ||
255 | memset(pkt+0, 0xff, 6); | ||
256 | else | ||
257 | memcpy(pkt, station_addr, 6); | ||
258 | memcpy(pkt+6, station_addr, 6); | ||
259 | pkt[12] = 0x08; /* Or 0x0806 for ARP, 0x8035 for RARP */ | ||
260 | pkt[13] = 0x42; | ||
261 | offset = 14; | ||
262 | |||
263 | memset(pkt+offset, 0xff, 6); | ||
264 | offset += 6; | ||
265 | |||
266 | for (i = 0; i < 16; ++i) { | ||
267 | memcpy(pkt+offset, station_addr, 6); | ||
268 | offset += 6; | ||
269 | } | ||
270 | |||
271 | return offset; | ||
272 | } | ||
273 | |||
274 | static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd) | ||
275 | { | ||
276 | int passwd[6]; | ||
277 | int byte_cnt, i; | ||
278 | |||
279 | /* handle MAC format */ | ||
280 | byte_cnt = sscanf(ethoptarg, "%2x:%2x:%2x:%2x:%2x:%2x", | ||
281 | &passwd[0], &passwd[1], &passwd[2], | ||
282 | &passwd[3], &passwd[4], &passwd[5]); | ||
283 | /* handle IP format */ | ||
284 | if (byte_cnt < 4) | ||
285 | byte_cnt = sscanf(ethoptarg, "%d.%d.%d.%d", | ||
286 | &passwd[0], &passwd[1], &passwd[2], &passwd[3]); | ||
287 | if (byte_cnt < 4) { | ||
288 | bb_error_msg("Unable to read the Wake-On-LAN pass"); | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | for (i = 0; i < byte_cnt; ++i) | ||
293 | wol_passwd[i] = passwd[i]; | ||
294 | |||
295 | bb_debug_msg("password: %2.2x %2.2x %2.2x %2.2x (%d)\n\n", | ||
296 | wol_passwd[0], wol_passwd[1], wol_passwd[2], wol_passwd[3], | ||
297 | byte_cnt); | ||
298 | |||
299 | return byte_cnt; | ||
300 | } | ||