From a399e7b70948f6b1b6589a259b2bba3fca1bd308 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 3 Nov 2023 10:59:20 +0100 Subject: [PATCH 001/125] - fast-reload, add unbound-control fast_reload --- daemon/remote.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index 3eb711ce6..1ce9f2119 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -652,6 +652,15 @@ do_reload(RES* ssl, struct worker* worker, int reuse_cache) send_ok(ssl); } +/** do the fast_reload command */ +static void +do_fast_reload(RES* ssl, struct worker* worker) +{ + if(!ssl_printf(ssl, "start fast_reload\n")) + return; + (void)worker; +} + /** do the verbosity command */ static void do_verbosity(RES* ssl, char* str) @@ -3020,6 +3029,9 @@ execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd, } else if(cmdcmp(p, "reload", 6)) { do_reload(ssl, worker, 0); return; + } else if(cmdcmp(p, "fast_reload", 11)) { + do_fast_reload(ssl, worker); + return; } else if(cmdcmp(p, "stats_noreset", 13)) { do_stats(ssl, worker, 0); return; From a38a37d66cecbf236b2362aae76bf33b2d9b8311 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 3 Nov 2023 11:44:22 +0100 Subject: [PATCH 002/125] - fast-reload, make a thread to service the unbound-control command. --- Makefile.in | 2 +- daemon/daemon.c | 2 ++ daemon/daemon.h | 3 +++ daemon/remote.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++- daemon/remote.h | 29 +++++++++++++++++++++ 5 files changed, 102 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index 22fb75c12..a1d98a971 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1296,7 +1296,7 @@ remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h $(srcdir)/validator/val_anchor.h $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h \ $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_delegpt.h \ $(srcdir)/services/outside_network.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h \ - $(srcdir)/sldns/wire2str.h + $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \ $(srcdir)/libunbound/unbound.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \ $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \ diff --git a/daemon/daemon.c b/daemon/daemon.c index 193608d40..186ae42f2 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -861,6 +861,8 @@ daemon_cleanup(struct daemon* daemon) dnsc_delete(daemon->dnscenv); daemon->dnscenv = NULL; #endif + if(daemon->fast_reload_thread) + fast_reload_thread_stop(daemon->fast_reload_thread); daemon->cfg = NULL; } diff --git a/daemon/daemon.h b/daemon/daemon.h index 57665446d..69e9a7280 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -58,6 +58,7 @@ struct ub_randstate; struct daemon_remote; struct respip_set; struct shm_main_info; +struct fast_reload_thread; #include "dnstap/dnstap_config.h" #ifdef USE_DNSTAP @@ -146,6 +147,8 @@ struct daemon { #endif /** reuse existing cache on reload if other conditions allow it. */ int reuse_cache; + /** the fast reload thread, or NULL */ + struct fast_reload_thread* fast_reload_thread; }; /** diff --git a/daemon/remote.c b/daemon/remote.c index 1ce9f2119..bdc4dd4f8 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -658,7 +658,7 @@ do_fast_reload(RES* ssl, struct worker* worker) { if(!ssl_printf(ssl, "start fast_reload\n")) return; - (void)worker; + fast_reload_thread_start(ssl, worker); } /** do the verbosity command */ @@ -3369,3 +3369,69 @@ int remote_control_callback(struct comm_point* c, void* arg, int err, clean_point(rc, s); return 0; } + +#ifndef THREADS_DISABLED +/** fast reload thread. the thread main function */ +static void* fast_reload_thread_main(void* arg) +{ + struct fast_reload_thread* frio = (struct fast_reload_thread*)arg; + log_thread_set(&frio->threadnum); + + verbose(VERB_ALGO, "start fast reload thread"); + + verbose(VERB_ALGO, "stop fast reload thread"); + return NULL; +} +#endif /* !THREADS_DISABLED */ + +/** fast reload thread. setup the thread info */ +static int +fast_reload_thread_setup(struct worker* worker) +{ + int numworkers = worker->daemon->num; + worker->daemon->fast_reload_thread = (struct fast_reload_thread*) + calloc(1, sizeof(*worker->daemon->fast_reload_thread)); + if(!worker->daemon->fast_reload_thread) + return 0; + /* The thread id printed in logs, numworker+1 is the dnstap thread. + * This is numworkers+2. */ + worker->daemon->fast_reload_thread->threadnum = numworkers+2; + return 1; +} + +/** fast reload thread. desetup and delete the thread info. */ +static void +fast_reload_thread_desetup(struct fast_reload_thread* fast_reload_thread) +{ + if(!fast_reload_thread) + return; + free(fast_reload_thread); +} + +void +fast_reload_thread_start(RES* ssl, struct worker* worker) +{ + if(worker->daemon->fast_reload_thread) { + log_err("fast reload thread already running"); + return; + } + if(!fast_reload_thread_setup(worker)) { + if(!ssl_printf(ssl, "error could not setup thread\n")) + return; + return; + } + worker->daemon->fast_reload_thread->started = 1; +#ifndef THREADS_DISABLED + ub_thread_create(&worker->daemon->fast_reload_thread->tid, + fast_reload_thread_main, worker->daemon->fast_reload_thread); +#else +#endif +} + +void +fast_reload_thread_stop(struct fast_reload_thread* fast_reload_thread) +{ + if(!fast_reload_thread) + return; + fast_reload_thread_desetup(fast_reload_thread); +} diff --git a/daemon/remote.h b/daemon/remote.h index 4902803f5..cb80d4e84 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -48,6 +48,7 @@ #ifdef HAVE_OPENSSL_SSL_H #include #endif +#include "util/locks.h" struct config_file; struct listen_list; struct listen_port; @@ -118,6 +119,21 @@ struct remote_stream { }; typedef struct remote_stream RES; +/** + * Fast reload thread structure + */ +struct fast_reload_thread { + /** the thread number for the dtio thread, + * must be first to cast thread arg to int* in checklock code. */ + int threadnum; + /** event base, for event handling */ + void* event_base; + /** thread id, of the io thread */ + ub_thread_type tid; + /** if the io processing has started */ + int started; +}; + /** * Create new remote control state for the daemon. * @param cfg: config file with key file settings. @@ -203,4 +219,17 @@ int ssl_printf(RES* ssl, const char* format, ...) int ssl_read_line(RES* ssl, char* buf, size_t max); #endif /* HAVE_SSL */ +/** + * Start fast reload thread + * @param ssl: the RES connection to print to. + * @param worker: the remote servicing worker. + */ +void fast_reload_thread_start(RES* ssl, struct worker* worker); + +/** + * Stop fast reload thread + * @param fast_reload_thread: the thread struct. + */ +void fast_reload_thread_stop(struct fast_reload_thread* fast_reload_thread); + #endif /* DAEMON_REMOTE_H */ From 88a60f044c6a13166924a06a589b38636581695b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 3 Nov 2023 17:12:44 +0100 Subject: [PATCH 003/125] - fast-reload, communication sockets for information transfer. --- Makefile.in | 4 +- daemon/daemon.c | 4 +- daemon/remote.c | 298 +++++++++++++++++++++++++++++++++++++++-- daemon/remote.h | 31 ++++- libunbound/libworker.c | 14 ++ smallapp/worker_cb.c | 14 ++ testcode/fake_event.c | 20 +++ util/fptr_wlist.c | 3 + 8 files changed, 372 insertions(+), 16 deletions(-) diff --git a/Makefile.in b/Makefile.in index a1d98a971..2ed4d1199 100644 --- a/Makefile.in +++ b/Makefile.in @@ -962,7 +962,7 @@ fptr_wlist.lo fptr_wlist.o: $(srcdir)/util/fptr_wlist.c config.h $(srcdir)/util/ $(srcdir)/validator/val_nsec3.h $(srcdir)/validator/val_sigcrypt.h $(srcdir)/validator/val_kentry.h \ $(srcdir)/validator/val_neg.h $(srcdir)/validator/autotrust.h $(srcdir)/libunbound/libworker.h \ $(srcdir)/libunbound/context.h $(srcdir)/util/alloc.h $(srcdir)/libunbound/unbound-event.h \ - $(srcdir)/libunbound/worker.h + $(srcdir)/libunbound/worker.h $(srcdir)/daemon/remote.h locks.lo locks.o: $(srcdir)/util/locks.c config.h $(srcdir)/util/locks.h $(srcdir)/util/log.h log.lo log.o: $(srcdir)/util/log.c config.h $(srcdir)/util/log.h $(srcdir)/util/locks.h $(srcdir)/sldns/sbuffer.h mini_event.lo mini_event.o: $(srcdir)/util/mini_event.c config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \ @@ -1296,7 +1296,7 @@ remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h $(srcdir)/validator/val_anchor.h $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h \ $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_delegpt.h \ $(srcdir)/services/outside_network.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h \ - $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h + $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h $(srcdir)/util/ub_event.h stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \ $(srcdir)/libunbound/unbound.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \ $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \ diff --git a/daemon/daemon.c b/daemon/daemon.c index 186ae42f2..b7d7b631b 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -842,6 +842,8 @@ daemon_cleanup(struct daemon* daemon) auth_zones_cleanup(daemon->env->auth_zones); /* key cache is cleared by module desetup during next daemon_fork() */ daemon_remote_clear(daemon->rc); + if(daemon->fast_reload_thread) + fast_reload_thread_stop(daemon->fast_reload_thread); for(i=0; inum; i++) worker_delete(daemon->workers[i]); free(daemon->workers); @@ -861,8 +863,6 @@ daemon_cleanup(struct daemon* daemon) dnsc_delete(daemon->dnscenv); daemon->dnscenv = NULL; #endif - if(daemon->fast_reload_thread) - fast_reload_thread_stop(daemon->fast_reload_thread); daemon->cfg = NULL; } diff --git a/daemon/remote.c b/daemon/remote.c index bdc4dd4f8..fdb8e1641 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -63,6 +63,7 @@ #include "util/config_file.h" #include "util/net_help.h" #include "util/module.h" +#include "util/ub_event.h" #include "services/listen_dnsport.h" #include "services/cache/rrset.h" #include "services/cache/infra.h" @@ -504,6 +505,11 @@ state_list_remove_elem(struct rc_state** list, struct comm_point* c) static void clean_point(struct daemon_remote* rc, struct rc_state* s) { + if(!s->rc) { + /* the state has been picked up and moved away */ + free(s); + return; + } state_list_remove_elem(&rc->busy_list, s->c); rc->active --; if(s->ssl) { @@ -654,11 +660,11 @@ do_reload(RES* ssl, struct worker* worker, int reuse_cache) /** do the fast_reload command */ static void -do_fast_reload(RES* ssl, struct worker* worker) +do_fast_reload(RES* ssl, struct worker* worker, struct rc_state* s) { if(!ssl_printf(ssl, "start fast_reload\n")) return; - fast_reload_thread_start(ssl, worker); + fast_reload_thread_start(ssl, worker, s); } /** do the verbosity command */ @@ -3015,7 +3021,7 @@ cmdcmp(char* p, const char* cmd, size_t len) /** execute a remote control command */ static void -execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd, +execute_cmd(struct daemon_remote* rc, struct rc_state* s, RES* ssl, char* cmd, struct worker* worker) { char* p = skipwhite(cmd); @@ -3030,7 +3036,7 @@ execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd, do_reload(ssl, worker, 0); return; } else if(cmdcmp(p, "fast_reload", 11)) { - do_fast_reload(ssl, worker); + do_fast_reload(ssl, worker, s); return; } else if(cmdcmp(p, "stats_noreset", 13)) { do_stats(ssl, worker, 0); @@ -3213,7 +3219,7 @@ daemon_remote_exec(struct worker* worker) return; } verbose(VERB_ALGO, "remote exec distributed: %s", (char*)msg); - execute_cmd(NULL, NULL, (char*)msg, worker); + execute_cmd(NULL, NULL, NULL, (char*)msg, worker); free(msg); } @@ -3277,7 +3283,7 @@ handle_req(struct daemon_remote* rc, struct rc_state* s, RES* res) verbose(VERB_DETAIL, "control cmd: %s", buf); /* figure out what to do */ - execute_cmd(rc, res, buf, rc->worker); + execute_cmd(rc, s, res, buf, rc->worker); } /** handle SSL_do_handshake changes to the file descriptor to wait for later */ @@ -3374,8 +3380,8 @@ int remote_control_callback(struct comm_point* c, void* arg, int err, /** fast reload thread. the thread main function */ static void* fast_reload_thread_main(void* arg) { - struct fast_reload_thread* frio = (struct fast_reload_thread*)arg; - log_thread_set(&frio->threadnum); + struct fast_reload_thread* fast_reload_thread = (struct fast_reload_thread*)arg; + log_thread_set(&fast_reload_thread->threadnum); verbose(VERB_ALGO, "start fast reload thread"); @@ -3384,6 +3390,204 @@ static void* fast_reload_thread_main(void* arg) } #endif /* !THREADS_DISABLED */ +/** create a socketpair for bidirectional communication, false on failure */ +static int +create_socketpair(int* pair, struct ub_randstate* rand) +{ +#ifndef USE_WINSOCK + if(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { + log_err("socketpair: %s", strerror(errno)); + return 0; + } + (void)rand; +#else + struct sockaddr_in addr, baddr, accaddr, connaddr; + socklen_t baddrlen, accaddrlen, connaddrlen; + uint8_t localhost[] = {127, 0, 0, 1}; + uint8_t nonce[16], recvnonce[16]; + size_t i; + int lst; + ssize_t ret; + pair[0] = -1; + pair[1] = -1; + for(i=0; i (socklen_t)sizeof(baddr)) { + log_err("create socketpair: getsockname returned addr too big"); + sock_close(lst); + sock_close(pair[1]); + pair[1] = -1; + return 0; + } + /* the socket is blocking */ + if(connect(pair[1], (struct sockaddr*)&baddr, baddrlen) == -1) { + log_err("create socketpair: connect: %s", + sock_strerror(errno)); + sock_close(lst); + sock_close(pair[1]); + pair[1] = -1; + return 0; + } + accaddrlen = (socklen_t)sizeof(accaddr); + pair[0] = accept(lst, &accaddr, &accaddrlen); + if(pair[0] == -1) { + log_err("create socketpair: accept: %s", sock_strerror(errno)); + sock_close(lst); + sock_close(pair[1]); + pair[1] = -1; + return 0; + } + if(accaddrlen > (socklen_t)sizeof(accaddr)) { + log_err("create socketpair: accept returned addr too big"); + sock_close(lst); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } + if(accaddr.sin_family != AF_INET || + memcmp(localhost, &accaddr.sin_addr, 4) != 0) { + log_err("create socketpair: accept from wrong address"); + sock_close(lst); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } + connaddrlen = (socklen_t)sizeof(connaddr); + if(getsockname(pair[1], (struct sockaddr*)&connaddr, &connaddrlen) + == -1) { + log_err("create socketpair: getsockname connectedaddr: %s", + sock_strerror(errno)); + sock_close(lst); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } + if(connaddrlen > (socklen_t)sizeof(connaddr)) { + log_err("create socketpair: getsockname connectedaddr returned addr too big"); + sock_close(lst); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } + if(connaddr.sin_family != AF_INET || + memcmp(localhost, &connaddr.sin_addr, 4) != 0) { + log_err("create socketpair: getsockname connectedadd returned wrong address"); + sock_close(lst); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } + if(accaddr.sin_port != connaddr.sin_port) { + log_err("create socketpair: accept from wrong port"); + sock_close(lst); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } + sock_close(lst); + + ret = send(pair[1], nonce, sizeof(nonce), 0); + if(ret == -1) { + log_err("create socketpair: send: %s", sock_strerror(errno)); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } else if(ret != sizeof(nonce)) { + log_err("create socketpair: send was truncated"); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } + + ret = recv(pair[0], recvnonce, sizeof(nonce), 0); + if(ret == -1) { + log_err("create socketpair: recv: %s", sock_strerror(errno)); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } else if(ret == 0) { + log_err("create socketpair: stream closed"); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } + if(ret != sizeof(nonce)) { + log_err("create socketpair: recv did not read all nonce bytes"); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } + if(memcmp(nonce, recvnonce, sizeof(nonce)) != 0) { + log_err("create socketpair: recv wrong nonce"); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } +#endif + return 1; +} + /** fast reload thread. setup the thread info */ static int fast_reload_thread_setup(struct worker* worker) @@ -3396,6 +3600,14 @@ fast_reload_thread_setup(struct worker* worker) /* The thread id printed in logs, numworker+1 is the dnstap thread. * This is numworkers+2. */ worker->daemon->fast_reload_thread->threadnum = numworkers+2; + worker->daemon->fast_reload_thread->commpair[0] = -1; + worker->daemon->fast_reload_thread->commpair[1] = -1; + if(!create_socketpair(worker->daemon->fast_reload_thread->commpair, + worker->daemon->rand)) { + free(worker->daemon->fast_reload_thread); + return 0; + } + worker->daemon->fast_reload_thread->worker = worker; return 1; } @@ -3405,11 +3617,44 @@ fast_reload_thread_desetup(struct fast_reload_thread* fast_reload_thread) { if(!fast_reload_thread) return; + if(fast_reload_thread->service_event && + fast_reload_thread->service_event_is_added) { + ub_event_del(fast_reload_thread->service_event); + fast_reload_thread->service_event_is_added = 0; + } + if(fast_reload_thread->service_event) + ub_event_free(fast_reload_thread->service_event); + sock_close(fast_reload_thread->commpair[0]); + sock_close(fast_reload_thread->commpair[1]); + if(fast_reload_thread->remote.ssl) { + SSL_shutdown(fast_reload_thread->remote.ssl); + SSL_free(fast_reload_thread->remote.ssl); + } + comm_point_delete(fast_reload_thread->client_cp); free(fast_reload_thread); } +void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), + void* arg) +{ + struct fast_reload_thread* fast_reload_thread = + (struct fast_reload_thread*)arg; + (void)fast_reload_thread; +} + +int fast_reload_client_callback(struct comm_point* c, void* arg, int err, + struct comm_reply* ATTR_UNUSED(rep)) +{ + struct fast_reload_thread* fast_reload_thread = + (struct fast_reload_thread*)arg; + (void)c; + (void)err; + (void)fast_reload_thread; + return 0; +} + void -fast_reload_thread_start(RES* ssl, struct worker* worker) +fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s) { if(worker->daemon->fast_reload_thread) { log_err("fast reload thread already running"); @@ -3420,8 +3665,43 @@ fast_reload_thread_start(RES* ssl, struct worker* worker) return; return; } + worker->daemon->fast_reload_thread->remote = *ssl; worker->daemon->fast_reload_thread->started = 1; + #ifndef THREADS_DISABLED + /* Setup command listener in remote servicing thread */ + worker->daemon->fast_reload_thread->service_event = ub_event_new( + comm_base_internal(worker->base), + worker->daemon->fast_reload_thread->commpair[0], + UB_EV_READ | UB_EV_PERSIST, fast_reload_service_cb, + worker->daemon->fast_reload_thread); + if(!worker->daemon->fast_reload_thread->service_event) { + if(!ssl_printf(ssl, "error out of memory\n")) + return; + return; + } + if(ub_event_add(worker->daemon->fast_reload_thread->service_event, + NULL) != 0) { + if(!ssl_printf(ssl, "error out of memory adding service event\n")) + return; + return; + } + worker->daemon->fast_reload_thread->service_event_is_added = 1; + + /* Setup the comm point to the remote control client as an event + * on the remote servicing thread, which it already is. + * It needs a new callback to service it. */ + log_assert(s); + state_list_remove_elem(&s->rc->busy_list, s->c); + s->rc->active --; + worker->daemon->fast_reload_thread->client_cp = s->c; + worker->daemon->fast_reload_thread->client_cp->callback = + fast_reload_client_callback; + worker->daemon->fast_reload_thread->client_cp->cb_arg = + worker->daemon->fast_reload_thread; + s->rc = NULL; /* move away the rc state */ + + /* Start fast reload thread */ ub_thread_create(&worker->daemon->fast_reload_thread->tid, fast_reload_thread_main, worker->daemon->fast_reload_thread); #else diff --git a/daemon/remote.h b/daemon/remote.h index cb80d4e84..c257916a3 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -126,12 +126,26 @@ struct fast_reload_thread { /** the thread number for the dtio thread, * must be first to cast thread arg to int* in checklock code. */ int threadnum; - /** event base, for event handling */ - void* event_base; + /** communication socket pair, that sends commands */ + int commpair[2]; /** thread id, of the io thread */ ub_thread_type tid; /** if the io processing has started */ int started; + + /** the event that listens on the remote service worker to the + * commpair, it receives content from the fast reload thread. */ + void* service_event; + /** if the event that listens on the remote service worker has + * been added to the comm base. */ + int service_event_is_added; + /** the remote control connection to print output to. */ + struct remote_stream remote; + /** the worker that the service_event is added in */ + struct worker* worker; + /** the comm point for the client connection, the remote control + * client. */ + struct comm_point* client_cp; }; /** @@ -223,8 +237,12 @@ int ssl_read_line(RES* ssl, char* buf, size_t max); * Start fast reload thread * @param ssl: the RES connection to print to. * @param worker: the remote servicing worker. + * @param s: the rc_state that is servicing the remote control connection to + * the remote control client. It needs to be moved away to stay connected + * while the fast reload is running. */ -void fast_reload_thread_start(RES* ssl, struct worker* worker); +void fast_reload_thread_start(RES* ssl, struct worker* worker, + struct rc_state* s); /** * Stop fast reload thread @@ -232,4 +250,11 @@ void fast_reload_thread_start(RES* ssl, struct worker* worker); */ void fast_reload_thread_stop(struct fast_reload_thread* fast_reload_thread); +/** fast reload thread commands to remote service thread event callback */ +void fast_reload_service_cb(int fd, short bits, void* arg); + +/** fast reload callback for the remote control client connection */ +int fast_reload_client_callback(struct comm_point* c, void* arg, int err, + struct comm_reply* rep); + #endif /* DAEMON_REMOTE_H */ diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 0e1c40393..412293f9a 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -1072,3 +1072,17 @@ void dtio_mainfdcallback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), log_assert(0); } #endif + +void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), + void* ATTR_UNUSED(arg)) +{ + log_assert(0); +} + +int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c), + void* ATTR_UNUSED(arg), int ATTR_UNUSED(error), + struct comm_reply* ATTR_UNUSED(repinfo)) +{ + log_assert(0); + return 0; +} diff --git a/smallapp/worker_cb.c b/smallapp/worker_cb.c index c68981735..5987e7622 100644 --- a/smallapp/worker_cb.c +++ b/smallapp/worker_cb.c @@ -255,3 +255,17 @@ void dtio_mainfdcallback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), log_assert(0); } #endif + +void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), + void* ATTR_UNUSED(arg)) +{ + log_assert(0); +} + +int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c), + void* ATTR_UNUSED(arg), int ATTR_UNUSED(error), + struct comm_reply* ATTR_UNUSED(repinfo)) +{ + log_assert(0); + return 0; +} diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 13970c377..45911fe29 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1906,4 +1906,24 @@ void http2_stream_add_meshstate(struct http2_stream* ATTR_UNUSED(h2_stream), { } +void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(event), + void* ATTR_UNUSED(arg)) +{ + log_assert(0); +} + +void fast_reload_thread_stop( + struct fast_reload_thread* ATTR_UNUSED(fast_reload_thread)) +{ + /* nothing */ +} + +int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c), + void* ATTR_UNUSED(arg), int ATTR_UNUSED(error), + struct comm_reply* ATTR_UNUSED(repinfo)) +{ + log_assert(0); + return 0; +} + /*********** End of Dummy routines ***********/ diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index 43d38dc37..2a2b89dba 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -73,6 +73,7 @@ #include "libunbound/worker.h" #include "util/tube.h" #include "util/config_file.h" +#include "daemon/remote.h" #ifdef UB_ON_WINDOWS #include "winrc/win_svc.h" #endif @@ -120,6 +121,7 @@ fptr_whitelist_comm_point_raw(comm_point_callback_type *fptr) else if(fptr == &tube_handle_write) return 1; else if(fptr == &remote_accept_callback) return 1; else if(fptr == &remote_control_callback) return 1; + else if(fptr == &fast_reload_client_callback) return 1; return 0; } @@ -180,6 +182,7 @@ fptr_whitelist_event(void (*fptr)(int, short, void *)) else if(fptr == &tube_handle_signal) return 1; else if(fptr == &comm_base_handle_slow_accept) return 1; else if(fptr == &comm_point_http_handle_callback) return 1; + else if(fptr == &fast_reload_service_cb) return 1; #ifdef USE_DNSTAP else if(fptr == &dtio_output_cb) return 1; else if(fptr == &dtio_cmd_cb) return 1; From bc0c9a421e387259e2dfb79702a4a6d058481eeb Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 3 Nov 2023 17:16:43 +0100 Subject: [PATCH 004/125] - fast-reload, fix compile for unbound-dnstap-socket. --- dnstap/unbound-dnstap-socket.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dnstap/unbound-dnstap-socket.c b/dnstap/unbound-dnstap-socket.c index 04fda74b8..0e2378837 100644 --- a/dnstap/unbound-dnstap-socket.c +++ b/dnstap/unbound-dnstap-socket.c @@ -1572,3 +1572,17 @@ void remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg)) { log_assert(0); } + +void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), + void* ATTR_UNUSED(arg)) +{ + log_assert(0); +} + +int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c), + void* ATTR_UNUSED(arg), int ATTR_UNUSED(error), + struct comm_reply* ATTR_UNUSED(repinfo)) +{ + log_assert(0); + return 0; +} From 6b00fd4cff6b4906eba6adb8354c675475711146 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 6 Nov 2023 11:17:38 +0100 Subject: [PATCH 005/125] - fast-reload, set nonblocking communication to keep the server thread responding to DNS requests. --- daemon/remote.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index fdb8e1641..abc95e17b 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3670,6 +3670,13 @@ fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s) #ifndef THREADS_DISABLED /* Setup command listener in remote servicing thread */ + /* The listener has to be nonblocking, so the the remote servicing + * thread can continue to service DNS queries, the fast reload + * thread is going to read the config from disk and apply it. */ + /* The commpair[1] element can stay blocking, it is used by the + * fast reload thread to communicate back. The thread needs to wait + * at these times, when it has to check briefly it can use poll. */ + fd_set_nonblock(worker->daemon->fast_reload_thread->commpair[0]); worker->daemon->fast_reload_thread->service_event = ub_event_new( comm_base_internal(worker->base), worker->daemon->fast_reload_thread->commpair[0], @@ -3694,6 +3701,10 @@ fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s) log_assert(s); state_list_remove_elem(&s->rc->busy_list, s->c); s->rc->active --; + /* Set the comm point file descriptor to nonblocking. So that + * printout to the remote control client does not block the + * server thread from servicing DNS queries. */ + fd_set_nonblock(s->c->fd); worker->daemon->fast_reload_thread->client_cp = s->c; worker->daemon->fast_reload_thread->client_cp->callback = fast_reload_client_callback; From 2c0f622623fecf4d411bbaac6ef995611b9b4929 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 6 Nov 2023 15:23:47 +0100 Subject: [PATCH 006/125] - fast-reload, poll routine to test for readiness, timeout fails connection. --- daemon/remote.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index abc95e17b..22128d2a5 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -99,6 +99,9 @@ #ifdef HAVE_NETDB_H #include #endif +#ifdef HAVE_POLL_H +#include +#endif /* just for portability */ #ifdef SQ @@ -3376,6 +3379,76 @@ int remote_control_callback(struct comm_point* c, void* arg, int err, return 0; } +#ifdef USE_WINSOCK +/** + * This routine polls a socket for readiness. + * @param fd: file descriptor, -1 uses no fd for a timer only. + * @param timeout: time in msec to wait. 0 means nonblocking test, + * -1 waits blocking for events. + * @param pollin: check for input event. + * @param pollout: check for output event. + * @param event: output variable, set to true if the event happens. + * It is false if there was an error or timeout. + * @return false is system call failure, also logged. + */ +static int +sock_poll_timeout(int fd, int timeout, int pollin, int pollout, int* event) +{ + /* Loop if the system call returns an errno to do so, like EINTR. */ + while(1) { + struct pollfd p, *fds; + int nfds, ret; + if(fd == -1) { + fds = NULL; + nfds = 0; + } else { + fds = &p; + nfds = 1; + memset(&p, 0, sizeof(p)); + p.fd = fd; + p.events = POLLERR | POLLHUP; + if(pollin) + p.events |= POLLIN; + if(pollout) + p.events |= POLLOUT; + } +#ifndef USE_WINSOCK + ret = poll(fds, nfds, timeout); +#else + ret = WSAPoll(fds, nfds, timeout); +#endif + if(ret == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) + continue; /* Try again. */ + log_err("poll: %s", sock_strerror(errno)); + if(event) + *event = 0; + return 0; + } else if(ret == 0) { + /* Timeout */ + if(event) + *event = 0; + return 1; + } + break; + } + if(event) + *event = 1; + return 1; +} +#endif /* USE_WINSOCK */ + #ifndef THREADS_DISABLED /** fast reload thread. the thread main function */ static void* fast_reload_thread_main(void* arg) @@ -3406,7 +3479,7 @@ create_socketpair(int* pair, struct ub_randstate* rand) uint8_t localhost[] = {127, 0, 0, 1}; uint8_t nonce[16], recvnonce[16]; size_t i; - int lst; + int lst, pollin_event; ssize_t ret; pair[0] = -1; pair[1] = -1; @@ -3465,6 +3538,21 @@ create_socketpair(int* pair, struct ub_randstate* rand) pair[1] = -1; return 0; } + if(!sock_poll_timeout(lst, 200, 1, 0, &pollin_event)) { + log_err("create socketpair: poll for accept failed: %s", + sock_strerror(errno)); + sock_close(lst); + sock_close(pair[1]); + pair[1] = -1; + return 0; + } + if(!pollin_event) { + log_err("create socketpair: poll timeout for accept"); + sock_close(lst); + sock_close(pair[1]); + pair[1] = -1; + return 0; + } accaddrlen = (socklen_t)sizeof(accaddr); pair[0] = accept(lst, &accaddr, &accaddrlen); if(pair[0] == -1) { @@ -3516,7 +3604,7 @@ create_socketpair(int* pair, struct ub_randstate* rand) } if(connaddr.sin_family != AF_INET || memcmp(localhost, &connaddr.sin_addr, 4) != 0) { - log_err("create socketpair: getsockname connectedadd returned wrong address"); + log_err("create socketpair: getsockname connectedaddr returned wrong address"); sock_close(lst); sock_close(pair[0]); sock_close(pair[1]); @@ -3552,6 +3640,23 @@ create_socketpair(int* pair, struct ub_randstate* rand) return 0; } + if(!sock_poll_timeout(pair[0], 200, 1, 0, &pollin_event)) { + log_err("create socketpair: poll failed: %s", + sock_strerror(errno)); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } + if(!pollin_event) { + log_err("create socketpair: poll timeout for recv"); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } ret = recv(pair[0], recvnonce, sizeof(nonce), 0); if(ret == -1) { log_err("create socketpair: recv: %s", sock_strerror(errno)); From 3e17db1f870e9a9b5851aafdc4b525d9f34cb99d Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 6 Nov 2023 16:04:46 +0100 Subject: [PATCH 007/125] - fast-reload, detect loop in sock_poll_timeout routine. --- daemon/remote.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index 22128d2a5..5f9d9064f 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3394,10 +3394,17 @@ int remote_control_callback(struct comm_point* c, void* arg, int err, static int sock_poll_timeout(int fd, int timeout, int pollin, int pollout, int* event) { + int loopcount = 0; /* Loop if the system call returns an errno to do so, like EINTR. */ while(1) { struct pollfd p, *fds; int nfds, ret; + if(++loopcount > 200) { + log_err("sock_poll_timeout: loop"); + if(event) + *event = 0; + return 0; + } if(fd == -1) { fds = NULL; nfds = 0; From 43ff8e112f6a560eb2e1a2bd39ae89209068b7ed Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 7 Nov 2023 15:07:02 +0100 Subject: [PATCH 008/125] - fast-reload, send done and exited notification. --- daemon/remote.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++- daemon/remote.h | 22 +++++++ 2 files changed, 171 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 5f9d9064f..65183878f 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3379,7 +3379,6 @@ int remote_control_callback(struct comm_point* c, void* arg, int err, return 0; } -#ifdef USE_WINSOCK /** * This routine polls a socket for readiness. * @param fd: file descriptor, -1 uses no fd for a timer only. @@ -3454,7 +3453,141 @@ sock_poll_timeout(int fd, int timeout, int pollin, int pollout, int* event) *event = 1; return 1; } -#endif /* USE_WINSOCK */ + +/** fast reload convert fast reload notification status to string */ +static const char* +fr_notification_to_string(enum fast_reload_notification status) +{ + switch(status) { + case fast_reload_notification_none: + return "none"; + case fast_reload_notification_done: + return "done"; + case fast_reload_notification_done_error: + return "done_error"; + case fast_reload_notification_exit: + return "exit"; + case fast_reload_notification_exited: + return "exited"; + case fast_reload_notification_printout: + return "printout"; + default: + break; + } + return "unknown"; +} + +/** fast reload, poll for notification incoming. True if quit */ +static int +fr_poll_for_quit(struct fast_reload_thread* fr) +{ + int inevent, loopexit = 0; + uint32_t cmd; + ssize_t ret; + + /* Is there data? */ + if(!sock_poll_timeout(fr->commpair[1], 0, 1, 0, &inevent)) { + log_err("fr_poll_for_quit: poll failed"); + return 0; + } + if(!inevent) + return 0; + + /* Read the data */ + while(1) { + if(++loopexit > 200) { + log_err("fr_poll_for_quit: recv loops %s", + sock_strerror(errno)); + return 0; + } + ret = recv(fr->commpair[1], &cmd, sizeof(cmd), 0); + if(ret == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) + continue; /* Try again. */ + log_err("fr_poll_for_quit: recv: %s", + sock_strerror(errno)); + return 0; + } if(ret != sizeof(cmd)) { + log_err("fr_poll_for_quit: partial read"); + return 0; + } + break; + } + if(cmd == fast_reload_notification_exit) { + fr->need_to_quit = 1; + verbose(VERB_ALGO, "fast reload: exit notification received"); + return 1; + } + log_err("fr_poll_for_quit: unknown notification status received: %d %s", + cmd, fr_notification_to_string(cmd)); + return 0; +} + +/** fast reload thread. Send notification from the fast reload thread */ +static void +fr_send_notification(struct fast_reload_thread* fr, + enum fast_reload_notification status) +{ + int outevent, loopexit = 0; + uint32_t cmd; + ssize_t ret; + verbose(VERB_ALGO, "fast reload: send notification %s", + fr_notification_to_string(status)); + /* Make a blocking attempt to send. But meanwhile stay responsive, + * once in a while for quit commands. In case the server has to quit. */ + /* see if there is incoming quit signals */ + if(fr_poll_for_quit(fr)) + return; + while(1) { + if(++loopexit > 200) { + log_err("fast reload: could not send notification"); + return; + } + if(!sock_poll_timeout(fr->commpair[1], 200, 0, 1, &outevent)) { + log_err("fast reload: poll failed"); + return; + } + if(fr_poll_for_quit(fr)) + return; + if(!outevent) + continue; + cmd = status; + ret = send(fr->commpair[1], &cmd, sizeof(cmd), 0); + if(ret == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) + continue; /* Try again. */ + log_err("fast reload send notification: send: %s", + sock_strerror(errno)); + return; + } else if(ret != sizeof(cmd)) { + log_err("fast reload send notification: partial send"); + return; + } + break; + } +} #ifndef THREADS_DISABLED /** fast reload thread. the thread main function */ @@ -3465,7 +3598,21 @@ static void* fast_reload_thread_main(void* arg) verbose(VERB_ALGO, "start fast reload thread"); + if(fr_poll_for_quit(fast_reload_thread)) + goto done; + verbose(VERB_ALGO, "stop fast reload thread"); + /* If this is not an exit due to quit earlier, send regular done. */ + if(!fast_reload_thread->need_to_quit) + fr_send_notification(fast_reload_thread, + fast_reload_notification_done); + /* If during the fast_reload_notification_done send, + * fast_reload_notification_exit was received, ack it. If the + * thread is exiting due to quit received earlier, also ack it.*/ +done: + if(fast_reload_thread->need_to_quit) + fr_send_notification(fast_reload_thread, + fast_reload_notification_exited); return NULL; } #endif /* !THREADS_DISABLED */ diff --git a/daemon/remote.h b/daemon/remote.h index c257916a3..c120643dc 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -119,6 +119,26 @@ struct remote_stream { }; typedef struct remote_stream RES; +/** + * Notification status. This is exchanged between the fast reload thread + * and the server thread, over the commpair sockets. + */ +enum fast_reload_notification { + /** nothing, not used */ + fast_reload_notification_none = 0, + /** the fast reload thread is done */ + fast_reload_notification_done = 1, + /** the fast reload thread is done but with an error, it failed */ + fast_reload_notification_done_error = 2, + /** the fast reload thread is told to exit by the server thread. + * Sent on server quit while the reload is running. */ + fast_reload_notification_exit = 3, + /** the fast reload thread has exited, after being told to exit */ + fast_reload_notification_exited = 4, + /** the fast reload thread has information to print out */ + fast_reload_notification_printout = 5 +}; + /** * Fast reload thread structure */ @@ -132,6 +152,8 @@ struct fast_reload_thread { ub_thread_type tid; /** if the io processing has started */ int started; + /** if the thread has to quit */ + int need_to_quit; /** the event that listens on the remote service worker to the * commpair, it receives content from the fast reload thread. */ From dab478d7771ebb67a22db2a707ffa13065958a52 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 30 Nov 2023 15:43:25 +0100 Subject: [PATCH 009/125] - fast-reload, defines for constants in ipc. --- daemon/remote.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 65183878f..33d5a6359 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -111,6 +111,11 @@ /** what to put on statistics lines between var and value, ": " or "=" */ #define SQ "=" +/** What number of loop iterations is too much for ipc retries */ +#define IPC_LOOP_MAX 200 +/** Timeout in msec for ipc socket poll. */ +#define IPC_NOTIFICATION_WAIT 20000 + static int remote_setup_ctx(struct daemon_remote* rc, struct config_file* cfg) { @@ -3398,7 +3403,7 @@ sock_poll_timeout(int fd, int timeout, int pollin, int pollout, int* event) while(1) { struct pollfd p, *fds; int nfds, ret; - if(++loopcount > 200) { + if(++loopcount > IPC_LOOP_MAX) { log_err("sock_poll_timeout: loop"); if(event) *event = 0; @@ -3495,7 +3500,7 @@ fr_poll_for_quit(struct fast_reload_thread* fr) /* Read the data */ while(1) { - if(++loopexit > 200) { + if(++loopexit > IPC_LOOP_MAX) { log_err("fr_poll_for_quit: recv loops %s", sock_strerror(errno)); return 0; @@ -3550,11 +3555,13 @@ fr_send_notification(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return; while(1) { - if(++loopexit > 200) { + if(++loopexit > IPC_LOOP_MAX) { log_err("fast reload: could not send notification"); return; } - if(!sock_poll_timeout(fr->commpair[1], 200, 0, 1, &outevent)) { + /* wait for socket to become writable */ + if(!sock_poll_timeout(fr->commpair[1], IPC_NOTIFICATION_WAIT, + 0, 1, &outevent)) { log_err("fast reload: poll failed"); return; } @@ -3634,6 +3641,7 @@ create_socketpair(int* pair, struct ub_randstate* rand) uint8_t nonce[16], recvnonce[16]; size_t i; int lst, pollin_event; + int connect_poll_timeout = 200; /* msec to wait for connection */ ssize_t ret; pair[0] = -1; pair[1] = -1; @@ -3692,7 +3700,7 @@ create_socketpair(int* pair, struct ub_randstate* rand) pair[1] = -1; return 0; } - if(!sock_poll_timeout(lst, 200, 1, 0, &pollin_event)) { + if(!sock_poll_timeout(lst, connect_poll_timeout, 1, 0, &pollin_event)) { log_err("create socketpair: poll for accept failed: %s", sock_strerror(errno)); sock_close(lst); @@ -3794,7 +3802,7 @@ create_socketpair(int* pair, struct ub_randstate* rand) return 0; } - if(!sock_poll_timeout(pair[0], 200, 1, 0, &pollin_event)) { + if(!sock_poll_timeout(pair[0], connect_poll_timeout, 1, 0, &pollin_event)) { log_err("create socketpair: poll failed: %s", sock_strerror(errno)); sock_close(pair[0]); From f2c0e22be72686867b888639eac86b17edc5dff9 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 30 Nov 2023 16:06:38 +0100 Subject: [PATCH 010/125] - fast-reload, ipc socket recv and send resists partial reads and writes and can continue byte by byte. Also it can continue after an interrupt. --- daemon/remote.c | 151 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 101 insertions(+), 50 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 33d5a6359..a98f34784 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3486,7 +3486,7 @@ fr_notification_to_string(enum fast_reload_notification status) static int fr_poll_for_quit(struct fast_reload_thread* fr) { - int inevent, loopexit = 0; + int inevent, loopexit = 0, bcount = 0; uint32_t cmd; ssize_t ret; @@ -3505,7 +3505,8 @@ fr_poll_for_quit(struct fast_reload_thread* fr) sock_strerror(errno)); return 0; } - ret = recv(fr->commpair[1], &cmd, sizeof(cmd), 0); + ret = recv(fr->commpair[1], ((char*)&cmd)+bcount, + sizeof(cmd)-bcount, 0); if(ret == -1) { if( #ifndef USE_WINSOCK @@ -3523,9 +3524,10 @@ fr_poll_for_quit(struct fast_reload_thread* fr) log_err("fr_poll_for_quit: recv: %s", sock_strerror(errno)); return 0; - } if(ret != sizeof(cmd)) { - log_err("fr_poll_for_quit: partial read"); - return 0; + } else if(ret+(ssize_t)bcount != sizeof(cmd)) { + bcount += ret; + if((size_t)bcount < sizeof(cmd)) + continue; } break; } @@ -3544,7 +3546,7 @@ static void fr_send_notification(struct fast_reload_thread* fr, enum fast_reload_notification status) { - int outevent, loopexit = 0; + int outevent, loopexit = 0, bcount = 0; uint32_t cmd; ssize_t ret; verbose(VERB_ALGO, "fast reload: send notification %s", @@ -3554,6 +3556,7 @@ fr_send_notification(struct fast_reload_thread* fr, /* see if there is incoming quit signals */ if(fr_poll_for_quit(fr)) return; + cmd = status; while(1) { if(++loopexit > IPC_LOOP_MAX) { log_err("fast reload: could not send notification"); @@ -3569,8 +3572,8 @@ fr_send_notification(struct fast_reload_thread* fr, return; if(!outevent) continue; - cmd = status; - ret = send(fr->commpair[1], &cmd, sizeof(cmd), 0); + ret = send(fr->commpair[1], ((char*)&cmd)+bcount, + sizeof(cmd)-bcount, 0); if(ret == -1) { if( #ifndef USE_WINSOCK @@ -3588,9 +3591,10 @@ fr_send_notification(struct fast_reload_thread* fr, log_err("fast reload send notification: send: %s", sock_strerror(errno)); return; - } else if(ret != sizeof(cmd)) { - log_err("fast reload send notification: partial send"); - return; + } else if(ret+(ssize_t)bcount != sizeof(cmd)) { + bcount += ret; + if((size_t)bcount < sizeof(cmd)) + continue; } break; } @@ -3640,7 +3644,7 @@ create_socketpair(int* pair, struct ub_randstate* rand) uint8_t localhost[] = {127, 0, 0, 1}; uint8_t nonce[16], recvnonce[16]; size_t i; - int lst, pollin_event; + int lst, pollin_event, bcount, loopcount; int connect_poll_timeout = 200; /* msec to wait for connection */ ssize_t ret; pair[0] = -1; @@ -3785,21 +3789,44 @@ create_socketpair(int* pair, struct ub_randstate* rand) } sock_close(lst); - ret = send(pair[1], nonce, sizeof(nonce), 0); - if(ret == -1) { - log_err("create socketpair: send: %s", sock_strerror(errno)); - sock_close(pair[0]); - sock_close(pair[1]); - pair[0] = -1; - pair[1] = -1; - return 0; - } else if(ret != sizeof(nonce)) { - log_err("create socketpair: send was truncated"); - sock_close(pair[0]); - sock_close(pair[1]); - pair[0] = -1; - pair[1] = -1; - return 0; + loopcount = 0; + bcount = 0; + while(1) { + if(++loopcount > IPC_LOOP_MAX) { + log_err("create socketpair: send failed due to loop"); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } + ret = send(pair[1], nonce+bcount, sizeof(nonce)-bcount, 0); + if(ret == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) + continue; /* Try again. */ + log_err("create socketpair: send: %s", sock_strerror(errno)); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } else if(ret+(ssize_t)bcount != sizeof(nonce)) { + bcount += ret; + if((size_t)bcount < sizeof(nonce)) + continue; + } + break; } if(!sock_poll_timeout(pair[0], connect_poll_timeout, 1, 0, &pollin_event)) { @@ -3819,30 +3846,54 @@ create_socketpair(int* pair, struct ub_randstate* rand) pair[1] = -1; return 0; } - ret = recv(pair[0], recvnonce, sizeof(nonce), 0); - if(ret == -1) { - log_err("create socketpair: recv: %s", sock_strerror(errno)); - sock_close(pair[0]); - sock_close(pair[1]); - pair[0] = -1; - pair[1] = -1; - return 0; - } else if(ret == 0) { - log_err("create socketpair: stream closed"); - sock_close(pair[0]); - sock_close(pair[1]); - pair[0] = -1; - pair[1] = -1; - return 0; - } - if(ret != sizeof(nonce)) { - log_err("create socketpair: recv did not read all nonce bytes"); - sock_close(pair[0]); - sock_close(pair[1]); - pair[0] = -1; - pair[1] = -1; - return 0; + + loopcount = 0; + bcount = 0; + while(1) { + if(++loopcount > IPC_LOOP_MAX) { + log_err("create socketpair: recv failed due to loop"); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } + ret = recv(pair[0], recvnonce+bcount, sizeof(nonce)-bcount, 0); + if(ret == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) + continue; /* Try again. */ + log_err("create socketpair: recv: %s", sock_strerror(errno)); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } else if(ret == 0) { + log_err("create socketpair: stream closed"); + sock_close(pair[0]); + sock_close(pair[1]); + pair[0] = -1; + pair[1] = -1; + return 0; + } else if(ret+(ssize_t)bcount != sizeof(nonce)) { + bcount += ret; + if((size_t)bcount < sizeof(nonce)) + continue; + } + break; } + if(memcmp(nonce, recvnonce, sizeof(nonce)) != 0) { log_err("create socketpair: recv wrong nonce"); sock_close(pair[0]); From fcfb3fe5b5dc5b4a67d3176ca827fd7180840ef7 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 30 Nov 2023 17:40:53 +0100 Subject: [PATCH 011/125] - fast-reload, send exit command to thread when done. --- daemon/remote.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++- daemon/remote.h | 5 ++ 2 files changed, 179 insertions(+), 3 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index a98f34784..f3ca79625 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -114,7 +114,7 @@ /** What number of loop iterations is too much for ipc retries */ #define IPC_LOOP_MAX 200 /** Timeout in msec for ipc socket poll. */ -#define IPC_NOTIFICATION_WAIT 20000 +#define IPC_NOTIFICATION_WAIT 200 static int remote_setup_ctx(struct daemon_remote* rc, struct config_file* cfg) @@ -3952,12 +3952,113 @@ fast_reload_thread_desetup(struct fast_reload_thread* fast_reload_thread) free(fast_reload_thread); } +/** Fast reload, the main thread handles that the fast reload thread has + * exited. */ +static void +fr_main_perform_done(struct fast_reload_thread* fr) +{ + struct worker* worker = fr->worker; + verbose(VERB_ALGO, "join with fastreload thread"); + ub_thread_join(fr->tid); + verbose(VERB_ALGO, "joined with fastreload thread"); + fast_reload_thread_desetup(fr); + worker->daemon->fast_reload_thread = NULL; +} + +/** Fast reload, perform the command received from the fast reload thread */ +static void +fr_main_perform_cmd(struct fast_reload_thread* fr, + enum fast_reload_notification status) +{ + verbose(VERB_ALGO, "main perform fast reload status: %s", + fr_notification_to_string(status)); + if(status == fast_reload_notification_printout) { + } else if(status == fast_reload_notification_done || + status == fast_reload_notification_done_error || + status == fast_reload_notification_exited) { + fr_main_perform_done(fr); + } else { + log_err("main received unknown status from fast reload: %d %s", + (int)status, fr_notification_to_string(status)); + } +} + +/** Fast reload, handle command from fast reload to the main thread. */ +static void +fr_main_handle_cmd(struct fast_reload_thread* fr) +{ + enum fast_reload_notification status; + ssize_t ret; + ret = recv(fr->commpair[0], + ((char*)&fr->service_read_cmd)+fr->service_read_cmd_count, + sizeof(fr->service_read_cmd)-fr->service_read_cmd_count, 0); + if(ret == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) + return; /* Continue later. */ + log_err("read cmd from fast reload thread, recv: %s", + sock_strerror(errno)); + return; + } else if(ret == 0) { + verbose(VERB_ALGO, "closed connection from fast reload thread"); + fr->service_read_cmd_count = 0; + /* handle this like an error */ + fr->service_read_cmd = fast_reload_notification_done_error; + } else if(ret + (ssize_t)fr->service_read_cmd_count < + (ssize_t)sizeof(fr->service_read_cmd)) { + fr->service_read_cmd_count += ret; + /* Continue later. */ + return; + } + status = fr->service_read_cmd; + fr->service_read_cmd = 0; + fr->service_read_cmd_count = 0; + fr_main_perform_cmd(fr, status); +} + +/** Fast reload, poll for and handle cmd from fast reload thread. */ +static void +fr_check_cmd_from_thread(struct fast_reload_thread* fr) +{ + int inevent = 0; + struct worker* worker = fr->worker; + /* Stop in case the thread has exited, or there is no read event. */ + while(worker->daemon->fast_reload_thread) { + if(!sock_poll_timeout(fr->commpair[0], 0, 1, 0, &inevent)) { + log_err("check for cmd from fast reload thread: " + "poll failed"); + return; + } + if(!inevent) + return; + fr_main_handle_cmd(fr); + } +} + void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg) { struct fast_reload_thread* fast_reload_thread = (struct fast_reload_thread*)arg; - (void)fast_reload_thread; + struct worker* worker = fast_reload_thread->worker; + + /* Read and handle the command */ + fr_main_handle_cmd(fast_reload_thread); + if(worker->daemon->fast_reload_thread != NULL) { + /* If not exited, see if there are more pending statuses + * from the fast reload thread. */ + fr_check_cmd_from_thread(fast_reload_thread); + } } int fast_reload_client_callback(struct comm_point* c, void* arg, int err, @@ -3971,6 +4072,70 @@ int fast_reload_client_callback(struct comm_point* c, void* arg, int err, return 0; } +/** + * Fast reload thread, send a command to the thread. Blocking on timeout. + * It handles received input from the thread, if any is received. + */ +static void +fr_send_cmd_to(struct fast_reload_thread* fr, + enum fast_reload_notification status) +{ + int outevent, loopexit = 0, bcount = 0; + uint32_t cmd; + ssize_t ret; + verbose(VERB_ALGO, "send notification to fast reload thread: %s", + fr_notification_to_string(status)); + cmd = status; + while(1) { + if(++loopexit > IPC_LOOP_MAX) { + log_err("send notification to fast reload: could not send notification: loop"); + return; + } + fr_check_cmd_from_thread(fr); + /* wait for socket to become writable */ + if(!sock_poll_timeout(fr->commpair[0], IPC_NOTIFICATION_WAIT, + 0, 1, &outevent)) { + log_err("send notification to fast reload: poll failed"); + return; + } + if(!outevent) + continue; + ret = send(fr->commpair[0], ((char*)&cmd)+bcount, + sizeof(cmd)-bcount, 0); + if(ret == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) + continue; /* Try again. */ + log_err("send notification to fast reload: send: %s", + sock_strerror(errno)); + return; + } else if(ret+(ssize_t)bcount != sizeof(cmd)) { + bcount += ret; + if((size_t)bcount < sizeof(cmd)) + continue; + } + break; + } +} + +/** fast reload thread, send stop command to the thread, from the main thread. + */ +static void +fr_send_stop(struct fast_reload_thread* fr) +{ + fr_send_cmd_to(fr, fast_reload_notification_exit); +} + void fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s) { @@ -4040,7 +4205,13 @@ fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s) void fast_reload_thread_stop(struct fast_reload_thread* fast_reload_thread) { + struct worker* worker = fast_reload_thread->worker; if(!fast_reload_thread) return; - fast_reload_thread_desetup(fast_reload_thread); + fr_send_stop(fast_reload_thread); + if(worker->daemon->fast_reload_thread != NULL) { + /* If it did not exit yet, join with the thread now. It is + * going to exit because the exit command is sent to it. */ + fr_main_perform_done(fast_reload_thread); + } } diff --git a/daemon/remote.h b/daemon/remote.h index c120643dc..290651a37 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -161,6 +161,11 @@ struct fast_reload_thread { /** if the event that listens on the remote service worker has * been added to the comm base. */ int service_event_is_added; + /** the service event can read a cmd, nonblocking, so it can + * save the partial read cmd here */ + uint32_t service_read_cmd; + /** the number of bytes in service_read_cmd */ + int service_read_cmd_count; /** the remote control connection to print output to. */ struct remote_stream remote; /** the worker that the service_event is added in */ From 32e98b4c96f1f82ccf8ad21290b640c0a45e5d19 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 1 Dec 2023 17:07:38 +0100 Subject: [PATCH 012/125] - fast-reload, output strings for client on string list. --- daemon/remote.c | 178 ++++++++++++++++++++++++++++++++++++++++++++---- daemon/remote.h | 11 +++ 2 files changed, 175 insertions(+), 14 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index f3ca79625..f6cac05aa 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3490,6 +3490,8 @@ fr_poll_for_quit(struct fast_reload_thread* fr) uint32_t cmd; ssize_t ret; + if(fr->need_to_quit) + return 1; /* Is there data? */ if(!sock_poll_timeout(fr->commpair[1], 0, 1, 0, &inevent)) { log_err("fr_poll_for_quit: poll failed"); @@ -3601,6 +3603,53 @@ fr_send_notification(struct fast_reload_thread* fr, } #ifndef THREADS_DISABLED +/** fast reload thread queue up text string for output */ +static int +fr_output_text(struct fast_reload_thread* fr, const char* msg) +{ + char* item = strdup(msg); + if(!item) { + log_err("fast reload output text: strdup out of memory"); + return 0; + } + lock_basic_lock(&fr->fr_output_lock); + if(!cfg_strlist_append(fr->fr_output, item)) { + lock_basic_unlock(&fr->fr_output_lock); + /* The item is freed by cfg_strlist_append on failure. */ + log_err("fast reload output text: append out of memory"); + return 0; + } + lock_basic_unlock(&fr->fr_output_lock); + return 1; +} + +/** fast reload thread output vmsg function */ +static int +fr_output_vmsg(struct fast_reload_thread* fr, const char* format, va_list args) +{ + char msg[1024]; + vsnprintf(msg, sizeof(msg), format, args); + return fr_output_text(fr, msg); +} + +/** fast reload thread printout function, with printf arguments */ +static int fr_output_printf(struct fast_reload_thread* fr, + const char* format, ...) ATTR_FORMAT(printf, 2, 3); + +/** fast reload thread printout function, prints to list and signals + * the remote control thread to move that to get written to the socket + * of the remote control connection. */ +static int +fr_output_printf(struct fast_reload_thread* fr, const char* format, ...) +{ + va_list args; + int ret; + va_start(args, format); + ret = fr_output_vmsg(fr, format, args); + va_end(args); + return ret; +} + /** fast reload thread. the thread main function */ static void* fast_reload_thread_main(void* arg) { @@ -3608,7 +3657,14 @@ static void* fast_reload_thread_main(void* arg) log_thread_set(&fast_reload_thread->threadnum); verbose(VERB_ALGO, "start fast reload thread"); + if(fr_poll_for_quit(fast_reload_thread)) + goto done; + /* print output to the client */ + if(!fr_output_printf(fast_reload_thread, "start fast reload")) + goto done_error; + fr_send_notification(fast_reload_thread, + fast_reload_notification_printout); if(fr_poll_for_quit(fast_reload_thread)) goto done; @@ -3625,6 +3681,11 @@ static void* fast_reload_thread_main(void* arg) fr_send_notification(fast_reload_thread, fast_reload_notification_exited); return NULL; +done_error: + verbose(VERB_ALGO, "stop fast reload thread with done_error"); + fr_send_notification(fast_reload_thread, + fast_reload_notification_done_error); + return NULL; } #endif /* !THREADS_DISABLED */ @@ -3910,22 +3971,46 @@ create_socketpair(int* pair, struct ub_randstate* rand) static int fast_reload_thread_setup(struct worker* worker) { + struct fast_reload_thread* fr; int numworkers = worker->daemon->num; worker->daemon->fast_reload_thread = (struct fast_reload_thread*) calloc(1, sizeof(*worker->daemon->fast_reload_thread)); if(!worker->daemon->fast_reload_thread) return 0; + fr = worker->daemon->fast_reload_thread; /* The thread id printed in logs, numworker+1 is the dnstap thread. * This is numworkers+2. */ - worker->daemon->fast_reload_thread->threadnum = numworkers+2; - worker->daemon->fast_reload_thread->commpair[0] = -1; - worker->daemon->fast_reload_thread->commpair[1] = -1; - if(!create_socketpair(worker->daemon->fast_reload_thread->commpair, - worker->daemon->rand)) { - free(worker->daemon->fast_reload_thread); + fr->threadnum = numworkers+2; + fr->commpair[0] = -1; + fr->commpair[1] = -1; + if(!create_socketpair(fr->commpair, worker->daemon->rand)) { + free(fr); + worker->daemon->fast_reload_thread = NULL; + return 0; + } + fr->worker = worker; + fr->fr_output = (struct config_strlist_head*)calloc(1, + sizeof(*fr->fr_output)); + if(!fr->fr_output) { + sock_close(fr->commpair[0]); + sock_close(fr->commpair[1]); + free(fr); + worker->daemon->fast_reload_thread = NULL; + return 0; + } + lock_basic_init(&fr->fr_output_lock); + lock_protect(&fr->fr_output_lock, fr->fr_output, + sizeof(*fr->fr_output)); + fr->to_print = (struct config_strlist_head*)calloc(1, + sizeof(*fr->to_print)); + if(!fr->fr_output) { + free(fr->fr_output); + sock_close(fr->commpair[0]); + sock_close(fr->commpair[1]); + free(fr); + worker->daemon->fast_reload_thread = NULL; return 0; } - worker->daemon->fast_reload_thread->worker = worker; return 1; } @@ -3949,6 +4034,15 @@ fast_reload_thread_desetup(struct fast_reload_thread* fast_reload_thread) SSL_free(fast_reload_thread->remote.ssl); } comm_point_delete(fast_reload_thread->client_cp); + lock_basic_destroy(&fast_reload_thread->fr_output_lock); + if(fast_reload_thread->fr_output) { + config_delstrlist(fast_reload_thread->fr_output->first); + free(fast_reload_thread->fr_output); + } + if(fast_reload_thread->to_print) { + config_delstrlist(fast_reload_thread->to_print->first); + free(fast_reload_thread->to_print); + } free(fast_reload_thread); } @@ -3965,6 +4059,50 @@ fr_main_perform_done(struct fast_reload_thread* fr) worker->daemon->fast_reload_thread = NULL; } +/** Append strlist after strlist */ +static void +cfg_strlist_append_listhead(struct config_strlist_head* list, + struct config_strlist_head* more) +{ + if(list->last) + list->last->next = more->first; + else + list->first = more->first; + list->last = more->last; +} + +/** Fast reload, the remote control thread handles that the fast reload thread + * has output to be printed, on the linked list that is locked. */ +static void +fr_main_perform_printout(struct fast_reload_thread* fr) +{ + struct config_strlist_head out; + + /* Fetch the list of items to be printed */ + lock_basic_lock(&fr->fr_output_lock); + out.first = fr->fr_output->first; + out.last = fr->fr_output->last; + fr->fr_output->first = NULL; + fr->fr_output->last = NULL; + lock_basic_unlock(&fr->fr_output_lock); + + if(!fr->client_cp) { + /* There is no output socket, delete it. */ + config_delstrlist(out.first); + return; + } + + /* Put them on the output list, not locked because the list + * producer and consumer are both owned by the remote control thread, + * it moves the items to the list for printing in the event callback + * for the client_cp. */ + cfg_strlist_append_listhead(fr->to_print, &out); + + /* Set the client_cp to output if not already */ + if(!fr->client_cp->event_added) + comm_point_listen_for_rw(fr->client_cp, 0, 1); +} + /** Fast reload, perform the command received from the fast reload thread */ static void fr_main_perform_cmd(struct fast_reload_thread* fr, @@ -3973,6 +4111,7 @@ fr_main_perform_cmd(struct fast_reload_thread* fr, verbose(VERB_ALGO, "main perform fast reload status: %s", fr_notification_to_string(status)); if(status == fast_reload_notification_printout) { + fr_main_perform_printout(fr); } else if(status == fast_reload_notification_done || status == fast_reload_notification_done_error || status == fast_reload_notification_exited) { @@ -4061,14 +4200,23 @@ void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), } } -int fast_reload_client_callback(struct comm_point* c, void* arg, int err, - struct comm_reply* ATTR_UNUSED(rep)) +int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c), void* arg, + int err, struct comm_reply* ATTR_UNUSED(rep)) { - struct fast_reload_thread* fast_reload_thread = - (struct fast_reload_thread*)arg; - (void)c; - (void)err; - (void)fast_reload_thread; + struct fast_reload_thread* fr = (struct fast_reload_thread*)arg; + if(!fr->client_cp) + return 0; /* the output is closed and deleted */ + if(err != NETEVENT_NOERROR) { + verbose(VERB_ALGO, "fast reload client: error, close it"); + comm_point_delete(fr->client_cp); + fr->client_cp = NULL; + return 0; + } + if(!fr->to_print->first) { + /* done with printing for now */ + comm_point_stop_listening(fr->client_cp); + return 0; + } return 0; } @@ -4194,6 +4342,8 @@ fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s) worker->daemon->fast_reload_thread->client_cp->cb_arg = worker->daemon->fast_reload_thread; s->rc = NULL; /* move away the rc state */ + /* Nothing to print right now, so no need to activate it. */ + comm_point_stop_listening(worker->daemon->fast_reload_thread->client_cp); /* Start fast reload thread */ ub_thread_create(&worker->daemon->fast_reload_thread->tid, diff --git a/daemon/remote.h b/daemon/remote.h index 290651a37..e13592aff 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -56,6 +56,7 @@ struct worker; struct comm_reply; struct comm_point; struct daemon_remote; +struct config_strlist_head; /** number of milliseconds timeout on incoming remote control handshake */ #define REMOTE_CONTROL_TCP_TIMEOUT 120000 @@ -173,6 +174,16 @@ struct fast_reload_thread { /** the comm point for the client connection, the remote control * client. */ struct comm_point* client_cp; + /** lock on fr_output, to stop race when both remote control thread + * and fast reload thread use fr_output list. */ + lock_basic_type fr_output_lock; + /** list of strings, that the fast reload thread produces that have + * to be printed. The remote control thread can pick them up with + * the lock. */ + struct config_strlist_head* fr_output; + /** List of strings that are for printing to the remote control + * client, over the client connection socket client_cp. */ + struct config_strlist_head* to_print; }; /** From c07e941e91b8cc695668fff0badb5955bb809966 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 4 Dec 2023 16:00:16 +0100 Subject: [PATCH 013/125] - fast-reload, add newline to terminal output. --- daemon/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index f6cac05aa..7d7666906 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3661,7 +3661,7 @@ static void* fast_reload_thread_main(void* arg) goto done; /* print output to the client */ - if(!fr_output_printf(fast_reload_thread, "start fast reload")) + if(!fr_output_printf(fast_reload_thread, "start fast reload\n")) goto done_error; fr_send_notification(fast_reload_thread, fast_reload_notification_printout); From 9c6d1020ba53914d4bb52bb7e0a3e061524b75a7 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 4 Dec 2023 17:12:49 +0100 Subject: [PATCH 014/125] - fast-reload, send client string to remote client. --- daemon/remote.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++- daemon/remote.h | 9 +++ 2 files changed, 191 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 7d7666906..607897f08 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4043,6 +4043,7 @@ fast_reload_thread_desetup(struct fast_reload_thread* fast_reload_thread) config_delstrlist(fast_reload_thread->to_print->first); free(fast_reload_thread->to_print); } + free(fast_reload_thread->client_item); free(fast_reload_thread); } @@ -4200,6 +4201,148 @@ void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), } } +#ifdef HAVE_SSL +/** fast reload, send client item over SSL. Returns number of bytes + * printed, 0 on wait later, or -1 on failure. */ +static int +fr_client_send_item_ssl(struct fast_reload_thread* fr) +{ + int r; + ERR_clear_error(); + r = SSL_write(fr->remote.ssl, + fr->client_item+fr->client_byte_count, + fr->client_len - fr->client_byte_count); + if(r <= 0) { + int want = SSL_get_error(fr->remote.ssl, r); + if(want == SSL_ERROR_ZERO_RETURN) { + log_err("fast_reload print to remote client: " + "SSL_write says connection closed."); + return -1; + } else if(want == SSL_ERROR_WANT_READ) { + /* wait for read condition */ + fr->client_cp->ssl_shake_state = comm_ssl_shake_hs_read; + comm_point_listen_for_rw(fr->client_cp, 1, 0); + return 0; + } else if(want == SSL_ERROR_WANT_WRITE) { +#ifdef USE_WINSOCK + ub_winsock_tcp_wouldblock(fr->client_cp->ev->ev, UB_EV_WRITE); +#endif + return 0; /* write more later */ + } else if(want == SSL_ERROR_SYSCALL) { +#ifdef EPIPE + if(errno == EPIPE && verbosity < 2) { + /* silence 'broken pipe' */ + return -1; + } +#endif + if(errno != 0) + log_err("fast_reload print to remote client: " + "SSL_write syscall: %s", + sock_strerror(errno)); + return -1; + } + log_crypto_err_io("fast_reload print to remote client: " + "could not SSL_write", want); + return -1; + } + return r; +} +#endif /* HAVE_SSL */ + +/** fast reload, send client item for fd, returns bytes sent, or 0 for wait + * later, or -1 on failure. */ +static int +fr_client_send_item_fd(struct fast_reload_thread* fr) +{ + int r; + r = (int)send(fr->remote.fd, + fr->client_item+fr->client_byte_count, + fr->client_len - fr->client_byte_count, 0); + if(r == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) { +#ifdef USE_WINSOCK + ub_winsock_tcp_wouldblock(fr->client_cp->ev->ev, UB_EV_WRITE); +#endif + return 0; /* Try again. */ + } + log_err("fast_reload print to remote client: send failed: %s", + sock_strerror(errno)); + return -1; + } + return r; +} + +/** fast reload, send current client item. false on failure or wait later. */ +static int +fr_client_send_item(struct fast_reload_thread* fr) +{ + int r; +#ifdef HAVE_SSL + if(fr->remote.ssl) { + r = fr_client_send_item_ssl(fr); + } else { +#endif + r = fr_client_send_item_fd(fr); +#ifdef HAVE_SSL + } +#endif + if(r == 0) { + /* Wait for later. */ + return 0; + } else if(r == -1) { + /* It failed, close comm point and stop sending. */ + comm_point_delete(fr->client_cp); + fr->client_cp = NULL; + return 0; + } + fr->client_byte_count += r; + if(fr->client_byte_count < fr->client_len) + return 0; /* Print more later. */ + return 1; +} + +/** fast reload, pick up the next item to print */ +static void +fr_client_pickup_next_item(struct fast_reload_thread* fr) +{ + struct config_strlist* item; + /* Pop first off the list. */ + if(!fr->to_print->first) { + fr->client_item = NULL; + fr->client_len = 0; + fr->client_byte_count = 0; + return; + } + item = fr->to_print->first; + if(item->next) { + fr->to_print->first = item->next; + } else { + fr->to_print->first = NULL; + fr->to_print->last = NULL; + } + item->next = NULL; + fr->client_len = 0; + fr->client_byte_count = 0; + fr->client_item = item->str; + item->str = NULL; + free(item); + /* The len is the number of bytes to print out, and thus excludes + * the terminator zero. */ + if(fr->client_item) + fr->client_len = (int)strlen(fr->client_item); +} + int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c), void* arg, int err, struct comm_reply* ATTR_UNUSED(rep)) { @@ -4212,11 +4355,48 @@ int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c), void* arg, fr->client_cp = NULL; return 0; } - if(!fr->to_print->first) { - /* done with printing for now */ +#ifdef HAVE_SSL + if(fr->client_cp->ssl_shake_state == comm_ssl_shake_hs_read) { + /* read condition satisfied back to writing */ + comm_point_listen_for_rw(fr->client_cp, 0, 1); + fr->client_cp->ssl_shake_state = comm_ssl_shake_none; + } +#endif /* HAVE_SSL */ + + /* Pickup an item if there are none */ + if(!fr->client_item) { + fr_client_pickup_next_item(fr); + } + if(!fr->client_item) { + /* Done with printing for now. */ comm_point_stop_listening(fr->client_cp); return 0; } + + /* Try to print out a number of items, if they can print in full. */ + while(fr->client_item) { + /* Send current item, if any. */ + if(fr->client_item && fr->client_len != 0 && + fr->client_byte_count < fr->client_len) { + if(!fr_client_send_item(fr)) + return 0; + } + + /* The current item is done. */ + if(fr->client_item) { + free(fr->client_item); + fr->client_item = NULL; + fr->client_len = 0; + fr->client_byte_count = 0; + } + if(!fr->to_print->first) { + /* Done with printing for now. */ + comm_point_stop_listening(fr->client_cp); + return 0; + } + fr_client_pickup_next_item(fr); + } + return 0; } diff --git a/daemon/remote.h b/daemon/remote.h index e13592aff..42abd44a9 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -171,9 +171,18 @@ struct fast_reload_thread { struct remote_stream remote; /** the worker that the service_event is added in */ struct worker* worker; + /** the comm point for the client connection, the remote control * client. */ struct comm_point* client_cp; + /** The string that is sent to the client, for output on the terminal, + * it is malloced. */ + char* client_item; + /** The length, strlen, of the client_item, that has to be sent. */ + int client_len; + /** The number of bytes sent of client_item. */ + int client_byte_count; + /** lock on fr_output, to stop race when both remote control thread * and fast reload thread use fr_output list. */ lock_basic_type fr_output_lock; From d9b965e0a1a4fbac9502842e311a7c9c4c0725b6 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 4 Dec 2023 17:18:21 +0100 Subject: [PATCH 015/125] - fast-reload, better debug output. --- daemon/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 607897f08..3ec429510 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3661,7 +3661,7 @@ static void* fast_reload_thread_main(void* arg) goto done; /* print output to the client */ - if(!fr_output_printf(fast_reload_thread, "start fast reload\n")) + if(!fr_output_printf(fast_reload_thread, "thread started\n")) goto done_error; fr_send_notification(fast_reload_thread, fast_reload_notification_printout); From caf6419a0ce3c1b769faa547d82dee29c2ef46d2 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 8 Dec 2023 13:17:52 +0100 Subject: [PATCH 016/125] - fast-reload, print queue structure, for output to the remote client. --- daemon/remote.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/daemon/remote.h b/daemon/remote.h index 42abd44a9..91573173f 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -140,6 +140,30 @@ enum fast_reload_notification { fast_reload_notification_printout = 5 }; +/** + * Fast reload printout queue. Contains a list of strings, that need to be + * printed over the file descriptor. + */ +struct fast_reload_printq { + /** if this item is in a list, the previous and next */ + struct fast_reload_printq *prev, *next; + /** if this item is in a list, it is true. */ + int in_list; + /** list of strings to printout */ + struct config_strlist_head* to_print; + /** the current item to print. It is malloced. NULL if none. */ + char* client_item; + /** The length, strlen, of the client_item, that has to be sent. */ + int client_len; + /** The number of bytes sent of client_item. */ + int client_byte_count; + /** the comm point for the client connection, the remote control + * client. */ + struct comm_point* client_cp; + /** the worker that the event is added in */ + struct worker* worker; +}; + /** * Fast reload thread structure */ From f93b71899941ece7a5c1ed52f88f8a29b8552aea Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 8 Dec 2023 14:13:54 +0100 Subject: [PATCH 017/125] - fast-reload, move print items to print queue from fast_reload_thread struct. --- daemon/remote.c | 221 +++++++++++++++++++++++++++++------------------- daemon/remote.h | 19 +---- 2 files changed, 137 insertions(+), 103 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 3ec429510..896b41424 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -116,6 +116,10 @@ /** Timeout in msec for ipc socket poll. */ #define IPC_NOTIFICATION_WAIT 200 +static void fr_printq_delete(struct fast_reload_printq* printq); +static void fr_main_perform_printout(struct fast_reload_thread* fr); +static int fr_printq_empty(struct fast_reload_printq* printq); + static int remote_setup_ctx(struct daemon_remote* rc, struct config_file* cfg) { @@ -4001,16 +4005,6 @@ fast_reload_thread_setup(struct worker* worker) lock_basic_init(&fr->fr_output_lock); lock_protect(&fr->fr_output_lock, fr->fr_output, sizeof(*fr->fr_output)); - fr->to_print = (struct config_strlist_head*)calloc(1, - sizeof(*fr->to_print)); - if(!fr->fr_output) { - free(fr->fr_output); - sock_close(fr->commpair[0]); - sock_close(fr->commpair[1]); - free(fr); - worker->daemon->fast_reload_thread = NULL; - return 0; - } return 1; } @@ -4029,21 +4023,19 @@ fast_reload_thread_desetup(struct fast_reload_thread* fast_reload_thread) ub_event_free(fast_reload_thread->service_event); sock_close(fast_reload_thread->commpair[0]); sock_close(fast_reload_thread->commpair[1]); - if(fast_reload_thread->remote.ssl) { - SSL_shutdown(fast_reload_thread->remote.ssl); - SSL_free(fast_reload_thread->remote.ssl); + if(fast_reload_thread->printq) { + fr_main_perform_printout(fast_reload_thread); + /* If it is empty now, there is nothing to print on fd. */ + if(fr_printq_empty(fast_reload_thread->printq)) { + fr_printq_delete(fast_reload_thread->printq); + } else { + } } - comm_point_delete(fast_reload_thread->client_cp); lock_basic_destroy(&fast_reload_thread->fr_output_lock); if(fast_reload_thread->fr_output) { config_delstrlist(fast_reload_thread->fr_output->first); free(fast_reload_thread->fr_output); } - if(fast_reload_thread->to_print) { - config_delstrlist(fast_reload_thread->to_print->first); - free(fast_reload_thread->to_print); - } - free(fast_reload_thread->client_item); free(fast_reload_thread); } @@ -4087,7 +4079,7 @@ fr_main_perform_printout(struct fast_reload_thread* fr) fr->fr_output->last = NULL; lock_basic_unlock(&fr->fr_output_lock); - if(!fr->client_cp) { + if(!fr->printq || !fr->printq->client_cp) { /* There is no output socket, delete it. */ config_delstrlist(out.first); return; @@ -4097,11 +4089,11 @@ fr_main_perform_printout(struct fast_reload_thread* fr) * producer and consumer are both owned by the remote control thread, * it moves the items to the list for printing in the event callback * for the client_cp. */ - cfg_strlist_append_listhead(fr->to_print, &out); + cfg_strlist_append_listhead(fr->printq->to_print, &out); /* Set the client_cp to output if not already */ - if(!fr->client_cp->event_added) - comm_point_listen_for_rw(fr->client_cp, 0, 1); + if(!fr->printq->client_cp->event_added) + comm_point_listen_for_rw(fr->printq->client_cp, 0, 1); } /** Fast reload, perform the command received from the fast reload thread */ @@ -4205,27 +4197,27 @@ void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), /** fast reload, send client item over SSL. Returns number of bytes * printed, 0 on wait later, or -1 on failure. */ static int -fr_client_send_item_ssl(struct fast_reload_thread* fr) +fr_client_send_item_ssl(struct fast_reload_printq* printq) { int r; ERR_clear_error(); - r = SSL_write(fr->remote.ssl, - fr->client_item+fr->client_byte_count, - fr->client_len - fr->client_byte_count); + r = SSL_write(printq->remote.ssl, + printq->client_item+printq->client_byte_count, + printq->client_len - printq->client_byte_count); if(r <= 0) { - int want = SSL_get_error(fr->remote.ssl, r); + int want = SSL_get_error(printq->remote.ssl, r); if(want == SSL_ERROR_ZERO_RETURN) { log_err("fast_reload print to remote client: " "SSL_write says connection closed."); return -1; } else if(want == SSL_ERROR_WANT_READ) { /* wait for read condition */ - fr->client_cp->ssl_shake_state = comm_ssl_shake_hs_read; - comm_point_listen_for_rw(fr->client_cp, 1, 0); + printq->client_cp->ssl_shake_state = comm_ssl_shake_hs_read; + comm_point_listen_for_rw(printq->client_cp, 1, 0); return 0; } else if(want == SSL_ERROR_WANT_WRITE) { #ifdef USE_WINSOCK - ub_winsock_tcp_wouldblock(fr->client_cp->ev->ev, UB_EV_WRITE); + ub_winsock_tcp_wouldblock(printq->client_cp->ev->ev, UB_EV_WRITE); #endif return 0; /* write more later */ } else if(want == SSL_ERROR_SYSCALL) { @@ -4252,12 +4244,12 @@ fr_client_send_item_ssl(struct fast_reload_thread* fr) /** fast reload, send client item for fd, returns bytes sent, or 0 for wait * later, or -1 on failure. */ static int -fr_client_send_item_fd(struct fast_reload_thread* fr) +fr_client_send_item_fd(struct fast_reload_printq* printq) { int r; - r = (int)send(fr->remote.fd, - fr->client_item+fr->client_byte_count, - fr->client_len - fr->client_byte_count, 0); + r = (int)send(printq->remote.fd, + printq->client_item+printq->client_byte_count, + printq->client_len - printq->client_byte_count, 0); if(r == -1) { if( #ifndef USE_WINSOCK @@ -4272,7 +4264,7 @@ fr_client_send_item_fd(struct fast_reload_thread* fr) #endif ) { #ifdef USE_WINSOCK - ub_winsock_tcp_wouldblock(fr->client_cp->ev->ev, UB_EV_WRITE); + ub_winsock_tcp_wouldblock(printq->client_cp->ev->ev, UB_EV_WRITE); #endif return 0; /* Try again. */ } @@ -4285,15 +4277,15 @@ fr_client_send_item_fd(struct fast_reload_thread* fr) /** fast reload, send current client item. false on failure or wait later. */ static int -fr_client_send_item(struct fast_reload_thread* fr) +fr_client_send_item(struct fast_reload_printq* printq) { int r; #ifdef HAVE_SSL - if(fr->remote.ssl) { - r = fr_client_send_item_ssl(fr); + if(printq->remote.ssl) { + r = fr_client_send_item_ssl(printq); } else { #endif - r = fr_client_send_item_fd(fr); + r = fr_client_send_item_fd(printq); #ifdef HAVE_SSL } #endif @@ -4302,104 +4294,152 @@ fr_client_send_item(struct fast_reload_thread* fr) return 0; } else if(r == -1) { /* It failed, close comm point and stop sending. */ - comm_point_delete(fr->client_cp); - fr->client_cp = NULL; + comm_point_delete(printq->client_cp); + printq->client_cp = NULL; return 0; } - fr->client_byte_count += r; - if(fr->client_byte_count < fr->client_len) + printq->client_byte_count += r; + if(printq->client_byte_count < printq->client_len) return 0; /* Print more later. */ return 1; } /** fast reload, pick up the next item to print */ static void -fr_client_pickup_next_item(struct fast_reload_thread* fr) +fr_client_pickup_next_item(struct fast_reload_printq* printq) { struct config_strlist* item; /* Pop first off the list. */ - if(!fr->to_print->first) { - fr->client_item = NULL; - fr->client_len = 0; - fr->client_byte_count = 0; + if(!printq->to_print->first) { + printq->client_item = NULL; + printq->client_len = 0; + printq->client_byte_count = 0; return; } - item = fr->to_print->first; + item = printq->to_print->first; if(item->next) { - fr->to_print->first = item->next; + printq->to_print->first = item->next; } else { - fr->to_print->first = NULL; - fr->to_print->last = NULL; + printq->to_print->first = NULL; + printq->to_print->last = NULL; } item->next = NULL; - fr->client_len = 0; - fr->client_byte_count = 0; - fr->client_item = item->str; + printq->client_len = 0; + printq->client_byte_count = 0; + printq->client_item = item->str; item->str = NULL; free(item); /* The len is the number of bytes to print out, and thus excludes * the terminator zero. */ - if(fr->client_item) - fr->client_len = (int)strlen(fr->client_item); + if(printq->client_item) + printq->client_len = (int)strlen(printq->client_item); } int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c), void* arg, int err, struct comm_reply* ATTR_UNUSED(rep)) { - struct fast_reload_thread* fr = (struct fast_reload_thread*)arg; - if(!fr->client_cp) + struct fast_reload_printq* printq = (struct fast_reload_printq*)arg; + if(!printq->client_cp) return 0; /* the output is closed and deleted */ if(err != NETEVENT_NOERROR) { verbose(VERB_ALGO, "fast reload client: error, close it"); - comm_point_delete(fr->client_cp); - fr->client_cp = NULL; + comm_point_delete(printq->client_cp); + printq->client_cp = NULL; return 0; } #ifdef HAVE_SSL - if(fr->client_cp->ssl_shake_state == comm_ssl_shake_hs_read) { + if(printq->client_cp->ssl_shake_state == comm_ssl_shake_hs_read) { /* read condition satisfied back to writing */ - comm_point_listen_for_rw(fr->client_cp, 0, 1); - fr->client_cp->ssl_shake_state = comm_ssl_shake_none; + comm_point_listen_for_rw(printq->client_cp, 0, 1); + printq->client_cp->ssl_shake_state = comm_ssl_shake_none; } #endif /* HAVE_SSL */ /* Pickup an item if there are none */ - if(!fr->client_item) { - fr_client_pickup_next_item(fr); + if(!printq->client_item) { + fr_client_pickup_next_item(printq); } - if(!fr->client_item) { + if(!printq->client_item) { /* Done with printing for now. */ - comm_point_stop_listening(fr->client_cp); + comm_point_stop_listening(printq->client_cp); return 0; } /* Try to print out a number of items, if they can print in full. */ - while(fr->client_item) { + while(printq->client_item) { /* Send current item, if any. */ - if(fr->client_item && fr->client_len != 0 && - fr->client_byte_count < fr->client_len) { - if(!fr_client_send_item(fr)) + if(printq->client_item && printq->client_len != 0 && + printq->client_byte_count < printq->client_len) { + if(!fr_client_send_item(printq)) return 0; } /* The current item is done. */ - if(fr->client_item) { - free(fr->client_item); - fr->client_item = NULL; - fr->client_len = 0; - fr->client_byte_count = 0; + if(printq->client_item) { + free(printq->client_item); + printq->client_item = NULL; + printq->client_len = 0; + printq->client_byte_count = 0; } - if(!fr->to_print->first) { + if(!printq->to_print->first) { /* Done with printing for now. */ - comm_point_stop_listening(fr->client_cp); + comm_point_stop_listening(printq->client_cp); return 0; } - fr_client_pickup_next_item(fr); + fr_client_pickup_next_item(printq); } return 0; } +/** fast reload printq create */ +static struct fast_reload_printq* +fr_printq_create(struct comm_point* c, struct worker* worker) +{ + struct fast_reload_printq* printq = calloc(1, sizeof(*printq)); + if(!printq) + return NULL; + printq->to_print = calloc(1, sizeof(*printq->to_print)); + if(!printq->to_print) { + free(printq); + return NULL; + } + printq->worker = worker; + printq->client_cp = c; + printq->client_cp->callback = fast_reload_client_callback; + printq->client_cp->cb_arg = printq; + return printq; +} + +/** fast reload printq delete */ +static void +fr_printq_delete(struct fast_reload_printq* printq) +{ + if(!printq) + return; +#ifdef HAVE_SSL + if(printq->remote.ssl) { + SSL_shutdown(printq->remote.ssl); + SSL_free(printq->remote.ssl); + } +#endif + comm_point_delete(printq->client_cp); + if(printq->to_print) { + config_delstrlist(printq->to_print->first); + free(printq->to_print); + } + free(printq); +} + +/** fast reload printq, returns true if the list is empty and no item */ +static int +fr_printq_empty(struct fast_reload_printq* printq) +{ + if(printq->to_print->first == NULL && printq->client_item == NULL) + return 1; + return 0; +} + /** * Fast reload thread, send a command to the thread. Blocking on timeout. * It handles received input from the thread, if any is received. @@ -4476,7 +4516,6 @@ fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s) return; return; } - worker->daemon->fast_reload_thread->remote = *ssl; worker->daemon->fast_reload_thread->started = 1; #ifndef THREADS_DISABLED @@ -4494,12 +4533,14 @@ fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s) UB_EV_READ | UB_EV_PERSIST, fast_reload_service_cb, worker->daemon->fast_reload_thread); if(!worker->daemon->fast_reload_thread->service_event) { + fast_reload_thread_desetup(worker->daemon->fast_reload_thread); if(!ssl_printf(ssl, "error out of memory\n")) return; return; } if(ub_event_add(worker->daemon->fast_reload_thread->service_event, NULL) != 0) { + fast_reload_thread_desetup(worker->daemon->fast_reload_thread); if(!ssl_printf(ssl, "error out of memory adding service event\n")) return; return; @@ -4516,14 +4557,18 @@ fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s) * printout to the remote control client does not block the * server thread from servicing DNS queries. */ fd_set_nonblock(s->c->fd); - worker->daemon->fast_reload_thread->client_cp = s->c; - worker->daemon->fast_reload_thread->client_cp->callback = - fast_reload_client_callback; - worker->daemon->fast_reload_thread->client_cp->cb_arg = - worker->daemon->fast_reload_thread; + worker->daemon->fast_reload_thread->printq = fr_printq_create(s->c, + worker); + if(!worker->daemon->fast_reload_thread->printq) { + fast_reload_thread_desetup(worker->daemon->fast_reload_thread); + if(!ssl_printf(ssl, "error out of memory create printq\n")) + return; + return; + } + worker->daemon->fast_reload_thread->printq->remote = *ssl; s->rc = NULL; /* move away the rc state */ /* Nothing to print right now, so no need to activate it. */ - comm_point_stop_listening(worker->daemon->fast_reload_thread->client_cp); + comm_point_stop_listening(worker->daemon->fast_reload_thread->printq->client_cp); /* Start fast reload thread */ ub_thread_create(&worker->daemon->fast_reload_thread->tid, diff --git a/daemon/remote.h b/daemon/remote.h index 91573173f..cf07362cb 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -160,6 +160,8 @@ struct fast_reload_printq { /** the comm point for the client connection, the remote control * client. */ struct comm_point* client_cp; + /** the remote control connection to print output to. */ + struct remote_stream remote; /** the worker that the event is added in */ struct worker* worker; }; @@ -191,21 +193,11 @@ struct fast_reload_thread { uint32_t service_read_cmd; /** the number of bytes in service_read_cmd */ int service_read_cmd_count; - /** the remote control connection to print output to. */ - struct remote_stream remote; /** the worker that the service_event is added in */ struct worker* worker; - /** the comm point for the client connection, the remote control - * client. */ - struct comm_point* client_cp; - /** The string that is sent to the client, for output on the terminal, - * it is malloced. */ - char* client_item; - /** The length, strlen, of the client_item, that has to be sent. */ - int client_len; - /** The number of bytes sent of client_item. */ - int client_byte_count; + /** the printout of output to the remote client. */ + struct fast_reload_printq *printq; /** lock on fr_output, to stop race when both remote control thread * and fast reload thread use fr_output list. */ @@ -214,9 +206,6 @@ struct fast_reload_thread { * to be printed. The remote control thread can pick them up with * the lock. */ struct config_strlist_head* fr_output; - /** List of strings that are for printing to the remote control - * client, over the client connection socket client_cp. */ - struct config_strlist_head* to_print; }; /** From 828f6d7d72231b3a475c4b37d2b6ca9156dfa41b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 8 Dec 2023 15:12:36 +0100 Subject: [PATCH 018/125] - fast-reload, keep list of pending print queue items in daemon struct. --- daemon/daemon.c | 2 ++ daemon/daemon.h | 3 ++ daemon/remote.c | 80 +++++++++++++++++++++++++++++++++++++++++++++---- daemon/remote.h | 3 ++ 4 files changed, 83 insertions(+), 5 deletions(-) diff --git a/daemon/daemon.c b/daemon/daemon.c index b7d7b631b..9ee9a01a4 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -844,6 +844,8 @@ daemon_cleanup(struct daemon* daemon) daemon_remote_clear(daemon->rc); if(daemon->fast_reload_thread) fast_reload_thread_stop(daemon->fast_reload_thread); + if(daemon->fast_reload_printq_list) + fast_reload_printq_list_delete(daemon->fast_reload_printq_list); for(i=0; inum; i++) worker_delete(daemon->workers[i]); free(daemon->workers); diff --git a/daemon/daemon.h b/daemon/daemon.h index 69e9a7280..aeb68bd94 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -59,6 +59,7 @@ struct daemon_remote; struct respip_set; struct shm_main_info; struct fast_reload_thread; +struct fast_reload_printq; #include "dnstap/dnstap_config.h" #ifdef USE_DNSTAP @@ -149,6 +150,8 @@ struct daemon { int reuse_cache; /** the fast reload thread, or NULL */ struct fast_reload_thread* fast_reload_thread; + /** the fast reload printq list */ + struct fast_reload_printq* fast_reload_printq_list; }; /** diff --git a/daemon/remote.c b/daemon/remote.c index 896b41424..e01bdb3c4 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -119,6 +119,9 @@ static void fr_printq_delete(struct fast_reload_printq* printq); static void fr_main_perform_printout(struct fast_reload_thread* fr); static int fr_printq_empty(struct fast_reload_printq* printq); +static void fr_printq_list_insert(struct fast_reload_printq* printq, + struct daemon* daemon); +static void fr_printq_remove(struct fast_reload_printq* printq); static int remote_setup_ctx(struct daemon_remote* rc, struct config_file* cfg) @@ -4029,6 +4032,9 @@ fast_reload_thread_desetup(struct fast_reload_thread* fast_reload_thread) if(fr_printq_empty(fast_reload_thread->printq)) { fr_printq_delete(fast_reload_thread->printq); } else { + fr_printq_list_insert(fast_reload_thread->printq, + fast_reload_thread->worker->daemon); + fast_reload_thread->printq = NULL; } } lock_basic_destroy(&fast_reload_thread->fr_output_lock); @@ -4294,8 +4300,7 @@ fr_client_send_item(struct fast_reload_printq* printq) return 0; } else if(r == -1) { /* It failed, close comm point and stop sending. */ - comm_point_delete(printq->client_cp); - printq->client_cp = NULL; + fr_printq_remove(printq); return 0; } printq->client_byte_count += r; @@ -4339,12 +4344,13 @@ int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c), void* arg, int err, struct comm_reply* ATTR_UNUSED(rep)) { struct fast_reload_printq* printq = (struct fast_reload_printq*)arg; - if(!printq->client_cp) + if(!printq->client_cp) { + fr_printq_remove(printq); return 0; /* the output is closed and deleted */ + } if(err != NETEVENT_NOERROR) { verbose(VERB_ALGO, "fast reload client: error, close it"); - comm_point_delete(printq->client_cp); - printq->client_cp = NULL; + fr_printq_remove(printq); return 0; } #ifdef HAVE_SSL @@ -4360,6 +4366,11 @@ int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c), void* arg, fr_client_pickup_next_item(printq); } if(!printq->client_item) { + if(printq->in_list) { + /* Nothing more to print, it can be removed. */ + fr_printq_remove(printq); + return 0; + } /* Done with printing for now. */ comm_point_stop_listening(printq->client_cp); return 0; @@ -4382,6 +4393,11 @@ int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c), void* arg, printq->client_byte_count = 0; } if(!printq->to_print->first) { + if(printq->in_list) { + /* Nothing more to print, it can be removed. */ + fr_printq_remove(printq); + return 0; + } /* Done with printing for now. */ comm_point_stop_listening(printq->client_cp); return 0; @@ -4440,6 +4456,60 @@ fr_printq_empty(struct fast_reload_printq* printq) return 0; } +/** fast reload printq, insert onto list */ +static void +fr_printq_list_insert(struct fast_reload_printq* printq, struct daemon* daemon) +{ + if(printq->in_list) + return; + printq->next = daemon->fast_reload_printq_list; + if(printq->next) + printq->next->prev = printq; + printq->prev = NULL; + printq->in_list = 1; + daemon->fast_reload_printq_list = printq; +} + +/** fast reload printq delete list */ +void +fast_reload_printq_list_delete(struct fast_reload_printq* list) +{ + struct fast_reload_printq* printq = list, *next; + while(printq) { + next = printq->next; + fr_printq_delete(printq); + printq = next; + } +} + +/** fast reload printq remove the item from the printq list */ +static void +fr_printq_list_remove(struct fast_reload_printq* printq) +{ + struct daemon* daemon = printq->worker->daemon; + if(printq->prev == NULL) + daemon->fast_reload_printq_list = printq->next; + else printq->prev->next = printq->next; + if(printq->next) + printq->next->prev = printq->prev; + printq->in_list = 0; +} + +/** fast reload printq, remove the printq when no longer needed, + * like the stream is closed. */ +static void +fr_printq_remove(struct fast_reload_printq* printq) +{ + if(!printq) + return; + if(printq->worker && printq->worker->daemon->fast_reload_thread && + printq->worker->daemon->fast_reload_thread->printq == printq) + printq->worker->daemon->fast_reload_thread->printq = NULL; + if(printq->in_list) + fr_printq_list_remove(printq); + fr_printq_delete(printq); +} + /** * Fast reload thread, send a command to the thread. Blocking on timeout. * It handles received input from the thread, if any is received. diff --git a/daemon/remote.h b/daemon/remote.h index cf07362cb..82441598e 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -317,4 +317,7 @@ void fast_reload_service_cb(int fd, short bits, void* arg); int fast_reload_client_callback(struct comm_point* c, void* arg, int err, struct comm_reply* rep); +/** fast reload printq delete list */ +void fast_reload_printq_list_delete(struct fast_reload_printq* list); + #endif /* DAEMON_REMOTE_H */ From a882c35bf2cc8c3f35c3e479735ed5fe8b8a1efb Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 8 Dec 2023 15:20:25 +0100 Subject: [PATCH 019/125] - fast-reload, comment explains in_list for printq to print remainder. --- daemon/remote.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index e01bdb3c4..63ece5c59 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4032,6 +4032,12 @@ fast_reload_thread_desetup(struct fast_reload_thread* fast_reload_thread) if(fr_printq_empty(fast_reload_thread->printq)) { fr_printq_delete(fast_reload_thread->printq); } else { + /* Keep the printq around to printout the remaining + * text to the remote client. Until it is done, it + * sits on a list, that is in the daemon struct. + * The event can then spool the remaining text to the + * remote client and eventually delete itself from the + * callback. */ fr_printq_list_insert(fast_reload_thread->printq, fast_reload_thread->worker->daemon); fast_reload_thread->printq = NULL; From 82c9e9cf3b5349f39b675c0fc43fde5883358596 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 4 Jan 2024 09:40:54 +0100 Subject: [PATCH 020/125] - fast-reload, unit test testdata/fast_reload_thread.tdir that tests the thread output. --- .../fast_reload_thread.conf | 20 +++++++++ .../fast_reload_thread.dsc | 16 +++++++ .../fast_reload_thread.post | 10 +++++ .../fast_reload_thread.pre | 26 +++++++++++ .../fast_reload_thread.test | 45 +++++++++++++++++++ 5 files changed, 117 insertions(+) create mode 100644 testdata/fast_reload_thread.tdir/fast_reload_thread.conf create mode 100644 testdata/fast_reload_thread.tdir/fast_reload_thread.dsc create mode 100644 testdata/fast_reload_thread.tdir/fast_reload_thread.post create mode 100644 testdata/fast_reload_thread.tdir/fast_reload_thread.pre create mode 100644 testdata/fast_reload_thread.tdir/fast_reload_thread.test diff --git a/testdata/fast_reload_thread.tdir/fast_reload_thread.conf b/testdata/fast_reload_thread.tdir/fast_reload_thread.conf new file mode 100644 index 000000000..719f4a00e --- /dev/null +++ b/testdata/fast_reload_thread.tdir/fast_reload_thread.conf @@ -0,0 +1,20 @@ +server: + verbosity: 4 + num-threads: 1 + interface: 127.0.0.1 + port: @PORT@ + use-syslog: no + directory: "" + pidfile: "unbound.pid" + chroot: "" + username: "" + do-not-query-localhost: no + +forward-zone: + name: "." + forward-addr: "127.0.0.1@12345" + +remote-control: + control-enable: yes + control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@ + control-use-cert: no diff --git a/testdata/fast_reload_thread.tdir/fast_reload_thread.dsc b/testdata/fast_reload_thread.tdir/fast_reload_thread.dsc new file mode 100644 index 000000000..ec3437b69 --- /dev/null +++ b/testdata/fast_reload_thread.tdir/fast_reload_thread.dsc @@ -0,0 +1,16 @@ +BaseName: fast_reload_thread +Version: 1.0 +Description: Test fast reload thread output. +CreationDate: Thu Jan 4 09:25:55 CET 2024 +Maintainer: dr. W.C.A. Wijngaards +Category: +Component: +CmdDepends: +Depends: +Help: +Pre: fast_reload_thread.pre +Post: fast_reload_thread.post +Test: fast_reload_thread.test +AuxFiles: +Passed: +Failure: diff --git a/testdata/fast_reload_thread.tdir/fast_reload_thread.post b/testdata/fast_reload_thread.tdir/fast_reload_thread.post new file mode 100644 index 000000000..561232b66 --- /dev/null +++ b/testdata/fast_reload_thread.tdir/fast_reload_thread.post @@ -0,0 +1,10 @@ +# #-- fast_reload_thread.post --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# source the test var file when it's there +[ -f .tpkg.var.test ] && source .tpkg.var.test +# +# do your teardown here +. ../common.sh +kill_pid $UNBOUND_PID +cat unbound.log diff --git a/testdata/fast_reload_thread.tdir/fast_reload_thread.pre b/testdata/fast_reload_thread.tdir/fast_reload_thread.pre new file mode 100644 index 000000000..5f23204e5 --- /dev/null +++ b/testdata/fast_reload_thread.tdir/fast_reload_thread.pre @@ -0,0 +1,26 @@ +# #-- fast_reload_thread.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +. ../common.sh +get_random_port 1 +UNBOUND_PORT=$RND_PORT +echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test + +# make config file +CONTROL_PATH=/tmp +CONTROL_PID=$$ +sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < fast_reload_thread.conf > ub.conf +# start unbound in the background +PRE="../.." +$PRE/unbound -d -c ub.conf >unbound.log 2>&1 & +UNBOUND_PID=$! +echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test +echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test +echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test + +cat .tpkg.var.test +wait_unbound_up unbound.log + diff --git a/testdata/fast_reload_thread.tdir/fast_reload_thread.test b/testdata/fast_reload_thread.tdir/fast_reload_thread.test new file mode 100644 index 000000000..135578fc1 --- /dev/null +++ b/testdata/fast_reload_thread.tdir/fast_reload_thread.test @@ -0,0 +1,45 @@ +# #-- fast_reload_thread.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +PRE="../.." +. ../common.sh + +echo "> unbound-control status" +$PRE/unbound-control -c ub.conf status +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi + +echo "> unbound-control fast_reload" +$PRE/unbound-control -c ub.conf fast_reload 2>&1 | tee output +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi +wait_logfile unbound.log "start fast reload thread" 60 +wait_logfile unbound.log "stop fast reload thread" 60 +wait_logfile unbound.log "joined with fastreload thread" 60 + +if grep "start fast_reload" output; then + echo "OK" +else + echo "output not correct" + exit 1 +fi + +if grep "thread started" output; then + echo "OK" +else + echo "output not correct" + exit 1 +fi + +exit 0 From cce409c7e35743f629afd2c724957f33d26bce2b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 4 Jan 2024 09:46:55 +0100 Subject: [PATCH 021/125] - fast-reload, fix test link for fast_reload_printq_list_delete function. --- testcode/testbound.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/testcode/testbound.c b/testcode/testbound.c index ec627cc8d..83a2fddac 100644 --- a/testcode/testbound.c +++ b/testcode/testbound.c @@ -615,3 +615,9 @@ void listen_desetup_locks(void) { /* nothing */ } + +void fast_reload_printq_list_delete( + struct fast_reload_printq* ATTR_UNUSED(list)) +{ + /* nothing */ +} From 85127a43cc37c02f728485f696eb26d60ffd2bb2 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 4 Jan 2024 10:30:59 +0100 Subject: [PATCH 022/125] - fast-reload, reread config file from disk. --- daemon/daemon.c | 1 + daemon/daemon.h | 2 ++ daemon/remote.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ daemon/unbound.c | 3 +++ 4 files changed, 62 insertions(+) diff --git a/daemon/daemon.c b/daemon/daemon.c index 9ee9a01a4..b229d2edb 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -896,6 +896,7 @@ daemon_delete(struct daemon* daemon) listen_desetup_locks(); free(daemon->chroot); free(daemon->pidfile); + free(daemon->cfgfile); free(daemon->env); #ifdef HAVE_SSL listen_sslctx_delete_ticket_keys(); diff --git a/daemon/daemon.h b/daemon/daemon.h index aeb68bd94..46cb5dbcc 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -152,6 +152,8 @@ struct daemon { struct fast_reload_thread* fast_reload_thread; /** the fast reload printq list */ struct fast_reload_printq* fast_reload_printq_list; + /** config file name */ + char* cfgfile; }; /** diff --git a/daemon/remote.c b/daemon/remote.c index 63ece5c59..1d6b18978 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3657,6 +3657,57 @@ fr_output_printf(struct fast_reload_thread* fr, const char* format, ...) return ret; } +/** fast reload thread, read config */ +static int +fr_read_config(struct fast_reload_thread* fr, struct config_file** newcfg) +{ + /* Create new config structure. */ + *newcfg = config_create(); + if(!*newcfg) { + if(!fr_output_printf(fr, "config_create failed: out of memory\n")) + return 0; + fr_send_notification(fr, fast_reload_notification_printout); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + + /* Read new config from file */ + if(!config_read(*newcfg, fr->worker->daemon->cfgfile, + fr->worker->daemon->chroot)) { + config_delete(*newcfg); + if(!fr_output_printf(fr, "config_read %s failed: %s\n", + fr->worker->daemon->cfgfile, strerror(errno))) + return 0; + fr_send_notification(fr, fast_reload_notification_printout); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + if(!fr_output_printf(fr, "done read config file %s\n", + fr->worker->daemon->cfgfile)) + return 0; + fr_send_notification(fr, fast_reload_notification_printout); + + return 1; +} + +/** fast reload thread, load config */ +static int +fr_load_config(struct fast_reload_thread* fr) +{ + struct config_file* newcfg = NULL; + if(!fr_read_config(fr, &newcfg)) + return 0; + if(fr_poll_for_quit(fr)) { + config_delete(newcfg); + return 1; + } + + config_delete(newcfg); + return 1; +} + /** fast reload thread. the thread main function */ static void* fast_reload_thread_main(void* arg) { @@ -3675,6 +3726,11 @@ static void* fast_reload_thread_main(void* arg) if(fr_poll_for_quit(fast_reload_thread)) goto done; + if(!fr_load_config(fast_reload_thread)) + goto done_error; + if(fr_poll_for_quit(fast_reload_thread)) + goto done; + verbose(VERB_ALGO, "stop fast reload thread"); /* If this is not an exit due to quit earlier, send regular done. */ if(!fast_reload_thread->need_to_quit) diff --git a/daemon/unbound.c b/daemon/unbound.c index 457a08032..24060b740 100644 --- a/daemon/unbound.c +++ b/daemon/unbound.c @@ -682,6 +682,9 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode, * it would succeed on SIGHUP as well */ if(!cfg->use_syslog) log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir); + daemon->cfgfile = strdup(*cfgfile); + if(!daemon->cfgfile) + fatal_exit("out of memory in daemon cfgfile strdup"); } /** From 4304754c76dc8ac6e7856293048797d028eaddd9 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 4 Jan 2024 16:53:02 +0100 Subject: [PATCH 023/125] - fast-reload, unshare forwards, making the structure locked, with an rwlock. --- Makefile.in | 6 +++--- cachedb/cachedb.c | 3 ++- daemon/cachedump.c | 9 +++++++-- daemon/daemon.c | 6 ++++++ daemon/remote.c | 43 +++++++++++++++++++++++++++++++---------- daemon/worker.c | 7 ------- edns-subnet/subnetmod.c | 2 +- iterator/iter_fwd.c | 21 +++++++++++++++++--- iterator/iter_fwd.h | 10 ++++++++++ iterator/iter_utils.c | 27 +++++++++++++++++++++----- iterator/iter_utils.h | 6 +++++- iterator/iterator.c | 43 +++++++++++++++++++++++++++-------------- libunbound/context.c | 4 ++++ libunbound/libunbound.c | 2 ++ libunbound/libworker.c | 9 +-------- util/module.h | 2 +- 16 files changed, 143 insertions(+), 57 deletions(-) diff --git a/Makefile.in b/Makefile.in index 2ed4d1199..4c37a980a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1278,7 +1278,7 @@ daemon.lo daemon.o: $(srcdir)/daemon/daemon.c config.h $(srcdir)/daemon/daemon.h $(srcdir)/util/edns.h $(srcdir)/services/listen_dnsport.h $(srcdir)/services/cache/rrset.h \ $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h $(srcdir)/services/localzone.h \ $(srcdir)/services/authzone.h $(srcdir)/services/mesh.h $(srcdir)/services/rpz.h $(srcdir)/respip/respip.h \ - $(srcdir)/util/random.h $(srcdir)/util/tube.h $(srcdir)/util/net_help.h $(srcdir)/sldns/keyraw.h + $(srcdir)/util/random.h $(srcdir)/util/tube.h $(srcdir)/util/net_help.h $(srcdir)/sldns/keyraw.h $(srcdir)/iterator/iter_fwd.h remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h $(srcdir)/daemon/worker.h \ $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/data/packed_rrset.h \ $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/netevent.h \ @@ -1484,7 +1484,7 @@ context.lo context.o: $(srcdir)/libunbound/context.c config.h $(srcdir)/libunbou $(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h \ $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \ $(srcdir)/services/authzone.h $(srcdir)/services/mesh.h $(srcdir)/services/rpz.h $(srcdir)/daemon/stats.h \ - $(srcdir)/util/timehist.h $(srcdir)/respip/respip.h $(srcdir)/util/edns.h + $(srcdir)/util/timehist.h $(srcdir)/respip/respip.h $(srcdir)/util/edns.h $(srcdir)/iterator/iter_fwd.h libunbound.lo libunbound.o: $(srcdir)/libunbound/libunbound.c $(srcdir)/libunbound/unbound.h \ $(srcdir)/libunbound/unbound-event.h config.h $(srcdir)/libunbound/context.h $(srcdir)/util/locks.h \ $(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \ @@ -1496,7 +1496,7 @@ libunbound.lo libunbound.o: $(srcdir)/libunbound/libunbound.c $(srcdir)/libunbou $(srcdir)/sldns/sbuffer.h $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h $(srcdir)/util/netevent.h \ $(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/services/cache/rrset.h \ $(srcdir)/util/storage/slabhash.h $(srcdir)/services/authzone.h $(srcdir)/services/mesh.h \ - $(srcdir)/services/rpz.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/respip/respip.h + $(srcdir)/services/rpz.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/respip/respip.h $(srcdir)/iterator/iter_fwd.h libworker.lo libworker.o: $(srcdir)/libunbound/libworker.c config.h $(srcdir)/libunbound/libworker.h \ $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \ $(srcdir)/libunbound/context.h $(srcdir)/util/alloc.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \ diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index b912be8ed..aa8b2645a 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -666,6 +666,7 @@ cachedb_extcache_store(struct module_qstate* qstate, struct cachedb_env* ie) static int cachedb_intcache_lookup(struct module_qstate* qstate, struct cachedb_env* cde) { + uint8_t dpname_storage[LDNS_MAX_DOMAINLEN+1]; uint8_t* dpname=NULL; size_t dpnamelen=0; struct dns_msg* msg; @@ -674,7 +675,7 @@ cachedb_intcache_lookup(struct module_qstate* qstate, struct cachedb_env* cde) return 0; } if(iter_stub_fwd_no_cache(qstate, &qstate->qinfo, - &dpname, &dpnamelen)) + &dpname, &dpnamelen, dpname_storage, sizeof(dpname_storage))) return 0; /* no cache for these queries */ msg = dns_cache_lookup(qstate->env, qstate->qinfo.qname, qstate->qinfo.qname_len, qstate->qinfo.qtype, diff --git a/daemon/cachedump.c b/daemon/cachedump.c index 61ee1d291..d12168008 100644 --- a/daemon/cachedump.c +++ b/daemon/cachedump.c @@ -850,15 +850,20 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm, if(!ssl_printf(ssl, "The following name servers are used for lookup " "of %s\n", b)) return 0; - + + lock_rw_rdlock(&worker->env.fwds->lock); dp = forwards_lookup(worker->env.fwds, nm, qinfo.qclass); if(dp) { - if(!ssl_printf(ssl, "forwarding request:\n")) + if(!ssl_printf(ssl, "forwarding request:\n")) { + lock_rw_unlock(&worker->env.fwds->lock); return 0; + } print_dp_main(ssl, dp, NULL); print_dp_details(ssl, worker, dp); + lock_rw_unlock(&worker->env.fwds->lock); return 1; } + lock_rw_unlock(&worker->env.fwds->lock); while(1) { dp = dns_cache_find_delegation(&worker->env, nm, nmlen, diff --git a/daemon/daemon.c b/daemon/daemon.c index b229d2edb..8f672950a 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -91,6 +91,7 @@ #include "util/net_help.h" #include "sldns/keyraw.h" #include "respip/respip.h" +#include "iterator/iter_fwd.h" #include #ifdef HAVE_SYSTEMD @@ -716,6 +717,9 @@ daemon_fork(struct daemon* daemon) fatal_exit("Could not create local zones: out of memory"); if(!local_zones_apply_cfg(daemon->local_zones, daemon->cfg)) fatal_exit("Could not set up local zones"); + if(!(daemon->env->fwds = forwards_create()) || + !forwards_apply_cfg(daemon->env->fwds, daemon->cfg)) + fatal_exit("Could not set forward zones"); /* process raw response-ip configuration data */ if(!(daemon->respip_set = respip_set_create())) @@ -832,6 +836,8 @@ daemon_cleanup(struct daemon* daemon) slabhash_clear(daemon->env->msg_cache); } daemon->old_num = daemon->num; /* save the current num */ + forwards_delete(daemon->env->fwds); + daemon->env->fwds = NULL; local_zones_delete(daemon->local_zones); daemon->local_zones = NULL; respip_set_delete(daemon->respip_set); diff --git a/daemon/remote.c b/daemon/remote.c index 1d6b18978..bc275ab9e 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -2022,12 +2022,20 @@ static int print_root_fwds(RES* ssl, struct iter_forwards* fwds, uint8_t* root) { struct delegpt* dp; + lock_rw_rdlock(&fwds->lock); dp = forwards_lookup(fwds, root, LDNS_RR_CLASS_IN); - if(!dp) + if(!dp) { + lock_rw_unlock(&fwds->lock); return ssl_printf(ssl, "off (using root hints)\n"); + } /* if dp is returned it must be the root */ log_assert(query_dname_compare(dp->name, root)==0); - return ssl_print_name_dp(ssl, NULL, root, LDNS_RR_CLASS_IN, dp); + if(!ssl_print_name_dp(ssl, NULL, root, LDNS_RR_CLASS_IN, dp)) { + lock_rw_unlock(&fwds->lock); + return 0; + } + lock_rw_unlock(&fwds->lock); + return 1; } /** parse args into delegpt */ @@ -2112,15 +2120,20 @@ do_forward(RES* ssl, struct worker* worker, char* args) /* delete all the existing queries first */ mesh_delete_all(worker->env.mesh); if(strcmp(args, "off") == 0) { + lock_rw_wrlock(&fwd->lock); forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, root); + lock_rw_unlock(&fwd->lock); } else { struct delegpt* dp; if(!(dp = parse_delegpt(ssl, args, root))) return; + lock_rw_wrlock(&fwd->lock); if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) { + lock_rw_unlock(&fwd->lock); (void)ssl_printf(ssl, "error out of memory\n"); return; } + lock_rw_unlock(&fwd->lock); } send_ok(ssl); } @@ -2179,9 +2192,11 @@ do_forward_add(RES* ssl, struct worker* worker, char* args) struct delegpt* dp = NULL; if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, NULL)) return; + lock_rw_wrlock(&fwd->lock); if(insecure && worker->env.anchors) { if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm)) { + lock_rw_unlock(&fwd->lock); (void)ssl_printf(ssl, "error out of memory\n"); delegpt_free_mlc(dp); free(nm); @@ -2189,10 +2204,12 @@ do_forward_add(RES* ssl, struct worker* worker, char* args) } } if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) { + lock_rw_unlock(&fwd->lock); (void)ssl_printf(ssl, "error out of memory\n"); free(nm); return; } + lock_rw_unlock(&fwd->lock); free(nm); send_ok(ssl); } @@ -2206,10 +2223,12 @@ do_forward_remove(RES* ssl, struct worker* worker, char* args) uint8_t* nm = NULL; if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL)) return; + lock_rw_wrlock(&fwd->lock); if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, nm); + lock_rw_unlock(&fwd->lock); free(nm); send_ok(ssl); } @@ -2224,6 +2243,7 @@ do_stub_add(RES* ssl, struct worker* worker, char* args) struct delegpt* dp = NULL; if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, &prime)) return; + lock_rw_wrlock(&fwd->lock); if(insecure && worker->env.anchors) { if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm)) { @@ -2237,6 +2257,7 @@ do_stub_add(RES* ssl, struct worker* worker, char* args) if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); + lock_rw_unlock(&fwd->lock); (void)ssl_printf(ssl, "error out of memory\n"); delegpt_free_mlc(dp); free(nm); @@ -2248,9 +2269,11 @@ do_stub_add(RES* ssl, struct worker* worker, char* args) if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); + lock_rw_unlock(&fwd->lock); free(nm); return; } + lock_rw_unlock(&fwd->lock); free(nm); send_ok(ssl); } @@ -2264,11 +2287,13 @@ do_stub_remove(RES* ssl, struct worker* worker, char* args) uint8_t* nm = NULL; if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL)) return; + lock_rw_wrlock(&fwd->lock); if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm); hints_delete_stub(worker->env.hints, LDNS_RR_CLASS_IN, nm); + lock_rw_unlock(&fwd->lock); free(nm); send_ok(ssl); } @@ -2697,6 +2722,7 @@ do_list_forwards(RES* ssl, struct worker* worker) struct iter_forward_zone* z; struct trust_anchor* a; int insecure; + lock_rw_rdlock(&fwds->lock); RBTREE_FOR(z, struct iter_forward_zone*, fwds->tree) { if(!z->dp) continue; /* skip empty marker for stub */ @@ -2711,9 +2737,12 @@ do_list_forwards(RES* ssl, struct worker* worker) } if(!ssl_print_name_dp(ssl, (insecure?"forward +i":"forward"), - z->name, z->dclass, z->dp)) + z->name, z->dclass, z->dp)) { + lock_rw_unlock(&fwds->lock); return; + } } + lock_rw_unlock(&fwds->lock); } /** do the list_stubs command */ @@ -3115,13 +3144,9 @@ execute_cmd(struct daemon_remote* rc, struct rc_state* s, RES* ssl, char* cmd, do_stub_remove(ssl, worker, skipwhite(p+11)); return; } else if(cmdcmp(p, "forward_add", 11)) { - /* must always distribute this cmd */ - if(rc) distribute_cmd(rc, ssl, cmd); do_forward_add(ssl, worker, skipwhite(p+11)); return; } else if(cmdcmp(p, "forward_remove", 14)) { - /* must always distribute this cmd */ - if(rc) distribute_cmd(rc, ssl, cmd); do_forward_remove(ssl, worker, skipwhite(p+14)); return; } else if(cmdcmp(p, "insecure_add", 12)) { @@ -3135,8 +3160,6 @@ execute_cmd(struct daemon_remote* rc, struct rc_state* s, RES* ssl, char* cmd, do_insecure_remove(ssl, worker, skipwhite(p+15)); return; } else if(cmdcmp(p, "forward", 7)) { - /* must always distribute this cmd */ - if(rc) distribute_cmd(rc, ssl, cmd); do_forward(ssl, worker, skipwhite(p+7)); return; } else if(cmdcmp(p, "flush_stats", 11)) { @@ -4564,7 +4587,7 @@ fr_printq_remove(struct fast_reload_printq* printq) { if(!printq) return; - if(printq->worker && printq->worker->daemon->fast_reload_thread && + if(printq->worker->daemon->fast_reload_thread && printq->worker->daemon->fast_reload_thread->printq == printq) printq->worker->daemon->fast_reload_thread->printq = NULL; if(printq->in_list) diff --git a/daemon/worker.c b/daemon/worker.c index aeadf32d4..8ac28ba19 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -2268,12 +2268,6 @@ worker_init(struct worker* worker, struct config_file *cfg, worker_delete(worker); return 0; } - if(!(worker->env.fwds = forwards_create()) || - !forwards_apply_cfg(worker->env.fwds, cfg)) { - log_err("Could not set forward zones"); - worker_delete(worker); - return 0; - } if(!(worker->env.hints = hints_create()) || !hints_apply_cfg(worker->env.hints, cfg)) { log_err("Could not set root or stub hints"); @@ -2352,7 +2346,6 @@ worker_delete(struct worker* worker) outside_network_quit_prepare(worker->back); mesh_delete(worker->env.mesh); sldns_buffer_free(worker->env.scratch_buffer); - forwards_delete(worker->env.fwds); hints_delete(worker->env.hints); listen_delete(worker->front); outside_network_delete(worker->back); diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index cefde84e5..2e663b1af 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -152,7 +152,7 @@ int ecs_whitelist_check(struct query_info* qinfo, /* Cache by default, might be disabled after parsing EDNS option * received from nameserver. */ - if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL)) { + if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)) { qstate->no_cache_store = 0; } diff --git a/iterator/iter_fwd.c b/iterator/iter_fwd.c index 151372912..cf418b072 100644 --- a/iterator/iter_fwd.c +++ b/iterator/iter_fwd.c @@ -71,6 +71,7 @@ forwards_create(void) sizeof(struct iter_forwards)); if(!fwd) return NULL; + lock_rw_init(&fwd->lock); return fwd; } @@ -100,6 +101,7 @@ forwards_delete(struct iter_forwards* fwd) { if(!fwd) return; + lock_rw_destroy(&fwd->lock); fwd_del_tree(fwd); free(fwd); } @@ -332,17 +334,27 @@ make_stub_holes(struct iter_forwards* fwd, struct config_file* cfg) int forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg) { + if(fwd->tree) { + lock_unprotect(&fwd->lock, fwd->tree); + } fwd_del_tree(fwd); fwd->tree = rbtree_create(fwd_cmp); if(!fwd->tree) return 0; + lock_protect(&fwd->lock, fwd->tree, sizeof(*fwd->tree)); + lock_rw_wrlock(&fwd->lock); /* read forward zones */ - if(!read_forwards(fwd, cfg)) + if(!read_forwards(fwd, cfg)) { + lock_rw_unlock(&fwd->lock); return 0; - if(!make_stub_holes(fwd, cfg)) + } + if(!make_stub_holes(fwd, cfg)) { + lock_rw_unlock(&fwd->lock); return 0; + } fwd_init_parents(fwd); + lock_rw_unlock(&fwd->lock); return 1; } @@ -458,10 +470,12 @@ forwards_get_mem(struct iter_forwards* fwd) size_t s; if(!fwd) return 0; + lock_rw_rdlock(&fwd->lock); s = sizeof(*fwd) + sizeof(*fwd->tree); RBTREE_FOR(p, struct iter_forward_zone*, fwd->tree) { s += sizeof(*p) + p->namelen + delegpt_get_mem(p->dp); } + lock_rw_unlock(&fwd->lock); return s; } @@ -504,6 +518,8 @@ forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm) int forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm) { + if(fwd_zone_find(fwd, c, nm) != NULL) + return 1; /* already a stub zone there */ if(!fwd_add_stub_hole(fwd, c, nm)) { return 0; } @@ -523,4 +539,3 @@ forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm) fwd_zone_free(z); fwd_init_parents(fwd); } - diff --git a/iterator/iter_fwd.h b/iterator/iter_fwd.h index e90b74c16..75ac597bd 100644 --- a/iterator/iter_fwd.h +++ b/iterator/iter_fwd.h @@ -43,6 +43,7 @@ #ifndef ITERATOR_ITER_FWD_H #define ITERATOR_ITER_FWD_H #include "util/rbtree.h" +#include "util/locks.h" struct config_file; struct delegpt; @@ -50,6 +51,10 @@ struct delegpt; * Iterator forward zones structure */ struct iter_forwards { + /** lock on the forwards tree. + * When grabbing both this lock and the anchors.lock, this lock + * is grabbed first. */ + lock_rw_type lock; /** * Zones are stored in this tree. Sort order is specially chosen. * first sorted on qclass. Then on dname in nsec-like order, so that @@ -106,6 +111,8 @@ int forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg); /** * Find forward zone exactly by name + * The return value is contents of the forwards structure, caller should + * lock and unlock a readlock on the forwards structure. * @param fwd: forward storage. * @param qname: The qname of the query. * @param qclass: The qclass of the query. @@ -118,6 +125,8 @@ struct delegpt* forwards_find(struct iter_forwards* fwd, uint8_t* qname, * Find forward zone information * For this qname/qclass find forward zone information, returns delegation * point with server names and addresses, or NULL if no forwarding is needed. + * The return value is contents of the forwards structure, caller should + * lock and unlock a readlock on the forwards structure. * * @param fwd: forward storage. * @param qname: The qname of the query. @@ -147,6 +156,7 @@ int forwards_next_root(struct iter_forwards* fwd, uint16_t* qclass); /** * Get memory in use by forward storage + * Locks and unlocks the structure. * @param fwd: forward storage. * @return bytes in use */ diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index 10a8ec3eb..c1c4999ad 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -1285,7 +1285,12 @@ iter_get_next_root(struct iter_hints* hints, struct iter_forwards* fwd, { uint16_t c1 = *c, c2 = *c; int r1 = hints_next_root(hints, &c1); - int r2 = forwards_next_root(fwd, &c2); + int r2; + + lock_rw_rdlock(&fwd->lock); + r2 = forwards_next_root(fwd, &c2); + lock_rw_unlock(&fwd->lock); + if(!r1 && !r2) /* got none, end of list */ return 0; else if(!r1) /* got one, return that */ @@ -1450,7 +1455,8 @@ int iter_dp_cangodown(struct query_info* qinfo, struct delegpt* dp) int iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf, - uint8_t** retdpname, size_t* retdpnamelen) + uint8_t** retdpname, size_t* retdpnamelen, uint8_t* dpname_storage, + size_t dpname_storage_len) { struct iter_hints_stub *stub; struct delegpt *dp; @@ -1458,6 +1464,7 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf, /* Check for stub. */ stub = hints_lookup_stub(qstate->env->hints, qinf->qname, qinf->qclass, NULL); + lock_rw_rdlock(&qstate->env->fwds->lock); dp = forwards_lookup(qstate->env->fwds, qinf->qname, qinf->qclass); /* see if forward or stub is more pertinent */ @@ -1472,6 +1479,7 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf, /* check stub */ if (stub != NULL && stub->dp != NULL) { + lock_rw_unlock(&qstate->env->fwds->lock); if(stub->dp->no_cache) { char qname[255+1]; char dpname[255+1]; @@ -1488,7 +1496,8 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf, /* Check for forward. */ if (dp) { - if(dp->no_cache) { + int dp_no_cache = dp->no_cache; + if(dp_no_cache) { char qname[255+1]; char dpname[255+1]; dname_str(qinf->qname, qname); @@ -1496,11 +1505,19 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf, verbose(VERB_ALGO, "forward for %s %s has no_cache", qname, dpname); } if(retdpname) { - *retdpname = dp->name; + if(dp->namelen > dpname_storage_len) { + verbose(VERB_ALGO, "no cache dpname too long"); + lock_rw_unlock(&qstate->env->fwds->lock); + return (dp_no_cache); + } + memmove(dpname_storage, dp->name, dp->namelen); + *retdpname = dpname_storage; *retdpnamelen = dp->namelen; } - return (dp->no_cache); + lock_rw_unlock(&qstate->env->fwds->lock); + return (dp_no_cache); } + lock_rw_unlock(&qstate->env->fwds->lock); if(retdpname) { *retdpname = NULL; *retdpnamelen = 0; diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h index fa860fa68..4024629e6 100644 --- a/iterator/iter_utils.h +++ b/iterator/iter_utils.h @@ -407,10 +407,14 @@ int iter_dp_cangodown(struct query_info* qinfo, struct delegpt* dp); * Used for NXDOMAIN checks, above that it is an nxdomain from a * different server and zone. You can pass NULL to not get it. * @param retdpnamelen: returns the length of the dpname. + * @param dpname_storage: this is where the dpname buf is stored, if any. + * So that caller can manage the buffer. + * @param dpname_storage_len: size of dpname_storage buffer. * @return true if no_cache is set in stub or fwd. */ int iter_stub_fwd_no_cache(struct module_qstate *qstate, - struct query_info *qinf, uint8_t** retdpname, size_t* retdpnamelen); + struct query_info *qinf, uint8_t** retdpname, size_t* retdpnamelen, + uint8_t* dpname_storage, size_t dpname_storage_len); /** * Set support for IP4 and IP6 depending on outgoing interfaces diff --git a/iterator/iterator.c b/iterator/iterator.c index 6ff811a27..45d278254 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -679,7 +679,8 @@ errinf_reply(struct module_qstate* qstate, struct iter_qstate* iq) /** see if last resort is possible - does config allow queries to parent */ static int can_have_last_resort(struct module_env* env, uint8_t* nm, size_t nmlen, - uint16_t qclass, struct delegpt** retdp) + uint16_t qclass, int* have_dp, struct delegpt** retdp, + struct regional* region) { struct delegpt* fwddp; struct iter_hints_stub* stub; @@ -693,15 +694,20 @@ can_have_last_resort(struct module_env* env, uint8_t* nm, size_t nmlen, * are allowed to go to the parent */ stub->dp->has_parent_side_NS) { if(retdp) *retdp = stub->dp; + if(have_dp) *have_dp = 1; return 0; } + lock_rw_rdlock(&env->fwds->lock); if((fwddp = forwards_find(env->fwds, nm, qclass)) && /* has_parent_side is turned off for forward_first, where * we are allowed to go to the parent */ fwddp->has_parent_side_NS) { - if(retdp) *retdp = fwddp; + if(retdp) *retdp = delegpt_copy(fwddp, region); + lock_rw_unlock(&env->fwds->lock); + if(have_dp) *have_dp = 1; return 0; } + lock_rw_unlock(&env->fwds->lock); return 1; } @@ -1181,7 +1187,7 @@ generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id) if(iq->depth == ie->max_dependency_depth) return; if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, - iq->qchase.qclass, NULL)) + iq->qchase.qclass, NULL, NULL, NULL)) return; /* is this query the same as the nscheck? */ if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS && @@ -1302,12 +1308,16 @@ forward_request(struct module_qstate* qstate, struct iter_qstate* iq) if( (iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) && !dname_is_root(iq->qchase.qname)) dname_remove_label(&delname, &delnamelen); + lock_rw_rdlock(&qstate->env->fwds->lock); dp = forwards_lookup(qstate->env->fwds, delname, iq->qchase.qclass); - if(!dp) + if(!dp) { + lock_rw_unlock(&qstate->env->fwds->lock); return 0; + } /* send recursion desired to forward addr */ iq->chase_flags |= BIT_RD; iq->dp = delegpt_copy(dp, qstate->region); + lock_rw_unlock(&qstate->env->fwds->lock); /* iq->dp checked by caller */ verbose(VERB_ALGO, "forwarding request"); return 1; @@ -1335,6 +1345,7 @@ static int processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, struct iter_env* ie, int id) { + uint8_t dpname_storage[LDNS_MAX_DOMAINLEN+1]; uint8_t* delname, *dpname=NULL; size_t delnamelen, dpnamelen=0; struct dns_msg* msg = NULL; @@ -1381,7 +1392,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, if (iq->refetch_glue && iq->dp && !can_have_last_resort(qstate->env, iq->dp->name, - iq->dp->namelen, iq->qchase.qclass, NULL)) { + iq->dp->namelen, iq->qchase.qclass, NULL, NULL, NULL)) { iq->refetch_glue = 0; } @@ -1390,7 +1401,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, /* This either results in a query restart (CNAME cache response), a * terminating response (ANSWER), or a cache miss (null). */ - if (iter_stub_fwd_no_cache(qstate, &iq->qchase, &dpname, &dpnamelen)) { + if (iter_stub_fwd_no_cache(qstate, &iq->qchase, &dpname, &dpnamelen, + dpname_storage, sizeof(dpname_storage))) { /* Asked to not query cache. */ verbose(VERB_ALGO, "no-cache set, going to the network"); qstate->no_cache_lookup = 1; @@ -1555,7 +1567,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, } if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue || (iq->qchase.qtype == LDNS_RR_TYPE_NS && qstate->prefetch_leeway - && can_have_last_resort(qstate->env, delname, delnamelen, iq->qchase.qclass, NULL))) { + && can_have_last_resort(qstate->env, delname, delnamelen, iq->qchase.qclass, NULL, NULL, NULL))) { /* remove first label from delname, root goes to hints, * but only to fetch glue, not for qtype=DS. */ /* also when prefetching an NS record, fetch it again from @@ -1597,8 +1609,10 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, break; /* got noprime-stub-zone, continue */ else if(r) return 0; /* stub prime request made */ + lock_rw_rdlock(&qstate->env->fwds->lock); if(forwards_lookup_root(qstate->env->fwds, iq->qchase.qclass)) { + lock_rw_unlock(&qstate->env->fwds->lock); /* forward zone root, no root prime needed */ /* fill in some dp - safety belt */ iq->dp = hints_lookup_root(qstate->env->hints, @@ -1618,6 +1632,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, } return next_state(iq, INIT_REQUEST_2_STATE); } + lock_rw_unlock(&qstate->env->fwds->lock); /* Note that the result of this will set a new * DelegationPoint based on the result of priming. */ if(!prime_root(qstate, iq, id, iq->qchase.qclass)) @@ -1649,15 +1664,13 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags, iq->dp, ie->supports_ipv4, ie->supports_ipv6, ie->use_nat64)) { - struct delegpt* retdp = NULL; - if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &retdp)) { - if(retdp) { + int have_dp = 0; + if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &have_dp, &iq->dp, qstate->region)) { + if(have_dp) { verbose(VERB_QUERY, "cache has stub " "or fwd but no addresses, " "fallback to config"); - iq->dp = delegpt_copy(retdp, - qstate->region); - if(!iq->dp) { + if(have_dp && !iq->dp) { log_err("out of memory in " "stub/fwd fallback"); errinf(qstate, "malloc failure, for fallback to config"); @@ -2062,7 +2075,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, log_assert(iq->dp); if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, - iq->qchase.qclass, NULL)) { + iq->qchase.qclass, NULL, NULL, NULL)) { /* fail -- no more targets, no more hope of targets, no hope * of a response. */ errinf(qstate, "all the configured stub or forward servers failed,"); @@ -2164,7 +2177,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, if( ((ie->supports_ipv6 && !ns->done_pside6) || ((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4)) && !can_have_last_resort(qstate->env, ns->name, ns->namelen, - iq->qchase.qclass, NULL)) { + iq->qchase.qclass, NULL, NULL, NULL)) { log_nametypeclass(VERB_ALGO, "cannot pside lookup ns " "because it is also a stub/forward,", ns->name, LDNS_RR_TYPE_NS, iq->qchase.qclass); diff --git a/libunbound/context.c b/libunbound/context.c index f7c0a2cd5..f3a57d43d 100644 --- a/libunbound/context.c +++ b/libunbound/context.c @@ -53,6 +53,7 @@ #include "util/storage/slabhash.h" #include "util/edns.h" #include "sldns/sbuffer.h" +#include "iterator/iter_fwd.h" int context_finalize(struct ub_ctx* ctx) @@ -85,6 +86,9 @@ context_finalize(struct ub_ctx* ctx) if(!auth_zones_apply_cfg(ctx->env->auth_zones, cfg, 1, &is_rpz, ctx->env, &ctx->mods)) return UB_INITFAIL; + if(!(ctx->env->fwds = forwards_create()) || + !forwards_apply_cfg(ctx->env->fwds, cfg)) + return UB_INITFAIL; if(!edns_strings_apply_cfg(ctx->env->edns_strings, cfg)) return UB_INITFAIL; if(!slabhash_is_size(ctx->env->msg_cache, cfg->msg_cache_size, diff --git a/libunbound/libunbound.c b/libunbound/libunbound.c index 80a82bb47..68e1990bc 100644 --- a/libunbound/libunbound.c +++ b/libunbound/libunbound.c @@ -66,6 +66,7 @@ #include "services/authzone.h" #include "services/listen_dnsport.h" #include "sldns/sbuffer.h" +#include "iterator/iter_fwd.h" #ifdef HAVE_PTHREAD #include #endif @@ -379,6 +380,7 @@ ub_ctx_delete(struct ub_ctx* ctx) config_delete(ctx->env->cfg); edns_known_options_delete(ctx->env); edns_strings_delete(ctx->env->edns_strings); + forwards_delete(ctx->env->fwds); auth_zones_delete(ctx->env->auth_zones); free(ctx->env); } diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 412293f9a..9149365c8 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -100,7 +100,6 @@ libworker_delete_env(struct libworker* w) !w->is_bg || w->is_bg_thread); sldns_buffer_free(w->env->scratch_buffer); regional_destroy(w->env->scratch); - forwards_delete(w->env->fwds); hints_delete(w->env->hints); ub_randfree(w->env->rnd); free(w->env); @@ -159,11 +158,6 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) } w->env->scratch = regional_create_custom(cfg->msg_buffer_size); w->env->scratch_buffer = sldns_buffer_new(cfg->msg_buffer_size); - w->env->fwds = forwards_create(); - if(w->env->fwds && !forwards_apply_cfg(w->env->fwds, cfg)) { - forwards_delete(w->env->fwds); - w->env->fwds = NULL; - } w->env->hints = hints_create(); if(w->env->hints && !hints_apply_cfg(w->env->hints, cfg)) { hints_delete(w->env->hints); @@ -181,8 +175,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) if(!w->is_bg || w->is_bg_thread) { lock_basic_unlock(&ctx->cfglock); } - if(!w->env->scratch || !w->env->scratch_buffer || !w->env->fwds || - !w->env->hints) { + if(!w->env->scratch || !w->env->scratch_buffer || !w->env->hints) { libworker_delete(w); return NULL; } diff --git a/util/module.h b/util/module.h index 8a9da3f93..65b82b59c 100644 --- a/util/module.h +++ b/util/module.h @@ -511,7 +511,7 @@ struct module_env { /** auth zones */ struct auth_zones* auth_zones; /** Mapping of forwarding zones to targets. - * iterator forwarder information. per-thread, created by worker */ + * iterator forwarder information. */ struct iter_forwards* fwds; /** * iterator forwarder information. per-thread, created by worker. From 091ea9f9eb64714a6c4c17f4ecc63aae9e24e4b9 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 4 Jan 2024 17:01:21 +0100 Subject: [PATCH 024/125] - fast-reload, for nonthreaded, the unbound-control commands forward, forward_add and forward_delete should be distributed to other processes, but when threaded, they should not be distributed to other threads because the structure is not thread specific any more. --- daemon/remote.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index bc275ab9e..0593a99b8 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3143,12 +3143,6 @@ execute_cmd(struct daemon_remote* rc, struct rc_state* s, RES* ssl, char* cmd, if(rc) distribute_cmd(rc, ssl, cmd); do_stub_remove(ssl, worker, skipwhite(p+11)); return; - } else if(cmdcmp(p, "forward_add", 11)) { - do_forward_add(ssl, worker, skipwhite(p+11)); - return; - } else if(cmdcmp(p, "forward_remove", 14)) { - do_forward_remove(ssl, worker, skipwhite(p+14)); - return; } else if(cmdcmp(p, "insecure_add", 12)) { /* must always distribute this cmd */ if(rc) distribute_cmd(rc, ssl, cmd); @@ -3159,9 +3153,6 @@ execute_cmd(struct daemon_remote* rc, struct rc_state* s, RES* ssl, char* cmd, if(rc) distribute_cmd(rc, ssl, cmd); do_insecure_remove(ssl, worker, skipwhite(p+15)); return; - } else if(cmdcmp(p, "forward", 7)) { - do_forward(ssl, worker, skipwhite(p+7)); - return; } else if(cmdcmp(p, "flush_stats", 11)) { /* must always distribute this cmd */ if(rc) distribute_cmd(rc, ssl, cmd); @@ -3203,6 +3194,12 @@ execute_cmd(struct daemon_remote* rc, struct rc_state* s, RES* ssl, char* cmd, do_data_add(ssl, worker->daemon->local_zones, skipwhite(p+10)); } else if(cmdcmp(p, "local_datas", 11)) { do_datas_add(ssl, worker->daemon->local_zones); + } else if(cmdcmp(p, "forward_add", 11)) { + do_forward_add(ssl, worker, skipwhite(p+11)); + } else if(cmdcmp(p, "forward_remove", 14)) { + do_forward_remove(ssl, worker, skipwhite(p+14)); + } else if(cmdcmp(p, "forward", 7)) { + do_forward(ssl, worker, skipwhite(p+7)); } else if(cmdcmp(p, "view_local_zone_remove", 22)) { do_view_zone_remove(ssl, worker, skipwhite(p+22)); } else if(cmdcmp(p, "view_local_zone", 15)) { From 39e5959406ffd009ca8a6f186c448a4ee42d7b2b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 5 Jan 2024 13:36:41 +0100 Subject: [PATCH 025/125] - fast-reload, unshared stub hints, making the structure locked, with an rwlock. --- Makefile.in | 12 +++++++----- daemon/cachedump.c | 13 ++++++++++--- daemon/daemon.c | 6 ++++++ daemon/remote.c | 28 +++++++++++++++++----------- daemon/worker.c | 7 ------- iterator/iter_fwd.h | 3 ++- iterator/iter_hints.c | 25 ++++++++++++++++++++----- iterator/iter_hints.h | 6 ++++++ iterator/iter_utils.c | 34 ++++++++++++++++++++++++++-------- iterator/iterator.c | 41 +++++++++++++++++++++++++++++++++++------ libunbound/context.c | 4 ++++ libunbound/libunbound.c | 2 ++ libunbound/libworker.c | 14 +++----------- pythonmod/interface.i | 6 +++++- util/module.h | 2 +- 15 files changed, 144 insertions(+), 59 deletions(-) diff --git a/Makefile.in b/Makefile.in index 4c37a980a..79379d8d1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1278,7 +1278,8 @@ daemon.lo daemon.o: $(srcdir)/daemon/daemon.c config.h $(srcdir)/daemon/daemon.h $(srcdir)/util/edns.h $(srcdir)/services/listen_dnsport.h $(srcdir)/services/cache/rrset.h \ $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h $(srcdir)/services/localzone.h \ $(srcdir)/services/authzone.h $(srcdir)/services/mesh.h $(srcdir)/services/rpz.h $(srcdir)/respip/respip.h \ - $(srcdir)/util/random.h $(srcdir)/util/tube.h $(srcdir)/util/net_help.h $(srcdir)/sldns/keyraw.h $(srcdir)/iterator/iter_fwd.h + $(srcdir)/util/random.h $(srcdir)/util/tube.h $(srcdir)/util/net_help.h $(srcdir)/sldns/keyraw.h \ + $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h $(srcdir)/daemon/worker.h \ $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/data/packed_rrset.h \ $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/netevent.h \ @@ -1484,7 +1485,8 @@ context.lo context.o: $(srcdir)/libunbound/context.c config.h $(srcdir)/libunbou $(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h \ $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \ $(srcdir)/services/authzone.h $(srcdir)/services/mesh.h $(srcdir)/services/rpz.h $(srcdir)/daemon/stats.h \ - $(srcdir)/util/timehist.h $(srcdir)/respip/respip.h $(srcdir)/util/edns.h $(srcdir)/iterator/iter_fwd.h + $(srcdir)/util/timehist.h $(srcdir)/respip/respip.h $(srcdir)/util/edns.h \ + $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h libunbound.lo libunbound.o: $(srcdir)/libunbound/libunbound.c $(srcdir)/libunbound/unbound.h \ $(srcdir)/libunbound/unbound-event.h config.h $(srcdir)/libunbound/context.h $(srcdir)/util/locks.h \ $(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \ @@ -1496,7 +1498,8 @@ libunbound.lo libunbound.o: $(srcdir)/libunbound/libunbound.c $(srcdir)/libunbou $(srcdir)/sldns/sbuffer.h $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h $(srcdir)/util/netevent.h \ $(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/services/cache/rrset.h \ $(srcdir)/util/storage/slabhash.h $(srcdir)/services/authzone.h $(srcdir)/services/mesh.h \ - $(srcdir)/services/rpz.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/respip/respip.h $(srcdir)/iterator/iter_fwd.h + $(srcdir)/services/rpz.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/respip/respip.h \ + $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h libworker.lo libworker.o: $(srcdir)/libunbound/libworker.c config.h $(srcdir)/libunbound/libworker.h \ $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \ $(srcdir)/libunbound/context.h $(srcdir)/util/alloc.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \ @@ -1510,8 +1513,7 @@ libworker.lo libworker.o: $(srcdir)/libunbound/libworker.c config.h $(srcdir)/li $(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h $(srcdir)/services/outbound_list.h \ $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h $(srcdir)/util/regional.h $(srcdir)/util/random.h \ $(srcdir)/util/storage/lookup3.h $(srcdir)/util/net_help.h $(srcdir)/util/data/dname.h \ - $(srcdir)/util/data/msgencode.h $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h \ - $(srcdir)/sldns/str2wire.h + $(srcdir)/util/data/msgencode.h $(srcdir)/sldns/str2wire.h unbound-host.lo unbound-host.o: $(srcdir)/smallapp/unbound-host.c config.h $(srcdir)/libunbound/unbound.h \ $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/wire2str.h asynclook.lo asynclook.o: $(srcdir)/testcode/asynclook.c config.h $(srcdir)/libunbound/unbound.h \ diff --git a/daemon/cachedump.c b/daemon/cachedump.c index d12168008..c7523497d 100644 --- a/daemon/cachedump.c +++ b/daemon/cachedump.c @@ -897,22 +897,29 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm, return 0; continue; } - } + } + lock_rw_rdlock(&worker->env.hints->lock); stub = hints_lookup_stub(worker->env.hints, nm, qinfo.qclass, dp); if(stub) { if(stub->noprime) { if(!ssl_printf(ssl, "The noprime stub servers " - "are used:\n")) + "are used:\n")) { + lock_rw_unlock(&worker->env.hints->lock); return 0; + } } else { if(!ssl_printf(ssl, "The stub is primed " - "with servers:\n")) + "with servers:\n")) { + lock_rw_unlock(&worker->env.hints->lock); return 0; + } } print_dp_main(ssl, stub->dp, NULL); print_dp_details(ssl, worker, stub->dp); + lock_rw_unlock(&worker->env.hints->lock); } else { + lock_rw_unlock(&worker->env.hints->lock); print_dp_main(ssl, dp, msg); print_dp_details(ssl, worker, dp); } diff --git a/daemon/daemon.c b/daemon/daemon.c index 8f672950a..75bae96ba 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -92,6 +92,7 @@ #include "sldns/keyraw.h" #include "respip/respip.h" #include "iterator/iter_fwd.h" +#include "iterator/iter_hints.h" #include #ifdef HAVE_SYSTEMD @@ -720,6 +721,9 @@ daemon_fork(struct daemon* daemon) if(!(daemon->env->fwds = forwards_create()) || !forwards_apply_cfg(daemon->env->fwds, daemon->cfg)) fatal_exit("Could not set forward zones"); + if(!(daemon->env->hints = hints_create()) || + !hints_apply_cfg(daemon->env->hints, daemon->cfg)) + fatal_exit("Could not set root or stub hints"); /* process raw response-ip configuration data */ if(!(daemon->respip_set = respip_set_create())) @@ -838,6 +842,8 @@ daemon_cleanup(struct daemon* daemon) daemon->old_num = daemon->num; /* save the current num */ forwards_delete(daemon->env->fwds); daemon->env->fwds = NULL; + hints_delete(daemon->env->hints); + daemon->env->hints = NULL; local_zones_delete(daemon->local_zones); daemon->local_zones = NULL; respip_set_delete(daemon->respip_set); diff --git a/daemon/remote.c b/daemon/remote.c index 18cc3dbbc..87246ddfe 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -2244,9 +2244,12 @@ do_stub_add(RES* ssl, struct worker* worker, char* args) if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, &prime)) return; lock_rw_wrlock(&fwd->lock); + lock_rw_wrlock(&worker->env.hints->lock); if(insecure && worker->env.anchors) { if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm)) { + lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&worker->env.hints->lock); (void)ssl_printf(ssl, "error out of memory\n"); delegpt_free_mlc(dp); free(nm); @@ -2258,6 +2261,7 @@ do_stub_add(RES* ssl, struct worker* worker, char* args) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&worker->env.hints->lock); (void)ssl_printf(ssl, "error out of memory\n"); delegpt_free_mlc(dp); free(nm); @@ -2270,10 +2274,12 @@ do_stub_add(RES* ssl, struct worker* worker, char* args) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&worker->env.hints->lock); free(nm); return; } lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&worker->env.hints->lock); free(nm); send_ok(ssl); } @@ -2288,12 +2294,14 @@ do_stub_remove(RES* ssl, struct worker* worker, char* args) if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL)) return; lock_rw_wrlock(&fwd->lock); + lock_rw_wrlock(&worker->env.hints->lock); if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm); hints_delete_stub(worker->env.hints, LDNS_RR_CLASS_IN, nm); lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&worker->env.hints->lock); free(nm); send_ok(ssl); } @@ -2753,6 +2761,7 @@ do_list_stubs(RES* ssl, struct worker* worker) struct trust_anchor* a; int insecure; char str[32]; + lock_rw_rdlock(&worker->env.hints->lock); RBTREE_FOR(z, struct iter_hints_stub*, &worker->env.hints->tree) { /* see if it is insecure */ @@ -2768,9 +2777,12 @@ do_list_stubs(RES* ssl, struct worker* worker) snprintf(str, sizeof(str), "stub %sprime%s", (z->noprime?"no":""), (insecure?" +i":"")); if(!ssl_print_name_dp(ssl, str, z->node.name, - z->node.dclass, z->dp)) + z->node.dclass, z->dp)) { + lock_rw_unlock(&worker->env.hints->lock); return; + } } + lock_rw_unlock(&worker->env.hints->lock); } /** do the list_auth_zones command */ @@ -3133,16 +3145,6 @@ execute_cmd(struct daemon_remote* rc, struct rc_state* s, RES* ssl, char* cmd, } else if(cmdcmp(p, "auth_zone_transfer", 18)) { do_auth_zone_transfer(ssl, worker, skipwhite(p+18)); return; - } else if(cmdcmp(p, "stub_add", 8)) { - /* must always distribute this cmd */ - if(rc) distribute_cmd(rc, ssl, cmd); - do_stub_add(ssl, worker, skipwhite(p+8)); - return; - } else if(cmdcmp(p, "stub_remove", 11)) { - /* must always distribute this cmd */ - if(rc) distribute_cmd(rc, ssl, cmd); - do_stub_remove(ssl, worker, skipwhite(p+11)); - return; } else if(cmdcmp(p, "insecure_add", 12)) { /* must always distribute this cmd */ if(rc) distribute_cmd(rc, ssl, cmd); @@ -3200,6 +3202,10 @@ execute_cmd(struct daemon_remote* rc, struct rc_state* s, RES* ssl, char* cmd, do_forward_remove(ssl, worker, skipwhite(p+14)); } else if(cmdcmp(p, "forward", 7)) { do_forward(ssl, worker, skipwhite(p+7)); + } else if(cmdcmp(p, "stub_add", 8)) { + do_stub_add(ssl, worker, skipwhite(p+8)); + } else if(cmdcmp(p, "stub_remove", 11)) { + do_stub_remove(ssl, worker, skipwhite(p+11)); } else if(cmdcmp(p, "view_local_zone_remove", 22)) { do_view_zone_remove(ssl, worker, skipwhite(p+22)); } else if(cmdcmp(p, "view_local_zone", 15)) { diff --git a/daemon/worker.c b/daemon/worker.c index 8ac28ba19..c4ae79352 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -2268,12 +2268,6 @@ worker_init(struct worker* worker, struct config_file *cfg, worker_delete(worker); return 0; } - if(!(worker->env.hints = hints_create()) || - !hints_apply_cfg(worker->env.hints, cfg)) { - log_err("Could not set root or stub hints"); - worker_delete(worker); - return 0; - } /* one probe timer per process -- if we have 5011 anchors */ if(autr_get_num_anchors(worker->env.anchors) > 0 #ifndef THREADS_DISABLED @@ -2346,7 +2340,6 @@ worker_delete(struct worker* worker) outside_network_quit_prepare(worker->back); mesh_delete(worker->env.mesh); sldns_buffer_free(worker->env.scratch_buffer); - hints_delete(worker->env.hints); listen_delete(worker->front); outside_network_delete(worker->back); comm_signal_delete(worker->comsig); diff --git a/iterator/iter_fwd.h b/iterator/iter_fwd.h index 75ac597bd..c1160b853 100644 --- a/iterator/iter_fwd.h +++ b/iterator/iter_fwd.h @@ -53,7 +53,8 @@ struct delegpt; struct iter_forwards { /** lock on the forwards tree. * When grabbing both this lock and the anchors.lock, this lock - * is grabbed first. */ + * is grabbed first. When grabbing both this lock and the hints.lock + * this lock is grabbed first. */ lock_rw_type lock; /** * Zones are stored in this tree. Sort order is specially chosen. diff --git a/iterator/iter_hints.c b/iterator/iter_hints.c index 4f86f3676..a56aa8e40 100644 --- a/iterator/iter_hints.c +++ b/iterator/iter_hints.c @@ -57,6 +57,8 @@ hints_create(void) sizeof(struct iter_hints)); if(!hints) return NULL; + lock_rw_init(&hints->lock); + lock_protect(&hints->lock, &hints->tree, sizeof(hints->tree)); return hints; } @@ -83,6 +85,7 @@ hints_delete(struct iter_hints* hints) { if(!hints) return; + lock_rw_destroy(&hints->lock); hints_del_tree(hints); free(hints); } @@ -438,29 +441,39 @@ read_root_hints_list(struct iter_hints* hints, struct config_file* cfg) int hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg) { + lock_rw_wrlock(&hints->lock); hints_del_tree(hints); name_tree_init(&hints->tree); - + /* read root hints */ - if(!read_root_hints_list(hints, cfg)) + if(!read_root_hints_list(hints, cfg)) { + lock_rw_unlock(&hints->lock); return 0; + } /* read stub hints */ - if(!read_stubs(hints, cfg)) + if(!read_stubs(hints, cfg)) { + lock_rw_unlock(&hints->lock); return 0; + } /* use fallback compiletime root hints */ if(!hints_lookup_root(hints, LDNS_RR_CLASS_IN)) { struct delegpt* dp = compile_time_root_prime(cfg->do_ip4, cfg->do_ip6); verbose(VERB_ALGO, "no config, using builtin root hints."); - if(!dp) + if(!dp) { + lock_rw_unlock(&hints->lock); return 0; - if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, 0)) + } + if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, 0)) { + lock_rw_unlock(&hints->lock); return 0; + } } name_tree_init_parents(&hints->tree); + lock_rw_unlock(&hints->lock); return 1; } @@ -524,10 +537,12 @@ hints_get_mem(struct iter_hints* hints) size_t s; struct iter_hints_stub* p; if(!hints) return 0; + lock_rw_rdlock(&hints->lock); s = sizeof(*hints); RBTREE_FOR(p, struct iter_hints_stub*, &hints->tree) { s += sizeof(*p) + delegpt_get_mem(p->dp); } + lock_rw_unlock(&hints->lock); return s; } diff --git a/iterator/iter_hints.h b/iterator/iter_hints.h index 06b4b9667..23af751ef 100644 --- a/iterator/iter_hints.h +++ b/iterator/iter_hints.h @@ -43,6 +43,7 @@ #ifndef ITERATOR_ITER_HINTS_H #define ITERATOR_ITER_HINTS_H #include "util/storage/dnstree.h" +#include "util/locks.h" struct iter_env; struct config_file; struct delegpt; @@ -51,6 +52,10 @@ struct delegpt; * Iterator hints structure */ struct iter_hints { + /** lock on the forwards tree. + * When grabbing both this lock and the anchors.lock, this lock + * is grabbed first. */ + lock_rw_type lock; /** * Hints are stored in this tree. Sort order is specially chosen. * first sorted on qclass. Then on dname in nsec-like order, so that @@ -131,6 +136,7 @@ struct iter_hints_stub* hints_lookup_stub(struct iter_hints* hints, /** * Get memory in use by hints + * Locks and unlocks the structure. * @param hints: hint storage. * @return bytes in use */ diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index c1c4999ad..79a6e3cb0 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -1284,12 +1284,14 @@ iter_get_next_root(struct iter_hints* hints, struct iter_forwards* fwd, uint16_t* c) { uint16_t c1 = *c, c2 = *c; - int r1 = hints_next_root(hints, &c1); - int r2; + int r1, r2; lock_rw_rdlock(&fwd->lock); + lock_rw_rdlock(&hints->lock); + r1 = hints_next_root(hints, &c1); r2 = forwards_next_root(fwd, &c2); lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&hints->lock); if(!r1 && !r2) /* got none, end of list */ return 0; @@ -1462,9 +1464,10 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf, struct delegpt *dp; /* Check for stub. */ + lock_rw_rdlock(&qstate->env->fwds->lock); + lock_rw_rdlock(&qstate->env->hints->lock); stub = hints_lookup_stub(qstate->env->hints, qinf->qname, qinf->qclass, NULL); - lock_rw_rdlock(&qstate->env->fwds->lock); dp = forwards_lookup(qstate->env->fwds, qinf->qname, qinf->qclass); /* see if forward or stub is more pertinent */ @@ -1479,8 +1482,9 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf, /* check stub */ if (stub != NULL && stub->dp != NULL) { + int stub_no_cache = stub->dp->no_cache; lock_rw_unlock(&qstate->env->fwds->lock); - if(stub->dp->no_cache) { + if(stub_no_cache) { char qname[255+1]; char dpname[255+1]; dname_str(qinf->qname, qname); @@ -1488,15 +1492,26 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf, verbose(VERB_ALGO, "stub for %s %s has no_cache", qname, dpname); } if(retdpname) { - *retdpname = stub->dp->name; + if(stub->dp->namelen > dpname_storage_len) { + verbose(VERB_ALGO, "no cache stub dpname too long"); + lock_rw_unlock(&qstate->env->hints->lock); + *retdpname = NULL; + *retdpnamelen = 0; + return stub_no_cache; + } + memmove(dpname_storage, stub->dp->name, + stub->dp->namelen); + *retdpname = dpname_storage; *retdpnamelen = stub->dp->namelen; } - return (stub->dp->no_cache); + lock_rw_unlock(&qstate->env->hints->lock); + return stub_no_cache; } /* Check for forward. */ if (dp) { int dp_no_cache = dp->no_cache; + lock_rw_unlock(&qstate->env->hints->lock); if(dp_no_cache) { char qname[255+1]; char dpname[255+1]; @@ -1508,16 +1523,19 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf, if(dp->namelen > dpname_storage_len) { verbose(VERB_ALGO, "no cache dpname too long"); lock_rw_unlock(&qstate->env->fwds->lock); - return (dp_no_cache); + *retdpname = NULL; + *retdpnamelen = 0; + return dp_no_cache; } memmove(dpname_storage, dp->name, dp->namelen); *retdpname = dpname_storage; *retdpnamelen = dp->namelen; } lock_rw_unlock(&qstate->env->fwds->lock); - return (dp_no_cache); + return dp_no_cache; } lock_rw_unlock(&qstate->env->fwds->lock); + lock_rw_unlock(&qstate->env->hints->lock); if(retdpname) { *retdpname = NULL; *retdpnamelen = 0; diff --git a/iterator/iterator.c b/iterator/iterator.c index 45d278254..e0716e967 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -688,15 +688,18 @@ can_have_last_resort(struct module_env* env, uint8_t* nm, size_t nmlen, /* do not process a last resort (the parent side) if a stub * or forward is configured, because we do not want to go 'above' * the configured servers */ + lock_rw_rdlock(&env->hints->lock); if(!dname_is_root(nm) && (stub = (struct iter_hints_stub*) name_tree_find(&env->hints->tree, nm, nmlen, labs, qclass)) && /* has_parent side is turned off for stub_first, where we * are allowed to go to the parent */ stub->dp->has_parent_side_NS) { - if(retdp) *retdp = stub->dp; + if(retdp) *retdp = delegpt_copy(stub->dp, region); + lock_rw_unlock(&env->hints->lock); if(have_dp) *have_dp = 1; return 0; } + lock_rw_unlock(&env->hints->lock); lock_rw_rdlock(&env->fwds->lock); if((fwddp = forwards_find(env->fwds, nm, qclass)) && /* has_parent_side is turned off for forward_first, where @@ -886,8 +889,10 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id, verbose(VERB_DETAIL, "priming . %s NS", sldns_lookup_by_id(sldns_rr_classes, (int)qclass)? sldns_lookup_by_id(sldns_rr_classes, (int)qclass)->name:"??"); + lock_rw_rdlock(&qstate->env->hints->lock); dp = hints_lookup_root(qstate->env->hints, qclass); if(!dp) { + lock_rw_unlock(&qstate->env->hints->lock); verbose(VERB_ALGO, "Cannot prime due to lack of hints"); return 0; } @@ -896,6 +901,7 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id, if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS, qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0, 0)) { + lock_rw_unlock(&qstate->env->hints->lock); verbose(VERB_ALGO, "could not prime root"); return 0; } @@ -906,6 +912,7 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id, * copy dp, it is now part of the root prime query. * dp was part of in the fixed hints structure. */ subiq->dp = delegpt_copy(dp, subq->region); + lock_rw_unlock(&qstate->env->hints->lock); if(!subiq->dp) { log_err("out of memory priming root, copydp"); fptr_ok(fptr_whitelist_modenv_kill_sub( @@ -917,6 +924,8 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id, subiq->num_target_queries = 0; subiq->dnssec_expected = iter_indicates_dnssec( qstate->env, subiq->dp, NULL, subq->qinfo.qclass); + } else { + lock_rw_unlock(&qstate->env->hints->lock); } /* this module stops, our submodule starts, and does the query. */ @@ -949,16 +958,21 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id, struct module_qstate* subq; if(!qname) return 0; + lock_rw_rdlock(&qstate->env->hints->lock); stub = hints_lookup_stub(qstate->env->hints, qname, qclass, iq->dp); /* The stub (if there is one) does not need priming. */ - if(!stub) + if(!stub) { + lock_rw_unlock(&qstate->env->hints->lock); return 0; + } stub_dp = stub->dp; /* if we have an auth_zone dp, and stub is equal, don't prime stub * yet, unless we want to fallback and avoid the auth_zone */ if(!iq->auth_zone_avoid && iq->dp && iq->dp->auth_dp && - query_dname_compare(iq->dp->name, stub_dp->name) == 0) + query_dname_compare(iq->dp->name, stub_dp->name) == 0) { + lock_rw_unlock(&qstate->env->hints->lock); return 0; + } /* is it a noprime stub (always use) */ if(stub->noprime) { @@ -967,13 +981,14 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id, /* copy the dp out of the fixed hints structure, so that * it can be changed when servicing this query */ iq->dp = delegpt_copy(stub_dp, qstate->region); + lock_rw_unlock(&qstate->env->hints->lock); if(!iq->dp) { log_err("out of memory priming stub"); errinf(qstate, "malloc failure, priming stub"); (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); return 1; /* return 1 to make module stop, with error */ } - log_nametypeclass(VERB_DETAIL, "use stub", stub_dp->name, + log_nametypeclass(VERB_DETAIL, "use stub", iq->dp->name, LDNS_RR_TYPE_NS, qclass); return r; } @@ -987,6 +1002,7 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id, if(!generate_sub_request(stub_dp->name, stub_dp->namelen, LDNS_RR_TYPE_NS, qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0, 0)) { + lock_rw_unlock(&qstate->env->hints->lock); verbose(VERB_ALGO, "could not prime stub"); errinf(qstate, "could not generate lookup for stub prime"); (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); @@ -999,6 +1015,7 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id, /* Set the initial delegation point to the hint. */ /* make copy to avoid use of stub dp by different qs/threads */ subiq->dp = delegpt_copy(stub_dp, subq->region); + lock_rw_unlock(&qstate->env->hints->lock); if(!subiq->dp) { log_err("out of memory priming stub, copydp"); fptr_ok(fptr_whitelist_modenv_kill_sub( @@ -1015,6 +1032,8 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id, subiq->wait_priming_stub = 1; subiq->dnssec_expected = iter_indicates_dnssec( qstate->env, subiq->dp, NULL, subq->qinfo.qclass); + } else { + lock_rw_unlock(&qstate->env->hints->lock); } /* this module stops, our submodule starts, and does the query. */ @@ -1615,15 +1634,18 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, lock_rw_unlock(&qstate->env->fwds->lock); /* forward zone root, no root prime needed */ /* fill in some dp - safety belt */ + lock_rw_rdlock(&qstate->env->hints->lock); iq->dp = hints_lookup_root(qstate->env->hints, iq->qchase.qclass); if(!iq->dp) { + lock_rw_unlock(&qstate->env->hints->lock); log_err("internal error: no hints dp"); errinf(qstate, "no hints for this class"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } iq->dp = delegpt_copy(iq->dp, qstate->region); + lock_rw_unlock(&qstate->env->hints->lock); if(!iq->dp) { log_err("out of memory in safety belt"); errinf(qstate, "malloc failure, in safety belt"); @@ -1692,16 +1714,19 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, /* use safety belt */ verbose(VERB_QUERY, "Cache has root NS but " "no addresses. Fallback to the safety belt."); + lock_rw_rdlock(&qstate->env->hints->lock); iq->dp = hints_lookup_root(qstate->env->hints, iq->qchase.qclass); /* note deleg_msg is from previous lookup, * but RD is on, so it is not used */ if(!iq->dp) { + lock_rw_unlock(&qstate->env->hints->lock); log_err("internal error: no hints dp"); return error_response(qstate, id, LDNS_RCODE_REFUSED); } iq->dp = delegpt_copy(iq->dp, qstate->region); + lock_rw_unlock(&qstate->env->hints->lock); if(!iq->dp) { log_err("out of memory in safety belt"); errinf(qstate, "malloc failure, in safety belt, for root"); @@ -1764,6 +1789,7 @@ processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq, } /* Do not send queries above stub, do not set delname to dp if * this is above stub without stub-first. */ + lock_rw_rdlock(&qstate->env->hints->lock); stub = hints_lookup_stub( qstate->env->hints, iq->qchase.qname, iq->qchase.qclass, iq->dp); @@ -1772,6 +1798,7 @@ processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq, delname = iq->dp->name; delnamelen = iq->dp->namelen; } + lock_rw_unlock(&qstate->env->hints->lock); } if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) { if(!dname_is_root(delname)) @@ -2085,8 +2112,9 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL); } if(!iq->dp->has_parent_side_NS && dname_is_root(iq->dp->name)) { - struct delegpt* p = hints_lookup_root(qstate->env->hints, - iq->qchase.qclass); + struct delegpt* p; + lock_rw_rdlock(&qstate->env->hints->lock); + p = hints_lookup_root(qstate->env->hints, iq->qchase.qclass); if(p) { struct delegpt_addr* a; iq->chase_flags &= ~BIT_RD; /* go to authorities */ @@ -2101,6 +2129,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, a->lame, a->tls_auth_name, -1, NULL); } } + lock_rw_unlock(&qstate->env->hints->lock); iq->dp->has_parent_side_NS = 1; } else if(!iq->dp->has_parent_side_NS) { if(!iter_lookup_parent_NS_from_cache(qstate->env, iq->dp, diff --git a/libunbound/context.c b/libunbound/context.c index f3a57d43d..a319f59cd 100644 --- a/libunbound/context.c +++ b/libunbound/context.c @@ -54,6 +54,7 @@ #include "util/edns.h" #include "sldns/sbuffer.h" #include "iterator/iter_fwd.h" +#include "iterator/iter_hints.h" int context_finalize(struct ub_ctx* ctx) @@ -89,6 +90,9 @@ context_finalize(struct ub_ctx* ctx) if(!(ctx->env->fwds = forwards_create()) || !forwards_apply_cfg(ctx->env->fwds, cfg)) return UB_INITFAIL; + if(!(ctx->env->hints = hints_create()) || + !hints_apply_cfg(ctx->env->hints, cfg)) + return UB_INITFAIL; if(!edns_strings_apply_cfg(ctx->env->edns_strings, cfg)) return UB_INITFAIL; if(!slabhash_is_size(ctx->env->msg_cache, cfg->msg_cache_size, diff --git a/libunbound/libunbound.c b/libunbound/libunbound.c index 68e1990bc..e7636d641 100644 --- a/libunbound/libunbound.c +++ b/libunbound/libunbound.c @@ -67,6 +67,7 @@ #include "services/listen_dnsport.h" #include "sldns/sbuffer.h" #include "iterator/iter_fwd.h" +#include "iterator/iter_hints.h" #ifdef HAVE_PTHREAD #include #endif @@ -381,6 +382,7 @@ ub_ctx_delete(struct ub_ctx* ctx) edns_known_options_delete(ctx->env); edns_strings_delete(ctx->env->edns_strings); forwards_delete(ctx->env->fwds); + hints_delete(ctx->env->hints); auth_zones_delete(ctx->env->auth_zones); free(ctx->env); } diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 9149365c8..788067852 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -70,8 +70,6 @@ #include "util/data/msgreply.h" #include "util/data/msgencode.h" #include "util/tube.h" -#include "iterator/iter_fwd.h" -#include "iterator/iter_hints.h" #include "sldns/sbuffer.h" #include "sldns/str2wire.h" #ifdef USE_DNSTAP @@ -100,7 +98,6 @@ libworker_delete_env(struct libworker* w) !w->is_bg || w->is_bg_thread); sldns_buffer_free(w->env->scratch_buffer); regional_destroy(w->env->scratch); - hints_delete(w->env->hints); ub_randfree(w->env->rnd); free(w->env); } @@ -158,24 +155,19 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) } w->env->scratch = regional_create_custom(cfg->msg_buffer_size); w->env->scratch_buffer = sldns_buffer_new(cfg->msg_buffer_size); - w->env->hints = hints_create(); - if(w->env->hints && !hints_apply_cfg(w->env->hints, cfg)) { - hints_delete(w->env->hints); - w->env->hints = NULL; - } #ifdef HAVE_SSL w->sslctx = connect_sslctx_create(NULL, NULL, cfg->tls_cert_bundle, cfg->tls_win_cert); if(!w->sslctx) { /* to make the setup fail after unlock */ - hints_delete(w->env->hints); - w->env->hints = NULL; + sldns_buffer_free(w->env->scratch_buffer); + w->env->scratch_buffer = NULL; } #endif if(!w->is_bg || w->is_bg_thread) { lock_basic_unlock(&ctx->cfglock); } - if(!w->env->scratch || !w->env->scratch_buffer || !w->env->hints) { + if(!w->env->scratch || !w->env->scratch_buffer) { libworker_delete(w); return NULL; } diff --git a/pythonmod/interface.i b/pythonmod/interface.i index d9839fc38..5bc7e8492 100644 --- a/pythonmod/interface.i +++ b/pythonmod/interface.i @@ -1455,10 +1455,14 @@ struct delegpt* find_delegation(struct module_qstate* qstate, char *nm, size_t n dname_str((uint8_t*)nm, b); continue; } + lock_rw_rdlock(&qstate->env->hints->lock); stub = hints_lookup_stub(qstate->env->hints, qinfo.qname, qinfo.qclass, dp); if (stub) { - return stub->dp; + struct delegpt* stubdp = delegpt_copy(stub->dp, region); + lock_rw_unlock(&qstate->env->hints->lock); + return stubdp; } else { + lock_rw_unlock(&qstate->env->hints->lock); return dp; } } diff --git a/util/module.h b/util/module.h index 65b82b59c..8e7aca635 100644 --- a/util/module.h +++ b/util/module.h @@ -514,7 +514,7 @@ struct module_env { * iterator forwarder information. */ struct iter_forwards* fwds; /** - * iterator forwarder information. per-thread, created by worker. + * iterator stub information. * The hints -- these aren't stored in the cache because they don't * expire. The hints are always used to "prime" the cache. Note * that both root hints and stub zone "hints" are stored in this From e146a9212535582acc1165a3c281e32d10583323 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 5 Jan 2024 16:14:38 +0100 Subject: [PATCH 026/125] - fast-reload, helpful comments for hints lookup function return value. --- iterator/iter_hints.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iterator/iter_hints.h b/iterator/iter_hints.h index 23af751ef..f201d8c11 100644 --- a/iterator/iter_hints.h +++ b/iterator/iter_hints.h @@ -101,6 +101,8 @@ int hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg); /** * Find root hints for the given class. + * The return value is contents of the hints structure, caller should + * lock and unlock a readlock on the hints structure. * @param hints: hint storage. * @param qclass: class for which root hints are requested. host order. * @return: NULL if no hints, or a ptr to stored hints. @@ -123,6 +125,8 @@ int hints_next_root(struct iter_hints* hints, uint16_t* qclass); * Given a qname/qclass combination, and the delegation point from the cache * for this qname/qclass, determine if this combination indicates that a * stub hint exists and must be primed. + * The return value is contents of the hints structure, caller should + * lock and unlock a readlock on the hints structure. * * @param hints: hint storage. * @param qname: The qname that generated the delegation point. From 5c3f72da15c6dd4c45e282fbd909c737e9539fc7 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 8 Jan 2024 10:16:56 +0100 Subject: [PATCH 027/125] - fast-reload, fix bug in fast reload printout, the strlist appendlist routine, and printout time statistics after the reload is done. --- daemon/remote.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 87246ddfe..b085884db 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3683,6 +3683,18 @@ fr_output_printf(struct fast_reload_thread* fr, const char* format, ...) return ret; } +/** fast reload thread, init time counters */ +static void +fr_init_time(struct timeval* time_start, struct timeval* time_read, + struct timeval* time_end) +{ + memset(time_start, 0, sizeof(*time_start)); + memset(time_read, 0, sizeof(*time_read)); + memset(time_end, 0, sizeof(*time_end)); + if(gettimeofday(time_start, NULL) < 0) + log_err("gettimeofday: %s", strerror(errno)); +} + /** fast reload thread, read config */ static int fr_read_config(struct fast_reload_thread* fr, struct config_file** newcfg) @@ -3718,13 +3730,36 @@ fr_read_config(struct fast_reload_thread* fr, struct config_file** newcfg) return 1; } +/** fast reload thread, finish timers */ +static int +fr_finish_time(struct fast_reload_thread* fr, struct timeval* time_start, + struct timeval* time_read, struct timeval* time_end) +{ + struct timeval total, readtime; + if(gettimeofday(time_end, NULL) < 0) + log_err("gettimeofday: %s", strerror(errno)); + + timeval_subtract(&total, time_end, time_start); + timeval_subtract(&readtime, time_read, time_start); + if(!fr_output_printf(fr, "read disk %3d.%6.6ds\n", + (int)readtime.tv_sec, (int)readtime.tv_usec)) + return 0; + if(!fr_output_printf(fr, "total time %3d.%6.6ds\n", (int)total.tv_sec, + (int)total.tv_usec)) + return 0; + fr_send_notification(fr, fast_reload_notification_printout); + return 1; +} + /** fast reload thread, load config */ static int -fr_load_config(struct fast_reload_thread* fr) +fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read) { struct config_file* newcfg = NULL; if(!fr_read_config(fr, &newcfg)) return 0; + if(gettimeofday(time_read, NULL) < 0) + log_err("gettimeofday: %s", strerror(errno)); if(fr_poll_for_quit(fr)) { config_delete(newcfg); return 1; @@ -3738,9 +3773,11 @@ fr_load_config(struct fast_reload_thread* fr) static void* fast_reload_thread_main(void* arg) { struct fast_reload_thread* fast_reload_thread = (struct fast_reload_thread*)arg; + struct timeval time_start, time_read, time_end; log_thread_set(&fast_reload_thread->threadnum); verbose(VERB_ALGO, "start fast reload thread"); + fr_init_time(&time_start, &time_read, &time_end); if(fr_poll_for_quit(fast_reload_thread)) goto done; @@ -3752,7 +3789,13 @@ static void* fast_reload_thread_main(void* arg) if(fr_poll_for_quit(fast_reload_thread)) goto done; - if(!fr_load_config(fast_reload_thread)) + if(!fr_load_config(fast_reload_thread, &time_read)) + goto done_error; + if(fr_poll_for_quit(fast_reload_thread)) + goto done; + + if(!fr_finish_time(fast_reload_thread, &time_start, &time_read, + &time_end)) goto done_error; if(fr_poll_for_quit(fast_reload_thread)) goto done; @@ -4151,6 +4194,8 @@ static void cfg_strlist_append_listhead(struct config_strlist_head* list, struct config_strlist_head* more) { + if(!more->first) + return; if(list->last) list->last->next = more->first; else From 94ef3378c4a44f41f89b41f2d8341da3dbf55824 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 8 Jan 2024 14:53:31 +0100 Subject: [PATCH 028/125] - fast-reload, keep track of reloadtime and deletestime and print them. --- daemon/remote.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index b085884db..9571cb885 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3686,10 +3686,11 @@ fr_output_printf(struct fast_reload_thread* fr, const char* format, ...) /** fast reload thread, init time counters */ static void fr_init_time(struct timeval* time_start, struct timeval* time_read, - struct timeval* time_end) + struct timeval* time_reload, struct timeval* time_end) { memset(time_start, 0, sizeof(*time_start)); memset(time_read, 0, sizeof(*time_read)); + memset(time_reload, 0, sizeof(*time_reload)); memset(time_end, 0, sizeof(*time_end)); if(gettimeofday(time_start, NULL) < 0) log_err("gettimeofday: %s", strerror(errno)); @@ -3733,17 +3734,26 @@ fr_read_config(struct fast_reload_thread* fr, struct config_file** newcfg) /** fast reload thread, finish timers */ static int fr_finish_time(struct fast_reload_thread* fr, struct timeval* time_start, - struct timeval* time_read, struct timeval* time_end) + struct timeval* time_read, struct timeval* time_reload, + struct timeval* time_end) { - struct timeval total, readtime; + struct timeval total, readtime, deletetime, reloadtime; if(gettimeofday(time_end, NULL) < 0) log_err("gettimeofday: %s", strerror(errno)); timeval_subtract(&total, time_end, time_start); timeval_subtract(&readtime, time_read, time_start); + timeval_subtract(&reloadtime, time_reload, time_read); + timeval_subtract(&deletetime, time_end, time_reload); if(!fr_output_printf(fr, "read disk %3d.%6.6ds\n", (int)readtime.tv_sec, (int)readtime.tv_usec)) return 0; + if(!fr_output_printf(fr, "reload %3d.%6.6ds\n", + (int)reloadtime.tv_sec, (int)reloadtime.tv_usec)) + return 0; + if(!fr_output_printf(fr, "deletes %3d.%6.6ds\n", + (int)deletetime.tv_sec, (int)deletetime.tv_usec)) + return 0; if(!fr_output_printf(fr, "total time %3d.%6.6ds\n", (int)total.tv_sec, (int)total.tv_usec)) return 0; @@ -3753,7 +3763,8 @@ fr_finish_time(struct fast_reload_thread* fr, struct timeval* time_start, /** fast reload thread, load config */ static int -fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read) +fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, + struct timeval* time_reload) { struct config_file* newcfg = NULL; if(!fr_read_config(fr, &newcfg)) @@ -3765,6 +3776,9 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read) return 1; } + if(gettimeofday(time_reload, NULL) < 0) + log_err("gettimeofday: %s", strerror(errno)); + config_delete(newcfg); return 1; } @@ -3773,11 +3787,11 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read) static void* fast_reload_thread_main(void* arg) { struct fast_reload_thread* fast_reload_thread = (struct fast_reload_thread*)arg; - struct timeval time_start, time_read, time_end; + struct timeval time_start, time_read, time_reload, time_end; log_thread_set(&fast_reload_thread->threadnum); verbose(VERB_ALGO, "start fast reload thread"); - fr_init_time(&time_start, &time_read, &time_end); + fr_init_time(&time_start, &time_read, &time_reload, &time_end); if(fr_poll_for_quit(fast_reload_thread)) goto done; @@ -3789,13 +3803,13 @@ static void* fast_reload_thread_main(void* arg) if(fr_poll_for_quit(fast_reload_thread)) goto done; - if(!fr_load_config(fast_reload_thread, &time_read)) + if(!fr_load_config(fast_reload_thread, &time_read, &time_reload)) goto done_error; if(fr_poll_for_quit(fast_reload_thread)) goto done; if(!fr_finish_time(fast_reload_thread, &time_start, &time_read, - &time_end)) + &time_reload, &time_end)) goto done_error; if(fr_poll_for_quit(fast_reload_thread)) goto done; From 38573f42bf4856e074e0af4ccd96b32da027f619 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 8 Jan 2024 14:59:03 +0100 Subject: [PATCH 029/125] - fast-reload, keep track of constructtime and print it. --- daemon/remote.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 9571cb885..d83db64d6 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3686,10 +3686,12 @@ fr_output_printf(struct fast_reload_thread* fr, const char* format, ...) /** fast reload thread, init time counters */ static void fr_init_time(struct timeval* time_start, struct timeval* time_read, - struct timeval* time_reload, struct timeval* time_end) + struct timeval* time_construct, struct timeval* time_reload, + struct timeval* time_end) { memset(time_start, 0, sizeof(*time_start)); memset(time_read, 0, sizeof(*time_read)); + memset(time_construct, 0, sizeof(*time_construct)); memset(time_reload, 0, sizeof(*time_reload)); memset(time_end, 0, sizeof(*time_end)); if(gettimeofday(time_start, NULL) < 0) @@ -3734,20 +3736,24 @@ fr_read_config(struct fast_reload_thread* fr, struct config_file** newcfg) /** fast reload thread, finish timers */ static int fr_finish_time(struct fast_reload_thread* fr, struct timeval* time_start, - struct timeval* time_read, struct timeval* time_reload, - struct timeval* time_end) + struct timeval* time_read, struct timeval* time_construct, + struct timeval* time_reload, struct timeval* time_end) { - struct timeval total, readtime, deletetime, reloadtime; + struct timeval total, readtime, constructtime, reloadtime, deletetime; if(gettimeofday(time_end, NULL) < 0) log_err("gettimeofday: %s", strerror(errno)); timeval_subtract(&total, time_end, time_start); timeval_subtract(&readtime, time_read, time_start); - timeval_subtract(&reloadtime, time_reload, time_read); + timeval_subtract(&constructtime, time_construct, time_read); + timeval_subtract(&reloadtime, time_reload, time_construct); timeval_subtract(&deletetime, time_end, time_reload); if(!fr_output_printf(fr, "read disk %3d.%6.6ds\n", (int)readtime.tv_sec, (int)readtime.tv_usec)) return 0; + if(!fr_output_printf(fr, "construct %3d.%6.6ds\n", + (int)constructtime.tv_sec, (int)constructtime.tv_usec)) + return 0; if(!fr_output_printf(fr, "reload %3d.%6.6ds\n", (int)reloadtime.tv_sec, (int)reloadtime.tv_usec)) return 0; @@ -3764,7 +3770,7 @@ fr_finish_time(struct fast_reload_thread* fr, struct timeval* time_start, /** fast reload thread, load config */ static int fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, - struct timeval* time_reload) + struct timeval* time_construct, struct timeval* time_reload) { struct config_file* newcfg = NULL; if(!fr_read_config(fr, &newcfg)) @@ -3775,6 +3781,8 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, config_delete(newcfg); return 1; } + if(gettimeofday(time_construct, NULL) < 0) + log_err("gettimeofday: %s", strerror(errno)); if(gettimeofday(time_reload, NULL) < 0) log_err("gettimeofday: %s", strerror(errno)); @@ -3787,11 +3795,13 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, static void* fast_reload_thread_main(void* arg) { struct fast_reload_thread* fast_reload_thread = (struct fast_reload_thread*)arg; - struct timeval time_start, time_read, time_reload, time_end; + struct timeval time_start, time_read, time_construct, time_reload, + time_end; log_thread_set(&fast_reload_thread->threadnum); verbose(VERB_ALGO, "start fast reload thread"); - fr_init_time(&time_start, &time_read, &time_reload, &time_end); + fr_init_time(&time_start, &time_read, &time_construct, &time_reload, + &time_end); if(fr_poll_for_quit(fast_reload_thread)) goto done; @@ -3803,13 +3813,14 @@ static void* fast_reload_thread_main(void* arg) if(fr_poll_for_quit(fast_reload_thread)) goto done; - if(!fr_load_config(fast_reload_thread, &time_read, &time_reload)) + if(!fr_load_config(fast_reload_thread, &time_read, &time_construct, + &time_reload)) goto done_error; if(fr_poll_for_quit(fast_reload_thread)) goto done; if(!fr_finish_time(fast_reload_thread, &time_start, &time_read, - &time_reload, &time_end)) + &time_construct, &time_reload, &time_end)) goto done_error; if(fr_poll_for_quit(fast_reload_thread)) goto done; From 53b647e2574ebd50462e9a6b9df0aa527dd08c34 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 8 Jan 2024 15:25:59 +0100 Subject: [PATCH 030/125] - fast-reload, construct new items. --- daemon/remote.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index d83db64d6..601866b4e 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3698,6 +3698,19 @@ fr_init_time(struct timeval* time_start, struct timeval* time_read, log_err("gettimeofday: %s", strerror(errno)); } +/** + * Structure with constructed elements for use during fast reload. + * At the start it contains the tree items for the new config. + * After the tree items are swapped into the server, the old elements + * are kept in here. They can then be deleted. + */ +struct fast_reload_construct { + /** construct for forwards */ + struct iter_forwards* fwds; + /** construct for stubs */ + struct iter_hints* hints; +}; + /** fast reload thread, read config */ static int fr_read_config(struct fast_reload_thread* fr, struct config_file** newcfg) @@ -3733,6 +3746,42 @@ fr_read_config(struct fast_reload_thread* fr, struct config_file** newcfg) return 1; } +/** fast reload thread, clear construct information, deletes items */ +static void +fr_construct_clear(struct fast_reload_construct* ct) +{ + if(!ct) + return; + forwards_delete(ct->fwds); + hints_delete(ct->hints); +} + +/** fast reload thread, construct from config the new items */ +static int +fr_construct_from_config(struct fast_reload_thread* fr, + struct config_file* newcfg, struct fast_reload_construct* ct) +{ + if(!(ct->fwds = forwards_create())) + return 0; + if(!forwards_apply_cfg(ct->fwds, newcfg)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + + if(!(ct->hints = hints_create())) + return 0; + if(!hints_apply_cfg(ct->hints, newcfg)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + + return 1; +} + /** fast reload thread, finish timers */ static int fr_finish_time(struct fast_reload_thread* fr, struct timeval* time_start, @@ -3772,7 +3821,11 @@ static int fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, struct timeval* time_construct, struct timeval* time_reload) { + struct fast_reload_construct ct; struct config_file* newcfg = NULL; + memset(&ct, 0, sizeof(ct)); + + /* Read file. */ if(!fr_read_config(fr, &newcfg)) return 0; if(gettimeofday(time_read, NULL) < 0) @@ -3781,13 +3834,32 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, config_delete(newcfg); return 1; } + + /* Construct items. */ + if(!fr_construct_from_config(fr, newcfg, &ct)) { + config_delete(newcfg); + if(!fr_output_printf(fr, "Could not construct from the " + "config, check for errors with unbound-checkconf, or " + "out of memory.\n")) + return 0; + fr_send_notification(fr, fast_reload_notification_printout); + return 0; + } if(gettimeofday(time_construct, NULL) < 0) log_err("gettimeofday: %s", strerror(errno)); + if(fr_poll_for_quit(fr)) { + config_delete(newcfg); + fr_construct_clear(&ct); + return 1; + } + /* Reload server. */ if(gettimeofday(time_reload, NULL) < 0) log_err("gettimeofday: %s", strerror(errno)); + /* Delete old. */ config_delete(newcfg); + fr_construct_clear(&ct); return 1; } From b0da43e5fba42d8a47a2de5124568d380f85bda2 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 8 Jan 2024 16:24:03 +0100 Subject: [PATCH 031/125] - fast-reload, better comment. --- daemon/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 601866b4e..28c2ceccd 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4867,7 +4867,7 @@ fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s) } worker->daemon->fast_reload_thread->printq->remote = *ssl; s->rc = NULL; /* move away the rc state */ - /* Nothing to print right now, so no need to activate it. */ + /* Nothing to print right now, so no need to have it active. */ comm_point_stop_listening(worker->daemon->fast_reload_thread->printq->client_cp); /* Start fast reload thread */ From a57c94371f35e9edab0bfcd11b9be814ad20afe7 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 9 Jan 2024 12:06:00 +0100 Subject: [PATCH 032/125] - fast-reload, reload the config and swap trees for forwards and stub hints. --- daemon/remote.c | 79 +++++++++++++++++++++++++++++++++++++++++++ iterator/iter_fwd.c | 8 +++++ iterator/iter_fwd.h | 9 +++++ iterator/iter_hints.c | 8 +++++ iterator/iter_hints.h | 9 +++++ 5 files changed, 113 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index 28c2ceccd..ead1067f2 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3709,6 +3709,9 @@ struct fast_reload_construct { struct iter_forwards* fwds; /** construct for stubs */ struct iter_hints* hints; + /** storage for the old configuration elements. The outer struct + * is allocated with malloc here, the items are from config. */ + struct config_file* oldcfg; }; /** fast reload thread, read config */ @@ -3754,6 +3757,13 @@ fr_construct_clear(struct fast_reload_construct* ct) return; forwards_delete(ct->fwds); hints_delete(ct->hints); + /* Delete the log identity here so that the global value is not + * reset by config_delete. */ + if(ct->oldcfg->log_identity) { + free(ct->oldcfg->log_identity); + ct->oldcfg->log_identity = NULL; + } + config_delete(ct->oldcfg); } /** fast reload thread, construct from config the new items */ @@ -3779,6 +3789,11 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; + if(!(ct->oldcfg = (struct config_file*)calloc(1, + sizeof(*ct->oldcfg)))) { + fr_construct_clear(ct); + return 0; + } return 1; } @@ -3816,6 +3831,62 @@ fr_finish_time(struct fast_reload_thread* fr, struct timeval* time_start, return 1; } +/** fast reload thread, reload config with putting the new config items + * in place and swapping out the old items. */ +static int +fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, + struct fast_reload_construct* ct) +{ + struct daemon* daemon = fr->worker->daemon; + struct module_env* env = daemon->env; + + /* Grab big locks to satisfy lock conditions. */ + lock_rw_wrlock(&ct->fwds->lock); + lock_rw_wrlock(&ct->hints->lock); + lock_rw_wrlock(&env->fwds->lock); + lock_rw_wrlock(&env->hints->lock); + + /* Store old config elements. */ + *ct->oldcfg = *env->cfg; + /* Insert new config elements. */ + *env->cfg = *newcfg; + if(env->cfg->log_identity || ct->oldcfg->log_identity) { + /* pick up new log_identity string to use for log output. */ + log_ident_set_or_default(env->cfg->log_identity); + } + /* the newcfg elements are in env->cfg, so should not be freed here. */ + memset(newcfg, 0, sizeof(*newcfg)); + + /* Quickly swap the tree roots themselves with the already allocated + * elements. This is a quick swap operation on the pointer. + * The other threads are stopped and locks are held, so that a + * consistent view of the configuration, before, and after, exists + * towards the state machine for query resolution. */ + forwards_swap_tree(env->fwds, ct->fwds); + hints_swap_tree(env->hints, ct->hints); + + /* Set globals with new config. */ + config_apply(env->cfg); + + lock_rw_unlock(&env->fwds->lock); + lock_rw_unlock(&env->hints->lock); + lock_rw_unlock(&ct->fwds->lock); + lock_rw_unlock(&ct->hints->lock); + + return 1; +} + +/** fast reload thread, reload ipc communication to stop and start threads. */ +static int +fr_reload_ipc(struct fast_reload_thread* fr, struct config_file* newcfg, + struct fast_reload_construct* ct) +{ + if(!fr_reload_config(fr, newcfg, ct)) { + return 0; + } + return 1; +} + /** fast reload thread, load config */ static int fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, @@ -3854,6 +3925,14 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, } /* Reload server. */ + if(!fr_reload_ipc(fr, newcfg, &ct)) { + config_delete(newcfg); + fr_construct_clear(&ct); + if(!fr_output_printf(fr, "error: reload failed\n")) + return 0; + fr_send_notification(fr, fast_reload_notification_printout); + return 0; + } if(gettimeofday(time_reload, NULL) < 0) log_err("gettimeofday: %s", strerror(errno)); diff --git a/iterator/iter_fwd.c b/iterator/iter_fwd.c index cf418b072..e2697bab4 100644 --- a/iterator/iter_fwd.c +++ b/iterator/iter_fwd.c @@ -539,3 +539,11 @@ forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm) fwd_zone_free(z); fwd_init_parents(fwd); } + +void +forwards_swap_tree(struct iter_forwards* fwd, struct iter_forwards* data) +{ + rbtree_type* oldtree = fwd->tree; + fwd->tree = data->tree; + data->tree = oldtree; +} diff --git a/iterator/iter_fwd.h b/iterator/iter_fwd.h index c1160b853..e51165ede 100644 --- a/iterator/iter_fwd.h +++ b/iterator/iter_fwd.h @@ -207,4 +207,13 @@ int forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm); void forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm); +/** + * Swap internal tree with preallocated entries. Caller should manage + * the locks. + * @param fwd: the forward data structure. + * @param data: the data structure used to take elements from. This contains + * the old elements on return. + */ +void forwards_swap_tree(struct iter_forwards* fwd, struct iter_forwards* data); + #endif /* ITERATOR_ITER_FWD_H */ diff --git a/iterator/iter_hints.c b/iterator/iter_hints.c index a56aa8e40..ce2741eaf 100644 --- a/iterator/iter_hints.c +++ b/iterator/iter_hints.c @@ -575,3 +575,11 @@ hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm) hints_stub_free(z); name_tree_init_parents(&hints->tree); } + +void +hints_swap_tree(struct iter_hints* hints, struct iter_hints* data) +{ + rbnode_type* oldroot = hints->tree.root; + hints->tree.root = data->tree.root; + data->tree.root = oldroot; +} diff --git a/iterator/iter_hints.h b/iterator/iter_hints.h index f201d8c11..6f6b6dc68 100644 --- a/iterator/iter_hints.h +++ b/iterator/iter_hints.h @@ -168,4 +168,13 @@ int hints_add_stub(struct iter_hints* hints, uint16_t c, struct delegpt* dp, */ void hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm); +/** + * Swap internal tree with preallocated entries. Caller should manage + * the locks. + * @param hints: the hints data structure. + * @param data: the data structure used to take elements from. This contains + * the old elements on return. + */ +void hints_swap_tree(struct iter_hints* hints, struct iter_hints* data); + #endif /* ITERATOR_ITER_HINTS_H */ From 9c1274ed303628712830eac50c540cd058469003 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 9 Jan 2024 12:09:39 +0100 Subject: [PATCH 033/125] - fast-reload, in forwards_swap_tree set protection of trees with locks. --- iterator/iter_fwd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/iterator/iter_fwd.c b/iterator/iter_fwd.c index e2697bab4..7954f865c 100644 --- a/iterator/iter_fwd.c +++ b/iterator/iter_fwd.c @@ -544,6 +544,14 @@ void forwards_swap_tree(struct iter_forwards* fwd, struct iter_forwards* data) { rbtree_type* oldtree = fwd->tree; + if(oldtree) { + lock_unprotect(&fwd->lock, oldtree); + } + if(data->tree) { + lock_unprotect(&data->lock, data->tree); + } fwd->tree = data->tree; data->tree = oldtree; + lock_protect(&fwd->lock, fwd->tree, sizeof(*fwd->tree)); + lock_protect(&data->lock, data->tree, sizeof(*data->tree)); } From b567a5c07ebc43f9c4ba258f879efbc863f7496c Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 9 Jan 2024 12:12:14 +0100 Subject: [PATCH 034/125] - fast-reload, in hints_swap_tree also swap the node count of the trees. --- iterator/iter_hints.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/iterator/iter_hints.c b/iterator/iter_hints.c index ce2741eaf..5a95b78cd 100644 --- a/iterator/iter_hints.c +++ b/iterator/iter_hints.c @@ -580,6 +580,9 @@ void hints_swap_tree(struct iter_hints* hints, struct iter_hints* data) { rbnode_type* oldroot = hints->tree.root; + size_t oldcount = hints->tree.count; hints->tree.root = data->tree.root; + hints->tree.count = data->tree.count; data->tree.root = oldroot; + data->tree.count = oldcount; } From eee7d884579bd1669cff847676211219ee15ae3a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 9 Jan 2024 15:02:27 +0100 Subject: [PATCH 035/125] - fast-reload, reload ipc to stop and start threads. --- daemon/remote.c | 334 +++++++++++++++++++++++++++++++++++++++--------- daemon/remote.h | 11 +- daemon/worker.c | 60 +++++++++ daemon/worker.h | 6 +- 4 files changed, 350 insertions(+), 61 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index ead1067f2..3234d625d 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -122,6 +122,7 @@ static int fr_printq_empty(struct fast_reload_printq* printq); static void fr_printq_list_insert(struct fast_reload_printq* printq, struct daemon* daemon); static void fr_printq_remove(struct fast_reload_printq* printq); +static void fr_check_cmd_from_thread(struct fast_reload_thread* fr); static int remote_setup_ctx(struct daemon_remote* rc, struct config_file* cfg) @@ -3509,6 +3510,12 @@ fr_notification_to_string(enum fast_reload_notification status) return "exited"; case fast_reload_notification_printout: return "printout"; + case fast_reload_notification_reload_stop: + return "reload_stop"; + case fast_reload_notification_reload_ack: + return "reload_ack"; + case fast_reload_notification_reload_start: + return "reload_start"; default: break; } @@ -3792,6 +3799,7 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(!(ct->oldcfg = (struct config_file*)calloc(1, sizeof(*ct->oldcfg)))) { fr_construct_clear(ct); + log_err("out of memory"); return 0; } return 1; @@ -3876,15 +3884,80 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, return 1; } +/** fast reload, poll for ack incoming. */ +static void +fr_poll_for_ack(struct fast_reload_thread* fr) +{ + int loopexit = 0, bcount = 0; + uint32_t cmd; + ssize_t ret; + + if(fr->need_to_quit) + return; + /* Is there data? */ + if(!sock_poll_timeout(fr->commpair[1], -1, 1, 0, NULL)) { + log_err("fr_poll_for_ack: poll failed"); + return; + } + + /* Read the data */ + while(1) { + if(++loopexit > IPC_LOOP_MAX) { + log_err("fr_poll_for_ack: recv loops %s", + sock_strerror(errno)); + return; + } + ret = recv(fr->commpair[1], ((char*)&cmd)+bcount, + sizeof(cmd)-bcount, 0); + if(ret == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) + continue; /* Try again. */ + log_err("fr_poll_for_ack: recv: %s", + sock_strerror(errno)); + return; + } else if(ret+(ssize_t)bcount != sizeof(cmd)) { + bcount += ret; + if((size_t)bcount < sizeof(cmd)) + continue; + } + break; + } + if(cmd == fast_reload_notification_exit) { + fr->need_to_quit = 1; + verbose(VERB_ALGO, "fast reload wait for ack: " + "exit notification received"); + return; + } + if(cmd != fast_reload_notification_reload_ack) { + verbose(VERB_ALGO, "fast reload wait for ack: " + "wrong notification %d", (int)cmd); + } +} + /** fast reload thread, reload ipc communication to stop and start threads. */ static int fr_reload_ipc(struct fast_reload_thread* fr, struct config_file* newcfg, struct fast_reload_construct* ct) { + int result = 1; + fr_send_notification(fr, fast_reload_notification_reload_stop); + fr_poll_for_ack(fr); if(!fr_reload_config(fr, newcfg, ct)) { - return 0; + result = 0; } - return 1; + fr_send_notification(fr, fast_reload_notification_reload_start); + return result; } /** fast reload thread, load config */ @@ -4291,6 +4364,8 @@ fast_reload_thread_setup(struct worker* worker) fr->threadnum = numworkers+2; fr->commpair[0] = -1; fr->commpair[1] = -1; + fr->commreload[0] = -1; + fr->commreload[1] = -1; if(!create_socketpair(fr->commpair, worker->daemon->rand)) { free(fr); worker->daemon->fast_reload_thread = NULL; @@ -4306,6 +4381,16 @@ fast_reload_thread_setup(struct worker* worker) worker->daemon->fast_reload_thread = NULL; return 0; } + if(!create_socketpair(fr->commreload, worker->daemon->rand)) { + sock_close(fr->commpair[0]); + sock_close(fr->commpair[1]); + sock_close(fr->commreload[0]); + sock_close(fr->commreload[1]); + free(fr->fr_output); + free(fr); + worker->daemon->fast_reload_thread = NULL; + return 0; + } lock_basic_init(&fr->fr_output_lock); lock_protect(&fr->fr_output_lock, fr->fr_output, sizeof(*fr->fr_output)); @@ -4327,6 +4412,8 @@ fast_reload_thread_desetup(struct fast_reload_thread* fast_reload_thread) ub_event_free(fast_reload_thread->service_event); sock_close(fast_reload_thread->commpair[0]); sock_close(fast_reload_thread->commpair[1]); + sock_close(fast_reload_thread->commreload[0]); + sock_close(fast_reload_thread->commreload[1]); if(fast_reload_thread->printq) { fr_main_perform_printout(fast_reload_thread); /* If it is empty now, there is nothing to print on fd. */ @@ -4352,6 +4439,64 @@ fast_reload_thread_desetup(struct fast_reload_thread* fast_reload_thread) free(fast_reload_thread); } +/** + * Fast reload thread, send a command to the thread. Blocking on timeout. + * It handles received input from the thread, if any is received. + */ +static void +fr_send_cmd_to(struct fast_reload_thread* fr, + enum fast_reload_notification status, int check_cmds, int blocking) +{ + int outevent, loopexit = 0, bcount = 0; + uint32_t cmd; + ssize_t ret; + verbose(VERB_ALGO, "send notification to fast reload thread: %s", + fr_notification_to_string(status)); + cmd = status; + while(1) { + if(++loopexit > IPC_LOOP_MAX) { + log_err("send notification to fast reload: could not send notification: loop"); + return; + } + if(check_cmds) + fr_check_cmd_from_thread(fr); + /* wait for socket to become writable */ + if(!sock_poll_timeout(fr->commpair[0], + (blocking?-1:IPC_NOTIFICATION_WAIT), + 0, 1, &outevent)) { + log_err("send notification to fast reload: poll failed"); + return; + } + if(!outevent) + continue; + ret = send(fr->commpair[0], ((char*)&cmd)+bcount, + sizeof(cmd)-bcount, 0); + if(ret == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) + continue; /* Try again. */ + log_err("send notification to fast reload: send: %s", + sock_strerror(errno)); + return; + } else if(ret+(ssize_t)bcount != sizeof(cmd)) { + bcount += ret; + if((size_t)bcount < sizeof(cmd)) + continue; + } + break; + } +} + /** Fast reload, the main thread handles that the fast reload thread has * exited. */ static void @@ -4411,6 +4556,131 @@ fr_main_perform_printout(struct fast_reload_thread* fr) comm_point_listen_for_rw(fr->printq->client_cp, 0, 1); } +/** fast reload, receive ack from workers that they are waiting, run + * by the mainthr after sending them reload_stop. */ +static void +fr_read_ack_from_workers(struct fast_reload_thread* fr) +{ + struct daemon* daemon = fr->worker->daemon; + /* Every worker sends one byte, wait for num-1 bytes. */ + int count=0, total=daemon->num-1; + while(count < total) { + uint8_t r; + ssize_t ret; + ret = recv(fr->commreload[0], &r, 1, 0); + if(ret == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) + continue; /* Try again */ + log_err("worker reload ack: recv failed: %s", + sock_strerror(errno)); + return; + } + count++; + verbose(VERB_ALGO, "worker reload ack from (uint8_t)%d", + (int)r); + } +} + +/** fast reload, poll for reload_start in mainthr waiting on a notification + * from the fast reload thread. */ +static void +fr_poll_for_reload_start(struct fast_reload_thread* fr) +{ + int loopexit = 0, bcount = 0; + uint32_t cmd; + ssize_t ret; + + /* Is there data? */ + if(!sock_poll_timeout(fr->commpair[0], -1, 1, 0, NULL)) { + log_err("fr_poll_for_reload_start: poll failed"); + return; + } + + /* Read the data */ + while(1) { + if(++loopexit > IPC_LOOP_MAX) { + log_err("fr_poll_for_reload_start: recv loops %s", + sock_strerror(errno)); + return; + } + ret = recv(fr->commpair[0], ((char*)&cmd)+bcount, + sizeof(cmd)-bcount, 0); + if(ret == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) + continue; /* Try again. */ + log_err("fr_poll_for_reload_start: recv: %s", + sock_strerror(errno)); + return; + } else if(ret+(ssize_t)bcount != sizeof(cmd)) { + bcount += ret; + if((size_t)bcount < sizeof(cmd)) + continue; + } + break; + } + if(cmd != fast_reload_notification_reload_start) { + verbose(VERB_ALGO, "fast reload wait for ack: " + "wrong notification %d", (int)cmd); + } +} + + +/** fast reload thread, handle reload_stop notification, send reload stop + * to other threads over IPC and collect their ack. When that is done, + * ack to the caller, the fast reload thread, and wait for it to send start. */ +static void +fr_main_perform_reload_stop(struct fast_reload_thread* fr) +{ + struct daemon* daemon = fr->worker->daemon; + int i; + + /* Send reload_stop to other threads. */ + for(i=0; inum; i++) { + if(i == fr->worker->thread_num) + continue; /* Do not send to ourselves. */ + worker_send_cmd(daemon->workers[i], worker_cmd_reload_stop); + } + + /* Wait for the other threads to ack. */ + fr_read_ack_from_workers(fr); + + /* Send ack to fast reload thread. */ + fr_send_cmd_to(fr, fast_reload_notification_reload_ack, 0, 1); + + /* Wait for reload_start from fast reload thread to resume. */ + fr_poll_for_reload_start(fr); + + /* Send reload_start to other threads */ + for(i=0; inum; i++) { + if(i == fr->worker->thread_num) + continue; /* Do not send to ourselves. */ + worker_send_cmd(daemon->workers[i], worker_cmd_reload_start); + } + verbose(VERB_ALGO, "worker resume after reload"); +} + /** Fast reload, perform the command received from the fast reload thread */ static void fr_main_perform_cmd(struct fast_reload_thread* fr, @@ -4424,6 +4694,8 @@ fr_main_perform_cmd(struct fast_reload_thread* fr, status == fast_reload_notification_done_error || status == fast_reload_notification_exited) { fr_main_perform_done(fr); + } else if(status == fast_reload_notification_reload_stop) { + fr_main_perform_reload_stop(fr); } else { log_err("main received unknown status from fast reload: %d %s", (int)status, fr_notification_to_string(status)); @@ -4819,68 +5091,12 @@ fr_printq_remove(struct fast_reload_printq* printq) fr_printq_delete(printq); } -/** - * Fast reload thread, send a command to the thread. Blocking on timeout. - * It handles received input from the thread, if any is received. - */ -static void -fr_send_cmd_to(struct fast_reload_thread* fr, - enum fast_reload_notification status) -{ - int outevent, loopexit = 0, bcount = 0; - uint32_t cmd; - ssize_t ret; - verbose(VERB_ALGO, "send notification to fast reload thread: %s", - fr_notification_to_string(status)); - cmd = status; - while(1) { - if(++loopexit > IPC_LOOP_MAX) { - log_err("send notification to fast reload: could not send notification: loop"); - return; - } - fr_check_cmd_from_thread(fr); - /* wait for socket to become writable */ - if(!sock_poll_timeout(fr->commpair[0], IPC_NOTIFICATION_WAIT, - 0, 1, &outevent)) { - log_err("send notification to fast reload: poll failed"); - return; - } - if(!outevent) - continue; - ret = send(fr->commpair[0], ((char*)&cmd)+bcount, - sizeof(cmd)-bcount, 0); - if(ret == -1) { - if( -#ifndef USE_WINSOCK - errno == EINTR || errno == EAGAIN -# ifdef EWOULDBLOCK - || errno == EWOULDBLOCK -# endif -#else - WSAGetLastError() == WSAEINTR || - WSAGetLastError() == WSAEINPROGRESS || - WSAGetLastError() == WSAEWOULDBLOCK -#endif - ) - continue; /* Try again. */ - log_err("send notification to fast reload: send: %s", - sock_strerror(errno)); - return; - } else if(ret+(ssize_t)bcount != sizeof(cmd)) { - bcount += ret; - if((size_t)bcount < sizeof(cmd)) - continue; - } - break; - } -} - /** fast reload thread, send stop command to the thread, from the main thread. */ static void fr_send_stop(struct fast_reload_thread* fr) { - fr_send_cmd_to(fr, fast_reload_notification_exit); + fr_send_cmd_to(fr, fast_reload_notification_exit, 1, 0); } void diff --git a/daemon/remote.h b/daemon/remote.h index 82441598e..3121f696a 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -137,7 +137,13 @@ enum fast_reload_notification { /** the fast reload thread has exited, after being told to exit */ fast_reload_notification_exited = 4, /** the fast reload thread has information to print out */ - fast_reload_notification_printout = 5 + fast_reload_notification_printout = 5, + /** stop as part of the reload the thread and other threads */ + fast_reload_notification_reload_stop = 6, + /** ack the stop as part of the reload */ + fast_reload_notification_reload_ack = 7, + /** resume from stop as part of the reload */ + fast_reload_notification_reload_start = 8 }; /** @@ -206,6 +212,9 @@ struct fast_reload_thread { * to be printed. The remote control thread can pick them up with * the lock. */ struct config_strlist_head* fr_output; + + /** communication socket pair, to respond to the reload request */ + int commreload[2]; }; /** diff --git a/daemon/worker.c b/daemon/worker.c index c4ae79352..7ee0d8d54 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -369,6 +369,62 @@ worker_check_request(sldns_buffer* pkt, struct worker* worker, return; } +/** stop and wait to resume the worker */ +static void +worker_stop_and_wait(struct worker* worker) +{ + uint8_t c = (uint8_t)worker->thread_num; + ssize_t ret; + uint8_t* buf = NULL; + uint32_t len = 0, cmd; + while(1) { + ret = send(worker->daemon->fast_reload_thread->commreload[1], + &c, 1, 0); + if(ret == -1) { + if( +#ifndef USE_WINSOCK + errno == EINTR || errno == EAGAIN +# ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +# endif +#else + WSAGetLastError() == WSAEINTR || + WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK +#endif + ) + continue; /* Try again. */ + log_err("worker reload ack reply: send failed: %s", + sock_strerror(errno)); + break; + } + break; + } + /* wait for reload */ + if(!tube_read_msg(worker->cmd, &buf, &len, 0)) { + log_err("worker reload read reply failed"); + return; + } + if(len != sizeof(uint32_t)) { + log_err("worker reload reply, bad control msg length %d", + (int)len); + free(buf); + return; + } + cmd = sldns_read_uint32(buf); + free(buf); + if(cmd == worker_cmd_quit) { + /* quit anyway */ + verbose(VERB_ALGO, "reload reply, control cmd quit"); + comm_base_exit(worker->base); + return; + } + if(cmd != worker_cmd_reload_start) { + log_err("worker reload reply, wrong reply command"); + } + verbose(VERB_ALGO, "worker resume after reload"); +} + void worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg, size_t len, int error, void* arg) @@ -404,6 +460,10 @@ worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg, verbose(VERB_ALGO, "got control cmd remote"); daemon_remote_exec(worker); break; + case worker_cmd_reload_stop: + verbose(VERB_ALGO, "got control cmd reload_stop"); + worker_stop_and_wait(worker); + break; default: log_err("bad command %d", (int)cmd); break; diff --git a/daemon/worker.h b/daemon/worker.h index ab2fc728d..84e851d06 100644 --- a/daemon/worker.h +++ b/daemon/worker.h @@ -72,7 +72,11 @@ enum worker_commands { /** obtain statistics without statsclear */ worker_cmd_stats_noreset, /** execute remote control command */ - worker_cmd_remote + worker_cmd_remote, + /** for fast-reload, perform stop */ + worker_cmd_reload_stop, + /** for fast-reload, start again */ + worker_cmd_reload_start }; /** From bcaebb159318441a600ce3c7967f6bd799e0b314 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 11 Jan 2024 16:50:43 +0100 Subject: [PATCH 036/125] - fast-reload, unused forward declarations removed. --- iterator/iterator.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/iterator/iterator.h b/iterator/iterator.h index e253f3f7e..f748d47f6 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -46,8 +46,6 @@ #include "util/data/msgreply.h" #include "util/module.h" struct delegpt; -struct iter_hints; -struct iter_forwards; struct iter_donotq; struct iter_prep_list; struct iter_priv; From c3a754f4b4b6620d1f960096468efa1dbe5e5080 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 22 Jan 2024 13:51:13 +0100 Subject: [PATCH 037/125] - fast-reload, unit test that fast reload works with forwards and stubs. --- .../fast_reload_fwd.tdir/fast_reload_fwd.conf | 74 ++++++ .../fast_reload_fwd.conf2 | 74 ++++++ .../fast_reload_fwd.tdir/fast_reload_fwd.dsc | 16 ++ .../fast_reload_fwd.tdir/fast_reload_fwd.ns1 | 251 ++++++++++++++++++ .../fast_reload_fwd.tdir/fast_reload_fwd.ns2 | 251 ++++++++++++++++++ .../fast_reload_fwd.tdir/fast_reload_fwd.post | 21 ++ .../fast_reload_fwd.tdir/fast_reload_fwd.pre | 43 +++ .../fast_reload_fwd.tdir/fast_reload_fwd.test | 109 ++++++++ .../fast_reload_thread.post | 1 + 9 files changed, 840 insertions(+) create mode 100644 testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf create mode 100644 testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 create mode 100644 testdata/fast_reload_fwd.tdir/fast_reload_fwd.dsc create mode 100644 testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns1 create mode 100644 testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns2 create mode 100644 testdata/fast_reload_fwd.tdir/fast_reload_fwd.post create mode 100644 testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre create mode 100644 testdata/fast_reload_fwd.tdir/fast_reload_fwd.test diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf new file mode 100644 index 000000000..cc148053d --- /dev/null +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf @@ -0,0 +1,74 @@ +server: + verbosity: 4 + num-threads: 1 + interface: 127.0.0.1 + port: @PORT@ + use-syslog: no + directory: "" + pidfile: "unbound.pid" + chroot: "" + username: "" + do-not-query-localhost: no + +forward-zone: + name: "." + forward-addr: "127.0.0.1@12345" + +remote-control: + control-enable: yes + control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@ + control-use-cert: no + +forward-zone: + name: "example1.org" + forward-addr: "127.0.0.1@@NS1_PORT@" + +forward-zone: + name: "example2.org" + forward-addr: "127.0.0.1@@NS1_PORT@" + +forward-zone: + name: "example3.org" + forward-addr: "127.0.0.1@@NS1_PORT@" + +forward-zone: + name: "example4.org" + forward-addr: "127.0.0.1@@NS2_PORT@" + +forward-zone: + name: "example5.org" + forward-addr: "127.0.0.1@@NS2_PORT@" + +forward-zone: + name: "example6.org" + forward-addr: "127.0.0.1@@NS2_PORT@" + +stub-zone: + name: "stub1.org" + stub-addr: "127.0.0.1@@NS1_PORT@" + stub-prime: no + +stub-zone: + name: "stub2.org" + stub-addr: "127.0.0.1@@NS1_PORT@" + stub-prime: no + +stub-zone: + name: "stub3.org" + stub-addr: "127.0.0.1@@NS1_PORT@" + stub-prime: no + +stub-zone: + name: "stub4.org" + stub-addr: "127.0.0.1@@NS2_PORT@" + stub-prime: no + +stub-zone: + name: "stub5.org" + stub-addr: "127.0.0.1@@NS2_PORT@" + stub-prime: no + +stub-zone: + name: "stub6.org" + stub-addr: "127.0.0.1@@NS2_PORT@" + stub-prime: no diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 new file mode 100644 index 000000000..98f43c0ef --- /dev/null +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 @@ -0,0 +1,74 @@ +server: + verbosity: 4 + num-threads: 1 + interface: 127.0.0.1 + port: @PORT@ + use-syslog: no + directory: "" + pidfile: "unbound.pid" + chroot: "" + username: "" + do-not-query-localhost: no + +forward-zone: + name: "." + forward-addr: "127.0.0.1@12345" + +remote-control: + control-enable: yes + control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@ + control-use-cert: no + +forward-zone: + name: "example1.org" + forward-addr: "127.0.0.1@@NS2_PORT@" + +forward-zone: + name: "example2.org" + forward-addr: "127.0.0.1@@NS1_PORT@" + +forward-zone: + name: "example3.org" + forward-addr: "127.0.0.1@@NS2_PORT@" + +forward-zone: + name: "example4.org" + forward-addr: "127.0.0.1@@NS1_PORT@" + +forward-zone: + name: "example5.org" + forward-addr: "127.0.0.1@@NS2_PORT@" + +forward-zone: + name: "example6.org" + forward-addr: "127.0.0.1@@NS1_PORT@" + +stub-zone: + name: "stub1.org" + stub-addr: "127.0.0.1@@NS2_PORT@" + stub-prime: no + +stub-zone: + name: "stub2.org" + stub-addr: "127.0.0.1@@NS1_PORT@" + stub-prime: no + +stub-zone: + name: "stub3.org" + stub-addr: "127.0.0.1@@NS2_PORT@" + stub-prime: no + +stub-zone: + name: "stub4.org" + stub-addr: "127.0.0.1@@NS1_PORT@" + stub-prime: no + +stub-zone: + name: "stub5.org" + stub-addr: "127.0.0.1@@NS2_PORT@" + stub-prime: no + +stub-zone: + name: "stub6.org" + stub-addr: "127.0.0.1@@NS1_PORT@" + stub-prime: no diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.dsc b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.dsc new file mode 100644 index 000000000..422cdee46 --- /dev/null +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.dsc @@ -0,0 +1,16 @@ +BaseName: fast_reload_fwd +Version: 1.0 +Description: Test fast reload change of forwards and stubs. +CreationDate: Thu Jan 22 11:55:55 CET 2024 +Maintainer: dr. W.C.A. Wijngaards +Category: +Component: +CmdDepends: +Depends: +Help: +Pre: fast_reload_fwd.pre +Post: fast_reload_fwd.post +Test: fast_reload_fwd.test +AuxFiles: +Passed: +Failure: diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns1 b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns1 new file mode 100644 index 000000000..ac43a7e51 --- /dev/null +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns1 @@ -0,0 +1,251 @@ +; match A records and return a reply indicating it is this server. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example1.org. IN A +SECTION ANSWER +www.example1.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example2.org. IN A +SECTION ANSWER +www.example2.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example3.org. IN A +SECTION ANSWER +www.example3.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example4.org. IN A +SECTION ANSWER +www.example4.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example5.org. IN A +SECTION ANSWER +www.example5.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example6.org. IN A +SECTION ANSWER +www.example6.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.example1.org. IN A +SECTION ANSWER +www2.example1.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.example2.org. IN A +SECTION ANSWER +www2.example2.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.example3.org. IN A +SECTION ANSWER +www2.example3.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.example4.org. IN A +SECTION ANSWER +www2.example4.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.example5.org. IN A +SECTION ANSWER +www2.example5.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.example6.org. IN A +SECTION ANSWER +www2.example6.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.stub1.org. IN A +SECTION ANSWER +www.stub1.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.stub2.org. IN A +SECTION ANSWER +www.stub2.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.stub3.org. IN A +SECTION ANSWER +www.stub3.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.stub4.org. IN A +SECTION ANSWER +www.stub4.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.stub5.org. IN A +SECTION ANSWER +www.stub5.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.stub6.org. IN A +SECTION ANSWER +www.stub6.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.stub1.org. IN A +SECTION ANSWER +www2.stub1.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.stub2.org. IN A +SECTION ANSWER +www2.stub2.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.stub3.org. IN A +SECTION ANSWER +www2.stub3.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.stub4.org. IN A +SECTION ANSWER +www2.stub4.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.stub5.org. IN A +SECTION ANSWER +www2.stub5.org. IN A 1.2.3.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.stub6.org. IN A +SECTION ANSWER +www2.stub6.org. IN A 1.2.3.1 +ENTRY_END + +; match anything and return a reply +ENTRY_BEGIN +MATCH opcode +ADJUST copy_id copy_query +REPLY QR AA NOERROR +SECTION QUESTION +example.org. IN SOA +SECTION AUTHORITY +example.org. IN SOA ns1.example.org. hostmaster.example.org. 1 3600 900 86400 3600 +ENTRY_END diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns2 b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns2 new file mode 100644 index 000000000..01c5bf448 --- /dev/null +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns2 @@ -0,0 +1,251 @@ +; match A records and return a reply indicating it is this server. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example1.org. IN A +SECTION ANSWER +www.example1.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example2.org. IN A +SECTION ANSWER +www.example2.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example3.org. IN A +SECTION ANSWER +www.example3.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example4.org. IN A +SECTION ANSWER +www.example4.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example5.org. IN A +SECTION ANSWER +www.example5.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example6.org. IN A +SECTION ANSWER +www.example6.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.example1.org. IN A +SECTION ANSWER +www2.example1.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.example2.org. IN A +SECTION ANSWER +www2.example2.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.example3.org. IN A +SECTION ANSWER +www2.example3.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.example4.org. IN A +SECTION ANSWER +www2.example4.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.example5.org. IN A +SECTION ANSWER +www2.example5.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.example6.org. IN A +SECTION ANSWER +www2.example6.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.stub1.org. IN A +SECTION ANSWER +www.stub1.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.stub2.org. IN A +SECTION ANSWER +www.stub2.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.stub3.org. IN A +SECTION ANSWER +www.stub3.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.stub4.org. IN A +SECTION ANSWER +www.stub4.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.stub5.org. IN A +SECTION ANSWER +www.stub5.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.stub6.org. IN A +SECTION ANSWER +www.stub6.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.stub1.org. IN A +SECTION ANSWER +www2.stub1.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.stub2.org. IN A +SECTION ANSWER +www2.stub2.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.stub3.org. IN A +SECTION ANSWER +www2.stub3.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.stub4.org. IN A +SECTION ANSWER +www2.stub4.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.stub5.org. IN A +SECTION ANSWER +www2.stub5.org. IN A 1.2.3.2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www2.stub6.org. IN A +SECTION ANSWER +www2.stub6.org. IN A 1.2.3.2 +ENTRY_END + +; match anything and return a reply +ENTRY_BEGIN +MATCH opcode +ADJUST copy_id copy_query +REPLY QR AA NOERROR +SECTION QUESTION +example.org. IN SOA +SECTION AUTHORITY +example.org. IN SOA ns1.example.org. hostmaster.example.org. 1 3600 900 86400 3600 +ENTRY_END diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.post b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.post new file mode 100644 index 000000000..c44b8c27c --- /dev/null +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.post @@ -0,0 +1,21 @@ +# #-- fast_reload_fwd.post --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# source the test var file when it's there +[ -f .tpkg.var.test ] && source .tpkg.var.test +# +# do your teardown here +. ../common.sh +kill_pid $NS1_PID +kill_pid $NS2_PID +kill_pid $UNBOUND_PID +rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID +echo +echo "> ns1.log" +cat ns1.log +echo +echo "> ns2.log" +cat ns2.log +echo +echo "> unbound.log" +cat unbound.log diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre new file mode 100644 index 000000000..e418fd213 --- /dev/null +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre @@ -0,0 +1,43 @@ +# #-- fast_reload_fwd.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +. ../common.sh +get_random_port 3 +UNBOUND_PORT=$RND_PORT +NS1_PORT=$(($RND_PORT + 1)) +NS2_PORT=$(($RND_PORT + 2)) +echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test +echo "NS1_PORT=$NS1_PORT" >> .tpkg.var.test +echo "NS2=$NS2_PORT" >> .tpkg.var.test + +# make config files +CONTROL_PATH=/tmp +CONTROL_PID=$$ +sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@NS1_PORT\@/'$NS1_PORT'/' -e 's/@NS2_PORT\@/'$NS2_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < fast_reload_fwd.conf > ub.conf +sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@NS1_PORT\@/'$NS1_PORT'/' -e 's/@NS2_PORT\@/'$NS2_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < fast_reload_fwd.conf2 > ub.conf2 + +# start forwarders +get_ldns_testns +$LDNS_TESTNS -p $NS1_PORT fast_reload_fwd.ns1 >ns1.log 2>&1 & +NS1_PID=$! +echo "NS1_PID=$NS1_PID" >> .tpkg.var.test + +$LDNS_TESTNS -p $NS2_PORT fast_reload_fwd.ns2 >ns2.log 2>&1 & +NS2_PID=$! +echo "NS2_PID=$NS2_PID" >> .tpkg.var.test + +# start unbound in the background +PRE="../.." +$PRE/unbound -d -c ub.conf >unbound.log 2>&1 & +UNBOUND_PID=$! +echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test +echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test +echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test + +cat .tpkg.var.test +wait_ldns_testns_up ns1.log +wait_ldns_testns_up ns2.log +wait_unbound_up unbound.log diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test new file mode 100644 index 000000000..a71ca96ef --- /dev/null +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test @@ -0,0 +1,109 @@ +# #-- fast_reload_fwd.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +PRE="../.." +. ../common.sh + +echo "> unbound-control status" +$PRE/unbound-control -c ub.conf status +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi + +# test that the forwards and stubs point to the right upstream. +for x in example1.org example2.org example3.org stub1.org stub2.org stub3.org; do + echo "" + echo "dig www.$x [upstream is NS1]" + dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile + if grep "1.2.3.1" outfile; then + echo "response OK" + else + echo "www.$x got the wrong answer" + exit 1 + fi +done + +for x in example4.org example5.org example6.org stub4.org stub5.org stub6.org; do + echo "" + echo "dig www.$x [upstream is NS2]" + dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile + if grep "1.2.3.2" outfile; then + echo "response OK" + else + echo "www.$x got the wrong answer" + exit 1 + fi +done + +echo "" +echo "> replace config file ub.conf" +mv ub.conf ub.conf.orig +mv ub.conf2 ub.conf +echo "" +echo "> unbound-control fast_reload" +$PRE/unbound-control -c ub.conf fast_reload 2>&1 | tee output +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi + +# for the previous digs to www.x the cached value should remain the same +# but for new lookups, to www2.x the new upstream should be used. +for x in example1.org example2.org example3.org stub1.org stub2.org stub3.org; do + echo "" + echo "dig www.$x [upstream is NS1]" + dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile + if grep "1.2.3.1" outfile; then + echo "response OK" + else + echo "www.$x got the wrong answer" + exit 1 + fi +done + +for x in example4.org example5.org example6.org stub4.org stub5.org stub6.org; do + echo "" + echo "dig www.$x [upstream is NS2]" + dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile + if grep "1.2.3.2" outfile; then + echo "response OK" + else + echo "www.$x got the wrong answer" + exit 1 + fi +done + +# new lookups for www2 go to the upstream. +for x in example2.org example4.org example6.org stub2.org stub4.org stub6.org; do + echo "" + echo "dig www2.$x [upstream is NS1]" + dig @127.0.0.1 -p $UNBOUND_PORT www2.$x A 2>&1 | tee outfile + if grep "1.2.3.1" outfile; then + echo "response OK" + else + echo "www2.$x got the wrong answer" + exit 1 + fi +done + +for x in example1.org example3.org example5.org stub1.org stub3.org stub5.org; do + echo "" + echo "dig www2.$x [upstream is NS2]" + dig @127.0.0.1 -p $UNBOUND_PORT www2.$x A 2>&1 | tee outfile + if grep "1.2.3.2" outfile; then + echo "response OK" + else + echo "www2.$x got the wrong answer" + exit 1 + fi +done + +exit 0 diff --git a/testdata/fast_reload_thread.tdir/fast_reload_thread.post b/testdata/fast_reload_thread.tdir/fast_reload_thread.post index 561232b66..569a17f85 100644 --- a/testdata/fast_reload_thread.tdir/fast_reload_thread.post +++ b/testdata/fast_reload_thread.tdir/fast_reload_thread.post @@ -7,4 +7,5 @@ # do your teardown here . ../common.sh kill_pid $UNBOUND_PID +rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID cat unbound.log From b28f671e18e326bb16b660eda4dab03641e0d765 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 23 Jan 2024 09:56:07 +0100 Subject: [PATCH 038/125] - fast-reload, fix clang analyzer warnings. --- daemon/remote.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 2cc981cc4..aa7f6e2f9 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3766,7 +3766,7 @@ fr_construct_clear(struct fast_reload_construct* ct) hints_delete(ct->hints); /* Delete the log identity here so that the global value is not * reset by config_delete. */ - if(ct->oldcfg->log_identity) { + if(ct->oldcfg && ct->oldcfg->log_identity) { free(ct->oldcfg->log_identity); ct->oldcfg->log_identity = NULL; } @@ -3848,6 +3848,11 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, struct daemon* daemon = fr->worker->daemon; struct module_env* env = daemon->env; + /* These are constructed in the fr_construct_from_config routine. */ + log_assert(ct->oldcfg); + log_assert(ct->fwds); + log_assert(ct->hints); + /* Grab big locks to satisfy lock conditions. */ lock_rw_wrlock(&ct->fwds->lock); lock_rw_wrlock(&ct->hints->lock); From 0b79daf7fec9073a45cee4456be8ccd63c034728 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 15 Feb 2024 16:17:14 +0100 Subject: [PATCH 039/125] - fast-reload, small documentation entry in unbound-control -h output. --- smallapp/unbound-control.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c index c4f730061..5e88e8083 100644 --- a/smallapp/unbound-control.c +++ b/smallapp/unbound-control.c @@ -109,6 +109,10 @@ usage(void) printf(" That means the caches sizes and\n"); printf(" the number of threads must not\n"); printf(" change between reloads.\n"); + printf(" fast_reload reloads the server but only briefly stops\n"); + printf(" server processing, keeps cache, and only\n"); + printf(" changes some options, like forwards\n"); + printf(" and stubs.\n"); printf(" stats print statistics\n"); printf(" stats_noreset peek at statistics\n"); #ifdef HAVE_SHMGET From d047c3fec3468b633aa86b5fd58eb6daeb1b1f44 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 19 Mar 2024 17:51:30 +0100 Subject: [PATCH 040/125] - fast-reload, printout memory use by fast reload, in bytes. --- daemon/remote.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index aa7f6e2f9..1a90822dd 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3773,6 +3773,262 @@ fr_construct_clear(struct fast_reload_construct* ct) config_delete(ct->oldcfg); } +/** get memory for string */ +static size_t +getmem_str(char* str) +{ + if(!str) return 0; + return strlen(str)+1; +} + +/** get memory for strlist */ +static size_t +getmem_config_strlist(struct config_strlist* p) +{ + size_t m = 0; + struct config_strlist* s; + for(s = p; s; s = s->next) + m += sizeof(*s) + getmem_str(s->str); + return m; +} + +/** get memory for str2list */ +static size_t +getmem_config_str2list(struct config_str2list* p) +{ + size_t m = 0; + struct config_str2list* s; + for(s = p; s; s = s->next) + m += sizeof(*s) + getmem_str(s->str) + getmem_str(s->str2); + return m; +} + +/** get memory for str3list */ +static size_t +getmem_config_str3list(struct config_str3list* p) +{ + size_t m = 0; + struct config_str3list* s; + for(s = p; s; s = s->next) + m += sizeof(*s) + getmem_str(s->str) + getmem_str(s->str2) + + getmem_str(s->str3); + return m; +} + +/** get memory for strbytelist */ +static size_t +getmem_config_strbytelist(struct config_strbytelist* p) +{ + size_t m = 0; + struct config_strbytelist* s; + for(s = p; s; s = s->next) + m += sizeof(*s) + getmem_str(s->str) + (s->str2?s->str2len:0); + return m; +} + +/** get memory used by ifs array */ +static size_t +getmem_ifs(int numifs, char** ifs) +{ + size_t m = 0; + int i; + m += numifs * sizeof(char*); + for(i=0; inext) + m += sizeof(*s) + getmem_str(s->name) + + getmem_config_strlist(s->hosts) + + getmem_config_strlist(s->addrs); + return m; +} + +/** get memory for config_auth */ +static size_t +getmem_config_auth(struct config_auth* p) +{ + size_t m = 0; + struct config_auth* s; + for(s = p; s; s = s->next) + m += sizeof(*s) + getmem_str(s->name) + + getmem_config_strlist(s->masters) + + getmem_config_strlist(s->urls) + + getmem_config_strlist(s->allow_notify) + + getmem_str(s->zonefile) + + s->rpz_taglistlen + + getmem_str(s->rpz_action_override) + + getmem_str(s->rpz_log_name) + + getmem_str(s->rpz_cname); + return m; +} + +/** get memory for config_view */ +static size_t +getmem_config_view(struct config_view* p) +{ + size_t m = 0; + struct config_view* s; + for(s = p; s; s = s->next) + m += sizeof(*s) + getmem_str(s->name) + + getmem_config_str2list(s->local_zones) + + getmem_config_strlist(s->local_data) + + getmem_config_strlist(s->local_zones_nodefault) +#ifdef USE_IPSET + + getmem_config_strlist(s->local_zones_ipset) +#endif + + getmem_config_str2list(s->respip_actions) + + getmem_config_str2list(s->respip_data); + + return m; +} + +/** get memory used by config_file item, estimate */ +static size_t +config_file_getmem(struct config_file* cfg) +{ + size_t m = 0; + m += sizeof(*cfg); + m += getmem_config_strlist(cfg->proxy_protocol_port); + m += getmem_str(cfg->ssl_service_key); + m += getmem_str(cfg->ssl_service_pem); + m += getmem_str(cfg->tls_cert_bundle); + m += getmem_config_strlist(cfg->tls_additional_port); + m += getmem_config_strlist(cfg->tls_session_ticket_keys.first); + m += getmem_str(cfg->tls_ciphers); + m += getmem_str(cfg->tls_ciphersuites); + m += getmem_str(cfg->http_endpoint); + m += (cfg->outgoing_avail_ports?65536*sizeof(int):0); + m += getmem_str(cfg->target_fetch_policy); + m += getmem_str(cfg->if_automatic_ports); + m += getmem_ifs(cfg->num_ifs, cfg->ifs); + m += getmem_ifs(cfg->num_out_ifs, cfg->out_ifs); + m += getmem_config_strlist(cfg->root_hints); + m += getmem_config_stub(cfg->stubs); + m += getmem_config_stub(cfg->forwards); + m += getmem_config_auth(cfg->auths); + m += getmem_config_view(cfg->views); + m += getmem_config_strlist(cfg->donotqueryaddrs); +#ifdef CLIENT_SUBNET + m += getmem_config_strlist(cfg->client_subnet); + m += getmem_config_strlist(cfg->client_subnet_zone); +#endif + m += getmem_config_str2list(cfg->acls); + m += getmem_config_str2list(cfg->tcp_connection_limits); + m += getmem_config_strlist(cfg->caps_whitelist); + m += getmem_config_strlist(cfg->private_address); + m += getmem_config_strlist(cfg->private_domain); + m += getmem_str(cfg->chrootdir); + m += getmem_str(cfg->username); + m += getmem_str(cfg->directory); + m += getmem_str(cfg->logfile); + m += getmem_str(cfg->pidfile); + m += getmem_str(cfg->log_identity); + m += getmem_str(cfg->identity); + m += getmem_str(cfg->version); + m += getmem_str(cfg->http_user_agent); + m += getmem_str(cfg->nsid_cfg_str); + m += (cfg->nsid?cfg->nsid_len:0); + m += getmem_str(cfg->module_conf); + m += getmem_config_strlist(cfg->trust_anchor_file_list); + m += getmem_config_strlist(cfg->trust_anchor_list); + m += getmem_config_strlist(cfg->auto_trust_anchor_file_list); + m += getmem_config_strlist(cfg->trusted_keys_file_list); + m += getmem_config_strlist(cfg->domain_insecure); + m += getmem_str(cfg->val_nsec3_key_iterations); + m += getmem_config_str2list(cfg->local_zones); + m += getmem_config_strlist(cfg->local_zones_nodefault); +#ifdef USE_IPSET + m += getmem_config_strlist(cfg->local_zones_ipset); +#endif + m += getmem_config_strlist(cfg->local_data); + m += getmem_config_str3list(cfg->local_zone_overrides); + m += getmem_config_strbytelist(cfg->local_zone_tags); + m += getmem_config_strbytelist(cfg->acl_tags); + m += getmem_config_str3list(cfg->acl_tag_actions); + m += getmem_config_str3list(cfg->acl_tag_datas); + m += getmem_config_str2list(cfg->acl_view); + m += getmem_config_str2list(cfg->interface_actions); + m += getmem_config_strbytelist(cfg->interface_tags); + m += getmem_config_str3list(cfg->interface_tag_actions); + m += getmem_config_str3list(cfg->interface_tag_datas); + m += getmem_config_str2list(cfg->interface_view); + m += getmem_config_strbytelist(cfg->respip_tags); + m += getmem_config_str2list(cfg->respip_actions); + m += getmem_config_str2list(cfg->respip_data); + m += getmem_ifs(cfg->num_tags, cfg->tagname); + m += getmem_config_strlist(cfg->control_ifs.first); + m += getmem_str(cfg->server_key_file); + m += getmem_str(cfg->server_cert_file); + m += getmem_str(cfg->control_key_file); + m += getmem_str(cfg->control_cert_file); + m += getmem_config_strlist(cfg->python_script); + m += getmem_config_strlist(cfg->dynlib_file); + m += getmem_str(cfg->dns64_prefix); + m += getmem_config_strlist(cfg->dns64_ignore_aaaa); + m += getmem_str(cfg->nat64_prefix); + m += getmem_str(cfg->dnstap_socket_path); + m += getmem_str(cfg->dnstap_ip); + m += getmem_str(cfg->dnstap_tls_server_name); + m += getmem_str(cfg->dnstap_tls_cert_bundle); + m += getmem_str(cfg->dnstap_tls_client_key_file); + m += getmem_str(cfg->dnstap_tls_client_cert_file); + m += getmem_str(cfg->dnstap_identity); + m += getmem_str(cfg->dnstap_version); + m += getmem_config_str2list(cfg->ratelimit_for_domain); + m += getmem_config_str2list(cfg->ratelimit_below_domain); + m += getmem_config_str2list(cfg->edns_client_strings); + m += getmem_str(cfg->dnscrypt_provider); + m += getmem_config_strlist(cfg->dnscrypt_secret_key); + m += getmem_config_strlist(cfg->dnscrypt_provider_cert); + m += getmem_config_strlist(cfg->dnscrypt_provider_cert_rotated); +#ifdef USE_IPSECMOD + m += getmem_config_strlist(cfg->ipsecmod_whitelist); + m += getmem_str(cfg->ipsecmod_hook); +#endif +#ifdef USE_CACHEDB + m += getmem_str(cfg->cachedb_backend); + m += getmem_str(cfg->cachedb_secret); +#ifdef USE_REDIS + m += getmem_str(cfg->redis_server_host); + m += getmem_str(cfg->redis_server_path); + m += getmem_str(cfg->redis_server_password); +#endif +#endif +#ifdef USE_IPSET + m += getmem_str(cfg->ipset_name_v4); + m += getmem_str(cfg->ipset_name_v6); +#endif + return m; +} + +/** fast reload thread, print memory used by construct of items. */ +static int +fr_printmem(struct fast_reload_thread* fr, + struct config_file* newcfg, struct fast_reload_construct* ct) +{ + size_t mem = 0; + if(fr_poll_for_quit(fr)) + return 1; + mem += forwards_get_mem(ct->fwds); + mem += hints_get_mem(ct->hints); + mem += sizeof(*ct->oldcfg); + mem += config_file_getmem(newcfg); + + if(!fr_output_printf(fr, "memory use %d bytes\n", (int)mem)) + return 0; + fr_send_notification(fr, fast_reload_notification_printout); + + return 1; +} + /** fast reload thread, construct from config the new items */ static int fr_construct_from_config(struct fast_reload_thread* fr, @@ -3802,6 +4058,8 @@ fr_construct_from_config(struct fast_reload_thread* fr, log_err("out of memory"); return 0; } + if(!fr_printmem(fr, newcfg, ct)) + return 0; return 1; } From d915c4c076fdd2ed6b671300cfa735c31cdece82 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 21 Mar 2024 13:17:34 +0100 Subject: [PATCH 041/125] - fast-reload, compile without threads. --- daemon/remote.c | 12 +++++++++++- testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre | 8 ++++++++ .../fast_reload_thread.tdir/fast_reload_thread.pre | 8 ++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 1a90822dd..7a82b3e3c 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -678,9 +678,16 @@ do_reload(RES* ssl, struct worker* worker, int reuse_cache) static void do_fast_reload(RES* ssl, struct worker* worker, struct rc_state* s) { +#ifdef THREADS_DISABLED + if(!ssl_printf(ssl, "error: no threads for fast_reload, compiled without threads.\n")) + return; + (void)worker; + (void)s; +#else if(!ssl_printf(ssl, "start fast_reload\n")) return; fast_reload_thread_start(ssl, worker, s); +#endif } /** do the verbosity command */ @@ -3522,6 +3529,7 @@ fr_notification_to_string(enum fast_reload_notification status) return "unknown"; } +#ifndef THREADS_DISABLED /** fast reload, poll for notification incoming. True if quit */ static int fr_poll_for_quit(struct fast_reload_thread* fr) @@ -3642,7 +3650,6 @@ fr_send_notification(struct fast_reload_thread* fr, } } -#ifndef THREADS_DISABLED /** fast reload thread queue up text string for output */ static int fr_output_text(struct fast_reload_thread* fr, const char* msg) @@ -5252,6 +5259,7 @@ int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c), void* arg, return 0; } +#ifndef THREADS_DISABLED /** fast reload printq create */ static struct fast_reload_printq* fr_printq_create(struct comm_point* c, struct worker* worker) @@ -5270,6 +5278,7 @@ fr_printq_create(struct comm_point* c, struct worker* worker) printq->client_cp->cb_arg = printq; return printq; } +#endif /* !THREADS_DISABLED */ /** fast reload printq delete */ static void @@ -5432,6 +5441,7 @@ fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s) ub_thread_create(&worker->daemon->fast_reload_thread->tid, fast_reload_thread_main, worker->daemon->fast_reload_thread); #else + (void)s; #endif } diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre index e418fd213..2fb705e74 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre @@ -4,7 +4,15 @@ # use .tpkg.var.test for in test variable passing [ -f .tpkg.var.test ] && source .tpkg.var.test +PRE="../.." . ../common.sh +# if no threads; exit +if grep -e "define HAVE_PTHREAD 1" -e "define HAVE_SOLARIS_THREADS 1" -e "define HAVE_WINDOWS_THREADS 1" $PRE/config.h; then + echo "have threads" +else + skip_test "no threads" +fi + get_random_port 3 UNBOUND_PORT=$RND_PORT NS1_PORT=$(($RND_PORT + 1)) diff --git a/testdata/fast_reload_thread.tdir/fast_reload_thread.pre b/testdata/fast_reload_thread.tdir/fast_reload_thread.pre index 5f23204e5..5521742fa 100644 --- a/testdata/fast_reload_thread.tdir/fast_reload_thread.pre +++ b/testdata/fast_reload_thread.tdir/fast_reload_thread.pre @@ -4,7 +4,15 @@ # use .tpkg.var.test for in test variable passing [ -f .tpkg.var.test ] && source .tpkg.var.test +PRE="../.." . ../common.sh +# if no threads; exit +if grep -e "define HAVE_PTHREAD 1" -e "define HAVE_SOLARIS_THREADS 1" -e "define HAVE_WINDOWS_THREADS 1" $PRE/config.h; then + echo "have threads" +else + skip_test "no threads" +fi + get_random_port 1 UNBOUND_PORT=$RND_PORT echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test From bebce303a733832d14c756754841894cc369ba2c Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 21 Mar 2024 13:27:14 +0100 Subject: [PATCH 042/125] - fast-reload, document fast_reload in man page. --- doc/unbound-control.8.in | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 7823de3aa..2fc72fe16 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -60,6 +60,15 @@ Reload the server but try to keep the RRset and message cache if That means the caches sizes and the number of threads must not change between reloads. .TP +.B fast_reload +Reload the server, but keep downtime to a minimum, so that user queries +keep seeing service. This needs the code compiled with threads. The config +is loaded in a thread, and prepared, then it briefly pauses the existing +server and updates config options. The intent is that the pause does not +impact the service of user queries. The cache is kept. Also user queries +worked on are kept and continue, but with the new config options. +Not all options are changed, but it changes like forwards and stubs. +.TP .B verbosity \fInumber Change verbosity value for logging. Same values as \fBverbosity\fR keyword in \fIunbound.conf\fR(5). This new setting lasts until the server is issued From 942dd56f7dff937cae8a5f5df8fc14badd734fd9 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 21 Mar 2024 13:37:48 +0100 Subject: [PATCH 043/125] - fast-reload, print ok when done successfully. --- daemon/remote.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index 7a82b3e3c..9e0034b6c 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4319,6 +4319,10 @@ static void* fast_reload_thread_main(void* arg) if(fr_poll_for_quit(fast_reload_thread)) goto done; + if(!fr_output_printf(fast_reload_thread, "ok\n")) + goto done_error; + fr_send_notification(fast_reload_thread, + fast_reload_notification_printout); verbose(VERB_ALGO, "stop fast reload thread"); /* If this is not an exit due to quit earlier, send regular done. */ if(!fast_reload_thread->need_to_quit) From 2ab619fd90e545d3348ed61723a1a98a5deb936d Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 21 Mar 2024 14:03:02 +0100 Subject: [PATCH 044/125] - fast-reload, option for fast-reload commandline, +v verbosity option, with timing and memory use output. --- daemon/remote.c | 89 +++++++++++++------ daemon/remote.h | 6 +- doc/unbound-control.8.in | 8 +- smallapp/unbound-control.c | 2 +- .../fast_reload_fwd.tdir/fast_reload_fwd.test | 2 +- .../fast_reload_thread.test | 9 +- 6 files changed, 75 insertions(+), 41 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 9e0034b6c..ecd30af27 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -676,17 +676,36 @@ do_reload(RES* ssl, struct worker* worker, int reuse_cache) /** do the fast_reload command */ static void -do_fast_reload(RES* ssl, struct worker* worker, struct rc_state* s) +do_fast_reload(RES* ssl, struct worker* worker, struct rc_state* s, char* arg) { #ifdef THREADS_DISABLED if(!ssl_printf(ssl, "error: no threads for fast_reload, compiled without threads.\n")) return; (void)worker; (void)s; + (void)arg; #else - if(!ssl_printf(ssl, "start fast_reload\n")) - return; - fast_reload_thread_start(ssl, worker, s); + int fr_verb = 0; + if(arg[0] == '+') { + int i=1; + while(arg[i]!=0 && arg[i]!=' ' && arg[i]!='\t') { + if(arg[i] == 'v') + fr_verb++; + else { + if(!ssl_printf(ssl, + "error: unknown option '+%c'\n", + arg[i])) + return; + return; + } + i++; + } + } + if(fr_verb >= 1) { + if(!ssl_printf(ssl, "start fast_reload\n")) + return; + } + fast_reload_thread_start(ssl, worker, s, fr_verb); #endif } @@ -3100,7 +3119,7 @@ execute_cmd(struct daemon_remote* rc, struct rc_state* s, RES* ssl, char* cmd, do_reload(ssl, worker, 0); return; } else if(cmdcmp(p, "fast_reload", 11)) { - do_fast_reload(ssl, worker, s); + do_fast_reload(ssl, worker, s, skipwhite(p+11)); return; } else if(cmdcmp(p, "stats_noreset", 13)) { do_stats(ssl, worker, 0); @@ -3755,10 +3774,12 @@ fr_read_config(struct fast_reload_thread* fr, struct config_file** newcfg) } if(fr_poll_for_quit(fr)) return 1; - if(!fr_output_printf(fr, "done read config file %s\n", - fr->worker->daemon->cfgfile)) - return 0; - fr_send_notification(fr, fast_reload_notification_printout); + if(fr->fr_verb >= 1) { + if(!fr_output_printf(fr, "done read config file %s\n", + fr->worker->daemon->cfgfile)) + return 0; + fr_send_notification(fr, fast_reload_notification_printout); + } return 1; } @@ -4065,8 +4086,10 @@ fr_construct_from_config(struct fast_reload_thread* fr, log_err("out of memory"); return 0; } - if(!fr_printmem(fr, newcfg, ct)) - return 0; + if(fr->fr_verb >= 2) { + if(!fr_printmem(fr, newcfg, ct)) + return 0; + } return 1; } @@ -4294,18 +4317,22 @@ static void* fast_reload_thread_main(void* arg) log_thread_set(&fast_reload_thread->threadnum); verbose(VERB_ALGO, "start fast reload thread"); - fr_init_time(&time_start, &time_read, &time_construct, &time_reload, - &time_end); - if(fr_poll_for_quit(fast_reload_thread)) - goto done; + if(fast_reload_thread->fr_verb >= 1) { + fr_init_time(&time_start, &time_read, &time_construct, + &time_reload, &time_end); + if(fr_poll_for_quit(fast_reload_thread)) + goto done; + } /* print output to the client */ - if(!fr_output_printf(fast_reload_thread, "thread started\n")) - goto done_error; - fr_send_notification(fast_reload_thread, - fast_reload_notification_printout); - if(fr_poll_for_quit(fast_reload_thread)) - goto done; + if(fast_reload_thread->fr_verb >= 1) { + if(!fr_output_printf(fast_reload_thread, "thread started\n")) + goto done_error; + fr_send_notification(fast_reload_thread, + fast_reload_notification_printout); + if(fr_poll_for_quit(fast_reload_thread)) + goto done; + } if(!fr_load_config(fast_reload_thread, &time_read, &time_construct, &time_reload)) @@ -4313,11 +4340,13 @@ static void* fast_reload_thread_main(void* arg) if(fr_poll_for_quit(fast_reload_thread)) goto done; - if(!fr_finish_time(fast_reload_thread, &time_start, &time_read, - &time_construct, &time_reload, &time_end)) - goto done_error; - if(fr_poll_for_quit(fast_reload_thread)) - goto done; + if(fast_reload_thread->fr_verb >= 1) { + if(!fr_finish_time(fast_reload_thread, &time_start, &time_read, + &time_construct, &time_reload, &time_end)) + goto done_error; + if(fr_poll_for_quit(fast_reload_thread)) + goto done; + } if(!fr_output_printf(fast_reload_thread, "ok\n")) goto done_error; @@ -4624,7 +4653,7 @@ create_socketpair(int* pair, struct ub_randstate* rand) /** fast reload thread. setup the thread info */ static int -fast_reload_thread_setup(struct worker* worker) +fast_reload_thread_setup(struct worker* worker, int fr_verb) { struct fast_reload_thread* fr; int numworkers = worker->daemon->num; @@ -4633,6 +4662,7 @@ fast_reload_thread_setup(struct worker* worker) if(!worker->daemon->fast_reload_thread) return 0; fr = worker->daemon->fast_reload_thread; + fr->fr_verb = fr_verb; /* The thread id printed in logs, numworker+1 is the dnstap thread. * This is numworkers+2. */ fr->threadnum = numworkers+2; @@ -5376,13 +5406,14 @@ fr_send_stop(struct fast_reload_thread* fr) } void -fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s) +fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s, + int fr_verb) { if(worker->daemon->fast_reload_thread) { log_err("fast reload thread already running"); return; } - if(!fast_reload_thread_setup(worker)) { + if(!fast_reload_thread_setup(worker, fr_verb)) { if(!ssl_printf(ssl, "error could not setup thread\n")) return; return; diff --git a/daemon/remote.h b/daemon/remote.h index 3121f696a..d508385ba 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -187,6 +187,8 @@ struct fast_reload_thread { int started; /** if the thread has to quit */ int need_to_quit; + /** verbosity of the fast_reload command, the number of +v options */ + int fr_verb; /** the event that listens on the remote service worker to the * commpair, it receives content from the fast reload thread. */ @@ -309,9 +311,11 @@ int ssl_read_line(RES* ssl, char* buf, size_t max); * @param s: the rc_state that is servicing the remote control connection to * the remote control client. It needs to be moved away to stay connected * while the fast reload is running. + * @param fr_verb: verbosity to print output at. 0 is nothing, 1 is some + * and 2 is more detail. */ void fast_reload_thread_start(RES* ssl, struct worker* worker, - struct rc_state* s); + struct rc_state* s, int fr_verb); /** * Stop fast reload thread diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 2fc72fe16..901636c11 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -60,7 +60,7 @@ Reload the server but try to keep the RRset and message cache if That means the caches sizes and the number of threads must not change between reloads. .TP -.B fast_reload +.B fast_reload \fR[\fI+v\fR] Reload the server, but keep downtime to a minimum, so that user queries keep seeing service. This needs the code compiled with threads. The config is loaded in a thread, and prepared, then it briefly pauses the existing @@ -68,6 +68,12 @@ server and updates config options. The intent is that the pause does not impact the service of user queries. The cache is kept. Also user queries worked on are kept and continue, but with the new config options. Not all options are changed, but it changes like forwards and stubs. +.IP +The '+v' option makes the output verbose. With '+vv' it is more verbose. +That includes the time it took to do the reload. And with more verbose output +the amount of memory that was allocated temporarily to perform the reload, +this amount of memory can be big if the config has large contents. In the +timing output the 'reload' time is the time during which the server was paused. .TP .B verbosity \fInumber Change verbosity value for logging. Same values as \fBverbosity\fR keyword in diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c index 5e88e8083..ded85a175 100644 --- a/smallapp/unbound-control.c +++ b/smallapp/unbound-control.c @@ -109,7 +109,7 @@ usage(void) printf(" That means the caches sizes and\n"); printf(" the number of threads must not\n"); printf(" change between reloads.\n"); - printf(" fast_reload reloads the server but only briefly stops\n"); + printf(" fast_reload [+v] reloads the server but only briefly stops\n"); printf(" server processing, keeps cache, and only\n"); printf(" changes some options, like forwards\n"); printf(" and stubs.\n"); diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test index a71ca96ef..b1212c96b 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test @@ -47,7 +47,7 @@ mv ub.conf ub.conf.orig mv ub.conf2 ub.conf echo "" echo "> unbound-control fast_reload" -$PRE/unbound-control -c ub.conf fast_reload 2>&1 | tee output +$PRE/unbound-control -c ub.conf fast_reload +vv 2>&1 | tee output if test $? -ne 0; then echo "wrong exit value." exit 1 diff --git a/testdata/fast_reload_thread.tdir/fast_reload_thread.test b/testdata/fast_reload_thread.tdir/fast_reload_thread.test index 135578fc1..d2ef25880 100644 --- a/testdata/fast_reload_thread.tdir/fast_reload_thread.test +++ b/testdata/fast_reload_thread.tdir/fast_reload_thread.test @@ -28,14 +28,7 @@ wait_logfile unbound.log "start fast reload thread" 60 wait_logfile unbound.log "stop fast reload thread" 60 wait_logfile unbound.log "joined with fastreload thread" 60 -if grep "start fast_reload" output; then - echo "OK" -else - echo "output not correct" - exit 1 -fi - -if grep "thread started" output; then +if grep "ok" output; then echo "OK" else echo "output not correct" From c97f5a6df2219f3a39a764e5fe08d46dd6b422c2 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 21 Mar 2024 15:06:38 +0100 Subject: [PATCH 045/125] - fast-reload, option for fast-reload commandline, +p does not pause threads. --- daemon/remote.c | 71 ++++++++++++++++++++++++++------------ daemon/remote.h | 5 ++- doc/unbound-control.8.in | 13 ++++++- smallapp/unbound-control.c | 2 +- 4 files changed, 65 insertions(+), 26 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index ecd30af27..5ee3d4d32 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -674,6 +674,39 @@ do_reload(RES* ssl, struct worker* worker, int reuse_cache) send_ok(ssl); } +#ifndef THREADS_DISABLED +/** parse fast reload command options. */ +static int +fr_parse_options(RES* ssl, char* arg, int* fr_verb, int* fr_nopause) +{ + char* argp = arg; + while(*argp=='+') { + argp++; + while(*argp!=0 && *argp!=' ' && *argp!='\t') { + if(*argp == 'v') { + (*fr_verb)++; + } else if(*argp == 'p') { + (*fr_nopause) = 1; + } else { + if(!ssl_printf(ssl, + "error: unknown option '+%c'\n", + *argp)) + return 0; + return 0; + } + argp++; + } + argp = skipwhite(argp); + } + if(*argp!=0) { + if(!ssl_printf(ssl, "error: unknown option '%s'\n", argp)) + return 0; + return 0; + } + return 1; +} +#endif /* !THREADS_DISABLED */ + /** do the fast_reload command */ static void do_fast_reload(RES* ssl, struct worker* worker, struct rc_state* s, char* arg) @@ -685,27 +718,14 @@ do_fast_reload(RES* ssl, struct worker* worker, struct rc_state* s, char* arg) (void)s; (void)arg; #else - int fr_verb = 0; - if(arg[0] == '+') { - int i=1; - while(arg[i]!=0 && arg[i]!=' ' && arg[i]!='\t') { - if(arg[i] == 'v') - fr_verb++; - else { - if(!ssl_printf(ssl, - "error: unknown option '+%c'\n", - arg[i])) - return; - return; - } - i++; - } - } + int fr_verb = 0, fr_nopause = 0; + if(!fr_parse_options(ssl, arg, &fr_verb, &fr_nopause)) + return; if(fr_verb >= 1) { if(!ssl_printf(ssl, "start fast_reload\n")) return; } - fast_reload_thread_start(ssl, worker, s, fr_verb); + fast_reload_thread_start(ssl, worker, s, fr_verb, fr_nopause); #endif } @@ -4244,12 +4264,16 @@ fr_reload_ipc(struct fast_reload_thread* fr, struct config_file* newcfg, struct fast_reload_construct* ct) { int result = 1; - fr_send_notification(fr, fast_reload_notification_reload_stop); - fr_poll_for_ack(fr); + if(!fr->fr_nopause) { + fr_send_notification(fr, fast_reload_notification_reload_stop); + fr_poll_for_ack(fr); + } if(!fr_reload_config(fr, newcfg, ct)) { result = 0; } - fr_send_notification(fr, fast_reload_notification_reload_start); + if(!fr->fr_nopause) { + fr_send_notification(fr, fast_reload_notification_reload_start); + } return result; } @@ -4653,7 +4677,7 @@ create_socketpair(int* pair, struct ub_randstate* rand) /** fast reload thread. setup the thread info */ static int -fast_reload_thread_setup(struct worker* worker, int fr_verb) +fast_reload_thread_setup(struct worker* worker, int fr_verb, int fr_nopause) { struct fast_reload_thread* fr; int numworkers = worker->daemon->num; @@ -4663,6 +4687,7 @@ fast_reload_thread_setup(struct worker* worker, int fr_verb) return 0; fr = worker->daemon->fast_reload_thread; fr->fr_verb = fr_verb; + fr->fr_nopause = fr_nopause; /* The thread id printed in logs, numworker+1 is the dnstap thread. * This is numworkers+2. */ fr->threadnum = numworkers+2; @@ -5407,13 +5432,13 @@ fr_send_stop(struct fast_reload_thread* fr) void fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s, - int fr_verb) + int fr_verb, int fr_nopause) { if(worker->daemon->fast_reload_thread) { log_err("fast reload thread already running"); return; } - if(!fast_reload_thread_setup(worker, fr_verb)) { + if(!fast_reload_thread_setup(worker, fr_verb, fr_nopause)) { if(!ssl_printf(ssl, "error could not setup thread\n")) return; return; diff --git a/daemon/remote.h b/daemon/remote.h index d508385ba..0c7209e51 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -189,6 +189,8 @@ struct fast_reload_thread { int need_to_quit; /** verbosity of the fast_reload command, the number of +v options */ int fr_verb; + /** option to not pause threads during reload */ + int fr_nopause; /** the event that listens on the remote service worker to the * commpair, it receives content from the fast reload thread. */ @@ -313,9 +315,10 @@ int ssl_read_line(RES* ssl, char* buf, size_t max); * while the fast reload is running. * @param fr_verb: verbosity to print output at. 0 is nothing, 1 is some * and 2 is more detail. + * @param fr_nopause: option to not pause threads during reload. */ void fast_reload_thread_start(RES* ssl, struct worker* worker, - struct rc_state* s, int fr_verb); + struct rc_state* s, int fr_verb, int fr_nopause); /** * Stop fast reload thread diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 901636c11..c2df9e182 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -60,7 +60,7 @@ Reload the server but try to keep the RRset and message cache if That means the caches sizes and the number of threads must not change between reloads. .TP -.B fast_reload \fR[\fI+v\fR] +.B fast_reload \fR[\fI+vp\fR] Reload the server, but keep downtime to a minimum, so that user queries keep seeing service. This needs the code compiled with threads. The config is loaded in a thread, and prepared, then it briefly pauses the existing @@ -74,6 +74,17 @@ That includes the time it took to do the reload. And with more verbose output the amount of memory that was allocated temporarily to perform the reload, this amount of memory can be big if the config has large contents. In the timing output the 'reload' time is the time during which the server was paused. +.IP +The '+p' option makes the reload not pause threads, they keep running. +Locks are acquired, but items are updated in sequence, so it is possible +for threads to see an inconsistent state with some options from the old +and some options from the new config, such as cache TTL parameters from the +old config and forwards from the new config. The stubs and forwards are +updated at the same time, so that they are viewed consistently, either old +or new values together. The option makes the reload time take eg. 3 +microseconds instead of 0.3 milliseconds during which the worker threads are +interrupted. So, the interruption is much shorter, at the expense of some +inconsistency. .TP .B verbosity \fInumber Change verbosity value for logging. Same values as \fBverbosity\fR keyword in diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c index ded85a175..e7f579797 100644 --- a/smallapp/unbound-control.c +++ b/smallapp/unbound-control.c @@ -109,7 +109,7 @@ usage(void) printf(" That means the caches sizes and\n"); printf(" the number of threads must not\n"); printf(" change between reloads.\n"); - printf(" fast_reload [+v] reloads the server but only briefly stops\n"); + printf(" fast_reload [+vp] reloads the server but only briefly stops\n"); printf(" server processing, keeps cache, and only\n"); printf(" changes some options, like forwards\n"); printf(" and stubs.\n"); From 123205cbc461726b0cd3dddbc962401316383621 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 21 Mar 2024 15:36:30 +0100 Subject: [PATCH 046/125] - fast-reload, option for fast-reload commandline, +d drops mesh queries. --- daemon/daemon.h | 2 ++ daemon/remote.c | 27 ++++++++++++++++++++------- daemon/remote.h | 5 ++++- daemon/worker.c | 4 ++++ doc/unbound-control.8.in | 8 +++++++- smallapp/unbound-control.c | 2 +- 6 files changed, 38 insertions(+), 10 deletions(-) diff --git a/daemon/daemon.h b/daemon/daemon.h index 46cb5dbcc..f76ced23a 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -152,6 +152,8 @@ struct daemon { struct fast_reload_thread* fast_reload_thread; /** the fast reload printq list */ struct fast_reload_printq* fast_reload_printq_list; + /** the fast reload option to drop mesh queries, true if so. */ + int fast_reload_drop_mesh; /** config file name */ char* cfgfile; }; diff --git a/daemon/remote.c b/daemon/remote.c index 5ee3d4d32..ba4c8d26d 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -677,7 +677,8 @@ do_reload(RES* ssl, struct worker* worker, int reuse_cache) #ifndef THREADS_DISABLED /** parse fast reload command options. */ static int -fr_parse_options(RES* ssl, char* arg, int* fr_verb, int* fr_nopause) +fr_parse_options(RES* ssl, char* arg, int* fr_verb, int* fr_nopause, + int* fr_drop_mesh) { char* argp = arg; while(*argp=='+') { @@ -687,6 +688,8 @@ fr_parse_options(RES* ssl, char* arg, int* fr_verb, int* fr_nopause) (*fr_verb)++; } else if(*argp == 'p') { (*fr_nopause) = 1; + } else if(*argp == 'd') { + (*fr_drop_mesh) = 1; } else { if(!ssl_printf(ssl, "error: unknown option '+%c'\n", @@ -718,14 +721,15 @@ do_fast_reload(RES* ssl, struct worker* worker, struct rc_state* s, char* arg) (void)s; (void)arg; #else - int fr_verb = 0, fr_nopause = 0; - if(!fr_parse_options(ssl, arg, &fr_verb, &fr_nopause)) + int fr_verb = 0, fr_nopause = 0, fr_drop_mesh = 0; + if(!fr_parse_options(ssl, arg, &fr_verb, &fr_nopause, &fr_drop_mesh)) return; if(fr_verb >= 1) { if(!ssl_printf(ssl, "start fast_reload\n")) return; } - fast_reload_thread_start(ssl, worker, s, fr_verb, fr_nopause); + fast_reload_thread_start(ssl, worker, s, fr_verb, fr_nopause, + fr_drop_mesh); #endif } @@ -4677,7 +4681,8 @@ create_socketpair(int* pair, struct ub_randstate* rand) /** fast reload thread. setup the thread info */ static int -fast_reload_thread_setup(struct worker* worker, int fr_verb, int fr_nopause) +fast_reload_thread_setup(struct worker* worker, int fr_verb, int fr_nopause, + int fr_drop_mesh) { struct fast_reload_thread* fr; int numworkers = worker->daemon->num; @@ -4688,6 +4693,8 @@ fast_reload_thread_setup(struct worker* worker, int fr_verb, int fr_nopause) fr = worker->daemon->fast_reload_thread; fr->fr_verb = fr_verb; fr->fr_nopause = fr_nopause; + fr->fr_drop_mesh = fr_drop_mesh; + worker->daemon->fast_reload_drop_mesh = fr->fr_drop_mesh; /* The thread id printed in logs, numworker+1 is the dnstap thread. * This is numworkers+2. */ fr->threadnum = numworkers+2; @@ -5007,6 +5014,11 @@ fr_main_perform_reload_stop(struct fast_reload_thread* fr) continue; /* Do not send to ourselves. */ worker_send_cmd(daemon->workers[i], worker_cmd_reload_start); } + + if(fr->worker->daemon->fast_reload_drop_mesh) { + verbose(VERB_ALGO, "worker: drop mesh queries after reload"); + mesh_delete_all(fr->worker->env.mesh); + } verbose(VERB_ALGO, "worker resume after reload"); } @@ -5432,13 +5444,14 @@ fr_send_stop(struct fast_reload_thread* fr) void fast_reload_thread_start(RES* ssl, struct worker* worker, struct rc_state* s, - int fr_verb, int fr_nopause) + int fr_verb, int fr_nopause, int fr_drop_mesh) { if(worker->daemon->fast_reload_thread) { log_err("fast reload thread already running"); return; } - if(!fast_reload_thread_setup(worker, fr_verb, fr_nopause)) { + if(!fast_reload_thread_setup(worker, fr_verb, fr_nopause, + fr_drop_mesh)) { if(!ssl_printf(ssl, "error could not setup thread\n")) return; return; diff --git a/daemon/remote.h b/daemon/remote.h index 0c7209e51..180fabed8 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -191,6 +191,8 @@ struct fast_reload_thread { int fr_verb; /** option to not pause threads during reload */ int fr_nopause; + /** option to drop mesh queries */ + int fr_drop_mesh; /** the event that listens on the remote service worker to the * commpair, it receives content from the fast reload thread. */ @@ -316,9 +318,10 @@ int ssl_read_line(RES* ssl, char* buf, size_t max); * @param fr_verb: verbosity to print output at. 0 is nothing, 1 is some * and 2 is more detail. * @param fr_nopause: option to not pause threads during reload. + * @param fr_drop_mesh: option to drop mesh queries. */ void fast_reload_thread_start(RES* ssl, struct worker* worker, - struct rc_state* s, int fr_verb, int fr_nopause); + struct rc_state* s, int fr_verb, int fr_nopause, int fr_drop_mesh); /** * Stop fast reload thread diff --git a/daemon/worker.c b/daemon/worker.c index 99faef8c3..be8c81302 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -422,6 +422,10 @@ worker_stop_and_wait(struct worker* worker) if(cmd != worker_cmd_reload_start) { log_err("worker reload reply, wrong reply command"); } + if(worker->daemon->fast_reload_drop_mesh) { + verbose(VERB_ALGO, "worker: drop mesh queries after reload"); + mesh_delete_all(worker->env.mesh); + } verbose(VERB_ALGO, "worker resume after reload"); } diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index c2df9e182..e67a628d8 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -60,7 +60,7 @@ Reload the server but try to keep the RRset and message cache if That means the caches sizes and the number of threads must not change between reloads. .TP -.B fast_reload \fR[\fI+vp\fR] +.B fast_reload \fR[\fI+dpv\fR] Reload the server, but keep downtime to a minimum, so that user queries keep seeing service. This needs the code compiled with threads. The config is loaded in a thread, and prepared, then it briefly pauses the existing @@ -85,6 +85,12 @@ or new values together. The option makes the reload time take eg. 3 microseconds instead of 0.3 milliseconds during which the worker threads are interrupted. So, the interruption is much shorter, at the expense of some inconsistency. +.IP +The '+d' option makes the reload drop queries that the worker threads are +working on. This is like flush_requestlist. Without it the queries are kept +so that users keep getting answers for those queries that are currently +processed. The drop makes it so that queries during the life time of the +query processing see only old, or only new config options. .TP .B verbosity \fInumber Change verbosity value for logging. Same values as \fBverbosity\fR keyword in diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c index e7f579797..8d5aee635 100644 --- a/smallapp/unbound-control.c +++ b/smallapp/unbound-control.c @@ -109,7 +109,7 @@ usage(void) printf(" That means the caches sizes and\n"); printf(" the number of threads must not\n"); printf(" change between reloads.\n"); - printf(" fast_reload [+vp] reloads the server but only briefly stops\n"); + printf(" fast_reload [+dpv] reloads the server but only briefly stops\n"); printf(" server processing, keeps cache, and only\n"); printf(" changes some options, like forwards\n"); printf(" and stubs.\n"); From 46bdbc7e6711e46d279b7445d5252ecff0e56a91 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 21 Mar 2024 17:20:46 +0100 Subject: [PATCH 047/125] - fast-reload, fix to poll every thread with nopause to make certain that resources are not held by the threads and can be deleted. --- daemon/remote.c | 50 ++++++++++++++++++++++++++++++++++++++++ daemon/remote.h | 5 +++- daemon/worker.c | 28 ++++++++++++++++++---- daemon/worker.h | 4 +++- doc/unbound-control.8.in | 5 +++- 5 files changed, 85 insertions(+), 7 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index ba4c8d26d..2b77d8763 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3564,6 +3564,8 @@ fr_notification_to_string(enum fast_reload_notification status) return "reload_stop"; case fast_reload_notification_reload_ack: return "reload_ack"; + case fast_reload_notification_reload_nopause_poll: + return "reload_nopause_poll"; case fast_reload_notification_reload_start: return "reload_start"; default: @@ -4331,6 +4333,27 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, log_err("gettimeofday: %s", strerror(errno)); /* Delete old. */ + if(fr_poll_for_quit(fr)) { + config_delete(newcfg); + fr_construct_clear(&ct); + return 1; + } + if(fr->fr_nopause) { + /* Poll every thread, with a no-work poll item over the + * command pipe. This makes the worker thread surely move + * to deal with that event, and thus the thread is no longer + * holding, eg. a string item from the old config struct. + * And then the old config struct can safely be deleted. + * Only needed when nopause is used, because without that + * the worker threads are already waiting on a command pipe + * item. This nopause command pipe item does not take work, + * it returns immediately, so it does not delay the workers. + * They can be polled one at a time. But its processing causes + * the worker to have released data items from old config. */ + fr_send_notification(fr, + fast_reload_notification_reload_nopause_poll); + fr_poll_for_ack(fr); + } config_delete(newcfg); fr_construct_clear(&ct); return 1; @@ -5022,6 +5045,31 @@ fr_main_perform_reload_stop(struct fast_reload_thread* fr) verbose(VERB_ALGO, "worker resume after reload"); } +/** Fast reload, the main thread performs the nopause poll. It polls every + * other worker thread briefly over the command pipe ipc. The command takes + * no time for the worker, it can return immediately. After that it sends + * an acknowledgement to the fastreload thread. */ +static void +fr_main_perform_reload_nopause_poll(struct fast_reload_thread* fr) +{ + struct daemon* daemon = fr->worker->daemon; + int i; + + /* Send the reload_poll to other threads. They can respond + * one at a time. */ + for(i=0; inum; i++) { + if(i == fr->worker->thread_num) + continue; /* Do not send to ourselves. */ + worker_send_cmd(daemon->workers[i], worker_cmd_reload_poll); + } + + /* Wait for the other threads to ack. */ + fr_read_ack_from_workers(fr); + + /* Send ack to fast reload thread. */ + fr_send_cmd_to(fr, fast_reload_notification_reload_ack, 0, 1); +} + /** Fast reload, perform the command received from the fast reload thread */ static void fr_main_perform_cmd(struct fast_reload_thread* fr, @@ -5037,6 +5085,8 @@ fr_main_perform_cmd(struct fast_reload_thread* fr, fr_main_perform_done(fr); } else if(status == fast_reload_notification_reload_stop) { fr_main_perform_reload_stop(fr); + } else if(status == fast_reload_notification_reload_nopause_poll) { + fr_main_perform_reload_nopause_poll(fr); } else { log_err("main received unknown status from fast reload: %d %s", (int)status, fr_notification_to_string(status)); diff --git a/daemon/remote.h b/daemon/remote.h index 180fabed8..4c4f442f4 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -143,7 +143,10 @@ enum fast_reload_notification { /** ack the stop as part of the reload */ fast_reload_notification_reload_ack = 7, /** resume from stop as part of the reload */ - fast_reload_notification_reload_start = 8 + fast_reload_notification_reload_start = 8, + /** the fast reload thread wants the mainthread to poll workers, + * after the reload, sent when nopause is used */ + fast_reload_notification_reload_nopause_poll = 9 }; /** diff --git a/daemon/worker.c b/daemon/worker.c index be8c81302..1738137e4 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -369,14 +369,21 @@ worker_check_request(sldns_buffer* pkt, struct worker* worker, return; } -/** stop and wait to resume the worker */ +/** + * Send fast-reload acknowledgement to the mainthread in one byte. + * This signals that this works has received the previous command. + * The worker is waiting if that is after a reload_stop command. + * Or the worker has briefly processed the event itself, and in doing so + * released data pointers to old config, after a reload_poll command. + */ static void -worker_stop_and_wait(struct worker* worker) +worker_send_reload_ack(struct worker* worker) { + /* If this is clipped to 8 bits because thread_num>255, then that + * is not a problem, the receiver counts the number of bytes received. + * The number is informative only. */ uint8_t c = (uint8_t)worker->thread_num; ssize_t ret; - uint8_t* buf = NULL; - uint32_t len = 0, cmd; while(1) { ret = send(worker->daemon->fast_reload_thread->commreload[1], &c, 1, 0); @@ -400,6 +407,15 @@ worker_stop_and_wait(struct worker* worker) } break; } +} + +/** stop and wait to resume the worker */ +static void +worker_stop_and_wait(struct worker* worker) +{ + uint8_t* buf = NULL; + uint32_t len = 0, cmd; + worker_send_reload_ack(worker); /* wait for reload */ if(!tube_read_msg(worker->cmd, &buf, &len, 0)) { log_err("worker reload read reply failed"); @@ -468,6 +484,10 @@ worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg, verbose(VERB_ALGO, "got control cmd reload_stop"); worker_stop_and_wait(worker); break; + case worker_cmd_reload_poll: + verbose(VERB_ALGO, "got control cmd reload_poll"); + worker_send_reload_ack(worker); + break; default: log_err("bad command %d", (int)cmd); break; diff --git a/daemon/worker.h b/daemon/worker.h index 84e851d06..b7bb52fd7 100644 --- a/daemon/worker.h +++ b/daemon/worker.h @@ -76,7 +76,9 @@ enum worker_commands { /** for fast-reload, perform stop */ worker_cmd_reload_stop, /** for fast-reload, start again */ - worker_cmd_reload_start + worker_cmd_reload_start, + /** for fast-reload, poll to make sure worker has released data */ + worker_cmd_reload_poll }; /** diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index e67a628d8..a3b10260d 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -84,7 +84,10 @@ updated at the same time, so that they are viewed consistently, either old or new values together. The option makes the reload time take eg. 3 microseconds instead of 0.3 milliseconds during which the worker threads are interrupted. So, the interruption is much shorter, at the expense of some -inconsistency. +inconsistency. After the reload itself, every worker thread is briefly +contacted to make them release resources, this makes the delete timing +a little longer, and takes up time from the remote control servicing +worker thread. .IP The '+d' option makes the reload drop queries that the worker threads are working on. This is like flush_requestlist. Without it the queries are kept From 58ff34c3e7c559787eb67312091b01d1791668d3 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 21 Mar 2024 18:44:16 +0100 Subject: [PATCH 048/125] - fast-reload, fix to use atomic store for config variables with nopause. --- config.h.in | 3 + configure | 8 ++ configure.ac | 1 + daemon/remote.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 378 insertions(+), 5 deletions(-) diff --git a/config.h.in b/config.h.in index bc39544c4..f04baa7e9 100644 --- a/config.h.in +++ b/config.h.in @@ -581,6 +581,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STDARG_H +/* Define to 1 if you have the header file. */ +#undef HAVE_STDATOMIC_H + /* Define to 1 if you have the header file. */ #undef HAVE_STDBOOL_H diff --git a/configure b/configure index 062b5ba02..e8b6910d2 100755 --- a/configure +++ b/configure @@ -16007,6 +16007,14 @@ then : fi +ac_fn_c_check_header_compile "$LINENO" "stdatomic.h" "ac_cv_header_stdatomic_h" "$ac_includes_default +" +if test "x$ac_cv_header_stdatomic_h" = xyes +then : + printf "%s\n" "#define HAVE_STDATOMIC_H 1" >>confdefs.h + +fi + # check for types. # Using own tests for int64* because autoconf builtin only give 32bit. diff --git a/configure.ac b/configure.ac index e85fd2eb6..7b8ad44cc 100644 --- a/configure.ac +++ b/configure.ac @@ -471,6 +471,7 @@ AC_CHECK_HEADERS([netioapi.h],,, [AC_INCLUDES_DEFAULT # Check for Linux timestamping headers AC_CHECK_HEADERS([linux/net_tstamp.h],,, [AC_INCLUDES_DEFAULT]) +AC_CHECK_HEADERS([stdatomic.h],,, [AC_INCLUDES_DEFAULT]) # check for types. # Using own tests for int64* because autoconf builtin only give 32bit. diff --git a/daemon/remote.c b/daemon/remote.c index 2b77d8763..12214a005 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -52,6 +52,9 @@ #ifdef HAVE_OPENSSL_BN_H #include #endif +#ifdef HAVE_STDATOMIC_H +#include +#endif #include #include "daemon/remote.h" @@ -4153,6 +4156,351 @@ fr_finish_time(struct fast_reload_thread* fr, struct timeval* time_start, return 1; } +#ifdef ATOMIC_POINTER_LOCK_FREE +/** Fast reload thread, if atomics are available, copy the config items + * one by one with atomic store operations. */ +static void +fr_atomic_copy_cfg(struct config_file* oldcfg, struct config_file* cfg, + struct config_file* newcfg) +{ +#define COPY_VAR(var) oldcfg->var = cfg->var; atomic_store(&cfg->var, newcfg->var); newcfg->var = 0; + /* If config file items are missing from this list, they are + * not updated by fast-reload +p. */ + /* For missing items, the oldcfg item is not updated, still NULL, + * and the cfg stays the same. The newcfg item is untouched. + * The newcfg item is then deleted later. */ + /* Items that need synchronisation are omitted from the list. + * Use fast-reload without +p to update them together. */ + COPY_VAR(verbosity); + COPY_VAR(stat_interval); + COPY_VAR(stat_cumulative); + COPY_VAR(stat_extended); + COPY_VAR(stat_inhibit_zero); + COPY_VAR(num_threads); + COPY_VAR(port); + COPY_VAR(do_ip4); + COPY_VAR(do_ip6); + COPY_VAR(do_nat64); + COPY_VAR(prefer_ip4); + COPY_VAR(prefer_ip6); + COPY_VAR(do_udp); + COPY_VAR(do_tcp); + COPY_VAR(max_reuse_tcp_queries); + COPY_VAR(tcp_reuse_timeout); + COPY_VAR(tcp_auth_query_timeout); + COPY_VAR(tcp_upstream); + COPY_VAR(udp_upstream_without_downstream); + COPY_VAR(tcp_mss); + COPY_VAR(outgoing_tcp_mss); + COPY_VAR(tcp_idle_timeout); + COPY_VAR(do_tcp_keepalive); + COPY_VAR(tcp_keepalive_timeout); + COPY_VAR(sock_queue_timeout); + COPY_VAR(proxy_protocol_port); + COPY_VAR(ssl_service_key); + COPY_VAR(ssl_service_pem); + COPY_VAR(ssl_port); + COPY_VAR(ssl_upstream); + COPY_VAR(tls_cert_bundle); + COPY_VAR(tls_win_cert); + COPY_VAR(tls_additional_port); + /* The first is used to walk throught the list but last is + * only used during config read. */ + COPY_VAR(tls_session_ticket_keys.first); + COPY_VAR(tls_session_ticket_keys.last); + COPY_VAR(tls_ciphers); + COPY_VAR(tls_ciphersuites); + COPY_VAR(tls_use_sni); + COPY_VAR(https_port); + COPY_VAR(http_endpoint); + COPY_VAR(http_max_streams); + COPY_VAR(http_query_buffer_size); + COPY_VAR(http_response_buffer_size); + COPY_VAR(http_nodelay); + COPY_VAR(http_notls_downstream); + COPY_VAR(outgoing_num_ports); + COPY_VAR(outgoing_num_tcp); + COPY_VAR(incoming_num_tcp); + COPY_VAR(outgoing_avail_ports); + COPY_VAR(edns_buffer_size); + COPY_VAR(stream_wait_size); + COPY_VAR(msg_buffer_size); + COPY_VAR(msg_cache_size); + COPY_VAR(msg_cache_slabs); + COPY_VAR(num_queries_per_thread); + COPY_VAR(jostle_time); + COPY_VAR(rrset_cache_size); + COPY_VAR(rrset_cache_slabs); + COPY_VAR(host_ttl); + COPY_VAR(infra_cache_slabs); + COPY_VAR(infra_cache_numhosts); + COPY_VAR(infra_cache_min_rtt); + COPY_VAR(infra_cache_max_rtt); + COPY_VAR(infra_keep_probing); + COPY_VAR(delay_close); + COPY_VAR(udp_connect); + COPY_VAR(target_fetch_policy); + COPY_VAR(fast_server_permil); + COPY_VAR(fast_server_num); + COPY_VAR(if_automatic); + COPY_VAR(if_automatic_ports); + COPY_VAR(so_rcvbuf); + COPY_VAR(so_sndbuf); + COPY_VAR(so_reuseport); + COPY_VAR(ip_transparent); + COPY_VAR(ip_freebind); + COPY_VAR(ip_dscp); + /* Not copied because the length and items could then not match. + num_ifs, ifs, num_out_ifs, out_ifs + */ + COPY_VAR(root_hints); + COPY_VAR(stubs); + COPY_VAR(forwards); + COPY_VAR(auths); + COPY_VAR(views); + COPY_VAR(donotqueryaddrs); +#ifdef CLIENT_SUBNET + COPY_VAR(client_subnet); + COPY_VAR(client_subnet_zone); + COPY_VAR(client_subnet_opcode); + COPY_VAR(client_subnet_always_forward); + COPY_VAR(max_client_subnet_ipv4); + COPY_VAR(max_client_subnet_ipv6); + COPY_VAR(min_client_subnet_ipv4); + COPY_VAR(min_client_subnet_ipv6); + COPY_VAR(max_ecs_tree_size_ipv4); + COPY_VAR(max_ecs_tree_size_ipv6); +#endif + COPY_VAR(acls); + COPY_VAR(donotquery_localhost); + COPY_VAR(tcp_connection_limits); + COPY_VAR(harden_short_bufsize); + COPY_VAR(harden_large_queries); + COPY_VAR(harden_glue); + COPY_VAR(harden_dnssec_stripped); + COPY_VAR(harden_below_nxdomain); + COPY_VAR(harden_referral_path); + COPY_VAR(harden_algo_downgrade); + COPY_VAR(harden_unknown_additional); + COPY_VAR(use_caps_bits_for_id); + COPY_VAR(caps_whitelist); + COPY_VAR(private_address); + COPY_VAR(private_domain); + COPY_VAR(unwanted_threshold); + COPY_VAR(max_ttl); + COPY_VAR(min_ttl); + COPY_VAR(max_negative_ttl); + COPY_VAR(prefetch); + COPY_VAR(prefetch_key); + COPY_VAR(deny_any); + COPY_VAR(chrootdir); + COPY_VAR(username); + COPY_VAR(directory); + COPY_VAR(logfile); + COPY_VAR(pidfile); + COPY_VAR(use_syslog); + COPY_VAR(log_time_ascii); + COPY_VAR(log_queries); + COPY_VAR(log_replies); + COPY_VAR(log_tag_queryreply); + COPY_VAR(log_local_actions); + COPY_VAR(log_servfail); + COPY_VAR(log_identity); + COPY_VAR(log_destaddr); + COPY_VAR(hide_identity); + COPY_VAR(hide_version); + COPY_VAR(hide_trustanchor); + COPY_VAR(hide_http_user_agent); + COPY_VAR(identity); + COPY_VAR(version); + COPY_VAR(http_user_agent); + COPY_VAR(nsid_cfg_str); + /* Not copied because the length and items could then not match. + nsid; + nsid_len; + */ + COPY_VAR(module_conf); + COPY_VAR(trust_anchor_file_list); + COPY_VAR(trust_anchor_list); + COPY_VAR(auto_trust_anchor_file_list); + COPY_VAR(trusted_keys_file_list); + COPY_VAR(domain_insecure); + COPY_VAR(trust_anchor_signaling); + COPY_VAR(root_key_sentinel); + COPY_VAR(val_date_override); + COPY_VAR(val_sig_skew_min); + COPY_VAR(val_sig_skew_max); + COPY_VAR(val_max_restart); + COPY_VAR(bogus_ttl); + COPY_VAR(val_clean_additional); + COPY_VAR(val_log_level); + COPY_VAR(val_log_squelch); + COPY_VAR(val_permissive_mode); + COPY_VAR(aggressive_nsec); + COPY_VAR(ignore_cd); + COPY_VAR(disable_edns_do); + COPY_VAR(serve_expired); + COPY_VAR(serve_expired_ttl); + COPY_VAR(serve_expired_ttl_reset); + COPY_VAR(serve_expired_reply_ttl); + COPY_VAR(serve_expired_client_timeout); + COPY_VAR(ede_serve_expired); + COPY_VAR(serve_original_ttl); + COPY_VAR(val_nsec3_key_iterations); + COPY_VAR(zonemd_permissive_mode); + COPY_VAR(add_holddown); + COPY_VAR(del_holddown); + COPY_VAR(keep_missing); + COPY_VAR(permit_small_holddown); + COPY_VAR(key_cache_size); + COPY_VAR(key_cache_slabs); + COPY_VAR(neg_cache_size); + COPY_VAR(local_zones); + COPY_VAR(local_zones_nodefault); +#ifdef USE_IPSET + COPY_VAR(local_zones_ipset); +#endif + COPY_VAR(local_zones_disable_default); + COPY_VAR(local_data); + COPY_VAR(local_zone_overrides); + COPY_VAR(unblock_lan_zones); + COPY_VAR(insecure_lan_zones); + /* These reference tags + COPY_VAR(local_zone_tags); + COPY_VAR(acl_tags); + COPY_VAR(acl_tag_actions); + COPY_VAR(acl_tag_datas); + */ + COPY_VAR(acl_view); + COPY_VAR(interface_actions); + /* These reference tags + COPY_VAR(interface_tags); + COPY_VAR(interface_tag_actions); + COPY_VAR(interface_tag_datas); + */ + COPY_VAR(interface_view); + /* This references tags + COPY_VAR(respip_tags); + */ + COPY_VAR(respip_actions); + COPY_VAR(respip_data); + /* Not copied because the length and items could then not match. + * also the respip module keeps a pointer to the array in its state. + tagname, num_tags + */ + COPY_VAR(remote_control_enable); + /* The first is used to walk throught the list but last is + * only used during config read. */ + COPY_VAR(control_ifs.first); + COPY_VAR(control_ifs.last); + COPY_VAR(control_use_cert); + COPY_VAR(control_port); + COPY_VAR(server_key_file); + COPY_VAR(server_cert_file); + COPY_VAR(control_key_file); + COPY_VAR(control_cert_file); + COPY_VAR(python_script); + COPY_VAR(dynlib_file); + COPY_VAR(use_systemd); + COPY_VAR(do_daemonize); + COPY_VAR(minimal_responses); + COPY_VAR(rrset_roundrobin); + COPY_VAR(unknown_server_time_limit); + COPY_VAR(max_udp_size); + COPY_VAR(dns64_prefix); + COPY_VAR(dns64_synthall); + COPY_VAR(dns64_ignore_aaaa); + COPY_VAR(nat64_prefix); + COPY_VAR(dnstap); + COPY_VAR(dnstap_bidirectional); + COPY_VAR(dnstap_socket_path); + COPY_VAR(dnstap_ip); + COPY_VAR(dnstap_tls); + COPY_VAR(dnstap_tls_server_name); + COPY_VAR(dnstap_tls_cert_bundle); + COPY_VAR(dnstap_tls_client_key_file); + COPY_VAR(dnstap_tls_client_cert_file); + COPY_VAR(dnstap_send_identity); + COPY_VAR(dnstap_send_version); + COPY_VAR(dnstap_identity); + COPY_VAR(dnstap_version); + COPY_VAR(dnstap_log_resolver_query_messages); + COPY_VAR(dnstap_log_resolver_response_messages); + COPY_VAR(dnstap_log_client_query_messages); + COPY_VAR(dnstap_log_client_response_messages); + COPY_VAR(dnstap_log_forwarder_query_messages); + COPY_VAR(dnstap_log_forwarder_response_messages); + COPY_VAR(disable_dnssec_lame_check); + COPY_VAR(ip_ratelimit); + COPY_VAR(ip_ratelimit_cookie); + COPY_VAR(ip_ratelimit_slabs); + COPY_VAR(ip_ratelimit_size); + COPY_VAR(ip_ratelimit_factor); + COPY_VAR(ip_ratelimit_backoff); + COPY_VAR(ratelimit); + COPY_VAR(ratelimit_slabs); + COPY_VAR(ratelimit_size); + COPY_VAR(ratelimit_for_domain); + COPY_VAR(ratelimit_below_domain); + COPY_VAR(ratelimit_factor); + COPY_VAR(ratelimit_backoff); + COPY_VAR(outbound_msg_retry); + COPY_VAR(max_sent_count); + COPY_VAR(max_query_restarts); + COPY_VAR(qname_minimisation); + COPY_VAR(qname_minimisation_strict); + COPY_VAR(shm_enable); + COPY_VAR(shm_key); + COPY_VAR(edns_client_strings); + COPY_VAR(edns_client_string_opcode); + COPY_VAR(dnscrypt); + COPY_VAR(dnscrypt_port); + COPY_VAR(dnscrypt_provider); + COPY_VAR(dnscrypt_secret_key); + COPY_VAR(dnscrypt_provider_cert); + COPY_VAR(dnscrypt_provider_cert_rotated); + COPY_VAR(dnscrypt_shared_secret_cache_size); + COPY_VAR(dnscrypt_shared_secret_cache_slabs); + COPY_VAR(dnscrypt_nonce_cache_size); + COPY_VAR(dnscrypt_nonce_cache_slabs); + COPY_VAR(pad_responses); + COPY_VAR(pad_responses_block_size); + COPY_VAR(pad_queries); + COPY_VAR(pad_queries_block_size); +#ifdef USE_IPSECMOD + COPY_VAR(ipsecmod_enabled); + COPY_VAR(ipsecmod_whitelist); + COPY_VAR(ipsecmod_hook); + COPY_VAR(ipsecmod_ignore_bogus); + COPY_VAR(ipsecmod_max_ttl); + COPY_VAR(ipsecmod_strict); +#endif +#ifdef USE_CACHEDB + COPY_VAR(cachedb_backend); + COPY_VAR(cachedb_secret); + COPY_VAR(cachedb_no_store); +#ifdef USE_REDIS + COPY_VAR(redis_server_host); + COPY_VAR(redis_server_port); + COPY_VAR(redis_server_path); + COPY_VAR(redis_server_password); + COPY_VAR(redis_timeout); + COPY_VAR(redis_expire_records); + COPY_VAR(redis_logical_db); +#endif +#endif + COPY_VAR(do_answer_cookie); + /* Not copied because the length and content could then not match. + cookie_secret[40], cookie_secret_len + */ +#ifdef USE_IPSET + COPY_VAR(ipset_name_v4); + COPY_VAR(ipset_name_v6); +#endif + COPY_VAR(ede); +} +#endif /* ATOMIC_POINTER_LOCK_FREE */ + /** fast reload thread, reload config with putting the new config items * in place and swapping out the old items. */ static int @@ -4173,16 +4521,29 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, lock_rw_wrlock(&env->fwds->lock); lock_rw_wrlock(&env->hints->lock); - /* Store old config elements. */ - *ct->oldcfg = *env->cfg; - /* Insert new config elements. */ - *env->cfg = *newcfg; +#ifdef ATOMIC_POINTER_LOCK_FREE + if(fr->fr_nopause) { + fr_atomic_copy_cfg(ct->oldcfg, env->cfg, newcfg); + } else { +#endif + /* Store old config elements. */ + *ct->oldcfg = *env->cfg; + /* Insert new config elements. */ + *env->cfg = *newcfg; +#ifdef ATOMIC_POINTER_LOCK_FREE + } +#endif + if(env->cfg->log_identity || ct->oldcfg->log_identity) { /* pick up new log_identity string to use for log output. */ log_ident_set_or_default(env->cfg->log_identity); } /* the newcfg elements are in env->cfg, so should not be freed here. */ - memset(newcfg, 0, sizeof(*newcfg)); +#ifdef ATOMIC_POINTER_LOCK_FREE + /* if used, the routine that copies the config has zeroed items. */ + if(!fr->fr_nopause) +#endif + memset(newcfg, 0, sizeof(*newcfg)); /* Quickly swap the tree roots themselves with the already allocated * elements. This is a quick swap operation on the pointer. From 1aa98000067a72ecd3f074d9d33c5c7f3157088b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 5 Apr 2024 14:28:40 +0200 Subject: [PATCH 049/125] - fast-reload, reload views. --- Makefile.in | 2 +- daemon/remote.c | 32 +++++++++++++++++++++++--------- respip/respip.c | 9 +++++++++ respip/respip.h | 5 +++++ services/localzone.c | 23 +++++++++++++++++++++++ services/localzone.h | 5 +++++ services/view.c | 39 ++++++++++++++++++++++++++++++++++----- services/view.h | 26 +++++++++++++++++++++++++- util/config_file.c | 7 +++++++ util/config_file.h | 3 +++ 10 files changed, 135 insertions(+), 16 deletions(-) diff --git a/Makefile.in b/Makefile.in index 79379d8d1..eb9fce66a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -866,7 +866,7 @@ view.lo view.o: $(srcdir)/services/view.c config.h $(srcdir)/services/view.h $(s $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h \ $(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h \ $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \ - $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h + $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h $(srcdir)/respip/respip.h rpz.lo rpz.o: $(srcdir)/services/rpz.c config.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \ $(srcdir)/util/rbtree.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/storage/dnstree.h \ $(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h \ diff --git a/daemon/remote.c b/daemon/remote.c index 12214a005..95b1bb229 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3767,6 +3767,8 @@ fr_init_time(struct timeval* time_start, struct timeval* time_read, * are kept in here. They can then be deleted. */ struct fast_reload_construct { + /** construct for views */ + struct views* views; /** construct for forwards */ struct iter_forwards* fwds; /** construct for stubs */ @@ -3821,6 +3823,7 @@ fr_construct_clear(struct fast_reload_construct* ct) return; forwards_delete(ct->fwds); hints_delete(ct->hints); + views_delete(ct->views); /* Delete the log identity here so that the global value is not * reset by config_delete. */ if(ct->oldcfg && ct->oldcfg->log_identity) { @@ -3830,14 +3833,6 @@ fr_construct_clear(struct fast_reload_construct* ct) config_delete(ct->oldcfg); } -/** get memory for string */ -static size_t -getmem_str(char* str) -{ - if(!str) return 0; - return strlen(str)+1; -} - /** get memory for strlist */ static size_t getmem_config_strlist(struct config_strlist* p) @@ -4074,6 +4069,7 @@ fr_printmem(struct fast_reload_thread* fr, size_t mem = 0; if(fr_poll_for_quit(fr)) return 1; + mem += views_get_mem(ct->views); mem += forwards_get_mem(ct->fwds); mem += hints_get_mem(ct->hints); mem += sizeof(*ct->oldcfg); @@ -4091,8 +4087,21 @@ static int fr_construct_from_config(struct fast_reload_thread* fr, struct config_file* newcfg, struct fast_reload_construct* ct) { - if(!(ct->fwds = forwards_create())) + if(!(ct->views = views_create())) { + fr_construct_clear(ct); + return 0; + } + if(!views_apply_cfg(ct->views, newcfg)) { + fr_construct_clear(ct); return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + + if(!(ct->fwds = forwards_create())) { + fr_construct_clear(ct); + return 0; + } if(!forwards_apply_cfg(ct->fwds, newcfg)) { fr_construct_clear(ct); return 0; @@ -4516,6 +4525,8 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, log_assert(ct->hints); /* Grab big locks to satisfy lock conditions. */ + lock_rw_wrlock(&ct->views->lock); + lock_rw_wrlock(&daemon->views->lock); lock_rw_wrlock(&ct->fwds->lock); lock_rw_wrlock(&ct->hints->lock); lock_rw_wrlock(&env->fwds->lock); @@ -4552,10 +4563,13 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, * towards the state machine for query resolution. */ forwards_swap_tree(env->fwds, ct->fwds); hints_swap_tree(env->hints, ct->hints); + views_swap_tree(daemon->views, ct->views); /* Set globals with new config. */ config_apply(env->cfg); + lock_rw_unlock(&ct->views->lock); + lock_rw_unlock(&daemon->views->lock); lock_rw_unlock(&env->fwds->lock); lock_rw_unlock(&env->hints->lock); lock_rw_unlock(&ct->fwds->lock); diff --git a/respip/respip.c b/respip/respip.c index 942e082b9..d698954b6 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -1326,3 +1326,12 @@ respip_inform_print(struct respip_action_info* respip_actinfo, uint8_t* qname, (actionstr) ? actionstr : "inform", srcip, port); log_nametypeclass(NO_VERBOSE, txt, qname, qtype, qclass); } + +size_t respip_set_get_mem(struct respip_set* set) +{ + size_t m = sizeof(*set); + lock_rw_rdlock(&set->lock); + m += regional_get_mem(set->region); + lock_rw_unlock(&set->lock); + return m; +} diff --git a/respip/respip.h b/respip/respip.h index e4ab5cc9c..f43052e22 100644 --- a/respip/respip.h +++ b/respip/respip.h @@ -302,4 +302,9 @@ respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node); struct ub_packed_rrset_key* respip_copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region); + +/** Get memory usage of respip set tree. The routine locks and unlocks the + * set for reading. */ +size_t respip_set_get_mem(struct respip_set* set); + #endif /* RESPIP_RESPIP_H */ diff --git a/services/localzone.c b/services/localzone.c index 51056c8ff..ec1123054 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -2203,3 +2203,26 @@ void local_zones_del_data(struct local_zones* zones, lock_rw_unlock(&z->lock); } + +/** Get memory usage for local_zone */ +static size_t +local_zone_get_mem(struct local_zone* z) +{ + size_t m = sizeof(*z); + lock_rw_rdlock(&z->lock); + m += z->namelen + z->taglen + regional_get_mem(z->region); + lock_rw_unlock(&z->lock); + return m; +} + +size_t local_zones_get_mem(struct local_zones* zones) +{ + struct local_zone* z; + size_t m = sizeof(*zones); + lock_rw_rdlock(&zones->lock); + RBTREE_FOR(z, struct local_zone*, &zones->ztree) { + m += local_zone_get_mem(z); + } + lock_rw_unlock(&zones->lock); + return m; +} diff --git a/services/localzone.h b/services/localzone.h index 4456893ee..7a51ec59d 100644 --- a/services/localzone.h +++ b/services/localzone.h @@ -641,4 +641,9 @@ local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen, */ struct local_data* local_zone_find_data(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs); + +/** Get memory usage for local_zones tree. The routine locks and unlocks + * the tree for reading. */ +size_t local_zones_get_mem(struct local_zones* zones); + #endif /* SERVICES_LOCALZONE_H */ diff --git a/services/view.c b/services/view.c index 72f364318..4ecd52d49 100644 --- a/services/view.c +++ b/services/view.c @@ -43,6 +43,7 @@ #include "services/view.h" #include "services/localzone.h" #include "util/config_file.h" +#include "respip/respip.h" int view_cmp(const void* v1, const void* v2) @@ -66,11 +67,6 @@ views_create(void) return v; } -/* \noop (ignore this comment for doxygen) - * This prototype is defined in in respip.h, but we want to avoid - * unnecessary dependencies */ -void respip_set_delete(struct respip_set *set); - void view_delete(struct view* v) { @@ -247,3 +243,36 @@ void views_print(struct views* v) /* TODO implement print */ (void)v; } + +size_t views_get_mem(struct views* vs) +{ + struct view* v; + size_t m = sizeof(struct views); + lock_rw_rdlock(&vs->lock); + RBTREE_FOR(v, struct view*, &vs->vtree) { + m += view_get_mem(v); + } + lock_rw_unlock(&vs->lock); + return m; +} + +size_t view_get_mem(struct view* v) +{ + size_t m = sizeof(*v); + lock_rw_rdlock(&v->lock); + m += getmem_str(v->name); + m += local_zones_get_mem(v->local_zones); + m += respip_set_get_mem(v->respip_set); + lock_rw_unlock(&v->lock); + return m; +} + +void views_swap_tree(struct views* vs, struct views* data) +{ + rbnode_type* oldroot = vs->vtree.root; + size_t oldcount = vs->vtree.count; + vs->vtree.root = data->vtree.root; + vs->vtree.count = data->vtree.count; + data->vtree.root = oldroot; + data->vtree.count = oldcount; +} diff --git a/services/view.h b/services/view.h index 177781004..b0eebf002 100644 --- a/services/view.h +++ b/services/view.h @@ -54,7 +54,8 @@ struct respip_set; * Views storage, shared. */ struct views { - /** lock on the view tree */ + /** lock on the view tree. When locking order, the views lock + * is before the forwards,hints,anchors lock. */ lock_rw_type lock; /** rbtree of struct view */ rbtree_type vtree; @@ -134,4 +135,27 @@ void views_print(struct views* v); */ struct view* views_find_view(struct views* vs, const char* name, int write); +/** + * Calculate memory usage of views. + * @param vs: the views tree. The routine locks and unlocks the structure + * for reading. + * @return memory in bytes. + */ +size_t views_get_mem(struct views* vs); + +/** + * Calculate memory usage of view. + * @param v: the view. The routine locks and unlocks the structure for reading. + * @return memory in bytes. + */ +size_t view_get_mem(struct view* v); + +/** + * Swap internal tree with preallocated entries. Caller should manage + * the locks. + * @param vs: views tree + * @param data: preallocated information. + */ +void views_swap_tree(struct views* vs, struct views* data); + #endif /* SERVICES_VIEW_H */ diff --git a/util/config_file.c b/util/config_file.c index 26185da02..90a4b6e4b 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -2722,3 +2722,10 @@ if_is_dnscrypt(const char* ifname, const char* port, int dnscrypt_port) return 0; #endif } + +size_t +getmem_str(char* str) +{ + if(!str) return 0; + return strlen(str)+1; +} diff --git a/util/config_file.h b/util/config_file.h index 491109833..0739e0a52 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -1373,4 +1373,7 @@ int if_is_dnscrypt(const char* ifname, const char* port, int dnscrypt_port); #define LINUX_IP_LOCAL_PORT_RANGE_PATH "/proc/sys/net/ipv4/ip_local_port_range" #endif +/** get memory for string */ +size_t getmem_str(char* str); + #endif /* UTIL_CONFIG_FILE_H */ From cd33d214511e342f59a0aa08538239758198e5f8 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 5 Apr 2024 14:58:23 +0200 Subject: [PATCH 050/125] - fast-reload, when tag defines are different, it drops the queries. --- daemon/remote.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index 061c379c5..070049850 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3821,6 +3821,66 @@ fr_read_config(struct fast_reload_thread* fr, struct config_file** newcfg) return 1; } +/** Check if two taglists are equal. */ +static int +taglist_equal(char** tagname_a, int num_tags_a, char** tagname_b, + int num_tags_b) +{ + int i; + if(num_tags_a != num_tags_b) + return 0; + for(i=0; i= num_tags_b) + return 0; + /* So, b is longer than a. Check if the initial start of the two + * taglists is the same. */ + if(!taglist_equal(tagname_a, num_tags_a, tagname_b, num_tags_a)) + return 0; + return 1; +} + +/** fast reload thread, check tag defines. */ +static int +fr_check_tag_defines(struct fast_reload_thread* fr, struct config_file* newcfg) +{ + /* The tags are kept in a bitlist for items. Some of them are stored + * in query info. If the tags change, then the old values are + * inaccurate. The solution is to then flush the query list. + * Unless the change only involves adding new tags at the end, that + * needs no changes. */ + if(!taglist_equal(fr->worker->daemon->cfg->tagname, + fr->worker->daemon->cfg->num_tags, newcfg->tagname, + newcfg->num_tags) || + !taglist_change_at_end(fr->worker->daemon->cfg->tagname, + fr->worker->daemon->cfg->num_tags, newcfg->tagname, + newcfg->num_tags)) { + /* The tags have changed too much, the define-tag config. */ + if(fr->fr_drop_mesh) + return 1; /* already dropping queries */ + fr->fr_drop_mesh = 1; + fr->worker->daemon->fast_reload_drop_mesh = fr->fr_drop_mesh; + if(!fr_output_printf(fr, "tags have changed, with " + "'define-tag', and the queries have to be dropped " + "for consistency, setting '+d'\n")) + return 0; + fr_send_notification(fr, fast_reload_notification_printout); + } + return 1; +} + /** fast reload thread, clear construct information, deletes items */ static void fr_construct_clear(struct fast_reload_construct* ct) @@ -4683,6 +4743,16 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, return 1; } + /* Check if the config can be loaded */ + if(!fr_check_tag_defines(fr, newcfg)) { + config_delete(newcfg); + return 0; + } + if(fr_poll_for_quit(fr)) { + config_delete(newcfg); + return 1; + } + /* Construct items. */ if(!fr_construct_from_config(fr, newcfg, &ct)) { config_delete(newcfg); From d9cfc8d3180ac39f47e2d75c93048281000ce059 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 5 Apr 2024 16:09:28 +0200 Subject: [PATCH 051/125] - fast-reload, fix tag define check. --- daemon/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 070049850..75135c155 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3863,7 +3863,7 @@ fr_check_tag_defines(struct fast_reload_thread* fr, struct config_file* newcfg) * needs no changes. */ if(!taglist_equal(fr->worker->daemon->cfg->tagname, fr->worker->daemon->cfg->num_tags, newcfg->tagname, - newcfg->num_tags) || + newcfg->num_tags) && !taglist_change_at_end(fr->worker->daemon->cfg->tagname, fr->worker->daemon->cfg->num_tags, newcfg->tagname, newcfg->num_tags)) { From d52018a201e31f345f28cf78b890332de15c018f Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 5 Apr 2024 16:58:36 +0200 Subject: [PATCH 052/125] - fast-reload, document that tag change causes drop of queries. --- doc/unbound-control.8.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 4bc20a9a0..1940f1211 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -94,6 +94,12 @@ working on. This is like flush_requestlist. Without it the queries are kept so that users keep getting answers for those queries that are currently processed. The drop makes it so that queries during the life time of the query processing see only old, or only new config options. +.IP +When there are changes to the config tags, from \fBdefine\-tag\fR config, +then the '+d' option is turned on with a warning printout, and queries are +dropped. This is to stop references to the old tag information, by the old +queries. If the number of tags is increased in the newly loaded config, by +adding tags at the end, then the '+d' option is not needed. .TP .B verbosity \fInumber Change verbosity value for logging. Same values as \fBverbosity\fR keyword in From f06b1c1ded0f99f548f410865687e4b0540b12bb Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 8 Apr 2024 17:07:41 +0200 Subject: [PATCH 053/125] - fast-reload, fix space in documentation man page. --- doc/unbound-control.8.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 1940f1211..f7d5a0509 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -95,7 +95,7 @@ so that users keep getting answers for those queries that are currently processed. The drop makes it so that queries during the life time of the query processing see only old, or only new config options. .IP -When there are changes to the config tags, from \fBdefine\-tag\fR config, +When there are changes to the config tags, from \fBdefine\-tag\fR config, then the '+d' option is turned on with a warning printout, and queries are dropped. This is to stop references to the old tag information, by the old queries. If the number of tags is increased in the newly loaded config, by From 4067473329817e2a0e6d40f5815cdc8096b9c9b4 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 8 Apr 2024 18:41:07 +0200 Subject: [PATCH 054/125] - fast-reload, copy respip client information to query state, put views tree in module env for lookup. --- daemon/daemon.c | 14 ++++----- daemon/daemon.h | 2 -- daemon/remote.c | 28 +++++++---------- daemon/worker.c | 6 ++-- respip/respip.c | 26 +++++++++++----- respip/respip.h | 11 +++++-- services/mesh.c | 81 +++++++++++++++++++++++++++++++++++++++++++++---- util/module.h | 2 ++ 8 files changed, 127 insertions(+), 43 deletions(-) diff --git a/daemon/daemon.c b/daemon/daemon.c index bb01f3206..c09e2f9ed 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -687,16 +687,16 @@ daemon_fork(struct daemon* daemon) #endif log_assert(daemon); - if(!(daemon->views = views_create())) + if(!(daemon->env->views = views_create())) fatal_exit("Could not create views: out of memory"); /* create individual views and their localzone/data trees */ - if(!views_apply_cfg(daemon->views, daemon->cfg)) + if(!views_apply_cfg(daemon->env->views, daemon->cfg)) fatal_exit("Could not set up views"); - if(!acl_list_apply_cfg(daemon->acl, daemon->cfg, daemon->views)) + if(!acl_list_apply_cfg(daemon->acl, daemon->cfg, daemon->env->views)) fatal_exit("Could not setup access control list"); if(!acl_interface_apply_cfg(daemon->acl_interface, daemon->cfg, - daemon->views)) + daemon->env->views)) fatal_exit("Could not setup interface control list"); if(!tcl_list_apply_cfg(daemon->tcl, daemon->cfg)) fatal_exit("Could not setup TCP connection limits"); @@ -728,7 +728,7 @@ daemon_fork(struct daemon* daemon) fatal_exit("Could not create response IP set"); if(!respip_global_apply_cfg(daemon->respip_set, daemon->cfg)) fatal_exit("Could not set up response IP set"); - if(!respip_views_apply_cfg(daemon->views, daemon->cfg, + if(!respip_views_apply_cfg(daemon->env->views, daemon->cfg, &have_view_respip_cfg)) fatal_exit("Could not set up per-view response IP sets"); daemon->use_response_ip = !respip_set_is_empty(daemon->respip_set) || @@ -846,8 +846,8 @@ daemon_cleanup(struct daemon* daemon) daemon->local_zones = NULL; respip_set_delete(daemon->respip_set); daemon->respip_set = NULL; - views_delete(daemon->views); - daemon->views = NULL; + views_delete(daemon->env->views); + daemon->env->views = NULL; if(daemon->env->auth_zones) auth_zones_cleanup(daemon->env->auth_zones); /* key cache is cleared by module desetup during next daemon_fork() */ diff --git a/daemon/daemon.h b/daemon/daemon.h index f76ced23a..a38365796 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -129,8 +129,6 @@ struct daemon { struct timeval time_last_stat; /** time when daemon started */ struct timeval time_boot; - /** views structure containing view tree */ - struct views* views; #ifdef USE_DNSTAP /** the dnstap environment master value, copied and changed by threads*/ struct dt_env* dtenv; diff --git a/daemon/remote.c b/daemon/remote.c index 75135c155..5992c4c30 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1482,8 +1482,7 @@ do_view_zone_add(RES* ssl, struct worker* worker, char* arg) struct view* v; if(!find_arg2(ssl, arg, &arg2)) return; - v = views_find_view(worker->daemon->views, - arg, 1 /* get write lock*/); + v = views_find_view(worker->env.views, arg, 1 /* get write lock*/); if(!v) { ssl_printf(ssl,"no view with name: %s\n", arg); return; @@ -1515,8 +1514,7 @@ do_view_zone_remove(RES* ssl, struct worker* worker, char* arg) struct view* v; if(!find_arg2(ssl, arg, &arg2)) return; - v = views_find_view(worker->daemon->views, - arg, 1 /* get write lock*/); + v = views_find_view(worker->env.views, arg, 1 /* get write lock*/); if(!v) { ssl_printf(ssl,"no view with name: %s\n", arg); return; @@ -1538,8 +1536,7 @@ do_view_data_add(RES* ssl, struct worker* worker, char* arg) struct view* v; if(!find_arg2(ssl, arg, &arg2)) return; - v = views_find_view(worker->daemon->views, - arg, 1 /* get write lock*/); + v = views_find_view(worker->env.views, arg, 1 /* get write lock*/); if(!v) { ssl_printf(ssl,"no view with name: %s\n", arg); return; @@ -1560,8 +1557,7 @@ static void do_view_datas_add(RES* ssl, struct worker* worker, char* arg) { struct view* v; - v = views_find_view(worker->daemon->views, - arg, 1 /* get write lock*/); + v = views_find_view(worker->env.views, arg, 1 /* get write lock*/); if(!v) { ssl_printf(ssl,"no view with name: %s\n", arg); return; @@ -1585,8 +1581,7 @@ do_view_data_remove(RES* ssl, struct worker* worker, char* arg) struct view* v; if(!find_arg2(ssl, arg, &arg2)) return; - v = views_find_view(worker->daemon->views, - arg, 1 /* get write lock*/); + v = views_find_view(worker->env.views, arg, 1 /* get write lock*/); if(!v) { ssl_printf(ssl,"no view with name: %s\n", arg); return; @@ -1605,8 +1600,7 @@ static void do_view_datas_remove(RES* ssl, struct worker* worker, char* arg) { struct view* v; - v = views_find_view(worker->daemon->views, - arg, 1 /* get write lock*/); + v = views_find_view(worker->env.views, arg, 1 /* get write lock*/); if(!v) { ssl_printf(ssl,"no view with name: %s\n", arg); return; @@ -2940,7 +2934,7 @@ do_list_local_data(RES* ssl, struct worker* worker, struct local_zones* zones) static void do_view_list_local_zones(RES* ssl, struct worker* worker, char* arg) { - struct view* v = views_find_view(worker->daemon->views, + struct view* v = views_find_view(worker->env.views, arg, 0 /* get read lock*/); if(!v) { ssl_printf(ssl,"no view with name: %s\n", arg); @@ -2956,7 +2950,7 @@ do_view_list_local_zones(RES* ssl, struct worker* worker, char* arg) static void do_view_list_local_data(RES* ssl, struct worker* worker, char* arg) { - struct view* v = views_find_view(worker->daemon->views, + struct view* v = views_find_view(worker->env.views, arg, 0 /* get read lock*/); if(!v) { ssl_printf(ssl,"no view with name: %s\n", arg); @@ -4592,7 +4586,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, /* Grab big locks to satisfy lock conditions. */ lock_rw_wrlock(&ct->views->lock); - lock_rw_wrlock(&daemon->views->lock); + lock_rw_wrlock(&env->views->lock); lock_rw_wrlock(&ct->fwds->lock); lock_rw_wrlock(&ct->hints->lock); lock_rw_wrlock(&env->fwds->lock); @@ -4629,13 +4623,13 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, * towards the state machine for query resolution. */ forwards_swap_tree(env->fwds, ct->fwds); hints_swap_tree(env->hints, ct->hints); - views_swap_tree(daemon->views, ct->views); + views_swap_tree(env->views, ct->views); /* Set globals with new config. */ config_apply(env->cfg); lock_rw_unlock(&ct->views->lock); - lock_rw_unlock(&daemon->views->lock); + lock_rw_unlock(&env->views->lock); lock_rw_unlock(&env->fwds->lock); lock_rw_unlock(&env->hints->lock); lock_rw_unlock(&ct->fwds->lock); diff --git a/daemon/worker.c b/daemon/worker.c index 45222c867..4bddf97d7 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -682,7 +682,8 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo, return 1; if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, &actinfo, - alias_rrset, 0, worker->scratchpad, az, NULL)) + alias_rrset, 0, worker->scratchpad, az, NULL, + worker->env.views)) return 0; /* xxx_deny actions mean dropping the reply, unless the original reply @@ -842,7 +843,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, } else if(partial_rep && !respip_merge_cname(partial_rep, qinfo, rep, cinfo, must_validate, &encode_rep, worker->scratchpad, - worker->env.auth_zones)) { + worker->env.auth_zones, worker->env.views)) { goto bail_out; } if(encode_rep != rep) { @@ -1893,6 +1894,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, cinfo_tmp.tag_datas = acladdr->tag_datas; cinfo_tmp.tag_datas_size = acladdr->tag_datas_size; cinfo_tmp.view = acladdr->view; + cinfo_tmp.view_name = NULL; cinfo_tmp.respip_set = worker->daemon->respip_set; cinfo = &cinfo_tmp; } diff --git a/respip/respip.c b/respip/respip.c index d698954b6..2a39a42c5 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -867,7 +867,8 @@ respip_rewrite_reply(const struct query_info* qinfo, const struct respip_client_info* cinfo, const struct reply_info* rep, struct reply_info** new_repp, struct respip_action_info* actinfo, struct ub_packed_rrset_key** alias_rrset, int search_only, - struct regional* region, struct auth_zones* az, int* rpz_passthru) + struct regional* region, struct auth_zones* az, int* rpz_passthru, + struct views* views) { const uint8_t* ctaglist; size_t ctaglen; @@ -899,7 +900,19 @@ respip_rewrite_reply(const struct query_info* qinfo, tag_actions_size = cinfo->tag_actions_size; tag_datas = cinfo->tag_datas; tag_datas_size = cinfo->tag_datas_size; - view = cinfo->view; + if(cinfo->view) { + view = cinfo->view; + lock_rw_rdlock(&view->lock); + } else if(cinfo->view_name) { + view = views_find_view(views, cinfo->view_name, 0); + if(!view) { + /* If the view no longer exists, the rewrite can not + * be processed further. */ + verbose(VERB_ALGO, "respip: failed because view %s no " + "longer exists", cinfo->view_name); + return 0; + } + } ipset = cinfo->respip_set; log_assert(ipset); @@ -915,7 +928,6 @@ respip_rewrite_reply(const struct query_info* qinfo, * Note also that we assume 'view' is valid in this function, which * should be safe (see unbound bug #1191) */ if(view) { - lock_rw_rdlock(&view->lock); if(view->respip_set) { if((raddr = respip_addr_lookup(rep, view->respip_set, &rrset_id, &rr_id))) { @@ -1101,7 +1113,7 @@ respip_operate(struct module_qstate* qstate, enum module_ev event, int id, qstate->client_info, qstate->return_msg->rep, &new_rep, &actinfo, &alias_rrset, 0, qstate->region, qstate->env->auth_zones, - &qstate->rpz_passthru)) { + &qstate->rpz_passthru, qstate->env->views)) { goto servfail; } if(actinfo.action != respip_none) { @@ -1149,7 +1161,7 @@ respip_merge_cname(struct reply_info* base_rep, const struct query_info* qinfo, const struct reply_info* tgt_rep, const struct respip_client_info* cinfo, int must_validate, struct reply_info** new_repp, struct regional* region, - struct auth_zones* az) + struct auth_zones* az, struct views* views) { struct reply_info* new_rep; struct reply_info* tmp_rep = NULL; /* just a placeholder */ @@ -1176,7 +1188,7 @@ respip_merge_cname(struct reply_info* base_rep, /* see if the target reply would be subject to a response-ip action. */ if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo, - &alias_rrset, 1, region, az, NULL)) + &alias_rrset, 1, region, az, NULL, views)) return 0; if(actinfo.action != respip_none) { log_info("CNAME target of redirect response-ip action would " @@ -1229,7 +1241,7 @@ respip_inform_super(struct module_qstate* qstate, int id, if(!respip_merge_cname(super->return_msg->rep, &qstate->qinfo, qstate->return_msg->rep, super->client_info, super->env->need_to_validate, &new_rep, super->region, - qstate->env->auth_zones)) + qstate->env->auth_zones, qstate->env->views)) goto fail; super->return_msg->rep = new_rep; return; diff --git a/respip/respip.h b/respip/respip.h index f43052e22..d1404017c 100644 --- a/respip/respip.h +++ b/respip/respip.h @@ -68,7 +68,12 @@ struct respip_client_info { size_t tag_actions_size; struct config_strlist** tag_datas; size_t tag_datas_size; + /** The view for the action, during cache callback that is by + * pointer. */ struct view* view; + /** If from module query state, the view pointer is NULL, but name is + * stored to the view. */ + char* view_name; struct respip_set* respip_set; }; @@ -149,13 +154,14 @@ int respip_views_apply_cfg(struct views* vs, struct config_file* cfg, * on error. * @param region: allocator to build *new_repp. * @param az: auth zones containing RPZ information. + * @param views: views tree to lookup view used. * @return 1 on success, 0 on error. */ int respip_merge_cname(struct reply_info* base_rep, const struct query_info* qinfo, const struct reply_info* tgt_rep, const struct respip_client_info* cinfo, int must_validate, struct reply_info** new_repp, struct regional* region, - struct auth_zones* az); + struct auth_zones* az, struct views* views); /** * See if any IP-based action should apply to any IP address of AAAA/A answer @@ -178,6 +184,7 @@ int respip_merge_cname(struct reply_info* base_rep, * @param region: allocator to build *new_repp. * @param rpz_passthru: keeps track of query state can have passthru that * stops further rpz processing. Or NULL for cached answer processing. + * @param views: views tree to lookup view used. * @return 1 on success, 0 on error. */ int respip_rewrite_reply(const struct query_info* qinfo, @@ -186,7 +193,7 @@ int respip_rewrite_reply(const struct query_info* qinfo, struct respip_action_info* actinfo, struct ub_packed_rrset_key** alias_rrset, int search_only, struct regional* region, struct auth_zones* az, - int* rpz_passthru); + int* rpz_passthru, struct views* views); /** * Get the response-ip function block. diff --git a/services/mesh.c b/services/mesh.c index 3c2b536b4..b51832ee6 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -131,8 +131,16 @@ client_info_compare(const struct respip_client_info* ci_a, } if(ci_a->tag_datas != ci_b->tag_datas) return ci_a->tag_datas < ci_b->tag_datas ? -1 : 1; - if(ci_a->view != ci_b->view) - return ci_a->view < ci_b->view ? -1 : 1; + if(ci_a->view || ci_a->view_name || ci_b->view || ci_b->view_name) { + if(ci_a->view == NULL && ci_a->view_name == NULL) + return -1; + if(ci_b->view == NULL && ci_b->view_name == NULL) + return 1; + cmp = strcmp((ci_a->view?ci_a->view->name:ci_a->view_name), + (ci_b->view?ci_b->view->name:ci_b->view_name)); + if(cmp != 0) + return cmp; + } /* For the unbound daemon these should be non-NULL and identical, * but we check that just in case. */ if(ci_a->respip_set != ci_b->respip_set) @@ -822,6 +830,68 @@ void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e, mesh_run(mesh, e->qstate->mesh_info, event, e); } +/** copy strlist to region */ +static struct config_strlist* +cfg_region_strlist_copy(struct regional* region, struct config_strlist* list) +{ + struct config_strlist* result = NULL, *last = NULL, *s = list; + while(s) { + struct config_strlist* n = regional_alloc_zero(region, + sizeof(*n)); + if(!n) + return NULL; + n->str = regional_strdup(region, s->str); + if(!n->str) + return NULL; + if(last) + last->next = n; + else result = n; + last = n; + s = s->next; + } + return result; +} + +/** Copy the client info to the query region. */ +static struct respip_client_info* +mesh_copy_client_info(struct regional* region, struct respip_client_info* cinfo) +{ + size_t i; + struct respip_client_info* client_info; + client_info = regional_alloc_init(region, cinfo, sizeof(*cinfo)); + if(!client_info) + return NULL; + /* Copy the client_info so that if the configuration changes, + * then the data stays valid. */ + client_info->taglist = regional_alloc_init(region, cinfo->taglist, + cinfo->taglen); + if(!client_info->taglist) + return NULL; + client_info->tag_datas = regional_alloc_zero(region, + sizeof(struct config_strlist*)*cinfo->tag_datas_size); + if(!client_info->tag_datas) + return NULL; + for(i=0; itag_datas_size; i++) { + if(cinfo->tag_datas[i]) { + client_info->tag_datas[i] = cfg_region_strlist_copy( + region, cinfo->tag_datas[i]); + if(!client_info->tag_datas[i]) + return NULL; + } + } + if(cinfo->view) { + /* Do not copy the view pointer but store a name instead. + * The name is looked up later when done, this means that + * the view tree can be changed, by reloads. */ + client_info->view = NULL; + client_info->view_name = regional_strdup(region, + cinfo->view->name); + if(!client_info->view_name) + return NULL; + } + return client_info; +} + struct mesh_state* mesh_state_create(struct module_env* env, struct query_info* qinfo, struct respip_client_info* cinfo, uint16_t qflags, int prime, @@ -862,8 +932,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo, return NULL; } if(cinfo) { - mstate->s.client_info = regional_alloc_init(region, cinfo, - sizeof(*cinfo)); + mstate->s.client_info = mesh_copy_client_info(region, cinfo); if(!mstate->s.client_info) { alloc_reg_release(env->alloc, region); return NULL; @@ -2059,7 +2128,7 @@ apply_respip_action(struct module_qstate* qstate, return 1; if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, actinfo, - alias_rrset, 0, qstate->region, az, NULL)) + alias_rrset, 0, qstate->region, az, NULL, qstate->env->views)) return 0; /* xxx_deny actions mean dropping the reply, unless the original reply @@ -2127,7 +2196,7 @@ mesh_serve_expired_callback(void* arg) } else if(partial_rep && !respip_merge_cname(partial_rep, &qstate->qinfo, msg->rep, qstate->client_info, must_validate, &encode_rep, qstate->region, - qstate->env->auth_zones)) { + qstate->env->auth_zones, qstate->env->views)) { return; } if(!encode_rep || alias_rrset) { diff --git a/util/module.h b/util/module.h index 8e7aca635..3a0c2b926 100644 --- a/util/module.h +++ b/util/module.h @@ -521,6 +521,8 @@ struct module_env { * data structure. */ struct iter_hints* hints; + /** views structure containing view tree */ + struct views* views; /** module specific data. indexed by module id. */ void* modinfo[MAX_MODULE]; From fff00420696fd224a1a61a4271b894d600c36a9f Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 9 Apr 2024 08:48:44 +0200 Subject: [PATCH 055/125] - fast-reload, nicer respip view comparison. --- respip/respip.c | 1 + respip/respip.h | 4 ++-- services/mesh.c | 22 +++++++++++++++++----- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/respip/respip.c b/respip/respip.c index 2a39a42c5..a17f046d2 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -912,6 +912,7 @@ respip_rewrite_reply(const struct query_info* qinfo, "longer exists", cinfo->view_name); return 0; } + /* The view is rdlocked by views_find_view. */ } ipset = cinfo->respip_set; diff --git a/respip/respip.h b/respip/respip.h index d1404017c..f3c0fc034 100644 --- a/respip/respip.h +++ b/respip/respip.h @@ -71,8 +71,8 @@ struct respip_client_info { /** The view for the action, during cache callback that is by * pointer. */ struct view* view; - /** If from module query state, the view pointer is NULL, but name is - * stored to the view. */ + /** If from module query state, the view pointer is NULL, but the + * name is stored in reference to the view. */ char* view_name; struct respip_set* respip_set; }; diff --git a/services/mesh.c b/services/mesh.c index b51832ee6..8d9d43a96 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -76,6 +76,20 @@ #include #endif +/** Compare two views by name */ +static int +view_name_compare(const char* v_a, const char* v_b) +{ + if(v_a == NULL && v_b == NULL) + return 0; + /* The NULL name is smaller than if the name is set. */ + if(v_a == NULL) + return -1; + if(v_b == NULL) + return 1; + return strcmp(v_a, v_b); +} + /** * Compare two response-ip client info entries for the purpose of mesh state * compare. It returns 0 if ci_a and ci_b are considered equal; otherwise @@ -132,11 +146,9 @@ client_info_compare(const struct respip_client_info* ci_a, if(ci_a->tag_datas != ci_b->tag_datas) return ci_a->tag_datas < ci_b->tag_datas ? -1 : 1; if(ci_a->view || ci_a->view_name || ci_b->view || ci_b->view_name) { - if(ci_a->view == NULL && ci_a->view_name == NULL) - return -1; - if(ci_b->view == NULL && ci_b->view_name == NULL) - return 1; - cmp = strcmp((ci_a->view?ci_a->view->name:ci_a->view_name), + /* Compare the views by name. */ + cmp = view_name_compare( + (ci_a->view?ci_a->view->name:ci_a->view_name), (ci_b->view?ci_b->view->name:ci_b->view_name)); if(cmp != 0) return cmp; From 22e5c7d1b4f8c5bfd637273c6d01548b28967a5a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 9 Apr 2024 09:06:50 +0200 Subject: [PATCH 056/125] - fast-reload, respip global set is in module env. --- daemon/daemon.c | 12 ++++++------ daemon/daemon.h | 2 -- daemon/worker.c | 6 +++--- respip/respip.c | 15 ++++++++------- respip/respip.h | 9 +++++---- services/mesh.c | 10 ++++------ util/module.h | 3 +++ 7 files changed, 29 insertions(+), 28 deletions(-) diff --git a/daemon/daemon.c b/daemon/daemon.c index c09e2f9ed..946ed9708 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -724,15 +724,15 @@ daemon_fork(struct daemon* daemon) fatal_exit("Could not set root or stub hints"); /* process raw response-ip configuration data */ - if(!(daemon->respip_set = respip_set_create())) + if(!(daemon->env->respip_set = respip_set_create())) fatal_exit("Could not create response IP set"); - if(!respip_global_apply_cfg(daemon->respip_set, daemon->cfg)) + if(!respip_global_apply_cfg(daemon->env->respip_set, daemon->cfg)) fatal_exit("Could not set up response IP set"); if(!respip_views_apply_cfg(daemon->env->views, daemon->cfg, &have_view_respip_cfg)) fatal_exit("Could not set up per-view response IP sets"); - daemon->use_response_ip = !respip_set_is_empty(daemon->respip_set) || - have_view_respip_cfg; + daemon->use_response_ip = !respip_set_is_empty( + daemon->env->respip_set) || have_view_respip_cfg; /* setup modules */ daemon_setup_modules(daemon); @@ -844,8 +844,8 @@ daemon_cleanup(struct daemon* daemon) daemon->env->hints = NULL; local_zones_delete(daemon->local_zones); daemon->local_zones = NULL; - respip_set_delete(daemon->respip_set); - daemon->respip_set = NULL; + respip_set_delete(daemon->env->respip_set); + daemon->env->respip_set = NULL; views_delete(daemon->env->views); daemon->env->views = NULL; if(daemon->env->auth_zones) diff --git a/daemon/daemon.h b/daemon/daemon.h index a38365796..dbfce9a24 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -134,8 +134,6 @@ struct daemon { struct dt_env* dtenv; #endif struct shm_main_info* shm_info; - /** response-ip set with associated actions and tags. */ - struct respip_set* respip_set; /** some response-ip tags or actions are configured if true */ int use_response_ip; /** some RPZ policies are configured */ diff --git a/daemon/worker.c b/daemon/worker.c index 4bddf97d7..f8fc3463a 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -683,7 +683,7 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo, if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, &actinfo, alias_rrset, 0, worker->scratchpad, az, NULL, - worker->env.views)) + worker->env.views, worker->env.respip_set)) return 0; /* xxx_deny actions mean dropping the reply, unless the original reply @@ -843,7 +843,8 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, } else if(partial_rep && !respip_merge_cname(partial_rep, qinfo, rep, cinfo, must_validate, &encode_rep, worker->scratchpad, - worker->env.auth_zones, worker->env.views)) { + worker->env.auth_zones, worker->env.views, + worker->env.respip_set)) { goto bail_out; } if(encode_rep != rep) { @@ -1895,7 +1896,6 @@ worker_handle_request(struct comm_point* c, void* arg, int error, cinfo_tmp.tag_datas_size = acladdr->tag_datas_size; cinfo_tmp.view = acladdr->view; cinfo_tmp.view_name = NULL; - cinfo_tmp.respip_set = worker->daemon->respip_set; cinfo = &cinfo_tmp; } diff --git a/respip/respip.c b/respip/respip.c index a17f046d2..a76b9f790 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -868,7 +868,7 @@ respip_rewrite_reply(const struct query_info* qinfo, struct reply_info** new_repp, struct respip_action_info* actinfo, struct ub_packed_rrset_key** alias_rrset, int search_only, struct regional* region, struct auth_zones* az, int* rpz_passthru, - struct views* views) + struct views* views, struct respip_set* ipset) { const uint8_t* ctaglist; size_t ctaglen; @@ -877,7 +877,6 @@ respip_rewrite_reply(const struct query_info* qinfo, struct config_strlist** tag_datas; size_t tag_datas_size; struct view* view = NULL; - struct respip_set* ipset = NULL; size_t rrset_id = 0, rr_id = 0; enum respip_action action = respip_none; int tag = -1; @@ -914,7 +913,6 @@ respip_rewrite_reply(const struct query_info* qinfo, } /* The view is rdlocked by views_find_view. */ } - ipset = cinfo->respip_set; log_assert(ipset); @@ -1114,7 +1112,8 @@ respip_operate(struct module_qstate* qstate, enum module_ev event, int id, qstate->client_info, qstate->return_msg->rep, &new_rep, &actinfo, &alias_rrset, 0, qstate->region, qstate->env->auth_zones, - &qstate->rpz_passthru, qstate->env->views)) { + &qstate->rpz_passthru, qstate->env->views, + qstate->env->respip_set)) { goto servfail; } if(actinfo.action != respip_none) { @@ -1162,7 +1161,8 @@ respip_merge_cname(struct reply_info* base_rep, const struct query_info* qinfo, const struct reply_info* tgt_rep, const struct respip_client_info* cinfo, int must_validate, struct reply_info** new_repp, struct regional* region, - struct auth_zones* az, struct views* views) + struct auth_zones* az, struct views* views, + struct respip_set* respip_set) { struct reply_info* new_rep; struct reply_info* tmp_rep = NULL; /* just a placeholder */ @@ -1189,7 +1189,7 @@ respip_merge_cname(struct reply_info* base_rep, /* see if the target reply would be subject to a response-ip action. */ if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo, - &alias_rrset, 1, region, az, NULL, views)) + &alias_rrset, 1, region, az, NULL, views, respip_set)) return 0; if(actinfo.action != respip_none) { log_info("CNAME target of redirect response-ip action would " @@ -1242,7 +1242,8 @@ respip_inform_super(struct module_qstate* qstate, int id, if(!respip_merge_cname(super->return_msg->rep, &qstate->qinfo, qstate->return_msg->rep, super->client_info, super->env->need_to_validate, &new_rep, super->region, - qstate->env->auth_zones, qstate->env->views)) + qstate->env->auth_zones, qstate->env->views, + qstate->env->respip_set)) goto fail; super->return_msg->rep = new_rep; return; diff --git a/respip/respip.h b/respip/respip.h index f3c0fc034..8bbf5159a 100644 --- a/respip/respip.h +++ b/respip/respip.h @@ -59,7 +59,6 @@ struct respip_addr_info; * This is essentially a subset of acl_addr (except for respip_set) but * defined as a separate structure to avoid dependency on the daemon-specific * structure. - * respip_set is supposed to refer to the response-ip set for the global view. */ struct respip_client_info { uint8_t* taglist; @@ -74,7 +73,6 @@ struct respip_client_info { /** If from module query state, the view pointer is NULL, but the * name is stored in reference to the view. */ char* view_name; - struct respip_set* respip_set; }; /** @@ -155,13 +153,15 @@ int respip_views_apply_cfg(struct views* vs, struct config_file* cfg, * @param region: allocator to build *new_repp. * @param az: auth zones containing RPZ information. * @param views: views tree to lookup view used. + * @param respip_set: the respip set for the global view. * @return 1 on success, 0 on error. */ int respip_merge_cname(struct reply_info* base_rep, const struct query_info* qinfo, const struct reply_info* tgt_rep, const struct respip_client_info* cinfo, int must_validate, struct reply_info** new_repp, struct regional* region, - struct auth_zones* az, struct views* views); + struct auth_zones* az, struct views* views, + struct respip_set* respip_set); /** * See if any IP-based action should apply to any IP address of AAAA/A answer @@ -185,6 +185,7 @@ int respip_merge_cname(struct reply_info* base_rep, * @param rpz_passthru: keeps track of query state can have passthru that * stops further rpz processing. Or NULL for cached answer processing. * @param views: views tree to lookup view used. + * @param ipset: the respip set for the global view. * @return 1 on success, 0 on error. */ int respip_rewrite_reply(const struct query_info* qinfo, @@ -193,7 +194,7 @@ int respip_rewrite_reply(const struct query_info* qinfo, struct respip_action_info* actinfo, struct ub_packed_rrset_key** alias_rrset, int search_only, struct regional* region, struct auth_zones* az, - int* rpz_passthru, struct views* views); + int* rpz_passthru, struct views* views, struct respip_set* ipset); /** * Get the response-ip function block. diff --git a/services/mesh.c b/services/mesh.c index 8d9d43a96..8130f7a4f 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -153,10 +153,6 @@ client_info_compare(const struct respip_client_info* ci_a, if(cmp != 0) return cmp; } - /* For the unbound daemon these should be non-NULL and identical, - * but we check that just in case. */ - if(ci_a->respip_set != ci_b->respip_set) - return ci_a->respip_set < ci_b->respip_set ? -1 : 1; return 0; } @@ -2140,7 +2136,8 @@ apply_respip_action(struct module_qstate* qstate, return 1; if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, actinfo, - alias_rrset, 0, qstate->region, az, NULL, qstate->env->views)) + alias_rrset, 0, qstate->region, az, NULL, qstate->env->views, + qstate->env->respip_set)) return 0; /* xxx_deny actions mean dropping the reply, unless the original reply @@ -2208,7 +2205,8 @@ mesh_serve_expired_callback(void* arg) } else if(partial_rep && !respip_merge_cname(partial_rep, &qstate->qinfo, msg->rep, qstate->client_info, must_validate, &encode_rep, qstate->region, - qstate->env->auth_zones, qstate->env->views)) { + qstate->env->auth_zones, qstate->env->views, + qstate->env->respip_set)) { return; } if(!encode_rep || alias_rrset) { diff --git a/util/module.h b/util/module.h index 3a0c2b926..db15b6c50 100644 --- a/util/module.h +++ b/util/module.h @@ -177,6 +177,7 @@ struct val_anchors; struct val_neg_cache; struct iter_forwards; struct iter_hints; +struct views; struct respip_set; struct respip_client_info; struct respip_addr_info; @@ -523,6 +524,8 @@ struct module_env { struct iter_hints* hints; /** views structure containing view tree */ struct views* views; + /** response-ip set with associated actions and tags. */ + struct respip_set* respip_set; /** module specific data. indexed by module id. */ void* modinfo[MAX_MODULE]; From 5606d61c3b4bf2dce54bf1134c324d405b9040cd Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 9 Apr 2024 09:14:58 +0200 Subject: [PATCH 057/125] - fast-reload, document that respip_client_info acl info is copied. --- doc/unbound-control.8.in | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index f7d5a0509..a112abc93 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -100,6 +100,13 @@ then the '+d' option is turned on with a warning printout, and queries are dropped. This is to stop references to the old tag information, by the old queries. If the number of tags is increased in the newly loaded config, by adding tags at the end, then the '+d' option is not needed. +.IP +For response ip, that is actions associated with IP addresses, and perhaps +intersected with access control tag and action information, those settings +are stored with a query when it comes in based on its source IP address. +The old information is kept with the query until the queries are done. +This is gone when those queries are resolved and finished, or it is possible +to flush the requestlist with '+d'. .TP .B verbosity \fInumber Change verbosity value for logging. Same values as \fBverbosity\fR keyword in From dabe8160fd1d6754eb44fab462976c0b01b0b493 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 9 Apr 2024 09:43:45 +0200 Subject: [PATCH 058/125] - fast-reload, reload the respip_set. --- daemon/remote.c | 44 +++++++++++++++++++++++++++++++++++++++++++- daemon/remote.h | 3 +++ daemon/worker.c | 2 ++ respip/respip.c | 21 +++++++++++++++++++++ respip/respip.h | 12 +++++++++++- 5 files changed, 80 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 5992c4c30..7d6826b51 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3773,6 +3773,10 @@ struct fast_reload_construct { struct iter_forwards* fwds; /** construct for stubs */ struct iter_hints* hints; + /** construct for respip_set */ + struct respip_set* respip_set; + /** if there is response ip configuration in use */ + int use_response_ip; /** storage for the old configuration elements. The outer struct * is allocated with malloc here, the items are from config. */ struct config_file* oldcfg; @@ -3883,6 +3887,7 @@ fr_construct_clear(struct fast_reload_construct* ct) return; forwards_delete(ct->fwds); hints_delete(ct->hints); + respip_set_delete(ct->respip_set); views_delete(ct->views); /* Delete the log identity here so that the global value is not * reset by config_delete. */ @@ -4130,6 +4135,7 @@ fr_printmem(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; mem += views_get_mem(ct->views); + mem += respip_set_get_mem(ct->respip_set); mem += forwards_get_mem(ct->fwds); mem += hints_get_mem(ct->hints); mem += sizeof(*ct->oldcfg); @@ -4147,6 +4153,8 @@ static int fr_construct_from_config(struct fast_reload_thread* fr, struct config_file* newcfg, struct fast_reload_construct* ct) { + int have_view_respip_cfg = 0; + if(!(ct->views = views_create())) { fr_construct_clear(ct); return 0; @@ -4169,8 +4177,10 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; - if(!(ct->hints = hints_create())) + if(!(ct->hints = hints_create())) { + fr_construct_clear(ct); return 0; + } if(!hints_apply_cfg(ct->hints, newcfg)) { fr_construct_clear(ct); return 0; @@ -4178,6 +4188,25 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; + if(!(ct->respip_set = respip_set_create())) { + fr_construct_clear(ct); + return 0; + } + if(!respip_global_apply_cfg(ct->respip_set, newcfg)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + if(!respip_views_apply_cfg(ct->views, newcfg, &have_view_respip_cfg)) { + fr_construct_clear(ct); + return 0; + } + ct->use_response_ip = !respip_set_is_empty(ct->respip_set) || + have_view_respip_cfg; + if(fr_poll_for_quit(fr)) + return 1; + if(!(ct->oldcfg = (struct config_file*)calloc(1, sizeof(*ct->oldcfg)))) { fr_construct_clear(ct); @@ -4587,6 +4616,8 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, /* Grab big locks to satisfy lock conditions. */ lock_rw_wrlock(&ct->views->lock); lock_rw_wrlock(&env->views->lock); + lock_rw_wrlock(&ct->respip_set->lock); + lock_rw_wrlock(&env->respip_set->lock); lock_rw_wrlock(&ct->fwds->lock); lock_rw_wrlock(&ct->hints->lock); lock_rw_wrlock(&env->fwds->lock); @@ -4624,12 +4655,16 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, forwards_swap_tree(env->fwds, ct->fwds); hints_swap_tree(env->hints, ct->hints); views_swap_tree(env->views, ct->views); + respip_set_swap_tree(env->respip_set, ct->respip_set); + daemon->use_response_ip = ct->use_response_ip; /* Set globals with new config. */ config_apply(env->cfg); lock_rw_unlock(&ct->views->lock); lock_rw_unlock(&env->views->lock); + lock_rw_unlock(&ct->respip_set->lock); + lock_rw_unlock(&env->respip_set->lock); lock_rw_unlock(&env->fwds->lock); lock_rw_unlock(&env->hints->lock); lock_rw_unlock(&ct->fwds->lock); @@ -5450,6 +5485,11 @@ fr_poll_for_reload_start(struct fast_reload_thread* fr) } } +void +fast_reload_worker_pickup_changes(struct worker* worker) +{ + worker->env.mesh->use_response_ip = worker->daemon->use_response_ip; +} /** fast reload thread, handle reload_stop notification, send reload stop * to other threads over IPC and collect their ack. When that is done, @@ -5487,6 +5527,7 @@ fr_main_perform_reload_stop(struct fast_reload_thread* fr) verbose(VERB_ALGO, "worker: drop mesh queries after reload"); mesh_delete_all(fr->worker->env.mesh); } + fast_reload_worker_pickup_changes(fr->worker); verbose(VERB_ALGO, "worker resume after reload"); } @@ -5510,6 +5551,7 @@ fr_main_perform_reload_nopause_poll(struct fast_reload_thread* fr) /* Wait for the other threads to ack. */ fr_read_ack_from_workers(fr); + fast_reload_worker_pickup_changes(fr->worker); /* Send ack to fast reload thread. */ fr_send_cmd_to(fr, fast_reload_notification_reload_ack, 0, 1); diff --git a/daemon/remote.h b/daemon/remote.h index 4c4f442f4..ce26ea22f 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -342,4 +342,7 @@ int fast_reload_client_callback(struct comm_point* c, void* arg, int err, /** fast reload printq delete list */ void fast_reload_printq_list_delete(struct fast_reload_printq* list); +/** Pick up per worker changes after a fast reload. */ +void fast_reload_worker_pickup_changes(struct worker* worker); + #endif /* DAEMON_REMOTE_H */ diff --git a/daemon/worker.c b/daemon/worker.c index f8fc3463a..0e9645d4d 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -442,6 +442,7 @@ worker_stop_and_wait(struct worker* worker) verbose(VERB_ALGO, "worker: drop mesh queries after reload"); mesh_delete_all(worker->env.mesh); } + fast_reload_worker_pickup_changes(worker); verbose(VERB_ALGO, "worker resume after reload"); } @@ -486,6 +487,7 @@ worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg, break; case worker_cmd_reload_poll: verbose(VERB_ALGO, "got control cmd reload_poll"); + fast_reload_worker_pickup_changes(worker); worker_send_reload_ack(worker); break; default: diff --git a/respip/respip.c b/respip/respip.c index a76b9f790..009bf0ac1 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -1349,3 +1349,24 @@ size_t respip_set_get_mem(struct respip_set* set) lock_rw_unlock(&set->lock); return m; } + +void +respip_set_swap_tree(struct respip_set* respip_set, + struct respip_set* data) +{ + rbnode_type* oldroot = respip_set->ip_tree.root; + size_t oldcount = respip_set->ip_tree.count; + struct regional* oldregion = respip_set->region; + char* const* oldtagname = respip_set->tagname; + int oldnum_tags = respip_set->num_tags; + respip_set->ip_tree.root = data->ip_tree.root; + respip_set->ip_tree.count = data->ip_tree.count; + respip_set->region = data->region; + respip_set->tagname = data->tagname; + respip_set->num_tags = data->num_tags; + data->ip_tree.root = oldroot; + data->ip_tree.count = oldcount; + data->region = oldregion; + data->tagname = oldtagname; + data->num_tags = oldnum_tags; +} diff --git a/respip/respip.h b/respip/respip.h index 8bbf5159a..07bd31912 100644 --- a/respip/respip.h +++ b/respip/respip.h @@ -23,7 +23,8 @@ struct respip_set { struct regional* region; struct rbtree_type ip_tree; - lock_rw_type lock; /* lock on the respip tree */ + lock_rw_type lock; /* lock on the respip tree. It is ordered + after views and before hints and stubs. */ char* const* tagname; /* shallow copy of tag names, for logging */ int num_tags; /* number of tagname entries */ }; @@ -315,4 +316,13 @@ respip_copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region * set for reading. */ size_t respip_set_get_mem(struct respip_set* set); +/** + * Swap internal tree with preallocated entries. Caller should manage + * the locks. + * @param respip_set: response ip tree + * @param data: preallocated information. + */ +void respip_set_swap_tree(struct respip_set* respip_set, + struct respip_set* data); + #endif /* RESPIP_RESPIP_H */ From 2e975d7e0d8e9e921b14a66d2dc0c0fe90dcf1d4 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 9 Apr 2024 10:24:52 +0200 Subject: [PATCH 059/125] - fast-reload, document no pause and pick up of use_response_ip boolean. --- daemon/remote.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index 7d6826b51..9185efdfd 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5488,6 +5488,16 @@ fr_poll_for_reload_start(struct fast_reload_thread* fr) void fast_reload_worker_pickup_changes(struct worker* worker) { + /* The pickup of changes is called when the fast reload has + * a syncronized moment, and all the threads are paused and the + * reload has been applied. Then the worker can pick up the new + * changes and store them in worker-specific structs. + * The pickup is also called when there is no pause, and then + * it is called after the reload has completed, and the worker + * get a signal to release old information, it can then pick + * up the new information. But in the mean time, the reload has + * swapped in trees, and the worker has been running with the + * older information for some time. */ worker->env.mesh->use_response_ip = worker->daemon->use_response_ip; } From 609434a3fe5264253a0128b850e547133905c4bc Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 9 Apr 2024 10:27:13 +0200 Subject: [PATCH 060/125] - fast-reload, fix test compile. --- testcode/testbound.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testcode/testbound.c b/testcode/testbound.c index 83a2fddac..eadb8a420 100644 --- a/testcode/testbound.c +++ b/testcode/testbound.c @@ -621,3 +621,8 @@ void fast_reload_printq_list_delete( { /* nothing */ } + +void fast_reload_worker_pickup_changes(struct worker* ATTR_UNUSED(worker)) +{ + /* nothing */ +} From 0637df27365c9e5e858bfc8c1e7354479ba25d7a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 1 May 2024 15:06:19 +0200 Subject: [PATCH 061/125] - fast-reload, reload local zones. --- daemon/remote.c | 28 +++++++++++++++++++++++++--- respip/respip.h | 2 +- services/localzone.c | 7 +++++++ services/localzone.h | 10 ++++++++++ services/view.h | 2 +- 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 9185efdfd..02cf8ef21 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3775,6 +3775,8 @@ struct fast_reload_construct { struct iter_hints* hints; /** construct for respip_set */ struct respip_set* respip_set; + /** construct for local zones */ + struct local_zones* local_zones; /** if there is response ip configuration in use */ int use_response_ip; /** storage for the old configuration elements. The outer struct @@ -3888,6 +3890,7 @@ fr_construct_clear(struct fast_reload_construct* ct) forwards_delete(ct->fwds); hints_delete(ct->hints); respip_set_delete(ct->respip_set); + local_zones_delete(ct->local_zones); views_delete(ct->views); /* Delete the log identity here so that the global value is not * reset by config_delete. */ @@ -4138,6 +4141,7 @@ fr_printmem(struct fast_reload_thread* fr, mem += respip_set_get_mem(ct->respip_set); mem += forwards_get_mem(ct->fwds); mem += hints_get_mem(ct->hints); + mem += local_zones_get_mem(ct->local_zones); mem += sizeof(*ct->oldcfg); mem += config_file_getmem(newcfg); @@ -4188,6 +4192,17 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; + if(!(ct->local_zones = local_zones_create())) { + fr_construct_clear(ct); + return 0; + } + if(!local_zones_apply_cfg(ct->local_zones, newcfg)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + if(!(ct->respip_set = respip_set_create())) { fr_construct_clear(ct); return 0; @@ -4618,9 +4633,11 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, lock_rw_wrlock(&env->views->lock); lock_rw_wrlock(&ct->respip_set->lock); lock_rw_wrlock(&env->respip_set->lock); + lock_rw_wrlock(&ct->local_zones->lock); + lock_rw_wrlock(&daemon->local_zones->lock); lock_rw_wrlock(&ct->fwds->lock); - lock_rw_wrlock(&ct->hints->lock); lock_rw_wrlock(&env->fwds->lock); + lock_rw_wrlock(&ct->hints->lock); lock_rw_wrlock(&env->hints->lock); #ifdef ATOMIC_POINTER_LOCK_FREE @@ -4655,6 +4672,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, forwards_swap_tree(env->fwds, ct->fwds); hints_swap_tree(env->hints, ct->hints); views_swap_tree(env->views, ct->views); + local_zones_swap_tree(daemon->local_zones, ct->local_zones); respip_set_swap_tree(env->respip_set, ct->respip_set); daemon->use_response_ip = ct->use_response_ip; @@ -4665,9 +4683,11 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, lock_rw_unlock(&env->views->lock); lock_rw_unlock(&ct->respip_set->lock); lock_rw_unlock(&env->respip_set->lock); + lock_rw_unlock(&ct->local_zones->lock); + lock_rw_unlock(&daemon->local_zones->lock); lock_rw_unlock(&env->fwds->lock); - lock_rw_unlock(&env->hints->lock); lock_rw_unlock(&ct->fwds->lock); + lock_rw_unlock(&env->hints->lock); lock_rw_unlock(&ct->hints->lock); return 1; @@ -4829,7 +4849,9 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, * item. This nopause command pipe item does not take work, * it returns immediately, so it does not delay the workers. * They can be polled one at a time. But its processing causes - * the worker to have released data items from old config. */ + * the worker to have released data items from old config. + * This also makes sure the threads are not holding locks on + * individual items in the local_zones, views, respip_set. */ fr_send_notification(fr, fast_reload_notification_reload_nopause_poll); fr_poll_for_ack(fr); diff --git a/respip/respip.h b/respip/respip.h index 07bd31912..6469854c5 100644 --- a/respip/respip.h +++ b/respip/respip.h @@ -24,7 +24,7 @@ struct respip_set { struct regional* region; struct rbtree_type ip_tree; lock_rw_type lock; /* lock on the respip tree. It is ordered - after views and before hints and stubs. */ + after views and before hints, stubs and local zones. */ char* const* tagname; /* shallow copy of tag names, for logging */ int num_tags; /* number of tagname entries */ }; diff --git a/services/localzone.c b/services/localzone.c index ec1123054..aab30086a 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -2226,3 +2226,10 @@ size_t local_zones_get_mem(struct local_zones* zones) lock_rw_unlock(&zones->lock); return m; } + +void local_zones_swap_tree(struct local_zones* zones, struct local_zones* data) +{ + rbtree_type oldtree = zones->ztree; + zones->ztree = data->ztree; + data->ztree = oldtree; +} diff --git a/services/localzone.h b/services/localzone.h index 7a51ec59d..a835a44a7 100644 --- a/services/localzone.h +++ b/services/localzone.h @@ -646,4 +646,14 @@ local_zone_find_data(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs * the tree for reading. */ size_t local_zones_get_mem(struct local_zones* zones); +/** + * Swap internal tree with preallocated entries. Caller should manage + * the locks. + * @param zones: the local zones structure. + * @param data: the data structure used to take elements from. This contains + * the old elements on return. + */ +void local_zones_swap_tree(struct local_zones* zones, + struct local_zones* data); + #endif /* SERVICES_LOCALZONE_H */ diff --git a/services/view.h b/services/view.h index 990212496..0ff39ed6e 100644 --- a/services/view.h +++ b/services/view.h @@ -55,7 +55,7 @@ struct respip_set; */ struct views { /** lock on the view tree. When locking order, the views lock - * is before the forwards,hints,anchors lock. */ + * is before the forwards,hints,anchors,localzones lock. */ lock_rw_type lock; /** rbtree of struct view */ rbtree_type vtree; From 770aec67c6ae7bc290973cf96d3ed98e0195a2d0 Mon Sep 17 00:00:00 2001 From: Yorgos Thessalonikefs Date: Thu, 25 Apr 2024 11:05:58 +0200 Subject: [PATCH 062/125] Update locking management for iter_fwd and iter_hints methods. (#1054) fast reload, move most of the locking management to iter_fwd and iter_hints methods. The caller still has the ability to handle its own locking, if desired, for atomic operations on sets of different structs. Co-authored-by: Wouter Wijngaards --- daemon/cachedump.c | 9 ++-- daemon/remote.c | 38 +++++++++------- iterator/iter_fwd.c | 101 +++++++++++++++++++++++++++++++----------- iterator/iter_fwd.h | 57 +++++++++++++++++------- iterator/iter_hints.c | 70 ++++++++++++++++++++++------- iterator/iter_hints.h | 52 +++++++++++++++++----- iterator/iter_utils.c | 13 ++++-- iterator/iterator.c | 98 +++++++++++++++++++--------------------- pythonmod/interface.i | 8 ++-- 9 files changed, 295 insertions(+), 151 deletions(-) diff --git a/daemon/cachedump.c b/daemon/cachedump.c index c7523497d..c4f55d8c9 100644 --- a/daemon/cachedump.c +++ b/daemon/cachedump.c @@ -839,6 +839,7 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm, char b[260]; struct query_info qinfo; struct iter_hints_stub* stub; + int nolock = 0; regional_free_all(region); qinfo.qname = nm; qinfo.qname_len = nmlen; @@ -851,8 +852,7 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm, "of %s\n", b)) return 0; - lock_rw_rdlock(&worker->env.fwds->lock); - dp = forwards_lookup(worker->env.fwds, nm, qinfo.qclass); + dp = forwards_lookup(worker->env.fwds, nm, qinfo.qclass, nolock); if(dp) { if(!ssl_printf(ssl, "forwarding request:\n")) { lock_rw_unlock(&worker->env.fwds->lock); @@ -863,7 +863,6 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm, lock_rw_unlock(&worker->env.fwds->lock); return 1; } - lock_rw_unlock(&worker->env.fwds->lock); while(1) { dp = dns_cache_find_delegation(&worker->env, nm, nmlen, @@ -898,9 +897,8 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm, continue; } } - lock_rw_rdlock(&worker->env.hints->lock); stub = hints_lookup_stub(worker->env.hints, nm, qinfo.qclass, - dp); + dp, nolock); if(stub) { if(stub->noprime) { if(!ssl_printf(ssl, "The noprime stub servers " @@ -919,7 +917,6 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm, print_dp_details(ssl, worker, stub->dp); lock_rw_unlock(&worker->env.hints->lock); } else { - lock_rw_unlock(&worker->env.hints->lock); print_dp_main(ssl, dp, msg); print_dp_details(ssl, worker, dp); } diff --git a/daemon/remote.c b/daemon/remote.c index 02cf8ef21..48952a4b1 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -2070,10 +2070,9 @@ static int print_root_fwds(RES* ssl, struct iter_forwards* fwds, uint8_t* root) { struct delegpt* dp; - lock_rw_rdlock(&fwds->lock); - dp = forwards_lookup(fwds, root, LDNS_RR_CLASS_IN); + int nolock = 0; + dp = forwards_lookup(fwds, root, LDNS_RR_CLASS_IN, nolock); if(!dp) { - lock_rw_unlock(&fwds->lock); return ssl_printf(ssl, "off (using root hints)\n"); } /* if dp is returned it must be the root */ @@ -2155,6 +2154,7 @@ do_forward(RES* ssl, struct worker* worker, char* args) { struct iter_forwards* fwd = worker->env.fwds; uint8_t* root = (uint8_t*)"\000"; + int nolock = 0; if(!fwd) { (void)ssl_printf(ssl, "error: structure not allocated\n"); return; @@ -2168,20 +2168,15 @@ do_forward(RES* ssl, struct worker* worker, char* args) /* delete all the existing queries first */ mesh_delete_all(worker->env.mesh); if(strcmp(args, "off") == 0) { - lock_rw_wrlock(&fwd->lock); - forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, root); - lock_rw_unlock(&fwd->lock); + forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, root, nolock); } else { struct delegpt* dp; if(!(dp = parse_delegpt(ssl, args, root))) return; - lock_rw_wrlock(&fwd->lock); - if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) { - lock_rw_unlock(&fwd->lock); + if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp, nolock)) { (void)ssl_printf(ssl, "error out of memory\n"); return; } - lock_rw_unlock(&fwd->lock); } send_ok(ssl); } @@ -2240,10 +2235,12 @@ do_forward_add(RES* ssl, struct worker* worker, char* args) int insecure = 0, tls = 0; uint8_t* nm = NULL; struct delegpt* dp = NULL; + int nolock = 1; if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, NULL, &tls)) return; if(tls) dp->ssl_upstream = 1; + /* prelock forwarders for atomic operation with anchors */ lock_rw_wrlock(&fwd->lock); if(insecure && worker->env.anchors) { if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, @@ -2255,7 +2252,7 @@ do_forward_add(RES* ssl, struct worker* worker, char* args) return; } } - if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) { + if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp, nolock)) { lock_rw_unlock(&fwd->lock); (void)ssl_printf(ssl, "error out of memory\n"); free(nm); @@ -2273,13 +2270,15 @@ do_forward_remove(RES* ssl, struct worker* worker, char* args) struct iter_forwards* fwd = worker->env.fwds; int insecure = 0; uint8_t* nm = NULL; + int nolock = 1; if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL, NULL)) return; + /* prelock forwarders for atomic operation with anchors */ lock_rw_wrlock(&fwd->lock); if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); - forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, nm); + forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, nm, nolock); lock_rw_unlock(&fwd->lock); free(nm); send_ok(ssl); @@ -2293,10 +2292,12 @@ do_stub_add(RES* ssl, struct worker* worker, char* args) int insecure = 0, prime = 0, tls = 0; uint8_t* nm = NULL; struct delegpt* dp = NULL; + int nolock = 1; if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, &prime, &tls)) return; if(tls) dp->ssl_upstream = 1; + /* prelock forwarders and hints for atomic operation with anchors */ lock_rw_wrlock(&fwd->lock); lock_rw_wrlock(&worker->env.hints->lock); if(insecure && worker->env.anchors) { @@ -2310,7 +2311,7 @@ do_stub_add(RES* ssl, struct worker* worker, char* args) return; } } - if(!forwards_add_stub_hole(fwd, LDNS_RR_CLASS_IN, nm)) { + if(!forwards_add_stub_hole(fwd, LDNS_RR_CLASS_IN, nm, nolock)) { if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); @@ -2321,9 +2322,10 @@ do_stub_add(RES* ssl, struct worker* worker, char* args) free(nm); return; } - if(!hints_add_stub(worker->env.hints, LDNS_RR_CLASS_IN, dp, !prime)) { + if(!hints_add_stub(worker->env.hints, LDNS_RR_CLASS_IN, dp, !prime, + nolock)) { (void)ssl_printf(ssl, "error out of memory\n"); - forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm); + forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm, nolock); if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); @@ -2345,15 +2347,17 @@ do_stub_remove(RES* ssl, struct worker* worker, char* args) struct iter_forwards* fwd = worker->env.fwds; int insecure = 0; uint8_t* nm = NULL; + int nolock = 1; if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL, NULL)) return; + /* prelock forwarders and hints for atomic operation with anchors */ lock_rw_wrlock(&fwd->lock); lock_rw_wrlock(&worker->env.hints->lock); if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); - forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm); - hints_delete_stub(worker->env.hints, LDNS_RR_CLASS_IN, nm); + forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm, nolock); + hints_delete_stub(worker->env.hints, LDNS_RR_CLASS_IN, nm, nolock); lock_rw_unlock(&fwd->lock); lock_rw_unlock(&worker->env.hints->lock); free(nm); diff --git a/iterator/iter_fwd.c b/iterator/iter_fwd.c index 7954f865c..6bf98861a 100644 --- a/iterator/iter_fwd.c +++ b/iterator/iter_fwd.c @@ -359,30 +359,39 @@ forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg) } struct delegpt* -forwards_find(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass) +forwards_find(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass, + int nolock) { - rbnode_type* res = NULL; + struct iter_forward_zone* res; struct iter_forward_zone key; + int has_dp; key.node.key = &key; key.dclass = qclass; key.name = qname; key.namelabs = dname_count_size_labels(qname, &key.namelen); - res = rbtree_search(fwd->tree, &key); - if(res) return ((struct iter_forward_zone*)res)->dp; - return NULL; + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_rdlock(&fwd->lock); } + res = (struct iter_forward_zone*)rbtree_search(fwd->tree, &key); + has_dp = res && res->dp; + if(!has_dp && !nolock) { lock_rw_unlock(&fwd->lock); } + return has_dp?res->dp:NULL; } struct delegpt* -forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass) +forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass, + int nolock) { /* lookup the forward zone in the tree */ rbnode_type* res = NULL; struct iter_forward_zone *result; struct iter_forward_zone key; + int has_dp; key.node.key = &key; key.dclass = qclass; key.name = qname; key.namelabs = dname_count_size_labels(qname, &key.namelen); + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_rdlock(&fwd->lock); } if(rbtree_find_less_equal(fwd->tree, &key, &res)) { /* exact */ result = (struct iter_forward_zone*)res; @@ -390,8 +399,10 @@ forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass) /* smaller element (or no element) */ int m; result = (struct iter_forward_zone*)res; - if(!result || result->dclass != qclass) + if(!result || result->dclass != qclass) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return NULL; + } /* count number of labels matched */ (void)dname_lab_cmp(result->name, result->namelabs, key.name, key.namelabs, &m); @@ -401,20 +412,22 @@ forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass) result = result->parent; } } - if(result) - return result->dp; - return NULL; + has_dp = result && result->dp; + if(!has_dp && !nolock) { lock_rw_unlock(&fwd->lock); } + return has_dp?result->dp:NULL; } struct delegpt* -forwards_lookup_root(struct iter_forwards* fwd, uint16_t qclass) +forwards_lookup_root(struct iter_forwards* fwd, uint16_t qclass, int nolock) { uint8_t root = 0; - return forwards_lookup(fwd, &root, qclass); + return forwards_lookup(fwd, &root, qclass, nolock); } -int -forwards_next_root(struct iter_forwards* fwd, uint16_t* dclass) +/* Finds next root item in forwards lookup tree. + * Caller needs to handle locking of the forwards structure. */ +static int +next_root_locked(struct iter_forwards* fwd, uint16_t* dclass) { struct iter_forward_zone key; rbnode_type* n; @@ -431,7 +444,7 @@ forwards_next_root(struct iter_forwards* fwd, uint16_t* dclass) } /* root not first item? search for higher items */ *dclass = p->dclass + 1; - return forwards_next_root(fwd, dclass); + return next_root_locked(fwd, dclass); } /* find class n in tree, we may get a direct hit, or if we don't * this is the last item of the previous class so rbtree_next() takes @@ -459,10 +472,21 @@ forwards_next_root(struct iter_forwards* fwd, uint16_t* dclass) } /* not a root node, return next higher item */ *dclass = p->dclass+1; - return forwards_next_root(fwd, dclass); + return next_root_locked(fwd, dclass); } } +int +forwards_next_root(struct iter_forwards* fwd, uint16_t* dclass, int nolock) +{ + int ret; + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_rdlock(&fwd->lock); } + ret = next_root_locked(fwd, dclass); + if(!nolock) { lock_rw_unlock(&fwd->lock); } + return ret; +} + size_t forwards_get_mem(struct iter_forwards* fwd) { @@ -491,53 +515,80 @@ fwd_zone_find(struct iter_forwards* fwd, uint16_t c, uint8_t* nm) } int -forwards_add_zone(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp) +forwards_add_zone(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp, + int nolock) { struct iter_forward_zone *z; + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_wrlock(&fwd->lock); } if((z=fwd_zone_find(fwd, c, dp->name)) != NULL) { (void)rbtree_delete(fwd->tree, &z->node); fwd_zone_free(z); } - if(!forwards_insert(fwd, c, dp)) + if(!forwards_insert(fwd, c, dp)) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return 0; + } fwd_init_parents(fwd); + if(!nolock) { lock_rw_unlock(&fwd->lock); } return 1; } void -forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm) +forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm, + int nolock) { struct iter_forward_zone *z; - if(!(z=fwd_zone_find(fwd, c, nm))) + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_wrlock(&fwd->lock); } + if(!(z=fwd_zone_find(fwd, c, nm))) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return; /* nothing to do */ + } (void)rbtree_delete(fwd->tree, &z->node); fwd_zone_free(z); fwd_init_parents(fwd); + if(!nolock) { lock_rw_unlock(&fwd->lock); } } int -forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm) +forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm, + int nolock) { - if(fwd_zone_find(fwd, c, nm) != NULL) + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_wrlock(&fwd->lock); } + if(fwd_zone_find(fwd, c, nm) != NULL) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return 1; /* already a stub zone there */ + } if(!fwd_add_stub_hole(fwd, c, nm)) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return 0; } fwd_init_parents(fwd); + if(!nolock) { lock_rw_unlock(&fwd->lock); } return 1; } void -forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm) +forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c, + uint8_t* nm, int nolock) { struct iter_forward_zone *z; - if(!(z=fwd_zone_find(fwd, c, nm))) + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_wrlock(&fwd->lock); } + if(!(z=fwd_zone_find(fwd, c, nm))) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return; /* nothing to do */ - if(z->dp != NULL) + } + if(z->dp != NULL) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return; /* not a stub hole */ + } (void)rbtree_delete(fwd->tree, &z->node); fwd_zone_free(z); fwd_init_parents(fwd); + if(!nolock) { lock_rw_unlock(&fwd->lock); } } void diff --git a/iterator/iter_fwd.h b/iterator/iter_fwd.h index e51165ede..095cd96df 100644 --- a/iterator/iter_fwd.h +++ b/iterator/iter_fwd.h @@ -112,48 +112,61 @@ int forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg); /** * Find forward zone exactly by name - * The return value is contents of the forwards structure, caller should - * lock and unlock a readlock on the forwards structure. + * The return value is contents of the forwards structure. + * Caller should lock and unlock a readlock on the forwards structure if nolock + * is set. + * Otherwise caller should unlock the readlock on the forwards structure if a + * value was returned. * @param fwd: forward storage. * @param qname: The qname of the query. * @param qclass: The qclass of the query. + * @param nolock: Skip locking, locking is handled by the caller. * @return: A delegation point or null. */ struct delegpt* forwards_find(struct iter_forwards* fwd, uint8_t* qname, - uint16_t qclass); + uint16_t qclass, int nolock); /** * Find forward zone information * For this qname/qclass find forward zone information, returns delegation * point with server names and addresses, or NULL if no forwarding is needed. - * The return value is contents of the forwards structure, caller should - * lock and unlock a readlock on the forwards structure. + * The return value is contents of the forwards structure. + * Caller should lock and unlock a readlock on the forwards structure if nolock + * is set. + * Otherwise caller should unlock the readlock on the forwards structure if a + * value was returned. * * @param fwd: forward storage. * @param qname: The qname of the query. * @param qclass: The qclass of the query. + * @param nolock: Skip locking, locking is handled by the caller. * @return: A delegation point if the query has to be forwarded to that list, * otherwise null. */ -struct delegpt* forwards_lookup(struct iter_forwards* fwd, - uint8_t* qname, uint16_t qclass); +struct delegpt* forwards_lookup(struct iter_forwards* fwd, + uint8_t* qname, uint16_t qclass, int nolock); /** * Same as forwards_lookup, but for the root only * @param fwd: forward storage. * @param qclass: The qclass of the query. + * @param nolock: Skip locking, locking is handled by the caller. * @return: A delegation point if root forward exists, otherwise null. */ -struct delegpt* forwards_lookup_root(struct iter_forwards* fwd, - uint16_t qclass); +struct delegpt* forwards_lookup_root(struct iter_forwards* fwd, + uint16_t qclass, int nolock); /** * Find next root item in forwards lookup tree. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a readlock on the forwards structure. * @param fwd: the forward storage * @param qclass: class to look at next, or higher. + * @param nolock: Skip locking, locking is handled by the caller. * @return false if none found, or if true stored in qclass. */ -int forwards_next_root(struct iter_forwards* fwd, uint16_t* qclass); +int forwards_next_root(struct iter_forwards* fwd, uint16_t* qclass, + int nolock); /** * Get memory in use by forward storage @@ -169,43 +182,57 @@ int fwd_cmp(const void* k1, const void* k2); /** * Add zone to forward structure. For external use since it recalcs * the tree parents. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a writelock on the forwards structure. * @param fwd: the forward data structure * @param c: class of zone * @param dp: delegation point with name and target nameservers for new * forward zone. malloced. + * @param nolock: Skip locking, locking is handled by the caller. * @return false on failure (out of memory); */ -int forwards_add_zone(struct iter_forwards* fwd, uint16_t c, - struct delegpt* dp); +int forwards_add_zone(struct iter_forwards* fwd, uint16_t c, + struct delegpt* dp, int nolock); /** * Remove zone from forward structure. For external use since it * recalcs the tree parents. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a writelock on the forwards structure. * @param fwd: the forward data structure * @param c: class of zone * @param nm: name of zone (in uncompressed wireformat). + * @param nolock: Skip locking, locking is handled by the caller. */ -void forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm); +void forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, + uint8_t* nm, int nolock); /** * Add stub hole (empty entry in forward table, that makes resolution skip * a forward-zone because the stub zone should override the forward zone). * Does not add one if not necessary. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a writelock on the forwards structure. * @param fwd: the forward data structure * @param c: class of zone * @param nm: name of zone (in uncompressed wireformat). + * @param nolock: Skip locking, locking is handled by the caller. * @return false on failure (out of memory); */ -int forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm); +int forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, + uint8_t* nm, int nolock); /** * Remove stub hole, if one exists. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a writelock on the forwards structure. * @param fwd: the forward data structure * @param c: class of zone * @param nm: name of zone (in uncompressed wireformat). + * @param nolock: Skip locking, locking is handled by the caller. */ void forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c, - uint8_t* nm); + uint8_t* nm, int nolock); /** * Swap internal tree with preallocated entries. Caller should manage diff --git a/iterator/iter_hints.c b/iterator/iter_hints.c index 5a95b78cd..b8a02f4d8 100644 --- a/iterator/iter_hints.c +++ b/iterator/iter_hints.c @@ -441,6 +441,7 @@ read_root_hints_list(struct iter_hints* hints, struct config_file* cfg) int hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg) { + int nolock = 1; lock_rw_wrlock(&hints->lock); hints_del_tree(hints); name_tree_init(&hints->tree); @@ -458,7 +459,7 @@ hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg) } /* use fallback compiletime root hints */ - if(!hints_lookup_root(hints, LDNS_RR_CLASS_IN)) { + if(!hints_find_root(hints, LDNS_RR_CLASS_IN, nolock)) { struct delegpt* dp = compile_time_root_prime(cfg->do_ip4, cfg->do_ip6); verbose(VERB_ALGO, "no config, using builtin root hints."); @@ -477,21 +478,33 @@ hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg) return 1; } -struct delegpt* -hints_lookup_root(struct iter_hints* hints, uint16_t qclass) +struct delegpt* +hints_find(struct iter_hints* hints, uint8_t* qname, uint16_t qclass, + int nolock) { - uint8_t rootlab = 0; struct iter_hints_stub *stub; + size_t len; + int has_dp; + int labs = dname_count_size_labels(qname, &len); + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_rdlock(&hints->lock); } stub = (struct iter_hints_stub*)name_tree_find(&hints->tree, - &rootlab, 1, 1, qclass); - if(!stub) - return NULL; - return stub->dp; + qname, len, labs, qclass); + has_dp = stub && stub->dp; + if(!has_dp && !nolock) { lock_rw_unlock(&hints->lock); } + return has_dp?stub->dp:NULL; +} + +struct delegpt* +hints_find_root(struct iter_hints* hints, uint16_t qclass, int nolock) +{ + uint8_t rootlab = 0; + return hints_find(hints, &rootlab, qclass, nolock); } struct iter_hints_stub* -hints_lookup_stub(struct iter_hints* hints, uint8_t* qname, - uint16_t qclass, struct delegpt* cache_dp) +hints_lookup_stub(struct iter_hints* hints, uint8_t* qname, + uint16_t qclass, struct delegpt* cache_dp, int nolock) { size_t len; int labs; @@ -499,14 +512,20 @@ hints_lookup_stub(struct iter_hints* hints, uint8_t* qname, /* first lookup the stub */ labs = dname_count_size_labels(qname, &len); + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_rdlock(&hints->lock); } r = (struct iter_hints_stub*)name_tree_lookup(&hints->tree, qname, len, labs, qclass); - if(!r) return NULL; + if(!r) { + if(!nolock) { lock_rw_unlock(&hints->lock); } + return NULL; + } /* If there is no cache (root prime situation) */ if(cache_dp == NULL) { if(r->dp->namelabs != 1) return r; /* no cache dp, use any non-root stub */ + if(!nolock) { lock_rw_unlock(&hints->lock); } return NULL; } @@ -523,12 +542,18 @@ hints_lookup_stub(struct iter_hints* hints, uint8_t* qname, if(dname_strict_subdomain(r->dp->name, r->dp->namelabs, cache_dp->name, cache_dp->namelabs)) return r; /* need to prime this stub */ + if(!nolock) { lock_rw_unlock(&hints->lock); } return NULL; } -int hints_next_root(struct iter_hints* hints, uint16_t* qclass) +int hints_next_root(struct iter_hints* hints, uint16_t* qclass, int nolock) { - return name_tree_next_root(&hints->tree, qclass); + int ret; + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_rdlock(&hints->lock); } + ret = name_tree_next_root(&hints->tree, qclass); + if(!nolock) { lock_rw_unlock(&hints->lock); } + return ret; } size_t @@ -548,32 +573,43 @@ hints_get_mem(struct iter_hints* hints) int hints_add_stub(struct iter_hints* hints, uint16_t c, struct delegpt* dp, - int noprime) + int noprime, int nolock) { struct iter_hints_stub *z; + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_wrlock(&hints->lock); } if((z=(struct iter_hints_stub*)name_tree_find(&hints->tree, dp->name, dp->namelen, dp->namelabs, c)) != NULL) { (void)rbtree_delete(&hints->tree, &z->node); hints_stub_free(z); } - if(!hints_insert(hints, c, dp, noprime)) + if(!hints_insert(hints, c, dp, noprime)) { + if(!nolock) { lock_rw_unlock(&hints->lock); } return 0; + } name_tree_init_parents(&hints->tree); + if(!nolock) { lock_rw_unlock(&hints->lock); } return 1; } void -hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm) +hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm, + int nolock) { struct iter_hints_stub *z; size_t len; int labs = dname_count_size_labels(nm, &len); + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_wrlock(&hints->lock); } if(!(z=(struct iter_hints_stub*)name_tree_find(&hints->tree, - nm, len, labs, c))) + nm, len, labs, c))) { + if(!nolock) { lock_rw_unlock(&hints->lock); } return; /* nothing to do */ + } (void)rbtree_delete(&hints->tree, &z->node); hints_stub_free(z); name_tree_init_parents(&hints->tree); + if(!nolock) { lock_rw_unlock(&hints->lock); } } void diff --git a/iterator/iter_hints.h b/iterator/iter_hints.h index 6f6b6dc68..87434b5ac 100644 --- a/iterator/iter_hints.h +++ b/iterator/iter_hints.h @@ -100,43 +100,66 @@ void hints_delete(struct iter_hints* hints); int hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg); /** - * Find root hints for the given class. - * The return value is contents of the hints structure, caller should - * lock and unlock a readlock on the hints structure. + * Find hints for the given class. + * The return value is contents of the hints structure. + * Caller should lock and unlock a readlock on the hints structure if nolock + * is set. + * Otherwise caller should unlock the readlock on the hints structure if a + * value was returned. * @param hints: hint storage. + * @param qname: the qname that generated the delegation point. * @param qclass: class for which root hints are requested. host order. + * @param nolock: Skip locking, locking is handled by the caller. * @return: NULL if no hints, or a ptr to stored hints. */ -struct delegpt* hints_lookup_root(struct iter_hints* hints, uint16_t qclass); +struct delegpt* hints_find(struct iter_hints* hints, uint8_t* qname, + uint16_t qclass, int nolock); + +/** + * Same as hints_lookup, but for the root only. + * @param hints: hint storage. + * @param qclass: class for which root hints are requested. host order. + * @param nolock: Skip locking, locking is handled by the caller. + * @return: NULL if no hints, or a ptr to stored hints. + */ +struct delegpt* hints_find_root(struct iter_hints* hints, + uint16_t qclass, int nolock); /** * Find next root hints (to cycle through all root hints). + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a readlock on the hints structure. * @param hints: hint storage * @param qclass: class for which root hints are sought. * 0 means give the first available root hints class. * x means, give class x or a higher class if any. * returns the found class in this variable. + * @param nolock: Skip locking, locking is handled by the caller. * @return true if a root hint class is found. * false if not root hint class is found (qclass may have been changed). */ -int hints_next_root(struct iter_hints* hints, uint16_t* qclass); +int hints_next_root(struct iter_hints* hints, uint16_t* qclass, int nolock); /** * Given a qname/qclass combination, and the delegation point from the cache * for this qname/qclass, determine if this combination indicates that a * stub hint exists and must be primed. - * The return value is contents of the hints structure, caller should - * lock and unlock a readlock on the hints structure. + * The return value is contents of the hints structure. + * Caller should lock and unlock a readlock on the hints structure if nolock + * is set. + * Otherwise caller should unlock the readlock on the hints structure if a + * value was returned. * * @param hints: hint storage. * @param qname: The qname that generated the delegation point. * @param qclass: The qclass that generated the delegation point. * @param dp: The cache generated delegation point. + * @param nolock: Skip locking, locking is handled by the caller. * @return: A priming delegation point if there is a stub hint that must * be primed, otherwise null. */ -struct iter_hints_stub* hints_lookup_stub(struct iter_hints* hints, - uint8_t* qname, uint16_t qclass, struct delegpt* dp); +struct iter_hints_stub* hints_lookup_stub(struct iter_hints* hints, + uint8_t* qname, uint16_t qclass, struct delegpt* dp, int nolock); /** * Get memory in use by hints @@ -149,24 +172,31 @@ size_t hints_get_mem(struct iter_hints* hints); /** * Add stub to hints structure. For external use since it recalcs * the tree parents. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a writelock on the hints structure. * @param hints: the hints data structure * @param c: class of zone * @param dp: delegation point with name and target nameservers for new * hints stub. malloced. * @param noprime: set noprime option to true or false on new hint stub. + * @param nolock: Skip locking, locking is handled by the caller. * @return false on failure (out of memory); */ int hints_add_stub(struct iter_hints* hints, uint16_t c, struct delegpt* dp, - int noprime); + int noprime, int nolock); /** * Remove stub from hints structure. For external use since it * recalcs the tree parents. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a writelock on the hints structure. * @param hints: the hints data structure * @param c: class of stub zone * @param nm: name of stub zone (in uncompressed wireformat). + * @param nolock: Skip locking, locking is handled by the caller. */ -void hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm); +void hints_delete_stub(struct iter_hints* hints, uint16_t c, + uint8_t* nm, int nolock); /** * Swap internal tree with preallocated entries. Caller should manage diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index 79a6e3cb0..f291178d2 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -1285,11 +1285,13 @@ iter_get_next_root(struct iter_hints* hints, struct iter_forwards* fwd, { uint16_t c1 = *c, c2 = *c; int r1, r2; + int nolock = 1; + /* prelock both forwards and hints for atomic read. */ lock_rw_rdlock(&fwd->lock); lock_rw_rdlock(&hints->lock); - r1 = hints_next_root(hints, &c1); - r2 = forwards_next_root(fwd, &c2); + r1 = hints_next_root(hints, &c1, nolock); + r2 = forwards_next_root(fwd, &c2, nolock); lock_rw_unlock(&fwd->lock); lock_rw_unlock(&hints->lock); @@ -1462,13 +1464,16 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf, { struct iter_hints_stub *stub; struct delegpt *dp; + int nolock = 1; /* Check for stub. */ + /* Lock both forwards and hints for atomic read. */ lock_rw_rdlock(&qstate->env->fwds->lock); lock_rw_rdlock(&qstate->env->hints->lock); stub = hints_lookup_stub(qstate->env->hints, qinf->qname, - qinf->qclass, NULL); - dp = forwards_lookup(qstate->env->fwds, qinf->qname, qinf->qclass); + qinf->qclass, NULL, nolock); + dp = forwards_lookup(qstate->env->fwds, qinf->qname, qinf->qclass, + nolock); /* see if forward or stub is more pertinent */ if(stub && stub->dp && dp) { diff --git a/iterator/iterator.c b/iterator/iterator.c index fbc0cec32..57d6e99df 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -678,39 +678,40 @@ errinf_reply(struct module_qstate* qstate, struct iter_qstate* iq) /** see if last resort is possible - does config allow queries to parent */ static int -can_have_last_resort(struct module_env* env, uint8_t* nm, size_t nmlen, +can_have_last_resort(struct module_env* env, uint8_t* nm, size_t ATTR_UNUSED(nmlen), uint16_t qclass, int* have_dp, struct delegpt** retdp, struct regional* region) { - struct delegpt* fwddp; - struct iter_hints_stub* stub; - int labs = dname_count_labels(nm); + struct delegpt* dp = NULL; + int nolock = 0; /* do not process a last resort (the parent side) if a stub * or forward is configured, because we do not want to go 'above' * the configured servers */ - lock_rw_rdlock(&env->hints->lock); - if(!dname_is_root(nm) && (stub = (struct iter_hints_stub*) - name_tree_find(&env->hints->tree, nm, nmlen, labs, qclass)) && + if(!dname_is_root(nm) && + (dp = hints_find(env->hints, nm, qclass, nolock)) && /* has_parent side is turned off for stub_first, where we * are allowed to go to the parent */ - stub->dp->has_parent_side_NS) { - if(retdp) *retdp = delegpt_copy(stub->dp, region); + dp->has_parent_side_NS) { + if(retdp) *retdp = delegpt_copy(dp, region); lock_rw_unlock(&env->hints->lock); if(have_dp) *have_dp = 1; return 0; } - lock_rw_unlock(&env->hints->lock); - lock_rw_rdlock(&env->fwds->lock); - if((fwddp = forwards_find(env->fwds, nm, qclass)) && + if(dp) { + lock_rw_unlock(&env->hints->lock); + dp = NULL; + } + if((dp = forwards_find(env->fwds, nm, qclass, nolock)) && /* has_parent_side is turned off for forward_first, where * we are allowed to go to the parent */ - fwddp->has_parent_side_NS) { - if(retdp) *retdp = delegpt_copy(fwddp, region); + dp->has_parent_side_NS) { + if(retdp) *retdp = delegpt_copy(dp, region); lock_rw_unlock(&env->fwds->lock); if(have_dp) *have_dp = 1; return 0; } - lock_rw_unlock(&env->fwds->lock); + /* lock_() calls are macros that could be nothing, surround in {} */ + if(dp) { lock_rw_unlock(&env->fwds->lock); } return 1; } @@ -886,13 +887,12 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id, { struct delegpt* dp; struct module_qstate* subq; + int nolock = 0; verbose(VERB_DETAIL, "priming . %s NS", sldns_lookup_by_id(sldns_rr_classes, (int)qclass)? sldns_lookup_by_id(sldns_rr_classes, (int)qclass)->name:"??"); - lock_rw_rdlock(&qstate->env->hints->lock); - dp = hints_lookup_root(qstate->env->hints, qclass); + dp = hints_find_root(qstate->env->hints, qclass, nolock); if(!dp) { - lock_rw_unlock(&qstate->env->hints->lock); verbose(VERB_ALGO, "Cannot prime due to lack of hints"); return 0; } @@ -956,15 +956,13 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id, struct iter_hints_stub* stub; struct delegpt* stub_dp; struct module_qstate* subq; + int nolock = 0; if(!qname) return 0; - lock_rw_rdlock(&qstate->env->hints->lock); - stub = hints_lookup_stub(qstate->env->hints, qname, qclass, iq->dp); + stub = hints_lookup_stub(qstate->env->hints, qname, qclass, iq->dp, + nolock); /* The stub (if there is one) does not need priming. */ - if(!stub) { - lock_rw_unlock(&qstate->env->hints->lock); - return 0; - } + if(!stub) return 0; stub_dp = stub->dp; /* if we have an auth_zone dp, and stub is equal, don't prime stub * yet, unless we want to fallback and avoid the auth_zone */ @@ -1319,6 +1317,7 @@ forward_request(struct module_qstate* qstate, struct iter_qstate* iq) struct delegpt* dp; uint8_t* delname = iq->qchase.qname; size_t delnamelen = iq->qchase.qname_len; + int nolock = 0; if(iq->refetch_glue && iq->dp) { delname = iq->dp->name; delnamelen = iq->dp->namelen; @@ -1327,12 +1326,9 @@ forward_request(struct module_qstate* qstate, struct iter_qstate* iq) if( (iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) && !dname_is_root(iq->qchase.qname)) dname_remove_label(&delname, &delnamelen); - lock_rw_rdlock(&qstate->env->fwds->lock); - dp = forwards_lookup(qstate->env->fwds, delname, iq->qchase.qclass); - if(!dp) { - lock_rw_unlock(&qstate->env->fwds->lock); - return 0; - } + dp = forwards_lookup(qstate->env->fwds, delname, iq->qchase.qclass, + nolock); + if(!dp) return 0; /* send recursion desired to forward addr */ iq->chase_flags |= BIT_RD; iq->dp = delegpt_copy(dp, qstate->region); @@ -1633,6 +1629,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, * root priming situation. */ if(iq->dp == NULL) { int r; + int nolock = 0; /* if under auth zone, no prime needed */ if(!auth_zone_delegpt(qstate, iq, delname, delnamelen)) return error_response(qstate, id, @@ -1646,17 +1643,14 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, break; /* got noprime-stub-zone, continue */ else if(r) return 0; /* stub prime request made */ - lock_rw_rdlock(&qstate->env->fwds->lock); - if(forwards_lookup_root(qstate->env->fwds, - iq->qchase.qclass)) { + if(forwards_lookup_root(qstate->env->fwds, + iq->qchase.qclass, nolock)) { lock_rw_unlock(&qstate->env->fwds->lock); /* forward zone root, no root prime needed */ /* fill in some dp - safety belt */ - lock_rw_rdlock(&qstate->env->hints->lock); - iq->dp = hints_lookup_root(qstate->env->hints, - iq->qchase.qclass); + iq->dp = hints_find_root(qstate->env->hints, + iq->qchase.qclass, nolock); if(!iq->dp) { - lock_rw_unlock(&qstate->env->hints->lock); log_err("internal error: no hints dp"); errinf(qstate, "no hints for this class"); return error_response(qstate, id, @@ -1672,7 +1666,6 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, } return next_state(iq, INIT_REQUEST_2_STATE); } - lock_rw_unlock(&qstate->env->fwds->lock); /* Note that the result of this will set a new * DelegationPoint based on the result of priming. */ if(!prime_root(qstate, iq, id, iq->qchase.qclass)) @@ -1730,15 +1723,14 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, } if(dname_is_root(iq->dp->name)) { /* use safety belt */ + int nolock = 0; verbose(VERB_QUERY, "Cache has root NS but " "no addresses. Fallback to the safety belt."); - lock_rw_rdlock(&qstate->env->hints->lock); - iq->dp = hints_lookup_root(qstate->env->hints, - iq->qchase.qclass); + iq->dp = hints_find_root(qstate->env->hints, + iq->qchase.qclass, nolock); /* note deleg_msg is from previous lookup, * but RD is on, so it is not used */ if(!iq->dp) { - lock_rw_unlock(&qstate->env->hints->lock); log_err("internal error: no hints dp"); return error_response(qstate, id, LDNS_RCODE_REFUSED); @@ -1800,6 +1792,7 @@ processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq, delnamelen = iq->qchase.qname_len; if(iq->refetch_glue) { struct iter_hints_stub* stub; + int nolock = 0; if(!iq->dp) { log_err("internal or malloc fail: no dp for refetch"); errinf(qstate, "malloc failure, no delegation info"); @@ -1807,16 +1800,16 @@ processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq, } /* Do not send queries above stub, do not set delname to dp if * this is above stub without stub-first. */ - lock_rw_rdlock(&qstate->env->hints->lock); stub = hints_lookup_stub( qstate->env->hints, iq->qchase.qname, iq->qchase.qclass, - iq->dp); + iq->dp, nolock); if(!stub || !stub->dp->has_parent_side_NS || dname_subdomain_c(iq->dp->name, stub->dp->name)) { delname = iq->dp->name; delnamelen = iq->dp->namelen; } - lock_rw_unlock(&qstate->env->hints->lock); + /* lock_() calls are macros that could be nothing, surround in {} */ + if(stub) { lock_rw_unlock(&qstate->env->hints->lock); } } if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) { if(!dname_is_root(delname)) @@ -2130,24 +2123,25 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL); } if(!iq->dp->has_parent_side_NS && dname_is_root(iq->dp->name)) { - struct delegpt* p; - lock_rw_rdlock(&qstate->env->hints->lock); - p = hints_lookup_root(qstate->env->hints, iq->qchase.qclass); - if(p) { + struct delegpt* dp; + int nolock = 0; + dp = hints_find_root(qstate->env->hints, + iq->qchase.qclass, nolock); + if(dp) { struct delegpt_addr* a; iq->chase_flags &= ~BIT_RD; /* go to authorities */ - for(ns = p->nslist; ns; ns=ns->next) { + for(ns = dp->nslist; ns; ns=ns->next) { (void)delegpt_add_ns(iq->dp, qstate->region, ns->name, ns->lame, ns->tls_auth_name, ns->port); } - for(a = p->target_list; a; a=a->next_target) { + for(a = dp->target_list; a; a=a->next_target) { (void)delegpt_add_addr(iq->dp, qstate->region, &a->addr, a->addrlen, a->bogus, a->lame, a->tls_auth_name, -1, NULL); } + lock_rw_unlock(&qstate->env->hints->lock); } - lock_rw_unlock(&qstate->env->hints->lock); iq->dp->has_parent_side_NS = 1; } else if(!iq->dp->has_parent_side_NS) { if(!iter_lookup_parent_NS_from_cache(qstate->env, iq->dp, diff --git a/pythonmod/interface.i b/pythonmod/interface.i index 5bc7e8492..c876ab072 100644 --- a/pythonmod/interface.i +++ b/pythonmod/interface.i @@ -1416,7 +1416,7 @@ struct delegpt* dns_cache_find_delegation(struct module_env* env, int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, struct delegpt* dp, int supports_ipv4, int supports_ipv6, int use_nat64); struct iter_hints_stub* hints_lookup_stub(struct iter_hints* hints, - uint8_t* qname, uint16_t qclass, struct delegpt* dp); + uint8_t* qname, uint16_t qclass, struct delegpt* dp, int nolock); /* Custom function to perform logic similar to the one in daemon/cachedump.c */ struct delegpt* find_delegation(struct module_qstate* qstate, char *nm, size_t nmlen); @@ -1433,6 +1433,7 @@ struct delegpt* find_delegation(struct module_qstate* qstate, char *nm, size_t n struct query_info qinfo; struct iter_hints_stub* stub; uint32_t timenow = *qstate->env->now; + int nolock = 0; regional_free_all(region); qinfo.qname = (uint8_t*)nm; @@ -1455,14 +1456,13 @@ struct delegpt* find_delegation(struct module_qstate* qstate, char *nm, size_t n dname_str((uint8_t*)nm, b); continue; } - lock_rw_rdlock(&qstate->env->hints->lock); - stub = hints_lookup_stub(qstate->env->hints, qinfo.qname, qinfo.qclass, dp); + stub = hints_lookup_stub(qstate->env->hints, qinfo.qname, + qinfo.qclass, dp, nolock); if (stub) { struct delegpt* stubdp = delegpt_copy(stub->dp, region); lock_rw_unlock(&qstate->env->hints->lock); return stubdp; } else { - lock_rw_unlock(&qstate->env->hints->lock); return dp; } } From 2747f4a6b450c919cfc79d9b5e05c6d22966db51 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 6 May 2024 14:21:23 +0200 Subject: [PATCH 063/125] - fast-reload, reload access-control. --- daemon/acl_list.c | 11 +++++++++++ daemon/acl_list.h | 9 +++++++++ daemon/remote.c | 16 ++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/daemon/acl_list.c b/daemon/acl_list.c index 83cfd7ddf..c6e33e5fa 100644 --- a/daemon/acl_list.c +++ b/daemon/acl_list.c @@ -823,3 +823,14 @@ log_acl_action(const char* action, struct sockaddr_storage* addr, (int)port); } } + +void acl_list_swap_tree(struct acl_list* acl, struct acl_list* data) +{ + /* swap tree and region */ + rbtree_type oldtree = acl->tree; + struct regional* oldregion = acl->region; + acl->tree = data->tree; + acl->region = data->region; + data->tree = oldtree; + data->region = oldregion; +} diff --git a/daemon/acl_list.h b/daemon/acl_list.h index 9da43bef3..45437c61d 100644 --- a/daemon/acl_list.h +++ b/daemon/acl_list.h @@ -202,4 +202,13 @@ const char* acl_access_to_str(enum acl_access acl); void log_acl_action(const char* action, struct sockaddr_storage* addr, socklen_t addrlen, enum acl_access acl, struct acl_addr* acladdr); +/** + * Swap internal tree with preallocated entries. Caller should manage + * the locks. + * @param acl: the acl structure. + * @param data: the data structure used to take elements from. This contains + * the old elements on return. + */ +void acl_list_swap_tree(struct acl_list* acl, struct acl_list* data); + #endif /* DAEMON_ACL_LIST_H */ diff --git a/daemon/remote.c b/daemon/remote.c index 48952a4b1..f397b1705 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3779,6 +3779,8 @@ struct fast_reload_construct { struct iter_hints* hints; /** construct for respip_set */ struct respip_set* respip_set; + /** construct for access control */ + struct acl_list* acl; /** construct for local zones */ struct local_zones* local_zones; /** if there is response ip configuration in use */ @@ -3895,6 +3897,7 @@ fr_construct_clear(struct fast_reload_construct* ct) hints_delete(ct->hints); respip_set_delete(ct->respip_set); local_zones_delete(ct->local_zones); + acl_list_delete(ct->acl); views_delete(ct->views); /* Delete the log identity here so that the global value is not * reset by config_delete. */ @@ -4146,6 +4149,7 @@ fr_printmem(struct fast_reload_thread* fr, mem += forwards_get_mem(ct->fwds); mem += hints_get_mem(ct->hints); mem += local_zones_get_mem(ct->local_zones); + mem += acl_list_get_mem(ct->acl); mem += sizeof(*ct->oldcfg); mem += config_file_getmem(newcfg); @@ -4174,6 +4178,17 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; + if(!(ct->acl = acl_list_create())) { + fr_construct_clear(ct); + return 0; + } + if(!acl_list_apply_cfg(ct->acl, newcfg, ct->views)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + if(!(ct->fwds = forwards_create())) { fr_construct_clear(ct); return 0; @@ -4676,6 +4691,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, forwards_swap_tree(env->fwds, ct->fwds); hints_swap_tree(env->hints, ct->hints); views_swap_tree(env->views, ct->views); + acl_list_swap_tree(daemon->acl, ct->acl); local_zones_swap_tree(daemon->local_zones, ct->local_zones); respip_set_swap_tree(env->respip_set, ct->respip_set); daemon->use_response_ip = ct->use_response_ip; From 1550720ba91e8c0885e9586f8d03b54b5a45c5d5 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 6 May 2024 14:39:49 +0200 Subject: [PATCH 064/125] - fast-reload, reload access control interface, such as interface-action. --- daemon/daemon.c | 3 +-- daemon/daemon.h | 8 ++++++++ daemon/remote.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/daemon/daemon.c b/daemon/daemon.c index fd06b3510..f119b7eb9 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -323,8 +323,7 @@ daemon_init(void) return daemon; } -static int setup_acl_for_ports(struct acl_list* list, - struct listen_port* port_list) +int setup_acl_for_ports(struct acl_list* list, struct listen_port* port_list) { struct acl_addr* acl_node; for(; port_list; port_list=port_list->next) { diff --git a/daemon/daemon.h b/daemon/daemon.h index dbfce9a24..9713c51b1 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -195,4 +195,12 @@ void daemon_delete(struct daemon* daemon); */ void daemon_apply_cfg(struct daemon* daemon, struct config_file* cfg); +/** + * Setup acl list to have entries for the port list. + * @param list: the acl interface + * @param port_list: list of open ports, or none. + * @return false on failure + */ +int setup_acl_for_ports(struct acl_list* list, struct listen_port* port_list); + #endif /* DAEMON_H */ diff --git a/daemon/remote.c b/daemon/remote.c index f397b1705..a26d02608 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3781,6 +3781,8 @@ struct fast_reload_construct { struct respip_set* respip_set; /** construct for access control */ struct acl_list* acl; + /** construct for access control interface */ + struct acl_list* acl_interface; /** construct for local zones */ struct local_zones* local_zones; /** if there is response ip configuration in use */ @@ -3898,6 +3900,7 @@ fr_construct_clear(struct fast_reload_construct* ct) respip_set_delete(ct->respip_set); local_zones_delete(ct->local_zones); acl_list_delete(ct->acl); + acl_list_delete(ct->acl_interface); views_delete(ct->views); /* Delete the log identity here so that the global value is not * reset by config_delete. */ @@ -4150,6 +4153,7 @@ fr_printmem(struct fast_reload_thread* fr, mem += hints_get_mem(ct->hints); mem += local_zones_get_mem(ct->local_zones); mem += acl_list_get_mem(ct->acl); + mem += acl_list_get_mem(ct->acl_interface); mem += sizeof(*ct->oldcfg); mem += config_file_getmem(newcfg); @@ -4160,6 +4164,27 @@ fr_printmem(struct fast_reload_thread* fr, return 1; } +/** fast reload thread, setup the acl_interface for the ports that + * the server has. */ +static int +ct_acl_interface_setup_ports(struct acl_list* acl_interface, + struct daemon* daemon) +{ + /* clean acl_interface */ + acl_interface_init(acl_interface); + if(!setup_acl_for_ports(acl_interface, daemon->ports[0])) + return 0; + if(daemon->reuseport) { + size_t i; + for(i=1; inum_ports; i++) { + if(!setup_acl_for_ports(acl_interface, + daemon->ports[i])) + return 0; + } + } + return 1; +} + /** fast reload thread, construct from config the new items */ static int fr_construct_from_config(struct fast_reload_thread* fr, @@ -4189,6 +4214,22 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; + if(!(ct->acl_interface = acl_list_create())) { + fr_construct_clear(ct); + return 0; + } + if(!ct_acl_interface_setup_ports(ct->acl_interface, + fr->worker->daemon)) { + fr_construct_clear(ct); + return 0; + } + if(!acl_interface_apply_cfg(ct->acl_interface, newcfg, ct->views)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + if(!(ct->fwds = forwards_create())) { fr_construct_clear(ct); return 0; @@ -4692,6 +4733,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, hints_swap_tree(env->hints, ct->hints); views_swap_tree(env->views, ct->views); acl_list_swap_tree(daemon->acl, ct->acl); + acl_list_swap_tree(daemon->acl_interface, ct->acl_interface); local_zones_swap_tree(daemon->local_zones, ct->local_zones); respip_set_swap_tree(env->respip_set, ct->respip_set); daemon->use_response_ip = ct->use_response_ip; From 6b6d706f7ebf715d7441dd9c2348891f8886ef25 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 6 May 2024 14:54:29 +0200 Subject: [PATCH 065/125] - fast-reload, reload tcp-connection-limit. --- Makefile.in | 2 +- daemon/remote.c | 17 +++++++++++++++++ util/tcp_conn_limit.c | 11 +++++++++++ util/tcp_conn_limit.h | 9 +++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 06dbe6596..aa66f0240 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1297,7 +1297,7 @@ remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h $(srcdir)/validator/val_anchor.h $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h \ $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_delegpt.h \ $(srcdir)/services/outside_network.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h \ - $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h $(srcdir)/util/ub_event.h + $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h $(srcdir)/util/ub_event.h $(srcdir)/util/tcp_conn_limit.h stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \ $(srcdir)/libunbound/unbound.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \ $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \ diff --git a/daemon/remote.c b/daemon/remote.c index a26d02608..e80acd5cc 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -92,6 +92,7 @@ #include "sldns/wire2str.h" #include "sldns/sbuffer.h" #include "util/timeval_func.h" +#include "util/tcp_conn_limit.h" #ifdef HAVE_SYS_TYPES_H # include @@ -3783,6 +3784,8 @@ struct fast_reload_construct { struct acl_list* acl; /** construct for access control interface */ struct acl_list* acl_interface; + /** construct for tcp connection limit */ + struct tcl_list* tcl; /** construct for local zones */ struct local_zones* local_zones; /** if there is response ip configuration in use */ @@ -3901,6 +3904,7 @@ fr_construct_clear(struct fast_reload_construct* ct) local_zones_delete(ct->local_zones); acl_list_delete(ct->acl); acl_list_delete(ct->acl_interface); + tcl_list_delete(ct->tcl); views_delete(ct->views); /* Delete the log identity here so that the global value is not * reset by config_delete. */ @@ -4154,6 +4158,7 @@ fr_printmem(struct fast_reload_thread* fr, mem += local_zones_get_mem(ct->local_zones); mem += acl_list_get_mem(ct->acl); mem += acl_list_get_mem(ct->acl_interface); + mem += tcl_list_get_mem(ct->tcl); mem += sizeof(*ct->oldcfg); mem += config_file_getmem(newcfg); @@ -4230,6 +4235,17 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; + if(!(ct->tcl = tcl_list_create())) { + fr_construct_clear(ct); + return 0; + } + if(!tcl_list_apply_cfg(ct->tcl, newcfg)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + if(!(ct->fwds = forwards_create())) { fr_construct_clear(ct); return 0; @@ -4734,6 +4750,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, views_swap_tree(env->views, ct->views); acl_list_swap_tree(daemon->acl, ct->acl); acl_list_swap_tree(daemon->acl_interface, ct->acl_interface); + tcl_list_swap_tree(daemon->tcl, ct->tcl); local_zones_swap_tree(daemon->local_zones, ct->local_zones); respip_set_swap_tree(env->respip_set, ct->respip_set); daemon->use_response_ip = ct->use_response_ip; diff --git a/util/tcp_conn_limit.c b/util/tcp_conn_limit.c index d7d86a540..284d89076 100644 --- a/util/tcp_conn_limit.c +++ b/util/tcp_conn_limit.c @@ -192,3 +192,14 @@ tcl_list_get_mem(struct tcl_list* tcl) if(!tcl) return 0; return sizeof(*tcl) + regional_get_mem(tcl->region); } + +void tcl_list_swap_tree(struct tcl_list* tcl, struct tcl_list* data) +{ + /* swap tree and region */ + rbtree_type oldtree = tcl->tree; + struct regional* oldregion = tcl->region; + tcl->tree = data->tree; + tcl->region = data->region; + data->tree = oldtree; + data->region = oldregion; +} diff --git a/util/tcp_conn_limit.h b/util/tcp_conn_limit.h index 4fb71a328..12715a1e3 100644 --- a/util/tcp_conn_limit.h +++ b/util/tcp_conn_limit.h @@ -127,4 +127,13 @@ tcl_addr_lookup(struct tcl_list* tcl, struct sockaddr_storage* addr, */ size_t tcl_list_get_mem(struct tcl_list* tcl); +/** + * Swap internal tree with preallocated entries. Caller should manage + * the locks. + * @param tcl: the tcp connection list structure. + * @param data: the data structure used to take elements from. This contains + * the old elements on return. + */ +void tcl_list_swap_tree(struct tcl_list* tcl, struct tcl_list* data); + #endif /* DAEMON_TCP_CONN_LIMIT_H */ From c31a40deb6ffefa6d24babf9c77d8b5c09798a25 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 6 May 2024 14:58:25 +0200 Subject: [PATCH 066/125] - fast-reload, improve comments on acl_list and tcl_list swap tree. --- daemon/acl_list.h | 3 +-- util/tcp_conn_limit.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/daemon/acl_list.h b/daemon/acl_list.h index 45437c61d..f0ca106f1 100644 --- a/daemon/acl_list.h +++ b/daemon/acl_list.h @@ -203,8 +203,7 @@ void log_acl_action(const char* action, struct sockaddr_storage* addr, socklen_t addrlen, enum acl_access acl, struct acl_addr* acladdr); /** - * Swap internal tree with preallocated entries. Caller should manage - * the locks. + * Swap internal tree with preallocated entries. * @param acl: the acl structure. * @param data: the data structure used to take elements from. This contains * the old elements on return. diff --git a/util/tcp_conn_limit.h b/util/tcp_conn_limit.h index 12715a1e3..52108942c 100644 --- a/util/tcp_conn_limit.h +++ b/util/tcp_conn_limit.h @@ -129,7 +129,7 @@ size_t tcl_list_get_mem(struct tcl_list* tcl); /** * Swap internal tree with preallocated entries. Caller should manage - * the locks. + * tcl_addr item locks. * @param tcl: the tcp connection list structure. * @param data: the data structure used to take elements from. This contains * the old elements on return. From 1c4c3add4a804d512ed2139043fddce3d69bc9cb Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 6 May 2024 16:04:11 +0200 Subject: [PATCH 067/125] - fast-reload, fixup references to old tcp connection limits in open tcp connections. --- daemon/remote.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index e80acd5cc..ace623c8d 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5586,6 +5586,38 @@ fr_poll_for_reload_start(struct fast_reload_thread* fr) } } +/** + * Remove the old tcl_addr entries from the open connections. + * They are only incremented when an accept is performed on a tcp comm point. + * @param front: listening comm ports of the worker. + */ +void +tcl_remove_old(struct listen_dnsport* front) +{ + struct listen_list* l; + l = front->cps; + while(l) { + if(l->com->type == comm_tcp_accept) { + int i; + for(i=0; icom->cur_tcp_count; i++) { + if(l->com->tcp_handlers[i]->tcl_addr) { + /* Because the increment of the + * connection limit was in the old + * tcl list, the new list does not + * need a decrement. With NULL it is + * not decremented when the connection + * is done, and also there is no + * reference to the old connection + * limit structure. */ + l->com->tcp_handlers[i]->tcl_addr = + NULL; + } + } + } + l = l->next; + } +} + void fast_reload_worker_pickup_changes(struct worker* worker) { @@ -5600,6 +5632,10 @@ fast_reload_worker_pickup_changes(struct worker* worker) * swapped in trees, and the worker has been running with the * older information for some time. */ worker->env.mesh->use_response_ip = worker->daemon->use_response_ip; + + /* Since the tcp connection limit has changed, the open connections + * need to remove their reference for the old tcp limits counters. */ + tcl_remove_old(worker->front); } /** fast reload thread, handle reload_stop notification, send reload stop From 1c8448d5389ae7fa3ce34dc242359bed35e64b17 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 6 May 2024 16:06:34 +0200 Subject: [PATCH 068/125] - fast-reload, fixup to clean tcp connection also for different linked order. --- daemon/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index ace623c8d..9d0878f5a 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5599,7 +5599,7 @@ tcl_remove_old(struct listen_dnsport* front) while(l) { if(l->com->type == comm_tcp_accept) { int i; - for(i=0; icom->cur_tcp_count; i++) { + for(i=0; icom->max_tcp_count; i++) { if(l->com->tcp_handlers[i]->tcl_addr) { /* Because the increment of the * connection limit was in the old From 48a391491b1361b493d49395679a3e9288171b98 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 6 May 2024 16:26:06 +0200 Subject: [PATCH 069/125] - fast-reload, if no tcp connection limits existed, no need to remove references for that. --- daemon/daemon.h | 3 +++ daemon/remote.c | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/daemon/daemon.h b/daemon/daemon.h index 9713c51b1..3210aae1e 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -150,6 +150,9 @@ struct daemon { struct fast_reload_printq* fast_reload_printq_list; /** the fast reload option to drop mesh queries, true if so. */ int fast_reload_drop_mesh; + /** for fast reload, if the tcl, tcp connection limits, has + * changes for workers */ + int fast_reload_tcl_has_changes; /** config file name */ char* cfgfile; }; diff --git a/daemon/remote.c b/daemon/remote.c index 9d0878f5a..7193f659d 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4243,6 +4243,9 @@ fr_construct_from_config(struct fast_reload_thread* fr, fr_construct_clear(ct); return 0; } + if(fr->worker->daemon->tcl->tree.count != 0) + fr->worker->daemon->fast_reload_tcl_has_changes = 1; + else fr->worker->daemon->fast_reload_tcl_has_changes = 0; if(fr_poll_for_quit(fr)) return 1; @@ -5633,9 +5636,10 @@ fast_reload_worker_pickup_changes(struct worker* worker) * older information for some time. */ worker->env.mesh->use_response_ip = worker->daemon->use_response_ip; - /* Since the tcp connection limit has changed, the open connections + /* If the tcp connection limit has changed, the open connections * need to remove their reference for the old tcp limits counters. */ - tcl_remove_old(worker->front); + if(worker->daemon->fast_reload_tcl_has_changes) + tcl_remove_old(worker->front); } /** fast reload thread, handle reload_stop notification, send reload stop From 9d85ae00da7c08a1135aaaed0b0cdb6729b2cc93 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 7 May 2024 09:24:31 +0200 Subject: [PATCH 070/125] - fast-reload, document more options that work and do not work. --- doc/unbound-control.8.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index a112abc93..c8bded087 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -67,7 +67,11 @@ is loaded in a thread, and prepared, then it briefly pauses the existing server and updates config options. The intent is that the pause does not impact the service of user queries. The cache is kept. Also user queries worked on are kept and continue, but with the new config options. -Not all options are changed, but it changes like forwards and stubs. +Not all options are changed, but it changes like forwards, stubs and +local zones. Also access-control and interface-action and similar options, +also tcp-connection-limits, views. It can reload some define-tag changes. +It does not work with interface, outgoing-interface changes, also not with +remote-control changes. .IP The '+v' option makes the output verbose. With '+vv' it is more verbose. That includes the time it took to do the reload. And with more verbose output From 69b49740e769dd15714d25f43b17b7acbb4ff45f Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 16 May 2024 11:07:10 +0200 Subject: [PATCH 071/125] - fast-reload, reload auth_zone and rpz data. --- daemon/remote.c | 47 +++++++++++++++ services/authzone.c | 143 ++++++++++++++++++++++++++++++++++++++++++++ services/authzone.h | 7 ++- services/rpz.c | 28 +++++++++ services/rpz.h | 7 +++ util/netevent.c | 3 +- 6 files changed, 233 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 7193f659d..2da083387 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3774,6 +3774,8 @@ fr_init_time(struct timeval* time_start, struct timeval* time_read, struct fast_reload_construct { /** construct for views */ struct views* views; + /** construct for auth zones */ + struct auth_zones* auth_zones; /** construct for forwards */ struct iter_forwards* fwds; /** construct for stubs */ @@ -3898,6 +3900,7 @@ fr_construct_clear(struct fast_reload_construct* ct) { if(!ct) return; + auth_zones_delete(ct->auth_zones); forwards_delete(ct->fwds); hints_delete(ct->hints); respip_set_delete(ct->respip_set); @@ -4153,6 +4156,7 @@ fr_printmem(struct fast_reload_thread* fr, return 1; mem += views_get_mem(ct->views); mem += respip_set_get_mem(ct->respip_set); + mem += auth_zones_get_mem(ct->auth_zones); mem += forwards_get_mem(ct->fwds); mem += hints_get_mem(ct->hints); mem += local_zones_get_mem(ct->local_zones); @@ -4249,6 +4253,19 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; + if(!(ct->auth_zones = auth_zones_create())) { + fr_construct_clear(ct); + return 0; + } + if(!auth_zones_apply_cfg(ct->auth_zones, newcfg, 1, + &fr->worker->daemon->use_rpz, fr->worker->daemon->env, + &fr->worker->daemon->mods)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + if(!(ct->fwds = forwards_create())) { fr_construct_clear(ct); return 0; @@ -4348,6 +4365,27 @@ fr_finish_time(struct fast_reload_thread* fr, struct timeval* time_start, return 1; } +/** Swap auth zone information */ +static void +auth_zones_swap(struct auth_zones* az, struct auth_zones* data) +{ + rbtree_type oldztree = az->ztree; + int old_have_downstream = az->have_downstream; + struct auth_zone* old_rpz_first = az->rpz_first; + + az->ztree = data->ztree; + data->ztree = oldztree; + + az->have_downstream = data->have_downstream; + data->have_downstream = old_have_downstream; + + /* Leave num_query_up and num_query_down, the statistics can + * remain counted. */ + + az->rpz_first = data->rpz_first; + data->rpz_first = old_rpz_first; +} + #ifdef ATOMIC_POINTER_LOCK_FREE /** Fast reload thread, if atomics are available, copy the config items * one by one with atomic store operations. */ @@ -4714,6 +4752,10 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, lock_rw_wrlock(&env->respip_set->lock); lock_rw_wrlock(&ct->local_zones->lock); lock_rw_wrlock(&daemon->local_zones->lock); + lock_rw_wrlock(&ct->auth_zones->rpz_lock); + lock_rw_wrlock(&env->auth_zones->rpz_lock); + lock_rw_wrlock(&ct->auth_zones->lock); + lock_rw_wrlock(&env->auth_zones->lock); lock_rw_wrlock(&ct->fwds->lock); lock_rw_wrlock(&env->fwds->lock); lock_rw_wrlock(&ct->hints->lock); @@ -4757,6 +4799,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, local_zones_swap_tree(daemon->local_zones, ct->local_zones); respip_set_swap_tree(env->respip_set, ct->respip_set); daemon->use_response_ip = ct->use_response_ip; + auth_zones_swap(env->auth_zones, ct->auth_zones); /* Set globals with new config. */ config_apply(env->cfg); @@ -4767,6 +4810,10 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, lock_rw_unlock(&env->respip_set->lock); lock_rw_unlock(&ct->local_zones->lock); lock_rw_unlock(&daemon->local_zones->lock); + lock_rw_unlock(&env->auth_zones->lock); + lock_rw_unlock(&ct->auth_zones->lock); + lock_rw_unlock(&env->auth_zones->rpz_lock); + lock_rw_unlock(&ct->auth_zones->rpz_lock); lock_rw_unlock(&env->fwds->lock); lock_rw_unlock(&ct->fwds->lock); lock_rw_unlock(&env->hints->lock); diff --git a/services/authzone.c b/services/authzone.c index f01a6d9e0..b791f03b0 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -8545,3 +8545,146 @@ void auth_zones_pickup_zonemd_verify(struct auth_zones* az, } lock_rw_unlock(&az->lock); } + +/** Get memory usage of auth rrset */ +static size_t +auth_rrset_get_mem(struct auth_rrset* rrset) +{ + size_t m = sizeof(*rrset) + packed_rrset_sizeof(rrset->data); + return m; +} + +/** Get memory usage of auth data */ +static size_t +auth_data_get_mem(struct auth_data* node) +{ + size_t m = sizeof(*node) + node->namelen; + struct auth_rrset* rrset; + for(rrset = node->rrsets; rrset; rrset = rrset->next) { + m += auth_rrset_get_mem(rrset); + } + return m; +} + +/** Get memory usage of auth zone */ +static size_t +auth_zone_get_mem(struct auth_zone* z) +{ + size_t m = sizeof(*z) + z->namelen; + struct auth_data* node; + if(z->zonefile) + m += strlen(z->zonefile)+1; + RBTREE_FOR(node, struct auth_data*, &z->data) { + m += auth_data_get_mem(node); + } + if(z->rpz) + m += rpz_get_mem(z->rpz); + return m; +} + +/** Get memory usage of list of auth addr */ +static size_t +auth_addrs_get_mem(struct auth_addr* list) +{ + size_t m = 0; + struct auth_addr* a; + for(a = list; a; a = a->next) { + m += sizeof(*a); + } + return m; +} + +/** Get memory usage of list of primaries for auth xfer */ +static size_t +auth_primaries_get_mem(struct auth_master* list) +{ + size_t m = 0; + struct auth_master* n; + for(n = list; n; n = n->next) { + m += sizeof(*n); + m += auth_addrs_get_mem(n->list); + if(n->host) + m += strlen(n->host)+1; + if(n->file) + m += strlen(n->file)+1; + } + return m; +} + +/** Get memory usage or list of auth chunks */ +static size_t +auth_chunks_get_mem(struct auth_chunk* list) +{ + size_t m = 0; + struct auth_chunk* chunk; + for(chunk = list; chunk; chunk = chunk->next) { + m += sizeof(*chunk) + chunk->len; + } + return m; +} + +/** Get memory usage of auth xfer */ +static size_t +auth_xfer_get_mem(struct auth_xfer* xfr) +{ + size_t m = sizeof(*xfr) + xfr->namelen; + + /* auth_nextprobe */ + m += comm_timer_get_mem(xfr->task_nextprobe->timer); + + /* auth_probe */ + m += auth_primaries_get_mem(xfr->task_probe->masters); + m += comm_point_get_mem(xfr->task_probe->cp); + m += comm_timer_get_mem(xfr->task_probe->timer); + + /* auth_transfer */ + m += auth_chunks_get_mem(xfr->task_transfer->chunks_first); + m += auth_primaries_get_mem(xfr->task_transfer->masters); + m += comm_point_get_mem(xfr->task_transfer->cp); + m += comm_timer_get_mem(xfr->task_transfer->timer); + + /* allow_notify_list */ + m += auth_primaries_get_mem(xfr->allow_notify_list); + + return m; +} + +/** Get memory usage of auth zones ztree */ +static size_t +az_ztree_get_mem(struct auth_zones* az) +{ + size_t m = 0; + struct auth_zone* z; + RBTREE_FOR(z, struct auth_zone*, &az->ztree) { + lock_rw_rdlock(&z->lock); + m += auth_zone_get_mem(z); + lock_rw_unlock(&z->lock); + } + return m; +} + +/** Get memory usage of auth zones xtree */ +static size_t +az_xtree_get_mem(struct auth_zones* az) +{ + size_t m = 0; + struct auth_xfer* xfr; + RBTREE_FOR(xfr, struct auth_xfer*, &az->xtree) { + lock_basic_lock(&xfr->lock); + m += auth_xfer_get_mem(xfr); + lock_basic_unlock(&xfr->lock); + } + return m; +} + +size_t auth_zones_get_mem(struct auth_zones* zones) +{ + size_t m = sizeof(*zones); + lock_rw_rdlock(&zones->rpz_lock); + lock_rw_rdlock(&zones->lock); + az_ztree_get_mem(zones); + az_xtree_get_mem(zones); + lock_rw_unlock(&zones->lock); + lock_rw_unlock(&zones->rpz_lock); + return m; +} diff --git a/services/authzone.h b/services/authzone.h index 07614ed82..f9e89c4dc 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -70,7 +70,8 @@ struct auth_chunk; * Authoritative zones, shared. */ struct auth_zones { - /** lock on the authzone trees */ + /** lock on the authzone trees. It is locked after views, respip, + * local_zones and before fwds and stubs. */ lock_rw_type lock; /** rbtree of struct auth_zone */ rbtree_type ztree; @@ -787,4 +788,8 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, void auth_zones_pickup_zonemd_verify(struct auth_zones* az, struct module_env* env); +/** Get memory usage for auth zones. The routine locks and unlocks + * for reading. */ +size_t auth_zones_get_mem(struct auth_zones* zones); + #endif /* SERVICES_AUTHZONE_H */ diff --git a/services/rpz.c b/services/rpz.c index f036cc5fd..18a72c588 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -2747,3 +2747,31 @@ void rpz_disable(struct rpz* r) return; r->disabled = 1; } + +/** Get memory usage for clientip_synthesized_rrset. Ignores memory usage + * of locks. */ +static size_t +rpz_clientip_synthesized_set_get_mem(struct clientip_synthesized_rrset* set) +{ + size_t m = sizeof(*set); + lock_rw_rdlock(&set->lock); + m += regional_get_mem(set->region); + lock_rw_unlock(&set->lock); + return m; +} + +size_t rpz_get_mem(struct rpz* r) +{ + size_t m = sizeof(*r); + if(r->taglist) + m += r->taglistlen; + if(r->log_name) + m += strlen(r->log_name) + 1; + m += regional_get_mem(r->region); + m += local_zones_get_mem(r->local_zones); + m += local_zones_get_mem(r->nsdname_zones); + m += respip_set_get_mem(r->respip_set); + m += rpz_clientip_synthesized_set_get_mem(r->client_set); + m += rpz_clientip_synthesized_set_get_mem(r->ns_set); + return m; +} diff --git a/services/rpz.h b/services/rpz.h index 7f409087f..6b5f17d1e 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -269,4 +269,11 @@ void rpz_enable(struct rpz* r); */ void rpz_disable(struct rpz* r); +/** + * Get memory usage of rpz. Caller must manage locks. + * @param r: RPZ struct. + * @return memory usage. + */ +size_t rpz_get_mem(struct rpz* r); + #endif /* SERVICES_RPZ_H */ diff --git a/util/netevent.c b/util/netevent.c index 980829d08..81e0c3eeb 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -5030,8 +5030,9 @@ comm_timer_is_set(struct comm_timer* timer) } size_t -comm_timer_get_mem(struct comm_timer* ATTR_UNUSED(timer)) +comm_timer_get_mem(struct comm_timer* timer) { + if(!timer) return 0; return sizeof(struct internal_timer); } From d819c78d50422705d7cd0aeda3bba0c8ba25c337 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 16 May 2024 11:13:00 +0200 Subject: [PATCH 072/125] - fast-reload, fix auth_zones_get_mem. --- services/authzone.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/authzone.c b/services/authzone.c index b791f03b0..73838d158 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -8682,8 +8682,8 @@ size_t auth_zones_get_mem(struct auth_zones* zones) size_t m = sizeof(*zones); lock_rw_rdlock(&zones->rpz_lock); lock_rw_rdlock(&zones->lock); - az_ztree_get_mem(zones); - az_xtree_get_mem(zones); + m += az_ztree_get_mem(zones); + m += az_xtree_get_mem(zones); lock_rw_unlock(&zones->lock); lock_rw_unlock(&zones->rpz_lock); return m; From 309346e757c3a730e2eb0428e8e4c25a4651e3ee Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 16 May 2024 12:26:01 +0200 Subject: [PATCH 073/125] - fast-reload, fix compilation of testbound for the new comm_timer_get_mem reference in remote control. --- testcode/fake_event.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 201df6dff..2bfd49b3c 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1479,6 +1479,11 @@ size_t comm_point_get_mem(struct comm_point* ATTR_UNUSED(c)) return 0; } +size_t comm_timer_get_mem(struct comm_timer* ATTR_UNUSED(timer)) +{ + return 0; +} + size_t serviced_get_mem(struct serviced_query* ATTR_UNUSED(c)) { return 0; From b1ff45abb4d1240a03bc1f995e9bf6a33da9c6a9 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 17 May 2024 15:59:42 +0200 Subject: [PATCH 074/125] - fast-reload, change use_rpz with reload. --- daemon/remote.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 2da083387..6be6ac5f9 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3792,6 +3792,8 @@ struct fast_reload_construct { struct local_zones* local_zones; /** if there is response ip configuration in use */ int use_response_ip; + /** if there is an rpz zone */ + int use_rpz; /** storage for the old configuration elements. The outer struct * is allocated with malloc here, the items are from config. */ struct config_file* oldcfg; @@ -4257,9 +4259,8 @@ fr_construct_from_config(struct fast_reload_thread* fr, fr_construct_clear(ct); return 0; } - if(!auth_zones_apply_cfg(ct->auth_zones, newcfg, 1, - &fr->worker->daemon->use_rpz, fr->worker->daemon->env, - &fr->worker->daemon->mods)) { + if(!auth_zones_apply_cfg(ct->auth_zones, newcfg, 1, &ct->use_rpz, + fr->worker->daemon->env, &fr->worker->daemon->mods)) { fr_construct_clear(ct); return 0; } @@ -4799,6 +4800,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, local_zones_swap_tree(daemon->local_zones, ct->local_zones); respip_set_swap_tree(env->respip_set, ct->respip_set); daemon->use_response_ip = ct->use_response_ip; + daemon->use_rpz = ct->use_rpz; auth_zones_swap(env->auth_zones, ct->auth_zones); /* Set globals with new config. */ From 0ebd5455b3accf42cd351c903a58365c8a51855a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 17 May 2024 17:37:08 +0200 Subject: [PATCH 075/125] - fast-reload, list changes in auth zones and stop zonemd callbacks for deleted auth zones. --- daemon/remote.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++++ daemon/remote.h | 23 +++++++ services/mesh.c | 41 ++++++++++++ services/mesh.h | 13 ++++ 4 files changed, 240 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index 87c1d5e54..a9dcfe713 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4363,6 +4363,106 @@ ct_acl_interface_setup_ports(struct acl_list* acl_interface, return 1; } +/** fast reload, add new change to list of auth zones */ +static int +fr_add_auth_zone_change(struct fast_reload_thread* fr, struct auth_zone* old_z, + struct auth_zone* new_z, int is_deleted, int is_added, int is_changed) +{ + struct fast_reload_auth_change* item; + item = calloc(1, sizeof(*item)); + if(!item) { + log_err("malloc failure in add auth zone change"); + return 0; + } + item->old_z = old_z; + item->new_z = new_z; + item->is_deleted = is_deleted; + item->is_added = is_added; + item->is_changed = is_changed; + + item->next = fr->auth_zone_change_list; + fr->auth_zone_change_list = item; + return 1; +} + +/** Check what has changed in auth zones, like added and deleted zones */ +static int +auth_zones_check_changes(struct fast_reload_thread* fr, + struct fast_reload_construct* ct) +{ + /* Check every zone in turn. */ + struct auth_zone* new_z, *old_z; + struct module_env* env = &fr->worker->env; + + /* Nobody is using the new ct version yet. + * Also the ct lock is picked up before the env lock for auth_zones. */ + lock_rw_rdlock(&ct->auth_zones->lock); + + /* Find deleted zones by looping over the current list and looking + * up in the new tree. */ + lock_rw_rdlock(&env->auth_zones->lock); + RBTREE_FOR(old_z, struct auth_zone*, &env->auth_zones->ztree) { + new_z = auth_zone_find(ct->auth_zones, old_z->name, + old_z->namelen, old_z->dclass); + if(!new_z) { + /* The zone has been removed. */ + if(!fr_add_auth_zone_change(fr, old_z, NULL, 1, 0, + 0)) { + lock_rw_unlock(&env->auth_zones->lock); + lock_rw_unlock(&ct->auth_zones->lock); + return 0; + } + } + } + lock_rw_unlock(&env->auth_zones->lock); + + /* Find added zones by looping over new list and lookup in current. */ + RBTREE_FOR(new_z, struct auth_zone*, &ct->auth_zones->ztree) { + lock_rw_rdlock(&env->auth_zones->lock); + old_z = auth_zone_find(env->auth_zones, new_z->name, + new_z->namelen, new_z->dclass); + if(!old_z) { + /* The zone has been added. */ + lock_rw_unlock(&env->auth_zones->lock); + if(!fr_add_auth_zone_change(fr, NULL, new_z, 0, 1, + 0)) { + lock_rw_unlock(&ct->auth_zones->lock); + return 0; + } + } else { + uint32_t old_serial = 0, new_serial = 0; + int have_old = 0, have_new = 0; + lock_rw_rdlock(&new_z->lock); + lock_rw_rdlock(&old_z->lock); + lock_rw_unlock(&env->auth_zones->lock); + + /* Change in the auth zone can be detected. */ + /* A change in serial number means that auth_xfer + * has to be updated. */ + have_old = (auth_zone_get_serial(old_z, + &old_serial)!=0); + have_new = (auth_zone_get_serial(new_z, + &new_serial)!=0); + if(have_old != have_new || old_serial != new_serial) { + /* The zone has been changed. */ + if(!fr_add_auth_zone_change(fr, old_z, new_z, + 0, 0, 1)) { + lock_rw_unlock(&old_z->lock); + lock_rw_unlock(&new_z->lock); + lock_rw_unlock(&ct->auth_zones->lock); + return 0; + } + } + + lock_rw_unlock(&old_z->lock); + lock_rw_unlock(&new_z->lock); + } + } + + lock_rw_unlock(&ct->auth_zones->lock); + return 1; +} + /** fast reload thread, construct from config the new items */ static int fr_construct_from_config(struct fast_reload_thread* fr, @@ -4431,6 +4531,10 @@ fr_construct_from_config(struct fast_reload_thread* fr, fr_construct_clear(ct); return 0; } + if(!auth_zones_check_changes(fr, ct)) { + fr_construct_clear(ct); + return 0; + } if(fr_poll_for_quit(fr)) return 1; @@ -5556,6 +5660,20 @@ fast_reload_thread_setup(struct worker* worker, int fr_verb, int fr_nopause, return 1; } +/** fast reload, delete auth zone change list */ +static void +fr_auth_change_list_delete( + struct fast_reload_auth_change* auth_zone_change_list) +{ + struct fast_reload_auth_change* item, *next; + item = auth_zone_change_list; + while(item) { + next = item->next; + free(item); + item = next; + } +} + /** fast reload thread. desetup and delete the thread info. */ static void fast_reload_thread_desetup(struct fast_reload_thread* fast_reload_thread) @@ -5595,6 +5713,8 @@ fast_reload_thread_desetup(struct fast_reload_thread* fast_reload_thread) config_delstrlist(fast_reload_thread->fr_output->first); free(fast_reload_thread->fr_output); } + fr_auth_change_list_delete(fast_reload_thread->auth_zone_change_list); + free(fast_reload_thread); } @@ -5837,6 +5957,44 @@ tcl_remove_old(struct listen_dnsport* front) } } +/** Stop zonemd lookup */ +static void +auth_zone_zonemd_stop_lookup(struct auth_zone* z, struct mesh_area* mesh) +{ + struct query_info qinfo; + uint16_t qflags = BIT_RD; + qinfo.qname_len = z->namelen; + qinfo.qname = z->name; + qinfo.qclass = z->dclass; + qinfo.qtype = z->zonemd_callback_qtype; + qinfo.local_alias = NULL; + + mesh_remove_callback(mesh, &qinfo, qflags, + &auth_zonemd_dnskey_lookup_callback, z); +} + +/** Fast reload, the worker picks up changes in auth zones. */ +static void +fr_worker_pickup_auth_changes(struct worker* worker, + struct fast_reload_auth_change* auth_zone_change_list) +{ + struct fast_reload_auth_change* item; + for(item = auth_zone_change_list; item; item = item->next) { + if(item->is_deleted) { + lock_rw_wrlock(&item->old_z->lock); + if(item->old_z->zonemd_callback_env && + item->old_z->zonemd_callback_env->worker == worker){ + /* This worker was performing a zonemd lookup, + * stop the lookup and remove that entry. */ + auth_zone_zonemd_stop_lookup(item->old_z, + worker->env.mesh); + item->old_z->zonemd_callback_env = NULL; + } + lock_rw_unlock(&item->old_z->lock); + } + } +} + void fast_reload_worker_pickup_changes(struct worker* worker) { @@ -5856,6 +6014,11 @@ fast_reload_worker_pickup_changes(struct worker* worker) * need to remove their reference for the old tcp limits counters. */ if(worker->daemon->fast_reload_tcl_has_changes) tcl_remove_old(worker->front); + + /* If there are zonemd lookups, but the zone was deleted, the + * lookups should be cancelled. */ + fr_worker_pickup_auth_changes(worker, + worker->daemon->fast_reload_thread->auth_zone_change_list); } /** fast reload thread, handle reload_stop notification, send reload stop diff --git a/daemon/remote.h b/daemon/remote.h index ce26ea22f..c76da7eb0 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -175,6 +175,26 @@ struct fast_reload_printq { struct worker* worker; }; +/** + * Fast reload auth zone change. Keeps track if an auth zone was removed, + * added or changed. This is needed because workers can have events for + * dealing with auth zones, like transfers, and those have to be removed + * too, not just the auth zone structure from the tree. */ +struct fast_reload_auth_change { + /** next in the list of auth zone changes. */ + struct fast_reload_auth_change* next; + /** the zone in the old config */ + struct auth_zone* old_z; + /** the zone in the new config */ + struct auth_zone* new_z; + /** if the zone was deleted */ + int is_deleted; + /** if the zone was added */ + int is_added; + /** if the zone has been changed */ + int is_changed; +}; + /** * Fast reload thread structure */ @@ -224,6 +244,9 @@ struct fast_reload_thread { /** communication socket pair, to respond to the reload request */ int commreload[2]; + + /** the list of auth zone changes. */ + struct fast_reload_auth_change* auth_zone_change_list; }; /** diff --git a/services/mesh.c b/services/mesh.c index 07d23acf1..866169312 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1738,6 +1738,25 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh, return result; } +/** remove mesh state callback */ +int mesh_state_del_cb(struct mesh_state* s, mesh_cb_func_type cb, void* cb_arg) +{ + struct mesh_cb* r, *prev = NULL; + r = s->cb_list; + while(r) { + if(r->cb == cb && r->cb_arg == cb_arg) { + /* Delete this entry. */ + /* It was allocated in the s.region, so no free. */ + if(prev) prev->next = r->next; + else s->cb_list = r->next; + return 1; + } + prev = r; + r = r->next; + } + return 0; +} + int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns, sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg, uint16_t qid, uint16_t qflags) @@ -2417,3 +2436,25 @@ int mesh_jostle_exceeded(struct mesh_area* mesh) return 0; return 1; } + +void mesh_remove_callback(struct mesh_area* mesh, struct query_info* qinfo, + uint16_t qflags, mesh_cb_func_type cb, void* cb_arg) +{ + struct mesh_state* s = NULL; + s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + if(s) { + if(mesh_state_del_cb(s, cb, cb_arg)) { + /* It was in the list and removed. */ + log_assert(mesh->num_reply_addrs > 0); + mesh->num_reply_addrs--; + if(!s->reply_list && !s->cb_list) { + /* was a reply state, not anymore */ + log_assert(mesh->num_reply_states > 0); + mesh->num_reply_states--; + } + if(!s->reply_list && !s->cb_list && + s->super_set.count == 0) + mesh->num_detached_states++; + } + } +} diff --git a/services/mesh.h b/services/mesh.h index 5bd53e065..f0f043940 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -696,4 +696,17 @@ int mesh_jostle_exceeded(struct mesh_area* mesh); */ void mesh_respond_serve_expired(struct mesh_state* mstate); +/** + * Remove callback from mesh. Removes the callback from the state. + * The state itself is left to run. Searches for the pointer values. + * + * @param mesh: the mesh. + * @param qinfo: query from client. + * @param qflags: flags from client query. + * @param cb: callback function. + * @param cb_arg: callback user arg. + */ +void mesh_remove_callback(struct mesh_area* mesh, struct query_info* qinfo, + uint16_t qflags, mesh_cb_func_type cb, void* cb_arg); + #endif /* SERVICES_MESH_H */ From e3dd35d1830a3c88bfc67594cbfa9c744755ff2e Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 5 Jun 2024 13:19:04 +0200 Subject: [PATCH 076/125] - fast-reload, note xtree is not swapped, and why it is not swapped. --- daemon/remote.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index a9dcfe713..486c827fa 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4656,6 +4656,11 @@ auth_zones_swap(struct auth_zones* az, struct auth_zones* data) az->rpz_first = data->rpz_first; data->rpz_first = old_rpz_first; + + /* The xtree is not swapped. This contains the auth_xfer elements + * that contain tasks in progress, like zone transfers. + * The unchanged zones can keep their tasks in the tree, and thus + * the xfer elements can continue to be their callbacks. */ } #ifdef ATOMIC_POINTER_LOCK_FREE From 2cef002c0bb3c280a658329ed2ff0ed6395d87e0 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 5 Jun 2024 14:48:22 +0200 Subject: [PATCH 077/125] - fast-reload, for added auth zones, pick up zone transfer and zonemd tasks. --- daemon/remote.c | 19 ++++++++++++++ services/authzone.c | 60 +++++++++++++++++++++++++++++++++++++++------ services/authzone.h | 28 +++++++++++++++++++++ 3 files changed, 99 insertions(+), 8 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 486c827fa..b5610ae34 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5997,6 +5997,25 @@ fr_worker_pickup_auth_changes(struct worker* worker, } lock_rw_unlock(&item->old_z->lock); } + if(item->is_added) { + if(worker->thread_num == 0) { + struct auth_xfer* xfr = NULL; + /* Start zone transfers and lookups. */ + lock_rw_wrlock(&item->new_z->lock); + auth_zones_lock_xfr(worker->env.auth_zones, + &item->new_z, 0, &xfr, 0, 0); + auth_xfer_pickup_initial_zone(xfr, + &worker->env); + + /* Perform ZONEMD verification lookups. */ + lock_rw_wrlock(&item->new_z->lock); + /* holding only the new_z lock */ + auth_zone_verify_zonemd(item->new_z, + &worker->env, &worker->env.mesh->mods, + NULL, 0, 1); + lock_rw_unlock(&item->new_z->lock); + } + } } } diff --git a/services/authzone.c b/services/authzone.c index 73838d158..bd6aa552d 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -6987,6 +6987,18 @@ xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env, comm_timer_set(xfr->task_nextprobe->timer, &tv); } +void auth_xfer_pickup_initial_zone(struct auth_xfer* x, struct module_env* env) +{ + /* set lease_time, because we now have timestamp in env, + * (not earlier during startup and apply_cfg), and this + * notes the start time when the data was acquired */ + if(x->have_zone) + x->lease_time = *env->now; + if(x->task_nextprobe && x->task_nextprobe->worker == NULL) { + xfr_set_timeout(x, env, 0, 1); + } +} + /** initial pick up of worker timeouts, ties events to worker event loop */ void auth_xfer_pickup_initial(struct auth_zones* az, struct module_env* env) @@ -6995,14 +7007,7 @@ auth_xfer_pickup_initial(struct auth_zones* az, struct module_env* env) lock_rw_wrlock(&az->lock); RBTREE_FOR(x, struct auth_xfer*, &az->xtree) { lock_basic_lock(&x->lock); - /* set lease_time, because we now have timestamp in env, - * (not earlier during startup and apply_cfg), and this - * notes the start time when the data was acquired */ - if(x->have_zone) - x->lease_time = *env->now; - if(x->task_nextprobe && x->task_nextprobe->worker == NULL) { - xfr_set_timeout(x, env, 0, 1); - } + auth_xfer_pickup_initial_zone(x, env); lock_basic_unlock(&x->lock); } lock_rw_unlock(&az->lock); @@ -8688,3 +8693,42 @@ size_t auth_zones_get_mem(struct auth_zones* zones) lock_rw_unlock(&zones->rpz_lock); return m; } + +void auth_zones_lock_xfr(struct auth_zones* az, struct auth_zone** z, int zwr, + struct auth_xfer** x, int azlock_start, int azlock_return) +{ + uint8_t nm[LDNS_MAX_DOMAINLEN+1]; + size_t nmlen; + uint16_t dclass; + + /* Store name */ + if((*z)->namelen > sizeof(nm)) { + /* This should never happen, the buffer is large enough. */ + *x = NULL; + return; + } + nmlen = (*z)->namelen; + dclass = (*z)->dclass; + memmove(nm, (*z)->name, nmlen); + + /* Look it up again */ + lock_rw_unlock(&(*z)->lock); + if(!azlock_start) { + lock_rw_rdlock(&az->lock); + } + *z = auth_zone_find(az, nm, nmlen, dclass); + *x = auth_xfer_find(az, nm, nmlen, dclass); + if(*z) { + if(zwr == 0) { + lock_rw_rdlock(&(*z)->lock); + } else if(zwr == 1) { + lock_rw_wrlock(&(*z)->lock); + } + } + if(*x) { + lock_basic_lock(&(*x)->lock); + } + if(!azlock_return) { + lock_rw_unlock(&az->lock); + } +} diff --git a/services/authzone.h b/services/authzone.h index f9e89c4dc..4abdd36eb 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -792,4 +792,32 @@ void auth_zones_pickup_zonemd_verify(struct auth_zones* az, * for reading. */ size_t auth_zones_get_mem(struct auth_zones* zones); +/** + * Pick up the xfr item for an auth zone. The routine assumes the + * zone object is there and is locked for reading. It unlocks and looks + * up new z, x pointers. + * @param az: auth zones. + * @param z: the zone with lookup name information. On start it is locked + * by the caller. + * On return it is changed to the new zone, if it is new, + * and with a lock if requested. + * @param zwr: is a writelock(1) or readlock(0) on z requested, 2 no lock. + * @param x: the xfr item for the zone. + * If it is found, it is locked on return. + * @param azlock_start: if true, the auth zones are locked, by caller, + * when the function is called. + * @param azlock_return: if true, the auth zones are locked on return. + */ +void auth_zones_lock_xfr(struct auth_zones* az, struct auth_zone** z, int zwr, + struct auth_xfer** x, int azlock_start, int azlock_return); + +/** + * Initial pick up of the auth zone nextprobe timeout and that turns + * into further zone transfer work, if any. Also sets the lease time. + * @param x: xfer structure, locked by caller. + * @param env: environment of the worker that picks up the task. + */ +void auth_xfer_pickup_initial_zone(struct auth_xfer* x, + struct module_env* env); + #endif /* SERVICES_AUTHZONE_H */ From ecdb5ba2d8379c33d927c126ec0991535f03b0f6 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 5 Jun 2024 14:52:27 +0200 Subject: [PATCH 078/125] - fast-reload, unlock xfr when done with transfer pick up. --- daemon/remote.c | 1 + 1 file changed, 1 insertion(+) diff --git a/daemon/remote.c b/daemon/remote.c index b5610ae34..ede653544 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6006,6 +6006,7 @@ fr_worker_pickup_auth_changes(struct worker* worker, &item->new_z, 0, &xfr, 0, 0); auth_xfer_pickup_initial_zone(xfr, &worker->env); + lock_basic_unlock(&xfr->lock); /* Perform ZONEMD verification lookups. */ lock_rw_wrlock(&item->new_z->lock); From b99f8dd9aa55b1cb66f43932182015485e35baf2 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 5 Jun 2024 14:54:18 +0200 Subject: [PATCH 079/125] - fast-reload, unlock z when picking up the xfr for it during transfer task pick up. --- daemon/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index ede653544..12a777d29 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6003,7 +6003,7 @@ fr_worker_pickup_auth_changes(struct worker* worker, /* Start zone transfers and lookups. */ lock_rw_wrlock(&item->new_z->lock); auth_zones_lock_xfr(worker->env.auth_zones, - &item->new_z, 0, &xfr, 0, 0); + &item->new_z, 2, &xfr, 0, 0); auth_xfer_pickup_initial_zone(xfr, &worker->env); lock_basic_unlock(&xfr->lock); From 00aa03d66a7a10294b1a680d79faa1dab7aa117b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 6 Jun 2024 13:40:09 +0200 Subject: [PATCH 080/125] - fast-reload, pick up task changes for added, deleted and modified auth zones. --- daemon/remote.c | 136 +++++++++++++++++++++++++++++++++++--------- services/authzone.c | 16 +++++- services/authzone.h | 16 ++++++ 3 files changed, 139 insertions(+), 29 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 12a777d29..591f177eb 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5978,6 +5978,111 @@ auth_zone_zonemd_stop_lookup(struct auth_zone* z, struct mesh_area* mesh) &auth_zonemd_dnskey_lookup_callback, z); } +/** Fast reload, worker picks up deleted auth zone */ +static void +fr_worker_auth_del(struct worker* worker, struct fast_reload_auth_change* item, + int for_change) +{ + int released = 0; /* Did this routine release callbacks. */ + struct auth_xfer* xfr = NULL; + + lock_rw_wrlock(&item->old_z->lock); + if(item->old_z->zonemd_callback_env && + item->old_z->zonemd_callback_env->worker == worker){ + /* This worker was performing a zonemd lookup, + * stop the lookup and remove that entry. */ + auth_zone_zonemd_stop_lookup(item->old_z, worker->env.mesh); + item->old_z->zonemd_callback_env = NULL; + } + lock_rw_unlock(&item->old_z->lock); + + lock_rw_rdlock(&item->old_z->lock); + auth_zones_lock_xfr(worker->env.auth_zones, &item->old_z, 2, &xfr, + 0, 0); + if(xfr) { + /* Release callbacks on the xfr, if this worker holds them. */ + if(xfr->task_nextprobe->worker == worker || + xfr->task_probe->worker == worker || + xfr->task_transfer->worker == worker) { + released = 1; + xfr_disown_tasks(xfr, worker); + } + lock_basic_unlock(&xfr->lock); + } + + if(!for_change && released) { + /* See if the xfr item can be deleted. */ + xfr = NULL; + lock_rw_rdlock(&item->old_z->lock); + auth_zones_lock_xfr(worker->env.auth_zones, &item->old_z, 2, + &xfr, 0, 1); + if(xfr && xfr->task_nextprobe->worker == NULL && + xfr->task_probe->worker == NULL && + xfr->task_transfer->worker == NULL) { + (void)rbtree_delete(&worker->env.auth_zones->xtree, + &xfr->node); + lock_rw_unlock(&worker->env.auth_zones->lock); + lock_basic_unlock(&xfr->lock); + auth_xfer_delete(xfr); + } else { + lock_rw_unlock(&worker->env.auth_zones->lock); + if(xfr) { + lock_basic_unlock(&xfr->lock); + } + } + } +} + +/** Fast reload, worker picks up added auth zone */ +static void +fr_worker_auth_add(struct worker* worker, struct fast_reload_auth_change* item, + int for_change) +{ + struct auth_xfer* xfr = NULL; + + /* Start zone transfers and lookups. */ + lock_rw_wrlock(&item->new_z->lock); + auth_zones_lock_xfr(worker->env.auth_zones, &item->new_z, 0, &xfr, + 0, 1); + if(xfr == NULL) { + /* The xfr item needs to be created. The auth zones lock + * is held to make this possible. */ + xfr = auth_xfer_create(worker->env.auth_zones, item->new_z); + /* Serial information is copied into the xfr struct. */ + if(!xfr_find_soa(item->new_z, xfr)) { + xfr->serial = 0; + } + } else if(for_change) { + if(!xfr_find_soa(item->new_z, xfr)) { + xfr->serial = 0; + } + } + lock_rw_unlock(&item->new_z->lock); + lock_rw_unlock(&worker->env.auth_zones->lock); + auth_xfer_pickup_initial_zone(xfr, &worker->env); + lock_basic_unlock(&xfr->lock); + + /* Perform ZONEMD verification lookups. */ + lock_rw_wrlock(&item->new_z->lock); + /* holding only the new_z lock */ + auth_zone_verify_zonemd(item->new_z, &worker->env, + &worker->env.mesh->mods, NULL, 0, 1); + lock_rw_unlock(&item->new_z->lock); +} + +/** Fast reload, worker picks up changed auth zone */ +static void +fr_worker_auth_cha(struct worker* worker, struct fast_reload_auth_change* item) +{ + /* Since the zone has been changed, by rereading it from zone file, + * existing transfers and probes are likely for the old version. + * Stop them, and start new ones if needed. */ + fr_worker_auth_del(worker, item, 1); + if(worker->thread_num == 0) { + fr_worker_auth_add(worker, item, 1); + } +} + /** Fast reload, the worker picks up changes in auth zones. */ static void fr_worker_pickup_auth_changes(struct worker* worker, @@ -5986,37 +6091,16 @@ fr_worker_pickup_auth_changes(struct worker* worker, struct fast_reload_auth_change* item; for(item = auth_zone_change_list; item; item = item->next) { if(item->is_deleted) { - lock_rw_wrlock(&item->old_z->lock); - if(item->old_z->zonemd_callback_env && - item->old_z->zonemd_callback_env->worker == worker){ - /* This worker was performing a zonemd lookup, - * stop the lookup and remove that entry. */ - auth_zone_zonemd_stop_lookup(item->old_z, - worker->env.mesh); - item->old_z->zonemd_callback_env = NULL; - } - lock_rw_unlock(&item->old_z->lock); + fr_worker_auth_del(worker, item, 0); } if(item->is_added) { if(worker->thread_num == 0) { - struct auth_xfer* xfr = NULL; - /* Start zone transfers and lookups. */ - lock_rw_wrlock(&item->new_z->lock); - auth_zones_lock_xfr(worker->env.auth_zones, - &item->new_z, 2, &xfr, 0, 0); - auth_xfer_pickup_initial_zone(xfr, - &worker->env); - lock_basic_unlock(&xfr->lock); - - /* Perform ZONEMD verification lookups. */ - lock_rw_wrlock(&item->new_z->lock); - /* holding only the new_z lock */ - auth_zone_verify_zonemd(item->new_z, - &worker->env, &worker->env.mesh->mods, - NULL, 0, 1); - lock_rw_unlock(&item->new_z->lock); + fr_worker_auth_add(worker, item, 0); } } + if(item->is_changed) { + fr_worker_auth_cha(worker, item); + } } } diff --git a/services/authzone.c b/services/authzone.c index bd6aa552d..ddeb6e8c3 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -2317,9 +2317,6 @@ auth_free_masters(struct auth_master* list) } } -/** delete auth xfer structure - * @param xfr: delete this xfer and its tasks. - */ void auth_xfer_delete(struct auth_xfer* xfr) { @@ -8732,3 +8729,16 @@ void auth_zones_lock_xfr(struct auth_zones* az, struct auth_zone** z, int zwr, lock_rw_unlock(&az->lock); } } + +void xfr_disown_tasks(struct auth_xfer* xfr, struct worker* worker) +{ + if(xfr->task_nextprobe->worker == worker) { + xfr_nextprobe_disown(xfr); + } + if(xfr->task_probe->worker == worker) { + xfr_probe_disown(xfr); + } + if(xfr->task_transfer->worker == worker) { + xfr_transfer_disown(xfr); + } +} diff --git a/services/authzone.h b/services/authzone.h index 4abdd36eb..5f630e9e9 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -820,4 +820,20 @@ void auth_zones_lock_xfr(struct auth_zones* az, struct auth_zone** z, int zwr, void auth_xfer_pickup_initial_zone(struct auth_xfer* x, struct module_env* env); +/** + * Delete auth xfer structure + * @param xfr: delete this xfer and its tasks. + */ +void auth_xfer_delete(struct auth_xfer* xfr); + +/** + * Disown tasks from the xfr that belong to this worker. + * Only tasks for the worker in question, the comm point and timer + * delete functions need to run in the thread of that worker to be + * able to delete the callback from the event base. + * @param xfr: xfr structure + * @param worker: the worker for which to stop tasks. + */ +void xfr_disown_tasks(struct auth_xfer* xfr, struct worker* worker); + #endif /* SERVICES_AUTHZONE_H */ From e6b46e9fbfbdc81e2370657b18bea31f4b07de85 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 6 Jun 2024 13:45:40 +0200 Subject: [PATCH 081/125] - fast-reload, remove xfr of auth zone deletion without tasks. --- daemon/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 591f177eb..c2a4bfb53 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6010,7 +6010,7 @@ fr_worker_auth_del(struct worker* worker, struct fast_reload_auth_change* item, lock_basic_unlock(&xfr->lock); } - if(!for_change && released) { + if(!for_change && (released || worker->thread_num == 0)) { /* See if the xfr item can be deleted. */ xfr = NULL; lock_rw_rdlock(&item->old_z->lock); From 07b00691e97cd61fb42af34cad22889a73bdfafd Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 11 Jun 2024 13:40:37 +0200 Subject: [PATCH 082/125] - fast-reload, pick up zone transfer config. --- daemon/remote.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++--- daemon/remote.h | 2 ++ 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index c2a4bfb53..06a911aa5 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4394,6 +4394,7 @@ auth_zones_check_changes(struct fast_reload_thread* fr, struct auth_zone* new_z, *old_z; struct module_env* env = &fr->worker->env; + fr->old_auth_zones = ct->auth_zones; /* Nobody is using the new ct version yet. * Also the ct lock is picked up before the env lock for auth_zones. */ lock_rw_rdlock(&ct->auth_zones->lock); @@ -6044,7 +6045,7 @@ fr_worker_auth_add(struct worker* worker, struct fast_reload_auth_change* item, lock_rw_wrlock(&item->new_z->lock); auth_zones_lock_xfr(worker->env.auth_zones, &item->new_z, 0, &xfr, 0, 1); - if(xfr == NULL) { + if(xfr == NULL && item->new_z->zone_is_slave) { /* The xfr item needs to be created. The auth zones lock * is held to make this possible. */ xfr = auth_xfer_create(worker->env.auth_zones, item->new_z); @@ -6052,15 +6053,17 @@ fr_worker_auth_add(struct worker* worker, struct fast_reload_auth_change* item, if(!xfr_find_soa(item->new_z, xfr)) { xfr->serial = 0; } - } else if(for_change) { + } else if(for_change && xfr) { if(!xfr_find_soa(item->new_z, xfr)) { xfr->serial = 0; } } lock_rw_unlock(&item->new_z->lock); lock_rw_unlock(&worker->env.auth_zones->lock); - auth_xfer_pickup_initial_zone(xfr, &worker->env); - lock_basic_unlock(&xfr->lock); + if(xfr) { + auth_xfer_pickup_initial_zone(xfr, &worker->env); + lock_basic_unlock(&xfr->lock); + } /* Perform ZONEMD verification lookups. */ lock_rw_wrlock(&item->new_z->lock); @@ -6070,14 +6073,81 @@ fr_worker_auth_add(struct worker* worker, struct fast_reload_auth_change* item, lock_rw_unlock(&item->new_z->lock); } +/** Fast reload, auth xfer config is picked up */ +static void +auth_xfr_pickup_config(struct auth_xfer* loadxfr, struct auth_xfer* xfr) +{ + struct auth_master *probe_masters, *transfer_masters; + log_assert(loadxfr->namelen == xfr->namelen); + log_assert(loadxfr->namelabs == xfr->namelabs); + log_assert(loadxfr->dclass == xfr->dclass); + + /* The lists can be swapped in, the other xfr struct will be deleted + * afterwards. */ + probe_masters = xfr->task_probe->masters; + transfer_masters = xfr->task_transfer->masters; + xfr->task_probe->masters = loadxfr->task_probe->masters; + xfr->task_transfer->masters = loadxfr->task_transfer->masters; + loadxfr->task_probe->masters = probe_masters; + loadxfr->task_transfer->masters = transfer_masters; +} + /** Fast reload, worker picks up changed auth zone */ static void fr_worker_auth_cha(struct worker* worker, struct fast_reload_auth_change* item) { + int todelete = 0; + struct auth_xfer* loadxfr = NULL, *xfr = NULL; /* Since the zone has been changed, by rereading it from zone file, * existing transfers and probes are likely for the old version. * Stop them, and start new ones if needed. */ fr_worker_auth_del(worker, item, 1); + + /* The old callbacks are stopped, tasks have been disowned. The + * new config contents can be picked up. SOA information is picked + * up in the auth_add routine, as it has the new_z ready. */ + + lock_rw_rdlock(&worker->daemon->fast_reload_thread->old_auth_zones->lock); + lock_rw_wrlock(&worker->env.auth_zones->lock); + + lock_rw_rdlock(&item->new_z->lock); + lock_rw_rdlock(&item->old_z->lock); + auth_zones_lock_xfr(worker->env.auth_zones, &item->new_z, 2, &xfr, + 1, 1); + auth_zones_lock_xfr(worker->daemon->fast_reload_thread->old_auth_zones, + &item->old_z, 2, &loadxfr, 1, 1); + + /* The xfr is not there any more if the zone is not set to have + * zone transfers. Or the xfr needs to be created if it is set to + * have zone transfers. */ + if(loadxfr && xfr) { + /* Copy the config from loadxfr to the xfr in current use. */ + auth_xfr_pickup_config(loadxfr, xfr); + } else if(!loadxfr && xfr) { + /* Delete the xfr. */ + (void)rbtree_delete(&worker->env.auth_zones->xtree, + &xfr->node); + todelete = 1; + item->new_z->zone_is_slave = 0; + } else if(loadxfr && !xfr) { + /* Create the xfr. */ + xfr = auth_xfer_create(worker->env.auth_zones, item->new_z); + item->new_z->zone_is_slave = 1; + } + lock_rw_unlock(&item->new_z->lock); + lock_rw_unlock(&item->old_z->lock); + lock_rw_unlock(&worker->daemon->fast_reload_thread->old_auth_zones->lock); + lock_rw_unlock(&worker->env.auth_zones->lock); + if(loadxfr) { + lock_basic_unlock(&loadxfr->lock); + } + if(xfr) { + lock_basic_unlock(&xfr->lock); + } + if(todelete) { + auth_xfer_delete(xfr); + } + if(worker->thread_num == 0) { fr_worker_auth_add(worker, item, 1); } diff --git a/daemon/remote.h b/daemon/remote.h index c76da7eb0..3a063076c 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -247,6 +247,8 @@ struct fast_reload_thread { /** the list of auth zone changes. */ struct fast_reload_auth_change* auth_zone_change_list; + /** the old tree of auth zones, to lookup. */ + struct auth_zones* old_auth_zones; }; /** From 1c80abd6b1d8832ac0c39f454578ab2f426efa02 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 11 Jun 2024 13:51:23 +0200 Subject: [PATCH 083/125] - fast-reload, the main worker thread picks up the transfer tasks and also performs setup of the xfer struct. --- daemon/remote.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 06a911aa5..061044ca8 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6103,6 +6103,9 @@ fr_worker_auth_cha(struct worker* worker, struct fast_reload_auth_change* item) * Stop them, and start new ones if needed. */ fr_worker_auth_del(worker, item, 1); + if(worker->thread_num != 0) + return; + /* The old callbacks are stopped, tasks have been disowned. The * new config contents can be picked up. SOA information is picked * up in the auth_add routine, as it has the new_z ready. */ @@ -6148,9 +6151,7 @@ fr_worker_auth_cha(struct worker* worker, struct fast_reload_auth_change* item) auth_xfer_delete(xfr); } - if(worker->thread_num == 0) { - fr_worker_auth_add(worker, item, 1); - } + fr_worker_auth_add(worker, item, 1); } /** Fast reload, the worker picks up changes in auth zones. */ From e209272cb94febfbb5a39d915441e65fc3a64f7f Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 11 Jun 2024 16:50:52 +0200 Subject: [PATCH 084/125] - fast-reload, keep writelock on newzone when auth zone changes. --- daemon/remote.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 061044ca8..65cde3fdc 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6115,10 +6115,10 @@ fr_worker_auth_cha(struct worker* worker, struct fast_reload_auth_change* item) lock_rw_rdlock(&item->new_z->lock); lock_rw_rdlock(&item->old_z->lock); - auth_zones_lock_xfr(worker->env.auth_zones, &item->new_z, 2, &xfr, + auth_zones_lock_xfr(worker->env.auth_zones, &item->new_z, 1, &xfr, 1, 1); auth_zones_lock_xfr(worker->daemon->fast_reload_thread->old_auth_zones, - &item->old_z, 2, &loadxfr, 1, 1); + &item->old_z, 1, &loadxfr, 1, 1); /* The xfr is not there any more if the zone is not set to have * zone transfers. Or the xfr needs to be created if it is set to From 89a1961cc0d7f0deea68c97888eb9561b8e44190 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 19 Jun 2024 14:45:45 +0200 Subject: [PATCH 085/125] - fast-reload, change cachedb_enabled setting. --- daemon/remote.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index 65cde3fdc..83704a89a 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5079,6 +5079,10 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, daemon->use_response_ip = ct->use_response_ip; daemon->use_rpz = ct->use_rpz; auth_zones_swap(env->auth_zones, ct->auth_zones); +#ifdef USE_CACHEDB + daemon->env->cachedb_enabled = cachedb_is_enabled(&daemon->mods, + daemon->env); +#endif /* Set globals with new config. */ config_apply(env->cfg); @@ -6199,6 +6203,9 @@ fast_reload_worker_pickup_changes(struct worker* worker) * lookups should be cancelled. */ fr_worker_pickup_auth_changes(worker, worker->daemon->fast_reload_thread->auth_zone_change_list); +#ifdef USE_CACHEDB + worker->env.cachedb_enabled = worker->daemon->env->cachedb_enabled; +#endif } /** fast reload thread, handle reload_stop notification, send reload stop From ea595e8a1dde190b0e0cb6ec5c4a2f5dd3b23c99 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 19 Jun 2024 15:07:50 +0200 Subject: [PATCH 086/125] - fast-reload, pick up edns-strings config. --- Makefile.in | 2 +- daemon/remote.c | 17 +++++++++++++++++ util/edns.c | 23 +++++++++++++++++++++++ util/edns.h | 16 ++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index aa66f0240..f0f39ebe9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1297,7 +1297,7 @@ remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h $(srcdir)/validator/val_anchor.h $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h \ $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_delegpt.h \ $(srcdir)/services/outside_network.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h \ - $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h $(srcdir)/util/ub_event.h $(srcdir)/util/tcp_conn_limit.h + $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h $(srcdir)/util/ub_event.h $(srcdir)/util/tcp_conn_limit.h $(srcdir)/util/edns.h stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \ $(srcdir)/libunbound/unbound.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \ $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \ diff --git a/daemon/remote.c b/daemon/remote.c index 83704a89a..6bda0a5ab 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -93,6 +93,7 @@ #include "sldns/sbuffer.h" #include "util/timeval_func.h" #include "util/tcp_conn_limit.h" +#include "util/edns.h" #ifdef USE_CACHEDB #include "cachedb/cachedb.h" #endif @@ -3961,6 +3962,8 @@ struct fast_reload_construct { int use_response_ip; /** if there is an rpz zone */ int use_rpz; + /** construct for edns strings */ + struct edns_strings* edns_strings; /** storage for the old configuration elements. The outer struct * is allocated with malloc here, the items are from config. */ struct config_file* oldcfg; @@ -4077,6 +4080,7 @@ fr_construct_clear(struct fast_reload_construct* ct) acl_list_delete(ct->acl); acl_list_delete(ct->acl_interface); tcl_list_delete(ct->tcl); + edns_strings_delete(ct->edns_strings); views_delete(ct->views); /* Delete the log identity here so that the global value is not * reset by config_delete. */ @@ -4332,6 +4336,7 @@ fr_printmem(struct fast_reload_thread* fr, mem += acl_list_get_mem(ct->acl); mem += acl_list_get_mem(ct->acl_interface); mem += tcl_list_get_mem(ct->tcl); + mem += edns_strings_get_mem(ct->edns_strings); mem += sizeof(*ct->oldcfg); mem += config_file_getmem(newcfg); @@ -4591,6 +4596,17 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; + if(!(ct->edns_strings = edns_strings_create())) { + fr_construct_clear(ct); + return 0; + } + if(!edns_strings_apply_cfg(ct->edns_strings, newcfg)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + if(!(ct->oldcfg = (struct config_file*)calloc(1, sizeof(*ct->oldcfg)))) { fr_construct_clear(ct); @@ -5079,6 +5095,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, daemon->use_response_ip = ct->use_response_ip; daemon->use_rpz = ct->use_rpz; auth_zones_swap(env->auth_zones, ct->auth_zones); + edns_strings_swap_tree(env->edns_strings, ct->edns_strings); #ifdef USE_CACHEDB daemon->env->cachedb_enabled = cachedb_is_enabled(&daemon->mods, daemon->env); diff --git a/util/edns.c b/util/edns.c index 2b4047f0b..b2dae16f1 100644 --- a/util/edns.c +++ b/util/edns.c @@ -131,6 +131,29 @@ edns_string_addr_lookup(rbtree_type* tree, struct sockaddr_storage* addr, return (struct edns_string_addr*)addr_tree_lookup(tree, addr, addrlen); } +size_t +edns_strings_get_mem(struct edns_strings* edns_strings) +{ + if(!edns_strings) return 0; + return regional_get_mem(edns_strings->region) + sizeof(*edns_strings); +} + +void +edns_strings_swap_tree(struct edns_strings* edns_strings, + struct edns_strings* data) +{ + rbtree_type tree = edns_strings->client_strings; + uint16_t opcode = edns_strings->client_string_opcode; + struct regional* region = edns_strings->region; + + edns_strings->client_strings = data->client_strings; + edns_strings->client_string_opcode = data->client_string_opcode; + edns_strings->region = data->region; + data->client_strings = tree; + data->client_string_opcode = opcode; + data->region = region; +} + uint8_t* edns_cookie_server_hash(const uint8_t* in, const uint8_t* secret, int v4, uint8_t* hash) diff --git a/util/edns.h b/util/edns.h index 5da0ecb29..aeda47840 100644 --- a/util/edns.h +++ b/util/edns.h @@ -115,6 +115,22 @@ struct edns_string_addr* edns_string_addr_lookup(rbtree_type* tree, struct sockaddr_storage* addr, socklen_t addrlen); +/** + * Get memory usage of edns strings. + * @param edns_strings: the edns strings + * @return memory usage + */ +size_t edns_strings_get_mem(struct edns_strings* edns_strings); + +/** + * Swap internal tree with preallocated entries. + * @param edns_strings: the edns strings structure. + * @param data: the data structure used to take elements from. This contains + * the old elements on return. + */ +void edns_strings_swap_tree(struct edns_strings* edns_strings, + struct edns_strings* data); + /** * Compute the interoperable DNS cookie (RFC9018) hash. * @param in: buffer input for the hash generation. It needs to be: From 23436f51b360871be09a779cbfd00429d56a8b2a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 19 Jun 2024 15:25:45 +0200 Subject: [PATCH 087/125] - fast-reload, note that settings are not updated. --- doc/unbound-control.8.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 4e9e33b51..ec3b5a7ac 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -71,7 +71,8 @@ Not all options are changed, but it changes like forwards, stubs and local zones. Also access-control and interface-action and similar options, also tcp-connection-limits, views. It can reload some define-tag changes. It does not work with interface, outgoing-interface changes, also not with -remote-control changes. +remote-control, outgoing-port-permit, outgoing-port-avoid, msg-buffer-size +and statistics-interval changes. .IP The '+v' option makes the output verbose. With '+vv' it is more verbose. That includes the time it took to do the reload. And with more verbose output From 9946473752df282cf8c277e5e6abcb26ed86b436 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 19 Jun 2024 15:37:35 +0200 Subject: [PATCH 088/125] - fast-reload, pick up dnstap config. --- daemon/remote.c | 5 +++++ dnstap/dnstap.c | 10 ++++++++-- doc/unbound-control.8.in | 10 ++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 6bda0a5ab..2bd518235 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5100,6 +5100,11 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, daemon->env->cachedb_enabled = cachedb_is_enabled(&daemon->mods, daemon->env); #endif +#ifdef USE_DNSTAP + if(env->cfg->dnstap) { + dt_apply_cfg(daemon->dtenv, env->cfg); + } +#endif /* Set globals with new config. */ config_apply(env->cfg); diff --git a/dnstap/dnstap.c b/dnstap/dnstap.c index 5cdda0c2d..9872bc7fb 100644 --- a/dnstap/dnstap.c +++ b/dnstap/dnstap.c @@ -166,8 +166,11 @@ static void dt_apply_identity(struct dt_env *env, struct config_file *cfg) { char buf[MAXHOSTNAMELEN+1]; - if (!cfg->dnstap_send_identity) + if (!cfg->dnstap_send_identity) { + free(env->identity); + env->identity = NULL; return; + } free(env->identity); if (cfg->dnstap_identity == NULL || cfg->dnstap_identity[0] == 0) { if (gethostname(buf, MAXHOSTNAMELEN) == 0) { @@ -189,8 +192,11 @@ dt_apply_identity(struct dt_env *env, struct config_file *cfg) static void dt_apply_version(struct dt_env *env, struct config_file *cfg) { - if (!cfg->dnstap_send_version) + if (!cfg->dnstap_send_version) { + free(env->version); + env->version = NULL; return; + } free(env->version); if (cfg->dnstap_version == NULL || cfg->dnstap_version[0] == 0) env->version = strdup(PACKAGE_STRING); diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index ec3b5a7ac..aad01f0f2 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -74,6 +74,16 @@ It does not work with interface, outgoing-interface changes, also not with remote-control, outgoing-port-permit, outgoing-port-avoid, msg-buffer-size and statistics-interval changes. .IP +For dnstap, the options can be changed: dnstap-send-identity, +dnstap-send-version, dnstap-identity, dnstap-version, +dnstap-log-resolver-query-messages, dnstap-log-resolver-response-messages, +dnstap-log-client-query-messages, dnstap-log-client-response-messages, +dnstap-log-forwarder-query-messages and dnstap-log-forwarder-response-messages. +It does not work with these options: dnstap-enable, dnstap-bidirectional, +dnstap-socket-path, dnstap-ip, dnstap-tls, dnstap-tls-server-name, +dnstap-tls-cert-bundle, dnstap-tls-client-key-file and +dnstap-tls-client-cert-file. +.IP The '+v' option makes the output verbose. With '+vv' it is more verbose. That includes the time it took to do the reload. And with more verbose output the amount of memory that was allocated temporarily to perform the reload, From 2d419cf6db6bf5089dbc462b98c671756c5467c6 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 19 Jun 2024 16:03:06 +0200 Subject: [PATCH 089/125] - fast-reload, dnstap options that need to be loaded without +p. --- daemon/remote.c | 4 +++- dnstap/dnstap.c | 18 ++++++++++++------ dnstap/dnstap.h | 7 +++++++ doc/unbound-control.8.in | 18 +++++++++--------- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 2bd518235..62be86165 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5102,7 +5102,9 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, #endif #ifdef USE_DNSTAP if(env->cfg->dnstap) { - dt_apply_cfg(daemon->dtenv, env->cfg); + if(!fr->fr_nopause) + dt_apply_cfg(daemon->dtenv, env->cfg); + else dt_apply_logcfg(daemon->dtenv, env->cfg); } #endif diff --git a/dnstap/dnstap.c b/dnstap/dnstap.c index 9872bc7fb..e79dc46fa 100644 --- a/dnstap/dnstap.c +++ b/dnstap/dnstap.c @@ -210,13 +210,8 @@ dt_apply_version(struct dt_env *env, struct config_file *cfg) } void -dt_apply_cfg(struct dt_env *env, struct config_file *cfg) +dt_apply_logcfg(struct dt_env *env, struct config_file *cfg) { - if (!cfg->dnstap) - return; - - dt_apply_identity(env, cfg); - dt_apply_version(env, cfg); if ((env->log_resolver_query_messages = (unsigned int) cfg->dnstap_log_resolver_query_messages)) { @@ -249,6 +244,17 @@ dt_apply_cfg(struct dt_env *env, struct config_file *cfg) } } +void +dt_apply_cfg(struct dt_env *env, struct config_file *cfg) +{ + if (!cfg->dnstap) + return; + + dt_apply_identity(env, cfg); + dt_apply_version(env, cfg); + dt_apply_logcfg(env, cfg); +} + int dt_init(struct dt_env *env, struct comm_base* base) { diff --git a/dnstap/dnstap.h b/dnstap/dnstap.h index 77914c20c..e868fe088 100644 --- a/dnstap/dnstap.h +++ b/dnstap/dnstap.h @@ -98,6 +98,13 @@ dt_create(struct config_file* cfg); void dt_apply_cfg(struct dt_env *env, struct config_file *cfg); +/** + * Apply config settings for log enable for message types. + * @param env: dnstap environment object. + * @param cfg: new config settings. + */ +void dt_apply_logcfg(struct dt_env *env, struct config_file *cfg); + /** * Initialize per-worker state in dnstap environment object. * @param env: dnstap environment object to initialize, created with dt_create(). diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index aad01f0f2..cb617b288 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -74,15 +74,15 @@ It does not work with interface, outgoing-interface changes, also not with remote-control, outgoing-port-permit, outgoing-port-avoid, msg-buffer-size and statistics-interval changes. .IP -For dnstap, the options can be changed: dnstap-send-identity, -dnstap-send-version, dnstap-identity, dnstap-version, -dnstap-log-resolver-query-messages, dnstap-log-resolver-response-messages, -dnstap-log-client-query-messages, dnstap-log-client-response-messages, -dnstap-log-forwarder-query-messages and dnstap-log-forwarder-response-messages. -It does not work with these options: dnstap-enable, dnstap-bidirectional, -dnstap-socket-path, dnstap-ip, dnstap-tls, dnstap-tls-server-name, -dnstap-tls-cert-bundle, dnstap-tls-client-key-file and -dnstap-tls-client-cert-file. +For dnstap, the options can be changed: dnstap-log-resolver-query-messages, +dnstap-log-resolver-response-messages, dnstap-log-client-query-messages, +dnstap-log-client-response-messages, dnstap-log-forwarder-query-messages +and dnstap-log-forwarder-response-messages. It does not work with +these options: dnstap-enable, dnstap-bidirectional, dnstap-socket-path, +dnstap-ip, dnstap-tls, dnstap-tls-server-name, dnstap-tls-cert-bundle, +dnstap-tls-client-key-file and dnstap-tls-client-cert-file. The options +dnstap-send-identity, dnstap-send-version, dnstap-identity, and +dnstap-version can be loaded when '+p' is not used. .IP The '+v' option makes the output verbose. With '+vv' it is more verbose. That includes the time it took to do the reload. And with more verbose output From 70b9949dd81b4300c8073847d61f85ab21448dfe Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 21 Jun 2024 10:26:10 +0200 Subject: [PATCH 090/125] - fast-reload, fix auth zone reload --- daemon/remote.c | 18 ++++++- daemon/remote.h | 2 +- daemon/worker.c | 1 + .../fast_reload_fwd.tdir/fast_reload_fwd.conf | 12 +++++ .../fast_reload_fwd.conf2 | 15 +++++- .../fast_reload_fwd.tdir/fast_reload_fwd.pre | 2 +- .../fast_reload_fwd.tdir/fast_reload_fwd.test | 51 +++++++++++++++++++ 7 files changed, 96 insertions(+), 5 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 62be86165..2c02947ed 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5205,6 +5205,7 @@ fr_reload_ipc(struct fast_reload_thread* fr, struct config_file* newcfg, } if(!fr->fr_nopause) { fr_send_notification(fr, fast_reload_notification_reload_start); + fr_poll_for_ack(fr); } return result; } @@ -5268,7 +5269,6 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, if(gettimeofday(time_reload, NULL) < 0) log_err("gettimeofday: %s", strerror(errno)); - /* Delete old. */ if(fr_poll_for_quit(fr)) { config_delete(newcfg); fr_construct_clear(&ct); @@ -5292,6 +5292,8 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, fast_reload_notification_reload_nopause_poll); fr_poll_for_ack(fr); } + + /* Delete old. */ config_delete(newcfg); fr_construct_clear(&ct); return 1; @@ -6013,6 +6015,7 @@ fr_worker_auth_del(struct worker* worker, struct fast_reload_auth_change* item, int for_change) { int released = 0; /* Did this routine release callbacks. */ + struct auth_zone* lookold_z; struct auth_xfer* xfr = NULL; lock_rw_wrlock(&item->old_z->lock); @@ -6026,7 +6029,10 @@ fr_worker_auth_del(struct worker* worker, struct fast_reload_auth_change* item, lock_rw_unlock(&item->old_z->lock); lock_rw_rdlock(&item->old_z->lock); - auth_zones_lock_xfr(worker->env.auth_zones, &item->old_z, 2, &xfr, + /* The auth zones do not contain the zone any more, so the + * lookup is going to return a NULL zone pointer. */ + lookold_z = item->old_z; + auth_zones_lock_xfr(worker->env.auth_zones, &lookold_z, 2, &xfr, 0, 0); if(xfr) { /* Release callbacks on the xfr, if this worker holds them. */ @@ -6264,11 +6270,19 @@ fr_main_perform_reload_stop(struct fast_reload_thread* fr) worker_send_cmd(daemon->workers[i], worker_cmd_reload_start); } + /* Pick up changes for this worker. */ if(fr->worker->daemon->fast_reload_drop_mesh) { verbose(VERB_ALGO, "worker: drop mesh queries after reload"); mesh_delete_all(fr->worker->env.mesh); } fast_reload_worker_pickup_changes(fr->worker); + + /* Wait for the other threads to ack. */ + fr_read_ack_from_workers(fr); + + /* Send ack to fast reload thread. */ + fr_send_cmd_to(fr, fast_reload_notification_reload_ack, 0, 1); + verbose(VERB_ALGO, "worker resume after reload"); } diff --git a/daemon/remote.h b/daemon/remote.h index 3a063076c..064d7b7fc 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -140,7 +140,7 @@ enum fast_reload_notification { fast_reload_notification_printout = 5, /** stop as part of the reload the thread and other threads */ fast_reload_notification_reload_stop = 6, - /** ack the stop as part of the reload */ + /** ack the stop as part of the reload, and also ack start */ fast_reload_notification_reload_ack = 7, /** resume from stop as part of the reload */ fast_reload_notification_reload_start = 8, diff --git a/daemon/worker.c b/daemon/worker.c index 01d10a7c2..c6f567e9c 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -443,6 +443,7 @@ worker_stop_and_wait(struct worker* worker) mesh_delete_all(worker->env.mesh); } fast_reload_worker_pickup_changes(worker); + worker_send_reload_ack(worker); verbose(VERB_ALGO, "worker resume after reload"); } diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf index cc148053d..df9031702 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf @@ -72,3 +72,15 @@ stub-zone: name: "stub6.org" stub-addr: "127.0.0.1@@NS2_PORT@" stub-prime: no + +auth-zone: + name: "auth1.org" + zonefile: "auth1.zone" + +auth-zone: + name: "auth2.org" + zonefile: "auth1.zone" + +auth-zone: + name: "auth3.org" + zonefile: "auth1.zone" diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 index 98f43c0ef..cdb580282 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 @@ -12,7 +12,8 @@ server: forward-zone: name: "." - forward-addr: "127.0.0.1@12345" + # No addresses makes the server return SERVFAIL for deleted zones. + #forward-addr: "127.0.0.1@12345" remote-control: control-enable: yes @@ -72,3 +73,15 @@ stub-zone: name: "stub6.org" stub-addr: "127.0.0.1@@NS1_PORT@" stub-prime: no + +auth-zone: + name: "auth1.org" + zonefile: "auth1.zone" + +auth-zone: + name: "auth3.org" + zonefile: "auth2.zone" + +auth-zone: + name: "auth4.org" + zonefile: "auth2.zone" diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre index 2fb705e74..937a8c0e6 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre @@ -39,7 +39,7 @@ echo "NS2_PID=$NS2_PID" >> .tpkg.var.test # start unbound in the background PRE="../.." -$PRE/unbound -d -c ub.conf >unbound.log 2>&1 & +valgrind --leak-check=full --show-reachable=yes $PRE/unbound -d -c ub.conf >unbound.log 2>&1 & UNBOUND_PID=$! echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test index b1212c96b..a6b1216a7 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test @@ -41,6 +41,18 @@ for x in example4.org example5.org example6.org stub4.org stub5.org stub6.org; d fi done +for x in auth1.org auth2.org auth3.org; do + echo "" + echo "dig www.$x [auth is 1.2.3.4]" + dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile + if grep "1.2.3.4" outfile; then + echo "response OK" + else + echo "www.$x got the wrong answer" + exit 1 + fi +done + echo "" echo "> replace config file ub.conf" mv ub.conf ub.conf.orig @@ -106,4 +118,43 @@ for x in example1.org example3.org example5.org stub1.org stub3.org stub5.org; d fi done +# unchanged auth +for x in auth1.org; do + echo "" + echo "dig www.$x [auth is 1.2.3.4]" + dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile + if grep "1.2.3.4" outfile; then + echo "response OK" + else + echo "www.$x got the wrong answer" + exit 1 + fi +done + +# deleted auth +for x in auth2.org; do + echo "" + echo "dig www.$x [auth is deleted]" + dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile + if grep "SERVFAIL" outfile; then + echo "response OK" + else + echo "www.$x got the wrong answer" + exit 1 + fi +done + +# changed and added auth +for x in auth3.org auth4.org; do + echo "" + echo "dig www.$x [auth is 1.2.3.5]" + dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile + if grep "1.2.3.5" outfile; then + echo "response OK" + else + echo "www.$x got the wrong answer" + exit 1 + fi +done + exit 0 From 7a76b69049238b0680c33c0dd3a7280e6e8b1c4b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 21 Jun 2024 10:44:38 +0200 Subject: [PATCH 091/125] - fast-reload, remove debug for auth zone test. --- testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre index 937a8c0e6..2fb705e74 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre @@ -39,7 +39,7 @@ echo "NS2_PID=$NS2_PID" >> .tpkg.var.test # start unbound in the background PRE="../.." -valgrind --leak-check=full --show-reachable=yes $PRE/unbound -d -c ub.conf >unbound.log 2>&1 & +$PRE/unbound -d -c ub.conf >unbound.log 2>&1 & UNBOUND_PID=$! echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test From f0d374d2136df5e3f7a5b4d8a1418f40a1783276 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 21 Jun 2024 13:06:06 +0200 Subject: [PATCH 092/125] - fast-reload, fix auth zone reload with zone transfer. --- daemon/remote.c | 157 +++++++++++++++--- .../fast_reload_fwd.tdir/fast_reload_fwd.conf | 15 ++ .../fast_reload_fwd.conf2 | 15 ++ .../fast_reload_fwd.tdir/fast_reload_fwd.ns1 | 88 ++++++++++ .../fast_reload_fwd.tdir/fast_reload_fwd.ns2 | 34 ++++ .../fast_reload_fwd.tdir/fast_reload_fwd.test | 10 +- 6 files changed, 290 insertions(+), 29 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 2c02947ed..87372c148 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4390,6 +4390,72 @@ fr_add_auth_zone_change(struct fast_reload_thread* fr, struct auth_zone* old_z, return 1; } +/** See if auth master is equal */ +static int +xfr_auth_master_equal(struct auth_master* m1, struct auth_master* m2) +{ + if(!m1 && !m2) + return 1; + if(!m1 || !m2) + return 0; + + if((m1->host && !m2->host) || (!m1->host && m2->host)) + return 0; + if(m1->host && m2->host && strcmp(m1->host, m2->host) != 0) + return 0; + + if((m1->file && !m2->file) || (!m1->file && m2->file)) + return 0; + if(m1->file && m2->file && strcmp(m1->file, m2->file) != 0) + return 0; + + if((m1->http && !m2->http) || (!m1->http && m2->http)) + return 0; + if((m1->ixfr && !m2->ixfr) || (!m1->ixfr && m2->ixfr)) + return 0; + if((m1->allow_notify && !m2->allow_notify) || (!m1->allow_notify && m2->allow_notify)) + return 0; + if((m1->ssl && !m2->ssl) || (!m1->ssl && m2->ssl)) + return 0; + if(m1->port != m2->port) + return 0; + return 1; +} + +/** See if list of auth masters is equal */ +static int +xfr_masterlist_equal(struct auth_master* list1, struct auth_master* list2) +{ + struct auth_master* p1 = list1, *p2 = list2; + while(p1 && p2) { + if(!xfr_auth_master_equal(p1, p2)) + return 0; + p1 = p1->next; + p2 = p2->next; + } + if(!p2 && !p2) + return 1; + return 0; +} + +/** See if the list of masters has changed. */ +static int +xfr_masters_equal(struct auth_xfer* xfr1, struct auth_xfer* xfr2) +{ + if(xfr1 == NULL && xfr2 == NULL) + return 1; + if(xfr1 == NULL && xfr2 != NULL) + return 0; + if(xfr1 != NULL && xfr2 == NULL) + return 0; + if(xfr_masterlist_equal(xfr1->task_probe->masters, + xfr2->task_probe->masters) && + xfr_masterlist_equal(xfr1->task_transfer->masters, + xfr2->task_transfer->masters)) + return 1; + return 0; +} + /** Check what has changed in auth zones, like added and deleted zones */ static int auth_zones_check_changes(struct fast_reload_thread* fr, @@ -4438,8 +4504,19 @@ auth_zones_check_changes(struct fast_reload_thread* fr, } else { uint32_t old_serial = 0, new_serial = 0; int have_old = 0, have_new = 0; + struct auth_xfer* old_xfr, *new_xfr; lock_rw_rdlock(&new_z->lock); lock_rw_rdlock(&old_z->lock); + new_xfr = auth_xfer_find(ct->auth_zones, old_z->name, + old_z->namelen, old_z->dclass); + old_xfr = auth_xfer_find(env->auth_zones, old_z->name, + old_z->namelen, old_z->dclass); + if(new_xfr) { + lock_basic_lock(&new_xfr->lock); + } + if(old_xfr) { + lock_basic_lock(&old_xfr->lock); + } lock_rw_unlock(&env->auth_zones->lock); /* Change in the auth zone can be detected. */ @@ -4449,17 +4526,30 @@ auth_zones_check_changes(struct fast_reload_thread* fr, &old_serial)!=0); have_new = (auth_zone_get_serial(new_z, &new_serial)!=0); - if(have_old != have_new || old_serial != new_serial) { + if(have_old != have_new || old_serial != new_serial + || !xfr_masters_equal(old_xfr, new_xfr)) { /* The zone has been changed. */ if(!fr_add_auth_zone_change(fr, old_z, new_z, 0, 0, 1)) { lock_rw_unlock(&old_z->lock); lock_rw_unlock(&new_z->lock); lock_rw_unlock(&ct->auth_zones->lock); + if(new_xfr) { + lock_basic_unlock(&new_xfr->lock); + } + if(old_xfr) { + lock_basic_unlock(&old_xfr->lock); + } return 0; } } + if(new_xfr) { + lock_basic_unlock(&new_xfr->lock); + } + if(old_xfr) { + lock_basic_unlock(&old_xfr->lock); + } lock_rw_unlock(&old_z->lock); lock_rw_unlock(&new_z->lock); } @@ -6048,9 +6138,11 @@ fr_worker_auth_del(struct worker* worker, struct fast_reload_auth_change* item, if(!for_change && (released || worker->thread_num == 0)) { /* See if the xfr item can be deleted. */ xfr = NULL; + lock_rw_wrlock(&worker->env.auth_zones->lock); lock_rw_rdlock(&item->old_z->lock); - auth_zones_lock_xfr(worker->env.auth_zones, &item->old_z, 2, - &xfr, 0, 1); + lookold_z = item->old_z; + auth_zones_lock_xfr(worker->env.auth_zones, &lookold_z, 2, + &xfr, 1, 1); if(xfr && xfr->task_nextprobe->worker == NULL && xfr->task_probe->worker == NULL && xfr->task_transfer->worker == NULL) { @@ -6068,21 +6160,49 @@ fr_worker_auth_del(struct worker* worker, struct fast_reload_auth_change* item, } } +/** Fast reload, auth xfer config is picked up */ +static void +auth_xfr_pickup_config(struct auth_xfer* loadxfr, struct auth_xfer* xfr) +{ + struct auth_master *probe_masters, *transfer_masters; + log_assert(loadxfr->namelen == xfr->namelen); + log_assert(loadxfr->namelabs == xfr->namelabs); + log_assert(loadxfr->dclass == xfr->dclass); + + /* The lists can be swapped in, the other xfr struct will be deleted + * afterwards. */ + probe_masters = xfr->task_probe->masters; + transfer_masters = xfr->task_transfer->masters; + xfr->task_probe->masters = loadxfr->task_probe->masters; + xfr->task_transfer->masters = loadxfr->task_transfer->masters; + loadxfr->task_probe->masters = probe_masters; + loadxfr->task_transfer->masters = transfer_masters; +} + /** Fast reload, worker picks up added auth zone */ static void fr_worker_auth_add(struct worker* worker, struct fast_reload_auth_change* item, int for_change) { - struct auth_xfer* xfr = NULL; + struct auth_xfer* xfr = NULL, *loadxfr = NULL; /* Start zone transfers and lookups. */ + lock_rw_rdlock(&worker->daemon->fast_reload_thread->old_auth_zones->lock); + lock_rw_wrlock(&worker->env.auth_zones->lock); lock_rw_wrlock(&item->new_z->lock); + loadxfr = auth_xfer_find(worker->daemon->fast_reload_thread-> + old_auth_zones, item->new_z->name, item->new_z->namelen, + item->new_z->dclass); + if(loadxfr) { + lock_basic_lock(&loadxfr->lock); + } auth_zones_lock_xfr(worker->env.auth_zones, &item->new_z, 0, &xfr, - 0, 1); + 1, 1); if(xfr == NULL && item->new_z->zone_is_slave) { /* The xfr item needs to be created. The auth zones lock * is held to make this possible. */ xfr = auth_xfer_create(worker->env.auth_zones, item->new_z); + auth_xfr_pickup_config(loadxfr, xfr); /* Serial information is copied into the xfr struct. */ if(!xfr_find_soa(item->new_z, xfr)) { xfr->serial = 0; @@ -6094,8 +6214,15 @@ fr_worker_auth_add(struct worker* worker, struct fast_reload_auth_change* item, } lock_rw_unlock(&item->new_z->lock); lock_rw_unlock(&worker->env.auth_zones->lock); + lock_rw_unlock(&worker->daemon->fast_reload_thread->old_auth_zones->lock); + if(loadxfr) { + lock_basic_unlock(&loadxfr->lock); + } if(xfr) { auth_xfer_pickup_initial_zone(xfr, &worker->env); + if(for_change) { + xfr->task_probe->only_lookup = 0; + } lock_basic_unlock(&xfr->lock); } @@ -6107,25 +6234,6 @@ fr_worker_auth_add(struct worker* worker, struct fast_reload_auth_change* item, lock_rw_unlock(&item->new_z->lock); } -/** Fast reload, auth xfer config is picked up */ -static void -auth_xfr_pickup_config(struct auth_xfer* loadxfr, struct auth_xfer* xfr) -{ - struct auth_master *probe_masters, *transfer_masters; - log_assert(loadxfr->namelen == xfr->namelen); - log_assert(loadxfr->namelabs == xfr->namelabs); - log_assert(loadxfr->dclass == xfr->dclass); - - /* The lists can be swapped in, the other xfr struct will be deleted - * afterwards. */ - probe_masters = xfr->task_probe->masters; - transfer_masters = xfr->task_transfer->masters; - xfr->task_probe->masters = loadxfr->task_probe->masters; - xfr->task_transfer->masters = loadxfr->task_transfer->masters; - loadxfr->task_probe->masters = probe_masters; - loadxfr->task_transfer->masters = transfer_masters; -} - /** Fast reload, worker picks up changed auth zone */ static void fr_worker_auth_cha(struct worker* worker, struct fast_reload_auth_change* item) @@ -6169,6 +6277,7 @@ fr_worker_auth_cha(struct worker* worker, struct fast_reload_auth_change* item) } else if(loadxfr && !xfr) { /* Create the xfr. */ xfr = auth_xfer_create(worker->env.auth_zones, item->new_z); + auth_xfr_pickup_config(loadxfr, xfr); item->new_z->zone_is_slave = 1; } lock_rw_unlock(&item->new_z->lock); diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf index df9031702..112f4b8d5 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf @@ -84,3 +84,18 @@ auth-zone: auth-zone: name: "auth3.org" zonefile: "auth1.zone" + +auth-zone: + name: "auth5.org" + zonefile: "auth5.zone" + primary: 127.0.0.1@@NS1_PORT@ + +auth-zone: + name: "auth6.org" + zonefile: "auth6.zone" + primary: 127.0.0.1@@NS1_PORT@ + +auth-zone: + name: "auth7.org" + zonefile: "auth7.zone" + primary: 127.0.0.1@@NS1_PORT@ diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 index cdb580282..e666cf344 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 @@ -85,3 +85,18 @@ auth-zone: auth-zone: name: "auth4.org" zonefile: "auth2.zone" + +auth-zone: + name: "auth5.org" + zonefile: "auth5.zone" + primary: 127.0.0.1@@NS1_PORT@ + +auth-zone: + name: "auth7.org" + zonefile: "auth7.zone" + primary: 127.0.0.1@@NS2_PORT@ + +auth-zone: + name: "auth8.org" + zonefile: "auth8.zone" + primary: 127.0.0.1@@NS1_PORT@ diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns1 b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns1 index ac43a7e51..d9644414b 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns1 +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns1 @@ -239,6 +239,94 @@ SECTION ANSWER www2.stub6.org. IN A 1.2.3.1 ENTRY_END +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +auth5.org. IN SOA +SECTION ANSWER +auth5.org. SOA ns root 1 3600 300 7200 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +auth5.org. IN AXFR +SECTION ANSWER +auth5.org. SOA ns root 1 3600 300 7200 3600 +www.auth5.org. A 1.2.3.4 +auth5.org. SOA ns root 1 3600 300 7200 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +auth6.org. IN SOA +SECTION ANSWER +auth6.org. SOA ns root 1 3600 300 7200 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +auth6.org. IN AXFR +SECTION ANSWER +auth6.org. SOA ns root 1 3600 300 7200 3600 +www.auth6.org. A 1.2.3.4 +auth6.org. SOA ns root 1 3600 300 7200 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +auth7.org. IN SOA +SECTION ANSWER +auth7.org. SOA ns root 1 3600 300 7200 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +auth7.org. IN AXFR +SECTION ANSWER +auth7.org. SOA ns root 1 3600 300 7200 3600 +www.auth7.org. A 1.2.3.4 +auth7.org. SOA ns root 1 3600 300 7200 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +auth8.org. IN SOA +SECTION ANSWER +auth8.org. SOA ns root 1 3600 300 7200 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +auth8.org. IN AXFR +SECTION ANSWER +auth8.org. SOA ns root 1 3600 300 7200 3600 +www.auth8.org. A 1.2.3.4 +auth8.org. SOA ns root 1 3600 300 7200 3600 +ENTRY_END + ; match anything and return a reply ENTRY_BEGIN MATCH opcode diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns2 b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns2 index 01c5bf448..8e7eb60c8 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns2 +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.ns2 @@ -239,6 +239,40 @@ SECTION ANSWER www2.stub6.org. IN A 1.2.3.2 ENTRY_END +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +auth7.org. IN SOA +SECTION ANSWER +auth7.org. SOA ns root 2 3600 300 7200 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +auth7.org. IN AXFR +SECTION ANSWER +auth7.org. SOA ns root 2 3600 300 7200 3600 +www.auth7.org. A 1.2.3.5 +auth7.org. SOA ns root 2 3600 300 7200 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +auth7.org. IN IXFR +SECTION ANSWER +auth7.org. SOA ns root 2 3600 300 7200 3600 +www.auth7.org. A 1.2.3.5 +auth7.org. SOA ns root 2 3600 300 7200 3600 +ENTRY_END + ; match anything and return a reply ENTRY_BEGIN MATCH opcode diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test index a6b1216a7..45af134ae 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test @@ -41,7 +41,7 @@ for x in example4.org example5.org example6.org stub4.org stub5.org stub6.org; d fi done -for x in auth1.org auth2.org auth3.org; do +for x in auth1.org auth2.org auth3.org auth5.org auth6.org auth7.org; do echo "" echo "dig www.$x [auth is 1.2.3.4]" dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile @@ -118,8 +118,8 @@ for x in example1.org example3.org example5.org stub1.org stub3.org stub5.org; d fi done -# unchanged auth -for x in auth1.org; do +# auth is unchanged, or at ns1. +for x in auth1.org auth5.org auth8.org; do echo "" echo "dig www.$x [auth is 1.2.3.4]" dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile @@ -132,7 +132,7 @@ for x in auth1.org; do done # deleted auth -for x in auth2.org; do +for x in auth2.org auth6.org; do echo "" echo "dig www.$x [auth is deleted]" dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile @@ -145,7 +145,7 @@ for x in auth2.org; do done # changed and added auth -for x in auth3.org auth4.org; do +for x in auth3.org auth4.org auth7.org; do echo "" echo "dig www.$x [auth is 1.2.3.5]" dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile From 613d47b18890b845d29f19f6bf63a8b8959fa892 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 21 Jun 2024 15:25:29 +0200 Subject: [PATCH 093/125] - fast-reload, fix auth zone reload lock order. --- daemon/remote.c | 84 ++++++++++++------- services/authzone.h | 4 +- .../fast_reload_fwd.tdir/fast_reload_fwd.post | 6 +- .../fast_reload_fwd.tdir/fast_reload_fwd.pre | 5 ++ .../fast_reload_fwd.tdir/fast_reload_fwd.test | 20 +++++ 5 files changed, 85 insertions(+), 34 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 87372c148..3a71a4c07 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6099,13 +6099,51 @@ auth_zone_zonemd_stop_lookup(struct auth_zone* z, struct mesh_area* mesh) &auth_zonemd_dnskey_lookup_callback, z); } +/** Pick up the auth zone locks. */ +static void +fr_pickup_auth_locks(struct worker* worker, struct auth_zone* namez, + struct auth_zone* old_z, struct auth_zone* new_z, + struct auth_xfer** xfr, struct auth_xfer** loadxfr) +{ + uint8_t nm[LDNS_MAX_DOMAINLEN+1]; + size_t nmlen; + uint16_t dclass; + + log_assert(namez->namelen <= sizeof(nm)); + lock_rw_rdlock(&namez->lock); + nmlen = namez->namelen; + dclass = namez->dclass; + memmove(nm, namez->name, nmlen); + lock_rw_unlock(&namez->lock); + + lock_rw_wrlock(&worker->daemon->fast_reload_thread->old_auth_zones->lock); + lock_rw_wrlock(&worker->env.auth_zones->lock); + if(new_z) { + lock_rw_wrlock(&new_z->lock); + } + if(old_z) { + lock_rw_wrlock(&old_z->lock); + } + if(loadxfr) + *loadxfr = auth_xfer_find(worker->daemon->fast_reload_thread-> + old_auth_zones, nm, nmlen, dclass); + if(xfr) + *xfr = auth_xfer_find(worker->env.auth_zones, nm, nmlen, + dclass); + if(loadxfr && *loadxfr) { + lock_basic_lock(&(*loadxfr)->lock); + } + if(xfr && *xfr) { + lock_basic_lock(&(*xfr)->lock); + } +} + /** Fast reload, worker picks up deleted auth zone */ static void fr_worker_auth_del(struct worker* worker, struct fast_reload_auth_change* item, int for_change) { int released = 0; /* Did this routine release callbacks. */ - struct auth_zone* lookold_z; struct auth_xfer* xfr = NULL; lock_rw_wrlock(&item->old_z->lock); @@ -6118,12 +6156,11 @@ fr_worker_auth_del(struct worker* worker, struct fast_reload_auth_change* item, } lock_rw_unlock(&item->old_z->lock); - lock_rw_rdlock(&item->old_z->lock); - /* The auth zones do not contain the zone any more, so the - * lookup is going to return a NULL zone pointer. */ - lookold_z = item->old_z; - auth_zones_lock_xfr(worker->env.auth_zones, &lookold_z, 2, &xfr, - 0, 0); + fr_pickup_auth_locks(worker, item->old_z, item->old_z, NULL, &xfr, + NULL); + lock_rw_unlock(&worker->daemon->fast_reload_thread->old_auth_zones->lock); + lock_rw_unlock(&worker->env.auth_zones->lock); + lock_rw_unlock(&item->old_z->lock); if(xfr) { /* Release callbacks on the xfr, if this worker holds them. */ if(xfr->task_nextprobe->worker == worker || @@ -6138,11 +6175,10 @@ fr_worker_auth_del(struct worker* worker, struct fast_reload_auth_change* item, if(!for_change && (released || worker->thread_num == 0)) { /* See if the xfr item can be deleted. */ xfr = NULL; - lock_rw_wrlock(&worker->env.auth_zones->lock); - lock_rw_rdlock(&item->old_z->lock); - lookold_z = item->old_z; - auth_zones_lock_xfr(worker->env.auth_zones, &lookold_z, 2, - &xfr, 1, 1); + fr_pickup_auth_locks(worker, item->old_z, item->old_z, NULL, + &xfr, NULL); + lock_rw_unlock(&worker->daemon->fast_reload_thread->old_auth_zones->lock); + lock_rw_unlock(&item->old_z->lock); if(xfr && xfr->task_nextprobe->worker == NULL && xfr->task_probe->worker == NULL && xfr->task_transfer->worker == NULL) { @@ -6187,17 +6223,8 @@ fr_worker_auth_add(struct worker* worker, struct fast_reload_auth_change* item, struct auth_xfer* xfr = NULL, *loadxfr = NULL; /* Start zone transfers and lookups. */ - lock_rw_rdlock(&worker->daemon->fast_reload_thread->old_auth_zones->lock); - lock_rw_wrlock(&worker->env.auth_zones->lock); - lock_rw_wrlock(&item->new_z->lock); - loadxfr = auth_xfer_find(worker->daemon->fast_reload_thread-> - old_auth_zones, item->new_z->name, item->new_z->namelen, - item->new_z->dclass); - if(loadxfr) { - lock_basic_lock(&loadxfr->lock); - } - auth_zones_lock_xfr(worker->env.auth_zones, &item->new_z, 0, &xfr, - 1, 1); + fr_pickup_auth_locks(worker, item->new_z, NULL, item->new_z, &xfr, + &loadxfr); if(xfr == NULL && item->new_z->zone_is_slave) { /* The xfr item needs to be created. The auth zones lock * is held to make this possible. */ @@ -6252,15 +6279,8 @@ fr_worker_auth_cha(struct worker* worker, struct fast_reload_auth_change* item) * new config contents can be picked up. SOA information is picked * up in the auth_add routine, as it has the new_z ready. */ - lock_rw_rdlock(&worker->daemon->fast_reload_thread->old_auth_zones->lock); - lock_rw_wrlock(&worker->env.auth_zones->lock); - - lock_rw_rdlock(&item->new_z->lock); - lock_rw_rdlock(&item->old_z->lock); - auth_zones_lock_xfr(worker->env.auth_zones, &item->new_z, 1, &xfr, - 1, 1); - auth_zones_lock_xfr(worker->daemon->fast_reload_thread->old_auth_zones, - &item->old_z, 1, &loadxfr, 1, 1); + fr_pickup_auth_locks(worker, item->new_z, item->old_z, item->new_z, + &xfr, &loadxfr); /* The xfr is not there any more if the zone is not set to have * zone transfers. Or the xfr needs to be created if it is set to diff --git a/services/authzone.h b/services/authzone.h index 5f630e9e9..4bfa997d1 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -212,7 +212,9 @@ struct auth_xfer { * one of the tasks. * Once it has the task assigned to it, the worker can access the * other elements of the task structure without a lock, because that - * is necessary for the eventloop and callbacks from that. */ + * is necessary for the eventloop and callbacks from that. + * The auth_zone->lock is locked before this lock. + */ lock_basic_type lock; /** zone name, in uncompressed wireformat */ diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.post b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.post index c44b8c27c..e7e644b7a 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.post +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.post @@ -5,10 +5,13 @@ [ -f .tpkg.var.test ] && source .tpkg.var.test # # do your teardown here +PRE="../.." . ../common.sh kill_pid $NS1_PID kill_pid $NS2_PID -kill_pid $UNBOUND_PID +if test -f unbound.pid; then + kill_pid $UNBOUND_PID +fi rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID echo echo "> ns1.log" @@ -19,3 +22,4 @@ cat ns2.log echo echo "> unbound.log" cat unbound.log +exit 0 diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre index 2fb705e74..67d02ca66 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.pre @@ -12,6 +12,11 @@ if grep -e "define HAVE_PTHREAD 1" -e "define HAVE_SOLARIS_THREADS 1" -e "define else skip_test "no threads" fi +if grep -e "define ENABLE_LOCK_CHECKS 1" $PRE/config.h; then + get_make + echo "> (cd $PRE ; $MAKE lock-verify)" + (cd $PRE ; $MAKE lock-verify) +fi get_random_port 3 UNBOUND_PORT=$RND_PORT diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test index 45af134ae..6ccc5dc15 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test @@ -157,4 +157,24 @@ for x in auth3.org auth4.org auth7.org; do fi done +echo "" +echo "> stop unbound" +kill_pid $UNBOUND_PID +if test -f unbound.pid; then sleep 1; fi +if test -f unbound.pid; then sleep 1; fi +if test -f unbound.pid; then sleep 1; fi +if test -f unbound.pid; then echo "unbound.pid still there"; fi +# check the locks. +function locktest() { + cp ublocktrace.* .. + if test -x $PRE/lock-verify -a -f ublocktrace.0; then + $PRE/lock-verify ublocktrace.* + if test $? -ne 0; then + echo "lock-verify error" + exit 1 + fi + fi +} +locktest + exit 0 From 9032d1d25973b8f20297e6acc2d9830c765e7776 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 21 Jun 2024 15:26:40 +0200 Subject: [PATCH 094/125] - fast-reload, remove debug from fast reload test. --- testdata/fast_reload_fwd.tdir/fast_reload_fwd.test | 1 - 1 file changed, 1 deletion(-) diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test index 6ccc5dc15..b2ce260e9 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test @@ -166,7 +166,6 @@ if test -f unbound.pid; then sleep 1; fi if test -f unbound.pid; then echo "unbound.pid still there"; fi # check the locks. function locktest() { - cp ublocktrace.* .. if test -x $PRE/lock-verify -a -f ublocktrace.0; then $PRE/lock-verify ublocktrace.* if test $? -ne 0; then From 6021840b1e92b74c3234c394c60f4d7e22989227 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 21 Jun 2024 15:31:28 +0200 Subject: [PATCH 095/125] - fast-reload, remove unused function. --- services/authzone.c | 39 --------------------------------------- services/authzone.h | 19 ------------------- 2 files changed, 58 deletions(-) diff --git a/services/authzone.c b/services/authzone.c index ddeb6e8c3..d0f2f9b23 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -8691,45 +8691,6 @@ size_t auth_zones_get_mem(struct auth_zones* zones) return m; } -void auth_zones_lock_xfr(struct auth_zones* az, struct auth_zone** z, int zwr, - struct auth_xfer** x, int azlock_start, int azlock_return) -{ - uint8_t nm[LDNS_MAX_DOMAINLEN+1]; - size_t nmlen; - uint16_t dclass; - - /* Store name */ - if((*z)->namelen > sizeof(nm)) { - /* This should never happen, the buffer is large enough. */ - *x = NULL; - return; - } - nmlen = (*z)->namelen; - dclass = (*z)->dclass; - memmove(nm, (*z)->name, nmlen); - - /* Look it up again */ - lock_rw_unlock(&(*z)->lock); - if(!azlock_start) { - lock_rw_rdlock(&az->lock); - } - *z = auth_zone_find(az, nm, nmlen, dclass); - *x = auth_xfer_find(az, nm, nmlen, dclass); - if(*z) { - if(zwr == 0) { - lock_rw_rdlock(&(*z)->lock); - } else if(zwr == 1) { - lock_rw_wrlock(&(*z)->lock); - } - } - if(*x) { - lock_basic_lock(&(*x)->lock); - } - if(!azlock_return) { - lock_rw_unlock(&az->lock); - } -} - void xfr_disown_tasks(struct auth_xfer* xfr, struct worker* worker) { if(xfr->task_nextprobe->worker == worker) { diff --git a/services/authzone.h b/services/authzone.h index 4bfa997d1..bac8bdf7c 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -794,25 +794,6 @@ void auth_zones_pickup_zonemd_verify(struct auth_zones* az, * for reading. */ size_t auth_zones_get_mem(struct auth_zones* zones); -/** - * Pick up the xfr item for an auth zone. The routine assumes the - * zone object is there and is locked for reading. It unlocks and looks - * up new z, x pointers. - * @param az: auth zones. - * @param z: the zone with lookup name information. On start it is locked - * by the caller. - * On return it is changed to the new zone, if it is new, - * and with a lock if requested. - * @param zwr: is a writelock(1) or readlock(0) on z requested, 2 no lock. - * @param x: the xfr item for the zone. - * If it is found, it is locked on return. - * @param azlock_start: if true, the auth zones are locked, by caller, - * when the function is called. - * @param azlock_return: if true, the auth zones are locked on return. - */ -void auth_zones_lock_xfr(struct auth_zones* az, struct auth_zone** z, int zwr, - struct auth_xfer** x, int azlock_start, int azlock_return); - /** * Initial pick up of the auth zone nextprobe timeout and that turns * into further zone transfer work, if any. Also sets the lease time. From 341cbed98769be328e91ef42481cf6269d861655 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 25 Jun 2024 12:02:03 +0200 Subject: [PATCH 096/125] - fast-reload, fix the worker trust anchor probe timer lock acquisition in the probe answer callback routine for trust anchor probes. --- validator/autotrust.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/validator/autotrust.c b/validator/autotrust.c index 3eb13b35c..676dd8ad0 100644 --- a/validator/autotrust.c +++ b/validator/autotrust.c @@ -2034,25 +2034,40 @@ wait_probe_time(struct val_anchors* anchors) return 0; } -/** reset worker timer */ +/** reset worker timer, at the time from wait_probe_time. */ static void -reset_worker_timer(struct module_env* env) +reset_worker_timer_at(struct module_env* env, time_t next) { struct timeval tv; #ifndef S_SPLINT_S - time_t next = (time_t)wait_probe_time(env->anchors); /* in case this is libunbound, no timer */ if(!env->probe_timer) return; if(next > *env->now) tv.tv_sec = (time_t)(next - *env->now); else tv.tv_sec = 0; +#else + (void)next; #endif tv.tv_usec = 0; comm_timer_set(env->probe_timer, &tv); verbose(VERB_ALGO, "scheduled next probe in " ARG_LL "d sec", (long long)tv.tv_sec); } +/** reset worker timer. This routine manages the locks on acquiring the + * next time for the timer. */ +static void +reset_worker_timer(struct module_env* env) +{ + time_t next; + if(!env->anchors) + return; + lock_basic_lock(&env->anchors->lock); + next = wait_probe_time(env->anchors); + lock_basic_unlock(&env->anchors->lock); + reset_worker_timer_at(env, next); +} + /** set next probe for trust anchor */ static int set_next_probe(struct module_env* env, struct trust_anchor* tp, @@ -2091,7 +2106,7 @@ set_next_probe(struct module_env* env, struct trust_anchor* tp, verbose(VERB_ALGO, "next probe set in %d seconds", (int)tp->autr->next_probe_time - (int)*env->now); if(mold != mnew) { - reset_worker_timer(env); + reset_worker_timer_at(env, mnew); } return 1; } @@ -2146,7 +2161,7 @@ autr_tp_remove(struct module_env* env, struct trust_anchor* tp, autr_point_delete(del_tp); } if(mold != mnew) { - reset_worker_timer(env); + reset_worker_timer_at(env, mnew); } } From d1681e5bdd8ce02111f07a691fb7702a8983bd07 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 25 Jun 2024 13:46:57 +0200 Subject: [PATCH 097/125] - fast-reload, reload trust anchors. --- daemon/remote.c | 27 ++++++++++++++++++++ doc/unbound-control.8.in | 6 +++++ validator/val_anchor.c | 55 ++++++++++++++++++++++++++++++++++++++++ validator/val_anchor.h | 10 +++++++- 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 3a71a4c07..d4018b83a 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3964,6 +3964,8 @@ struct fast_reload_construct { int use_rpz; /** construct for edns strings */ struct edns_strings* edns_strings; + /** construct for trust anchors */ + struct val_anchors* anchors; /** storage for the old configuration elements. The outer struct * is allocated with malloc here, the items are from config. */ struct config_file* oldcfg; @@ -4081,6 +4083,7 @@ fr_construct_clear(struct fast_reload_construct* ct) acl_list_delete(ct->acl_interface); tcl_list_delete(ct->tcl); edns_strings_delete(ct->edns_strings); + anchors_delete(ct->anchors); views_delete(ct->views); /* Delete the log identity here so that the global value is not * reset by config_delete. */ @@ -4337,6 +4340,7 @@ fr_printmem(struct fast_reload_thread* fr, mem += acl_list_get_mem(ct->acl_interface); mem += tcl_list_get_mem(ct->tcl); mem += edns_strings_get_mem(ct->edns_strings); + mem += anchors_get_mem(ct->anchors); mem += sizeof(*ct->oldcfg); mem += config_file_getmem(newcfg); @@ -4697,6 +4701,20 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; + if(fr->worker->env.anchors) { + /* There are trust anchors already, so create it for reload. */ + if(!(ct->anchors = anchors_create())) { + fr_construct_clear(ct); + return 0; + } + if(!anchors_apply_cfg(ct->anchors, newcfg)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + } + if(!(ct->oldcfg = (struct config_file*)calloc(1, sizeof(*ct->oldcfg)))) { fr_construct_clear(ct); @@ -5144,6 +5162,10 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, lock_rw_wrlock(&env->fwds->lock); lock_rw_wrlock(&ct->hints->lock); lock_rw_wrlock(&env->hints->lock); + if(ct->anchors) { + lock_basic_lock(&ct->anchors->lock); + lock_basic_lock(&env->anchors->lock); + } #ifdef ATOMIC_POINTER_LOCK_FREE if(fr->fr_nopause) { @@ -5186,6 +5208,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, daemon->use_rpz = ct->use_rpz; auth_zones_swap(env->auth_zones, ct->auth_zones); edns_strings_swap_tree(env->edns_strings, ct->edns_strings); + anchors_swap_tree(env->anchors, ct->anchors); #ifdef USE_CACHEDB daemon->env->cachedb_enabled = cachedb_is_enabled(&daemon->mods, daemon->env); @@ -5215,6 +5238,10 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, lock_rw_unlock(&ct->fwds->lock); lock_rw_unlock(&env->hints->lock); lock_rw_unlock(&ct->hints->lock); + if(ct->anchors) { + lock_basic_unlock(&ct->anchors->lock); + lock_basic_unlock(&env->anchors->lock); + } return 1; } diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index cb617b288..45534ee42 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -74,6 +74,12 @@ It does not work with interface, outgoing-interface changes, also not with remote-control, outgoing-port-permit, outgoing-port-avoid, msg-buffer-size and statistics-interval changes. .IP +The fast reload also works on the options: insecure-lan-zones, domain-insecure, +trust-anchor-file, trust-anchor, trusted-key-file, auto-trust-anchor-file, +auth-zone and its options, rpz and its options, edns-strings, respip_set, +view and its options, access-control options, tcp-connection-limit, +log-identity. +.IP For dnstap, the options can be changed: dnstap-log-resolver-query-messages, dnstap-log-resolver-response-messages, dnstap-log-client-query-messages, dnstap-log-client-response-messages, dnstap-log-forwarder-query-messages diff --git a/validator/val_anchor.c b/validator/val_anchor.c index 8466a8923..23cc195d0 100644 --- a/validator/val_anchor.c +++ b/validator/val_anchor.c @@ -1170,17 +1170,53 @@ anchors_lookup(struct val_anchors* anchors, return result; } +/** Get memory usage of assembled key rrset */ +static size_t +assembled_rrset_get_mem(struct ub_packed_rrset_key* pkey) +{ + size_t s; + if(!pkey) + return 0; + s = sizeof(*pkey) + pkey->rk.dname_len; + if(pkey->entry.data) { + struct packed_rrset_data* pd = (struct packed_rrset_data*) + pkey->entry.data; + s += sizeof(*pd) + pd->count * (sizeof(size_t)+sizeof(time_t)+ + sizeof(uint8_t*)); + } + return s; +} + size_t anchors_get_mem(struct val_anchors* anchors) { struct trust_anchor *ta; + struct ta_key *k; size_t s = sizeof(*anchors); if(!anchors) return 0; + lock_basic_lock(&anchors->lock); RBTREE_FOR(ta, struct trust_anchor*, anchors->tree) { + lock_basic_lock(&ta->lock); s += sizeof(*ta) + ta->namelen; /* keys and so on */ + for(k = ta->keylist; k; k = k->next) { + s += sizeof(*k) + k->len; + } + s += assembled_rrset_get_mem(ta->ds_rrset); + s += assembled_rrset_get_mem(ta->dnskey_rrset); + if(ta->autr) { + struct autr_ta* p; + s += sizeof(*ta->autr); + if(ta->autr->file) + s += strlen(ta->autr->file); + for(p = ta->autr->keys; p; p=p->next) { + s += sizeof(*p) + p->rr_len; + } + } + lock_basic_unlock(&ta->lock); } + lock_basic_unlock(&anchors->lock); return s; } @@ -1343,3 +1379,22 @@ anchors_find_any_noninsecure(struct val_anchors* anchors) lock_basic_unlock(&anchors->lock); return NULL; } + +void +anchors_swap_tree(struct val_anchors* anchors, struct val_anchors* data) +{ + rbtree_type* oldtree; + struct autr_global_data* oldautr; + + if(!anchors || !data) + return; /* If anchors is NULL, there is no validation. */ + + oldtree = anchors->tree; + oldautr = anchors->autr; + + anchors->tree = data->tree; + anchors->autr = data->autr; + + data->tree = oldtree; + data->autr = oldautr; +} diff --git a/validator/val_anchor.h b/validator/val_anchor.h index 02e7e17b5..3fcf70eec 100644 --- a/validator/val_anchor.h +++ b/validator/val_anchor.h @@ -58,7 +58,7 @@ struct sldns_buffer; * on a trust anchor and look it up again to delete it. */ struct val_anchors { - /** lock on trees */ + /** lock on trees. It is locked in order after stubs. */ lock_basic_type lock; /** * Anchors are store in this tree. Sort order is chosen, so that @@ -248,4 +248,12 @@ int anchor_has_keytag(struct val_anchors* anchors, uint8_t* name, int namelabs, */ struct trust_anchor* anchors_find_any_noninsecure(struct val_anchors* anchors); +/** + * Swap internal tree with preallocated entries. + * @param anchors: anchor storage. + * @param data: the data structure used to take elements from. This contains + * the old elements on return. + */ +void anchors_swap_tree(struct val_anchors* anchors, struct val_anchors* data); + #endif /* VALIDATOR_VAL_ANCHOR_H */ From cc63e1d6156db8fe31f59871bdc6c6cf2a61dc10 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 25 Jun 2024 14:25:26 +0200 Subject: [PATCH 098/125] - fast-reload, fix trust anchor reload lock on autr global data and test for trust anchor reload. --- .../fast_reload_fwd.tdir/fast_reload_fwd.conf | 6 ++ .../fast_reload_fwd.conf2 | 6 ++ .../fast_reload_fwd.tdir/fast_reload_fwd.test | 82 +++++++++++++++++++ validator/val_anchor.c | 8 +- 4 files changed, 98 insertions(+), 4 deletions(-) diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf index 112f4b8d5..dca76342f 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf @@ -9,6 +9,12 @@ server: chroot: "" username: "" do-not-query-localhost: no + trust-anchor: "ta1.example.com DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af" + trust-anchor: "ta2.example.com DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af" + trust-anchor: "ta3.example.com DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af" + domain-insecure: "insec1.ta1.example.com" + domain-insecure: "insec2.ta1.example.com" + domain-insecure: "insec3.ta1.example.com" forward-zone: name: "." diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 index e666cf344..dbe6e4ffa 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.conf2 @@ -9,6 +9,12 @@ server: chroot: "" username: "" do-not-query-localhost: no + trust-anchor: "ta1.example.com DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af" + trust-anchor: "ta3.example.com DS 55567 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af" + trust-anchor: "ta4.example.com DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af" + domain-insecure: "insec1.ta1.example.com" + domain-insecure: "insec3.ta1.example.com" + domain-insecure: "insec4.ta1.example.com" forward-zone: name: "." diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test index b2ce260e9..d33c826f1 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test @@ -53,6 +53,41 @@ for x in auth1.org auth2.org auth3.org auth5.org auth6.org auth7.org; do fi done +echo "" +echo "> list_insecure" +$PRE/unbound-control -c ub.conf list_insecure 2>&1 | tee output +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +fi +if grep "insec1.ta1.example.com" output >/dev/null; then :; else + echo "wrong output" + exit 1 +fi +if grep "insec2.ta1.example.com" output >/dev/null; then :; else + echo "wrong output" + exit 1 +fi +if grep "insec3.ta1.example.com" output >/dev/null; then :; else + echo "wrong output" + exit 1 +fi +echo "" +echo "> trustanchor.unbound" +dig @127.0.0.1 -p $UNBOUND_PORT trustanchor.unbound CH TXT 2>&1 | tee outfile +if grep "ta1.example.com. 55566" outfile >/dev/null; then :; else + echo "wrong output ta1" + exit 1 +fi +if grep "ta2.example.com. 55566" outfile >/dev/null; then :; else + echo "wrong output" + exit 1 +fi +if grep "ta3.example.com. 55566" outfile >/dev/null; then :; else + echo "wrong output" + exit 1 +fi + echo "" echo "> replace config file ub.conf" mv ub.conf ub.conf.orig @@ -157,6 +192,53 @@ for x in auth3.org auth4.org auth7.org; do fi done +echo "" +echo "> list_insecure" +$PRE/unbound-control -c ub.conf list_insecure 2>&1 | tee output +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +fi +if grep "insec1.ta1.example.com" output >/dev/null; then :; else + echo "wrong output" + exit 1 +fi +if grep "insec2.ta1.example.com" output >/dev/null; then + echo "wrong output" + exit 1 +fi +if grep "insec3.ta1.example.com" output >/dev/null; then :; else + echo "wrong output" + exit 1 +fi +if grep "insec4.ta1.example.com" output >/dev/null; then :; else + echo "wrong output" + exit 1 +fi +echo "" +echo "> trustanchor.unbound" +dig @127.0.0.1 -p $UNBOUND_PORT trustanchor.unbound CH TXT 2>&1 | tee outfile +if grep "ta1.example.com. 55566" outfile >/dev/null; then :; else + echo "wrong output" + exit 1 +fi +if grep "ta2.example.com. 55566" outfile >/dev/null; then + echo "wrong output" + exit 1 +fi +if grep "ta3.example.com. 55566" outfile >/dev/null; then + echo "wrong output" + exit 1 +fi +if grep "ta3.example.com. 55567" outfile >/dev/null; then :; else + echo "wrong output" + exit 1 +fi +if grep "ta4.example.com. 55566" outfile >/dev/null; then :; else + echo "wrong output" + exit 1 +fi + echo "" echo "> stop unbound" kill_pid $UNBOUND_PID diff --git a/validator/val_anchor.c b/validator/val_anchor.c index 23cc195d0..0c9d0c4fd 100644 --- a/validator/val_anchor.c +++ b/validator/val_anchor.c @@ -1384,17 +1384,17 @@ void anchors_swap_tree(struct val_anchors* anchors, struct val_anchors* data) { rbtree_type* oldtree; - struct autr_global_data* oldautr; + rbtree_type oldprobe; if(!anchors || !data) return; /* If anchors is NULL, there is no validation. */ oldtree = anchors->tree; - oldautr = anchors->autr; + oldprobe = anchors->autr->probe; anchors->tree = data->tree; - anchors->autr = data->autr; + anchors->autr->probe = data->autr->probe; data->tree = oldtree; - data->autr = oldautr; + data->autr->probe = oldprobe; } From d81cf364f354df12d3397e850dac74441d0a6fb4 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 25 Jun 2024 16:24:55 +0200 Subject: [PATCH 099/125] - fast-reload, adjust cache sizes. --- Makefile.in | 2 +- daemon/remote.c | 25 +++++++++++++++++++++++++ doc/unbound-control.8.in | 7 ++++--- util/storage/lruhash.c | 30 ++++++++++++++++++++++++++++++ util/storage/lruhash.h | 10 ++++++++++ util/storage/slabhash.c | 9 +++++++++ util/storage/slabhash.h | 7 +++++++ validator/val_neg.c | 9 +++++++++ validator/val_neg.h | 7 +++++++ 9 files changed, 102 insertions(+), 4 deletions(-) diff --git a/Makefile.in b/Makefile.in index f0f39ebe9..61f57fe0e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1297,7 +1297,7 @@ remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h $(srcdir)/validator/val_anchor.h $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h \ $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_delegpt.h \ $(srcdir)/services/outside_network.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h \ - $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h $(srcdir)/util/ub_event.h $(srcdir)/util/tcp_conn_limit.h $(srcdir)/util/edns.h + $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h $(srcdir)/util/ub_event.h $(srcdir)/util/tcp_conn_limit.h $(srcdir)/util/edns.h $(srcdir)/validator/val_neg.h stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \ $(srcdir)/libunbound/unbound.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \ $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \ diff --git a/daemon/remote.c b/daemon/remote.c index d4018b83a..1247b6d30 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -81,6 +81,7 @@ #include "validator/val_kcache.h" #include "validator/val_kentry.h" #include "validator/val_anchor.h" +#include "validator/val_neg.h" #include "iterator/iterator.h" #include "iterator/iter_fwd.h" #include "iterator/iter_hints.h" @@ -5133,6 +5134,29 @@ fr_atomic_copy_cfg(struct config_file* oldcfg, struct config_file* cfg, } #endif /* ATOMIC_POINTER_LOCK_FREE */ +/** fast reload thread, adjust the cache sizes */ +static void +fr_adjust_cache(struct module_env* env) +{ + size_t inframem = env->cfg->infra_cache_numhosts * + (sizeof(struct infra_key) + sizeof(struct infra_data) + + INFRA_BYTES_NAME); + slabhash_adjust_size(env->msg_cache, env->cfg->msg_cache_size); + slabhash_adjust_size(&env->rrset_cache->table, + env->cfg->rrset_cache_size); + if(env->key_cache) + slabhash_adjust_size(env->key_cache->slab, + env->cfg->key_cache_size); + slabhash_adjust_size(env->infra_cache->hosts, inframem); + slabhash_adjust_size(env->infra_cache->domain_rates, + env->cfg->ratelimit_size); + slabhash_adjust_size(env->infra_cache->client_ip_rates, + env->cfg->ratelimit_size); + if(env->neg_cache) { + val_neg_adjust_size(env->neg_cache, env->cfg->neg_cache_size); + } +} + /** fast reload thread, reload config with putting the new config items * in place and swapping out the old items. */ static int @@ -5220,6 +5244,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, else dt_apply_logcfg(daemon->dtenv, env->cfg); } #endif + fr_adjust_cache(env); /* Set globals with new config. */ config_apply(env->cfg); diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 45534ee42..888f428ed 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -71,14 +71,15 @@ Not all options are changed, but it changes like forwards, stubs and local zones. Also access-control and interface-action and similar options, also tcp-connection-limits, views. It can reload some define-tag changes. It does not work with interface, outgoing-interface changes, also not with -remote-control, outgoing-port-permit, outgoing-port-avoid, msg-buffer-size -and statistics-interval changes. +remote-control, outgoing-port-permit, outgoing-port-avoid, msg-buffer-size, +slabs options and statistics-interval changes. .IP The fast reload also works on the options: insecure-lan-zones, domain-insecure, trust-anchor-file, trust-anchor, trusted-key-file, auto-trust-anchor-file, auth-zone and its options, rpz and its options, edns-strings, respip_set, view and its options, access-control options, tcp-connection-limit, -log-identity. +log-identity, infra-cache-numhosts, msg-cache-size, rrset-cache-size, +key-cache-size, ratelimit-size, neg-cache-size. .IP For dnstap, the options can be changed: dnstap-log-resolver-query-messages, dnstap-log-resolver-response-messages, dnstap-log-client-query-messages, diff --git a/util/storage/lruhash.c b/util/storage/lruhash.c index aba9fcc1d..028b199aa 100644 --- a/util/storage/lruhash.c +++ b/util/storage/lruhash.c @@ -562,6 +562,36 @@ lruhash_update_space_used(struct lruhash* table, void* cb_arg, int diff_size) } } +void lruhash_update_space_max(struct lruhash* table, void* cb_arg, size_t max) +{ + struct lruhash_entry *reclaimlist = NULL; + + fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc)); + fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc)); + fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc)); + fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc)); + + if(cb_arg == NULL) cb_arg = table->cb_arg; + + /* update space max */ + lock_quick_lock(&table->lock); + table->space_max = max; + + if(table->space_used > table->space_max) + reclaim_space(table, &reclaimlist); + + lock_quick_unlock(&table->lock); + + /* finish reclaim if any (outside of critical region) */ + while(reclaimlist) { + struct lruhash_entry* n = reclaimlist->overflow_next; + void* d = reclaimlist->data; + (*table->delkeyfunc)(reclaimlist->key, cb_arg); + (*table->deldatafunc)(d, cb_arg); + reclaimlist = n; + } +} + void lruhash_traverse(struct lruhash* h, int wr, void (*func)(struct lruhash_entry*, void*), void* arg) diff --git a/util/storage/lruhash.h b/util/storage/lruhash.h index 5ab488beb..667eba59c 100644 --- a/util/storage/lruhash.h +++ b/util/storage/lruhash.h @@ -314,6 +314,16 @@ void lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_type md); void lruhash_update_space_used(struct lruhash* table, void* cb_override, int diff_size); +/** + * Update the max space for the hashtable. + * + * @param table: hash table. + * @param cb_override: if not NULL overrides the cb_arg for deletefunc. + * @param max: the new max. + */ +void lruhash_update_space_max(struct lruhash* table, void* cb_override, + size_t max); + /************************* getdns functions ************************/ /*** these are used by getdns only and not by unbound. ***/ diff --git a/util/storage/slabhash.c b/util/storage/slabhash.c index 62396e16a..b2bee0838 100644 --- a/util/storage/slabhash.c +++ b/util/storage/slabhash.c @@ -267,3 +267,12 @@ void get_slabhash_stats(struct slabhash* sh, long long* num, long long* collisio if (collisions != NULL) *collisions = max_collisions; } + +void slabhash_adjust_size(struct slabhash* sl, size_t max) +{ + size_t space_max = max / sl->size; + size_t i; + for(i=0; isize; i++) { + lruhash_update_space_max(sl->array[i], NULL, space_max); + } +} diff --git a/util/storage/slabhash.h b/util/storage/slabhash.h index 089847d93..d6d94a940 100644 --- a/util/storage/slabhash.h +++ b/util/storage/slabhash.h @@ -221,6 +221,13 @@ size_t count_slabhash_entries(struct slabhash* table); void get_slabhash_stats(struct slabhash* table, long long* entries_count, long long* max_collisions); +/** + * Adjust size of slabhash memory max + * @param table: slabbed hash table + * @param max: new max memory + */ +void slabhash_adjust_size(struct slabhash* table, size_t max); + /* --- test representation --- */ /** test structure contains test key */ struct slabhash_testkey { diff --git a/validator/val_neg.c b/validator/val_neg.c index 52bc68387..feab475f9 100644 --- a/validator/val_neg.c +++ b/validator/val_neg.c @@ -1552,3 +1552,12 @@ val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, lock_basic_unlock(&neg->lock); return msg; } + +void +val_neg_adjust_size(struct val_neg_cache* neg, size_t max) +{ + lock_basic_lock(&neg->lock); + neg->max = max; + neg_make_space(neg, 0); + lock_basic_unlock(&neg->lock); +} diff --git a/validator/val_neg.h b/validator/val_neg.h index 5643ca331..27617dee5 100644 --- a/validator/val_neg.h +++ b/validator/val_neg.h @@ -299,4 +299,11 @@ struct val_neg_zone* neg_create_zone(struct val_neg_cache* neg, */ void val_neg_zone_take_inuse(struct val_neg_zone* zone); +/** + * Adjust the size of the negative cache. + * @param neg: negative cache + * @param max: new size for max mem. + */ +void val_neg_adjust_size(struct val_neg_cache* neg, size_t max); + #endif /* VALIDATOR_VAL_NEG_H */ From f08d584b1b81deed59877ca4ed509dc3123b2f1f Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 27 Jun 2024 10:29:24 +0200 Subject: [PATCH 100/125] - fast-reload, reload cache sizes when changed. --- daemon/remote.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 1247b6d30..e91ba15dc 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5136,23 +5136,31 @@ fr_atomic_copy_cfg(struct config_file* oldcfg, struct config_file* cfg, /** fast reload thread, adjust the cache sizes */ static void -fr_adjust_cache(struct module_env* env) -{ - size_t inframem = env->cfg->infra_cache_numhosts * - (sizeof(struct infra_key) + sizeof(struct infra_data) - + INFRA_BYTES_NAME); - slabhash_adjust_size(env->msg_cache, env->cfg->msg_cache_size); - slabhash_adjust_size(&env->rrset_cache->table, - env->cfg->rrset_cache_size); - if(env->key_cache) +fr_adjust_cache(struct module_env* env, struct config_file* oldcfg) +{ + if(env->cfg->msg_cache_size != oldcfg->msg_cache_size) + slabhash_adjust_size(env->msg_cache, env->cfg->msg_cache_size); + if(env->cfg->rrset_cache_size != oldcfg->rrset_cache_size) + slabhash_adjust_size(&env->rrset_cache->table, + env->cfg->rrset_cache_size); + if(env->key_cache && + env->cfg->key_cache_size != oldcfg->key_cache_size) slabhash_adjust_size(env->key_cache->slab, env->cfg->key_cache_size); - slabhash_adjust_size(env->infra_cache->hosts, inframem); - slabhash_adjust_size(env->infra_cache->domain_rates, - env->cfg->ratelimit_size); - slabhash_adjust_size(env->infra_cache->client_ip_rates, - env->cfg->ratelimit_size); - if(env->neg_cache) { + if(env->cfg->infra_cache_numhosts != oldcfg->infra_cache_numhosts) { + size_t inframem = env->cfg->infra_cache_numhosts * + (sizeof(struct infra_key) + sizeof(struct infra_data) + + INFRA_BYTES_NAME); + slabhash_adjust_size(env->infra_cache->hosts, inframem); + } + if(env->cfg->ratelimit_size != oldcfg->ratelimit_size) { + slabhash_adjust_size(env->infra_cache->domain_rates, + env->cfg->ratelimit_size); + slabhash_adjust_size(env->infra_cache->client_ip_rates, + env->cfg->ratelimit_size); + } + if(env->neg_cache && + env->cfg->neg_cache_size != oldcfg->neg_cache_size) { val_neg_adjust_size(env->neg_cache, env->cfg->neg_cache_size); } } @@ -5244,7 +5252,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, else dt_apply_logcfg(daemon->dtenv, env->cfg); } #endif - fr_adjust_cache(env); + fr_adjust_cache(env, ct->oldcfg); /* Set globals with new config. */ config_apply(env->cfg); From 51974874cb8064af5c2f5b1d440ebf7864f58b1d Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 27 Jun 2024 12:29:10 +0200 Subject: [PATCH 101/125] - fast-reload, reload validator env changes. --- daemon/remote.c | 145 +++++++++++++++++++++++++++++++++++++++ doc/unbound-control.8.in | 5 ++ validator/validator.c | 78 +++++++++++++-------- validator/validator.h | 23 +++++++ 4 files changed, 224 insertions(+), 27 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index e91ba15dc..67e5fd1cb 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3967,6 +3967,12 @@ struct fast_reload_construct { struct edns_strings* edns_strings; /** construct for trust anchors */ struct val_anchors* anchors; + /** construct for nsec3 key size */ + size_t* nsec3_keysize; + /** construct for nsec3 max iter */ + size_t* nsec3_maxiter; + /** construct for nsec3 keyiter count */ + int nsec3_keyiter_count; /** storage for the old configuration elements. The outer struct * is allocated with malloc here, the items are from config. */ struct config_file* oldcfg; @@ -4069,6 +4075,87 @@ fr_check_tag_defines(struct fast_reload_thread* fr, struct config_file* newcfg) return 1; } +/** fast reload thread, check if config item has changed, if not add to + * the explanatory string. */ +static void +fr_check_changed_cfg(int cmp, const char* desc, char* str, size_t len) +{ + if(!cmp) { + size_t slen = strlen(str); + size_t desclen = strlen(desc); + if(slen == 0) { + snprintf(str, len, "%s", desc); + return; + } + if(len - slen < desclen+2) + return; /* It does not fit */ + snprintf(str+slen, len-slen, " %s", desc); + } +} + +/** fast reload thread, check if config string has changed, checks NULLs. */ +static void +fr_check_changed_cfg_str(char* cmp1, char* cmp2, const char* desc, char* str, + size_t len) +{ + if((!cmp1 && cmp2) || + (cmp1 && !cmp2) || + (cmp1 && cmp2 && strcmp(cmp1, cmp2) != 0)) { + fr_check_changed_cfg(0, desc, str, len); + } +} + +/** fast reload thread, check nopause config items */ +static int +fr_check_nopause_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) +{ + char changed_str[1024]; + struct config_file* cfg = fr->worker->env.cfg; + if(!fr->fr_nopause) + return 1; /* The nopause is not enabled, so no problem. */ + changed_str[0]=0; + + /* Check for val_env. */ + fr_check_changed_cfg(cfg->bogus_ttl != newcfg->bogus_ttl, + "val-bogus-ttl", changed_str, sizeof(changed_str)); + fr_check_changed_cfg( + cfg->val_date_override != newcfg->val_date_override, + "val-date-override", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->val_sig_skew_min != newcfg->val_sig_skew_min, + "val-sig-skew-min", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->val_sig_skew_max != newcfg->val_sig_skew_max, + "val-sig-skew-max", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->val_max_restart != newcfg->val_max_restart, + "val-max-restart", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(strcmp(cfg->val_nsec3_key_iterations, + newcfg->val_nsec3_key_iterations) != 0, + "val-nsec3-keysize-iterations", changed_str, + sizeof(changed_str)); + + /* Check for dnstap. */ + fr_check_changed_cfg( + cfg->dnstap_send_identity != newcfg->dnstap_send_identity, + "dnstap-send-identity", changed_str, sizeof(changed_str)); + fr_check_changed_cfg( + cfg->dnstap_send_version != newcfg->dnstap_send_version, + "dnstap-send-version", changed_str, sizeof(changed_str)); + fr_check_changed_cfg_str(cfg->dnstap_identity, newcfg->dnstap_identity, + "dnstap-identity", changed_str, sizeof(changed_str)); + fr_check_changed_cfg_str(cfg->dnstap_version, newcfg->dnstap_version, + "dnstap-version", changed_str, sizeof(changed_str)); + + if(changed_str[0] != 0) { + /* The new config changes some items that need a pause, + * to be able to update the variables. */ + if(!fr_output_printf(fr, "The config changes items that need " + "the +p option disabled: %s\n", changed_str)) + return 0; + fr_send_notification(fr, fast_reload_notification_printout); + return 0; + } + return 1; +} + /** fast reload thread, clear construct information, deletes items */ static void fr_construct_clear(struct fast_reload_construct* ct) @@ -4086,6 +4173,8 @@ fr_construct_clear(struct fast_reload_construct* ct) edns_strings_delete(ct->edns_strings); anchors_delete(ct->anchors); views_delete(ct->views); + free(ct->nsec3_keysize); + free(ct->nsec3_maxiter); /* Delete the log identity here so that the global value is not * reset by config_delete. */ if(ct->oldcfg && ct->oldcfg->log_identity) { @@ -4716,6 +4805,15 @@ fr_construct_from_config(struct fast_reload_thread* fr, return 1; } + if(!val_env_parse_key_iter(newcfg->val_nsec3_key_iterations, + &ct->nsec3_keysize, &ct->nsec3_maxiter, + &ct->nsec3_keyiter_count)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + if(!(ct->oldcfg = (struct config_file*)calloc(1, sizeof(*ct->oldcfg)))) { fr_construct_clear(ct); @@ -5165,6 +5263,46 @@ fr_adjust_cache(struct module_env* env, struct config_file* oldcfg) } } +/** fast reload thread, adjust the validator env */ +static void +fr_adjust_val_env(struct module_env* env, struct fast_reload_construct* ct, + struct config_file* oldcfg) +{ + int m; + struct val_env* val_env = NULL; + if(env->cfg->bogus_ttl == oldcfg->bogus_ttl && + env->cfg->val_date_override == oldcfg->val_date_override && + env->cfg->val_sig_skew_min == oldcfg->val_sig_skew_min && + env->cfg->val_sig_skew_max == oldcfg->val_sig_skew_max && + env->cfg->val_max_restart == oldcfg->val_max_restart && + strcmp(env->cfg->val_nsec3_key_iterations, + oldcfg->val_nsec3_key_iterations) == 0) + return; /* no changes */ + + /* Because the validator env is not locked, the update cannot happen + * when fr nopause is used. Without it the fast reload pauses the + * other threads, so they are not currently using the structure. */ + m = modstack_find(&env->mesh->mods, "validator"); + if(m != -1) val_env = (struct val_env*)env->modinfo[m]; + if(val_env) { + /* Swap the arrays so that the delete happens afterwards. */ + size_t* oldkeysize = val_env->nsec3_keysize; + size_t* oldmaxiter = val_env->nsec3_maxiter; + val_env->nsec3_keysize = NULL; + val_env->nsec3_maxiter = NULL; + val_env_apply_cfg(val_env, env->cfg, ct->nsec3_keysize, + ct->nsec3_maxiter, ct->nsec3_keyiter_count); + ct->nsec3_keysize = oldkeysize; + ct->nsec3_maxiter = oldmaxiter; + if(env->neg_cache) { + lock_basic_lock(&env->neg_cache->lock); + env->neg_cache->nsec3_max_iter = val_env-> + nsec3_maxiter[val_env->nsec3_keyiter_count-1]; + lock_basic_unlock(&env->neg_cache->lock); + } + } +} + /** fast reload thread, reload config with putting the new config items * in place and swapping out the old items. */ static int @@ -5253,6 +5391,9 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, } #endif fr_adjust_cache(env, ct->oldcfg); + if(!fr->fr_nopause) { + fr_adjust_val_env(env, ct, ct->oldcfg); + } /* Set globals with new config. */ config_apply(env->cfg); @@ -5384,6 +5525,10 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, config_delete(newcfg); return 0; } + if(!fr_check_nopause_cfg(fr, newcfg)) { + config_delete(newcfg); + return 0; + } if(fr_poll_for_quit(fr)) { config_delete(newcfg); return 1; diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 888f428ed..cf13c6827 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -111,6 +111,11 @@ contacted to make them release resources, this makes the delete timing a little longer, and takes up time from the remote control servicing worker thread. .IP +With the nopause option, the reload does not work to reload some options, +that fast reload works on without the nopause option: val-bogus-ttl, +val-date-override, val-sig-key-min, val-sig-skew-max, val-max-restart, +val-nsec3-keysize-iterations. +.IP The '+d' option makes the reload drop queries that the worker threads are working on. This is like flush_requestlist. Without it the queries are kept so that users keep getting answers for those queries that are currently diff --git a/validator/validator.c b/validator/validator.c index ec656db12..1523b893b 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -90,50 +90,84 @@ update_reason_bogus(struct reply_info* rep, sldns_ede_code reason_bogus) /** fill up nsec3 key iterations config entry */ static int -fill_nsec3_iter(struct val_env* ve, char* s, int c) +fill_nsec3_iter(size_t** keysize, size_t** maxiter, char* s, int c) { char* e; int i; - free(ve->nsec3_keysize); - free(ve->nsec3_maxiter); - ve->nsec3_keysize = (size_t*)calloc(sizeof(size_t), (size_t)c); - ve->nsec3_maxiter = (size_t*)calloc(sizeof(size_t), (size_t)c); - if(!ve->nsec3_keysize || !ve->nsec3_maxiter) { + *keysize = (size_t*)calloc(sizeof(size_t), (size_t)c); + *maxiter = (size_t*)calloc(sizeof(size_t), (size_t)c); + if(!*keysize || !*maxiter) { + free(*keysize); + free(*maxiter); log_err("out of memory"); return 0; } for(i=0; insec3_keysize[i] = (size_t)strtol(s, &e, 10); + (*keysize)[i] = (size_t)strtol(s, &e, 10); if(s == e) { log_err("cannot parse: %s", s); return 0; } s = e; - ve->nsec3_maxiter[i] = (size_t)strtol(s, &e, 10); + (*maxiter)[i] = (size_t)strtol(s, &e, 10); if(s == e) { log_err("cannot parse: %s", s); return 0; } s = e; - if(i>0 && ve->nsec3_keysize[i-1] >= ve->nsec3_keysize[i]) { + if(i>0 && (*keysize)[i-1] >= (*keysize)[i]) { log_err("nsec3 key iterations not ascending: %d %d", - (int)ve->nsec3_keysize[i-1], - (int)ve->nsec3_keysize[i]); + (int)(*keysize)[i-1], (int)(*keysize)[i]); return 0; } verbose(VERB_ALGO, "validator nsec3cfg keysz %d mxiter %d", - (int)ve->nsec3_keysize[i], (int)ve->nsec3_maxiter[i]); + (int)(*keysize)[i], (int)(*maxiter)[i]); } return 1; } +int +val_env_parse_key_iter(char* val_nsec3_key_iterations, size_t** keysize, + size_t** maxiter, int* keyiter_count) +{ + int c; + c = cfg_count_numbers(val_nsec3_key_iterations); + if(c < 1 || (c&1)) { + log_err("validator: unparsable or odd nsec3 key " + "iterations: %s", val_nsec3_key_iterations); + return 0; + } + *keyiter_count = c/2; + if(!fill_nsec3_iter(keysize, maxiter, val_nsec3_key_iterations, c/2)) { + log_err("validator: cannot apply nsec3 key iterations"); + return 0; + } + return 1; +} + +void +val_env_apply_cfg(struct val_env* val_env, struct config_file* cfg, + size_t* keysize, size_t* maxiter, int keyiter_count) +{ + free(val_env->nsec3_keysize); + free(val_env->nsec3_maxiter); + val_env->nsec3_keysize = keysize; + val_env->nsec3_maxiter = maxiter; + val_env->nsec3_keyiter_count = keyiter_count; + val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl; + val_env->date_override = cfg->val_date_override; + val_env->skew_min = cfg->val_sig_skew_min; + val_env->skew_max = cfg->val_sig_skew_max; + val_env->max_restart = cfg->val_max_restart; +} + /** apply config settings to validator */ static int val_apply_cfg(struct module_env* env, struct val_env* val_env, struct config_file* cfg) { - int c; - val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl; + size_t* keysize=NULL, *maxiter=NULL; + int keyiter_count = 0; if(!env->anchors) env->anchors = anchors_create(); if(!env->anchors) { @@ -153,21 +187,11 @@ val_apply_cfg(struct module_env* env, struct val_env* val_env, log_err("validator: error in trustanchors config"); return 0; } - val_env->date_override = cfg->val_date_override; - val_env->skew_min = cfg->val_sig_skew_min; - val_env->skew_max = cfg->val_sig_skew_max; - val_env->max_restart = cfg->val_max_restart; - c = cfg_count_numbers(cfg->val_nsec3_key_iterations); - if(c < 1 || (c&1)) { - log_err("validator: unparsable or odd nsec3 key " - "iterations: %s", cfg->val_nsec3_key_iterations); - return 0; - } - val_env->nsec3_keyiter_count = c/2; - if(!fill_nsec3_iter(val_env, cfg->val_nsec3_key_iterations, c/2)) { - log_err("validator: cannot apply nsec3 key iterations"); + if(!val_env_parse_key_iter(cfg->val_nsec3_key_iterations, + &keysize, &maxiter, &keyiter_count)) { return 0; } + val_env_apply_cfg(val_env, cfg, keysize, maxiter, keyiter_count); if (env->neg_cache) val_env->neg_cache = env->neg_cache; if(!val_env->neg_cache) diff --git a/validator/validator.h b/validator/validator.h index 72f44b16e..33b0c1f9a 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -52,6 +52,7 @@ struct key_entry_key; struct val_neg_cache; struct config_strlist; struct comm_timer; +struct config_file; /** * This is the TTL to use when a trust anchor fails to prime. A trust anchor @@ -280,4 +281,26 @@ size_t val_get_mem(struct module_env* env, int id); /** Timer callback for msg signatures continue timer */ void validate_suspend_timer_cb(void* arg); +/** + * Parse the val_nsec3_key_iterations string. + * @param val_nsec3_key_iterations: the string with nsec3 iterations config. + * @param keysize: returns malloced key size array on success. + * @param maxiter: returns malloced max iterations array on success. + * @param keyiter_count: returns size of keysize and maxiter arrays. + * @return false if it does not parse correctly. + */ +int val_env_parse_key_iter(char* val_nsec3_key_iterations, size_t** keysize, + size_t** maxiter, int* keyiter_count); + +/** + * Apply config to validator env + * @param val_env: validator env. + * @param cfg: config + * @param keysize: nsec3 key size array. + * @param maxiter: nsec3 max iterations array. + * @param keyiter_count: size of keysize and maxiter arrays. + */ +void val_env_apply_cfg(struct val_env* val_env, struct config_file* cfg, + size_t* keysize, size_t* maxiter, int keyiter_count); + #endif /* VALIDATOR_VALIDATOR_H */ From e598005194cecd8b55e951cb2a5e278d0ff6e67f Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 27 Jun 2024 14:11:10 +0200 Subject: [PATCH 102/125] - fast-reload, reload mesh changes. --- daemon/remote.c | 18 +++++++++++++++++- doc/unbound-control.8.in | 3 ++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 67e5fd1cb..812baaaf2 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6256,6 +6256,22 @@ fr_poll_for_reload_start(struct fast_reload_thread* fr) } } +/** Pick up the worker mesh changes, after fast reload. */ +void +fr_worker_pickup_mesh(struct worker* worker) +{ + struct mesh_area* mesh = worker->env.mesh; + struct config_file* cfg = worker->env.cfg; + mesh->use_response_ip = worker->daemon->use_response_ip; + mesh->use_rpz = worker->daemon->use_rpz; + mesh->max_reply_states = cfg->num_queries_per_thread; + mesh->max_forever_states = (mesh->max_reply_states+1)/2; +#ifndef S_SPLINT_S + mesh->jostle_max.tv_sec = (time_t)(cfg->jostle_time / 1000); + mesh->jostle_max.tv_usec = (time_t)((cfg->jostle_time % 1000)*1000); +#endif +} + /** * Remove the old tcl_addr entries from the open connections. * They are only incremented when an accept is performed on a tcp comm point. @@ -6556,7 +6572,7 @@ fast_reload_worker_pickup_changes(struct worker* worker) * up the new information. But in the mean time, the reload has * swapped in trees, and the worker has been running with the * older information for some time. */ - worker->env.mesh->use_response_ip = worker->daemon->use_response_ip; + fr_worker_pickup_mesh(worker); /* If the tcp connection limit has changed, the open connections * need to remove their reference for the old tcp limits counters. */ diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index cf13c6827..0cb44b7ab 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -79,7 +79,8 @@ trust-anchor-file, trust-anchor, trusted-key-file, auto-trust-anchor-file, auth-zone and its options, rpz and its options, edns-strings, respip_set, view and its options, access-control options, tcp-connection-limit, log-identity, infra-cache-numhosts, msg-cache-size, rrset-cache-size, -key-cache-size, ratelimit-size, neg-cache-size. +key-cache-size, ratelimit-size, neg-cache-size, num-queries-per-thread, +jostle-timeout. .IP For dnstap, the options can be changed: dnstap-log-resolver-query-messages, dnstap-log-resolver-response-messages, dnstap-log-client-query-messages, From ca975bd1b34a804b490dcdc5554f13d75547566d Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 27 Jun 2024 15:32:53 +0200 Subject: [PATCH 103/125] - fast-reload, check for incompatible changes. --- daemon/remote.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index 812baaaf2..3462d1adb 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4105,6 +4105,67 @@ fr_check_changed_cfg_str(char* cmp1, char* cmp2, const char* desc, char* str, } } +/** fast reload thread, check compatible config items */ +static int +fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) +{ + char changed_str[1024]; + struct config_file* cfg = fr->worker->env.cfg; + changed_str[0]=0; + + /* Find incompatible options, and if so, print an error. */ + fr_check_changed_cfg(cfg->num_threads != newcfg->num_threads, + "num-threads", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->do_ip4 != newcfg->do_ip4, + "do-ip4", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->do_ip6 != newcfg->do_ip6, + "do-ip6", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->do_udp != newcfg->do_udp, + "do-udp", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->do_tcp != newcfg->do_tcp, + "do-tcp", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->port != newcfg->port, + "port", changed_str, sizeof(changed_str)); + fr_check_changed_cfg( + cfg->outgoing_num_ports != newcfg->outgoing_num_ports, + "outgoing_num_ports", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->outgoing_num_tcp != newcfg->outgoing_num_tcp, + "outgoing_num_tcp", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->incoming_num_tcp != newcfg->incoming_num_tcp, + "incoming_num_tcp", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->incoming_num_tcp != newcfg->incoming_num_tcp, + "incoming_num_tcp", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->num_out_ifs != newcfg->num_out_ifs, + "outgoing-interface", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->num_ifs != newcfg->num_ifs, + "interface", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->if_automatic != newcfg->if_automatic, + "interface-automatic", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->so_rcvbuf != newcfg->so_rcvbuf, + "so-rcvbuf", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->so_sndbuf != newcfg->so_sndbuf, + "so-sndbuf", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->so_reuseport != newcfg->so_reuseport, + "so-reuseport", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->ip_transparent != newcfg->ip_transparent, + "ip-transparent", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->ip_freebind != newcfg->ip_freebind, + "ip-freebind", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->udp_connect != newcfg->udp_connect, + "udp-connect", changed_str, sizeof(changed_str)); + + if(changed_str[0] != 0) { + /* The new config changes some items that do not work with + * fast reload. */ + if(!fr_output_printf(fr, "The config changes items that are " + "not compatible with fast_reload: %s\n", changed_str)) + return 0; + fr_send_notification(fr, fast_reload_notification_printout); + return 0; + } + return 1; +} + /** fast reload thread, check nopause config items */ static int fr_check_nopause_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) @@ -5525,6 +5586,10 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, config_delete(newcfg); return 0; } + if(!fr_check_compat_cfg(fr, newcfg)) { + config_delete(newcfg); + return 0; + } if(!fr_check_nopause_cfg(fr, newcfg)) { config_delete(newcfg); return 0; From d355c6b5b7b0528278ccebf8eead3be1939a0a50 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 27 Jun 2024 15:36:37 +0200 Subject: [PATCH 104/125] - fast-reload, improve error text for incompatible change. --- daemon/remote.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 3462d1adb..6469cb93d 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4158,7 +4158,8 @@ fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) /* The new config changes some items that do not work with * fast reload. */ if(!fr_output_printf(fr, "The config changes items that are " - "not compatible with fast_reload: %s\n", changed_str)) + "not compatible with fast_reload, perhaps do reload " + "or restart: %s\n", changed_str)) return 0; fr_send_notification(fr, fast_reload_notification_printout); return 0; From 1364d2b6a99bae33856a87297ac09797e8c7707b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 27 Jun 2024 15:40:28 +0200 Subject: [PATCH 105/125] - fast-reload, fix check config option compatibility. --- daemon/remote.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 6469cb93d..8e846b003 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4080,7 +4080,7 @@ fr_check_tag_defines(struct fast_reload_thread* fr, struct config_file* newcfg) static void fr_check_changed_cfg(int cmp, const char* desc, char* str, size_t len) { - if(!cmp) { + if(cmp) { size_t slen = strlen(str); size_t desclen = strlen(desc); if(slen == 0) { @@ -4101,7 +4101,7 @@ fr_check_changed_cfg_str(char* cmp1, char* cmp2, const char* desc, char* str, if((!cmp1 && cmp2) || (cmp1 && !cmp2) || (cmp1 && cmp2 && strcmp(cmp1, cmp2) != 0)) { - fr_check_changed_cfg(0, desc, str, len); + fr_check_changed_cfg(1, desc, str, len); } } From e841e4bf2b2a9698a1f0e8a4aa85c23a48704976 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 27 Jun 2024 15:55:38 +0200 Subject: [PATCH 106/125] - fast-reload, improve error text for nopause change. --- daemon/remote.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 8e846b003..4fa7aafaf 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4210,7 +4210,8 @@ fr_check_nopause_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) /* The new config changes some items that need a pause, * to be able to update the variables. */ if(!fr_output_printf(fr, "The config changes items that need " - "the +p option disabled: %s\n", changed_str)) + "the fast_reload +p option, for nopause, " + "disabled to be reloaded: %s\n", changed_str)) return 0; fr_send_notification(fr, fast_reload_notification_printout); return 0; From 0cccab55743f37d454121a50080d6d4d599015ec Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 27 Jun 2024 16:24:03 +0200 Subject: [PATCH 107/125] - fast-reload, fix spelling of incompatible options. --- daemon/remote.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 4fa7aafaf..04e85f300 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4128,13 +4128,11 @@ fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) "port", changed_str, sizeof(changed_str)); fr_check_changed_cfg( cfg->outgoing_num_ports != newcfg->outgoing_num_ports, - "outgoing_num_ports", changed_str, sizeof(changed_str)); + "outgoing-range", changed_str, sizeof(changed_str)); fr_check_changed_cfg(cfg->outgoing_num_tcp != newcfg->outgoing_num_tcp, - "outgoing_num_tcp", changed_str, sizeof(changed_str)); + "outgoing-num-tcp", changed_str, sizeof(changed_str)); fr_check_changed_cfg(cfg->incoming_num_tcp != newcfg->incoming_num_tcp, - "incoming_num_tcp", changed_str, sizeof(changed_str)); - fr_check_changed_cfg(cfg->incoming_num_tcp != newcfg->incoming_num_tcp, - "incoming_num_tcp", changed_str, sizeof(changed_str)); + "incoming-num-tcp", changed_str, sizeof(changed_str)); fr_check_changed_cfg(cfg->num_out_ifs != newcfg->num_out_ifs, "outgoing-interface", changed_str, sizeof(changed_str)); fr_check_changed_cfg(cfg->num_ifs != newcfg->num_ifs, From a4dbc8cc815ef266fff82145cf127671a36f02eb Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 Jul 2024 13:36:37 +0200 Subject: [PATCH 108/125] - fast-reload, reload target-fetch-policy, outbound-msg-retry, max-sent-count and max-query-restarts. --- Makefile.in | 2 +- daemon/remote.c | 50 ++++++++++++++++++++++++++++++++++++++++ doc/unbound-control.8.in | 3 ++- iterator/iter_utils.c | 34 ++++++++++++++++----------- iterator/iter_utils.h | 10 ++++++++ 5 files changed, 83 insertions(+), 16 deletions(-) diff --git a/Makefile.in b/Makefile.in index 61f57fe0e..11af92b0b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1297,7 +1297,7 @@ remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h $(srcdir)/validator/val_anchor.h $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h \ $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_delegpt.h \ $(srcdir)/services/outside_network.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h \ - $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h $(srcdir)/util/ub_event.h $(srcdir)/util/tcp_conn_limit.h $(srcdir)/util/edns.h $(srcdir)/validator/val_neg.h + $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h $(srcdir)/util/ub_event.h $(srcdir)/util/tcp_conn_limit.h $(srcdir)/util/edns.h $(srcdir)/validator/val_neg.h $(srcdir)/iterator/iter_utils.h stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \ $(srcdir)/libunbound/unbound.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \ $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \ diff --git a/daemon/remote.c b/daemon/remote.c index 04e85f300..83db0f5e5 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -86,6 +86,7 @@ #include "iterator/iter_fwd.h" #include "iterator/iter_hints.h" #include "iterator/iter_delegpt.h" +#include "iterator/iter_utils.h" #include "services/outbound_list.h" #include "services/outside_network.h" #include "sldns/str2wire.h" @@ -3973,6 +3974,10 @@ struct fast_reload_construct { size_t* nsec3_maxiter; /** construct for nsec3 keyiter count */ int nsec3_keyiter_count; + /** construct for target fetch policy */ + int* target_fetch_policy; + /** construct for max dependency depth */ + int max_dependency_depth; /** storage for the old configuration elements. The outer struct * is allocated with malloc here, the items are from config. */ struct config_file* oldcfg; @@ -4236,6 +4241,7 @@ fr_construct_clear(struct fast_reload_construct* ct) views_delete(ct->views); free(ct->nsec3_keysize); free(ct->nsec3_maxiter); + free(ct->target_fetch_policy); /* Delete the log identity here so that the global value is not * reset by config_delete. */ if(ct->oldcfg && ct->oldcfg->log_identity) { @@ -4875,6 +4881,14 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; + if(!read_fetch_policy(&ct->target_fetch_policy, + &ct->max_dependency_depth, newcfg->target_fetch_policy)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + if(!(ct->oldcfg = (struct config_file*)calloc(1, sizeof(*ct->oldcfg)))) { fr_construct_clear(ct); @@ -5324,6 +5338,41 @@ fr_adjust_cache(struct module_env* env, struct config_file* oldcfg) } } +/** fast reload thread, adjust the iterator env */ +static void +fr_adjust_iter_env(struct module_env* env, struct fast_reload_construct* ct, + struct config_file* oldcfg) +{ + int m; + struct iter_env* iter_env = NULL; + if(env->cfg->outbound_msg_retry == oldcfg->outbound_msg_retry && + env->cfg->max_sent_count == oldcfg->max_sent_count && + env->cfg->max_query_restarts == oldcfg->max_query_restarts && + strcmp(env->cfg->target_fetch_policy, + oldcfg->target_fetch_policy) == 0) + return; /* no changes */ + + /* Because the iterator env is not locked, the update cannot happen + * when fr nopause is used. Without it the fast reload pauses the + * other threads, so they are not currently using the structure. */ + m = modstack_find(&env->mesh->mods, "iterator"); + if(m != -1) iter_env = (struct iter_env*)env->modinfo[m]; + if(iter_env) { + /* Swap the data so that the delete happens afterwards. */ + int* oldtargetfetchpolicy = iter_env->target_fetch_policy; + int oldmaxdependencydepth = iter_env->max_dependency_depth; + + iter_env->target_fetch_policy = ct->target_fetch_policy; + iter_env->max_dependency_depth = ct->max_dependency_depth; + iter_env->outbound_msg_retry = env->cfg->outbound_msg_retry; + iter_env->max_sent_count = env->cfg->max_sent_count; + iter_env->max_query_restarts = env->cfg->max_query_restarts; + + ct->target_fetch_policy = oldtargetfetchpolicy; + ct->max_dependency_depth = oldmaxdependencydepth; + } +} + /** fast reload thread, adjust the validator env */ static void fr_adjust_val_env(struct module_env* env, struct fast_reload_construct* ct, @@ -5453,6 +5502,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, #endif fr_adjust_cache(env, ct->oldcfg); if(!fr->fr_nopause) { + fr_adjust_iter_env(env, ct, ct->oldcfg); fr_adjust_val_env(env, ct, ct->oldcfg); } diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 0cb44b7ab..b4fcfbb6e 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -115,7 +115,8 @@ worker thread. With the nopause option, the reload does not work to reload some options, that fast reload works on without the nopause option: val-bogus-ttl, val-date-override, val-sig-key-min, val-sig-skew-max, val-max-restart, -val-nsec3-keysize-iterations. +val-nsec3-keysize-iterations, target-fetch-policy, outbound-msg-retry, +max-sent-count, max-query-restarts. .IP The '+d' option makes the reload drop queries that the worker threads are working on. This is like flush_requestlist. Without it the queries are kept diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index 1b4f5f6eb..7770915ec 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -77,36 +77,41 @@ static const char DEFAULT_NAT64_PREFIX[] = "64:ff9b::/96"; /** fillup fetch policy array */ -static void -fetch_fill(struct iter_env* ie, const char* str) +static int +fetch_fill(int* target_fetch_policy, int max_dependency_depth, const char* str) { char* s = (char*)str, *e; int i; - for(i=0; imax_dependency_depth+1; i++) { - ie->target_fetch_policy[i] = strtol(s, &e, 10); - if(s == e) - fatal_exit("cannot parse fetch policy number %s", s); + for(i=0; imax_dependency_depth = count - 1; - ie->target_fetch_policy = (int*)calloc( - (size_t)ie->max_dependency_depth+1, sizeof(int)); - if(!ie->target_fetch_policy) { + *max_dependency_depth = count - 1; + *target_fetch_policy = (int*)calloc( + (size_t)(*max_dependency_depth)+1, sizeof(int)); + if(!*target_fetch_policy) { log_err("alloc fetch policy: out of memory"); return 0; } - fetch_fill(ie, str); + if(!fetch_fill(*target_fetch_policy, *max_dependency_depth, str)) + return 0; return 1; } @@ -150,7 +155,8 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg) const char *nat64_prefix; int i; /* target fetch policy */ - if(!read_fetch_policy(iter_env, cfg->target_fetch_policy)) + if(!read_fetch_policy(&iter_env->target_fetch_policy, + &iter_env->max_dependency_depth, cfg->target_fetch_policy)) return 0; for(i=0; imax_dependency_depth+1; i++) verbose(VERB_QUERY, "target fetch policy for level %d is %d", diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h index 4024629e6..af8e8ba03 100644 --- a/iterator/iter_utils.h +++ b/iterator/iter_utils.h @@ -428,4 +428,14 @@ int iter_stub_fwd_no_cache(struct module_qstate *qstate, void iterator_set_ip46_support(struct module_stack* mods, struct module_env* env, struct outside_network* outnet); +/** + * Read config string that represents the target fetch policy. + * @param target_fetch_policy: alloced on return. + * @param max_dependency_depth: set on return. + * @param str: the config string + * @return false on failure. + */ +int read_fetch_policy(int** target_fetch_policy, int* max_dependency_depth, + const char* str); + #endif /* ITERATOR_ITER_UTILS_H */ From 795b0b2c1061cfc361520f2be25f7528ba1bc7c0 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 Jul 2024 13:51:55 +0200 Subject: [PATCH 109/125] - fast-reload, check nopause config change for target-fetch-policy. --- daemon/remote.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index 264fbeedd..6c05d2bcb 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4182,6 +4182,19 @@ fr_check_nopause_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) return 1; /* The nopause is not enabled, so no problem. */ changed_str[0]=0; + /* Check for iter_env */ + fr_check_changed_cfg( + cfg->outbound_msg_retry != newcfg->outbound_msg_retry, + "outbound-msg-retry", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->max_sent_count != newcfg->max_sent_count, + "max-sent-count", changed_str, sizeof(changed_str)); + fr_check_changed_cfg( + cfg->max_query_restarts != newcfg->max_query_restarts, + "max-query-restarts", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(strcmp(cfg->target_fetch_policy, + newcfg->target_fetch_policy) != 0, + "target-fetch-policy", changed_str, sizeof(changed_str)); + /* Check for val_env. */ fr_check_changed_cfg(cfg->bogus_ttl != newcfg->bogus_ttl, "val-bogus-ttl", changed_str, sizeof(changed_str)); From 2b2a210b1bf5665c5abbf238f4ec4c3d471e1b54 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 Jul 2024 14:18:21 +0200 Subject: [PATCH 110/125] - fast-reload, reload do-not-query-address, private-address and capt-exempt. --- Makefile.in | 4 ++- daemon/remote.c | 59 +++++++++++++++++++++++++++++++++------- doc/unbound-control.8.in | 3 +- iterator/iter_utils.c | 33 ++++++++++++++++++++-- iterator/iter_utils.h | 19 +++++++++++++ iterator/iterator.c | 15 +--------- 6 files changed, 104 insertions(+), 29 deletions(-) diff --git a/Makefile.in b/Makefile.in index 11af92b0b..5d031e846 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1297,7 +1297,9 @@ remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h $(srcdir)/validator/val_anchor.h $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h \ $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_delegpt.h \ $(srcdir)/services/outside_network.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h \ - $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h $(srcdir)/util/ub_event.h $(srcdir)/util/tcp_conn_limit.h $(srcdir)/util/edns.h $(srcdir)/validator/val_neg.h $(srcdir)/iterator/iter_utils.h + $(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h $(srcdir)/util/ub_event.h \ + $(srcdir)/util/tcp_conn_limit.h $(srcdir)/util/edns.h $(srcdir)/validator/val_neg.h \ + $(srcdir)/iterator/iter_utils.h $(srcdir)/iterator/iter_donotq.h $(srcdir)/iterator/iter_priv.h stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \ $(srcdir)/libunbound/unbound.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \ $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \ diff --git a/daemon/remote.c b/daemon/remote.c index 6c05d2bcb..05be5f6e7 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -87,6 +87,8 @@ #include "iterator/iter_hints.h" #include "iterator/iter_delegpt.h" #include "iterator/iter_utils.h" +#include "iterator/iter_donotq.h" +#include "iterator/iter_priv.h" #include "services/outbound_list.h" #include "services/outside_network.h" #include "sldns/str2wire.h" @@ -3980,6 +3982,12 @@ struct fast_reload_construct { int* target_fetch_policy; /** construct for max dependency depth */ int max_dependency_depth; + /** construct for donotquery addresses */ + struct iter_donotq* donotq; + /** construct for private addresses and domains */ + struct iter_priv* priv; + /** construct whitelist for capsforid names */ + struct rbtree_type* caps_white; /** storage for the old configuration elements. The outer struct * is allocated with malloc here, the items are from config. */ struct config_file* oldcfg; @@ -4182,7 +4190,7 @@ fr_check_nopause_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) return 1; /* The nopause is not enabled, so no problem. */ changed_str[0]=0; - /* Check for iter_env */ + /* Check for iter_env. */ fr_check_changed_cfg( cfg->outbound_msg_retry != newcfg->outbound_msg_retry, "outbound-msg-retry", changed_str, sizeof(changed_str)); @@ -4257,6 +4265,9 @@ fr_construct_clear(struct fast_reload_construct* ct) free(ct->nsec3_keysize); free(ct->nsec3_maxiter); free(ct->target_fetch_policy); + donotq_delete(ct->donotq); + priv_delete(ct->priv); + caps_white_delete(ct->caps_white); /* Delete the log identity here so that the global value is not * reset by config_delete. */ if(ct->oldcfg && ct->oldcfg->log_identity) { @@ -4901,6 +4912,32 @@ fr_construct_from_config(struct fast_reload_thread* fr, fr_construct_clear(ct); return 0; } + if(!(ct->donotq = donotq_create())) { + fr_construct_clear(ct); + return 0; + } + if(!donotq_apply_cfg(ct->donotq, newcfg)) { + fr_construct_clear(ct); + return 0; + } + if(!(ct->priv = priv_create())) { + fr_construct_clear(ct); + return 0; + } + if(!priv_apply_cfg(ct->priv, newcfg)) { + fr_construct_clear(ct); + return 0; + } + if(newcfg->caps_whitelist) { + if(!(ct->caps_white = caps_white_create())) { + fr_construct_clear(ct); + return 0; + } + if(!caps_white_apply_cfg(ct->caps_white, newcfg)) { + fr_construct_clear(ct); + return 0; + } + } if(fr_poll_for_quit(fr)) return 1; @@ -5355,17 +5392,10 @@ fr_adjust_cache(struct module_env* env, struct config_file* oldcfg) /** fast reload thread, adjust the iterator env */ static void -fr_adjust_iter_env(struct module_env* env, struct fast_reload_construct* ct, - struct config_file* oldcfg) +fr_adjust_iter_env(struct module_env* env, struct fast_reload_construct* ct) { int m; struct iter_env* iter_env = NULL; - if(env->cfg->outbound_msg_retry == oldcfg->outbound_msg_retry && - env->cfg->max_sent_count == oldcfg->max_sent_count && - env->cfg->max_query_restarts == oldcfg->max_query_restarts && - strcmp(env->cfg->target_fetch_policy, - oldcfg->target_fetch_policy) == 0) - return; /* no changes */ /* Because the iterator env is not locked, the update cannot happen * when fr nopause is used. Without it the fast reload pauses the @@ -5376,15 +5406,24 @@ fr_adjust_iter_env(struct module_env* env, struct fast_reload_construct* ct, /* Swap the data so that the delete happens afterwards. */ int* oldtargetfetchpolicy = iter_env->target_fetch_policy; int oldmaxdependencydepth = iter_env->max_dependency_depth; + struct iter_donotq* olddonotq = iter_env->donotq; + struct iter_priv* oldpriv = iter_env->priv; + struct rbtree_type* oldcapswhite = iter_env->caps_white; iter_env->target_fetch_policy = ct->target_fetch_policy; iter_env->max_dependency_depth = ct->max_dependency_depth; + iter_env->donotq = ct->donotq; + iter_env->priv = ct->priv; + iter_env->caps_white = ct->caps_white; iter_env->outbound_msg_retry = env->cfg->outbound_msg_retry; iter_env->max_sent_count = env->cfg->max_sent_count; iter_env->max_query_restarts = env->cfg->max_query_restarts; ct->target_fetch_policy = oldtargetfetchpolicy; ct->max_dependency_depth = oldmaxdependencydepth; + ct->donotq = olddonotq; + ct->priv = oldpriv; + ct->caps_white = oldcapswhite; } } @@ -5517,7 +5556,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, #endif fr_adjust_cache(env, ct->oldcfg); if(!fr->fr_nopause) { - fr_adjust_iter_env(env, ct, ct->oldcfg); + fr_adjust_iter_env(env, ct); fr_adjust_val_env(env, ct, ct->oldcfg); } diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index b4fcfbb6e..b9cb93e96 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -116,7 +116,8 @@ With the nopause option, the reload does not work to reload some options, that fast reload works on without the nopause option: val-bogus-ttl, val-date-override, val-sig-key-min, val-sig-skew-max, val-max-restart, val-nsec3-keysize-iterations, target-fetch-policy, outbound-msg-retry, -max-sent-count, max-query-restarts. +max-sent-count, max-query-restarts, do-not-query-address, +do-not-query-localhost, private-address, private-domain, caps-exempt. .IP The '+d' option makes the reload drop queries that the worker threads are working on. This is like flush_requestlist. Without it the queries are kept diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index 7770915ec..e5d405f76 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -115,8 +115,35 @@ read_fetch_policy(int** target_fetch_policy, int* max_dependency_depth, return 1; } -/** apply config caps whitelist items to name tree */ -static int +struct rbtree_type* +caps_white_create(void) +{ + struct rbtree_type* caps_white = rbtree_create(name_tree_compare); + if(!caps_white) + log_err("out of memory"); + return caps_white; +} + +/** delete caps_whitelist element */ +static void +caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d)) +{ + if(n) { + free(((struct name_tree_node*)n)->name); + free(n); + } +} + +void +caps_white_delete(struct rbtree_type* caps_white) +{ + if(!caps_white) + return; + traverse_postorder(caps_white, caps_free, NULL); + free(caps_white); +} + +int caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg) { struct config_strlist* p; @@ -176,7 +203,7 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg) } if(cfg->caps_whitelist) { if(!iter_env->caps_white) - iter_env->caps_white = rbtree_create(name_tree_compare); + iter_env->caps_white = caps_white_create(); if(!iter_env->caps_white || !caps_white_apply_cfg( iter_env->caps_white, cfg)) { log_err("Could not set capsforid whitelist"); diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h index af8e8ba03..f53bf14c3 100644 --- a/iterator/iter_utils.h +++ b/iterator/iter_utils.h @@ -438,4 +438,23 @@ void iterator_set_ip46_support(struct module_stack* mods, int read_fetch_policy(int** target_fetch_policy, int* max_dependency_depth, const char* str); +/** + * Create caps exempt data structure. + * @return NULL on failure. + */ +struct rbtree_type* caps_white_create(void); + +/** + * Delete caps exempt data structure. + * @param caps_white: caps exempt tree. + */ +void caps_white_delete(struct rbtree_type* caps_white); + +/** + * Apply config caps whitelist items to name tree + * @param ntree: caps exempt tree. + * @param cfg: config with options. + */ +int caps_white_apply_cfg(struct rbtree_type* ntree, struct config_file* cfg); + #endif /* ITERATOR_ITER_UTILS_H */ diff --git a/iterator/iterator.c b/iterator/iterator.c index 5a99ab0d7..48c1d7e1b 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -103,16 +103,6 @@ iter_init(struct module_env* env, int id) return 1; } -/** delete caps_whitelist element */ -static void -caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d)) -{ - if(n) { - free(((struct name_tree_node*)n)->name); - free(n); - } -} - void iter_deinit(struct module_env* env, int id) { @@ -124,10 +114,7 @@ iter_deinit(struct module_env* env, int id) free(iter_env->target_fetch_policy); priv_delete(iter_env->priv); donotq_delete(iter_env->donotq); - if(iter_env->caps_white) { - traverse_postorder(iter_env->caps_white, caps_free, NULL); - free(iter_env->caps_white); - } + caps_white_delete(iter_env->caps_white); free(iter_env); env->modinfo[id] = NULL; } From c7f68d3e46db51d5f8c7a6d70eb4cb48f3cae54a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 Jul 2024 14:43:03 +0200 Subject: [PATCH 111/125] - fast-reload, check nopause config change for do-not-query-address, private-address and capt-exempt. --- daemon/remote.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 05be5f6e7..7dc0c3a30 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4113,13 +4113,35 @@ static void fr_check_changed_cfg_str(char* cmp1, char* cmp2, const char* desc, char* str, size_t len) { - if((!cmp1 && cmp2) || + if((!cmp1 && cmp2) || (cmp1 && !cmp2) || (cmp1 && cmp2 && strcmp(cmp1, cmp2) != 0)) { fr_check_changed_cfg(1, desc, str, len); } } +/** fast reload thread, check if config strlist has changed. */ +static void +fr_check_changed_cfg_strlist(struct config_strlist* cmp1, + struct config_strlist* cmp2, const char* desc, char* str, size_t len) +{ + struct config_strlist* p1 = cmp1, *p2 = cmp2; + while(p1 && p2) { + if((!p1->str && p2->str) || + (p1->str && !p2->str) || + (p1->str && p2->str && strcmp(p1->str, p2->str) != 0)) { + /* The strlist is different. */ + fr_check_changed_cfg(1, desc, str, len); + return; + } + p1 = p1->next; + p2 = p2->next; + } + if((!p1 && p2) || (p1 && !p2)) { + fr_check_changed_cfg(1, desc, str, len); + } +} + /** fast reload thread, check compatible config items */ static int fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) @@ -4202,6 +4224,21 @@ fr_check_nopause_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) fr_check_changed_cfg(strcmp(cfg->target_fetch_policy, newcfg->target_fetch_policy) != 0, "target-fetch-policy", changed_str, sizeof(changed_str)); + fr_check_changed_cfg( + cfg->donotquery_localhost != newcfg->donotquery_localhost, + "do-not-query-localhost", changed_str, sizeof(changed_str)); + fr_check_changed_cfg_strlist(cfg->donotqueryaddrs, + newcfg->donotqueryaddrs, "do-not-query-localhost", + changed_str, sizeof(changed_str)); + fr_check_changed_cfg_strlist(cfg->private_address, + newcfg->private_address, "private-address", + changed_str, sizeof(changed_str)); + fr_check_changed_cfg_strlist(cfg->private_domain, + newcfg->private_domain, "private-domain", + changed_str, sizeof(changed_str)); + fr_check_changed_cfg_strlist(cfg->caps_whitelist, + newcfg->caps_whitelist, "caps-exempt", + changed_str, sizeof(changed_str)); /* Check for val_env. */ fr_check_changed_cfg(cfg->bogus_ttl != newcfg->bogus_ttl, @@ -5396,6 +5433,10 @@ fr_adjust_iter_env(struct module_env* env, struct fast_reload_construct* ct) { int m; struct iter_env* iter_env = NULL; + /* There is no comparison here to see if no options changed and thus + * no swap is needed, the trees with addresses and domains can be + * large and that would take too long. Instead the trees are + * swapped in. */ /* Because the iterator env is not locked, the update cannot happen * when fr nopause is used. Without it the fast reload pauses the From 6618777a339d60ce0f17d973a164df8d00ab055a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 Jul 2024 15:01:10 +0200 Subject: [PATCH 112/125] - fast-reload, check fast reload not possible due to interface and outgoing-interface changes. --- daemon/remote.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index 7dc0c3a30..31a98a557 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4146,6 +4146,7 @@ fr_check_changed_cfg_strlist(struct config_strlist* cmp1, static int fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) { + int i; char changed_str[1024]; struct config_file* cfg = fr->worker->env.cfg; changed_str[0]=0; @@ -4172,8 +4173,20 @@ fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) "incoming-num-tcp", changed_str, sizeof(changed_str)); fr_check_changed_cfg(cfg->num_out_ifs != newcfg->num_out_ifs, "outgoing-interface", changed_str, sizeof(changed_str)); + if(cfg->num_out_ifs == newcfg->num_out_ifs) { + for(i=0; inum_out_ifs; i++) + fr_check_changed_cfg(strcmp(cfg->out_ifs[i], + newcfg->out_ifs[i]) != 0, "outgoing-interface", + changed_str, sizeof(changed_str)); + } fr_check_changed_cfg(cfg->num_ifs != newcfg->num_ifs, "interface", changed_str, sizeof(changed_str)); + if(cfg->num_ifs == newcfg->num_ifs) { + for(i=0; inum_ifs; i++) + fr_check_changed_cfg(strcmp(cfg->ifs[i], + newcfg->ifs[i]) != 0, "interface", + changed_str, sizeof(changed_str)); + } fr_check_changed_cfg(cfg->if_automatic != newcfg->if_automatic, "interface-automatic", changed_str, sizeof(changed_str)); fr_check_changed_cfg(cfg->so_rcvbuf != newcfg->so_rcvbuf, From 346037ab5a7902a6d28fdf0bf37319a90190320b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 Jul 2024 15:19:32 +0200 Subject: [PATCH 113/125] - fast-reload, reload nat64 settings. --- daemon/remote.c | 13 ++++++++++ doc/unbound-control.8.in | 3 ++- iterator/iter_utils.c | 54 ++++++++++++++++++++++++---------------- iterator/iter_utils.h | 9 +++++++ iterator/iterator.c | 24 +++++++++--------- iterator/iterator.h | 24 ++++++++++++------ 6 files changed, 84 insertions(+), 43 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 31a98a557..95334efbe 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3988,6 +3988,8 @@ struct fast_reload_construct { struct iter_priv* priv; /** construct whitelist for capsforid names */ struct rbtree_type* caps_white; + /** construct for nat64 */ + struct iter_nat64 nat64; /** storage for the old configuration elements. The outer struct * is allocated with malloc here, the items are from config. */ struct config_file* oldcfg; @@ -4252,6 +4254,10 @@ fr_check_nopause_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) fr_check_changed_cfg_strlist(cfg->caps_whitelist, newcfg->caps_whitelist, "caps-exempt", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->do_nat64 != newcfg->do_nat64, + "do-nat64", changed_str, sizeof(changed_str)); + fr_check_changed_cfg_str(cfg->nat64_prefix, newcfg->nat64_prefix, + "nat64-prefix", changed_str, sizeof(changed_str)); /* Check for val_env. */ fr_check_changed_cfg(cfg->bogus_ttl != newcfg->bogus_ttl, @@ -4988,6 +4994,10 @@ fr_construct_from_config(struct fast_reload_thread* fr, return 0; } } + if(!nat64_apply_cfg(&ct->nat64, newcfg)) { + fr_construct_clear(ct); + return 0; + } if(fr_poll_for_quit(fr)) return 1; @@ -5463,12 +5473,14 @@ fr_adjust_iter_env(struct module_env* env, struct fast_reload_construct* ct) struct iter_donotq* olddonotq = iter_env->donotq; struct iter_priv* oldpriv = iter_env->priv; struct rbtree_type* oldcapswhite = iter_env->caps_white; + struct iter_nat64 oldnat64 = iter_env->nat64; iter_env->target_fetch_policy = ct->target_fetch_policy; iter_env->max_dependency_depth = ct->max_dependency_depth; iter_env->donotq = ct->donotq; iter_env->priv = ct->priv; iter_env->caps_white = ct->caps_white; + iter_env->nat64 = ct->nat64; iter_env->outbound_msg_retry = env->cfg->outbound_msg_retry; iter_env->max_sent_count = env->cfg->max_sent_count; iter_env->max_query_restarts = env->cfg->max_query_restarts; @@ -5478,6 +5490,7 @@ fr_adjust_iter_env(struct module_env* env, struct fast_reload_construct* ct) ct->donotq = olddonotq; ct->priv = oldpriv; ct->caps_white = oldcapswhite; + ct->nat64 = oldnat64; } } diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index b9cb93e96..76f635fe5 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -117,7 +117,8 @@ that fast reload works on without the nopause option: val-bogus-ttl, val-date-override, val-sig-key-min, val-sig-skew-max, val-max-restart, val-nsec3-keysize-iterations, target-fetch-policy, outbound-msg-retry, max-sent-count, max-query-restarts, do-not-query-address, -do-not-query-localhost, private-address, private-domain, caps-exempt. +do-not-query-localhost, private-address, private-domain, caps-exempt, +nat64-prefix, do-nat64. .IP The '+d' option makes the reload drop queries that the worker threads are working on. This is like flush_requestlist. Without it the queries are kept diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index e5d405f76..179b02635 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -177,9 +177,37 @@ caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg) } int -iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg) +nat64_apply_cfg(struct iter_nat64* nat64, struct config_file* cfg) { const char *nat64_prefix; + + nat64_prefix = cfg->nat64_prefix; + if(!nat64_prefix) + nat64_prefix = cfg->dns64_prefix; + if(!nat64_prefix) + nat64_prefix = DEFAULT_NAT64_PREFIX; + if(!netblockstrtoaddr(nat64_prefix, 0, &nat64->nat64_prefix_addr, + &nat64->nat64_prefix_addrlen, &nat64->nat64_prefix_net)) { + log_err("cannot parse nat64-prefix netblock: %s", nat64_prefix); + return 0; + } + if(!addr_is_ip6(&nat64->nat64_prefix_addr, + nat64->nat64_prefix_addrlen)) { + log_err("nat64-prefix is not IPv6: %s", cfg->nat64_prefix); + return 0; + } + if(!prefixnet_is_nat64(nat64->nat64_prefix_net)) { + log_err("nat64-prefix length it not 32, 40, 48, 56, 64 or 96: %s", + nat64_prefix); + return 0; + } + nat64->use_nat64 = cfg->do_nat64; + return 1; +} + +int +iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg) +{ int i; /* target fetch policy */ if(!read_fetch_policy(&iter_env->target_fetch_policy, @@ -212,31 +240,13 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg) } - nat64_prefix = cfg->nat64_prefix; - if(!nat64_prefix) - nat64_prefix = cfg->dns64_prefix; - if(!nat64_prefix) - nat64_prefix = DEFAULT_NAT64_PREFIX; - if(!netblockstrtoaddr(nat64_prefix, 0, &iter_env->nat64_prefix_addr, - &iter_env->nat64_prefix_addrlen, - &iter_env->nat64_prefix_net)) { - log_err("cannot parse nat64-prefix netblock: %s", nat64_prefix); - return 0; - } - if(!addr_is_ip6(&iter_env->nat64_prefix_addr, - iter_env->nat64_prefix_addrlen)) { - log_err("nat64-prefix is not IPv6: %s", cfg->nat64_prefix); - return 0; - } - if(!prefixnet_is_nat64(iter_env->nat64_prefix_net)) { - log_err("nat64-prefix length it not 32, 40, 48, 56, 64 or 96: %s", - nat64_prefix); + if(!nat64_apply_cfg(&iter_env->nat64, cfg)) { + log_err("Could not setup nat64"); return 0; } iter_env->supports_ipv6 = cfg->do_ip6; iter_env->supports_ipv4 = cfg->do_ip4; - iter_env->use_nat64 = cfg->do_nat64; iter_env->outbound_msg_retry = cfg->outbound_msg_retry; iter_env->max_sent_count = cfg->max_sent_count; iter_env->max_query_restarts = cfg->max_query_restarts; @@ -303,7 +313,7 @@ iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env, if(!iter_env->supports_ipv6 && addr_is_ip6(&a->addr, a->addrlen)) { return -1; /* there is no ip6 available */ } - if(!iter_env->supports_ipv4 && !iter_env->use_nat64 && + if(!iter_env->supports_ipv4 && !iter_env->nat64.use_nat64 && !addr_is_ip6(&a->addr, a->addrlen)) { return -1; /* there is no ip4 available */ } diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h index f53bf14c3..a3f33a482 100644 --- a/iterator/iter_utils.h +++ b/iterator/iter_utils.h @@ -61,6 +61,7 @@ struct sock_list; struct ub_packed_rrset_key; struct module_stack; struct outside_network; +struct iter_nat64; /* max number of lookups in the cache for target nameserver names. * This stops, for large delegations, N*N lookups in the cache. */ @@ -457,4 +458,12 @@ void caps_white_delete(struct rbtree_type* caps_white); */ int caps_white_apply_cfg(struct rbtree_type* ntree, struct config_file* cfg); +/** + * Apply config for nat64 + * @param nat64: the nat64 state. + * @param cfg: config with options. + * @return false on failure. + */ +int nat64_apply_cfg(struct iter_nat64* nat64, struct config_file* cfg); + #endif /* ITERATOR_ITER_UTILS_H */ diff --git a/iterator/iterator.c b/iterator/iterator.c index 48c1d7e1b..7a06ad7d3 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -243,7 +243,7 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super) log_err("out of memory adding missing"); } delegpt_mark_neg(dpns, qstate->qinfo.qtype); - if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) && + if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->nat64.use_nat64)) && (dpns->got6 == 2 || !ie->supports_ipv6)) { dpns->resolved = 1; /* mark as failed */ target_count_increase_nx(super_iq, 1); @@ -1684,7 +1684,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, */ if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags, iq->dp, ie->supports_ipv4, ie->supports_ipv6, - ie->use_nat64)) { + ie->nat64.use_nat64)) { int have_dp = 0; if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &have_dp, &iq->dp, qstate->region)) { if(have_dp) { @@ -2048,7 +2048,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq, if(mesh_jostle_exceeded(qstate->env->mesh)) { /* If no ip4 query is possible, that makes * this ns resolved. */ - if(!((ie->supports_ipv4 || ie->use_nat64) && + if(!((ie->supports_ipv4 || ie->nat64.use_nat64) && ((ns->lame && !ns->done_pside4) || (!ns->lame && !ns->got4)))) { ns->resolved = 1; @@ -2057,7 +2057,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq, } } /* Send the A request. */ - if((ie->supports_ipv4 || ie->use_nat64) && + if((ie->supports_ipv4 || ie->nat64.use_nat64) && ((ns->lame && !ns->done_pside4) || (!ns->lame && !ns->got4))) { if(!generate_target_query(qstate, iq, id, @@ -2220,14 +2220,14 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, /* if this nameserver is at a delegation point, but that * delegation point is a stub and we cannot go higher, skip*/ if( ((ie->supports_ipv6 && !ns->done_pside6) || - ((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4)) && + ((ie->supports_ipv4 || ie->nat64.use_nat64) && !ns->done_pside4)) && !can_have_last_resort(qstate->env, ns->name, ns->namelen, iq->qchase.qclass, NULL, NULL, NULL)) { log_nametypeclass(VERB_ALGO, "cannot pside lookup ns " "because it is also a stub/forward,", ns->name, LDNS_RR_TYPE_NS, iq->qchase.qclass); if(ie->supports_ipv6) ns->done_pside6 = 1; - if(ie->supports_ipv4 || ie->use_nat64) ns->done_pside4 = 1; + if(ie->supports_ipv4 || ie->nat64.use_nat64) ns->done_pside4 = 1; continue; } /* query for parent-side A and AAAA for nameservers */ @@ -2252,7 +2252,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, return 0; } } - if((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4) { + if((ie->supports_ipv4 || ie->nat64.use_nat64) && !ns->done_pside4) { /* Send the A request. */ if(!generate_parentside_target_query(qstate, iq, id, ns->name, ns->namelen, @@ -2521,7 +2521,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, } if(!ie->supports_ipv6) delegpt_no_ipv6(iq->dp); - if(!ie->supports_ipv4 && !ie->use_nat64) + if(!ie->supports_ipv4 && !ie->nat64.use_nat64) delegpt_no_ipv4(iq->dp); delegpt_log(VERB_ALGO, iq->dp); @@ -3011,9 +3011,9 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, real_addr = target->addr; real_addrlen = target->addrlen; - if(ie->use_nat64 && target->addr.ss_family == AF_INET) { - addr_to_nat64(&target->addr, &ie->nat64_prefix_addr, - ie->nat64_prefix_addrlen, ie->nat64_prefix_net, + if(ie->nat64.use_nat64 && target->addr.ss_family == AF_INET) { + addr_to_nat64(&target->addr, &ie->nat64.nat64_prefix_addr, + ie->nat64.nat64_prefix_addrlen, ie->nat64.nat64_prefix_net, &real_addr, &real_addrlen); log_name_addr(VERB_QUERY, "applied NAT64:", iq->dp->name, &real_addr, real_addrlen); @@ -3812,7 +3812,7 @@ processTargetResponse(struct module_qstate* qstate, int id, } else { verbose(VERB_ALGO, "iterator TargetResponse failed"); delegpt_mark_neg(dpns, qstate->qinfo.qtype); - if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) && + if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->nat64.use_nat64)) && (dpns->got6 == 2 || !ie->supports_ipv6)) { dpns->resolved = 1; /* fail the target */ /* do not count cached answers */ diff --git a/iterator/iterator.h b/iterator/iterator.h index f748d47f6..1b8f3f0af 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -103,15 +103,9 @@ extern int BLACKLIST_PENALTY; #define EMPTY_NODATA_RETRY_COUNT 2 /** - * Global state for the iterator. + * Iterator global state for nat64. */ -struct iter_env { - /** A flag to indicate whether or not we have an IPv6 route */ - int supports_ipv6; - - /** A flag to indicate whether or not we have an IPv4 route */ - int supports_ipv4; - +struct iter_nat64 { /** A flag to locally apply NAT64 to make IPv4 addrs into IPv6 */ int use_nat64; @@ -123,6 +117,20 @@ struct iter_env { /** CIDR mask length of NAT64 prefix */ int nat64_prefix_net; +}; + +/** + * Global state for the iterator. + */ +struct iter_env { + /** A flag to indicate whether or not we have an IPv6 route */ + int supports_ipv6; + + /** A flag to indicate whether or not we have an IPv4 route */ + int supports_ipv4; + + /** State for nat64 */ + struct iter_nat64 nat64; /** A set of inetaddrs that should never be queried. */ struct iter_donotq* donotq; From fa2da45499c272561ff0420fbc2691875939f2e4 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 Jul 2024 16:15:11 +0200 Subject: [PATCH 114/125] - fast-reload, reload settings stored in the infra structure. --- daemon/remote.c | 108 +++++++++++++++++++++++++++++++++++++++ doc/unbound-control.8.in | 4 +- services/cache/infra.c | 94 +++++++++++++++++++--------------- services/cache/infra.h | 15 ++++++ 4 files changed, 179 insertions(+), 42 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 95334efbe..096d9ece0 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3990,6 +3990,12 @@ struct fast_reload_construct { struct rbtree_type* caps_white; /** construct for nat64 */ struct iter_nat64 nat64; + /** construct for wait_limits_netblock */ + struct rbtree_type wait_limits_netblock; + /** construct for wait_limits_cookie_netblock */ + struct rbtree_type wait_limits_cookie_netblock; + /** construct for domain limits */ + struct rbtree_type domain_limits; /** storage for the old configuration elements. The outer struct * is allocated with malloc here, the items are from config. */ struct config_file* oldcfg; @@ -4144,6 +4150,36 @@ fr_check_changed_cfg_strlist(struct config_strlist* cmp1, } } +/** fast reload thread, check if config str2list has changed. */ +static void +fr_check_changed_cfg_str2list(struct config_str2list* cmp1, + struct config_str2list* cmp2, const char* desc, char* str, size_t len) +{ + struct config_str2list* p1 = cmp1, *p2 = cmp2; + while(p1 && p2) { + if((!p1->str && p2->str) || + (p1->str && !p2->str) || + (p1->str && p2->str && strcmp(p1->str, p2->str) != 0)) { + /* The str2list is different. */ + fr_check_changed_cfg(1, desc, str, len); + return; + } + if((!p1->str2 && p2->str2) || + (p1->str2 && !p2->str2) || + (p1->str2 && p2->str2 && + strcmp(p1->str2, p2->str2) != 0)) { + /* The str2list is different. */ + fr_check_changed_cfg(1, desc, str, len); + return; + } + p1 = p1->next; + p2 = p2->next; + } + if((!p1 && p2) || (p1 && !p2)) { + fr_check_changed_cfg(1, desc, str, len); + } +} + /** fast reload thread, check compatible config items */ static int fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) @@ -4276,6 +4312,35 @@ fr_check_nopause_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) "val-nsec3-keysize-iterations", changed_str, sizeof(changed_str)); + /* Check for infra. */ + fr_check_changed_cfg(cfg->host_ttl != newcfg->host_ttl, + "infra-host-ttl", changed_str, sizeof(changed_str)); + fr_check_changed_cfg( + cfg->infra_keep_probing != newcfg->infra_keep_probing, + "infra-keep-probing", changed_str, sizeof(changed_str)); + fr_check_changed_cfg( + cfg->ratelimit != newcfg->ratelimit, + "ratelimit", changed_str, sizeof(changed_str)); + fr_check_changed_cfg( + cfg->ip_ratelimit != newcfg->ip_ratelimit, + "ip-ratelimit", changed_str, sizeof(changed_str)); + fr_check_changed_cfg( + cfg->ip_ratelimit_cookie != newcfg->ip_ratelimit_cookie, + "ip-ratelimit-cookie", changed_str, sizeof(changed_str)); + fr_check_changed_cfg_str2list(cfg->wait_limit_netblock, + newcfg->wait_limit_netblock, "wait-limit-netblock", + changed_str, sizeof(changed_str)); + fr_check_changed_cfg_str2list(cfg->wait_limit_cookie_netblock, + newcfg->wait_limit_cookie_netblock, + "wait-limit-cookie-netblock", changed_str, + sizeof(changed_str)); + fr_check_changed_cfg_str2list(cfg->ratelimit_below_domain, + newcfg->ratelimit_below_domain, "ratelimit-below-domain", + changed_str, sizeof(changed_str)); + fr_check_changed_cfg_str2list(cfg->ratelimit_for_domain, + newcfg->ratelimit_for_domain, "ratelimit-for-domain", + changed_str, sizeof(changed_str)); + /* Check for dnstap. */ fr_check_changed_cfg( cfg->dnstap_send_identity != newcfg->dnstap_send_identity, @@ -4324,6 +4389,9 @@ fr_construct_clear(struct fast_reload_construct* ct) donotq_delete(ct->donotq); priv_delete(ct->priv); caps_white_delete(ct->caps_white); + wait_limits_free(&ct->wait_limits_netblock); + wait_limits_free(&ct->wait_limits_cookie_netblock); + domain_limits_free(&ct->domain_limits); /* Delete the log identity here so that the global value is not * reset by config_delete. */ if(ct->oldcfg && ct->oldcfg->log_identity) { @@ -5001,6 +5069,18 @@ fr_construct_from_config(struct fast_reload_thread* fr, if(fr_poll_for_quit(fr)) return 1; + if(!setup_wait_limits(&ct->wait_limits_netblock, + &ct->wait_limits_cookie_netblock, newcfg)) { + fr_construct_clear(ct); + return 0; + } + if(!setup_domain_limits(&ct->domain_limits, newcfg)) { + fr_construct_clear(ct); + return 0; + } + if(fr_poll_for_quit(fr)) + return 1; + if(!(ct->oldcfg = (struct config_file*)calloc(1, sizeof(*ct->oldcfg)))) { fr_construct_clear(ct); @@ -5534,6 +5614,33 @@ fr_adjust_val_env(struct module_env* env, struct fast_reload_construct* ct, } } +/** fast reload thread, adjust the infra cache parameters */ +static void +fr_adjust_infra(struct module_env* env, struct fast_reload_construct* ct) +{ + struct infra_cache* infra = env->infra_cache; + struct config_file* cfg = env->cfg; + struct rbtree_type oldwaitlim = infra->wait_limits_netblock; + struct rbtree_type oldwaitlimcookie = + infra->wait_limits_cookie_netblock; + struct rbtree_type olddomainlim = infra->domain_limits; + + /* The size of the infra cache and ip rates is changed + * in fr_adjust_cache. */ + infra->host_ttl = cfg->host_ttl; + infra->infra_keep_probing = cfg->infra_keep_probing; + infra_dp_ratelimit = cfg->ratelimit; + infra_ip_ratelimit = cfg->ip_ratelimit; + infra_ip_ratelimit_cookie = cfg->ip_ratelimit_cookie; + infra->wait_limits_netblock = ct->wait_limits_netblock; + infra->wait_limits_cookie_netblock = ct->wait_limits_cookie_netblock; + infra->domain_limits = ct->domain_limits; + + ct->wait_limits_netblock = oldwaitlim; + ct->wait_limits_cookie_netblock = oldwaitlimcookie; + ct->domain_limits = olddomainlim; +} + /** fast reload thread, reload config with putting the new config items * in place and swapping out the old items. */ static int @@ -5625,6 +5732,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg, if(!fr->fr_nopause) { fr_adjust_iter_env(env, ct); fr_adjust_val_env(env, ct, ct->oldcfg); + fr_adjust_infra(env, ct); } /* Set globals with new config. */ diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 76f635fe5..43533a7a5 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -118,7 +118,9 @@ val-date-override, val-sig-key-min, val-sig-skew-max, val-max-restart, val-nsec3-keysize-iterations, target-fetch-policy, outbound-msg-retry, max-sent-count, max-query-restarts, do-not-query-address, do-not-query-localhost, private-address, private-domain, caps-exempt, -nat64-prefix, do-nat64. +nat64-prefix, do-nat64, infra-host-ttl, infra-keep-probing, ratelimit, +ip-ratelimit, ip-ratelimit-cookie, wait-limit-netblock, +wait-limit-cookie-netblock, ratelimit-below-domain, ratelimit-for-domain. .IP The '+d' option makes the reload drop queries that the worker threads are working on. This is like flush_requestlist. Without it the queries are kept diff --git a/services/cache/infra.c b/services/cache/infra.c index 66b17c121..5b055a71c 100644 --- a/services/cache/infra.c +++ b/services/cache/infra.c @@ -165,7 +165,7 @@ rate_deldatafunc(void* d, void* ATTR_UNUSED(arg)) /** find or create element in domainlimit tree */ static struct domain_limit_data* domain_limit_findcreate( - struct infra_cache* infra, char* name) + struct rbtree_type* domain_limits, char* name) { uint8_t* nm; int labs; @@ -181,8 +181,8 @@ static struct domain_limit_data* domain_limit_findcreate( labs = dname_count_labels(nm); /* can we find it? */ - d = (struct domain_limit_data*)name_tree_find(&infra->domain_limits, - nm, nmlen, labs, LDNS_RR_CLASS_IN); + d = (struct domain_limit_data*)name_tree_find(domain_limits, nm, + nmlen, labs, LDNS_RR_CLASS_IN); if(d) { free(nm); return d; @@ -201,8 +201,8 @@ static struct domain_limit_data* domain_limit_findcreate( d->node.dclass = LDNS_RR_CLASS_IN; d->lim = -1; d->below = -1; - if(!name_tree_insert(&infra->domain_limits, &d->node, nm, nmlen, - labs, LDNS_RR_CLASS_IN)) { + if(!name_tree_insert(domain_limits, &d->node, nm, nmlen, labs, + LDNS_RR_CLASS_IN)) { log_err("duplicate element in domainlimit tree"); free(nm); free(d); @@ -212,19 +212,19 @@ static struct domain_limit_data* domain_limit_findcreate( } /** insert rate limit configuration into lookup tree */ -static int infra_ratelimit_cfg_insert(struct infra_cache* infra, +static int infra_ratelimit_cfg_insert(struct rbtree_type* domain_limits, struct config_file* cfg) { struct config_str2list* p; struct domain_limit_data* d; for(p = cfg->ratelimit_for_domain; p; p = p->next) { - d = domain_limit_findcreate(infra, p->str); + d = domain_limit_findcreate(domain_limits, p->str); if(!d) return 0; d->lim = atoi(p->str2); } for(p = cfg->ratelimit_below_domain; p; p = p->next) { - d = domain_limit_findcreate(infra, p->str); + d = domain_limit_findcreate(domain_limits, p->str); if(!d) return 0; d->below = atoi(p->str2); @@ -232,24 +232,21 @@ static int infra_ratelimit_cfg_insert(struct infra_cache* infra, return 1; } -/** setup domain limits tree (0 on failure) */ -static int -setup_domain_limits(struct infra_cache* infra, struct config_file* cfg) +int +setup_domain_limits(struct rbtree_type* domain_limits, struct config_file* cfg) { - name_tree_init(&infra->domain_limits); - if(!infra_ratelimit_cfg_insert(infra, cfg)) { + name_tree_init(domain_limits); + if(!infra_ratelimit_cfg_insert(domain_limits, cfg)) { return 0; } - name_tree_init_parents(&infra->domain_limits); + name_tree_init_parents(domain_limits); return 1; } /** find or create element in wait limit netblock tree */ static struct wait_limit_netblock_info* -wait_limit_netblock_findcreate(struct infra_cache* infra, char* str, - int cookie) +wait_limit_netblock_findcreate(struct rbtree_type* tree, char* str) { - rbtree_type* tree; struct sockaddr_storage addr; int net; socklen_t addrlen; @@ -261,10 +258,6 @@ wait_limit_netblock_findcreate(struct infra_cache* infra, char* str, } /* can we find it? */ - if(cookie) - tree = &infra->wait_limits_cookie_netblock; - else - tree = &infra->wait_limits_netblock; d = (struct wait_limit_netblock_info*)addr_tree_find(tree, &addr, addrlen, net); if(d) @@ -286,19 +279,21 @@ wait_limit_netblock_findcreate(struct infra_cache* infra, char* str, /** insert wait limit information into lookup tree */ static int -infra_wait_limit_netblock_insert(struct infra_cache* infra, - struct config_file* cfg) +infra_wait_limit_netblock_insert(rbtree_type* wait_limits_netblock, + rbtree_type* wait_limits_cookie_netblock, struct config_file* cfg) { struct config_str2list* p; struct wait_limit_netblock_info* d; for(p = cfg->wait_limit_netblock; p; p = p->next) { - d = wait_limit_netblock_findcreate(infra, p->str, 0); + d = wait_limit_netblock_findcreate(wait_limits_netblock, + p->str); if(!d) return 0; d->limit = atoi(p->str2); } for(p = cfg->wait_limit_cookie_netblock; p; p = p->next) { - d = wait_limit_netblock_findcreate(infra, p->str, 1); + d = wait_limit_netblock_findcreate(wait_limits_cookie_netblock, + p->str); if(!d) return 0; d->limit = atoi(p->str2); @@ -306,16 +301,17 @@ infra_wait_limit_netblock_insert(struct infra_cache* infra, return 1; } -/** setup wait limits tree (0 on failure) */ -static int -setup_wait_limits(struct infra_cache* infra, struct config_file* cfg) +int +setup_wait_limits(rbtree_type* wait_limits_netblock, + rbtree_type* wait_limits_cookie_netblock, struct config_file* cfg) { - addr_tree_init(&infra->wait_limits_netblock); - addr_tree_init(&infra->wait_limits_cookie_netblock); - if(!infra_wait_limit_netblock_insert(infra, cfg)) + addr_tree_init(wait_limits_netblock); + addr_tree_init(wait_limits_cookie_netblock); + if(!infra_wait_limit_netblock_insert(wait_limits_netblock, + wait_limits_cookie_netblock, cfg)) return 0; - addr_tree_init_parents(&infra->wait_limits_netblock); - addr_tree_init_parents(&infra->wait_limits_cookie_netblock); + addr_tree_init_parents(wait_limits_netblock); + addr_tree_init_parents(wait_limits_cookie_netblock); return 1; } @@ -348,11 +344,12 @@ infra_create(struct config_file* cfg) return NULL; } /* insert config data into ratelimits */ - if(!setup_domain_limits(infra, cfg)) { + if(!setup_domain_limits(&infra->domain_limits, cfg)) { infra_delete(infra); return NULL; } - if(!setup_wait_limits(infra, cfg)) { + if(!setup_wait_limits(&infra->wait_limits_netblock, + &infra->wait_limits_cookie_netblock, cfg)) { infra_delete(infra); return NULL; } @@ -377,12 +374,29 @@ static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg)) } } +void +domain_limits_free(struct rbtree_type* domain_limits) +{ + if(!domain_limits) + return; + traverse_postorder(domain_limits, domain_limit_free, NULL); +} + /** delete wait_limit_netblock_info entries */ static void wait_limit_netblock_del(rbnode_type* n, void* ATTR_UNUSED(arg)) { free(n); } +void +wait_limits_free(struct rbtree_type* wait_limits_tree) +{ + if(!wait_limits_tree) + return; + traverse_postorder(wait_limits_tree, wait_limit_netblock_del, + NULL); +} + void infra_delete(struct infra_cache* infra) { @@ -390,12 +404,10 @@ infra_delete(struct infra_cache* infra) return; slabhash_delete(infra->hosts); slabhash_delete(infra->domain_rates); - traverse_postorder(&infra->domain_limits, domain_limit_free, NULL); + domain_limits_free(&infra->domain_limits); slabhash_delete(infra->client_ip_rates); - traverse_postorder(&infra->wait_limits_netblock, - wait_limit_netblock_del, NULL); - traverse_postorder(&infra->wait_limits_cookie_netblock, - wait_limit_netblock_del, NULL); + wait_limits_free(&infra->wait_limits_netblock); + wait_limits_free(&infra->wait_limits_cookie_netblock); free(infra); } @@ -426,7 +438,7 @@ infra_adjust(struct infra_cache* infra, struct config_file* cfg) /* reapply domain limits */ traverse_postorder(&infra->domain_limits, domain_limit_free, NULL); - if(!setup_domain_limits(infra, cfg)) { + if(!setup_domain_limits(&infra->domain_limits, cfg)) { infra_delete(infra); return NULL; } diff --git a/services/cache/infra.h b/services/cache/infra.h index 1a88bbb94..b93919e94 100644 --- a/services/cache/infra.h +++ b/services/cache/infra.h @@ -502,4 +502,19 @@ void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep, void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep, struct config_file* cfg); +/** setup wait limits tree (0 on failure) */ +int setup_wait_limits(struct rbtree_type* wait_limits_netblock, + struct rbtree_type* wait_limits_cookie_netblock, + struct config_file* cfg); + +/** Free the wait limits and wait cookie limits tree. */ +void wait_limits_free(struct rbtree_type* wait_limits_tree); + +/** setup domain limits tree (0 on failure) */ +int setup_domain_limits(struct rbtree_type* domain_limits, + struct config_file* cfg); + +/** Free the domain limits tree. */ +void domain_limits_free(struct rbtree_type* domain_limits); + #endif /* SERVICES_CACHE_INFRA_H */ From df15b110ffaae1dd56a4ee0b9d4198ad0bee86e0 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 Jul 2024 17:31:31 +0200 Subject: [PATCH 115/125] - fast-reload, fix modstack lookup and remove outgoing-range check. --- daemon/remote.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 096d9ece0..1dc5aee57 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4202,9 +4202,8 @@ fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) "do-tcp", changed_str, sizeof(changed_str)); fr_check_changed_cfg(cfg->port != newcfg->port, "port", changed_str, sizeof(changed_str)); - fr_check_changed_cfg( - cfg->outgoing_num_ports != newcfg->outgoing_num_ports, - "outgoing-range", changed_str, sizeof(changed_str)); + /* But cfg->outgoing_num_ports has been changed at startup, + * possibly to reduce it, so cannot check it here. */ fr_check_changed_cfg(cfg->outgoing_num_tcp != newcfg->outgoing_num_tcp, "outgoing-num-tcp", changed_str, sizeof(changed_str)); fr_check_changed_cfg(cfg->incoming_num_tcp != newcfg->incoming_num_tcp, @@ -5544,7 +5543,7 @@ fr_adjust_iter_env(struct module_env* env, struct fast_reload_construct* ct) /* Because the iterator env is not locked, the update cannot happen * when fr nopause is used. Without it the fast reload pauses the * other threads, so they are not currently using the structure. */ - m = modstack_find(&env->mesh->mods, "iterator"); + m = modstack_find(env->modstack, "iterator"); if(m != -1) iter_env = (struct iter_env*)env->modinfo[m]; if(iter_env) { /* Swap the data so that the delete happens afterwards. */ @@ -5593,7 +5592,7 @@ fr_adjust_val_env(struct module_env* env, struct fast_reload_construct* ct, /* Because the validator env is not locked, the update cannot happen * when fr nopause is used. Without it the fast reload pauses the * other threads, so they are not currently using the structure. */ - m = modstack_find(&env->mesh->mods, "validator"); + m = modstack_find(env->modstack, "validator"); if(m != -1) val_env = (struct val_env*)env->modinfo[m]; if(val_env) { /* Swap the arrays so that the delete happens afterwards. */ From 9b2d25fe389362ac1f07980c1b24631a0a628f87 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 3 Jul 2024 11:03:39 +0200 Subject: [PATCH 116/125] - fast-reload, more explanation for config parse failure. --- daemon/remote.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 1dc5aee57..618fdc9b5 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5882,7 +5882,8 @@ fr_load_config(struct fast_reload_thread* fr, struct timeval* time_read, config_delete(newcfg); if(!fr_output_printf(fr, "Could not construct from the " "config, check for errors with unbound-checkconf, or " - "out of memory.\n")) + "out of memory. The parse errors are printed in " + "the log.\n")) return 0; fr_send_notification(fr, fast_reload_notification_printout); return 0; From 95169cfafd1fc0c3e8ed2ee18ace1a2dee4544e8 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 3 Jul 2024 11:28:27 +0200 Subject: [PATCH 117/125] - fast-reload, reload worker outside network changes. --- daemon/remote.c | 24 ++++++++++++++++++++++++ doc/unbound-control.8.in | 4 +++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 618fdc9b5..663630cfe 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6903,6 +6903,29 @@ fr_worker_pickup_auth_changes(struct worker* worker, } } +/** Fast reload, the worker picks up changes in outside_network. */ +static void +fr_worker_pickup_outside_network(struct worker* worker) +{ + struct outside_network* outnet = worker->back; + struct config_file* cfg = worker->env.cfg; + outnet->use_caps_for_id = cfg->use_caps_bits_for_id; + outnet->unwanted_threshold = cfg->unwanted_threshold; + outnet->tls_use_sni = cfg->tls_use_sni; + outnet->tcp_mss = cfg->outgoing_tcp_mss; + outnet->ip_dscp = cfg->ip_dscp; + outnet->max_reuse_tcp_queries = cfg->max_reuse_tcp_queries; + outnet->tcp_reuse_timeout = cfg->tcp_reuse_timeout; + outnet->tcp_auth_query_timeout = cfg->tcp_auth_query_timeout; + outnet->delayclose = cfg->delay_close; + if(outnet->delayclose) { +#ifndef S_SPLINT_S + outnet->delay_tv.tv_sec = cfg->delay_close/1000; + outnet->delay_tv.tv_usec = (cfg->delay_close%1000)*1000; +#endif + } +} + void fast_reload_worker_pickup_changes(struct worker* worker) { @@ -6930,6 +6953,7 @@ fast_reload_worker_pickup_changes(struct worker* worker) #ifdef USE_CACHEDB worker->env.cachedb_enabled = worker->daemon->env->cachedb_enabled; #endif + fr_worker_pickup_outside_network(worker); } /** fast reload thread, handle reload_stop notification, send reload stop diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 43533a7a5..92cdcbc50 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -80,7 +80,9 @@ auth-zone and its options, rpz and its options, edns-strings, respip_set, view and its options, access-control options, tcp-connection-limit, log-identity, infra-cache-numhosts, msg-cache-size, rrset-cache-size, key-cache-size, ratelimit-size, neg-cache-size, num-queries-per-thread, -jostle-timeout. +jostle-timeout, use-caps-for-id, unwanted-reply-threshold, tls-use-sni, +outgoing-tcp-mss, ip-dscp, max-reuse-tcp-queries, tcp-reuse-timeout, +tcp-auth-query-timeout, delay-close. .IP For dnstap, the options can be changed: dnstap-log-resolver-query-messages, dnstap-log-resolver-response-messages, dnstap-log-client-query-messages, From 1dd1860468cffa34266fa84fdf6d82f7a468ca06 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 3 Jul 2024 11:57:25 +0200 Subject: [PATCH 118/125] - fast-reload, detect incompatible changes in network settings. --- daemon/remote.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 663630cfe..dc1b9efe0 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4203,7 +4203,7 @@ fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) fr_check_changed_cfg(cfg->port != newcfg->port, "port", changed_str, sizeof(changed_str)); /* But cfg->outgoing_num_ports has been changed at startup, - * possibly to reduce it, so cannot check it here. */ + * possibly to reduce it, so do not check it here. */ fr_check_changed_cfg(cfg->outgoing_num_tcp != newcfg->outgoing_num_tcp, "outgoing-num-tcp", changed_str, sizeof(changed_str)); fr_check_changed_cfg(cfg->incoming_num_tcp != newcfg->incoming_num_tcp, @@ -4238,6 +4238,50 @@ fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg) "ip-freebind", changed_str, sizeof(changed_str)); fr_check_changed_cfg(cfg->udp_connect != newcfg->udp_connect, "udp-connect", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->msg_buffer_size != newcfg->msg_buffer_size, + "msg-buffer-size", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->do_tcp_keepalive != newcfg->do_tcp_keepalive, + "edns-tcp-keepalive", changed_str, sizeof(changed_str)); + fr_check_changed_cfg( + cfg->tcp_keepalive_timeout != newcfg->tcp_keepalive_timeout, + "edns-tcp-keepalive-timeout", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->tcp_idle_timeout != newcfg->tcp_idle_timeout, + "tcp-idle-timeout", changed_str, sizeof(changed_str)); + /* Not changed, only if DoH is used, it is then stored in commpoints, + * as well as used from cfg. */ + fr_check_changed_cfg( + cfg->harden_large_queries != newcfg->harden_large_queries, + "harden-large-queries", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->http_max_streams != newcfg->http_max_streams, + "http-max-streams", changed_str, sizeof(changed_str)); + fr_check_changed_cfg_str(cfg->http_endpoint, newcfg->http_endpoint, + "http-endpoint", changed_str, sizeof(changed_str)); + fr_check_changed_cfg( + cfg->http_notls_downstream != newcfg->http_notls_downstream, + "http_notls_downstream", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->https_port != newcfg->https_port, + "https-port", changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->ssl_port != newcfg->ssl_port, + "tls-port", changed_str, sizeof(changed_str)); + fr_check_changed_cfg_str(cfg->ssl_service_key, newcfg->ssl_service_key, + "tls-service-key", changed_str, sizeof(changed_str)); + fr_check_changed_cfg_str(cfg->ssl_service_pem, newcfg->ssl_service_pem, + "tls-service-pem", changed_str, sizeof(changed_str)); + fr_check_changed_cfg_str(cfg->tls_cert_bundle, newcfg->tls_cert_bundle, + "tls-cert-bundle", changed_str, sizeof(changed_str)); + fr_check_changed_cfg_strlist(cfg->proxy_protocol_port, + newcfg->proxy_protocol_port, "proxy-protocol-port", + changed_str, sizeof(changed_str)); + fr_check_changed_cfg_strlist(cfg->tls_additional_port, + newcfg->tls_additional_port, "tls-additional-port", + changed_str, sizeof(changed_str)); + fr_check_changed_cfg_str(cfg->if_automatic_ports, + newcfg->if_automatic_ports, "interface-automatic-ports", + changed_str, sizeof(changed_str)); + fr_check_changed_cfg(cfg->udp_upstream_without_downstream != + newcfg->udp_upstream_without_downstream, + "udp-upstream-without-downstream", changed_str, + sizeof(changed_str)); if(changed_str[0] != 0) { /* The new config changes some items that do not work with From adb9942eae295b902d2d869b87d82ea116185951 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 3 Jul 2024 14:52:06 +0200 Subject: [PATCH 119/125] fast-reload, commit test files. --- testdata/fast_reload_fwd.tdir/auth1.zone | 2 ++ testdata/fast_reload_fwd.tdir/auth2.zone | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 testdata/fast_reload_fwd.tdir/auth1.zone create mode 100644 testdata/fast_reload_fwd.tdir/auth2.zone diff --git a/testdata/fast_reload_fwd.tdir/auth1.zone b/testdata/fast_reload_fwd.tdir/auth1.zone new file mode 100644 index 000000000..b6b551a42 --- /dev/null +++ b/testdata/fast_reload_fwd.tdir/auth1.zone @@ -0,0 +1,2 @@ +@ SOA ns root 1 3600 300 7200 3600 +www A 1.2.3.4 diff --git a/testdata/fast_reload_fwd.tdir/auth2.zone b/testdata/fast_reload_fwd.tdir/auth2.zone new file mode 100644 index 000000000..fc59810c9 --- /dev/null +++ b/testdata/fast_reload_fwd.tdir/auth2.zone @@ -0,0 +1,2 @@ +@ SOA ns root 1 3600 300 7200 3600 +www A 1.2.3.5 From ce876459c727b70a4baf066f94a8e4ec77bbf67b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 23 Jul 2024 11:56:55 +0200 Subject: [PATCH 120/125] - fast-reload, fix warnings for call types in windows compile. --- daemon/remote.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index dc1b9efe0..79bb232e7 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6139,7 +6139,7 @@ create_socketpair(int* pair, struct ub_randstate* rand) return 0; } accaddrlen = (socklen_t)sizeof(accaddr); - pair[0] = accept(lst, &accaddr, &accaddrlen); + pair[0] = accept(lst, (struct sockaddr*)&accaddr, &accaddrlen); if(pair[0] == -1) { log_err("create socketpair: accept: %s", sock_strerror(errno)); sock_close(lst); @@ -6219,7 +6219,8 @@ create_socketpair(int* pair, struct ub_randstate* rand) pair[1] = -1; return 0; } - ret = send(pair[1], nonce+bcount, sizeof(nonce)-bcount, 0); + ret = send(pair[1], (void*)(nonce+bcount), + sizeof(nonce)-bcount, 0); if(ret == -1) { if( #ifndef USE_WINSOCK @@ -6277,7 +6278,8 @@ create_socketpair(int* pair, struct ub_randstate* rand) pair[1] = -1; return 0; } - ret = recv(pair[0], recvnonce+bcount, sizeof(nonce)-bcount, 0); + ret = recv(pair[0], (void*)(recvnonce+bcount), + sizeof(nonce)-bcount, 0); if(ret == -1) { if( #ifndef USE_WINSOCK From f046ce2b39de365d0c08b58b3fe7e7d46edaad52 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 23 Jul 2024 13:41:42 +0200 Subject: [PATCH 121/125] - fast-reload, fix warnings and comm_point_internal for tcp wouldblock calls. --- daemon/remote.c | 6 +++--- daemon/worker.c | 2 +- util/netevent.c | 5 +++++ util/netevent.h | 8 ++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 79bb232e7..03621aadd 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6567,7 +6567,7 @@ fr_read_ack_from_workers(struct fast_reload_thread* fr) while(count < total) { uint8_t r; ssize_t ret; - ret = recv(fr->commreload[0], &r, 1, 0); + ret = recv(fr->commreload[0], (void*)&r, 1, 0); if(ret == -1) { if( #ifndef USE_WINSOCK @@ -7201,7 +7201,7 @@ fr_client_send_item_ssl(struct fast_reload_printq* printq) return 0; } else if(want == SSL_ERROR_WANT_WRITE) { #ifdef USE_WINSOCK - ub_winsock_tcp_wouldblock(printq->client_cp->ev->ev, UB_EV_WRITE); + ub_winsock_tcp_wouldblock(comm_point_internal(printq->client_cp), UB_EV_WRITE); #endif return 0; /* write more later */ } else if(want == SSL_ERROR_SYSCALL) { @@ -7248,7 +7248,7 @@ fr_client_send_item_fd(struct fast_reload_printq* printq) #endif ) { #ifdef USE_WINSOCK - ub_winsock_tcp_wouldblock(printq->client_cp->ev->ev, UB_EV_WRITE); + ub_winsock_tcp_wouldblock(comm_point_internal(printq->client_cp), UB_EV_WRITE); #endif return 0; /* Try again. */ } diff --git a/daemon/worker.c b/daemon/worker.c index c6f567e9c..d169d1580 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -386,7 +386,7 @@ worker_send_reload_ack(struct worker* worker) ssize_t ret; while(1) { ret = send(worker->daemon->fast_reload_thread->commreload[1], - &c, 1, 0); + (void*)&c, 1, 0); if(ret == -1) { if( #ifndef USE_WINSOCK diff --git a/util/netevent.c b/util/netevent.c index 365290c01..de49dce4c 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -306,6 +306,11 @@ struct ub_event_base* comm_base_internal(struct comm_base* b) return b->eb->base; } +struct ub_event* comm_point_internal(struct comm_point* c) +{ + return c->ev->ev; +} + /** see if errno for udp has to be logged or not uses globals */ static int udp_send_errno_needs_log(struct sockaddr* addr, socklen_t addrlen) diff --git a/util/netevent.h b/util/netevent.h index 6f43ce56c..5579a74cd 100644 --- a/util/netevent.h +++ b/util/netevent.h @@ -517,6 +517,14 @@ void comm_base_set_slow_accept_handlers(struct comm_base* b, */ struct ub_event_base* comm_base_internal(struct comm_base* b); +/** + * Access internal event structure. It is for use with + * ub_winsock_tcp_wouldblock on windows. + * @param c: comm point. + * @return event. + */ +struct ub_event* comm_point_internal(struct comm_point* c); + /** * Create an UDP comm point. Calls malloc. * setups the structure with the parameters you provide. From e8703dbfe8d77b3b194bc70eccdd57cddd0ce052 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 23 Jul 2024 16:59:20 +0200 Subject: [PATCH 122/125] - fast-reload, extend lock checks for repeat thread ids. --- testcode/checklocks.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/testcode/checklocks.c b/testcode/checklocks.c index d1c877467..ac3e70ad1 100644 --- a/testcode/checklocks.c +++ b/testcode/checklocks.c @@ -64,6 +64,9 @@ static int key_deleted = 0; static ub_thread_key_type thr_debug_key; /** the list of threads, so all threads can be examined. NULL if unused. */ static struct thr_check* thread_infos[THRDEBUG_MAX_THREADS]; +/** stored maximum lock number for threads, when a thread is restarted the + * number is kept track of, because the new locks get new id numbers. */ +static int thread_lockcount[THRDEBUG_MAX_THREADS]; /** do we check locking order */ int check_locking_order = 1; /** the pid of this runset, reasonably unique. */ @@ -682,10 +685,20 @@ open_lockorder(struct thr_check* thr) char buf[24]; time_t t; snprintf(buf, sizeof(buf), "ublocktrace.%d", thr->num); - thr->order_info = fopen(buf, "w"); - if(!thr->order_info) - fatal_exit("could not open %s: %s", buf, strerror(errno)); - thr->locks_created = 0; + thr->locks_created = thread_lockcount[thr->num]; + if(thr->locks_created == 0) { + thr->order_info = fopen(buf, "w"); + if(!thr->order_info) + fatal_exit("could not open %s: %s", buf, strerror(errno)); + } else { + /* There is already a file to append on with the previous + * thread information. */ + thr->order_info = fopen(buf, "a"); + if(!thr->order_info) + fatal_exit("could not open for append %s: %s", buf, strerror(errno)); + return; + } + t = time(NULL); /* write: */ if(fwrite(&t, sizeof(t), 1, thr->order_info) != 1 || @@ -712,6 +725,7 @@ static void* checklock_main(void* arg) if(check_locking_order) open_lockorder(thr); ret = thr->func(thr->arg); + thread_lockcount[thr->num] = thr->locks_created; thread_infos[thr->num] = NULL; if(check_locking_order) fclose(thr->order_info); From 238d9c609655c5f5769a19cb6e2799a937712726 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 24 Jul 2024 14:13:22 +0200 Subject: [PATCH 123/125] - fast-reload, additional test cases, cache change and tag changes. --- .../fast_reload_fwd.tdir/fast_reload_fwd.test | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test index d33c826f1..9248593c7 100644 --- a/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test +++ b/testdata/fast_reload_fwd.tdir/fast_reload_fwd.test @@ -239,6 +239,65 @@ if grep "ta4.example.com. 55566" outfile >/dev/null; then :; else exit 1 fi +echo "" +echo "> test change: add tag1 tag2" +cp ub.conf ub.conf.orig2 +echo "server:" >> ub.conf +echo ' define-tag: "tag1 tag2"' >> ub.conf +echo "> unbound-control fast_reload" +$PRE/unbound-control -c ub.conf fast_reload +vv 2>&1 | tee output +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi + +echo "" +echo "> test change: change to tag2 tag3" +cp ub.conf.orig2 ub.conf +echo "server:" >> ub.conf +echo ' define-tag: "tag2 tag3"' >> ub.conf +echo "> unbound-control fast_reload" +$PRE/unbound-control -c ub.conf fast_reload +vv 2>&1 | tee output +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi +if grep "tags have changed" output; then + echo "output OK" +else + echo "wrong output" + exit 1 +fi + +echo "" +echo "> test change: change cache size" +cp ub.conf.orig2 ub.conf +echo "server:" >> ub.conf +echo " msg-cache-size: 10m" >> ub.conf +echo " rrset-cache-size: 5m" >> ub.conf +echo "> unbound-control fast_reload" +$PRE/unbound-control -c ub.conf fast_reload +vv 2>&1 | tee output +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi + +echo "" +echo "> test change: change nothing, +p too" +$PRE/unbound-control -c ub.conf fast_reload +vv +p 2>&1 | tee output +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi + echo "" echo "> stop unbound" kill_pid $UNBOUND_PID From fb448df1b8671de88fac0a263faac1a9a7cc3ceb Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 24 Jul 2024 14:34:49 +0200 Subject: [PATCH 124/125] - fast-reload, fix documentation for auth_zone_verify_zonemd_with_key. --- services/authzone.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/authzone.c b/services/authzone.c index c0bc1ca5c..ad9351d9e 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -8114,6 +8114,8 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env, * @param why_bogus: if the routine fails, returns the failure reason. * @param keystorage: where to store the ub_packed_rrset_key that is created * on success. A pointer to it is returned on success. + * @param reasonbuf: buffer to use for fail reason string print. + * @param reasonlen: length of reasonbuf. * @return the dnskey RRset, reference to zone data and keystorage, or * NULL on failure. */ From 498e51a25660c89821b5777d5f21c98f4deb6961 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 24 Jul 2024 15:09:32 +0200 Subject: [PATCH 125/125] - fast-reload, fix copy_cfg type casts and memory leak on config parse failure. --- daemon/remote.c | 599 ++++++++++++++++++++++-------------------- validator/validator.c | 14 + 2 files changed, 321 insertions(+), 292 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 03621aadd..d47196636 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5204,7 +5204,14 @@ static void fr_atomic_copy_cfg(struct config_file* oldcfg, struct config_file* cfg, struct config_file* newcfg) { -#define COPY_VAR(var) oldcfg->var = cfg->var; atomic_store(&cfg->var, newcfg->var); newcfg->var = 0; +#define COPY_VAR_int(var) oldcfg->var = cfg->var; atomic_store((_Atomic int*)&cfg->var, newcfg->var); newcfg->var = 0; +#define COPY_VAR_ptr(var) oldcfg->var = cfg->var; atomic_store((void* _Atomic*)&cfg->var, newcfg->var); newcfg->var = 0; +#define COPY_VAR_unsigned_int(var) oldcfg->var = cfg->var; atomic_store((_Atomic unsigned*)&cfg->var, newcfg->var); newcfg->var = 0; +#define COPY_VAR_size_t(var) oldcfg->var = cfg->var; atomic_store((_Atomic size_t*)&cfg->var, newcfg->var); newcfg->var = 0; +#define COPY_VAR_uint8_t(var) oldcfg->var = cfg->var; atomic_store((_Atomic uint8_t*)&cfg->var, newcfg->var); newcfg->var = 0; +#define COPY_VAR_uint16_t(var) oldcfg->var = cfg->var; atomic_store((_Atomic uint16_t*)&cfg->var, newcfg->var); newcfg->var = 0; +#define COPY_VAR_uint32_t(var) oldcfg->var = cfg->var; atomic_store((_Atomic uint32_t*)&cfg->var, newcfg->var); newcfg->var = 0; +#define COPY_VAR_int32_t(var) oldcfg->var = cfg->var; atomic_store((_Atomic int32_t*)&cfg->var, newcfg->var); newcfg->var = 0; /* If config file items are missing from this list, they are * not updated by fast-reload +p. */ /* For missing items, the oldcfg item is not updated, still NULL, @@ -5212,333 +5219,341 @@ fr_atomic_copy_cfg(struct config_file* oldcfg, struct config_file* cfg, * The newcfg item is then deleted later. */ /* Items that need synchronisation are omitted from the list. * Use fast-reload without +p to update them together. */ - COPY_VAR(verbosity); - COPY_VAR(stat_interval); - COPY_VAR(stat_cumulative); - COPY_VAR(stat_extended); - COPY_VAR(stat_inhibit_zero); - COPY_VAR(num_threads); - COPY_VAR(port); - COPY_VAR(do_ip4); - COPY_VAR(do_ip6); - COPY_VAR(do_nat64); - COPY_VAR(prefer_ip4); - COPY_VAR(prefer_ip6); - COPY_VAR(do_udp); - COPY_VAR(do_tcp); - COPY_VAR(max_reuse_tcp_queries); - COPY_VAR(tcp_reuse_timeout); - COPY_VAR(tcp_auth_query_timeout); - COPY_VAR(tcp_upstream); - COPY_VAR(udp_upstream_without_downstream); - COPY_VAR(tcp_mss); - COPY_VAR(outgoing_tcp_mss); - COPY_VAR(tcp_idle_timeout); - COPY_VAR(do_tcp_keepalive); - COPY_VAR(tcp_keepalive_timeout); - COPY_VAR(sock_queue_timeout); - COPY_VAR(proxy_protocol_port); - COPY_VAR(ssl_service_key); - COPY_VAR(ssl_service_pem); - COPY_VAR(ssl_port); - COPY_VAR(ssl_upstream); - COPY_VAR(tls_cert_bundle); - COPY_VAR(tls_win_cert); - COPY_VAR(tls_additional_port); + COPY_VAR_int(verbosity); + COPY_VAR_int(stat_interval); + COPY_VAR_int(stat_cumulative); + COPY_VAR_int(stat_extended); + COPY_VAR_int(stat_inhibit_zero); + COPY_VAR_int(num_threads); + COPY_VAR_int(port); + COPY_VAR_int(do_ip4); + COPY_VAR_int(do_ip6); + COPY_VAR_int(do_nat64); + COPY_VAR_int(prefer_ip4); + COPY_VAR_int(prefer_ip6); + COPY_VAR_int(do_udp); + COPY_VAR_int(do_tcp); + COPY_VAR_size_t(max_reuse_tcp_queries); + COPY_VAR_int(tcp_reuse_timeout); + COPY_VAR_int(tcp_auth_query_timeout); + COPY_VAR_int(tcp_upstream); + COPY_VAR_int(udp_upstream_without_downstream); + COPY_VAR_int(tcp_mss); + COPY_VAR_int(outgoing_tcp_mss); + COPY_VAR_int(tcp_idle_timeout); + COPY_VAR_int(do_tcp_keepalive); + COPY_VAR_int(tcp_keepalive_timeout); + COPY_VAR_int(sock_queue_timeout); + COPY_VAR_ptr(proxy_protocol_port); + COPY_VAR_ptr(ssl_service_key); + COPY_VAR_ptr(ssl_service_pem); + COPY_VAR_int(ssl_port); + COPY_VAR_int(ssl_upstream); + COPY_VAR_ptr(tls_cert_bundle); + COPY_VAR_int(tls_win_cert); + COPY_VAR_ptr(tls_additional_port); /* The first is used to walk throught the list but last is * only used during config read. */ - COPY_VAR(tls_session_ticket_keys.first); - COPY_VAR(tls_session_ticket_keys.last); - COPY_VAR(tls_ciphers); - COPY_VAR(tls_ciphersuites); - COPY_VAR(tls_use_sni); - COPY_VAR(https_port); - COPY_VAR(http_endpoint); - COPY_VAR(http_max_streams); - COPY_VAR(http_query_buffer_size); - COPY_VAR(http_response_buffer_size); - COPY_VAR(http_nodelay); - COPY_VAR(http_notls_downstream); - COPY_VAR(outgoing_num_ports); - COPY_VAR(outgoing_num_tcp); - COPY_VAR(incoming_num_tcp); - COPY_VAR(outgoing_avail_ports); - COPY_VAR(edns_buffer_size); - COPY_VAR(stream_wait_size); - COPY_VAR(msg_buffer_size); - COPY_VAR(msg_cache_size); - COPY_VAR(msg_cache_slabs); - COPY_VAR(num_queries_per_thread); - COPY_VAR(jostle_time); - COPY_VAR(rrset_cache_size); - COPY_VAR(rrset_cache_slabs); - COPY_VAR(host_ttl); - COPY_VAR(infra_cache_slabs); - COPY_VAR(infra_cache_numhosts); - COPY_VAR(infra_cache_min_rtt); - COPY_VAR(infra_cache_max_rtt); - COPY_VAR(infra_keep_probing); - COPY_VAR(delay_close); - COPY_VAR(udp_connect); - COPY_VAR(target_fetch_policy); - COPY_VAR(fast_server_permil); - COPY_VAR(fast_server_num); - COPY_VAR(if_automatic); - COPY_VAR(if_automatic_ports); - COPY_VAR(so_rcvbuf); - COPY_VAR(so_sndbuf); - COPY_VAR(so_reuseport); - COPY_VAR(ip_transparent); - COPY_VAR(ip_freebind); - COPY_VAR(ip_dscp); + COPY_VAR_ptr(tls_session_ticket_keys.first); + COPY_VAR_ptr(tls_session_ticket_keys.last); + COPY_VAR_ptr(tls_ciphers); + COPY_VAR_ptr(tls_ciphersuites); + COPY_VAR_int(tls_use_sni); + COPY_VAR_int(https_port); + COPY_VAR_ptr(http_endpoint); + COPY_VAR_uint32_t(http_max_streams); + COPY_VAR_size_t(http_query_buffer_size); + COPY_VAR_size_t(http_response_buffer_size); + COPY_VAR_int(http_nodelay); + COPY_VAR_int(http_notls_downstream); + COPY_VAR_int(outgoing_num_ports); + COPY_VAR_size_t(outgoing_num_tcp); + COPY_VAR_size_t(incoming_num_tcp); + COPY_VAR_ptr(outgoing_avail_ports); + COPY_VAR_size_t(edns_buffer_size); + COPY_VAR_size_t(stream_wait_size); + COPY_VAR_size_t(msg_buffer_size); + COPY_VAR_size_t(msg_cache_size); + COPY_VAR_size_t(msg_cache_slabs); + COPY_VAR_size_t(num_queries_per_thread); + COPY_VAR_size_t(jostle_time); + COPY_VAR_size_t(rrset_cache_size); + COPY_VAR_size_t(rrset_cache_slabs); + COPY_VAR_int(host_ttl); + COPY_VAR_size_t(infra_cache_slabs); + COPY_VAR_size_t(infra_cache_numhosts); + COPY_VAR_int(infra_cache_min_rtt); + COPY_VAR_int(infra_cache_max_rtt); + COPY_VAR_int(infra_keep_probing); + COPY_VAR_int(delay_close); + COPY_VAR_int(udp_connect); + COPY_VAR_ptr(target_fetch_policy); + COPY_VAR_int(fast_server_permil); + COPY_VAR_size_t(fast_server_num); + COPY_VAR_int(if_automatic); + COPY_VAR_ptr(if_automatic_ports); + COPY_VAR_size_t(so_rcvbuf); + COPY_VAR_size_t(so_sndbuf); + COPY_VAR_int(so_reuseport); + COPY_VAR_int(ip_transparent); + COPY_VAR_int(ip_freebind); + COPY_VAR_int(ip_dscp); /* Not copied because the length and items could then not match. num_ifs, ifs, num_out_ifs, out_ifs */ - COPY_VAR(root_hints); - COPY_VAR(stubs); - COPY_VAR(forwards); - COPY_VAR(auths); - COPY_VAR(views); - COPY_VAR(donotqueryaddrs); + COPY_VAR_ptr(root_hints); + COPY_VAR_ptr(stubs); + COPY_VAR_ptr(forwards); + COPY_VAR_ptr(auths); + COPY_VAR_ptr(views); + COPY_VAR_ptr(donotqueryaddrs); #ifdef CLIENT_SUBNET - COPY_VAR(client_subnet); - COPY_VAR(client_subnet_zone); - COPY_VAR(client_subnet_opcode); - COPY_VAR(client_subnet_always_forward); - COPY_VAR(max_client_subnet_ipv4); - COPY_VAR(max_client_subnet_ipv6); - COPY_VAR(min_client_subnet_ipv4); - COPY_VAR(min_client_subnet_ipv6); - COPY_VAR(max_ecs_tree_size_ipv4); - COPY_VAR(max_ecs_tree_size_ipv6); + COPY_VAR_ptr(client_subnet); + COPY_VAR_ptr(client_subnet_zone); + COPY_VAR_uint16_t(client_subnet_opcode); + COPY_VAR_int(client_subnet_always_forward); + COPY_VAR_uint8_t(max_client_subnet_ipv4); + COPY_VAR_uint8_t(max_client_subnet_ipv6); + COPY_VAR_uint8_t(min_client_subnet_ipv4); + COPY_VAR_uint8_t(min_client_subnet_ipv6); + COPY_VAR_uint32_t(max_ecs_tree_size_ipv4); + COPY_VAR_uint32_t(max_ecs_tree_size_ipv6); #endif - COPY_VAR(acls); - COPY_VAR(donotquery_localhost); - COPY_VAR(tcp_connection_limits); - COPY_VAR(harden_short_bufsize); - COPY_VAR(harden_large_queries); - COPY_VAR(harden_glue); - COPY_VAR(harden_dnssec_stripped); - COPY_VAR(harden_below_nxdomain); - COPY_VAR(harden_referral_path); - COPY_VAR(harden_algo_downgrade); - COPY_VAR(harden_unknown_additional); - COPY_VAR(use_caps_bits_for_id); - COPY_VAR(caps_whitelist); - COPY_VAR(private_address); - COPY_VAR(private_domain); - COPY_VAR(unwanted_threshold); - COPY_VAR(max_ttl); - COPY_VAR(min_ttl); - COPY_VAR(max_negative_ttl); - COPY_VAR(prefetch); - COPY_VAR(prefetch_key); - COPY_VAR(deny_any); - COPY_VAR(chrootdir); - COPY_VAR(username); - COPY_VAR(directory); - COPY_VAR(logfile); - COPY_VAR(pidfile); - COPY_VAR(use_syslog); - COPY_VAR(log_time_ascii); - COPY_VAR(log_queries); - COPY_VAR(log_replies); - COPY_VAR(log_tag_queryreply); - COPY_VAR(log_local_actions); - COPY_VAR(log_servfail); - COPY_VAR(log_identity); - COPY_VAR(log_destaddr); - COPY_VAR(hide_identity); - COPY_VAR(hide_version); - COPY_VAR(hide_trustanchor); - COPY_VAR(hide_http_user_agent); - COPY_VAR(identity); - COPY_VAR(version); - COPY_VAR(http_user_agent); - COPY_VAR(nsid_cfg_str); + COPY_VAR_ptr(acls); + COPY_VAR_int(donotquery_localhost); + COPY_VAR_ptr(tcp_connection_limits); + COPY_VAR_int(harden_short_bufsize); + COPY_VAR_int(harden_large_queries); + COPY_VAR_int(harden_glue); + COPY_VAR_int(harden_dnssec_stripped); + COPY_VAR_int(harden_below_nxdomain); + COPY_VAR_int(harden_referral_path); + COPY_VAR_int(harden_algo_downgrade); + COPY_VAR_int(harden_unknown_additional); + COPY_VAR_int(use_caps_bits_for_id); + COPY_VAR_ptr(caps_whitelist); + COPY_VAR_ptr(private_address); + COPY_VAR_ptr(private_domain); + COPY_VAR_size_t(unwanted_threshold); + COPY_VAR_int(max_ttl); + COPY_VAR_int(min_ttl); + COPY_VAR_int(max_negative_ttl); + COPY_VAR_int(min_negative_ttl); + COPY_VAR_int(prefetch); + COPY_VAR_int(prefetch_key); + COPY_VAR_int(deny_any); + COPY_VAR_ptr(chrootdir); + COPY_VAR_ptr(username); + COPY_VAR_ptr(directory); + COPY_VAR_ptr(logfile); + COPY_VAR_ptr(pidfile); + COPY_VAR_int(use_syslog); + COPY_VAR_int(log_time_ascii); + COPY_VAR_int(log_queries); + COPY_VAR_int(log_replies); + COPY_VAR_int(log_tag_queryreply); + COPY_VAR_int(log_local_actions); + COPY_VAR_int(log_servfail); + COPY_VAR_ptr(log_identity); + COPY_VAR_int(log_destaddr); + COPY_VAR_int(hide_identity); + COPY_VAR_int(hide_version); + COPY_VAR_int(hide_trustanchor); + COPY_VAR_int(hide_http_user_agent); + COPY_VAR_ptr(identity); + COPY_VAR_ptr(version); + COPY_VAR_ptr(http_user_agent); + COPY_VAR_ptr(nsid_cfg_str); /* Not copied because the length and items could then not match. nsid; nsid_len; */ - COPY_VAR(module_conf); - COPY_VAR(trust_anchor_file_list); - COPY_VAR(trust_anchor_list); - COPY_VAR(auto_trust_anchor_file_list); - COPY_VAR(trusted_keys_file_list); - COPY_VAR(domain_insecure); - COPY_VAR(trust_anchor_signaling); - COPY_VAR(root_key_sentinel); - COPY_VAR(val_date_override); - COPY_VAR(val_sig_skew_min); - COPY_VAR(val_sig_skew_max); - COPY_VAR(val_max_restart); - COPY_VAR(bogus_ttl); - COPY_VAR(val_clean_additional); - COPY_VAR(val_log_level); - COPY_VAR(val_log_squelch); - COPY_VAR(val_permissive_mode); - COPY_VAR(aggressive_nsec); - COPY_VAR(ignore_cd); - COPY_VAR(disable_edns_do); - COPY_VAR(serve_expired); - COPY_VAR(serve_expired_ttl); - COPY_VAR(serve_expired_ttl_reset); - COPY_VAR(serve_expired_reply_ttl); - COPY_VAR(serve_expired_client_timeout); - COPY_VAR(ede_serve_expired); - COPY_VAR(serve_original_ttl); - COPY_VAR(val_nsec3_key_iterations); - COPY_VAR(zonemd_permissive_mode); - COPY_VAR(add_holddown); - COPY_VAR(del_holddown); - COPY_VAR(keep_missing); - COPY_VAR(permit_small_holddown); - COPY_VAR(key_cache_size); - COPY_VAR(key_cache_slabs); - COPY_VAR(neg_cache_size); - COPY_VAR(local_zones); - COPY_VAR(local_zones_nodefault); + COPY_VAR_ptr(module_conf); + COPY_VAR_ptr(trust_anchor_file_list); + COPY_VAR_ptr(trust_anchor_list); + COPY_VAR_ptr(auto_trust_anchor_file_list); + COPY_VAR_ptr(trusted_keys_file_list); + COPY_VAR_ptr(domain_insecure); + COPY_VAR_int(trust_anchor_signaling); + COPY_VAR_int(root_key_sentinel); + COPY_VAR_int32_t(val_date_override); + COPY_VAR_int32_t(val_sig_skew_min); + COPY_VAR_int32_t(val_sig_skew_max); + COPY_VAR_int32_t(val_max_restart); + COPY_VAR_int(bogus_ttl); + COPY_VAR_int(val_clean_additional); + COPY_VAR_int(val_log_level); + COPY_VAR_int(val_log_squelch); + COPY_VAR_int(val_permissive_mode); + COPY_VAR_int(aggressive_nsec); + COPY_VAR_int(ignore_cd); + COPY_VAR_int(disable_edns_do); + COPY_VAR_int(serve_expired); + COPY_VAR_int(serve_expired_ttl); + COPY_VAR_int(serve_expired_ttl_reset); + COPY_VAR_int(serve_expired_reply_ttl); + COPY_VAR_int(serve_expired_client_timeout); + COPY_VAR_int(ede_serve_expired); + COPY_VAR_int(serve_original_ttl); + COPY_VAR_ptr(val_nsec3_key_iterations); + COPY_VAR_int(zonemd_permissive_mode); + COPY_VAR_unsigned_int(add_holddown); + COPY_VAR_unsigned_int(del_holddown); + COPY_VAR_unsigned_int(keep_missing); + COPY_VAR_int(permit_small_holddown); + COPY_VAR_size_t(key_cache_size); + COPY_VAR_size_t(key_cache_slabs); + COPY_VAR_size_t(neg_cache_size); + COPY_VAR_ptr(local_zones); + COPY_VAR_ptr(local_zones_nodefault); #ifdef USE_IPSET - COPY_VAR(local_zones_ipset); + COPY_VAR_ptr(local_zones_ipset); #endif - COPY_VAR(local_zones_disable_default); - COPY_VAR(local_data); - COPY_VAR(local_zone_overrides); - COPY_VAR(unblock_lan_zones); - COPY_VAR(insecure_lan_zones); + COPY_VAR_int(local_zones_disable_default); + COPY_VAR_ptr(local_data); + COPY_VAR_ptr(local_zone_overrides); + COPY_VAR_int(unblock_lan_zones); + COPY_VAR_int(insecure_lan_zones); /* These reference tags - COPY_VAR(local_zone_tags); - COPY_VAR(acl_tags); - COPY_VAR(acl_tag_actions); - COPY_VAR(acl_tag_datas); + COPY_VAR_ptr(local_zone_tags); + COPY_VAR_ptr(acl_tags); + COPY_VAR_ptr(acl_tag_actions); + COPY_VAR_ptr(acl_tag_datas); */ - COPY_VAR(acl_view); - COPY_VAR(interface_actions); + COPY_VAR_ptr(acl_view); + COPY_VAR_ptr(interface_actions); /* These reference tags - COPY_VAR(interface_tags); - COPY_VAR(interface_tag_actions); - COPY_VAR(interface_tag_datas); + COPY_VAR_ptr(interface_tags); + COPY_VAR_ptr(interface_tag_actions); + COPY_VAR_ptr(interface_tag_datas); */ - COPY_VAR(interface_view); + COPY_VAR_ptr(interface_view); /* This references tags - COPY_VAR(respip_tags); + COPY_VAR_ptr(respip_tags); */ - COPY_VAR(respip_actions); - COPY_VAR(respip_data); + COPY_VAR_ptr(respip_actions); + COPY_VAR_ptr(respip_data); /* Not copied because the length and items could then not match. * also the respip module keeps a pointer to the array in its state. tagname, num_tags */ - COPY_VAR(remote_control_enable); + COPY_VAR_int(remote_control_enable); /* The first is used to walk throught the list but last is * only used during config read. */ - COPY_VAR(control_ifs.first); - COPY_VAR(control_ifs.last); - COPY_VAR(control_use_cert); - COPY_VAR(control_port); - COPY_VAR(server_key_file); - COPY_VAR(server_cert_file); - COPY_VAR(control_key_file); - COPY_VAR(control_cert_file); - COPY_VAR(python_script); - COPY_VAR(dynlib_file); - COPY_VAR(use_systemd); - COPY_VAR(do_daemonize); - COPY_VAR(minimal_responses); - COPY_VAR(rrset_roundrobin); - COPY_VAR(unknown_server_time_limit); - COPY_VAR(max_udp_size); - COPY_VAR(dns64_prefix); - COPY_VAR(dns64_synthall); - COPY_VAR(dns64_ignore_aaaa); - COPY_VAR(nat64_prefix); - COPY_VAR(dnstap); - COPY_VAR(dnstap_bidirectional); - COPY_VAR(dnstap_socket_path); - COPY_VAR(dnstap_ip); - COPY_VAR(dnstap_tls); - COPY_VAR(dnstap_tls_server_name); - COPY_VAR(dnstap_tls_cert_bundle); - COPY_VAR(dnstap_tls_client_key_file); - COPY_VAR(dnstap_tls_client_cert_file); - COPY_VAR(dnstap_send_identity); - COPY_VAR(dnstap_send_version); - COPY_VAR(dnstap_identity); - COPY_VAR(dnstap_version); - COPY_VAR(dnstap_log_resolver_query_messages); - COPY_VAR(dnstap_log_resolver_response_messages); - COPY_VAR(dnstap_log_client_query_messages); - COPY_VAR(dnstap_log_client_response_messages); - COPY_VAR(dnstap_log_forwarder_query_messages); - COPY_VAR(dnstap_log_forwarder_response_messages); - COPY_VAR(disable_dnssec_lame_check); - COPY_VAR(ip_ratelimit); - COPY_VAR(ip_ratelimit_cookie); - COPY_VAR(ip_ratelimit_slabs); - COPY_VAR(ip_ratelimit_size); - COPY_VAR(ip_ratelimit_factor); - COPY_VAR(ip_ratelimit_backoff); - COPY_VAR(ratelimit); - COPY_VAR(ratelimit_slabs); - COPY_VAR(ratelimit_size); - COPY_VAR(ratelimit_for_domain); - COPY_VAR(ratelimit_below_domain); - COPY_VAR(ratelimit_factor); - COPY_VAR(ratelimit_backoff); - COPY_VAR(outbound_msg_retry); - COPY_VAR(max_sent_count); - COPY_VAR(max_query_restarts); - COPY_VAR(qname_minimisation); - COPY_VAR(qname_minimisation_strict); - COPY_VAR(shm_enable); - COPY_VAR(shm_key); - COPY_VAR(edns_client_strings); - COPY_VAR(edns_client_string_opcode); - COPY_VAR(dnscrypt); - COPY_VAR(dnscrypt_port); - COPY_VAR(dnscrypt_provider); - COPY_VAR(dnscrypt_secret_key); - COPY_VAR(dnscrypt_provider_cert); - COPY_VAR(dnscrypt_provider_cert_rotated); - COPY_VAR(dnscrypt_shared_secret_cache_size); - COPY_VAR(dnscrypt_shared_secret_cache_slabs); - COPY_VAR(dnscrypt_nonce_cache_size); - COPY_VAR(dnscrypt_nonce_cache_slabs); - COPY_VAR(pad_responses); - COPY_VAR(pad_responses_block_size); - COPY_VAR(pad_queries); - COPY_VAR(pad_queries_block_size); + COPY_VAR_ptr(control_ifs.first); + COPY_VAR_ptr(control_ifs.last); + COPY_VAR_int(control_use_cert); + COPY_VAR_int(control_port); + COPY_VAR_ptr(server_key_file); + COPY_VAR_ptr(server_cert_file); + COPY_VAR_ptr(control_key_file); + COPY_VAR_ptr(control_cert_file); + COPY_VAR_ptr(python_script); + COPY_VAR_ptr(dynlib_file); + COPY_VAR_int(use_systemd); + COPY_VAR_int(do_daemonize); + COPY_VAR_int(minimal_responses); + COPY_VAR_int(rrset_roundrobin); + COPY_VAR_int(unknown_server_time_limit); + COPY_VAR_int(discard_timeout); + COPY_VAR_int(wait_limit); + COPY_VAR_int(wait_limit_cookie); + COPY_VAR_ptr(wait_limit_netblock); + COPY_VAR_ptr(wait_limit_cookie_netblock); + COPY_VAR_size_t(max_udp_size); + COPY_VAR_ptr(dns64_prefix); + COPY_VAR_int(dns64_synthall); + COPY_VAR_ptr(dns64_ignore_aaaa); + COPY_VAR_ptr(nat64_prefix); + COPY_VAR_int(dnstap); + COPY_VAR_int(dnstap_bidirectional); + COPY_VAR_ptr(dnstap_socket_path); + COPY_VAR_ptr(dnstap_ip); + COPY_VAR_int(dnstap_tls); + COPY_VAR_ptr(dnstap_tls_server_name); + COPY_VAR_ptr(dnstap_tls_cert_bundle); + COPY_VAR_ptr(dnstap_tls_client_key_file); + COPY_VAR_ptr(dnstap_tls_client_cert_file); + COPY_VAR_int(dnstap_send_identity); + COPY_VAR_int(dnstap_send_version); + COPY_VAR_ptr(dnstap_identity); + COPY_VAR_ptr(dnstap_version); + COPY_VAR_int(dnstap_sample_rate); + COPY_VAR_int(dnstap_log_resolver_query_messages); + COPY_VAR_int(dnstap_log_resolver_response_messages); + COPY_VAR_int(dnstap_log_client_query_messages); + COPY_VAR_int(dnstap_log_client_response_messages); + COPY_VAR_int(dnstap_log_forwarder_query_messages); + COPY_VAR_int(dnstap_log_forwarder_response_messages); + COPY_VAR_int(disable_dnssec_lame_check); + COPY_VAR_int(ip_ratelimit); + COPY_VAR_int(ip_ratelimit_cookie); + COPY_VAR_size_t(ip_ratelimit_slabs); + COPY_VAR_size_t(ip_ratelimit_size); + COPY_VAR_int(ip_ratelimit_factor); + COPY_VAR_int(ip_ratelimit_backoff); + COPY_VAR_int(ratelimit); + COPY_VAR_size_t(ratelimit_slabs); + COPY_VAR_size_t(ratelimit_size); + COPY_VAR_ptr(ratelimit_for_domain); + COPY_VAR_ptr(ratelimit_below_domain); + COPY_VAR_int(ratelimit_factor); + COPY_VAR_int(ratelimit_backoff); + COPY_VAR_int(outbound_msg_retry); + COPY_VAR_int(max_sent_count); + COPY_VAR_int(max_query_restarts); + COPY_VAR_int(qname_minimisation); + COPY_VAR_int(qname_minimisation_strict); + COPY_VAR_int(shm_enable); + COPY_VAR_int(shm_key); + COPY_VAR_ptr(edns_client_strings); + COPY_VAR_uint16_t(edns_client_string_opcode); + COPY_VAR_int(dnscrypt); + COPY_VAR_int(dnscrypt_port); + COPY_VAR_ptr(dnscrypt_provider); + COPY_VAR_ptr(dnscrypt_secret_key); + COPY_VAR_ptr(dnscrypt_provider_cert); + COPY_VAR_ptr(dnscrypt_provider_cert_rotated); + COPY_VAR_size_t(dnscrypt_shared_secret_cache_size); + COPY_VAR_size_t(dnscrypt_shared_secret_cache_slabs); + COPY_VAR_size_t(dnscrypt_nonce_cache_size); + COPY_VAR_size_t(dnscrypt_nonce_cache_slabs); + COPY_VAR_int(pad_responses); + COPY_VAR_size_t(pad_responses_block_size); + COPY_VAR_int(pad_queries); + COPY_VAR_size_t(pad_queries_block_size); #ifdef USE_IPSECMOD - COPY_VAR(ipsecmod_enabled); - COPY_VAR(ipsecmod_whitelist); - COPY_VAR(ipsecmod_hook); - COPY_VAR(ipsecmod_ignore_bogus); - COPY_VAR(ipsecmod_max_ttl); - COPY_VAR(ipsecmod_strict); + COPY_VAR_int(ipsecmod_enabled); + COPY_VAR_ptr(ipsecmod_whitelist); + COPY_VAR_ptr(ipsecmod_hook); + COPY_VAR_int(ipsecmod_ignore_bogus); + COPY_VAR_int(ipsecmod_max_ttl); + COPY_VAR_int(ipsecmod_strict); #endif #ifdef USE_CACHEDB - COPY_VAR(cachedb_backend); - COPY_VAR(cachedb_secret); - COPY_VAR(cachedb_no_store); + COPY_VAR_ptr(cachedb_backend); + COPY_VAR_ptr(cachedb_secret); + COPY_VAR_int(cachedb_no_store); + COPY_VAR_int(cachedb_check_when_serve_expired); #ifdef USE_REDIS - COPY_VAR(redis_server_host); - COPY_VAR(redis_server_port); - COPY_VAR(redis_server_path); - COPY_VAR(redis_server_password); - COPY_VAR(redis_timeout); - COPY_VAR(redis_expire_records); - COPY_VAR(redis_logical_db); + COPY_VAR_ptr(redis_server_host); + COPY_VAR_int(redis_server_port); + COPY_VAR_ptr(redis_server_path); + COPY_VAR_ptr(redis_server_password); + COPY_VAR_int(redis_timeout); + COPY_VAR_int(redis_expire_records); + COPY_VAR_int(redis_logical_db); #endif #endif - COPY_VAR(do_answer_cookie); + COPY_VAR_int(do_answer_cookie); /* Not copied because the length and content could then not match. cookie_secret[40], cookie_secret_len */ #ifdef USE_IPSET - COPY_VAR(ipset_name_v4); - COPY_VAR(ipset_name_v6); + COPY_VAR_ptr(ipset_name_v4); + COPY_VAR_ptr(ipset_name_v6); #endif - COPY_VAR(ede); + COPY_VAR_int(ede); } #endif /* ATOMIC_POINTER_LOCK_FREE */ diff --git a/validator/validator.c b/validator/validator.c index 903daae9c..171135a7f 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -99,7 +99,9 @@ fill_nsec3_iter(size_t** keysize, size_t** maxiter, char* s, int c) *maxiter = (size_t*)calloc(sizeof(size_t), (size_t)c); if(!*keysize || !*maxiter) { free(*keysize); + *keysize = NULL; free(*maxiter); + *maxiter = NULL; log_err("out of memory"); return 0; } @@ -107,18 +109,30 @@ fill_nsec3_iter(size_t** keysize, size_t** maxiter, char* s, int c) (*keysize)[i] = (size_t)strtol(s, &e, 10); if(s == e) { log_err("cannot parse: %s", s); + free(*keysize); + *keysize = NULL; + free(*maxiter); + *maxiter = NULL; return 0; } s = e; (*maxiter)[i] = (size_t)strtol(s, &e, 10); if(s == e) { log_err("cannot parse: %s", s); + free(*keysize); + *keysize = NULL; + free(*maxiter); + *maxiter = NULL; return 0; } s = e; if(i>0 && (*keysize)[i-1] >= (*keysize)[i]) { log_err("nsec3 key iterations not ascending: %d %d", (int)(*keysize)[i-1], (int)(*keysize)[i]); + free(*keysize); + *keysize = NULL; + free(*maxiter); + *maxiter = NULL; return 0; } verbose(VERB_ALGO, "validator nsec3cfg keysz %d mxiter %d",