AC_MSG_RESULT(no)
])
+ AC_CHECK_DECL([CLOCK_MONOTONIC]
+ , []
+ , [AC_MSG_ERROR([ngtcp2 for QUIC needs at least CLOCK_MONOTONIC on the system])]
+ , [AC_INCLUDES_DEFAULT
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+ ])
+
fi
# set static linking for uninstalled libraries if requested
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
-#include <sys/time.h>
#include <limits.h>
#ifdef USE_TCP_FASTOPEN
#include <netinet/tcp.h>
}
struct doq_timer*
-doq_timer_find_time(struct doq_table* table, struct timeval* tv)
+doq_timer_find_time(struct doq_table* table, ngtcp2_tstamp ts)
{
struct doq_timer key;
struct rbnode_type* node;
log_assert(table != NULL);
memset(&key, 0, sizeof(key));
- key.time.tv_sec = tv->tv_sec;
- key.time.tv_usec = tv->tv_usec;
+ key.time_mono = ts;
node = rbtree_search(table->timer_tree, &key);
if(node)
return (struct doq_timer*)node->key;
if(!timer->timer_in_list)
return;
/* The item in the rbtree has the list start and end. */
- rb_timer = doq_timer_find_time(table, &timer->time);
+ rb_timer = doq_timer_find_time(table, timer->time_mono);
if(rb_timer) {
if(timer->setlist_prev)
timer->setlist_prev->setlist_next = timer->setlist_next;
}
void doq_timer_set(struct doq_table* table, struct doq_timer* timer,
- struct doq_server_socket* worker_doq_socket, struct timeval* tv)
+ struct doq_server_socket* worker_doq_socket, struct timeval* tv,
+ ngtcp2_tstamp ts)
{
struct doq_timer* rb_timer;
if(verbosity >= VERB_ALGO && timer->conn) {
(int)rel.tv_sec, (int)rel.tv_usec);
}
if(timer->timer_in_tree || timer->timer_in_list) {
- if(timer->time.tv_sec == tv->tv_sec &&
- timer->time.tv_usec == tv->tv_usec)
+ if(timer->time_mono == ts)
return; /* already set on that time */
doq_timer_unset(table, timer);
}
- timer->time.tv_sec = tv->tv_sec;
- timer->time.tv_usec = tv->tv_usec;
- rb_timer = doq_timer_find_time(table, tv);
+ timer->time_real.tv_sec = tv->tv_sec;
+ timer->time_real.tv_usec = tv->tv_usec;
+ timer->time_mono = ts;
+ rb_timer = doq_timer_find_time(table, ts);
if(rb_timer) {
/* There is a timeout already with this value. Timer is
* added to the setlist. */
{
struct doq_timer* e = (struct doq_timer*)key1;
struct doq_timer* f = (struct doq_timer*)key2;
- if(e->time.tv_sec < f->time.tv_sec)
- return -1;
- if(e->time.tv_sec > f->time.tv_sec)
- return 1;
- if(e->time.tv_usec < f->time.tv_usec)
+ if(e->time_mono < f->time_mono)
return -1;
- if(e->time.tv_usec > f->time.tv_usec)
+ if(e->time_mono > f->time_mono)
return 1;
return 0;
}
ngtcp2_ssize tokenlen;
int ret;
const ngtcp2_path* path = ngtcp2_conn_get_path(conn->conn);
- ngtcp2_tstamp ts = doq_get_timestamp_nanosec();
tokenlen = ngtcp2_crypto_generate_regular_token(token,
conn->doq_socket->static_secret,
conn->doq_socket->static_secret_len, path->remote.addr,
- path->remote.addrlen, ts);
+ path->remote.addrlen, doq_get_timestamp_nanosec());
if(tokenlen < 0) {
log_err("doq ngtcp2_crypto_generate_regular_token failed");
return 1;
ngtcp2_tstamp doq_get_timestamp_nanosec(void)
{
-#ifdef CLOCK_REALTIME
struct timespec tp;
memset(&tp, 0, sizeof(tp));
- /* Get a nanosecond time, that can be compared with the event base. */
- if(clock_gettime(CLOCK_REALTIME, &tp) == -1) {
- log_err("clock_gettime failed: %s", strerror(errno));
+#ifdef CLOCK_BOOTTIME
+ if(clock_gettime(CLOCK_BOOTTIME, &tp) == -1) {
+#endif
+ if(clock_gettime(CLOCK_MONOTONIC, &tp) == -1) {
+ log_err("clock_gettime failed: %s", strerror(errno));
+ }
+#ifdef CLOCK_BOOTTIME
}
+#endif
return ((uint64_t)tp.tv_sec)*((uint64_t)1000000000) +
((uint64_t)tp.tv_nsec);
-#else
+}
+
+static struct timeval doq_get_timevalue(void)
+{
struct timeval tv;
+ memset(&tv, 0, sizeof(tv));
if(gettimeofday(&tv, NULL) < 0) {
log_err("gettimeofday failed: %s", strerror(errno));
+ memset(&tv, 0, sizeof(tv));
}
- return ((uint64_t)tv.tv_sec)*((uint64_t)1000000000) +
- ((uint64_t)tv.tv_usec)*((uint64_t)1000);
-#endif /* CLOCK_REALTIME */
+ return tv;
}
/** doq start the closing period for the connection. */
int* err_drop)
{
int ret;
- ngtcp2_tstamp ts;
struct ngtcp2_path path;
memset(&path, 0, sizeof(path));
path.remote.addr = (struct sockaddr*)&paddr->addr;
path.remote.addrlen = paddr->addrlen;
path.local.addr = (struct sockaddr*)&paddr->localaddr;
path.local.addrlen = paddr->localaddrlen;
- ts = doq_get_timestamp_nanosec();
ret = ngtcp2_conn_read_pkt(conn->conn, &path, pi,
sldns_buffer_begin(c->doq_socket->pkt_buf),
- sldns_buffer_limit(c->doq_socket->pkt_buf), ts);
+ sldns_buffer_limit(c->doq_socket->pkt_buf),
+ doq_get_timestamp_nanosec());
if(ret != 0) {
if(err_retry)
*err_retry = 0;
{
struct doq_stream* stream = conn->stream_write_first;
ngtcp2_path_storage ps;
- ngtcp2_tstamp ts = doq_get_timestamp_nanosec();
size_t num_packets = 0, max_packets = 65535;
ngtcp2_path_storage_zero(&ps);
ret = ngtcp2_conn_writev_stream(conn->conn, &ps.path, &pi,
sldns_buffer_begin(c->doq_socket->pkt_buf),
sldns_buffer_remaining(c->doq_socket->pkt_buf),
- &ndatalen, flags, stream_id, datav, datav_count, ts);
+ &ndatalen, flags, stream_id, datav, datav_count,
+ doq_get_timestamp_nanosec());
if(ret < 0) {
if(ret == NGTCP2_ERR_WRITE_MORE) {
verbose(VERB_ALGO, "doq: write more, ndatalen %d", (int)ndatalen);
if(ret == 0) {
/* congestion limited */
doq_conn_write_disable(conn);
- ngtcp2_conn_update_pkt_tx_time(conn->conn, ts);
+ ngtcp2_conn_update_pkt_tx_time(conn->conn,
+ doq_get_timestamp_nanosec());
return 1;
}
sldns_buffer_set_position(c->doq_socket->pkt_buf, ret);
if(stream)
stream = stream->write_next;
}
- ngtcp2_conn_update_pkt_tx_time(conn->conn, ts);
+ ngtcp2_conn_update_pkt_tx_time(conn->conn, doq_get_timestamp_nanosec());
return 1;
}
}
int
-doq_conn_check_timer(struct doq_conn* conn, struct timeval* tv)
+doq_conn_check_timer(struct doq_conn* conn, struct timeval* tv, ngtcp2_tstamp* ts)
{
- ngtcp2_tstamp expiry = ngtcp2_conn_get_expiry(conn->conn);
- ngtcp2_tstamp now = doq_get_timestamp_nanosec();
+ ngtcp2_tstamp doq_expiry = ngtcp2_conn_get_expiry(conn->conn);
+ ngtcp2_tstamp doq_now = doq_get_timestamp_nanosec();
ngtcp2_tstamp t;
+ struct timeval now = doq_get_timevalue();
- if(expiry <= now) {
+ if(doq_expiry <= doq_now || doq_expiry == UINT64_MAX) {
+ /* UINT64_MAX means there is no next expiry. */
/* The timer has already expired, add with zero timeout.
* This should call the callback straight away. Calling it
* from the event callbacks is cleaner than calling it here,
* because then it is always called with the same locks and
* so on. This routine only has the conn.lock. */
- t = now;
+ t = doq_now;
+ memcpy(tv, &now, sizeof(*tv));
} else {
- t = expiry;
+ t = doq_expiry;
+ memset(tv, 0, sizeof(*tv));
+ tv->tv_sec = (doq_expiry - doq_now) / NGTCP2_SECONDS;
+ tv->tv_usec = ((doq_expiry - doq_now) / NGTCP2_MICROSECONDS)%1000000;
+ timeval_add(tv, &now);
}
- /* convert to timeval */
- memset(tv, 0, sizeof(*tv));
- tv->tv_sec = t / NGTCP2_SECONDS;
- tv->tv_usec = (t / NGTCP2_MICROSECONDS)%1000000;
+ *ts = t;
/* If we already have a timer, is it the right value? */
if(conn->timer.timer_in_tree || conn->timer.timer_in_list) {
- if(conn->timer.time.tv_sec == tv->tv_sec &&
- conn->timer.time.tv_usec == tv->tv_usec)
+ if(conn->timer.time_mono == *ts)
return 0;
}
return 1;
int
doq_conn_handle_timeout(struct doq_conn* conn)
{
- ngtcp2_tstamp now = doq_get_timestamp_nanosec();
int rv;
if(verbosity >= VERB_ALGO)
doq_conn_log_line(conn, "timeout");
- rv = ngtcp2_conn_handle_expiry(conn->conn, now);
+ rv = ngtcp2_conn_handle_expiry(conn->conn, doq_get_timestamp_nanosec());
if(rv != 0) {
verbose(VERB_ALGO, "ngtcp2_conn_handle_expiry failed: %s",
ngtcp2_strerror(rv));
struct doq_timer {
/** The rbnode in the tree sorted by timeout value. Key this struct. */
struct rbnode_type node;
+ /** The timeout value. Monotonic value used with ngtcp2.
+ * This time value is used for the tree operations. */
+ ngtcp2_tstamp time_mono;
/** The timeout value. Absolute time value. */
- struct timeval time;
+ struct timeval time_real;
/** If the timer is in the time tree, with the node. */
int timer_in_tree;
/** If there are more timers with the exact same timeout value,
* doq check if the timer for the conn needs to be changed.
* @param conn: connection, caller must hold lock on it.
* @param tv: time value, absolute time, returned.
+ * @param ts: time stamp, absolute time, returned.
* @return true if timer needs to be set to tv, false if no change is needed
* to the timer. The timer is already set to the right time in that case.
*/
-int doq_conn_check_timer(struct doq_conn* conn, struct timeval* tv);
+int doq_conn_check_timer(struct doq_conn* conn, struct timeval* tv,
+ ngtcp2_tstamp* ts);
/** doq remove timer from tree */
void doq_timer_tree_remove(struct doq_table* table, struct doq_timer* timer);
/** doq set the timer and add it. */
void doq_timer_set(struct doq_table* table, struct doq_timer* timer,
- struct doq_server_socket* worker_doq_socket, struct timeval* tv);
+ struct doq_server_socket* worker_doq_socket, struct timeval* tv,
+ ngtcp2_tstamp ts);
/** doq find a timeout in the timer tree */
struct doq_timer* doq_timer_find_time(struct doq_table* table,
- struct timeval* tv);
+ ngtcp2_tstamp ts);
/** doq handle timeout for a connection. Pass conn locked. Returns false for
* deletion. */
/** doq get the quic size value */
size_t doq_table_quic_size_get(struct doq_table* table);
+
+/** get a timestamp in nanoseconds */
+ngtcp2_tstamp doq_get_timestamp_nanosec(void);
#endif /* HAVE_NGTCP2 */
char* set_ip_dscp(int socket, int addrfamily, int ds);
/** timer event callback for testcode/doqclient */
void doq_client_timer_cb(int fd, short event, void* arg);
-#ifdef HAVE_NGTCP2
-/** get a timestamp in nanoseconds */
-ngtcp2_tstamp doq_get_timestamp_nanosec(void);
-#endif
#endif /* LISTEN_DNSPORT_H */
char host[256], port[32];
struct ngtcp2_cid scid;
uint8_t token[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN];
- ngtcp2_tstamp ts;
ngtcp2_ssize tokenlen, ret;
if(!doq_print_addr_port(&paddr->addr, paddr->addrlen, host,
scid.datalen = c->doq_socket->sv_scidlen;
doq_cid_randfill(&scid, scid.datalen, c->doq_socket->rnd);
- ts = doq_get_timestamp_nanosec();
-
tokenlen = ngtcp2_crypto_generate_retry_token(token,
c->doq_socket->static_secret, c->doq_socket->static_secret_len,
hd->version, (void*)&paddr->addr, paddr->addrlen, &scid,
- &hd->dcid, ts);
+ &hd->dcid, doq_get_timestamp_nanosec());
if(tokenlen < 0) {
log_err("ngtcp2_crypto_generate_retry_token failed: %s",
ngtcp2_strerror(tokenlen));
struct ngtcp2_cid* ocid, struct ngtcp2_pkt_hd* hd)
{
char host[256], port[32];
- ngtcp2_tstamp ts;
if(!doq_print_addr_port(&paddr->addr, paddr->addrlen, host,
sizeof(host), port, sizeof(port))) {
log_err("doq_verify_retry_token failed");
return 0;
}
- ts = doq_get_timestamp_nanosec();
verbose(VERB_ALGO, "doq: verifying retry token from %s %s", host,
port);
if(ngtcp2_crypto_verify_retry_token(ocid,
c->doq_socket->static_secret,
c->doq_socket->static_secret_len, hd->version,
(void*)&paddr->addr, paddr->addrlen, &hd->dcid,
- 10*NGTCP2_SECONDS, ts) != 0) {
+ 10*NGTCP2_SECONDS, doq_get_timestamp_nanosec()) != 0) {
verbose(VERB_ALGO, "doq: could not verify retry token "
"from %s %s", host, port);
return 0;
struct ngtcp2_pkt_hd* hd)
{
char host[256], port[32];
- ngtcp2_tstamp ts;
if(!doq_print_addr_port(&paddr->addr, paddr->addrlen, host,
sizeof(host), port, sizeof(port))) {
log_err("doq_verify_token failed");
return 0;
}
- ts = doq_get_timestamp_nanosec();
verbose(VERB_ALGO, "doq: verifying token from %s %s", host, port);
if(ngtcp2_crypto_verify_regular_token(
#ifdef HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN
#endif
c->doq_socket->static_secret, c->doq_socket->static_secret_len,
(void*)&paddr->addr, paddr->addrlen, 3600*NGTCP2_SECONDS,
- ts) != 0) {
+ doq_get_timestamp_nanosec()) != 0) {
verbose(VERB_ALGO, "doq: could not verify token from %s %s",
host, port);
return 0;
{
struct doq_timer* t;
struct timeval tv;
+ ngtcp2_tstamp ts = 0;
int have_time = 0;
memset(&tv, 0, sizeof(tv));
t->worker_doq_socket == c->doq_socket) {
/* pick up this element */
t->worker_doq_socket = c->doq_socket;
+ memcpy(&tv, &t->time_real, sizeof(tv));
+ ts = t->time_mono;
have_time = 1;
- memcpy(&tv, &t->time, sizeof(tv));
break;
}
}
lock_rw_unlock(&c->doq_socket->table->lock);
-
+ c->doq_socket->marked_time = ts;
if(have_time) {
struct timeval rel;
timeval_subtract(&rel, &tv, c->doq_socket->now_tv);
comm_timer_set(c->doq_socket->timer, &rel);
- memcpy(&c->doq_socket->marked_time, &tv,
- sizeof(c->doq_socket->marked_time));
verbose(VERB_ALGO, "doq pickup timer at %d.%6.6d in %d.%6.6d",
(int)tv.tv_sec, (int)tv.tv_usec, (int)rel.tv_sec,
(int)rel.tv_usec);
} else {
if(comm_timer_is_set(c->doq_socket->timer))
comm_timer_disable(c->doq_socket->timer);
- memset(&c->doq_socket->marked_time, 0,
- sizeof(c->doq_socket->marked_time));
verbose(VERB_ALGO, "doq timer disabled");
}
}
uint8_t cid[NGTCP2_MAX_CIDLEN];
rbnode_type* node;
struct timeval new_tv;
+ ngtcp2_tstamp new_ts;
int write_change = 0, timer_change = 0;
/* No longer in callbacks, so the pointer to doq_socket is back
* to NULL. */
conn->doq_socket = NULL;
- if(doq_conn_check_timer(conn, &new_tv))
+ if(doq_conn_check_timer(conn, &new_tv, &new_ts))
timer_change = 1;
if( (conn->write_interest && !conn->on_write_list) ||
(!conn->write_interest && conn->on_write_list))
}
if(timer_change) {
doq_timer_set(c->doq_socket->table, &conn->timer,
- c->doq_socket, &new_tv);
+ c->doq_socket, &new_tv, new_ts);
}
lock_rw_unlock(&c->doq_socket->table->lock);
lock_basic_unlock(&conn->lock);
return 1;
}
-/** doq find a timer that timeouted and return the conn, locked. */
+/** doq find a timer that timed out and return the conn, locked. */
static struct doq_conn*
doq_timer_timeout_conn(struct doq_server_socket* doq_socket)
{
conn = t->conn;
/* If now < timer then no further timeouts in tree. */
- if(timeval_smaller(doq_socket->now_tv, &t->time)) {
+ if(timeval_smaller(doq_socket->now_tv, &t->time_real)) {
lock_rw_unlock(&doq_socket->table->lock);
return NULL;
}
{
struct doq_timer* t;
lock_rw_wrlock(&doq_socket->table->lock);
- t = doq_timer_find_time(doq_socket->table, &doq_socket->marked_time);
+ t = doq_timer_find_time(doq_socket->table, doq_socket->marked_time);
if(t && t->worker_doq_socket == doq_socket)
t->worker_doq_socket = NULL;
lock_rw_unlock(&doq_socket->table->lock);
- memset(&doq_socket->marked_time, 0, sizeof(doq_socket->marked_time));
+ doq_socket->marked_time = 0;
}
void
free(doq_socket);
return NULL;
}
- memset(&doq_socket->marked_time, 0, sizeof(doq_socket->marked_time));
+ doq_socket->marked_time = 0;
comm_base_timept(base, &doq_socket->now_tt, &doq_socket->now_tv);
doq_socket->cfg = cfg;
return doq_socket;
struct doq_pkt_addr* blocked_paddr;
/** timer for this worker on this comm_point to wait on. */
struct comm_timer* timer;
+#ifdef HAVE_NGTCP2
/** the timer that is marked by the doq_socket as waited on. */
- struct timeval marked_time;
+ ngtcp2_tstamp marked_time;
+#endif
/** the current time for use by time functions, time_t. */
time_t* now_tt;
/** the current time for use by time functions, timeval. */