diff options
Diffstat (limited to 'busybox/networking/arping.c')
-rw-r--r-- | busybox/networking/arping.c | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/busybox/networking/arping.c b/busybox/networking/arping.c new file mode 100644 index 000000000..7279e8653 --- /dev/null +++ b/busybox/networking/arping.c | |||
@@ -0,0 +1,499 @@ | |||
1 | /* | ||
2 | * arping.c - Ping hosts by ARP requests/replies | ||
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: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> | ||
10 | * Busybox port: Nick Fedchik <nick@fedchik.org.ua> | ||
11 | */ | ||
12 | |||
13 | #include <sys/ioctl.h> | ||
14 | #include <sys/signal.h> | ||
15 | #include <sys/time.h> | ||
16 | |||
17 | #include <errno.h> | ||
18 | #include <stdlib.h> | ||
19 | #include <string.h> | ||
20 | #include <unistd.h> | ||
21 | |||
22 | #include <arpa/inet.h> | ||
23 | #include <net/if.h> | ||
24 | #include <netinet/ether.h> | ||
25 | #include <netpacket/packet.h> | ||
26 | |||
27 | #include "busybox.h" | ||
28 | |||
29 | #define APPLET_NAME "arping" | ||
30 | |||
31 | static struct in_addr src; | ||
32 | static struct in_addr dst; | ||
33 | static struct sockaddr_ll me; | ||
34 | static struct sockaddr_ll he; | ||
35 | static struct timeval last; | ||
36 | static int dad; | ||
37 | static int unsolicited; | ||
38 | static int advert; | ||
39 | static int quiet; | ||
40 | static int quit_on_reply = 0; | ||
41 | static int count = -1; | ||
42 | static int timeout; | ||
43 | static int unicasting; | ||
44 | static int s; | ||
45 | static int broadcast_only; | ||
46 | static int sent; | ||
47 | static int brd_sent; | ||
48 | static int received; | ||
49 | static int brd_recv; | ||
50 | static int req_recv; | ||
51 | |||
52 | #define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \ | ||
53 | ((tv1).tv_usec-(tv2).tv_usec)/1000 ) | ||
54 | #if 0 | ||
55 | static void set_signal(int signo, void (*handler) (void)) | ||
56 | { | ||
57 | struct sigaction sa; | ||
58 | |||
59 | memset(&sa, 0, sizeof(sa)); | ||
60 | sa.sa_handler = (void (*)(int)) handler; | ||
61 | sa.sa_flags = SA_RESTART; | ||
62 | sigaction(signo, &sa, NULL); | ||
63 | } | ||
64 | #endif | ||
65 | |||
66 | static int send_pack(int sock, struct in_addr *src_addr, | ||
67 | struct in_addr *dst_addr, struct sockaddr_ll *ME, | ||
68 | struct sockaddr_ll *HE) | ||
69 | { | ||
70 | int err; | ||
71 | struct timeval now; | ||
72 | unsigned char buf[256]; | ||
73 | struct arphdr *ah = (struct arphdr *) buf; | ||
74 | unsigned char *p = (unsigned char *) (ah + 1); | ||
75 | |||
76 | ah->ar_hrd = htons(ME->sll_hatype); | ||
77 | ah->ar_hrd = htons(ARPHRD_ETHER); | ||
78 | ah->ar_pro = htons(ETH_P_IP); | ||
79 | ah->ar_hln = ME->sll_halen; | ||
80 | ah->ar_pln = 4; | ||
81 | ah->ar_op = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST); | ||
82 | |||
83 | memcpy(p, &ME->sll_addr, ah->ar_hln); | ||
84 | p += ME->sll_halen; | ||
85 | |||
86 | memcpy(p, src_addr, 4); | ||
87 | p += 4; | ||
88 | |||
89 | if (advert) | ||
90 | memcpy(p, &ME->sll_addr, ah->ar_hln); | ||
91 | else | ||
92 | memcpy(p, &HE->sll_addr, ah->ar_hln); | ||
93 | p += ah->ar_hln; | ||
94 | |||
95 | memcpy(p, dst_addr, 4); | ||
96 | p += 4; | ||
97 | |||
98 | gettimeofday(&now, NULL); | ||
99 | err = sendto(sock, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE)); | ||
100 | if (err == p - buf) { | ||
101 | last = now; | ||
102 | sent++; | ||
103 | if (!unicasting) | ||
104 | brd_sent++; | ||
105 | } | ||
106 | return err; | ||
107 | } | ||
108 | |||
109 | void finish(void) | ||
110 | { | ||
111 | if (!quiet) { | ||
112 | printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent); | ||
113 | printf("Received %d repl%s", received, (received > 1) ? "ies" : "y"); | ||
114 | if (brd_recv || req_recv) { | ||
115 | printf(" ("); | ||
116 | if (req_recv) | ||
117 | printf("%d request(s)", req_recv); | ||
118 | if (brd_recv) | ||
119 | printf("%s%d broadcast(s)", req_recv ? ", " : "", brd_recv); | ||
120 | putchar(')'); | ||
121 | } | ||
122 | putchar('\n'); | ||
123 | fflush(stdout); | ||
124 | } | ||
125 | if (dad) | ||
126 | exit(!!received); | ||
127 | if (unsolicited) | ||
128 | exit(0); | ||
129 | exit(!received); | ||
130 | } | ||
131 | |||
132 | void catcher(void) | ||
133 | { | ||
134 | struct timeval tv; | ||
135 | static struct timeval start; | ||
136 | |||
137 | gettimeofday(&tv, NULL); | ||
138 | |||
139 | if (start.tv_sec == 0) | ||
140 | start = tv; | ||
141 | |||
142 | if (count-- == 0 | ||
143 | || (timeout && MS_TDIFF(tv, start) > timeout * 1000 + 500)) | ||
144 | finish(); | ||
145 | |||
146 | if (last.tv_sec == 0 || MS_TDIFF(tv, last) > 500) { | ||
147 | send_pack(s, &src, &dst, &me, &he); | ||
148 | if (count == 0 && unsolicited) | ||
149 | finish(); | ||
150 | } | ||
151 | alarm(1); | ||
152 | } | ||
153 | |||
154 | int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM) | ||
155 | { | ||
156 | struct timeval tv; | ||
157 | struct arphdr *ah = (struct arphdr *) buf; | ||
158 | unsigned char *p = (unsigned char *) (ah + 1); | ||
159 | struct in_addr src_ip, dst_ip; | ||
160 | |||
161 | gettimeofday(&tv, NULL); | ||
162 | |||
163 | /* Filter out wild packets */ | ||
164 | if (FROM->sll_pkttype != PACKET_HOST && | ||
165 | FROM->sll_pkttype != PACKET_BROADCAST && | ||
166 | FROM->sll_pkttype != PACKET_MULTICAST) | ||
167 | return 0; | ||
168 | |||
169 | /* Only these types are recognised */ | ||
170 | if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY)) | ||
171 | return 0; | ||
172 | |||
173 | /* ARPHRD check and this darned FDDI hack here :-( */ | ||
174 | if (ah->ar_hrd != htons(FROM->sll_hatype) && | ||
175 | (FROM->sll_hatype != ARPHRD_FDDI | ||
176 | || ah->ar_hrd != htons(ARPHRD_ETHER))) | ||
177 | return 0; | ||
178 | |||
179 | /* Protocol must be IP. */ | ||
180 | if (ah->ar_pro != htons(ETH_P_IP)) | ||
181 | return 0; | ||
182 | if (ah->ar_pln != 4) | ||
183 | return 0; | ||
184 | if (ah->ar_hln != me.sll_halen) | ||
185 | return 0; | ||
186 | if (len < sizeof(*ah) + 2 * (4 + ah->ar_hln)) | ||
187 | return 0; | ||
188 | memcpy(&src_ip, p + ah->ar_hln, 4); | ||
189 | memcpy(&dst_ip, p + ah->ar_hln + 4 + ah->ar_hln, 4); | ||
190 | if (!dad) { | ||
191 | if (src_ip.s_addr != dst.s_addr) | ||
192 | return 0; | ||
193 | if (src.s_addr != dst_ip.s_addr) | ||
194 | return 0; | ||
195 | if (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)) | ||
196 | return 0; | ||
197 | } else { | ||
198 | /* DAD packet was: | ||
199 | src_ip = 0 (or some src) | ||
200 | src_hw = ME | ||
201 | dst_ip = tested address | ||
202 | dst_hw = <unspec> | ||
203 | |||
204 | We fail, if receive request/reply with: | ||
205 | src_ip = tested_address | ||
206 | src_hw != ME | ||
207 | if src_ip in request was not zero, check | ||
208 | also that it matches to dst_ip, otherwise | ||
209 | dst_ip/dst_hw do not matter. | ||
210 | */ | ||
211 | if (src_ip.s_addr != dst.s_addr) | ||
212 | return 0; | ||
213 | if (memcmp(p, &me.sll_addr, me.sll_halen) == 0) | ||
214 | return 0; | ||
215 | if (src.s_addr && src.s_addr != dst_ip.s_addr) | ||
216 | return 0; | ||
217 | } | ||
218 | if (!quiet) { | ||
219 | int s_printed = 0; | ||
220 | |||
221 | printf("%s ", | ||
222 | FROM->sll_pkttype == PACKET_HOST ? "Unicast" : "Broadcast"); | ||
223 | printf("%s from ", | ||
224 | ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request"); | ||
225 | printf("%s ", inet_ntoa(src_ip)); | ||
226 | printf("[%s]", ether_ntoa((struct ether_addr *) p)); | ||
227 | if (dst_ip.s_addr != src.s_addr) { | ||
228 | printf("for %s ", inet_ntoa(dst_ip)); | ||
229 | s_printed = 1; | ||
230 | } | ||
231 | if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) { | ||
232 | if (!s_printed) | ||
233 | printf("for "); | ||
234 | printf("[%s]", | ||
235 | ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4)); | ||
236 | } | ||
237 | if (last.tv_sec) { | ||
238 | long usecs = (tv.tv_sec - last.tv_sec) * 1000000 + | ||
239 | tv.tv_usec - last.tv_usec; | ||
240 | long msecs = (usecs + 500) / 1000; | ||
241 | |||
242 | usecs -= msecs * 1000 - 500; | ||
243 | printf(" %ld.%03ldms\n", msecs, usecs); | ||
244 | } else { | ||
245 | printf(" UNSOLICITED?\n"); | ||
246 | } | ||
247 | fflush(stdout); | ||
248 | } | ||
249 | received++; | ||
250 | if (FROM->sll_pkttype != PACKET_HOST) | ||
251 | brd_recv++; | ||
252 | if (ah->ar_op == htons(ARPOP_REQUEST)) | ||
253 | req_recv++; | ||
254 | if (quit_on_reply) | ||
255 | finish(); | ||
256 | if (!broadcast_only) { | ||
257 | memcpy(he.sll_addr, p, me.sll_halen); | ||
258 | unicasting = 1; | ||
259 | } | ||
260 | return 1; | ||
261 | } | ||
262 | |||
263 | int arping_main(int argc, char **argv) | ||
264 | { | ||
265 | int socket_errno; | ||
266 | int ch; | ||
267 | uid_t uid = getuid(); | ||
268 | char *device = "eth0"; | ||
269 | int ifindex = 0; | ||
270 | char *source = NULL; | ||
271 | char *target; | ||
272 | |||
273 | s = socket(PF_PACKET, SOCK_DGRAM, 0); | ||
274 | socket_errno = errno; | ||
275 | |||
276 | setuid(uid); | ||
277 | |||
278 | while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:")) != EOF) { | ||
279 | switch (ch) { | ||
280 | case 'b': | ||
281 | broadcast_only = 1; | ||
282 | break; | ||
283 | case 'D': | ||
284 | dad++; | ||
285 | quit_on_reply = 1; | ||
286 | break; | ||
287 | case 'U': | ||
288 | unsolicited++; | ||
289 | break; | ||
290 | case 'A': | ||
291 | advert++; | ||
292 | unsolicited++; | ||
293 | break; | ||
294 | case 'q': | ||
295 | quiet++; | ||
296 | break; | ||
297 | case 'c': | ||
298 | count = atoi(optarg); | ||
299 | break; | ||
300 | case 'w': | ||
301 | timeout = atoi(optarg); | ||
302 | break; | ||
303 | case 'I': | ||
304 | if (optarg == NULL) | ||
305 | bb_show_usage(); | ||
306 | if (bb_strlen(optarg) > IF_NAMESIZE) { | ||
307 | bb_error_msg("Interface name `%s' must be less than %d", optarg, | ||
308 | IF_NAMESIZE); | ||
309 | exit(2); | ||
310 | } | ||
311 | device = optarg; | ||
312 | break; | ||
313 | case 'f': | ||
314 | quit_on_reply = 1; | ||
315 | break; | ||
316 | case 's': | ||
317 | source = optarg; | ||
318 | break; | ||
319 | case 'h': | ||
320 | case '?': | ||
321 | default: | ||
322 | bb_show_usage(); | ||
323 | } | ||
324 | } | ||
325 | argc -= optind; | ||
326 | argv += optind; | ||
327 | |||
328 | if (argc != 1) | ||
329 | bb_show_usage(); | ||
330 | |||
331 | target = *argv; | ||
332 | |||
333 | |||
334 | if (s < 0) { | ||
335 | bb_error_msg("socket"); | ||
336 | exit(socket_errno); | ||
337 | } | ||
338 | |||
339 | { | ||
340 | struct ifreq ifr; | ||
341 | |||
342 | memset(&ifr, 0, sizeof(ifr)); | ||
343 | strncpy(ifr.ifr_name, device, IFNAMSIZ - 1); | ||
344 | if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { | ||
345 | bb_error_msg("Interface %s not found", device); | ||
346 | exit(2); | ||
347 | } | ||
348 | ifindex = ifr.ifr_ifindex; | ||
349 | |||
350 | if (ioctl(s, SIOCGIFFLAGS, (char *) &ifr)) { | ||
351 | bb_error_msg("SIOCGIFFLAGS"); | ||
352 | exit(2); | ||
353 | } | ||
354 | if (!(ifr.ifr_flags & IFF_UP)) { | ||
355 | bb_error_msg("Interface %s is down", device); | ||
356 | exit(2); | ||
357 | } | ||
358 | if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) { | ||
359 | bb_error_msg("Interface %s is not ARPable", device); | ||
360 | exit(dad ? 0 : 2); | ||
361 | } | ||
362 | } | ||
363 | |||
364 | if (!inet_aton(target, &dst)) { | ||
365 | struct hostent *hp; | ||
366 | |||
367 | hp = gethostbyname2(target, AF_INET); | ||
368 | if (!hp) { | ||
369 | bb_error_msg("invalid or unknown target %s", target); | ||
370 | exit(2); | ||
371 | } | ||
372 | memcpy(&dst, hp->h_addr, 4); | ||
373 | } | ||
374 | |||
375 | if (source && !inet_aton(source, &src)) { | ||
376 | bb_error_msg("invalid source address %s", source); | ||
377 | exit(2); | ||
378 | } | ||
379 | |||
380 | if (!dad && unsolicited && src.s_addr == 0) | ||
381 | src = dst; | ||
382 | |||
383 | if (!dad || src.s_addr) { | ||
384 | struct sockaddr_in saddr; | ||
385 | int probe_fd = socket(AF_INET, SOCK_DGRAM, 0); | ||
386 | |||
387 | if (probe_fd < 0) { | ||
388 | bb_error_msg("socket"); | ||
389 | exit(2); | ||
390 | } | ||
391 | if (device) { | ||
392 | if (setsockopt | ||
393 | (probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, | ||
394 | strlen(device) + 1) == -1) | ||
395 | bb_error_msg("WARNING: interface %s is ignored", device); | ||
396 | } | ||
397 | memset(&saddr, 0, sizeof(saddr)); | ||
398 | saddr.sin_family = AF_INET; | ||
399 | if (src.s_addr) { | ||
400 | saddr.sin_addr = src; | ||
401 | if (bind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) { | ||
402 | bb_error_msg("bind"); | ||
403 | exit(2); | ||
404 | } | ||
405 | } else if (!dad) { | ||
406 | int on = 1; | ||
407 | int alen = sizeof(saddr); | ||
408 | |||
409 | saddr.sin_port = htons(1025); | ||
410 | saddr.sin_addr = dst; | ||
411 | |||
412 | if (setsockopt | ||
413 | (probe_fd, SOL_SOCKET, SO_DONTROUTE, (char *) &on, | ||
414 | sizeof(on)) == -1) | ||
415 | perror("WARNING: setsockopt(SO_DONTROUTE)"); | ||
416 | if (connect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)) | ||
417 | == -1) { | ||
418 | bb_error_msg("connect"); | ||
419 | exit(2); | ||
420 | } | ||
421 | if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == | ||
422 | -1) { | ||
423 | bb_error_msg("getsockname"); | ||
424 | exit(2); | ||
425 | } | ||
426 | src = saddr.sin_addr; | ||
427 | } | ||
428 | close(probe_fd); | ||
429 | }; | ||
430 | |||
431 | me.sll_family = AF_PACKET; | ||
432 | me.sll_ifindex = ifindex; | ||
433 | me.sll_protocol = htons(ETH_P_ARP); | ||
434 | if (bind(s, (struct sockaddr *) &me, sizeof(me)) == -1) { | ||
435 | bb_error_msg("bind"); | ||
436 | exit(2); | ||
437 | } | ||
438 | |||
439 | { | ||
440 | int alen = sizeof(me); | ||
441 | |||
442 | if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) { | ||
443 | bb_error_msg("getsockname"); | ||
444 | exit(2); | ||
445 | } | ||
446 | } | ||
447 | if (me.sll_halen == 0) { | ||
448 | bb_error_msg("Interface \"%s\" is not ARPable (no ll address)", device); | ||
449 | exit(dad ? 0 : 2); | ||
450 | } | ||
451 | he = me; | ||
452 | memset(he.sll_addr, -1, he.sll_halen); | ||
453 | |||
454 | if (!quiet) { | ||
455 | printf("ARPING to %s", inet_ntoa(dst)); | ||
456 | printf(" from %s via %s\n", inet_ntoa(src), | ||
457 | device ? device : "unknown"); | ||
458 | } | ||
459 | |||
460 | if (!src.s_addr && !dad) { | ||
461 | bb_error_msg("no src address in the non-DAD mode"); | ||
462 | exit(2); | ||
463 | } | ||
464 | |||
465 | { | ||
466 | struct sigaction sa; | ||
467 | |||
468 | memset(&sa, 0, sizeof(sa)); | ||
469 | sa.sa_flags = SA_RESTART; | ||
470 | |||
471 | sa.sa_handler = (void (*)(int)) finish; | ||
472 | sigaction(SIGINT, &sa, NULL); | ||
473 | |||
474 | sa.sa_handler = (void (*)(int)) catcher; | ||
475 | sigaction(SIGALRM, &sa, NULL); | ||
476 | } | ||
477 | |||
478 | catcher(); | ||
479 | |||
480 | while (1) { | ||
481 | sigset_t sset, osset; | ||
482 | char packet[4096]; | ||
483 | struct sockaddr_ll from; | ||
484 | int alen = sizeof(from); | ||
485 | int cc; | ||
486 | |||
487 | if ((cc = recvfrom(s, packet, sizeof(packet), 0, | ||
488 | (struct sockaddr *) &from, &alen)) < 0) { | ||
489 | perror("recvfrom"); | ||
490 | continue; | ||
491 | } | ||
492 | sigemptyset(&sset); | ||
493 | sigaddset(&sset, SIGALRM); | ||
494 | sigaddset(&sset, SIGINT); | ||
495 | sigprocmask(SIG_BLOCK, &sset, &osset); | ||
496 | recv_pack(packet, cc, &from); | ||
497 | sigprocmask(SIG_SETMASK, &osset, NULL); | ||
498 | } | ||
499 | } | ||