On 2021-09-01 04:05 -06, "Theo de Raadt" <dera...@openbsd.org> wrote: > Stuart Henderson <s...@spacehopper.org> wrote: > >> On 2021/09/01 11:25, Florian Obser wrote: >> > So traceroute sends one probe, waits upto 5^W3 seconds for an answer to >> > arrive, sends the next probe and so on. >> > >> > This makes it a bit faster (10x on a path with two intermediate systems >> > not answering) by sending probes, waiting for the answer and doing >> > reverse DNS lookups async. >> >> Basics are working here with icmp and udp. -A doesn't work reliably, >> try it with an empty cache - I think it only prints if the route lookup >> had a reply before the rdns lookup. > > interesting, this will need to be found and fixed.
I can't reproduce it here. > >> I don't know libevent, is it possible to reset the timer and skip the >> 30ms delay if a response is received? Currently it's a little slower >> for the case where all hops respond. yes, one way is to shedule a timeout right now, done in the diff below. > > that is an interesting idea also, it can be even faster. claudio/florian > and i have talked a lot about trying to send back-to-back packets to a > single ttl target, because it may trigger control plane protection filters > in those intermediate hops. > > but increasing the pace to other targets, is fine. > >> Hosts that don't respond result in a lot of *'s printed quickly, maybe >> scrolling the useful output off the terminal. The overall output is the >> same as the sequential version if you actually leave it to finish but >> most users would have hit ^C by then. I don't know what could be done >> to improve it (suppressing multiple * * * lines might help?) but user >> experience with that isn't great as-is. Try mp3.com for an example. > > there are two proposals for this, which would diverge the output: > > 15 * * * [repeating to ttl 64] > > or > > 15 * * * > [repeating to ttl 64] > we discussed a 3rd option in the hackroom: 15 192.168.2.23 42.0 ms 42.0 ms 42.0 ms 64 * * * > how often do people run scripts against traceroute output, which will care > about this change in format. > > I also considered a third option -- to meter the * * * printout lines > rate to 3/second, but this kind of conflicts with the goal for speeding > things up. > > diff --git Makefile Makefile index c9797ab7244..6b745132ff1 100644 --- Makefile +++ Makefile @@ -8,6 +8,8 @@ CFLAGS+= -Wall -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +LDADD= -levent +DPADD= ${LIBEVENT} MAN= traceroute.8 diff --git traceroute.8 traceroute.8 index cc211c14083..4e8d0d5f52a 100644 --- traceroute.8 +++ traceroute.8 @@ -42,7 +42,7 @@ .Nd print the route packets take to network host .Sh SYNOPSIS .Nm traceroute\ \& -.Op Fl AcDdIlnSvx +.Op Fl ADdIlnSvx .Op Fl f Ar first_ttl .Op Fl g Ar gateway_addr .Op Fl m Ar max_ttl @@ -56,7 +56,7 @@ .Ar host .Op Ar datalen .Nm traceroute6 -.Op Fl AcDdIlnSv +.Op Fl ADdIlnSv .Op Fl f Ar first_hop .Op Fl m Ar max_hop .Op Fl p Ar port @@ -91,11 +91,6 @@ The options are as follows: Look up the AS number for each hop address. Uses the DNS service described at .Lk https://www.team-cymru.com/IP-ASN-mapping.html#dns -.It Fl c -Do not increment the destination port number in successive UDP packets. -Rather, all UDP packets will have the same destination port, as set via the -.Fl p -flag (or 33434 if none is specified). .It Fl D Dump the packet data to standard error before transmitting it. .It Fl d diff --git traceroute.c traceroute.c index 21ee76dba1c..d1a40d292ac 100644 --- traceroute.c +++ traceroute.c @@ -249,6 +249,7 @@ #include <err.h> #include <errno.h> +#include <event.h> #include <limits.h> #include <netdb.h> #include <pwd.h> @@ -271,33 +272,44 @@ int sndsock; /* send (udp) socket file descriptor */ int rcvhlim; struct in6_pktinfo *rcvpktinfo; - int datalen; /* How much data */ +int datalen; /* How much data */ char *hostname; u_int16_t srcport; -void usage(int); +void usage(void); #define TRACEROUTE_USER "_traceroute" +void sock_read(int, short, void *); +void send_timer(int, short, void *); + +struct tr_conf *conf; /* configuration defaults */ +struct tr_result *tr_results; +struct sockaddr_in from4, to4; +struct sockaddr_in6 from6, to6; +struct sockaddr *from, *to; +struct msghdr rcvmhdr; +struct event timer_ev; +int v6flag; +int *waiting_ttls; +int last_tos = 0; + int main(int argc, char *argv[]) { int mib[4] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_DEFTTL }; char hbuf[NI_MAXHOST]; - struct tr_conf *conf; /* configuration defaults */ - struct sockaddr_in from4, to4; - struct sockaddr_in6 from6, to6; - struct sockaddr *from, *to; struct addrinfo hints, *res; struct hostent *hp; struct ip *ip = NULL; struct iovec rcviov[2]; - struct msghdr rcvmhdr; static u_char *rcvcmsgbuf; struct passwd *pw; + struct event sock_ev; + struct timeval tv = {0, 0}; long l; socklen_t len; @@ -305,25 +317,18 @@ main(int argc, char *argv[]) int ch; int on = 1; - int seq = 0; int error; - int curwaittime; int headerlen; /* How long packet's header is */ int i; - int last_tos = 0; int packetlen; - int probe; int rcvcmsglen; int rcvsock4, rcvsock6; int sndsock4, sndsock6; u_int32_t tmprnd; int v4sock_errno, v6sock_errno; - int v6flag = 0; - int xflag = 0; /* show ICMP extension header */ char *dest; const char *errstr; - u_int8_t ttl; uid_t ouid, uid; gid_t gid; @@ -337,11 +342,11 @@ main(int argc, char *argv[]) if ((conf = calloc(1, sizeof(*conf))) == NULL) err(1,NULL); - conf->incflag = 1; conf->first_ttl = 1; conf->proto = IPPROTO_UDP; conf->max_ttl = IPDEFTTL; conf->nprobes = 3; + conf->expected_responses = 2; /* icmp + DNS */ /* start udp dest port # for probe packets */ conf->port = 32768+666; @@ -421,14 +426,12 @@ main(int argc, char *argv[]) err(1, "sysctl"); conf->max_ttl = i; - while ((ch = getopt(argc, argv, v6flag ? "AcDdf:Ilm:np:q:Ss:t:w:vV:" : - "AcDdf:g:Ilm:nP:p:q:Ss:t:V:vw:x")) != -1) + while ((ch = getopt(argc, argv, v6flag ? "ADdf:Ilm:np:q:Ss:t:w:vV:" : + "ADdf:g:Ilm:nP:p:q:Ss:t:V:vw:x")) != -1) switch (ch) { case 'A': conf->Aflag = 1; - break; - case 'c': - conf->incflag = 0; + conf->expected_responses++; break; case 'd': conf->dflag = 1; @@ -476,6 +479,7 @@ main(int argc, char *argv[]) break; case 'n': conf->nflag = 1; + conf->expected_responses--; break; case 'p': conf->port = strtonum(optarg, 1, 65535, &errstr); @@ -500,7 +504,7 @@ main(int argc, char *argv[]) } break; case 'q': - conf->nprobes = strtonum(optarg, 1, INT_MAX, &errstr); + conf->nprobes = strtonum(optarg, 1, 1024, &errstr); if (errstr) errx(1, "nprobes must be >0."); break; @@ -561,10 +565,10 @@ main(int argc, char *argv[]) conf->waittime *= 1000; break; case 'x': - xflag = 1; + conf->xflag = 1; break; default: - usage(v6flag); + usage(); } if (ouid == 0 && (setgroups(1, &gid) || @@ -576,7 +580,16 @@ main(int argc, char *argv[]) argv += optind; if (argc < 1 || argc > 2) - usage(v6flag); + usage(); + + tr_results = calloc(sizeof(struct tr_result), conf->max_ttl * + conf->nprobes); + if (tr_results == NULL) + err(1, NULL); + + waiting_ttls = calloc(sizeof(int), conf->max_ttl); + for (i = 0; i < conf->max_ttl; i++) + waiting_ttls[i] = conf->nprobes * conf->expected_responses; setvbuf(stdout, NULL, _IOLBF, 0); @@ -862,112 +875,26 @@ main(int argc, char *argv[]) if (conf->first_ttl > 1) printf("Skipping %u intermediate hops\n", conf->first_ttl - 1); - for (ttl = conf->first_ttl; ttl && ttl <= conf->max_ttl; ++ttl) { - int got_there = 0, unreachable = 0, timeout = 0, loss; - in_addr_t lastaddr = 0; - struct in6_addr lastaddr6; - - printf("%2u ", ttl); - memset(&lastaddr6, 0, sizeof(lastaddr6)); - for (probe = 0, loss = 0; probe < conf->nprobes; ++probe) { - int cc; - struct timeval t1, t2; - - gettime(&t1); - send_probe(conf, ++seq, ttl, conf->incflag, to); - curwaittime = conf->waittime; - while ((cc = wait_for_reply(rcvsock, &rcvmhdr, - curwaittime))) { - gettime(&t2); - i = packet_ok(conf, to->sa_family, &rcvmhdr, - cc, seq, conf->incflag); - /* Skip wrong packet */ - if (i == 0) { - curwaittime = conf->waittime - - ((t2.tv_sec - t1.tv_sec) * 1000 + - (t2.tv_usec - t1.tv_usec) / 1000); - if (curwaittime < 0) - curwaittime = 0; - continue; - } - if (to->sa_family == AF_INET) { - ip = (struct ip *)packet; - if (from4.sin_addr.s_addr != lastaddr) { - print(conf, from, - cc - (ip->ip_hl << 2), - inet_ntop(AF_INET, - &ip->ip_dst, hbuf, - sizeof(hbuf))); - lastaddr = - from4.sin_addr.s_addr; - } - } else if (to->sa_family == AF_INET6) { - if (!IN6_ARE_ADDR_EQUAL( - &from6.sin6_addr, &lastaddr6)) { - print(conf, from, cc, - rcvpktinfo ? - inet_ntop( AF_INET6, - &rcvpktinfo->ipi6_addr, - hbuf, sizeof(hbuf)) : "?"); - lastaddr6 = from6.sin6_addr; - } - } else - errx(1, "unsupported AF: %d", - to->sa_family); - - printf(" %g ms", deltaT(&t1, &t2)); - if (conf->ttl_flag) - printf(" (%u)", v6flag ? rcvhlim : - ip->ip_ttl); - if (to->sa_family == AF_INET) { - if (i == -2) { - if (ip->ip_ttl <= 1) - printf(" !"); - ++got_there; - break; - } - - if (conf->tflag) - check_tos(ip, &last_tos); - } + event_init(); - /* time exceeded in transit */ - if (i == -1) - break; - icmp_code(to->sa_family, i - 1, &got_there, - &unreachable); - break; - } - if (cc == 0) { - printf(" *"); - timeout++; - loss++; - } else if (cc && probe == conf->nprobes - 1 && - (xflag || conf->verbose)) - print_exthdr(packet, cc); - (void) fflush(stdout); - } - if (conf->sump) - printf(" (%d%% loss)", (loss * 100) / conf->nprobes); - putchar('\n'); - if (got_there || - (unreachable && (unreachable + timeout) >= conf->nprobes)) - break; - } - exit(0); + event_set(&sock_ev, rcvsock, EV_READ | EV_PERSIST, sock_read, NULL); + event_add(&sock_ev, NULL); + evtimer_set(&timer_ev, send_timer, &timer_ev); + evtimer_add(&timer_ev, &tv); + event_dispatch(); } void -usage(int v6flag) +usage(void) { if (v6flag) { fprintf(stderr, "usage: %s " - "[-AcDdIlnSv] [-f first_hop] [-m max_hop] [-p port]\n" + "[-ADdIlnSv] [-f first_hop] [-m max_hop] [-p port]\n" "\t[-q nqueries] [-s sourceaddr] [-t toskeyword] [-V rtable] " "[-w waittime]\n\thost [datalen]\n", __progname); } else { fprintf(stderr, - "usage: %s [-AcDdIlnSvx] [-f first_ttl] [-g gateway_addr] " + "usage: %s [-ADdIlnSvx] [-f first_ttl] [-g gateway_addr] " "[-m max_ttl]\n" "\t[-P proto] [-p port] [-q nqueries] [-s sourceaddr]\n" "\t[-t toskeyword] " @@ -976,3 +903,104 @@ usage(int v6flag) } exit(1); } + +void +sock_read(int fd, short events, void *arg) +{ + struct ip *ip; + struct timeval t2, tv = {0, 0}; + int pkg_ok, cc, recv_seq, recv_seq_row; + char hbuf[NI_MAXHOST]; + + cc = recvmsg(rcvsock, &rcvmhdr, 0); + + if (cc == 0) + return; + + evtimer_add(&timer_ev, &tv); + + gettime(&t2); + + pkg_ok = packet_ok(conf, to->sa_family, &rcvmhdr, cc, &recv_seq); + + /* Skip wrong packet */ + if (pkg_ok == 0) + goto out; + + /* skip corrupt sequence number */ + if (recv_seq < 0 || recv_seq >= conf->max_ttl * conf->nprobes) + goto out; + + recv_seq_row = recv_seq / conf->nprobes; + + /* skipping dup */ + if (tr_results[recv_seq].dup++) + goto out; + + switch (to->sa_family) { + case AF_INET: + ip = (struct ip *)packet; + + print(conf, from, cc - (ip->ip_hl << 2), inet_ntop(AF_INET, + &ip->ip_dst, hbuf, sizeof(hbuf)), &tr_results[recv_seq]); + break; + case AF_INET6: + print(conf, from, cc, rcvpktinfo ? inet_ntop(AF_INET6, + &rcvpktinfo->ipi6_addr, hbuf, sizeof(hbuf)) : "?", + &tr_results[recv_seq]); + break; + default: + errx(1, "unsupported AF: %d", to->sa_family); + } + + tr_results[recv_seq].t2 = t2; + tr_results[recv_seq].resp_ttl = v6flag ? rcvhlim : ip->ip_ttl; + + waiting_ttls[recv_seq_row]--; + + if (pkg_ok == -2) { + if ((v6flag && rcvhlim <= 1) || + (!v6flag && ip->ip_ttl <=1)) + snprintf(tr_results[recv_seq].icmp_code, + sizeof(tr_results[recv_seq].icmp_code), "%s", " !"); + tr_results[recv_seq].got_there++; + } else { + if (to->sa_family == AF_INET && conf->tflag) + check_tos(ip, &last_tos, &tr_results[recv_seq]); + if (pkg_ok != -1) { + icmp_code(to->sa_family, pkg_ok - 1, + &tr_results[recv_seq].got_there, + &tr_results[recv_seq].unreachable, + &tr_results[recv_seq]); + } + } + + if (cc && ((recv_seq + 1) % conf->nprobes) == 0 && + (conf->xflag || conf->verbose)) + print_exthdr(packet, cc, &tr_results[recv_seq]); + out: + catchup_result_rows(tr_results, conf); +} + +void +send_timer(int fd, short events, void *arg) +{ + static int seq; + struct timeval tv = {0, 30000}, t1; + struct event *ev = arg; + int ttl; + + evtimer_add(ev, &tv); + + ttl = conf->first_ttl + seq / conf->nprobes; + if (ttl <= conf->max_ttl) { + gettime(&t1); + tr_results[seq].seq = seq; + tr_results[seq].row = seq / conf->nprobes; + tr_results[seq].ttl = ttl; + tr_results[seq].t1 = t1; + send_probe(conf, seq, ttl, to); + seq++; + } else + catchup_result_rows(tr_results, conf); +} diff --git traceroute.h traceroute.h index 1704068d29c..6e186cb29a2 100644 --- traceroute.h +++ traceroute.h @@ -67,6 +67,8 @@ #include <netinet/ip_var.h> #include <netmpls/mpls.h> +#define ICMP_CODE 0; + #define DUMMY_PORT 10010 #define MAX_LSRR ((MAX_IPOPTLEN - 4) / 4) @@ -86,7 +88,6 @@ struct packetdata { } __packed; struct tr_conf { - int incflag; /* Do not inc the dest. port num */ int first_ttl; /* Set the first TTL or hop limit */ u_char proto; /* IP payload protocol to use */ u_int8_t max_ttl; /* Set the maximum TTL / hop limit */ @@ -106,46 +107,55 @@ struct tr_conf { int sump; int tos; int tflag; /* tos value was set */ + int xflag; /* show ICMP extension header */ int verbose; u_int rtableid; /* Set the routing table */ u_short ident; + int expected_responses; +}; + +struct tr_result { + int seq; + int row; + int dup; + int timeout; + uint8_t ttl; + uint8_t resp_ttl; + char hbuf[NI_MAXHOST]; + char inetname[NI_MAXHOST]; + char *asn; + char *exthdr; + char to[NI_MAXHOST]; + int cc; + struct timeval t1; + struct timeval t2; + char icmp_code[sizeof("!<255>")]; + char tos[sizeof(" (TOS=255!)")]; + int got_there; + int unreachable; + int inetname_done; + int asn_done; }; +extern int *waiting_ttls; extern int32_t sec_perturb; extern int32_t usec_perturb; extern u_char packet[512]; extern u_char *outpacket; /* last inbound (icmp) packet */ -int wait_for_reply(int, struct msghdr *, int); -void dump_packet(void); -void build_probe4(struct tr_conf *, int, u_int8_t, int); -void build_probe6(struct tr_conf *, int, u_int8_t, int, - struct sockaddr *); -void send_probe(struct tr_conf *, int, u_int8_t, int, - struct sockaddr *); -struct udphdr *get_udphdr(struct tr_conf *, struct ip6_hdr *, u_char *); -int packet_ok(struct tr_conf *, int, struct msghdr *, int, int, - int); -int packet_ok4(struct tr_conf *, struct msghdr *, int, int, int); -int packet_ok6(struct tr_conf *, struct msghdr *, int, int, int); -void icmp_code(int, int, int *, int *); -void icmp4_code(int, int *, int *); -void icmp6_code(int, int *, int *); -void dump_packet(void); -void print_exthdr(u_char *, int); -void check_tos(struct ip*, int *); -void print(struct tr_conf *, struct sockaddr *, int, const char *); -const char *inetname(struct sockaddr*); -void print_asn(struct sockaddr_storage *); -u_short in_cksum(u_short *, int); -char *pr_type(u_int8_t); +void send_probe(struct tr_conf *, int, u_int8_t, struct sockaddr *); +int packet_ok(struct tr_conf *, int, struct msghdr *, int, int *); +void icmp_code(int, int, int *, int *, struct tr_result *); +void check_tos(struct ip*, int *, struct tr_result *); int map_tos(char *, int *); -double deltaT(struct timeval *, struct timeval *); - +void print(struct tr_conf *, struct sockaddr *, int, const char *, + struct tr_result *); +void print_exthdr(u_char *, int, struct tr_result *); void gettime(struct timeval *); -extern int rcvsock; /* receive (icmp) socket file descriptor */ +void catchup_result_rows(struct tr_result *, struct tr_conf *); + extern int sndsock; /* send (udp) socket file descriptor */ extern int rcvhlim; @@ -157,8 +167,6 @@ extern char *hostname; extern u_int16_t srcport; -#define ICMP_CODE 0; - extern int verbose; extern int dump; diff --git worker.c worker.c index 7a6aef082c2..88dd5376b57 100644 --- worker.c +++ worker.c @@ -77,30 +77,50 @@ #include <arpa/inet.h> #include <arpa/nameser.h> +#include <asr.h> #include <err.h> +#include <event.h> #include <limits.h> #include <netdb.h> -#include <poll.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #include "traceroute.h" -static u_int8_t icmp_type = ICMP_ECHO; /* default ICMP code/type */ +void build_probe4(struct tr_conf *, int, u_int8_t); +void build_probe6(struct tr_conf *, int, u_int8_t, + struct sockaddr *); +int packet_ok4(struct tr_conf *, struct msghdr *, int, int *); +int packet_ok6(struct tr_conf *, struct msghdr *, int, int *); +void icmp4_code(int, int *, int *, struct tr_result *); +void icmp6_code(int, int *, int *, struct tr_result *); +struct udphdr *get_udphdr(struct tr_conf *, struct ip6_hdr *, u_char *); +void dump_packet(void); +void print_asn(struct sockaddr_storage *, struct tr_result *); +u_short in_cksum(u_short *, int); +char *pr_type(u_int8_t); +double deltaT(struct timeval *, struct timeval *); +void check_timeout(struct tr_result *, struct tr_conf *); +void print_result_row(struct tr_result *, struct tr_conf *); +void getnameinfo_async_done(struct asr_result *, void *); +void getrrsetbyname_async_done(struct asr_result *, void *); void -print_exthdr(u_char *buf, int cc) +print_exthdr(u_char *buf, int cc, struct tr_result *tr_res) { struct icmp_ext_hdr exthdr; struct icmp_ext_obj_hdr objhdr; struct ip *ip; struct icmp *icp; + size_t exthdr_size, len; int hlen, first; u_int32_t label; u_int16_t off, olen; u_int8_t type; + char *exthdr_str; ip = (struct ip *)buf; hlen = ip->ip_hl << 2; @@ -148,6 +168,13 @@ print_exthdr(u_char *buf, int cc) buf += sizeof(exthdr); cc -= sizeof(exthdr); + /* rough estimate of needed space */ + exthdr_size = sizeof("[MPLS Label 1048576 (Exp 3)]") * + (cc / sizeof(u_int32_t)); + if ((tr_res->exthdr = calloc(1, exthdr_size)) == NULL) + err(1, NULL); + exthdr_str = tr_res->exthdr; + while (cc > sizeof(objhdr)) { memcpy(&objhdr, buf, sizeof(objhdr)); olen = ntohs(objhdr.ieo_length); @@ -175,21 +202,62 @@ print_exthdr(u_char *buf, int cc) olen -= sizeof(u_int32_t); if (first == 0) { - printf(" [MPLS Label "); + len = snprintf(exthdr_str, + exthdr_size, "%s", + " [MPLS Label "); + if (len != -1 && len < + exthdr_size) { + exthdr_str += len; + exthdr_size -= len; + } first++; - } else - printf(", "); - printf("%d", MPLS_LABEL(label)); - if (MPLS_EXP(label)) - printf(" (Exp %x)", + } else { + len = snprintf(exthdr_str, + exthdr_size, "%s", + ", "); + if (len != -1 && len < + exthdr_size) { + exthdr_str += len; + exthdr_size -= len; + } + } + len = snprintf(exthdr_str, + exthdr_size, + "%d", MPLS_LABEL(label)); + if (len != -1 && len < exthdr_size) { + exthdr_str += len; + exthdr_size -= len; + } + if (MPLS_EXP(label)) { + len = snprintf(exthdr_str, + exthdr_size, " (Exp %x)", MPLS_EXP(label)); + if (len != -1 && len < + exthdr_size) { + exthdr_str += len; + exthdr_size -= len; + } + } } if (olen > 0) { - printf("|]"); + len = snprintf(exthdr_str, + exthdr_size, "%s", "|]"); + if (len != -1 && len < + exthdr_size) { + exthdr_str += len; + exthdr_size -= len; + } return; } - if (first != 0) - printf("]"); + if (first != 0) { + len = snprintf(exthdr_str, + exthdr_size, "%s", "]"); + if (len != -1 && len < + exthdr_size) { + exthdr_str += len; + exthdr_size -= len; + } + } break; default: buf += olen; @@ -205,7 +273,7 @@ print_exthdr(u_char *buf, int cc) } void -check_tos(struct ip *ip, int *last_tos) +check_tos(struct ip *ip, int *last_tos, struct tr_result *tr_res) { struct icmp *icp; struct ip *inner_ip; @@ -214,27 +282,12 @@ check_tos(struct ip *ip, int *last_tos) inner_ip = (struct ip *) (((u_char *)icp)+8); if (inner_ip->ip_tos != *last_tos) - printf (" (TOS=%d!)", inner_ip->ip_tos); + snprintf(tr_res->tos, sizeof(tr_res->tos), + " (TOS=%d!)", inner_ip->ip_tos); *last_tos = inner_ip->ip_tos; } -int -wait_for_reply(int sock, struct msghdr *mhdr, int curwaittime) -{ - struct pollfd pfd[1]; - int cc = 0; - - pfd[0].fd = sock; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - - if (poll(pfd, 1, curwaittime) > 0) - cc = recvmsg(rcvsock, mhdr, 0); - - return (cc); -} - void dump_packet(void) { @@ -251,7 +304,7 @@ dump_packet(void) } void -build_probe4(struct tr_conf *conf, int seq, u_int8_t ttl, int iflag) +build_probe4(struct tr_conf *conf, int seq, u_int8_t ttl) { struct ip *ip = (struct ip *)outpacket; u_char *p = (u_char *)(ip + 1); @@ -266,7 +319,7 @@ build_probe4(struct tr_conf *conf, int seq, u_int8_t ttl, int iflag) switch (conf->proto) { case IPPROTO_ICMP: - icmpp->icmp_type = icmp_type; + icmpp->icmp_type = ICMP_ECHO; icmpp->icmp_code = ICMP_CODE; icmpp->icmp_seq = htons(seq); icmpp->icmp_id = htons(conf->ident); @@ -274,10 +327,7 @@ build_probe4(struct tr_conf *conf, int seq, u_int8_t ttl, int iflag) break; case IPPROTO_UDP: up->uh_sport = htons(conf->ident); - if (iflag) - up->uh_dport = htons(conf->port+seq); - else - up->uh_dport = htons(conf->port); + up->uh_dport = htons(conf->port+seq); up->uh_ulen = htons((u_short)(datalen - sizeof(struct ip) - conf->lsrrlen)); up->uh_sum = 0; @@ -307,7 +357,7 @@ build_probe4(struct tr_conf *conf, int seq, u_int8_t ttl, int iflag) op->sec = htonl(tv.tv_sec + sec_perturb); op->usec = htonl((tv.tv_usec + usec_perturb) % 1000000); - if (conf->proto == IPPROTO_ICMP && icmp_type == ICMP_ECHO) { + if (conf->proto == IPPROTO_ICMP) { icmpp->icmp_cksum = 0; icmpp->icmp_cksum = in_cksum((u_short *)icmpp, datalen - sizeof(struct ip) - conf->lsrrlen); @@ -317,7 +367,7 @@ build_probe4(struct tr_conf *conf, int seq, u_int8_t ttl, int iflag) } void -build_probe6(struct tr_conf *conf, int seq, u_int8_t hops, int iflag, +build_probe6(struct tr_conf *conf, int seq, u_int8_t hops, struct sockaddr *to) { struct timeval tv; @@ -329,10 +379,9 @@ build_probe6(struct tr_conf *conf, int seq, u_int8_t hops, int iflag, (char *)&i, sizeof(i)) == -1) warn("setsockopt IPV6_UNICAST_HOPS"); - if (iflag) - ((struct sockaddr_in6*)to)->sin6_port = htons(conf->port + seq); - else - ((struct sockaddr_in6*)to)->sin6_port = htons(conf->port); + + ((struct sockaddr_in6*)to)->sin6_port = htons(conf->port + seq); + gettime(&tv); if (conf->proto == IPPROTO_ICMP) { @@ -354,17 +403,16 @@ build_probe6(struct tr_conf *conf, int seq, u_int8_t hops, int iflag, } void -send_probe(struct tr_conf *conf, int seq, u_int8_t ttl, int iflag, - struct sockaddr *to) +send_probe(struct tr_conf *conf, int seq, u_int8_t ttl, struct sockaddr *to) { int i; switch (to->sa_family) { case AF_INET: - build_probe4(conf, seq, ttl, iflag); + build_probe4(conf, seq, ttl); break; case AF_INET6: - build_probe6(conf, seq, ttl, iflag, to); + build_probe6(conf, seq, ttl, to); break; default: errx(1, "unsupported AF: %d", to->sa_family); @@ -428,15 +476,14 @@ pr_type(u_int8_t t) } int -packet_ok(struct tr_conf *conf, int af, struct msghdr *mhdr, int cc, int seq, - int iflag) +packet_ok(struct tr_conf *conf, int af, struct msghdr *mhdr, int cc, int *seq) { switch (af) { case AF_INET: - return packet_ok4(conf, mhdr, cc, seq, iflag); + return packet_ok4(conf, mhdr, cc, seq); break; case AF_INET6: - return packet_ok6(conf, mhdr, cc, seq, iflag); + return packet_ok6(conf, mhdr, cc, seq); break; default: errx(1, "unsupported AF: %d", af); @@ -445,7 +492,7 @@ packet_ok(struct tr_conf *conf, int af, struct msghdr *mhdr, int cc, int seq, } int -packet_ok4(struct tr_conf *conf, struct msghdr *mhdr, int cc,int seq, int iflag) +packet_ok4(struct tr_conf *conf, struct msghdr *mhdr, int cc, int *seq) { struct sockaddr_in *from = (struct sockaddr_in *)mhdr->msg_name; struct icmp *icp; @@ -478,27 +525,26 @@ packet_ok4(struct tr_conf *conf, struct msghdr *mhdr, int cc,int seq, int iflag) switch (conf->proto) { case IPPROTO_ICMP: - if (icmp_type == ICMP_ECHO && - type == ICMP_ECHOREPLY && - icp->icmp_id == htons(conf->ident) && - icp->icmp_seq == htons(seq)) + if (type == ICMP_ECHOREPLY && + icp->icmp_id == htons(conf->ident)) { + *seq = ntohs(icp->icmp_seq); return (-2); /* we got there */ - + } icmpp = (struct icmp *)((u_char *)hip + hlen); if (hlen + 8 <= cc && hip->ip_p == IPPROTO_ICMP && - icmpp->icmp_id == htons(conf->ident) && - icmpp->icmp_seq == htons(seq)) + icmpp->icmp_id == htons(conf->ident)) { + *seq = ntohs(icmpp->icmp_seq); return (type == ICMP_TIMXCEED? -1 : code + 1); + } break; case IPPROTO_UDP: up = (struct udphdr *)((u_char *)hip + hlen); if (hlen + 12 <= cc && hip->ip_p == conf->proto && - up->uh_sport == htons(conf->ident) && - ((iflag && up->uh_dport == htons(conf->port + - seq)) || - (!iflag && up->uh_dport == htons(conf->port)))) + up->uh_sport == htons(conf->ident)) { + *seq = ntohs(up->uh_dport) - conf->port; return (type == ICMP_TIMXCEED? -1 : code + 1); + } break; default: /* this is some odd, user specified proto, @@ -523,8 +569,7 @@ packet_ok4(struct tr_conf *conf, struct msghdr *mhdr, int cc,int seq, int iflag) } int -packet_ok6(struct tr_conf *conf, struct msghdr *mhdr, int cc, int seq, - int iflag) +packet_ok6(struct tr_conf *conf, struct msghdr *mhdr, int cc, int *seq) { struct icmp6_hdr *icp; struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name; @@ -540,7 +585,8 @@ packet_ok6(struct tr_conf *conf, struct msghdr *mhdr, int cc, int seq, if (getnameinfo((struct sockaddr *)from, from->sin6_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(hbuf, "invalid", sizeof(hbuf)); - printf("data too short (%d bytes) from %s\n", cc, hbuf); + printf("packet too short (%d bytes) from %s\n", cc, + hbuf); } return(0); } @@ -582,18 +628,19 @@ packet_ok6(struct tr_conf *conf, struct msghdr *mhdr, int cc, int seq, return(0); } if (useicmp && - ((struct icmp6_hdr *)up)->icmp6_id == conf->ident && - ((struct icmp6_hdr *)up)->icmp6_seq == htons(seq)) + ((struct icmp6_hdr *)up)->icmp6_id == conf->ident) { + *seq = ntohs(((struct icmp6_hdr *)up)->icmp6_seq); return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1); - else if (!useicmp && - up->uh_sport == htons(srcport) && - ((iflag && up->uh_dport == htons(conf->port + seq)) || - (!iflag && up->uh_dport == htons(conf->port)))) + } else if (!useicmp && + up->uh_sport == htons(srcport)) { + *seq = ntohs(up->uh_dport) - conf->port; return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1); + } } else if (useicmp && type == ICMP6_ECHO_REPLY) { - if (icp->icmp6_id == conf->ident && - icp->icmp6_seq == htons(seq)) - return (ICMP6_DST_UNREACH_NOPORT + 1); + if (icp->icmp6_id == conf->ident) { + *seq = ntohs(icp->icmp6_seq); + return (-2); + } } if (conf->verbose) { char sbuf[NI_MAXHOST], dbuf[INET6_ADDRSTRLEN]; @@ -626,22 +673,32 @@ packet_ok6(struct tr_conf *conf, struct msghdr *mhdr, int cc, int seq, } void -print(struct tr_conf *conf, struct sockaddr *from, int cc, const char *to) +print(struct tr_conf *conf, struct sockaddr *from, int cc, const char *to, + struct tr_result *tr_res) { - char hbuf[NI_MAXHOST]; + struct asr_query *aq; + char hbuf[NI_MAXHOST]; + if (getnameinfo(from, from->sa_len, - hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) - strlcpy(hbuf, "invalid", sizeof(hbuf)); - if (conf->nflag) - printf(" %s", hbuf); - else - printf(" %s (%s)", inetname(from), hbuf); + tr_res->hbuf, sizeof(tr_res->hbuf), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(tr_res->hbuf, "invalid", sizeof(hbuf)); + + if (!conf->nflag) { + aq = getnameinfo_async(from, from->sa_len, tr_res->inetname, + sizeof(tr_res->inetname), NULL, 0, NI_NAMEREQD, NULL); + if (aq != NULL) + event_asr_run(aq, getnameinfo_async_done, tr_res); + else { + waiting_ttls[tr_res->row]--; + tr_res->inetname_done = 1; /* use hbuf */ + } + } if (conf->Aflag) - print_asn((struct sockaddr_storage *)from); + print_asn((struct sockaddr_storage *)from, tr_res); - if (conf->verbose) - printf(" %d bytes to %s", cc, to); + strlcpy(tr_res->to, to, sizeof(tr_res->to)); + tr_res->cc = cc; } /* @@ -690,14 +747,15 @@ get_udphdr(struct tr_conf *conf, struct ip6_hdr *ip6, u_char *lim) } void -icmp_code(int af, int code, int *got_there, int *unreachable) +icmp_code(int af, int code, int *got_there, int *unreachable, + struct tr_result *tr_res) { switch (af) { case AF_INET: - icmp4_code(code, got_there, unreachable); + icmp4_code(code, got_there, unreachable, tr_res); break; case AF_INET6: - icmp6_code(code, got_there, unreachable); + icmp6_code(code, got_there, unreachable, tr_res); break; default: errx(1, "unsupported AF: %d", af); @@ -706,97 +764,116 @@ icmp_code(int af, int code, int *got_there, int *unreachable) } void -icmp4_code(int code, int *got_there, int *unreachable) +icmp4_code(int code, int *got_there, int *unreachable, struct tr_result *tr_res) { struct ip *ip = (struct ip *)packet; switch (code) { case ICMP_UNREACH_PORT: if (ip->ip_ttl <= 1) - printf(" !"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), + "%s", " !"); ++(*got_there); break; case ICMP_UNREACH_NET: ++(*unreachable); - printf(" !N"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !N"); break; case ICMP_UNREACH_HOST: ++(*unreachable); - printf(" !H"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !H"); break; case ICMP_UNREACH_PROTOCOL: ++(*got_there); - printf(" !P"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !P"); break; case ICMP_UNREACH_NEEDFRAG: ++(*unreachable); - printf(" !F"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !F"); break; case ICMP_UNREACH_SRCFAIL: ++(*unreachable); - printf(" !S"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !S"); break; case ICMP_UNREACH_FILTER_PROHIB: ++(*unreachable); - printf(" !X"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !X"); break; case ICMP_UNREACH_NET_PROHIB: /*misuse*/ ++(*unreachable); - printf(" !A"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !A"); break; case ICMP_UNREACH_HOST_PROHIB: ++(*unreachable); - printf(" !C"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !C"); break; case ICMP_UNREACH_NET_UNKNOWN: case ICMP_UNREACH_HOST_UNKNOWN: ++(*unreachable); - printf(" !U"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !U"); break; case ICMP_UNREACH_ISOLATED: ++(*unreachable); - printf(" !I"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !I"); break; case ICMP_UNREACH_TOSNET: case ICMP_UNREACH_TOSHOST: ++(*unreachable); - printf(" !T"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !T"); break; default: ++(*unreachable); - printf(" !<%d>", code); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), " !<%d>", + code & 0xff); break; } } void -icmp6_code(int code, int *got_there, int *unreachable) +icmp6_code(int code, int *got_there, int *unreachable, struct tr_result *tr_res) { switch (code) { case ICMP6_DST_UNREACH_NOROUTE: ++(*unreachable); - printf(" !N"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !N"); break; case ICMP6_DST_UNREACH_ADMIN: ++(*unreachable); - printf(" !P"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !P"); break; case ICMP6_DST_UNREACH_BEYONDSCOPE: ++(*unreachable); - printf(" !S"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !S"); break; case ICMP6_DST_UNREACH_ADDR: ++(*unreachable); - printf(" !A"); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), "%s", + " !A"); break; case ICMP6_DST_UNREACH_NOPORT: - if (rcvhlim >= 0 && rcvhlim <= 1) - printf(" !"); + if (rcvhlim <= 1) + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), + "%s", " !"); ++(*got_there); break; default: ++(*unreachable); - printf(" !<%d>", code); + snprintf(tr_res->icmp_code, sizeof(tr_res->icmp_code), " !<%d>", + code & 0xff); break; } } @@ -834,45 +911,12 @@ in_cksum(u_short *addr, int len) return (answer); } -/* - * Construct an Internet address representation. - */ -const char * -inetname(struct sockaddr *sa) -{ - static char line[NI_MAXHOST], domain[HOST_NAME_MAX + 1]; - static int first = 1; - char *cp; - - if (first) { - first = 0; - if (gethostname(domain, sizeof(domain)) == 0 && - (cp = strchr(domain, '.')) != NULL) - memmove(domain, cp + 1, strlen(cp + 1) + 1); - else - domain[0] = 0; - } - if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, - NI_NAMEREQD) == 0) { - if ((cp = strchr(line, '.')) != NULL && strcmp(cp + 1, - domain) == 0) - *cp = '\0'; - return (line); - } - - if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, - NI_NUMERICHOST) != 0) - return ("invalid"); - return (line); -} - void -print_asn(struct sockaddr_storage *ss) +print_asn(struct sockaddr_storage *ss, struct tr_result *tr_res) { - struct rrsetinfo *answers = NULL; - int counter; - const u_char *uaddr; - char qbuf[MAXDNAME]; + struct asr_query *aq; + const u_char *uaddr; + char qbuf[MAXDNAME]; switch (ss->ss_family) { case AF_INET: @@ -911,21 +955,12 @@ print_asn(struct sockaddr_storage *ss) return; } - if (getrrsetbyname(qbuf, C_IN, T_TXT, 0, &answers) != 0) - return; - for (counter = 0; counter < answers->rri_nrdatas; counter++) { - char *p, *as = answers->rri_rdatas[counter].rdi_data; - as++; /* skip first byte, it contains length */ - if ((p = strchr(as,'|'))) { - printf(counter ? ", " : " ["); - p[-1] = 0; - printf("AS%s", as); - } + if ((aq = getrrsetbyname_async(qbuf, C_IN, T_TXT, 0, NULL)) != NULL) + event_asr_run(aq, getrrsetbyname_async_done, tr_res); + else { + waiting_ttls[tr_res->row]--; + tr_res->asn_done = 1; } - if (counter) - printf("]"); - - freerrset(answers); } int @@ -986,3 +1021,201 @@ gettime(struct timeval *tv) TIMESPEC_TO_TIMEVAL(tv, &ts); } + +void +check_timeout(struct tr_result *tr_row, struct tr_conf *conf) +{ + struct timeval t2; + int i; + + gettime(&t2); + + for (i = 0; i < conf->nprobes; i++) { + /* we didn't send the probe yet */ + if (tr_row[i].ttl == 0) + return; + /* we got a result, it can no longer timeout */ + if (tr_row[i].dup) + continue; + + if (deltaT(&tr_row[i].t1, &t2) > conf->waittime) { + tr_row[i].timeout = 1; + tr_row[i].dup++; /* we "saw" the result */ + waiting_ttls[tr_row[i].row] -= + conf->expected_responses; + } + } +} + +void +catchup_result_rows(struct tr_result *tr_results, struct tr_conf *conf) +{ + static int timeout_row = 0; + static int print_row = 0; + int i, j, all_timeout = 1; + + for (; timeout_row < conf->max_ttl; timeout_row++) { + struct tr_result *tr_row = tr_results + + timeout_row * conf->nprobes; + check_timeout(tr_row, conf); + if (waiting_ttls[timeout_row] > 0) + break; + } + + for (i = print_row; i < timeout_row; i++) { + struct tr_result *tr_row = tr_results + i * conf->nprobes; + + if (waiting_ttls[i] > 0) + break; + + for (j = 0; j < conf->nprobes; j++) { + if (!tr_row[j].timeout) { + all_timeout = 0; + break; + } + } + if (!all_timeout) + break; + } + + if (all_timeout && i != conf->max_ttl) + return; + + if (i == conf->max_ttl) + print_row = i - 1; /* jump ahead, skip long trail of * * * */ + + for (; print_row <= i; print_row++) { + struct tr_result *tr_row = tr_results + + print_row * conf->nprobes; + if (waiting_ttls[print_row] > 0) + break; + print_result_row(tr_row, conf); + } +} + +void +print_result_row(struct tr_result *tr_results, struct tr_conf *conf) +{ + int i, loss = 0, got_there = 0, unreachable = 0; + char *lastaddr = NULL; + + printf("%2u ", tr_results[0].ttl); + for (i = 0; i < conf->nprobes; i++) { + got_there += tr_results[i].got_there; + unreachable += tr_results[i].unreachable; + + if (tr_results[i].timeout) { + printf(" %s%s", "*", tr_results[i].icmp_code); + loss++; + continue; + } + + if (lastaddr == NULL || strcmp(lastaddr, tr_results[i].hbuf) + != 0) { + if (*tr_results[i].hbuf != '\0') { + if (conf->nflag) + printf(" %s", tr_results[i].hbuf); + else + printf(" %s (%s)", + tr_results[i].inetname[0] == '\0' ? + tr_results[i].hbuf : + tr_results[i].inetname, + tr_results[i].hbuf); + if (conf->Aflag && tr_results[i].asn != NULL) + printf(" %s", tr_results[i].asn); + if (conf->verbose) + printf(" %d bytes to %s", + tr_results[i].cc, + tr_results[i].to); + } + } + lastaddr = tr_results[i].hbuf; + printf(" %g ms%s%s", + deltaT(&tr_results[i].t1, + &tr_results[i].t2), + tr_results[i].tos, + tr_results[i].icmp_code); + if (conf->ttl_flag) + printf(" (%u)", tr_results[i].resp_ttl); + + if (tr_results[i].exthdr) + printf("%s", tr_results[i].exthdr); + } + if (conf->sump) + printf(" (%d%% loss)", (loss * 100) / conf->nprobes); + putchar('\n'); + fflush(stdout); + if (got_there || unreachable || tr_results[0].ttl == conf->max_ttl) + exit(0); +} + +void +getnameinfo_async_done(struct asr_result *ar, void *arg) +{ + static char domain[HOST_NAME_MAX + 1]; + static int first = 1; + struct tr_result *tr_res = arg; + char *cp; + + if (first) { + first = 0; + if (gethostname(domain, sizeof(domain)) == 0 && + (cp = strchr(domain, '.')) != NULL) + memmove(domain, cp + 1, strlen(cp + 1) + 1); + else + domain[0] = 0; + } + + tr_res->inetname_done = 1; + waiting_ttls[tr_res->row]--; + + if (ar->ar_gai_errno == 0) { + if ((cp = strchr(tr_res->inetname, '.')) != NULL && + strcmp(cp + 1, domain) == 0) + *cp = '\0'; + } else + tr_res->inetname[0]='\0'; +} + +void +getrrsetbyname_async_done(struct asr_result *ar, void *arg) +{ + struct tr_result *tr_res = arg; + struct rrsetinfo *answers; + size_t asn_size = 0, len; + int counter; + char *asn; + + tr_res->asn_done = 1; + waiting_ttls[tr_res->row]--; + if (ar->ar_rrset_errno != 0) + return; + + answers = ar->ar_rrsetinfo; + + if (answers->rri_nrdatas > 0) { + asn_size = answers->rri_nrdatas * sizeof("AS2147483647, ") + 3; + if ((tr_res->asn = calloc(1, asn_size)) == NULL) + err(1, NULL); + asn = tr_res->asn; + } + + for (counter = 0; counter < answers->rri_nrdatas; counter++) { + char *p, *as = answers->rri_rdatas[counter].rdi_data; + as++; /* skip first byte, it contains length */ + if ((p = strchr(as,'|'))) { + p[-1] = 0; + len = snprintf(asn, asn_size, "%sAS%s", + counter ? ", " : "[", as); + if (len != -1 && len < asn_size) { + asn += len; + asn_size -= len; + } else + asn_size = 0; + } + } + if (counter && asn_size > 0) + *asn=']'; + + freerrset(answers); +} -- I'm not entirely sure you are real.