~ubuntu-branches/debian/stretch/ndisc6/stretch

« back to all changes in this revision

Viewing changes to src/ndisc.c

  • Committer: Bazaar Package Importer
  • Author(s): Rémi Denis-Courmont
  • Date: 2006-10-08 20:21:18 UTC
  • Revision ID: james.westby@ubuntu.com-20061008202118-zil2ffq4mjo3xcbk
Tags: upstream-0.7.3
Import upstream version 0.7.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * ndisc.c - ICMPv6 neighbour discovery command line tool
 
3
 * $Id: ndisc.c 355 2006-10-07 15:02:56Z remi $
 
4
 */
 
5
 
 
6
/***********************************************************************
 
7
 *  Copyright (C) 2004-2006 Rémi Denis-Courmont.                       *
 
8
 *  This program is free software; you can redistribute and/or modify  *
 
9
 *  it under the terms of the GNU General Public License as published  *
 
10
 *  by the Free Software Foundation; version 2 of the license.         *
 
11
 *                                                                     *
 
12
 *  This program is distributed in the hope that it will be useful,    *
 
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of     *
 
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               *
 
15
 *  See the GNU General Public License for more details.               *
 
16
 *                                                                     *
 
17
 *  You should have received a copy of the GNU General Public License  *
 
18
 *  along with this program; if not, you can get it from:              *
 
19
 *  http://www.gnu.org/copyleft/gpl.html                               *
 
20
 ***********************************************************************/
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
# include <config.h>
 
24
#endif
 
25
 
 
26
#include <stdio.h>
 
27
#include <string.h>
 
28
#include <stdlib.h> /* div() */
 
29
#include <inttypes.h>
 
30
#include <limits.h> /* UINT_MAX */
 
31
#include <locale.h>
 
32
 
 
33
#include <errno.h> /* EMFILE */
 
34
#include <sys/types.h>
 
35
#include <unistd.h> /* close() */
 
36
#include <time.h> /* clock_gettime() */
 
37
#include <poll.h> /* poll() */
 
38
#include <sys/socket.h>
 
39
#include <sys/uio.h>
 
40
#include <fcntl.h>
 
41
 
 
42
#include "gettime.h"
 
43
 
 
44
#ifdef HAVE_GETOPT_H
 
45
# include <getopt.h>
 
46
#endif
 
47
 
 
48
#include <netdb.h> /* getaddrinfo() */
 
49
#include <arpa/inet.h> /* inet_ntop() */
 
50
#include <net/if.h> /* if_nametoindex() */
 
51
 
 
52
#include <netinet/in.h>
 
53
#include <netinet/icmp6.h>
 
54
 
 
55
#ifndef IPV6_RECVHOPLIMIT
 
56
/* Using obsolete RFC 2292 instead of RFC 3542 */ 
 
57
# define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT
 
58
#endif
 
59
 
 
60
#ifndef AI_IDN
 
61
# define AI_IDN 0
 
62
#endif
 
63
 
 
64
#ifndef RDISC
 
65
# define NAME "ndisc"
 
66
# define ND_TYPE_ADVERT ND_NEIGHBOR_ADVERT
 
67
# define TYPE_NAME "Neighbor"
 
68
# define NDISC_DEFAULT (NDISC_VERBOSE1 | NDISC_SINGLE)
 
69
# ifdef __linux__
 
70
#  include <sys/ioctl.h>
 
71
# endif
 
72
#else
 
73
# define NAME "rdisc"
 
74
# define ND_TYPE_ADVERT ND_ROUTER_ADVERT
 
75
# define TYPE_NAME "Router"
 
76
# define NDISC_DEFAULT NDISC_VERBOSE1
 
77
#endif
 
78
 
 
79
 
 
80
enum ndisc_flags
 
81
{
 
82
        NDISC_VERBOSE1=0x1,
 
83
        NDISC_VERBOSE2=0x2,
 
84
        NDISC_VERBOSE3=0x3,
 
85
        NDISC_VERBOSE =0x3,
 
86
        NDISC_NUMERIC =0x4,
 
87
        NDISC_SINGLE  =0x8,
 
88
};
 
89
 
 
90
 
 
91
static int
 
