~arky/ubuntu/lucid/iputils/fix-512227

« back to all changes in this revision

Viewing changes to arping.c

  • Committer: Bazaar Package Importer
  • Author(s): Noah Meyerhans
  • Date: 2002-04-21 02:07:55 UTC
  • Revision ID: james.westby@ubuntu.com-20020421020755-ttbj5lnh9l137vct
Tags: upstream-20020124
ImportĀ upstreamĀ versionĀ 20020124

Show diffs side-by-side

added added

removed removed

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