4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <netinet/icmp6.h>
19 #include <linux/types.h>
20 #include <linux/errqueue.h>
27 #include <arpa/inet.h>
30 #define SOL_IPV6 IPPROTO_IPV6
47 void data_wait(int fd)
55 select(fd+1, &fds, NULL, NULL, &tv);
58 int recverr(int fd, int ttl)
61 struct probehdr rcvbuf;
66 struct sock_extended_err *e;
68 struct timeval *rettv;
75 memset(&rcvbuf, -1, sizeof(rcvbuf));
76 iov.iov_base = &rcvbuf;
77 iov.iov_len = sizeof(rcvbuf);
83 msg.msg_control = cbuf;
84 msg.msg_controllen = sizeof(cbuf);
86 gettimeofday(&tv, NULL);
87 res = recvmsg(fd, &msg, MSG_ERRQUEUE);
101 if (res == sizeof(rcvbuf)) {
102 if (rcvbuf.ttl == 0 || rcvbuf.tv.tv_sec == 0)
105 sndhops = rcvbuf.ttl;
111 printf("%2d: ", sndhops);
113 printf("%2d?: ", ttl);
115 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
116 if (cmsg->cmsg_level == SOL_IPV6) {
117 switch(cmsg->cmsg_type) {
119 e = (struct sock_extended_err *)CMSG_DATA(cmsg);
122 #ifdef IPV6_2292HOPLIMIT
123 case IPV6_2292HOPLIMIT:
125 rethops = *(int*)CMSG_DATA(cmsg);
128 } else if (cmsg->cmsg_level == SOL_IP) {
129 if (cmsg->cmsg_type == IP_TTL) {
130 rethops = *(__u8*)CMSG_DATA(cmsg);
138 if (e->ee_origin == SO_EE_ORIGIN_LOCAL)
139 printf("%-32s ", "[LOCALHOST]");
140 else if (e->ee_origin == SO_EE_ORIGIN_ICMP6 ||
141 e->ee_origin == SO_EE_ORIGIN_ICMP) {
142 struct hostent * h = NULL;
144 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(e+1);
145 struct sockaddr_in *sin = (struct sockaddr_in*)(e+1);
150 if (sin->sin_family == AF_INET6) {
151 inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf));
153 h = gethostbyaddr((char *) &sin6->sin6_addr, sizeof(sin6->sin6_addr), AF_INET6);
155 inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
157 h = gethostbyaddr((char *) &sin->sin_addr, sizeof(sin->sin_addr), AF_INET);
163 snprintf(fabuf, sizeof(fabuf), "%s %s", h->h_name, abuf);
165 snprintf(fabuf, sizeof(fabuf), "%s", abuf);
167 snprintf(fabuf, sizeof(fabuf), "%s", h ? h->h_name : abuf);
169 printf("%-40s ", fabuf);
171 printf("%-32s ", abuf);
177 rethops = 65-rethops;
178 else if (rethops<=128)
179 rethops = 129-rethops;
181 rethops = 256-rethops;
182 if (sndhops>=0 && rethops != sndhops)
183 printf("asymm %2d ", rethops);
184 else if (sndhops<0 && rethops != ttl)
185 printf("asymm %2d ", rethops);
189 int diff = (tv.tv_sec-rettv->tv_sec)*1000000+(tv.tv_usec-rettv->tv_usec);
190 printf("%3d.%03dms ", diff/1000, diff%1000);
192 printf("(This broken router returned corrupted payload) ");
195 switch (e->ee_errno) {
200 printf("pmtu %d\n", e->ee_info);
206 hops_to = sndhops<0 ? ttl : sndhops;
213 if ((e->ee_origin == SO_EE_ORIGIN_ICMP &&
216 (e->ee_origin == SO_EE_ORIGIN_ICMP6 &&
239 int probe_ttl(int fd, int ttl)
243 struct probehdr *hdr = (struct probehdr*)sndbuf;
247 for (i=0; i<10; i++) {
251 gettimeofday(&hdr->tv, NULL);
252 if (send(fd, sndbuf, mtu-overhead, 0) > 0)
254 res = recverr(fd, ttl);
265 if (recv(fd, sndbuf, sizeof(sndbuf), MSG_DONTWAIT) > 0) {
266 printf("%2d?: reply received 8)\n", ttl);
269 res = recverr(fd, ttl);
275 printf("%2d: send failed\n", ttl);
279 static void usage(void) __attribute((noreturn));
281 static void usage(void)
283 fprintf(stderr, "Usage: tracepath6 [-n] [-b] <destination>[/<port>]\n");
288 int main(int argc, char **argv)
292 struct sockaddr_in6 sin;
295 struct addrinfo hints, *ai, *ai0;
298 char pbuf[NI_MAXSERV];
300 while ((ch = getopt(argc, argv, "nbh?")) != EOF) {
319 memset(&sin, 0, sizeof(sin));
321 p = strchr(argv[0], '/');
324 sprintf(pbuf, "%u", (unsigned)atoi(p+1));
326 sprintf(pbuf, "%u", (0x8000 | getpid()) & 0xffff);
329 memset(&hints, 0, sizeof(hints));
330 hints.ai_family = AF_INET6;
331 hints.ai_socktype = SOCK_DGRAM;
332 hints.ai_protocol = IPPROTO_UDP;
333 hints.ai_flags = no_resolve ? AI_NUMERICHOST : 0;
334 gai = getaddrinfo(argv[0], pbuf, &hints, &ai0);
336 herror("getaddrinfo"); /*XXX*/
341 for (ai = ai0; ai; ai = ai->ai_next) {
342 fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
345 if (connect(fd, ai->ai_addr, ai->ai_addrlen) < 0) {
350 memcpy(&sin, ai->ai_addr, sizeof(sin));
354 perror("socket/connect");
359 if (!sin.sin6_addr.s6_addr32[0] && !sin.sin6_addr.s6_addr32[1]
360 && sin.sin6_addr.s6_addr32[2] == htonl(0xFFFF)) {
366 on = IPV6_PMTUDISC_DO;
367 if (setsockopt(fd, SOL_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on))) {
368 perror("IPV6_MTU_DISCOVER");
371 if (mapped && setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on))) {
372 perror("IP_MTU_DISCOVER");
376 if (setsockopt(fd, SOL_IPV6, IPV6_RECVERR, &on, sizeof(on))) {
377 perror("IPV6_RECVERR");
380 if (mapped && setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) {
381 perror("IP_RECVERR");
385 #ifdef IPV6_RECVHOPLIMIT
386 setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)) &&
387 setsockopt(fd, SOL_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on))
389 setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on))
392 perror("IPV6_HOPLIMIT");
395 if (mapped && setsockopt(fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) {
396 perror("IP_RECVTTL");
400 for (ttl=1; ttl<32; ttl++) {
405 if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &on, sizeof(on))) {
406 perror("IPV6_UNICAST_HOPS");
409 if (mapped && setsockopt(fd, SOL_IP, IP_TTL, &on, sizeof(on))) {
414 for (i=0; i<3; i++) {
415 res = probe_ttl(fd, ttl);
423 printf("%2d: no reply\n", ttl);
425 printf(" Too many hops: pmtu %d\n", mtu);
428 printf(" Resume: pmtu %d ", mtu);
430 printf("hops %d ", hops_to);
432 printf("back %d ", hops_from);