diff options
Diffstat (limited to 'eperd/traceroute.c')
-rw-r--r-- | eperd/traceroute.c | 2866 |
1 files changed, 2866 insertions, 0 deletions
diff --git a/eperd/traceroute.c b/eperd/traceroute.c new file mode 100644 index 0000000..014849b --- /dev/null +++ b/eperd/traceroute.c @@ -0,0 +1,2866 @@ +/* + * Copyright (c) 2013 RIPE NCC <atlas@ripe.net> + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * traceroute.c + */ + +#include "libbb.h" +#include <event2/dns.h> +#include <event2/event.h> +#include <event2/event_struct.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> +#include <netinet/udp.h> + +#include "eperd.h" + +#define SAFE_PREFIX ATLAS_DATA_NEW + +#define DBQ(str) "\"" #str "\"" + +#ifndef STANDALONE_BUSYBOX +#define uh_sport source +#define uh_dport dest +#define uh_ulen len +#define uh_sum check +#endif + +#define TRACEROUTE_OPT_STRING ("!46IUFra:c:f:g:m:w:z:A:O:S:") + +#define OPT_4 (1 << 0) +#define OPT_6 (1 << 1) +#define OPT_I (1 << 2) +#define OPT_U (1 << 3) +#define OPT_F (1 << 4) +#define OPT_r (1 << 5) + +#define BASE_PORT (0x8000 + 666) +#define SRC_BASE_PORT (20480) +#define MAX_DATA_SIZE (4096) + +#define DBQ(str) "\"" #str "\"" + +#define ICMPEXT_VERSION_SHIFT 4 + +#define ICMPEXT_MPLS 1 +#define ICMPEXT_MPLS_IN 1 + +#define MPLS_LABEL_SHIFT 12 +#define MPLS_EXT_SHIFT 9 +#define MPLS_EXT_MASK 0x7 +#define MPLS_S_BIT 0x100 +#define MPLS_TTL_MASK 0xff + +struct trtbase +{ + struct event_base *event_base; + + int v4icmp_rcv; + int v6icmp_rcv; + int v4icmp_snd; + int v6icmp_snd; + int v4udp_snd; + + int my_pid; + + struct event event4; + struct event event6; + + struct trtstate **table; + int tabsiz; + + /* For standalone traceroute. Called when a traceroute instance is + * done. Just one pointer for all instances. It is up to the caller + * to keep it consistent. + */ + void (*done)(void *state); + + u_char packet[MAX_DATA_SIZE]; +}; + +struct trtstate +{ + /* Parameters */ + char *atlas; + char *hostname; + char *out_filename; + char do_icmp; + char do_v6; + char dont_fragment; + char delay_name_res; + char trtcount; + unsigned short maxpacksize; + unsigned char firsthop; + unsigned char maxhops; + unsigned char gaplimit; + unsigned char parismod; + unsigned duptimeout; + unsigned timeout; + + /* Base and index in table */ + struct trtbase *base; + int index; + + struct sockaddr_in6 sin6; + socklen_t socklen; + struct sockaddr_in6 loc_sin6; + socklen_t loc_socklen; + + int sent; + uint8_t hop; + uint16_t paris; + uint16_t seq; + unsigned short curpacksize; + + uint8_t last_response_hop; /* Hop at which we last got something + * back. + */ + unsigned done:1; /* We got something from the target + * host or a destination unreachable. + */ + unsigned not_done:1; /* Not got something else */ + unsigned lastditch:1; /* In last-ditch hop */ + unsigned busy:1; /* Busy, do not start another one */ + unsigned gotresp:1; /* Got a response to the last packet + * we sent. For dup detection. + */ + unsigned dnsip:1; /* Busy with dns name resolution */ + struct evutil_addrinfo *dns_res; + struct evutil_addrinfo *dns_curr; + + time_t starttime; + struct timeval xmit_time; + + struct event timer; + + unsigned long min; + unsigned long max; + unsigned long sum; + int sentpkts; + int rcvdpkts; + int duppkts; + + char *result; + size_t reslen; + size_t resmax; +}; + +static struct trtbase *trt_base; + +struct udp_ph +{ + struct in_addr src; + struct in_addr dst; + uint8_t zero; + uint8_t proto; + uint16_t len; +}; + +struct v6_ph +{ + struct in6_addr src; + struct in6_addr dst; + uint32_t len; + uint8_t zero[3]; + uint8_t nxt; +}; + +struct v6info +{ + uint16_t fuzz; + uint32_t pid; + uint32_t id; + uint32_t seq; + struct timeval tv; +}; + +static int in_cksum(unsigned short *buf, int sz) +{ + int nleft = sz; + int sum = 0; + unsigned short *w = buf; + unsigned short ans = 0; + + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + if (nleft == 1) { + *(unsigned char *) (&ans) = *(unsigned char *) w; + sum += ans; + } + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + ans = ~sum; + return ans; +} + +static int in_cksum_udp(struct udp_ph *udp_ph, struct udphdr *udp, + unsigned short *buf, int sz) +{ + int nleft = sz; + int sum = 0; + unsigned short *w = buf; + unsigned short ans = 0; + + nleft= sizeof(*udp_ph); + w= (unsigned short *)udp_ph; + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + nleft= sizeof(*udp); + w= (unsigned short *)udp; + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + nleft= sz; + w= buf; + + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + if (nleft == 1) { + *(unsigned char *) (&ans) = *(unsigned char *) w; + sum += ans; + } + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + ans = ~sum; + return ans; +} + +static int in_cksum_icmp6(struct v6_ph *v6_ph, unsigned short *buf, int sz) +{ + int nleft = sz; + int sum = 0; + unsigned short *w = buf; + unsigned short ans = 0; + + nleft= sizeof(*v6_ph); + w= (unsigned short *)v6_ph; + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + nleft= sz; + w= buf; + + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + if (nleft == 1) { + *(unsigned char *) (&ans) = *(unsigned char *) w; + sum += ans; + } + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + ans = ~sum; + return ans; +} + +static void add_str(struct trtstate *state, const char *str) +{ + size_t len; + + len= strlen(str); + if (state->reslen + len+1 > state->resmax) + { + state->resmax= state->reslen + len+1 + 80; + state->result= xrealloc(state->result, state->resmax); + } + memcpy(state->result+state->reslen, str, len+1); + state->reslen += len; + //printf("add_str: result = '%s'\n", state->result); +} + +static void report(struct trtstate *state) +{ + FILE *fh; + char namebuf[NI_MAXHOST]; + + event_del(&state->timer); + + if (state->out_filename) + { + fh= fopen(state->out_filename, "a"); + if (!fh) + crondlog(DIE9 "unable to append to '%s'", + state->out_filename); + } + else + fh= stdout; + + fprintf(fh, "RESULT { "); + if (state->atlas) + { + fprintf(fh, DBQ(id) ":" DBQ(%s) + ", " DBQ(fw) ":%d" + ", " DBQ(time) ":%ld" + ", " DBQ(endtime) ":%ld, ", + state->atlas, get_atlas_fw_version(), + state->starttime, + (long)time(NULL)); + } + + fprintf(fh, DBQ(dst_name) ":" DBQ(%s), + state->hostname); + + if (!state->dnsip) + { + getnameinfo((struct sockaddr *)&state->sin6, state->socklen, + namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); + + fprintf(fh, ", " DBQ(dst_addr) ":" DBQ(%s), namebuf); + + namebuf[0]= '\0'; + getnameinfo((struct sockaddr *)&state->loc_sin6, + state->loc_socklen, + namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); + + fprintf(fh, ", " DBQ(src_addr) ":" DBQ(%s), namebuf); + } + + fprintf(fh, ", " DBQ(proto) ":" DBQ(%s) ", " DBQ(af) ": %d", + state->do_icmp ? "ICMP" : "UDP", + state->dnsip ? (state->do_v6 ? 6 : 4) : + (state->sin6.sin6_family == AF_INET6 ? 6 : 4)); + + fprintf(fh, ", \"size\":%d", state->maxpacksize); + if (state->parismod) + { + fprintf(fh, ", \"paris_id\":%d", + state->paris % state->parismod); + } + fprintf(fh, ", \"result\": [ %s ] }\n", state->result); + free(state->result); + state->result= NULL; + state->busy= 0; + + if (state->out_filename) + fclose(fh); + + if (state->base->done) + state->base->done(state); +} + +static void send_pkt(struct trtstate *state) +{ + int r, hop, len, on, sock, serrno; + uint16_t sum, val; + unsigned usum; + struct trtbase *base; + struct icmp *icmp_hdr; + struct icmp6_hdr *icmp6_hdr; + struct v6info *v6info; + struct udp_ph udp_ph; + struct v6_ph v6_ph; + struct udphdr udp; + struct timeval interval; + char line[80]; + char id[]= "http://atlas.ripe.net Atlas says Hi!"; + + state->gotresp= 0; + + base= state->base; + + if (state->sent >= state->trtcount) + { + add_str(state, " } ] }"); + if (state->hop >= state->maxhops || + (state->done && !state->not_done)) + { + /* We are done */ + report(state); + return; + } + + state->hop++; + state->sent= 0; + state->done= 0; + state->not_done= 0; + + if (state->hop - state->last_response_hop > + state->gaplimit) + { +#if 0 + printf("gaplimit reached: %d > %d + %d\n", + state->hop, state->last_response_hop, + state->gaplimit); +#endif + if (state->lastditch) + { + /* Also done with last-ditch probe. */ + report(state); + return; + } + state->lastditch= 1; + state->hop= 255; + } + + snprintf(line, sizeof(line), + ", { \"hop\":%d, \"result\": [ ", state->hop); + add_str(state, line); + } + state->seq++; + + gettimeofday(&state->xmit_time, NULL); + + if (state->sin6.sin6_family == AF_INET6) + { + hop= state->hop; + + if (state->do_icmp) + { + /* Set hop count */ + setsockopt(base->v6icmp_snd, SOL_IPV6, + IPV6_UNICAST_HOPS, &hop, sizeof(hop)); + + /* Set/clear don't fragment */ + on= (state->dont_fragment ? IPV6_PMTUDISC_DO : + IPV6_PMTUDISC_DONT); + setsockopt(base->v6icmp_snd, IPPROTO_IPV6, + IPV6_MTU_DISCOVER, &on, sizeof(on)); + + icmp6_hdr= (struct icmp6_hdr *)base->packet; + icmp6_hdr->icmp6_type= ICMP6_ECHO_REQUEST; + icmp6_hdr->icmp6_code= 0; + icmp6_hdr->icmp6_cksum= 0; + icmp6_hdr->icmp6_id= htons(base->my_pid); + icmp6_hdr->icmp6_seq= htons(state->seq); + + v6info= (struct v6info *)&icmp6_hdr[1]; + v6info->fuzz= 0; + v6info->pid= htonl(base->my_pid); + v6info->id= htonl(state->index); + v6info->seq= htonl(state->seq); + v6info->tv= state->xmit_time; + + len= sizeof(*icmp6_hdr)+sizeof(*v6info); + + if (state->curpacksize < len) + state->curpacksize= len; + if (state->curpacksize > len) + { + memset(&base->packet[len], '\0', + state->curpacksize-len); + strcpy((char *)&base->packet[len], id); + len= state->curpacksize; + } + + if (state->parismod) + { + memset(&v6_ph, '\0', sizeof(v6_ph)); + v6_ph.src= state->loc_sin6.sin6_addr; + v6_ph.dst= state->sin6.sin6_addr; + v6_ph.len= htonl(len); + v6_ph.nxt= IPPROTO_ICMPV6; + + sum= in_cksum_icmp6(&v6_ph, + (unsigned short *)base->packet, len); + + /* Avoid 0 */ + val= state->paris % state->parismod + 1; + + sum= ntohs(sum); + usum= sum + (0xffff - val); + sum= usum + (usum >> 16); + + v6info->fuzz= htons(sum); + + sum= in_cksum_icmp6(&v6_ph, + (unsigned short *)base->packet, len); + +#if 0 + printf( + "send_pkt: seq %d, paris %d, cksum= htons(0x%x)\n", + state->seq, state->paris, + ntohs(sum)); +#endif + } + + r= sendto(base->v6icmp_snd, base->packet, len, 0, + (struct sockaddr *)&state->sin6, + state->socklen); + +#if 0 + { static int doit=1; if (doit && r != -1) + { errno= ENOSYS; r= -1; } doit= !doit; } +#endif + + if (r == -1) + { + if (errno != EMSGSIZE) + { + serrno= errno; + + snprintf(line, sizeof(line), + "%s{ " DBQ(error) ":" DBQ(sendto failed: %s) " } ] }", + state->sent ? " }, " : "", + strerror(serrno)); + add_str(state, line); + report(state); + return; + } + } + } + else + { + sock= socket(AF_INET6, SOCK_DGRAM, 0); + if (sock == -1) + { + crondlog(DIE9 "socket failed"); + } + + on= 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, + sizeof(on)); + + /* Bind to source addr/port */ + r= bind(sock, + (struct sockaddr *)&state->loc_sin6, + state->loc_socklen); + if (r == -1) + { + serrno= errno; + + snprintf(line, sizeof(line), + "%s{ " DBQ(error) ":" DBQ(bind failed: %s) " } ] }", + state->sent ? " }, " : "", + strerror(serrno)); + add_str(state, line); + report(state); + close(sock); + return; + } + + /* Set port */ + if (state->parismod) + { + state->sin6.sin6_port= htons(BASE_PORT + + (state->paris % state->parismod)); + } + else + { + state->sin6.sin6_port= htons(BASE_PORT + + state->seq); + } + + /* Set hop count */ + setsockopt(sock, SOL_IPV6, IPV6_UNICAST_HOPS, + &hop, sizeof(hop)); + + /* Set/clear don't fragment */ + on= (state->dont_fragment ? IPV6_PMTUDISC_DO : + IPV6_PMTUDISC_DONT); + setsockopt(sock, IPPROTO_IPV6, + IPV6_MTU_DISCOVER, &on, sizeof(on)); + + v6info= (struct v6info *)base->packet; + v6info->fuzz= 0; + v6info->pid= htonl(base->my_pid); + v6info->id= htonl(state->index); + v6info->seq= htonl(state->seq); + v6info->tv= state->xmit_time; + +#if 0 + printf( +"send_pkt: IPv6 UDP: pid = htonl(%d), id = htonl(%d), seq = htonl(%d)\n", + ntohl(v6info->pid), + ntohl(v6info->id), + ntohl(v6info->seq)); +#endif + + len= sizeof(*v6info); + + if (state->curpacksize < len) + state->curpacksize= len; + if (state->curpacksize > len) + { + memset(&base->packet[len], '\0', + state->curpacksize-len); + strcpy((char *)&base->packet[len], id); + len= state->curpacksize; + } + + r= sendto(sock, base->packet, len, 0, + (struct sockaddr *)&state->sin6, + state->socklen); + +#if 0 + { static int doit=1; if (doit && r != -1) + { errno= ENOSYS; r= -1; } doit= !doit; } +#endif + serrno= errno; + close(sock); + + if (r == -1) + { + if (serrno != EACCES && + serrno != ECONNREFUSED && + serrno != EMSGSIZE) + { + snprintf(line, sizeof(line), + "%s{ " DBQ(error) ":" DBQ(sendto failed: %s) " } ] }", + state->sent ? " }, " : "", + strerror(serrno)); + add_str(state, line); + report(state); + return; + } + } + } + } + else + { +#if 0 + printf( +"send_pkt: sending IPv4 packet, do_icmp %d, parismod %d, index %d, state %p\n", + state->do_icmp, state->parismod, state->index, state); +#endif + + if (state->do_icmp) + { + hop= state->hop; + + icmp_hdr= (struct icmp *)base->packet; + icmp_hdr->icmp_type= ICMP_ECHO; + icmp_hdr->icmp_code= 0; + icmp_hdr->icmp_cksum= 0; + icmp_hdr->icmp_id= htons(state->index); + icmp_hdr->icmp_seq= htons(state->seq); + icmp_hdr->icmp_data[0]= '\0'; + icmp_hdr->icmp_data[1]= '\0'; + + len= offsetof(struct icmp, icmp_data[2]); + + if (state->curpacksize < len) + state->curpacksize= len; + if (state->curpacksize > len) + { + memset(&base->packet[len], '\0', + state->curpacksize-len); + strcpy((char *)&base->packet[len], id); + len= state->curpacksize; + } + + if (state->parismod) + { + sum= in_cksum((unsigned short *)icmp_hdr, len); + + sum= ntohs(sum); + usum= sum + (0xffff - state->paris); + sum= usum + (usum >> 16); + + icmp_hdr->icmp_data[0]= sum >> 8; + icmp_hdr->icmp_data[1]= sum; + } + + icmp_hdr->icmp_cksum= + in_cksum((unsigned short *)icmp_hdr, len); + +#if 0 + printf( + "send_pkt: seq %d, paris %d, icmp_cksum= htons(%d)\n", + state->seq, state->paris, + ntohs(icmp_hdr->icmp_cksum)); +#endif + + /* Set hop count */ + setsockopt(base->v4icmp_snd, IPPROTO_IP, IP_TTL, + &hop, sizeof(hop)); + + /* Set/clear don't fragment */ + on= (state->dont_fragment ? IP_PMTUDISC_DO : + IP_PMTUDISC_DONT); + setsockopt(base->v4icmp_snd, IPPROTO_IP, + IP_MTU_DISCOVER, &on, sizeof(on)); + + r= sendto(base->v4icmp_snd, base->packet, len, 0, + (struct sockaddr *)&state->sin6, + state->socklen); + +#if 0 + { static int doit=1; if (doit && r != -1) + { errno= ENOSYS; r= -1; } doit= !doit; } +#endif + + if (r == -1) + { + if (errno != EMSGSIZE) + { + serrno= errno; + + snprintf(line, sizeof(line), + "%s{ " DBQ(error) ":" DBQ(sendto failed: %s) " } ] }", + state->sent ? " }, " : "", + strerror(serrno)); + add_str(state, line); + report(state); + return; + } + } + } + else + { + sock= socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + { + crondlog(DIE9 "socket failed"); + } + + on= 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, + sizeof(on)); + + /* Bind to source addr/port */ + r= bind(sock, + (struct sockaddr *)&state->loc_sin6, + state->loc_socklen); +#if 0 + { static int doit=1; if (doit && r != -1) + { errno= ENOSYS; r= -1; } doit= !doit; } +#endif + if (r == -1) + { + serrno= errno; + + snprintf(line, sizeof(line), + "%s{ " DBQ(error) ":" DBQ(bind failed: %s) " } ] }", + state->sent ? " }, " : "", + strerror(serrno)); + add_str(state, line); + report(state); + close(sock); + return; + } + + hop= state->hop; + + /* Set port */ + if (state->parismod) + { + ((struct sockaddr_in *)&state->sin6)->sin_port= + htons(BASE_PORT + + (state->paris % state->parismod)); + } + else + { + ((struct sockaddr_in *)&state->sin6)->sin_port= + htons(BASE_PORT + state->seq); + } + + base->packet[0]= '\0'; + base->packet[1]= '\0'; + len= 2; /* We need to fudge checksum */ + + if (state->curpacksize < len) + state->curpacksize= len; + if (state->curpacksize > len) + { + memset(&base->packet[len], '\0', + state->curpacksize-len); + strcpy((char *)&base->packet[len], id); + len= state->curpacksize; + } + + udp_ph.src= ((struct sockaddr_in *)&state->loc_sin6)-> + sin_addr; + udp_ph.dst= ((struct sockaddr_in *)&state->sin6)-> + sin_addr; + udp_ph.zero= 0; + udp_ph.proto= IPPROTO_UDP; + udp_ph.len= htons(sizeof(udp)+len); + udp.uh_sport= + ((struct sockaddr_in *)&state->loc_sin6)-> + sin_port; + udp.uh_dport= ((struct sockaddr_in *)&state->sin6)-> + sin_port; + udp.uh_ulen= udp_ph.len; + udp.uh_sum= 0; + + sum= in_cksum_udp(&udp_ph, &udp, + (unsigned short *)base->packet, len); + + if (state->parismod) + { + /* Make sure that the sequence number ends + * up in the checksum field. We can't store + * 0. So we add 1. + */ + if (state->seq == 0) + state->seq++; + val= state->seq; + } + else + { + /* Use id+1 */ + val= state->index+1; + } + + sum= ntohs(sum); + usum= sum + (0xffff - val); + sum= usum + (usum >> 16); + + base->packet[0]= sum >> 8; + base->packet[1]= sum; + + sum= in_cksum_udp(&udp_ph, &udp, + (unsigned short *)base->packet, len); + + /* Set hop count */ + setsockopt(sock, IPPROTO_IP, IP_TTL, + &hop, sizeof(hop)); + + /* Set/clear don't fragment */ + on= (state->dont_fragment ? IP_PMTUDISC_DO : + IP_PMTUDISC_DONT); + setsockopt(sock, IPPROTO_IP, + IP_MTU_DISCOVER, &on, sizeof(on)); + + r= sendto(sock, base->packet, len, 0, + (struct sockaddr *)&state->sin6, + state->socklen); + +#if 0 + { static int doit=0; if (doit && r != -1) + { errno= ENOSYS; r= -1; } doit= !doit; } +#endif + + serrno= errno; + close(sock); + if (r == -1) + { + if (serrno != EMSGSIZE) + { + serrno= errno; + + snprintf(line, sizeof(line), + "%s{ " DBQ(error) ":" DBQ(sendto failed: %s) " } ] }", + state->sent ? " }, " : "", + strerror(serrno)); + add_str(state, line); + report(state); + return; + } + } + } + } + + if (state->sent) + add_str(state, " }, "); + add_str(state, "{ "); + + /* Increment packets sent */ + state->sent++; + + /* Set timer */ + interval.tv_sec= state->timeout/1000000; + interval.tv_usec= state->timeout % 1000000; + evtimer_add(&state->timer, &interval); + +} + +static void do_mpls(struct trtstate *state, unsigned char *packet, + size_t size) +{ + int o, exp, s, ttl; + uint32_t v, label; + char line[256]; + + add_str(state, ", " DBQ(mpls) ": ["); + + for (o= 0; o+4 <= size; o += 4) + { + v= (ntohl(*(uint32_t *)&packet[o])); + label= (v >> MPLS_LABEL_SHIFT); + exp= ((v >> MPLS_EXT_SHIFT) & MPLS_EXT_MASK); + s= !!(v & MPLS_S_BIT); + ttl= (v & MPLS_TTL_MASK); + + snprintf(line, sizeof(line), "%s { " DBQ(label) ":%d, " + DBQ(exp) ":%d, " DBQ(s) ":%d, " DBQ(ttl) ":%d }", + o == 0 ? "" : ",", + label, exp, s, ttl); + add_str(state, line); + } + + add_str(state, " ]"); +} + +static void do_icmp_multi(struct trtstate *state, + unsigned char *packet, size_t size, int pre_rfc4884) +{ + int o, len; + uint16_t cksum; + uint8_t class, ctype, version; + char line[256]; + + if (size < 4) + { + printf("do_icmp_multi: not enough for ICMP extension header\n"); + return; + } + cksum= in_cksum((unsigned short *)packet, size); + if (cksum != 0) + { + /* There is also anoption for a zero checksum. */ + if (!pre_rfc4884) + printf("do_icmp_multi: bad checksum\n"); + return; + } + + version= (*(uint8_t *)packet >> ICMPEXT_VERSION_SHIFT); + + snprintf(line, sizeof(line), ", " DBQ(icmpext) ": { " + DBQ(version) ":%d" ", " DBQ(rfc4884) ":%d", + version, !pre_rfc4884); + add_str(state, line); + + add_str(state, ", " DBQ(obj) ": ["); + + o= 4; + while (o+4 < size) + { + len= ntohs(*(uint16_t *)&packet[o]); + class= packet[o+2]; + ctype= packet[o+3]; + + snprintf(line, sizeof(line), "%s { " DBQ(class) ":%d, " + DBQ(type) ":%d", + o == 4 ? "" : ",", class, ctype); + add_str(state, line); + + if (len < 4 || o+len > size) + { + add_str(state, " }"); + printf("do_icmp_multi: bad len %d\n", len); + break; + } + if (class == ICMPEXT_MPLS && ctype == ICMPEXT_MPLS_IN) + do_mpls(state, packet+o+4, len-4); + o += len; + + add_str(state, " }"); + } + + add_str(state, " ] }"); +} + +static void ready_callback4(int __attribute((unused)) unused, + const short __attribute((unused)) event, void *s) +{ + struct trtbase *base; + struct trtstate *state; + int hlen, ehlen, ind, nextmtu, late, isDup, icmp_prefixlen, offset; + unsigned seq; + ssize_t nrecv; + socklen_t slen; + struct ip *ip, *eip; + struct icmp *icmp, *eicmp; + struct udphdr *eudp; + double ms; + struct timeval now, interval; + struct sockaddr_in remote; + char line[80]; + + gettimeofday(&now, NULL); + + base= s; + + slen= sizeof(remote); + nrecv= recvfrom(base->v4icmp_rcv, base->packet, sizeof(base->packet), + MSG_DONTWAIT, (struct sockaddr *)&remote, &slen); + if (nrecv == -1) + { + /* Strange, read error */ + printf("ready_callback4: read error '%s'\n", strerror(errno)); + return; + } + // printf("ready_callback4: got packet\n"); + + ip= (struct ip *)base->packet; + hlen= ip->ip_hl*4; + + if (nrecv < hlen + ICMP_MINLEN || ip->ip_hl < 5) + { + /* Short packet */ + printf("ready_callback4: too short %d\n", (int)nrecv); + return; + } + + icmp= (struct icmp *)(base->packet+hlen); + + if (icmp->icmp_type == ICMP_TIME_EXCEEDED || + icmp->icmp_type == ICMP_DEST_UNREACH) + { + eip= &icmp->icmp_ip; + ehlen= eip->ip_hl*4; + + /* Make sure the packet we have is big enough */ + if (nrecv < hlen + ICMP_MINLEN + ehlen || eip->ip_hl < 5) + { + printf("ready_callback4: too short %d\n", (int)nrecv); + return; + } + + if (eip->ip_p == IPPROTO_UDP) + { + /* Now check if there is also a UDP header in the + * packet + */ + if (nrecv < hlen + ICMP_MINLEN + ehlen + sizeof(*eudp)) + { + printf("ready_callback4: too short %d\n", + (int)nrecv); + return; + } + + eudp= (struct udphdr *)((char *)eip+ehlen); + + /* We store the id in the source port. + */ + ind= ntohs(eudp->uh_sport) - SRC_BASE_PORT; + + state= NULL; + if (ind >= 0 && ind < base->tabsiz) + state= base->table[ind]; + if (state && state->sin6.sin6_family != AF_INET) + state= NULL; + if (state && state->do_icmp) + state= NULL; + + if (!state) + { + /* Nothing here */ + // printf("ready_callback4: no state\n"); + return; + } + +#if 0 + printf("ready_callback4: from %s", + inet_ntoa(remote.sin_addr)); + printf(" for %s hop %d\n", + inet_ntoa(((struct sockaddr_in *) + &state->sin6)->sin_addr), state->hop); +#endif + + if (!state->busy) + { +#if 0 + printf( + "ready_callback4: index (%d) is not busy\n", + ind); +#endif + return; + } + + late= 0; + isDup= 0; + if (state->parismod) + { + /* Sequence number is in checksum field */ + seq= ntohs(eudp->uh_sum); + + /* Unfortunately, cheap home routers may + * forget to restore the checksum field + * when they are doing NAT. Ignore the + * sequence number if it seems wrong. + */ + if (seq > state->seq) + seq= state->seq; + } + else + { + /* Sequence number is in destination field */ + seq= ntohs(eudp->uh_dport)-BASE_PORT; + } + + if (seq != state->seq) + { + if (seq > state->seq) + { +#if 0 + printf( + "ready_callback4: mismatch for seq, got 0x%x, expected 0x%x (for %s)\n", + seq, state->seq, + state->hostname); +#endif + return; + } + late= 1; + + snprintf(line, sizeof(line), "\"late\":%d", + state->seq-seq); + add_str(state, line); + } + else if (state->gotresp) + { + isDup= 1; + add_str(state, " }, { \"dup\":true"); + } + + if (!late && !isDup) + state->last_response_hop= state->hop; + + ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 + + (now.tv_usec-state->xmit_time.tv_usec)/1e3; + + snprintf(line, sizeof(line), "%s\"from\":\"%s\"", + (late || isDup) ? ", " : "", + inet_ntoa(remote.sin_addr)); + add_str(state, line); + snprintf(line, sizeof(line), + ", \"ttl\":%d, \"size\":%d", + ip->ip_ttl, (int)nrecv); + add_str(state, line); + if (!late) + { + snprintf(line, sizeof(line), ", \"rtt\":%.3f", + ms); + add_str(state, line); + } + if (eip->ip_ttl != 1) + { + snprintf(line, sizeof(line), ", \"ittl\":%d", + eip->ip_ttl); + add_str(state, line); + } + + if (memcmp(&eip->ip_src, + &((struct sockaddr_in *)&state->loc_sin6)-> + sin_addr, sizeof(eip->ip_src)) != 0) + { + printf("ready_callback4: changed source %s\n", + inet_ntoa(eip->ip_src)); + } + if (memcmp(&eip->ip_dst, + &((struct sockaddr_in *)&state->sin6)-> + sin_addr, sizeof(eip->ip_dst)) != 0) + { + snprintf(line, sizeof(line), + ", \"edst\":\"%s\"", + inet_ntoa(eip->ip_dst)); + add_str(state, line); + } + if (memcmp(&ip->ip_dst, + &((struct sockaddr_in *)&state->loc_sin6)-> + sin_addr, sizeof(eip->ip_src)) != 0) + { + printf("ready_callback4: weird destination %s\n", + inet_ntoa(ip->ip_dst)); + } + +#if 0 + printf("ready_callback4: from %s, ttl %d", + inet_ntoa(remote.sin_addr), ip->ip_ttl); + printf(" for %s hop %d\n", + inet_ntoa(((struct sockaddr_in *) + &state->sin6)->sin_addr), state->hop); +#endif + + if (icmp->icmp_type == ICMP_TIME_EXCEEDED) + { + if (!late) + state->not_done= 1; + } + else if (icmp->icmp_type == ICMP_DEST_UNREACH) + { + if (!late) + state->done= 1; + switch(icmp->icmp_code) + { + case ICMP_UNREACH_NET: + add_str(state, ", \"err\":\"N\""); + break; + case ICMP_UNREACH_HOST: + add_str(state, ", \"err\":\"H\""); + break; + case ICMP_UNREACH_PROTOCOL: + add_str(state, ", \"err\":\"P\""); + break; + case ICMP_UNREACH_PORT: + break; + case ICMP_UNREACH_NEEDFRAG: + nextmtu= ntohs(icmp->icmp_nextmtu); + snprintf(line, sizeof(line), + ", \"mtu\":%d", + nextmtu); + add_str(state, line); + if (!late && nextmtu >= sizeof(*ip)+ + sizeof(*eudp)) + { + nextmtu -= sizeof(*ip)+ + sizeof(*eudp); + if (nextmtu < + state->curpacksize) + { + state->curpacksize= + nextmtu; + } + } +printf("curpacksize: %d\n", state->curpacksize); + if (!late) + state->not_done= 1; + break; + case ICMP_UNREACH_FILTER_PROHIB: + add_str(state, ", \"err\":\"A\""); + break; + default: + snprintf(line, sizeof(line), + ", \"err\":%d", + icmp->icmp_code); + add_str(state, line); + break; + } + } + } + else if (eip->ip_p == IPPROTO_ICMP) + { + /* Now check if there is also an ICMP header in the + * packet + */ + if (nrecv < hlen + ICMP_MINLEN + ehlen + + offsetof(struct icmp, icmp_data[0])) + { + printf("ready_callback4: too short %d\n", + (int)nrecv); + return; + } + + eicmp= (struct icmp *)((char *)eip+ehlen); + + if (eicmp->icmp_type != ICMP_ECHO || + eicmp->icmp_code != 0) + { + printf("ready_callback4: not ECHO\n"); + return; + } + + ind= ntohs(eicmp->icmp_id); + + if (ind >= base->tabsiz) + { + /* Out of range */ +#if 0 + printf( + "ready_callback4: index out of range (%d)\n", + ind); +#endif + return; + } + + state= base->table[ind]; + if (!state) + { + /* Nothing here */ + printf( + "ready_callback4: nothing at index (%d)\n", + ind); + return; + } + + if (state->sin6.sin6_family != AF_INET) + { + // printf("ready_callback4: bad family\n"); + return; + } + + if (!state->do_icmp) + { + printf( + "ready_callback4: index (%d) is not doing ICMP\n", + ind); + return; + } + if (!state->busy) + { +printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family); + printf( + "ready_callback4: index (%d) is not busy\n", + ind); + return; + } + + if (state->parismod && + ntohs(eicmp->icmp_cksum) != state->paris) + { + printf( + "ready_callback4: mismatch for paris, got 0x%x, expected 0x%x (%s)\n", + ntohs(eicmp->icmp_cksum), state->paris, + state->hostname); + } + + late= 0; + isDup= 0; + seq= ntohs(eicmp->icmp_seq); + if (seq != state->seq) + { + if (seq > state->seq) + { +#if 0 + printf( + "ready_callback4: mismatch for seq, got 0x%x, expected 0x%x (for %s)\n", + seq, state->seq, + state->hostname); +#endif + return; + } + late= 1; + + snprintf(line, sizeof(line), "\"late\":%d", + state->seq-seq); + add_str(state, line); + } + else if (state->gotresp) + { + isDup= 1; + add_str(state, " }, { \"dup\":true"); + } + + if (!late && !isDup) + state->last_response_hop= state->hop; + + ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 + + (now.tv_usec-state->xmit_time.tv_usec)/1e3; + + snprintf(line, sizeof(line), "%s\"from\":\"%s\"", + (late || isDup) ? ", " : "", + inet_ntoa(remote.sin_addr)); + add_str(state, line); + snprintf(line, sizeof(line), + ", \"ttl\":%d, \"size\":%d", + ip->ip_ttl, (int)nrecv); + add_str(state, line); + if (!late) + { + snprintf(line, sizeof(line), ", \"rtt\":%.3f", + ms); + add_str(state, line); + } + + if (eip->ip_ttl != 1) + { + snprintf(line, sizeof(line), ", \"ittl\":%d", + eip->ip_ttl); + add_str(state, line); + } + + if (memcmp(&eip->ip_src, + &((struct sockaddr_in *)&state->loc_sin6)-> + sin_addr, sizeof(eip->ip_src)) != 0) + { + printf("ready_callback4: changed source %s\n", + inet_ntoa(eip->ip_src)); + } + if (memcmp(&eip->ip_dst, + &((struct sockaddr_in *)&state->sin6)-> + sin_addr, sizeof(eip->ip_dst)) != 0) + { + snprintf(line, sizeof(line), + ", \"edst\":\"%s\"", + inet_ntoa(eip->ip_dst)); + add_str(state, line); + } + if (memcmp(&ip->ip_dst, + &((struct sockaddr_in *)&state->loc_sin6)-> + sin_addr, sizeof(eip->ip_src)) != 0) + { + printf("ready_callback4: weird destination %s\n", + inet_ntoa(ip->ip_dst)); + } + +#if 0 + printf("ready_callback4: from %s, ttl %d", + inet_ntoa(remote.sin_addr), ip->ip_ttl); + printf(" for %s hop %d\n", + inet_ntoa(((struct sockaddr_in *) + &state->sin6)->sin_addr), state->hop); +#endif + + if (icmp->icmp_type == ICMP_TIME_EXCEEDED) + { + if (!late && !isDup) + state->not_done= 1; + } + else if (icmp->icmp_type == ICMP_DEST_UNREACH) + { + if (!late) + state->done= 1; + switch(icmp->icmp_code) + { + case ICMP_UNREACH_NET: + add_str(state, ", \"err\":\"N\""); + break; + case ICMP_UNREACH_HOST: + add_str(state, ", \"err\":\"H\""); + break; + case ICMP_UNREACH_PROTOCOL: + add_str(state, ", \"err\":\"P\""); + break; + case ICMP_UNREACH_PORT: + add_str(state, ", \"err\":\"p\""); + break; + case ICMP_UNREACH_NEEDFRAG: + nextmtu= ntohs(icmp->icmp_nextmtu); + snprintf(line, sizeof(line), + ", \"mtu\":%d", + nextmtu); + add_str(state, line); + if (!late && nextmtu >= sizeof(*ip)) + { + nextmtu -= sizeof(*ip); + if (nextmtu < + state->curpacksize) + { + state->curpacksize= + nextmtu; + } + } + if (!late) + state->not_done= 1; + break; + case ICMP_UNREACH_FILTER_PROHIB: + add_str(state, ", \"err\":\"A\""); + break; + default: + snprintf(line, sizeof(line), + ", \"err\":%d", + icmp->icmp_code); + add_str(state, line); + break; + } + } + else + { + printf("imcp type %d\n", icmp->icmp_type); + } + } + else + { + printf("ready_callback4: not UDP or ICMP (%d\n", + eip->ip_p); + return; + } + + /* RFC-4884, Multi-Part ICMP messages */ + icmp_prefixlen= (ntohs(icmp->icmp_pmvoid) & 0xff) * 4; + if (icmp_prefixlen != 0) + { + + printf("icmp_pmvoid: 0x%x for %s\n", icmp->icmp_pmvoid, state->hostname); + printf("icmp_prefixlen: 0x%x for %s\n", icmp_prefixlen, inet_ntoa(remote.sin_addr)); + offset= hlen + ICMP_MINLEN + icmp_prefixlen; + if (nrecv > offset) + { + do_icmp_multi(state, base->packet+offset, + nrecv-offset, 0 /*!pre_rfc4884*/); + } + else + { + printf( + "ready_callback4: too short %d (Multi-Part ICMP)\n", + (int)nrecv); + } + } + else if (nrecv > hlen + ICMP_MINLEN + 128) + { + /* Try old style extensions */ + icmp_prefixlen= 128; + offset= hlen + ICMP_MINLEN + icmp_prefixlen; + if (nrecv > offset) + { + do_icmp_multi(state, base->packet+offset, + nrecv-offset, 1 /*pre_rfc4884*/); + } + else + { + printf( + "ready_callback4: too short %d (Multi-Part ICMP)\n", + (int)nrecv); + } + } + + if (late) + add_str(state, " }, { "); + + if (!late && !isDup) + { + if (state->duptimeout) + { + state->gotresp= 1; + interval.tv_sec= state->duptimeout/1000000; + interval.tv_usec= state->duptimeout % 1000000; + evtimer_add(&state->timer, &interval); + } + else + send_pkt(state); + } + } + else if (icmp->icmp_type == ICMP_ECHOREPLY) + { + if (icmp->icmp_code != 0) + { + printf("ready_callback4: not proper ECHO REPLY\n"); + return; + } + + ind= ntohs(icmp->icmp_id); + + if (ind >= base->tabsiz) + { + /* Out of range */ +#if 0 + printf( + "ready_callback4: index out of range (%d)\n", + ind); +#endif + return; + } + + state= base->table[ind]; + if (!state) + { + /* Nothing here */ + printf( + "ready_callback4: nothing at index (%d)\n", + ind); + return; + } + + if (state->sin6.sin6_family != AF_INET) + { + // printf("ready_callback4: bad family\n"); + return; + } + + if (!state->busy) + { +printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family); + printf( + "ready_callback4: index (%d) is not busy\n", + ind); + return; + } + + late= 0; + isDup= 0; + seq= ntohs(icmp->icmp_seq); + if (seq != state->seq) + { + if (seq > state->seq) + { +#if 0 + printf( +"ready_callback4: mismatch for seq, got 0x%x, expected 0x%x, for %s\n", + seq, state->seq, state->hostname); +#endif + return; + } + late= 1; + + snprintf(line, sizeof(line), "\"late\":%d", + state->seq-seq); + add_str(state, line); + } + else if (state->gotresp) + { + isDup= 1; + add_str(state, " }, { \"dup\":true"); + } + + if (memcmp(&ip->ip_dst, + &((struct sockaddr_in *)&state->loc_sin6)-> + sin_addr, sizeof(eip->ip_src)) != 0) + { + printf("ready_callback4: weird destination %s\n", + inet_ntoa(ip->ip_dst)); + } + + ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 + + (now.tv_usec-state->xmit_time.tv_usec)/1e3; + + snprintf(line, sizeof(line), "%s\"from\":\"%s\"", + (late || isDup) ? ", " : "", + inet_ntoa(remote.sin_addr)); + add_str(state, line); + snprintf(line, sizeof(line), ", \"ttl\":%d, \"size\":%d", + ip->ip_ttl, (int)nrecv); + add_str(state, line); + if (!late) + { + snprintf(line, sizeof(line), ", \"rtt\":%.3f", ms); + add_str(state, line); + } + +#if 0 + printf("ready_callback4: from %s, ttl %d", + inet_ntoa(remote.sin_addr), ip->ip_ttl); + printf(" for %s hop %d\n", + inet_ntoa(((struct sockaddr_in *) + &state->sin6)->sin_addr), state->hop); +#endif + + /* Done */ + state->done= 1; + + if (late) + add_str(state, " }, { "); + + if (!late && !isDup) + { + if (state->duptimeout) + { + state->gotresp= 1; + interval.tv_sec= state->duptimeout/1000000; + interval.tv_usec= state->duptimeout % 1000000; + evtimer_add(&state->timer, &interval); + } + else + send_pkt(state); + } + + return; + } + else if (icmp->icmp_type == ICMP_ECHO || + icmp->icmp_type == ICMP_ROUTERADVERT) + { + /* No need to do anything */ + } + else + { + printf("ready_callback4: got type %d\n", icmp->icmp_type); + return; + } +} + +static void ready_callback6(int __attribute((unused)) unused, + const short __attribute((unused)) event, void *s) +{ + ssize_t nrecv; + int ind, rcvdttl, late, isDup, nxt, icmp_prefixlen, offset; + unsigned nextmtu, seq; + size_t ehdrsiz, siz; + struct trtbase *base; + struct trtstate *state; + struct ip6_hdr *eip; + struct ip6_frag *frag; + struct icmp6_hdr *icmp, *eicmp; + struct udphdr *eudp; + struct v6info *v6info; + struct cmsghdr *cmsgptr; + void *ptr; + double ms; + struct timeval now; + struct sockaddr_in6 remote; + struct in6_addr dstaddr; + struct msghdr msg; + struct iovec iov[1]; + struct timeval interval; + char buf[INET6_ADDRSTRLEN]; + char line[80]; + char cmsgbuf[256]; + + gettimeofday(&now, NULL); + + base= s; + + iov[0].iov_base= base->packet; + iov[0].iov_len= sizeof(base->packet); + msg.msg_name= &remote; + msg.msg_namelen= sizeof(remote); + msg.msg_iov= iov; + msg.msg_iovlen= 1; + msg.msg_control= cmsgbuf; + msg.msg_controllen= sizeof(cmsgbuf); + msg.msg_flags= 0; /* Not really needed */ + + nrecv= recvmsg(base->v6icmp_rcv, &msg, MSG_DONTWAIT); + if (nrecv == -1) + { + /* Strange, read error */ + printf("ready_callback6: read error '%s'\n", strerror(errno)); + return; + } + + rcvdttl= -42; /* To spot problems */ + memset(&dstaddr, '\0', sizeof(dstaddr)); + for (cmsgptr= CMSG_FIRSTHDR(&msg); cmsgptr; + cmsgptr= CMSG_NXTHDR(&msg, cmsgptr)) + { + if (cmsgptr->cmsg_len == 0) + break; /* Can this happen? */ + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && + cmsgptr->cmsg_type == IPV6_HOPLIMIT) + { + rcvdttl= *(int *)CMSG_DATA(cmsgptr); + } + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && + cmsgptr->cmsg_type == IPV6_PKTINFO) + { + dstaddr= ((struct in6_pktinfo *) + CMSG_DATA(cmsgptr))->ipi6_addr; + } + } + + if (nrecv < sizeof(*icmp)) + { + /* Short packet */ +#if 0 + printf("ready_callback6: too short %d (icmp)\n", (int)nrecv); +#endif + return; + } + + icmp= (struct icmp6_hdr *)&base->packet; + + if (icmp->icmp6_type == ICMP6_DST_UNREACH || + icmp->icmp6_type == ICMP6_PACKET_TOO_BIG || + icmp->icmp6_type == ICMP6_TIME_EXCEEDED) + { + eip= (struct ip6_hdr *)&icmp[1]; + + /* Make sure the packet we have is big enough */ + if (nrecv < sizeof(*icmp) + sizeof(*eip)) + { +#if 0 + printf("ready_callback6: too short %d (icmp_ip)\n", + (int)nrecv); +#endif + return; + } + + /* Make sure we have UDP or ICMP or a fragment header */ + if (eip->ip6_nxt == IPPROTO_FRAGMENT || + eip->ip6_nxt == IPPROTO_UDP || + eip->ip6_nxt == IPPROTO_ICMPV6) + { + ehdrsiz= 0; + frag= NULL; + nxt= eip->ip6_nxt; + if (nxt == IPPROTO_FRAGMENT) + { + /* Make sure the fragment header is completely + * there. + */ + if (nrecv < sizeof(*icmp) + sizeof(*eip) + + sizeof(*frag)) + { +#if 0 + printf( + "ready_callback6: too short %d (icmp+ip+frag)\n", + (int)nrecv); +#endif + return; + } + frag= (struct ip6_frag *)&eip[1]; + if ((ntohs(frag->ip6f_offlg) & ~3) != 0) + { + /* Not first fragment, just ignore + * it. + */ + return; + } + ehdrsiz= sizeof(*frag); + nxt= frag->ip6f_nxt; + } + + if (nxt == IPPROTO_UDP) + ehdrsiz += sizeof(*eudp); + else + ehdrsiz += sizeof(*eicmp); + + /* Now check if there is also a header in the + * packet. + */ + if (nrecv < sizeof(*icmp) + sizeof(*eip) + + ehdrsiz + sizeof(*v6info)) + { +#if 0 + printf( + "ready_callback6: too short %d (all) from %s\n", + (int)nrecv, inet_ntop(AF_INET6, + &remote.sin6_addr, buf, sizeof(buf))); +#endif + return; + } + + eudp= NULL; + eicmp= NULL; + ptr= (frag ? (void *)&frag[1] : (void *)&eip[1]); + if (nxt == IPPROTO_UDP) + { + eudp= (struct udphdr *)ptr; + v6info= (struct v6info *)&eudp[1]; + } + else + { + eicmp= (struct icmp6_hdr *)ptr; + v6info= (struct v6info *)&eicmp[1]; + } + +#if 0 + printf( +"ready_callback6: pid = htonl(%d), id = htonl(%d), seq = htonl(%d)\n", + ntohl(v6info->pid), + ntohl(v6info->id), + ntohl(v6info->seq)); +#endif + + if (ntohl(v6info->pid) != base->my_pid) + { + /* From a different process */ + return; + } + + ind= ntohl(v6info->id); + + state= NULL; + if (ind >= 0 && ind < base->tabsiz) + state= base->table[ind]; + + if (state && state->sin6.sin6_family != AF_INET6) + state= NULL; + + if (state) + { + if ((eudp && state->do_icmp) || + (eicmp && !state->do_icmp)) + { + state= NULL; + } + } + + if (!state) + { + /* Nothing here */ + return; + } + +#if 0 + printf("ready_callback6: from %s", + inet_ntop(AF_INET6, &remote.sin6_addr, + buf, sizeof(buf))); + printf(" for %s hop %d\n", + inet_ntop(AF_INET6, &state->sin6.sin6_addr, + buf, sizeof(buf)), state->hop); +#endif + + if (!state->busy) + { +printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family); + printf( + "ready_callback6: index (%d) is not busy\n", + ind); + return; + } + + late= 0; + isDup= 0; + seq= ntohl(v6info->seq); + if (seq != state->seq) + { + if (seq > state->seq) + { + printf( + "ready_callback6: mismatch for seq, got 0x%x, expected 0x%x\n", + ntohl(v6info->seq), + state->seq); + return; + } + late= 1; + + snprintf(line, sizeof(line), "\"late\":%d", + state->seq-seq); + add_str(state, line); + } else if (state->gotresp) + { + isDup= 1; + add_str(state, " }, { \"dup\":true"); + } + + if (!late && !isDup) + state->last_response_hop= state->hop; + + if (memcmp(&eip->ip6_src, + &state->loc_sin6.sin6_addr, + sizeof(eip->ip6_src)) != 0) + { + printf("ready_callback6: changed source %s\n", + inet_ntop(AF_INET6, &eip->ip6_src, + buf, sizeof(buf))); + } + if (memcmp(&eip->ip6_dst, + &state->sin6.sin6_addr, + sizeof(eip->ip6_dst)) != 0) + { + printf( + "ready_callback6: changed destination %s for %s\n", + inet_ntop(AF_INET6, &eip->ip6_dst, + buf, sizeof(buf)), + state->hostname); + } + if (memcmp(&dstaddr, + &state->loc_sin6.sin6_addr, + sizeof(dstaddr)) != 0) + { + printf("ready_callback6: weird destination %s\n", + inet_ntop(AF_INET6, &dstaddr, + buf, sizeof(buf))); + } + + if (eicmp && state->parismod && + ntohs(eicmp->icmp6_cksum) != + state->paris % state->parismod + 1) + { + printf( + "ready_callback6: got checksum 0x%x, expected 0x%x\n", + ntohs(eicmp->icmp6_cksum), + state->paris % state->parismod + 1); + } + + if (!late) + { + ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 + + (now.tv_usec-state->xmit_time.tv_usec)/ + 1e3; + } + else + { + ms= (now.tv_sec-v6info->tv.tv_sec)*1000 + + (now.tv_usec-v6info->tv.tv_usec)/ + 1e3; + } + + snprintf(line, sizeof(line), "%s\"from\":\"%s\"", + (late || isDup) ? ", " : "", + inet_ntop(AF_INET6, &remote.sin6_addr, + buf, sizeof(buf))); + add_str(state, line); + snprintf(line, sizeof(line), + ", \"ttl\":%d, \"rtt\":%.3f, \"size\":%d", + rcvdttl, ms, (int)nrecv); + add_str(state, line); + if (eip->ip6_hops != 1) + { + snprintf(line, sizeof(line), ", \"ittl\":%d", + eip->ip6_hops); + add_str(state, line); + } + +#if 0 + printf("ready_callback6: from %s, ttl %d", + inet_ntop(AF_INET6, &remote.sin6_addr, buf, + sizeof(buf)), rcvdttl); + printf(" for %s hop %d\n", + inet_ntop(AF_INET6, &state->sin6.sin6_addr, buf, + sizeof(buf)), state->hop); +#endif + + if (icmp->icmp6_type == ICMP6_TIME_EXCEEDED) + { + if (!late && !isDup) + state->not_done= 1; + } + else if (icmp->icmp6_type == ICMP6_PACKET_TOO_BIG) + { + nextmtu= ntohl(icmp->icmp6_mtu); + snprintf(line, sizeof(line), ", \"mtu\":%d", + nextmtu); + add_str(state, line); + siz= sizeof(*eip); + if (eudp) + siz += sizeof(*eudp); + if (!late && nextmtu >= siz) + { + nextmtu -= siz; + if (nextmtu < state->curpacksize) + state->curpacksize= nextmtu; + } + if (!late) + state->not_done= 1; + } + else if (icmp->icmp6_type == ICMP6_DST_UNREACH) + { + if (!late) + state->done= 1; + switch(icmp->icmp6_code) + { + case ICMP6_DST_UNREACH_NOROUTE: + add_str(state, ", \"err\":\"N\""); + break; + case ICMP6_DST_UNREACH_ADDR: + add_str(state, ", \"err\":\"H\""); + break; + case ICMP6_DST_UNREACH_NOPORT: + break; + case ICMP6_DST_UNREACH_ADMIN: + add_str(state, ", \"err\":\"A\""); + break; + default: + snprintf(line, sizeof(line), + ", \"err\":%d", + icmp->icmp6_code); + add_str(state, line); + break; + } + } + } + else + { + printf( + "ready_callback6: not UDP or ICMP (ip6_nxt = %d)\n", + eip->ip6_nxt); + return; + } + + /* RFC-4884, Multi-Part ICMP messages */ + icmp_prefixlen= icmp->icmp6_data8[0] * 8; + if (icmp_prefixlen != 0) + { + + printf("icmp6_data8[0]: 0x%x for %s\n", icmp->icmp6_data8[0], state->hostname); + printf("icmp_prefixlen: 0x%x for %s\n", icmp_prefixlen, inet_ntop(AF_INET6, &state->sin6.sin6_addr, buf, sizeof(buf))); + offset= sizeof(*icmp) + icmp_prefixlen; + if (nrecv > offset) + { + do_icmp_multi(state, base->packet+offset, + nrecv-offset, 0 /*!pre_rfc4884*/); + } + else + { +#if 0 + printf( + "ready_callback6: too short %d (Multi-Part ICMP)\n", + (int)nrecv); +#endif + } + } + else if (nrecv > 128) + { + /* Try old style extensions */ + icmp_prefixlen= 128; + offset= sizeof(*icmp) + icmp_prefixlen; + if (nrecv > offset) + { + do_icmp_multi(state, base->packet+offset, + nrecv-offset, 1 /*pre_rfc4884*/); + } + else + { + printf( + "ready_callback6: too short %d (Multi-Part ICMP)\n", + (int)nrecv); + } + } + + if (late) + add_str(state, " }, { "); + + if (!late && !isDup) + { + if (state->duptimeout) + { + state->gotresp= 1; + interval.tv_sec= state->duptimeout/1000000; + interval.tv_usec= state->duptimeout % 1000000; + evtimer_add(&state->timer, &interval); + } + else + send_pkt(state); + } + } + else if (icmp->icmp6_type == ICMP6_ECHO_REPLY) + { + eip= NULL; + + /* Now check if there is also a header in the packet */ + if (nrecv < sizeof(*icmp) + sizeof(*v6info)) + { +#if 0 + printf("ready_callback6: too short %d (echo reply)\n", + (int)nrecv); +#endif + return; + } + + eudp= NULL; + eicmp= NULL; + + v6info= (struct v6info *)&icmp[1]; + + if (ntohl(v6info->pid) != base->my_pid) + { + /* From a different process */ + return; + } + + ind= ntohl(v6info->id); + + state= NULL; + if (ind >= 0 && ind < base->tabsiz) + state= base->table[ind]; + + if (state && state->sin6.sin6_family != AF_INET6) + state= NULL; + + if (state && !state->do_icmp) + { + state= NULL; + } + + if (!state) + { + /* Nothing here */ + return; + } + +#if 0 + printf("ready_callback6: from %s", + inet_ntop(AF_INET6, &remote.sin6_addr, + buf, sizeof(buf))); + printf(" for %s hop %d\n", + inet_ntop(AF_INET6, &state->sin6.sin6_addr, + buf, sizeof(buf)), state->hop); +#endif + + if (!state->busy) + { +printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family); + printf( + "ready_callback6: index (%d) is not busy\n", + ind); + return; + } + + late= 0; + isDup= 0; + seq= ntohl(v6info->seq); + if (seq != state->seq) + { + if (seq > state->seq) + { + printf( +"ready_callback6: mismatch for seq, got 0x%x, expected 0x%x\n", + ntohl(v6info->seq), + state->seq); + return; + } + late= 1; + + snprintf(line, sizeof(line), "\"late\":%d", + state->seq-seq); + add_str(state, line); + } + else if (state->gotresp) + { + isDup= 1; + add_str(state, " }, { \"dup\":true"); + } + + if (!late && !isDup) + { + state->last_response_hop= state->hop; + state->done= 1; + } + + if (memcmp(&dstaddr, &state->loc_sin6.sin6_addr, + sizeof(dstaddr)) != 0) + { + printf("ready_callback6: weird destination %s\n", + inet_ntop(AF_INET6, &dstaddr, + buf, sizeof(buf))); + } + + if (!late) + { + ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 + + (now.tv_usec-state->xmit_time.tv_usec)/ + 1e3; + } + else + { + ms= (now.tv_sec-v6info->tv.tv_sec)*1000 + + (now.tv_usec-v6info->tv.tv_usec)/ + 1e3; + } + + snprintf(line, sizeof(line), "%s\"from\":\"%s\"", + (late || isDup) ? ", " : "", + inet_ntop(AF_INET6, &remote.sin6_addr, + buf, sizeof(buf))); + add_str(state, line); + snprintf(line, sizeof(line), + ", \"ttl\":%d, \"rtt\":%.3f, \"size\":%d", + rcvdttl, ms, (int)nrecv); + add_str(state, line); + +#if 0 + printf("ready_callback6: from %s, ttl %d", + inet_ntop(AF_INET6, &remote.sin6_addr, buf, + sizeof(buf)), rcvdttl); + printf(" for %s hop %d\n", + inet_ntop(AF_INET6, &state->sin6.sin6_addr, buf, + sizeof(buf)), state->hop); +#endif + + if (late) + add_str(state, " }, { "); + + if (!late && !isDup) + { + if (state->duptimeout) + { + state->gotresp= 1; + interval.tv_sec= state->duptimeout/1000000; + interval.tv_usec= state->duptimeout % 1000000; + evtimer_add(&state->timer, &interval); + } + else + send_pkt(state); + } + } + else if (icmp->icmp6_type == ICMP6_ECHO_REQUEST /* 128 */ || + icmp->icmp6_type == MLD_LISTENER_QUERY /* 130 */ || + icmp->icmp6_type == MLD_LISTENER_REPORT /* 131 */ || + icmp->icmp6_type == ND_ROUTER_ADVERT /* 134 */ || + icmp->icmp6_type == ND_NEIGHBOR_SOLICIT /* 135 */ || + icmp->icmp6_type == ND_NEIGHBOR_ADVERT /* 136 */ || + icmp->icmp6_type == ND_REDIRECT /* 137 */) + { + /* No need to do anything */ + } + else + { + printf("ready_callback6: got type %d\n", icmp->icmp6_type); + return; + } +} + +static struct trtbase *traceroute_base_new(struct event_base + *event_base) +{ + int on; + struct trtbase *base; + + base= xzalloc(sizeof(*base)); + + base->event_base= event_base; + + base->tabsiz= 10; + base->table= xzalloc(base->tabsiz * sizeof(*base->table)); + + base->v4icmp_rcv= xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + base->v6icmp_rcv= xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + base->v4icmp_snd= xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + base->v6icmp_snd= xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + base->v4udp_snd= xsocket(AF_INET, SOCK_DGRAM, 0); + + base->my_pid= getpid(); + + on = 1; + setsockopt(base->v6icmp_rcv, IPPROTO_IPV6, IPV6_RECVPKTINFO, + &on, sizeof(on)); + + on = 1; + setsockopt(base->v6icmp_rcv, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, + &on, sizeof(on)); + + event_assign(&base->event4, base->event_base, base->v4icmp_rcv, + EV_READ | EV_PERSIST, ready_callback4, base); + event_assign(&base->event6, base->event_base, base->v6icmp_rcv, + EV_READ | EV_PERSIST, ready_callback6, base); + event_add(&base->event4, NULL); + event_add(&base->event6, NULL); + + return base; +} + +static void noreply_callback(int __attribute((unused)) unused, + const short __attribute((unused)) event, void *s) +{ + struct trtstate *state; + + state= s; + +#if 0 + printf("noreply_callback: gotresp = %d\n", + state->gotresp); +#endif + + if (!state->gotresp) + add_str(state, "\"x\":\"*\""); + + send_pkt(state); +} + +static void *traceroute_init(int __attribute((unused)) argc, char *argv[], + void (*done)(void *state)) +{ + uint32_t opt; + int i, do_icmp, do_v6, dont_fragment, delay_name_res; + unsigned count, duptimeout, firsthop, gaplimit, maxhops, maxpacksize, + parismod, timeout; /* must be int-sized */ + size_t newsiz; + char *str_Atlas; + const char *hostname; + char *out_filename; + struct trtstate *state; + sa_family_t af; + len_and_sockaddr *lsa; + FILE *fh; + + if (!trt_base) + { + trt_base= traceroute_base_new(EventBase); + if (!trt_base) + crondlog(DIE9 "traceroute_base_new failed"); + } + + /* Parse arguments */ + count= 3; + firsthop= 1; + gaplimit= 5; + maxhops= 32; + maxpacksize= 40; + duptimeout= 10; + timeout= 1000; + parismod= 16; + str_Atlas= NULL; + out_filename= NULL; + opt_complementary = "=1:4--6:i--u:a+:c+:f+:g+:m+:w+:z+:S+"; + opt = getopt32(argv, TRACEROUTE_OPT_STRING, &parismod, &count, + &firsthop, &gaplimit, &maxhops, &timeout, &duptimeout, + &str_Atlas, &out_filename, &maxpacksize); + hostname = argv[optind]; + + if (opt == 0xffffffff) + { + crondlog(LVL8 "bad options"); + return NULL; + } + + do_icmp= !!(opt & OPT_I); + do_v6= !!(opt & OPT_6); + dont_fragment= !!(opt & OPT_F); + delay_name_res= !!(opt & OPT_r); + if (maxpacksize > sizeof(trt_base->packet)) + maxpacksize= sizeof(trt_base->packet); + + if (out_filename) + { + if (!validate_filename(out_filename, SAFE_PREFIX)) + { + crondlog(LVL8 "insecure file '%s'", out_filename); + return NULL; + } + fh= fopen(out_filename, "a"); + if (!fh) + { + crondlog(LVL8 "unable to append to '%s'", + out_filename); + return NULL; + } + fclose(fh); + } + + if (!delay_name_res) + { + /* Attempt to resolve 'name' */ + af= do_v6 ? AF_INET6 : AF_INET; + lsa= host_and_af2sockaddr(hostname, 0, af); + if (!lsa) + return NULL; + + if (lsa->len > sizeof(state->sin6)) + { + free(lsa); + return NULL; + } + } + else + { + /* lint */ + lsa= NULL; + af= -1; + } + + state= xzalloc(sizeof(*state)); + state->parismod= parismod; + state->trtcount= count; + state->firsthop= firsthop; + state->maxpacksize= maxpacksize; + state->maxhops= maxhops; + state->gaplimit= gaplimit; + state->duptimeout= duptimeout*1000; + state->timeout= timeout*1000; + state->atlas= str_Atlas ? strdup(str_Atlas) : NULL; + state->hostname= strdup(hostname); + state->do_icmp= do_icmp; + state->do_v6= do_v6; + state->dont_fragment= dont_fragment; + state->delay_name_res= delay_name_res; + state->out_filename= out_filename ? strdup(out_filename) : NULL; + state->base= trt_base; + state->busy= 0; + state->result= NULL; + state->reslen= 0; + state->resmax= 0; + + for (i= 0; i<trt_base->tabsiz; i++) + { + if (trt_base->table[i] == NULL) + break; + } + if (i >= trt_base->tabsiz) + { + newsiz= 2*trt_base->tabsiz; + trt_base->table= xrealloc(trt_base->table, + newsiz*sizeof(*trt_base->table)); + for (i= trt_base->tabsiz; i<newsiz; i++) + trt_base->table[i]= NULL; + i= trt_base->tabsiz; + trt_base->tabsiz= newsiz; + } + state->index= i; + trt_base->table[i]= state; + trt_base->done= done; + + printf("traceroute_init: state %p, index %d\n", + state, state->index); + + memset(&state->loc_sin6, '\0', sizeof(state->loc_sin6)); + state->loc_socklen= 0; + + if (!delay_name_res) + { + memcpy(&state->sin6, &lsa->u.sa, lsa->len); + state->socklen= lsa->len; + free(lsa); lsa= NULL; + if (af == AF_INET6) + { + char buf[INET6_ADDRSTRLEN]; + printf("traceroute_init: %s, len %d for %s\n", + inet_ntop(AF_INET6, &state->sin6.sin6_addr, + buf, sizeof(buf)), state->socklen, + state->hostname); + } + } + + evtimer_assign(&state->timer, state->base->event_base, + noreply_callback, state); + + return state; +} + +static void traceroute_start2(void *state) +{ + int r, serrno; + struct trtstate *trtstate; + struct trtbase *trtbase; + struct sockaddr_in loc_sa4; + struct sockaddr_in6 loc_sa6; + char line[80]; + + trtstate= state; + trtbase= trtstate->base; + + if (trtstate->busy) + { + printf("traceroute_start: busy, can't start\n"); + return; + } + trtstate->busy= 1; + + trtstate->min= ULONG_MAX; + trtstate->max= 0; + trtstate->sum= 0; + trtstate->sentpkts= 0; + trtstate->rcvdpkts= 0; + trtstate->duppkts= 0; + + trtstate->hop= trtstate->firsthop; + trtstate->sent= 0; + trtstate->seq= 0; + trtstate->paris++; + trtstate->last_response_hop= 0; /* Should be starting hop */ + trtstate->done= 0; + trtstate->not_done= 0; + trtstate->lastditch= 0; + trtstate->curpacksize= trtstate->maxpacksize; + + if (trtstate->result) free(trtstate->result); + trtstate->resmax= 80; + trtstate->result= xmalloc(trtstate->resmax); + trtstate->reslen= 0; + trtstate->starttime= time(NULL); + + snprintf(line, sizeof(line), "{ \"hop\":%d", trtstate->hop); + add_str(trtstate, line); + + if (trtstate->do_icmp) + { + if (trtstate->do_v6) + { + memset(&loc_sa6, '\0', sizeof(loc_sa6)); + loc_sa6.sin6_family= AF_INET; + + r= connect(trtbase->v6icmp_snd, + (struct sockaddr *)&trtstate->sin6, + trtstate->socklen); +#if 0 + { errno= ENOSYS; r= -1; } +#endif + if (r == -1) + { + serrno= errno; + + snprintf(line, sizeof(line), + ", " DBQ(error) ":" DBQ(connect failed: %s) " }", + strerror(serrno)); + add_str(trtstate, line); + report(trtstate); + return; + } + trtstate->loc_socklen= sizeof(trtstate->loc_sin6); + if (getsockname(trtbase->v6icmp_snd, + &trtstate->loc_sin6, + &trtstate->loc_socklen) == -1) + { + crondlog(DIE9 "getsockname failed"); + } +#if 0 + printf("Got localname: %s\n", + inet_ntop(AF_INET6, + &trtstate->loc_sin6.sin6_addr, + buf, sizeof(buf))); +#endif + } + else + { + memset(&loc_sa4, '\0', sizeof(loc_sa4)); + loc_sa4.sin_family= AF_INET; + ((struct sockaddr_in *)&trtstate->sin6)->sin_port= + htons(0x8000); + + r= connect(trtbase->v4icmp_snd, + (struct sockaddr *)&trtstate->sin6, + trtstate->socklen); +#if 0 + { errno= ENOSYS; r= -1; } +#endif + if (r == -1) + { + serrno= errno; + + snprintf(line, sizeof(line), + ", " DBQ(error) ":" DBQ(connect failed: %s) " }", + strerror(serrno)); + add_str(trtstate, line); + report(trtstate); + return; + } + trtstate->loc_socklen= sizeof(trtstate->loc_sin6); + if (getsockname(trtbase->v4icmp_snd, + &trtstate->loc_sin6, + &trtstate->loc_socklen) == -1) + { + crondlog(DIE9 "getsockname failed"); + } +#if 0 + printf("Got localname: %s\n", + inet_ntoa(((struct sockaddr_in *) + &trtstate->loc_sin6)->sin_addr)); +#endif + } + } + else + { + if (trtstate->do_v6) + { + int sock; + + memset(&loc_sa6, '\0', sizeof(loc_sa6)); + loc_sa6.sin6_family= AF_INET6; + loc_sa6.sin6_port= htons(SRC_BASE_PORT + + trtstate->index);; + + sock= socket(AF_INET6, SOCK_DGRAM, 0); + if (sock == -1) + { + crondlog(DIE9 "socket failed"); + } +printf("traceroute_start2: before bind\n"); + r= bind(sock, (struct sockaddr *)&loc_sa6, + sizeof(loc_sa6)); + if (r == -1) + { + serrno= errno; + + snprintf(line, sizeof(line), + ", " DBQ(error) ":" DBQ(bind failed: %s) " }", + strerror(serrno)); + add_str(trtstate, line); + report(trtstate); + close(sock); + return; + } + + r= connect(sock, (struct sockaddr *)&trtstate->sin6, + trtstate->socklen); +#if 0 + { errno= ENOSYS; r= -1; } +#endif + if (r == -1) + { + serrno= errno; + + snprintf(line, sizeof(line), + ", " DBQ(error) ":" DBQ(connect failed: %s) " }", + strerror(serrno)); + add_str(trtstate, line); + report(trtstate); + return; + } + + trtstate->loc_socklen= sizeof(trtstate->loc_sin6); + if (getsockname(sock, + &trtstate->loc_sin6, + &trtstate->loc_socklen) == -1) + { + crondlog(DIE9 "getsockname failed"); + } + + close(sock); +#if 0 + printf("Got localname: %s:%d\n", + inet_ntop(AF_INET6, + &trtstate->loc_sin6.sin6_addr, + buf, sizeof(buf)), + ntohs(((struct sockaddr_in *)&trtstate-> + loc_sin6)->sin_port)); +#endif + } + else + { + int sock; + + memset(&loc_sa4, '\0', sizeof(loc_sa4)); + loc_sa4.sin_family= AF_INET; + + loc_sa4.sin_port= htons(SRC_BASE_PORT + + trtstate->index);; + + /* Also set destination port */ + ((struct sockaddr_in *)&trtstate->sin6)-> + sin_port= htons(BASE_PORT); + + sock= socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + { + crondlog(DIE9 "socket failed"); + } + r= bind(sock, (struct sockaddr *)&loc_sa4, + sizeof(loc_sa4)); +#if 0 + { errno= ENOSYS; r= -1; } +#endif + if (r == -1) + { + serrno= errno; + + snprintf(line, sizeof(line), + ", " DBQ(error) ":" DBQ(bind failed: %s) " }", + strerror(serrno)); + add_str(trtstate, line); + report(trtstate); + close(sock); + return; + } + + r= connect(sock, (struct sockaddr *) &trtstate->sin6, + trtstate->socklen); +#if 0 + { errno= ENOSYS; r= -1; } +#endif + if (r == -1) + { + serrno= errno; + + snprintf(line, sizeof(line), + ", " DBQ(error) ":" DBQ(connect failed: %s) " }", + strerror(serrno)); + add_str(trtstate, line); + report(trtstate); + close(sock); + return; + } + trtstate->loc_socklen= sizeof(trtstate->loc_sin6); + if (getsockname(sock, + &trtstate->loc_sin6, + &trtstate->loc_socklen) == -1) + { + crondlog(DIE9 "getsockname failed"); + } + close(sock); +#if 0 + printf("Got localname: %s:%d\n", + inet_ntoa(((struct sockaddr_in *) + &trtstate->loc_sin6)->sin_addr), + ntohs(((struct sockaddr_in *)&trtstate-> + loc_sin6)->sin_port)); +#endif + } + } + + add_str(trtstate, ", \"result\": [ "); + + send_pkt(trtstate); +} + +static void dns_cb(int result, struct evutil_addrinfo *res, void *ctx) +{ + int count; + struct trtstate *env; + struct evutil_addrinfo *cur; + char line[160]; + + env= ctx; + + if (!env->dnsip) + { + crondlog(LVL7 + "dns_cb: in dns_cb but not doing dns at this time"); + if (res) + evutil_freeaddrinfo(res); + return; + } + + if (result != 0) + { + /* Hmm, great. Where do we put this init code */ + if (env->result) free(env->result); + env->resmax= 80; + env->result= xmalloc(env->resmax); + env->reslen= 0; + + env->starttime= time(NULL); + snprintf(line, sizeof(line), + "{ " DBQ(error) ":" DBQ(name resolution failed: %s) " }", + evutil_gai_strerror(result)); + add_str(env, line); + report(env); + return; + } + + env->dnsip= 0; + + env->dns_res= res; + env->dns_curr= res; + + count= 0; + for (cur= res; cur; cur= cur->ai_next) + count++; + + // env->reportcount(env, count); + + while (env->dns_curr) + { + env->socklen= env->dns_curr->ai_addrlen; + if (env->socklen > sizeof(env->sin6)) + continue; /* Weird */ + memcpy(&env->sin6, env->dns_curr->ai_addr, + env->socklen); + + traceroute_start2(env); + + evutil_freeaddrinfo(env->dns_res); + env->dns_res= NULL; + env->dns_curr= NULL; + return; + } + + /* Something went wrong */ + evutil_freeaddrinfo(env->dns_res); + env->dns_res= NULL; + env->dns_curr= NULL; + snprintf(line, sizeof(line), +"%s{ " DBQ(error) ":" DBQ(name resolution failed: out of addresses) " } ] }", + env->sent ? " }, " : ""); + add_str(env, line); + report(env); +} + +static void traceroute_start(void *state) +{ + struct trtstate *trtstate; + struct evutil_addrinfo hints; + + trtstate= state; + + if (!trtstate->delay_name_res) + { + traceroute_start2(state); + return; + } + + memset(&hints, '\0', sizeof(hints)); + hints.ai_socktype= SOCK_DGRAM; + hints.ai_family= trtstate->do_v6 ? AF_INET6 : AF_INET; + trtstate->dnsip= 1; + (void) evdns_getaddrinfo(DnsBase, trtstate->hostname, NULL, + &hints, dns_cb, trtstate); +} + +static int traceroute_delete(void *state) +{ + int ind; + struct trtstate *trtstate; + struct trtbase *base; + + trtstate= state; + + printf("traceroute_delete: state %p, index %d, busy %d\n", + state, trtstate->index, trtstate->busy); + + if (trtstate->busy) + return 0; + + base= trtstate->base; + ind= trtstate->index; + + if (base->table[ind] != trtstate) + crondlog(DIE9 "strange, state not in table"); + base->table[ind]= NULL; + + event_del(&trtstate->timer); + + free(trtstate->atlas); + trtstate->atlas= NULL; + free(trtstate->hostname); + trtstate->hostname= NULL; + free(trtstate->out_filename); + trtstate->out_filename= NULL; + + free(trtstate); + + return 1; +} + +struct testops traceroute_ops = { traceroute_init, traceroute_start, + traceroute_delete }; + |