90a03a58e4a0e5976b9083eefcbf4c335c4f5d46
[oweals/busybox.git] / networking / arping.c
1 /* vi:set ts=4:*/
2 /*
3  * arping.c - Ping hosts by ARP requests/replies
4  *
5  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
6  *
7  * Author:      Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
8  * Busybox port: Nick Fedchik <nick@fedchik.org.ua>
9  */
10
11 #include <sys/ioctl.h>
12 #include <signal.h>
13 #include <sys/time.h>
14
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19
20 #include <arpa/inet.h>
21 #include <net/if.h>
22 #include <netinet/ether.h>
23 #include <netpacket/packet.h>
24
25 #include "busybox.h"
26
27 static struct in_addr src;
28 static struct in_addr dst;
29 static struct sockaddr_ll me;
30 static struct sockaddr_ll he;
31 static struct timeval last;
32 struct cfg_s {
33         int dad:1;
34         int unsolicited:1;
35         int advert:1;
36         int quiet:1;
37         int quit_on_reply:1;
38         int unicasting:1;
39         int broadcast_only:1;
40 };
41 static struct cfg_s cfg;
42
43 static int s;
44 static int count = -1;
45 static int timeout;
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
53 #define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \
54                            ((tv1).tv_usec-(tv2).tv_usec)/1000 )
55 #if 0
56 static void set_signal(int signo, void (*handler) (void))
57 {
58         struct sigaction sa;
59
60         memset(&sa, 0, sizeof(sa));
61         sa.sa_handler = (void (*)(int)) handler;
62         sa.sa_flags = SA_RESTART;
63         sigaction(signo, &sa, NULL);
64 }
65 #endif
66
67 static int send_pack(int sock, struct in_addr *src_addr,
68                                          struct in_addr *dst_addr, struct sockaddr_ll *ME,
69                                          struct sockaddr_ll *HE)
70 {
71         int err;
72         struct timeval now;
73         RESERVE_CONFIG_UBUFFER(buf, 256);
74         struct arphdr *ah = (struct arphdr *) buf;
75         unsigned char *p = (unsigned char *) (ah + 1);
76
77         ah->ar_hrd = htons(ME->sll_hatype);
78         ah->ar_hrd = htons(ARPHRD_ETHER);
79         ah->ar_pro = htons(ETH_P_IP);
80         ah->ar_hln = ME->sll_halen;
81         ah->ar_pln = 4;
82         ah->ar_op = cfg.advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
83
84         memcpy(p, &ME->sll_addr, ah->ar_hln);
85         p += ME->sll_halen;
86
87         memcpy(p, src_addr, 4);
88         p += 4;
89
90         if (cfg.advert)
91                 memcpy(p, &ME->sll_addr, ah->ar_hln);
92         else
93                 memcpy(p, &HE->sll_addr, ah->ar_hln);
94         p += ah->ar_hln;
95
96         memcpy(p, dst_addr, 4);
97         p += 4;
98
99         gettimeofday(&now, NULL);
100         err = sendto(sock, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
101         if (err == p - buf) {
102                 last = now;
103                 sent++;
104                 if (!cfg.unicasting)
105                         brd_sent++;
106         }
107         RELEASE_CONFIG_BUFFER(buf);
108         return err;
109 }
110
111 static void finish(void)
112 {
113         if (!cfg.quiet) {
114                 printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent);
115                 printf("Received %d repl%s", received, (received > 1) ? "ies" : "y");
116                 if (brd_recv || req_recv) {
117                         printf(" (");
118                         if (req_recv)
119                                 printf("%d request(s)", req_recv);
120                         if (brd_recv)
121                                 printf("%s%d broadcast(s)", req_recv ? ", " : "", brd_recv);
122                         putchar(')');
123                 }
124                 putchar('\n');
125                 fflush(stdout);
126         }
127         if (cfg.dad)
128                 exit(!!received);
129         if (cfg.unsolicited)
130                 exit(0);
131         exit(!received);
132 }
133
134 static void catcher(void)
135 {
136         struct timeval tv;
137         static struct timeval start;
138
139         gettimeofday(&tv, NULL);
140
141         if (start.tv_sec == 0)
142                 start = tv;
143
144         if (count-- == 0
145                 || (timeout && MS_TDIFF(tv, start) > timeout * 1000 + 500))
146                 finish();
147
148         if (last.tv_sec == 0 || MS_TDIFF(tv, last) > 500) {
149                 send_pack(s, &src, &dst, &me, &he);
150                 if (count == 0 && cfg.unsolicited)
151                         finish();
152         }
153         alarm(1);
154 }
155
156 static int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
157 {
158         struct arphdr *ah = (struct arphdr *) buf;
159         unsigned char *p = (unsigned char *) (ah + 1);
160         struct in_addr src_ip, dst_ip;
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 (!cfg.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 (!cfg.quiet) {
218                 int s_printed = 0;
219                 struct timeval tv;
220
221                 gettimeofday(&tv, NULL);
222
223                 printf("%s ",
224                            FROM->sll_pkttype == PACKET_HOST ? "Unicast" : "Broadcast");
225                 printf("%s from ",
226                            ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request");
227                 printf("%s ", inet_ntoa(src_ip));
228                 printf("[%s]", ether_ntoa((struct ether_addr *) p));
229                 if (dst_ip.s_addr != src.s_addr) {
230                         printf("for %s ", inet_ntoa(dst_ip));
231                         s_printed = 1;
232                 }
233                 if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
234                         if (!s_printed)
235                                 printf("for ");
236                         printf("[%s]",
237                                    ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4));
238                 }
239
240                 if (last.tv_sec) {
241                         long usecs = (tv.tv_sec - last.tv_sec) * 1000000 +
242                                 tv.tv_usec - last.tv_usec;
243                         long msecs = (usecs + 500) / 1000;
244
245                         usecs -= msecs * 1000 - 500;
246                         printf(" %ld.%03ldms\n", msecs, usecs);
247                 } else {
248                         printf(" UNSOLICITED?\n");
249                 }
250                 fflush(stdout);
251         }
252         received++;
253         if (FROM->sll_pkttype != PACKET_HOST)
254                 brd_recv++;
255         if (ah->ar_op == htons(ARPOP_REQUEST))
256                 req_recv++;
257         if (cfg.quit_on_reply)
258                 finish();
259         if (!cfg.broadcast_only) {
260                 memcpy(he.sll_addr, p, me.sll_halen);
261                 cfg.unicasting = 1;
262         }
263         return 1;
264 }
265
266 int arping_main(int argc, char **argv)
267 {
268         int ch;
269         char *device = "eth0";
270         int ifindex;
271         char *source = NULL;
272         char *target;
273
274         s = socket(PF_PACKET, SOCK_DGRAM, 0);
275         ifindex = errno;
276
277         setuid(getuid());
278
279         while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:")) != EOF) {
280                 switch (ch) {
281                 case 'b':
282                         cfg.broadcast_only = 1;
283                         break;
284                 case 'D':
285                         cfg.dad = 1;
286                         cfg.quit_on_reply = 1;
287                         break;
288                 case 'U':
289                         cfg.unsolicited = 1;
290                         break;
291                 case 'A':
292                         cfg.advert = 1;
293                         cfg.unsolicited = 1;
294                         break;
295                 case 'q':
296                         cfg.quiet = 1;
297                         break;
298                 case 'c':
299                         count = atoi(optarg);
300                         break;
301                 case 'w':
302                         timeout = atoi(optarg);
303                         break;
304                 case 'I':
305                         if (optarg == NULL)
306                                 bb_show_usage();
307                         if (bb_strlen(optarg) > IF_NAMESIZE) {
308                                 bb_error_msg_and_die("Interface name `%s' must be less than %d",
309                                                                 optarg, IF_NAMESIZE);
310                         }
311                         device = optarg;
312                         break;
313                 case 'f':
314                         cfg.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         bb_default_error_retval = 2;
333
334         if (s < 0) {
335                 bb_perror_msg("socket");
336                 exit(ifindex);
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_and_die("Interface %s not found", device);
346                 }
347                 ifindex = ifr.ifr_ifindex;
348
349                 if (ioctl(s, SIOCGIFFLAGS, (char *) &ifr)) {
350                         bb_error_msg_and_die("SIOCGIFFLAGS");
351                 }
352                 if (!(ifr.ifr_flags & IFF_UP)) {
353                         bb_error_msg_and_die("Interface %s is down", device);
354                 }
355                 if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
356                         bb_error_msg("Interface %s is not ARPable", device);
357                         exit(cfg.dad ? 0 : 2);
358                 }
359         }
360
361         if (!inet_aton(target, &dst)) {
362                 struct hostent *hp;
363
364                 hp = gethostbyname2(target, AF_INET);
365                 if (!hp) {
366                         bb_error_msg_and_die("invalid or unknown target %s", target);
367                 }
368                 memcpy(&dst, hp->h_addr, 4);
369         }
370
371         if (source && !inet_aton(source, &src)) {
372                 bb_error_msg_and_die("invalid source address %s", source);
373         }
374
375         if (!cfg.dad && cfg.unsolicited && src.s_addr == 0)
376                 src = dst;
377
378         if (!cfg.dad || src.s_addr) {
379                 struct sockaddr_in saddr;
380                 int probe_fd = socket(AF_INET, SOCK_DGRAM, 0);
381
382                 if (probe_fd < 0) {
383                         bb_error_msg_and_die("socket");
384                 }
385                 if (device) {
386                         if (setsockopt
387                                 (probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device,
388                                  strlen(device) + 1) == -1)
389                                 bb_error_msg("WARNING: interface %s is ignored", device);
390                 }
391                 memset(&saddr, 0, sizeof(saddr));
392                 saddr.sin_family = AF_INET;
393                 if (src.s_addr) {
394                         saddr.sin_addr = src;
395                         if (bind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) {
396                                 bb_error_msg_and_die("bind");
397                         }
398                 } else if (!cfg.dad) {
399                         int on = 1;
400                         socklen_t alen = sizeof(saddr);
401
402                         saddr.sin_port = htons(1025);
403                         saddr.sin_addr = dst;
404
405                         if (setsockopt
406                                 (probe_fd, SOL_SOCKET, SO_DONTROUTE, (char *) &on,
407                                  sizeof(on)) == -1)
408                                 bb_perror_msg("WARNING: setsockopt(SO_DONTROUTE)");
409                         if (connect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr))
410                                 == -1) {
411                                 bb_error_msg_and_die("connect");
412                         }
413                         if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) ==
414                                 -1) {
415                                 bb_error_msg_and_die("getsockname");
416                         }
417                         src = saddr.sin_addr;
418                 }
419                 close(probe_fd);
420         };
421
422         me.sll_family = AF_PACKET;
423         me.sll_ifindex = ifindex;
424         me.sll_protocol = htons(ETH_P_ARP);
425         if (bind(s, (struct sockaddr *) &me, sizeof(me)) == -1) {
426                 bb_error_msg_and_die("bind");
427         }
428
429         {
430                 socklen_t alen = sizeof(me);
431
432                 if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) {
433                         bb_error_msg_and_die("getsockname");
434                 }
435         }
436         if (me.sll_halen == 0) {
437                 bb_error_msg("Interface \"%s\" is not ARPable (no ll address)", device);
438                 exit(cfg.dad ? 0 : 2);
439         }
440         he = me;
441         memset(he.sll_addr, -1, he.sll_halen);
442
443         if (!cfg.quiet) {
444                 printf("ARPING to %s", inet_ntoa(dst));
445                 printf(" from %s via %s\n", inet_ntoa(src),
446                            device ? device : "unknown");
447         }
448
449         if (!src.s_addr && !cfg.dad) {
450                 bb_error_msg_and_die("no src address in the non-DAD mode");
451         }
452
453         {
454                 struct sigaction sa;
455
456                 memset(&sa, 0, sizeof(sa));
457                 sa.sa_flags = SA_RESTART;
458
459                 sa.sa_handler = (void (*)(int)) finish;
460                 sigaction(SIGINT, &sa, NULL);
461
462                 sa.sa_handler = (void (*)(int)) catcher;
463                 sigaction(SIGALRM, &sa, NULL);
464         }
465
466         catcher();
467
468         while (1) {
469                 sigset_t sset, osset;
470                 unsigned char packet[4096];
471                 struct sockaddr_ll from;
472                 socklen_t alen = sizeof(from);
473                 int cc;
474
475                 if ((cc = recvfrom(s, packet, sizeof(packet), 0,
476                                                    (struct sockaddr *) &from, &alen)) < 0) {
477                         perror("recvfrom");
478                         continue;
479                 }
480                 sigemptyset(&sset);
481                 sigaddset(&sset, SIGALRM);
482                 sigaddset(&sset, SIGINT);
483                 sigprocmask(SIG_BLOCK, &sset, &osset);
484                 recv_pack(packet, cc, &from);
485                 sigprocmask(SIG_SETMASK, &osset, NULL);
486         }
487 }