92
getipv6byname (const char *name, const char *ifname, int numeric,
 
93
               struct sockaddr_in6 *addr)
 
94
{
 
95
        struct addrinfo hints, *res;
 
96
        memset (&hints, 0, sizeof (hints));
 
97
        hints.ai_family = PF_INET6;
 
98
        hints.ai_socktype = SOCK_DGRAM; /* dummy */
 
99
        hints.ai_flags = numeric ? AI_NUMERICHOST : 0;
 
100
 
 
101
        int val = getaddrinfo (name, NULL, &hints, &res);
 
102
        if (val)
 
103
        {
 
104
                fprintf (stderr, _("%s: %s\n"), name, gai_strerror (val));
 
105
                return -1;
 
106
        }
 
107
 
 
108
        memcpy (addr, res->ai_addr, sizeof (struct sockaddr_in6));
 
109
        freeaddrinfo (res);
 
110
 
 
111
        val = if_nametoindex (ifname);
 
112
        if (val == 0)
 
113
        {
 
114
                perror (ifname);
 
115
                return -1;
 
116
        }
 
117
        addr->sin6_scope_id = val;
 
118
 
 
119
        return 0;
 
120
}
 
121
 
 
122
 
 
123
inline int
 
124
setmcasthoplimit (int fd, int value)
 
125
{
 
126
        return setsockopt (fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
 
127
                           &value, sizeof (value));
 
128
}
 
129
 
 
130
 
 
131
static void
 
132
printmacaddress (const uint8_t *ptr, size_t len)
 
133
{
 
134
        while (len > 1)
 
135
        {
 
136
                printf ("%02X:", *ptr);
 
137
                ptr++;
 
138
                len--;
 
139
        }
 
140
 
 
141
        if (len == 1)
 
142
                printf ("%02X\n", *ptr);
 
143
}
 
144
 
 
145
 
 
146
#ifndef RDISC
 
147
static int
 
148
getmacaddress (const char *ifname, uint8_t *addr)
 
149
{
 
150
# ifdef SIOCGIFHWADDR
 
151
        struct ifreq req;
 
152
        memset (&req, 0, sizeof (req));
 
153
 
 
154
        if (((unsigned)strlen (ifname)) >= (unsigned)IFNAMSIZ)
 
155
                return -1; /* buffer overflow = local root */
 
156
        strcpy (req.ifr_name, ifname);
 
157
 
 
158
        int fd = socket (AF_INET6, SOCK_DGRAM, 0);
 
159
        if (fd == -1)
 
160
                return -1;
 
161
 
 
162
        if (ioctl (fd, SIOCGIFHWADDR, &req))
 
163
        {
 
164
                perror (ifname);
 
165
                close (fd);
 
166
                return -1;
 
167
        }
 
168
        close (fd);
 
169
 
 
170
        memcpy (addr, req.ifr_hwaddr.sa_data, 6);
 
171
        return 0;
 
172
# else
 
173
        (void)ifname;
 
174
        (void)addr;
 
175
        return -1;
 
176
# endif
 
177
}
 
178
 
 
179
typedef struct
 
180
{
 
181
        struct nd_neighbor_solicit hdr;
 
182
        struct nd_opt_hdr opt;
 
183
        uint8_t hw_addr[6];
 
184
} solicit_packet;
 
185
 
 
186
static ssize_t
 
187
buildsol (solicit_packet *ns, struct sockaddr_in6 *tgt, const char *ifname)
 
188
{
 
189
        /* builds ICMPv6 Neighbor Solicitation packet */
 
190
        ns->hdr.nd_ns_type = ND_NEIGHBOR_SOLICIT;
 
191
        ns->hdr.nd_ns_code = 0;
 
192
        ns->hdr.nd_ns_cksum = 0; /* computed by the kernel */
 
193
        ns->hdr.nd_ns_reserved = 0;
 
194
        memcpy (&ns->hdr.nd_ns_target, &tgt->sin6_addr, 16);
 
195
 
 
196
        /* determines actual multicast destination address */
 
197
        memcpy (&tgt->sin6_addr.s6_addr, "\xff\x02\x00\x00\x00\x00\x00\x00"
 
198
                                         "\x00\x00\x00\x01\xff", 13);
 
199
 
 
200
        /* gets our own interface's link-layer address (MAC) */
 
201
        if (getmacaddress (ifname, ns->hw_addr))
 
202
                return sizeof (ns->hdr);
 
203
        
 
204
        ns->opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
 
205
        ns->opt.nd_opt_len = 1; /* 8 bytes */
 
206
        return sizeof (*ns);
 
207
}
 
