diff --git a/configure.ac b/configure.ac index f1c5b0419..96b805aa4 100644 --- a/configure.ac +++ b/configure.ac @@ -1735,6 +1735,22 @@ if test x_$withval = x_yes -o x_$withval != x_no; then 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 +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + ]) + fi # set static linking for uninstalled libraries if requested diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index 5db2b940b..a08a5352e 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -42,7 +42,6 @@ #ifdef HAVE_SYS_TYPES_H # include #endif -#include #include #ifdef USE_TCP_FASTOPEN #include @@ -3399,14 +3398,13 @@ doq_table_delete(struct doq_table* table) } 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; @@ -3454,7 +3452,7 @@ doq_timer_list_remove(struct doq_table* table, struct doq_timer* timer) 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; @@ -3500,7 +3498,8 @@ doq_timer_unset(struct doq_table* table, struct doq_timer* timer) } 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) { @@ -3514,14 +3513,14 @@ void doq_timer_set(struct doq_table* table, struct doq_timer* timer, (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. */ @@ -3700,13 +3699,9 @@ int doq_timer_cmp(const void* key1, const void* key2) { 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; } @@ -4254,12 +4249,11 @@ doq_submit_new_token(struct doq_conn* conn) 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; @@ -5101,23 +5095,30 @@ doq_conn_clear_conids(struct doq_conn* conn) 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. */ @@ -5240,18 +5241,17 @@ doq_conn_recv(struct comm_point* c, struct doq_pkt_addr* paddr, 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; @@ -5339,7 +5339,6 @@ doq_conn_write_streams(struct comm_point* c, struct doq_conn* conn, { 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); @@ -5392,7 +5391,8 @@ doq_conn_write_streams(struct comm_point* c, struct doq_conn* conn, 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); @@ -5464,7 +5464,8 @@ doq_conn_write_streams(struct comm_point* c, struct doq_conn* conn, 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); @@ -5478,7 +5479,7 @@ doq_conn_write_streams(struct comm_point* c, struct doq_conn* conn, 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; } @@ -5555,32 +5556,35 @@ doq_table_pop_first(struct doq_table* table) } 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; @@ -5601,13 +5605,12 @@ doq_conn_log_line(struct doq_conn* conn, char* s) 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)); diff --git a/services/listen_dnsport.h b/services/listen_dnsport.h index 963595a1c..95aa3e11e 100644 --- a/services/listen_dnsport.h +++ b/services/listen_dnsport.h @@ -538,8 +538,11 @@ void doq_table_delete(struct doq_table* table); 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, @@ -813,10 +816,12 @@ struct doq_conn* doq_table_pop_first(struct doq_table* table); * 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); @@ -829,11 +834,12 @@ void doq_timer_unset(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. */ @@ -851,6 +857,9 @@ int doq_table_quic_size_available(struct doq_table* table, /** 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); @@ -866,8 +875,4 @@ void doq_client_event_cb(int fd, short event, void* arg); /** 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 */ diff --git a/util/netevent.c b/util/netevent.c index a86e22518..e1480dc22 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -1827,7 +1827,6 @@ doq_send_retry(struct comm_point* c, struct doq_pkt_addr* paddr, 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, @@ -1841,12 +1840,10 @@ doq_send_retry(struct comm_point* c, struct doq_pkt_addr* paddr, 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)); @@ -1895,13 +1892,11 @@ doq_verify_retry_token(struct comm_point* c, struct doq_pkt_addr* paddr, 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, @@ -1913,7 +1908,7 @@ doq_verify_retry_token(struct comm_point* c, struct doq_pkt_addr* paddr, 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; @@ -1928,13 +1923,11 @@ doq_verify_token(struct comm_point* c, struct doq_pkt_addr* paddr, 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 @@ -1944,7 +1937,7 @@ doq_verify_token(struct comm_point* c, struct doq_pkt_addr* paddr, #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; @@ -2171,6 +2164,7 @@ doq_pickup_timer(struct comm_point* c) { struct doq_timer* t; struct timeval tv; + ngtcp2_tstamp ts = 0; int have_time = 0; memset(&tv, 0, sizeof(tv)); @@ -2180,27 +2174,24 @@ doq_pickup_timer(struct comm_point* c) 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"); } } @@ -2213,13 +2204,14 @@ doq_done_setup_timer_and_write(struct comm_point* c, struct doq_conn* conn) 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)) @@ -2265,7 +2257,7 @@ doq_done_setup_timer_and_write(struct comm_point* c, struct doq_conn* conn) } 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); @@ -2429,7 +2421,7 @@ doq_write_blocked_pkt(struct comm_point* c) 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) { @@ -2442,7 +2434,7 @@ 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; } @@ -2465,11 +2457,11 @@ doq_timer_erase_marker(struct doq_server_socket* doq_socket) { 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 @@ -2776,7 +2768,7 @@ doq_server_socket_create(struct doq_table* table, struct ub_randstate* rnd, 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; diff --git a/util/netevent.h b/util/netevent.h index c5114bbbe..7235843ee 100644 --- a/util/netevent.h +++ b/util/netevent.h @@ -1093,8 +1093,10 @@ struct doq_server_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. */