diff options
author | David Heidelberg <david@ixit.cz> | 2016-05-18 01:17:09 +0200 |
---|---|---|
committer | David Heidelberg <david@ixit.cz> | 2016-05-18 01:17:09 +0200 |
commit | 55828d1fef3fed7f07abcbf7be9282a9662e78c7 (patch) | |
tree | 5d673714d0caa0adf1fdbf11c7e2643abfeb12fa | |
parent | 7e7ffffacc2af71ff87a252783d0301f39f9a87b (diff) | |
parent | 8d18bf92ae9cf354164ae40c09951b47a464d6e6 (diff) | |
download | iputils-55828d1fef3fed7f07abcbf7be9282a9662e78c7.tar.gz |
Merge pull request #48 from pavlix/dualstack
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | tracepath.c | 306 | ||||
-rw-r--r-- | tracepath6.c | 591 |
3 files changed, 233 insertions, 670 deletions
@@ -116,7 +116,7 @@ endif endif # ------------------------------------- -TARGETS=ping tracepath tracepath6 traceroute6 clockdiff rdisc arping tftpd rarpd +TARGETS=ping tracepath traceroute6 clockdiff rdisc arping tftpd rarpd LDLIBS=$(LDLIB) $(ADDLIB) @@ -175,10 +175,6 @@ LIB_rdisc = DEF_tracepath = $(DEF_IDN) LIB_tracepath = $(LIB_IDN) -# tracepath6 -DEF_tracepath6 = $(DEF_IDN) -LIB_tracepath6 = - # traceroute6 DEF_traceroute6 = $(DEF_CAP) $(DEF_IDN) LIB_traceroute6 = $(LIB_CAP) $(LIB_IDN) diff --git a/tracepath.c b/tracepath.c index c57a127..74a829d 100644 --- a/tracepath.c +++ b/tracepath.c @@ -13,29 +13,37 @@ #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/icmp6.h> + #include <linux/types.h> +#include <linux/errqueue.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <limits.h> -#include <netinet/in.h> #include <resolv.h> #include <sys/time.h> -#include <linux/errqueue.h> #include <sys/uio.h> #include <arpa/inet.h> #ifdef USE_IDN #include <idna.h> #include <locale.h> - #define getnameinfo_flags NI_IDN #else #define getnameinfo_flags 0 #endif -#ifndef IP_PMTUDISC_PROBE -#define IP_PMTUDISC_PROBE 3 +#ifndef SOL_IPV6 +#define SOL_IPV6 IPPROTO_IPV6 +#endif + +#ifndef IP_PMTUDISC_DO +#define IP_PMTUDISC_DO 3 +#endif +#ifndef IPV6_PMTUDISC_DO +#define IPV6_PMTUDISC_DO 3 #endif #define MAX_HOPS_LIMIT 255 @@ -50,17 +58,19 @@ struct hhistory struct hhistory his[64]; int hisptr; -struct sockaddr_in target; +struct sockaddr_storage target; +socklen_t targetlen; __u16 base_port; int max_hops = MAX_HOPS_DEFAULT; -const int overhead = 28; -int mtu = 65535; +int overhead; +int mtu; void *pktbuf; int hops_to = -1; int hops_from = -1; int no_resolve = 0; int show_both = 0; +int mapped; #define HOST_COLUMN_SIZE 52 @@ -92,7 +102,7 @@ void print_host(const char *a, const char *b, int both) printf("%*s", HOST_COLUMN_SIZE - plen, ""); } -int recverr(int fd, int ttl) +int recverr(int fd, struct addrinfo *ai, int ttl) { int res; struct probehdr rcvbuf; @@ -101,10 +111,10 @@ int recverr(int fd, int ttl) struct msghdr msg; struct cmsghdr *cmsg; struct sock_extended_err *e; - struct sockaddr_in addr; + struct sockaddr_storage addr; struct timeval tv; struct timeval *rettv; - int slot; + int slot = 0; int rethops; int sndhops; int progress = -1; @@ -137,30 +147,59 @@ restart: sndhops = -1; e = NULL; rettv = NULL; - slot = ntohs(addr.sin_port) - base_port; - if (slot>=0 && slot < 63 && his[slot].hops) { + + slot = -base_port; + switch (ai->ai_family) { + case AF_INET6: + slot += ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); + break; + case AF_INET: + slot += ntohs(((struct sockaddr_in *)&addr)->sin_port); + break; + } + + if (slot >= 0 && slot < 63 && his[slot].hops) { sndhops = his[slot].hops; rettv = &his[slot].sendtime; his[slot].hops = 0; } broken_router = 0; if (res == sizeof(rcvbuf)) { - if (rcvbuf.ttl == 0 || rcvbuf.tv.tv_sec == 0) { + if (rcvbuf.ttl == 0 || rcvbuf.tv.tv_sec == 0) broken_router = 1; - } else { + else { sndhops = rcvbuf.ttl; rettv = &rcvbuf.tv; } } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level == SOL_IP) { - if (cmsg->cmsg_type == IP_RECVERR) { - e = (struct sock_extended_err *) CMSG_DATA(cmsg); - } else if (cmsg->cmsg_type == IP_TTL) { + switch (cmsg->cmsg_level) { + case SOL_IPV6: + switch(cmsg->cmsg_type) { + case IPV6_RECVERR: + e = (struct sock_extended_err *)CMSG_DATA(cmsg); + break; + case IPV6_HOPLIMIT: +#ifdef IPV6_2292HOPLIMIT + case IPV6_2292HOPLIMIT: +#endif memcpy(&rethops, CMSG_DATA(cmsg), sizeof(rethops)); - } else { - printf("cmsg:%d\n ", cmsg->cmsg_type); + break; + default: + printf("cmsg6:%d\n ", cmsg->cmsg_type); + } + break; + case SOL_IP: + switch(cmsg->cmsg_type) { + case IP_RECVERR: + e = (struct sock_extended_err *)CMSG_DATA(cmsg); + break; + case IP_TTL: + rethops = *(__u8*)CMSG_DATA(cmsg); + break; + default: + printf("cmsg4:%d\n ", cmsg->cmsg_type); } } } @@ -168,23 +207,44 @@ restart: printf("no info\n"); return 0; } - if (e->ee_origin == SO_EE_ORIGIN_LOCAL) { - printf("%2d?: %*s ", ttl, -(HOST_COLUMN_SIZE - 1), "[LOCALHOST]"); - } else if (e->ee_origin == SO_EE_ORIGIN_ICMP) { - char abuf[128]; - struct sockaddr_in *sin = (struct sockaddr_in*)(e+1); - - inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf)); + if (e->ee_origin == SO_EE_ORIGIN_LOCAL) + printf("%2d?: %-32s ", ttl, "[LOCALHOST]"); + else if (e->ee_origin == SO_EE_ORIGIN_ICMP6 || + e->ee_origin == SO_EE_ORIGIN_ICMP) { + char abuf[NI_MAXHOST]; + struct sockaddr *sa = (struct sockaddr *)(e + 1); + socklen_t salen; if (sndhops>0) printf("%2d: ", sndhops); else printf("%2d?: ", ttl); + switch (sa->sa_family) { + case AF_INET6: + salen = sizeof(struct sockaddr_in6); + break; + case AF_INET: + salen = sizeof(struct sockaddr_in); + break; + default: + salen = 0; + } + + if (no_resolve || show_both) { + if (getnameinfo(sa, salen, + abuf, sizeof(abuf), NULL, 0, + NI_NUMERICHOST)) + strcpy(abuf, "???"); + } else + abuf[0] = 0; + if (!no_resolve || show_both) { fflush(stdout); - getnameinfo((struct sockaddr *) sin, sizeof *sin, hnamebuf, sizeof hnamebuf, NULL, 0, getnameinfo_flags); - } + if (getnameinfo(sa, salen, hnamebuf, sizeof hnamebuf, NULL, 0, getnameinfo_flags)) + strcpy(hnamebuf, "???"); + } else + hnamebuf[0] = 0; if (no_resolve) print_host(abuf, hnamebuf, show_both); @@ -224,9 +284,12 @@ restart: printf("!P\n"); return 0; case EHOSTUNREACH: - if (e->ee_origin == SO_EE_ORIGIN_ICMP && - e->ee_type == 11 && - e->ee_code == 0) { + if ((e->ee_origin == SO_EE_ORIGIN_ICMP && + e->ee_type == 11 && + e->ee_code == 0) || + (e->ee_origin == SO_EE_ORIGIN_ICMP6 && + e->ee_type == 3 && + e->ee_code == 0)) { if (rethops>=0) { if (sndhops>=0 && rethops != sndhops) printf("asymm %2d ", rethops); @@ -253,7 +316,7 @@ restart: goto restart; } -int probe_ttl(int fd, int ttl) +int probe_ttl(int fd, struct addrinfo *ai, int ttl) { int i; struct probehdr *hdr = pktbuf; @@ -264,20 +327,27 @@ restart: int res; hdr->ttl = ttl; - target.sin_port = htons(base_port + hisptr); + switch (ai->ai_family) { + case AF_INET6: + ((struct sockaddr_in6 *)&target)->sin6_port = htons(base_port + hisptr); + break; + case AF_INET: + ((struct sockaddr_in *)&target)->sin_port = htons(base_port + hisptr); + break; + } gettimeofday(&hdr->tv, NULL); his[hisptr].hops = ttl; his[hisptr].sendtime = hdr->tv; - if (sendto(fd, pktbuf, mtu-overhead, 0, (struct sockaddr*)&target, sizeof(target)) > 0) + if (sendto(fd, pktbuf, mtu-overhead, 0, (struct sockaddr *)&target, targetlen) > 0) break; - res = recverr(fd, ttl); + res = recverr(fd, ai, ttl); his[hisptr].hops = 0; if (res==0) return 0; if (res > 0) goto restart; } - hisptr = (hisptr + 1)&63; + hisptr = (hisptr + 1) & 63; if (i<10) { data_wait(fd); @@ -285,7 +355,7 @@ restart: printf("%2d?: reply received 8)\n", ttl); return 0; } - return recverr(fd, ttl); + return recverr(fd, ai, ttl); } printf("%2d: send failed\n", ttl); @@ -300,26 +370,46 @@ static void usage(void) exit(-1); } -int -main(int argc, char **argv) + +int main(int argc, char **argv) { struct addrinfo hints = { - .ai_family = AF_INET, - .ai_socktype = SOCK_RAW, + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = IPPROTO_UDP, #ifdef USE_IDN - .ai_flags = AI_IDN | AI_CANONIDN, + .ai_flags = AI_IDN | AI_CANONNAME, #endif }; - struct addrinfo *ai; + struct addrinfo *ai, *result; + int ch; int status; int fd; int on; int ttl; char *p; - int ch; + char pbuf[NI_MAXSERV]; + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif - while ((ch = getopt(argc, argv, "nbh?l:m:p:")) != EOF) { + while ((ch = getopt(argc, argv, "46nbh?l:m:p:")) != EOF) { switch(ch) { + case '4': + if (hints.ai_family != AF_UNSPEC) { + fprintf(stderr, "tracepath: Only one -4 or -6 option may be specified\n"); + exit(2); + } + hints.ai_family = AF_INET; + break; + case '6': + if (hints.ai_family != AF_UNSPEC) { + fprintf(stderr, "tracepath: Only one -4 or -6 option may be specified\n"); + exit(2); + } + hints.ai_family = AF_INET6; + break; case 'n': no_resolve = 1; break; @@ -355,13 +445,6 @@ main(int argc, char **argv) if (argc != 1) usage(); - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - perror("socket"); - exit(1); - } - target.sin_family = AF_INET; - /* Backward compatiblity */ if (!base_port) { p = strchr(argv[0], '/'); @@ -371,31 +454,87 @@ main(int argc, char **argv) } else base_port = 44444; } + sprintf(pbuf, "%u", base_port); - status = getaddrinfo(argv[0], NULL, &hints, &ai); + status = getaddrinfo(argv[0], pbuf, &hints, &result); if (status) { fprintf(stderr, "tracepath: %s: %s\n", argv[0], gai_strerror(status)); exit(1); } - memcpy(&target.sin_addr, &((struct sockaddr_in *) ai->ai_addr)->sin_addr, sizeof target.sin_addr); - freeaddrinfo(ai); - - on = IP_PMTUDISC_PROBE; - if (setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on)) && - (on = IP_PMTUDISC_DO, - setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on)))) { - perror("IP_MTU_DISCOVER"); - exit(1); + fd = -1; + for (ai = result; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET6 && + ai->ai_family != AF_INET) + continue; + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd < 0) + continue; + memcpy(&target, ai->ai_addr, sizeof(target)); + targetlen = ai->ai_addrlen; + break; } - on = 1; - if (setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) { - perror("IP_RECVERR"); + if (fd < 0) { + perror("socket/connect"); exit(1); } - if (setsockopt(fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) { - perror("IP_RECVTTL"); - exit(1); + + switch (ai->ai_family) { + case AF_INET6: + overhead = 48; + if (!mtu) + mtu = 128000; + if (mtu <= overhead) + goto pktlen_error; + + on = IPV6_PMTUDISC_DO; + if (setsockopt(fd, SOL_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on)) && + (on = IPV6_PMTUDISC_DO, + setsockopt(fd, SOL_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on)))) { + perror("IPV6_MTU_DISCOVER"); + exit(1); + } + on = 1; + if (setsockopt(fd, SOL_IPV6, IPV6_RECVERR, &on, sizeof(on))) { + perror("IPV6_RECVERR"); + exit(1); + } + if ( +#ifdef IPV6_RECVHOPLIMIT + setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)) && + setsockopt(fd, SOL_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on)) +#else + setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)) +#endif + ) { + perror("IPV6_HOPLIMIT"); + exit(1); + } + if (!IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6 *)&target)->sin6_addr))) + break; + mapped = 1; + /*FALLTHROUGH*/ + case AF_INET: + overhead = 28; + if (!mtu) + mtu = 65535; + if (mtu <= overhead) + goto pktlen_error; + + on = IP_PMTUDISC_DO; + if (setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on))) { + perror("IP_MTU_DISCOVER"); + exit(1); + } + on = 1; + if (setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) { + perror("IP_RECVERR"); + exit(1); + } + if (setsockopt(fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) { + perror("IP_RECVTTL"); + exit(1); + } } pktbuf = malloc(mtu); @@ -409,9 +548,20 @@ main(int argc, char **argv) int i; on = ttl; - if (setsockopt(fd, SOL_IP, IP_TTL, &on, sizeof(on))) { - perror("IP_TTL"); - exit(1); + switch (ai->ai_family) { + case AF_INET6: + if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &on, sizeof(on))) { + perror("IPV6_UNICAST_HOPS"); + exit(1); + } + if (!mapped) + break; + /*FALLTHROUGH*/ + case AF_INET: + if (setsockopt(fd, SOL_IP, IP_TTL, &on, sizeof(on))) { + perror("IP_TTL"); + exit(1); + } } restart: @@ -419,7 +569,7 @@ restart: int old_mtu; old_mtu = mtu; - res = probe_ttl(fd, ttl); + res = probe_ttl(fd, ai, ttl); if (mtu != old_mtu) goto restart; if (res == 0) @@ -432,7 +582,10 @@ restart: printf("%2d: no reply\n", ttl); } printf(" Too many hops: pmtu %d\n", mtu); + done: + freeaddrinfo(result); + printf(" Resume: pmtu %d ", mtu); if (hops_to>=0) printf("hops %d ", hops_to); @@ -440,4 +593,9 @@ done: printf("back %d ", hops_from); printf("\n"); exit(0); + +pktlen_error: + fprintf(stderr, "Error: pktlen must be > %d and <= %d\n", + overhead, INT_MAX); + exit(1); } diff --git a/tracepath6.c b/tracepath6.c deleted file mode 100644 index 2db6803..0000000 --- a/tracepath6.c +++ /dev/null @@ -1,591 +0,0 @@ -/* - * tracepath6.c - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> - */ - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <netinet/icmp6.h> - -#include <linux/types.h> -#include <linux/errqueue.h> -#include <errno.h> -#include <string.h> -#include <netdb.h> -#include <limits.h> -#include <resolv.h> -#include <sys/time.h> -#include <sys/uio.h> -#include <arpa/inet.h> - -#ifdef USE_IDN -#include <idna.h> -#include <locale.h> -#endif - -#ifndef SOL_IPV6 -#define SOL_IPV6 IPPROTO_IPV6 -#endif - -#ifndef IP_PMTUDISC_DO -#define IP_PMTUDISC_DO 3 -#endif -#ifndef IPV6_PMTUDISC_DO -#define IPV6_PMTUDISC_DO 3 -#endif - -#define MAX_HOPS_LIMIT 255 -#define MAX_HOPS_DEFAULT 30 - -struct hhistory -{ - int hops; - struct timeval sendtime; -}; - -struct hhistory his[64]; -int hisptr; - -sa_family_t family = AF_INET6; -struct sockaddr_storage target; -socklen_t targetlen; -__u16 base_port; -int max_hops = MAX_HOPS_DEFAULT; - -int overhead; -int mtu; -void *pktbuf; -int hops_to = -1; -int hops_from = -1; -int no_resolve = 0; -int show_both = 0; -int mapped; - -#define HOST_COLUMN_SIZE 52 - -struct probehdr -{ - __u32 ttl; - struct timeval tv; -}; - -void data_wait(int fd) -{ - fd_set fds; - struct timeval tv; - FD_ZERO(&fds); - FD_SET(fd, &fds); - tv.tv_sec = 1; - tv.tv_usec = 0; - select(fd+1, &fds, NULL, NULL, &tv); -} - -void print_host(const char *a, const char *b, int both) -{ - int plen; - plen = printf("%s", a); - if (both) - plen += printf(" (%s)", b); - if (plen >= HOST_COLUMN_SIZE) - plen = HOST_COLUMN_SIZE - 1; - printf("%*s", HOST_COLUMN_SIZE - plen, ""); -} - -int recverr(int fd, int ttl) -{ - int res; - struct probehdr rcvbuf; - char cbuf[512]; - struct iovec iov; - struct msghdr msg; - struct cmsghdr *cmsg; - struct sock_extended_err *e; - struct sockaddr_storage addr; - struct timeval tv; - struct timeval *rettv; - int slot = 0; - int rethops; - int sndhops; - int progress = -1; - int broken_router; - -restart: - memset(&rcvbuf, -1, sizeof(rcvbuf)); - iov.iov_base = &rcvbuf; - iov.iov_len = sizeof(rcvbuf); - msg.msg_name = (caddr_t)&addr; - msg.msg_namelen = sizeof(addr); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_flags = 0; - msg.msg_control = cbuf; - msg.msg_controllen = sizeof(cbuf); - - gettimeofday(&tv, NULL); - res = recvmsg(fd, &msg, MSG_ERRQUEUE); - if (res < 0) { - if (errno == EAGAIN) - return progress; - goto restart; - } - - progress = mtu; - - rethops = -1; - sndhops = -1; - e = NULL; - rettv = NULL; - - slot = -base_port; - switch (family) { - case AF_INET6: - slot += ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); - break; - case AF_INET: - slot += ntohs(((struct sockaddr_in *)&addr)->sin_port); - break; - } - - if (slot >= 0 && slot < 63 && his[slot].hops) { - sndhops = his[slot].hops; - rettv = &his[slot].sendtime; - his[slot].hops = 0; - } - broken_router = 0; - if (res == sizeof(rcvbuf)) { - if (rcvbuf.ttl == 0 || rcvbuf.tv.tv_sec == 0) - broken_router = 1; - else { - sndhops = rcvbuf.ttl; - rettv = &rcvbuf.tv; - } - } - - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - switch (cmsg->cmsg_level) { - case SOL_IPV6: - switch(cmsg->cmsg_type) { - case IPV6_RECVERR: - e = (struct sock_extended_err *)CMSG_DATA(cmsg); - break; - case IPV6_HOPLIMIT: -#ifdef IPV6_2292HOPLIMIT - case IPV6_2292HOPLIMIT: -#endif - memcpy(&rethops, CMSG_DATA(cmsg), sizeof(rethops)); - break; - default: - printf("cmsg6:%d\n ", cmsg->cmsg_type); - } - break; - case SOL_IP: - switch(cmsg->cmsg_type) { - case IP_RECVERR: - e = (struct sock_extended_err *)CMSG_DATA(cmsg); - break; - case IP_TTL: - rethops = *(__u8*)CMSG_DATA(cmsg); - break; - default: - printf("cmsg4:%d\n ", cmsg->cmsg_type); - } - } - } - if (e == NULL) { - printf("no info\n"); - return 0; - } - if (e->ee_origin == SO_EE_ORIGIN_LOCAL) - printf("%2d?: %-32s ", ttl, "[LOCALHOST]"); - else if (e->ee_origin == SO_EE_ORIGIN_ICMP6 || - e->ee_origin == SO_EE_ORIGIN_ICMP) { - char abuf[NI_MAXHOST], hbuf[NI_MAXHOST]; - struct sockaddr *sa = (struct sockaddr *)(e + 1); - socklen_t salen; - - if (sndhops>0) - printf("%2d: ", sndhops); - else - printf("%2d?: ", ttl); - - switch (sa->sa_family) { - case AF_INET6: - salen = sizeof(struct sockaddr_in6); - break; - case AF_INET: - salen = sizeof(struct sockaddr_in); - break; - default: - salen = 0; - } - - if (no_resolve || show_both) { - if (getnameinfo(sa, salen, - abuf, sizeof(abuf), NULL, 0, - NI_NUMERICHOST)) - strcpy(abuf, "???"); - } else - abuf[0] = 0; - - if (!no_resolve || show_both) { - fflush(stdout); - if (getnameinfo(sa, salen, - hbuf, sizeof(hbuf), NULL, 0, - 0 -#ifdef USE_IDN - | NI_IDN -#endif - )) - strcpy(hbuf, "???"); - } else - hbuf[0] = 0; - - if (no_resolve) - print_host(abuf, hbuf, show_both); - else - print_host(hbuf, abuf, show_both); - } - - if (rettv) { - int diff = (tv.tv_sec-rettv->tv_sec)*1000000+(tv.tv_usec-rettv->tv_usec); - printf("%3d.%03dms ", diff/1000, diff%1000); - if (broken_router) - printf("(This broken router returned corrupted payload) "); - } - - if (rethops<=64) - rethops = 65-rethops; - else if (rethops<=128) - rethops = 129-rethops; - else - rethops = 256-rethops; - - switch (e->ee_errno) { - case ETIMEDOUT: - printf("\n"); - break; - case EMSGSIZE: - printf("pmtu %d\n", e->ee_info); - mtu = e->ee_info; - progress = mtu; - break; - case ECONNREFUSED: - printf("reached\n"); - hops_to = sndhops<0 ? ttl : sndhops; - hops_from = rethops; - return 0; - case EPROTO: - printf("!P\n"); - return 0; - case EHOSTUNREACH: - if ((e->ee_origin == SO_EE_ORIGIN_ICMP && - e->ee_type == 11 && - e->ee_code == 0) || - (e->ee_origin == SO_EE_ORIGIN_ICMP6 && - e->ee_type == 3 && - e->ee_code == 0)) { - if (rethops>=0) { - if (sndhops>=0 && rethops != sndhops) - printf("asymm %2d ", rethops); - else if (sndhops<0 && rethops != ttl) - printf("asymm %2d ", rethops); - } - printf("\n"); - break; - } - printf("!H\n"); - return 0; - case ENETUNREACH: - printf("!N\n"); - return 0; - case EACCES: - printf("!A\n"); - return 0; - default: - printf("\n"); - errno = e->ee_errno; - perror("NET ERROR"); - return 0; - } - goto restart; -} - -int probe_ttl(int fd, int ttl) -{ - int i; - struct probehdr *hdr = pktbuf; - - memset(pktbuf, 0, mtu); -restart: - - for (i=0; i<10; i++) { - int res; - - hdr->ttl = ttl; - switch (family) { - case AF_INET6: - ((struct sockaddr_in6 *)&target)->sin6_port = htons(base_port + hisptr); - break; - case AF_INET: - ((struct sockaddr_in *)&target)->sin_port = htons(base_port + hisptr); - break; - } - gettimeofday(&hdr->tv, NULL); - his[hisptr].hops = ttl; - his[hisptr].sendtime = hdr->tv; - if (sendto(fd, pktbuf, mtu-overhead, 0, (struct sockaddr *)&target, targetlen) > 0) - break; - res = recverr(fd, ttl); - his[hisptr].hops = 0; - if (res==0) - return 0; - if (res > 0) - goto restart; - } - hisptr = (hisptr + 1) & 63; - - if (i<10) { - data_wait(fd); - if (recv(fd, pktbuf, mtu, MSG_DONTWAIT) > 0) { - printf("%2d?: reply received 8)\n", ttl); - return 0; - } - return recverr(fd, ttl); - } - - printf("%2d: send failed\n", ttl); - return 0; -} - -static void usage(void) __attribute((noreturn)); - -static void usage(void) -{ - fprintf(stderr, "Usage: tracepath6 [-n] [-b] [-l <len>] [-p port] <destination>\n"); - exit(-1); -} - - -int main(int argc, char **argv) -{ - int fd; - int on; - int ttl; - char *p; - struct addrinfo hints = { - .ai_family = family, - .ai_socktype = SOCK_DGRAM, - .ai_protocol = IPPROTO_UDP, -#ifdef USE_IDN - .ai_flags = AI_IDN | AI_CANONNAME, -#endif - }; - struct addrinfo *ai, *result; - int ch; - int status; - char pbuf[NI_MAXSERV]; - -#ifdef USE_IDN - setlocale(LC_ALL, ""); -#endif - - while ((ch = getopt(argc, argv, "nbh?l:m:p:")) != EOF) { - switch(ch) { - case 'n': - no_resolve = 1; - break; - case 'b': - show_both = 1; - break; - case 'l': - mtu = atoi(optarg); - break; - case 'm': - max_hops = atoi(optarg); - if (max_hops < 0 || max_hops > MAX_HOPS_LIMIT) { - fprintf(stderr, - "Error: max hops must be 0 .. %d (inclusive).\n", - MAX_HOPS_LIMIT); - } - break; - case 'p': - base_port = atoi(optarg); - break; - default: - usage(); - } - } - - argc -= optind; - argv += optind; - - if (argc != 1) - usage(); - - /* Backward compatiblity */ - if (!base_port) { - p = strchr(argv[0], '/'); - if (p) { - *p = 0; - base_port = (unsigned)atoi(p+1); - } else { - base_port = 44444; - } - } - sprintf(pbuf, "%u", base_port); - - status = getaddrinfo(argv[0], pbuf, &hints, &result); - if (status) { - fprintf(stderr, "tracepath6: %s: %s\n", argv[0], gai_strerror(status)); - exit(1); - } - - fd = -1; - for (ai = result; ai; ai = ai->ai_next) { - /* sanity check */ - if (family && ai->ai_family != family) - continue; - if (ai->ai_family != AF_INET6 && - ai->ai_family != AF_INET) - continue; - family = ai->ai_family; - fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (fd < 0) - continue; - memcpy(&target, ai->ai_addr, sizeof(target)); - targetlen = ai->ai_addrlen; - break; - } - if (fd < 0) { - perror("socket/connect"); - exit(1); - } - freeaddrinfo(result); - - switch (family) { - case AF_INET6: - overhead = 48; - if (!mtu) - mtu = 128000; - if (mtu <= overhead) - goto pktlen_error; - - on = IPV6_PMTUDISC_DO; - if (setsockopt(fd, SOL_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on)) && - (on = IPV6_PMTUDISC_DO, - setsockopt(fd, SOL_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on)))) { - perror("IPV6_MTU_DISCOVER"); - exit(1); - } - on = 1; - if (setsockopt(fd, SOL_IPV6, IPV6_RECVERR, &on, sizeof(on))) { - perror("IPV6_RECVERR"); - exit(1); - } - if ( -#ifdef IPV6_RECVHOPLIMIT - setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)) && - setsockopt(fd, SOL_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on)) -#else - setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)) -#endif - ) { - perror("IPV6_HOPLIMIT"); - exit(1); - } - if (!IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6 *)&target)->sin6_addr))) - break; - mapped = 1; - /*FALLTHROUGH*/ - case AF_INET: - overhead = 28; - if (!mtu) - mtu = 65535; - if (mtu <= overhead) - goto pktlen_error; - - on = IP_PMTUDISC_DO; - if (setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on))) { - perror("IP_MTU_DISCOVER"); - exit(1); - } - on = 1; - if (setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) { - perror("IP_RECVERR"); - exit(1); - } - if (setsockopt(fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) { - perror("IP_RECVTTL"); - exit(1); - } - } - - pktbuf = malloc(mtu); - if (!pktbuf) { - perror("malloc"); - exit(1); - } - - for (ttl = 1; ttl <= max_hops; ttl++) { - int res; - int i; - - on = ttl; - switch (family) { - case AF_INET6: - if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &on, sizeof(on))) { - perror("IPV6_UNICAST_HOPS"); - exit(1); - } - if (!mapped) - break; - /*FALLTHROUGH*/ - case AF_INET: - if (setsockopt(fd, SOL_IP, IP_TTL, &on, sizeof(on))) { - perror("IP_TTL"); - exit(1); - } - } - -restart: - for (i=0; i<3; i++) { - int old_mtu; - - old_mtu = mtu; - res = probe_ttl(fd, ttl); - if (mtu != old_mtu) - goto restart; - if (res == 0) - goto done; - if (res > 0) - break; - } - - if (res < 0) - printf("%2d: no reply\n", ttl); - } - printf(" Too many hops: pmtu %d\n", mtu); - -done: - printf(" Resume: pmtu %d ", mtu); - if (hops_to>=0) - printf("hops %d ", hops_to); - if (hops_from>=0) - printf("back %d ", hops_from); - printf("\n"); - exit(0); - -pktlen_error: - fprintf(stderr, "Error: pktlen must be > %d and <= %d\n", - overhead, INT_MAX); - exit(1); -} |