source: MondoRescue/branches/3.3/mindi-busybox/networking/arping.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: 11.3 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4 *
5 * Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
6 * Busybox port: Nick Fedchik <nick@fedchik.org.ua>
7 */
8
9//usage:#define arping_trivial_usage
10//usage: "[-fqbDUA] [-c CNT] [-w TIMEOUT] [-I IFACE] [-s SRC_IP] DST_IP"
11//usage:#define arping_full_usage "\n\n"
12//usage: "Send ARP requests/replies\n"
13//usage: "\n -f Quit on first ARP reply"
14//usage: "\n -q Quiet"
15//usage: "\n -b Keep broadcasting, don't go unicast"
16//usage: "\n -D Exit with 1 if DST_IP replies"
17//usage: "\n -U Unsolicited ARP mode, update your neighbors"
18//usage: "\n -A ARP answer mode, update your neighbors"
19//usage: "\n -c N Stop after sending N ARP requests"
20//usage: "\n -w TIMEOUT Seconds to wait for ARP reply"
21//usage: "\n -I IFACE Interface to use (default eth0)"
22//usage: "\n -s SRC_IP Sender IP address"
23//usage: "\n DST_IP Target IP address"
24
25#include <arpa/inet.h>
26#include <net/if.h>
27#include <netinet/ether.h>
28#include <netpacket/packet.h>
29
30#include "libbb.h"
31#include "common_bufsiz.h"
32
33/* We don't expect to see 1000+ seconds delay, unsigned is enough */
34#define MONOTONIC_US() ((unsigned)monotonic_us())
35
36enum {
37 DAD = 1,
38 UNSOLICITED = 2,
39 ADVERT = 4,
40 QUIET = 8,
41 QUIT_ON_REPLY = 16,
42 BCAST_ONLY = 32,
43 UNICASTING = 64
44};
45
46struct globals {
47 struct in_addr src;
48 struct in_addr dst;
49 struct sockaddr_ll me;
50 struct sockaddr_ll he;
51 int sock_fd;
52
53 int count; // = -1;
54 unsigned last;
55 unsigned timeout_us;
56 unsigned start;
57
58 unsigned sent;
59 unsigned brd_sent;
60 unsigned received;
61 unsigned brd_recv;
62 unsigned req_recv;
63} FIX_ALIASING;
64#define G (*(struct globals*)bb_common_bufsiz1)
65#define src (G.src )
66#define dst (G.dst )
67#define me (G.me )
68#define he (G.he )
69#define sock_fd (G.sock_fd )
70#define count (G.count )
71#define last (G.last )
72#define timeout_us (G.timeout_us)
73#define start (G.start )
74#define sent (G.sent )
75#define brd_sent (G.brd_sent )
76#define received (G.received )
77#define brd_recv (G.brd_recv )
78#define req_recv (G.req_recv )
79#define INIT_G() do { \
80 setup_common_bufsiz(); \
81 count = -1; \
82} while (0)
83
84// If GNUisms are not available...
85//static void *mempcpy(void *_dst, const void *_src, int n)
86//{
87// memcpy(_dst, _src, n);
88// return (char*)_dst + n;
89//}
90
91static int send_pack(struct in_addr *src_addr,
92 struct in_addr *dst_addr, struct sockaddr_ll *ME,
93 struct sockaddr_ll *HE)
94{
95 int err;
96 unsigned char buf[256];
97 struct arphdr *ah = (struct arphdr *) buf;
98 unsigned char *p = (unsigned char *) (ah + 1);
99
100 ah->ar_hrd = htons(ARPHRD_ETHER);
101 ah->ar_pro = htons(ETH_P_IP);
102 ah->ar_hln = ME->sll_halen;
103 ah->ar_pln = 4;
104 ah->ar_op = option_mask32 & ADVERT ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
105
106 p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
107 p = mempcpy(p, src_addr, 4);
108
109 if (option_mask32 & ADVERT)
110 p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
111 else
112 p = mempcpy(p, &HE->sll_addr, ah->ar_hln);
113
114 p = mempcpy(p, dst_addr, 4);
115
116 err = sendto(sock_fd, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
117 if (err == p - buf) {
118 last = MONOTONIC_US();
119 sent++;
120 if (!(option_mask32 & UNICASTING))
121 brd_sent++;
122 }
123 return err;
124}
125
126static void finish(void) NORETURN;
127static void finish(void)
128{
129 if (!(option_mask32 & QUIET)) {
130 printf("Sent %u probe(s) (%u broadcast(s))\n"
131 "Received %u repl%s"
132 " (%u request(s), %u broadcast(s))\n",
133 sent, brd_sent,
134 received, (received == 1) ? "ies" : "y",
135 req_recv, brd_recv);
136 }
137 if (option_mask32 & DAD)
138 exit(!!received);
139 if (option_mask32 & UNSOLICITED)
140 exit(EXIT_SUCCESS);
141 exit(!received);
142}
143
144static void catcher(void)
145{
146 unsigned now;
147
148 now = MONOTONIC_US();
149 if (start == 0)
150 start = now;
151
152 if (count == 0 || (timeout_us && (now - start) > timeout_us))
153 finish();
154
155 /* count < 0 means "infinite count" */
156 if (count > 0)
157 count--;
158
159 if (last == 0 || (now - last) > 500000) {
160 send_pack(&src, &dst, &me, &he);
161 if (count == 0 && (option_mask32 & UNSOLICITED))
162 finish();
163 }
164 alarm(1);
165}
166
167static void recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
168{
169 struct arphdr *ah = (struct arphdr *) buf;
170 unsigned char *p = (unsigned char *) (ah + 1);
171 struct in_addr src_ip, dst_ip;
172 /* moves below assume in_addr is 4 bytes big, ensure that */
173 struct BUG_in_addr_must_be_4 {
174 char BUG_in_addr_must_be_4[
175 sizeof(struct in_addr) == 4 ? 1 : -1
176 ];
177 char BUG_s_addr_must_be_4[
178 sizeof(src_ip.s_addr) == 4 ? 1 : -1
179 ];
180 };
181
182 /* Filter out wild packets */
183 if (FROM->sll_pkttype != PACKET_HOST
184 && FROM->sll_pkttype != PACKET_BROADCAST
185 && FROM->sll_pkttype != PACKET_MULTICAST)
186 return;
187
188 /* Only these types are recognized */
189 if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
190 return;
191
192 /* ARPHRD check and this darned FDDI hack here :-( */
193 if (ah->ar_hrd != htons(FROM->sll_hatype)
194 && (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
195 return;
196
197 /* Protocol must be IP. */
198 if (ah->ar_pro != htons(ETH_P_IP)
199 || (ah->ar_pln != 4)
200 || (ah->ar_hln != me.sll_halen)
201 || (len < (int)(sizeof(*ah) + 2 * (4 + ah->ar_hln))))
202 return;
203
204 move_from_unaligned32(src_ip.s_addr, p + ah->ar_hln);
205 move_from_unaligned32(dst_ip.s_addr, p + ah->ar_hln + 4 + ah->ar_hln);
206
207 if (dst.s_addr != src_ip.s_addr)
208 return;
209 if (!(option_mask32 & DAD)) {
210 if ((src.s_addr != dst_ip.s_addr)
211 || (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)))
212 return;
213 } else {
214 /* DAD packet was:
215 src_ip = 0 (or some src)
216 src_hw = ME
217 dst_ip = tested address
218 dst_hw = <unspec>
219
220 We fail, if receive request/reply with:
221 src_ip = tested_address
222 src_hw != ME
223 if src_ip in request was not zero, check
224 also that it matches to dst_ip, otherwise
225 dst_ip/dst_hw do not matter.
226 */
227 if ((memcmp(p, &me.sll_addr, me.sll_halen) == 0)
228 || (src.s_addr && src.s_addr != dst_ip.s_addr))
229 return;
230 }
231 if (!(option_mask32 & QUIET)) {
232 int s_printed = 0;
233
234 printf("%scast re%s from %s [%02x:%02x:%02x:%02x:%02x:%02x]",
235 FROM->sll_pkttype == PACKET_HOST ? "Uni" : "Broad",
236 ah->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest",
237 inet_ntoa(src_ip),
238 p[0], p[1], p[2], p[3], p[4], p[5]
239 );
240 if (dst_ip.s_addr != src.s_addr) {
241 printf("for %s ", inet_ntoa(dst_ip));
242 s_printed = 1;
243 }
244 if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
245 unsigned char *pp = p + ah->ar_hln + 4;
246 if (!s_printed)
247 printf("for ");
248 printf("[%02x:%02x:%02x:%02x:%02x:%02x]",
249 pp[0], pp[1], pp[2], pp[3], pp[4], pp[5]
250 );
251 }
252
253 if (last) {
254 unsigned diff = MONOTONIC_US() - last;
255 printf(" %u.%03ums\n", diff / 1000, diff % 1000);
256 } else {
257 puts(" UNSOLICITED?");
258 }
259 fflush_all();
260 }
261 received++;
262 if (FROM->sll_pkttype != PACKET_HOST)
263 brd_recv++;
264 if (ah->ar_op == htons(ARPOP_REQUEST))
265 req_recv++;
266 if (option_mask32 & QUIT_ON_REPLY)
267 finish();
268 if (!(option_mask32 & BCAST_ONLY)) {
269 memcpy(he.sll_addr, p, me.sll_halen);
270 option_mask32 |= UNICASTING;
271 }
272}
273
274int arping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
275int arping_main(int argc UNUSED_PARAM, char **argv)
276{
277 const char *device = "eth0";
278 char *source = NULL;
279 char *target;
280 unsigned char *packet;
281 char *err_str;
282
283 INIT_G();
284
285 sock_fd = xsocket(AF_PACKET, SOCK_DGRAM, 0);
286
287 // Drop suid root privileges
288 // Need to remove SUID_NEVER from applets.h for this to work
289 //xsetuid(getuid());
290
291 {
292 unsigned opt;
293 char *str_timeout;
294
295 /* Dad also sets quit_on_reply.
296 * Advert also sets unsolicited.
297 */
298 opt_complementary = "=1:Df:AU:c+";
299 opt = getopt32(argv, "DUAqfbc:w:I:s:",
300 &count, &str_timeout, &device, &source);
301 if (opt & 0x80) /* -w: timeout */
302 timeout_us = xatou_range(str_timeout, 0, INT_MAX/2000000) * 1000000 + 500000;
303 //if (opt & 0x200) /* -s: source */
304 option_mask32 &= 0x3f; /* set respective flags */
305 }
306
307 target = argv[optind];
308 err_str = xasprintf("interface %s %%s", device);
309 xfunc_error_retval = 2;
310
311 {
312 struct ifreq ifr;
313
314 memset(&ifr, 0, sizeof(ifr));
315 strncpy_IFNAMSIZ(ifr.ifr_name, device);
316 /* We use ifr.ifr_name in error msg so that problem
317 * with truncated name will be visible */
318 ioctl_or_perror_and_die(sock_fd, SIOCGIFINDEX, &ifr, err_str, "not found");
319 me.sll_ifindex = ifr.ifr_ifindex;
320
321 xioctl(sock_fd, SIOCGIFFLAGS, (char *) &ifr);
322
323 if (!(ifr.ifr_flags & IFF_UP)) {
324 bb_error_msg_and_die(err_str, "is down");
325 }
326 if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
327 bb_error_msg(err_str, "is not ARPable");
328 return (option_mask32 & DAD ? 0 : 2);
329 }
330 }
331
332 /* if (!inet_aton(target, &dst)) - not needed */ {
333 len_and_sockaddr *lsa;
334 lsa = xhost_and_af2sockaddr(target, 0, AF_INET);
335 dst = lsa->u.sin.sin_addr;
336 if (ENABLE_FEATURE_CLEAN_UP)
337 free(lsa);
338 }
339
340 if (source && !inet_aton(source, &src)) {
341 bb_error_msg_and_die("invalid source address %s", source);
342 }
343
344 if ((option_mask32 & (DAD|UNSOLICITED)) == UNSOLICITED && src.s_addr == 0)
345 src = dst;
346
347 if (!(option_mask32 & DAD) || src.s_addr) {
348 struct sockaddr_in saddr;
349 int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0);
350
351 setsockopt_bindtodevice(probe_fd, device);
352 memset(&saddr, 0, sizeof(saddr));
353 saddr.sin_family = AF_INET;
354 if (src.s_addr) {
355 /* Check that this is indeed our IP */
356 saddr.sin_addr = src;
357 xbind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
358 } else { /* !(option_mask32 & DAD) case */
359 /* Find IP address on this iface */
360 socklen_t alen = sizeof(saddr);
361
362 saddr.sin_port = htons(1025);
363 saddr.sin_addr = dst;
364
365 if (setsockopt_SOL_SOCKET_1(probe_fd, SO_DONTROUTE) != 0)
366 bb_perror_msg("setsockopt(%s)", "SO_DONTROUTE");
367 xconnect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
368 getsockname(probe_fd, (struct sockaddr *) &saddr, &alen);
369 //never happens:
370 //if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == -1)
371 // bb_perror_msg_and_die("getsockname");
372 if (saddr.sin_family != AF_INET)
373 bb_error_msg_and_die("no IP address configured");
374 src = saddr.sin_addr;
375 }
376 close(probe_fd);
377 }
378
379 me.sll_family = AF_PACKET;
380 //me.sll_ifindex = ifindex; - done before
381 me.sll_protocol = htons(ETH_P_ARP);
382 xbind(sock_fd, (struct sockaddr *) &me, sizeof(me));
383
384 {
385 socklen_t alen = sizeof(me);
386 getsockname(sock_fd, (struct sockaddr *) &me, &alen);
387 //never happens:
388 //if (getsockname(sock_fd, (struct sockaddr *) &me, &alen) == -1)
389 // bb_perror_msg_and_die("getsockname");
390 }
391 if (me.sll_halen == 0) {
392 bb_error_msg(err_str, "is not ARPable (no ll address)");
393 return (option_mask32 & DAD ? 0 : 2);
394 }
395 he = me;
396 memset(he.sll_addr, -1, he.sll_halen);
397
398 if (!(option_mask32 & QUIET)) {
399 /* inet_ntoa uses static storage, can't use in same printf */
400 printf("ARPING to %s", inet_ntoa(dst));
401 printf(" from %s via %s\n", inet_ntoa(src), device);
402 }
403
404 signal_SA_RESTART_empty_mask(SIGINT, (void (*)(int))finish);
405 signal_SA_RESTART_empty_mask(SIGALRM, (void (*)(int))catcher);
406
407 catcher();
408
409 packet = xmalloc(4096);
410 while (1) {
411 sigset_t sset, osset;
412 struct sockaddr_ll from;
413 socklen_t alen = sizeof(from);
414 int cc;
415
416 cc = recvfrom(sock_fd, packet, 4096, 0, (struct sockaddr *) &from, &alen);
417 if (cc < 0) {
418 bb_perror_msg("recvfrom");
419 continue;
420 }
421 sigemptyset(&sset);
422 sigaddset(&sset, SIGALRM);
423 sigaddset(&sset, SIGINT);
424 sigprocmask(SIG_BLOCK, &sset, &osset);
425 recv_pack(packet, cc, &from);
426 sigprocmask(SIG_SETMASK, &osset, NULL);
427 }
428}
Note: See TracBrowser for help on using the repository browser.