-/* $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 $ */
/*
#include <err.h>
#include <errno.h>
+#include <event.h>
#include <limits.h>
#include <netdb.h>
#include <pwd.h>
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;
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;
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;
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;
break;
case 'n':
conf->nflag = 1;
+ conf->expected_responses--;
break;
case 'p':
conf->port = strtonum(optarg, 1, 65535, &errstr);
}
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;
conf->waittime *= 1000;
break;
case 'x':
- xflag = 1;
+ conf->xflag = 1;
break;
default:
- usage(v6flag);
+ usage();
}
if (ouid == 0 && (setgroups(1, &gid) ||
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);
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] "
}
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);
+
+}
-/* $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 $ */
/*
#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;
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);
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;
}
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;
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)
{
}
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);
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);
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;
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);
}
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;
(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) {
}
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);
}
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);
}
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;
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,
}
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;
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);
}
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];
}
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;
}
/*
}
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);
}
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;
}
}
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:
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
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);
+}