3 * Modified for AF_INET6 by Pedro Roque
7 * Original copyright notice included bellow
11 * Copyright (c) 1989 The Regents of the University of California.
12 * All rights reserved.
14 * This code is derived from software contributed to Berkeley by
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
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.
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
48 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
49 All rights reserved.\n";
55 * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
56 * measure round-trip-delays and packet loss across network paths.
60 * U. S. Army Ballistic Research Laboratory
64 * Public Domain. Distribution Unlimited.
66 * More statistics could always be gathered.
67 * This program has to run SUID to ROOT to access the ICMP socket.
69 #include "ping_common.h"
71 #include <linux/filter.h>
72 #include <netinet/ip6.h>
73 #include <netinet/icmp6.h>
76 #include "ping6_niquery.h"
79 #define SOL_IPV6 IPPROTO_IPV6
83 #define SOL_ICMPV6 IPPROTO_ICMPV6
87 #ifndef ICMP6_DST_UNREACH_BEYONDSCOPE
88 #define ICMP6_DST_UNREACH_BEYONDSCOPE ICMP6_DST_UNREACH_NOTNEIGHBOR
91 #ifndef IPV6_SRCRT_TYPE_0
92 #define IPV6_SRCRT_TYPE_0 0
95 #ifndef MLD_LISTENER_QUERY
96 #define MLD_LISTENER_QUERY 130
97 #define MLD_LISTENER_REPORT 131
98 #define MLD_LISTENER_REDUCTION 132
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)
105 #ifndef ICMP6_FILTER_WILLPASS
106 #define ICMP6_FILTER_WILLPASS(type, filterp) \
107 (BIT_TEST((type), filterp) == 0)
109 #define ICMP6_FILTER_WILLBLOCK(type, filterp) \
110 BIT_TEST((type), filterp)
112 #define ICMP6_FILTER_SETPASS(type, filterp) \
113 BIT_CLEAR((type), filterp)
115 #define ICMP6_FILTER_SETBLOCK(type, filterp) \
116 BIT_SET((type), filterp)
118 #define ICMP6_FILTER_SETPASSALL(filterp) \
119 memset(filterp, 0, sizeof(struct icmp6_filter));
121 #define ICMP6_FILTER_SETBLOCKALL(filterp) \
122 memset(filterp, 0xFF, sizeof(struct icmp6_filter));
125 #define MAXPACKET 128000 /* max packet size */
128 #define HAVE_SIN6_SCOPEID 1
131 #ifndef SCOPE_DELIMITER
132 # define SCOPE_DELIMITER '%'
137 struct cmsghdr *srcrt;
139 struct sockaddr_in6 whereto; /* who to ping */
140 u_char outpack[MAXPACKET];
141 int maxpacket = sizeof(outpack);
143 static unsigned char cmsgbuf[4096];
144 static int cmsglen = 0;
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));
151 struct sockaddr_in6 source;
155 static int icmp_sock;
157 #include <openssl/md5.h>
159 /* Node Information query */
162 void *ni_subject = NULL;
163 int ni_subject_len = 0;
164 int ni_subject_type = 0;
169 static struct in6_addr in6_anyaddr;
170 static __inline__ int ipv6_addr_any(struct in6_addr *addr)
172 return (memcmp(addr, &in6_anyaddr, 16) == 0);
175 size_t inet6_srcrt_space(int type, int segments)
177 if (type != 0 || segments > 24)
180 return (sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0) +
181 segments * sizeof(struct in6_addr));
184 extern struct cmsghdr * inet6_srcrt_init(void *bp, int type)
186 struct cmsghdr *cmsg;
191 memset(bp, 0, sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0));
192 cmsg = (struct cmsghdr *) bp;
194 cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0);
195 cmsg->cmsg_level = SOL_IPV6;
196 cmsg->cmsg_type = IPV6_RTHDR;
201 int inet6_srcrt_add(struct cmsghdr *cmsg, const struct in6_addr *addr)
203 struct ip6_rthdr0 *hdr;
205 hdr = (struct ip6_rthdr0 *) CMSG_DATA(cmsg);
207 cmsg->cmsg_len += sizeof(struct in6_addr);
208 hdr->ip6r0_len += sizeof(struct in6_addr) / 8;
210 memcpy(&hdr->ip6r0_addr[hdr->ip6r0_segleft++], addr,
211 sizeof(struct in6_addr));
216 unsigned int if_name2index(const char *ifname)
218 unsigned int i = if_nametoindex(ifname);
220 fprintf(stderr, "ping: unknown iface %s\n", ifname);
226 struct niquery_option {
231 int (*handler)(int index, const char *arg);
234 #define NIQUERY_OPTION(_name, _has_arg, _data, _handler) \
237 .namelen = sizeof(_name) - 1, \
238 .has_arg = _has_arg, \
240 .handler = _handler \
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);
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),
270 static int niquery_set_qtype(int type)
272 if (ni_query >= 0 && ni_query != type) {
273 printf("Qtype conflict\n");
280 static int niquery_option_name_handler(int index, const char *arg)
282 if (niquery_set_qtype(NI_QTYPE_NAME) < 0)
287 static int niquery_option_ipv6_handler(int index, const char *arg)
289 if (niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0)
294 static int niquery_option_ipv6_flag_handler(int index, const char *arg)
296 if (niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0)
298 ni_flag |= niquery_options[index].data;
302 static int niquery_option_ipv4_handler(int index, const char *arg)
304 if (niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0)
309 static int niquery_option_ipv4_flag_handler(int index, const char *arg)
311 if (niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0)
313 ni_flag |= niquery_options[index].data;
317 static int niquery_set_subject_type(int type)
319 if (ni_subject_type && ni_subject_type != type) {
320 printf("Subject type conflict\n");
323 ni_subject_type = type;
327 #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
328 #define OFFSET_OF(type,elem) ((size_t)&((type *)0)->elem)
330 static int niquery_option_subject_addr_handler(int index, const char *arg)
332 struct addrinfo hints, *ai0, *ai;
336 if (niquery_set_subject_type(niquery_options[index].data) < 0)
339 ni_subject_type = niquery_options[index].data;
341 switch (niquery_options[index].data) {
343 ni_subject_len = sizeof(struct in6_addr);
344 offset = OFFSET_OF(struct sockaddr_in6, sin6_addr);
345 hints.ai_family = AF_INET6;
348 ni_subject_len = sizeof(struct in_addr);
349 offset = OFFSET_OF(struct sockaddr_in, sin_addr);
350 hints.ai_family = AF_INET;
353 /* should not happen. */
357 hints.ai_socktype = SOCK_DGRAM;
359 gai = getaddrinfo(arg, 0, &hints, &ai0);
361 fprintf(stderr, "Unknown host: %s\n", arg);
365 for (ai = ai0; ai; ai = ai->ai_next) {
366 void *p = malloc(ni_subject_len);
369 memcpy(p, (__u8 *)ai->ai_addr + offset, ni_subject_len);
379 static int niquery_count_dots(const char *arg)
383 for (p = arg; *p; p++) {
390 static int niquery_option_subject_name_handler(int index, const char *arg)
392 unsigned char *dnptrs[2], **dpp, **lastdnptr;
396 size_t buflen = strlen(arg) + 1;
397 int fqdn = niquery_options[index].data;
399 if (niquery_set_subject_type(NI_SUBJ_NAME) < 0)
403 /* guess if hostname is FQDN */
404 fqdn = niquery_count_dots(arg) ? 1 : -1;
408 buf = malloc(buflen + 1);
412 fprintf(stderr, "ping6: out of memory.\n");
416 ni_group = ni_groupaddr(name);
418 p = strchr(name, '%');
423 lastdnptr = &dnptrs[ARRAY_SIZE(dnptrs)];
425 *dpp++ = (unsigned char *)buf;
428 n = dn_comp(name, (unsigned char *)buf, buflen, dnptrs, lastdnptr);
430 fprintf(stderr, "ping6: Inappropriate subject name: %s\n", buf);
441 ni_subject_len = n + (fqdn < 0);
447 int niquery_option_handler(const char *opt_arg)
449 struct niquery_option *p;
452 for (i = 0, p = niquery_options; p->name; i++, p++) {
453 if (strncmp(p->name, opt_arg, p->namelen))
456 if (opt_arg[p->namelen] == '\0') {
457 ret = p->handler(i, NULL);
462 if (opt_arg[p->namelen] == '=') {
463 ret = p->handler(i, &opt_arg[p->namelen] + 1);
472 char *ni_groupaddr(const char *name)
476 static char nigroup_buf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ];
478 char buf[64], *p = buf, *q;
482 fprintf(stderr, "ping6: memory allocation failure.\n");
486 len = strcspn(name, ".%");
488 fprintf(stderr, "ping6: label too long for subject: %s\n",
493 q = strrchr(name, '%');
494 if (q && strlen(q + 1) >= IFNAMSIZ) {
495 fprintf(stderr, "ping6: scope too long: %s\n",
501 for (i = 0; i < len; i++)
502 *p++ = isupper(name[i]) ? tolower(name[i]) : name[i];
505 MD5_Update(&ctxt, buf, len + 1);
506 MD5_Final(digest, &ctxt);
508 sprintf(nigroup_buf, "ff02::2:%02x%02x:%02x%02x",
509 digest[0], digest[1], digest[2], digest[3]);
512 strcat(nigroup_buf, q);
516 int main(int argc, char *argv[])
518 int ch, hold, packlen;
521 struct addrinfo hints, *ai;
523 struct sockaddr_in6 firsthop;
525 struct icmp6_filter filter;
528 int csum_offset, sz_opt;
530 static uint32_t scope_id = 0;
532 icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
533 socket_errno = errno;
537 perror("ping: setuid");
541 source.sin6_family = AF_INET6;
542 memset(&firsthop, 0, sizeof(firsthop));
543 firsthop.sin6_family = AF_INET6;
546 while ((ch = getopt(argc, argv, COMMON_OPTSTR "F:N:")) != EOF) {
549 sscanf(optarg, "%x", &flowlabel);
550 options |= F_FLOWINFO;
553 sscanf(optarg, "%x", &tclass);
557 if (strchr(optarg, ':')) {
558 char *p, *addr = strdup(optarg);
561 fprintf(stderr, "ping: out of memory\n");
565 p = strchr(addr, SCOPE_DELIMITER);
568 device = optarg + (p - addr) + 1;
571 if (inet_pton(AF_INET6, addr, (char*)&source.sin6_addr) <= 0) {
572 fprintf(stderr, "ping: invalid source address %s\n", optarg);
576 options |= F_STRICTSOURCE;
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;
591 fprintf(stderr, "ping: wrong value for -M: do, dont, want are valid ones.\n");
596 printf("ping6 utility, iputils-ss%s\n", SNAPSHOT);
599 if (niquery_option_handler(optarg) < 0) {
615 struct in6_addr *addr;
620 space = inet6_srcrt_space(IPV6_SRCRT_TYPE_0, argc - 1);
623 fprintf(stderr, "srcrt_space failed\n");
626 if (space + cmsglen > sizeof(cmsgbuf)) {
627 fprintf(stderr, "no room for options\n");
631 srcrt = (struct cmsghdr*)(cmsgbuf+cmsglen);
632 cmsglen += CMSG_ALIGN(space);
633 inet6_srcrt_init(srcrt, IPV6_SRCRT_TYPE_0);
638 memset(&hints, 0, sizeof(hints));
639 hints.ai_family = AF_INET6;
640 gai = getaddrinfo(target, NULL, &hints, &ai);
642 fprintf(stderr, "unknown host\n");
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");
655 } else if (!scope_id) {
656 scope_id = firsthop.sin6_scope_id;
668 for (i = 0; i < 8; i++)
669 ni_nonce[i] = rand();
672 ni_subject = &whereto.sin6_addr;
673 ni_subject_len = sizeof(whereto.sin6_addr);
674 ni_subject_type = NI_SUBJ_IPV6;
680 else if (argc == 1) {
683 if (ni_query < 0 && ni_subject_type != NI_SUBJ_NAME)
688 memset(&hints, 0, sizeof(hints));
689 hints.ai_family = AF_INET6;
690 gai = getaddrinfo(target, NULL, &hints, &ai);
692 fprintf(stderr, "unknown host\n");
696 memcpy(&whereto, ai->ai_addr, sizeof(whereto));
697 whereto.sin6_port = htons(IPPROTO_ICMPV6);
699 if (memchr(target, ':', strlen(target)))
700 options |= F_NUMERIC;
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");
712 } else if (!scope_id) {
713 scope_id = firsthop.sin6_scope_id;
720 if (ipv6_addr_any(&source.sin6_addr)) {
722 int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0);
729 #if defined(IPV6_RECVPKTINFO) || defined(HAVE_SIN6_SCOPEID)
730 unsigned int iface = if_name2index(device);
732 #ifdef IPV6_RECVPKTINFO
733 struct in6_pktinfo ipi;
735 memset(&ipi, 0, sizeof(ipi));
736 ipi.ipi6_ifindex = iface;
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;
745 #ifdef IPV6_RECVPKTINFO
746 setsockopt(probe_fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof(ipi)) == -1 &&
748 setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) {
749 perror("setsockopt(SO_BINDTODEVICE)");
752 firsthop.sin6_port = htons(1025);
753 if (connect(probe_fd, (struct sockaddr*)&firsthop, sizeof(firsthop)) == -1) {
757 alen = sizeof(source);
758 if (getsockname(probe_fd, (struct sockaddr*)&source, &alen) == -1) {
759 perror("getsockname");
762 source.sin6_port = 0;
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);
772 errno = socket_errno;
773 perror("ping: icmp open socket");
778 struct cmsghdr *cmsg;
779 struct in6_pktinfo *ipi;
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;
787 ipi = (struct in6_pktinfo*)CMSG_DATA(cmsg);
788 memset(ipi, 0, sizeof(*ipi));
789 ipi->ipi6_ifindex = if_name2index(device);
792 if ((whereto.sin6_addr.s6_addr16[0]&htons(0xff00)) == htons (0xff00)) {
794 if (interval < 1000) {
795 fprintf(stderr, "ping: multicast ping with too short interval.\n");
798 if (pmtudisc >= 0 && pmtudisc != IPV6_PMTUDISC_DO) {
799 fprintf(stderr, "ping: multicast ping does not fragment.\n");
804 pmtudisc = IPV6_PMTUDISC_DO;
808 if (setsockopt(icmp_sock, SOL_IPV6, IPV6_MTU_DISCOVER, &pmtudisc, sizeof(pmtudisc)) == -1) {
809 perror("ping: IPV6_MTU_DISCOVER");
814 if ((options&F_STRICTSOURCE) &&
815 bind(icmp_sock, (struct sockaddr*)&source, sizeof(source)) == -1) {
816 perror("ping: bind icmp socket");
820 if (datalen >= sizeof(struct timeval) && (ni_query < 0)) {
821 /* can we time transfer */
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");
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");
837 /* Estimate memory eaten by single packet. It is rough estimate.
838 * Actually, for small datalen's it depends on kernel side a lot. */
840 hold += ((hold+511)/512)*(40+16+64+160);
841 sock_setbufs(icmp_sock, hold);
845 sz_opt = sizeof(int);
847 err = setsockopt(icmp_sock, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sz_opt);
849 /* checksum should be enabled by default and setting this
850 * option might fail anyway.
852 fprintf(stderr, "setsockopt(RAW_CHECKSUM) failed - try to continue.");
857 * select icmp echo reply as icmp type to receive
860 ICMP6_FILTER_SETBLOCKALL(&filter);
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);
870 ICMP6_FILTER_SETPASS(ICMPV6_NI_REPLY, &filter);
872 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
874 err = setsockopt(icmp_sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
875 sizeof(struct icmp6_filter));
878 perror("setsockopt(ICMP6_FILTER)");
882 if (options & F_NOLOOP) {
884 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
885 &loop, sizeof(loop)) == -1) {
886 perror ("can't disable multicast loopback");
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");
896 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
897 &ttl, sizeof(ttl)) == -1) {
898 perror ("can't set unicast hop limit");
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
912 setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_HOPLIMIT,
913 &on, sizeof(on)) == -1
916 perror ("can't receive hop limit");
921 if (options & F_TCLASS) {
923 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_TCLASS,
924 &tclass, sizeof(tclass)) == -1) {
925 perror ("setsockopt(IPV6_TCLASS)");
929 fprintf(stderr, "Traffic class is not supported.\n");
933 if (options&F_FLOWINFO) {
934 #ifdef IPV6_FLOWINFO_SEND
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);
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);
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");
956 flowlabel = freq->flr_label;
958 cmsglen = (char*)srcrt - (char*)cmsgbuf;
962 fprintf(stderr, "Flow labels are not supported.\n");
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");
974 fprintf(stderr, "Flowinfo is not supported.\n");
979 printf("PING %s(%s) ", hostname, pr_addr(&whereto.sin6_addr));
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 ? : "");
986 printf("%d data bytes\n", datalen);
990 main_loop(icmp_sock, packet, packlen);
993 int receive_error_msg()
999 struct cmsghdr *cmsg;
1000 struct sock_extended_err *e;
1001 struct icmp6_hdr icmph;
1002 struct sockaddr_in6 target;
1004 int local_errors = 0;
1005 int saved_errno = errno;
1007 iov.iov_base = &icmph;
1008 iov.iov_len = sizeof(icmph);
1009 msg.msg_name = (void*)⌖
1010 msg.msg_namelen = sizeof(target);
1014 msg.msg_control = cbuf;
1015 msg.msg_controllen = sizeof(cbuf);
1017 res = recvmsg(icmp_sock, &msg, MSG_ERRQUEUE|MSG_DONTWAIT);
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);
1031 if (e->ee_origin == SO_EE_ORIGIN_LOCAL) {
1033 if (options & F_QUIET)
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));
1040 fprintf(stderr, "ping: local error: Message too long, mtu=%u\n", e->ee_info);
1042 } else if (e->ee_origin == SO_EE_ORIGIN_ICMP6) {
1043 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(e+1);
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. */
1056 if (options & F_QUIET)
1058 if (options & F_FLOOD) {
1059 write(STDOUT_FILENO, "\bE", 2);
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);
1070 errno = saved_errno;
1071 return net_errors ? : -local_errors;
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.
1082 int build_echo(__u8 *_icmph)
1084 struct icmp6_hdr *icmph;
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;
1095 gettimeofday((struct timeval *)&outpack[8],
1096 (struct timezone *)NULL);
1098 cc = datalen + 8; /* skips ICMP portion */
1103 int build_niquery(__u8 *_nih)
1108 nih = (struct ni_hdr *)_nih;
1111 CLR(ntohs((*(__u16*)(nih->ni_nonce))) % mx_dup_ck);
1113 nih->ni_type = ICMPV6_NI_QUERY;
1117 memcpy(nih->ni_nonce, ni_nonce, sizeof(nih->ni_nonce));
1118 *(__u16*)(nih->ni_nonce) = htons(ntransmitted + 1);
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;
1129 int send_probe(void)
1133 CLR((ntransmitted+1) % mx_dup_ck);
1136 len = build_niquery(outpack);
1138 len = build_echo(outpack);
1141 cc = sendto(icmp_sock, (char *)outpack, len, confirm,
1142 (struct sockaddr *) &whereto,
1143 sizeof(struct sockaddr_in6));
1149 iov.iov_base = outpack;
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;
1158 cc = sendmsg(icmp_sock, &mhdr, confirm);
1162 return (cc == len ? 0 : cc);
1165 void pr_echo_reply(__u8 *_icmph, int cc)
1167 struct icmp6_hdr *icmph = (struct icmp6_hdr *) _icmph;
1168 printf(" icmp_seq=%u", ntohs(icmph->icmp6_seq));
1171 static void putchar_safe(char c)
1176 printf("\\%03o", c);
1179 void pr_niquery_reply_name(struct ni_hdr *nih, int len)
1181 __u8 *h = (__u8 *)(nih + 1);
1183 __u8 *end = (__u8 *)nih + len;
1188 len -= sizeof(struct ni_hdr) + 4;
1191 printf(" parse error (too short)");
1199 memset(buf, 0xff, sizeof(buf));
1204 ret = dn_expand(h, end, p, buf, sizeof(buf));
1206 printf(" parse error (truncated)");
1209 if (p + ret < end && *(p + ret) == '\0')
1214 for (i = 0; i < strlen(buf); i++)
1215 putchar_safe(buf[i]);
1225 void pr_niquery_reply_addr(struct ni_hdr *nih, int len)
1227 __u8 *h = (__u8 *)(nih + 1);
1229 __u8 *end = (__u8 *)nih + len;
1236 switch (ntohs(nih->ni_qtype)) {
1237 case NI_QTYPE_IPV4ADDR:
1239 aflen = sizeof(struct in_addr);
1240 truncated = nih->ni_flags & NI_IPV6ADDR_F_TRUNCATE;
1242 case NI_QTYPE_IPV6ADDR:
1244 aflen = sizeof(struct in6_addr);
1245 truncated = nih->ni_flags & NI_IPV4ADDR_F_TRUNCATE;
1248 /* should not happen */
1249 af = aflen = truncated = 0;
1253 printf(" parse error (too short)");
1261 if (p + sizeof(__u32) + aflen > end) {
1262 printf(" parse error (truncated)");
1265 if (!inet_ntop(af, p + sizeof(__u32), buf, sizeof(buf)))
1266 printf(" unexpeced error in inet_ntop(%s)",
1270 p += sizeof(__u32) + aflen;
1275 printf(" (truncated)");
1278 void pr_niquery_reply(__u8 *_nih, int len)
1280 struct ni_hdr *nih = (struct ni_hdr *)_nih;
1282 switch (nih->ni_code) {
1284 switch (ntohs(nih->ni_qtype)) {
1286 pr_niquery_reply_name(nih, len);
1288 case NI_QTYPE_IPV4ADDR:
1289 case NI_QTYPE_IPV6ADDR:
1290 pr_niquery_reply_addr(nih, len);
1293 printf(" unknown qtype(0x%02x)", ntohs(nih->ni_qtype));
1303 printf(" unknown code(%02x)", ntohs(nih->ni_code));
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!).
1316 parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
1318 struct sockaddr_in6 *from = addr;
1319 __u8 *buf = msg->msg_iov->iov_base;
1321 struct icmp6_hdr *icmph;
1324 for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) {
1325 if (c->cmsg_level != SOL_IPV6)
1327 switch(c->cmsg_type) {
1329 #ifdef IPV6_2292HOPLIMIT
1330 case IPV6_2292HOPLIMIT:
1332 if (c->cmsg_len < CMSG_LEN(sizeof(int)))
1334 hops = *(int*)CMSG_DATA(c);
1339 /* Now the ICMP part */
1341 icmph = (struct icmp6_hdr *) buf;
1343 if (options & F_VERBOSE)
1344 fprintf(stderr, "ping: packet too short (%d bytes)\n", cc);
1348 if (icmph->icmp6_type == ICMP6_ECHO_REPLY) {
1349 if (icmph->icmp6_id != ident)
1351 if (gather_statistics((__u8*)icmph, sizeof(*icmph), cc,
1352 ntohs(icmph->icmp6_seq),
1353 hops, 0, tv, pr_addr(&from->sin6_addr),
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)))
1361 if (gather_statistics((__u8*)icmph, sizeof(*icmph), cc,
1363 hops, 0, tv, pr_addr(&from->sin6_addr),
1368 struct ip6_hdr *iph1 = (struct ip6_hdr*)(icmph+1);
1369 struct icmp6_hdr *icmph1 = (struct icmp6_hdr *)(iph1+1);
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. :-)
1378 if (cc < 8+sizeof(struct ip6_hdr)+8)
1381 if (memcmp(&iph1->ip6_dst, &whereto.sin6_addr, 16))
1384 nexthdr = iph1->ip6_nxt;
1386 if (nexthdr == 44) {
1387 nexthdr = *(__u8*)icmph1;
1390 if (nexthdr == IPPROTO_ICMPV6) {
1391 if (icmph1->icmp6_type != ICMP6_ECHO_REQUEST ||
1392 icmph1->icmp6_id != ident)
1394 acknowledge(ntohs(icmph1->icmp6_seq));
1395 if (working_recverr)
1398 if (options & F_FLOOD) {
1399 write(STDOUT_FILENO, "\bE", 2);
1403 printf("From %s: icmp_seq=%u ", pr_addr(&from->sin6_addr), ntohs(icmph1->icmp6_seq));
1405 /* We've got something other than an ECHOREPLY */
1406 if (!(options & F_VERBOSE) || uid)
1409 printf("From %s: ", pr_addr(&from->sin6_addr));
1411 pr_icmph(icmph->icmp6_type, icmph->icmp6_code, ntohl(icmph->icmp6_mtu));
1414 if (!(options & F_FLOOD)) {
1415 if (options & F_AUDIBLE)
1427 int pr_icmph(__u8 type, __u8 code, __u32 info)
1430 case ICMP6_DST_UNREACH:
1431 printf("Destination unreachable: ");
1433 case ICMP6_DST_UNREACH_NOROUTE:
1436 case ICMP6_DST_UNREACH_ADMIN:
1437 printf("Administratively prohibited");
1439 case ICMP6_DST_UNREACH_BEYONDSCOPE:
1440 printf("Beyond scope of source address");
1442 case ICMP6_DST_UNREACH_ADDR:
1443 printf("Address unreachable");
1445 case ICMP6_DST_UNREACH_NOPORT:
1446 printf("Port unreachable");
1449 printf("Unknown code %d", code);
1453 case ICMP6_PACKET_TOO_BIG:
1454 printf("Packet too big: mtu=%u", info);
1456 printf(", code=%d", code);
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");
1465 printf("code %d", code);
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 ");
1476 printf("code %d ", code);
1477 printf ("at %u", info);
1479 case ICMP6_ECHO_REQUEST:
1480 printf("Echo request");
1482 case ICMP6_ECHO_REPLY:
1483 printf("Echo reply");
1485 case MLD_LISTENER_QUERY:
1486 printf("MLD Query");
1488 case MLD_LISTENER_REPORT:
1489 printf("MLD Report");
1491 case MLD_LISTENER_REDUCTION:
1492 printf("MLD Reduction");
1495 printf("unknown icmp type");
1501 #include <linux/filter.h>
1503 void install_filter(void)
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. */
1515 static struct sock_fprog filter = {
1516 sizeof insns / sizeof(insns[0]),
1524 /* Patch bpflet for current identifier. */
1525 insns[1] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1);
1527 if (setsockopt(icmp_sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)))
1528 perror("WARNING: failed to install socket filter\n");
1534 * Return an ascii host address as a dotted quad and optionally with
1537 char * pr_addr(struct in6_addr *addr)
1539 struct hostent *hp = NULL;
1541 if (!(options&F_NUMERIC))
1542 hp = gethostbyaddr((__u8*)addr, sizeof(struct in6_addr), AF_INET6);
1544 return hp ? hp->h_name : pr_addr_n(addr);
1547 char * pr_addr_n(struct in6_addr *addr)
1549 static char str[64];
1550 inet_ntop(AF_INET6, addr, str, sizeof(str));
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");