1 /* vi: set sw=4 ts=4: */
3 * arping.c - Ping hosts by ARP requests/replies
5 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
7 * Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
8 * Busybox port: Nick Fedchik <nick@fedchik.org.ua>
11 #include <arpa/inet.h>
13 #include <netinet/ether.h>
14 #include <netpacket/packet.h>
18 /* We don't expect to see 1000+ seconds delay, unsigned is enough */
19 #define MONOTONIC_US() ((unsigned)monotonic_us())
21 static struct in_addr src;
22 static struct in_addr dst;
23 static struct sockaddr_ll me;
24 static struct sockaddr_ll he;
39 static unsigned count = UINT_MAX;
40 static unsigned timeout_us;
47 static int send_pack(int sock, struct in_addr *src_addr,
48 struct in_addr *dst_addr, struct sockaddr_ll *ME,
49 struct sockaddr_ll *HE)
53 unsigned char buf[256];
54 struct arphdr *ah = (struct arphdr *) buf;
55 unsigned char *p = (unsigned char *) (ah + 1);
57 ah->ar_hrd = htons(ME->sll_hatype);
58 ah->ar_hrd = htons(ARPHRD_ETHER);
59 ah->ar_pro = htons(ETH_P_IP);
60 ah->ar_hln = ME->sll_halen;
62 ah->ar_op = cfg & ADVERT ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
64 memcpy(p, &ME->sll_addr, ah->ar_hln);
67 memcpy(p, src_addr, 4);
71 memcpy(p, &ME->sll_addr, ah->ar_hln);
73 memcpy(p, &HE->sll_addr, ah->ar_hln);
76 memcpy(p, dst_addr, 4);
80 err = sendto(sock, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
84 if (!(cfg & UNICASTING))
90 static void finish(void) ATTRIBUTE_NORETURN;
91 static void finish(void)
94 printf("Sent %d probe(s) (%d broadcast(s))\n"
96 " (%d request(s), %d broadcast(s))\n",
98 received, (received == 1) ? "ies" : "y",
103 if (cfg & UNSOLICITED)
108 static void catcher(void)
110 static unsigned start;
114 now = MONOTONIC_US();
118 if (count == 0 || (timeout_us && (now - start) > (timeout_us + 500000)))
123 if (last == 0 || (now - last) > 500000) {
124 send_pack(s, &src, &dst, &me, &he);
125 if (count == 0 && (cfg & UNSOLICITED))
131 static int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
133 struct arphdr *ah = (struct arphdr *) buf;
134 unsigned char *p = (unsigned char *) (ah + 1);
135 struct in_addr src_ip, dst_ip;
137 /* Filter out wild packets */
138 if (FROM->sll_pkttype != PACKET_HOST
139 && FROM->sll_pkttype != PACKET_BROADCAST
140 && FROM->sll_pkttype != PACKET_MULTICAST)
143 /* Only these types are recognised */
144 if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
147 /* ARPHRD check and this darned FDDI hack here :-( */
148 if (ah->ar_hrd != htons(FROM->sll_hatype)
149 && (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
152 /* Protocol must be IP. */
153 if (ah->ar_pro != htons(ETH_P_IP))
157 if (ah->ar_hln != me.sll_halen)
159 if (len < sizeof(*ah) + 2 * (4 + ah->ar_hln))
161 memcpy(&src_ip, p + ah->ar_hln, 4);
162 memcpy(&dst_ip, p + ah->ar_hln + 4 + ah->ar_hln, 4);
164 if (src_ip.s_addr != dst.s_addr)
166 if (src.s_addr != dst_ip.s_addr)
168 if (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln))
172 src_ip = 0 (or some src)
174 dst_ip = tested address
177 We fail, if receive request/reply with:
178 src_ip = tested_address
180 if src_ip in request was not zero, check
181 also that it matches to dst_ip, otherwise
182 dst_ip/dst_hw do not matter.
184 if (src_ip.s_addr != dst.s_addr)
186 if (memcmp(p, &me.sll_addr, me.sll_halen) == 0)
188 if (src.s_addr && src.s_addr != dst_ip.s_addr)
191 if (!(cfg & QUIET)) {
194 printf("%scast re%s from %s [%s]",
195 FROM->sll_pkttype == PACKET_HOST ? "Uni" : "Broad",
196 ah->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest",
198 ether_ntoa((struct ether_addr *) p));
199 if (dst_ip.s_addr != src.s_addr) {
200 printf("for %s ", inet_ntoa(dst_ip));
203 if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
207 ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4));
211 printf(" %u.%03ums\n", last / 1000, last % 1000);
213 printf(" UNSOLICITED?\n");
218 if (FROM->sll_pkttype != PACKET_HOST)
220 if (ah->ar_op == htons(ARPOP_REQUEST))
222 if (cfg & QUIT_ON_REPLY)
224 if (!(cfg & BCAST_ONLY)) {
225 memcpy(he.sll_addr, p, me.sll_halen);
231 int arping_main(int argc, char **argv);
232 int arping_main(int argc, char **argv)
234 const char *device = "eth0";
238 unsigned char *packet;
240 s = xsocket(PF_PACKET, SOCK_DGRAM, 0);
242 // Drop suid root privileges
247 char *_count, *_timeout;
249 /* Dad also sets quit_on_reply.
250 * Advert also sets unsolicited.
252 opt_complementary = "=1:Df:AU";
253 opt = getopt32(argc, argv, "DUAqfbc:w:I:s:",
254 &_count, &_timeout, &device, &source);
255 cfg |= opt & 0x3f; /* set respective flags */
256 if (opt & 0x40) /* -c: count */
257 count = xatou(_count);
258 if (opt & 0x80) /* -w: timeout */
259 timeout_us = xatou_range(_timeout, 0, INT_MAX/2000000) * 1000000;
260 //if (opt & 0x100) /* -I: interface */
261 if (strlen(device) >= IF_NAMESIZE) {
262 bb_error_msg_and_die("interface name '%s' is too long",
265 //if (opt & 0x200) /* -s: source */
268 target = argv[optind];
270 xfunc_error_retval = 2;
275 memset(&ifr, 0, sizeof(ifr));
276 strncpy(ifr.ifr_name, device, IFNAMSIZ - 1);
277 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
278 bb_error_msg_and_die("interface %s not found", device);
280 ifindex = ifr.ifr_ifindex;
282 if (ioctl(s, SIOCGIFFLAGS, (char *) &ifr)) {
283 bb_error_msg_and_die("SIOCGIFFLAGS");
285 if (!(ifr.ifr_flags & IFF_UP)) {
286 bb_error_msg_and_die("interface %s is down", device);
288 if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
289 bb_error_msg("interface %s is not ARPable", device);
290 return (cfg & DAD ? 0 : 2);
294 if (!inet_aton(target, &dst)) {
295 len_and_sockaddr *lsa;
296 lsa = xhost_and_af2sockaddr(target, 0, AF_INET);
297 memcpy(&dst, &lsa->sin.sin_addr.s_addr, 4);
298 if (ENABLE_FEATURE_CLEAN_UP)
302 if (source && !inet_aton(source, &src)) {
303 bb_error_msg_and_die("invalid source address %s", source);
306 if (!(cfg & DAD) && (cfg & UNSOLICITED) && src.s_addr == 0)
309 if (!(cfg & DAD) || src.s_addr) {
310 struct sockaddr_in saddr;
311 int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0);
314 if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) + 1) == -1)
315 bb_error_msg("warning: interface %s is ignored", device);
317 memset(&saddr, 0, sizeof(saddr));
318 saddr.sin_family = AF_INET;
320 saddr.sin_addr = src;
321 xbind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
322 } else if (!(cfg & DAD)) {
323 socklen_t alen = sizeof(saddr);
325 saddr.sin_port = htons(1025);
326 saddr.sin_addr = dst;
328 if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, &const_int_1, sizeof(const_int_1)) == -1)
329 bb_perror_msg("warning: setsockopt(SO_DONTROUTE)");
330 xconnect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
331 if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == -1) {
332 bb_error_msg_and_die("getsockname");
334 src = saddr.sin_addr;
339 me.sll_family = AF_PACKET;
340 me.sll_ifindex = ifindex;
341 me.sll_protocol = htons(ETH_P_ARP);
342 xbind(s, (struct sockaddr *) &me, sizeof(me));
345 socklen_t alen = sizeof(me);
347 if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) {
348 bb_error_msg_and_die("getsockname");
351 if (me.sll_halen == 0) {
352 bb_error_msg("interface \"%s\" is not ARPable (no ll address)", device);
353 return (cfg & DAD ? 0 : 2);
356 memset(he.sll_addr, -1, he.sll_halen);
358 if (!(cfg & QUIET)) {
359 printf("ARPING to %s from %s via %s\n",
360 inet_ntoa(dst), inet_ntoa(src),
361 device ? device : "unknown");
364 if (!src.s_addr && !(cfg & DAD)) {
365 bb_error_msg_and_die("no src address in the non-DAD mode");
371 memset(&sa, 0, sizeof(sa));
372 sa.sa_flags = SA_RESTART;
374 sa.sa_handler = (void (*)(int)) finish;
375 sigaction(SIGINT, &sa, NULL);
377 sa.sa_handler = (void (*)(int)) catcher;
378 sigaction(SIGALRM, &sa, NULL);
383 packet = xmalloc(4096);
385 sigset_t sset, osset;
386 struct sockaddr_ll from;
387 socklen_t alen = sizeof(from);
390 cc = recvfrom(s, packet, 4096, 0, (struct sockaddr *) &from, &alen);
392 bb_perror_msg("recvfrom");
396 sigaddset(&sset, SIGALRM);
397 sigaddset(&sset, SIGINT);
398 sigprocmask(SIG_BLOCK, &sset, &osset);
399 recv_pack(packet, cc, &from);
400 sigprocmask(SIG_SETMASK, &osset, NULL);