From d55fb05190251ca705e0f0255eab37290dee2e1c Mon Sep 17 00:00:00 2001 From: Aleksandr Lysikov <lysikov@inbox.ru> Date: Mon, 17 Dec 2018 19:57:15 +0500 Subject: [PATCH] tracepath --- iputils/iputils.c | 42 +++ iputils/iputils.h | 11 +- iputils/ping.c | 40 +-- iputils/tracepath.c | 774 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 832 insertions(+), 35 deletions(-) create mode 100644 iputils/iputils.c create mode 100755 iputils/tracepath.c diff --git a/iputils/iputils.c b/iputils/iputils.c new file mode 100644 index 0000000000..389ea06ea0 --- /dev/null +++ b/iputils/iputils.c @@ -0,0 +1,42 @@ +/* + * Set utilities for networking + */ + +#include <stdio.h> +#include <stdbool.h> +#include <glib.h> + +static bool LOG_VERBOSE = false; + +void iputils_set_verbose(void) +{ + LOG_VERBOSE = true; +} + +void iputils_reset_verbose(void) +{ + LOG_VERBOSE = false; +} + +// analog printf() +int log_printf(const char *format, ...) +{ + int ret = 0; + if(LOG_VERBOSE) + { + gchar *log_str = NULL; + va_list args; + + va_start(args, format); + log_str = g_strdup_vprintf(format, args); + va_end(args); + + if(log_str) + { + + ret = printf(log_str); + g_free(log_str); + } + } + return ret; +} diff --git a/iputils/iputils.h b/iputils/iputils.h index 1e04b5de05..614979c1a5 100644 --- a/iputils/iputils.h +++ b/iputils/iputils.h @@ -19,7 +19,7 @@ extern "C" { * @count number of packets to transmit * @return ping time in microsecond or -1 if error */ -int ping_util4(const char *addr, int count); +int ping_util(const char *addr, int count); /** * Send ping for ipv6 @@ -34,11 +34,14 @@ int ping_util6(const char *addr, int count); /** * */ -int tracepath_util(int type, const char *addr); +int tracepath_util(const char *addr, int *hops, int *time_usec); + +void iputils_set_verbose(void); +void iputils_reset_verbose(void); + -//#define PING_DBG // analog printf() -void log_printf(const char *format, ...); +int log_printf(const char *format, ...); #define PACKAGE_NAME "iputils" #define PACKAGE_VERSION "0.1" diff --git a/iputils/ping.c b/iputils/ping.c index 497906684a..a5d320a4f8 100644 --- a/iputils/ping.c +++ b/iputils/ping.c @@ -58,29 +58,6 @@ #include <math.h> #include <glib.h> -// analog printf() -void log_printf(const char *format, ...) -{ -#ifdef PING_DBG - gchar *log_str = NULL; - va_list args; - - va_start (args, format); - log_str = g_strdup_vprintf (format, args); - va_end (args); - - if(log_str) - { - - printf(log_str); - g_free(log_str); - } -#endif - const char *str = NULL; - if(str) - str = format; -} - #ifndef ICMP_FILTER #define ICMP_FILTER 1 struct icmp_filter { @@ -476,6 +453,7 @@ ping_main(int argc, char **argv) argc -= optind; argv += optind; + optind = 0; if(!argc) error(1, EDESTADDRREQ, "usage error"); @@ -543,7 +521,7 @@ ping_main(int argc, char **argv) * @count number of packets to transmit * @return ping time in microsecond or -1 if error */ -int ping_util(int type, const char *addr, int count) +int ping_util_common(int type, const char *addr, int count) { /* @@ -556,14 +534,14 @@ int ping_util(int type, const char *addr, int count) */ int argc = 3; const char *argv[argc]; - if(type!=4) + if(type != 4) argv[0] = "ping6"; else argv[0] = "ping4"; argv[1] = g_strdup_printf("-c%d", count); argv[2] = addr; ping_main(argc, (char**) argv); - g_free((char*)argv[1]); + g_free((char*) argv[1]); if(ntransmitted > 1 && nreceived > 1) return tsum; return -1; @@ -576,9 +554,9 @@ int ping_util(int type, const char *addr, int count) * @count number of packets to transmit * @return ping time in microsecond or -1 if error */ -int ping_util4(const char *addr, int count) +int ping_util(const char *addr, int count) { - return ping_util(4, addr, count); + return ping_util_common(4, addr, count); } /** @@ -590,7 +568,7 @@ int ping_util4(const char *addr, int count) */ int ping_util6(const char *addr, int count) { - return ping_util(6, addr, count); + return ping_util_common(6, addr, count); } int ping4_run(int argc, char **argv, struct addrinfo *ai, socket_st *sock) @@ -890,10 +868,10 @@ int ping4_run(int argc, char **argv, struct addrinfo *ai, socket_st *sock) if(!(packet = (unsigned char *) malloc((unsigned int) packlen))) error(2, errno, "memory allocation failed"); - //printf("PING %s (%s) ", hostname, inet_ntoa(whereto.sin_addr)); +//printf("PING %s (%s) ", hostname, inet_ntoa(whereto.sin_addr)); if(device || (options & F_STRICTSOURCE)) printf("from %s %s: ", inet_ntoa(source.sin_addr), device ? device : ""); - //printf("%d(%d) bytes of data.\n", datalen, datalen + 8 + optlen + 20); +//printf("%d(%d) bytes of data.\n", datalen, datalen + 8 + optlen + 20); setup(sock); log_printf("main_loop start %s (%s)\n", hostname, inet_ntoa(whereto.sin_addr)); diff --git a/iputils/tracepath.c b/iputils/tracepath.c new file mode 100755 index 0000000000..5319e74f40 --- /dev/null +++ b/iputils/tracepath.c @@ -0,0 +1,774 @@ +/* + * tracepath.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 <arpa/inet.h> +#include <errno.h> +#include <limits.h> +#include <linux/errqueue.h> +#include <linux/icmp.h> +#include <linux/icmpv6.h> +#include <linux/types.h> +#include <netdb.h> +#include <netinet/in.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/uio.h> +#include <unistd.h> +#include <glib.h> + +#include "iputils.h" + +#ifdef USE_IDN +# include <locale.h> +# ifndef AI_IDN +# define AI_IDN 0x0040 +# endif +# ifndef NI_IDN +# define NI_IDN 32 +# endif +# define getnameinfo_flags NI_IDN +#else +# define getnameinfo_flags 0 +#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 + +enum { + MAX_PROBES = 10, + + MAX_HOPS_DEFAULT = 30, + MAX_HOPS_LIMIT = 255, + + HOST_COLUMN_SIZE = 52, + + HIS_ARRAY_SIZE = 64, + + DEFAULT_OVERHEAD_IPV4 = 28, + DEFAULT_OVERHEAD_IPV6 = 48, + + DEFAULT_MTU_IPV4 = 65535, + DEFAULT_MTU_IPV6 = 128000, + + DEFAULT_BASEPORT = 44444, + + ANCILLARY_DATA_LEN = 512, +}; + +struct hhistory { + int hops; + struct timeval sendtime; + struct timeval deltatime; + int hop; + char *host_namea; + char *host_nameb; +}; + +struct probehdr { + uint32_t ttl; + struct timeval tv; +}; + +struct run_state { + struct hhistory his[HIS_ARRAY_SIZE]; + int hisptr; + struct sockaddr_storage target; + struct addrinfo *ai; + int socket_fd; + socklen_t targetlen; + uint16_t base_port; + uint8_t ttl; + int max_hops; + int overhead; + int mtu; + void *pktbuf; + int hops_to; + int hops_from; + unsigned int + no_resolve :1, + show_both :1, + mapped :1; +}; + +/* + * All includes, definitions, struct declarations, and global variables are + * above. After this comment all you can find is functions. + */ + +static void data_wait(struct run_state const * const ctl) +{ + fd_set fds; + struct timeval tv = { + .tv_sec = 1, + .tv_usec = 0 + }; + + FD_ZERO(&fds); + FD_SET(ctl->socket_fd, &fds); + select(ctl->socket_fd + 1, &fds, NULL, NULL, &tv); +} + +static void print_host(struct run_state const * const ctl, char const * const a, + char const * const b) +{ + int plen; + + plen = log_printf("%s", a); + if(ctl->show_both) + plen += log_printf(" (%s)", b); + if(plen >= HOST_COLUMN_SIZE) + plen = HOST_COLUMN_SIZE - 1; + log_printf("%*s", HOST_COLUMN_SIZE - plen, ""); +} + +static int recverr(struct run_state * const ctl) +{ + ssize_t recv_size; + struct probehdr rcvbuf; + char cbuf[ANCILLARY_DATA_LEN]; + struct cmsghdr *cmsg; + struct sock_extended_err *e; + struct sockaddr_storage addr; + struct timeval tv; + struct timeval *rettv; + struct timeval *deltatv; + int slot = 0; + int rethops; + int sndhops; + int progress = -1; + int broken_router; + char hnamebuf[NI_MAXHOST] = ""; + struct iovec iov = { + .iov_base = &rcvbuf, + .iov_len = sizeof(rcvbuf) + }; + struct msghdr msg; + const struct msghdr reset = { + .msg_name = (uint8_t *) &addr, + .msg_namelen = sizeof(addr), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cbuf, + .msg_controllen = sizeof(cbuf), + 0 + }; + + restart: + memset(&rcvbuf, -1, sizeof(rcvbuf)); + msg = reset; + + gettimeofday(&tv, NULL); + recv_size = recvmsg(ctl->socket_fd, &msg, MSG_ERRQUEUE); + if(recv_size < 0) { + if(errno == EAGAIN) + return progress; + goto restart; + } + + progress = ctl->mtu; + + rethops = -1; + sndhops = -1; + e = NULL; + rettv = NULL; + deltatv = NULL; + broken_router = 0; + + slot = -ctl->base_port; + switch (ctl->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 < (HIS_ARRAY_SIZE - 1) && ctl->his[slot].hops) { + sndhops = ctl->his[slot].hops; + rettv = &ctl->his[slot].sendtime; + deltatv = &ctl->his[slot].deltatime; + ctl->his[slot].hop = sndhops; + ctl->his[slot].hops = 0; + } + if(recv_size == 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: + log_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 = *(uint8_t *) CMSG_DATA(cmsg); + break; + default: + log_printf("cmsg4:%d\n ", cmsg->cmsg_type); + } + } + } + if(e == NULL) { + log_printf("no info\n"); + return 0; + } + if(e->ee_origin == SO_EE_ORIGIN_LOCAL) + log_printf("%2d?: %-32s ", ctl->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) + log_printf("%2d: ", sndhops); + else + log_printf("%2d?: ", ctl->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(ctl->no_resolve || ctl->show_both) + { + if(getnameinfo(sa, salen, abuf, sizeof(abuf), NULL, 0, + NI_NUMERICHOST)) + strcpy(abuf, "???"); + ctl->his[slot].host_namea = strdup(abuf); + } else + abuf[0] = 0; + + if(!ctl->no_resolve || ctl->show_both) + { + fflush(stdout); + if(getnameinfo(sa, salen, hnamebuf, sizeof hnamebuf, NULL, 0, + getnameinfo_flags)) + strcpy(hnamebuf, "???"); + ctl->his[slot].host_nameb = strdup(hnamebuf); + } else + hnamebuf[0] = 0; + + if(ctl->no_resolve) { + print_host(ctl, abuf, hnamebuf); + } + else { + print_host(ctl, hnamebuf, abuf); + } + } + + if(rettv) { + struct timeval res; + + timersub(&tv, rettv, &res); + if(deltatv) + memcpy(deltatv, &res, sizeof(struct timeval)); + log_printf("%3ld.%03ldms ", res.tv_sec * 1000 + res.tv_usec / 1000, res.tv_usec % 1000); + if(broken_router) + log_printf("(This broken router returned corrupted payload) "); + } + + if(rethops <= 64) + rethops = 65 - rethops; + else if(rethops <= 128) + rethops = 129 - rethops; + else + rethops = 256 - rethops; + + //log_printf("ERROR=%d ", e->ee_errno); + switch (e->ee_errno) { + case ETIMEDOUT: + log_printf("\n"); + break; + case EMSGSIZE: + log_printf("pmtu %d\n", e->ee_info); + ctl->mtu = e->ee_info; + progress = ctl->mtu; + break; + case ECONNREFUSED: + log_printf("reached\n"); + ctl->hops_to = sndhops < 0 ? ctl->ttl : sndhops; + ctl->hops_from = rethops; + return 0; + case EPROTO: + log_printf("!P\n"); + return 0; + case EHOSTUNREACH: + if((e->ee_origin == SO_EE_ORIGIN_ICMP && + e->ee_type == ICMP_TIME_EXCEEDED && + e->ee_code == ICMP_EXC_TTL) || + (e->ee_origin == SO_EE_ORIGIN_ICMP6 && + e->ee_type == ICMPV6_TIME_EXCEED && + e->ee_code == ICMPV6_EXC_HOPLIMIT)) { + if(rethops >= 0) { + if(sndhops >= 0 && rethops != sndhops) + log_printf("asymm %2d ", rethops); + else if(sndhops < 0 && rethops != ctl->ttl) + log_printf("asymm %2d ", rethops); + } + //log_printf("hops=->%2d, <-%2d ", sndhops, rethops); + log_printf("\n"); + break; + } + printf("!H\n"); + return 0; + case ENETUNREACH: + log_printf("!N\n"); + return 0; + case EACCES: + log_printf("!A\n"); + return 0; + default: + printf("\n"); + errno = e->ee_errno; + perror("NET ERROR"); + return 0; + } + goto restart; +} + +static int probe_ttl(struct run_state * const ctl) +{ + int i; + struct probehdr *hdr = ctl->pktbuf; + + memset(ctl->pktbuf, 0, ctl->mtu); + restart: + for(i = 0; i < MAX_PROBES; i++) { + int res; + + hdr->ttl = ctl->ttl; + switch (ctl->ai->ai_family) { + case AF_INET6: + ((struct sockaddr_in6 *) &ctl->target)->sin6_port = + htons(ctl->base_port + ctl->hisptr); + break; + case AF_INET: + ((struct sockaddr_in *) &ctl->target)->sin_port = + htons(ctl->base_port + ctl->hisptr); + break; + } + gettimeofday(&hdr->tv, NULL); + ctl->his[ctl->hisptr].hops = ctl->ttl; + ctl->his[ctl->hisptr].sendtime = hdr->tv; + if(sendto(ctl->socket_fd, ctl->pktbuf, ctl->mtu - ctl->overhead, 0, + (struct sockaddr *) &ctl->target, ctl->targetlen) > 0) + break; + res = recverr(ctl); + ctl->his[ctl->hisptr].hops = 0; + if(res == 0) + return 0; + if(res > 0) + goto restart; + } + ctl->hisptr = (ctl->hisptr + 1) & (HIS_ARRAY_SIZE - 1); + + if(i < MAX_PROBES) { + data_wait(ctl); + if(recv(ctl->socket_fd, ctl->pktbuf, ctl->mtu, MSG_DONTWAIT) > 0) { + log_printf("%2d?: reply received 8)\n", ctl->ttl); + return 0; + } + return recverr(ctl); + } + + log_printf("%2d: send failed\n", ctl->ttl); + return 0; +} + +static void usage(void) +{ + fprintf(stderr, + "\nUsage\n" + " tracepath [options] <destination>\n" + "\nOptions:\n" + " -4 use IPv4\n" + " -6 use IPv6\n" + " -b print both name and ip\n" + " -l <length> use packet <length>\n" + " -m <hops> use maximum <hops>\n" + " -n no dns name resolution\n" + " -p <port> use destination <port>\n" + " -V print version and exit\n" + " <destination> dns name or ip address\n" + "\nFor more details see tracepath(8).\n"); + exit(-1); +} + +int tracepath_main(int argc, char **argv, struct run_state *ctl) +{ + int ret = -1; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = IPPROTO_UDP, +#ifdef USE_IDN + .ai_flags = AI_IDN | AI_CANONNAME, +#endif + }; + struct addrinfo *result; + int ch; + int status; + int on; + char *p; + char pbuf[NI_MAXSERV]; + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif + + /* Support being called using `tracepath4` or `tracepath6` symlinks */ + if(argv[0][strlen(argv[0]) - 1] == '4') + hints.ai_family = AF_INET; + else if(argv[0][strlen(argv[0]) - 1] == '6') + hints.ai_family = AF_INET6; + + while((ch = getopt(argc, argv, "46nbh?l:m:p:V")) != EOF) { + switch (ch) { + case '4': + if(hints.ai_family == AF_INET6) { + fprintf(stderr, + "tracepath: Only one -4 or -6 option may be specified\n"); + return -1; //exit(2); + } + hints.ai_family = AF_INET; + break; + case '6': + if(hints.ai_family == AF_INET) { + fprintf(stderr, + "tracepath: Only one -4 or -6 option may be specified\n"); + return -1; //exit(2); + } + hints.ai_family = AF_INET6; + break; + case 'n': + ctl->no_resolve = 1; + break; + case 'b': + ctl->show_both = 1; + break; + case 'l': + if((ctl->mtu = atoi(optarg)) <= ctl->overhead) { + fprintf(stderr, + "Error: pktlen must be > %d and <= %d.\n", + ctl->overhead, INT_MAX); + return -1; //exit(1); + } + break; + case 'm': + ctl->max_hops = atoi(optarg); + if(ctl->max_hops < 0 || ctl->max_hops > MAX_HOPS_LIMIT) { + fprintf(stderr, + "Error: max hops must be 0 .. %d (inclusive).\n", + MAX_HOPS_LIMIT); + return -1; //exit(1); + } + break; + case 'p': + ctl->base_port = atoi(optarg); + break; + case 'V': + printf(IPUTILS_VERSION("tracepath")); + return 0; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if(argc != 1) + usage(); + + /* Backward compatibility */ + if(!ctl->base_port) { + p = strchr(argv[0], '/'); + if(p) { + *p = 0; + ctl->base_port = atoi(p + 1); + } else + ctl->base_port = DEFAULT_BASEPORT; + } + sprintf(pbuf, "%u", ctl->base_port); + + status = getaddrinfo(argv[0], pbuf, &hints, &result); + if(status) { + fprintf(stderr, "tracepath: %s: %s\n", argv[0], + gai_strerror(status)); + exit(1); + } + + for(ctl->ai = result; ctl->ai; ctl->ai = ctl->ai->ai_next) { + if(ctl->ai->ai_family != AF_INET6 && ctl->ai->ai_family != AF_INET) + continue; + ctl->socket_fd = socket(ctl->ai->ai_family, ctl->ai->ai_socktype, ctl->ai->ai_protocol); + if(ctl->socket_fd < 0) + continue; + memcpy(&ctl->target, ctl->ai->ai_addr, ctl->ai->ai_addrlen); + ctl->targetlen = ctl->ai->ai_addrlen; + break; + } + if(ctl->socket_fd < 0) { + perror("socket/connect"); + exit(1); + } + + switch (ctl->ai->ai_family) { + case AF_INET6: + ctl->overhead = DEFAULT_OVERHEAD_IPV6; + if(!ctl->mtu) + ctl->mtu = DEFAULT_MTU_IPV6; + if(ctl->mtu <= ctl->overhead) + goto pktlen_error; + + on = IPV6_PMTUDISC_DO; + if(setsockopt(ctl->socket_fd, SOL_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on)) && + (on = IPV6_PMTUDISC_DO, setsockopt(ctl->socket_fd, SOL_IPV6, + IPV6_MTU_DISCOVER, &on, sizeof(on)))) { + perror("IPV6_MTU_DISCOVER"); + exit(1); + } + on = 1; + if(setsockopt(ctl->socket_fd, SOL_IPV6, IPV6_RECVERR, &on, sizeof(on))) { + perror("IPV6_RECVERR"); + exit(1); + } + if(setsockopt(ctl->socket_fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)) + #ifdef IPV6_RECVHOPLIMIT + && setsockopt(ctl->socket_fd, SOL_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on)) + #endif + ) { + perror("IPV6_HOPLIMIT"); + exit(1); + } + if(!IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6 * )&ctl->target)->sin6_addr))) + break; + ctl->mapped = 1; + /*FALLTHROUGH*/ + case AF_INET: + ctl->overhead = DEFAULT_OVERHEAD_IPV4; + if(!ctl->mtu) + ctl->mtu = DEFAULT_MTU_IPV4; + if(ctl->mtu <= ctl->overhead) + goto pktlen_error; + + on = IP_PMTUDISC_DO; + if(setsockopt(ctl->socket_fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on))) { + perror("IP_MTU_DISCOVER"); + exit(1); + } + on = 1; + if(setsockopt(ctl->socket_fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) { + perror("IP_RECVERR"); + exit(1); + } + if(setsockopt(ctl->socket_fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) { + perror("IP_RECVTTL"); + exit(1); + } + } + + ctl->pktbuf = malloc(ctl->mtu); + if(!ctl->pktbuf) { + perror("malloc"); + return -1; //exit(1); + } + +// struct sockaddr sa; +// int salen6 = sizeof(struct sockaddr_in6); +// int salen = sizeof(struct sockaddr_in); + char target_ip[NI_MAXHOST]; + char *target_name = argv[0]; + memset(&target_ip, 0, sizeof(target_ip)); + getnameinfo((struct sockaddr *) &ctl->target, ctl->targetlen, target_ip, sizeof(target_ip), NULL, 0, + NI_NUMERICHOST); + log_printf("START target_name=%s target_ip=%s\n", target_name, target_ip); + + for(ctl->ttl = 1; ctl->ttl <= ctl->max_hops; ctl->ttl++) { + int res; + int i; + + on = ctl->ttl; + switch (ctl->ai->ai_family) { + case AF_INET6: + if(setsockopt(ctl->socket_fd, SOL_IPV6, IPV6_UNICAST_HOPS, &on, sizeof(on))) { + perror("IPV6_UNICAST_HOPS"); + return -1; //exit(1); + } + if(!ctl->mapped) + break; + /*FALLTHROUGH*/ + case AF_INET: + if(setsockopt(ctl->socket_fd, SOL_IP, IP_TTL, &on, sizeof(on))) { + perror("IP_TTL"); + return -1; //exit(1); + } + } + + restart: + for(i = 0; i < 2; i++) { + int old_mtu; + + old_mtu = ctl->mtu; + res = probe_ttl(ctl); + if(ctl->mtu != old_mtu) + goto restart; + if(res == 0) + goto done; + if(res > 0) + break; + } + // if already find need name, make ret>=0 and break + { + int i = ctl->hisptr - 1; + //for(int i = 0; i < MAX_HOPS_DEFAULT; i++) + { + char *host_namea = (ctl->his[i].host_namea); + char *host_nameb = (ctl->his[i].host_nameb); + if(host_namea) { + if(!strcmp(host_namea, target_name) || !strcmp(host_namea, target_ip)) { + ctl->ttl = ctl->max_hops; + ret = i; + break; + } + } + if(host_nameb) { + if(!strcmp(host_nameb, target_name) || !strcmp(host_nameb, target_ip)) { + ctl->ttl = ctl->max_hops; + ret = i; + break; + } + } + } + } + if(res < 0) + log_printf("%2d: no reply\n", ctl->ttl); + } + log_printf(" Too many hops: pmtu %d\n", ctl->mtu); + + done: + freeaddrinfo(result); + + log_printf(" Resume: pmtu %d ", ctl->mtu); + if(ctl->hops_to >= 0) + { + ret = ctl->hisptr - 1; + log_printf("hops %d ", ctl->hops_to); + } + if(ctl->hops_from >= 0) + log_printf("back %d ", ctl->hops_from); + log_printf("\n"); + return ret; //exit(0); + + pktlen_error: + fprintf(stderr, "Error: pktlen must be > %d and <= %d\n", + ctl->overhead, INT_MAX); + return -1; //exit(1); +} + +int tracepath_util(const char *addr, int *hops, int *time_usec) +{ + int type = 4; // 4 or 6 + int total_hops = 0; + long int total_time_usec = 0; // latency in microseconds + int argc = 4; + const char *argv[argc]; + if(type != 4) + argv[0] = "tracepath6"; + else + argv[0] = "tracepath4"; + argv[1] = "-b"; // print both name and ip + argv[2] = "-m 26"; // -n -m 16 + argv[3] = addr; + struct run_state ctl = { + .max_hops = MAX_HOPS_DEFAULT, + .hops_to = -1, + .hops_from = -1, + 0 + }; + int ret = tracepath_main(argc, (char**) argv, &ctl); + for(int i = 0; i < MAX_HOPS_DEFAULT; i++) { + free(ctl.his[i].host_namea); + free(ctl.his[i].host_nameb); + } + if(ret >= 0) + { + struct timeval *deltatime = &(ctl.his[ret].deltatime); + total_hops = ctl.his[ret].hop; + total_time_usec = deltatime->tv_sec * 1000000 + deltatime->tv_usec; + } + else + for(int i = 0; i < MAX_HOPS_DEFAULT; i++) { + struct timeval *deltatime = &(ctl.his[i].deltatime); + if(ctl.his[i].hop) { //if(!ctl.his[i].hops && (deltatime->tv_sec > 0 || deltatime->tv_usec > 0)) { + total_hops = ctl.his[i].hop; + total_time_usec = deltatime->tv_sec * 1000000 + deltatime->tv_usec; + } + /*if(ctl.his[i].hop > 0) + { + char *host_name = (ctl.his[i].host_name) ? ctl.his[i].host_name : "-"; + printf("%d %d: %s %ld\n", ctl.his[i].hops, ctl.his[i].hop, host_name, + deltatime->tv_sec * 1000000 + deltatime->tv_usec); + }*/ + } + //g_free((char*) argv[2]); + if(hops) { + *hops = total_hops; + } + if(time_usec) { + *time_usec = total_time_usec; + } + return (ret >= 0) ? 0 : -1; + +} + -- GitLab