208
 
 
209
 
 
210
static int
 
211
parseadv (const uint8_t *buf, size_t len, const struct sockaddr_in6 *tgt,
 
212
         int verbose)
 
213
{
 
214
        const struct nd_neighbor_advert *na =
 
215
                (const struct nd_neighbor_advert *)buf;
 
216
        const uint8_t *ptr;
 
217
        
 
218
        /* checks if the packet is a Neighbor Advertisement, and
 
219
         * if the target IPv6 address is the right one */
 
220
        if ((len < sizeof (struct nd_neighbor_advert))
 
221
         || (na->nd_na_type != ND_NEIGHBOR_ADVERT)
 
222
         || (na->nd_na_code != 0)
 
223
         || memcmp (&na->nd_na_target, &tgt->sin6_addr, 16))
 
224
                return -1;
 
225
 
 
226
        len -= sizeof (struct nd_neighbor_advert);
 
227
 
 
228
        /* looks for Target Link-layer address option */
 
229
        ptr = buf + sizeof (struct nd_neighbor_advert);
 
230
 
 
231
        while (len >= 8)
 
232
        {
 
233
                uint16_t optlen;
 
234
 
 
235
                optlen = ((uint16_t)(ptr[1])) << 3;
 
236
                if (optlen == 0)
 
237
                        break; /* invalid length */
 
238
 
 
239
                if (len < optlen) /* length > remaining bytes */
 
240
                        break;
 
241
                len -= optlen;
 
242
 
 
243
 
 
244
                /* skips unrecognized option */
 
245
                if (ptr[0] != ND_OPT_TARGET_LINKADDR)
 
246
                {
 
247
                        ptr += optlen;
 
248
                        continue;
 
249
                }
 
250
 
 
251
                /* Found! displays link-layer address */
 
252
                ptr += 2;
 
253
                optlen -= 2;
 
254
                if (verbose)
 
255
                        fputs (_("Target link-layer address: "), stdout);
 
256
 
 
257
                printmacaddress (ptr, optlen);
 
258
                return 0;
 
259
        }
 
260
 
 
261
        return -1;
 
262
}
 
263
#else
 
264
typedef struct nd_router_solicit solicit_packet;
 
265
 
 
266
static ssize_t
 
267
buildsol (solicit_packet *rs)
 
268
{
 
269
        /* builds ICMPv6 Router Solicitation packet */
 
270
        rs->nd_rs_type = ND_ROUTER_SOLICIT;
 
271
        rs->nd_rs_code = 0;
 
272
        rs->nd_rs_cksum = 0; /* computed by the kernel */
 
273
        rs->nd_rs_reserved = 0;
 
274
        return sizeof (*rs);
 
275
}
 
276
 
 
277
 
 
278
static int
 
279
parseprefix (const struct nd_opt_prefix_info *pi, size_t optlen, int verbose)
 
280
{
 
281
        char str[INET6_ADDRSTRLEN];
 
282
 
 
283
        if (optlen < sizeof (*pi))
 
284
                return -1;
 
285
 
 
286
        /* displays prefix informations */
 
287
        if (inet_ntop (AF_INET6, &pi->nd_opt_pi_prefix, str,
 
288
                       sizeof (str)) == NULL)
 
289
                return -1;
 
290
 
 
291
        if (verbose)
 
292
                fputs (_(" Prefix                   : "), stdout);
 
293
        printf ("%s/%u\n", str, pi->nd_opt_pi_prefix_len);
 
294
 
 
295
        if (verbose)
 
296
        {
 
297
                /* INET6_ADDRSTRLEN > 13 */
 
298
                unsigned v;
 
299
 
 
300
                fputs (_("  Valid time              : "), stdout);
 
301
                v = ntohl (pi->nd_opt_pi_valid_time);
 
302
                if (v == 0xffffffff)
 
303
                        fputs (_("    infinite (0xffffffff)\n"), stdout);
 
304
                else
 
305
                        printf (_("%12u (0x%08x) %s\n"),
 
306
                                v, v, ngettext ("second", "seconds", v));
 
307
 
 
308
                fputs (_("  Pref. time              : "), stdout);
 
309
                v = ntohl (pi->nd_opt_pi_preferred_time);
 
310
                if (v == 0xffffffff)
 
311
                        fputs (_("    infinite (0xffffffff)\n"), stdout);
 
312
                else
 
313
                        printf (_("%12u (0x%08x) %s\n"),
 
314
                                v, v, ngettext ("second", "seconds", v));
 
315
        }
 
316
        return 0;
 
317
}
 
