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