ether-wake.c 8.9 KB

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