318
 
 
319
 
 
320
static void
 
321
parsemtu (const struct nd_opt_mtu *m)
 
322
{
 
323
        unsigned mtu = ntohl (m->nd_opt_mtu_mtu);
 
324
 
 
325
        fputs (_(" MTU                      : "), stdout);
 
326
        printf ("       %5u %s (%s)\n", mtu,
 
327
                ngettext ("byte", "bytes", mtu),
 
328
                        gettext((mtu >= 1280) ? N_("valid") : N_("invalid")));
 
329
}
 
330
 
 
331
 
 
332
static int
 
333
parseadv (const uint8_t *buf, size_t len, int verbose)
 
334
{
 
335
        const struct nd_router_advert *ra =
 
336
                (const struct nd_router_advert *)buf;
 
337
        const uint8_t *ptr;
 
338
        
 
339
        /* checks if the packet is a Router Advertisement */
 
340
        if ((len < sizeof (struct nd_router_advert))
 
341
         || (ra->nd_ra_type != ND_ROUTER_ADVERT)
 
342
         || (ra->nd_ra_code != 0))
 
343
                return -1;
 
344
 
 
345
        if (verbose)
 
346
        {
 
347
                unsigned v;
 
348
 
 
349
                /* Hop limit */
 
350
                fputs (_("\n"
 
351
                         "Hop limit                 :    "), stdout);
 
352
                v = ra->nd_ra_curhoplimit;
 
353
                if (v != 0)
 
354
                        printf (_("      %3u"), v);
 
355
                else
 
356
                        fputs (_("undefined"), stdout);
 
357
                printf (_(" (      0x%02x)\n"), v);
 
358
 
 
359
                /* Router lifetime */
 
360
                fputs (_("Router lifetime           : "), stdout);
 
361
                v = ntohs (ra->nd_ra_router_lifetime);
 
362
                printf (_("%12u (0x%08x) %s\n"), v, v,
 
363
                        ngettext ("millisecond", "milliseconds", v));
 
364
 
 
365
                /* ND Reachable time */
 
366
                fputs (_("Reachable time            : "), stdout);
 
367
                v = ntohl (ra->nd_ra_reachable);
 
368
                if (v != 0)
 
369
                        printf (_("%12u (0x%08x) %s\n"), v, v,
 
370
                                ngettext ("millisecond", "milliseconds", v));
 
371
                else
 
372
                        fputs (_(" unspecified (0x00000000)\n"), stdout);
 
373
 
 
374
                /* ND Retransmit time */
 
375
                fputs (_("Retransmit time           : "), stdout);
 
376
                v = ntohl (ra->nd_ra_retransmit);
 
377
                if (v != 0)
 
378
                        printf (_("%12u (0x%08x) %s\n"), v, v,
 
379
                                ngettext ("millisecond", "milliseconds", v));
 
380
                else
 
381
                        fputs (_(" unspecified (0x00000000)\n"), stdout);
 
382
        }
 
383
        len -= sizeof (struct nd_router_advert);
 
384
 
 
385
        /* parses options */
 
386
        ptr = buf + sizeof (struct nd_router_advert);
 
387
 
 
388
        while (len >= 8)
 
389
        {
 
390
                uint16_t optlen;
 
391
 
 
392
                optlen = ((uint16_t)(ptr[1])) << 3;
 
393
                if ((optlen == 0) /* invalid length */
 
394
                 || (len < optlen) /* length > remaining bytes */)
 
395
                        break;
 
396
 
 
397
                len -= optlen;
 
398
 
 
399
                /* skips unrecognized option */
 
400
                switch (ptr[0])
 
401
                {
 
402
                        case ND_OPT_SOURCE_LINKADDR:
 
403
                                if (verbose)
 
404
                                {
 
405
                                        fputs (" Source link-layer address: ", stdout);
 
406
                                        printmacaddress (ptr + 2, optlen - 2);
 
407
                                }
 
408
                                break;
 
409
 
 
410
                        case ND_OPT_TARGET_LINKADDR:
 
411
                                break; /* ignore */
 
412
 
 
413
                        case ND_OPT_PREFIX_INFORMATION:
 
414
                                if (parseprefix ((const struct nd_opt_prefix_info *)ptr,
 
415
                                                 optlen, verbose))
 
416
                                        return -1;
 
417
 
 
418
                        case ND_OPT_REDIRECTED_HEADER:
 
419
                                break; /* ignore */
 
420
 
 
421
                        case ND_OPT_MTU:
 
422
                                if (verbose)
 
423
                                        parsemtu ((const struct nd_opt_mtu *)ptr);
 
424
                                break;
 
425
                }
 
426
 
 
427
                ptr += optlen;
 
428
        }
 
429
 
 
430
        return 0;
 
431
}
 
