From: florian Date: Fri, 3 Sep 2021 09:13:00 +0000 (+0000) Subject: Make traceroute(8) faster by sending probes and doing DNS async. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=21b3e8786537fe05ba6f421951c85fa226eb0e20;p=openbsd Make traceroute(8) faster by sending probes and doing DNS async. Traditional traceroute would send one probe and then wait for up to 5 seconds for a reply and then send the next probe. On a lossy link that eventually ends in a black hole this would take about 15 minutes and people would hit control-c in anger. This rewrites the traceroute engine to use libevent and asr's async DNS interface. Probes are now send every 30ms or as soon as we get an answer back. With that we got the 15 minute worse case down to about 10 seconds. A minor adjustment that is possible with this is to delay printing a line until we get to a line with answers. This has two effects: 1) If there are intermediate hops that don't answer, output pauses for a bit so we keep the visual cue of "something might be wrong here". 2) If there is a black hole at the end, we don't print out many "* * *" lines and thus scrolling the interesting bits out of the terminal. We collapse those lines and just print 64 * * * at the end. Unfortunately the -c option to send udp probes to a fixed port had to go for now. But we should be able to add it back. "Once you have seen the new one you can't go back to the old one" & enthusiastic OK deraadt@ OK sthen@ "I am very distressed that florian went to bed without committing it" beck@ --- diff --git a/usr.sbin/traceroute/Makefile b/usr.sbin/traceroute/Makefile index c9797ab7244..bc6fbb8badb 100644 --- a/usr.sbin/traceroute/Makefile +++ b/usr.sbin/traceroute/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.10 2016/09/03 22:00:06 benno Exp $ +# $OpenBSD: Makefile,v 1.11 2021/09/03 09:13:00 florian Exp $ PROG= traceroute @@ -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 a/usr.sbin/traceroute/traceroute.8 b/usr.sbin/traceroute/traceroute.8 index cc211c14083..d3f5afa21a6 100644 --- a/usr.sbin/traceroute/traceroute.8 +++ b/usr.sbin/traceroute/traceroute.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: traceroute.8,v 1.71 2021/08/29 06:50:29 jmc Exp $ +.\" $OpenBSD: traceroute.8,v 1.72 2021/09/03 09:13:00 florian Exp $ .\" $NetBSD: traceroute.8,v 1.6 1995/10/12 03:05:50 mycroft Exp $ .\" .\" Copyright (c) 1990, 1991, 1993 @@ -33,7 +33,7 @@ .\" .\" @(#)traceroute.8 8.1 (Berkeley) 6/6/93 .\" -.Dd $Mdocdate: August 29 2021 $ +.Dd $Mdocdate: September 3 2021 $ .Dt TRACEROUTE 8 .Os .Sh NAME @@ -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 a/usr.sbin/traceroute/traceroute.c b/usr.sbin/traceroute/traceroute.c index 21ee76dba1c..e3bd866cf03 100644 --- a/usr.sbin/traceroute/traceroute.c +++ b/usr.sbin/traceroute/traceroute.c @@ -1,4 +1,4 @@ -/* $OpenBSD: traceroute.c,v 1.167 2021/08/31 18:12:47 florian Exp $ */ +/* $OpenBSD: traceroute.c,v 1.168 2021/09/03 09:13:00 florian Exp $ */ /* $NetBSD: traceroute.c,v 1.10 1995/05/21 15:50:45 mycroft Exp $ */ /* @@ -249,6 +249,7 @@ #include #include +#include #include #include #include @@ -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,106 @@ 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++; + } + + catchup_result_rows(tr_results, conf); + +} diff --git a/usr.sbin/traceroute/traceroute.h b/usr.sbin/traceroute/traceroute.h index 1704068d29c..c148a6c2dd7 100644 --- a/usr.sbin/traceroute/traceroute.h +++ b/usr.sbin/traceroute/traceroute.h @@ -1,4 +1,4 @@ -/* $OpenBSD: traceroute.h,v 1.6 2021/08/31 18:12:47 florian Exp $ */ +/* $OpenBSD: traceroute.h,v 1.7 2021/09/03 09:13:00 florian Exp $ */ /* $NetBSD: traceroute.c,v 1.10 1995/05/21 15:50:45 mycroft Exp $ */ /* @@ -67,6 +67,8 @@ #include #include +#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 a/usr.sbin/traceroute/worker.c b/usr.sbin/traceroute/worker.c index 7a6aef082c2..d53d0a39e5a 100644 --- a/usr.sbin/traceroute/worker.c +++ b/usr.sbin/traceroute/worker.c @@ -1,4 +1,4 @@ -/* $OpenBSD: worker.c,v 1.7 2021/08/31 18:12:47 florian Exp $ */ +/* $OpenBSD: worker.c,v 1.8 2021/09/03 09:13:00 florian Exp $ */ /* $NetBSD: traceroute.c,v 1.10 1995/05/21 15:50:45 mycroft Exp $ */ /* @@ -77,30 +77,50 @@ #include #include +#include #include +#include #include #include -#include #include +#include #include #include #include #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); +}