aboutsummaryrefslogtreecommitdiff
path: root/eperd/ntp.c
diff options
context:
space:
mode:
Diffstat (limited to 'eperd/ntp.c')
-rw-r--r--eperd/ntp.c1955
1 files changed, 1955 insertions, 0 deletions
diff --git a/eperd/ntp.c b/eperd/ntp.c
new file mode 100644
index 0000000..251510d
--- /dev/null
+++ b/eperd/ntp.c
@@ -0,0 +1,1955 @@
+/*
+ * 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/tcp.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 NTP_PORT 123
+
+#define NTP_OPT_STRING ("!46c:i:w:A:O:")
+
+#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 OPT_T (1 << 6)
+
+#define IPHDR 20
+
+#define SRC_BASE_PORT (20480)
+#define MAX_DATA_SIZE (4096)
+
+#define DBQ(str) "\"" #str "\""
+
+struct ntp_ts
+{
+ uint32_t ntp_seconds;
+ uint32_t ntp_fraction;
+};
+
+struct ntpbase
+{
+ struct event_base *event_base;
+
+ int my_pid;
+
+ struct ntpstate **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 ntpstate
+{
+ /* Parameters */
+ char *atlas;
+ char *hostname;
+ char *destportstr;
+ char *out_filename;
+ char *interface;
+ char do_v6;
+ char count;
+ unsigned timeout;
+
+ /* Base and index in table */
+ struct ntpbase *base;
+ int index;
+
+ struct sockaddr_in6 sin6;
+ socklen_t socklen;
+ struct sockaddr_in6 loc_sin6;
+ socklen_t loc_socklen;
+
+ int sent;
+ uint16_t seq;
+ int socket; /* Socket for sending and receiving */
+ struct event event_socket; /* Event for this socket */
+ unsigned first:1; /* Waiting for first response */
+ unsigned done:1; /* We got something from the target
+ * host or a destination unreachable.
+ */
+ unsigned not_done:1; /* Not got something else */
+ 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;
+
+ uint8_t ntp_flags;
+ uint8_t ntp_stratum;
+ int8_t ntp_poll;
+ int8_t ntp_precision;
+ uint32_t ntp_root_delay;
+ uint32_t ntp_root_dispersion;
+ uint32_t ntp_reference_id;
+ struct ntp_ts ntp_reference_ts;
+
+ 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;
+ char open_result;
+};
+
+static struct ntpbase *ntp_base;
+
+struct ntphdr
+{
+ uint8_t ntp_flags;
+ uint8_t ntp_stratum;
+ int8_t ntp_poll;
+ int8_t ntp_precision;
+ uint32_t ntp_root_delay;
+ uint32_t ntp_root_dispersion;
+ uint32_t ntp_reference_id;
+ struct ntp_ts ntp_reference_ts;
+ struct ntp_ts ntp_origin_ts;
+ struct ntp_ts ntp_receive_ts;
+ struct ntp_ts ntp_transmit_ts;
+};
+
+#define NTP_LI_MASK 0xC0
+#define NTP_LI_SHIFT 6
+#define LI_NO_WARNING 0
+#define LI_61 1
+#define LI_59 2
+#define LI_UNKNOWN 3
+#define NTP_VERSION 4
+#define NTP_VERSION_MASK 0x38
+#define NTP_VERSION_SHIFT 3
+#define NTP_MODE_CLIENT 3
+#define NTP_MODE_MASK 0x7
+#define MODE_RESERVED 0
+#define MODE_SYM_ACT 1
+#define MODE_SYM_PASS 2
+#define MODE_CLIENT 3
+#define MODE_SERVER 4
+#define MODE_BROADCAST 5
+#define MODE_CONTROL 6
+#define MODE_PRIVATE 7
+
+#define STRATUM_INVALID 0
+#define STRATUM_UNSYNCHRONIZED 16
+
+#define NTP_1970 2208988800UL /* 1970 - 1900 in seconds */
+
+#define NTP_4G 4294967296.0
+
+
+static int create_socket(struct ntpstate *state);
+
+static void add_str(struct ntpstate *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 format_li(char *line, size_t size, uint8_t flags)
+{
+ const char *str;
+
+ switch((flags & NTP_LI_MASK) >> NTP_LI_SHIFT)
+ {
+ case LI_NO_WARNING: str= "no"; break;
+ case LI_61: str= "61"; break;
+ case LI_59: str= "59"; break;
+ case LI_UNKNOWN: str= "unknown"; break;
+ }
+ snprintf(line, size, DBQ(li) ": " DBQ(%s), str);
+}
+
+static void format_mode(char *line, size_t size, uint8_t flags)
+{
+ const char *str;
+
+ switch(flags & NTP_MODE_MASK)
+ {
+ case MODE_RESERVED: str= "reserved"; break;
+ case MODE_SYM_ACT: str= "sym. active"; break;
+ case MODE_SYM_PASS: str= "sym. passive"; break;
+ case MODE_CLIENT: str= "client"; break;
+ case MODE_SERVER: str= "server"; break;
+ case MODE_BROADCAST: str= "broadcast"; break;
+ case MODE_CONTROL: str= "control"; break;
+ case MODE_PRIVATE: str= "private"; break;
+ }
+ snprintf(line, size, DBQ(mode) ": " DBQ(%s), str);
+}
+
+static void format_stratum(char *line, size_t size, uint8_t stratum)
+{
+ const char *str;
+
+ str= NULL;
+ switch(stratum)
+ {
+ case STRATUM_INVALID: str= "invalid"; break;
+ case STRATUM_UNSYNCHRONIZED: str= "unsynchronized"; break;
+ }
+ if (str)
+ {
+ snprintf(line, size, DBQ(stratum) ": " DBQ(%s),
+ str);
+ }
+ else if (stratum < STRATUM_UNSYNCHRONIZED)
+ {
+ snprintf(line, size, DBQ(stratum) ": %d",
+ stratum);
+ }
+ else
+ {
+ snprintf(line, size, DBQ(stratum) ": " DBQ(reserved (%d)),
+ stratum);
+ }
+}
+
+static void format_8bit(char *line, size_t size, const char *label,
+ int8_t value)
+{
+ if (value >= 0)
+ {
+ snprintf(line, size, DBQ(%s) ": %d", label, 1 << value);
+ }
+ else
+ {
+ snprintf(line, size, DBQ(%s) ": %g", label,
+ 1.0 / (1 << -value));
+ }
+}
+
+static void format_short_ts(char *line, size_t size, const char *label,
+ uint32_t value)
+{
+ snprintf(line, size, DBQ(%s) ": %g", label, value/65536.0);
+}
+
+static void format_ref_id(char *line, size_t size, uint32_t value,
+ uint8_t stratum)
+{
+ if (stratum == 0 || stratum == 1)
+ {
+ snprintf(line, size, DBQ(ref-id) ": " DBQ(%.4s),
+ (char *)&value);
+ }
+ else
+ {
+ snprintf(line, size, DBQ(ref-id) ": " DBQ(%08x),
+ ntohl(value));
+ }
+}
+
+static void format_ts(char *line, size_t size, const char *label,
+ struct ntp_ts *ts)
+{
+ double d;
+
+ d= ntohl(ts->ntp_seconds) + ntohl(ts->ntp_fraction)/NTP_4G;
+ snprintf(line, size, DBQ(%s) ": %.9f", label, d);
+}
+
+static void report(struct ntpstate *state)
+{
+ FILE *fh;
+ const char *proto;
+ char namebuf[NI_MAXHOST];
+ char line[80];
+
+ 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(lts) ":%d"
+ ", " DBQ(time) ":%ld, ",
+ state->atlas, get_atlas_fw_version(),
+ get_timesync(),
+ state->starttime);
+ }
+
+ 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);
+ }
+
+ proto= "UDP";
+ fprintf(fh, ", " DBQ(proto) ":" DBQ(%s) ", " DBQ(af) ": %d",
+ proto,
+ state->dnsip ? (state->do_v6 ? 6 : 4) :
+ (state->sin6.sin6_family == AF_INET6 ? 6 : 4));
+
+ if (!state->first)
+ {
+ format_li(line, sizeof(line), state->ntp_flags);
+ fprintf(fh, ", %s", line);
+ fprintf(fh, ", " DBQ(version) ": %d",
+ ((state->ntp_flags & NTP_VERSION_MASK) >>
+ NTP_VERSION_SHIFT));
+
+ format_mode(line, sizeof(line), state->ntp_flags);
+ fprintf(fh, ", %s", line);
+
+ format_stratum(line, sizeof(line), state->ntp_stratum);
+ fprintf(fh, ", %s", line);
+
+ format_8bit(line, sizeof(line), "poll", state->ntp_poll);
+ fprintf(fh, ", %s", line);
+
+ format_8bit(line, sizeof(line), "precision",
+ state->ntp_precision);
+ fprintf(fh, ", %s", line);
+
+ format_short_ts(line, sizeof(line), "root-delay",
+ ntohl(state->ntp_root_delay));
+ fprintf(fh, ", %s", line);
+
+ format_short_ts(line, sizeof(line), "root-dispersion",
+ ntohl(state->ntp_root_dispersion));
+ fprintf(fh, ", %s", line);
+
+ format_ref_id(line, sizeof(line), state->ntp_reference_id,
+ state->ntp_stratum);
+ fprintf(fh, ", %s", line);
+
+ format_ts(line, sizeof(line), "ref-ts",
+ &state->ntp_reference_ts);
+ fprintf(fh, ", %s", line);
+ }
+
+ fprintf(fh, ", " DBQ(result) ": [ %s ] }\n", state->result);
+
+ free(state->result);
+ state->result= NULL;
+
+ if (state->out_filename)
+ fclose(fh);
+
+ /* Kill the event and close socket */
+ if (state->socket != -1)
+ {
+ event_del(&state->event_socket);
+ close(state->socket);
+ state->socket= -1;
+ }
+
+ state->busy= 0;
+
+ if (state->base->done)
+ state->base->done(state);
+}
+
+static void send_pkt(struct ntpstate *state)
+{
+ int r, len, serrno;
+ struct ntpbase *base;
+ struct ntphdr *ntphdr;
+ double d;
+ struct timeval interval;
+ char line[80];
+
+ state->gotresp= 0;
+
+ base= state->base;
+
+ if (state->sent >= state->count)
+ {
+ add_str(state, " }");
+
+ /* We are done */
+ report(state);
+ return;
+ }
+ state->seq++;
+
+ ntphdr= (struct ntphdr *)base->packet;
+ len= sizeof(*ntphdr);
+
+ memset(ntphdr, '\0', len);
+ ntphdr->ntp_flags= (NTP_VERSION << NTP_VERSION_SHIFT) | NTP_MODE_CLIENT;
+
+ gettimeofday(&state->xmit_time, NULL);
+
+ ntphdr->ntp_transmit_ts.ntp_seconds=
+ htonl(state->xmit_time.tv_sec + NTP_1970);
+ d= state->xmit_time.tv_usec / 1e6;
+ d *= NTP_4G;
+ ntphdr->ntp_transmit_ts.ntp_fraction= htonl((uint32_t)d);
+
+ if (state->sin6.sin6_family == AF_INET6)
+ {
+ /* Set port */
+ state->sin6.sin6_port= htons(NTP_PORT);
+
+ r= sendto(state->socket, 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;
+
+ 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
+
+ /* Set port */
+ ((struct sockaddr_in *)&state->sin6)->sin_port=
+ htons(NTP_PORT);
+
+ r= sendto(state->socket, 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;
+ 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->open_result)
+ add_str(state, " }, ");
+ add_str(state, "{ ");
+ state->open_result= 0;
+
+ /* 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 ready_callback(int __attribute((unused)) unused,
+ const short __attribute((unused)) event, void *s)
+{
+ struct ntpbase *base;
+ struct ntpstate *state;
+ int head;
+ ssize_t nrecv;
+ socklen_t slen;
+ double d;
+ struct ntphdr *ntphdr;
+ struct timeval now;
+ struct ntp_ts final_ts;
+ struct sockaddr_in remote;
+ char line[80];
+
+ gettimeofday(&now, NULL);
+
+ state= s;
+ base= state->base;
+
+ slen= sizeof(remote);
+ nrecv= recvfrom(state->socket, base->packet, sizeof(base->packet),
+ MSG_DONTWAIT, (struct sockaddr *)&remote, &slen);
+ if (nrecv == -1)
+ {
+ /* Strange, read error */
+ printf("ready_callback: read error '%s'\n", strerror(errno));
+ return;
+ }
+ // printf("ready_callback: got packet\n");
+
+ if (nrecv < sizeof(*ntphdr))
+ {
+ /* Short packet */
+ printf("ready_callback: too short %d\n", (int)nrecv);
+ return;
+ }
+
+ if (!state->busy)
+ {
+printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family);
+ return;
+ }
+
+ if (state->open_result)
+ add_str(state, " }, { ");
+
+ head= 1;
+
+ ntphdr= (struct ntphdr *)base->packet;
+
+ if (state->first)
+ {
+ /* Copy mostly static fields */
+ state->ntp_flags= ntphdr->ntp_flags;
+ state->ntp_stratum= ntphdr->ntp_stratum;
+ state->ntp_poll= ntphdr->ntp_poll;
+ state->ntp_precision= ntphdr->ntp_precision;
+ state->ntp_root_delay= ntphdr->ntp_root_delay;
+ state->ntp_root_dispersion= ntphdr->ntp_root_dispersion;
+ state->ntp_reference_id= ntphdr->ntp_reference_id;
+ state->ntp_reference_ts= ntphdr->ntp_reference_ts;
+
+ state->first= 0;
+ }
+ else
+ {
+ if ((ntphdr->ntp_flags & NTP_LI_MASK) !=
+ (state->ntp_flags & NTP_LI_MASK))
+ {
+ format_li(line, sizeof(line), ntphdr->ntp_flags);
+ add_str(state, line);
+ head= 0;
+ }
+
+ if ((ntphdr->ntp_flags & NTP_VERSION_MASK) !=
+ (state->ntp_flags & NTP_VERSION_MASK))
+ {
+ snprintf(line, sizeof(line), ", " DBQ(version) ": %d",
+ ((ntphdr->ntp_flags & NTP_VERSION_MASK) >>
+ NTP_VERSION_SHIFT));
+ add_str(state, line);
+ head= 0;
+ }
+
+ if ((ntphdr->ntp_flags & NTP_MODE_MASK) !=
+ (state->ntp_flags & NTP_MODE_MASK))
+ {
+ if (!head)
+ add_str(state, ", ");
+ format_mode(line, sizeof(line), ntphdr->ntp_flags);
+ add_str(state, line);
+ head= 0;
+ }
+
+ if (ntphdr->ntp_stratum != state->ntp_stratum)
+ {
+ if (!head)
+ add_str(state, ", ");
+ format_stratum(line, sizeof(line), ntphdr->ntp_stratum);
+ add_str(state, line);
+ head= 0;
+ }
+
+ if (ntphdr->ntp_poll != state->ntp_poll)
+ {
+ if (!head)
+ add_str(state, ", ");
+ format_8bit(line, sizeof(line), "poll",
+ ntphdr->ntp_poll);
+ add_str(state, line);
+ head= 0;
+ }
+
+ if (ntphdr->ntp_precision != state->ntp_precision)
+ {
+ if (!head)
+ add_str(state, ", ");
+ format_8bit(line, sizeof(line), "precision",
+ ntphdr->ntp_precision);
+ add_str(state, line);
+ head= 0;
+ }
+
+ if (ntphdr->ntp_root_delay != state->ntp_root_delay)
+ {
+ if (!head)
+ add_str(state, ", ");
+ format_short_ts(line, sizeof(line), "root-delay",
+ ntohl(ntphdr->ntp_root_delay));
+ add_str(state, line);
+ head= 0;
+ }
+
+ if (ntphdr->ntp_root_dispersion != state->ntp_root_dispersion)
+ {
+ if (!head)
+ add_str(state, ", ");
+ format_short_ts(line, sizeof(line), "root-dispersion",
+ ntohl(ntphdr->ntp_root_dispersion));
+ add_str(state, line);
+ head= 0;
+ }
+
+ if (ntphdr->ntp_reference_id != state->ntp_reference_id)
+ {
+ if (!head)
+ add_str(state, ", ");
+ format_ref_id(line, sizeof(line),
+ ntphdr->ntp_reference_id, ntphdr->ntp_stratum);
+ add_str(state, line);
+ head= 0;
+ }
+
+ if (memcmp(&ntphdr->ntp_reference_ts, &state->ntp_reference_ts,
+ sizeof(ntphdr->ntp_reference_ts)) != 0)
+ {
+ if (!head)
+ add_str(state, ", ");
+ format_ts(line, sizeof(line), "ref-ts",
+ &ntphdr->ntp_reference_ts);
+ add_str(state, line);
+ head= 0;
+ }
+ }
+
+ d= ntohl(ntphdr->ntp_origin_ts.ntp_seconds) +
+ ntohl(ntphdr->ntp_origin_ts.ntp_fraction)/NTP_4G;
+ snprintf(line, sizeof(line), "%s" DBQ(origin-ts) ": %.9f",
+ head ? "" : ", ", d);
+ head= 0;
+ add_str(state, line);
+
+ d= ntohl(ntphdr->ntp_receive_ts.ntp_seconds) +
+ ntohl(ntphdr->ntp_receive_ts.ntp_fraction)/NTP_4G;
+ snprintf(line, sizeof(line), ", " DBQ(receive-ts) ": %.9f", d);
+ add_str(state, line);
+
+ d= ntohl(ntphdr->ntp_transmit_ts.ntp_seconds) +
+ ntohl(ntphdr->ntp_transmit_ts.ntp_fraction)/NTP_4G;
+ snprintf(line, sizeof(line), ", " DBQ(transmit-ts) ": %.9f", d);
+ add_str(state, line);
+
+ final_ts.ntp_seconds= now.tv_sec + NTP_1970;
+ d= now.tv_usec / 1e6;
+ d *= 4294967296.0;
+ final_ts.ntp_fraction= d;
+
+ d= final_ts.ntp_seconds + final_ts.ntp_fraction/NTP_4G;
+ snprintf(line, sizeof(line), ", " DBQ(final-ts) ": %.9f", d);
+ add_str(state, line);
+
+ /* Compute rtt */
+ d= final_ts.ntp_seconds - ntohl(ntphdr->ntp_origin_ts.ntp_seconds) -
+ (ntohl(ntphdr->ntp_transmit_ts.ntp_seconds) -
+ ntohl(ntphdr->ntp_receive_ts.ntp_seconds)) +
+ final_ts.ntp_fraction/NTP_4G -
+ ntohl(ntphdr->ntp_origin_ts.ntp_fraction)/NTP_4G -
+ (ntohl(ntphdr->ntp_transmit_ts.ntp_fraction)/NTP_4G -
+ ntohl(ntphdr->ntp_receive_ts.ntp_fraction)/NTP_4G);
+ snprintf(line, sizeof(line), ", " DBQ(rtt) ": %f", d);
+ add_str(state, line);
+
+ d= (ntohl(ntphdr->ntp_origin_ts.ntp_seconds) +
+ final_ts.ntp_seconds)/2.0 -
+ (ntohl(ntphdr->ntp_receive_ts.ntp_seconds) +
+ ntohl(ntphdr->ntp_transmit_ts.ntp_seconds))/2.0 +
+ (ntohl(ntphdr->ntp_origin_ts.ntp_fraction)/NTP_4G +
+ final_ts.ntp_fraction/NTP_4G)/2.0 -
+ (ntohl(ntphdr->ntp_receive_ts.ntp_fraction)/NTP_4G +
+ ntohl(ntphdr->ntp_transmit_ts.ntp_fraction)/NTP_4G)/2.0;
+ snprintf(line, sizeof(line), ", " DBQ(offset) ": %f", d);
+ add_str(state, line);
+
+ state->open_result= 1;
+
+ send_pkt(state);
+#if 0
+ 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 - IPHDR - ICMP_MINLEN);
+ 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;
+
+ state->open_result= 1;
+
+ 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;
+ }
+#endif
+}
+
+#if 0
+static void ready_callback6(int __attribute((unused)) unused,
+ const short __attribute((unused)) event, void *s)
+{
+ crondlog(DIE9 "ready_callback6"); abort();
+#if 0
+ ssize_t nrecv;
+ int ind, rcvdttl, nxt, icmp_prefixlen, offset;
+ unsigned nextmtu, seq, optlen, hbhoptsize, dstoptsize;
+ size_t ehdrsiz, v6info_siz, siz;
+ struct trtbase *base;
+ struct trtstate *state;
+ struct ip6_hdr *eip;
+ struct ip6_frag *frag;
+ struct ip6_ext *opthdr;
+ struct icmp6_hdr *icmp, *eicmp;
+ struct tcphdr *etcp;
+ 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);
+
+ state= s;
+ base= state->base;
+
+ 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(state->socket_icmp, &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;
+
+ hbhoptsize= 0;
+ dstoptsize= 0;
+ 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 TCP, UDP, ICMP or a fragment header */
+ if (eip->ip6_nxt == IPPROTO_FRAGMENT ||
+ eip->ip6_nxt == IPPROTO_HOPOPTS ||
+ eip->ip6_nxt == IPPROTO_DSTOPTS ||
+ eip->ip6_nxt == IPPROTO_TCP ||
+ eip->ip6_nxt == IPPROTO_UDP ||
+ eip->ip6_nxt == IPPROTO_ICMPV6)
+ {
+ ehdrsiz= 0;
+ frag= NULL;
+ nxt= eip->ip6_nxt;
+ ptr= &eip[1];
+ if (nxt == IPPROTO_HOPOPTS)
+ {
+ /* Make sure the options header is completely
+ * there.
+ */
+ if (nrecv < sizeof(*icmp) + sizeof(*eip)
+ + sizeof(*opthdr))
+ {
+#if 0
+ printf(
+ "ready_callback6: too short %d (icmp+ip+opt)\n",
+ (int)nrecv);
+#endif
+ return;
+ }
+ opthdr= (struct ip6_ext *)ptr;
+ hbhoptsize= 8*opthdr->ip6e_len;
+ optlen= hbhoptsize+8;
+ if (nrecv < sizeof(*icmp) + sizeof(*eip) +
+ optlen)
+ {
+ /* Does not contain the full header */
+ return;
+ }
+ ehdrsiz += optlen;
+ nxt= opthdr->ip6e_nxt;
+ ptr= ((char *)opthdr)+optlen;
+ }
+ 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 *)ptr;
+ if ((ntohs(frag->ip6f_offlg) & ~3) != 0)
+ {
+ /* Not first fragment, just ignore
+ * it.
+ */
+ return;
+ }
+ ehdrsiz += sizeof(*frag);
+ nxt= frag->ip6f_nxt;
+ ptr= &frag[1];
+ }
+ if (nxt == IPPROTO_DSTOPTS)
+ {
+ /* Make sure the options header is completely
+ * there.
+ */
+ if (nrecv < sizeof(*icmp) + sizeof(*eip)
+ + sizeof(*opthdr))
+ {
+#if 0
+ printf(
+ "ready_callback6: too short %d (icmp+ip+opt)\n",
+ (int)nrecv);
+#endif
+ return;
+ }
+ opthdr= (struct ip6_ext *)ptr;
+ dstoptsize= 8*opthdr->ip6e_len;
+ optlen= dstoptsize+8;
+ if (nrecv < sizeof(*icmp) + sizeof(*eip) +
+ optlen)
+ {
+ /* Does not contain the full header */
+ return;
+ }
+ ehdrsiz += optlen;
+ nxt= opthdr->ip6e_nxt;
+ ptr= ((char *)opthdr)+optlen;
+ }
+
+ v6info_siz= sizeof(*v6info);
+ if (nxt == IPPROTO_TCP)
+ {
+ ehdrsiz += sizeof(*etcp);
+ v6info_siz= 0;
+ }
+ else 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 + v6info_siz)
+ {
+#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;
+ }
+
+ etcp= NULL;
+ eudp= NULL;
+ eicmp= NULL;
+ v6info= NULL;
+ if (nxt == IPPROTO_TCP)
+ {
+ etcp= (struct tcphdr *)ptr;
+ }
+ else 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
+ if (v6info)
+ {
+ printf(
+"ready_callback6: pid = htonl(%d), id = htonl(%d), seq = htonl(%d)\n",
+ ntohl(v6info->pid),
+ ntohl(v6info->id),
+ ntohl(v6info->seq));
+ }
+#endif
+
+ if (etcp)
+ {
+ /* We store the id in high order 16 bits of the
+ * sequence number
+ */
+ ind= ntohl(etcp->seq) >> 16;
+ }
+ else
+ {
+ if (ntohl(v6info->pid) != base->my_pid)
+ {
+ /* From a different process */
+ return;
+ }
+
+ ind= ntohl(v6info->id);
+ }
+
+ if (ind != state->index)
+ state= NULL;
+
+ if (state && state->sin6.sin6_family != AF_INET6)
+ state= NULL;
+
+ if (state)
+ {
+ if ((etcp && !state->do_tcp) ||
+ (eudp && !state->do_udp) ||
+ (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;
+ if (etcp)
+ {
+ /* Sequence number is in seq field */
+ seq= ntohl(etcp->seq) & 0xffff;
+ }
+ else
+ seq= ntohl(v6info->seq);
+
+ if (state->open_result)
+ add_str(state, " }, { ");
+
+ if (seq != state->seq)
+ {
+ if (seq > state->seq)
+ {
+#if 0
+ printf(
+ "ready_callback6: mismatch for seq, got 0x%x, expected 0x%x\n",
+ ntohl(v6info->seq),
+ state->seq);
+#endif
+ return;
+ }
+ late= 1;
+
+ snprintf(line, sizeof(line), DBQ(late) ":%d",
+ state->seq-seq);
+ add_str(state, line);
+ } else if (state->gotresp)
+ {
+ isDup= 1;
+ add_str(state, DBQ(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 if (v6info)
+ {
+ 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-ICMP6_HDR));
+ add_str(state, line);
+ if (eip->ip6_hops != 1)
+ {
+ snprintf(line, sizeof(line), ", \"ittl\":%d",
+ eip->ip6_hops);
+ add_str(state, line);
+ }
+ if (hbhoptsize)
+ {
+ snprintf(line, sizeof(line),
+ ", \"hbhoptsize\":%d", hbhoptsize);
+ add_str(state, line);
+ }
+ if (dstoptsize)
+ {
+ snprintf(line, sizeof(line),
+ ", \"dstoptsize\":%d", dstoptsize);
+ 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);
+ else if (eicmp)
+ siz += sizeof(*eicmp);
+ else if (etcp)
+ siz += sizeof(*etcp);
+ if (nextmtu < 1200)
+ {
+ /* This is IPv6, no need to go
+ * below 1280. Use 1200 to deal with
+ * off by one error or weird tunnels.
+ */
+ nextmtu= 1200;
+ }
+ 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: /* 0 */
+ add_str(state, ", \"err\":\"N\"");
+ break;
+ case ICMP6_DST_UNREACH_ADMIN: /* 1 */
+ add_str(state, ", \"err\":\"A\"");
+ break;
+ case ICMP6_DST_UNREACH_BEYONDSCOPE: /* 2 */
+ add_str(state, ", \"err\":\"h\"");
+ break;
+ case ICMP6_DST_UNREACH_ADDR: /* 3 */
+ add_str(state, ", \"err\":\"H\"");
+ break;
+ case ICMP6_DST_UNREACH_NOPORT: /* 4 */
+ 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);
+ }
+ }
+
+ state->open_result= 1;
+
+ 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);
+
+ if (ind != state->index)
+ state= NULL;
+ 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;
+ }
+
+ if (state->open_result)
+ add_str(state, " }, { ");
+
+ 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), DBQ(late) ":%d",
+ state->seq-seq);
+ add_str(state, line);
+ }
+ else if (state->gotresp)
+ {
+ isDup= 1;
+ add_str(state, DBQ(dup) ":true");
+ }
+
+ 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 - ICMP6_HDR));
+ 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
+
+ state->open_result= 1;
+
+ 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;
+ }
+#endif
+}
+#endif
+
+static struct ntpbase *ntp_base_new(struct event_base
+ *event_base)
+{
+ struct ntpbase *base;
+
+ base= xzalloc(sizeof(*base));
+
+ base->event_base= event_base;
+
+ base->tabsiz= 10;
+ base->table= xzalloc(base->tabsiz * sizeof(*base->table));
+
+ base->my_pid= getpid();
+
+ return base;
+}
+
+static void noreply_callback(int __attribute((unused)) unused,
+ const short __attribute((unused)) event, void *s)
+{
+ struct ntpstate *state;
+
+ state= s;
+
+#if 0
+ printf("noreply_callback: gotresp = %d\n",
+ state->gotresp);
+#endif
+
+ if (!state->gotresp)
+ {
+ if (state->open_result)
+ add_str(state, " }, { ");
+ add_str(state, DBQ(x) ":" DBQ(*));
+ state->open_result= 1;
+ }
+
+ send_pkt(state);
+}
+
+static void *ntp_init(int __attribute((unused)) argc, char *argv[],
+ void (*done)(void *state))
+{
+ uint32_t opt;
+ int i, do_v6;
+ unsigned count, timeout;
+ /* must be int-sized */
+ size_t newsiz;
+ char *str_Atlas;
+ const char *hostname;
+ char *out_filename;
+ const char *destportstr;
+ char *interface;
+ struct ntpstate *state;
+ FILE *fh;
+
+ if (!ntp_base)
+ {
+ ntp_base= ntp_base_new(EventBase);
+ if (!ntp_base)
+ crondlog(DIE9 "ntp_base_new failed");
+ }
+
+ /* Parse arguments */
+ count= 3;
+ interface= NULL;
+ timeout= 1000;
+ str_Atlas= NULL;
+ out_filename= NULL;
+ opt_complementary = "=1:4--6:i--u:c+:w+:";
+
+ opt = getopt32(argv, NTP_OPT_STRING, &count,
+ &interface, &timeout, &str_Atlas, &out_filename);
+ hostname = argv[optind];
+
+ if (opt == 0xffffffff)
+ {
+ crondlog(LVL8 "bad options");
+ return NULL;
+ }
+
+ do_v6= !!(opt & OPT_6);
+
+ 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 (str_Atlas)
+ {
+ if (!validate_atlas_id(str_Atlas))
+ {
+ crondlog(LVL8 "bad atlas ID '%s'", str_Atlas);
+ return NULL;
+ }
+ }
+
+ destportstr= "123";
+
+ state= xzalloc(sizeof(*state));
+ state->count= count;
+ state->interface= interface;
+ state->destportstr= strdup(destportstr);
+ state->timeout= timeout*1000;
+ state->atlas= str_Atlas ? strdup(str_Atlas) : NULL;
+ state->hostname= strdup(hostname);
+ state->do_v6= do_v6;
+ state->out_filename= out_filename ? strdup(out_filename) : NULL;
+ state->base= ntp_base;
+ state->busy= 0;
+ state->result= NULL;
+ state->reslen= 0;
+ state->resmax= 0;
+
+ for (i= 0; i<ntp_base->tabsiz; i++)
+ {
+ if (ntp_base->table[i] == NULL)
+ break;
+ }
+ if (i >= ntp_base->tabsiz)
+ {
+ newsiz= 2*ntp_base->tabsiz;
+ ntp_base->table= xrealloc(ntp_base->table,
+ newsiz*sizeof(*ntp_base->table));
+ for (i= ntp_base->tabsiz; i<newsiz; i++)
+ ntp_base->table[i]= NULL;
+ i= ntp_base->tabsiz;
+ ntp_base->tabsiz= newsiz;
+ }
+ state->index= i;
+ ntp_base->table[i]= state;
+ ntp_base->done= done;
+
+ memset(&state->loc_sin6, '\0', sizeof(state->loc_sin6));
+ state->loc_socklen= 0;
+
+ evtimer_assign(&state->timer, state->base->event_base,
+ noreply_callback, state);
+
+ return state;
+}
+
+static void traceroute_start2(void *state)
+{
+ struct ntpstate *ntpstate;
+
+ ntpstate= state;
+
+ if (ntpstate->busy)
+ {
+ printf("ntp_start: busy, can't start\n");
+ return;
+ }
+ ntpstate->busy= 1;
+
+ ntpstate->min= ULONG_MAX;
+ ntpstate->max= 0;
+ ntpstate->sum= 0;
+ ntpstate->sentpkts= 0;
+ ntpstate->rcvdpkts= 0;
+ ntpstate->duppkts= 0;
+
+ ntpstate->sent= 0;
+ ntpstate->seq= 0;
+ ntpstate->first= 1;
+ ntpstate->done= 0;
+ ntpstate->not_done= 0;
+
+ if (ntpstate->result) free(ntpstate->result);
+ ntpstate->resmax= 80;
+ ntpstate->result= xmalloc(ntpstate->resmax);
+ ntpstate->reslen= 0;
+ ntpstate->open_result= 0;
+ ntpstate->starttime= time(NULL);
+
+ ntpstate->socket= -1;
+
+ if (create_socket(ntpstate) == -1)
+ return;
+ if (ntpstate->do_v6)
+ {
+ ntpstate->loc_sin6.sin6_port= htons(SRC_BASE_PORT +
+ ntpstate->index);
+ }
+ else
+ {
+ ((struct sockaddr_in *)(&ntpstate->loc_sin6))->
+ sin_port= htons(SRC_BASE_PORT +
+ ntpstate->index);
+ }
+
+ send_pkt(ntpstate);
+}
+
+static int create_socket(struct ntpstate *state)
+{
+ int af, type, protocol;
+ int r, serrno;
+ char line[80];
+
+ af= (state->do_v6 ? AF_INET6 : AF_INET);
+ type= SOCK_DGRAM;
+ protocol= 0;
+
+ state->socket= xsocket(af, type, protocol);
+ if (state->socket == -1)
+ {
+ serrno= errno;
+
+ snprintf(line, sizeof(line),
+ ", " DBQ(error) ":" DBQ(socket failed: %s) " }",
+ strerror(serrno));
+ add_str(state, line);
+ report(state);
+ return -1;
+ }
+
+ if (state->interface)
+ {
+ if (bind_interface(state->socket,
+ af, state->interface) == -1)
+ {
+ snprintf(line, sizeof(line),
+ ", " DBQ(error) ":" DBQ(bind_interface failed) " }");
+ add_str(state, line);
+ report(state);
+ return -1;
+ }
+ }
+
+ r= connect(state->socket,
+ (struct sockaddr *)&state->sin6,
+ state->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(state, line);
+ report(state);
+ return -1;
+ }
+ state->loc_socklen= sizeof(state->loc_sin6);
+ if (getsockname(state->socket,
+ &state->loc_sin6,
+ &state->loc_socklen) == -1)
+ {
+ crondlog(DIE9 "getsockname failed");
+ }
+#if 0
+ printf("Got localname: %s\n",
+ inet_ntop(AF_INET6,
+ &state->loc_sin6.sin6_addr,
+ buf, sizeof(buf)));
+#endif
+
+
+ event_assign(&state->event_socket, state->base->event_base,
+ state->socket,
+ EV_READ | EV_PERSIST,
+ (af == AF_INET6 ? ready_callback : ready_callback),
+ state);
+ event_add(&state->event_socket, NULL);
+
+ return 0;
+}
+
+static void dns_cb(int result, struct evutil_addrinfo *res, void *ctx)
+{
+ int count;
+ struct ntpstate *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 ntp_start(void *state)
+{
+ struct ntpstate *ntpstate;
+ struct evutil_addrinfo hints;
+
+ ntpstate= state;
+
+ memset(&hints, '\0', sizeof(hints));
+ hints.ai_socktype= SOCK_DGRAM;
+ hints.ai_family= ntpstate->do_v6 ? AF_INET6 : AF_INET;
+ ntpstate->dnsip= 1;
+ (void) evdns_getaddrinfo(DnsBase, ntpstate->hostname,
+ ntpstate->destportstr, &hints, dns_cb, ntpstate);
+}
+
+static int ntp_delete(void *state)
+{
+ int ind;
+ struct ntpstate *ntpstate;
+ struct ntpbase *base;
+
+ ntpstate= state;
+
+ printf("ntp_delete: state %p, index %d, busy %d\n",
+ state, ntpstate->index, ntpstate->busy);
+
+ if (ntpstate->busy)
+ return 0;
+
+ base= ntpstate->base;
+ ind= ntpstate->index;
+
+ if (base->table[ind] != ntpstate)
+ crondlog(DIE9 "strange, state not in table");
+ base->table[ind]= NULL;
+
+ event_del(&ntpstate->timer);
+
+ free(ntpstate->atlas);
+ ntpstate->atlas= NULL;
+ free(ntpstate->hostname);
+ ntpstate->hostname= NULL;
+ free(ntpstate->destportstr);
+ ntpstate->destportstr= NULL;
+ free(ntpstate->out_filename);
+ ntpstate->out_filename= NULL;
+
+ free(ntpstate);
+
+ return 1;
+}
+
+struct testops ntp_ops = { ntp_init, ntp_start, ntp_delete };
+