432
 
 
433
# define buildsol( a, b, c ) buildsol (a)
 
434
# define parseadv( a, b, c, d ) parseadv (a, b, d)
 
435
#endif
 
436
 
 
437
 
 
438
static ssize_t
 
439
recvfromLL (int fd, void *buf, size_t len, int flags,
 
440
            struct sockaddr_in6 *addr)
 
441
{
 
442
        char cbuf[CMSG_SPACE (sizeof (int))];
 
443
        struct iovec iov =
 
444
        {
 
445
                .iov_base = buf,
 
446
                .iov_len = len
 
447
        };
 
448
        struct msghdr hdr =
 
449
        {
 
450
                .msg_name = addr,
 
451
                .msg_namelen = sizeof (*addr),
 
452
                .msg_iov = &iov,
 
453
                .msg_iovlen = 1,
 
454
                .msg_control = cbuf,
 
455
                .msg_controllen = sizeof (cbuf)
 
456
        };
 
457
 
 
458
        ssize_t val = recvmsg (fd, &hdr, flags);
 
459
        if (val == -1)
 
460
                return val;
 
461
 
 
462
        /* ensures the hop limit is 255 */
 
463
        for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (&hdr);
 
464
             cmsg != NULL;
 
465
             cmsg = CMSG_NXTHDR (&hdr, cmsg))
 
466
        {
 
467
                if ((cmsg->cmsg_level == IPPROTO_IPV6)
 
468
                 && (cmsg->cmsg_type == IPV6_HOPLIMIT))
 
469
                {
 
470
                        if (255 != *(int *)CMSG_DATA (cmsg))
 
471
                        {
 
472
                                // pretend to be a spurious wake-up
 
473
                                errno = EAGAIN;
 
474
                                return -1;
 
475
                        }
 
476
                }
 
477
        }
 
478
 
 
479
        return val;
 
480
}
 
481
 
 
482
 
 
483
static ssize_t
 
484
recvadv (int fd, const struct sockaddr_in6 *tgt, unsigned wait_ms,
 
485
         unsigned flags)
 
