source: MondoRescue/branches/3.3/mindi-busybox/networking/ether-wake.c@ 3734

Last change on this file since 3734 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 8.9 KB
RevLine 
[821]1/* vi: set sw=4 ts=4: */
2/*
3 * ether-wake.c - Send a magic packet to wake up sleeping machines.
4 *
[2725]5 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[821]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
[3232]52 * Scyld Computing Corporation
53 * 914 Bay Ridge Road, Suite 220
54 * Annapolis MD 21403
[821]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.
[3621]65 */
[821]66
[3232]67//usage:#define ether_wake_trivial_usage
[3621]68//usage: "[-b] [-i IFACE] [-p aa:bb:cc:dd[:ee:ff]/a.b.c.d] MAC"
[3232]69//usage:#define ether_wake_full_usage "\n\n"
70//usage: "Send a magic packet to wake up sleeping machines.\n"
71//usage: "MAC must be a station address (00:11:22:33:44:55) or\n"
72//usage: "a hostname with a known 'ethers' entry.\n"
[3621]73//usage: "\n -b Broadcast the packet"
74//usage: "\n -i IFACE Interface to use (default eth0)"
75//usage: "\n -p PASSWORD Append four or six byte PASSWORD to the packet"
[821]76
[3232]77#include "libbb.h"
[821]78#include <netpacket/packet.h>
79#include <netinet/ether.h>
80#include <linux/if.h>
81
82/* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to
83 * work as non-root, but we need SOCK_PACKET to specify the Ethernet
84 * destination address.
85 */
86#ifdef PF_PACKET
87# define whereto_t sockaddr_ll
[1765]88# define make_socket() xsocket(PF_PACKET, SOCK_RAW, 0)
[821]89#else
90# define whereto_t sockaddr
[1765]91# define make_socket() xsocket(AF_INET, SOCK_PACKET, SOCK_PACKET)
[821]92#endif
93
94#ifdef DEBUG
95# define bb_debug_msg(fmt, args...) fprintf(stderr, fmt, ## args)
96void bb_debug_dump_packet(unsigned char *outpack, int pktsize)
97{
98 int i;
99 printf("packet dump:\n");
100 for (i = 0; i < pktsize; ++i) {
101 printf("%2.2x ", outpack[i]);
[2725]102 if (i % 20 == 19) bb_putchar('\n');
[821]103 }
104 printf("\n\n");
105}
106#else
[1765]107# define bb_debug_msg(fmt, args...) ((void)0)
108# define bb_debug_dump_packet(outpack, pktsize) ((void)0)
[821]109#endif
110
[1765]111/* Convert the host ID string to a MAC address.
112 * The string may be a:
113 * Host name
114 * IP address string
115 * MAC address string
[3232]116 */
[1765]117static void get_dest_addr(const char *hostid, struct ether_addr *eaddr)
118{
119 struct ether_addr *eap;
[821]120
[2725]121 eap = ether_aton_r(hostid, eaddr);
[1765]122 if (eap) {
[2725]123 bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eap));
[3621]124#if !defined(__UCLIBC__) || UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 30)
[1765]125 } else if (ether_hostton(hostid, eaddr) == 0) {
126 bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr));
127#endif
[2725]128 } else {
[1765]129 bb_show_usage();
[2725]130 }
[1765]131}
132
[3621]133#define PKT_HEADER_SIZE (20 + 16*6)
134static int fill_pkt_header(unsigned char *pkt, struct ether_addr *eaddr, int broadcast)
[821]135{
[1765]136 int i;
137 unsigned char *station_addr = eaddr->ether_addr_octet;
138
139 memset(pkt, 0xff, 6);
140 if (!broadcast)
141 memcpy(pkt, station_addr, 6);
142 pkt += 6;
143
144 memcpy(pkt, station_addr, 6); /* 6 */
145 pkt += 6;
146
147 *pkt++ = 0x08; /* 12 */ /* Or 0x0806 for ARP, 0x8035 for RARP */
148 *pkt++ = 0x42; /* 13 */
149
150 memset(pkt, 0xff, 6); /* 14 */
151
152 for (i = 0; i < 16; ++i) {
153 pkt += 6;
154 memcpy(pkt, station_addr, 6); /* 20,26,32,... */
155 }
156
[3621]157 return PKT_HEADER_SIZE; /* length of packet */
[1765]158}
159
160static int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd)
161{
162 unsigned passwd[6];
163 int byte_cnt, i;
164
165 /* handle MAC format */
166 byte_cnt = sscanf(ethoptarg, "%2x:%2x:%2x:%2x:%2x:%2x",
167 &passwd[0], &passwd[1], &passwd[2],
168 &passwd[3], &passwd[4], &passwd[5]);
169 /* handle IP format */
170// FIXME: why < 4?? should it be < 6?
171 if (byte_cnt < 4)
172 byte_cnt = sscanf(ethoptarg, "%u.%u.%u.%u",
173 &passwd[0], &passwd[1], &passwd[2], &passwd[3]);
174 if (byte_cnt < 4) {
[2725]175 bb_error_msg("can't read Wake-On-LAN pass");
[1765]176 return 0;
177 }
178// TODO: check invalid numbers >255??
179 for (i = 0; i < byte_cnt; ++i)
180 wol_passwd[i] = passwd[i];
181
182 bb_debug_msg("password: %2.2x %2.2x %2.2x %2.2x (%d)\n\n",
183 wol_passwd[0], wol_passwd[1], wol_passwd[2], wol_passwd[3],
184 byte_cnt);
185
186 return byte_cnt;
187}
188
[2725]189int ether_wake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
190int ether_wake_main(int argc UNUSED_PARAM, char **argv)
[1765]191{
192 const char *ifname = "eth0";
193 char *pass;
194 unsigned flags;
[821]195 unsigned char wol_passwd[6];
196 int wol_passwd_sz = 0;
[2725]197 int s; /* Raw socket */
[821]198 int pktsize;
[3621]199 unsigned char outpack[PKT_HEADER_SIZE + 6 /* max passwd size */ + 16 /* paranoia */];
[821]200
201 struct ether_addr eaddr;
[2725]202 struct whereto_t whereto; /* who to wake up */
[821]203
204 /* handle misc user options */
[1765]205 opt_complementary = "=1";
206 flags = getopt32(argv, "bi:p:", &ifname, &pass);
207 if (flags & 4) /* -p */
[821]208 wol_passwd_sz = get_wol_pw(pass, wol_passwd);
[1765]209 flags &= 1; /* we further interested only in -b [bcast] flag */
[821]210
211 /* create the raw socket */
212 s = make_socket();
213
214 /* now that we have a raw socket we can drop root */
[1765]215 /* xsetuid(getuid()); - but save on code size... */
[821]216
217 /* look up the dest mac address */
218 get_dest_addr(argv[optind], &eaddr);
219
220 /* fill out the header of the packet */
[3621]221 pktsize = fill_pkt_header(outpack, &eaddr, flags /* & 1 OPT_BROADCAST */);
[821]222
223 bb_debug_dump_packet(outpack, pktsize);
224
225 /* Fill in the source address, if possible. */
226#ifdef __linux__
227 {
228 struct ifreq if_hwaddr;
229
[2725]230 strncpy_IFNAMSIZ(if_hwaddr.ifr_name, ifname);
[1765]231 ioctl_or_perror_and_die(s, SIOCGIFHWADDR, &if_hwaddr, "SIOCGIFHWADDR on %s failed", ifname);
[821]232
233 memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6);
234
235# ifdef DEBUG
236 {
237 unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data;
238 printf("The hardware address (SIOCGIFHWADDR) of %s is type %d "
[3232]239 "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n\n", ifname,
240 if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1],
241 hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
[821]242 }
243# endif
244 }
245#endif /* __linux__ */
246
247 bb_debug_dump_packet(outpack, pktsize);
248
249 /* append the password if specified */
250 if (wol_passwd_sz > 0) {
251 memcpy(outpack+pktsize, wol_passwd, wol_passwd_sz);
252 pktsize += wol_passwd_sz;
253 }
254
255 bb_debug_dump_packet(outpack, pktsize);
256
257 /* This is necessary for broadcasts to work */
[1765]258 if (flags /* & 1 OPT_BROADCAST */) {
259 if (setsockopt_broadcast(s) != 0)
[821]260 bb_perror_msg("SO_BROADCAST");
261 }
262
263#if defined(PF_PACKET)
264 {
265 struct ifreq ifr;
[2725]266 strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
[1765]267 xioctl(s, SIOCGIFINDEX, &ifr);
[821]268 memset(&whereto, 0, sizeof(whereto));
269 whereto.sll_family = AF_PACKET;
270 whereto.sll_ifindex = ifr.ifr_ifindex;
271 /* The manual page incorrectly claims the address must be filled.
272 We do so because the code may change to match the docs. */
273 whereto.sll_halen = ETH_ALEN;
274 memcpy(whereto.sll_addr, outpack, ETH_ALEN);
275 }
276#else
277 whereto.sa_family = 0;
278 strcpy(whereto.sa_data, ifname);
279#endif
[1765]280 xsendto(s, outpack, pktsize, (struct sockaddr *)&whereto, sizeof(whereto));
281 if (ENABLE_FEATURE_CLEAN_UP)
282 close(s);
[821]283 return EXIT_SUCCESS;
284}
Note: See TracBrowser for help on using the repository browser.