1 /* vi: set sw=4 ts=4: */
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5 * Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
6 * Busybox port: Nick Fedchik <nick@fedchik.org.ua>
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 Duplicated address detection mode"
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 Time to wait for ARP reply, seconds"
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"
25 #include <arpa/inet.h>
27 #include <netinet/ether.h>
28 #include <netpacket/packet.h>
32 /* We don't expect to see 1000+ seconds delay, unsigned is enough */
33 #define MONOTONIC_US() ((unsigned)monotonic_us())
48 struct sockaddr_ll me
;
49 struct sockaddr_ll he
;
63 #define G (*(struct globals*)&bb_common_bufsiz1)
68 #define sock_fd (G.sock_fd )
69 #define count (G.count )
70 #define last (G.last )
71 #define timeout_us (G.timeout_us)
72 #define start (G.start )
73 #define sent (G.sent )
74 #define brd_sent (G.brd_sent )
75 #define received (G.received )
76 #define brd_recv (G.brd_recv )
77 #define req_recv (G.req_recv )
78 #define INIT_G() do { \
82 // If GNUisms are not available...
83 //static void *mempcpy(void *_dst, const void *_src, int n)
85 // memcpy(_dst, _src, n);
86 // return (char*)_dst + n;
89 static int send_pack(struct in_addr
*src_addr
,
90 struct in_addr
*dst_addr
, struct sockaddr_ll
*ME
,
91 struct sockaddr_ll
*HE
)
94 unsigned char buf
[256];
95 struct arphdr
*ah
= (struct arphdr
*) buf
;
96 unsigned char *p
= (unsigned char *) (ah
+ 1);
98 ah
->ar_hrd
= htons(ARPHRD_ETHER
);
99 ah
->ar_pro
= htons(ETH_P_IP
);
100 ah
->ar_hln
= ME
->sll_halen
;
102 ah
->ar_op
= option_mask32
& ADVERT
? htons(ARPOP_REPLY
) : htons(ARPOP_REQUEST
);
104 p
= mempcpy(p
, &ME
->sll_addr
, ah
->ar_hln
);
105 p
= mempcpy(p
, src_addr
, 4);
107 if (option_mask32
& ADVERT
)
108 p
= mempcpy(p
, &ME
->sll_addr
, ah
->ar_hln
);
110 p
= mempcpy(p
, &HE
->sll_addr
, ah
->ar_hln
);
112 p
= mempcpy(p
, dst_addr
, 4);
114 err
= sendto(sock_fd
, buf
, p
- buf
, 0, (struct sockaddr
*) HE
, sizeof(*HE
));
115 if (err
== p
- buf
) {
116 last
= MONOTONIC_US();
118 if (!(option_mask32
& UNICASTING
))
124 static void finish(void) NORETURN
;
125 static void finish(void)
127 if (!(option_mask32
& QUIET
)) {
128 printf("Sent %u probe(s) (%u broadcast(s))\n"
130 " (%u request(s), %u broadcast(s))\n",
132 received
, (received
== 1) ? "ies" : "y",
135 if (option_mask32
& DAD
)
137 if (option_mask32
& UNSOLICITED
)
142 static void catcher(void)
146 now
= MONOTONIC_US();
150 if (count
== 0 || (timeout_us
&& (now
- start
) > timeout_us
))
153 /* count < 0 means "infinite count" */
157 if (last
== 0 || (now
- last
) > 500000) {
158 send_pack(&src
, &dst
, &me
, &he
);
159 if (count
== 0 && (option_mask32
& UNSOLICITED
))
165 static bool recv_pack(unsigned char *buf
, int len
, struct sockaddr_ll
*FROM
)
167 struct arphdr
*ah
= (struct arphdr
*) buf
;
168 unsigned char *p
= (unsigned char *) (ah
+ 1);
169 struct in_addr src_ip
, dst_ip
;
170 /* moves below assume in_addr is 4 bytes big, ensure that */
171 struct BUG_in_addr_must_be_4
{
172 char BUG_in_addr_must_be_4
[
173 sizeof(struct in_addr
) == 4 ? 1 : -1
175 char BUG_s_addr_must_be_4
[
176 sizeof(src_ip
.s_addr
) == 4 ? 1 : -1
180 /* Filter out wild packets */
181 if (FROM
->sll_pkttype
!= PACKET_HOST
182 && FROM
->sll_pkttype
!= PACKET_BROADCAST
183 && FROM
->sll_pkttype
!= PACKET_MULTICAST
)
186 /* Only these types are recognized */
187 if (ah
->ar_op
!= htons(ARPOP_REQUEST
) && ah
->ar_op
!= htons(ARPOP_REPLY
))
190 /* ARPHRD check and this darned FDDI hack here :-( */
191 if (ah
->ar_hrd
!= htons(FROM
->sll_hatype
)
192 && (FROM
->sll_hatype
!= ARPHRD_FDDI
|| ah
->ar_hrd
!= htons(ARPHRD_ETHER
)))
195 /* Protocol must be IP. */
196 if (ah
->ar_pro
!= htons(ETH_P_IP
)
198 || (ah
->ar_hln
!= me
.sll_halen
)
199 || (len
< (int)(sizeof(*ah
) + 2 * (4 + ah
->ar_hln
))))
202 move_from_unaligned32(src_ip
.s_addr
, p
+ ah
->ar_hln
);
203 move_from_unaligned32(dst_ip
.s_addr
, p
+ ah
->ar_hln
+ 4 + ah
->ar_hln
);
205 if (dst
.s_addr
!= src_ip
.s_addr
)
207 if (!(option_mask32
& DAD
)) {
208 if ((src
.s_addr
!= dst_ip
.s_addr
)
209 || (memcmp(p
+ ah
->ar_hln
+ 4, &me
.sll_addr
, ah
->ar_hln
)))
213 src_ip = 0 (or some src)
215 dst_ip = tested address
218 We fail, if receive request/reply with:
219 src_ip = tested_address
221 if src_ip in request was not zero, check
222 also that it matches to dst_ip, otherwise
223 dst_ip/dst_hw do not matter.
225 if ((memcmp(p
, &me
.sll_addr
, me
.sll_halen
) == 0)
226 || (src
.s_addr
&& src
.s_addr
!= dst_ip
.s_addr
))
229 if (!(option_mask32
& QUIET
)) {
232 printf("%scast re%s from %s [%s]",
233 FROM
->sll_pkttype
== PACKET_HOST
? "Uni" : "Broad",
234 ah
->ar_op
== htons(ARPOP_REPLY
) ? "ply" : "quest",
236 ether_ntoa((struct ether_addr
*) p
));
237 if (dst_ip
.s_addr
!= src
.s_addr
) {
238 printf("for %s ", inet_ntoa(dst_ip
));
241 if (memcmp(p
+ ah
->ar_hln
+ 4, me
.sll_addr
, ah
->ar_hln
)) {
245 ether_ntoa((struct ether_addr
*) p
+ ah
->ar_hln
+ 4));
249 unsigned diff
= MONOTONIC_US() - last
;
250 printf(" %u.%03ums\n", diff
/ 1000, diff
% 1000);
252 printf(" UNSOLICITED?\n");
257 if (FROM
->sll_pkttype
!= PACKET_HOST
)
259 if (ah
->ar_op
== htons(ARPOP_REQUEST
))
261 if (option_mask32
& QUIT_ON_REPLY
)
263 if (!(option_mask32
& BCAST_ONLY
)) {
264 memcpy(he
.sll_addr
, p
, me
.sll_halen
);
265 option_mask32
|= UNICASTING
;
270 int arping_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
271 int arping_main(int argc UNUSED_PARAM
, char **argv
)
273 const char *device
= "eth0";
276 unsigned char *packet
;
281 sock_fd
= xsocket(AF_PACKET
, SOCK_DGRAM
, 0);
283 // Drop suid root privileges
284 // Need to remove SUID_NEVER from applets.h for this to work
287 err_str
= xasprintf("interface %s %%s", device
);
292 /* Dad also sets quit_on_reply.
293 * Advert also sets unsolicited.
295 opt_complementary
= "=1:Df:AU:c+";
296 opt
= getopt32(argv
, "DUAqfbc:w:I:s:",
297 &count
, &str_timeout
, &device
, &source
);
298 if (opt
& 0x80) /* -w: timeout */
299 timeout_us
= xatou_range(str_timeout
, 0, INT_MAX
/2000000) * 1000000 + 500000;
300 //if (opt & 0x200) /* -s: source */
301 option_mask32
&= 0x3f; /* set respective flags */
304 target
= argv
[optind
];
306 xfunc_error_retval
= 2;
311 memset(&ifr
, 0, sizeof(ifr
));
312 strncpy_IFNAMSIZ(ifr
.ifr_name
, device
);
313 /* We use ifr.ifr_name in error msg so that problem
314 * with truncated name will be visible */
315 ioctl_or_perror_and_die(sock_fd
, SIOCGIFINDEX
, &ifr
, err_str
, "not found");
316 me
.sll_ifindex
= ifr
.ifr_ifindex
;
318 xioctl(sock_fd
, SIOCGIFFLAGS
, (char *) &ifr
);
320 if (!(ifr
.ifr_flags
& IFF_UP
)) {
321 bb_error_msg_and_die(err_str
, "is down");
323 if (ifr
.ifr_flags
& (IFF_NOARP
| IFF_LOOPBACK
)) {
324 bb_error_msg(err_str
, "is not ARPable");
325 return (option_mask32
& DAD
? 0 : 2);
329 /* if (!inet_aton(target, &dst)) - not needed */ {
330 len_and_sockaddr
*lsa
;
331 lsa
= xhost_and_af2sockaddr(target
, 0, AF_INET
);
332 dst
= lsa
->u
.sin
.sin_addr
;
333 if (ENABLE_FEATURE_CLEAN_UP
)
337 if (source
&& !inet_aton(source
, &src
)) {
338 bb_error_msg_and_die("invalid source address %s", source
);
341 if ((option_mask32
& (DAD
|UNSOLICITED
)) == UNSOLICITED
&& src
.s_addr
== 0)
344 if (!(option_mask32
& DAD
) || src
.s_addr
) {
345 struct sockaddr_in saddr
;
346 int probe_fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
348 setsockopt_bindtodevice(probe_fd
, device
);
349 memset(&saddr
, 0, sizeof(saddr
));
350 saddr
.sin_family
= AF_INET
;
352 /* Check that this is indeed our IP */
353 saddr
.sin_addr
= src
;
354 xbind(probe_fd
, (struct sockaddr
*) &saddr
, sizeof(saddr
));
355 } else { /* !(option_mask32 & DAD) case */
356 /* Find IP address on this iface */
357 socklen_t alen
= sizeof(saddr
);
359 saddr
.sin_port
= htons(1025);
360 saddr
.sin_addr
= dst
;
362 if (setsockopt(probe_fd
, SOL_SOCKET
, SO_DONTROUTE
, &const_int_1
, sizeof(const_int_1
)) == -1)
363 bb_perror_msg("setsockopt(SO_DONTROUTE)");
364 xconnect(probe_fd
, (struct sockaddr
*) &saddr
, sizeof(saddr
));
365 getsockname(probe_fd
, (struct sockaddr
*) &saddr
, &alen
);
367 //if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == -1)
368 // bb_perror_msg_and_die("getsockname");
369 if (saddr
.sin_family
!= AF_INET
)
370 bb_error_msg_and_die("no IP address configured");
371 src
= saddr
.sin_addr
;
376 me
.sll_family
= AF_PACKET
;
377 //me.sll_ifindex = ifindex; - done before
378 me
.sll_protocol
= htons(ETH_P_ARP
);
379 xbind(sock_fd
, (struct sockaddr
*) &me
, sizeof(me
));
382 socklen_t alen
= sizeof(me
);
383 getsockname(sock_fd
, (struct sockaddr
*) &me
, &alen
);
385 //if (getsockname(sock_fd, (struct sockaddr *) &me, &alen) == -1)
386 // bb_perror_msg_and_die("getsockname");
388 if (me
.sll_halen
== 0) {
389 bb_error_msg(err_str
, "is not ARPable (no ll address)");
390 return (option_mask32
& DAD
? 0 : 2);
393 memset(he
.sll_addr
, -1, he
.sll_halen
);
395 if (!(option_mask32
& QUIET
)) {
396 /* inet_ntoa uses static storage, can't use in same printf */
397 printf("ARPING to %s", inet_ntoa(dst
));
398 printf(" from %s via %s\n", inet_ntoa(src
), device
);
401 signal_SA_RESTART_empty_mask(SIGINT
, (void (*)(int))finish
);
402 signal_SA_RESTART_empty_mask(SIGALRM
, (void (*)(int))catcher
);
406 packet
= xmalloc(4096);
408 sigset_t sset
, osset
;
409 struct sockaddr_ll from
;
410 socklen_t alen
= sizeof(from
);
413 cc
= recvfrom(sock_fd
, packet
, 4096, 0, (struct sockaddr
*) &from
, &alen
);
415 bb_perror_msg("recvfrom");
419 sigaddset(&sset
, SIGALRM
);
420 sigaddset(&sset
, SIGINT
);
421 sigprocmask(SIG_BLOCK
, &sset
, &osset
);
422 recv_pack(packet
, cc
, &from
);
423 sigprocmask(SIG_SETMASK
, &osset
, NULL
);