486
{
 
487
        struct timespec now, end;
 
488
        unsigned responses = 0;
 
489
 
 
490
        /* computes deadline time */
 
491
        mono_gettime (&now);
 
492
        {
 
493
                div_t d;
 
494
                
 
495
                d = div (wait_ms, 1000);
 
496
                end.tv_sec = now.tv_sec + d.quot;
 
497
                end.tv_nsec = now.tv_nsec + (d.rem * 1000000);
 
498
        }
 
499
 
 
500
        /* receive loop */
 
501
        for (;;)
 
502
        {
 
503
                /* waits for reply until deadline */
 
504
                ssize_t val = 0;
 
505
                if (end.tv_sec >= now.tv_sec)
 
506
                {
 
507
                        val = (end.tv_sec - now.tv_sec) * 1000
 
508
                                + (int)((end.tv_nsec - now.tv_nsec) / 1000000);
 
509
                        if (val < 0)
 
510
                                val = 0;
 
511
                }
 
512
 
 
513
                val = poll (&(struct pollfd){ .fd = fd, .events = POLLIN }, 1, val);
 
514
                if (val < 0)
 
515
                        break;
 
516
 
 
517
                if (val == 0)
 
518
                        return responses;
 
519
 
 
520
                /* receives an ICMPv6 packet */
 
521
                // TODO: use interface MTU as buffer size
 
522
                uint8_t buf[1460];
 
523
                struct sockaddr_in6 addr;
 
524
 
 
525
                val = recvfromLL (fd, buf, sizeof (buf), MSG_DONTWAIT, &addr);
 
526
                if (val == -1)
 
527
                {
 
528
                        if (errno != EAGAIN)
 
529
                                perror (_("Receiving ICMPv6 packet"));
 
530
                        continue;
 
531
                }
 
532
 
 
533
                /* ensures the response came through the right interface */
 
534
                if (addr.sin6_scope_id
 
535
                 && (addr.sin6_scope_id != tgt->sin6_scope_id))
 
536
                        continue;
 
537
 
 
538
                if (parseadv (buf, val, tgt, flags & NDISC_VERBOSE) == 0)
 
539
                {
 
540
                        if (flags & NDISC_VERBOSE)
 
541
                        {
 
542
                                char str[INET6_ADDRSTRLEN];
 
543
 
 
544
                                if (inet_ntop (AF_INET6, &addr.sin6_addr, str,
 
545
                                                sizeof (str)) != NULL)
 
546
                                        printf (_(" from %s\n"), str);
 
547
                        }
 
548
 
 
549
                        if (responses < INT_MAX)
 
550
                                responses++;
 
551
 
 
552
                        if (flags & NDISC_SINGLE)
 
553
                                return 1 /* = responses */;
 
554
                }
 
555
                mono_gettime (&now);
 
556
        }
 
557
 
 
558
        return -1; /* error */
 
559
}
 
560
 
 
561
 
 
562
static int fd;
 
563
 
 
564
static int
 
565
ndisc (const char *name, const char *ifname, unsigned flags, unsigned retry,
 
566
       unsigned wait_ms)
 
567
{
 
568
        struct sockaddr_in6 tgt;
 
569
 
 
570
        if (fd == -1)
 
571
        {
 
572
                perror (_("Raw IPv6 socket"));
 
573
                return -1;
 
574
        }
 
575
 
 
576
        fcntl (fd, F_SETFD, FD_CLOEXEC);
 
577
 
 
578
        /* set ICMPv6 filter */
 
579
        {
 
580
                struct icmp6_filter f;
 
581
 
 
582
                ICMP6_FILTER_SETBLOCKALL (&f);
 
583
                ICMP6_FILTER_SETPASS (ND_TYPE_ADVERT, &f);
 
584
                setsockopt (fd, IPPROTO_ICMPV6, ICMP6_FILTER, &f, sizeof (f));
 
585
        }
 
586
 
 
587
        setsockopt (fd, SOL_SOCKET, SO_DONTROUTE, &(int){ 1 }, sizeof (int));
 
588
 
 
589
        /* sets Hop-by-hop limit to 255 */
 
590
        setmcasthoplimit (fd, 255);
 
591
        setsockopt (fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
 
592
                    &(int){ 1 }, sizeof (int));
 
593
 
 
594
        /* resolves target's IPv6 address */
 
595
        if (getipv6byname (name, ifname, (flags & NDISC_NUMERIC) ? 1 : 0, &tgt))
 
596
                goto error;
 
597
        else
 
598
        {
 
599
                char s[INET6_ADDRSTRLEN];
 
600
 
 
601
                inet_ntop (AF_INET6, &tgt.sin6_addr, s, sizeof (s));
 
602
                if (flags & NDISC_VERBOSE)
 
603
                        printf (_("Soliciting %s (%s) on %s...\n"), name, s, ifname);
 
604
        }
 
605
 
 
606
        {
 
607
                solicit_packet packet;
 
608
                struct sockaddr_in6 dst;
 
609
                ssize_t plen;
 
610
 
 
611
                memcpy (&dst, &tgt, sizeof (dst));
 
612
                plen = buildsol (&packet, &dst, ifname);
 
613
                if (plen == -1)
 
614
                        goto error;
 
615
 
 
616
                while (retry > 0)
 
617
                {
 
618
                        /* sends a Solitication */
 
619
                        if (sendto (fd, &packet, plen, 0,
 
620
                                    (const struct sockaddr *)&dst,
 
621
                                    sizeof (dst)) != plen)
 
622
                        {
 
623
                                perror (_("Sending ICMPv6 packet"));
 
624
                                goto error;
 
625
                        }
 
626
                        retry--;
 
627
        
 
628
                        /* receives an Advertisement */
 
629
                        ssize_t val = recvadv (fd, &tgt, wait_ms, flags);
 
630
                        if (val > 0)
 
631
                        {
 
632
                                close (fd);
 
633
                                return 0;
 
634
                        }
 
635
                        else
 
636
                        if (val == 0)
 
637
                        {
 
638
                                if (flags & NDISC_VERBOSE)
 
639
                                        puts (_("Timed out."));
 
640
                        }
 
641
                        else
 
642
                                goto error;
 
643
                }
 
644
        }
 
645
 
 
646
        close (fd);
 
647
        if (flags & NDISC_VERBOSE)
 
648
                puts (_("No response."));
 
649
        return -2;
 
650
 
 
651
error:
 
652
        close (fd);
 
653
        return -1;
 
654
}
 
