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