diff options
author | Pavel Šimerda <psimerda@redhat.com> | 2016-03-30 16:23:46 +0200 |
---|---|---|
committer | Pavel Šimerda <psimerda@redhat.com> | 2016-03-30 16:39:20 +0200 |
commit | 5562b6f958ffab476148df58857fae54e299d1c2 (patch) | |
tree | 15aecdc7543b99f56747fb01e3f398be39af36e9 | |
parent | 3f71426605f9cf46c01e4b78b387da2717693bd1 (diff) | |
download | iputils-5562b6f958ffab476148df58857fae54e299d1c2.tar.gz |
tracepath: borrow everything good from tracepath6
-rw-r--r-- | tracepath.c | 288 |
1 files changed, 216 insertions, 72 deletions
diff --git a/tracepath.c b/tracepath.c index c57a127..8cc63a7 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,23 +370,29 @@ 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_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) { switch(ch) { @@ -355,13 +431,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 +440,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 +534,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 +555,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 +568,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 +579,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); } |