655
 
 
656
 
 
657
static int
 
658
quick_usage (const char *path)
 
659
{
 
660
        fprintf (stderr, _("Try \"%s -h\" for more information.\n"), path);
 
661
        return 2;
 
662
}
 
663
 
 
664
 
 
665
static int
 
666
usage (const char *path)
 
667
{
 
668
        printf (
 
669
#ifndef RDISC
 
670
_("Usage: %s [options] <IPv6 address> <interface>\n"
 
671
"Looks up an on-link IPv6 node link-layer address (Neighbor Discovery)\n")
 
672
#else
 
673
_("Usage: %s [options] [IPv6 address] <interface>\n"
 
674
"Solicits on-link IPv6 routers (Router Discovery)\n")
 
675
#endif
 
676
                , path);
 
677
 
 
678
        printf (_("\n"
 
679
"  -1, --single   display first response and exit\n"
 
680
"  -h, --help     display this help and exit\n"
 
681
"  -m, --multiple wait and display all responses\n"
 
682
"  -n, --numeric  don't resolve host names\n"
 
683
"  -q, --quiet    only print the %s (mainly for scripts)\n"
 
684
"  -r, --retry    maximum number of attempts (default: 3)\n"
 
685
"  -V, --version  display program version and exit\n"
 
686
"  -v, --verbose  verbose display (this is the default)\n"
 
687
"  -w, --wait     how long to wait for a response [ms] (default: 1000)\n"
 
688
                   "\n"),
 
689
#ifndef RDISC
 
690
                   _("link-layer address")
 
691
#else
 
692
                   _("advertised prefixes")
 
693
#endif
 
694
                );
 
695
 
 
696
        return 0;
 
697
}
 
698
 
 
699
 
 
700
static int
 
701
version (void)
 
702
{
 
703
        printf (_(
 
704
NAME"6: IPv6 "TYPE_NAME" Discovery userland tool %s ($Rev: 355 $)\n"
 
705
" built %s on %s\n"), VERSION, __DATE__, PACKAGE_BUILD_HOSTNAME);
 
706
        printf (_("Configured with: %s\n"), PACKAGE_CONFIGURE_INVOCATION);
 
707
        puts (_("Written by Remi Denis-Courmont\n"));
 
708
 
 
709
        printf (_("Copyright (C) %u-%u Remi Denis-Courmont\n"
 
710
"This is free software; see the source for copying conditions.\n"
 
711
"There is NO warranty; not even for MERCHANTABILITY or\n"
 
712
"FITNESS FOR A PARTICULAR PURPOSE.\n"), 2004, 2006);
 
713
        return 0;
 
714
}
 
715
 
 
716
 
 
717
 
 
718
static const struct option opts[] = 
 
