Ditch the sysvinit stuff
[profile/ivi/iputils.git] / ping6.c
1 /*
2  *
3  *      Modified for AF_INET6 by Pedro Roque
4  *
5  *      <roque@di.fc.ul.pt>
6  *
7  *      Original copyright notice included bellow
8  */
9
10 /*
11  * Copyright (c) 1989 The Regents of the University of California.
12  * All rights reserved.
13  *
14  * This code is derived from software contributed to Berkeley by
15  * Mike Muuss.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must reproduce the above copyright
23  *    notice, this list of conditions and the following disclaimer in the
24  *    documentation and/or other materials provided with the distribution.
25  * 3. All advertising materials mentioning features or use of this software
26  *    must display the following acknowledgement:
27  *      This product includes software developed by the University of
28  *      California, Berkeley and its contributors.
29  * 4. Neither the name of the University nor the names of its contributors
30  *    may be used to endorse or promote products derived from this software
31  *    without specific prior written permission.
32  *
33  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
34  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
37  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43  * SUCH DAMAGE.
44  */
45
46 #ifndef lint
47 char copyright[] =
48 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
49  All rights reserved.\n";
50 #endif /* not lint */
51
52 /*
53  *                      P I N G . C
54  *
55  * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
56  * measure round-trip-delays and packet loss across network paths.
57  *
58  * Author -
59  *      Mike Muuss
60  *      U. S. Army Ballistic Research Laboratory
61  *      December, 1983
62  *
63  * Status -
64  *      Public Domain.  Distribution Unlimited.
65  * Bugs -
66  *      More statistics could always be gathered.
67  *      This program has to run SUID to ROOT to access the ICMP socket.
68  */
69 #include "ping_common.h"
70
71 #include <linux/filter.h>
72 #include <netinet/ip6.h>
73 #include <netinet/icmp6.h>
74 #include <resolv.h>
75
76 #include "ping6_niquery.h"
77
78 #ifndef SOL_IPV6
79 #define SOL_IPV6 IPPROTO_IPV6
80 #endif
81
82 #ifndef SOL_ICMPV6
83 #define SOL_ICMPV6 IPPROTO_ICMPV6
84 #endif
85
86 /* RFC3542 */
87 #ifndef ICMP6_DST_UNREACH_BEYONDSCOPE
88 #define ICMP6_DST_UNREACH_BEYONDSCOPE ICMP6_DST_UNREACH_NOTNEIGHBOR
89 #endif
90
91 #ifndef IPV6_SRCRT_TYPE_0
92 #define IPV6_SRCRT_TYPE_0       0
93 #endif
94
95 #ifndef MLD_LISTENER_QUERY
96 #define MLD_LISTENER_QUERY      130
97 #define MLD_LISTENER_REPORT     131
98 #define MLD_LISTENER_REDUCTION  132
99 #endif
100
101 #define BIT_CLEAR(nr, addr) do { ((__u32 *)(addr))[(nr) >> 5] &= ~(1U << ((nr) & 31)); } while(0)
102 #define BIT_SET(nr, addr) do { ((__u32 *)(addr))[(nr) >> 5] |= (1U << ((nr) & 31)); } while(0)
103 #define BIT_TEST(nr, addr) do { (__u32 *)(addr))[(nr) >> 5] & (1U << ((nr) & 31)); } while(0)
104
105 #ifndef ICMP6_FILTER_WILLPASS
106 #define ICMP6_FILTER_WILLPASS(type, filterp) \
107         (BIT_TEST((type), filterp) == 0)
108
109 #define ICMP6_FILTER_WILLBLOCK(type, filterp) \
110         BIT_TEST((type), filterp)
111
112 #define ICMP6_FILTER_SETPASS(type, filterp) \
113         BIT_CLEAR((type), filterp)
114
115 #define ICMP6_FILTER_SETBLOCK(type, filterp) \
116         BIT_SET((type), filterp)
117
118 #define ICMP6_FILTER_SETPASSALL(filterp) \
119         memset(filterp, 0, sizeof(struct icmp6_filter));
120
121 #define ICMP6_FILTER_SETBLOCKALL(filterp) \
122         memset(filterp, 0xFF, sizeof(struct icmp6_filter));
123 #endif
124
125 #define MAXPACKET       128000          /* max packet size */
126
127 #ifdef SO_TIMESTAMP
128 #define HAVE_SIN6_SCOPEID 1
129 #endif
130
131 #ifndef SCOPE_DELIMITER
132 # define SCOPE_DELIMITER '%'
133 #endif
134
135 __u32 flowlabel;
136 __u32 tclass;
137 struct cmsghdr *srcrt;
138
139 struct sockaddr_in6 whereto;    /* who to ping */
140 u_char outpack[MAXPACKET];
141 int maxpacket = sizeof(outpack);
142
143 static unsigned char cmsgbuf[4096];
144 static int cmsglen = 0;
145
146 static char * pr_addr(struct in6_addr *addr);
147 static char * pr_addr_n(struct in6_addr *addr);
148 static int pr_icmph(__u8 type, __u8 code, __u32 info);
149 static void usage(void) __attribute((noreturn));
150
151 struct sockaddr_in6 source;
152 char *device;
153 int pmtudisc=-1;
154
155 static int icmp_sock;
156
157 #include <openssl/md5.h>
158
159 /* Node Information query */
160 int ni_query = -1;
161 int ni_flag = 0;
162 void *ni_subject = NULL;
163 int ni_subject_len = 0;
164 int ni_subject_type = 0;
165 char *ni_group;
166
167 __u8 ni_nonce[8];
168
169 static struct in6_addr in6_anyaddr;
170 static __inline__ int ipv6_addr_any(struct in6_addr *addr)
171 {
172         return (memcmp(addr, &in6_anyaddr, 16) == 0);
173 }
174
175 size_t inet6_srcrt_space(int type, int segments)
176 {
177         if (type != 0 || segments > 24)
178                 return 0;
179
180         return (sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0) +
181                 segments * sizeof(struct in6_addr));
182 }
183
184 extern struct cmsghdr * inet6_srcrt_init(void *bp, int type)
185 {
186         struct cmsghdr *cmsg;
187
188         if (type)
189                 return NULL;
190
191         memset(bp, 0, sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0));
192         cmsg = (struct cmsghdr *) bp;
193
194         cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0);
195         cmsg->cmsg_level = SOL_IPV6;
196         cmsg->cmsg_type = IPV6_RTHDR;
197
198         return cmsg;
199 }
200
201 int inet6_srcrt_add(struct cmsghdr *cmsg, const struct in6_addr *addr)
202 {
203         struct ip6_rthdr0 *hdr;
204
205         hdr = (struct ip6_rthdr0 *) CMSG_DATA(cmsg);
206
207         cmsg->cmsg_len += sizeof(struct in6_addr);
208         hdr->ip6r0_len += sizeof(struct in6_addr) / 8;
209
210         memcpy(&hdr->ip6r0_addr[hdr->ip6r0_segleft++], addr,
211                sizeof(struct in6_addr));
212
213         return 0;
214 }
215
216 unsigned int if_name2index(const char *ifname)
217 {
218         unsigned int i = if_nametoindex(ifname);
219         if (!i) {
220                 fprintf(stderr, "ping: unknown iface %s\n", ifname);
221                 exit(2);
222         }
223         return i;
224 }
225
226 struct niquery_option {
227         char *name;
228         int namelen;
229         int has_arg;
230         int data;
231         int (*handler)(int index, const char *arg);
232 };
233
234 #define NIQUERY_OPTION(_name, _has_arg, _data, _handler)        \
235         {                                                       \
236                 .name = _name,                                  \
237                 .namelen = sizeof(_name) - 1,                   \
238                 .has_arg = _has_arg,                            \
239                 .data = _data,                                  \
240                 .handler = _handler                             \
241         }
242
243 static int niquery_option_name_handler(int index, const char *arg);
244 static int niquery_option_ipv6_handler(int index, const char *arg);
245 static int niquery_option_ipv6_flag_handler(int index, const char *arg);
246 static int niquery_option_ipv4_handler(int index, const char *arg);
247 static int niquery_option_ipv4_flag_handler(int index, const char *arg);
248 static int niquery_option_subject_addr_handler(int index, const char *arg);
249 static int niquery_option_subject_name_handler(int index, const char *arg);
250 char *ni_groupaddr(const char *name);
251
252 struct niquery_option niquery_options[] = {
253         NIQUERY_OPTION("name",                  0,      0,                              niquery_option_name_handler),
254         NIQUERY_OPTION("fqdn",                  0,      0,                              niquery_option_name_handler),
255         NIQUERY_OPTION("ipv6",                  0,      0,                              niquery_option_ipv6_handler),
256         NIQUERY_OPTION("ipv6-all",              0,      NI_IPV6ADDR_F_ALL,              niquery_option_ipv6_flag_handler),
257         NIQUERY_OPTION("ipv6-compatible",       0,      NI_IPV6ADDR_F_COMPAT,           niquery_option_ipv6_flag_handler),
258         NIQUERY_OPTION("ipv6-linklocal",        0,      NI_IPV6ADDR_F_LINKLOCAL,        niquery_option_ipv6_flag_handler),
259         NIQUERY_OPTION("ipv6-sitelocal",        0,      NI_IPV6ADDR_F_SITELOCAL,        niquery_option_ipv6_flag_handler),
260         NIQUERY_OPTION("ipv6-global",           0,      NI_IPV6ADDR_F_GLOBAL,           niquery_option_ipv6_flag_handler),
261         NIQUERY_OPTION("ipv4",                  0,      0,                              niquery_option_ipv4_handler),
262         NIQUERY_OPTION("ipv4-all",              0,      NI_IPV4ADDR_F_ALL,              niquery_option_ipv4_flag_handler),
263         NIQUERY_OPTION("subject-ipv6",          1,      NI_SUBJ_IPV6,                   niquery_option_subject_addr_handler),
264         NIQUERY_OPTION("subject-ipv4",          1,      NI_SUBJ_IPV4,                   niquery_option_subject_addr_handler),
265         NIQUERY_OPTION("subject-name",          1,      0,                              niquery_option_subject_name_handler),
266         NIQUERY_OPTION("subject-fqdn",          1,      -1,                             niquery_option_subject_name_handler),
267         {},
268 };
269
270 static int niquery_set_qtype(int type)
271 {
272         if (ni_query >= 0 && ni_query != type) {
273                 printf("Qtype conflict\n");
274                 return -1;
275         }
276         ni_query = type;
277         return 0;
278 }
279
280 static int niquery_option_name_handler(int index, const char *arg)
281 {
282         if (niquery_set_qtype(NI_QTYPE_NAME) < 0)
283                 return -1;
284         return 0;
285 }
286
287 static int niquery_option_ipv6_handler(int index, const char *arg)
288 {
289         if (niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0)
290                 return -1;
291         return 0;
292 }
293
294 static int niquery_option_ipv6_flag_handler(int index, const char *arg)
295 {
296         if (niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0)
297                 return -1;
298         ni_flag |= niquery_options[index].data;
299         return 0;
300 }
301
302 static int niquery_option_ipv4_handler(int index, const char *arg)
303 {
304         if (niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0)
305                 return -1;
306         return 0;
307 }
308
309 static int niquery_option_ipv4_flag_handler(int index, const char *arg)
310 {
311         if (niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0)
312                 return -1;
313         ni_flag |= niquery_options[index].data;
314         return 0;
315 }
316
317 static int niquery_set_subject_type(int type)
318 {
319         if (ni_subject_type && ni_subject_type != type) {
320                 printf("Subject type conflict\n");
321                 return -1;
322         }
323         ni_subject_type = type;
324         return 0;
325 }
326
327 #define ARRAY_SIZE(array)       (sizeof(array) / sizeof(array[0]))
328 #define OFFSET_OF(type,elem)    ((size_t)&((type *)0)->elem)
329
330 static int niquery_option_subject_addr_handler(int index, const char *arg)
331 {
332         struct addrinfo hints, *ai0, *ai;
333         int offset;
334         int gai;
335
336         if (niquery_set_subject_type(niquery_options[index].data) < 0)
337                 return -1;
338
339         ni_subject_type = niquery_options[index].data;
340
341         switch (niquery_options[index].data) {
342         case NI_SUBJ_IPV6:
343                 ni_subject_len = sizeof(struct in6_addr);
344                 offset = OFFSET_OF(struct sockaddr_in6, sin6_addr);
345                 hints.ai_family = AF_INET6;
346                 break;
347         case NI_SUBJ_IPV4:
348                 ni_subject_len = sizeof(struct in_addr);
349                 offset = OFFSET_OF(struct sockaddr_in, sin_addr);
350                 hints.ai_family = AF_INET;
351                 break;
352         default:
353                 /* should not happen. */
354                 offset = -1;
355         }
356
357         hints.ai_socktype = SOCK_DGRAM;
358
359         gai = getaddrinfo(arg, 0, &hints, &ai0);
360         if (!gai) {
361                 fprintf(stderr, "Unknown host: %s\n", arg);
362                 return -1;
363         }
364
365         for (ai = ai0; ai; ai = ai->ai_next) {
366                 void *p = malloc(ni_subject_len);
367                 if (!p)
368                         continue;
369                 memcpy(p, (__u8 *)ai->ai_addr + offset, ni_subject_len);
370                 free(ni_subject);
371                 ni_subject = p;
372                 break;
373         }
374         freeaddrinfo(ai0);
375
376         return 0;
377 }
378
379 static int niquery_count_dots(const char *arg)
380 {
381         const char *p;
382         int count = 0;
383         for (p = arg; *p; p++) {
384                 if (*p == '.')
385                         count++;
386         }
387         return count;
388 }
389
390 static int niquery_option_subject_name_handler(int index, const char *arg)
391 {
392         unsigned char *dnptrs[2], **dpp, **lastdnptr;
393         int n;
394         char *name, *p;
395         unsigned char *buf;
396         size_t buflen = strlen(arg) + 1;
397         int fqdn = niquery_options[index].data;
398
399         if (niquery_set_subject_type(NI_SUBJ_NAME) < 0)
400                 return -1;
401
402         if (fqdn == 0) {
403                 /* guess if hostname is FQDN */
404                 fqdn = niquery_count_dots(arg) ? 1 : -1;
405         }
406
407         name = strdup(arg);
408         buf = malloc(buflen + 1);
409         if (!name || !buf) {
410                 free(name);
411                 free(buf);
412                 fprintf(stderr, "ping6: out of memory.\n");
413                 exit(1);
414         }
415
416         ni_group = ni_groupaddr(name);
417
418         p = strchr(name, '%');
419         if (p)
420                 *p = '\0';
421
422         dpp = dnptrs;
423         lastdnptr = &dnptrs[ARRAY_SIZE(dnptrs)];
424
425         *dpp++ = (unsigned char *)buf;
426         *dpp++ = NULL;
427
428         n = dn_comp(name, (unsigned char *)buf, buflen, dnptrs, lastdnptr);
429         if (n < 0) {
430                 fprintf(stderr, "ping6: Inappropriate subject name: %s\n", buf);
431                 free(name);
432                 free(buf);
433                 exit(1);
434         }
435
436         if (fqdn < 0)
437                 buf[n] = 0;
438
439         free(ni_subject);
440         ni_subject = buf;
441         ni_subject_len = n + (fqdn < 0);
442
443         free(name);
444         return 0;
445 }
446
447 int niquery_option_handler(const char *opt_arg)
448 {
449         struct niquery_option *p;
450         int i;
451         int ret = -1;
452         for (i = 0, p = niquery_options; p->name; i++, p++) {
453                 if (strncmp(p->name, opt_arg, p->namelen))
454                         continue;
455                 if (!p->has_arg) {
456                         if (opt_arg[p->namelen] == '\0') {
457                                 ret = p->handler(i, NULL);
458                                 if (ret >= 0)
459                                         break;
460                         }
461                 } else {
462                         if (opt_arg[p->namelen] == '=') {
463                                 ret = p->handler(i, &opt_arg[p->namelen] + 1);
464                                 if (ret >= 0)
465                                         break;
466                         }
467                 }
468         }
469         return ret;
470 }
471
472 char *ni_groupaddr(const char *name)
473 {
474         MD5_CTX ctxt;
475         __u8 digest[16];
476         static char nigroup_buf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ];
477         size_t len;
478         char buf[64], *p = buf, *q;
479         int i;
480
481         if (!p) {
482                 fprintf(stderr, "ping6: memory allocation failure.\n");
483                 exit(1);
484         }
485
486         len = strcspn(name, ".%");
487         if (len & ~0x3f) {
488                 fprintf(stderr, "ping6: label too long for subject: %s\n",
489                         name);
490                 exit(1);
491         }
492
493         q = strrchr(name, '%');
494         if (q && strlen(q + 1) >= IFNAMSIZ) {
495                 fprintf(stderr, "ping6: scope too long: %s\n",
496                         q + 1);
497                 exit(1);
498         }
499
500         *p++ = len;
501         for (i = 0; i < len; i++)
502                 *p++ = isupper(name[i]) ? tolower(name[i]) : name[i];
503
504         MD5_Init(&ctxt);
505         MD5_Update(&ctxt, buf, len + 1);
506         MD5_Final(digest, &ctxt);
507
508         sprintf(nigroup_buf, "ff02::2:%02x%02x:%02x%02x",
509                 digest[0], digest[1], digest[2], digest[3]);
510
511         if (q)
512                 strcat(nigroup_buf, q);
513         return nigroup_buf;
514 }
515
516 int main(int argc, char *argv[])
517 {
518         int ch, hold, packlen;
519         u_char *packet;
520         char *target;
521         struct addrinfo hints, *ai;
522         int gai;
523         struct sockaddr_in6 firsthop;
524         int socket_errno;
525         struct icmp6_filter filter;
526         int err;
527 #ifdef __linux__
528         int csum_offset, sz_opt;
529 #endif
530         static uint32_t scope_id = 0;
531
532         icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
533         socket_errno = errno;
534
535         uid = getuid();
536         if (setuid(uid)) {
537                 perror("ping: setuid");
538                 exit(-1);
539         }
540
541         source.sin6_family = AF_INET6;
542         memset(&firsthop, 0, sizeof(firsthop));
543         firsthop.sin6_family = AF_INET6;
544
545         preload = 1;
546         while ((ch = getopt(argc, argv, COMMON_OPTSTR "F:N:")) != EOF) {
547                 switch(ch) {
548                 case 'F':
549                         sscanf(optarg, "%x", &flowlabel);
550                         options |= F_FLOWINFO;
551                         break;
552                 case 'Q':
553                         sscanf(optarg, "%x", &tclass);
554                         options |= F_TCLASS;
555                         break;
556                 case 'I':
557                         if (strchr(optarg, ':')) {
558                                 char *p, *addr = strdup(optarg);
559
560                                 if (!addr) {
561                                         fprintf(stderr, "ping: out of memory\n");
562                                         exit(2);
563                                 }
564
565                                 p = strchr(addr, SCOPE_DELIMITER);
566                                 if (p) {
567                                         *p = '\0';
568                                         device = optarg + (p - addr) + 1;
569                                 }
570
571                                 if (inet_pton(AF_INET6, addr, (char*)&source.sin6_addr) <= 0) {
572                                         fprintf(stderr, "ping: invalid source address %s\n", optarg);
573                                         exit(2);
574                                 }
575
576                                 options |= F_STRICTSOURCE;
577
578                                 free(addr);
579                         } else {
580                                 device = optarg;
581                         }
582                         break;
583                 case 'M':
584                         if (strcmp(optarg, "do") == 0)
585                                 pmtudisc = IPV6_PMTUDISC_DO;
586                         else if (strcmp(optarg, "dont") == 0)
587                                 pmtudisc = IPV6_PMTUDISC_DONT;
588                         else if (strcmp(optarg, "want") == 0)
589                                 pmtudisc = IPV6_PMTUDISC_WANT;
590                         else {
591                                 fprintf(stderr, "ping: wrong value for -M: do, dont, want are valid ones.\n");
592                                 exit(2);
593                         }
594                         break;
595                 case 'V':
596                         printf("ping6 utility, iputils-ss%s\n", SNAPSHOT);
597                         exit(0);
598                 case 'N':
599                         if (niquery_option_handler(optarg) < 0) {
600                                 usage();
601                                 break;
602                         }
603                         break;
604                 COMMON_OPTIONS
605                         common_options(ch);
606                         break;
607                 default:
608                         usage();
609                 }
610         }
611         argc -= optind;
612         argv += optind;
613
614         while (argc > 1) {
615                 struct in6_addr *addr;
616
617                 if (srcrt == NULL) {
618                         int space;
619
620                         space = inet6_srcrt_space(IPV6_SRCRT_TYPE_0, argc - 1);
621
622                         if (space == 0) {
623                                 fprintf(stderr, "srcrt_space failed\n");
624                                 exit(2);
625                         }
626                         if (space + cmsglen > sizeof(cmsgbuf)) {
627                                 fprintf(stderr, "no room for options\n");
628                                 exit(2);
629                         }
630
631                         srcrt = (struct cmsghdr*)(cmsgbuf+cmsglen);
632                         cmsglen += CMSG_ALIGN(space);
633                         inet6_srcrt_init(srcrt, IPV6_SRCRT_TYPE_0);
634                 }
635
636                 target = *argv;
637
638                 memset(&hints, 0, sizeof(hints));
639                 hints.ai_family = AF_INET6;
640                 gai = getaddrinfo(target, NULL, &hints, &ai);
641                 if (gai) {
642                         fprintf(stderr, "unknown host\n");
643                         exit(2);
644                 }
645                 addr = &((struct sockaddr_in6 *)(ai->ai_addr))->sin6_addr;
646                 inet6_srcrt_add(srcrt, addr);
647                 if (ipv6_addr_any(&firsthop.sin6_addr)) {
648                         memcpy(&firsthop.sin6_addr, addr, 16);
649 #ifdef HAVE_SIN6_SCOPEID
650                         firsthop.sin6_scope_id = ((struct sockaddr_in6 *)(ai->ai_addr))->sin6_scope_id;
651                         /* Verify scope_id is the same as previous nodes */
652                         if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) {
653                                 fprintf(stderr, "scope discrepancy among the nodes\n");
654                                 exit(2);
655                         } else if (!scope_id) {
656                                 scope_id = firsthop.sin6_scope_id;
657                         }
658 #endif
659                 }
660                 freeaddrinfo(ai);
661
662                 argv++;
663                 argc--;
664         }
665
666         if (ni_query >= 0) {
667                 int i;
668                 for (i = 0; i < 8; i++)
669                         ni_nonce[i] = rand();
670
671                 if (!ni_subject) {
672                         ni_subject = &whereto.sin6_addr;
673                         ni_subject_len = sizeof(whereto.sin6_addr);
674                         ni_subject_type = NI_SUBJ_IPV6;
675                 }
676         }
677
678         if (argc > 1)
679                 usage();
680         else if (argc == 1) {
681                 target = *argv;
682         } else {
683                 if (ni_query < 0 && ni_subject_type != NI_SUBJ_NAME)
684                         usage();
685                 target = ni_group;
686         }
687
688         memset(&hints, 0, sizeof(hints));
689         hints.ai_family = AF_INET6;
690         gai = getaddrinfo(target, NULL, &hints, &ai);
691         if (gai) {
692                 fprintf(stderr, "unknown host\n");
693                 exit(2);
694         }
695
696         memcpy(&whereto, ai->ai_addr, sizeof(whereto));
697         whereto.sin6_port = htons(IPPROTO_ICMPV6);
698
699         if (memchr(target, ':', strlen(target)))
700                 options |= F_NUMERIC;
701
702         freeaddrinfo(ai);
703
704         if (ipv6_addr_any(&firsthop.sin6_addr)) {
705                 memcpy(&firsthop.sin6_addr, &whereto.sin6_addr, 16);
706 #ifdef HAVE_SIN6_SCOPEID
707                 firsthop.sin6_scope_id = whereto.sin6_scope_id;
708                 /* Verify scope_id is the same as intermediate nodes */
709                 if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) {
710                         fprintf(stderr, "scope discrepancy among the nodes\n");
711                         exit(2);
712                 } else if (!scope_id) {
713                         scope_id = firsthop.sin6_scope_id;
714                 }
715 #endif
716         }
717
718         hostname = target;
719
720         if (ipv6_addr_any(&source.sin6_addr)) {
721                 socklen_t alen;
722                 int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0);
723
724                 if (probe_fd < 0) {
725                         perror("socket");
726                         exit(2);
727                 }
728                 if (device) {
729 #if defined(IPV6_RECVPKTINFO) || defined(HAVE_SIN6_SCOPEID)
730                         unsigned int iface = if_name2index(device);
731 #endif
732 #ifdef IPV6_RECVPKTINFO
733                         struct in6_pktinfo ipi;
734
735                         memset(&ipi, 0, sizeof(ipi));
736                         ipi.ipi6_ifindex = iface;
737 #endif
738
739 #ifdef HAVE_SIN6_SCOPEID
740                         if (IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) ||
741                             IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr))
742                                 firsthop.sin6_scope_id = iface;
743 #endif
744                         if (
745 #ifdef IPV6_RECVPKTINFO
746                             setsockopt(probe_fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof(ipi)) == -1 &&
747 #endif
748                             setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) {
749                                 perror("setsockopt(SO_BINDTODEVICE)");
750                         }
751                 }
752                 firsthop.sin6_port = htons(1025);
753                 if (connect(probe_fd, (struct sockaddr*)&firsthop, sizeof(firsthop)) == -1) {
754                         perror("connect");
755                         exit(2);
756                 }
757                 alen = sizeof(source);
758                 if (getsockname(probe_fd, (struct sockaddr*)&source, &alen) == -1) {
759                         perror("getsockname");
760                         exit(2);
761                 }
762                 source.sin6_port = 0;
763                 close(probe_fd);
764         }
765 #ifdef HAVE_SIN6_SCOPEID
766         else if (device && (IN6_IS_ADDR_LINKLOCAL(&source.sin6_addr) ||
767                             IN6_IS_ADDR_MC_LINKLOCAL(&source.sin6_addr)))
768                 source.sin6_scope_id = if_name2index(device);
769 #endif
770
771         if (icmp_sock < 0) {
772                 errno = socket_errno;
773                 perror("ping: icmp open socket");
774                 exit(2);
775         }
776
777         if (device) {
778                 struct cmsghdr *cmsg;
779                 struct in6_pktinfo *ipi;
780
781                 cmsg = (struct cmsghdr*)(cmsgbuf+cmsglen);
782                 cmsglen += CMSG_SPACE(sizeof(*ipi));
783                 cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi));
784                 cmsg->cmsg_level = SOL_IPV6;
785                 cmsg->cmsg_type = IPV6_PKTINFO;
786
787                 ipi = (struct in6_pktinfo*)CMSG_DATA(cmsg);
788                 memset(ipi, 0, sizeof(*ipi));
789                 ipi->ipi6_ifindex = if_name2index(device);
790         }
791
792         if ((whereto.sin6_addr.s6_addr16[0]&htons(0xff00)) == htons (0xff00)) {
793                 if (uid) {
794                         if (interval < 1000) {
795                                 fprintf(stderr, "ping: multicast ping with too short interval.\n");
796                                 exit(2);
797                         }
798                         if (pmtudisc >= 0 && pmtudisc != IPV6_PMTUDISC_DO) {
799                                 fprintf(stderr, "ping: multicast ping does not fragment.\n");
800                                 exit(2);
801                         }
802                 }
803                 if (pmtudisc < 0)
804                         pmtudisc = IPV6_PMTUDISC_DO;
805         }
806
807         if (pmtudisc >= 0) {
808                 if (setsockopt(icmp_sock, SOL_IPV6, IPV6_MTU_DISCOVER, &pmtudisc, sizeof(pmtudisc)) == -1) {
809                         perror("ping: IPV6_MTU_DISCOVER");
810                         exit(2);
811                 }
812         }
813
814         if ((options&F_STRICTSOURCE) &&
815             bind(icmp_sock, (struct sockaddr*)&source, sizeof(source)) == -1) {
816                 perror("ping: bind icmp socket");
817                 exit(2);
818         }
819
820         if (datalen >= sizeof(struct timeval) && (ni_query < 0)) {
821                 /* can we time transfer */
822                 timing = 1;
823         }
824         packlen = datalen + 8 + 4096 + 40 + 8; /* 4096 for rthdr */
825         if (!(packet = (u_char *)malloc((u_int)packlen))) {
826                 fprintf(stderr, "ping: out of memory.\n");
827                 exit(2);
828         }
829
830         working_recverr = 1;
831         hold = 1;
832         if (setsockopt(icmp_sock, SOL_IPV6, IPV6_RECVERR, (char *)&hold, sizeof(hold))) {
833                 fprintf(stderr, "WARNING: your kernel is veeery old. No problems.\n");
834                 working_recverr = 0;
835         }
836
837         /* Estimate memory eaten by single packet. It is rough estimate.
838          * Actually, for small datalen's it depends on kernel side a lot. */
839         hold = datalen+8;
840         hold += ((hold+511)/512)*(40+16+64+160);
841         sock_setbufs(icmp_sock, hold);
842
843 #ifdef __linux__
844         csum_offset = 2;
845         sz_opt = sizeof(int);
846
847         err = setsockopt(icmp_sock, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sz_opt);
848         if (err < 0) {
849                 /* checksum should be enabled by default and setting this
850                  * option might fail anyway.
851                  */
852                 fprintf(stderr, "setsockopt(RAW_CHECKSUM) failed - try to continue.");
853         }
854 #endif
855
856         /*
857          *      select icmp echo reply as icmp type to receive
858          */
859
860         ICMP6_FILTER_SETBLOCKALL(&filter);
861
862         if (!working_recverr) {
863                 ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &filter);
864                 ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &filter);
865                 ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &filter);
866                 ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &filter);
867         }
868
869         if (ni_query >= 0)
870                 ICMP6_FILTER_SETPASS(ICMPV6_NI_REPLY, &filter);
871         else
872                 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
873
874         err = setsockopt(icmp_sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
875                          sizeof(struct icmp6_filter));
876
877         if (err < 0) {
878                 perror("setsockopt(ICMP6_FILTER)");
879                 exit(2);
880         }
881
882         if (options & F_NOLOOP) {
883                 int loop = 0;
884                 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
885                                                         &loop, sizeof(loop)) == -1) {
886                         perror ("can't disable multicast loopback");
887                         exit(2);
888                 }
889         }
890         if (options & F_TTL) {
891                 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
892                                &ttl, sizeof(ttl)) == -1) {
893                         perror ("can't set multicast hop limit");
894                         exit(2);
895                 }
896                 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
897                                &ttl, sizeof(ttl)) == -1) {
898                         perror ("can't set unicast hop limit");
899                         exit(2);
900                 }
901         }
902
903         if (1) {
904                 int on = 1;
905                 if (
906 #ifdef IPV6_RECVHOPLIMIT
907                     setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
908                                &on, sizeof(on)) == -1 &&
909                     setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_2292HOPLIMIT,
910                                &on, sizeof(on)) == -1
911 #else
912                     setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_HOPLIMIT,
913                                &on, sizeof(on)) == -1
914 #endif
915                    ){
916                         perror ("can't receive hop limit");
917                         exit(2);
918                 }
919         }
920
921         if (options & F_TCLASS) {
922 #ifdef IPV6_TCLASS
923                 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_TCLASS,
924                                &tclass, sizeof(tclass)) == -1) {
925                         perror ("setsockopt(IPV6_TCLASS)");
926                         exit(2);
927                 }
928 #else
929                 fprintf(stderr, "Traffic class is not supported.\n");
930 #endif
931         }
932
933         if (options&F_FLOWINFO) {
934 #ifdef IPV6_FLOWINFO_SEND
935                 int on = 1;
936 #endif
937 #ifdef IPV6_FLOWLABEL_MGR
938                 char freq_buf[CMSG_ALIGN(sizeof(struct in6_flowlabel_req)) + cmsglen];
939                 struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf;
940                 int freq_len = sizeof(*freq);
941                 if (srcrt)
942                         freq_len = CMSG_ALIGN(sizeof(*freq)) + srcrt->cmsg_len;
943                 memset(freq, 0, sizeof(*freq));
944                 freq->flr_label = htonl(flowlabel&0xFFFFF);
945                 freq->flr_action = IPV6_FL_A_GET;
946                 freq->flr_flags = IPV6_FL_F_CREATE;
947                 freq->flr_share = IPV6_FL_S_EXCL;
948                 memcpy(&freq->flr_dst, &whereto.sin6_addr, 16);
949                 if (srcrt)
950                         memcpy(freq_buf + CMSG_ALIGN(sizeof(*freq)), srcrt, srcrt->cmsg_len);
951                 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR,
952                                freq, freq_len) == -1) {
953                         perror ("can't set flowlabel");
954                         exit(2);
955                 }
956                 flowlabel = freq->flr_label;
957                 if (srcrt) {
958                         cmsglen = (char*)srcrt - (char*)cmsgbuf;
959                         srcrt = NULL;
960                 }
961 #else
962                 fprintf(stderr, "Flow labels are not supported.\n");
963                 exit(2);
964 #endif
965
966 #ifdef IPV6_FLOWINFO_SEND
967                 whereto.sin6_flowinfo = flowlabel;
968                 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_FLOWINFO_SEND,
969                                &on, sizeof(on)) == -1) {
970                         perror ("can't send flowinfo");
971                         exit(2);
972                 }
973 #else
974                 fprintf(stderr, "Flowinfo is not supported.\n");
975                 exit(2);
976 #endif
977         }
978
979         printf("PING %s(%s) ", hostname, pr_addr(&whereto.sin6_addr));
980         if (flowlabel)
981                 printf(", flow 0x%05x, ", (unsigned)ntohl(flowlabel));
982         if (device || (options&F_STRICTSOURCE)) {
983                 printf("from %s %s: ",
984                        pr_addr_n(&source.sin6_addr), device ? : "");
985         }
986         printf("%d data bytes\n", datalen);
987
988         setup(icmp_sock);
989
990         main_loop(icmp_sock, packet, packlen);
991 }
992
993 int receive_error_msg()
994 {
995         int res;
996         char cbuf[512];
997         struct iovec  iov;
998         struct msghdr msg;
999         struct cmsghdr *cmsg;
1000         struct sock_extended_err *e;
1001         struct icmp6_hdr icmph;
1002         struct sockaddr_in6 target;
1003         int net_errors = 0;
1004         int local_errors = 0;
1005         int saved_errno = errno;
1006
1007         iov.iov_base = &icmph;
1008         iov.iov_len = sizeof(icmph);
1009         msg.msg_name = (void*)&target;
1010         msg.msg_namelen = sizeof(target);
1011         msg.msg_iov = &iov;
1012         msg.msg_iovlen = 1;
1013         msg.msg_flags = 0;
1014         msg.msg_control = cbuf;
1015         msg.msg_controllen = sizeof(cbuf);
1016
1017         res = recvmsg(icmp_sock, &msg, MSG_ERRQUEUE|MSG_DONTWAIT);
1018         if (res < 0)
1019                 goto out;
1020
1021         e = NULL;
1022         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1023                 if (cmsg->cmsg_level == SOL_IPV6) {
1024                         if (cmsg->cmsg_type == IPV6_RECVERR)
1025                                 e = (struct sock_extended_err *)CMSG_DATA(cmsg);
1026                 }
1027         }
1028         if (e == NULL)
1029                 abort();
1030
1031         if (e->ee_origin == SO_EE_ORIGIN_LOCAL) {
1032                 local_errors++;
1033                 if (options & F_QUIET)
1034                         goto out;
1035                 if (options & F_FLOOD)
1036                         write(STDOUT_FILENO, "E", 1);
1037                 else if (e->ee_errno != EMSGSIZE)
1038                         fprintf(stderr, "ping: local error: %s\n", strerror(e->ee_errno));
1039                 else
1040                         fprintf(stderr, "ping: local error: Message too long, mtu=%u\n", e->ee_info);
1041                 nerrors++;
1042         } else if (e->ee_origin == SO_EE_ORIGIN_ICMP6) {
1043                 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(e+1);
1044
1045                 if (res < sizeof(icmph) ||
1046                     memcmp(&target.sin6_addr, &whereto.sin6_addr, 16) ||
1047                     icmph.icmp6_type != ICMP6_ECHO_REQUEST ||
1048                     icmph.icmp6_id != ident) {
1049                         /* Not our error, not an error at all. Clear. */
1050                         saved_errno = 0;
1051                         goto out;
1052                 }
1053
1054                 net_errors++;
1055                 nerrors++;
1056                 if (options & F_QUIET)
1057                         goto out;
1058                 if (options & F_FLOOD) {
1059                         write(STDOUT_FILENO, "\bE", 2);
1060                 } else {
1061                         print_timestamp();
1062                         printf("From %s icmp_seq=%u ", pr_addr(&sin6->sin6_addr), ntohs(icmph.icmp6_seq));
1063                         pr_icmph(e->ee_type, e->ee_code, e->ee_info);
1064                         putchar('\n');
1065                         fflush(stdout);
1066                 }
1067         }
1068
1069 out:
1070         errno = saved_errno;
1071         return net_errors ? : -local_errors;
1072 }
1073
1074 /*
1075  * pinger --
1076  *      Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
1077  * will be added on by the kernel.  The ID field is our UNIX process ID,
1078  * and the sequence number is an ascending integer.  The first 8 bytes
1079  * of the data portion are used to hold a UNIX "timeval" struct in VAX
1080  * byte-order, to compute the round-trip time.
1081  */
1082 int build_echo(__u8 *_icmph)
1083 {
1084         struct icmp6_hdr *icmph;
1085         int cc;
1086
1087         icmph = (struct icmp6_hdr *)_icmph;
1088         icmph->icmp6_type = ICMP6_ECHO_REQUEST;
1089         icmph->icmp6_code = 0;
1090         icmph->icmp6_cksum = 0;
1091         icmph->icmp6_seq = htons(ntransmitted+1);
1092         icmph->icmp6_id = ident;
1093
1094         if (timing)
1095                 gettimeofday((struct timeval *)&outpack[8],
1096                     (struct timezone *)NULL);
1097
1098         cc = datalen + 8;                       /* skips ICMP portion */
1099
1100         return cc;
1101 }
1102
1103 int build_niquery(__u8 *_nih)
1104 {
1105         struct ni_hdr *nih;
1106         int cc;
1107
1108         nih = (struct ni_hdr *)_nih;
1109         nih->ni_cksum = 0;
1110
1111         CLR(ntohs((*(__u16*)(nih->ni_nonce))) % mx_dup_ck);
1112
1113         nih->ni_type = ICMPV6_NI_QUERY;
1114         cc = sizeof(*nih);
1115         datalen = 0;
1116
1117         memcpy(nih->ni_nonce, ni_nonce, sizeof(nih->ni_nonce));
1118         *(__u16*)(nih->ni_nonce) = htons(ntransmitted + 1);
1119
1120         nih->ni_code = ni_subject_type;
1121         nih->ni_qtype = htons(ni_query);
1122         nih->ni_flags = ni_flag;
1123         memcpy(nih + 1, ni_subject, ni_subject_len);
1124         cc += ni_subject_len;
1125
1126         return cc;
1127 }
1128
1129 int send_probe(void)
1130 {
1131         int len, cc;
1132
1133         CLR((ntransmitted+1) % mx_dup_ck);
1134
1135         if (ni_query >= 0)
1136                 len = build_niquery(outpack);
1137         else
1138                 len = build_echo(outpack);
1139
1140         if (cmsglen == 0) {
1141                 cc = sendto(icmp_sock, (char *)outpack, len, confirm,
1142                             (struct sockaddr *) &whereto,
1143                             sizeof(struct sockaddr_in6));
1144         } else {
1145                 struct msghdr mhdr;
1146                 struct iovec iov;
1147
1148                 iov.iov_len  = len;
1149                 iov.iov_base = outpack;
1150
1151                 mhdr.msg_name = &whereto;
1152                 mhdr.msg_namelen = sizeof(struct sockaddr_in6);
1153                 mhdr.msg_iov = &iov;
1154                 mhdr.msg_iovlen = 1;
1155                 mhdr.msg_control = cmsgbuf;
1156                 mhdr.msg_controllen = cmsglen;
1157
1158                 cc = sendmsg(icmp_sock, &mhdr, confirm);
1159         }
1160         confirm = 0;
1161
1162         return (cc == len ? 0 : cc);
1163 }
1164
1165 void pr_echo_reply(__u8 *_icmph, int cc)
1166 {
1167         struct icmp6_hdr *icmph = (struct icmp6_hdr *) _icmph;
1168         printf(" icmp_seq=%u", ntohs(icmph->icmp6_seq));
1169 };
1170
1171 static void putchar_safe(char c)
1172 {
1173         if (isprint(c))
1174                 putchar(c);
1175         else
1176                 printf("\\%03o", c);
1177 }
1178
1179 void pr_niquery_reply_name(struct ni_hdr *nih, int len)
1180 {
1181         __u8 *h = (__u8 *)(nih + 1);
1182         __u8 *p = h + 4;
1183         __u8 *end = (__u8 *)nih + len;
1184         int continued = 0;
1185         char buf[1024];
1186         int ret;
1187
1188         len -= sizeof(struct ni_hdr) + 4;
1189
1190         if (len < 0) {
1191                 printf(" parse error (too short)");
1192                 return;
1193         }
1194         while (p < end) {
1195                 int fqdn = 1;
1196                 int len;
1197                 int i;
1198
1199                 memset(buf, 0xff, sizeof(buf));
1200
1201                 if (continued)
1202                         putchar(',');
1203
1204                 ret = dn_expand(h, end, p, buf, sizeof(buf));
1205                 if (ret < 0) {
1206                         printf(" parse error (truncated)");
1207                         break;
1208                 }
1209                 if (p + ret < end && *(p + ret) == '\0')
1210                         fqdn = 0;
1211                 len = strlen(buf);
1212
1213                 putchar(' ');
1214                 for (i = 0; i < strlen(buf); i++)
1215                         putchar_safe(buf[i]);
1216                 if (fqdn)
1217                         putchar('.');
1218
1219                 p += ret + !fqdn;
1220
1221                 continued = 1;
1222         }
1223 }
1224
1225 void pr_niquery_reply_addr(struct ni_hdr *nih, int len)
1226 {
1227         __u8 *h = (__u8 *)(nih + 1);
1228         __u8 *p = h + 4;
1229         __u8 *end = (__u8 *)nih + len;
1230         int af;
1231         int aflen;
1232         int continued = 0;
1233         int truncated;
1234         char buf[1024];
1235
1236         switch (ntohs(nih->ni_qtype)) {
1237         case NI_QTYPE_IPV4ADDR:
1238                 af = AF_INET;
1239                 aflen = sizeof(struct in_addr);
1240                 truncated = nih->ni_flags & NI_IPV6ADDR_F_TRUNCATE;
1241                 break;
1242         case NI_QTYPE_IPV6ADDR:
1243                 af = AF_INET6;
1244                 aflen = sizeof(struct in6_addr);
1245                 truncated = nih->ni_flags & NI_IPV4ADDR_F_TRUNCATE;
1246                 break;
1247         default:
1248                 /* should not happen */
1249                 af = aflen = truncated = 0;
1250         }
1251         p = h;
1252         if (len < 0) {
1253                 printf(" parse error (too short)");
1254                 return;
1255         }
1256
1257         while (p < end) {
1258                 if (continued)
1259                         putchar(',');
1260
1261                 if (p + sizeof(__u32) + aflen > end) {
1262                         printf(" parse error (truncated)");
1263                         break;
1264                 }
1265                 if (!inet_ntop(af, p + sizeof(__u32), buf, sizeof(buf)))
1266                         printf(" unexpeced error in inet_ntop(%s)",
1267                                strerror(errno));
1268                 else
1269                         printf(" %s", buf);
1270                 p += sizeof(__u32) + aflen;
1271
1272                 continued = 1;
1273         }
1274         if (truncated)
1275                 printf(" (truncated)");
1276 }
1277
1278 void pr_niquery_reply(__u8 *_nih, int len)
1279 {
1280         struct ni_hdr *nih = (struct ni_hdr *)_nih;
1281
1282         switch (nih->ni_code) {
1283         case NI_SUCCESS:
1284                 switch (ntohs(nih->ni_qtype)) {
1285                 case NI_QTYPE_NAME:
1286                         pr_niquery_reply_name(nih, len);
1287                         break;
1288                 case NI_QTYPE_IPV4ADDR:
1289                 case NI_QTYPE_IPV6ADDR:
1290                         pr_niquery_reply_addr(nih, len);
1291                         break;
1292                 default:
1293                         printf(" unknown qtype(0x%02x)", ntohs(nih->ni_qtype));
1294                 }
1295                 break;
1296         case NI_REFUSED:
1297                 printf(" refused");
1298                 break;
1299         case NI_UNKNOWN:
1300                 printf(" unknown");
1301                 break;
1302         default:
1303                 printf(" unknown code(%02x)", ntohs(nih->ni_code));
1304         }
1305         putchar(';');
1306 }
1307
1308 /*
1309  * parse_reply --
1310  *      Print out the packet, if it came from us.  This logic is necessary
1311  * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
1312  * which arrive ('tis only fair).  This permits multiple copies of this
1313  * program to be run without having intermingled output (or statistics!).
1314  */
1315 int
1316 parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
1317 {
1318         struct sockaddr_in6 *from = addr;
1319         __u8 *buf = msg->msg_iov->iov_base;
1320         struct cmsghdr *c;
1321         struct icmp6_hdr *icmph;
1322         int hops = -1;
1323
1324         for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) {
1325                 if (c->cmsg_level != SOL_IPV6)
1326                         continue;
1327                 switch(c->cmsg_type) {
1328                 case IPV6_HOPLIMIT:
1329 #ifdef IPV6_2292HOPLIMIT
1330                 case IPV6_2292HOPLIMIT:
1331 #endif
1332                         if (c->cmsg_len < CMSG_LEN(sizeof(int)))
1333                                 continue;
1334                         hops = *(int*)CMSG_DATA(c);
1335                 }
1336         }
1337
1338
1339         /* Now the ICMP part */
1340
1341         icmph = (struct icmp6_hdr *) buf;
1342         if (cc < 8) {
1343                 if (options & F_VERBOSE)
1344                         fprintf(stderr, "ping: packet too short (%d bytes)\n", cc);
1345                 return 1;
1346         }
1347
1348         if (icmph->icmp6_type == ICMP6_ECHO_REPLY) {
1349                 if (icmph->icmp6_id != ident)
1350                         return 1;
1351                 if (gather_statistics((__u8*)icmph, sizeof(*icmph), cc,
1352                                       ntohs(icmph->icmp6_seq),
1353                                       hops, 0, tv, pr_addr(&from->sin6_addr),
1354                                       pr_echo_reply))
1355                         return 0;
1356         } else if (icmph->icmp6_type == ICMPV6_NI_REPLY) {
1357                 struct ni_hdr *nih = (struct ni_hdr *)icmph;
1358                 __u16 seq = ntohs(*(__u16 *)nih->ni_nonce);
1359                 if (memcmp(&nih->ni_nonce[2], &ni_nonce[2], sizeof(ni_nonce) - sizeof(__u16)))
1360                         return 1;
1361                 if (gather_statistics((__u8*)icmph, sizeof(*icmph), cc,
1362                                       seq,
1363                                       hops, 0, tv, pr_addr(&from->sin6_addr),
1364                                       pr_niquery_reply))
1365                         return 0;
1366         } else {
1367                 int nexthdr;
1368                 struct ip6_hdr *iph1 = (struct ip6_hdr*)(icmph+1);
1369                 struct icmp6_hdr *icmph1 = (struct icmp6_hdr *)(iph1+1);
1370
1371                 /* We must not ever fall here. All the messages but
1372                  * echo reply are blocked by filter and error are
1373                  * received with IPV6_RECVERR. Ugly code is preserved
1374                  * however, just to remember what crap we avoided
1375                  * using RECVRERR. :-)
1376                  */
1377
1378                 if (cc < 8+sizeof(struct ip6_hdr)+8)
1379                         return 1;
1380
1381                 if (memcmp(&iph1->ip6_dst, &whereto.sin6_addr, 16))
1382                         return 1;
1383
1384                 nexthdr = iph1->ip6_nxt;
1385
1386                 if (nexthdr == 44) {
1387                         nexthdr = *(__u8*)icmph1;
1388                         icmph1++;
1389                 }
1390                 if (nexthdr == IPPROTO_ICMPV6) {
1391                         if (icmph1->icmp6_type != ICMP6_ECHO_REQUEST ||
1392                             icmph1->icmp6_id != ident)
1393                                 return 1;
1394                         acknowledge(ntohs(icmph1->icmp6_seq));
1395                         if (working_recverr)
1396                                 return 0;
1397                         nerrors++;
1398                         if (options & F_FLOOD) {
1399                                 write(STDOUT_FILENO, "\bE", 2);
1400                                 return 0;
1401                         }
1402                         print_timestamp();
1403                         printf("From %s: icmp_seq=%u ", pr_addr(&from->sin6_addr), ntohs(icmph1->icmp6_seq));
1404                 } else {
1405                         /* We've got something other than an ECHOREPLY */
1406                         if (!(options & F_VERBOSE) || uid)
1407                                 return 1;
1408                         print_timestamp();
1409                         printf("From %s: ", pr_addr(&from->sin6_addr));
1410                 }
1411                 pr_icmph(icmph->icmp6_type, icmph->icmp6_code, ntohl(icmph->icmp6_mtu));
1412         }
1413
1414         if (!(options & F_FLOOD)) {
1415                 if (options & F_AUDIBLE)
1416                         putchar('\a');
1417                 putchar('\n');
1418                 fflush(stdout);
1419         } else {
1420                 putchar('\a');
1421                 fflush(stdout);
1422         }
1423         return 0;
1424 }
1425
1426
1427 int pr_icmph(__u8 type, __u8 code, __u32 info)
1428 {
1429         switch(type) {
1430         case ICMP6_DST_UNREACH:
1431                 printf("Destination unreachable: ");
1432                 switch (code) {
1433                 case ICMP6_DST_UNREACH_NOROUTE:
1434                         printf("No route");
1435                         break;
1436                 case ICMP6_DST_UNREACH_ADMIN:
1437                         printf("Administratively prohibited");
1438                         break;
1439                 case ICMP6_DST_UNREACH_BEYONDSCOPE:
1440                         printf("Beyond scope of source address");
1441                         break;
1442                 case ICMP6_DST_UNREACH_ADDR:
1443                         printf("Address unreachable");
1444                         break;
1445                 case ICMP6_DST_UNREACH_NOPORT:
1446                         printf("Port unreachable");
1447                         break;
1448                 default:
1449                         printf("Unknown code %d", code);
1450                         break;
1451                 }
1452                 break;
1453         case ICMP6_PACKET_TOO_BIG:
1454                 printf("Packet too big: mtu=%u", info);
1455                 if (code)
1456                         printf(", code=%d", code);
1457                 break;
1458         case ICMP6_TIME_EXCEEDED:
1459                 printf("Time exceeded: ");
1460                 if (code == ICMP6_TIME_EXCEED_TRANSIT)
1461                         printf("Hop limit");
1462                 else if (code == ICMP6_TIME_EXCEED_REASSEMBLY)
1463                         printf("Defragmentation failure");
1464                 else
1465                         printf("code %d", code);
1466                 break;
1467         case ICMP6_PARAM_PROB:
1468                 printf("Parameter problem: ");
1469                 if (code == ICMP6_PARAMPROB_HEADER)
1470                         printf("Wrong header field ");
1471                 else if (code == ICMP6_PARAMPROB_NEXTHEADER)
1472                         printf("Unknown header ");
1473                 else if (code == ICMP6_PARAMPROB_OPTION)
1474                         printf("Unknown option ");
1475                 else
1476                         printf("code %d ", code);
1477                 printf ("at %u", info);
1478                 break;
1479         case ICMP6_ECHO_REQUEST:
1480                 printf("Echo request");
1481                 break;
1482         case ICMP6_ECHO_REPLY:
1483                 printf("Echo reply");
1484                 break;
1485         case MLD_LISTENER_QUERY:
1486                 printf("MLD Query");
1487                 break;
1488         case MLD_LISTENER_REPORT:
1489                 printf("MLD Report");
1490                 break;
1491         case MLD_LISTENER_REDUCTION:
1492                 printf("MLD Reduction");
1493                 break;
1494         default:
1495                 printf("unknown icmp type");
1496
1497         }
1498         return 0;
1499 }
1500
1501 #include <linux/filter.h>
1502
1503 void install_filter(void)
1504 {
1505         static int once;
1506         static struct sock_filter insns[] = {
1507                 BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 4),  /* Load icmp echo ident */
1508                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1),  /* Ours? */
1509                 BPF_STMT(BPF_RET|BPF_K, ~0U),  /* Yes, it passes. */
1510                 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0),  /* Load icmp type */
1511                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMP6_ECHO_REPLY, 1, 0), /* Echo? */
1512                 BPF_STMT(BPF_RET|BPF_K, ~0U), /* No. It passes. This must not happen. */
1513                 BPF_STMT(BPF_RET|BPF_K, 0), /* Echo with wrong ident. Reject. */
1514         };
1515         static struct sock_fprog filter = {
1516                 sizeof insns / sizeof(insns[0]),
1517                 insns
1518         };
1519
1520         if (once)
1521                 return;
1522         once = 1;
1523
1524         /* Patch bpflet for current identifier. */
1525         insns[1] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1);
1526
1527         if (setsockopt(icmp_sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)))
1528                 perror("WARNING: failed to install socket filter\n");
1529 }
1530
1531
1532 /*
1533  * pr_addr --
1534  *      Return an ascii host address as a dotted quad and optionally with
1535  * a hostname.
1536  */
1537 char * pr_addr(struct in6_addr *addr)
1538 {
1539         struct hostent *hp = NULL;
1540
1541         if (!(options&F_NUMERIC))
1542                 hp = gethostbyaddr((__u8*)addr, sizeof(struct in6_addr), AF_INET6);
1543
1544         return hp ? hp->h_name : pr_addr_n(addr);
1545 }
1546
1547 char * pr_addr_n(struct in6_addr *addr)
1548 {
1549         static char str[64];
1550         inet_ntop(AF_INET6, addr, str, sizeof(str));
1551         return str;
1552 }
1553
1554 void usage(void)
1555 {
1556         fprintf(stderr,
1557 "Usage: ping6 [-LUdfnqrvVaAD] [-c count] [-i interval] [-w deadline]\n"
1558 "             [-p pattern] [-s packetsize] [-t ttl] [-I interface]\n"
1559 "             [-M pmtudisc-hint] [-S sndbuf] [-F flowlabel] [-Q tclass]\n"
1560 "             [[-N nodeinfo-option] ...]\n"
1561 "             [hop1 ...] destination\n");
1562         exit(2);
1563 }