Ditch the sysvinit stuff
[profile/ivi/iputils.git] / arping.c
1 /*
2  * arping.c
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  * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10  */
11
12 #include <stdlib.h>
13 #include <sys/param.h>
14 #include <sys/socket.h>
15 #include <linux/sockios.h>
16 #include <sys/file.h>
17 #include <sys/time.h>
18 #include <sys/signal.h>
19 #include <sys/ioctl.h>
20 #include <linux/if.h>
21 #include <linux/if_packet.h>
22 #include <linux/if_ether.h>
23 #include <net/if_arp.h>
24 #include <sys/uio.h>
25
26 #include <netdb.h>
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34
35 #include <sysfs/libsysfs.h>
36
37 #include "SNAPSHOT.h"
38
39 static void usage(void) __attribute__((noreturn));
40
41 int quit_on_reply=0;
42 char *device="eth0";
43 int ifindex;
44 char *source;
45 struct in_addr src, dst;
46 char *target;
47 int dad, unsolicited, advert;
48 int quiet;
49 int count=-1;
50 int timeout;
51 int unicasting;
52 int s;
53 int broadcast_only;
54
55 struct sockaddr_storage me;
56 struct sockaddr_storage he;
57
58 struct timeval start, last;
59
60 int sent, brd_sent;
61 int received, brd_recv, req_recv;
62
63 #define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \
64                            ((tv1).tv_usec-(tv2).tv_usec)/1000 )
65
66 #define OFFSET_OF(name,ele)     ((size_t)(((name *)0)->ele))
67
68 static inline socklen_t sll_len(size_t halen)
69 {
70         socklen_t len = OFFSET_OF(struct sockaddr_ll, sll_addr) + halen;
71         if (len < sizeof(struct sockaddr_ll))
72                 len = sizeof(struct sockaddr_ll);
73         return len;
74 }
75
76 #define SLL_LEN(hln)            sll_len(hln)
77
78 void usage(void)
79 {
80         fprintf(stderr,
81                 "Usage: arping [-fqbDUAV] [-c count] [-w timeout] [-I device] [-s source] destination\n"
82                 "  -f : quit on first reply\n"
83                 "  -q : be quiet\n"
84                 "  -b : keep broadcasting, don't go unicast\n"
85                 "  -D : duplicate address detection mode\n"
86                 "  -U : Unsolicited ARP mode, update your neighbours\n"
87                 "  -A : ARP answer mode, update your neighbours\n"
88                 "  -V : print version and exit\n"
89                 "  -c count : how many packets to send\n"
90                 "  -w timeout : how long to wait for a reply\n"
91                 "  -I device : which ethernet device to use (eth0)\n"
92                 "  -s source : source ip address\n"
93                 "  destination : ask for what ip address\n"
94                 );
95         exit(2);
96 }
97
98 void set_signal(int signo, void (*handler)(void))
99 {
100         struct sigaction sa;
101
102         memset(&sa, 0, sizeof(sa));
103         sa.sa_handler = (void (*)(int))handler;
104         sa.sa_flags = SA_RESTART;
105         sigaction(signo, &sa, NULL);
106 }
107
108 int send_pack(int s, struct in_addr src, struct in_addr dst,
109               struct sockaddr_ll *ME, struct sockaddr_ll *HE)
110 {
111         int err;
112         struct timeval now;
113         unsigned char buf[256];
114         struct arphdr *ah = (struct arphdr*)buf;
115         unsigned char *p = (unsigned char *)(ah+1);
116
117         ah->ar_hrd = htons(ME->sll_hatype);
118         if (ah->ar_hrd == htons(ARPHRD_FDDI))
119                 ah->ar_hrd = htons(ARPHRD_ETHER);
120         ah->ar_pro = htons(ETH_P_IP);
121         ah->ar_hln = ME->sll_halen;
122         ah->ar_pln = 4;
123         ah->ar_op  = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
124
125         memcpy(p, &ME->sll_addr, ah->ar_hln);
126         p+=ME->sll_halen;
127
128         memcpy(p, &src, 4);
129         p+=4;
130
131         if (advert)
132                 memcpy(p, &ME->sll_addr, ah->ar_hln);
133         else
134                 memcpy(p, &HE->sll_addr, ah->ar_hln);
135         p+=ah->ar_hln;
136
137         memcpy(p, &dst, 4);
138         p+=4;
139
140         gettimeofday(&now, NULL);
141         err = sendto(s, buf, p-buf, 0, (struct sockaddr*)HE, SLL_LEN(ah->ar_hln));
142         if (err == p-buf) {
143                 last = now;
144                 sent++;
145                 if (!unicasting)
146                         brd_sent++;
147         }
148         return err;
149 }
150
151 void finish(void)
152 {
153         if (!quiet) {
154                 printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent);
155                 printf("Received %d response(s)", received);
156                 if (brd_recv || req_recv) {
157                         printf(" (");
158                         if (req_recv)
159                                 printf("%d request(s)", req_recv);
160                         if (brd_recv)
161                                 printf("%s%d broadcast(s)",
162                                        req_recv ? ", " : "",
163                                        brd_recv);
164                         printf(")");
165                 }
166                 printf("\n");
167                 fflush(stdout);
168         }
169         if (dad)
170                 exit(!!received);
171         if (unsolicited)
172                 exit(0);
173         exit(!received);
174 }
175
176 void catcher(void)
177 {
178         struct timeval tv;
179
180         gettimeofday(&tv, NULL);
181
182         if (start.tv_sec==0)
183                 start = tv;
184
185         if (count-- == 0 || (timeout && MS_TDIFF(tv,start) > timeout*1000 + 500))
186                 finish();
187
188         if (last.tv_sec==0 || MS_TDIFF(tv,last) > 500) {
189                 send_pack(s, src, dst,
190                           (struct sockaddr_ll *)&me, (struct sockaddr_ll *)&he);
191                 if (count == 0 && unsolicited)
192                         finish();
193         }
194         alarm(1);
195 }
196
197 void print_hex(unsigned char *p, int len)
198 {
199         int i;
200         for (i=0; i<len; i++) {
201                 printf("%02X", p[i]);
202                 if (i != len-1)
203                         printf(":");
204         }
205 }
206
207 int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
208 {
209         struct timeval tv;
210         struct arphdr *ah = (struct arphdr*)buf;
211         unsigned char *p = (unsigned char *)(ah+1);
212         struct in_addr src_ip, dst_ip;
213
214         gettimeofday(&tv, NULL);
215
216         /* Filter out wild packets */
217         if (FROM->sll_pkttype != PACKET_HOST &&
218             FROM->sll_pkttype != PACKET_BROADCAST &&
219             FROM->sll_pkttype != PACKET_MULTICAST)
220                 return 0;
221
222         /* Only these types are recognised */
223         if (ah->ar_op != htons(ARPOP_REQUEST) &&
224             ah->ar_op != htons(ARPOP_REPLY))
225                 return 0;
226
227         /* ARPHRD check and this darned FDDI hack here :-( */
228         if (ah->ar_hrd != htons(FROM->sll_hatype) &&
229             (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
230                 return 0;
231
232         /* Protocol must be IP. */
233         if (ah->ar_pro != htons(ETH_P_IP))
234                 return 0;
235         if (ah->ar_pln != 4)
236                 return 0;
237         if (ah->ar_hln != ((struct sockaddr_ll *)&me)->sll_halen)
238                 return 0;
239         if (len < sizeof(*ah) + 2*(4 + ah->ar_hln))
240                 return 0;
241         memcpy(&src_ip, p+ah->ar_hln, 4);
242         memcpy(&dst_ip, p+ah->ar_hln+4+ah->ar_hln, 4);
243         if (!dad) {
244                 if (src_ip.s_addr != dst.s_addr)
245                         return 0;
246                 if (src.s_addr != dst_ip.s_addr)
247                         return 0;
248                 if (memcmp(p+ah->ar_hln+4, ((struct sockaddr_ll *)&me)->sll_addr, ah->ar_hln))
249                         return 0;
250         } else {
251                 /* DAD packet was:
252                    src_ip = 0 (or some src)
253                    src_hw = ME
254                    dst_ip = tested address
255                    dst_hw = <unspec>
256
257                    We fail, if receive request/reply with:
258                    src_ip = tested_address
259                    src_hw != ME
260                    if src_ip in request was not zero, check
261                    also that it matches to dst_ip, otherwise
262                    dst_ip/dst_hw do not matter.
263                  */
264                 if (src_ip.s_addr != dst.s_addr)
265                         return 0;
266                 if (memcmp(p, ((struct sockaddr_ll *)&me)->sll_addr, ((struct sockaddr_ll *)&me)->sll_halen) == 0)
267                         return 0;
268                 if (src.s_addr && src.s_addr != dst_ip.s_addr)
269                         return 0;
270         }
271         if (!quiet) {
272                 int s_printed = 0;
273                 printf("%s ", FROM->sll_pkttype==PACKET_HOST ? "Unicast" : "Broadcast");
274                 printf("%s from ", ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request");
275                 printf("%s [", inet_ntoa(src_ip));
276                 print_hex(p, ah->ar_hln);
277                 printf("] ");
278                 if (dst_ip.s_addr != src.s_addr) {
279                         printf("for %s ", inet_ntoa(dst_ip));
280                         s_printed = 1;
281                 }
282                 if (memcmp(p+ah->ar_hln+4, ((struct sockaddr_ll *)&me)->sll_addr, ah->ar_hln)) {
283                         if (!s_printed)
284                                 printf("for ");
285                         printf("[");
286                         print_hex(p+ah->ar_hln+4, ah->ar_hln);
287                         printf("]");
288                 }
289                 if (last.tv_sec) {
290                         long usecs = (tv.tv_sec-last.tv_sec) * 1000000 +
291                                 tv.tv_usec-last.tv_usec;
292                         long msecs = (usecs+500)/1000;
293                         usecs -= msecs*1000 - 500;
294                         printf(" %ld.%03ldms\n", msecs, usecs);
295                 } else {
296                         printf(" UNSOLICITED?\n");
297                 }
298                 fflush(stdout);
299         }
300         received++;
301         if (FROM->sll_pkttype != PACKET_HOST)
302                 brd_recv++;
303         if (ah->ar_op == htons(ARPOP_REQUEST))
304                 req_recv++;
305         if (quit_on_reply)
306                 finish();
307         if(!broadcast_only) {
308                 memcpy(((struct sockaddr_ll *)&he)->sll_addr, p, ((struct sockaddr_ll *)&me)->sll_halen);
309                 unicasting=1;
310         }
311         return 1;
312 }
313
314 void set_device_broadcast(char *device, unsigned char *ba, size_t balen)
315 {
316         struct sysfs_class_device *dev;
317         struct sysfs_attribute *brdcast;
318         unsigned char *p;
319         int ch;
320
321         dev = sysfs_open_class_device("net", device);
322         if (!dev) {
323                 perror("sysfs_open_class_device(net)");
324                 exit(2);
325         }
326
327         brdcast = sysfs_get_classdev_attr(dev, "broadcast");
328         if (!brdcast) {
329                 perror("sysfs_get_classdev_attr(broadcast)");
330                 exit(2);
331         }
332
333         if (sysfs_read_attribute(brdcast)) {
334                 perror("sysfs_read_attribute");
335                 exit(2);
336         }
337
338         for (p = ba, ch = 0; p < ba + balen; p++, ch += 3)
339                 *p = strtoul(brdcast->value + ch, NULL, 16);
340
341         return;
342 }
343
344 int
345 main(int argc, char **argv)
346 {
347         int socket_errno;
348         int ch;
349         uid_t uid = getuid();
350
351         s = socket(PF_PACKET, SOCK_DGRAM, 0);
352         socket_errno = errno;
353
354         if (setuid(uid)) {
355                 perror("arping: setuid");
356                 exit(-1);
357         }
358
359         while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:V")) != EOF) {
360                 switch(ch) {
361                 case 'b':
362                         broadcast_only=1;
363                         break;
364                 case 'D':
365                         dad++;
366                         quit_on_reply=1;
367                         break;
368                 case 'U':
369                         unsolicited++;
370                         break;
371                 case 'A':
372                         advert++;
373                         unsolicited++;
374                         break;
375                 case 'q':
376                         quiet++;
377                         break;
378                 case 'c':
379                         count = atoi(optarg);
380                         break;
381                 case 'w':
382                         timeout = atoi(optarg);
383                         break;
384                 case 'I':
385                         device = optarg;
386                         break;
387                 case 'f':
388                         quit_on_reply=1;
389                         break;
390                 case 's':
391                         source = optarg;
392                         break;
393                 case 'V':
394                         printf("arping utility, iputils-ss%s\n", SNAPSHOT);
395                         exit(0);
396                 case 'h':
397                 case '?':
398                 default:
399                         usage();
400                 }
401         }
402         argc -= optind;
403         argv += optind;
404
405         if (argc != 1)
406                 usage();
407
408         target = *argv;
409
410         if (device == NULL) {
411                 fprintf(stderr, "arping: device (option -I) is required\n");
412                 usage();
413         }
414
415         if (s < 0) {
416                 errno = socket_errno;
417                 perror("arping: socket");
418                 exit(2);
419         }
420
421         if (1) {
422                 struct ifreq ifr;
423                 memset(&ifr, 0, sizeof(ifr));
424                 strncpy(ifr.ifr_name, device, IFNAMSIZ-1);
425                 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
426                         fprintf(stderr, "arping: unknown iface %s\n", device);
427                         exit(2);
428                 }
429                 ifindex = ifr.ifr_ifindex;
430
431                 if (ioctl(s, SIOCGIFFLAGS, (char*)&ifr)) {
432                         perror("ioctl(SIOCGIFFLAGS)");
433                         exit(2);
434                 }
435                 if (!(ifr.ifr_flags&IFF_UP)) {
436                         if (!quiet)
437                                 printf("Interface \"%s\" is down\n", device);
438                         exit(2);
439                 }
440                 if (ifr.ifr_flags&(IFF_NOARP|IFF_LOOPBACK)) {
441                         if (!quiet)
442                                 printf("Interface \"%s\" is not ARPable\n", device);
443                         exit(dad?0:2);
444                 }
445         }
446
447         if (inet_aton(target, &dst) != 1) {
448                 struct hostent *hp;
449                 hp = gethostbyname2(target, AF_INET);
450                 if (!hp) {
451                         fprintf(stderr, "arping: unknown host %s\n", target);
452                         exit(2);
453                 }
454                 memcpy(&dst, hp->h_addr, 4);
455         }
456
457         if (source && inet_aton(source, &src) != 1) {
458                 fprintf(stderr, "arping: invalid source %s\n", source);
459                 exit(2);
460         }
461
462         if (!dad && unsolicited && src.s_addr == 0)
463                 src = dst;
464
465         if (!dad || src.s_addr) {
466                 struct sockaddr_in saddr;
467                 int probe_fd = socket(AF_INET, SOCK_DGRAM, 0);
468
469                 if (probe_fd < 0) {
470                         perror("socket");
471                         exit(2);
472                 }
473                 if (device) {
474                         if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1)
475                                 perror("WARNING: interface is ignored");
476                 }
477                 memset(&saddr, 0, sizeof(saddr));
478                 saddr.sin_family = AF_INET;
479                 if (src.s_addr) {
480                         saddr.sin_addr = src;
481                         if (bind(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
482                                 perror("bind");
483                                 exit(2);
484                         }
485                 } else if (!dad) {
486                         int on = 1;
487                         socklen_t alen = sizeof(saddr);
488
489                         saddr.sin_port = htons(1025);
490                         saddr.sin_addr = dst;
491
492                         if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, (char*)&on, sizeof(on)) == -1)
493                                 perror("WARNING: setsockopt(SO_DONTROUTE)");
494                         if (connect(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
495                                 perror("connect");
496                                 exit(2);
497                         }
498                         if (getsockname(probe_fd, (struct sockaddr*)&saddr, &alen) == -1) {
499                                 perror("getsockname");
500                                 exit(2);
501                         }
502                         src = saddr.sin_addr;
503                 }
504                 close(probe_fd);
505         };
506
507         ((struct sockaddr_ll *)&me)->sll_family = AF_PACKET;
508         ((struct sockaddr_ll *)&me)->sll_ifindex = ifindex;
509         ((struct sockaddr_ll *)&me)->sll_protocol = htons(ETH_P_ARP);
510         if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
511                 perror("bind");
512                 exit(2);
513         }
514
515         if (1) {
516                 socklen_t alen = sizeof(me);
517                 if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
518                         perror("getsockname");
519                         exit(2);
520                 }
521         }
522         if (((struct sockaddr_ll *)&me)->sll_halen == 0) {
523                 if (!quiet)
524                         printf("Interface \"%s\" is not ARPable (no ll address)\n", device);
525                 exit(dad?0:2);
526         }
527
528         he = me;
529
530 #if 1
531         set_device_broadcast(device, ((struct sockaddr_ll *)&he)->sll_addr,
532                              ((struct sockaddr_ll *)&he)->sll_halen);
533 #else
534         memset(((struct sockaddr_ll *)&he)->sll_addr, -1, ((struct sockaddr_ll *)&he)->sll_halen);
535 #endif
536
537         if (!quiet) {
538                 printf("ARPING %s ", inet_ntoa(dst));
539                 printf("from %s %s\n",  inet_ntoa(src), device ? : "");
540         }
541
542         if (!src.s_addr && !dad) {
543                 fprintf(stderr, "arping: no source address in not-DAD mode\n");
544                 exit(2);
545         }
546
547         set_signal(SIGINT, finish);
548         set_signal(SIGALRM, catcher);
549
550         catcher();
551
552         while(1) {
553                 sigset_t sset, osset;
554                 unsigned char packet[4096];
555                 struct sockaddr_storage from;
556                 socklen_t alen = sizeof(from);
557                 int cc;
558
559                 if ((cc = recvfrom(s, packet, sizeof(packet), 0,
560                                    (struct sockaddr *)&from, &alen)) < 0) {
561                         perror("arping: recvfrom");
562                         continue;
563                 }
564
565                 sigemptyset(&sset);
566                 sigaddset(&sset, SIGALRM);
567                 sigaddset(&sset, SIGINT);
568                 sigprocmask(SIG_BLOCK, &sset, &osset);
569                 recv_pack(packet, cc, (struct sockaddr_ll *)&from);
570                 sigprocmask(SIG_SETMASK, &osset, NULL);
571         }
572 }
573
574