719
{
 
720
        { "single",   no_argument,       NULL, '1' },
 
721
        { "help",     no_argument,       NULL, 'h' },
 
722
        { "multiple", required_argument, NULL, 'm' },
 
723
        { "numeric",  no_argument,       NULL, 'n' },
 
724
        { "quiet",    no_argument,       NULL, 'q' },
 
725
        { "retry",    required_argument, NULL, 'r' },
 
726
        { "version",  no_argument,       NULL, 'V' },
 
727
        { "verbose",  no_argument,       NULL, 'v' },
 
728
        { "wait",     required_argument, NULL, 'w' },
 
729
        { NULL,       0,                 NULL, 0   }
 
730
};
 
731
 
 
732
 
 
733
int
 
734
main (int argc, char *argv[])
 
735
{
 
736
        fd = socket (PF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
 
737
        int errval = errno;
 
738
 
 
739
        /* Drops root privileges (if setuid not run by root).
 
740
         * Also make sure the socket is not STDIN/STDOUT/STDERR. */
 
741
        if (setuid (getuid ()) || ((fd >= 0) && (fd <= 2)))
 
742
                return 1;
 
743
 
 
744
        setlocale (LC_CTYPE, "");
 
745
 
 
746
        int val;
 
747
        unsigned retry = 3, flags = NDISC_DEFAULT, wait_ms = 1000;
 
748
        const char *hostname, *ifname;
 
749
 
 
750
        while ((val = getopt_long (argc, argv, "1hmnqr:Vvw:", opts, NULL)) != EOF)
 
751
        {
 
752
                switch (val)
 
753
                {
 
754
                        case '1':
 
755
                                flags |= NDISC_SINGLE;
 
756
                                break;
 
757
 
 
758
                        case 'h':
 
759
                                return usage (argv[0]);
 
760
 
 
761
                        case 'm':
 
762
                                flags &= ~NDISC_SINGLE;
 
763
                                break;
 
764
 
 
765
                        case 'n':
 
766
                                flags |= NDISC_NUMERIC;
 
767
                                break;
 
768
 
 
769
                        case 'q':
 
770
                                flags &= ~NDISC_VERBOSE;
 
771
                                break;
 
772
 
 
773
                        case 'r':
 
774
                        {
 
775
                                unsigned long l;
 
776
                                char *end;
 
777
 
 
778
                                l = strtoul (optarg, &end, 0);
 
779
                                if (*end || l > UINT_MAX)
 
780
                                        return quick_usage (argv[0]);
 
781
                                retry = l;
 
782
                                break;
 
783
                        }
 
784
                                
 
785
                        case 'V':
 
786
                                return version ();
 
787
 
 
788
                        case 'v':
 
789
                                /* NOTE: assume NDISC_VERBOSE occupies low-order bits */
 
790
                                if ((flags & NDISC_VERBOSE) < NDISC_VERBOSE)
 
791
                                        flags++;
 
792
                                break;
 
793
 
 
794
                        case 'w':
 
795
                        {
 
796
                                unsigned long l;
 
797
                                char *end;
 
798
 
 
799
                                l = strtoul (optarg, &end, 0);
 
800
                                if (*end || l > UINT_MAX)
 
801
                                        return quick_usage (argv[0]);
 
802
                                wait_ms = l;
 
803
                                break;
 
804
                        }
 
805
 
 
806
                        case '?':
 
807
                        default:
 
808
                                return quick_usage (argv[0]);
 
809
                }
 
810
        }
 
811
 
 
812
        if (optind < argc)
 
813
        {
 
814
                hostname = argv[optind++];
 
815
 
 
816
                if (optind < argc)
 
817
                        ifname = argv[optind++];
 
818
                else
 
819
                        ifname = NULL;
 
820
        }
 
821
        else
 
822
                return quick_usage (argv[0]);
 
823
 
 
824
#ifdef RDISC
 
825
        if (ifname == NULL)
 
826
        {
 
827
                ifname = hostname;
 
828
                hostname = "ff02::2";
 
829
        }
 
830
        else
 
831
#endif
 
832
        if ((optind != argc) || (ifname == NULL))
 
833
                return quick_usage (argv[0]);
 
834
 
 
835
        errno = errval; /* restore socket() error value */
 
836
        return -ndisc (hostname, ifname, flags, retry, wait_ms);
 
837
}
 
838