70 |
#include <netinet/ether.h> |
#include <netinet/ether.h> |
71 |
#include <linux/if.h> |
#include <linux/if.h> |
72 |
|
|
73 |
#include "busybox.h" |
#include "libbb.h" |
74 |
|
|
75 |
/* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to |
/* 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 |
* work as non-root, but we need SOCK_PACKET to specify the Ethernet |
92 |
printf("packet dump:\n"); |
printf("packet dump:\n"); |
93 |
for (i = 0; i < pktsize; ++i) { |
for (i = 0; i < pktsize; ++i) { |
94 |
printf("%2.2x ", outpack[i]); |
printf("%2.2x ", outpack[i]); |
95 |
if (i % 20 == 19) puts(""); |
if (i % 20 == 19) bb_putchar('\n'); |
96 |
} |
} |
97 |
printf("\n\n"); |
printf("\n\n"); |
98 |
} |
} |
99 |
#else |
#else |
100 |
# define bb_debug_msg(fmt, args...) |
# define bb_debug_msg(fmt, args...) ((void)0) |
101 |
# define bb_debug_dump_packet(outpack, pktsize) |
# define bb_debug_dump_packet(outpack, pktsize) ((void)0) |
102 |
#endif |
#endif |
103 |
|
|
104 |
static inline void get_dest_addr(const char *arg, struct ether_addr *eaddr); |
/* Convert the host ID string to a MAC address. |
105 |
static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast); |
* The string may be a: |
106 |
static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd); |
* Host name |
107 |
|
* IP address string |
108 |
|
* MAC address string |
109 |
|
*/ |
110 |
|
static void get_dest_addr(const char *hostid, struct ether_addr *eaddr) |
111 |
|
{ |
112 |
|
struct ether_addr *eap; |
113 |
|
|
114 |
int ether_wake_main(int argc, char *argv[]) |
eap = ether_aton(hostid); |
115 |
|
if (eap) { |
116 |
|
*eaddr = *eap; |
117 |
|
bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eaddr)); |
118 |
|
#if !defined(__UCLIBC__) |
119 |
|
} else if (ether_hostton(hostid, eaddr) == 0) { |
120 |
|
bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr)); |
121 |
|
#endif |
122 |
|
} else |
123 |
|
bb_show_usage(); |
124 |
|
} |
125 |
|
|
126 |
|
static int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast) |
127 |
{ |
{ |
128 |
char *ifname = "eth0", *pass = NULL; |
int i; |
129 |
unsigned long flags; |
unsigned char *station_addr = eaddr->ether_addr_octet; |
130 |
|
|
131 |
|
memset(pkt, 0xff, 6); |
132 |
|
if (!broadcast) |
133 |
|
memcpy(pkt, station_addr, 6); |
134 |
|
pkt += 6; |
135 |
|
|
136 |
|
memcpy(pkt, station_addr, 6); /* 6 */ |
137 |
|
pkt += 6; |
138 |
|
|
139 |
|
*pkt++ = 0x08; /* 12 */ /* Or 0x0806 for ARP, 0x8035 for RARP */ |
140 |
|
*pkt++ = 0x42; /* 13 */ |
141 |
|
|
142 |
|
memset(pkt, 0xff, 6); /* 14 */ |
143 |
|
|
144 |
|
for (i = 0; i < 16; ++i) { |
145 |
|
pkt += 6; |
146 |
|
memcpy(pkt, station_addr, 6); /* 20,26,32,... */ |
147 |
|
} |
148 |
|
|
149 |
|
return 20 + 16*6; /* length of packet */ |
150 |
|
} |
151 |
|
|
152 |
|
static int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd) |
153 |
|
{ |
154 |
|
unsigned passwd[6]; |
155 |
|
int byte_cnt, i; |
156 |
|
|
157 |
|
/* handle MAC format */ |
158 |
|
byte_cnt = sscanf(ethoptarg, "%2x:%2x:%2x:%2x:%2x:%2x", |
159 |
|
&passwd[0], &passwd[1], &passwd[2], |
160 |
|
&passwd[3], &passwd[4], &passwd[5]); |
161 |
|
/* handle IP format */ |
162 |
|
// FIXME: why < 4?? should it be < 6? |
163 |
|
if (byte_cnt < 4) |
164 |
|
byte_cnt = sscanf(ethoptarg, "%u.%u.%u.%u", |
165 |
|
&passwd[0], &passwd[1], &passwd[2], &passwd[3]); |
166 |
|
if (byte_cnt < 4) { |
167 |
|
bb_error_msg("cannot read Wake-On-LAN pass"); |
168 |
|
return 0; |
169 |
|
} |
170 |
|
// TODO: check invalid numbers >255?? |
171 |
|
for (i = 0; i < byte_cnt; ++i) |
172 |
|
wol_passwd[i] = passwd[i]; |
173 |
|
|
174 |
|
bb_debug_msg("password: %2.2x %2.2x %2.2x %2.2x (%d)\n\n", |
175 |
|
wol_passwd[0], wol_passwd[1], wol_passwd[2], wol_passwd[3], |
176 |
|
byte_cnt); |
177 |
|
|
178 |
|
return byte_cnt; |
179 |
|
} |
180 |
|
|
181 |
|
int ether_wake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
182 |
|
int ether_wake_main(int argc UNUSED_PARAM, char **argv) |
183 |
|
{ |
184 |
|
const char *ifname = "eth0"; |
185 |
|
char *pass; |
186 |
|
unsigned flags; |
187 |
unsigned char wol_passwd[6]; |
unsigned char wol_passwd[6]; |
188 |
int wol_passwd_sz = 0; |
int wol_passwd_sz = 0; |
|
|
|
189 |
int s; /* Raw socket */ |
int s; /* Raw socket */ |
190 |
int pktsize; |
int pktsize; |
191 |
unsigned char outpack[1000]; |
unsigned char outpack[1000]; |
194 |
struct whereto_t whereto; /* who to wake up */ |
struct whereto_t whereto; /* who to wake up */ |
195 |
|
|
196 |
/* handle misc user options */ |
/* handle misc user options */ |
197 |
flags = getopt32(argc, argv, "bi:p:", &ifname, &pass); |
opt_complementary = "=1"; |
198 |
if (optind == argc) |
flags = getopt32(argv, "bi:p:", &ifname, &pass); |
199 |
bb_show_usage(); |
if (flags & 4) /* -p */ |
|
if (pass) |
|
200 |
wol_passwd_sz = get_wol_pw(pass, wol_passwd); |
wol_passwd_sz = get_wol_pw(pass, wol_passwd); |
201 |
|
flags &= 1; /* we further interested only in -b [bcast] flag */ |
202 |
|
|
203 |
/* create the raw socket */ |
/* create the raw socket */ |
204 |
s = make_socket(); |
s = make_socket(); |
205 |
|
|
206 |
/* now that we have a raw socket we can drop root */ |
/* now that we have a raw socket we can drop root */ |
207 |
xsetuid(getuid()); |
/* xsetuid(getuid()); - but save on code size... */ |
208 |
|
|
209 |
/* look up the dest mac address */ |
/* look up the dest mac address */ |
210 |
get_dest_addr(argv[optind], &eaddr); |
get_dest_addr(argv[optind], &eaddr); |
211 |
|
|
212 |
/* fill out the header of the packet */ |
/* fill out the header of the packet */ |
213 |
pktsize = get_fill(outpack, &eaddr, flags /*& 1 [OPT_BROADCAST]*/); |
pktsize = get_fill(outpack, &eaddr, flags /* & 1 OPT_BROADCAST */); |
214 |
|
|
215 |
bb_debug_dump_packet(outpack, pktsize); |
bb_debug_dump_packet(outpack, pktsize); |
216 |
|
|
220 |
struct ifreq if_hwaddr; |
struct ifreq if_hwaddr; |
221 |
|
|
222 |
strncpy(if_hwaddr.ifr_name, ifname, sizeof(if_hwaddr.ifr_name)); |
strncpy(if_hwaddr.ifr_name, ifname, sizeof(if_hwaddr.ifr_name)); |
223 |
if (ioctl(s, SIOCGIFHWADDR, &if_hwaddr) < 0) |
ioctl_or_perror_and_die(s, SIOCGIFHWADDR, &if_hwaddr, "SIOCGIFHWADDR on %s failed", ifname); |
|
bb_perror_msg_and_die("SIOCGIFHWADDR on %s failed", ifname); |
|
224 |
|
|
225 |
memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6); |
memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6); |
226 |
|
|
247 |
bb_debug_dump_packet(outpack, pktsize); |
bb_debug_dump_packet(outpack, pktsize); |
248 |
|
|
249 |
/* This is necessary for broadcasts to work */ |
/* This is necessary for broadcasts to work */ |
250 |
if (flags /*& 1 [OPT_BROADCAST]*/) { |
if (flags /* & 1 OPT_BROADCAST */) { |
251 |
if (setsockopt_broadcast(s) < 0) |
if (setsockopt_broadcast(s) != 0) |
252 |
bb_perror_msg("SO_BROADCAST"); |
bb_perror_msg("SO_BROADCAST"); |
253 |
} |
} |
254 |
|
|
256 |
{ |
{ |
257 |
struct ifreq ifr; |
struct ifreq ifr; |
258 |
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
259 |
if (ioctl(s, SIOCGIFINDEX, &ifr) == -1) |
xioctl(s, SIOCGIFINDEX, &ifr); |
|
bb_perror_msg_and_die("SIOCGIFINDEX"); |
|
260 |
memset(&whereto, 0, sizeof(whereto)); |
memset(&whereto, 0, sizeof(whereto)); |
261 |
whereto.sll_family = AF_PACKET; |
whereto.sll_family = AF_PACKET; |
262 |
whereto.sll_ifindex = ifr.ifr_ifindex; |
whereto.sll_ifindex = ifr.ifr_ifindex; |
269 |
whereto.sa_family = 0; |
whereto.sa_family = 0; |
270 |
strcpy(whereto.sa_data, ifname); |
strcpy(whereto.sa_data, ifname); |
271 |
#endif |
#endif |
272 |
|
xsendto(s, outpack, pktsize, (struct sockaddr *)&whereto, sizeof(whereto)); |
273 |
if (sendto(s, outpack, pktsize, 0, (struct sockaddr *)&whereto, sizeof(whereto)) < 0) |
if (ENABLE_FEATURE_CLEAN_UP) |
274 |
bb_perror_msg(bb_msg_write_error); |
close(s); |
|
|
|
|
close(s); |
|
|
|
|
275 |
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |
276 |
} |
} |
|
|
|
|
/* Convert the host ID string to a MAC address. |
|
|
* The string may be a: |
|
|
* Host name |
|
|
* IP address string |
|
|
* MAC address string |
|
|
*/ |
|
|
static inline void get_dest_addr(const char *hostid, struct ether_addr *eaddr) |
|
|
{ |
|
|
struct ether_addr *eap; |
|
|
|
|
|
eap = ether_aton(hostid); |
|
|
if (eap) { |
|
|
*eaddr = *eap; |
|
|
bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eaddr)); |
|
|
#if !defined(__UCLIBC__) |
|
|
} else if (ether_hostton(hostid, eaddr) == 0) { |
|
|
bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr)); |
|
|
#else |
|
|
# warning Need to implement ether_hostton() for uClibc |
|
|
#endif |
|
|
} else |
|
|
bb_show_usage(); |
|
|
} |
|
|
|
|
|
static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast) |
|
|
{ |
|
|
int offset, i; |
|
|
unsigned char *station_addr = eaddr->ether_addr_octet; |
|
|
|
|
|
if (broadcast) |
|
|
memset(pkt+0, 0xff, 6); |
|
|
else |
|
|
memcpy(pkt, station_addr, 6); |
|
|
memcpy(pkt+6, station_addr, 6); |
|
|
pkt[12] = 0x08; /* Or 0x0806 for ARP, 0x8035 for RARP */ |
|
|
pkt[13] = 0x42; |
|
|
offset = 14; |
|
|
|
|
|
memset(pkt+offset, 0xff, 6); |
|
|
offset += 6; |
|
|
|
|
|
for (i = 0; i < 16; ++i) { |
|
|
memcpy(pkt+offset, station_addr, 6); |
|
|
offset += 6; |
|
|
} |
|
|
|
|
|
return offset; |
|
|
} |
|
|
|
|
|
static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd) |
|
|
{ |
|
|
int passwd[6]; |
|
|
int byte_cnt, i; |
|
|
|
|
|
/* handle MAC format */ |
|
|
byte_cnt = sscanf(ethoptarg, "%2x:%2x:%2x:%2x:%2x:%2x", |
|
|
&passwd[0], &passwd[1], &passwd[2], |
|
|
&passwd[3], &passwd[4], &passwd[5]); |
|
|
/* handle IP format */ |
|
|
if (byte_cnt < 4) |
|
|
byte_cnt = sscanf(ethoptarg, "%d.%d.%d.%d", |
|
|
&passwd[0], &passwd[1], &passwd[2], &passwd[3]); |
|
|
if (byte_cnt < 4) { |
|
|
bb_error_msg("cannot read Wake-On-LAN pass"); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
for (i = 0; i < byte_cnt; ++i) |
|
|
wol_passwd[i] = passwd[i]; |
|
|
|
|
|
bb_debug_msg("password: %2.2x %2.2x %2.2x %2.2x (%d)\n\n", |
|
|
wol_passwd[0], wol_passwd[1], wol_passwd[2], wol_passwd[3], |
|
|
byte_cnt); |
|
|
|
|
|
return byte_cnt; |
|
|
} |
|