summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-09-12 04:45:07 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-09-12 04:45:07 +0000
commit0335817ced71e8355806ea0445aa3b105a22364c (patch)
treedffe735f2668a4728d8567feaf7ccb2d73076bac /src
parentAdding upstream version 3.3.9. (diff)
downloadknot-0335817ced71e8355806ea0445aa3b105a22364c.tar.xz
knot-0335817ced71e8355806ea0445aa3b105a22364c.zip
Adding upstream version 3.4.0.upstream/3.4.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.in462
-rw-r--r--src/config.h.in44
-rw-r--r--src/contrib/Makefile.inc1
-rw-r--r--src/contrib/asan.h13
-rw-r--r--src/contrib/atomic.h73
-rw-r--r--src/contrib/files.c23
-rw-r--r--src/contrib/files.h9
-rw-r--r--src/contrib/json.c7
-rw-r--r--src/contrib/json.h7
-rw-r--r--src/contrib/mempattern.c10
-rw-r--r--src/contrib/spinlock.h80
-rw-r--r--src/contrib/string.c20
-rw-r--r--src/contrib/string.h4
-rw-r--r--src/contrib/time.h5
-rw-r--r--src/knot/Makefile.inc12
-rw-r--r--src/knot/catalog/catalog_db.c4
-rw-r--r--src/knot/catalog/catalog_update.c4
-rw-r--r--src/knot/catalog/interpret.c4
-rw-r--r--src/knot/common/dbus.c243
-rw-r--r--src/knot/common/dbus.h86
-rw-r--r--src/knot/common/fdset.c9
-rw-r--r--src/knot/common/fdset.h13
-rw-r--r--src/knot/common/stats.c356
-rw-r--r--src/knot/common/stats.h61
-rw-r--r--src/knot/common/systemd.c98
-rw-r--r--src/knot/common/systemd.h66
-rw-r--r--src/knot/conf/base.c19
-rw-r--r--src/knot/conf/base.h3
-rw-r--r--src/knot/conf/conf.c15
-rw-r--r--src/knot/conf/conf.h17
-rw-r--r--src/knot/conf/schema.c35
-rw-r--r--src/knot/conf/schema.h25
-rw-r--r--src/knot/conf/tools.c93
-rw-r--r--src/knot/ctl/commands.c547
-rw-r--r--src/knot/ctl/commands.h4
-rw-r--r--src/knot/ctl/process.c17
-rw-r--r--src/knot/ctl/process.h12
-rw-r--r--src/knot/dnssec/context.c15
-rw-r--r--src/knot/dnssec/context.h16
-rw-r--r--src/knot/dnssec/ds_query.c4
-rw-r--r--src/knot/dnssec/key-events.c6
-rw-r--r--src/knot/dnssec/key_records.c23
-rw-r--r--src/knot/dnssec/key_records.h6
-rw-r--r--src/knot/dnssec/rrset-sign.c17
-rw-r--r--src/knot/dnssec/rrset-sign.h6
-rw-r--r--src/knot/dnssec/zone-events.c95
-rw-r--r--src/knot/dnssec/zone-events.h22
-rw-r--r--src/knot/dnssec/zone-sign.c98
-rw-r--r--src/knot/dnssec/zone-sign.h14
-rw-r--r--src/knot/events/events.c3
-rw-r--r--src/knot/events/events.h3
-rw-r--r--src/knot/events/handlers.h4
-rw-r--r--src/knot/events/handlers/dnskey_sync.c4
-rw-r--r--src/knot/events/handlers/ds_push.c6
-rw-r--r--src/knot/events/handlers/notify.c4
-rw-r--r--src/knot/events/handlers/refresh.c39
-rw-r--r--src/knot/events/handlers/update.c86
-rw-r--r--src/knot/events/handlers/validate.c34
-rw-r--r--src/knot/events/replan.c5
-rw-r--r--src/knot/include/module.h55
-rw-r--r--src/knot/modules/cookies/cookies.c19
-rw-r--r--src/knot/modules/probe/probe.c13
-rw-r--r--src/knot/modules/rrl/Makefile.inc9
-rw-r--r--src/knot/modules/rrl/functions.c589
-rw-r--r--src/knot/modules/rrl/functions.h88
-rw-r--r--src/knot/modules/rrl/kru-avx2.c66
-rw-r--r--src/knot/modules/rrl/kru-generic.c20
-rw-r--r--src/knot/modules/rrl/kru.h90
-rw-r--r--src/knot/modules/rrl/kru.inc.c615
-rw-r--r--src/knot/modules/rrl/rrl.c243
-rw-r--r--src/knot/modules/rrl/rrl.rst169
-rw-r--r--src/knot/modules/stats/stats.c14
-rw-r--r--src/knot/modules/stats/stats.rst2
-rw-r--r--src/knot/nameserver/axfr.c6
-rw-r--r--src/knot/nameserver/axfr.h6
-rw-r--r--src/knot/nameserver/internet.c50
-rw-r--r--src/knot/nameserver/internet.h17
-rw-r--r--src/knot/nameserver/ixfr.c10
-rw-r--r--src/knot/nameserver/ixfr.h8
-rw-r--r--src/knot/nameserver/log.h2
-rw-r--r--src/knot/nameserver/notify.c6
-rw-r--r--src/knot/nameserver/notify.h7
-rw-r--r--src/knot/nameserver/nsec_proofs.c6
-rw-r--r--src/knot/nameserver/process_query.c83
-rw-r--r--src/knot/nameserver/process_query.h13
-rw-r--r--src/knot/nameserver/query_module.c50
-rw-r--r--src/knot/nameserver/query_module.h31
-rw-r--r--src/knot/nameserver/update.c35
-rw-r--r--src/knot/nameserver/update.h6
-rw-r--r--src/knot/nameserver/xfr.h6
-rw-r--r--src/knot/query/quic-requestor.c13
-rw-r--r--src/knot/query/quic-requestor.h8
-rw-r--r--src/knot/query/requestor.c62
-rw-r--r--src/knot/query/requestor.h38
-rw-r--r--src/knot/query/tls-requestor.c59
-rw-r--r--src/knot/query/tls-requestor.h51
-rw-r--r--src/knot/server/handler.c13
-rw-r--r--src/knot/server/handler.h35
-rw-r--r--src/knot/server/quic-handler.c11
-rw-r--r--src/knot/server/server.c220
-rw-r--r--src/knot/server/server.h17
-rw-r--r--src/knot/server/tcp-handler.c115
-rw-r--r--src/knot/server/udp-handler.c25
-rw-r--r--src/knot/server/xdp-handler.c61
-rw-r--r--src/knot/updates/acl.c29
-rw-r--r--src/knot/updates/acl.h33
-rw-r--r--src/knot/updates/ddns.c181
-rw-r--r--src/knot/updates/ddns.h14
-rw-r--r--src/knot/updates/zone-update.c26
-rw-r--r--src/knot/updates/zone-update.h2
-rw-r--r--src/knot/zone/backup.c8
-rw-r--r--src/knot/zone/backup.h6
-rw-r--r--src/knot/zone/backup_dir.c42
-rw-r--r--src/knot/zone/contents.c10
-rw-r--r--src/knot/zone/contents.h4
-rw-r--r--src/knot/zone/digest.c6
-rw-r--r--src/knot/zone/semantic-check.c6
-rw-r--r--src/knot/zone/semantic-check.h1
-rw-r--r--src/knot/zone/zone-tree.c4
-rw-r--r--src/knot/zone/zone.c10
-rw-r--r--src/knot/zone/zone.h6
-rw-r--r--src/knot/zone/zonedb-load.c10
-rw-r--r--src/knot/zone/zonedb.c4
-rw-r--r--src/knot/zone/zonefile.c15
-rw-r--r--src/libdnssec/key/algorithm.c12
-rw-r--r--src/libdnssec/key/convert.c22
-rw-r--r--src/libdnssec/pem.c21
-rw-r--r--src/libdnssec/sign/sign.c41
-rw-r--r--src/libdnssec/version.h4
-rwxr-xr-xsrc/libknot/Makefile.inc4
-rw-r--r--src/libknot/attribute.h14
-rw-r--r--src/libknot/control/control.c23
-rw-r--r--src/libknot/control/control.h24
-rw-r--r--src/libknot/dname.c36
-rw-r--r--src/libknot/dname.h9
-rw-r--r--src/libknot/errcode.h3
-rw-r--r--src/libknot/error.c3
-rw-r--r--src/libknot/packet/pkt.h1
-rw-r--r--src/libknot/packet/rrset-wire.c22
-rw-r--r--src/libknot/packet/rrset-wire.h6
-rw-r--r--src/libknot/packet/wire.h21
-rw-r--r--src/libknot/quic/quic.c343
-rw-r--r--src/libknot/quic/quic.h59
-rw-r--r--src/libknot/quic/quic_conn.c35
-rw-r--r--src/libknot/quic/quic_conn.h20
-rw-r--r--src/libknot/quic/tls.c262
-rw-r--r--src/libknot/quic/tls.h135
-rw-r--r--src/libknot/quic/tls_common.c472
-rw-r--r--src/libknot/quic/tls_common.h134
-rw-r--r--src/libknot/rrset-dump.c140
-rw-r--r--src/libknot/rrtype/tsig.c6
-rw-r--r--src/libknot/version.h4
-rw-r--r--src/libknot/xdp/Makefile.in5
-rw-r--r--src/libknot/xdp/bpf-user.h12
-rw-r--r--src/libknot/xdp/tcp.c266
-rw-r--r--src/libknot/xdp/tcp.h13
-rw-r--r--src/libknot/xdp/xdp.c253
-rw-r--r--src/libknot/xdp/xdp.h58
-rw-r--r--src/libknot/yparser/ypschema.h2
-rw-r--r--src/libknot/yparser/yptrafo.c60
-rw-r--r--src/libzscanner/version.h4
-rw-r--r--src/utils/Makefile.inc6
-rw-r--r--src/utils/common/msg.h16
-rw-r--r--src/utils/common/netio.c7
-rw-r--r--src/utils/common/params.c2
-rw-r--r--src/utils/common/params.h13
-rw-r--r--src/utils/common/quic.h4
-rw-r--r--src/utils/common/tls.c7
-rw-r--r--src/utils/kcatalogprint/main.c14
-rw-r--r--src/utils/kdig/kdig_params.c13
-rw-r--r--src/utils/keymgr/bind_privkey.c8
-rw-r--r--src/utils/keymgr/main.c10
-rw-r--r--src/utils/keymgr/offline_ksk.c31
-rw-r--r--src/utils/khost/khost_params.c10
-rw-r--r--src/utils/kjournalprint/main.c17
-rw-r--r--src/utils/knotc/commands.c9
-rw-r--r--src/utils/knotc/main.c6
-rw-r--r--src/utils/knotd/main.c256
-rw-r--r--src/utils/knsec3hash/knsec3hash.c12
-rw-r--r--src/utils/knsupdate/knsupdate_exec.c12
-rw-r--r--src/utils/knsupdate/knsupdate_params.c160
-rw-r--r--src/utils/knsupdate/knsupdate_params.h8
-rw-r--r--src/utils/kxdpgun/load_queries.c241
-rw-r--r--src/utils/kxdpgun/load_queries.h14
-rw-r--r--src/utils/kxdpgun/main.c578
-rw-r--r--src/utils/kxdpgun/main.h87
-rw-r--r--src/utils/kxdpgun/stats.c292
-rw-r--r--src/utils/kxdpgun/stats.h78
-rw-r--r--src/utils/kzonecheck/main.c6
-rw-r--r--src/utils/kzonesign/main.c10
190 files changed, 7212 insertions, 3754 deletions
diff --git a/src/Makefile.in b/src/Makefile.in
index 09daaa2..8630517 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -160,28 +160,29 @@ host_triplet = @host@
@STATIC_MODULE_queryacl_TRUE@am__append_41 = $(knot_modules_queryacl_la_SOURCES)
@SHARED_MODULE_queryacl_TRUE@am__append_42 = knot/modules/queryacl.la
@STATIC_MODULE_rrl_TRUE@am__append_43 = $(knot_modules_rrl_la_SOURCES)
-@SHARED_MODULE_rrl_TRUE@am__append_44 = knot/modules/rrl.la
-@STATIC_MODULE_stats_TRUE@am__append_45 = $(knot_modules_stats_la_SOURCES)
-@SHARED_MODULE_stats_TRUE@am__append_46 = knot/modules/stats.la
-@STATIC_MODULE_synthrecord_TRUE@am__append_47 = $(knot_modules_synthrecord_la_SOURCES)
-@SHARED_MODULE_synthrecord_TRUE@am__append_48 = knot/modules/synthrecord.la
-@STATIC_MODULE_whoami_TRUE@am__append_49 = $(knot_modules_whoami_la_SOURCES)
-@SHARED_MODULE_whoami_TRUE@am__append_50 = knot/modules/whoami.la
+@STATIC_MODULE_rrl_TRUE@am__append_44 = $(math_LIBS)
+@SHARED_MODULE_rrl_TRUE@am__append_45 = knot/modules/rrl.la
+@STATIC_MODULE_stats_TRUE@am__append_46 = $(knot_modules_stats_la_SOURCES)
+@SHARED_MODULE_stats_TRUE@am__append_47 = knot/modules/stats.la
+@STATIC_MODULE_synthrecord_TRUE@am__append_48 = $(knot_modules_synthrecord_la_SOURCES)
+@SHARED_MODULE_synthrecord_TRUE@am__append_49 = knot/modules/synthrecord.la
+@STATIC_MODULE_whoami_TRUE@am__append_50 = $(knot_modules_whoami_la_SOURCES)
+@SHARED_MODULE_whoami_TRUE@am__append_51 = knot/modules/whoami.la
bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2)
sbin_PROGRAMS = $(am__EXEEXT_3) $(am__EXEEXT_4) $(am__EXEEXT_5)
-@HAVE_LIBUTILS_TRUE@am__append_51 = libknotus.la
-@EMBEDDED_LIBNGTCP2_TRUE@@HAVE_LIBUTILS_TRUE@am__append_52 = $(libembngtcp2_LIBS)
-@HAVE_UTILS_TRUE@am__append_53 = kdig khost knsec3hash knsupdate
-@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_54 = $(DNSTAP_CFLAGS)
-@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_55 = $(libdnstap_LIBS)
-@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_56 = $(DNSTAP_CFLAGS)
-@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_57 = $(libdnstap_LIBS)
-@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__append_58 = kxdpgun
-@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__append_59 = $(gnutls_CFLAGS)
-@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__append_60 = $(gnutls_LIBS)
-@HAVE_DAEMON_TRUE@am__append_61 = knotc knotd
-@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__append_62 = kzonecheck kzonesign
-@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__append_63 = keymgr kjournalprint kcatalogprint
+@HAVE_LIBUTILS_TRUE@am__append_52 = libknotus.la
+@EMBEDDED_LIBNGTCP2_TRUE@@HAVE_LIBUTILS_TRUE@am__append_53 = $(libembngtcp2_LIBS)
+@HAVE_UTILS_TRUE@am__append_54 = kdig khost knsec3hash knsupdate
+@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_55 = $(DNSTAP_CFLAGS)
+@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_56 = $(libdnstap_LIBS)
+@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_57 = $(DNSTAP_CFLAGS)
+@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_58 = $(libdnstap_LIBS)
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__append_59 = kxdpgun
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__append_60 = $(gnutls_CFLAGS)
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__append_61 = $(gnutls_LIBS)
+@HAVE_DAEMON_TRUE@am__append_62 = knotc knotd
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__append_63 = kzonecheck kzonesign
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__append_64 = keymgr kjournalprint kcatalogprint
subdir = src
SUBDIRS =
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -198,7 +199,8 @@ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(include_libdnssec_HEADERS) \
$(include_libknotd_HEADERS) $(include_libzscanner_HEADERS) \
- $(am__nobase_include_libknot_HEADERS_DIST) $(am__DIST_COMMON)
+ $(am__nobase_include_libknot_HEADERS_DIST) $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES = knotd.pc libknot.pc libdnssec.pc libzscanner.pc
@@ -359,9 +361,12 @@ knot_modules_queryacl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
@SHARED_MODULE_queryacl_TRUE@am_knot_modules_queryacl_la_rpath = \
@SHARED_MODULE_queryacl_TRUE@ -rpath $(pkglibdir)
@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_DEPENDENCIES = \
-@SHARED_MODULE_rrl_TRUE@ $(am__DEPENDENCIES_3)
+@SHARED_MODULE_rrl_TRUE@ $(am__DEPENDENCIES_3) \
+@SHARED_MODULE_rrl_TRUE@ $(am__DEPENDENCIES_1)
am_knot_modules_rrl_la_OBJECTS = knot/modules/rrl/la-rrl.lo \
- knot/modules/rrl/la-functions.lo
+ knot/modules/rrl/la-functions.lo \
+ knot/modules/rrl/la-kru-generic.lo \
+ knot/modules/rrl/la-kru-avx2.lo
knot_modules_rrl_la_OBJECTS = $(am_knot_modules_rrl_la_OBJECTS)
knot_modules_rrl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
@@ -627,17 +632,19 @@ am__libknot_la_SOURCES_DIST = libknot/codes.c \
libknot/descriptor.c libknot/dname.c libknot/error.c \
libknot/db/db_lmdb.c libknot/db/db_trie.c libknot/packet/pkt.c \
libknot/packet/rrset-wire.c libknot/probe/data.c \
- libknot/probe/probe.c libknot/rdataset.c libknot/rrset-dump.c \
- libknot/rrset.c libknot/rrtype/naptr.c libknot/rrtype/opt.c \
- libknot/rrtype/tsig.c libknot/tsig-op.c libknot/tsig.c \
- libknot/yparser/yparser.c libknot/yparser/ypbody.c \
- libknot/yparser/ypformat.c libknot/yparser/ypschema.c \
- libknot/yparser/yptrafo.c libknot/xdp/tcp_iobuf.c \
- libknot/xdp/bpf-kernel-obj.c libknot/xdp/bpf-kernel-obj.h \
- libknot/xdp/bpf-user.c libknot/xdp/bpf-user.h \
- libknot/xdp/eth.c libknot/xdp/msg_init.h \
- libknot/xdp/protocols.h libknot/xdp/tcp.c libknot/xdp/xdp.c \
- libknot/quic/quic.c libknot/quic/quic_conn.c
+ libknot/probe/probe.c libknot/quic/tls.c \
+ libknot/quic/tls_common.c libknot/rdataset.c \
+ libknot/rrset-dump.c libknot/rrset.c libknot/rrtype/naptr.c \
+ libknot/rrtype/opt.c libknot/rrtype/tsig.c libknot/tsig-op.c \
+ libknot/tsig.c libknot/yparser/yparser.c \
+ libknot/yparser/ypbody.c libknot/yparser/ypformat.c \
+ libknot/yparser/ypschema.c libknot/yparser/yptrafo.c \
+ libknot/xdp/tcp_iobuf.c libknot/xdp/bpf-kernel-obj.c \
+ libknot/xdp/bpf-kernel-obj.h libknot/xdp/bpf-user.c \
+ libknot/xdp/bpf-user.h libknot/xdp/eth.c \
+ libknot/xdp/msg_init.h libknot/xdp/protocols.h \
+ libknot/xdp/tcp.c libknot/xdp/xdp.c libknot/quic/quic.c \
+ libknot/quic/quic_conn.c
@ENABLE_XDP_TRUE@am__objects_1 = libknot/xdp/la-bpf-kernel-obj.lo \
@ENABLE_XDP_TRUE@ libknot/xdp/la-bpf-user.lo \
@ENABLE_XDP_TRUE@ libknot/xdp/la-eth.lo libknot/xdp/la-tcp.lo \
@@ -650,7 +657,8 @@ am_libknot_la_OBJECTS = libknot/la-codes.lo \
libknot/la-error.lo libknot/db/la-db_lmdb.lo \
libknot/db/la-db_trie.lo libknot/packet/la-pkt.lo \
libknot/packet/la-rrset-wire.lo libknot/probe/la-data.lo \
- libknot/probe/la-probe.lo libknot/la-rdataset.lo \
+ libknot/probe/la-probe.lo libknot/quic/la-tls.lo \
+ libknot/quic/la-tls_common.lo libknot/la-rdataset.lo \
libknot/la-rrset-dump.lo libknot/la-rrset.lo \
libknot/rrtype/la-naptr.lo libknot/rrtype/la-opt.lo \
libknot/rrtype/la-tsig.lo libknot/la-tsig-op.lo \
@@ -665,10 +673,11 @@ libknot_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
@STATIC_MODULE_dnstap_TRUE@am__DEPENDENCIES_10 = \
@STATIC_MODULE_dnstap_TRUE@ $(am__DEPENDENCIES_4)
@STATIC_MODULE_geoip_TRUE@am__DEPENDENCIES_11 = $(am__DEPENDENCIES_1)
+@STATIC_MODULE_rrl_TRUE@am__DEPENDENCIES_12 = $(am__DEPENDENCIES_1)
libknotd_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_7) $(am__DEPENDENCIES_10) \
- $(am__DEPENDENCIES_11)
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_7) \
+ $(am__DEPENDENCIES_10) $(am__DEPENDENCIES_11) \
+ $(am__DEPENDENCIES_12)
am__libknotd_la_SOURCES_DIST = knot/catalog/catalog_db.c \
knot/catalog/catalog_db.h knot/catalog/catalog_update.c \
knot/catalog/catalog_update.h knot/catalog/generate.c \
@@ -705,15 +714,15 @@ am__libknotd_la_SOURCES_DIST = knot/catalog/catalog_db.c \
knot/events/handlers/flush.c \
knot/events/handlers/freeze_thaw.c knot/events/handlers/load.c \
knot/events/handlers/notify.c knot/events/handlers/refresh.c \
- knot/events/handlers/update.c knot/events/replan.c \
- knot/events/replan.h knot/nameserver/axfr.c \
- knot/nameserver/axfr.h knot/nameserver/chaos.c \
- knot/nameserver/chaos.h knot/nameserver/internet.c \
- knot/nameserver/internet.h knot/nameserver/ixfr.c \
- knot/nameserver/ixfr.h knot/nameserver/log.h \
- knot/nameserver/notify.c knot/nameserver/notify.h \
- knot/nameserver/nsec_proofs.c knot/nameserver/nsec_proofs.h \
- knot/nameserver/process_query.c \
+ knot/events/handlers/update.c knot/events/handlers/validate.c \
+ knot/events/replan.c knot/events/replan.h \
+ knot/nameserver/axfr.c knot/nameserver/axfr.h \
+ knot/nameserver/chaos.c knot/nameserver/chaos.h \
+ knot/nameserver/internet.c knot/nameserver/internet.h \
+ knot/nameserver/ixfr.c knot/nameserver/ixfr.h \
+ knot/nameserver/log.h knot/nameserver/notify.c \
+ knot/nameserver/notify.h knot/nameserver/nsec_proofs.c \
+ knot/nameserver/nsec_proofs.h knot/nameserver/process_query.c \
knot/nameserver/process_query.h knot/nameserver/query_module.c \
knot/nameserver/query_module.h knot/nameserver/tsig_ctx.c \
knot/nameserver/tsig_ctx.h knot/nameserver/update.c \
@@ -721,14 +730,15 @@ am__libknotd_la_SOURCES_DIST = knot/catalog/catalog_db.c \
knot/nameserver/xfr.h knot/query/capture.c \
knot/query/capture.h knot/query/layer.h knot/query/query.c \
knot/query/query.h knot/query/requestor.c \
- knot/query/requestor.h knot/common/evsched.c \
- knot/common/evsched.h knot/common/fdset.c knot/common/fdset.h \
- knot/common/log.c knot/common/log.h knot/common/process.c \
- knot/common/process.h knot/common/stats.c knot/common/stats.h \
- knot/common/systemd.c knot/common/systemd.h \
- knot/common/unreachable.c knot/common/unreachable.h \
- knot/journal/journal_basic.c knot/journal/journal_basic.h \
- knot/journal/journal_metadata.c \
+ knot/query/requestor.h knot/query/tls-requestor.c \
+ knot/query/tls-requestor.h knot/common/dbus.c \
+ knot/common/dbus.h knot/common/evsched.c knot/common/evsched.h \
+ knot/common/fdset.c knot/common/fdset.h knot/common/log.c \
+ knot/common/log.h knot/common/process.c knot/common/process.h \
+ knot/common/stats.c knot/common/stats.h knot/common/systemd.c \
+ knot/common/systemd.h knot/common/unreachable.c \
+ knot/common/unreachable.h knot/journal/journal_basic.c \
+ knot/journal/journal_basic.h knot/journal/journal_metadata.c \
knot/journal/journal_metadata.h knot/journal/journal_read.c \
knot/journal/journal_read.h knot/journal/journal_write.c \
knot/journal/journal_write.h knot/journal/knot_lmdb.c \
@@ -774,7 +784,8 @@ am__libknotd_la_SOURCES_DIST = knot/catalog/catalog_db.c \
knot/modules/onlinesign/nsec_next.h knot/modules/probe/probe.c \
knot/modules/queryacl/queryacl.c knot/modules/rrl/rrl.c \
knot/modules/rrl/functions.c knot/modules/rrl/functions.h \
- knot/modules/stats/stats.c \
+ knot/modules/rrl/kru-generic.c knot/modules/rrl/kru-avx2.c \
+ knot/modules/rrl/kru.h knot/modules/stats/stats.c \
knot/modules/synthrecord/synthrecord.c \
knot/modules/whoami/whoami.c
@ENABLE_QUIC_TRUE@am__objects_3 = \
@@ -801,7 +812,9 @@ am__objects_18 = knot/modules/probe/libknotd_la-probe.lo
am__objects_20 = knot/modules/queryacl/libknotd_la-queryacl.lo
@STATIC_MODULE_queryacl_TRUE@am__objects_21 = $(am__objects_20)
am__objects_22 = knot/modules/rrl/libknotd_la-rrl.lo \
- knot/modules/rrl/libknotd_la-functions.lo
+ knot/modules/rrl/libknotd_la-functions.lo \
+ knot/modules/rrl/libknotd_la-kru-generic.lo \
+ knot/modules/rrl/libknotd_la-kru-avx2.lo
@STATIC_MODULE_rrl_TRUE@am__objects_23 = $(am__objects_22)
am__objects_24 = knot/modules/stats/libknotd_la-stats.lo
@STATIC_MODULE_stats_TRUE@am__objects_25 = $(am__objects_24)
@@ -850,6 +863,7 @@ am_libknotd_la_OBJECTS = knot/catalog/libknotd_la-catalog_db.lo \
knot/events/handlers/libknotd_la-notify.lo \
knot/events/handlers/libknotd_la-refresh.lo \
knot/events/handlers/libknotd_la-update.lo \
+ knot/events/handlers/libknotd_la-validate.lo \
knot/events/libknotd_la-replan.lo \
knot/nameserver/libknotd_la-axfr.lo \
knot/nameserver/libknotd_la-chaos.lo \
@@ -865,6 +879,8 @@ am_libknotd_la_OBJECTS = knot/catalog/libknotd_la-catalog_db.lo \
knot/query/libknotd_la-capture.lo \
knot/query/libknotd_la-query.lo \
knot/query/libknotd_la-requestor.lo \
+ knot/query/libknotd_la-tls-requestor.lo \
+ knot/common/libknotd_la-dbus.lo \
knot/common/libknotd_la-evsched.lo \
knot/common/libknotd_la-fdset.lo \
knot/common/libknotd_la-log.lo \
@@ -921,12 +937,11 @@ libknotd_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libknotd_la_LDFLAGS) $(LDFLAGS) -o $@
@HAVE_DAEMON_TRUE@am_libknotd_la_rpath =
-@EMBEDDED_LIBNGTCP2_TRUE@@HAVE_LIBUTILS_TRUE@am__DEPENDENCIES_12 = $(am__DEPENDENCIES_6)
+@EMBEDDED_LIBNGTCP2_TRUE@@HAVE_LIBUTILS_TRUE@am__DEPENDENCIES_13 = $(am__DEPENDENCIES_6)
@HAVE_LIBUTILS_TRUE@libknotus_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \
@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \
-@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \
-@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_12)
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_13)
am__libknotus_la_SOURCES_DIST = utils/common/exec.c \
utils/common/exec.h utils/common/hex.c utils/common/hex.h \
utils/common/https.c utils/common/https.h \
@@ -972,17 +987,17 @@ libzscanner_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
am__kcatalogprint_SOURCES_DIST = utils/kcatalogprint/main.c
@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am_kcatalogprint_OBJECTS = utils/kcatalogprint/kcatalogprint-main.$(OBJEXT)
kcatalogprint_OBJECTS = $(am_kcatalogprint_OBJECTS)
-am__DEPENDENCIES_13 = libknotd.la libknot.la libdnssec.la \
+am__DEPENDENCIES_14 = libknotd.la libknot.la libdnssec.la \
libzscanner.la $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
-@HAVE_LIBUTILS_TRUE@am__DEPENDENCIES_14 = libknotus.la libknot.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+@HAVE_LIBUTILS_TRUE@am__DEPENDENCIES_15 = libknotus.la libknot.la \
@HAVE_LIBUTILS_TRUE@ libdnssec.la $(am__DEPENDENCIES_3) \
@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \
@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1)
@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kcatalogprint_DEPENDENCIES = \
-@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_13) \
-@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_14)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_14) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_15)
kcatalogprint_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(kcatalogprint_LDFLAGS) $(LDFLAGS) -o $@
@@ -994,10 +1009,10 @@ am__kdig_SOURCES_DIST = utils/kdig/kdig_exec.c utils/kdig/kdig_exec.h \
@HAVE_UTILS_TRUE@ utils/kdig/kdig-kdig_main.$(OBJEXT) \
@HAVE_UTILS_TRUE@ utils/kdig/kdig-kdig_params.$(OBJEXT)
kdig_OBJECTS = $(am_kdig_OBJECTS)
-@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__DEPENDENCIES_15 = \
+@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__DEPENDENCIES_16 = \
@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_4)
-@HAVE_UTILS_TRUE@kdig_DEPENDENCIES = $(am__DEPENDENCIES_14) \
-@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_15)
+@HAVE_UTILS_TRUE@kdig_DEPENDENCIES = $(am__DEPENDENCIES_15) \
+@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_16)
am__keymgr_SOURCES_DIST = utils/keymgr/bind_privkey.c \
utils/keymgr/bind_privkey.h utils/keymgr/functions.c \
utils/keymgr/functions.h utils/keymgr/keystore.c \
@@ -1010,8 +1025,8 @@ am__keymgr_SOURCES_DIST = utils/keymgr/bind_privkey.c \
@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/keymgr-main.$(OBJEXT)
keymgr_OBJECTS = $(am_keymgr_OBJECTS)
@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@keymgr_DEPENDENCIES = \
-@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_13) \
-@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_14)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_14) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_15)
keymgr_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(keymgr_LDFLAGS) $(LDFLAGS) -o $@
@@ -1025,14 +1040,14 @@ am__khost_SOURCES_DIST = utils/kdig/kdig_exec.c utils/kdig/kdig_exec.h \
@HAVE_UTILS_TRUE@ utils/khost/khost-khost_main.$(OBJEXT) \
@HAVE_UTILS_TRUE@ utils/khost/khost-khost_params.$(OBJEXT)
khost_OBJECTS = $(am_khost_OBJECTS)
-@HAVE_UTILS_TRUE@khost_DEPENDENCIES = $(am__DEPENDENCIES_14) \
-@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_15)
+@HAVE_UTILS_TRUE@khost_DEPENDENCIES = $(am__DEPENDENCIES_15) \
+@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_16)
am__kjournalprint_SOURCES_DIST = utils/kjournalprint/main.c
@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am_kjournalprint_OBJECTS = utils/kjournalprint/kjournalprint-main.$(OBJEXT)
kjournalprint_OBJECTS = $(am_kjournalprint_OBJECTS)
@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kjournalprint_DEPENDENCIES = \
-@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_13) \
-@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_14)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_14) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_15)
kjournalprint_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(kjournalprint_LDFLAGS) $(LDFLAGS) -o $@
@@ -1045,8 +1060,8 @@ am__knotc_SOURCES_DIST = utils/knotc/commands.c utils/knotc/commands.h \
@HAVE_DAEMON_TRUE@ utils/knotc/knotc-process.$(OBJEXT) \
@HAVE_DAEMON_TRUE@ utils/knotc/knotc-main.$(OBJEXT)
knotc_OBJECTS = $(am_knotc_OBJECTS)
-@HAVE_DAEMON_TRUE@knotc_DEPENDENCIES = $(am__DEPENDENCIES_13) \
-@HAVE_DAEMON_TRUE@ $(am__DEPENDENCIES_14)
+@HAVE_DAEMON_TRUE@knotc_DEPENDENCIES = $(am__DEPENDENCIES_14) \
+@HAVE_DAEMON_TRUE@ $(am__DEPENDENCIES_15)
knotc_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(knotc_LDFLAGS) $(LDFLAGS) -o $@
@@ -1054,7 +1069,7 @@ am__knotd_SOURCES_DIST = utils/knotd/main.c
@HAVE_DAEMON_TRUE@am_knotd_OBJECTS = utils/knotd/knotd-main.$(OBJEXT)
knotd_OBJECTS = $(am_knotd_OBJECTS)
@HAVE_DAEMON_TRUE@knotd_DEPENDENCIES = $(am__DEPENDENCIES_1) \
-@HAVE_DAEMON_TRUE@ $(am__DEPENDENCIES_13) $(am__DEPENDENCIES_1)
+@HAVE_DAEMON_TRUE@ $(am__DEPENDENCIES_14) $(am__DEPENDENCIES_1)
knotd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(knotd_LDFLAGS) $(LDFLAGS) -o $@
@@ -1075,28 +1090,31 @@ am__knsupdate_SOURCES_DIST = utils/knsupdate/knsupdate_exec.c \
@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate-knsupdate_main.$(OBJEXT) \
@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate-knsupdate_params.$(OBJEXT)
knsupdate_OBJECTS = $(am_knsupdate_OBJECTS)
-@HAVE_UTILS_TRUE@knsupdate_DEPENDENCIES = $(am__DEPENDENCIES_14) \
+@HAVE_UTILS_TRUE@knsupdate_DEPENDENCIES = $(am__DEPENDENCIES_15) \
@HAVE_UTILS_TRUE@ libzscanner.la
am__kxdpgun_SOURCES_DIST = utils/kxdpgun/ip_route.c \
utils/kxdpgun/ip_route.h utils/kxdpgun/load_queries.c \
- utils/kxdpgun/load_queries.h utils/kxdpgun/main.c
+ utils/kxdpgun/load_queries.h utils/kxdpgun/main.c \
+ utils/kxdpgun/main.h utils/kxdpgun/stats.c \
+ utils/kxdpgun/stats.h
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am_kxdpgun_OBJECTS = utils/kxdpgun/kxdpgun-ip_route.$(OBJEXT) \
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/kxdpgun-load_queries.$(OBJEXT) \
-@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/kxdpgun-main.$(OBJEXT)
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/kxdpgun-main.$(OBJEXT) \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/kxdpgun-stats.$(OBJEXT)
kxdpgun_OBJECTS = $(am_kxdpgun_OBJECTS)
-@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__DEPENDENCIES_16 = $(am__DEPENDENCIES_1)
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__DEPENDENCIES_17 = $(am__DEPENDENCIES_1)
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@kxdpgun_DEPENDENCIES = libknot.la \
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_3) \
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_1) \
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_1) \
-@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_16)
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_17)
am__kzonecheck_SOURCES_DIST = utils/kzonecheck/main.c \
utils/kzonecheck/zone_check.c utils/kzonecheck/zone_check.h
@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am_kzonecheck_OBJECTS = utils/kzonecheck/kzonecheck-main.$(OBJEXT) \
@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kzonecheck/kzonecheck-zone_check.$(OBJEXT)
kzonecheck_OBJECTS = $(am_kzonecheck_OBJECTS)
@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonecheck_DEPENDENCIES = \
-@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_13)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_14)
kzonecheck_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(kzonecheck_LDFLAGS) $(LDFLAGS) -o $@
@@ -1104,8 +1122,8 @@ am__kzonesign_SOURCES_DIST = utils/kzonesign/main.c
@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am_kzonesign_OBJECTS = utils/kzonesign/kzonesign-main.$(OBJEXT)
kzonesign_OBJECTS = $(am_kzonesign_OBJECTS)
@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonesign_DEPENDENCIES = \
-@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_13) \
-@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_14)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_14) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_15)
kzonesign_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(kzonesign_LDFLAGS) $(LDFLAGS) -o $@
@@ -1200,6 +1218,7 @@ am__depfiles_remade = contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo \
knot/catalog/$(DEPDIR)/libknotd_la-catalog_update.Plo \
knot/catalog/$(DEPDIR)/libknotd_la-generate.Plo \
knot/catalog/$(DEPDIR)/libknotd_la-interpret.Plo \
+ knot/common/$(DEPDIR)/libknotd_la-dbus.Plo \
knot/common/$(DEPDIR)/libknotd_la-evsched.Plo \
knot/common/$(DEPDIR)/libknotd_la-fdset.Plo \
knot/common/$(DEPDIR)/libknotd_la-log.Plo \
@@ -1247,6 +1266,7 @@ am__depfiles_remade = contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo \
knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Plo \
knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Plo \
knot/events/handlers/$(DEPDIR)/libknotd_la-update.Plo \
+ knot/events/handlers/$(DEPDIR)/libknotd_la-validate.Plo \
knot/journal/$(DEPDIR)/libknotd_la-journal_basic.Plo \
knot/journal/$(DEPDIR)/libknotd_la-journal_metadata.Plo \
knot/journal/$(DEPDIR)/libknotd_la-journal_read.Plo \
@@ -1276,8 +1296,12 @@ am__depfiles_remade = contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo \
knot/modules/queryacl/$(DEPDIR)/la-queryacl.Plo \
knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Plo \
knot/modules/rrl/$(DEPDIR)/la-functions.Plo \
+ knot/modules/rrl/$(DEPDIR)/la-kru-avx2.Plo \
+ knot/modules/rrl/$(DEPDIR)/la-kru-generic.Plo \
knot/modules/rrl/$(DEPDIR)/la-rrl.Plo \
knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Plo \
+ knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-avx2.Plo \
+ knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-generic.Plo \
knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Plo \
knot/modules/stats/$(DEPDIR)/la-stats.Plo \
knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Plo \
@@ -1300,6 +1324,7 @@ am__depfiles_remade = contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo \
knot/query/$(DEPDIR)/libknotd_la-query.Plo \
knot/query/$(DEPDIR)/libknotd_la-quic-requestor.Plo \
knot/query/$(DEPDIR)/libknotd_la-requestor.Plo \
+ knot/query/$(DEPDIR)/libknotd_la-tls-requestor.Plo \
knot/server/$(DEPDIR)/libknotd_la-dthreads.Plo \
knot/server/$(DEPDIR)/libknotd_la-handler.Plo \
knot/server/$(DEPDIR)/libknotd_la-proxyv2.Plo \
@@ -1380,6 +1405,8 @@ am__depfiles_remade = contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo \
libknot/probe/$(DEPDIR)/la-probe.Plo \
libknot/quic/$(DEPDIR)/la-quic.Plo \
libknot/quic/$(DEPDIR)/la-quic_conn.Plo \
+ libknot/quic/$(DEPDIR)/la-tls.Plo \
+ libknot/quic/$(DEPDIR)/la-tls_common.Plo \
libknot/rrtype/$(DEPDIR)/la-naptr.Plo \
libknot/rrtype/$(DEPDIR)/la-opt.Plo \
libknot/rrtype/$(DEPDIR)/la-tsig.Plo \
@@ -1438,6 +1465,7 @@ am__depfiles_remade = contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo \
utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Po \
utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Po \
utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Po \
+ utils/kxdpgun/$(DEPDIR)/kxdpgun-stats.Po \
utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po \
utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po \
utils/kzonesign/$(DEPDIR)/kzonesign-main.Po
@@ -1528,7 +1556,8 @@ am__nobase_include_libknot_HEADERS_DIST = libknot/attribute.h \
libknot/db/db_trie.h libknot/packet/compr.h \
libknot/packet/pkt.h libknot/packet/rrset-wire.h \
libknot/packet/wire.h libknot/probe/data.h \
- libknot/probe/probe.h libknot/rdata.h libknot/rdataset.h \
+ libknot/probe/probe.h libknot/quic/tls.h \
+ libknot/quic/tls_common.h libknot/rdata.h libknot/rdataset.h \
libknot/rrset-dump.h libknot/rrset.h libknot/rrtype/dnskey.h \
libknot/rrtype/ds.h libknot/rrtype/naptr.h \
libknot/rrtype/nsec.h libknot/rrtype/nsec3.h \
@@ -1545,7 +1574,7 @@ am__nobase_include_libknot_HEADERS_DIST = libknot/attribute.h \
libknot/quic/quic_conn.h
HEADERS = $(include_libdnssec_HEADERS) $(include_libknotd_HEADERS) \
$(include_libzscanner_HEADERS) \
- $(nobase_include_libknot_HEADERS)
+ $(nobase_include_libknot_HEADERS) $(noinst_HEADERS)
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
am__recursive_targets = \
@@ -1751,6 +1780,8 @@ infodir = @infodir@
install_sh = @install_sh@
libbpf_CFLAGS = @libbpf_CFLAGS@
libbpf_LIBS = @libbpf_LIBS@
+libdbus_CFLAGS = @libdbus_CFLAGS@
+libdbus_LIBS = @libdbus_LIBS@
libdir = @libdir@
libdnssec_SONAME = @libdnssec_SONAME@
libdnssec_SOVERSION = @libdnssec_SOVERSION@
@@ -1762,8 +1793,6 @@ libfstrm_CFLAGS = @libfstrm_CFLAGS@
libfstrm_LIBS = @libfstrm_LIBS@
libidn2_CFLAGS = @libidn2_CFLAGS@
libidn2_LIBS = @libidn2_LIBS@
-libidn_CFLAGS = @libidn_CFLAGS@
-libidn_LIBS = @libidn_LIBS@
libknot_SONAME = @libknot_SONAME@
libknot_SOVERSION = @libknot_SOVERSION@
libknot_VERSION_INFO = @libknot_VERSION_INFO@
@@ -1781,7 +1810,6 @@ libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@
libprotobuf_c_LIBS = @libprotobuf_c_LIBS@
liburcu_CFLAGS = @liburcu_CFLAGS@
liburcu_LIBS = @liburcu_LIBS@
-liburcu_PKGCONFIG = @liburcu_PKGCONFIG@
libxdp_CFLAGS = @libxdp_CFLAGS@
libxdp_LIBS = @libxdp_LIBS@
libzscanner_SONAME = @libzscanner_SONAME@
@@ -1849,7 +1877,7 @@ CLEANFILES = $(am__append_5) libzscanner/scanner.c
BUILT_SOURCES = $(am__append_4) libzscanner/scanner.c
lib_LTLIBRARIES = libdnssec.la libknot.la libzscanner.la
noinst_LTLIBRARIES = libcontrib.la $(am__append_3) $(am__append_6) \
- $(am__append_19) $(am__append_51)
+ $(am__append_19) $(am__append_52)
pkgconfig_DATA = libdnssec.pc libknot.pc libzscanner.pc \
$(am__append_20)
libcontrib_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) \
@@ -1859,6 +1887,7 @@ libcontrib_la_LIBADD = $(pthread_LIBS)
libcontrib_LIBS = libcontrib.la $(am__append_2)
libcontrib_la_SOURCES = \
contrib/asan.h \
+ contrib/atomic.h \
contrib/base32hex.c \
contrib/base32hex.h \
contrib/base64.c \
@@ -2121,25 +2150,26 @@ nobase_include_libknot_HEADERS = libknot/attribute.h libknot/codes.h \
libknot/db/db.h libknot/db/db_lmdb.h libknot/db/db_trie.h \
libknot/packet/compr.h libknot/packet/pkt.h \
libknot/packet/rrset-wire.h libknot/packet/wire.h \
- libknot/probe/data.h libknot/probe/probe.h libknot/rdata.h \
- libknot/rdataset.h libknot/rrset-dump.h libknot/rrset.h \
- libknot/rrtype/dnskey.h libknot/rrtype/ds.h \
- libknot/rrtype/naptr.h libknot/rrtype/nsec.h \
- libknot/rrtype/nsec3.h libknot/rrtype/nsec3param.h \
- libknot/rrtype/opt.h libknot/rrtype/rdname.h \
- libknot/rrtype/rrsig.h libknot/rrtype/soa.h \
- libknot/rrtype/svcb.h libknot/rrtype/tsig.h \
- libknot/rrtype/zonemd.h libknot/tsig-op.h libknot/tsig.h \
- libknot/wire.h libknot/yparser/yparser.h \
- libknot/yparser/ypformat.h libknot/yparser/ypschema.h \
- libknot/yparser/yptrafo.h libknot/version.h \
- libknot/xdp/tcp_iobuf.h libknot/xdp.h $(am__append_11) \
- $(am__append_15)
+ libknot/probe/data.h libknot/probe/probe.h libknot/quic/tls.h \
+ libknot/quic/tls_common.h libknot/rdata.h libknot/rdataset.h \
+ libknot/rrset-dump.h libknot/rrset.h libknot/rrtype/dnskey.h \
+ libknot/rrtype/ds.h libknot/rrtype/naptr.h \
+ libknot/rrtype/nsec.h libknot/rrtype/nsec3.h \
+ libknot/rrtype/nsec3param.h libknot/rrtype/opt.h \
+ libknot/rrtype/rdname.h libknot/rrtype/rrsig.h \
+ libknot/rrtype/soa.h libknot/rrtype/svcb.h \
+ libknot/rrtype/tsig.h libknot/rrtype/zonemd.h \
+ libknot/tsig-op.h libknot/tsig.h libknot/wire.h \
+ libknot/yparser/yparser.h libknot/yparser/ypformat.h \
+ libknot/yparser/ypschema.h libknot/yparser/yptrafo.h \
+ libknot/version.h libknot/xdp/tcp_iobuf.h libknot/xdp.h \
+ $(am__append_11) $(am__append_15)
libknot_la_SOURCES = libknot/codes.c libknot/control/control.c \
libknot/cookies.c libknot/descriptor.c libknot/dname.c \
libknot/error.c libknot/db/db_lmdb.c libknot/db/db_trie.c \
libknot/packet/pkt.c libknot/packet/rrset-wire.c \
- libknot/probe/data.c libknot/probe/probe.c libknot/rdataset.c \
+ libknot/probe/data.c libknot/probe/probe.c libknot/quic/tls.c \
+ libknot/quic/tls_common.c libknot/rdataset.c \
libknot/rrset-dump.c libknot/rrset.c libknot/rrtype/naptr.c \
libknot/rrtype/opt.c libknot/rrtype/tsig.c libknot/tsig-op.c \
libknot/tsig.c libknot/yparser/yparser.c \
@@ -2169,15 +2199,15 @@ nodist_libzscanner_la_SOURCES = \
libknotd_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) \
$(libkqueue_CFLAGS) $(liburcu_CFLAGS) $(lmdb_CFLAGS) \
- $(systemd_CFLAGS) $(gnutls_CFLAGS) $(libngtcp2_CFLAGS) \
+ $(systemd_CFLAGS) $(libdbus_CFLAGS) $(gnutls_CFLAGS) \
-DKNOTD_MOD_STATIC $(am__append_28) $(am__append_32)
libknotd_la_LDFLAGS = $(AM_LDFLAGS) -export-symbols-regex '^knotd_'
libknotd_la_LIBADD = $(dlopen_LIBS) $(libkqueue_LIBS) $(pthread_LIBS) \
- $(libngtcp2_LIBS) $(am__append_17) $(am__append_29) \
- $(am__append_33)
+ $(am__append_17) $(am__append_29) $(am__append_33) \
+ $(am__append_44)
libknotd_LIBS = libknotd.la libknot.la libdnssec.la libzscanner.la \
$(libcontrib_LIBS) $(liburcu_LIBS) $(lmdb_LIBS) \
- $(systemd_LIBS) $(gnutls_LIBS)
+ $(systemd_LIBS) $(libdbus_LIBS) $(gnutls_LIBS)
include_libknotddir = $(includedir)/knot
include_libknotd_HEADERS = \
@@ -2219,15 +2249,15 @@ libknotd_la_SOURCES = knot/catalog/catalog_db.c \
knot/events/handlers/flush.c \
knot/events/handlers/freeze_thaw.c knot/events/handlers/load.c \
knot/events/handlers/notify.c knot/events/handlers/refresh.c \
- knot/events/handlers/update.c knot/events/replan.c \
- knot/events/replan.h knot/nameserver/axfr.c \
- knot/nameserver/axfr.h knot/nameserver/chaos.c \
- knot/nameserver/chaos.h knot/nameserver/internet.c \
- knot/nameserver/internet.h knot/nameserver/ixfr.c \
- knot/nameserver/ixfr.h knot/nameserver/log.h \
- knot/nameserver/notify.c knot/nameserver/notify.h \
- knot/nameserver/nsec_proofs.c knot/nameserver/nsec_proofs.h \
- knot/nameserver/process_query.c \
+ knot/events/handlers/update.c knot/events/handlers/validate.c \
+ knot/events/replan.c knot/events/replan.h \
+ knot/nameserver/axfr.c knot/nameserver/axfr.h \
+ knot/nameserver/chaos.c knot/nameserver/chaos.h \
+ knot/nameserver/internet.c knot/nameserver/internet.h \
+ knot/nameserver/ixfr.c knot/nameserver/ixfr.h \
+ knot/nameserver/log.h knot/nameserver/notify.c \
+ knot/nameserver/notify.h knot/nameserver/nsec_proofs.c \
+ knot/nameserver/nsec_proofs.h knot/nameserver/process_query.c \
knot/nameserver/process_query.h knot/nameserver/query_module.c \
knot/nameserver/query_module.h knot/nameserver/tsig_ctx.c \
knot/nameserver/tsig_ctx.h knot/nameserver/update.c \
@@ -2235,14 +2265,15 @@ libknotd_la_SOURCES = knot/catalog/catalog_db.c \
knot/nameserver/xfr.h knot/query/capture.c \
knot/query/capture.h knot/query/layer.h knot/query/query.c \
knot/query/query.h knot/query/requestor.c \
- knot/query/requestor.h knot/common/evsched.c \
- knot/common/evsched.h knot/common/fdset.c knot/common/fdset.h \
- knot/common/log.c knot/common/log.h knot/common/process.c \
- knot/common/process.h knot/common/stats.c knot/common/stats.h \
- knot/common/systemd.c knot/common/systemd.h \
- knot/common/unreachable.c knot/common/unreachable.h \
- knot/journal/journal_basic.c knot/journal/journal_basic.h \
- knot/journal/journal_metadata.c \
+ knot/query/requestor.h knot/query/tls-requestor.c \
+ knot/query/tls-requestor.h knot/common/dbus.c \
+ knot/common/dbus.h knot/common/evsched.c knot/common/evsched.h \
+ knot/common/fdset.c knot/common/fdset.h knot/common/log.c \
+ knot/common/log.h knot/common/process.c knot/common/process.h \
+ knot/common/stats.c knot/common/stats.h knot/common/systemd.c \
+ knot/common/systemd.h knot/common/unreachable.c \
+ knot/common/unreachable.h knot/journal/journal_basic.c \
+ knot/journal/journal_basic.h knot/journal/journal_metadata.c \
knot/journal/journal_metadata.h knot/journal/journal_read.c \
knot/journal/journal_read.h knot/journal/journal_write.c \
knot/journal/journal_write.h knot/journal/knot_lmdb.c \
@@ -2279,15 +2310,15 @@ libknotd_la_SOURCES = knot/catalog/catalog_db.c \
$(am__append_21) $(am__append_23) $(am__append_25) \
$(am__append_27) $(am__append_31) $(am__append_35) \
$(am__append_37) $(am__append_39) $(am__append_41) \
- $(am__append_43) $(am__append_45) $(am__append_47) \
- $(am__append_49)
+ $(am__append_43) $(am__append_46) $(am__append_48) \
+ $(am__append_50)
KNOTD_MOD_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY)
KNOTD_MOD_LDFLAGS = $(AM_LDFLAGS) -module -shared -avoid-version
pkglib_LTLIBRARIES = $(am__append_22) $(am__append_24) \
$(am__append_26) $(am__append_30) $(am__append_34) \
$(am__append_36) $(am__append_38) $(am__append_40) \
- $(am__append_42) $(am__append_44) $(am__append_46) \
- $(am__append_48) $(am__append_50)
+ $(am__append_42) $(am__append_45) $(am__append_47) \
+ $(am__append_49) $(am__append_51)
knot_modules_authsignal_la_SOURCES = knot/modules/authsignal/authsignal.c
@SHARED_MODULE_authsignal_TRUE@knot_modules_authsignal_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
@SHARED_MODULE_authsignal_TRUE@knot_modules_authsignal_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
@@ -2328,11 +2359,15 @@ knot_modules_queryacl_la_SOURCES = knot/modules/queryacl/queryacl.c
@SHARED_MODULE_queryacl_TRUE@knot_modules_queryacl_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
knot_modules_rrl_la_SOURCES = knot/modules/rrl/rrl.c \
knot/modules/rrl/functions.c \
- knot/modules/rrl/functions.h
+ knot/modules/rrl/functions.h \
+ knot/modules/rrl/kru-generic.c \
+ knot/modules/rrl/kru-avx2.c \
+ knot/modules/rrl/kru.h
+noinst_HEADERS = knot/modules/rrl/kru.inc.c
@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
-@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_LIBADD = $(libcontrib_LIBS)
+@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_LIBADD = $(libcontrib_LIBS) $(math_LIBS)
knot_modules_stats_la_SOURCES = knot/modules/stats/stats.c
@SHARED_MODULE_stats_TRUE@knot_modules_stats_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
@SHARED_MODULE_stats_TRUE@knot_modules_stats_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
@@ -2353,7 +2388,7 @@ knot_modules_whoami_la_SOURCES = knot/modules/whoami/whoami.c
@HAVE_LIBUTILS_TRUE@libknotus_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS)
@HAVE_LIBUTILS_TRUE@libknotus_la_LIBADD = $(libidn2_LIBS) \
@HAVE_LIBUTILS_TRUE@ $(libidn_LIBS) $(libnghttp2_LIBS) \
-@HAVE_LIBUTILS_TRUE@ $(libngtcp2_LIBS) $(am__append_52)
+@HAVE_LIBUTILS_TRUE@ $(libngtcp2_LIBS) $(am__append_53)
@HAVE_LIBUTILS_TRUE@libknotus_LIBS = libknotus.la libknot.la libdnssec.la $(libcontrib_LIBS) \
@HAVE_LIBUTILS_TRUE@ $(gnutls_LIBS) $(libedit_LIBS)
@@ -2416,11 +2451,11 @@ knot_modules_whoami_la_SOURCES = knot/modules/whoami/whoami.c
@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_params.h
@HAVE_UTILS_TRUE@kdig_CPPFLAGS = $(libknotus_la_CPPFLAGS) \
-@HAVE_UTILS_TRUE@ $(am__append_54)
-@HAVE_UTILS_TRUE@kdig_LDADD = $(libknotus_LIBS) $(am__append_55)
+@HAVE_UTILS_TRUE@ $(am__append_55)
+@HAVE_UTILS_TRUE@kdig_LDADD = $(libknotus_LIBS) $(am__append_56)
@HAVE_UTILS_TRUE@khost_CPPFLAGS = $(libknotus_la_CPPFLAGS) \
-@HAVE_UTILS_TRUE@ $(am__append_56)
-@HAVE_UTILS_TRUE@khost_LDADD = $(libknotus_LIBS) $(am__append_57)
+@HAVE_UTILS_TRUE@ $(am__append_57)
+@HAVE_UTILS_TRUE@khost_LDADD = $(libknotus_LIBS) $(am__append_58)
@HAVE_UTILS_TRUE@knsec3hash_CPPFLAGS = $(libknotus_la_CPPFLAGS)
@HAVE_UTILS_TRUE@knsec3hash_LDADD = libknot.la libdnssec.la $(libcontrib_LIBS)
@HAVE_UTILS_TRUE@knsupdate_CPPFLAGS = $(libknotus_la_CPPFLAGS)
@@ -2430,17 +2465,20 @@ knot_modules_whoami_la_SOURCES = knot/modules/whoami/whoami.c
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/ip_route.h \
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/load_queries.c \
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/load_queries.h \
-@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/main.c
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/main.c \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/main.h \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/stats.c \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/stats.h
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@kxdpgun_CPPFLAGS = \
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(libknotus_la_CPPFLAGS) \
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(libmnl_CFLAGS) \
-@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__append_59)
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__append_60)
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@kxdpgun_LDADD = libknot.la \
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(libcontrib_LIBS) \
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(libmnl_LIBS) \
@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(pthread_LIBS) \
-@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__append_60)
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__append_61)
@HAVE_DAEMON_TRUE@knotc_SOURCES = \
@HAVE_DAEMON_TRUE@ utils/knotc/commands.c \
@HAVE_DAEMON_TRUE@ utils/knotc/commands.h \
@@ -2890,6 +2928,10 @@ knot/modules/rrl/la-rrl.lo: knot/modules/rrl/$(am__dirstamp) \
knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
knot/modules/rrl/la-functions.lo: knot/modules/rrl/$(am__dirstamp) \
knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
+knot/modules/rrl/la-kru-generic.lo: knot/modules/rrl/$(am__dirstamp) \
+ knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
+knot/modules/rrl/la-kru-avx2.lo: knot/modules/rrl/$(am__dirstamp) \
+ knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
knot/modules/rrl.la: $(knot_modules_rrl_la_OBJECTS) $(knot_modules_rrl_la_DEPENDENCIES) $(EXTRA_knot_modules_rrl_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
$(AM_V_CCLD)$(knot_modules_rrl_la_LINK) $(am_knot_modules_rrl_la_rpath) $(knot_modules_rrl_la_OBJECTS) $(knot_modules_rrl_la_LIBADD) $(LIBS)
@@ -3352,6 +3394,16 @@ libknot/probe/la-data.lo: libknot/probe/$(am__dirstamp) \
libknot/probe/$(DEPDIR)/$(am__dirstamp)
libknot/probe/la-probe.lo: libknot/probe/$(am__dirstamp) \
libknot/probe/$(DEPDIR)/$(am__dirstamp)
+libknot/quic/$(am__dirstamp):
+ @$(MKDIR_P) libknot/quic
+ @: > libknot/quic/$(am__dirstamp)
+libknot/quic/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libknot/quic/$(DEPDIR)
+ @: > libknot/quic/$(DEPDIR)/$(am__dirstamp)
+libknot/quic/la-tls.lo: libknot/quic/$(am__dirstamp) \
+ libknot/quic/$(DEPDIR)/$(am__dirstamp)
+libknot/quic/la-tls_common.lo: libknot/quic/$(am__dirstamp) \
+ libknot/quic/$(DEPDIR)/$(am__dirstamp)
libknot/la-rdataset.lo: libknot/$(am__dirstamp) \
libknot/$(DEPDIR)/$(am__dirstamp)
libknot/la-rrset-dump.lo: libknot/$(am__dirstamp) \
@@ -3408,12 +3460,6 @@ libknot/xdp/la-tcp.lo: libknot/xdp/$(am__dirstamp) \
libknot/xdp/$(DEPDIR)/$(am__dirstamp)
libknot/xdp/la-xdp.lo: libknot/xdp/$(am__dirstamp) \
libknot/xdp/$(DEPDIR)/$(am__dirstamp)
-libknot/quic/$(am__dirstamp):
- @$(MKDIR_P) libknot/quic
- @: > libknot/quic/$(am__dirstamp)
-libknot/quic/$(DEPDIR)/$(am__dirstamp):
- @$(MKDIR_P) libknot/quic/$(DEPDIR)
- @: > libknot/quic/$(DEPDIR)/$(am__dirstamp)
libknot/quic/la-quic.lo: libknot/quic/$(am__dirstamp) \
libknot/quic/$(DEPDIR)/$(am__dirstamp)
libknot/quic/la-quic_conn.lo: libknot/quic/$(am__dirstamp) \
@@ -3566,6 +3612,9 @@ knot/events/handlers/libknotd_la-refresh.lo: \
knot/events/handlers/libknotd_la-update.lo: \
knot/events/handlers/$(am__dirstamp) \
knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/libknotd_la-validate.lo: \
+ knot/events/handlers/$(am__dirstamp) \
+ knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
knot/events/libknotd_la-replan.lo: knot/events/$(am__dirstamp) \
knot/events/$(DEPDIR)/$(am__dirstamp)
knot/nameserver/$(am__dirstamp):
@@ -3615,12 +3664,16 @@ knot/query/libknotd_la-query.lo: knot/query/$(am__dirstamp) \
knot/query/$(DEPDIR)/$(am__dirstamp)
knot/query/libknotd_la-requestor.lo: knot/query/$(am__dirstamp) \
knot/query/$(DEPDIR)/$(am__dirstamp)
+knot/query/libknotd_la-tls-requestor.lo: knot/query/$(am__dirstamp) \
+ knot/query/$(DEPDIR)/$(am__dirstamp)
knot/common/$(am__dirstamp):
@$(MKDIR_P) knot/common
@: > knot/common/$(am__dirstamp)
knot/common/$(DEPDIR)/$(am__dirstamp):
@$(MKDIR_P) knot/common/$(DEPDIR)
@: > knot/common/$(DEPDIR)/$(am__dirstamp)
+knot/common/libknotd_la-dbus.lo: knot/common/$(am__dirstamp) \
+ knot/common/$(DEPDIR)/$(am__dirstamp)
knot/common/libknotd_la-evsched.lo: knot/common/$(am__dirstamp) \
knot/common/$(DEPDIR)/$(am__dirstamp)
knot/common/libknotd_la-fdset.lo: knot/common/$(am__dirstamp) \
@@ -3792,6 +3845,12 @@ knot/modules/rrl/libknotd_la-rrl.lo: knot/modules/rrl/$(am__dirstamp) \
knot/modules/rrl/libknotd_la-functions.lo: \
knot/modules/rrl/$(am__dirstamp) \
knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
+knot/modules/rrl/libknotd_la-kru-generic.lo: \
+ knot/modules/rrl/$(am__dirstamp) \
+ knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
+knot/modules/rrl/libknotd_la-kru-avx2.lo: \
+ knot/modules/rrl/$(am__dirstamp) \
+ knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
knot/modules/stats/libknotd_la-stats.lo: \
knot/modules/stats/$(am__dirstamp) \
knot/modules/stats/$(DEPDIR)/$(am__dirstamp)
@@ -4017,6 +4076,8 @@ utils/kxdpgun/kxdpgun-load_queries.$(OBJEXT): \
utils/kxdpgun/$(DEPDIR)/$(am__dirstamp)
utils/kxdpgun/kxdpgun-main.$(OBJEXT): utils/kxdpgun/$(am__dirstamp) \
utils/kxdpgun/$(DEPDIR)/$(am__dirstamp)
+utils/kxdpgun/kxdpgun-stats.$(OBJEXT): utils/kxdpgun/$(am__dirstamp) \
+ utils/kxdpgun/$(DEPDIR)/$(am__dirstamp)
kxdpgun$(EXEEXT): $(kxdpgun_OBJECTS) $(kxdpgun_DEPENDENCIES) $(EXTRA_kxdpgun_DEPENDENCIES)
@rm -f kxdpgun$(EXEEXT)
@@ -4259,6 +4320,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@knot/catalog/$(DEPDIR)/libknotd_la-catalog_update.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/catalog/$(DEPDIR)/libknotd_la-generate.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/catalog/$(DEPDIR)/libknotd_la-interpret.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-dbus.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-evsched.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-fdset.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-log.Plo@am__quote@ # am--include-marker
@@ -4306,6 +4368,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-update.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-validate.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/journal/$(DEPDIR)/libknotd_la-journal_basic.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/journal/$(DEPDIR)/libknotd_la-journal_metadata.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/journal/$(DEPDIR)/libknotd_la-journal_read.Plo@am__quote@ # am--include-marker
@@ -4335,8 +4398,12 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/queryacl/$(DEPDIR)/la-queryacl.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/la-functions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/la-kru-avx2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/la-kru-generic.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/la-rrl.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-avx2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-generic.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/stats/$(DEPDIR)/la-stats.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Plo@am__quote@ # am--include-marker
@@ -4359,6 +4426,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@knot/query/$(DEPDIR)/libknotd_la-query.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/query/$(DEPDIR)/libknotd_la-quic-requestor.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/query/$(DEPDIR)/libknotd_la-requestor.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/query/$(DEPDIR)/libknotd_la-tls-requestor.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-dthreads.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-handler.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-proxyv2.Plo@am__quote@ # am--include-marker
@@ -4441,6 +4509,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@libknot/probe/$(DEPDIR)/la-probe.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@libknot/quic/$(DEPDIR)/la-quic.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@libknot/quic/$(DEPDIR)/la-quic_conn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/quic/$(DEPDIR)/la-tls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/quic/$(DEPDIR)/la-tls_common.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@libknot/rrtype/$(DEPDIR)/la-naptr.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@libknot/rrtype/$(DEPDIR)/la-opt.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@libknot/rrtype/$(DEPDIR)/la-tsig.Plo@am__quote@ # am--include-marker
@@ -4499,6 +4569,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kxdpgun/$(DEPDIR)/kxdpgun-stats.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@utils/kzonesign/$(DEPDIR)/kzonesign-main.Po@am__quote@ # am--include-marker
@@ -4624,6 +4695,20 @@ knot/modules/rrl/la-functions.lo: knot/modules/rrl/functions.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/la-functions.lo `test -f 'knot/modules/rrl/functions.c' || echo '$(srcdir)/'`knot/modules/rrl/functions.c
+knot/modules/rrl/la-kru-generic.lo: knot/modules/rrl/kru-generic.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/rrl/la-kru-generic.lo -MD -MP -MF knot/modules/rrl/$(DEPDIR)/la-kru-generic.Tpo -c -o knot/modules/rrl/la-kru-generic.lo `test -f 'knot/modules/rrl/kru-generic.c' || echo '$(srcdir)/'`knot/modules/rrl/kru-generic.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/rrl/$(DEPDIR)/la-kru-generic.Tpo knot/modules/rrl/$(DEPDIR)/la-kru-generic.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/rrl/kru-generic.c' object='knot/modules/rrl/la-kru-generic.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/la-kru-generic.lo `test -f 'knot/modules/rrl/kru-generic.c' || echo '$(srcdir)/'`knot/modules/rrl/kru-generic.c
+
+knot/modules/rrl/la-kru-avx2.lo: knot/modules/rrl/kru-avx2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/rrl/la-kru-avx2.lo -MD -MP -MF knot/modules/rrl/$(DEPDIR)/la-kru-avx2.Tpo -c -o knot/modules/rrl/la-kru-avx2.lo `test -f 'knot/modules/rrl/kru-avx2.c' || echo '$(srcdir)/'`knot/modules/rrl/kru-avx2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/rrl/$(DEPDIR)/la-kru-avx2.Tpo knot/modules/rrl/$(DEPDIR)/la-kru-avx2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/rrl/kru-avx2.c' object='knot/modules/rrl/la-kru-avx2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/la-kru-avx2.lo `test -f 'knot/modules/rrl/kru-avx2.c' || echo '$(srcdir)/'`knot/modules/rrl/kru-avx2.c
+
knot/modules/stats/la-stats.lo: knot/modules/stats/stats.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_stats_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/stats/la-stats.lo -MD -MP -MF knot/modules/stats/$(DEPDIR)/la-stats.Tpo -c -o knot/modules/stats/la-stats.lo `test -f 'knot/modules/stats/stats.c' || echo '$(srcdir)/'`knot/modules/stats/stats.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/stats/$(DEPDIR)/la-stats.Tpo knot/modules/stats/$(DEPDIR)/la-stats.Plo
@@ -5429,6 +5514,20 @@ libknot/probe/la-probe.lo: libknot/probe/probe.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/probe/la-probe.lo `test -f 'libknot/probe/probe.c' || echo '$(srcdir)/'`libknot/probe/probe.c
+libknot/quic/la-tls.lo: libknot/quic/tls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/quic/la-tls.lo -MD -MP -MF libknot/quic/$(DEPDIR)/la-tls.Tpo -c -o libknot/quic/la-tls.lo `test -f 'libknot/quic/tls.c' || echo '$(srcdir)/'`libknot/quic/tls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/quic/$(DEPDIR)/la-tls.Tpo libknot/quic/$(DEPDIR)/la-tls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/quic/tls.c' object='libknot/quic/la-tls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/quic/la-tls.lo `test -f 'libknot/quic/tls.c' || echo '$(srcdir)/'`libknot/quic/tls.c
+
+libknot/quic/la-tls_common.lo: libknot/quic/tls_common.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/quic/la-tls_common.lo -MD -MP -MF libknot/quic/$(DEPDIR)/la-tls_common.Tpo -c -o libknot/quic/la-tls_common.lo `test -f 'libknot/quic/tls_common.c' || echo '$(srcdir)/'`libknot/quic/tls_common.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/quic/$(DEPDIR)/la-tls_common.Tpo libknot/quic/$(DEPDIR)/la-tls_common.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/quic/tls_common.c' object='libknot/quic/la-tls_common.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/quic/la-tls_common.lo `test -f 'libknot/quic/tls_common.c' || echo '$(srcdir)/'`libknot/quic/tls_common.c
+
libknot/la-rdataset.lo: libknot/rdataset.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/la-rdataset.lo -MD -MP -MF libknot/$(DEPDIR)/la-rdataset.Tpo -c -o libknot/la-rdataset.lo `test -f 'libknot/rdataset.c' || echo '$(srcdir)/'`libknot/rdataset.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/la-rdataset.Tpo libknot/$(DEPDIR)/la-rdataset.Plo
@@ -5877,6 +5976,13 @@ knot/events/handlers/libknotd_la-update.lo: knot/events/handlers/update.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-update.lo `test -f 'knot/events/handlers/update.c' || echo '$(srcdir)/'`knot/events/handlers/update.c
+knot/events/handlers/libknotd_la-validate.lo: knot/events/handlers/validate.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-validate.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-validate.Tpo -c -o knot/events/handlers/libknotd_la-validate.lo `test -f 'knot/events/handlers/validate.c' || echo '$(srcdir)/'`knot/events/handlers/validate.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-validate.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-validate.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/validate.c' object='knot/events/handlers/libknotd_la-validate.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-validate.lo `test -f 'knot/events/handlers/validate.c' || echo '$(srcdir)/'`knot/events/handlers/validate.c
+
knot/events/libknotd_la-replan.lo: knot/events/replan.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/libknotd_la-replan.lo -MD -MP -MF knot/events/$(DEPDIR)/libknotd_la-replan.Tpo -c -o knot/events/libknotd_la-replan.lo `test -f 'knot/events/replan.c' || echo '$(srcdir)/'`knot/events/replan.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/$(DEPDIR)/libknotd_la-replan.Tpo knot/events/$(DEPDIR)/libknotd_la-replan.Plo
@@ -5982,6 +6088,20 @@ knot/query/libknotd_la-requestor.lo: knot/query/requestor.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/query/libknotd_la-requestor.lo `test -f 'knot/query/requestor.c' || echo '$(srcdir)/'`knot/query/requestor.c
+knot/query/libknotd_la-tls-requestor.lo: knot/query/tls-requestor.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/query/libknotd_la-tls-requestor.lo -MD -MP -MF knot/query/$(DEPDIR)/libknotd_la-tls-requestor.Tpo -c -o knot/query/libknotd_la-tls-requestor.lo `test -f 'knot/query/tls-requestor.c' || echo '$(srcdir)/'`knot/query/tls-requestor.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/query/$(DEPDIR)/libknotd_la-tls-requestor.Tpo knot/query/$(DEPDIR)/libknotd_la-tls-requestor.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/query/tls-requestor.c' object='knot/query/libknotd_la-tls-requestor.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/query/libknotd_la-tls-requestor.lo `test -f 'knot/query/tls-requestor.c' || echo '$(srcdir)/'`knot/query/tls-requestor.c
+
+knot/common/libknotd_la-dbus.lo: knot/common/dbus.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-dbus.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-dbus.Tpo -c -o knot/common/libknotd_la-dbus.lo `test -f 'knot/common/dbus.c' || echo '$(srcdir)/'`knot/common/dbus.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-dbus.Tpo knot/common/$(DEPDIR)/libknotd_la-dbus.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/dbus.c' object='knot/common/libknotd_la-dbus.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-dbus.lo `test -f 'knot/common/dbus.c' || echo '$(srcdir)/'`knot/common/dbus.c
+
knot/common/libknotd_la-evsched.lo: knot/common/evsched.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-evsched.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-evsched.Tpo -c -o knot/common/libknotd_la-evsched.lo `test -f 'knot/common/evsched.c' || echo '$(srcdir)/'`knot/common/evsched.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-evsched.Tpo knot/common/$(DEPDIR)/libknotd_la-evsched.Plo
@@ -6416,6 +6536,20 @@ knot/modules/rrl/libknotd_la-functions.lo: knot/modules/rrl/functions.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/libknotd_la-functions.lo `test -f 'knot/modules/rrl/functions.c' || echo '$(srcdir)/'`knot/modules/rrl/functions.c
+knot/modules/rrl/libknotd_la-kru-generic.lo: knot/modules/rrl/kru-generic.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/rrl/libknotd_la-kru-generic.lo -MD -MP -MF knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-generic.Tpo -c -o knot/modules/rrl/libknotd_la-kru-generic.lo `test -f 'knot/modules/rrl/kru-generic.c' || echo '$(srcdir)/'`knot/modules/rrl/kru-generic.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-generic.Tpo knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-generic.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/rrl/kru-generic.c' object='knot/modules/rrl/libknotd_la-kru-generic.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/libknotd_la-kru-generic.lo `test -f 'knot/modules/rrl/kru-generic.c' || echo '$(srcdir)/'`knot/modules/rrl/kru-generic.c
+
+knot/modules/rrl/libknotd_la-kru-avx2.lo: knot/modules/rrl/kru-avx2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/rrl/libknotd_la-kru-avx2.lo -MD -MP -MF knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-avx2.Tpo -c -o knot/modules/rrl/libknotd_la-kru-avx2.lo `test -f 'knot/modules/rrl/kru-avx2.c' || echo '$(srcdir)/'`knot/modules/rrl/kru-avx2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-avx2.Tpo knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-avx2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/rrl/kru-avx2.c' object='knot/modules/rrl/libknotd_la-kru-avx2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/libknotd_la-kru-avx2.lo `test -f 'knot/modules/rrl/kru-avx2.c' || echo '$(srcdir)/'`knot/modules/rrl/kru-avx2.c
+
knot/modules/stats/libknotd_la-stats.lo: knot/modules/stats/stats.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/stats/libknotd_la-stats.lo -MD -MP -MF knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Tpo -c -o knot/modules/stats/libknotd_la-stats.lo `test -f 'knot/modules/stats/stats.c' || echo '$(srcdir)/'`knot/modules/stats/stats.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Tpo knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Plo
@@ -6934,6 +7068,20 @@ utils/kxdpgun/kxdpgun-main.obj: utils/kxdpgun/main.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kxdpgun/kxdpgun-main.obj `if test -f 'utils/kxdpgun/main.c'; then $(CYGPATH_W) 'utils/kxdpgun/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kxdpgun/main.c'; fi`
+utils/kxdpgun/kxdpgun-stats.o: utils/kxdpgun/stats.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kxdpgun/kxdpgun-stats.o -MD -MP -MF utils/kxdpgun/$(DEPDIR)/kxdpgun-stats.Tpo -c -o utils/kxdpgun/kxdpgun-stats.o `test -f 'utils/kxdpgun/stats.c' || echo '$(srcdir)/'`utils/kxdpgun/stats.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kxdpgun/$(DEPDIR)/kxdpgun-stats.Tpo utils/kxdpgun/$(DEPDIR)/kxdpgun-stats.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kxdpgun/stats.c' object='utils/kxdpgun/kxdpgun-stats.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kxdpgun/kxdpgun-stats.o `test -f 'utils/kxdpgun/stats.c' || echo '$(srcdir)/'`utils/kxdpgun/stats.c
+
+utils/kxdpgun/kxdpgun-stats.obj: utils/kxdpgun/stats.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kxdpgun/kxdpgun-stats.obj -MD -MP -MF utils/kxdpgun/$(DEPDIR)/kxdpgun-stats.Tpo -c -o utils/kxdpgun/kxdpgun-stats.obj `if test -f 'utils/kxdpgun/stats.c'; then $(CYGPATH_W) 'utils/kxdpgun/stats.c'; else $(CYGPATH_W) '$(srcdir)/utils/kxdpgun/stats.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kxdpgun/$(DEPDIR)/kxdpgun-stats.Tpo utils/kxdpgun/$(DEPDIR)/kxdpgun-stats.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kxdpgun/stats.c' object='utils/kxdpgun/kxdpgun-stats.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kxdpgun/kxdpgun-stats.obj `if test -f 'utils/kxdpgun/stats.c'; then $(CYGPATH_W) 'utils/kxdpgun/stats.c'; else $(CYGPATH_W) '$(srcdir)/utils/kxdpgun/stats.c'; fi`
+
utils/kzonecheck/kzonecheck-main.o: utils/kzonecheck/main.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kzonecheck/kzonecheck-main.o -MD -MP -MF utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Tpo -c -o utils/kzonecheck/kzonecheck-main.o `test -f 'utils/kzonecheck/main.c' || echo '$(srcdir)/'`utils/kzonecheck/main.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Tpo utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po
@@ -7577,6 +7725,7 @@ distclean: distclean-recursive
-rm -f knot/catalog/$(DEPDIR)/libknotd_la-catalog_update.Plo
-rm -f knot/catalog/$(DEPDIR)/libknotd_la-generate.Plo
-rm -f knot/catalog/$(DEPDIR)/libknotd_la-interpret.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-dbus.Plo
-rm -f knot/common/$(DEPDIR)/libknotd_la-evsched.Plo
-rm -f knot/common/$(DEPDIR)/libknotd_la-fdset.Plo
-rm -f knot/common/$(DEPDIR)/libknotd_la-log.Plo
@@ -7624,6 +7773,7 @@ distclean: distclean-recursive
-rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Plo
-rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Plo
-rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-update.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-validate.Plo
-rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_basic.Plo
-rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_metadata.Plo
-rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_read.Plo
@@ -7653,8 +7803,12 @@ distclean: distclean-recursive
-rm -f knot/modules/queryacl/$(DEPDIR)/la-queryacl.Plo
-rm -f knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Plo
-rm -f knot/modules/rrl/$(DEPDIR)/la-functions.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/la-kru-avx2.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/la-kru-generic.Plo
-rm -f knot/modules/rrl/$(DEPDIR)/la-rrl.Plo
-rm -f knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-avx2.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-generic.Plo
-rm -f knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Plo
-rm -f knot/modules/stats/$(DEPDIR)/la-stats.Plo
-rm -f knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Plo
@@ -7677,6 +7831,7 @@ distclean: distclean-recursive
-rm -f knot/query/$(DEPDIR)/libknotd_la-query.Plo
-rm -f knot/query/$(DEPDIR)/libknotd_la-quic-requestor.Plo
-rm -f knot/query/$(DEPDIR)/libknotd_la-requestor.Plo
+ -rm -f knot/query/$(DEPDIR)/libknotd_la-tls-requestor.Plo
-rm -f knot/server/$(DEPDIR)/libknotd_la-dthreads.Plo
-rm -f knot/server/$(DEPDIR)/libknotd_la-handler.Plo
-rm -f knot/server/$(DEPDIR)/libknotd_la-proxyv2.Plo
@@ -7759,6 +7914,8 @@ distclean: distclean-recursive
-rm -f libknot/probe/$(DEPDIR)/la-probe.Plo
-rm -f libknot/quic/$(DEPDIR)/la-quic.Plo
-rm -f libknot/quic/$(DEPDIR)/la-quic_conn.Plo
+ -rm -f libknot/quic/$(DEPDIR)/la-tls.Plo
+ -rm -f libknot/quic/$(DEPDIR)/la-tls_common.Plo
-rm -f libknot/rrtype/$(DEPDIR)/la-naptr.Plo
-rm -f libknot/rrtype/$(DEPDIR)/la-opt.Plo
-rm -f libknot/rrtype/$(DEPDIR)/la-tsig.Plo
@@ -7817,6 +7974,7 @@ distclean: distclean-recursive
-rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Po
-rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Po
-rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Po
+ -rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-stats.Po
-rm -f utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po
-rm -f utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po
-rm -f utils/kzonesign/$(DEPDIR)/kzonesign-main.Po
@@ -7946,6 +8104,7 @@ maintainer-clean: maintainer-clean-recursive
-rm -f knot/catalog/$(DEPDIR)/libknotd_la-catalog_update.Plo
-rm -f knot/catalog/$(DEPDIR)/libknotd_la-generate.Plo
-rm -f knot/catalog/$(DEPDIR)/libknotd_la-interpret.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-dbus.Plo
-rm -f knot/common/$(DEPDIR)/libknotd_la-evsched.Plo
-rm -f knot/common/$(DEPDIR)/libknotd_la-fdset.Plo
-rm -f knot/common/$(DEPDIR)/libknotd_la-log.Plo
@@ -7993,6 +8152,7 @@ maintainer-clean: maintainer-clean-recursive
-rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Plo
-rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Plo
-rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-update.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-validate.Plo
-rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_basic.Plo
-rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_metadata.Plo
-rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_read.Plo
@@ -8022,8 +8182,12 @@ maintainer-clean: maintainer-clean-recursive
-rm -f knot/modules/queryacl/$(DEPDIR)/la-queryacl.Plo
-rm -f knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Plo
-rm -f knot/modules/rrl/$(DEPDIR)/la-functions.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/la-kru-avx2.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/la-kru-generic.Plo
-rm -f knot/modules/rrl/$(DEPDIR)/la-rrl.Plo
-rm -f knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-avx2.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/libknotd_la-kru-generic.Plo
-rm -f knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Plo
-rm -f knot/modules/stats/$(DEPDIR)/la-stats.Plo
-rm -f knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Plo
@@ -8046,6 +8210,7 @@ maintainer-clean: maintainer-clean-recursive
-rm -f knot/query/$(DEPDIR)/libknotd_la-query.Plo
-rm -f knot/query/$(DEPDIR)/libknotd_la-quic-requestor.Plo
-rm -f knot/query/$(DEPDIR)/libknotd_la-requestor.Plo
+ -rm -f knot/query/$(DEPDIR)/libknotd_la-tls-requestor.Plo
-rm -f knot/server/$(DEPDIR)/libknotd_la-dthreads.Plo
-rm -f knot/server/$(DEPDIR)/libknotd_la-handler.Plo
-rm -f knot/server/$(DEPDIR)/libknotd_la-proxyv2.Plo
@@ -8128,6 +8293,8 @@ maintainer-clean: maintainer-clean-recursive
-rm -f libknot/probe/$(DEPDIR)/la-probe.Plo
-rm -f libknot/quic/$(DEPDIR)/la-quic.Plo
-rm -f libknot/quic/$(DEPDIR)/la-quic_conn.Plo
+ -rm -f libknot/quic/$(DEPDIR)/la-tls.Plo
+ -rm -f libknot/quic/$(DEPDIR)/la-tls_common.Plo
-rm -f libknot/rrtype/$(DEPDIR)/la-naptr.Plo
-rm -f libknot/rrtype/$(DEPDIR)/la-opt.Plo
-rm -f libknot/rrtype/$(DEPDIR)/la-tsig.Plo
@@ -8186,6 +8353,7 @@ maintainer-clean: maintainer-clean-recursive
-rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Po
-rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Po
-rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Po
+ -rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-stats.Po
-rm -f utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po
-rm -f utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po
-rm -f utils/kzonesign/$(DEPDIR)/kzonesign-main.Po
diff --git a/src/config.h.in b/src/config.h.in
index 0be849d..3e82c0d 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -3,12 +3,6 @@
/* Define if building universal (internal helper macro) */
#undef AC_APPLE_UNIVERSAL_BUILD
-/* Passed CFLAGS from environment */
-#undef CONFIGURE_CFLAGS
-
-/* Params passed to configure */
-#undef CONFIGURE_PARAMS
-
/* Configure summary */
#undef CONFIGURE_SUMMARY
@@ -18,8 +12,11 @@
/* POSIX capabilities available */
#undef ENABLE_CAP_NG
+/* libdbus D-Bus available */
+#undef ENABLE_DBUS_LIBDBUS
+
/* systemd D-Bus available */
-#undef ENABLE_DBUS
+#undef ENABLE_DBUS_SYSTEMD
/* PKCS #11 support available */
#undef ENABLE_PKCS11
@@ -48,12 +45,12 @@
/* Define to 1 if you have the <arpa/nameser.h> header file. */
#undef HAVE_ARPA_NAMESER_H
-/* Define to 1 if you have '__atomic' functions. */
-#undef HAVE_ATOMIC
-
/* Define to 1 if you have the <bsd/string.h> header file. */
#undef HAVE_BSD_STRING_H
+/* Define to 1 if you have C11 'atomic' functions. */
+#undef HAVE_C11_ATOMIC
+
/* Define if FreeBSD-like cpuset_t exists. */
#undef HAVE_CPUSET_BSD
@@ -66,9 +63,6 @@
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
-/* GnuTLS ED25519 support available */
-#undef HAVE_ED25519
-
/* GnuTLS ED448 support available */
#undef HAVE_ED448
@@ -84,24 +78,18 @@
/* explicit_memset available */
#undef HAVE_EXPLICIT_MEMSET
-/* gnutls_privkey_export_x509 available */
-#undef HAVE_EXPORT_X509
-
/* Define to 1 if you have the `fgetln' function. */
#undef HAVE_FGETLN
+/* Define to 1 if you have GCC-style '__atomic' functions. */
+#undef HAVE_GCC_ATOMIC
+
/* Define to 1 if you have the `getline' function. */
#undef HAVE_GETLINE
-/* gnutls_memset available */
-#undef HAVE_GNUTLS_MEMSET
-
/* gnutls_early_cipher_get available */
#undef HAVE_GNUTLS_QUIC
-/* GnuTLS reproducible signing available */
-#undef HAVE_GNUTLS_REPRODUCIBLE
-
/* Define to 1 if you have the `initgroups' function. */
#undef HAVE_INITGROUPS
@@ -138,12 +126,6 @@
/* Define to 1 if you have the `setgroups' function. */
#undef HAVE_SETGROUPS
-/* gnutls_privkey_sign_data2 available */
-#undef HAVE_SIGN_DATA2
-
-/* Define to 1 if you have the <stdatomic.h> header file. */
-#undef HAVE_STDATOMIC_H
-
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
@@ -165,9 +147,6 @@
/* Define to 1 if you have the `strlcpy' function. */
#undef HAVE_STRLCPY
-/* Define to 1 if you have '__sync' functions. */
-#undef HAVE_SYNC_ATOMIC
-
/* Define to 1 if you have the `sysctlbyname' function. */
#undef HAVE_SYSCTLBYNAME
@@ -193,9 +172,6 @@
/* Define to 1 to enable IDN support */
#undef LIBIDN
-/* Define to proper libidn header */
-#undef LIBIDN_HEADER
-
/* Define to 1 to enable DoH support */
#undef LIBNGHTTP2
diff --git a/src/contrib/Makefile.inc b/src/contrib/Makefile.inc
index e1577d5..d64c2eb 100644
--- a/src/contrib/Makefile.inc
+++ b/src/contrib/Makefile.inc
@@ -24,6 +24,7 @@ EXTRA_DIST += \
libcontrib_la_SOURCES = \
contrib/asan.h \
+ contrib/atomic.h \
contrib/base32hex.c \
contrib/base32hex.h \
contrib/base64.c \
diff --git a/src/contrib/asan.h b/src/contrib/asan.h
index 5feb2c1..ef6fe66 100644
--- a/src/contrib/asan.h
+++ b/src/contrib/asan.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,13 +25,20 @@
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
void __asan_poison_memory_region(void const volatile *addr, size_t size);
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
+#if defined(__GNUC__) && !defined(__clang__) /* A faulty GCC workaround. */
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
#define ASAN_POISON_MEMORY_REGION(addr, size) \
__asan_poison_memory_region((addr), (size))
+#if defined(__GNUC__) && !defined(__clang__) /* End of the workaround. */
+ #pragma GCC diagnostic pop
+#endif
#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
__asan_unpoison_memory_region((addr), (size))
-#else
+#else /* __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) */
#define ASAN_POISON_MEMORY_REGION(addr, size) \
((void)(addr), (void)(size))
#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
((void)(addr), (void)(size))
-#endif
+#endif /* __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) */
diff --git a/src/contrib/atomic.h b/src/contrib/atomic.h
new file mode 100644
index 0000000..b8dace1
--- /dev/null
+++ b/src/contrib/atomic.h
@@ -0,0 +1,73 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \brief C11 atomic operations with fallbacks.
+ */
+
+#pragma once
+
+#ifdef HAVE_C11_ATOMIC /* C11 */
+ #define KNOT_HAVE_ATOMIC
+
+ #include <stdatomic.h>
+
+ #define ATOMIC_SET(dst, val) atomic_store_explicit(&(dst), (val), memory_order_relaxed)
+ #define ATOMIC_GET(src) atomic_load_explicit(&(src), memory_order_relaxed)
+ #define ATOMIC_ADD(dst, val) (void)atomic_fetch_add_explicit(&(dst), (val), memory_order_relaxed)
+ #define ATOMIC_SUB(dst, val) (void)atomic_fetch_sub_explicit(&(dst), (val), memory_order_relaxed)
+ #define ATOMIC_XCHG(dst, val) atomic_exchange_explicit(&(dst), (val), memory_order_relaxed)
+
+ typedef atomic_uint_fast16_t knot_atomic_uint16_t;
+ typedef atomic_uint_fast64_t knot_atomic_uint64_t;
+ typedef atomic_size_t knot_atomic_size_t;
+ typedef _Atomic (void *) knot_atomic_ptr_t;
+ typedef atomic_bool knot_atomic_bool;
+#elif defined(HAVE_GCC_ATOMIC) /* GCC __atomic */
+ #define KNOT_HAVE_ATOMIC
+
+ #include <stdint.h>
+ #include <stdbool.h>
+
+ #define ATOMIC_SET(dst, val) __atomic_store_n(&(dst), (val), __ATOMIC_RELAXED)
+ #define ATOMIC_GET(src) __atomic_load_n(&(src), __ATOMIC_RELAXED)
+ #define ATOMIC_ADD(dst, val) __atomic_add_fetch(&(dst), (val), __ATOMIC_RELAXED)
+ #define ATOMIC_SUB(dst, val) __atomic_sub_fetch(&(dst), (val), __ATOMIC_RELAXED)
+ #define ATOMIC_XCHG(dst, val) __atomic_exchange_n(&(dst), (val), __ATOMIC_RELAXED)
+
+ typedef uint16_t knot_atomic_uint16_t;
+ typedef uint64_t knot_atomic_uint64_t;
+ typedef size_t knot_atomic_size_t;
+ typedef void* knot_atomic_ptr_t;
+ typedef bool knot_atomic_bool;
+#else /* Fallback, non-atomic. */
+ #warning "Atomic operations not availabe, using unreliable replacement."
+
+ #include <stdint.h>
+ #include <stdbool.h>
+
+ #define ATOMIC_SET(dst, val) ((dst) = (val))
+ #define ATOMIC_GET(src) (src)
+ #define ATOMIC_ADD(dst, val) ((dst) += (val))
+ #define ATOMIC_SUB(dst, val) ((dst) -= (val))
+ #define ATOMIC_XCHG(dst, val) ({ __typeof__ (dst) _z = (dst); (dst) = (val); _z; })
+
+ typedef uint16_t knot_atomic_uint16_t;
+ typedef uint64_t knot_atomic_uint64_t;
+ typedef size_t knot_atomic_size_t;
+ typedef void* knot_atomic_ptr_t;
+ typedef bool knot_atomic_bool;
+#endif
diff --git a/src/contrib/files.c b/src/contrib/files.c
index 5b38469..f3a4b78 100644
--- a/src/contrib/files.c
+++ b/src/contrib/files.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -92,16 +92,29 @@ static int remove_file(const char *path, const struct stat *stat, int type, stru
{
(void)stat;
(void)ftw;
- if (type == FTW_DP) {
+
+ switch (type) {
+ case FTW_D:
+ case FTW_DNR:
+ case FTW_DP:
return rmdir(path);
- } else {
+ default:
return unlink(path);
}
}
-bool remove_path(const char *path)
+static int remove_in_dir(const char *path, const struct stat *stat, int type, struct FTW *ftw)
+{
+ return (ftw->level > 0) ? remove_file(path, stat, type, ftw) : 0;
+}
+
+int remove_path(const char *path, bool keep_apex)
{
- return (0 == nftw(path, remove_file, 1, FTW_DEPTH | FTW_PHYS));
+ if (0 != nftw(path, keep_apex ? remove_in_dir : remove_file,
+ 1, FTW_DEPTH | FTW_PHYS)) {
+ return knot_map_errno();
+ }
+ return KNOT_EOK;
}
int make_dir(const char *path, mode_t mode, bool ignore_existing)
diff --git a/src/contrib/files.h b/src/contrib/files.h
index c505ac8..4250a47 100644
--- a/src/contrib/files.h
+++ b/src/contrib/files.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -52,9 +52,12 @@ bool same_path(const char *path1, const char *path2);
/*!
* \brief Delete file or directory (recursive).
*
- * \return true on success, false when one or more files failed to be removed.
+ * \param[in] path Absolute path or a relative path suffix; a string.
+ * \param[in] keep_apex If true, don't remove the starting point (apex).
+ *
+ * \return KNOT_E*
*/
-bool remove_path(const char *path);
+int remove_path(const char *path, bool keep_apex);
/*!
* Equivalent to mkdir(2), can succeed if the directory already exists.
diff --git a/src/contrib/json.c b/src/contrib/json.c
index d44da87..5173f90 100644
--- a/src/contrib/json.c
+++ b/src/contrib/json.c
@@ -217,6 +217,13 @@ void jsonw_int(jsonw_t *w, const char *key, int value)
fprintf(w->out, "%d", value);
}
+void jsonw_double(jsonw_t *w, const char *key, double value)
+{
+ assert(w);
+
+ align_key(w, key);
+ fprintf(w->out, "%.4f", value);
+}
void jsonw_bool(jsonw_t *w, const char *key, bool value)
{
diff --git a/src/contrib/json.h b/src/contrib/json.h
index cf8abe6..17513bc 100644
--- a/src/contrib/json.h
+++ b/src/contrib/json.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -82,6 +82,11 @@ void jsonw_ulong(jsonw_t *w, const char *key, unsigned long value);
void jsonw_int(jsonw_t *w, const char *key, int value);
/*!
+ * Write double as JSON.
+ */
+void jsonw_double(jsonw_t *w, const char *key, double value);
+
+/*!
* Write boolean value as JSON.
*/
void jsonw_bool(jsonw_t *w, const char *key, bool value);
diff --git a/src/contrib/mempattern.c b/src/contrib/mempattern.c
index f57139d..f86f0ac 100644
--- a/src/contrib/mempattern.c
+++ b/src/contrib/mempattern.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -114,9 +114,15 @@ void mm_ctx_init(knot_mm_t *mm)
mm->free = free;
}
+// UBSAN type punning workaround
+static void *mp_alloc_wrap(void *ctx, size_t size)
+{
+ return mp_alloc(ctx, size);
+}
+
void mm_ctx_mempool(knot_mm_t *mm, size_t chunk_size)
{
mm->ctx = mp_new(chunk_size);
- mm->alloc = (knot_mm_alloc_t)mp_alloc;
+ mm->alloc = mp_alloc_wrap;
mm->free = mm_nofree;
}
diff --git a/src/contrib/spinlock.h b/src/contrib/spinlock.h
index 837f500..a7ec5ec 100644
--- a/src/contrib/spinlock.h
+++ b/src/contrib/spinlock.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -15,48 +15,23 @@
*/
/*!
- * \brief Multiplatform spinlock.
+ * \brief A C11 spinlock (POSIX pthread spinlock as a fallback).
*/
#pragma once
#if (__STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_ATOMICS__)
-/* Not tested and activated yet. */
-/* #define HAVE_STDATOMIC */
-#endif
-
-#if defined(__APPLE__)
-# if defined(MAC_OS_X_VERSION_10_12) || \
- MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
-# define APPLE_SPIN_NEW
-# else
-# define APPLE_SPIN_OLD
-# endif /* MAC_OS_X_VERSION_10_12 ... */
-#endif /* __APPLE__ */
-
-#if defined(HAVE_SYNC_ATOMIC) || defined(HAVE_ATOMIC)
-# include <stdbool.h>
-#elif defined(HAVE_STDATOMIC)
-# include <stdbool.h>
-# include <stdatomic.h>
-#elif defined(APPLE_SPIN_NEW)
-# include <os/lock.h>
-#elif defined(APPLE_SPIN_OLD)
-# include <libkern/OSAtomic.h>
+ #define HAVE_STDATOMIC
+ #include <stdatomic.h>
+ #include <stdbool.h>
#else /* POSIX pthread spinlock. */
-# include <pthread.h>
+ #include <pthread.h>
#endif
/*! \brief Spinlock lock variable type. */
typedef
-#if defined(HAVE_SYNC_ATOMIC) || defined(HAVE_ATOMIC)
- bool /*!< Spinlock lock - a simple & fast atomic version. */
-#elif defined(HAVE_STDATOMIC)
+#if defined(HAVE_STDATOMIC)
atomic_bool /*!< Spinlock lock - a simple & fast atomic version, C11 */
-#elif defined(APPLE_SPIN_NEW)
- os_unfair_lock /*!< Spinlock lock - a newer macOS version (macOS >= 10.12). */
-#elif defined(APPLE_SPIN_OLD)
- OSSpinLock /*!< Spinlock lock - an older macOS version (macOS < 10.12). */
#else /* POSIX */
pthread_spinlock_t /*!< Spinlock lock - a POSIX pthread version. */
#endif
@@ -65,14 +40,8 @@ typedef
/*! \brief Initialize the spinlock pointed to by the parameter "lock". */
static inline void knot_spin_init(knot_spin_t *lock)
{
-#if defined(HAVE_SYNC_ATOMIC) || defined(HAVE_ATOMIC)
- *lock = false;
-#elif defined(HAVE_STDATOMIC)
+#if defined(HAVE_STDATOMIC)
atomic_init(lock, false);
-#elif defined(APPLE_SPIN_NEW)
- *lock = OS_UNFAIR_LOCK_INIT;
-#elif defined(APPLE_SPIN_OLD)
- *lock = OS_SPINLOCK_INIT;
#else /* POSIX */
pthread_spin_init(lock, PTHREAD_PROCESS_PRIVATE);
#endif
@@ -81,8 +50,7 @@ static inline void knot_spin_init(knot_spin_t *lock)
/*! \brief Destroy the spinlock pointed to by the parameter "lock". */
static inline void knot_spin_destroy(knot_spin_t *lock)
{
-#if defined(HAVE_SYNC_ATOMIC) || defined(HAVE_ATOMIC) || defined(HAVE_STDATOMIC) || \
- defined(APPLE_SPIN_NEW) || defined(APPLE_SPIN_OLD)
+#if defined(HAVE_STDATOMIC)
/* Nothing needed here. */
#else /* POSIX */
pthread_spin_destroy(lock);
@@ -92,23 +60,11 @@ static inline void knot_spin_destroy(knot_spin_t *lock)
/*! \brief Lock the spinlock pointed to by the parameter "lock". */
static inline void knot_spin_lock(knot_spin_t *lock)
{
-#if defined(HAVE_SYNC_ATOMIC)
- while (__sync_lock_test_and_set(lock, 1)) {
- }
-#elif defined(HAVE_ATOMIC)
- int expected = 0;
- while (!__atomic_compare_exchange_n(lock, &expected, 1, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
- expected = 0;
- }
-#elif defined(HAVE_STDATOMIC)
- int expected = 0;
- while (!atomic_compare_exchange_strong(lock, &expected, false)) {
- expected = 0;
+#if defined(HAVE_STDATOMIC)
+ bool expected = false;
+ while (!atomic_compare_exchange_strong(lock, &expected, true)) {
+ expected = false;
}
-#elif defined(APPLE_SPIN_NEW)
- os_unfair_lock_lock(lock);
-#elif defined(APPLE_SPIN_OLD)
- OSSpinLockLock(lock);
#else /* POSIX */
pthread_spin_lock(lock);
#endif
@@ -117,16 +73,8 @@ static inline void knot_spin_lock(knot_spin_t *lock)
/*! \brief Unlock the spinlock pointed to by the parameter "lock". */
static inline void knot_spin_unlock(knot_spin_t *lock)
{
-#if defined(HAVE_SYNC_ATOMIC)
- __sync_lock_release(lock);
-#elif defined(HAVE_ATOMIC)
- __atomic_clear(lock, __ATOMIC_RELAXED);
-#elif defined(HAVE_STDATOMIC)
+#if defined(HAVE_STDATOMIC)
atomic_store(lock, false);
-#elif defined(APPLE_SPIN_NEW)
- os_unfair_lock_unlock(lock);
-#elif defined(APPLE_SPIN_OLD)
- OSSpinLockUnlock(lock);
#else /* POSIX */
pthread_spin_unlock(lock);
#endif
diff --git a/src/contrib/string.c b/src/contrib/string.c
index 272116e..6fa2d0a 100644
--- a/src/contrib/string.c
+++ b/src/contrib/string.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,16 +26,16 @@
/* #include <string.h> is needed. */
#elif defined(HAVE_EXPLICIT_MEMSET)
/* #include <string.h> is needed. */
-#elif defined(HAVE_GNUTLS_MEMSET)
- #include <gnutls/gnutls.h>
#else
- #define USE_CUSTOM_MEMSET
+ #include <gnutls/gnutls.h>
#endif
#include "contrib/string.h"
#include "contrib/ctype.h"
#include "contrib/tolower.h"
+const char *configure_summary = CONFIGURE_SUMMARY;
+
uint8_t *memdup(const uint8_t *data, size_t data_size)
{
uint8_t *result = (uint8_t *)malloc(data_size);
@@ -137,11 +137,6 @@ int const_time_memcmp(const void *s1, const void *s2, size_t n)
return equal;
}
-#if defined(USE_CUSTOM_MEMSET)
-typedef void *(*memset_t)(void *, int, size_t);
-static volatile memset_t volatile_memset = memset;
-#endif
-
void *memzero(void *s, size_t n)
{
#if defined(HAVE_EXPLICIT_BZERO) /* In OpenBSD since 5.5. */
@@ -161,14 +156,9 @@ void *memzero(void *s, size_t n)
return s;
#elif defined(HAVE_EXPLICIT_MEMSET) /* In NetBSD since 7.0. */
return explicit_memset(s, 0, n);
-#elif defined(HAVE_GNUTLS_MEMSET) /* In GnuTLS since 3.4.0. */
+#else
gnutls_memset(s, 0, n);
return s;
-#else /* Knot custom solution as a fallback. */
- /* Warning: the use of the return value is *probably* needed
- * so as to avoid the volatile_memset() to be optimized out.
- */
- return volatile_memset(s, 0, n);
#endif
}
diff --git a/src/contrib/string.h b/src/contrib/string.h
index ad3c990..3e113b1 100644
--- a/src/contrib/string.h
+++ b/src/contrib/string.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,6 +24,8 @@
#include <stddef.h>
#include <stdint.h>
+extern const char *configure_summary;
+
/*!
* \brief Create a copy of a binary buffer.
*
diff --git a/src/contrib/time.h b/src/contrib/time.h
index 20d241e..b12b366 100644
--- a/src/contrib/time.h
+++ b/src/contrib/time.h
@@ -88,6 +88,11 @@ inline static int knot_time_cmp(knot_time_t a, knot_time_t b)
{
return (a == b ? 0 : 1) * ((a && b) == 0 ? -1 : 1) * (a < b ? -1 : 1);
}
+inline static bool knot_time_lt (knot_time_t a, knot_time_t b) { return knot_time_cmp(a, b) < 0; }
+inline static bool knot_time_leq(knot_time_t a, knot_time_t b) { return knot_time_cmp(a, b) <= 0; }
+inline static bool knot_time_eq (knot_time_t a, knot_time_t b) { return knot_time_cmp(a, b) == 0; }
+inline static bool knot_time_geq(knot_time_t a, knot_time_t b) { return knot_time_cmp(a, b) >= 0; }
+inline static bool knot_time_gt (knot_time_t a, knot_time_t b) { return knot_time_cmp(a, b) > 0; }
/*!
* \brief Return the smaller (=earlier) from given two timestamps.
diff --git a/src/knot/Makefile.inc b/src/knot/Makefile.inc
index f67fe7f..0cbc9f3 100644
--- a/src/knot/Makefile.inc
+++ b/src/knot/Makefile.inc
@@ -1,12 +1,11 @@
libknotd_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(libkqueue_CFLAGS) \
$(liburcu_CFLAGS) $(lmdb_CFLAGS) $(systemd_CFLAGS) \
- $(gnutls_CFLAGS) $(libngtcp2_CFLAGS) -DKNOTD_MOD_STATIC
+ $(libdbus_CFLAGS) $(gnutls_CFLAGS) -DKNOTD_MOD_STATIC
libknotd_la_LDFLAGS = $(AM_LDFLAGS) -export-symbols-regex '^knotd_'
-libknotd_la_LIBADD = $(dlopen_LIBS) $(libkqueue_LIBS) $(pthread_LIBS) \
- $(libngtcp2_LIBS)
+libknotd_la_LIBADD = $(dlopen_LIBS) $(libkqueue_LIBS) $(pthread_LIBS)
libknotd_LIBS = libknotd.la libknot.la libdnssec.la libzscanner.la \
$(libcontrib_LIBS) $(liburcu_LIBS) $(lmdb_LIBS) \
- $(systemd_LIBS) $(gnutls_LIBS)
+ $(systemd_LIBS) $(libdbus_LIBS) $(gnutls_LIBS)
if EMBEDDED_LIBNGTCP2
libknotd_la_LIBADD += $(libembngtcp2_LIBS)
@@ -93,6 +92,7 @@ libknotd_la_SOURCES = \
knot/events/handlers/notify.c \
knot/events/handlers/refresh.c \
knot/events/handlers/update.c \
+ knot/events/handlers/validate.c \
knot/events/replan.c \
knot/events/replan.h \
knot/nameserver/axfr.c \
@@ -125,6 +125,10 @@ libknotd_la_SOURCES = \
knot/query/query.h \
knot/query/requestor.c \
knot/query/requestor.h \
+ knot/query/tls-requestor.c \
+ knot/query/tls-requestor.h \
+ knot/common/dbus.c \
+ knot/common/dbus.h \
knot/common/evsched.c \
knot/common/evsched.h \
knot/common/fdset.c \
diff --git a/src/knot/catalog/catalog_db.c b/src/knot/catalog/catalog_db.c
index b483f4d..2ea776f 100644
--- a/src/knot/catalog/catalog_db.c
+++ b/src/knot/catalog/catalog_db.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -44,7 +44,7 @@ int catalog_bailiwick_shift(const knot_dname_t *subname, const knot_dname_t *nam
if (*res == '\0') {
return -1;
}
- res = knot_wire_next_label(res, NULL);
+ res = knot_dname_next_label(res);
}
return res - subname;
}
diff --git a/src/knot/catalog/catalog_update.c b/src/knot/catalog/catalog_update.c
index edfd8c5..71b7dbe 100644
--- a/src/knot/catalog/catalog_update.c
+++ b/src/knot/catalog/catalog_update.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -132,7 +132,7 @@ static const knot_dname_t *get_uniq(const knot_dname_t *ptr_owner,
int labels = knot_dname_labels(ptr_owner, NULL);
labels -= knot_dname_labels(catz, NULL);
assert(labels >= 2);
- return ptr_owner + knot_dname_prefixlen(ptr_owner, labels - 2, NULL);
+ return ptr_owner + knot_dname_prefixlen(ptr_owner, labels - 2);
}
static bool same_uniq(const knot_dname_t *owner1, const knot_dname_t *catz1,
diff --git a/src/knot/catalog/interpret.c b/src/knot/catalog/interpret.c
index 7337105..3e8e5db 100644
--- a/src/knot/catalog/interpret.c
+++ b/src/knot/catalog/interpret.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -105,7 +105,7 @@ static const knot_dname_t *property_get_member(const zone_node_t *prop_node,
knot_rdataset_t *ptr = node_rdataset(prop_node->parent, KNOT_RRTYPE_PTR);
if (ptr == NULL) {
// fallback: search in provided complete zone contents
- const knot_dname_t *memb_name = knot_wire_next_label(prop_node->owner, NULL);
+ const knot_dname_t *memb_name = knot_dname_next_label(prop_node->owner);
const zone_node_t *memb_node = zone_contents_find_node(complete_conts, memb_name);
ptr = node_rdataset(memb_node, KNOT_RRTYPE_PTR);
if (memb_node != NULL) {
diff --git a/src/knot/common/dbus.c b/src/knot/common/dbus.c
new file mode 100644
index 0000000..38ae5fa
--- /dev/null
+++ b/src/knot/common/dbus.c
@@ -0,0 +1,243 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+
+#include "knot/common/dbus.h"
+#include "knot/common/log.h"
+
+#define ENABLE_DBUS (ENABLE_DBUS_SYSTEMD | ENABLE_DBUS_LIBDBUS)
+
+#if defined(ENABLE_DBUS_SYSTEMD)
+
+#include <systemd/sd-bus.h>
+#define VALUE_OF(x) (x)
+typedef sd_bus * dbus_ctx_t;
+
+#elif defined(ENABLE_DBUS_LIBDBUS)
+
+#include <assert.h>
+#include <dbus/dbus.h>
+#define VALUE_OF(x) (&(x))
+typedef DBusConnection * dbus_ctx_t;
+
+#else
+
+typedef struct {} * dbus_ctx_t; // Dummy
+
+#endif // ENABLE_DBUS_LIBDBUS
+
+static dbus_ctx_t _dbus = NULL;
+
+int dbus_open(void)
+{
+ if (_dbus != NULL) {
+ return KNOT_EOK;
+ }
+#if defined(ENABLE_DBUS_SYSTEMD)
+ int ret = sd_bus_open_system(&_dbus);
+ if (ret < 0) {
+ goto error_systemd;
+ }
+
+ /* Take a well-known service name so that clients can find us. */
+ ret = sd_bus_request_name(_dbus, KNOT_DBUS_NAME, 0);
+ if (ret < 0) {
+ goto error_systemd;
+ }
+
+ log_info("d-bus: connected to system bus");
+ return KNOT_EOK;
+error_systemd:
+ log_error("d-bus: failed to open system bus (%s)", knot_strerror(ret));
+ dbus_close();
+ return ret;
+#elif defined(ENABLE_DBUS_LIBDBUS)
+ DBusError err;
+ dbus_error_init(&err);
+
+ _dbus = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+ if (dbus_error_is_set(&err) == TRUE) {
+ goto error_libdbus;
+ }
+
+ /* Take a well-known service name so that clients can find us. */
+ dbus_bus_request_name(_dbus, KNOT_DBUS_NAME, 0, &err);
+ if (dbus_error_is_set(&err) == TRUE) {
+ goto error_libdbus;
+ }
+
+ dbus_error_free(&err);
+ log_info("d-bus: connected to system bus");
+ return KNOT_EOK;
+error_libdbus:
+ log_error("d-bus: failed to open system bus (%s)", err.message);
+ dbus_error_free(&err);
+ dbus_close();
+ return KNOT_ERROR;
+#endif
+ log_error("d-bus: not supported");
+ return KNOT_ENOTSUP;
+}
+
+void dbus_close(void)
+{
+ if (_dbus == NULL) {
+ return;
+ }
+#if defined(ENABLE_DBUS_SYSTEMD)
+ _dbus = sd_bus_unref(_dbus);
+#elif defined(ENABLE_DBUS_LIBDBUS)
+ dbus_connection_unref(_dbus);
+ _dbus = NULL;
+#endif // ENABLE_DBUS_LIBDBUS
+}
+
+#if ENABLE_DBUS
+static void emit_event(const char *event, char *first_arg_type, ...)
+{
+ int ret = KNOT_ENOENT;
+ if (_dbus == NULL) {
+ goto failed;
+ }
+
+#if defined(ENABLE_DBUS_SYSTEMD)
+ sd_bus_message *msg = NULL;
+ ret = sd_bus_message_new_signal(_dbus, &msg, KNOT_DBUS_PATH,
+ KNOT_DBUS_NAME".events", event);
+ if (ret < 0) {
+ goto failed;
+ }
+
+ va_list args;
+ va_start(args, first_arg_type);
+ ret = sd_bus_message_appendv(msg, first_arg_type, args);
+ if (ret < 0) {
+ sd_bus_message_unref(msg);
+ va_end(args);
+ goto failed;
+ }
+ /*
+ * \note sd_bus_message_send(msg) or even sd_bus_emit_signalv() can
+ * be used with a newer systemd.
+ */
+ ret = sd_bus_send(sd_bus_message_get_bus(msg), msg, NULL);
+ if (ret < 0) {
+ sd_bus_message_unref(msg);
+ va_end(args);
+ goto failed;
+ }
+ va_end(args);
+#elif defined(ENABLE_DBUS_LIBDBUS)
+ DBusMessage *msg = NULL;
+ msg = dbus_message_new_signal(KNOT_DBUS_PATH, KNOT_DBUS_NAME".events",
+ event);
+ if (msg == NULL) {
+ ret = KNOT_ENOMEM;
+ goto failed;
+ }
+
+ /*
+ * \note This loop considers only basic data types; composite ones,
+ * such as arrays, result in undefined behavior.
+ */
+ va_list args;
+ va_start(args, first_arg_type);
+ for (const char *type = first_arg_type; *type; ++type) {
+ dbus_bool_t bret = dbus_message_append_args(msg, *type,
+ va_arg(args, void *),
+ DBUS_TYPE_INVALID);
+ if (bret == FALSE) {
+ dbus_message_unref(msg);
+ va_end(args);
+ assert(0); // Read note
+ ret = KNOT_EINVAL;
+ goto failed;
+ }
+ }
+
+ if (dbus_connection_send(_dbus, msg, NULL) == 0) {
+ dbus_message_unref(msg);
+ va_end(args);
+ ret = KNOT_NET_ESEND;
+ goto failed;
+ }
+ dbus_message_unref(msg);
+ va_end(args);
+#endif // ENABLE_DBUS_LIBDBUS
+ return;
+failed:
+ log_error("d-bus: failed to emit signal '%s' (%s)", event, knot_strerror(ret));
+}
+#endif // ENABLE_DBUS
+
+void dbus_emit_running(bool up)
+{
+#if ENABLE_DBUS
+ emit_event(up ? KNOT_BUS_EVENT_STARTED : KNOT_BUS_EVENT_STOPPED, "");
+#endif // ENABLE_DBUS
+}
+
+void dbus_emit_zone_updated(const knot_dname_t *zone_name, uint32_t serial)
+{
+#if ENABLE_DBUS
+ knot_dname_txt_storage_t buff;
+ char *zone_str = knot_dname_to_str(buff, zone_name, sizeof(buff));
+ if (zone_str != NULL) {
+ emit_event(KNOT_BUS_EVENT_ZONE_UPD, "su", VALUE_OF(zone_str),
+ VALUE_OF(serial));
+ }
+#endif // ENABLE_DBUS
+}
+
+void dbus_emit_keys_updated(const knot_dname_t *zone_name)
+{
+#if ENABLE_DBUS
+ knot_dname_txt_storage_t buff;
+ char *zone_str = knot_dname_to_str(buff, zone_name, sizeof(buff));
+ if (zone_str != NULL) {
+ emit_event(KNOT_BUS_EVENT_ZONE_KEYS_UPD, "s",
+ VALUE_OF(zone_str));
+ }
+#endif // ENABLE_DBUS
+}
+
+void dbus_emit_zone_submission(const knot_dname_t *zone_name, uint16_t keytag,
+ const char *keyid)
+{
+#if ENABLE_DBUS
+ knot_dname_txt_storage_t buff;
+ char *zone_str = knot_dname_to_str(buff, zone_name, sizeof(buff));
+ if (zone_str != NULL) {
+ emit_event(KNOT_BUS_EVENT_ZONE_KSK_SUBM, "sqs",
+ VALUE_OF(zone_str), VALUE_OF(keytag),
+ VALUE_OF(keyid));
+ }
+#endif // ENABLE_DBUS
+}
+
+void dbus_emit_zone_invalid(const knot_dname_t *zone_name, uint32_t remaining_secs)
+{
+#if ENABLE_DBUS
+ knot_dname_txt_storage_t buff;
+ char *zone_str = knot_dname_to_str(buff, zone_name, sizeof(buff));
+ if (zone_str != NULL) {
+ emit_event(KNOT_BUS_EVENT_ZONE_INVALID, "su",
+ VALUE_OF(zone_str),
+ VALUE_OF(remaining_secs));
+ }
+#endif // ENABLE_DBUS
+}
diff --git a/src/knot/common/dbus.h b/src/knot/common/dbus.h
new file mode 100644
index 0000000..3a529f3
--- /dev/null
+++ b/src/knot/common/dbus.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \brief D-Bus API wrappers.
+ */
+
+#pragma once
+
+#include "libknot/libknot.h"
+
+#define KNOT_DBUS_NAME "cz.nic.knotd"
+#define KNOT_DBUS_PATH "/cz/nic/knotd"
+
+#define KNOT_BUS_EVENT_STARTED "started"
+#define KNOT_BUS_EVENT_STOPPED "stopped"
+#define KNOT_BUS_EVENT_ZONE_UPD "zone_updated"
+#define KNOT_BUS_EVENT_ZONE_KEYS_UPD "keys_updated"
+#define KNOT_BUS_EVENT_ZONE_KSK_SUBM "zone_ksk_submission"
+#define KNOT_BUS_EVENT_ZONE_INVALID "zone_dnssec_invalid"
+
+/*!
+ * \brief Creates unique D-Bus sender reference (common for whole process).
+ *
+ * \retval KNOT_EOK on successful create of reference.
+ * \retval Negative value on error.
+ */
+int dbus_open(void);
+
+/*!
+ * \brief Closes D-Bus.
+ */
+void dbus_close(void);
+
+/*!
+ * \brief Emit event signal for started daemon.
+ *
+ * \param up Indication if the server has been started.
+ */
+void dbus_emit_running(bool up);
+
+/*!
+ * \brief Emit event signal for updated zones.
+ *
+ * \param zone_name Zone name.
+ * \param serial Current zone SOA serial.
+ */
+void dbus_emit_zone_updated(const knot_dname_t *zone_name, uint32_t serial);
+
+/*!
+ * \brief Emit event signal for updated DNSSEC key set.
+ *
+ * \param zone_name Zone name.
+ */
+void dbus_emit_keys_updated(const knot_dname_t *zone_name);
+
+/*!
+ * \brief Emit event signal for KSK submission.
+ *
+ * \param zone_name Zone name.
+ * \param keytag Keytag of the ready key.
+ * \param keyid KASP id of the ready key.
+ */
+void dbus_emit_zone_submission(const knot_dname_t *zone_name, uint16_t keytag,
+ const char *keyid);
+
+/*!
+ * \brief Emit event signal for failed DNSSEC validation.
+ *
+ * \param zone_name Zone name.
+ * \param remaining_secs Remaining time until a RRSIG expires.
+ */
+void dbus_emit_zone_invalid(const knot_dname_t *zone_name, uint32_t remaining_secs);
diff --git a/src/knot/common/fdset.c b/src/knot/common/fdset.c
index a0a0212..2bf4113 100644
--- a/src/knot/common/fdset.c
+++ b/src/knot/common/fdset.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,6 +35,7 @@ static int fdset_resize(fdset_t *set, const unsigned size)
assert(set);
MEM_RESIZE(set->ctx, size);
+ MEM_RESIZE(set->ctx2, size);
MEM_RESIZE(set->timeout, size);
#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
MEM_RESIZE(set->ev, size);
@@ -80,6 +81,7 @@ void fdset_clear(fdset_t *set)
}
free(set->ctx);
+ free(set->ctx2);
free(set->timeout);
#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
free(set->ev);
@@ -104,6 +106,7 @@ int fdset_add(fdset_t *set, const int fd, const fdset_event_t events, void *ctx)
const int idx = set->n++;
set->ctx[idx] = ctx;
+ set->ctx2[idx] = NULL;
set->timeout[idx] = 0;
#ifdef HAVE_EPOLL
set->ev[idx].data.fd = fd;
@@ -164,6 +167,7 @@ int fdset_remove(fdset_t *set, const unsigned idx)
/* Nothing else if it is the last one. Move last -> i if some remain. */
if (idx < last) {
set->ctx[idx] = set->ctx[last];
+ set->ctx2[idx] = set->ctx2[last];
set->timeout[idx] = set->timeout[last];
#if defined(HAVE_EPOLL) || defined (HAVE_KQUEUE)
set->ev[idx] = set->ev[last];
@@ -326,8 +330,7 @@ void fdset_sweep(fdset_t *set, const fdset_sweep_cb_t cb, void *data)
while (idx < set->n) {
/* Check sweep state, remove if requested. */
if (set->timeout[idx] > 0 && set->timeout[idx] <= now.tv_sec) {
- const int fd = fdset_get_fd(set, idx);
- if (cb(set, fd, data) == FDSET_SWEEP) {
+ if (cb(set, idx, data) == FDSET_SWEEP) {
(void)fdset_remove(set, idx);
continue;
}
diff --git a/src/knot/common/fdset.h b/src/knot/common/fdset.h
index e0c3dbe..81ec7a8 100644
--- a/src/knot/common/fdset.h
+++ b/src/knot/common/fdset.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -45,6 +45,7 @@ typedef struct {
unsigned n; /*!< Active fds. */
unsigned size; /*!< Array size (allocated). */
void **ctx; /*!< Context for each fd. */
+ void **ctx2; /*!< Another context for each fd. */
time_t *timeout; /*!< Timeout for each fd (seconds precision). */
#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
#ifdef HAVE_EPOLL
@@ -271,6 +272,16 @@ inline static void *fdset_it_get_ctx(const fdset_it_t *it)
}
/*!
+ * \brief Get a read/write pointer on (void *) second context.
+ */
+inline static void **fdset_ctx2(const fdset_t *set, const unsigned idx)
+{
+ assert(set && idx < set->n);
+
+ return &set->ctx2[idx];
+}
+
+/*!
* \brief Move iterator on next received event.
*
* \param it Target iterator.
diff --git a/src/knot/common/stats.c b/src/knot/common/stats.c
index 79c0f23..da31e3d 100644
--- a/src/knot/common/stats.c
+++ b/src/knot/common/stats.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,140 +24,168 @@
#include "knot/common/stats.h"
#include "knot/common/log.h"
#include "knot/nameserver/query_module.h"
+#include "libknot/xdp.h"
-struct {
- bool active_dumper;
- pthread_t dumper;
- uint32_t timer;
- server_t *server;
-} stats = { 0 };
-
-typedef struct {
- FILE *fd;
- const list_t *query_modules;
- const knot_dname_t *zone;
- unsigned threads;
- int level;
- bool zone_emitted;
- bool zone_name_emitted;
- bool module_emitted;
-} dump_ctx_t;
-
-#define DUMP_STR(fd, level, name, ...) do { \
- fprintf(fd, "%-.*s"name":\n", level, " ", ##__VA_ARGS__); \
-} while (0)
-
-#define DUMP_CTR(fd, level, name, idx, val) do { \
- fprintf(fd, "%-.*s"name": %"PRIu64"\n", level, " ", idx, val); \
-} while (0)
-
-static uint64_t server_zone_count(server_t *server)
+static uint64_t stats_get_counter(knot_atomic_uint64_t **stats_vals, uint32_t offset,
+ unsigned threads)
{
- return knot_zonedb_size(server->zone_db);
+ uint64_t res = 0;
+ for (unsigned i = 0; i < threads; i++) {
+ res += ATOMIC_GET(stats_vals[i][offset]);
+ }
+ return res;
}
-const stats_item_t server_stats[] = {
- { "zone-count", { .server_val = server_zone_count } },
- { 0 }
-};
-
-static uint64_t zone_size(zone_t *zone)
+int stats_xdp(stats_dump_ctr_f fcn, stats_dump_ctx_t *ctx)
{
- return zone->contents != NULL ? zone->contents->size : 0;
+#ifdef ENABLE_XDP
+#define DUMP(structure, item_name, avg) { \
+ params.item_begin = true; \
+ params.item = #item_name; \
+ params.id = stats[0].if_name; \
+ params.value = 0; \
+ for (int j = 0; j < i->fd_xdp_count; j++) { \
+ params.value += stats[j].structure.item_name; \
+ } \
+ if (avg) { \
+ params.value /= i->fd_xdp_count; \
+ } \
+ int ret = fcn(&params, ctx); \
+ if (ret != KNOT_EOK) { \
+ return ret; \
+ } \
}
+ stats_dump_params_t params = { .section = "xdp" };
-static uint64_t zone_max_ttl(zone_t *zone)
-{
- return zone->contents != NULL ? zone->contents->max_ttl : 0;
+ if (ctx->section != NULL && strcasecmp(ctx->section, params.section) != 0) {
+ return KNOT_EOK;
+ }
+
+ for (const iface_t *i = ctx->server->ifaces;
+ i != ctx->server->ifaces + ctx->server->n_ifaces; i++) {
+ if (i->fd_xdp_count == 0) {
+ continue;
+ }
+ knot_xdp_stats_t stats[i->fd_xdp_count];
+ for (int j = 0; j < i->fd_xdp_count; j++) {
+ knot_xdp_socket_stats(i->xdp_sockets[j], &stats[j]);
+ }
+
+ DUMP(socket, rx_dropped, false);
+ DUMP(socket, rx_invalid, false);
+ DUMP(socket, tx_invalid, false);
+ DUMP(socket, rx_full, false);
+ DUMP(socket, fq_empty, false);
+ DUMP(socket, tx_empty, false);
+ DUMP(rings, tx_busy, true);
+ DUMP(rings, fq_fill, true);
+ DUMP(rings, rx_fill, true);
+ DUMP(rings, tx_fill, true);
+ DUMP(rings, cq_fill, true);
+ }
+#undef DUMP
+#endif
+ return KNOT_EOK;
}
-const stats_item_t zone_contents_stats[] = {
- { "size", { .zone_val = zone_size } },
- { "max-ttl", { .zone_val = zone_max_ttl } },
- { 0 }
-};
+#define DUMP_VAL(params, it, val) { \
+ (params).item = (it); \
+ (params).value = (val); \
+ int ret = fcn(&(params), ctx); \
+ if (ret != KNOT_EOK) { \
+ return ret; \
+ } \
+}
-uint64_t stats_get_counter(uint64_t **stats_vals, uint32_t offset, unsigned threads)
+int stats_server(stats_dump_ctr_f fcn, stats_dump_ctx_t *ctx)
{
- uint64_t res = 0;
- for (unsigned i = 0; i < threads; i++) {
- res += ATOMIC_GET(stats_vals[i][offset]);
+ stats_dump_params_t params = { .section = "server" };
+
+ if (ctx->section != NULL && strcasecmp(ctx->section, params.section) != 0) {
+ return KNOT_EOK;
}
- return res;
+
+ DUMP_VAL(params, "zone-count", knot_zonedb_size(ctx->server->zone_db));
+
+ return KNOT_EOK;
}
-static void dump_common(dump_ctx_t *ctx, knotd_mod_t *mod)
+int stats_zone(stats_dump_ctr_f fcn, stats_dump_ctx_t *ctx)
{
- // Dump zone name.
- if (ctx->zone != NULL) {
- // Prevent from zone section override.
- if (!ctx->zone_emitted) {
- ctx->level = 0;
- DUMP_STR(ctx->fd, ctx->level++, "zone");
- ctx->zone_emitted = true;
- }
+ knot_dname_txt_storage_t zone;
+ stats_dump_params_t params = { .section = "zone", .zone = zone };
- if (!ctx->zone_name_emitted) {
- ctx->level = 1;
- knot_dname_txt_storage_t name;
- if (knot_dname_to_str(name, ctx->zone, sizeof(name)) == NULL) {
- return;
- }
- DUMP_STR(ctx->fd, ctx->level++, "\"%s\"", name);
- ctx->zone_name_emitted = true;
- }
+ if (ctx->section != NULL && strcasecmp(ctx->section, params.section) != 0) {
+ return KNOT_EOK;
}
- if (!ctx->module_emitted) {
- DUMP_STR(ctx->fd, ctx->level++, "%s", mod->id->name + 1);
- ctx->module_emitted = true;
+ assert(ctx->zone);
+ zone_contents_t *contents = ctx->zone->contents;
+
+ if (knot_dname_to_str(zone, ctx->zone->name, sizeof(zone)) == NULL) {
+ return KNOT_EINVAL;
}
+
+ DUMP_VAL(params, "size", contents != NULL ? contents->size : 0);
+ DUMP_VAL(params, "max-ttl", contents != NULL ? contents->max_ttl : 0);
+
+ return KNOT_EOK;
}
-static void dump_counter(dump_ctx_t *ctx, knotd_mod_t *mod, mod_ctr_t *ctr)
+static int stats_counter(stats_dump_ctr_f fcn, stats_dump_ctx_t *ctx,
+ stats_dump_params_t *params, knotd_mod_t *mod, mod_ctr_t *ctr)
{
- uint64_t counter = stats_get_counter(mod->stats_vals, ctr->offset, ctx->threads);
- if (counter == 0) {
- // Skip empty counter.
- return;
- }
+ params->id = NULL;
+ params->value_pos = 0;
- dump_common(ctx, mod);
+ uint64_t val = stats_get_counter(mod->stats_vals, ctr->offset, ctx->threads);
- DUMP_CTR(ctx->fd, ctx->level, "%s", ctr->name, counter);
+ DUMP_VAL(*params, ctr->name, val);
+
+ return KNOT_EOK;
}
-static void dump_counters(dump_ctx_t *ctx, knotd_mod_t *mod, mod_ctr_t *ctr)
+static int stats_counters(stats_dump_ctr_f fcn, stats_dump_ctx_t *ctx,
+ stats_dump_params_t *params, knotd_mod_t *mod, mod_ctr_t *ctr)
{
- bool counter_emitted = false;
- for (uint32_t j = 0; j < ctr->count; j++) {
- uint64_t counter = stats_get_counter(mod->stats_vals, ctr->offset + j, ctx->threads);
- if (counter == 0) {
- // Skip empty counter.
- continue;
- }
+ char id[64];
+ params->id = id;
+ params->value_pos = 0;
+ params->item_begin = true;
- dump_common(ctx, mod);
-
- if (!counter_emitted) {
- DUMP_STR(ctx->fd, ctx->level, "%s", ctr->name);
- counter_emitted = true;
- }
+ for (uint32_t i = 0; i < ctr->count; i++) {
+ uint64_t val = stats_get_counter(mod->stats_vals, ctr->offset + i,
+ ctx->threads);
if (ctr->idx_to_str != NULL) {
- char *str = ctr->idx_to_str(j, ctr->count);
- if (str != NULL) {
- DUMP_CTR(ctx->fd, ctx->level + 1, "%s", str, counter);
- free(str);
+ char *str = ctr->idx_to_str(i, ctr->count);
+ if (str == NULL) {
+ continue;
}
+ (void)snprintf(id, sizeof(id), "%s", str);
+ free(str);
} else {
- DUMP_CTR(ctx->fd, ctx->level + 1, "%u", j, counter);
+ (void)snprintf(id, sizeof(id), "%u", i);
}
+
+ DUMP_VAL(*params, ctr->name, val);
+ params->value_pos++;
}
+
+ params->id = NULL;
+
+ return KNOT_EOK;
}
-static void dump_modules(dump_ctx_t *ctx)
+int stats_modules(stats_dump_ctr_f fcn, stats_dump_ctx_t *ctx)
{
+ if (ctx->section != NULL && strncasecmp(ctx->section, "mod-", strlen("mod-")) != 0) {
+ return KNOT_EOK;
+ }
+
+ knot_dname_txt_storage_t zone;
+ stats_dump_params_t params = { 0 };
+
knotd_mod_t *mod;
WALK_LIST(mod, *ctx->query_modules) {
// Skip modules without statistics.
@@ -169,39 +197,114 @@ static void dump_modules(dump_ctx_t *ctx)
ctx->threads = knotd_mod_threads(mod);
}
+ params.section = mod->id->name + 1;
+ params.module_begin = true;
+ if (ctx->zone != NULL && params.zone == NULL) {
+ params.zone = knot_dname_to_str(zone, ctx->zone->name,
+ sizeof(zone));
+ if (params.zone == NULL) {
+ return KNOT_EINVAL;
+ }
+ }
+
// Dump module counters.
- ctx->module_emitted = false;
for (int i = 0; i < mod->stats_count; i++) {
mod_ctr_t *ctr = mod->stats_info + i;
if (ctr->name == NULL) {
// Empty counter.
continue;
}
- if (ctr->count == 1) {
- // Simple counter.
- dump_counter(ctx, mod, ctr);
- } else {
- // Array of counters.
- dump_counters(ctx, mod, ctr);
+ int ret = (ctr->count == 1) ?
+ stats_counter(fcn, ctx, &params, mod, ctr) :
+ stats_counters(fcn, ctx, &params, mod, ctr);
+ if (ret != KNOT_EOK) {
+ return ret;
}
}
- if (ctx->module_emitted) {
- ctx->level--;
+ }
+
+ return KNOT_EOK;
+}
+
+struct {
+ bool active_dumper;
+ pthread_t dumper;
+ uint32_t timer;
+ server_t *server;
+} stats = { 0 };
+
+typedef struct {
+ FILE *fd;
+ bool section_emitted;
+ bool zone_section_emitted;
+ bool zone_emitted;
+ bool id_emitted;
+} dump_ctx_t;
+
+static int dump_ctr(stats_dump_params_t *params, stats_dump_ctx_t *dump_ctx)
+{
+ dump_ctx_t *ctx = dump_ctx->ctx;
+
+ if (params->value == 0) {
+ return KNOT_EOK;
+ }
+
+ const char *INDENT = " ";
+ unsigned indent = 0;
+
+ if (params->zone != NULL) {
+ if (!ctx->zone_section_emitted) {
+ fprintf(ctx->fd, "zone:\n");
+ ctx->zone_section_emitted = true;
+ }
+
+ if (!ctx->zone_emitted) {
+ fprintf(ctx->fd, " \"%s\":\n", params->zone);
+ ctx->zone_emitted = true;
}
+ indent += 2;
}
+
+ if (!ctx->section_emitted || params->module_begin) {
+ fprintf(ctx->fd, "%-.*s%s:\n", indent, INDENT, params->section);
+ ctx->section_emitted = true;
+ params->module_begin = false;
+ }
+ indent++;
+
+ if (params->id != NULL) {
+ if (params->item_begin) {
+ fprintf(ctx->fd, "%-.*s%s:\n", indent, INDENT, params->item);
+ params->item_begin = false;
+ }
+ indent++;
+ fprintf(ctx->fd, "%-.*s%s: %"PRIu64"\n", indent, INDENT,
+ params->id, params->value);
+ } else {
+ fprintf(ctx->fd, "%-.*s%s: %"PRIu64"\n", indent, INDENT,
+ params->item, params->value);
+ }
+
+ return KNOT_EOK;
}
-static void zone_stats_dump(zone_t *zone, dump_ctx_t *ctx)
+static void zone_stats_dump(zone_t *zone, stats_dump_ctx_t *dump_ctx)
{
if (EMPTY_LIST(zone->query_modules)) {
return;
}
- ctx->query_modules = &zone->query_modules;
- ctx->zone = zone->name;
- ctx->zone_name_emitted = false;
+ // Reset per-zone context.
+ dump_ctx_t *ctx = dump_ctx->ctx;
+ *ctx = (dump_ctx_t){
+ .fd = ctx->fd,
+ .zone_section_emitted = ctx->zone_section_emitted,
+ };
+
+ dump_ctx->zone = zone;
+ dump_ctx->query_modules = &zone->query_modules;
- dump_modules(ctx);
+ (void)stats_modules(dump_ctr, dump_ctx);
}
static void dump_to_file(conf_t *conf, FILE *fd, server_t *server)
@@ -224,22 +327,28 @@ static void dump_to_file(conf_t *conf, FILE *fd, server_t *server)
"identity: %s\n",
date, ident);
- dump_ctx_t ctx = {
- .fd = fd,
+ dump_ctx_t ctx = { .fd = fd };
+
+ stats_dump_ctx_t dump_ctx = {
+ .server = server,
.query_modules = conf->query_modules,
+ .ctx = &ctx,
};
- // Dump server statistics.
- DUMP_STR(ctx.fd, ctx.level, "server");
- for (const stats_item_t *item = server_stats; item->name != NULL; item++) {
- DUMP_CTR(ctx.fd, ctx.level + 1, "%s", item->name, item->server_val(server));
- }
+ // Dump server counters.
+ (void)stats_server(dump_ctr, &dump_ctx);
+
+ // Dump XDP counters.
+ ctx = (dump_ctx_t){ .fd = fd };
+ (void)stats_xdp(dump_ctr, &dump_ctx);
- // Dump global statistics.
- dump_modules(&ctx);
+ // Dump global module counters.
+ ctx = (dump_ctx_t){ .fd = fd };
+ (void)stats_modules(dump_ctr, &dump_ctx);
- // Dump zone statistics.
- knot_zonedb_foreach(server->zone_db, zone_stats_dump, &ctx);
+ // Dump per zone module counters (fixed zone counters not included).
+ ctx = (dump_ctx_t){ .fd = fd };
+ knot_zonedb_foreach(server->zone_db, zone_stats_dump, &dump_ctx);
}
static void dump_stats(server_t *server)
@@ -321,7 +430,6 @@ void stats_reconfigure(conf_t *conf, server_t *server)
return;
}
- // Update server context.
stats.server = server;
conf_val_t val = conf_get(conf, C_STATS, C_TIMER);
diff --git a/src/knot/common/stats.h b/src/knot/common/stats.h
index 5cb51ca..4391178 100644
--- a/src/knot/common/stats.h
+++ b/src/knot/common/stats.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,36 +20,65 @@
#pragma once
+#include "contrib/atomic.h"
#include "knot/server/server.h"
-typedef uint64_t (*stats_server_val_f)(server_t *server);
-typedef uint64_t (*stats_zone_val_f)(zone_t *zone);
+/*!
+ * \brief Parameters for a statistic metric dump callback.
+ */
+typedef struct {
+ const char *section;
+ const char *item;
+ const char *id;
+ const char *zone;
+ uint64_t value;
+ unsigned value_pos; // Counted from 0.
+
+ bool module_begin; // Indication of a new module.
+ bool item_begin; // Indication of a new item.
+} stats_dump_params_t;
/*!
- * \brief Statistics metrics item.
+ * \brief Statistic metric context.
*/
typedef struct {
- const char *name; /*!< Metrics name. */
- union {
- stats_server_val_f server_val; /*!< Server metrics value getter. */
- stats_zone_val_f zone_val; /*!< Zone metrics value getter. */
- };
-} stats_item_t;
+ server_t *server;
+ zone_t *zone;
+ const list_t *query_modules;
+
+ const char *section; // Optional section specification.
+ const char *item; // Optional item specification.
+ bool match; // Indication of non-empty [[section[.item]] selection.
+
+ unsigned threads; // Internal cache for the number of workers.
+
+ void *ctx;
+} stats_dump_ctx_t;
+
+/*!
+ * \brief Statistic metric dump callback.
+ */
+typedef int (*stats_dump_ctr_f)(stats_dump_params_t *, stats_dump_ctx_t *);
+
+/*!
+ * \brief XDP metrics.
+ */
+int stats_xdp(stats_dump_ctr_f fcn, stats_dump_ctx_t *ctx);
/*!
- * \brief Basic server metrics.
+ * \brief Server metrics.
*/
-extern const stats_item_t server_stats[];
+int stats_server(stats_dump_ctr_f fcn, stats_dump_ctx_t *ctx);
/*!
- * \brief Basic zone metrics.
+ * \brief Zone metrics.
*/
-extern const stats_item_t zone_contents_stats[];
+int stats_zone(stats_dump_ctr_f fcn, stats_dump_ctx_t *ctx);
/*!
- * \brief Read out value of single counter summed across threads.
+ * \brief Modules metrics.
*/
-uint64_t stats_get_counter(uint64_t **stats_vals, uint32_t offset, unsigned threads);
+int stats_modules(stats_dump_ctr_f fcn, stats_dump_ctx_t *ctx);
/*!
* \brief Reconfigures the statistics facility.
diff --git a/src/knot/common/systemd.c b/src/knot/common/systemd.c
index 5a72fb4..3f9d9a6 100644
--- a/src/knot/common/systemd.c
+++ b/src/knot/common/systemd.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,8 +14,6 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-#include <stdarg.h>
-#include <stdio.h>
#include <stdlib.h>
#include "knot/common/systemd.h"
@@ -43,12 +41,6 @@ static int systemd_zone_load_timeout(void)
}
#endif
-#ifdef ENABLE_DBUS
-#include <systemd/sd-bus.h>
-
-static sd_bus *_dbus = NULL;
-#endif
-
void systemd_zone_load_timeout_notify(void)
{
#ifdef ENABLE_SYSTEMD
@@ -92,91 +84,3 @@ void systemd_stopping_notify(void)
sd_notify(0, "STOPPING=1\nSTATUS=");
#endif
}
-
-int systemd_dbus_open(void)
-{
-#ifdef ENABLE_DBUS
- if (_dbus != NULL) {
- return KNOT_EOK;
- }
-
- int ret = sd_bus_open_system(&_dbus);
- if (ret < 0) {
- return ret;
- }
-
- /* Take a well-known service name so that clients can find us. */
- ret = sd_bus_request_name(_dbus, KNOT_DBUS_NAME, 0);
- if (ret < 0) {
- systemd_dbus_close();
- return ret;
- }
-
- return KNOT_EOK;
-#else
- return KNOT_ENOTSUP;
-#endif
-}
-
-void systemd_dbus_close(void)
-{
-#ifdef ENABLE_DBUS
- _dbus = sd_bus_unref(_dbus);
-#endif
-}
-
-#define emit_event(event, ...) \
- sd_bus_emit_signal(_dbus, KNOT_DBUS_PATH, KNOT_DBUS_NAME".events", \
- event, __VA_ARGS__)
-
-void systemd_emit_running(bool up)
-{
-#ifdef ENABLE_DBUS
- emit_event(up ? KNOT_BUS_EVENT_STARTED : KNOT_BUS_EVENT_STOPPED, "");
-#endif
-}
-
-void systemd_emit_zone_updated(const knot_dname_t *zone_name, uint32_t serial)
-{
-#ifdef ENABLE_DBUS
- knot_dname_txt_storage_t buff;
- char *zone_str = knot_dname_to_str(buff, zone_name, sizeof(buff));
- if (zone_str != NULL) {
- emit_event(KNOT_BUS_EVENT_ZONE_UPD, "su", zone_str, serial);
- }
-#endif
-}
-
-void systemd_emit_keys_updated(const knot_dname_t *zone_name)
-{
-#ifdef ENABLE_DBUS
- knot_dname_txt_storage_t buff;
- char *zone_str = knot_dname_to_str(buff, zone_name, sizeof(buff));
- if (zone_str != NULL) {
- emit_event(KNOT_BUS_EVENT_ZONE_KEYS_UPD, "s", zone_str);
- }
-#endif
-}
-
-void systemd_emit_zone_submission(const knot_dname_t *zone_name, uint16_t keytag,
- const char *keyid)
-{
-#ifdef ENABLE_DBUS
- knot_dname_txt_storage_t buff;
- char *zone_str = knot_dname_to_str(buff, zone_name, sizeof(buff));
- if (zone_str != NULL) {
- emit_event(KNOT_BUS_EVENT_ZONE_KSK_SUBM, "sqs", zone_str, keytag, keyid);
- }
-#endif
-}
-
-void systemd_emit_zone_invalid(const knot_dname_t *zone_name)
-{
-#ifdef ENABLE_DBUS
- knot_dname_txt_storage_t buff;
- char *zone_str = knot_dname_to_str(buff, zone_name, sizeof(buff));
- if (zone_str != NULL) {
- emit_event(KNOT_BUS_EVENT_ZONE_INVALID, "s", zone_str);
- }
-#endif
-}
diff --git a/src/knot/common/systemd.h b/src/knot/common/systemd.h
index c6fe260..2038e94 100644
--- a/src/knot/common/systemd.h
+++ b/src/knot/common/systemd.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,18 +20,6 @@
#pragma once
-#include "libknot/libknot.h"
-
-#define KNOT_DBUS_NAME "cz.nic.knotd"
-#define KNOT_DBUS_PATH "/cz/nic/knotd"
-
-#define KNOT_BUS_EVENT_STARTED "started"
-#define KNOT_BUS_EVENT_STOPPED "stopped"
-#define KNOT_BUS_EVENT_ZONE_UPD "zone_updated"
-#define KNOT_BUS_EVENT_ZONE_KEYS_UPD "keys_updated"
-#define KNOT_BUS_EVENT_ZONE_KSK_SUBM "zone_ksk_submission"
-#define KNOT_BUS_EVENT_ZONE_INVALID "zone_dnssec_invalid"
-
/*!
* \brief Notify systemd about zone loading start.
*/
@@ -59,55 +47,3 @@ void systemd_reloading_notify(void);
* \brief Notify systemd about service is stopping.
*/
void systemd_stopping_notify(void);
-
-/*!
- * \brief Creates unique D-Bus sender reference (common for whole process).
- *
- * \retval KNOT_EOK on successful create of reference.
- * \retval Negative value on error.
- */
-int systemd_dbus_open(void);
-
-/*!
- * \brief Closes D-Bus.
- */
-void systemd_dbus_close(void);
-
-/*!
- * \brief Emit event signal for started daemon.
- *
- * \param up Indication if the server has been started.
- */
-void systemd_emit_running(bool up);
-
-/*!
- * \brief Emit event signal for updated zones.
- *
- * \param zone_name Zone name.
- * \param serial Current zone SOA serial.
- */
-void systemd_emit_zone_updated(const knot_dname_t *zone_name, uint32_t serial);
-
-/*!
- * \brief Emit event signal for updated DNSSEC key set.
- *
- * \param zone_name Zone name.
- */
-void systemd_emit_keys_updated(const knot_dname_t *zone_name);
-
-/*!
- * \brief Emit event signal for KSK submission.
- *
- * \param zone_name Zone name.
- * \param keytag Keytag of the ready key.
- * \param keyid KASP id of the ready key.
- */
-void systemd_emit_zone_submission(const knot_dname_t *zone_name, uint16_t keytag,
- const char *keyid);
-
-/*!
- * \brief Emit event signal for failed DNSSEC validation.
- *
- * \param zone_name Zone name.
- */
-void systemd_emit_zone_invalid(const knot_dname_t *zone_name);
diff --git a/src/knot/conf/base.c b/src/knot/conf/base.c
index 51caf10..4683171 100644
--- a/src/knot/conf/base.c
+++ b/src/knot/conf/base.c
@@ -130,6 +130,9 @@ static void init_cache(
static bool running_xdp_tcp;
static uint16_t running_xdp_quic;
static bool running_route_check;
+ static uint16_t running_ring_size;
+ static uint16_t running_busypoll_budget;
+ static uint16_t running_busypoll_timeout;
static size_t running_udp_threads;
static size_t running_tcp_threads;
static size_t running_xdp_threads;
@@ -148,6 +151,9 @@ static void init_cache(
running_xdp_quic = conf_get_int(conf, C_XDP, C_QUIC_PORT);
}
running_route_check = conf_get_bool(conf, C_XDP, C_ROUTE_CHECK);
+ running_ring_size = conf_get_int(conf, C_XDP, C_RING_SIZE);
+ running_busypoll_budget = conf_get_int(conf, C_XDP, C_BUSYPOLL_BUDGET);
+ running_busypoll_timeout = conf_get_int(conf, C_XDP, C_BUSYPOLL_TIMEOUT);
running_udp_threads = conf_udp_threads(conf);
running_tcp_threads = conf_tcp_threads(conf);
running_xdp_threads = conf_xdp_threads(conf);
@@ -235,6 +241,12 @@ static void init_cache(
conf->cache.xdp_route_check = running_route_check;
+ conf->cache.xdp_ring_size = running_ring_size;
+
+ conf->cache.xdp_busypoll_budget = running_busypoll_budget;
+
+ conf->cache.xdp_busypoll_timeout = running_busypoll_timeout;
+
val = conf_get(conf, C_CTL, C_TIMEOUT);
conf->cache.ctl_timeout = conf_int(&val) * 1000;
/* infinite_adjust() call isn't needed, 0 is adjusted later anyway. */
@@ -335,9 +347,10 @@ int conf_new(
ret = out->api->init(&out->db, NULL, &lmdb_opts);
// Remove the database to ensure it is temporary.
- if (!remove_path(lmdb_opts.path)) {
- CONF_LOG(LOG_WARNING, "failed to purge temporary directory '%s'",
- lmdb_opts.path);
+ int ret2 = remove_path(lmdb_opts.path, false);
+ if (ret2 != KNOT_EOK) {
+ CONF_LOG(LOG_WARNING, "failed to purge temporary directory '%s' (%s)",
+ lmdb_opts.path, knot_strerror(ret2));
}
} else {
// Set the specified database.
diff --git a/src/knot/conf/base.h b/src/knot/conf/base.h
index 5c77cac..1ac5ef5 100644
--- a/src/knot/conf/base.h
+++ b/src/knot/conf/base.h
@@ -165,6 +165,9 @@ typedef struct {
const char *srv_version;
uint32_t srv_quic_idle_close;
uint16_t xdp_quic;
+ uint16_t xdp_ring_size;
+ uint16_t xdp_busypoll_budget;
+ uint16_t xdp_busypoll_timeout;
int ctl_timeout;
bool xdp_udp;
bool xdp_tcp;
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
index 55ee971..5e9ed83 100644
--- a/src/knot/conf/conf.c
+++ b/src/knot/conf/conf.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -606,8 +606,9 @@ void conf_mix_iter_next(
}
}
-int64_t conf_int(
- conf_val_t *val)
+int64_t conf_int_alt(
+ conf_val_t *val,
+ bool alternative)
{
assert(val != NULL && val->item != NULL);
assert(val->item->type == YP_TINT ||
@@ -618,7 +619,7 @@ int64_t conf_int(
conf_val(val);
return yp_int(val->data);
} else {
- return val->item->var.i.dflt;
+ return alternative ? val->item->var.i.dflt_alt : val->item->var.i.dflt;
}
}
@@ -1063,7 +1064,7 @@ static int str_label(
size_t index = labels - right_index - 1;
// Create a dname from the single label.
- size_t prefix_len = knot_dname_prefixlen(zone, index, NULL);
+ size_t prefix_len = knot_dname_prefixlen(zone, index);
size_t label_len = *(zone + prefix_len);
memcpy(label, zone + prefix_len, 1 + label_len);
label[1 + label_len] = '\0';
@@ -1379,6 +1380,8 @@ conf_remote_t conf_remote_txn(
conf_val_t val = conf_id_get_txn(conf, txn, C_RMT, C_QUIC, id);
out.quic = conf_bool(&val);
+ val = conf_id_get_txn(conf, txn, C_RMT, C_TLS, id);
+ out.tls = conf_bool(&val);
conf_val_t rundir_val = conf_get_txn(conf, txn, C_SRV, C_RUNDIR);
char *rundir = conf_abs_path(&rundir_val, NULL);
@@ -1398,7 +1401,7 @@ conf_remote_t conf_remote_txn(
conf_val_next(&val);
}
// Index overflow causes empty socket.
- out.addr = conf_addr_alt(&val, rundir, out.quic);
+ out.addr = conf_addr_alt(&val, rundir, out.quic || out.tls);
// Get outgoing address if family matches (optional).
uint16_t via_pos = 0;
diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h
index 562722d..f79a284 100644
--- a/src/knot/conf/conf.h
+++ b/src/knot/conf/conf.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -44,6 +44,8 @@ typedef struct {
struct sockaddr_storage via;
/*! QUIC context. */
bool quic;
+ /*! TLS context. */
+ bool tls;
/*! TSIG key. */
knot_tsig_key_t key;
/*! Suppress sending NOTIFY after zone transfer from this master. */
@@ -469,13 +471,20 @@ void conf_mix_iter_next(
/*!
* Gets the numeric value of the item.
*
- * \param[in] val Item value.
+ * \param[in] val Item value.
+ * \param[in] alternative Use alternative default value.
*
* \return Integer.
*/
-int64_t conf_int(
- conf_val_t *val
+int64_t conf_int_alt(
+ conf_val_t *val,
+ bool alternative
);
+inline static int64_t conf_int(
+ conf_val_t *val)
+{
+ return conf_int_alt(val, false);
+}
/*!
* Gets the boolean value of the item.
diff --git a/src/knot/conf/schema.c b/src/knot/conf/schema.c
index 7b30a2f..fd55fc8 100644
--- a/src/knot/conf/schema.c
+++ b/src/knot/conf/schema.c
@@ -64,9 +64,7 @@ static const knot_lookup_t dnssec_key_algs[] = {
{ DNSSEC_KEY_ALGORITHM_RSA_SHA512, "rsasha512" },
{ DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256, "ecdsap256sha256" },
{ DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384, "ecdsap384sha384" },
-#ifdef HAVE_ED25519
{ DNSSEC_KEY_ALGORITHM_ED25519, "ed25519" },
-#endif
#ifdef HAVE_ED448
{ DNSSEC_KEY_ALGORITHM_ED448, "ed448" },
#endif
@@ -249,15 +247,8 @@ static const yp_item_t desc_server[] = {
{ C_DBUS_INIT_DELAY, YP_TINT, YP_VINT = { 0, INT32_MAX, 1, YP_STIME } },
{ C_LISTEN, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI, { check_listen } },
{ C_LISTEN_QUIC, YP_TADDR, YP_VADDR = { 853 }, YP_FMULTI, { check_listen } },
+ { C_LISTEN_TLS, YP_TADDR, YP_VADDR = { 853 }, YP_FMULTI, { check_listen } },
{ C_COMMENT, YP_TSTR, YP_VNONE },
- // Legacy items.
- { C_LISTEN_XDP, YP_TADDR, YP_VADDR = { 0 }, YP_FMULTI, { legacy_item } },
- { C_MAX_TCP_CLIENTS, YP_TINT, YP_VINT = { 0, INT32_MAX, 0 }, YP_FNONE, { legacy_item } },
- { C_TCP_HSHAKE_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 0, YP_STIME }, YP_FNONE, { legacy_item } },
- { C_TCP_REPLY_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 0, YP_STIME }, YP_FNONE, { legacy_item } },
- { C_MAX_UDP_PAYLOAD, YP_TINT, YP_VINT = { 0, INT32_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } },
- { C_MAX_IPV4_UDP_PAYLOAD, YP_TINT, YP_VINT = { 0, INT32_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } },
- { C_MAX_IPV6_UDP_PAYLOAD, YP_TINT, YP_VINT = { 0, INT32_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } },
{ NULL }
};
@@ -274,10 +265,10 @@ static const yp_item_t desc_xdp[] = {
{ C_TCP_IDLE_RESET, YP_TINT, YP_VINT = { 1, INT32_MAX, 20, YP_STIME } },
{ C_TCP_RESEND, YP_TINT, YP_VINT = { 1, INT32_MAX, 5, YP_STIME } },
{ C_ROUTE_CHECK, YP_TBOOL, YP_VNONE },
- { C_EXTRA_FRAMES, YP_TBOOL, YP_VNONE },
+ { C_RING_SIZE, YP_TINT, YP_VINT = { 4, 32768, 2048 } },
+ { C_BUSYPOLL_BUDGET, YP_TINT, YP_VINT = { 0, UINT16_MAX, 0 } },
+ { C_BUSYPOLL_TIMEOUT, YP_TINT, YP_VINT = { 1, UINT16_MAX, 20 } },
{ C_COMMENT, YP_TSTR, YP_VNONE },
- // Legacy items.
- { C_QUIC_LOG, YP_TBOOL, YP_VNONE, YP_FNONE, { legacy_item } },
{ NULL }
};
@@ -350,6 +341,7 @@ static const yp_item_t desc_remote[] = {
{ C_ADDR, YP_TADDR, YP_VADDR = { 53, 853 }, YP_FMULTI },
{ C_VIA, YP_TADDR, YP_VNONE, YP_FMULTI },
{ C_QUIC, YP_TBOOL, YP_VNONE },
+ { C_TLS, YP_TBOOL, YP_VNONE },
{ C_KEY, YP_TREF, YP_VREF = { C_KEY }, YP_FNONE, { check_ref } },
{ C_CERT_KEY, YP_TB64, YP_VNONE, YP_FMULTI, { check_cert_pin } },
{ C_BLOCK_NOTIFY_XFR, YP_TBOOL, YP_VNONE },
@@ -432,7 +424,7 @@ static const yp_item_t desc_policy[] = {
CONF_IO_FRLD_ZONES },
{ C_RRSIG_REFRESH, YP_TINT, YP_VINT = { 1, INT32_MAX, YP_NIL, YP_STIME },
CONF_IO_FRLD_ZONES },
- { C_RRSIG_PREREFRESH, YP_TINT, YP_VINT = { 0, INT32_MAX, HOURS(1), YP_STIME },
+ { C_RRSIG_PREREFRESH, YP_TINT, YP_VINT = { 0, INT32_MAX, HOURS(1), YP_STIME, DAYS(1) },
CONF_IO_FRLD_ZONES },
{ C_REPRO_SIGNING, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES },
{ C_NSEC3, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES },
@@ -506,27 +498,12 @@ static const yp_item_t desc_policy[] = {
{ C_MODULE, YP_TDATA, YP_VDATA = { 0, NULL, mod_id_to_bin, mod_id_to_txt }, \
YP_FMULTI | FLAGS, { check_modref } }, \
{ C_COMMENT, YP_TSTR, YP_VNONE }, \
- /* Legacy items.*/ \
- { C_DISABLE_ANY, YP_TBOOL, YP_VNONE, YP_FNONE, { legacy_item } }, \
- { C_MAX_ZONE_SIZE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } }, \
- { C_MAX_JOURNAL_USAGE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } }, \
- { C_MAX_JOURNAL_DEPTH, YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0 }, YP_FNONE, { legacy_item } }, \
- { C_MAX_REFRESH_INTERVAL,YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_STIME }, YP_FNONE, { legacy_item } }, \
- { C_MIN_REFRESH_INTERVAL,YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_STIME }, YP_FNONE, { legacy_item } }, \
static const yp_item_t desc_template[] = {
{ C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF },
{ C_GLOBAL_MODULE, YP_TDATA, YP_VDATA = { 0, NULL, mod_id_to_bin, mod_id_to_txt },
YP_FMULTI | CONF_IO_FRLD_MOD, { check_modref } },
ZONE_ITEMS(CONF_IO_FRLD_ZONES)
- // Legacy items.
- { C_TIMER_DB, YP_TSTR, YP_VSTR = { "" }, YP_FNONE, { legacy_item } },
- { C_MAX_TIMER_DB_SIZE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } },
- { C_JOURNAL_DB, YP_TSTR, YP_VSTR = { "" }, YP_FNONE, { legacy_item } },
- { C_JOURNAL_DB_MODE, YP_TOPT, YP_VOPT = { journal_modes, 0 }, YP_FNONE, { legacy_item } },
- { C_MAX_JOURNAL_DB_SIZE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } },
- { C_KASP_DB, YP_TSTR, YP_VSTR = { "" }, YP_FNONE, { legacy_item } },
- { C_MAX_KASP_DB_SIZE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } },
{ NULL }
};
diff --git a/src/knot/conf/schema.h b/src/knot/conf/schema.h
index dc5a04e..d2966e6 100644
--- a/src/knot/conf/schema.h
+++ b/src/knot/conf/schema.h
@@ -33,6 +33,8 @@
#define C_BACKLOG "\x07""backlog"
#define C_BG_WORKERS "\x12""background-workers"
#define C_BLOCK_NOTIFY_XFR "\x1B""block-notify-after-transfer"
+#define C_BUSYPOLL_BUDGET "\x0F""busypoll-budget"
+#define C_BUSYPOLL_TIMEOUT "\x10""busypoll-timeout"
#define C_CATALOG_DB "\x0A""catalog-db"
#define C_CATALOG_DB_MAX_SIZE "\x13""catalog-db-max-size"
#define C_CATALOG_GROUP "\x0D""catalog-group"
@@ -65,7 +67,6 @@
#define C_ECS "\x12""edns-client-subnet"
#define C_EXPIRE_MAX_INTERVAL "\x13""expire-max-interval"
#define C_EXPIRE_MIN_INTERVAL "\x13""expire-min-interval"
-#define C_EXTRA_FRAMES "\x0C""extra-frames"
#define C_FILE "\x04""file"
#define C_GLOBAL_MODULE "\x0D""global-module"
#define C_ID "\x02""id"
@@ -94,6 +95,7 @@
#define C_KSK_SIZE "\x08""ksk-size"
#define C_LISTEN "\x06""listen"
#define C_LISTEN_QUIC "\x0B""listen-quic"
+#define C_LISTEN_TLS "\x0A""listen-tls"
#define C_LOG "\x03""log"
#define C_MANUAL "\x06""manual"
#define C_MASTER "\x06""master"
@@ -117,7 +119,6 @@
#define C_PROXY_ALLOWLIST "\x0F""proxy-allowlist"
#define C_QUIC "\x04""quic"
#define C_QUIC_IDLE_CLOSE "\x17""quic-idle-close-timeout"
-#define C_QUIC_LOG "\x08""quic-log"
#define C_QUIC_MAX_CLIENTS "\x10""quic-max-clients"
#define C_QUIC_OUTBUF_MAX_SIZE "\x14""quic-outbuf-max-size"
#define C_QUIC_PORT "\x09""quic-port"
@@ -127,6 +128,7 @@
#define C_RETRY_MAX_INTERVAL "\x12""retry-max-interval"
#define C_RETRY_MIN_INTERVAL "\x12""retry-min-interval"
#define C_REVERSE_GEN "\x10""reverse-generate"
+#define C_RING_SIZE "\x09""ring-size"
#define C_RMT "\x06""remote"
#define C_RMTS "\x07""remotes"
#define C_RMT_POOL_LIMIT "\x11""remote-pool-limit"
@@ -167,6 +169,7 @@
#define C_TIMER "\x05""timer"
#define C_TIMER_DB "\x08""timer-db"
#define C_TIMER_DB_MAX_SIZE "\x11""timer-db-max-size"
+#define C_TLS "\x03""tls"
#define C_TPL "\x08""template"
#define C_UDP "\x03""udp"
#define C_UDP_MAX_PAYLOAD "\x0F""udp-max-payload"
@@ -192,24 +195,6 @@
#define C_ZSK_LIFETIME "\x0C""zsk-lifetime"
#define C_ZSK_SIZE "\x08""zsk-size"
-// Legacy items.
-#define C_DISABLE_ANY "\x0B""disable-any"
-#define C_LISTEN_XDP "\x0A""listen-xdp"
-#define C_MAX_TIMER_DB_SIZE "\x11""max-timer-db-size"
-#define C_MAX_JOURNAL_DB_SIZE "\x13""max-journal-db-size"
-#define C_MAX_KASP_DB_SIZE "\x10""max-kasp-db-size"
-#define C_TCP_HSHAKE_TIMEOUT "\x15""tcp-handshake-timeout"
-#define C_TCP_REPLY_TIMEOUT "\x11""tcp-reply-timeout"
-#define C_MAX_TCP_CLIENTS "\x0F""max-tcp-clients"
-#define C_MAX_UDP_PAYLOAD "\x0F""max-udp-payload"
-#define C_MAX_IPV4_UDP_PAYLOAD "\x14""max-ipv4-udp-payload"
-#define C_MAX_IPV6_UDP_PAYLOAD "\x14""max-ipv6-udp-payload"
-#define C_MAX_ZONE_SIZE "\x0D""max-zone-size"
-#define C_MAX_REFRESH_INTERVAL "\x14""max-refresh-interval"
-#define C_MIN_REFRESH_INTERVAL "\x14""min-refresh-interval"
-#define C_MAX_JOURNAL_DEPTH "\x11""max-journal-depth"
-#define C_MAX_JOURNAL_USAGE "\x11""max-journal-usage"
-
enum {
KEYSTORE_BACKEND_PEM = 1,
KEYSTORE_BACKEND_PKCS11 = 2,
diff --git a/src/knot/conf/tools.c b/src/knot/conf/tools.c
index e232fba..1ea8446 100644
--- a/src/knot/conf/tools.c
+++ b/src/knot/conf/tools.c
@@ -43,9 +43,7 @@
#include "knot/updates/acl.h"
#include "knot/zone/serial.h"
#include "libknot/errcode.h"
-#ifdef ENABLE_QUIC
-#include "libknot/quic/quic.h"
-#endif // ENABLE_QUIC
+#include "libknot/quic/tls_common.h"
#include "libknot/yparser/yptrafo.h"
#include "libknot/xdp.h"
#include "contrib/files.h"
@@ -345,15 +343,13 @@ int check_xdp_listen(
int check_cert_pin(
knotd_conf_check_args_t *args)
{
-#ifdef ENABLE_QUIC
- if (args->data_len != sizeof(uint16_t) + KNOT_QUIC_PIN_LEN) {
+ if (args->data_len != sizeof(uint16_t) + KNOT_TLS_PIN_LEN) {
(void)snprintf(check_str, sizeof(check_str),
"invalid certificate pin, expected base64-encoded "
- "%u bytes", KNOT_QUIC_PIN_LEN);
+ "%u bytes", KNOT_TLS_PIN_LEN);
args->err_str = check_str;
return KNOT_EINVAL;
}
-#endif // ENABLE_QUIC
return KNOT_EOK;
}
@@ -551,7 +547,6 @@ static void check_mtu(knotd_conf_check_args_t *args, conf_val_t *xdp_listen)
#endif
}
-#ifdef ENABLE_QUIC
static bool listen_hit(const struct sockaddr_storage *ss1,
const struct sockaddr_storage *ss2)
{
@@ -562,7 +557,33 @@ static bool listen_hit(const struct sockaddr_storage *ss1,
return sockaddr_cmp(ss1, ss2, false) == 0;
}
}
-#endif // ENABLE_QUIC
+
+static bool listen_overlaps(
+ knotd_conf_check_args_t *args,
+ conf_val_t *chk_listen,
+ size_t chk_listen_count)
+{
+ conf_val_t listen_val = conf_get_txn(args->extra->conf, args->extra->txn,
+ C_SRV, C_LISTEN);
+ size_t listen_count = conf_val_count(&listen_val);
+
+ for (size_t i = 0; listen_count > 0 && i < chk_listen_count; i++) {
+ struct sockaddr_storage chk_addr = conf_addr(chk_listen, NULL);
+
+ for (size_t j = 0; j < listen_count; j++) {
+ struct sockaddr_storage listen_addr = conf_addr(&listen_val, NULL);
+ if (listen_hit(&chk_addr, &listen_addr)) {
+ return true;
+ }
+ conf_val_next(&listen_val);
+ }
+
+ conf_val(&listen_val);
+ conf_val_next(chk_listen);
+ }
+
+ return false;
+}
int check_server(
knotd_conf_check_args_t *args)
@@ -576,30 +597,26 @@ int check_server(
return KNOT_EINVAL;
}
+ conf_val_t listls_val = conf_get_txn(args->extra->conf, args->extra->txn,
+ C_SRV, C_LISTEN_TLS);
+ size_t listls_count = conf_val_count(&listls_val);
+ if (listls_count > 0) {
+ if (listen_overlaps(args, &listls_val, listls_count)) {
+ args->err_str = "TLS listen address/port overlaps "
+ "with TCP listen address/port";
+ return KNOT_EINVAL;
+ }
+ }
+
conf_val_t liquic_val = conf_get_txn(args->extra->conf, args->extra->txn,
C_SRV, C_LISTEN_QUIC);
size_t liquic_count = conf_val_count(&liquic_val);
if (liquic_count > 0) {
#ifdef ENABLE_QUIC
- conf_val_t listen_val = conf_get_txn(args->extra->conf, args->extra->txn,
- C_SRV, C_LISTEN);
- size_t listen_count = conf_val_count(&listen_val);
-
- for (size_t i = 0; listen_count > 0 && i < liquic_count; i++) {
- struct sockaddr_storage liquic_addr = conf_addr(&liquic_val, NULL);
-
- for (size_t j = 0; j < listen_count; j++) {
- struct sockaddr_storage listen_addr = conf_addr(&listen_val, NULL);
- if (listen_hit(&liquic_addr, &listen_addr)) {
- args->err_str = "QUIC listen address/port overlaps "
- "with UDP listen address/port";
- return KNOT_EINVAL;
- }
- conf_val_next(&listen_val);
- }
-
- conf_val(&listen_val);
- conf_val_next(&liquic_val);
+ if (listen_overlaps(args, &liquic_val, liquic_count)) {
+ args->err_str = "QUIC listen address/port overlaps "
+ "with UDP listen address/port";
+ return KNOT_EINVAL;
}
#else
args->err_str = "QUIC processing not available";
@@ -713,10 +730,6 @@ int check_policy(
C_NSEC3_ITER, args->id, args->id_len);
unsigned algorithm = conf_opt(&alg);
- if (algorithm < DNSSEC_KEY_ALGORITHM_RSA_SHA256) {
- CONF_LOG(LOG_NOTICE, "algorithm %u is deprecated and shouldn't be used for DNSSEC signing",
- algorithm);
- }
int64_t ksk_size = conf_int(&ksk);
if (ksk_size != YP_NIL && !dnssec_algorithm_key_size_check(algorithm, ksk_size)) {
@@ -779,14 +792,6 @@ int check_policy(
return KNOT_EINVAL;
}
-#ifndef HAVE_GNUTLS_REPRODUCIBLE
- conf_val_t repro_sign = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
- C_REPRO_SIGNING, args->id, args->id_len);
- if (conf_bool(&repro_sign)) {
- CONF_LOG(LOG_WARNING, "reproducible signing not available, signing normally");
- }
-#endif
-
if (conf_bool(&nsec3)) {
uint16_t iters = conf_int(&nsec3_iters);
if (iters > 0) {
@@ -880,12 +885,18 @@ int check_remote(
return KNOT_EINVAL;
}
+ conf_val_t tls = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_RMT,
+ C_TLS, args->id, args->id_len);
conf_val_t quic = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_RMT,
C_QUIC, args->id, args->id_len);
if (quic.code == KNOT_EOK) {
#ifdef ENABLE_QUIC
- (void)0;
+ if (conf_bool(&quic) && conf_bool(&tls)) {
+ args->err_str = "remote can't use both QUIC and TLS";
+ return KNOT_EINVAL;
+ }
#else
+ (void)tls;
args->err_str = "QUIC not available";
return KNOT_EINVAL;
#endif
diff --git a/src/knot/ctl/commands.c b/src/knot/ctl/commands.c
index 7febce1..56e1d67 100644
--- a/src/knot/ctl/commands.c
+++ b/src/knot/ctl/commands.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,12 +17,14 @@
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
+#include <time.h>
#include <urcu.h>
#include "knot/common/log.h"
#include "knot/common/stats.h"
#include "knot/conf/confio.h"
#include "knot/ctl/commands.h"
+#include "knot/ctl/process.h"
#include "knot/dnssec/key-events.h"
#include "knot/events/events.h"
#include "knot/events/handlers.h"
@@ -36,6 +38,7 @@
#include "knot/zone/zonefile.h"
#include "libknot/libknot.h"
#include "libknot/yparser/yptrafo.h"
+#include "contrib/atomic.h"
#include "contrib/files.h"
#include "contrib/string.h"
#include "contrib/strtonum.h"
@@ -68,7 +71,7 @@ static struct {
sizeof(((send_ctx_t *)0)->ttl) +
sizeof(((send_ctx_t *)0)->type) +
sizeof(((send_ctx_t *)0)->rdata)];
-} ctl_globals;
+} ctl_globals[CTL_MAX_CONCURRENT + 1];
static bool allow_blocking_while_ctl_txn(zone_event_type_t event)
{
@@ -645,47 +648,49 @@ static int zone_backup_cmd(zone_t *zone, ctl_args_t *args)
return KNOT_EOK;
}
+ int ret = KNOT_EOK;
+ pthread_mutex_lock(&zone->cu_lock);
if (zone->backup_ctx != NULL) {
log_zone_warning(zone->name, "backup or restore already in progress, skipping zone");
ctx->failed = true;
- return KNOT_EPROGRESS;
+ ret = KNOT_EPROGRESS;
}
- if (ctx->restore_mode && zone->control_update != NULL) {
+ if (ctx->restore_mode && zone->control_update != NULL && ret == KNOT_EOK) {
log_zone_warning(zone->name, "restoring backup not possible due to open control transaction");
ctx->failed = true;
- return KNOT_TXN_EEXISTS;
+ ret = KNOT_TXN_EEXISTS;
+ }
+
+ if (ret == KNOT_EOK) {
+ zone->backup_ctx = ctx;
}
+ pthread_mutex_unlock(&zone->cu_lock);
ctx->zone_count++;
- int ret;
- if (!ctx->backup_global) {
+ if (!ctx->backup_global && ret == KNOT_EOK) {
ret = global_backup(ctx, zone_catalog(zone), zone->name);
- if (ret != KNOT_EOK) {
- return ret;
- }
}
- if (ctx->backup_params & BACKUP_PARAM_KEYSONLY) {
+ bool finish = false;
+ if ((ctx->backup_params & BACKUP_PARAM_KEYSONLY) && ret == KNOT_EOK) {
ret = zone_backup_keysonly(ctx, conf(), zone);
- if (ret != KNOT_EOK) {
- return ret;
- }
- if (ctx->restore_mode) {
+ if (ctx->restore_mode && ret == KNOT_EOK) {
ret = zone_keys_load(zone, args);
- if (ret != KNOT_EOK) {
- return ret;
- }
}
if (!(ctx->backup_params & BACKUP_PARAM_EVENT)) {
- return ret;
+ finish = true;
}
}
- zone->backup_ctx = ctx;
+ if (ret != KNOT_EOK || finish) {
+ zone->backup_ctx = NULL;
+ return ret;
+ }
+
pthread_mutex_lock(&ctx->readers_mutex);
ctx->readers++;
pthread_mutex_unlock(&ctx->readers_mutex);
@@ -720,7 +725,7 @@ static int zones_apply_backup(ctl_args_t *args, bool restore_mode)
zone_backup_ctx_t *ctx = latest_backup_ctx(args);
/* QUIC - server key and cert backup. */
- ret = backup_quic(ctx, args->server->quic_active);
+ ret = backup_quic(ctx, args->server->quic_active || args->server->tls_active);
if (ret != KNOT_EOK) {
log_ctl_error("control, QUIC %s error (%s)",
restore_mode ? "restore" : "backup",
@@ -761,6 +766,11 @@ static int zone_sign(zone_t *zone, _unused_ ctl_args_t *args)
return schedule_trigger(zone, args, ZONE_EVENT_DNSSEC, true);
}
+static int zone_validate(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ return schedule_trigger(zone, args, ZONE_EVENT_VALIDATE, true);
+}
+
static int zone_keys_load(zone_t *zone, _unused_ ctl_args_t *args)
{
conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
@@ -811,8 +821,8 @@ static int zone_ksk_sbm_confirm(zone_t *zone, _unused_ ctl_args_t *args)
conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
if (ret == KNOT_EOK && conf_bool(&val)) {
- // NOT zone_events_schedule_user(), intentionally!
- ret = schedule_trigger(zone, args, ZONE_EVENT_DNSSEC, false);
+ // NOT zone_events_schedule_user() or schedule_trigger(), intentionally!
+ zone_events_schedule_now(zone, ZONE_EVENT_DNSSEC);
}
return ret;
@@ -846,17 +856,23 @@ static int zone_xfr_thaw(zone_t *zone, _unused_ ctl_args_t *args)
return KNOT_EOK;
}
-static int zone_txn_begin(zone_t *zone, _unused_ ctl_args_t *args)
+static int zone_txn_begin_l(zone_t *zone, _unused_ ctl_args_t *args)
{
- if (zone->control_update != NULL) {
+ if (zone->control_update != NULL || conf()->io.txn != NULL) {
return KNOT_TXN_EEXISTS;
}
- if (zone->backup_ctx != NULL && zone->backup_ctx->restore_mode) {
+ struct zone_backup_ctx *backup_ctx = zone->backup_ctx;
+ if (backup_ctx != NULL && backup_ctx->restore_mode) {
log_zone_warning(zone->name, "zone restore pending, try opening control transaction later");
return KNOT_EAGAIN;
}
+ if (zone->events.running && zone->events.type >= 0 && zone->events.blocking[zone->events.type] != NULL) {
+ log_zone_warning(zone->name, "some blocking event running, try opening control transaction later");
+ return KNOT_EAGAIN;
+ }
+
zone->control_update = malloc(sizeof(zone_update_t));
if (zone->control_update == NULL) {
return KNOT_ENOMEM;
@@ -872,7 +888,15 @@ static int zone_txn_begin(zone_t *zone, _unused_ ctl_args_t *args)
return ret;
}
-static int zone_txn_commit(zone_t *zone, _unused_ ctl_args_t *args)
+static int zone_txn_begin(zone_t *zone, ctl_args_t *args)
+{
+ pthread_mutex_lock(&zone->cu_lock);
+ int ret = zone_txn_begin_l(zone, args);
+ pthread_mutex_unlock(&zone->cu_lock);
+ return ret;
+}
+
+static int zone_txn_commit_l(zone_t *zone, _unused_ ctl_args_t *args)
{
if (zone->control_update == NULL) {
args->suppress = true;
@@ -934,15 +958,26 @@ static int zone_txn_commit(zone_t *zone, _unused_ ctl_args_t *args)
return KNOT_EOK;
}
+static int zone_txn_commit(zone_t *zone, ctl_args_t *args)
+{
+ pthread_mutex_lock(&zone->cu_lock);
+ int ret = zone_txn_commit_l(zone, args);
+ pthread_mutex_unlock(&zone->cu_lock);
+ return ret;
+}
+
static int zone_txn_abort(zone_t *zone, _unused_ ctl_args_t *args)
{
+ pthread_mutex_lock(&zone->cu_lock);
if (zone->control_update == NULL) {
args->suppress = true;
+ pthread_mutex_unlock(&zone->cu_lock);
return KNOT_TXN_ENOTEXISTS;
}
zone_control_clear(zone);
+ pthread_mutex_unlock(&zone->cu_lock);
return KNOT_EOK;
}
@@ -1089,7 +1124,7 @@ static int get_owner(uint8_t *out, size_t out_len, knot_dname_t *origin,
static int zone_read(zone_t *zone, ctl_args_t *args)
{
- send_ctx_t *ctx = &ctl_globals.send_ctx;
+ send_ctx_t *ctx = &ctl_globals[args->thread_idx].send_ctx;
int ret = init_send_ctx(ctx, zone->name, args);
if (ret != KNOT_EOK) {
return ret;
@@ -1131,7 +1166,7 @@ static int zone_flag_txn_get(zone_t *zone, ctl_args_t *args, const char *flag)
return KNOT_TXN_ENOTEXISTS;
}
- send_ctx_t *ctx = &ctl_globals.send_ctx;
+ send_ctx_t *ctx = &ctl_globals[args->thread_idx].send_ctx;
int ret = init_send_ctx(ctx, zone->name, args);
if (ret != KNOT_EOK) {
return ret;
@@ -1170,7 +1205,10 @@ static int zone_flag_txn_get(zone_t *zone, ctl_args_t *args, const char *flag)
static int zone_txn_get(zone_t *zone, ctl_args_t *args)
{
- return zone_flag_txn_get(zone, args, NULL);
+ pthread_mutex_lock(&zone->cu_lock);
+ int ret = zone_flag_txn_get(zone, args, NULL);
+ pthread_mutex_unlock(&zone->cu_lock);
+ return ret;
}
static int send_changeset_part(changeset_t *ch, send_ctx_t *ctx, bool from)
@@ -1233,7 +1271,7 @@ static int send_changeset(changeset_t *ch, send_ctx_t *ctx)
return send_changeset_part(ch, ctx, false);
}
-static int zone_txn_diff(zone_t *zone, ctl_args_t *args)
+static int zone_txn_diff_l(zone_t *zone, ctl_args_t *args)
{
if (zone->control_update == NULL) {
args->suppress = true;
@@ -1245,7 +1283,7 @@ static int zone_txn_diff(zone_t *zone, ctl_args_t *args)
return zone_flag_txn_get(zone, args, CTL_FLAG_DIFF_ADD);
}
- send_ctx_t *ctx = &ctl_globals.send_ctx;
+ send_ctx_t *ctx = &ctl_globals[args->thread_idx].send_ctx;
int ret = init_send_ctx(ctx, zone->name, args);
if (ret != KNOT_EOK) {
return ret;
@@ -1254,6 +1292,14 @@ static int zone_txn_diff(zone_t *zone, ctl_args_t *args)
return send_changeset(&zone->control_update->change, ctx);
}
+static int zone_txn_diff(zone_t *zone, ctl_args_t *args)
+{
+ pthread_mutex_lock(&zone->cu_lock);
+ int ret = zone_txn_diff_l(zone, args);
+ pthread_mutex_unlock(&zone->cu_lock);
+ return ret;
+}
+
static int get_ttl(zone_t *zone, ctl_args_t *args, uint32_t *ttl)
{
knot_dname_storage_t owner;
@@ -1297,8 +1343,8 @@ static int create_rrset(knot_rrset_t **rrset, zone_t *zone, ctl_args_t *args,
const char *ttl = need_ttl ? args->data[KNOT_CTL_IDX_TTL] : NULL;
// Prepare a buffer for a reconstructed record.
- const size_t buff_len = sizeof(ctl_globals.txt_rr);
- char *buff = ctl_globals.txt_rr;
+ const size_t buff_len = sizeof(ctl_globals[args->thread_idx].txt_rr);
+ char *buff = ctl_globals[args->thread_idx].txt_rr;
// Choose default TTL if none was specified.
uint32_t default_ttl = 0;
@@ -1321,7 +1367,7 @@ static int create_rrset(knot_rrset_t **rrset, zone_t *zone, ctl_args_t *args,
size_t rdata_len = ret;
// Parse the record.
- zs_scanner_t *scanner = &ctl_globals.scanner;
+ zs_scanner_t *scanner = &ctl_globals[args->thread_idx].scanner;
if (zs_init(scanner, origin, KNOT_CLASS_IN, default_ttl) != 0 ||
zs_set_input_string(scanner, buff, rdata_len) != 0 ||
zs_parse_record(scanner) != 0 ||
@@ -1347,7 +1393,7 @@ parser_failed:
return ret;
}
-static int zone_txn_set(zone_t *zone, ctl_args_t *args)
+static int zone_txn_set_l(zone_t *zone, ctl_args_t *args)
{
if (zone->control_update == NULL) {
args->suppress = true;
@@ -1371,7 +1417,15 @@ static int zone_txn_set(zone_t *zone, ctl_args_t *args)
return ret;
}
-static int zone_txn_unset(zone_t *zone, ctl_args_t *args)
+static int zone_txn_set(zone_t *zone, ctl_args_t *args)
+{
+ pthread_mutex_lock(&zone->cu_lock);
+ int ret = zone_txn_set_l(zone, args);
+ pthread_mutex_unlock(&zone->cu_lock);
+ return ret;
+}
+
+static int zone_txn_unset_l(zone_t *zone, ctl_args_t *args)
{
if (zone->control_update == NULL) {
args->suppress = true;
@@ -1421,6 +1475,14 @@ static int zone_txn_unset(zone_t *zone, ctl_args_t *args)
}
}
+static int zone_txn_unset(zone_t *zone, ctl_args_t *args)
+{
+ pthread_mutex_lock(&zone->cu_lock);
+ int ret = zone_txn_unset_l(zone, args);
+ pthread_mutex_unlock(&zone->cu_lock);
+ return ret;
+}
+
static bool zone_exists(const knot_dname_t *zone, void *data)
{
assert(zone);
@@ -1487,7 +1549,7 @@ static int purge_orphan_member_cb(const knot_dname_t *member, const knot_dname_t
orphan->name = (knot_dname_t *)member;
orphan->server = server;
- purge_flag_t params =
+ const purge_flag_t params =
PURGE_ZONE_TIMERS | PURGE_ZONE_JOURNAL | PURGE_ZONE_KASPDB |
PURGE_ZONE_BEST | PURGE_ZONE_LOG;
@@ -1530,7 +1592,7 @@ static int catalog_orphans_sweep(server_t *server)
knot_strerror(ret));
}
} else {
- log_error("can't open catalog for purging (%s)",
+ log_error("can not open catalog for purging (%s)",
knot_strerror(ret));
}
@@ -1683,7 +1745,7 @@ static int zone_purge(zone_t *zone, ctl_args_t *args)
(void)zone_events_schedule_blocking(zone, ZONE_EVENT_EXPIRE, true);
}
- purge_flag_t params =
+ const purge_flag_t params =
MATCH_OR_FILTER(args, CTL_FILTER_PURGE_TIMERS) * PURGE_ZONE_TIMERS |
MATCH_OR_FILTER(args, CTL_FILTER_PURGE_ZONEFILE) * PURGE_ZONE_ZONEFILE |
MATCH_OR_FILTER(args, CTL_FILTER_PURGE_JOURNAL) * PURGE_ZONE_JOURNAL |
@@ -1695,213 +1757,82 @@ static int zone_purge(zone_t *zone, ctl_args_t *args)
return selective_zone_purge(conf(), zone, params);
}
-static int send_stats_ctr(mod_ctr_t *ctr, uint64_t **stats_vals, unsigned threads,
- ctl_args_t *args, knot_ctl_data_t *data)
+int ctl_dump_ctr(stats_dump_params_t *params, stats_dump_ctx_t *ctx)
{
- char index[128];
- char value[32];
-
- if (ctr->count == 1) {
- uint64_t counter = stats_get_counter(stats_vals, ctr->offset, threads);
- int ret = snprintf(value, sizeof(value), "%"PRIu64, counter);
- if (ret <= 0 || ret >= sizeof(value)) {
- return KNOT_ESPACE;
- }
-
- (*data)[KNOT_CTL_IDX_ID] = NULL;
- (*data)[KNOT_CTL_IDX_DATA] = value;
+ ctl_args_t *args = ctx->ctx;
- ret = knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, data);
- if (ret != KNOT_EOK) {
- return ret;
- }
- } else {
- bool force = ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS],
- CTL_FLAG_FORCE);
-
- for (uint32_t i = 0; i < ctr->count; i++) {
- uint64_t counter = stats_get_counter(stats_vals, ctr->offset + i, threads);
-
- // Skip empty counters.
- if (counter == 0 && !force) {
- continue;
- }
-
- int ret;
- if (ctr->idx_to_str) {
- char *str = ctr->idx_to_str(i, ctr->count);
- if (str == NULL) {
- continue;
- }
- ret = snprintf(index, sizeof(index), "%s", str);
- free(str);
- } else {
- ret = snprintf(index, sizeof(index), "%u", i);
- }
- if (ret <= 0 || ret >= sizeof(index)) {
- return KNOT_ESPACE;
- }
-
- ret = snprintf(value, sizeof(value), "%"PRIu64, counter);
- if (ret <= 0 || ret >= sizeof(value)) {
- return KNOT_ESPACE;
- }
-
- (*data)[KNOT_CTL_IDX_ID] = index;
- (*data)[KNOT_CTL_IDX_DATA] = value;
-
- knot_ctl_type_t type = (i == 0) ? KNOT_CTL_TYPE_DATA :
- KNOT_CTL_TYPE_EXTRA;
- ret = knot_ctl_send(args->ctl, type, data);
- if (ret != KNOT_EOK) {
- return ret;
- }
- }
+ if (ctx->item != NULL && strcasecmp(ctx->item, params->item) != 0) {
+ return KNOT_EOK;
}
+ ctx->match = true;
- return KNOT_EOK;
-}
-
-static int modules_stats(list_t *query_modules, ctl_args_t *args, knot_ctl_data_t *data)
-{
- if (query_modules == NULL) {
+ if (params->value == 0 &&
+ !ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS], CTL_FLAG_FORCE)) {
return KNOT_EOK;
}
- const char *section = args->data[KNOT_CTL_IDX_SECTION];
- const char *item = args->data[KNOT_CTL_IDX_ITEM];
-
- bool section_found = (section == NULL) ? true : false;
- bool item_found = (item == NULL) ? true : false;
-
- knotd_mod_t *mod;
- WALK_LIST(mod, *query_modules) {
- // Skip modules without statistics.
- if (mod->stats_count == 0) {
- continue;
- }
-
- // Check for specific module.
- if (section != NULL) {
- if (section_found) {
- break;
- } else if (strcasecmp(mod->id->name + 1, section) == 0) {
- section_found = true;
- } else {
- continue;
- }
- }
-
- (*data)[KNOT_CTL_IDX_SECTION] = mod->id->name + 1;
-
- unsigned threads = knotd_mod_threads(mod);
-
- for (int i = 0; i < mod->stats_count; i++) {
- mod_ctr_t *ctr = mod->stats_info + i;
-
- // Skip empty counter.
- if (ctr->name == NULL) {
- continue;
- }
-
- // Check for specific counter.
- if (item != NULL) {
- if (item_found) {
- break;
- } else if (strcasecmp(ctr->name, item) == 0) {
- item_found = true;
- } else {
- continue;
- }
- }
+ char value[32];
+ int ret = snprintf(value, sizeof(value), "%"PRIu64, params->value);
+ if (ret <= 0 || ret >= sizeof(value)) {
+ return KNOT_ESPACE;
+ }
- (*data)[KNOT_CTL_IDX_ITEM] = ctr->name;
+ knot_ctl_data_t data = {
+ [KNOT_CTL_IDX_SECTION] = params->section,
+ [KNOT_CTL_IDX_ITEM] = params->item,
+ [KNOT_CTL_IDX_ID] = params->id,
+ [KNOT_CTL_IDX_ZONE] = params->zone,
+ [KNOT_CTL_IDX_DATA] = value,
+ };
- // Send the counters.
- int ret = send_stats_ctr(ctr, mod->stats_vals, threads, args, data);
- if (ret != KNOT_EOK) {
- return ret;
- }
- }
- }
+ knot_ctl_type_t type = (params->value_pos == 0) ?
+ KNOT_CTL_TYPE_DATA : KNOT_CTL_TYPE_EXTRA;
- return (section_found && item_found) ? KNOT_EOK : KNOT_ENOENT;
+ return knot_ctl_send(args->ctl, type, &data);
}
static int common_stats(ctl_args_t *args, zone_t *zone)
{
- const char *section = args->data[KNOT_CTL_IDX_SECTION];
- const char *item = args->data[KNOT_CTL_IDX_ITEM];
-
- char value[32];
- knot_ctl_data_t data = {
- [KNOT_CTL_IDX_DATA] = value
+ stats_dump_ctx_t dump_ctx = {
+ .server = args->server,
+ .zone = zone,
+ .section = args->data[KNOT_CTL_IDX_SECTION],
+ .item = args->data[KNOT_CTL_IDX_ITEM],
+ .ctx = args,
};
- knot_dname_txt_storage_t name = "";
- if (zone != NULL) {
- if (knot_dname_to_str(name, zone->name, sizeof(name)) == NULL) {
- return KNOT_EINVAL;
- }
- data[KNOT_CTL_IDX_ZONE] = name;
- }
-
- bool found = (section == NULL) ? true : false;
-
- // Process zone metrics.
- const char *section_name = (zone != NULL) ? "zone" : "server";
- if (section == NULL || strcasecmp(section, section_name) == 0) {
- data[KNOT_CTL_IDX_SECTION] = section_name;
-
- const stats_item_t *items = (zone != NULL) ? zone_contents_stats :
- server_stats;
- for (const stats_item_t *i = items; i->name != NULL; i++) {
- if (item != NULL) {
- if (found) {
- break;
- } else if (strcmp(i->name, item) == 0) {
- found = true;
- } else {
- continue;
- }
- } else {
- found = true;
- }
+#define STATS_CHECK(ret, send) { \
+ if (ret != KNOT_EOK) { \
+ if ((send)) { /* Prevents duplicit zone error logs. */ \
+ send_error(args, knot_strerror(ret)); \
+ } \
+ return ret; \
+ } \
+}
- data[KNOT_CTL_IDX_ITEM] = i->name;
- int ret = snprintf(value, sizeof(value), "%"PRIu64,
- (zone != NULL) ? i->zone_val(zone) :
- i->server_val(args->server));
- if (ret <= 0 || ret >= sizeof(value)) {
- ret = KNOT_ESPACE;
- send_error(args, knot_strerror(ret));
- return ret;
- }
+ if (zone == NULL) {
+ int ret = stats_server(ctl_dump_ctr, &dump_ctx);
+ STATS_CHECK(ret, true);
- ret = knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, &data);
- if (ret != KNOT_EOK) {
- send_error(args, knot_strerror(ret));
- return ret;
- }
- }
- }
+ ret = stats_xdp(ctl_dump_ctr, &dump_ctx);
+ STATS_CHECK(ret, true);
- if (section == NULL || strncasecmp(section, "mod-", strlen("mod-")) == 0) {
- list_t *query_modules = (zone != NULL) ? &zone->query_modules :
- conf()->query_modules;
- int ret = modules_stats(query_modules, args, &data);
- if (ret != KNOT_EOK) {
- send_error(args, knot_strerror(ret));
- return ret;
- }
+ dump_ctx.query_modules = conf()->query_modules;
+ ret = stats_modules(ctl_dump_ctr, &dump_ctx);
+ STATS_CHECK(ret, true);
+ } else {
+ int ret = stats_zone(ctl_dump_ctr, &dump_ctx);
+ STATS_CHECK(ret, false);
- found = true;
+ dump_ctx.query_modules = &zone->query_modules;
+ ret = stats_modules(ctl_dump_ctr, &dump_ctx);
+ STATS_CHECK(ret, false);
}
- if (!found) {
- send_error(args, knot_strerror(KNOT_EINVAL));
- return KNOT_EINVAL;
+ if (!dump_ctx.match) {
+ STATS_CHECK(KNOT_EINVAL, zone == NULL);
}
+#undef STATS_CHECK
return KNOT_EOK;
}
@@ -1932,6 +1863,8 @@ static int ctl_zone(ctl_args_t *args, ctl_cmd_t cmd)
return zones_apply_backup(args, true);
case CTL_ZONE_SIGN:
return zones_apply(args, zone_sign);
+ case CTL_ZONE_VALIDATE:
+ return zones_apply(args, zone_validate);
case CTL_ZONE_KEYS_LOAD:
return zones_apply(args, zone_keys_load);
case CTL_ZONE_KEY_ROLL:
@@ -1976,6 +1909,27 @@ static int ctl_zone(ctl_args_t *args, ctl_cmd_t cmd)
}
}
+static void check_zone_txn(zone_t *zone, const knot_dname_t **exists)
+{
+ if (zone->control_update != NULL) {
+ *exists = zone->name;
+ }
+}
+
+static int check_no_zone_txn(server_t *server, const char *action)
+{
+ const knot_dname_t *zone_txn_exists = NULL;
+ knot_zonedb_foreach(server->zone_db, check_zone_txn, &zone_txn_exists);
+ if (zone_txn_exists != NULL) {
+ knot_dname_txt_storage_t zone_str;
+ knot_dname_to_str(zone_str, zone_txn_exists, sizeof(zone_str));
+ log_warning("%s rejected due to existing transaction for zone %s",
+ action, zone_str);
+ return KNOT_TXN_EEXISTS;
+ }
+ return KNOT_EOK;
+}
+
static int server_status(ctl_args_t *args)
{
const char *type = args->data[KNOT_CTL_IDX_TYPE];
@@ -1998,7 +1952,7 @@ static int server_status(ctl_args_t *args)
conf()->cache.srv_xdp_threads, conf()->cache.srv_bg_threads,
running_bkg_wrk, wrk_queue);
} else if (strcasecmp(type, "configure") == 0) {
- ret = snprintf(buff, sizeof(buff), "%s", CONFIGURE_SUMMARY);
+ ret = snprintf(buff, sizeof(buff), "%s", configure_summary);
} else if (strcasecmp(type, "cert-key") == 0) {
uint8_t pin[128];
size_t pin_len = server_cert_pin(args->server, pin, sizeof(pin));
@@ -2034,7 +1988,10 @@ static int ctl_server(ctl_args_t *args, ctl_cmd_t cmd)
ret = KNOT_CTL_ESTOP;
break;
case CTL_RELOAD:
- ret = server_reload(args->server, RELOAD_FULL);
+ ret = check_no_zone_txn(args->server, "server reload");
+ if (ret == KNOT_EOK) {
+ ret = server_reload(args->server, RELOAD_FULL);
+ }
if (ret != KNOT_EOK) {
send_error(args, knot_strerror(ret));
}
@@ -2171,7 +2128,10 @@ static int ctl_conf_txn(ctl_args_t *args, ctl_cmd_t cmd)
switch (cmd) {
case CTL_CONF_BEGIN:
- ret = conf_io_begin(false);
+ ret = check_no_zone_txn(args->server, "config, transaction");
+ if (ret == KNOT_EOK) {
+ ret = conf_io_begin(false);
+ }
break;
case CTL_CONF_ABORT:
conf_io_abort(false);
@@ -2368,56 +2328,64 @@ static int ctl_conf_modify(ctl_args_t *args, ctl_cmd_t cmd)
return ret;
}
+typedef enum {
+ CTL_LOCK_NONE = 0x00,
+ CTL_LOCK_SRV_R = 0x01, // Can run in parallel with other R commands.
+ CTL_LOCK_SRV_W = 0x02, // Cannot run in parallel with other commands.
+} ctl_lock_flag_t;
+
typedef struct {
const char *name;
int (*fcn)(ctl_args_t *, ctl_cmd_t);
+ ctl_lock_flag_t locks;
} desc_t;
static const desc_t cmd_table[] = {
[CTL_NONE] = { "" },
- [CTL_STATUS] = { "status", ctl_server },
- [CTL_STOP] = { "stop", ctl_server },
- [CTL_RELOAD] = { "reload", ctl_server },
- [CTL_STATS] = { "stats", ctl_stats },
-
- [CTL_ZONE_STATUS] = { "zone-status", ctl_zone },
- [CTL_ZONE_RELOAD] = { "zone-reload", ctl_zone },
- [CTL_ZONE_REFRESH] = { "zone-refresh", ctl_zone },
- [CTL_ZONE_RETRANSFER] = { "zone-retransfer", ctl_zone },
- [CTL_ZONE_NOTIFY] = { "zone-notify", ctl_zone },
- [CTL_ZONE_FLUSH] = { "zone-flush", ctl_zone },
- [CTL_ZONE_BACKUP] = { "zone-backup", ctl_zone },
- [CTL_ZONE_RESTORE] = { "zone-restore", ctl_zone },
- [CTL_ZONE_SIGN] = { "zone-sign", ctl_zone },
- [CTL_ZONE_KEYS_LOAD] = { "zone-keys-load", ctl_zone },
- [CTL_ZONE_KEY_ROLL] = { "zone-key-rollover", ctl_zone },
- [CTL_ZONE_KSK_SBM] = { "zone-ksk-submitted", ctl_zone },
- [CTL_ZONE_FREEZE] = { "zone-freeze", ctl_zone },
- [CTL_ZONE_THAW] = { "zone-thaw", ctl_zone },
- [CTL_ZONE_XFR_FREEZE] = { "zone-xfr-freeze", ctl_zone },
- [CTL_ZONE_XFR_THAW] = { "zone-xfr-thaw", ctl_zone },
-
- [CTL_ZONE_READ] = { "zone-read", ctl_zone },
- [CTL_ZONE_BEGIN] = { "zone-begin", ctl_zone },
- [CTL_ZONE_COMMIT] = { "zone-commit", ctl_zone },
- [CTL_ZONE_ABORT] = { "zone-abort", ctl_zone },
- [CTL_ZONE_DIFF] = { "zone-diff", ctl_zone },
- [CTL_ZONE_GET] = { "zone-get", ctl_zone },
- [CTL_ZONE_SET] = { "zone-set", ctl_zone },
- [CTL_ZONE_UNSET] = { "zone-unset", ctl_zone },
- [CTL_ZONE_PURGE] = { "zone-purge", ctl_zone },
- [CTL_ZONE_STATS] = { "zone-stats", ctl_zone },
-
- [CTL_CONF_LIST] = { "conf-list", ctl_conf_list },
- [CTL_CONF_READ] = { "conf-read", ctl_conf_read },
- [CTL_CONF_BEGIN] = { "conf-begin", ctl_conf_txn },
- [CTL_CONF_COMMIT] = { "conf-commit", ctl_conf_txn },
- [CTL_CONF_ABORT] = { "conf-abort", ctl_conf_txn },
- [CTL_CONF_DIFF] = { "conf-diff", ctl_conf_read },
- [CTL_CONF_GET] = { "conf-get", ctl_conf_read },
- [CTL_CONF_SET] = { "conf-set", ctl_conf_modify },
- [CTL_CONF_UNSET] = { "conf-unset", ctl_conf_modify },
+ [CTL_STATUS] = { "status", ctl_server, CTL_LOCK_SRV_R },
+ [CTL_STOP] = { "stop", ctl_server, CTL_LOCK_SRV_R },
+ [CTL_RELOAD] = { "reload", ctl_server, CTL_LOCK_SRV_W },
+ [CTL_STATS] = { "stats", ctl_stats, CTL_LOCK_SRV_R },
+
+ [CTL_ZONE_STATUS] = { "zone-status", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_RELOAD] = { "zone-reload", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_REFRESH] = { "zone-refresh", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_RETRANSFER] = { "zone-retransfer", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_NOTIFY] = { "zone-notify", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_FLUSH] = { "zone-flush", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_BACKUP] = { "zone-backup", ctl_zone, CTL_LOCK_SRV_W }, // Backup and restore must be exclusive as the global backup ctx is accessed.
+ [CTL_ZONE_RESTORE] = { "zone-restore", ctl_zone, CTL_LOCK_SRV_W },
+ [CTL_ZONE_SIGN] = { "zone-sign", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_VALIDATE] = { "zone-validate", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_KEYS_LOAD] = { "zone-keys-load", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_KEY_ROLL] = { "zone-key-rollover", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_KSK_SBM] = { "zone-ksk-submitted", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_FREEZE] = { "zone-freeze", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_THAW] = { "zone-thaw", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_XFR_FREEZE] = { "zone-xfr-freeze", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_XFR_THAW] = { "zone-xfr-thaw", ctl_zone, CTL_LOCK_SRV_R },
+
+ [CTL_ZONE_READ] = { "zone-read", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_BEGIN] = { "zone-begin", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_COMMIT] = { "zone-commit", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_ABORT] = { "zone-abort", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_DIFF] = { "zone-diff", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_GET] = { "zone-get", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_SET] = { "zone-set", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_UNSET] = { "zone-unset", ctl_zone, CTL_LOCK_SRV_R },
+ [CTL_ZONE_PURGE] = { "zone-purge", ctl_zone, CTL_LOCK_SRV_W },
+ [CTL_ZONE_STATS] = { "zone-stats", ctl_zone, CTL_LOCK_SRV_R },
+
+ [CTL_CONF_LIST] = { "conf-list", ctl_conf_list, CTL_LOCK_SRV_R }, // Can either read live conf or conf txn. The latter would deserve CTL_LOCK_SRV_W, but when conf txn exists, all cmds are done by single thread anyway.
+ [CTL_CONF_READ] = { "conf-read", ctl_conf_read, CTL_LOCK_SRV_R },
+ [CTL_CONF_BEGIN] = { "conf-begin", ctl_conf_txn, CTL_LOCK_SRV_W }, // It's locked only during conf-begin, not for the whole duration of the transaction.
+ [CTL_CONF_COMMIT] = { "conf-commit", ctl_conf_txn, CTL_LOCK_SRV_W },
+ [CTL_CONF_ABORT] = { "conf-abort", ctl_conf_txn, CTL_LOCK_SRV_W },
+ [CTL_CONF_DIFF] = { "conf-diff", ctl_conf_read, CTL_LOCK_SRV_W },
+ [CTL_CONF_GET] = { "conf-get", ctl_conf_read, CTL_LOCK_SRV_W },
+ [CTL_CONF_SET] = { "conf-set", ctl_conf_modify, CTL_LOCK_SRV_W },
+ [CTL_CONF_UNSET] = { "conf-unset", ctl_conf_modify, CTL_LOCK_SRV_W },
};
#define MAX_CTL_CODE (sizeof(cmd_table) / sizeof(desc_t) - 1)
@@ -2446,13 +2414,52 @@ ctl_cmd_t ctl_str_to_cmd(const char *cmd_str)
return CTL_NONE;
}
+static int ctl_lock(server_t *server, ctl_lock_flag_t flags, uint64_t timeout_ms)
+{
+ struct timespec ts;
+ int ret = clock_gettime(CLOCK_REALTIME, &ts);
+ if (ret != 0) {
+ return KNOT_ERROR;
+ }
+ ts.tv_sec += timeout_ms / 1000;
+ ts.tv_nsec += (timeout_ms % 1000) * 1000000LU;
+
+ if ((flags & CTL_LOCK_SRV_W)) {
+ assert(!(flags & CTL_LOCK_SRV_R));
+#if !defined(__APPLE__)
+ ret = pthread_rwlock_timedwrlock(&server->ctl_lock, &ts);
+#else
+ ret = pthread_rwlock_wrlock(&server->ctl_lock);
+#endif
+ }
+ if ((flags & CTL_LOCK_SRV_R)) {
+#if !defined(__APPLE__)
+ ret = pthread_rwlock_timedrdlock(&server->ctl_lock, &ts);
+#else
+ ret = pthread_rwlock_rdlock(&server->ctl_lock);
+#endif
+ }
+ return (ret != 0 ? KNOT_EBUSY : KNOT_EOK);
+}
+
+static void ctl_unlock(server_t *server)
+{
+ pthread_rwlock_unlock(&server->ctl_lock);
+}
+
int ctl_exec(ctl_cmd_t cmd, ctl_args_t *args)
{
if (args == NULL) {
return KNOT_EINVAL;
}
- return cmd_table[cmd].fcn(args, cmd);
+ int ret = ctl_lock(args->server, cmd_table[cmd].locks, conf()->cache.ctl_timeout);
+ if (ret == KNOT_EOK) {
+ ret = cmd_table[cmd].fcn(args, cmd);
+ ctl_unlock(args->server);
+ }
+
+ return ret;
}
bool ctl_has_flag(const char *flags, const char *flag)
diff --git a/src/knot/ctl/commands.h b/src/knot/ctl/commands.h
index ed7e75c..b0f33d8 100644
--- a/src/knot/ctl/commands.h
+++ b/src/knot/ctl/commands.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -87,6 +87,7 @@ typedef enum {
CTL_ZONE_BACKUP,
CTL_ZONE_RESTORE,
CTL_ZONE_SIGN,
+ CTL_ZONE_VALIDATE,
CTL_ZONE_KEYS_LOAD,
CTL_ZONE_KEY_ROLL,
CTL_ZONE_KSK_SBM,
@@ -124,6 +125,7 @@ typedef struct {
knot_ctl_data_t data;
server_t *server;
bool suppress; // Suppress error reporting in the "all zones" ctl commands.
+ unsigned thread_idx;
} ctl_args_t;
/*!
diff --git a/src/knot/ctl/process.c b/src/knot/ctl/process.c
index 50fde21..9e6e0df 100644
--- a/src/knot/ctl/process.c
+++ b/src/knot/ctl/process.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@
#include "libknot/error.h"
#include "contrib/string.h"
-int ctl_process(knot_ctl_t *ctl, server_t *server)
+int ctl_process(knot_ctl_t *ctl, server_t *server, int thread_idx, bool *exclusive)
{
if (ctl == NULL || server == NULL) {
return KNOT_EINVAL;
@@ -29,7 +29,8 @@ int ctl_process(knot_ctl_t *ctl, server_t *server)
ctl_args_t args = {
.ctl = ctl,
.type = KNOT_CTL_TYPE_END,
- .server = server
+ .server = server,
+ .thread_idx = thread_idx,
};
// Strip redundant/unprocessed data units in the current block.
@@ -90,11 +91,21 @@ int ctl_process(knot_ctl_t *ctl, server_t *server)
continue;
}
+ if ((cmd == CTL_CONF_COMMIT || cmd == CTL_CONF_ABORT) && !*exclusive) {
+ log_ctl_warning("control, invalid reception of '%s'", cmd_name);
+ continue;
+ }
+
// Execute the command.
int cmd_ret = ctl_exec(cmd, &args);
switch (cmd_ret) {
case KNOT_EOK:
strip = false;
+ if (cmd == CTL_CONF_BEGIN) {
+ *exclusive = true;
+ } else if (cmd == CTL_CONF_COMMIT || cmd == CTL_CONF_ABORT) {
+ *exclusive = false;
+ }
case KNOT_CTL_ESTOP:
case KNOT_CTL_EZONE:
// KNOT_CTL_EZONE - don't change strip, but don't be reported
diff --git a/src/knot/ctl/process.h b/src/knot/ctl/process.h
index ab0f75f..2ae6ea0 100644
--- a/src/knot/ctl/process.h
+++ b/src/knot/ctl/process.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,12 +19,16 @@
#include "libknot/libknot.h"
#include "knot/server/server.h"
+#define CTL_MAX_CONCURRENT 8 // Number of CTL threads EXCLUDING the main thread which can also process CTL.
+
/*!
* Processes incoming control commands.
*
- * \param[in] ctl Control context.
- * \param[in] server Server instance.
+ * \param[in] ctl Control context.
+ * \param[in] server Server instance.
+ * \param[in] thread_idx Index of a thread which performs the operation.
+ * \param[out] exclusive All following CTLs shall (not) be processed exclusively by this thread.
*
* \return Error code, KNOT_EOK if successful.
*/
-int ctl_process(knot_ctl_t *ctl, server_t *server);
+int ctl_process(knot_ctl_t *ctl, server_t *server, int thread_idx, bool *exclusive);
diff --git a/src/knot/dnssec/context.c b/src/knot/dnssec/context.c
index 1a19f68..24b41f7 100644
--- a/src/knot/dnssec/context.c
+++ b/src/knot/dnssec/context.c
@@ -213,11 +213,13 @@ int kdnssec_ctx_init(conf_t *conf, kdnssec_ctx_t *ctx, const knot_dname_t *zone_
goto init_error;
}
- ctx->policy = calloc(1, sizeof(*ctx->policy));
+ ctx->policy = calloc(1, sizeof(*ctx->policy) + sizeof(*ctx->stats));
if (ctx->policy == NULL) {
ret = KNOT_ENOMEM;
goto init_error;
}
+ ctx->stats = (void *)ctx->policy + sizeof(*ctx->policy);
+ knot_spin_init(&ctx->stats->lock);
ret = kasp_db_get_saved_ttls(ctx->kasp_db, zone_name,
&ctx->policy->saved_max_ttl,
@@ -287,6 +289,7 @@ void kdnssec_ctx_deinit(kdnssec_ctx_t *ctx)
}
if (ctx->policy != NULL) {
+ knot_spin_destroy(&ctx->stats->lock);
free(ctx->policy->string);
knot_dynarray_foreach(parent, knot_kasp_parent_t, i, ctx->policy->parents) {
free(i->addr);
@@ -331,18 +334,22 @@ int kdnssec_validation_ctx(conf_t *conf, kdnssec_ctx_t *ctx, const zone_contents
return KNOT_ENOMEM;
}
- ctx->policy = calloc(1, sizeof(*ctx->policy));
+ ctx->policy = calloc(1, sizeof(*ctx->policy) + sizeof(*ctx->stats));
if (ctx->policy == NULL) {
free(ctx->zone);
return KNOT_ENOMEM;
}
+ ctx->stats = (void *)ctx->policy + sizeof(*ctx->policy);
+ knot_spin_init(&ctx->stats->lock);
policy_from_zone(ctx->policy, zone);
if (conf != NULL) {
conf_val_t policy_id = conf_zone_get(conf, C_DNSSEC_POLICY, zone->apex->owner);
conf_id_fix_default(&policy_id);
- conf_val_t num_threads = conf_id_get(conf, C_POLICY, C_SIGNING_THREADS, &policy_id);
- ctx->policy->signing_threads = conf_int(&num_threads);
+ conf_val_t val = conf_id_get(conf, C_POLICY, C_SIGNING_THREADS, &policy_id);
+ ctx->policy->signing_threads = conf_int(&val);
+ val = conf_id_get(conf, C_POLICY, C_RRSIG_REFRESH, &policy_id);
+ ctx->policy->rrsig_refresh_before = conf_int_alt(&val, true);
} else {
ctx->policy->signing_threads = MAX(dt_optimal_size(), 1);
}
diff --git a/src/knot/dnssec/context.h b/src/knot/dnssec/context.h
index 756bc56..2d126db 100644
--- a/src/knot/dnssec/context.h
+++ b/src/knot/dnssec/context.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,14 +16,20 @@
#pragma once
-#include <time.h>
-
#include "libdnssec/keystore.h"
-
+#include "contrib/spinlock.h"
+#include "contrib/time.h"
#include "knot/conf/conf.h"
#include "knot/dnssec/kasp/kasp_zone.h"
#include "knot/dnssec/kasp/policy.h"
+typedef struct {
+ size_t rrsig_count;
+ knot_time_t expire;
+
+ knot_spin_t lock;
+} zone_sign_stats_t;
+
/*!
* \brief DNSSEC signing context.
*/
@@ -38,6 +44,8 @@ typedef struct {
char *kasp_zone_path;
+ zone_sign_stats_t *stats;
+
bool rrsig_drop_existing;
bool keep_deleted_keys;
bool keytag_conflict;
diff --git a/src/knot/dnssec/ds_query.c b/src/knot/dnssec/ds_query.c
index 2ac91cc..0b85247 100644
--- a/src/knot/dnssec/ds_query.c
+++ b/src/knot/dnssec/ds_query.c
@@ -28,8 +28,8 @@
#define DS_CHECK_LOG(priority, zone, remote, flags, fmt, ...) \
ns_log(priority, zone, LOG_OPERATION_DS_CHECK, LOG_DIRECTION_OUT, &(remote)->addr, \
- ((flags) & KNOT_REQUESTOR_QUIC) ? KNOTD_QUERY_PROTO_QUIC : KNOTD_QUERY_PROTO_TCP, \
- ((flags) & KNOT_REQUESTOR_REUSED), (remote)->key.name, fmt, ## __VA_ARGS__)
+ flags2proto(flags), ((flags) & KNOT_REQUESTOR_REUSED), (remote)->key.name, \
+ fmt, ## __VA_ARGS__)
static bool match_key_ds(knot_kasp_key_t *key, knot_rdata_t *ds)
{
diff --git a/src/knot/dnssec/key-events.c b/src/knot/dnssec/key-events.c
index 58d7ec0..db762b6 100644
--- a/src/knot/dnssec/key-events.c
+++ b/src/knot/dnssec/key-events.c
@@ -17,8 +17,8 @@
#include <assert.h>
#include "contrib/macros.h"
+#include "knot/common/dbus.h"
#include "knot/common/log.h"
-#include "knot/common/systemd.h"
#include "knot/dnssec/kasp/keystate.h"
#include "knot/dnssec/key-events.h"
#include "knot/dnssec/policy.h"
@@ -881,7 +881,7 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_roll_flags_t flags,
if (ret == KNOT_EOK && reschedule->keys_changed) {
ret = kdnssec_ctx_commit(ctx);
if (ret == KNOT_EOK && (ctx->dbus_event & DBUS_EVENT_KEYS_UPDATED)) {
- systemd_emit_keys_updated(ctx->zone->dname);
+ dbus_emit_keys_updated(ctx->zone->dname);
}
}
@@ -891,7 +891,7 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_roll_flags_t flags,
log_fmt_zone(LOG_NOTICE, LOG_SOURCE_ZONE, ctx->zone->dname, param,
"DNSSEC, KSK submission, waiting for confirmation");
if (ctx->dbus_event & DBUS_EVENT_ZONE_SUBMISSION) {
- systemd_emit_zone_submission(ctx->zone->dname, ready_keytag, ready_keyid);
+ dbus_emit_zone_submission(ctx->zone->dname, ready_keytag, ready_keyid);
}
}
diff --git a/src/knot/dnssec/key_records.c b/src/knot/dnssec/key_records.c
index 9b22f7a..366ab4a 100644
--- a/src/knot/dnssec/key_records.c
+++ b/src/knot/dnssec/key_records.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -189,7 +189,7 @@ int key_records_dump(char **buf, size_t *buf_size, const key_records_t *r, bool
return ret >= 0 ? KNOT_EOK : ret;
}
-int key_records_sign(const zone_key_t *key, key_records_t *r, const kdnssec_ctx_t *kctx, knot_time_t *expires)
+int key_records_sign(const zone_key_t *key, key_records_t *r, const kdnssec_ctx_t *kctx)
{
dnssec_sign_ctx_t *sign_ctx;
int ret = dnssec_sign_new(&sign_ctx, key->key);
@@ -198,20 +198,20 @@ int key_records_sign(const zone_key_t *key, key_records_t *r, const kdnssec_ctx_
}
if (!knot_rrset_empty(&r->dnskey) && knot_zone_sign_use_key(key, &r->dnskey)) {
- ret = knot_sign_rrset(&r->rrsig, &r->dnskey, key->key, sign_ctx, kctx, NULL, expires);
+ ret = knot_sign_rrset(&r->rrsig, &r->dnskey, key->key, sign_ctx, kctx, NULL);
}
if (ret == KNOT_EOK && !knot_rrset_empty(&r->cdnskey) && knot_zone_sign_use_key(key, &r->cdnskey)) {
- ret = knot_sign_rrset(&r->rrsig, &r->cdnskey, key->key, sign_ctx, kctx, NULL, expires);
+ ret = knot_sign_rrset(&r->rrsig, &r->cdnskey, key->key, sign_ctx, kctx, NULL);
}
if (ret == KNOT_EOK && !knot_rrset_empty(&r->cds) && knot_zone_sign_use_key(key, &r->cds)) {
- ret = knot_sign_rrset(&r->rrsig, &r->cds, key->key, sign_ctx, kctx, NULL, expires);
+ ret = knot_sign_rrset(&r->rrsig, &r->cds, key->key, sign_ctx, kctx, NULL);
}
dnssec_sign_free(sign_ctx);
return ret;
}
-int key_records_verify(key_records_t *r, kdnssec_ctx_t *kctx, knot_time_t timestamp)
+int key_records_verify(key_records_t *r, kdnssec_ctx_t *kctx, knot_time_t timestamp, knot_time_t min_valid)
{
kctx->now = timestamp;
int ret = kasp_zone_keys_from_rr(kctx->zone, &r->dnskey.rrs, false, &kctx->keytag_conflict);
@@ -224,12 +224,17 @@ int key_records_verify(key_records_t *r, kdnssec_ctx_t *kctx, knot_time_t timest
return KNOT_ENOMEM;
}
- ret = knot_validate_rrsigs(&r->dnskey, &r->rrsig, sign_ctx, false);
+ knot_time_t until = 0;
+ ret = knot_validate_rrsigs(&r->dnskey, &r->rrsig, sign_ctx, false, &until);
if (ret == KNOT_EOK && !knot_rrset_empty(&r->cdnskey)) {
- ret = knot_validate_rrsigs(&r->cdnskey, &r->rrsig, sign_ctx, false);
+ ret = knot_validate_rrsigs(&r->cdnskey, &r->rrsig, sign_ctx, false, &until);
}
if (ret == KNOT_EOK && !knot_rrset_empty(&r->cds)) {
- ret = knot_validate_rrsigs(&r->cds, &r->rrsig, sign_ctx, false);
+ ret = knot_validate_rrsigs(&r->cds, &r->rrsig, sign_ctx, false, &until);
+ }
+
+ if (ret == KNOT_EOK && knot_time_lt(until, min_valid)) {
+ ret = KNOT_ESOON_EXPIRE;
}
zone_sign_ctx_free(sign_ctx);
diff --git a/src/knot/dnssec/key_records.h b/src/knot/dnssec/key_records.h
index b53ed86..dd28b4f 100644
--- a/src/knot/dnssec/key_records.h
+++ b/src/knot/dnssec/key_records.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,10 +39,10 @@ int key_records_intersect(key_records_t *r, const key_records_t *against);
int key_records_dump(char **buf, size_t *buf_size, const key_records_t *r, bool verbose);
-int key_records_sign(const zone_key_t *key, key_records_t *r, const kdnssec_ctx_t *kctx, knot_time_t *expires);
+int key_records_sign(const zone_key_t *key, key_records_t *r, const kdnssec_ctx_t *kctx);
// WARNING this modifies 'kctx' with updated timestamp and with zone_keys from r->dnskey
-int key_records_verify(key_records_t *r, kdnssec_ctx_t *kctx, knot_time_t timestamp);
+int key_records_verify(key_records_t *r, kdnssec_ctx_t *kctx, knot_time_t timestamp, knot_time_t min_valid);
size_t key_records_serialized_size(const key_records_t *r);
diff --git a/src/knot/dnssec/rrset-sign.c b/src/knot/dnssec/rrset-sign.c
index 4c9e904..1dc9edc 100644
--- a/src/knot/dnssec/rrset-sign.c
+++ b/src/knot/dnssec/rrset-sign.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -157,13 +157,13 @@ static int sign_ctx_add_self(dnssec_sign_ctx_t *ctx, const uint8_t *rdata)
*/
static int sign_ctx_add_records(dnssec_sign_ctx_t *ctx, const knot_rrset_t *covered)
{
- size_t rrwl = knot_rrset_size(covered);
+ size_t rrwl = knot_rrset_size_estimate(covered);
uint8_t *rrwf = malloc(rrwl);
if (!rrwf) {
return KNOT_ENOMEM;
}
- int written = knot_rrset_to_wire_extra(covered, rrwf, rrwl, 0, NULL, KNOT_PF_BUFENOUGH);
+ int written = knot_rrset_to_wire_extra(covered, rrwf, rrwl, 0, NULL, 0);
if (written < 0) {
free(rrwf);
return written;
@@ -263,7 +263,7 @@ static int rrsigs_create_rdata(knot_rrset_t *rrsigs, dnssec_sign_ctx_t *ctx,
int knot_sign_rrset(knot_rrset_t *rrsigs, const knot_rrset_t *covered,
const dnssec_key_t *key, dnssec_sign_ctx_t *sign_ctx,
- const kdnssec_ctx_t *dnssec_ctx, knot_mm_t *mm, knot_time_t *expires)
+ const kdnssec_ctx_t *dnssec_ctx, knot_mm_t *mm)
{
if (knot_rrset_empty(covered) || !key || !sign_ctx || !dnssec_ctx ||
rrsigs->type != KNOT_RRTYPE_RRSIG ||
@@ -279,8 +279,11 @@ int knot_sign_rrset(knot_rrset_t *rrsigs, const knot_rrset_t *covered,
int ret = rrsigs_create_rdata(rrsigs, sign_ctx, covered, key, (uint32_t)sig_incept,
(uint32_t)sig_expire, sign_flags, mm);
- if (ret == KNOT_EOK && expires != NULL) {
- *expires = knot_time_min(*expires, sig_expire);
+ if (ret == KNOT_EOK) {
+ knot_spin_lock(&dnssec_ctx->stats->lock);
+ dnssec_ctx->stats->rrsig_count++;
+ dnssec_ctx->stats->expire = knot_time_min(dnssec_ctx->stats->expire, sig_expire);
+ knot_spin_unlock(&dnssec_ctx->stats->lock);
}
return ret;
}
@@ -300,7 +303,7 @@ int knot_sign_rrset2(knot_rrset_t *rrsigs, const knot_rrset_t *rrset,
}
int ret = knot_sign_rrset(rrsigs, rrset, key->key, sign_ctx->sign_ctxs[i],
- sign_ctx->dnssec_ctx, mm, NULL);
+ sign_ctx->dnssec_ctx, mm);
if (ret != KNOT_EOK) {
return ret;
}
diff --git a/src/knot/dnssec/rrset-sign.h b/src/knot/dnssec/rrset-sign.h
index 8e00402..ba0f027 100644
--- a/src/knot/dnssec/rrset-sign.h
+++ b/src/knot/dnssec/rrset-sign.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -31,7 +31,6 @@
* \param sign_ctx Signing context.
* \param dnssec_ctx DNSSEC context.
* \param mm Memory context.
- * \param expires Out: When will the new RRSIG expire.
*
* \return Error code, KNOT_EOK if successful.
*/
@@ -40,8 +39,7 @@ int knot_sign_rrset(knot_rrset_t *rrsigs,
const dnssec_key_t *key,
dnssec_sign_ctx_t *sign_ctx,
const kdnssec_ctx_t *dnssec_ctx,
- knot_mm_t *mm,
- knot_time_t *expires);
+ knot_mm_t *mm);
/*!
* \brief Create RRSIG RR for given RR set, choose which key to use.
diff --git a/src/knot/dnssec/zone-events.c b/src/knot/dnssec/zone-events.c
index faf093d..2ca35ac 100644
--- a/src/knot/dnssec/zone-events.c
+++ b/src/knot/dnssec/zone-events.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
#include "libdnssec/random.h"
#include "libknot/libknot.h"
#include "knot/conf/conf.h"
+#include "knot/common/dbus.h"
#include "knot/common/log.h"
#include "knot/dnssec/key-events.h"
#include "knot/dnssec/key_records.h"
@@ -157,7 +158,6 @@ int knot_dnssec_zone_sign(zone_update_t *update,
const knot_dname_t *zone_name = update->new_cont->apex->owner;
kdnssec_ctx_t ctx = { 0 };
zone_keyset_t keyset = { 0 };
- knot_time_t zone_expire = 0;
int result = kdnssec_ctx_init(conf, &ctx, zone_name, zone_kaspdb(update->zone), NULL);
if (result != KNOT_EOK) {
@@ -259,7 +259,7 @@ int knot_dnssec_zone_sign(zone_update_t *update,
goto done;
}
- result = knot_zone_sign(update, &keyset, &ctx, &zone_expire);
+ result = knot_zone_sign(update, &keyset, &ctx);
if (result != KNOT_EOK) {
log_zone_error(zone_name, "DNSSEC, failed to sign zone content (%s)",
knot_strerror(result));
@@ -302,13 +302,14 @@ int knot_dnssec_zone_sign(zone_update_t *update,
}
}
- log_zone_info(zone_name, "DNSSEC, successfully signed, serial %u",
- zone_contents_serial(update->new_cont));
+ log_zone_info(zone_name, "DNSSEC, successfully signed, serial %u, new RRSIGs %zu",
+ zone_contents_serial(update->new_cont), ctx.stats->rrsig_count);
done:
if (result == KNOT_EOK) {
- reschedule->next_sign = schedule_next(&ctx, &keyset, ctx.offline_next_time, zone_expire);
+ reschedule->next_sign = schedule_next(&ctx, &keyset, ctx.offline_next_time, ctx.stats->expire);
reschedule->plan_dnskey_sync = ctx.policy->has_dnskey_sync;
+ update->new_cont->dnssec_expire = ctx.stats->expire;
} else {
reschedule->next_sign = knot_dnssec_failover_delay(&ctx);
reschedule->next_rollover = 0;
@@ -329,7 +330,6 @@ int knot_dnssec_sign_update(zone_update_t *update, conf_t *conf)
const knot_dname_t *zone_name = update->new_cont->apex->owner;
kdnssec_ctx_t ctx = { 0 };
zone_keyset_t keyset = { 0 };
- knot_time_t zone_expire = 0;
int result = kdnssec_ctx_init(conf, &ctx, zone_name, zone_kaspdb(update->zone), NULL);
if (result != KNOT_EOK) {
@@ -381,7 +381,7 @@ int knot_dnssec_sign_update(zone_update_t *update, conf_t *conf)
goto done;
}
- result = knot_zone_sign_update(update, &keyset, &ctx, &zone_expire);
+ result = knot_zone_sign_update(update, &keyset, &ctx);
if (result != KNOT_EOK) {
log_zone_error(zone_name, "DNSSEC, failed to sign changeset (%s)",
knot_strerror(result));
@@ -434,17 +434,18 @@ int knot_dnssec_sign_update(zone_update_t *update, conf_t *conf)
}
}
- log_zone_info(zone_name, "DNSSEC, incrementally signed, serial %u",
- zone_contents_serial(update->new_cont));
+ log_zone_info(zone_name, "DNSSEC, incrementally signed, serial %u, new RRSIGs %zu",
+ zone_contents_serial(update->new_cont), ctx.stats->rrsig_count);
done:
if (result == KNOT_EOK) {
- knot_time_t next = knot_time_min(ctx.offline_next_time, zone_expire);
+ knot_time_t next = knot_time_min(ctx.offline_next_time, ctx.stats->expire);
// NOTE: this is usually NOOP since signing planned earlier
zone_events_schedule_at(update->zone, ZONE_EVENT_DNSSEC, (time_t)(next ? next : -1));
if (ctx.policy->has_dnskey_sync) {
zone_events_schedule_now(update->zone, ZONE_EVENT_DNSKEY_SYNC);
}
+ update->new_cont->dnssec_expire = knot_time_min(update->zone->contents->dnssec_expire, ctx.stats->expire);
}
free_zone_keys(&keyset);
@@ -462,25 +463,83 @@ knot_time_t knot_dnssec_failover_delay(const kdnssec_ctx_t *ctx)
}
}
-int knot_dnssec_validate_zone(zone_update_t *update, conf_t *conf, knot_time_t now, bool incremental)
+static void log_validation_error(zone_update_t *update, const char *msg_valid,
+ int ret, bool warning)
+{
+ unsigned level = warning ? LOG_WARNING : LOG_ERR;
+
+ log_fmt_zone(level, LOG_SOURCE_ZONE, update->zone->name, NULL,
+ "DNSSEC, %svalidation failed (%s)", msg_valid, knot_strerror(ret));
+
+ char type_str[16];
+ knot_dname_txt_storage_t name_str;
+ if (knot_dname_to_str(name_str, update->validation_hint.node, sizeof(name_str)) != NULL &&
+ knot_rrtype_to_string(update->validation_hint.rrtype, type_str, sizeof(type_str)) >= 0) {
+ log_fmt_zone(level, LOG_SOURCE_ZONE, update->zone->name, NULL,
+ "DNSSEC, validation hint: %s %s", name_str, type_str);
+ }
+}
+
+int knot_dnssec_validate_zone(zone_update_t *update, conf_t *conf,
+ knot_time_t now, bool incremental, bool log_plan)
{
kdnssec_ctx_t ctx = { 0 };
int ret = kdnssec_validation_ctx(conf, &ctx, update->new_cont);
+ if (ret != KNOT_EOK) {
+ goto end;
+ }
if (now != 0) {
ctx.now = now;
}
+
+ ret = knot_zone_check_nsec_chain(update, &ctx, incremental);
if (ret == KNOT_EOK) {
- ret = knot_zone_check_nsec_chain(update, &ctx, incremental);
- }
- if (ret == KNOT_EOK) {
- knot_time_t unused = 0;
assert(ctx.validation_mode);
if (incremental) {
- ret = knot_zone_sign_update(update, NULL, &ctx, &unused);
+ ret = knot_zone_sign_update(update, NULL, &ctx);
} else {
- ret = knot_zone_sign(update, NULL, &ctx, &unused);
+ ret = knot_zone_sign(update, NULL, &ctx);
}
}
+end:
+ if (log_plan) {
+ const char *msg_valid = incremental ? "incremental " : "";
+ if (ret != KNOT_EOK) {
+ log_validation_error(update, msg_valid, ret, false);
+ if (conf->cache.srv_dbus_event & DBUS_EVENT_ZONE_INVALID) {
+ dbus_emit_zone_invalid(update->zone->name, 0);
+ }
+ } else if (update->validation_hint.warning != KNOT_EOK) {
+ log_validation_error(update, msg_valid, update->validation_hint.warning, true);
+ if (conf->cache.srv_dbus_event & DBUS_EVENT_ZONE_INVALID) {
+ dbus_emit_zone_invalid(update->zone->name, update->validation_hint.remaining_secs);
+ }
+ } else {
+ log_zone_info(update->zone->name, "DNSSEC, %svalidation successful, checked RRSIGs %zu",
+ msg_valid, ctx.stats->rrsig_count);
+ }
+
+ conf_val_t val = conf_zone_get(conf, C_DNSSEC_VALIDATION, update->zone->name);
+ bool configured = conf_bool(&val);
+ bool bogus = (ret != KNOT_EOK);
+ bool running = (update->zone->contents == update->new_cont);
+ bool may_expire = zone_is_slave(conf, update->zone);
+ knot_time_t expire = (ctx.stats != NULL ? ctx.stats->expire : 0);
+ assert(bogus || knot_time_geq(expire, ctx.now));
+
+ if (running && bogus && may_expire) {
+ zone_events_schedule_now(update->zone, ZONE_EVENT_EXPIRE);
+ }
+ if (configured && !bogus) {
+ if (!incremental) {
+ zone_events_schedule_at(update->zone, ZONE_EVENT_VALIDATE, 0); // cancel previously planned re-check when fully re-checked
+ }
+ zone_events_schedule_at(update->zone, ZONE_EVENT_VALIDATE, // this works for incremental verify as well, re-planning on later
+ knot_time_add(expire, 1)); // is a NOOP, sooner is proper
+ }
+ }
+
kdnssec_ctx_deinit(&ctx);
+
return ret;
}
diff --git a/src/knot/dnssec/zone-events.h b/src/knot/dnssec/zone-events.h
index 462f87a..2f75614 100644
--- a/src/knot/dnssec/zone-events.h
+++ b/src/knot/dnssec/zone-events.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,10 +16,7 @@
#pragma once
-#include <time.h>
-
-#include "knot/zone/zone.h"
-#include "knot/updates/changesets.h"
+#include "contrib/time.h"
#include "knot/updates/zone-update.h"
#include "knot/dnssec/context.h"
@@ -54,17 +51,6 @@ typedef struct {
} zone_sign_reschedule_t;
/*!
- * \brief Generate/rollover keys in keystore as needed.
- *
- * \param kctx Pointers to the keytore, policy, etc.
- * \param zone_name Zone name.
- *
- * \return Error code, KNOT_EOK if successful.
- */
-int knot_dnssec_sign_process_events(const kdnssec_ctx_t *kctx,
- const knot_dname_t *zone_name);
-
-/*!
* \brief DNSSEC re-sign zone, store new records into changeset. Valid signatures
* and NSEC(3) records will not be changed.
*
@@ -130,7 +116,9 @@ knot_time_t knot_dnssec_failover_delay(const kdnssec_ctx_t *ctx);
* \param conf Knot configuration.
* \param now If not zero: adjust "now" to this timestamp.
* \param incremental Try to validate incrementally.
+ * \param log_plan Log the result and plan subsequent validation event.
*
* \return KNOT_E*
*/
-int knot_dnssec_validate_zone(zone_update_t *update, conf_t *conf, knot_time_t now, bool incremental);
+int knot_dnssec_validate_zone(zone_update_t *update, conf_t *conf,
+ knot_time_t now, bool incremental, bool log_plan);
diff --git a/src/knot/dnssec/zone-sign.c b/src/knot/dnssec/zone-sign.c
index 62f809e..3cd2420 100644
--- a/src/knot/dnssec/zone-sign.c
+++ b/src/knot/dnssec/zone-sign.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -209,7 +209,6 @@ bool rrsig_covers_type(const knot_rrset_t *rrsig, uint16_t type)
* \param skip_crypto All RRSIGs in this node have been verified, just check validity.
* \param changeset Changeset to be updated.
* \param update Zone update to be updated. Exactly one of "changeset" and "update" must be NULL!
- * \param expires_at Earliest RRSIG expiration.
*
* \return Error code, KNOT_EOK if successful.
*/
@@ -218,8 +217,7 @@ static int add_missing_rrsigs(const knot_rrset_t *covered,
zone_sign_ctx_t *sign_ctx,
bool skip_crypto,
changeset_t *changeset,
- zone_update_t *update,
- knot_time_t *expires_at)
+ zone_update_t *update)
{
assert(!knot_rrset_empty(covered));
assert(sign_ctx);
@@ -258,11 +256,14 @@ static int add_missing_rrsigs(const knot_rrset_t *covered,
sign_ctx->dnssec_ctx, refresh, skip_crypto, NULL, &valid_at)) {
knot_rdata_t *valid_rr = knot_rdataset_at(&rrsigs->rrs, valid_at);
result = knot_rdataset_remove(&to_remove.rrs, valid_rr, NULL);
- note_earliest_expiration(valid_rr, sign_ctx->dnssec_ctx->now, expires_at);
+ knot_spin_lock(&sign_ctx->dnssec_ctx->stats->lock);
+ note_earliest_expiration(valid_rr, sign_ctx->dnssec_ctx->now,
+ &sign_ctx->dnssec_ctx->stats->expire);
+ knot_spin_unlock(&sign_ctx->dnssec_ctx->stats->lock);
continue;
}
result = knot_sign_rrset(&to_add, covered, key->key, sign_ctx->sign_ctxs[i],
- sign_ctx->dnssec_ctx, NULL, expires_at);
+ sign_ctx->dnssec_ctx, NULL);
}
if (!knot_rrset_empty(&to_remove) && result == KNOT_EOK) {
@@ -306,9 +307,10 @@ static bool key_used(bool ksk, bool zsk, uint16_t type,
int knot_validate_rrsigs(const knot_rrset_t *covered,
const knot_rrset_t *rrsigs,
zone_sign_ctx_t *sign_ctx,
- bool skip_crypto)
+ bool skip_crypto,
+ knot_time_t *valid_until)
{
- if (covered == NULL || rrsigs == NULL || sign_ctx == NULL) {
+ if (covered == NULL || rrsigs == NULL || sign_ctx == NULL || valid_until == NULL) {
return KNOT_EINVAL;
}
@@ -327,7 +329,14 @@ int knot_validate_rrsigs(const knot_rrset_t *covered,
if (valid_signature_exists(covered, rrsigs, key->key, sign_ctx->sign_ctxs[i],
sign_ctx->dnssec_ctx, 0, skip_crypto, val_inval_map, &valid_at)) {
valid_exists = true;
+ knot_rdata_t *valid_rr = knot_rdataset_at(&rrsigs->rrs, valid_at);
+ note_earliest_expiration(valid_rr, sign_ctx->dnssec_ctx->now, valid_until);
}
+
+ knot_spin_lock(&sign_ctx->dnssec_ctx->stats->lock);
+ sign_ctx->dnssec_ctx->stats->rrsig_count++;
+ sign_ctx->dnssec_ctx->stats->expire = knot_time_min(sign_ctx->dnssec_ctx->stats->expire, *valid_until);
+ knot_spin_unlock(&sign_ctx->dnssec_ctx->stats->lock);
}
for (int i = 0; i < rrsigs->rrs.count; i++) {
@@ -398,7 +407,7 @@ static int force_resign_rrset(const knot_rrset_t *covered,
}
}
- return add_missing_rrsigs(covered, NULL, sign_ctx, false, changeset, NULL, NULL);
+ return add_missing_rrsigs(covered, NULL, sign_ctx, false, changeset, NULL);
}
/*!
@@ -409,7 +418,6 @@ static int force_resign_rrset(const knot_rrset_t *covered,
* \param sign_ctx Local zone signing context.
* \param skip_crypto All RRSIGs in this node have been verified, just check validity.
* \param changeset Changeset to be updated.
- * \param expires_at Current earliest expiration, will be updated.
*
* \return Error code, KNOT_EOK if successful.
*/
@@ -417,12 +425,11 @@ static int resign_rrset(const knot_rrset_t *covered,
const knot_rrset_t *rrsigs,
zone_sign_ctx_t *sign_ctx,
bool skip_crypto,
- changeset_t *changeset,
- knot_time_t *expires_at)
+ changeset_t *changeset)
{
assert(!knot_rrset_empty(covered));
- return add_missing_rrsigs(covered, rrsigs, sign_ctx, skip_crypto, changeset, NULL, expires_at);
+ return add_missing_rrsigs(covered, rrsigs, sign_ctx, skip_crypto, changeset, NULL);
}
static int remove_standalone_rrsigs(const zone_node_t *node,
@@ -463,14 +470,13 @@ static int remove_standalone_rrsigs(const zone_node_t *node,
* \param node Node to be signed.
* \param sign_ctx Local zone signing context.
* \param changeset Changeset to be updated.
- * \param expires_at Current earliest expiration, will be updated.
+ * \param hint Out: if DNSSEC validation failed, hint why and where.
*
* \return Error code, KNOT_EOK if successful.
*/
static int sign_node_rrsets(const zone_node_t *node,
zone_sign_ctx_t *sign_ctx,
changeset_t *changeset,
- knot_time_t *expires_at,
dnssec_validation_hint_t *hint)
{
assert(node);
@@ -499,17 +505,25 @@ static int sign_node_rrsets(const zone_node_t *node,
}
if (sign_ctx->dnssec_ctx->validation_mode) {
- result = knot_validate_rrsigs(&rrset, &rrsigs, sign_ctx, skip_crypto);
+ knot_time_t until = 0;
+ result = knot_validate_rrsigs(&rrset, &rrsigs, sign_ctx, skip_crypto, &until);
+ knot_time_t diff = knot_time_diff(until, sign_ctx->dnssec_ctx->now);
if (result != KNOT_EOK) {
hint->node = node->owner;
hint->rrtype = rrset.type;
+ } else if (diff < sign_ctx->dnssec_ctx->policy->rrsig_refresh_before) {
+ hint->node = node->owner;
+ hint->rrtype = rrset.type;
+ hint->warning = KNOT_ESOON_EXPIRE;
+ assert(until > 0);
+ hint->remaining_secs = MAX(0, diff);
}
} else if (sign_ctx->dnssec_ctx->rrsig_drop_existing) {
result = force_resign_rrset(&rrset, &rrsigs,
sign_ctx, changeset);
} else {
result = resign_rrset(&rrset, &rrsigs, sign_ctx, skip_crypto,
- changeset, expires_at);
+ changeset);
}
}
@@ -526,7 +540,6 @@ typedef struct {
zone_tree_t *tree;
zone_sign_ctx_t *sign_ctx;
changeset_t changeset;
- knot_time_t expires_at;
dnssec_validation_hint_t *hint;
size_t num_threads;
size_t thread_index;
@@ -557,11 +570,7 @@ static int sign_node(zone_node_t *node, void *data)
return KNOT_EOK;
}
- int result = sign_node_rrsets(node, args->sign_ctx,
- &args->changeset, &args->expires_at,
- args->hint);
-
- return result;
+ return sign_node_rrsets(node, args->sign_ctx, &args->changeset, args->hint);
}
static void *tree_sign_thread(void *_arg)
@@ -585,7 +594,6 @@ static int set_signed(zone_node_t *node, _unused_ void *data)
* \param zone_keys Zone keys.
* \param policy DNSSEC policy.
* \param update Zone update structure to be updated.
- * \param expires_at Expiration time of the oldest signature in zone.
*
* \return Error code, KNOT_EOK if successful.
*/
@@ -593,8 +601,7 @@ static int zone_tree_sign(zone_tree_t *tree,
size_t num_threads,
zone_keyset_t *zone_keys,
const kdnssec_ctx_t *dnssec_ctx,
- zone_update_t *update,
- knot_time_t *expires_at)
+ zone_update_t *update)
{
assert(zone_keys || dnssec_ctx->validation_mode);
assert(dnssec_ctx);
@@ -603,7 +610,6 @@ static int zone_tree_sign(zone_tree_t *tree,
int ret = KNOT_EOK;
node_sign_args_t args[num_threads];
memset(args, 0, sizeof(args));
- *expires_at = knot_time_plus(dnssec_ctx->now, dnssec_ctx->policy->rrsig_lifetime);
// init context structures
for (size_t i = 0; i < num_threads; i++) {
@@ -619,7 +625,6 @@ static int zone_tree_sign(zone_tree_t *tree,
if (ret != KNOT_EOK) {
break;
}
- args[i].expires_at = 0;
args[i].hint = &update->validation_hint;
args[i].num_threads = num_threads;
args[i].thread_index = i;
@@ -662,7 +667,6 @@ static int zone_tree_sign(zone_tree_t *tree,
ret = args[i].errcode;
if (ret == KNOT_EOK && !dnssec_ctx->validation_mode) {
ret = zone_update_apply_changeset(update, &args[i].changeset); // _fix not needed
- *expires_at = knot_time_min(*expires_at, args[i].expires_at);
}
}
}
@@ -701,10 +705,9 @@ static int rrset_add_zone_ds(knot_rrset_t *rrset, zone_key_t *zone_key, dnssec_k
int knot_zone_sign(zone_update_t *update,
zone_keyset_t *zone_keys,
- const kdnssec_ctx_t *dnssec_ctx,
- knot_time_t *expire_at)
+ const kdnssec_ctx_t *dnssec_ctx)
{
- if (!update || !dnssec_ctx || !expire_at ||
+ if (!update || !dnssec_ctx ||
dnssec_ctx->policy->signing_threads < 1 ||
(zone_keys == NULL && !dnssec_ctx->validation_mode)) {
return KNOT_EINVAL;
@@ -712,16 +715,14 @@ int knot_zone_sign(zone_update_t *update,
int result;
- knot_time_t normal_expire = 0;
result = zone_tree_sign(update->new_cont->nodes, dnssec_ctx->policy->signing_threads,
- zone_keys, dnssec_ctx, update, &normal_expire);
+ zone_keys, dnssec_ctx, update);
if (result != KNOT_EOK) {
return result;
}
- knot_time_t nsec3_expire = 0;
result = zone_tree_sign(update->new_cont->nsec3_nodes, dnssec_ctx->policy->signing_threads,
- zone_keys, dnssec_ctx, update, &nsec3_expire);
+ zone_keys, dnssec_ctx, update);
if (result != KNOT_EOK) {
return result;
}
@@ -732,8 +733,6 @@ int knot_zone_sign(zone_update_t *update,
result = zone_tree_apply(whole ? update->new_cont->nsec3_nodes : update->a_ctx->nsec3_ptrs, set_signed, NULL);
}
- *expire_at = knot_time_min(normal_expire, nsec3_expire);
-
return result;
}
@@ -948,7 +947,7 @@ bool knot_zone_sign_use_key(const zone_key_t *key, const knot_rrset_t *covered)
}
}
-static int sign_in_changeset(zone_node_t *node, uint16_t rrtype, knot_rrset_t *rrsigs,
+static int sign_in_changeset(zone_node_t *node, uint16_t rrtype,
zone_sign_ctx_t *sign_ctx, int ret_prev,
bool skip_crypto, zone_update_t *up)
{
@@ -959,7 +958,8 @@ static int sign_in_changeset(zone_node_t *node, uint16_t rrtype, knot_rrset_t *r
if (knot_rrset_empty(&rr)) {
return KNOT_EOK;
}
- return add_missing_rrsigs(&rr, rrsigs, sign_ctx, skip_crypto, NULL, up, NULL);
+ knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
+ return add_missing_rrsigs(&rr, &rrsigs, sign_ctx, skip_crypto, NULL, up);
}
int knot_zone_sign_nsecs_in_changeset(const zone_keyset_t *zone_keys,
@@ -982,10 +982,9 @@ int knot_zone_sign_nsecs_in_changeset(const zone_keyset_t *zone_keys,
zone_node_t *n = zone_tree_it_val(&it);
bool skip_crypto = (n->flags & NODE_FLAGS_RRSIGS_VALID) && !dnssec_ctx->keytag_conflict;
- knot_rrset_t rrsigs = node_rrset(n, KNOT_RRTYPE_RRSIG);
- ret = sign_in_changeset(n, KNOT_RRTYPE_NSEC, &rrsigs, sign_ctx, ret, skip_crypto, update);
- ret = sign_in_changeset(n, KNOT_RRTYPE_NSEC3, &rrsigs, sign_ctx, ret, skip_crypto, update);
- ret = sign_in_changeset(n, KNOT_RRTYPE_NSEC3PARAM, &rrsigs, sign_ctx, ret, skip_crypto, update);
+ ret = sign_in_changeset(n, KNOT_RRTYPE_NSEC, sign_ctx, ret, skip_crypto, update);
+ ret = sign_in_changeset(n, KNOT_RRTYPE_NSEC3, sign_ctx, ret, skip_crypto, update);
+ ret = sign_in_changeset(n, KNOT_RRTYPE_NSEC3PARAM, sign_ctx, ret, skip_crypto, update);
if (ret == KNOT_EOK) {
n->flags |= NODE_FLAGS_RRSIGS_VALID; // non-NSEC RRSIGs had been validated in knot_dnssec_sign_update()
@@ -1023,10 +1022,9 @@ bool knot_zone_sign_rr_should_be_signed(const zone_node_t *node,
int knot_zone_sign_update(zone_update_t *update,
zone_keyset_t *zone_keys,
- const kdnssec_ctx_t *dnssec_ctx,
- knot_time_t *expire_at)
+ const kdnssec_ctx_t *dnssec_ctx)
{
- if (update == NULL || dnssec_ctx == NULL || expire_at == NULL ||
+ if (update == NULL || dnssec_ctx == NULL ||
dnssec_ctx->policy->signing_threads < 1 ||
(zone_keys == NULL && !dnssec_ctx->validation_mode)) {
return KNOT_EINVAL;
@@ -1038,16 +1036,16 @@ int knot_zone_sign_update(zone_update_t *update,
* If so, we have to sign the whole zone. */
const bool full_sign = apex_dnssec_changed(update);
if (full_sign) {
- ret = knot_zone_sign(update, zone_keys, dnssec_ctx, expire_at);
+ ret = knot_zone_sign(update, zone_keys, dnssec_ctx);
} else {
ret = zone_tree_sign(update->a_ctx->node_ptrs, dnssec_ctx->policy->signing_threads,
- zone_keys, dnssec_ctx, update, expire_at);
+ zone_keys, dnssec_ctx, update);
if (ret == KNOT_EOK) {
ret = zone_tree_apply(update->a_ctx->node_ptrs, set_signed, NULL);
}
if (ret == KNOT_EOK && dnssec_ctx->validation_mode) {
ret = zone_tree_sign(update->a_ctx->nsec3_ptrs, dnssec_ctx->policy->signing_threads,
- zone_keys, dnssec_ctx, update, expire_at);
+ zone_keys, dnssec_ctx, update);
}
if (ret == KNOT_EOK && dnssec_ctx->validation_mode) {
ret = zone_tree_apply(update->a_ctx->nsec3_ptrs, set_signed, NULL);
diff --git a/src/knot/dnssec/zone-sign.h b/src/knot/dnssec/zone-sign.h
index ba6e2b2..480bcf9 100644
--- a/src/knot/dnssec/zone-sign.h
+++ b/src/knot/dnssec/zone-sign.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -81,13 +81,15 @@ keyptr_dynarray_t knot_zone_sign_get_cdnskeys(const kdnssec_ctx_t *ctx,
* \param rrsigs RRSIG with signatures.
* \param sign_ctx Signing context (with keys == NULL)
* \param skip_crypto Crypto operations might be skipped as they had been successful earlier.
+ * \param valid_until End of soonest RRSIG validity.
*
* \return KNOT_E*
*/
int knot_validate_rrsigs(const knot_rrset_t *covered,
const knot_rrset_t *rrsigs,
zone_sign_ctx_t *sign_ctx,
- bool skip_crypto);
+ bool skip_crypto,
+ knot_time_t *valid_until);
/*!
* \brief Update zone signatures and store performed changes in update.
@@ -97,14 +99,12 @@ int knot_validate_rrsigs(const knot_rrset_t *covered,
* \param update Zone Update containing the zone and to be updated with new DNSKEYs and RRSIGs.
* \param zone_keys Zone keys.
* \param dnssec_ctx DNSSEC context.
- * \param expire_at Time, when the oldest signature in the zone expires.
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_zone_sign(zone_update_t *update,
zone_keyset_t *zone_keys,
- const kdnssec_ctx_t *dnssec_ctx,
- knot_time_t *expire_at);
+ const kdnssec_ctx_t *dnssec_ctx);
/*!
* \brief Sign NSEC/NSEC3 nodes in changeset and update the changeset.
@@ -138,14 +138,12 @@ bool knot_zone_sign_rr_should_be_signed(const zone_node_t *node,
* \param update Zone Update structure.
* \param zone_keys Zone keys.
* \param dnssec_ctx DNSSEC context.
- * \param expire_at Time, when the oldest signature in the update expires.
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_zone_sign_update(zone_update_t *update,
zone_keyset_t *zone_keys,
- const kdnssec_ctx_t *dnssec_ctx,
- knot_time_t *expire_at);
+ const kdnssec_ctx_t *dnssec_ctx);
/*!
* \brief Force re-sign of a RRSet in zone apex.
diff --git a/src/knot/events/events.c b/src/knot/events/events.c
index f0de68e..6224006 100644
--- a/src/knot/events/events.c
+++ b/src/knot/events/events.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -46,6 +46,7 @@ static const event_info_t EVENT_INFO[] = {
{ ZONE_EVENT_BACKUP, event_backup, "backup/restore" },
{ ZONE_EVENT_NOTIFY, event_notify, "notify" },
{ ZONE_EVENT_DNSSEC, event_dnssec, "re-sign" },
+ { ZONE_EVENT_VALIDATE, event_validate, "DNSSEC-validate" },
{ ZONE_EVENT_UFREEZE, event_ufreeze, "update-freeze" },
{ ZONE_EVENT_UTHAW, event_uthaw, "update-thaw" },
{ ZONE_EVENT_DS_CHECK, event_ds_check, "DS-check" },
diff --git a/src/knot/events/events.h b/src/knot/events/events.h
index a32d195..cf1b362 100644
--- a/src/knot/events/events.h
+++ b/src/knot/events/events.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -38,6 +38,7 @@ typedef enum zone_event_type {
ZONE_EVENT_BACKUP,
ZONE_EVENT_NOTIFY,
ZONE_EVENT_DNSSEC,
+ ZONE_EVENT_VALIDATE,
ZONE_EVENT_UFREEZE,
ZONE_EVENT_UTHAW,
ZONE_EVENT_DS_CHECK,
diff --git a/src/knot/events/handlers.h b/src/knot/events/handlers.h
index d74fd8a..45b7b45 100644
--- a/src/knot/events/handlers.h
+++ b/src/knot/events/handlers.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,6 +39,8 @@ int event_dnssec(conf_t *conf, zone_t *zone);
/*! \brief NOT A HANDLER, just a helper function to reschedule based on reschedule_t */
void event_dnssec_reschedule(conf_t *conf, zone_t *zone,
const zone_sign_reschedule_t *refresh, bool zone_changed);
+/*! \brief Validate the wole zone's DNSSEC. */
+int event_validate(conf_t *conf, zone_t *zone);
/*! \brief Freeze those events causing zone contents change. */
int event_ufreeze(conf_t *conf, zone_t *zone);
/*! \brief Unfreeze zone updates. */
diff --git a/src/knot/events/handlers/dnskey_sync.c b/src/knot/events/handlers/dnskey_sync.c
index c6d80ff..018f66b 100644
--- a/src/knot/events/handlers/dnskey_sync.c
+++ b/src/knot/events/handlers/dnskey_sync.c
@@ -26,8 +26,8 @@
#define DNSKEY_SYNC_LOG(priority, zone, remote, flags, fmt, ...) \
ns_log(priority, zone, LOG_OPERATION_DNSKEY_SYNC, LOG_DIRECTION_OUT, &(remote)->addr, \
- ((flags) & KNOT_REQUESTOR_QUIC) ? KNOTD_QUERY_PROTO_QUIC : KNOTD_QUERY_PROTO_TCP, \
- ((flags) & KNOT_REQUESTOR_REUSED), (remote)->key.name, fmt, ## __VA_ARGS__)
+ flags2proto(flags), ((flags) & KNOT_REQUESTOR_REUSED), (remote)->key.name, \
+ fmt, ## __VA_ARGS__)
static const unsigned remote_rrs[] = { KNOT_RRTYPE_DNSKEY, KNOT_RRTYPE_CDNSKEY, KNOT_RRTYPE_CDS };
#define REMOTE_NTYPES (sizeof(remote_rrs) / sizeof(remote_rrs[0]))
diff --git a/src/knot/events/handlers/ds_push.c b/src/knot/events/handlers/ds_push.c
index be7621f..761b800 100644
--- a/src/knot/events/handlers/ds_push.c
+++ b/src/knot/events/handlers/ds_push.c
@@ -38,8 +38,8 @@ struct ds_push_data {
#define DS_PUSH_LOG(priority, zone, remote, flags, fmt, ...) \
ns_log(priority, zone, LOG_OPERATION_DS_PUSH, LOG_DIRECTION_OUT, &(remote)->addr, \
- ((flags) & KNOT_REQUESTOR_QUIC) ? KNOTD_QUERY_PROTO_QUIC : KNOTD_QUERY_PROTO_TCP, \
- ((flags) & KNOT_REQUESTOR_REUSED), (remote)->key.name, fmt, ## __VA_ARGS__)
+ flags2proto(flags), ((flags) & KNOT_REQUESTOR_REUSED), (remote)->key.name, \
+ fmt, ## __VA_ARGS__)
static const knot_rdata_t remove_cds = { 5, { 0, 0, 0, 0, 0 } };
@@ -53,7 +53,7 @@ static int ds_push_begin(knot_layer_t *layer, void *params)
static int parent_soa_produce(struct ds_push_data *data, knot_pkt_t *pkt)
{
assert(data->parent_query[0] != '\0');
- data->parent_query = knot_wire_next_label(data->parent_query, NULL);
+ data->parent_query = knot_dname_next_label(data->parent_query);
int ret = knot_pkt_put_question(pkt, data->parent_query, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
if (ret != KNOT_EOK) {
diff --git a/src/knot/events/handlers/notify.c b/src/knot/events/handlers/notify.c
index 9dae70a..9a8b8df 100644
--- a/src/knot/events/handlers/notify.c
+++ b/src/knot/events/handlers/notify.c
@@ -84,8 +84,8 @@ static const knot_layer_api_t NOTIFY_API = {
#define NOTIFY_OUT_LOG(priority, zone, remote, flags, fmt, ...) \
ns_log(priority, zone, LOG_OPERATION_NOTIFY, LOG_DIRECTION_OUT, &(remote)->addr, \
- ((flags) & KNOT_REQUESTOR_QUIC) ? KNOTD_QUERY_PROTO_QUIC : KNOTD_QUERY_PROTO_TCP, \
- ((flags) & KNOT_REQUESTOR_REUSED), (remote)->key.name, fmt, ## __VA_ARGS__)
+ flags2proto(flags), ((flags) & KNOT_REQUESTOR_REUSED), (remote)->key.name, \
+ fmt, ## __VA_ARGS__)
static int send_notify(conf_t *conf, zone_t *zone, const knot_rrset_t *soa,
const conf_remote_t *slave, int timeout, bool retry)
diff --git a/src/knot/events/handlers/refresh.c b/src/knot/events/handlers/refresh.c
index 8099df6..6c22707 100644
--- a/src/knot/events/handlers/refresh.c
+++ b/src/knot/events/handlers/refresh.c
@@ -68,24 +68,24 @@
* \endverbatim
*/
-#define PROTO(data) \
- ((data)->layer->flags & KNOT_REQUESTOR_QUIC) ? KNOTD_QUERY_PROTO_QUIC : KNOTD_QUERY_PROTO_TCP
-
#define REFRESH_LOG(priority, data, msg...) \
ns_log(priority, (data)->zone->name, LOG_OPERATION_REFRESH, LOG_DIRECTION_NONE, \
&(data)->remote->addr, 0, false, (data)->remote->key.name, msg)
#define REFRESH_LOG_PROTO(priority, data, msg...) \
ns_log(priority, (data)->zone->name, LOG_OPERATION_REFRESH, LOG_DIRECTION_NONE, \
- &(data)->remote->addr, PROTO(data), (data)->layer->flags & KNOT_REQUESTOR_REUSED, (data)->remote->key.name, msg)
+ &(data)->remote->addr, flags2proto((data)->layer->flags), \
+ (data)->layer->flags & KNOT_REQUESTOR_REUSED, (data)->remote->key.name, msg)
#define AXFRIN_LOG(priority, data, msg...) \
ns_log(priority, (data)->zone->name, LOG_OPERATION_AXFR, LOG_DIRECTION_IN, \
- &(data)->remote->addr, PROTO(data), (data)->layer->flags & KNOT_REQUESTOR_REUSED, (data)->remote->key.name, msg)
+ &(data)->remote->addr, flags2proto((data)->layer->flags), \
+ (data)->layer->flags & KNOT_REQUESTOR_REUSED, (data)->remote->key.name, msg)
#define IXFRIN_LOG(priority, data, msg...) \
ns_log(priority, (data)->zone->name, LOG_OPERATION_IXFR, LOG_DIRECTION_IN, \
- &(data)->remote->addr, PROTO(data), (data)->layer->flags & KNOT_REQUESTOR_REUSED, (data)->remote->key.name, msg)
+ &(data)->remote->addr, flags2proto((data)->layer->flags), \
+ (data)->layer->flags & KNOT_REQUESTOR_REUSED, (data)->remote->key.name, msg)
enum state {
REFRESH_STATE_INVALID = 0,
@@ -123,7 +123,7 @@ struct refresh_data {
int ret; //!< Error code.
enum state state; //!< Event processing state.
- enum xfr_type xfr_type; //!< Transer type (mostly IXFR versus AXFR).
+ enum xfr_type xfr_type; //!< Transfer type (mostly IXFR versus AXFR).
bool axfr_style_ixfr; //!< Master responded with AXFR-style-IXFR.
knot_rrset_t *initial_soa_copy; //!< Copy of the received initial SOA.
struct xfr_stats stats; //!< Transfer statistics.
@@ -1200,14 +1200,33 @@ static int transfer_consume(knot_layer_t *layer, knot_pkt_t *pkt)
// Transfer completed
if (next == KNOT_STATE_DONE) {
// Log transfer even if we still can fail
+ uint32_t serial;
+ switch (data->xfr_type) {
+ case XFR_TYPE_AXFR:
+ serial = zone_contents_serial(data->axfr.zone);
+ break;
+ case XFR_TYPE_IXFR:
+ serial = knot_soa_serial(data->ixfr.final_soa->rrs.rdata);
+ break;
+ case XFR_TYPE_UPTODATE:
+ if (slave_zone_serial(data->zone, data->conf, &serial) == KNOT_EOK) {
+ break;
+ }
+ // FALLTHROUGH
+ default:
+ serial = 0;
+ }
+ char serial_log[32];
+ (void)snprintf(serial_log, sizeof(serial_log),
+ " remote serial %u,", serial);
xfr_log_finished(data->zone->name,
data->xfr_type == XFR_TYPE_IXFR ||
data->xfr_type == XFR_TYPE_UPTODATE ?
LOG_OPERATION_IXFR : LOG_OPERATION_AXFR,
LOG_DIRECTION_IN, &data->remote->addr,
- (layer->flags & KNOT_REQUESTOR_QUIC ?
- KNOTD_QUERY_PROTO_QUIC : KNOTD_QUERY_PROTO_TCP),
- data->remote->key.name, &data->stats);
+ flags2proto(layer->flags),
+ data->remote->key.name,
+ serial_log, &data->stats);
/*
* TODO: Move finialization into finish
diff --git a/src/knot/events/handlers/update.c b/src/knot/events/handlers/update.c
index b4092a3..2c6758c 100644
--- a/src/knot/events/handlers/update.c
+++ b/src/knot/events/handlers/update.c
@@ -28,6 +28,9 @@
#include "knot/zone/zone.h"
#include "libdnssec/random.h"
#include "libknot/libknot.h"
+#include "libknot/quic/quic_conn.h"
+#include "libknot/quic/quic.h"
+#include "libknot/quic/tls.h"
#include "contrib/net.h"
#include "contrib/time.h"
@@ -51,6 +54,33 @@ static void init_qdata_from_request(knotd_qdata_t *qdata,
qdata->extra->zone = zone;
}
+#ifdef ENABLE_QUIC
+static int ddnsq_alloc_reply(knot_quic_reply_t *r)
+{
+ r->out_payload->iov_len = KNOT_WIRE_MAX_PKTSIZE;
+
+ return KNOT_EOK;
+}
+
+static int ddnsq_send_reply(knot_quic_reply_t *r)
+{
+ int fd = *(int *)r->sock;
+ int ret = net_dgram_send(fd, r->out_payload->iov_base, r->out_payload->iov_len, r->ip_rem);
+ if (ret < 0) {
+ return knot_map_errno();
+ } else if (ret == r->out_payload->iov_len) {
+ return KNOT_EOK;
+ } else {
+ return KNOT_EAGAIN;
+ }
+}
+
+static void ddnsq_free_reply(knot_quic_reply_t *r)
+{
+ r->out_payload->iov_len = 0;
+}
+#endif // ENABLE_QUIC
+
static int check_prereqs(knot_request_t *request,
zone_update_t *update,
knotd_qdata_t *qdata)
@@ -65,6 +95,15 @@ static int check_prereqs(knot_request_t *request,
return ret;
}
+ ret = ddns_precheck_update(request->query, update, &rcode);
+ if (ret != KNOT_EOK) {
+ UPDATE_LOG(LOG_WARNING, qdata, "broken update format (%s)",
+ knot_strerror(ret));
+ assert(rcode != KNOT_RCODE_NOERROR);
+ knot_wire_set_rcode(request->resp->wire, rcode);
+ return ret;
+ }
+
return KNOT_EOK;
}
@@ -104,6 +143,7 @@ static int process_bulk(zone_t *zone, list_t *requests, zone_update_t *up)
knot_request_t *req = node->d;
// Init qdata structure for logging (unique per-request).
knotd_qdata_params_t params = {
+ .proto = flags2proto(req->flags),
.remote = &req->remote
};
knotd_qdata_t qdata;
@@ -118,6 +158,8 @@ static int process_bulk(zone_t *zone, list_t *requests, zone_update_t *up)
ret = process_single_update(req, up, &qdata);
if (ret != KNOT_EOK) {
+ log_zone_error(zone->name, "DDNS, dropping %zu updates in a bulk",
+ list_size(requests));
return ret;
}
}
@@ -338,6 +380,39 @@ static void send_update_response(conf_t *conf, zone_t *zone, knot_request_t *req
(void)process_query_sign_response(req->resp, &qdata);
}
+ if (net_is_stream(req->fd) && req->tls_req_ctx.conn != NULL) {
+ (void)knot_tls_send_dns(req->tls_req_ctx.conn,
+ req->resp->wire, req->resp->size);
+ knot_tls_conn_block(req->tls_req_ctx.conn, false);
+ }
+#ifdef ENABLE_QUIC
+ else if (req->quic_conn != NULL) {
+ assert(!net_is_stream(req->fd));
+ uint8_t op_buf[KNOT_WIRE_MAX_PKTSIZE];
+ struct iovec out_payload = { .iov_base = op_buf, .iov_len = sizeof(op_buf) };
+ knot_quic_reply_t rpl = {
+ .ip_rem = &req->remote,
+ .ip_loc = &req->source,
+ .in_payload = NULL,
+ .out_payload = &out_payload,
+ .sock = &req->fd,
+ .alloc_reply = ddnsq_alloc_reply,
+ .send_reply = ddnsq_send_reply,
+ .free_reply = ddnsq_free_reply
+ };
+
+ void *succ = knot_quic_stream_add_data(req->quic_conn, req->quic_stream,
+ req->resp->wire, req->resp->size);
+ if (succ != NULL) { // else ENOMEM
+ (void)knot_quic_send(req->quic_conn->quic_table, req->quic_conn,
+ &rpl, 4, KNOT_QUIC_SEND_IGNORE_BLOCKED);
+ }
+ knot_quic_conn_block(req->quic_conn, false);
+ } else // NOTE ties to 'if' below
+#else
+ assert(req->quic_conn == NULL);
+#endif // ENABLE_QUIC
+
if (net_is_stream(req->fd)) {
net_dns_tcp_send(req->fd, req->resp->wire, req->resp->size,
conf->cache.srv_tcp_remote_io_timeout, NULL);
@@ -348,22 +423,13 @@ static void send_update_response(conf_t *conf, zone_t *zone, knot_request_t *req
}
}
-static void free_request(knot_request_t *req)
-{
- close(req->fd);
- knot_pkt_free(req->query);
- knot_pkt_free(req->resp);
- dnssec_binary_free(&req->sign.tsig_key.secret);
- free(req);
-}
-
static void send_update_responses(conf_t *conf, zone_t *zone, list_t *updates)
{
ptrnode_t *node, *nxt;
WALK_LIST_DELSAFE(node, nxt, *updates) {
knot_request_t *req = node->d;
send_update_response(conf, zone, req);
- free_request(req);
+ knot_request_free(req, NULL);
}
ptrlist_free(updates, NULL);
}
diff --git a/src/knot/events/handlers/validate.c b/src/knot/events/handlers/validate.c
new file mode 100644
index 0000000..e410b4f
--- /dev/null
+++ b/src/knot/events/handlers/validate.c
@@ -0,0 +1,34 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "knot/common/log.h"
+#include "knot/conf/conf.h"
+#include "knot/dnssec/zone-events.h"
+#include "knot/zone/zone.h"
+
+int event_validate(conf_t *conf, zone_t *zone)
+{
+ knot_time_t now = knot_time();
+ zone_update_t fake_upd = {
+ .zone = zone,
+ .new_cont = zone->contents,
+ // .validation_hint is zeroed
+ };
+
+ log_zone_info(zone->name, "DNSSEC, re-validating zone fully");
+
+ return knot_dnssec_validate_zone(&fake_upd, conf, now, false, true);
+}
diff --git a/src/knot/events/replan.c b/src/knot/events/replan.c
index ed03fe1..29a07ea 100644
--- a/src/knot/events/replan.c
+++ b/src/knot/events/replan.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -108,6 +108,9 @@ void replan_from_timers(conf_t *conf, zone_t *zone)
if (zone->contents == NULL && zone->timers.last_refresh_ok) { // zone disappeared w/o expiry
refresh = now;
}
+ if (refresh == 0) { // sanitize in case of concurrent purge event
+ refresh = now;
+ }
assert(refresh > 0);
}
diff --git a/src/knot/include/module.h b/src/knot/include/module.h
index 15a9077..9998e48 100644
--- a/src/knot/include/module.h
+++ b/src/knot/include/module.h
@@ -36,7 +36,7 @@
/*** Query module API. ***/
/*! Current module ABI version. */
-#define KNOTD_MOD_ABI_VERSION 500
+#define KNOTD_MOD_ABI_VERSION 600
/*! Module configuration name prefix. */
#define KNOTD_MOD_NAME_PREFIX "mod-"
@@ -354,7 +354,7 @@ knotd_conf_t knotd_conf_check_item(knotd_conf_check_args_t *args,
const yp_name_t *item_name);
/*!
- * \brief Checks if address is in at least one of given ranges.
+ * Checks if address is in at least one of given ranges.
*
* \param[in] range
* \param[in] addr
@@ -393,11 +393,13 @@ typedef enum {
KNOTD_QUERY_PROTO_UDP = KNOT_PROBE_PROTO_UDP, /*!< Pure UDP. */
KNOTD_QUERY_PROTO_TCP = KNOT_PROBE_PROTO_TCP, /*!< Pure TCP. */
KNOTD_QUERY_PROTO_QUIC = KNOT_PROBE_PROTO_QUIC, /*!< QUIC/UDP. */
+ KNOTD_QUERY_PROTO_TLS = KNOT_PROBE_PROTO_TLS, /*!< TLS/TCP. */
} knotd_query_proto_t;
/*! Query processing specific flags. */
typedef enum {
- KNOTD_QUERY_FLAG_COOKIE = 1 << 0, /*!< Valid DNS Cookie indication. */
+ KNOTD_QUERY_FLAG_COOKIE = 1 << 0, /*!< Valid DNS Cookie indication. */
+ KNOTD_QUERY_FLAG_AUTHORIZED = 1 << 1, /*!< Successfully authorized operation. */
} knotd_query_flag_t;
/*! Query processing data context parameters. */
@@ -411,6 +413,7 @@ typedef struct {
void *server; /*!< Server object private item. */
const struct knot_xdp_msg *xdp_msg; /*!< Possible XDP message context. */
struct knot_quic_conn *quic_conn; /*!< QUIC connection context. */
+ struct knot_tls_conn *tls_conn; /*!< TLS connection context. */
int64_t quic_stream; /*!< QUIC stream ID inside quic_conn. */
uint32_t measured_rtt; /*!< Measured RTT in usecs: QUIC or TCP-XDP. */
} knotd_qdata_params_t;
@@ -493,6 +496,7 @@ knot_rrset_t knotd_qdata_zone_apex_rrset(const knotd_qdata_t *qdata, uint16_t ty
* \param[in] qdata Query data.
* \param[in] zone_name Optional zone name, the current one otherwise.
* \param[in] node_name Optional node name, apex otherwise.
+ * \param[in] type RRSet type.
* \param[out] out Destination rrset to store the output to.
*
* \return Error code (KNOT_ENOZONE, KNOT_EEMPTYZONE, KNOT_ENONODE), KNOT_EOK if success.
@@ -501,6 +505,12 @@ int knotd_qdata_zone_rrset(const knotd_qdata_t *qdata, const knot_dname_t *zone_
const knot_dname_t *node_name, uint16_t type,
knot_rrset_t *out);
+/*! Transport protocol processing states. */
+typedef enum {
+ KNOTD_PROTO_STATE_PASS = 0, /*!< Process normally. */
+ KNOTD_PROTO_STATE_BLOCK = 1, /*!< Block the packet/connection. */
+} knotd_proto_state_t;
+
/*! General query processing states. */
typedef enum {
KNOTD_STATE_NOOP = 0, /*!< No response. */
@@ -509,7 +519,7 @@ typedef enum {
KNOTD_STATE_FINAL = 6, /*!< Finished and finalized (QNAME, EDNS, TSIG). */
} knotd_state_t;
-/*! brief Internet query processing states. */
+/*! Internet query processing states. */
typedef enum {
KNOTD_IN_STATE_BEGIN, /*!< Begin name resolution. */
KNOTD_IN_STATE_NODATA, /*!< Positive result with NO data. */
@@ -523,15 +533,29 @@ typedef enum {
/*! Query module processing stages. */
typedef enum {
- KNOTD_STAGE_BEGIN = 0, /*!< Before query processing. */
- KNOTD_STAGE_PREANSWER, /*!< Before section processing. */
- KNOTD_STAGE_ANSWER, /*!< Answer section processing. */
- KNOTD_STAGE_AUTHORITY, /*!< Authority section processing. */
- KNOTD_STAGE_ADDITIONAL, /*!< Additional section processing. */
- KNOTD_STAGE_END, /*!< After query processing. */
+ KNOTD_STAGE_PROTO_BEGIN = 0, /*!< Start of transport protocol processing. */
+ KNOTD_STAGE_BEGIN, /*!< Before query processing. */
+ KNOTD_STAGE_PREANSWER, /*!< Before section processing. */
+ KNOTD_STAGE_ANSWER, /*!< Answer section processing. */
+ KNOTD_STAGE_AUTHORITY, /*!< Authority section processing. */
+ KNOTD_STAGE_ADDITIONAL, /*!< Additional section processing. */
+ KNOTD_STAGE_END, /*!< After query processing. */
+ KNOTD_STAGE_PROTO_END, /*!< End of transport protocol processing. */
} knotd_stage_t;
/*!
+ * Transport protocol processing hook.
+ *
+ * \param[in] state Current processing state.
+ * \param[in] params Processing parameters.
+ * \param[in] mod Module context.
+ *
+ * \return Next processing state.
+ */
+typedef knotd_proto_state_t (*knotd_mod_proto_hook_f)
+ (knotd_proto_state_t state, knotd_qdata_params_t *params, knotd_mod_t *mod);
+
+/*!
* General processing hook.
*
* \param[in] state Current processing state.
@@ -558,6 +582,17 @@ typedef knotd_in_state_t (*knotd_mod_in_hook_f)
(knotd_in_state_t state, knot_pkt_t *pkt, knotd_qdata_t *qdata, knotd_mod_t *mod);
/*!
+ * Registers transport protocol processing module hook.
+ *
+ * \param[in] mod Module context.
+ * \param[in] stage Processing stage (KNOTD_STAGE_PROTO_BEGIN or KNOTD_STAGE_PROTO_END).
+ * \param[in] hook Module hook.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int knotd_mod_proto_hook(knotd_mod_t *mod, knotd_stage_t stage, knotd_mod_proto_hook_f hook);
+
+/*!
* Registers general processing module hook.
*
* \param[in] mod Module context.
diff --git a/src/knot/modules/cookies/cookies.c b/src/knot/modules/cookies/cookies.c
index 34c4b22..48eca7d 100644
--- a/src/knot/modules/cookies/cookies.c
+++ b/src/knot/modules/cookies/cookies.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,19 +20,10 @@
#include "knot/include/module.h"
#include "libknot/libknot.h"
+#include "contrib/atomic.h"
#include "contrib/string.h"
#include "libdnssec/random.h"
-#ifdef HAVE_ATOMIC
-#define ATOMIC_SET(dst, val) __atomic_store_n(&(dst), (val), __ATOMIC_RELAXED)
-#define ATOMIC_GET(src) __atomic_load_n(&(src), __ATOMIC_RELAXED)
-#define ATOMIC_ADD(dst, val) __atomic_add_fetch(&(dst), (val), __ATOMIC_RELAXED)
-#else
-#define ATOMIC_SET(dst, val) ((dst) = (val))
-#define ATOMIC_GET(src) (src)
-#define ATOMIC_ADD(dst, val) ((dst) += (val))
-#endif
-
#define BADCOOKIE_CTR_INIT 1
#define MOD_SECRET_LIFETIME "\x0F""secret-lifetime"
@@ -59,13 +50,13 @@ int cookies_conf_check(knotd_conf_check_args_t *args)
typedef struct {
struct {
- uint64_t variable;
+ knot_atomic_uint64_t variable;
uint64_t constant;
} secret;
pthread_t update_secret;
uint32_t secret_lifetime;
uint32_t badcookie_slip;
- uint16_t badcookie_ctr; // Counter for BADCOOKIE answers.
+ knot_atomic_uint16_t badcookie_ctr; // Counter for BADCOOKIE answers.
} cookies_ctx_t;
static void update_ctr(cookies_ctx_t *ctx)
@@ -285,7 +276,7 @@ int cookies_load(knotd_mod_t *mod)
}
}
-#ifndef HAVE_ATOMIC
+#ifndef KNOT_HAVE_ATOMIC
knotd_mod_log(mod, LOG_WARNING, "the module might work slightly wrong on this platform");
ctx->badcookie_slip = 1;
#endif
diff --git a/src/knot/modules/probe/probe.c b/src/knot/modules/probe/probe.c
index 9207308..3e0a646 100644
--- a/src/knot/modules/probe/probe.c
+++ b/src/knot/modules/probe/probe.c
@@ -19,18 +19,11 @@
#include "knot/conf/schema.h"
#include "knot/include/module.h"
+#include "contrib/atomic.h"
#include "contrib/string.h"
#include "contrib/time.h"
#include "libknot/libknot.h"
-#ifdef HAVE_ATOMIC
-#define ATOMIC_SET(dst, val) __atomic_store_n(&(dst), (val), __ATOMIC_RELAXED)
-#define ATOMIC_GET(src) __atomic_load_n(&(src), __ATOMIC_RELAXED)
-#else
-#define ATOMIC_SET(dst, val) ((dst) = (val))
-#define ATOMIC_GET(src) (src)
-#endif
-
#define MOD_PATH "\x04""path"
#define MOD_CHANNELS "\x08""channels"
#define MOD_MAX_RATE "\x08""max-rate"
@@ -45,7 +38,7 @@ const yp_item_t probe_conf[] = {
typedef struct {
knot_probe_t **probes;
size_t probe_count;
- uint64_t *last_times;
+ knot_atomic_uint64_t *last_times;
uint64_t min_diff_ns;
char *path;
} probe_ctx_t;
@@ -139,7 +132,7 @@ int probe_load(knotd_mod_t *mod)
return KNOT_ENOMEM;
}
- ctx->last_times = calloc(ctx->probe_count, sizeof(uint64_t));
+ ctx->last_times = calloc(ctx->probe_count, sizeof(*ctx->last_times));
if (ctx->last_times == NULL) {
free_probe_ctx(ctx);
return KNOT_ENOMEM;
diff --git a/src/knot/modules/rrl/Makefile.inc b/src/knot/modules/rrl/Makefile.inc
index d82edf9..fbb9eb0 100644
--- a/src/knot/modules/rrl/Makefile.inc
+++ b/src/knot/modules/rrl/Makefile.inc
@@ -1,15 +1,20 @@
knot_modules_rrl_la_SOURCES = knot/modules/rrl/rrl.c \
knot/modules/rrl/functions.c \
- knot/modules/rrl/functions.h
+ knot/modules/rrl/functions.h \
+ knot/modules/rrl/kru-generic.c \
+ knot/modules/rrl/kru-avx2.c \
+ knot/modules/rrl/kru.h
+noinst_HEADERS = knot/modules/rrl/kru.inc.c
EXTRA_DIST += knot/modules/rrl/rrl.rst
if STATIC_MODULE_rrl
libknotd_la_SOURCES += $(knot_modules_rrl_la_SOURCES)
+libknotd_la_LIBADD += $(math_LIBS)
endif
if SHARED_MODULE_rrl
knot_modules_rrl_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
knot_modules_rrl_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
-knot_modules_rrl_la_LIBADD = $(libcontrib_LIBS)
+knot_modules_rrl_la_LIBADD = $(libcontrib_LIBS) $(math_LIBS)
pkglib_LTLIBRARIES += knot/modules/rrl.la
endif
diff --git a/src/knot/modules/rrl/functions.c b/src/knot/modules/rrl/functions.c
index df35394..01d89cb 100644
--- a/src/knot/modules/rrl/functions.c
+++ b/src/knot/modules/rrl/functions.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,516 +14,223 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-#include <assert.h>
+#include <stdatomic.h>
#include <time.h>
#include "knot/modules/rrl/functions.h"
+#include "knot/modules/rrl/kru.h"
+#include "contrib/macros.h"
#include "contrib/musl/inet_ntop.h"
-#include "contrib/openbsd/strlcat.h"
#include "contrib/sockaddr.h"
#include "contrib/time.h"
-#include "libdnssec/error.h"
#include "libdnssec/random.h"
-/* Hopscotch defines. */
-#define HOP_LEN (sizeof(unsigned)*8)
-/* Limits (class, ipv6 remote, dname) */
-#define RRL_CLSBLK_MAXLEN (1 + 8 + 255)
-/* CIDR block prefix lengths for v4/v6 */
-#define RRL_V4_PREFIX_LEN 3 /* /24 */
-#define RRL_V6_PREFIX_LEN 7 /* /56 */
-/* Defaults */
-#define RRL_SSTART 2 /* 1/Nth of the rate for slow start */
-#define RRL_PSIZE_LARGE 1024
-#define RRL_CAPACITY 4 /* Window size in seconds */
-#define RRL_LOCK_GRANULARITY 32 /* Last digit granularity */
-
-/* Classification */
-enum {
- CLS_NULL = 0 << 0, /* Empty bucket. */
- CLS_NORMAL = 1 << 0, /* Normal response. */
- CLS_ERROR = 1 << 1, /* Error response. */
- CLS_NXDOMAIN = 1 << 2, /* NXDOMAIN (special case of error). */
- CLS_EMPTY = 1 << 3, /* Empty response. */
- CLS_LARGE = 1 << 4, /* Response size over threshold (1024k). */
- CLS_WILDCARD = 1 << 5, /* Wildcard query. */
- CLS_ANY = 1 << 6, /* ANY query (spec. class). */
- CLS_DNSSEC = 1 << 7 /* DNSSEC related RR query (spec. class) */
-};
+// CIDR block prefix lengths for v4/v6 (hardcoded also in unit tests).
+#define RRL_V4_PREFIXES (uint8_t[]) { 18, 20, 24, 32 }
+#define RRL_V4_RATE_MULT (kru_price_t[]) { 768, 256, 32, 1 }
-/* Classification string. */
-struct cls_name {
- int code;
- const char *name;
-};
+#define RRL_V6_PREFIXES (uint8_t[]) { 32, 48, 56, 64, 128 }
+#define RRL_V6_RATE_MULT (kru_price_t[]) { 64, 4, 3, 2, 1 }
-static const struct cls_name rrl_cls_names[] = {
- { CLS_NORMAL, "POSITIVE" },
- { CLS_ERROR, "ERROR" },
- { CLS_NXDOMAIN, "NXDOMAIN"},
- { CLS_EMPTY, "EMPTY"},
- { CLS_LARGE, "LARGE"},
- { CLS_WILDCARD, "WILDCARD"},
- { CLS_ANY, "ANY"},
- { CLS_DNSSEC, "DNSSEC"},
- { CLS_NULL, "NULL"},
- { CLS_NULL, NULL}
-};
+#define RRL_V4_PREFIXES_CNT (sizeof(RRL_V4_PREFIXES) / sizeof(*RRL_V4_PREFIXES))
+#define RRL_V6_PREFIXES_CNT (sizeof(RRL_V6_PREFIXES) / sizeof(*RRL_V6_PREFIXES))
+#define RRL_MAX_PREFIXES_CNT ((RRL_V4_PREFIXES_CNT > RRL_V6_PREFIXES_CNT) ? RRL_V4_PREFIXES_CNT : RRL_V6_PREFIXES_CNT)
-static inline const char *rrl_clsstr(int code)
-{
- for (const struct cls_name *c = rrl_cls_names; c->name; c++) {
- if (c->code == code) {
- return c->name;
- }
- }
+#ifndef CLOCK_MONOTONIC_COARSE
+#define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC
+#endif
- return "unknown class";
-}
+#define RRL_LIMIT_KOEF 1/2 // Avoid probabilistic rounding wherever possible.
-/* Bucket flags. */
-enum {
- RRL_BF_NULL = 0 << 0, /* No flags. */
- RRL_BF_SSTART = 1 << 0, /* Bucket in slow-start after collision. */
- RRL_BF_ELIMIT = 1 << 1 /* Bucket is rate-limited. */
+struct rrl_table {
+ kru_price_t v4_prices[RRL_V4_PREFIXES_CNT];
+ kru_price_t v6_prices[RRL_V6_PREFIXES_CNT];
+ uint32_t log_period;
+ bool rw_mode;
+ _Atomic uint32_t log_time;
+ _Alignas(64) uint8_t kru[];
};
-static uint8_t rrl_clsid(rrl_req_t *p)
-{
- /* Check error code */
- int ret = CLS_NULL;
- switch (knot_wire_get_rcode(p->wire)) {
- case KNOT_RCODE_NOERROR: ret = CLS_NORMAL; break;
- case KNOT_RCODE_NXDOMAIN: return CLS_NXDOMAIN; break;
- default: return CLS_ERROR; break;
- }
-
- /* Check if answered from a qname */
- if (ret == CLS_NORMAL && p->flags & RRL_REQ_WILDCARD) {
- return CLS_WILDCARD;
- }
-
- /* Check query type for spec. classes. */
- if (p->query) {
- switch(knot_pkt_qtype(p->query)) {
- case KNOT_RRTYPE_ANY: /* ANY spec. class */
- return CLS_ANY;
- break;
- case KNOT_RRTYPE_DNSKEY:
- case KNOT_RRTYPE_RRSIG:
- case KNOT_RRTYPE_DS: /* DNSSEC-related RR class. */
- return CLS_DNSSEC;
- break;
- default:
- break;
- }
- }
-
- /* Check packet size for threshold. */
- if (p->len >= RRL_PSIZE_LARGE) {
- return CLS_LARGE;
- }
-
- /* Check ancount */
- if (knot_wire_get_ancount(p->wire) == 0) {
- return CLS_EMPTY;
- }
-
- return ret;
-}
-
-static int rrl_clsname(uint8_t *dst, size_t maxlen, uint8_t cls, rrl_req_t *req,
- const knot_dname_t *name)
+static void addr_tostr(char *dst, size_t maxlen, const struct sockaddr_storage *ss)
{
- if (name == NULL) {
- /* Fallback for errors etc. */
- name = (const knot_dname_t *)"\x00";
- }
-
- switch (cls) {
- case CLS_ERROR: /* Could be a non-existent zone or garbage. */
- case CLS_NXDOMAIN: /* Queries to non-existent names in zone. */
- case CLS_WILDCARD: /* Queries to names covered by a wildcard. */
- break;
- default:
- /* Use QNAME */
- if (req->query) {
- name = knot_pkt_qname(req->query);
- }
- break;
- }
+ assert(ss);
- /* Write to wire */
- return knot_dname_to_wire(dst, name, maxlen);
-}
-
-static int rrl_classify(uint8_t *dst, size_t maxlen, const struct sockaddr_storage *remote,
- rrl_req_t *req, const knot_dname_t *name)
-{
- /* Class */
- uint8_t cls = rrl_clsid(req);
- *dst = cls;
- int blklen = sizeof(cls);
-
- /* Address (in network byteorder, adjust masks). */
- uint64_t netblk = 0;
- if (remote->ss_family == AF_INET6) {
- struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)remote;
- memcpy(&netblk, &ipv6->sin6_addr, RRL_V6_PREFIX_LEN);
- } else {
- struct sockaddr_in *ipv4 = (struct sockaddr_in *)remote;
- memcpy(&netblk, &ipv4->sin_addr, RRL_V4_PREFIX_LEN);
- }
- memcpy(dst + blklen, &netblk, sizeof(netblk));
- blklen += sizeof(netblk);
-
- /* Name */
- int ret = rrl_clsname(dst + blklen, maxlen - blklen, cls, req, name);
- if (ret < 0) {
- return ret;
- }
- uint8_t len = ret;
- blklen += len;
-
- return blklen;
-}
-
-static int bucket_free(rrl_item_t *bucket, uint32_t now)
-{
- return bucket->cls == CLS_NULL || (bucket->time + 1 < now);
-}
-
-static int bucket_match(rrl_item_t *bucket, rrl_item_t *match)
-{
- return bucket->cls == match->cls &&
- bucket->netblk == match->netblk &&
- bucket->qname == match->qname;
-}
-
-static int find_free(rrl_table_t *tbl, unsigned id, uint32_t now)
-{
- for (int i = id; i < tbl->size; i++) {
- if (bucket_free(&tbl->arr[i], now)) {
- return i - id;
- }
- }
- for (int i = 0; i < id; i++) {
- if (bucket_free(&tbl->arr[i], now)) {
- return i + (tbl->size - id);
- }
- }
-
- /* this happens if table is full... force vacate current elm */
- return id;
-}
-
-static inline unsigned find_match(rrl_table_t *tbl, uint32_t id, rrl_item_t *m)
-{
- unsigned new_id = 0;
- unsigned hop = 0;
- unsigned match_bitmap = tbl->arr[id].hop;
- while (match_bitmap != 0) {
- hop = __builtin_ctz(match_bitmap); /* offset of next potential match */
- new_id = (id + hop) % tbl->size;
- if (bucket_match(&tbl->arr[new_id], m)) {
- return hop;
- } else {
- match_bitmap &= ~(1 << hop); /* clear potential match */
- }
- }
-
- return HOP_LEN + 1;
-}
-
-static inline unsigned reduce_dist(rrl_table_t *tbl, unsigned id, unsigned dist, unsigned *free_id)
-{
- unsigned rd = HOP_LEN - 1;
- while (rd > 0) {
- unsigned vacate_id = (tbl->size + *free_id - rd) % tbl->size; /* bucket to be vacated */
- if (tbl->arr[vacate_id].hop != 0) {
- unsigned hop = __builtin_ctz(tbl->arr[vacate_id].hop); /* offset of first valid bucket */
- if (hop < rd) { /* only offsets in <vacate_id, free_id> are interesting */
- unsigned new_id = (vacate_id + hop) % tbl->size; /* this item will be displaced to [free_id] */
- unsigned keep_hop = tbl->arr[*free_id].hop; /* unpredictable padding */
- memcpy(tbl->arr + *free_id, tbl->arr + new_id, sizeof(rrl_item_t));
- tbl->arr[*free_id].hop = keep_hop;
- tbl->arr[new_id].cls = CLS_NULL;
- tbl->arr[vacate_id].hop &= ~(1 << hop);
- tbl->arr[vacate_id].hop |= 1 << rd;
- *free_id = new_id;
- return dist - (rd - hop);
- }
- }
- --rd;
- }
-
- assert(rd == 0); /* this happens with p=1/fact(HOP_LEN) */
- *free_id = id;
- dist = 0; /* force vacate initial element */
- return dist;
-}
-
-static void subnet_tostr(char *dst, size_t maxlen, const struct sockaddr_storage *ss)
-{
const void *addr;
- const char *suffix;
if (ss->ss_family == AF_INET6) {
addr = &((struct sockaddr_in6 *)ss)->sin6_addr;
- suffix = "/56";
} else {
addr = &((struct sockaddr_in *)ss)->sin_addr;
- suffix = "/24";
}
- if (knot_inet_ntop(ss->ss_family, addr, dst, maxlen) != NULL) {
- strlcat(dst, suffix, maxlen);
- } else {
+ if (knot_inet_ntop(ss->ss_family, addr, dst, maxlen) == NULL) {
dst[0] = '\0';
}
}
-static void rrl_log_state(knotd_mod_t *mod, const struct sockaddr_storage *ss,
- uint16_t flags, uint8_t cls, const knot_dname_t *qname)
+static void rrl_log_limited(knotd_mod_t *mod, const struct sockaddr_storage *ss,
+ const uint8_t prefix, bool rate)
{
- if (mod == NULL || ss == NULL) {
+ if (mod == NULL) {
return;
}
char addr_str[SOCKADDR_STRLEN];
- subnet_tostr(addr_str, sizeof(addr_str), ss);
-
- const char *what = "leaves";
- if (flags & RRL_BF_ELIMIT) {
- what = "enters";
- }
+ addr_tostr(addr_str, sizeof(addr_str), ss);
- knot_dname_txt_storage_t buf;
- char *qname_str = knot_dname_to_str(buf, qname, sizeof(buf));
- if (qname_str == NULL) {
- qname_str = "?";
- }
-
- knotd_mod_log(mod, LOG_NOTICE, "address/subnet %s, class %s, qname %s, %s limiting",
- addr_str, rrl_clsstr(cls), qname_str, what);
+ knotd_mod_log(mod, LOG_NOTICE, "address %s limited on /%d by %s",
+ addr_str, prefix, rate ? "rate" : "time");
}
-static void rrl_lock(rrl_table_t *tbl, int lk_id)
+rrl_table_t *rrl_create(size_t size, uint32_t instant_limit, uint32_t rate_limit,
+ bool rw_mode, uint32_t log_period)
{
- assert(lk_id > -1);
- pthread_mutex_lock(tbl->lk + lk_id);
-}
-
-static void rrl_unlock(rrl_table_t *tbl, int lk_id)
-{
- assert(lk_id > -1);
- pthread_mutex_unlock(tbl->lk + lk_id);
-}
-
-static int rrl_setlocks(rrl_table_t *tbl, uint32_t granularity)
-{
- assert(!tbl->lk); /* Cannot change while locks are used. */
- assert(granularity <= tbl->size / 10); /* Due to int. division err. */
-
- if (pthread_mutex_init(&tbl->ll, NULL) < 0) {
- return KNOT_ENOMEM;
+ if (size == 0 || instant_limit == 0 || rate_limit == 0) {
+ return NULL;
}
- /* Alloc new locks. */
- tbl->lk = malloc(granularity * sizeof(pthread_mutex_t));
- if (!tbl->lk) {
- return KNOT_ENOMEM;
- }
- memset(tbl->lk, 0, granularity * sizeof(pthread_mutex_t));
+ size--;
+ size_t capacity_log = 1;
+ while (size >>= 1) capacity_log++;
- /* Initialize. */
- for (size_t i = 0; i < granularity; ++i) {
- if (pthread_mutex_init(tbl->lk + i, NULL) < 0) {
- break;
- }
- ++tbl->lk_count;
+ rrl_table_t *rrl;
+ size_t rrl_size = offsetof(struct rrl_table, kru) + KRU.get_size(capacity_log);
+ if (posix_memalign((void **)&rrl, 64, rrl_size) != 0) {
+ return NULL;
}
+ memset(rrl, 0, rrl_size);
- /* Incomplete initialization */
- if (tbl->lk_count != granularity) {
- for (size_t i = 0; i < tbl->lk_count; ++i) {
- pthread_mutex_destroy(tbl->lk + i);
- }
- free(tbl->lk);
- tbl->lk_count = 0;
- return KNOT_ERROR;
+ assert(rate_limit <= 1000ll * instant_limit); // Ensured by config check.
+ kru_price_t base_price = KRU_LIMIT / instant_limit;
+ const kru_price_t max_decay = (uint64_t)base_price * rate_limit / 1000;
+ if (!rw_mode) {
+ base_price = base_price * RRL_LIMIT_KOEF;
}
- return KNOT_EOK;
-}
-
-rrl_table_t *rrl_create(size_t size, uint32_t rate)
-{
- if (size == 0) {
+ if (!KRU.initialize((struct kru *)rrl->kru, capacity_log, max_decay)) {
+ free(rrl);
return NULL;
}
- const size_t tbl_len = sizeof(rrl_table_t) + size * sizeof(rrl_item_t);
- rrl_table_t *tbl = calloc(1, tbl_len);
- if (!tbl) {
- return NULL;
+ for (size_t i = 0; i < RRL_V4_PREFIXES_CNT; i++) {
+ rrl->v4_prices[i] = base_price / RRL_V4_RATE_MULT[i];
}
- tbl->size = size;
- tbl->rate = rate;
- if (dnssec_random_buffer((uint8_t *)&tbl->key, sizeof(tbl->key)) != DNSSEC_EOK) {
- free(tbl);
- return NULL;
+ for (size_t i = 0; i < RRL_V6_PREFIXES_CNT; i++) {
+ rrl->v6_prices[i] = base_price / RRL_V6_RATE_MULT[i];
}
- if (rrl_setlocks(tbl, RRL_LOCK_GRANULARITY) != KNOT_EOK) {
- free(tbl);
- return NULL;
- }
+ rrl->rw_mode = rw_mode;
+ rrl->log_period = log_period;
- return tbl;
-}
+ struct timespec now_ts;
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &now_ts);
+ uint32_t now = now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000;
+ rrl->log_time = now - log_period;
-static knot_dname_t *buf_qname(uint8_t *buf)
-{
- return buf + sizeof(uint8_t) + sizeof(uint64_t);
+ return rrl;
}
-/*! \brief Get bucket for current combination of parameters. */
-static rrl_item_t *rrl_hash(rrl_table_t *tbl, const struct sockaddr_storage *remote,
- rrl_req_t *req, const knot_dname_t *zone, uint32_t stamp,
- int *lock, uint8_t *buf, size_t buf_len)
+int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote, knotd_mod_t *mod)
{
- int len = rrl_classify(buf, buf_len, remote, req, zone);
- if (len < 0) {
- return NULL;
- }
+ assert(rrl);
+ assert(remote);
- uint32_t id = SipHash24(&tbl->key, buf, len) % tbl->size;
-
- /* Lock for lookup. */
- pthread_mutex_lock(&tbl->ll);
-
- /* Find an exact match in <id, id + HOP_LEN). */
- knot_dname_t *qname = buf_qname(buf);
- uint64_t netblk;
- memcpy(&netblk, buf + sizeof(uint8_t), sizeof(netblk));
- rrl_item_t match = {
- .hop = 0,
- .netblk = netblk,
- .ntok = tbl->rate * RRL_CAPACITY,
- .cls = buf[0],
- .flags = RRL_BF_NULL,
- .qname = SipHash24(&tbl->key, qname, knot_dname_size(qname)),
- .time = stamp
- };
-
- unsigned dist = find_match(tbl, id, &match);
- if (dist > HOP_LEN) { /* not an exact match, find free element [f] */
- dist = find_free(tbl, id, stamp);
- }
+ struct timespec now_ts;
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &now_ts);
+ uint32_t now = now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000;
- /* Reduce distance to fit <id, id + HOP_LEN) */
- unsigned free_id = (id + dist) % tbl->size;
- while (dist >= HOP_LEN) {
- dist = reduce_dist(tbl, id, dist, &free_id);
- }
+ uint16_t load = 0;
+ uint8_t prefix = 0;
+ _Alignas(16) uint8_t key[16] = { 0 };
+ if (remote->ss_family == AF_INET6) {
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)remote;
+ memcpy(key, &ipv6->sin6_addr, 16);
- /* Assign granular lock and unlock lookup. */
- *lock = free_id % tbl->lk_count;
- rrl_lock(tbl, *lock);
- pthread_mutex_unlock(&tbl->ll);
-
- /* found free bucket which is in <id, id + HOP_LEN) */
- tbl->arr[id].hop |= (1 << dist);
- rrl_item_t *bucket = &tbl->arr[free_id];
- assert(free_id == (id + dist) % tbl->size);
-
- /* Inspect bucket state. */
- unsigned hop = bucket->hop;
- if (bucket->cls == CLS_NULL) {
- memcpy(bucket, &match, sizeof(rrl_item_t));
- bucket->hop = hop;
+ if (rrl->rw_mode) {
+ prefix = KRU.limited_multi_prefix_or(
+ (struct kru *)rrl->kru, now, 1, key, RRL_V6_PREFIXES,
+ rrl->v6_prices, RRL_V6_PREFIXES_CNT, NULL);
+ } else {
+ load = KRU.load_multi_prefix_max(
+ (struct kru *)rrl->kru, now, 1, key, RRL_V6_PREFIXES,
+ NULL, RRL_V6_PREFIXES_CNT, &prefix);
+ }
+ } else {
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *)remote;
+ memcpy(key, &ipv4->sin_addr, 4);
+
+ if (rrl->rw_mode) {
+ prefix = KRU.limited_multi_prefix_or(
+ (struct kru *)rrl->kru, now, 0, key, RRL_V4_PREFIXES,
+ rrl->v4_prices, RRL_V4_PREFIXES_CNT, NULL);
+ } else {
+ load = KRU.load_multi_prefix_max(
+ (struct kru *)rrl->kru, now, 0, key, RRL_V4_PREFIXES,
+ NULL, RRL_V4_PREFIXES_CNT, &prefix);
+ }
}
- /* Check for collisions. */
- if (!bucket_match(bucket, &match)) {
- if (!(bucket->flags & RRL_BF_SSTART)) {
- memcpy(bucket, &match, sizeof(rrl_item_t));
- bucket->hop = hop;
- bucket->ntok = tbl->rate + tbl->rate / RRL_SSTART;
- bucket->flags |= RRL_BF_SSTART;
+
+ if (rrl->rw_mode) {
+ if (prefix == 0) {
+ return KNOT_EOK;
}
+ } else {
+ if (load <= (1 << 16) * RRL_LIMIT_KOEF) {
+ return KNOT_EOK;
+ }
+ }
+
+ uint32_t log_time_orig = atomic_load_explicit(&rrl->log_time, memory_order_relaxed);
+ if (rrl->log_period && (now - log_time_orig + 1024 >= rrl->log_period + 1024)) {
+ do {
+ if (atomic_compare_exchange_weak_explicit(&rrl->log_time, &log_time_orig, now,
+ memory_order_relaxed, memory_order_relaxed)) {
+ rrl_log_limited(mod, remote, prefix, rrl->rw_mode);
+ break;
+ }
+ } while (now - log_time_orig + 1024 >= rrl->log_period + 1024);
}
- return bucket;
+ return KNOT_ELIMIT;
}
-int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote,
- rrl_req_t *req, const knot_dname_t *zone, knotd_mod_t *mod)
+void rrl_update(rrl_table_t *rrl, const struct sockaddr_storage *remote, size_t value)
{
- if (!rrl || !req || !remote) {
- return KNOT_EINVAL;
- }
+ assert(rrl);
+ assert(remote);
+ assert(!rrl->rw_mode);
- uint8_t buf[RRL_CLSBLK_MAXLEN];
+ struct timespec now_ts;
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &now_ts);
+ uint32_t now = now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000;
- /* Calculate hash and fetch */
- int ret = KNOT_EOK;
- int lock = -1;
- uint32_t now = time_now().tv_sec;
- rrl_item_t *bucket = rrl_hash(rrl, remote, req, zone, now, &lock, buf, sizeof(buf));
- if (!bucket) {
- if (lock > -1) {
- rrl_unlock(rrl, lock);
- }
- return KNOT_ERROR;
- }
-
- /* Calculate rate for dT */
- uint32_t dt = now - bucket->time;
- if (dt > RRL_CAPACITY) {
- dt = RRL_CAPACITY;
- }
- /* Visit bucket. */
- bucket->time = now;
- if (dt > 0) { /* Window moved. */
-
- /* Check state change. */
- if ((bucket->ntok > 0 || dt > 1) && (bucket->flags & RRL_BF_ELIMIT)) {
- bucket->flags &= ~RRL_BF_ELIMIT;
- rrl_log_state(mod, remote, bucket->flags, bucket->cls,
- knot_pkt_qname(req->query));
- }
+ _Alignas(16) uint8_t key[16] = { 0 };
+ if (remote->ss_family == AF_INET6) {
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)remote;
+ memcpy(key, &ipv6->sin6_addr, 16);
- /* Add new tokens. */
- uint32_t dn = rrl->rate * dt;
- if (bucket->flags & RRL_BF_SSTART) { /* Bucket in slow-start. */
- bucket->flags &= ~RRL_BF_SSTART;
- }
- bucket->ntok += dn;
- if (bucket->ntok > RRL_CAPACITY * rrl->rate) {
- bucket->ntok = RRL_CAPACITY * rrl->rate;
+ kru_price_t prices[RRL_V6_PREFIXES_CNT];
+ for (size_t i = 0; i < RRL_V6_PREFIXES_CNT; i++) {
+ prices[i] = MIN(value * rrl->v6_prices[i], (kru_price_t)-1LL);
}
- }
- /* Last item taken. */
- if (bucket->ntok == 1 && !(bucket->flags & RRL_BF_ELIMIT)) {
- bucket->flags |= RRL_BF_ELIMIT;
- rrl_log_state(mod, remote, bucket->flags, bucket->cls,
- knot_pkt_qname(req->query));
- }
+ (void)KRU.load_multi_prefix_max((struct kru *)rrl->kru, now,
+ 1, key, RRL_V6_PREFIXES, prices,
+ RRL_V6_PREFIXES_CNT, NULL);
+ } else {
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *)remote;
+ memcpy(key, &ipv4->sin_addr, 4);
- /* Decay current bucket. */
- if (bucket->ntok > 0) {
- --bucket->ntok;
- } else if (bucket->ntok == 0) {
- ret = KNOT_ELIMIT;
- }
+ kru_price_t prices[RRL_V4_PREFIXES_CNT];
+ for (size_t i = 0; i < RRL_V4_PREFIXES_CNT; i++) {
+ prices[i] = MIN(value * rrl->v4_prices[i], (kru_price_t)-1LL);
+ }
- if (lock > -1) {
- rrl_unlock(rrl, lock);
+ (void)KRU.load_multi_prefix_max((struct kru *)rrl->kru, now,
+ 0, key, RRL_V4_PREFIXES, prices,
+ RRL_V4_PREFIXES_CNT, NULL);
}
- return ret;
}
bool rrl_slip_roll(int n_slip)
@@ -540,15 +247,5 @@ bool rrl_slip_roll(int n_slip)
void rrl_destroy(rrl_table_t *rrl)
{
- if (rrl) {
- if (rrl->lk_count > 0) {
- pthread_mutex_destroy(&rrl->ll);
- }
- for (size_t i = 0; i < rrl->lk_count; ++i) {
- pthread_mutex_destroy(rrl->lk + i);
- }
- free(rrl->lk);
- }
-
free(rrl);
}
diff --git a/src/knot/modules/rrl/functions.h b/src/knot/modules/rrl/functions.h
index 0f09234..0941c83 100644
--- a/src/knot/modules/rrl/functions.h
+++ b/src/knot/modules/rrl/functions.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,95 +17,63 @@
#pragma once
#include <stdint.h>
-#include <pthread.h>
#include <sys/socket.h>
-#include "libknot/libknot.h"
#include "knot/include/module.h"
-#include "contrib/openbsd/siphash.h"
-/*!
- * \brief RRL hash bucket.
- */
-typedef struct {
- unsigned hop; /* Hop bitmap. */
- uint64_t netblk; /* Prefix associated. */
- uint16_t ntok; /* Tokens available. */
- uint8_t cls; /* Bucket class. */
- uint8_t flags; /* Flags. */
- uint32_t qname; /* imputed(QNAME) hash. */
- uint32_t time; /* Timestamp. */
-} rrl_item_t;
+typedef struct rrl_table rrl_table_t;
/*!
- * \brief RRL hash bucket table.
+ * \brief Create a RRL table.
*
- * Table is fixed size, so collisions may occur and are dealt with
- * in a way, that hashbucket rate is reset and enters slow-start for 1 dt.
- * When a bucket is in a slow-start mode, it cannot reset again for the time
- * period.
+ * \param size Fixed table size.
+ * \param instant_limit Instant limit.
+ * \param rate_limit Rate limit.
+ * \param rw_mode If disabled, RW operation is divided into R and W operations.
+ * \param log_period If nonzero, maximum logging period (in milliseconds).
*
- * To avoid lock contention, N locks are created and distributed amongst buckets.
- * As of now lock K for bucket N is calculated as K = N % (num_buckets).
- */
-
-typedef struct {
- SIPHASH_KEY key; /* Siphash key. */
- uint32_t rate; /* Configured RRL limit. */
- pthread_mutex_t ll;
- pthread_mutex_t *lk; /* Table locks. */
- unsigned lk_count; /* Table lock count (granularity). */
- size_t size; /* Number of buckets. */
- rrl_item_t arr[]; /* Buckets. */
-} rrl_table_t;
-
-/*! \brief RRL request flags. */
-typedef enum {
- RRL_REQ_NOFLAG = 0 << 0, /*!< No flags. */
- RRL_REQ_WILDCARD = 1 << 1 /*!< Query to wildcard name. */
-} rrl_req_flag_t;
-
-/*!
- * \brief RRL request descriptor.
- */
-typedef struct {
- const uint8_t *wire;
- uint16_t len;
- rrl_req_flag_t flags;
- knot_pkt_t *query;
-} rrl_req_t;
-
-/*!
- * \brief Create a RRL table.
- * \param size Fixed hashtable size (reasonable large prime is recommended).
- * \param rate Rate (in pkts/sec).
* \return created table or NULL.
*/
-rrl_table_t *rrl_create(size_t size, uint32_t rate);
+rrl_table_t *rrl_create(size_t size, uint32_t instant_limit, uint32_t rate_limit,
+ bool rw_mode, uint32_t log_period);
/*!
* \brief Query the RRL table for accept or deny, when the rate limit is reached.
*
+ * \note This function is common to both RW and non-RW modes!
+ *
* \param rrl RRL table.
* \param remote Source address.
- * \param req RRL request (containing resp., flags and question).
- * \param zone Zone name related to the response (or NULL).
* \param mod Query module (needed for logging).
+ *
* \retval KNOT_EOK if passed.
* \retval KNOT_ELIMIT when the limit is reached.
*/
-int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote,
- rrl_req_t *req, const knot_dname_t *zone, knotd_mod_t *mod);
+int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote, knotd_mod_t *mod);
+
+/*!
+ * \brief Update the RRL table.
+ *
+ * \note This function is only for the non-RW mode!
+ *
+ * \param rrl RRL table.
+ * \param remote Source address.
+ * \param value Value with which the table is updated.
+ */
+void rrl_update(rrl_table_t *rrl, const struct sockaddr_storage *remote, size_t value);
/*!
* \brief Roll a dice whether answer slips or not.
+ *
* \param n_slip Number represents every Nth answer that is slipped.
+ *
* \return true or false
*/
bool rrl_slip_roll(int n_slip);
/*!
* \brief Destroy RRL table.
+ *
* \param rrl RRL table.
*/
void rrl_destroy(rrl_table_t *rrl);
diff --git a/src/knot/modules/rrl/kru-avx2.c b/src/knot/modules/rrl/kru-avx2.c
new file mode 100644
index 0000000..183ae44
--- /dev/null
+++ b/src/knot/modules/rrl/kru-avx2.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+// Checked with clang 5 (2017) and gcc 6 (2016).
+// For other cases we'll rather keep just the generic implementation.
+#if defined(__x86_64__) && (__clang_major__ >= 5 || __GNUC__ >= 6)
+
+// This file has code for new-ish x86 (2015+ usually, Atom 2021+) - AES + AVX2
+#if __clang_major__ >= 12
+ #pragma clang attribute push (__attribute__((target("arch=x86-64-v3,aes"))), \
+ apply_to = function)
+#elif __clang__
+ #pragma clang attribute push (__attribute__((target("avx2,aes"))), \
+ apply_to = function)
+#else
+ #pragma GCC push_options
+ #if __GNUC__ >= 11
+ #pragma GCC target("arch=x86-64-v3,aes")
+ // try harder for auto-vectorization, etc.
+ #pragma GCC optimize("O3")
+ #else
+ #pragma GCC target("avx2,aes")
+ #endif
+#endif
+
+#define USE_AES 1
+#define USE_AVX2 1
+#define USE_SSE41 1
+
+#include "./kru.inc.c"
+const struct kru_api KRU_AVX2 = KRU_API_INITIALIZER;
+
+#ifdef __clang__
+ #pragma clang attribute pop
+#else
+ #pragma GCC pop_options
+#endif
+
+__attribute__((constructor))
+static void detect_CPU_avx2(void)
+{
+ // Checking just AES+AVX2 will most likely be OK even if we used arch=x86-64-v3
+ if (__builtin_cpu_supports("aes") && __builtin_cpu_supports("avx2")) {
+ KRU = KRU_AVX2;
+ }
+}
+
+#else
+
+#include "./kru.h"
+const struct kru_api KRU_AVX2 = {NULL};
+
+#endif
diff --git a/src/knot/modules/rrl/kru-generic.c b/src/knot/modules/rrl/kru-generic.c
new file mode 100644
index 0000000..71ffdd4
--- /dev/null
+++ b/src/knot/modules/rrl/kru-generic.c
@@ -0,0 +1,20 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "./kru.inc.c"
+
+const struct kru_api KRU_GENERIC = KRU_API_INITIALIZER;
+struct kru_api KRU = KRU_API_INITIALIZER; // generic version is the default
diff --git a/src/knot/modules/rrl/kru.h b/src/knot/modules/rrl/kru.h
new file mode 100644
index 0000000..7eef6c0
--- /dev/null
+++ b/src/knot/modules/rrl/kru.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#define ALIGNED_CPU_CACHE _Alignas(64)
+
+// An unsigned integral type used for prices, blocking occurs when sum of prices overflows.
+// Greater than 16-bit type enables randomized fractional incrementing as the internal counters are still 16-bit.
+// Exponential decay always uses randomized rounding on 32 bits.
+typedef uint32_t kru_price_t;
+
+#define KRU_PRICE_BITS (8 * sizeof(kru_price_t))
+
+// maximal allowed sum of prices without limiting
+#define KRU_LIMIT (((kru_price_t)-1ll) - (1ll << (KRU_PRICE_BITS - 16)) + 1)
+
+struct kru;
+
+/// Usage: KRU.limited(...)
+struct kru_api {
+ /// Initialize a new KRU structure that can track roughly 2^capacity_log limited keys.
+ ///
+ /// The kru parameter should point to a zeroed preallocated memory
+ /// of size returned by get_size aligned to 64-bytes;
+ /// deallocate the memory to destroy KRU.
+ /// RAM: the current parametrization will use roughly 8 bytes * 2^capacity_log.
+ ///
+ /// The number of non-limited keys is basically arbitrary,
+ /// but the total sum of prices per tick (for queries returning false)
+ /// should not get over roughly 2^(capacity_log + 15).
+ /// Note that the _multi variants increase these totals
+ /// by tracking multiple keys in a single query.
+ ///
+ /// Returns false if kru is NULL or other failure occurs.
+ bool (*initialize)(struct kru *kru, int capacity_log, kru_price_t max_decay);
+
+ /// Calculate size of the KRU structure.
+ size_t (*get_size)(int capacity_log);
+
+ /// Determine if a key should get limited (and update the KRU).
+ /// key needs to be aligned to a multiple of 16 bytes.
+ bool (*limited)(struct kru *kru, uint32_t time_now, uint8_t key[static const 16], kru_price_t price);
+
+ /// Multiple queries. Returns OR of answers. Updates KRU only if no query is blocked (and possibly on race).
+ bool (*limited_multi_or)(struct kru *kru, uint32_t time_now, uint8_t **keys, kru_price_t *prices, size_t queries_cnt);
+
+ /// Same as previous but without short-circuit evaluation; for time measurement purposes.
+ bool (*limited_multi_or_nobreak)(struct kru *kru, uint32_t time_now, uint8_t ** keys, kru_price_t *prices, size_t queries_cnt);
+
+ /// Multiple queries based on different prefixes of a single key.
+ /// Returns a prefix (value in prefixes) on which the key is blocked, or zero if all queries passed.
+ /// Updates KRU only if no query is blocked, unless a race condition occurs --
+ /// in such a case all longer prefixes might have been updated.
+ /// The key of i-th query consists of prefixes[i] bits of key, prefixes[i], and namespace.
+ /// If zero is returned, *max_load_out (unless NULL) is set to
+ /// the maximum of final values of the involved counters normalized to the limit 2^16.
+ uint8_t (*limited_multi_prefix_or)(struct kru *kru, uint32_t time_now,
+ uint8_t namespace, uint8_t key[static 16], uint8_t *prefixes, kru_price_t *prices, size_t queries_cnt, uint16_t *max_load_out);
+
+ /// Multiple queries based on different prefixes of a single key.
+ /// Returns the maximum of final values of the involved counters normalized to the limit 2^16
+ /// and stores the corresponding prefix (value in prefixes) to *prefix_out (unless NULL).
+ /// Set prices to NULL to skip updating; otherwise, KRU is always updated, using maximal allowed value on overflow.
+ /// The key of i-th query consists of prefixes[i] bits of key, prefixes[i], and namespace.
+ uint16_t (*load_multi_prefix_max)(struct kru *kru, uint32_t time_now,
+ uint8_t namespace, uint8_t key[static 16], uint8_t *prefixes, kru_price_t *prices, size_t queries_cnt, uint8_t *prefix_out);
+};
+
+// The functions are stored this way to make it easier to switch
+// implementation based on detected CPU.
+extern struct kru_api KRU;
+extern const struct kru_api KRU_GENERIC, KRU_AVX2;
diff --git a/src/knot/modules/rrl/kru.inc.c b/src/knot/modules/rrl/kru.inc.c
new file mode 100644
index 0000000..49e359e
--- /dev/null
+++ b/src/knot/modules/rrl/kru.inc.c
@@ -0,0 +1,615 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+KRU estimates recently pricey inputs
+
+Authors of the simple agorithm (without aging, multi-choice, etc.):
+ Metwally, D. Agrawal, and A. E. Abbadi.
+ Efficient computation of frequent and top-k elements in data streams.
+ In International Conference on Database Theory, 2005.
+
+With TABLE_COUNT > 1 we're improving reliability by utilizing the property that
+longest buckets (cache-lines) get very much shortened, already by providing two choices:
+ https://en.wikipedia.org/wiki/2-choice_hashing
+
+The point is to answer point-queries that estimate if the item has been heavily used recently.
+To give more weight to recent usage, we use aging via exponential decay (simple to compute).
+That has applications for garbage collection of cache and various limiting scenario
+(excessive rate, traffic, CPU, maybe RAM).
+
+### Choosing parameters
+
+Size (`loads_bits` = log2 length):
+ - The KRU takes 64 bytes * length * TABLE_COUNT + some small constants.
+ As TABLE_COUNT == 2 and loads_bits = capacity_log >> 4, we get capacity * 8 Bytes.
+ - The length should probably be at least something like the square of the number of utilized CPUs.
+ But this most likely won't be a limiting factor.
+*/
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+
+#include "./kru.h"
+#include "contrib/macros.h"
+#include "libdnssec/error.h"
+#include "libdnssec/random.h"
+typedef uint64_t hash_t;
+#if USE_AES
+ /// 4-8 rounds should be an OK choice, most likely.
+ #define AES_ROUNDS 4
+#else
+ #include "contrib/openbsd/siphash.h"
+
+ /// 1,3 should be OK choice, probably.
+ enum {
+ SIPHASH_RC = 1,
+ SIPHASH_RF = 3,
+ };
+#endif
+
+#if USE_AVX2 || USE_SSE41 || USE_AES
+ #include <immintrin.h>
+ #include <x86intrin.h>
+#endif
+
+/// Block of loads sharing the same time, so that we're more space-efficient.
+/// It's exactly a single cache line.
+struct load_cl {
+ ALIGNED_CPU_CACHE
+ _Atomic uint32_t time;
+ #define LOADS_LEN 15
+ uint16_t ids[LOADS_LEN];
+ uint16_t loads[LOADS_LEN];
+};
+static_assert(64 == sizeof(struct load_cl), "bad size of struct load_cl");
+
+/// Parametrization for speed of decay.
+struct decay_config {
+ /// Bit shift per tick, fractional
+ double shift_bits;
+
+ /// Ticks to get zero loads
+ uint32_t max_ticks;
+
+ uint32_t mult_cache[32];
+};
+
+struct kru {
+#if USE_AES
+ /// Hashing secret. Random but shared by all users of the table.
+ /// Let's not make it too large, so that header fits into 64 Bytes.
+ _Alignas(32) char hash_key[48];
+#else
+ /// Hashing secret. Random but shared by all users of the table.
+ SIPHASH_KEY hash_key;
+#endif
+ struct decay_config decay;
+
+ /// Length of `loads_cls`, stored as binary logarithm.
+ uint32_t loads_bits;
+
+ #define TABLE_COUNT 2
+ /// These are read-write. Each struct has exactly one cache line.
+ struct load_cl load_cls[][TABLE_COUNT];
+};
+
+inline static uint64_t rand_bits(unsigned int bits)
+{
+ static _Thread_local uint64_t state = 3723796604792068981ull;
+ const uint64_t prime1 = 11737314301796036329ull;
+ const uint64_t prime2 = 3107264277052274849ull;
+ state = prime1 * state + prime2;
+ //return state & ((1 << bits) - 1);
+ return state >> (64 - bits);
+}
+
+static inline void decay_initialize(struct decay_config *decay, kru_price_t max_decay)
+{
+ decay->shift_bits = log2(KRU_LIMIT - 1) - log2(KRU_LIMIT - 1 - max_decay);
+ decay->max_ticks = 18 / decay->shift_bits;
+
+ decay->mult_cache[0] = 0; // not used
+ for (size_t ticks = 1; ticks < sizeof(decay->mult_cache) / sizeof(*decay->mult_cache); ticks++) {
+ decay->mult_cache[ticks] = exp2(32 - decay->shift_bits * ticks) + 0.5;
+ }
+}
+
+/// Catch up the time drift with configurably slower decay.
+static inline void update_time(struct load_cl *l, const uint32_t time_now,
+ const struct decay_config *decay)
+{
+ uint32_t ticks;
+ uint32_t time_last = atomic_load_explicit(&l->time, memory_order_relaxed);
+ do {
+ ticks = time_now - time_last;
+ if (__builtin_expect(!ticks, true)) // we optimize for time not advancing
+ return;
+ // We accept some desynchronization of time_now (e.g. from different threads).
+ if (ticks > (uint32_t)-1024)
+ return;
+ } while (!atomic_compare_exchange_weak_explicit(&l->time, &time_last, time_now, memory_order_relaxed, memory_order_relaxed));
+
+ // If we passed here, we have acquired a time difference we are responsibe for.
+
+ // Don't bother with complex computations if lots of ticks have passed. (little to no speed-up)
+ if (ticks > decay->max_ticks) {
+ memset(l->loads, 0, sizeof(l->loads));
+ return;
+ }
+
+ uint32_t mult;
+ if (__builtin_expect(ticks < sizeof(decay->mult_cache) / sizeof(*decay->mult_cache), 1)) {
+ mult = decay->mult_cache[ticks];
+ } else {
+ mult = exp2(32 - decay->shift_bits * ticks) + 0.5;
+ }
+
+ for (int i = 0; i < LOADS_LEN; ++i) {
+ // We perform decay for the acquired time difference; decays from different threads are commutative.
+ _Atomic uint16_t *load_at = (_Atomic uint16_t *)&l->loads[i];
+ uint16_t l1, load_orig = atomic_load_explicit(load_at, memory_order_relaxed);
+ const uint16_t rnd = rand_bits(16);
+ do {
+ uint64_t m = (((uint64_t)load_orig << 16)) * mult;
+ m = (m >> 32) + ((m >> 31) & 1);
+ l1 = (m >> 16) + (rnd < (uint16_t)m);
+ } while (!atomic_compare_exchange_weak_explicit(load_at, &load_orig, l1, memory_order_relaxed, memory_order_relaxed));
+ }
+}
+
+/// Convert capacity_log to loads_bits
+static inline int32_t capacity2loads(int capacity_log)
+{
+ static_assert(LOADS_LEN == 15 && TABLE_COUNT == 2, "");
+ // So, the pair of cache lines hold up to 2*15 elements.
+ // Let's say that we can reliably store 16 = 1 << (1+3).
+ // (probably more but certainly not 1 << 5)
+ const int shift = 1 + 3;
+ int loads_bits = capacity_log - shift;
+ // Let's behave reasonably for weird capacity_log values.
+ return loads_bits > 0 ? loads_bits : 1;
+}
+
+static size_t kru_get_size(int capacity_log)
+{
+ uint32_t loads_bits = capacity2loads(capacity_log);
+ if (8 * sizeof(hash_t) < TABLE_COUNT * loads_bits
+ + 8 * sizeof(((struct kru *)0)->load_cls[0]->ids[0])) {
+ assert(false);
+ return 0;
+ }
+
+ return offsetof(struct kru, load_cls)
+ + sizeof(struct load_cl) * TABLE_COUNT * (1 << loads_bits);
+}
+
+static bool kru_initialize(struct kru *kru, int capacity_log, kru_price_t max_decay)
+{
+ if (!kru) {
+ return false;
+ }
+
+ uint32_t loads_bits = capacity2loads(capacity_log);
+ if (8 * sizeof(hash_t) < TABLE_COUNT * loads_bits
+ + 8 * sizeof(((struct kru *)0)->load_cls[0]->ids[0])) {
+ assert(false);
+ return false;
+ }
+
+ kru->loads_bits = loads_bits;
+
+ if (dnssec_random_buffer((uint8_t *)&kru->hash_key, sizeof(kru->hash_key)) != DNSSEC_EOK) {
+ return false;
+ }
+
+ decay_initialize(&kru->decay, max_decay);
+
+ return true;
+}
+
+struct query_ctx {
+ struct load_cl *l[TABLE_COUNT];
+ uint32_t time_now;
+ kru_price_t price;
+ uint16_t price16, limit16;
+ uint16_t id;
+ uint16_t final_load_value; // set by kru_limited_update if not blocked
+ uint16_t *load;
+};
+
+/// Phase 1/3 of a query -- hash, prefetch, ctx init. Based on one 16-byte key.
+static inline void kru_limited_prefetch(struct kru *kru, uint32_t time_now, uint8_t key[static 16], kru_price_t price, struct query_ctx *ctx)
+{
+ // Obtain hash of *buf.
+ hash_t hash;
+#if !USE_AES
+ hash = SipHash(&kru->hash_key, SIPHASH_RC, SIPHASH_RF, key, 16);
+#else
+ {
+ __m128i h; /// hashing state
+ h = _mm_load_si128((__m128i *)key);
+ // Now do the the hashing itself.
+ __m128i *aes_key = (void*)kru->hash_key;
+ for (int i = 0; i < AES_ROUNDS; ++i) {
+ int key_id = i % (sizeof(kru->hash_key) / sizeof(__m128i));
+ h = _mm_aesenc_si128(h, _mm_load_si128(&aes_key[key_id]));
+ }
+ memcpy(&hash, &h, sizeof(hash));
+ }
+#endif
+
+ // Choose the cache-lines to operate on
+ const uint32_t loads_mask = (1 << kru->loads_bits) - 1;
+ // Fetch the two cache-lines in parallel before we really touch them.
+ for (int li = 0; li < TABLE_COUNT; ++li) {
+ struct load_cl * const l = &kru->load_cls[hash & loads_mask][li];
+ __builtin_prefetch(l, 0); // hope for read-only access
+ hash >>= kru->loads_bits;
+ ctx->l[li] = l;
+ }
+
+ ctx->time_now = time_now;
+ ctx->price = price;
+ ctx->id = hash;
+}
+
+/// Phase 1/3 of a query -- hash, prefetch, ctx init. Based on a bit prefix of one 16-byte key.
+static inline void kru_limited_prefetch_prefix(struct kru *kru, uint32_t time_now, uint8_t namespace, uint8_t key[static 16], uint8_t prefix, kru_price_t price, struct query_ctx *ctx)
+{
+ // Obtain hash of *buf.
+ hash_t hash;
+
+#if !USE_AES
+ {
+ const int rc = SIPHASH_RC, rf = SIPHASH_RF;
+
+ // Hash prefix of key, prefix size, and namespace together.
+ SIPHASH_CTX hctx;
+ SipHash_Init(&hctx, &kru->hash_key);
+ SipHash_Update(&hctx, rc, rf, &namespace, sizeof(namespace));
+ SipHash_Update(&hctx, rc, rf, &prefix, sizeof(prefix));
+ SipHash_Update(&hctx, rc, rf, key, prefix / 8);
+ if (prefix % 8) {
+ const uint8_t masked_byte = key[prefix / 8] & (0xFF00 >> (prefix % 8));
+ SipHash_Update(&hctx, rc, rf, &masked_byte, 1);
+ }
+ hash = SipHash_End(&hctx, rc, rf);
+ }
+#else
+ {
+
+ __m128i h; /// hashing state
+ h = _mm_load_si128((__m128i *)key);
+
+ { // Keep only the prefix.
+ const uint8_t p = prefix;
+
+ // Prefix mask (1...0) -> little endian byte array (0x00 ... 0x00 0xFF ... 0xFF).
+ __m128i mask = _mm_set_epi64x(
+ (p < 64 ? (p == 0 ? 0 : (uint64_t)-1 << (64 - p)) : (uint64_t)-1), // higher 64 bits (1...) -> second half of byte array (... 0xFF)
+ (p <= 64 ? 0 : (uint64_t)-1 << (128 - p))); // lower 64 bits (...0) -> first half of byte array (0x00 ...)
+
+ // Swap mask endianness (0x11 ... 0x11 0x00 ... 0x00).
+ mask = _mm_shuffle_epi8(mask,
+ _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
+
+ // Apply mask.
+ h = _mm_and_si128(h, mask);
+ }
+
+ // Now do the the hashing itself.
+ __m128i *aes_key = (void*)kru->hash_key;
+ {
+ // Mix namespace and prefix size into the first aes key.
+ __m128i aes_key1 = _mm_insert_epi16(_mm_load_si128(aes_key), (namespace << 8) | prefix, 0);
+ h = _mm_aesenc_si128(h, aes_key1);
+ }
+ for (int j = 1; j < AES_ROUNDS; ++j) {
+ int key_id = j % (sizeof(kru->hash_key) / sizeof(__m128i));
+ h = _mm_aesenc_si128(h, _mm_load_si128(&aes_key[key_id]));
+ }
+ memcpy(&hash, &h, sizeof(hash));
+ }
+#endif
+
+ // Choose the cache-lines to operate on
+ const uint32_t loads_mask = (1 << kru->loads_bits) - 1;
+ // Fetch the two cache-lines in parallel before we really touch them.
+ for (int li = 0; li < TABLE_COUNT; ++li) {
+ struct load_cl * const l = &kru->load_cls[hash & loads_mask][li];
+ __builtin_prefetch(l, 0); // hope for read-only access
+ hash >>= kru->loads_bits;
+ ctx->l[li] = l;
+ }
+
+ ctx->time_now = time_now;
+ ctx->price = price;
+ ctx->id = hash;
+}
+
+/// Phase 2/3 of a query -- returns answer with no state modification (except update_time).
+static inline bool kru_limited_fetch(struct kru *kru, struct query_ctx *ctx)
+{
+ // Compute 16-bit limit and price.
+ // For 32-bit prices we assume that a 16-bit load value corresponds
+ // to the 32-bit value extended by low-significant ones and the limit is 2^32 (without ones).
+ // The 16-bit price is thus rounded up for the comparison with limit,
+ // but rounded probabilistically for rising the load.
+ {
+ const int fract_bits = 8 * sizeof(ctx->price) - 16;
+ const kru_price_t price = ctx->price;
+ const kru_price_t fract = price & ((((kru_price_t)1) << fract_bits) - 1);
+
+ ctx->price16 = price >> fract_bits;
+ ctx->limit16 = -ctx->price16;
+
+ if ((fract_bits > 0) && (fract > 0)) {
+ ctx->price16 += (rand_bits(fract_bits) < fract);
+ ctx->limit16--;
+ }
+ }
+
+ for (int li = 0; li < TABLE_COUNT; ++li) {
+ update_time(ctx->l[li], ctx->time_now, &kru->decay);
+ }
+
+ const uint16_t id = ctx->id;
+
+ // Find matching element. Matching 16 bits in addition to loads_bits.
+ ctx->load = NULL;
+#if !USE_AVX2
+ for (int li = 0; li < TABLE_COUNT; ++li)
+ for (int i = 0; i < LOADS_LEN; ++i)
+ if (ctx->l[li]->ids[i] == id) {
+ ctx->load = &ctx->l[li]->loads[i];
+ goto load_found;
+ }
+#else
+ const __m256i id_v = _mm256_set1_epi16(id);
+ for (int li = 0; li < TABLE_COUNT; ++li) {
+ static_assert(LOADS_LEN == 15 && sizeof(ctx->l[li]->ids[0]) == 2, "");
+ // unfortunately we can't use aligned load here
+ __m256i ids_v = _mm256_loadu_si256((__m256i *)((uint16_t *)ctx->l[li]->ids - 1));
+ __m256i match_mask = _mm256_cmpeq_epi16(ids_v, id_v);
+ if (_mm256_testz_si256(match_mask, match_mask))
+ continue; // no match of id
+ int index = _bit_scan_reverse(_mm256_movemask_epi8(match_mask)) / 2 - 1;
+ // there's a small possibility that we hit equality only on the -1 index
+ if (index >= 0) {
+ ctx->load = &ctx->l[li]->loads[index];
+ goto load_found;
+ }
+ }
+#endif
+
+ ctx->final_load_value = 0;
+ return false;
+
+load_found:;
+ ctx->final_load_value = *ctx->load;
+ return (ctx->final_load_value >= ctx->limit16);
+}
+
+/// Phase 3/3 of a query -- state update, return value overrides previous answer in case of race.
+/// Not needed if blocked by fetch phase. If overflow_update is activated, false is always returned.
+static inline bool kru_limited_update(struct kru *kru, struct query_ctx *ctx, bool overflow_update)
+{
+ _Atomic uint16_t *load_at;
+ if (!ctx->load) {
+ // No match, so find position of the smallest load.
+ int min_li = 0;
+ int min_i = 0;
+#if !USE_SSE41
+ for (int li = 0; li < TABLE_COUNT; ++li)
+ for (int i = 0; i < LOADS_LEN; ++i)
+ if (ctx->l[li]->loads[i] < ctx->l[min_li]->loads[min_i]) {
+ min_li = li;
+ min_i = i;
+ }
+#else
+ int min_val = 0;
+ for (int li = 0; li < TABLE_COUNT; ++li) {
+ // BEWARE: we're relying on the exact memory layout of struct load_cl,
+ // where the .loads array take 15 16-bit values at the very end.
+ static_assert((offsetof(struct load_cl, loads) - 2) % 16 == 0,
+ "bad alignment of struct load_cl::loads");
+ static_assert(LOADS_LEN == 15 && sizeof(ctx->l[li]->loads[0]) == 2, "");
+ __m128i *l_v = (__m128i *)((uint16_t *)ctx->l[li]->loads - 1);
+ __m128i l0 = _mm_load_si128(l_v);
+ __m128i l1 = _mm_load_si128(l_v + 1);
+ // We want to avoid the first item in l0, so we maximize it.
+ // (but this function takes a signed integer, so -1 is the maximum)
+ l0 = _mm_insert_epi16(l0, -1, 0);
+
+ // Only one instruction can find minimum and its position,
+ // and it works on 8x uint16_t.
+ __m128i mp0 = _mm_minpos_epu16(l0);
+ __m128i mp1 = _mm_minpos_epu16(l1);
+ int min0 = _mm_extract_epi16(mp0, 0);
+ int min1 = _mm_extract_epi16(mp1, 0);
+ int min01, min_ix;
+ if (min0 < min1) {
+ min01 = min0;
+ min_ix = _mm_extract_epi16(mp0, 1);
+ } else {
+ min01 = min1;
+ min_ix = 8 + _mm_extract_epi16(mp1, 1);
+ }
+
+ if (li == 0 || min_val > min01) {
+ min_li = li;
+ min_i = min_ix;
+ min_val = min01;
+ }
+ }
+ // now, min_i (and min_ix) is offset by one due to alignment of .loads
+ if (min_i != 0) // zero is very unlikely
+ --min_i;
+#endif
+
+ ctx->l[min_li]->ids[min_i] = ctx->id;
+ load_at = (_Atomic uint16_t *)&ctx->l[min_li]->loads[min_i];
+ } else {
+ load_at = (_Atomic uint16_t *)ctx->load;
+ }
+
+ static_assert(ATOMIC_CHAR16_T_LOCK_FREE == 2, "insufficient atomics");
+ const uint16_t price = ctx->price16;
+ const uint16_t limit = ctx->limit16;
+ uint16_t load_orig = atomic_load_explicit(load_at, memory_order_relaxed);
+ uint16_t load_new;
+ do {
+ if (load_orig >= limit) {
+ if (overflow_update) {
+ load_new = -1;
+ } else {
+ return true;
+ }
+ } else {
+ load_new = load_orig + price;
+ }
+ } while (!atomic_compare_exchange_weak_explicit(load_at, &load_orig, load_new, memory_order_relaxed, memory_order_relaxed));
+
+ ctx->final_load_value = load_new;
+ return false;
+}
+
+static bool kru_limited_multi_or(struct kru *kru, uint32_t time_now, uint8_t **keys, kru_price_t *prices, size_t queries_cnt)
+{
+ struct query_ctx ctx[queries_cnt];
+
+ for (size_t i = 0; i < queries_cnt; i++) {
+ kru_limited_prefetch(kru, time_now, keys[i], prices[i], ctx + i);
+ }
+ for (size_t i = 0; i < queries_cnt; i++) {
+ if (kru_limited_fetch(kru, ctx + i))
+ return true;
+ }
+ bool ret = false;
+
+ for (size_t i = 0; i < queries_cnt; i++) {
+ ret |= kru_limited_update(kru, ctx + i, false);
+ }
+
+ return ret;
+}
+
+static bool kru_limited_multi_or_nobreak(struct kru *kru, uint32_t time_now, uint8_t **keys, kru_price_t *prices, size_t queries_cnt)
+{
+ struct query_ctx ctx[queries_cnt];
+ bool ret = false;
+
+ for (size_t i = 0; i < queries_cnt; i++) {
+ kru_limited_prefetch(kru, time_now, keys[i], prices[i], ctx + i);
+ }
+ for (size_t i = 0; i < queries_cnt; i++) {
+ if (kru_limited_fetch(kru, ctx + i))
+ ret = true;
+ }
+ if (ret) return true;
+
+ for (size_t i = 0; i < queries_cnt; i++) {
+ if (kru_limited_update(kru, ctx + i, false))
+ ret = true;
+ }
+
+ return ret;
+}
+
+static uint8_t kru_limited_multi_prefix_or(struct kru *kru, uint32_t time_now, uint8_t namespace,
+ uint8_t key[static 16], uint8_t *prefixes, kru_price_t *prices, size_t queries_cnt, uint16_t *max_load_out)
+{
+ struct query_ctx ctx[queries_cnt];
+
+ for (size_t i = 0; i < queries_cnt; i++) {
+ kru_limited_prefetch_prefix(kru, time_now, namespace, key, prefixes[i], prices[i], ctx + i);
+ }
+
+ for (size_t i = 0; i < queries_cnt; i++) {
+ if (kru_limited_fetch(kru, ctx + i))
+ return prefixes[i];
+ }
+
+ for (int i = queries_cnt - 1; i >= 0; i--) {
+ if (kru_limited_update(kru, ctx + i, false))
+ return prefixes[i];
+ }
+
+ if (max_load_out) {
+ *max_load_out = 0;
+ for (size_t i = 0; i < queries_cnt; i++) {
+ *max_load_out = MAX(*max_load_out, ctx[i].final_load_value);
+ }
+ }
+
+ return 0;
+}
+
+static uint16_t kru_load_multi_prefix_max(struct kru *kru, uint32_t time_now, uint8_t namespace,
+ uint8_t key[static 16], uint8_t *prefixes, kru_price_t *prices, size_t queries_cnt, uint8_t *prefix_out)
+{
+ struct query_ctx ctx[queries_cnt];
+
+ for (size_t i = 0; i < queries_cnt; i++) {
+ kru_limited_prefetch_prefix(kru, time_now, namespace, key, prefixes[i], (prices ? prices[i] : 0), ctx + i);
+ }
+
+ for (size_t i = 0; i < queries_cnt; i++) {
+ kru_limited_fetch(kru, ctx + i);
+ }
+
+ if (prices) {
+ for (int i = queries_cnt - 1; i >= 0; i--) {
+ kru_limited_update(kru, ctx + i, true);
+ }
+ }
+
+ uint8_t prefix = 0;
+ uint16_t max_load = 0;
+ for (size_t i = 0; i < queries_cnt; i++) {
+ if (max_load < ctx[i].final_load_value) {
+ max_load = ctx[i].final_load_value;
+ prefix = prefixes[i];
+ }
+ }
+ if (prefix_out) {
+ *prefix_out = prefix;
+ }
+
+ return max_load;
+}
+
+/// Update limiting and return true iff it hit the limit instead.
+static bool kru_limited(struct kru *kru, uint32_t time_now, uint8_t key[static 16], kru_price_t price)
+{
+ return kru_limited_multi_or(kru, time_now, &key, &price, 1);
+}
+
+#define KRU_API_INITIALIZER { \
+ .get_size = kru_get_size, \
+ .initialize = kru_initialize, \
+ .limited = kru_limited, \
+ .limited_multi_or = kru_limited_multi_or, \
+ .limited_multi_or_nobreak = kru_limited_multi_or_nobreak, \
+ .limited_multi_prefix_or = kru_limited_multi_prefix_or, \
+ .load_multi_prefix_max = kru_load_multi_prefix_max, \
+}
diff --git a/src/knot/modules/rrl/rrl.c b/src/knot/modules/rrl/rrl.c
index 64f6cbf..d1ec7b5 100644
--- a/src/knot/modules/rrl/rrl.c
+++ b/src/knot/modules/rrl/rrl.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,28 +14,47 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+#include "contrib/time.h"
#include "knot/include/module.h"
-#include "knot/nameserver/process_query.h" // Dependency on qdata->extra!
#include "knot/modules/rrl/functions.h"
+#include "knot/modules/rrl/kru.h"
#define MOD_RATE_LIMIT "\x0A""rate-limit"
+#define MOD_INST_LIMIT "\x0D""instant-limit"
+#define MOD_T_RATE_LIMIT "\x0F""time-rate-limit"
+#define MOD_T_INST_LIMIT "\x12""time-instant-limit"
#define MOD_SLIP "\x04""slip"
#define MOD_TBL_SIZE "\x0A""table-size"
#define MOD_WHITELIST "\x09""whitelist"
+#define MOD_LOG_PERIOD "\x0A""log-period"
+#define MOD_DRY_RUN "\x07""dry-run"
const yp_item_t rrl_conf[] = {
- { MOD_RATE_LIMIT, YP_TINT, YP_VINT = { 1, INT32_MAX } },
- { MOD_SLIP, YP_TINT, YP_VINT = { 0, 100, 1 } },
- { MOD_TBL_SIZE, YP_TINT, YP_VINT = { 1, INT32_MAX, 393241 } },
- { MOD_WHITELIST, YP_TNET, YP_VNONE, YP_FMULTI },
+ { MOD_INST_LIMIT, YP_TINT, YP_VINT = { 1, (1ll << 32) / 768 - 1, 50 } },
+ { MOD_RATE_LIMIT, YP_TINT, YP_VINT = { 0, ((1ll << 32) / 768 - 1) * 1000, 20 } },
+ { MOD_T_INST_LIMIT, YP_TINT, YP_VINT = { 1, 1000000, 5000 } },
+ { MOD_T_RATE_LIMIT, YP_TINT, YP_VINT = { 0, 1000000000, 4000 } },
+ { MOD_SLIP, YP_TINT, YP_VINT = { 0, 100, 1 } },
+ { MOD_TBL_SIZE, YP_TINT, YP_VINT = { 1, INT32_MAX, 524288 } },
+ { MOD_WHITELIST, YP_TNET, YP_VNONE, YP_FMULTI },
+ { MOD_LOG_PERIOD, YP_TINT, YP_VINT = { 0, INT32_MAX, 0 } },
+ { MOD_DRY_RUN, YP_TBOOL, YP_VNONE },
{ NULL }
};
int rrl_conf_check(knotd_conf_check_args_t *args)
{
- knotd_conf_t limit = knotd_conf_check_item(args, MOD_RATE_LIMIT);
- if (limit.count == 0) {
- args->err_str = "no rate limit specified";
+ knotd_conf_t rate_limit = knotd_conf_check_item(args, MOD_RATE_LIMIT);
+ knotd_conf_t inst_limit = knotd_conf_check_item(args, MOD_INST_LIMIT);
+ if (rate_limit.single.integer > 1000ll * inst_limit.single.integer) {
+ args->err_str = "rate limit is higher than 1000 times instant rate limit";
+ return KNOT_EINVAL;
+ }
+
+ knotd_conf_t t_rate_limit = knotd_conf_check_item(args, MOD_T_RATE_LIMIT);
+ knotd_conf_t t_inst_limit = knotd_conf_check_item(args, MOD_T_INST_LIMIT);
+ if (t_rate_limit.single.integer > 1000ll * t_inst_limit.single.integer) {
+ args->err_str = "time rate limit is higher than 1000 times time instant limit";
return KNOT_EINVAL;
}
@@ -43,35 +62,78 @@ int rrl_conf_check(knotd_conf_check_args_t *args)
}
typedef struct {
- rrl_table_t *rrl;
+ ALIGNED_CPU_CACHE // Ensures that one thread context occupies one cache line.
+ struct timespec start_time; // Start time of the measurement.
+ bool whitelist_checked; // Indication whether whitelist check took place.
+ bool skip; // Skip the rest of the module callbacks.
+} thrd_ctx_t;
+
+typedef struct {
+ rrl_table_t *rate_table;
+ rrl_table_t *time_table;
+ thrd_ctx_t *thrd_ctx;
int slip;
+ bool dry_run;
knotd_conf_t whitelist;
} rrl_ctx_t;
-static const knot_dname_t *name_from_rrsig(const knot_rrset_t *rr)
+static uint32_t time_diff_us(const struct timespec *begin, const struct timespec *end)
+{
+ struct timespec result = time_diff(begin, end);
+ return (result.tv_sec * 1000000) + (result.tv_nsec / 1000);
+}
+
+static knotd_proto_state_t protolimit_start(knotd_proto_state_t state,
+ knotd_qdata_params_t *params,
+ knotd_mod_t *mod)
{
- if (rr == NULL) {
- return NULL;
+ rrl_ctx_t *ctx = knotd_mod_ctx(mod);
+ thrd_ctx_t *thrd = &ctx->thrd_ctx[params->thread_id];
+ thrd->skip = false;
+
+ // Check if a whitelisted client.
+ thrd->whitelist_checked = true;
+ if (knotd_conf_addr_range_match(&ctx->whitelist, params->remote)) {
+ thrd->skip = true;
+ return state;
}
- if (rr->type != KNOT_RRTYPE_RRSIG) {
- return NULL;
+
+ // UDP time limiting not implemented (source address can be forged).
+ if (params->proto == KNOTD_QUERY_PROTO_UDP) {
+ return state;
}
- // This is a signature.
- return knot_rrsig_signer_name(rr->rrs.rdata);
+ // Check if the packet is limited.
+ if (rrl_query(ctx->time_table, params->remote, mod) != KNOT_EOK) {
+ thrd->skip = true;
+ knotd_mod_stats_incr(mod, params->thread_id, 2, 0, 1);
+ return ctx->dry_run ? state : KNOTD_PROTO_STATE_BLOCK;
+ } else {
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &thrd->start_time);
+ return state; // Not limited.
+ }
}
-static const knot_dname_t *name_from_authrr(const knot_rrset_t *rr)
+static knotd_proto_state_t protolimit_end(knotd_proto_state_t state,
+ knotd_qdata_params_t *params,
+ knotd_mod_t *mod)
{
- if (rr == NULL) {
- return NULL;
+ rrl_ctx_t *ctx = knotd_mod_ctx(mod);
+ thrd_ctx_t *thrd = &ctx->thrd_ctx[params->thread_id];
+
+ if (thrd->skip || params->proto == KNOTD_QUERY_PROTO_UDP) {
+ return state;
}
- if (rr->type != KNOT_RRTYPE_NS && rr->type != KNOT_RRTYPE_SOA) {
- return NULL;
+
+ // Update the time table.
+ struct timespec end_time;
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end_time);
+ uint64_t diff = time_diff_us(&thrd->start_time, &end_time);
+ if (diff > 0) { // Zero KRU update is NOOP.
+ rrl_update(ctx->time_table, params->remote, diff);
}
- // This is a valid authority RR.
- return rr->owner;
+ return state;
}
static knotd_state_t ratelimit_apply(knotd_state_t state, knot_pkt_t *pkt,
@@ -80,57 +142,36 @@ static knotd_state_t ratelimit_apply(knotd_state_t state, knot_pkt_t *pkt,
assert(pkt && qdata && mod);
rrl_ctx_t *ctx = knotd_mod_ctx(mod);
+ thrd_ctx_t *thrd = &ctx->thrd_ctx[qdata->params->thread_id];
- // Rate limit is applied to pure UDP only.
- if (qdata->params->proto != KNOTD_QUERY_PROTO_UDP) {
+ if (thrd->skip) {
return state;
}
- // Rate limit is not applied to responses with a valid cookie.
- if (qdata->params->flags & KNOTD_QUERY_FLAG_COOKIE) {
+ // Don't limit authorized operations.
+ if (qdata->params->flags & KNOTD_QUERY_FLAG_AUTHORIZED) {
+ thrd->skip = true;
return state;
}
- // Exempt clients.
- if (knotd_conf_addr_range_match(&ctx->whitelist, knotd_qdata_remote_addr(qdata))) {
+ // Rate limit is applied to UDP only.
+ if (qdata->params->proto != KNOTD_QUERY_PROTO_UDP) {
return state;
}
- rrl_req_t req = {
- .wire = pkt->wire,
- .query = qdata->query
- };
-
- if (!EMPTY_LIST(qdata->extra->wildcards)) {
- req.flags = RRL_REQ_WILDCARD;
- }
-
- // Take the zone name if known.
- const knot_dname_t *zone_name = knotd_qdata_zone_name(qdata);
-
- // Take the signer name as zone name if there is an RRSIG.
- if (zone_name == NULL) {
- const knot_pktsection_t *ans = knot_pkt_section(pkt, KNOT_ANSWER);
- for (int i = 0; i < ans->count; i++) {
- zone_name = name_from_rrsig(knot_pkt_rr(ans, i));
- if (zone_name != NULL) {
- break;
- }
- }
+ // Check for whitelisted client IF PER-ZONE module (no proto callbacks).
+ if (!thrd->whitelist_checked &&
+ knotd_conf_addr_range_match(&ctx->whitelist, qdata->params->remote)) {
+ thrd->skip = true;
+ return state;
}
- // Take the NS or SOA owner name if there is no RRSIG.
- if (zone_name == NULL) {
- const knot_pktsection_t *auth = knot_pkt_section(pkt, KNOT_AUTHORITY);
- for (int i = 0; i < auth->count; i++) {
- zone_name = name_from_authrr(knot_pkt_rr(auth, i));
- if (zone_name != NULL) {
- break;
- }
- }
+ // Rate limit is not applied to responses with a valid cookie.
+ if (qdata->params->flags & KNOTD_QUERY_FLAG_COOKIE) {
+ return state;
}
- if (rrl_query(ctx->rrl, knotd_qdata_remote_addr(qdata), &req, zone_name, mod) == KNOT_EOK) {
+ if (rrl_query(ctx->rate_table, knotd_qdata_remote_addr(qdata), mod) == KNOT_EOK) {
// Rate limiting not applied.
return state;
}
@@ -139,11 +180,11 @@ static knotd_state_t ratelimit_apply(knotd_state_t state, knot_pkt_t *pkt,
// Slip the answer.
knotd_mod_stats_incr(mod, qdata->params->thread_id, 0, 0, 1);
qdata->err_truncated = true;
- return KNOTD_STATE_FAIL;
+ return ctx->dry_run ? state : KNOTD_STATE_FAIL;
} else {
// Drop the answer.
knotd_mod_stats_incr(mod, qdata->params->thread_id, 1, 0, 1);
- return KNOTD_STATE_NOOP;
+ return ctx->dry_run ? state : KNOTD_STATE_NOOP;
}
}
@@ -151,58 +192,96 @@ static void ctx_free(rrl_ctx_t *ctx)
{
assert(ctx);
- rrl_destroy(ctx->rrl);
+ free(ctx->thrd_ctx);
+ rrl_destroy(ctx->rate_table);
+ rrl_destroy(ctx->time_table);
+ knotd_conf_free(&ctx->whitelist);
free(ctx);
}
int rrl_load(knotd_mod_t *mod)
{
- // Create RRL context.
rrl_ctx_t *ctx = calloc(1, sizeof(rrl_ctx_t));
if (ctx == NULL) {
return KNOT_ENOMEM;
}
- // Create table.
- uint32_t rate = knotd_conf_mod(mod, MOD_RATE_LIMIT).single.integer;
- size_t size = knotd_conf_mod(mod, MOD_TBL_SIZE).single.integer;
- ctx->rrl = rrl_create(size, rate);
- if (ctx->rrl == NULL) {
+ ctx->dry_run = knotd_conf_mod(mod, MOD_DRY_RUN).single.boolean;
+ ctx->whitelist = knotd_conf_mod(mod, MOD_WHITELIST);
+
+ ctx->thrd_ctx = calloc(knotd_mod_threads(mod), sizeof(*ctx->thrd_ctx));
+ if (ctx->thrd_ctx == NULL) {
ctx_free(ctx);
return KNOT_ENOMEM;
}
- // Get slip.
- ctx->slip = knotd_conf_mod(mod, MOD_SLIP).single.integer;
+ size_t size = knotd_conf_mod(mod, MOD_TBL_SIZE).single.integer;
+ uint32_t log_period = knotd_conf_mod(mod, MOD_LOG_PERIOD).single.integer;
+
+ uint32_t rate_limit = knotd_conf_mod(mod, MOD_RATE_LIMIT).single.integer;
+ if (rate_limit > 0) {
+ uint32_t inst_limit = knotd_conf_mod(mod, MOD_INST_LIMIT).single.integer;
+ ctx->rate_table = rrl_create(size, inst_limit, rate_limit, true, log_period);
+ if (ctx->rate_table == NULL) {
+ ctx_free(ctx);
+ return KNOT_ENOMEM;
+ }
+ ctx->slip = knotd_conf_mod(mod, MOD_SLIP).single.integer;
+ }
- // Get whitelist.
- ctx->whitelist = knotd_conf_mod(mod, MOD_WHITELIST);
+ uint32_t time_limit = knotd_conf_mod(mod, MOD_T_RATE_LIMIT).single.integer;
+ if (time_limit > 0) {
+ uint32_t inst_limit = knotd_conf_mod(mod, MOD_T_INST_LIMIT).single.integer;
+ ctx->time_table = rrl_create(size, inst_limit, time_limit, false, log_period);
+ if (ctx->time_table == NULL) {
+ ctx_free(ctx);
+ return KNOT_ENOMEM;
+ }
+ }
- // Set up statistics counters.
int ret = knotd_mod_stats_add(mod, "slipped", 1, NULL);
if (ret != KNOT_EOK) {
ctx_free(ctx);
return ret;
}
-
ret = knotd_mod_stats_add(mod, "dropped", 1, NULL);
if (ret != KNOT_EOK) {
ctx_free(ctx);
return ret;
}
+ ret = knotd_mod_stats_add(mod, "dropped-time", 1, NULL);
+ if (ret != KNOT_EOK) {
+ ctx_free(ctx);
+ return ret;
+ }
+
+ /* The explicit reference of the AVX2 variant ensures the optimized
+ * code isn't removed by linker if linking statically.
+ * Check: nm ./src/.libs/knotd | grep KRU_
+ * https://stackoverflow.com/a/28663156/587396
+ */
+ knotd_mod_log(mod, LOG_DEBUG, "using %s implementation",
+ KRU.limited == KRU_AVX2.limited ? "optimized" : "generic");
knotd_mod_ctx_set(mod, ctx);
- return knotd_mod_hook(mod, KNOTD_STAGE_END, ratelimit_apply);
+ if (rate_limit > 0) {
+ knotd_mod_hook(mod, KNOTD_STAGE_BEGIN, ratelimit_apply);
+ }
+
+ if (time_limit > 0) {
+ // Note that these two callbacks aren't executed IF PER-ZONE module!
+ knotd_mod_proto_hook(mod, KNOTD_STAGE_PROTO_BEGIN, protolimit_start);
+ knotd_mod_proto_hook(mod, KNOTD_STAGE_PROTO_END, protolimit_end);
+ }
+
+ return KNOT_EOK;
}
void rrl_unload(knotd_mod_t *mod)
{
- rrl_ctx_t *ctx = knotd_mod_ctx(mod);
-
- knotd_conf_free(&ctx->whitelist);
- ctx_free(ctx);
+ ctx_free(knotd_mod_ctx(mod));
}
-KNOTD_MOD_API(rrl, KNOTD_MOD_FLAG_SCOPE_ANY,
+KNOTD_MOD_API(rrl, KNOTD_MOD_FLAG_SCOPE_ANY | KNOTD_MOD_FLAG_OPT_CONF,
rrl_load, rrl_unload, rrl_conf, rrl_conf_check);
diff --git a/src/knot/modules/rrl/rrl.rst b/src/knot/modules/rrl/rrl.rst
index 0daae16..85c8d35 100644
--- a/src/knot/modules/rrl/rrl.rst
+++ b/src/knot/modules/rrl/rrl.rst
@@ -4,37 +4,52 @@
================================
Response rate limiting (RRL) is a method to combat DNS reflection amplification
-attacks. These attacks rely on the fact that source address of a UDP query
+attacks. These attacks rely on the fact that the source address of a UDP query
can be forged, and without a worldwide deployment of `BCP38
<https://tools.ietf.org/html/bcp38>`_, such a forgery cannot be prevented.
An attacker can use a DNS server (or multiple servers) as an amplification
-source and can flood a victim with a large number of unsolicited DNS responses.
-The RRL lowers the amplification factor of these attacks by sending some of
-the responses as truncated or by dropping them altogether.
+source to flood a victim with a large number of unsolicited DNS responses.
+RRL lowers the amplification factor of these attacks by sending some
+responses as truncated or by dropping them altogether.
+
+This module can also help protect the server from excessive utilization by
+limiting incoming packets (including handshakes) based on consumed time.
+If a packet is time rate limited, it's dropped. This function works with
+all supported non-UDP transport protocols and cannot be configured per zone.
.. NOTE::
- The module introduces two statistics counters. The number of slipped and
- dropped responses.
+ This module introduces three statistics counters:
+
+ - ``slipped`` – The number of slipped UDP responses.
+ - ``dropped`` – The number of dropped UDP responses due to the rate limit.
+ - ``dropped-time`` – The number of dropped non-UDP packets due to the time rate limit.
.. NOTE::
If the :ref:`Cookies<mod-cookies>` module is active, RRL is not applied
- for responses with a valid DNS cookie.
+ to UDP responses with a valid DNS cookie.
Example
-------
-You can enable RRL by setting the module globally or per zone.
+You can enable RRL by setting the module globally
::
- mod-rrl:
- - id: default
- rate-limit: 200 # Allow 200 resp/s for each flow
- slip: 2 # Approximately every other response slips
-
template:
- id: default
- global-module: mod-rrl/default # Enable RRL globally
+ global-module: mod-rrl # Default module configuration
+
+or per zone
+
+::
+
+ mod-rrl:
+ - id: custom
+ rate-limit: 200
+
+ zone:
+ - domain: example.com
+ module: mod-rrl/custom # Custom module configuration
Module reference
----------------
@@ -44,9 +59,14 @@ Module reference
mod-rrl:
- id: STR
rate-limit: INT
+ instant-limit: INT
slip: INT
+ time-rate-limit: INT
+ time-instant-limit: INT
table-size: INT
whitelist: ADDR[/INT] | ADDR-ADDR | STR ...
+ log-period: INT
+ dry-run: BOOL
.. _mod-rrl_id:
@@ -60,30 +80,42 @@ A module identifier.
rate-limit
..........
-Rate limiting is based on the token bucket scheme. A rate basically
-represents a number of tokens available each second. Each response is
-processed and classified (based on several discriminators, e.g.
-source netblock, query type, zone name, rcode, etc.). Classified responses are
-then hashed and assigned to a bucket containing number of available
-tokens, timestamp and metadata. When available tokens are exhausted,
-response is dropped or sent as truncated (see :ref:`mod-rrl_slip`).
-Number of available tokens is recalculated each second.
+Maximal allowed number of UDP queries per second from a single IPv6 or IPv4 address.
-*Required*
+Rate limiting is performed for the whole address and several chosen prefixes.
+The limits of prefixes are constant multiples of :ref:`mod-rrl_rate-limit`.
-.. _mod-rrl_table-size:
+The specific prefixes and multipliers, which might be adjusted in the future, are
+for IPv6 /128: 1, /64: 2, /56: 3, /48: 4, /32: 64;
+for IPv4 /32: 1, /24: 32, /20: 256, /18: 768.
-table-size
-..........
+With each host/network, a counter of unrestricted responses is associated;
+if the counter would exceed its capacity, it is not incremented and the response is restricted.
+Counters use exponential decay for lowering their values,
+i.e. they are lowered by a constant fraction of their value each millisecond.
+The specified rate limit is reached, when the number of queries is the same every millisecond;
+sending many queries once a second or even a larger timespan leads to a more strict limiting.
+
+*Default:* ``20``
+
+.. _mod-rrl_instant-limit:
+
+instant-limit
+.............
+
+Maximal allowed number of queries at a single point in time from a single IPv6 or IPv4 address.
+The limits for prefixes use the same multipliers as for :ref:`mod-rrl_rate-limit`.
-Size of the hash table in a number of buckets. The larger the hash table, the lesser
-the probability of a hash collision, but at the expense of additional memory costs.
-Each bucket is estimated roughly to 32 bytes. The size should be selected as
-a reasonably large prime due to better hash function distribution properties.
-Hash table is internally chained and works well up to a fill rate of 90 %, general
-rule of thumb is to select a prime near 1.2 * maximum_qps.
+This limit is reached when many queries come from a new host/network,
+or after a longer time of inactivity.
-*Default:* ``393241``
+The :ref:`mod-rrl_instant-limit` sets the actual capacity of each counter of responses,
+and together with the :ref:`mod-rrl_rate-limit` they set the fraction by which the counter
+is periodically lowered.
+The :ref:`mod-rrl_instant-limit` may be at least :ref:`mod-rrl_rate-limit` **/ 1000**, at which point the
+counters are zeroed each millisecond.
+
+*Default:* ``50``
.. _mod-rrl_slip:
@@ -121,6 +153,49 @@ noting, that some responses can't be truncated (e.g. SERVFAIL).
*Default:* ``1``
+.. _mod-rrl_time-rate-limit:
+
+time-rate-limit
+...............
+
+This limit works similarly to :ref:`mod-rrl_rate-limit` but considers the time
+consumed (in microseconds) by the remote over non-UDP transport protocols.
+
+*Default:* ``4000`` (microseconds)
+
+.. _mod-rrl_time-instant-limit:
+
+time-instant-limit
+..................
+
+This limit works similarly to :ref:`mod-rrl_instant-limit` but considers the time
+consumed (in microseconds) by the remote over non-UDP transport protocols.
+
+*Default:* ``5000`` (microseconds)
+
+.. _mod-rrl_table-size:
+
+table-size
+..........
+
+Maximal number of stored hosts/networks with their counters.
+The data structure tries to store only the most frequent sources,
+so it is safe to set it according to the expected maximal number of limited ones.
+
+Use `1.4 * maximum_qps / rate-limit`,
+where `maximum_qps` is the number of queries which can be handled by the server per second.
+There is at most `maximum_qps / rate-limit` limited hosts;
+larger networks have higher limits and so require only a fraction of the value (handled by the `1.4` multiplier).
+The value will be rounded up to the nearest power of two.
+
+The same table size is used for both counting-based and time-based limiting;
+the maximum number of time-limited hosts is expected to be lower, so it's not typically needed to be considered.
+There is at most `1 000 000 * #cpus / time-rate-limit` of them.
+
+The memory occupied by one table structure is `8 * table-size B`.
+
+*Default:* ``524288``
+
.. _mod-rrl_whitelist:
whitelist
@@ -131,3 +206,31 @@ or network ranges to exempt from rate limiting.
Empty list means that no incoming connection will be white-listed.
*Default:* not set
+
+.. _mod-rrl_log-period:
+
+log-period
+..........
+
+Minimal time in milliseconds between two log messages,
+or zero to disable logging.
+
+If a response is limited, the address and the prefix on which it was blocked is logged
+and logging is disabled for the `log-period` milliseconds.
+As long as limiting is needed, one source is logged each period
+and sources with more blocked queries have greater probability to be chosen.
+
+The approach is used by counting-based and time-based limiting separately,
+so you can expect one message per `log-period` from each of them.
+
+*Default:* ``0`` (disabled)
+
+.. _mod-rrl_dry-run:
+
+dry-run
+.......
+
+If enabled, the module doesn't alter any response. Only query classification
+is performed with possible statistics counter incrementation.
+
+*Default:* ``off``
diff --git a/src/knot/modules/stats/stats.c b/src/knot/modules/stats/stats.c
index 26262ac..c5b797b 100644
--- a/src/knot/modules/stats/stats.c
+++ b/src/knot/modules/stats/stats.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -118,9 +118,11 @@ enum {
PROTOCOL_UDP4 = 0,
PROTOCOL_TCP4,
PROTOCOL_QUIC4,
+ PROTOCOL_TLS4,
PROTOCOL_UDP6,
PROTOCOL_TCP6,
PROTOCOL_QUIC6,
+ PROTOCOL_TLS6,
PROTOCOL_UDP4_XDP,
PROTOCOL_TCP4_XDP,
PROTOCOL_QUIC4_XDP,
@@ -136,9 +138,11 @@ static char *protocol_to_str(uint32_t idx, uint32_t count)
case PROTOCOL_UDP4: return strdup("udp4");
case PROTOCOL_TCP4: return strdup("tcp4");
case PROTOCOL_QUIC4: return strdup("quic4");
+ case PROTOCOL_TLS4: return strdup("tls4");
case PROTOCOL_UDP6: return strdup("udp6");
case PROTOCOL_TCP6: return strdup("tcp6");
case PROTOCOL_QUIC6: return strdup("quic6");
+ case PROTOCOL_TLS6: return strdup("tls6");
case PROTOCOL_UDP4_XDP: return strdup("udp4-xdp");
case PROTOCOL_TCP4_XDP: return strdup("tcp4-xdp");
case PROTOCOL_QUIC4_XDP: return strdup("quic4-xdp");
@@ -521,6 +525,10 @@ static knotd_state_t update_counters(knotd_state_t state, knot_pkt_t *pkt,
knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
PROTOCOL_QUIC4, 1);
}
+ } else if (qdata->params->proto == KNOTD_QUERY_PROTO_TLS) {
+ assert(!xdp);
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_TLS4, 1);
} else {
if (xdp) {
knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
@@ -547,6 +555,10 @@ static knotd_state_t update_counters(knotd_state_t state, knot_pkt_t *pkt,
knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
PROTOCOL_QUIC6, 1);
}
+ } else if (qdata->params->proto == KNOTD_QUERY_PROTO_TLS) {
+ assert(!xdp);
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_TLS6, 1);
} else {
if (xdp) {
knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
diff --git a/src/knot/modules/stats/stats.rst b/src/knot/modules/stats/stats.rst
index 8acf1aa..71cf87a 100644
--- a/src/knot/modules/stats/stats.rst
+++ b/src/knot/modules/stats/stats.rst
@@ -73,9 +73,11 @@ If enabled, all incoming requests are counted by the network protocol:
* udp4 - UDP over IPv4
* tcp4 - TCP over IPv4
* quic4 - QUIC over IPv4
+* tls4 - TLS over IPv4
* udp6 - UDP over IPv6
* tcp6 - TCP over IPv6
* quic6 - QUIC over IPv6
+* tls6 - TLS over IPv6
* udp4-xdp - UDP over IPv4 through XDP
* tcp4-xdp - TCP over IPv4 through XDP
* quic4-xdp - QUIC over IPv4 through XDP
diff --git a/src/knot/nameserver/axfr.c b/src/knot/nameserver/axfr.c
index dcd62e9..cd9ee32 100644
--- a/src/knot/nameserver/axfr.c
+++ b/src/knot/nameserver/axfr.c
@@ -121,14 +121,14 @@ static void axfr_answer_finished(knotd_qdata_t *qdata, knot_pkt_t *pkt, int stat
xfr_stats_add(&xfr->stats, pkt->size);
xfr_stats_end(&xfr->stats);
xfr_log_finished(ZONE_NAME(qdata), LOG_OPERATION_AXFR, LOG_DIRECTION_OUT,
- REMOTE(qdata), PROTO(qdata), KEY(qdata), &xfr->stats);
+ REMOTE(qdata), PROTO(qdata), KEY(qdata), "", &xfr->stats);
break;
default:
break;
}
}
-static int axfr_query_check(knotd_qdata_t *qdata)
+static knot_layer_state_t axfr_query_check(knotd_qdata_t *qdata)
{
NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);
NS_NEED_AUTH(qdata, ACL_ACTION_TRANSFER);
@@ -187,7 +187,7 @@ static int axfr_query_init(knotd_qdata_t *qdata)
return KNOT_EOK;
}
-int axfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+knot_layer_state_t axfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
{
if (pkt == NULL || qdata == NULL) {
return KNOT_STATE_FAIL;
diff --git a/src/knot/nameserver/axfr.h b/src/knot/nameserver/axfr.h
index 81fcad8..25c5f1c 100644
--- a/src/knot/nameserver/axfr.h
+++ b/src/knot/nameserver/axfr.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,7 +21,5 @@
/*!
* \brief Process an AXFR query message.
- *
- * \return KNOT_STATE_* processing states
*/
-int axfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
+knot_layer_state_t axfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
diff --git a/src/knot/nameserver/internet.c b/src/knot/nameserver/internet.c
index 51bde97..034fd26 100644
--- a/src/knot/nameserver/internet.c
+++ b/src/knot/nameserver/internet.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -286,7 +286,7 @@ static int put_additional(knot_pkt_t *pkt, const knot_rrset_t *rr,
return ret;
}
-static int follow_cname(knot_pkt_t *pkt, uint16_t rrtype, knotd_qdata_t *qdata)
+static knotd_in_state_t follow_cname(knot_pkt_t *pkt, uint16_t rrtype, knotd_qdata_t *qdata)
{
/* CNAME chain processing limit. */
if (++qdata->extra->cname_chain > CNAME_CHAIN_MAX) {
@@ -369,7 +369,7 @@ static int follow_cname(knot_pkt_t *pkt, uint16_t rrtype, knotd_qdata_t *qdata)
return KNOTD_IN_STATE_FOLLOW;
}
-static int name_found(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+static knotd_in_state_t name_found(knot_pkt_t *pkt, knotd_qdata_t *qdata)
{
uint16_t qtype = knot_pkt_qtype(pkt);
@@ -406,7 +406,7 @@ static int name_found(knot_pkt_t *pkt, knotd_qdata_t *qdata)
}
}
-static int name_not_found(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+static knotd_in_state_t name_not_found(knot_pkt_t *pkt, knotd_qdata_t *qdata)
{
/* Name is covered by wildcard. */
if (qdata->extra->encloser->flags & NODE_FLAGS_WILDCARD_CHILD) {
@@ -419,7 +419,7 @@ static int name_not_found(knot_pkt_t *pkt, knotd_qdata_t *qdata)
assert(qdata->extra->node != NULL);
/* Follow expanded wildcard. */
- int next_state = name_found(pkt, qdata);
+ knotd_in_state_t next_state = name_found(pkt, qdata);
/* Put to wildcard node list. */
if (wildcard_has_visited(qdata, wildcard_node)) {
@@ -456,7 +456,8 @@ static int name_not_found(knot_pkt_t *pkt, knotd_qdata_t *qdata)
return KNOTD_IN_STATE_MISS;
}
-static int solve_name(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata)
+static knotd_in_state_t solve_name(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata)
{
int ret = zone_contents_find_dname(qdata->extra->contents, qdata->name,
&qdata->extra->node, &qdata->extra->encloser,
@@ -475,7 +476,8 @@ static int solve_name(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata)
}
}
-static int solve_answer(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
+static knotd_in_state_t solve_answer(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, void *ctx)
{
int old_state = state;
@@ -506,7 +508,8 @@ static int solve_answer(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *
return state;
}
-static int solve_answer_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
+static knotd_in_state_t solve_answer_dnssec(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, void *ctx)
{
/* RFC4035, section 3.1 RRSIGs for RRs in ANSWER are mandatory. */
int ret = nsec_append_rrsigs(pkt, qdata, false);
@@ -517,7 +520,8 @@ static int solve_answer_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata,
}
}
-static int solve_authority(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
+static knotd_in_state_t solve_authority(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, void *ctx)
{
int ret = KNOT_ERROR;
const zone_contents_t *zone_contents = qdata->extra->contents;
@@ -554,7 +558,8 @@ static int solve_authority(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, voi
}
}
-static int solve_authority_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
+static knotd_in_state_t solve_authority_dnssec(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, void *ctx)
{
int ret = KNOT_ERROR;
@@ -591,8 +596,8 @@ static int solve_authority_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qda
}
}
-static int solve_additional(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata,
- void *ctx)
+static knotd_in_state_t solve_additional(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, void *ctx)
{
int ret = KNOT_EOK, rrset_count = pkt->rrset_count;
@@ -621,7 +626,8 @@ static int solve_additional(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata,
}
}
-static int solve_additional_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
+static knotd_in_state_t solve_additional_dnssec(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, void *ctx)
{
/* RFC4035, section 3.1 RRSIGs for RRs in ADDITIONAL are optional. */
int ret = nsec_append_rrsigs(pkt, qdata, true);
@@ -641,9 +647,9 @@ static int solve_additional_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qd
return KNOT_STATE_FAIL; \
}
-static int answer_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+static knot_layer_state_t answer_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
{
- int state = KNOTD_IN_STATE_BEGIN;
+ knotd_in_state_t state = KNOTD_IN_STATE_BEGIN;
struct query_plan *plan = qdata->extra->zone->query_plan;
struct query_step *step;
@@ -652,7 +658,8 @@ static int answer_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
/* Resolve PREANSWER. */
if (plan != NULL) {
WALK_LIST(step, plan->stage[KNOTD_STAGE_PREANSWER]) {
- SOLVE_STEP(step->process, state, step->ctx);
+ assert(step->type == QUERY_HOOK_TYPE_IN);
+ SOLVE_STEP(step->in_hook, state, step->ctx);
}
}
@@ -664,7 +671,8 @@ static int answer_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
}
if (plan != NULL) {
WALK_LIST(step, plan->stage[KNOTD_STAGE_ANSWER]) {
- SOLVE_STEP(step->process, state, step->ctx);
+ assert(step->type == QUERY_HOOK_TYPE_IN);
+ SOLVE_STEP(step->in_hook, state, step->ctx);
}
}
@@ -676,7 +684,8 @@ static int answer_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
}
if (plan != NULL) {
WALK_LIST(step, plan->stage[KNOTD_STAGE_AUTHORITY]) {
- SOLVE_STEP(step->process, state, step->ctx);
+ assert(step->type == QUERY_HOOK_TYPE_IN);
+ SOLVE_STEP(step->in_hook, state, step->ctx);
}
}
@@ -688,7 +697,8 @@ static int answer_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
}
if (plan != NULL) {
WALK_LIST(step, plan->stage[KNOTD_STAGE_ADDITIONAL]) {
- SOLVE_STEP(step->process, state, step->ctx);
+ assert(step->type == QUERY_HOOK_TYPE_IN);
+ SOLVE_STEP(step->in_hook, state, step->ctx);
}
}
@@ -698,7 +708,7 @@ static int answer_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
return KNOT_STATE_DONE;
}
-int internet_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+knot_layer_state_t internet_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
{
if (pkt == NULL || qdata == NULL) {
return KNOT_STATE_FAIL;
diff --git a/src/knot/nameserver/internet.h b/src/knot/nameserver/internet.h
index 52afe62..bb6927d 100644
--- a/src/knot/nameserver/internet.h
+++ b/src/knot/nameserver/internet.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,11 +25,8 @@
/*!
* \brief Answer query from an IN class zone.
- *
- * \retval KNOT_STATE_FAIL if it encountered an error.
- * \retval KNOT_STATE_DONE if finished.
*/
-int internet_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
+knot_layer_state_t internet_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
/*! \brief Require given QUERY TYPE or return error code. */
#define NS_NEED_QTYPE(qdata, qtype_want, error_rcode) \
@@ -68,12 +65,6 @@ int internet_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
if (!process_query_acl_check(conf(), (action), (qdata)) || \
process_query_verify(qdata) != KNOT_EOK) { \
return KNOT_STATE_FAIL; \
- }
-
-/*! \brief Require the zone not to be frozen. */
-#define NS_NEED_NOT_FROZEN(qdata) \
- if ((qdata)->extra->zone->events.ufrozen) { \
- (qdata)->rcode = KNOT_RCODE_REFUSED; \
- (qdata)->rcode_ede = KNOT_EDNS_EDE_NOT_READY; \
- return KNOT_STATE_FAIL; \
+ } else { \
+ qdata->params->flags |= KNOTD_QUERY_FLAG_AUTHORIZED; \
}
diff --git a/src/knot/nameserver/ixfr.c b/src/knot/nameserver/ixfr.c
index b57759c..c0e17ea 100644
--- a/src/knot/nameserver/ixfr.c
+++ b/src/knot/nameserver/ixfr.c
@@ -130,7 +130,7 @@ static int ixfr_load_chsets(journal_read_t **journal_read, zone_t *zone,
return journal_read_begin(zone_journal(zone), false, serial_from, journal_read);
}
-static int ixfr_query_check(knotd_qdata_t *qdata)
+static knot_layer_state_t ixfr_query_check(knotd_qdata_t *qdata)
{
NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);
NS_NEED_AUTH(qdata, ACL_ACTION_TRANSFER);
@@ -174,7 +174,7 @@ static void ixfr_answer_finished(knotd_qdata_t *qdata, knot_pkt_t *pkt, int stat
xfr_stats_add(&xfr->stats, pkt->size);
xfr_stats_end(&xfr->stats);
xfr_log_finished(ZONE_NAME(qdata), LOG_OPERATION_IXFR, LOG_DIRECTION_OUT,
- REMOTE(qdata), PROTO(qdata), KEY(qdata), &xfr->stats);
+ REMOTE(qdata), PROTO(qdata), KEY(qdata), "", &xfr->stats);
break;
default:
break;
@@ -245,13 +245,13 @@ static int ixfr_answer_init(knotd_qdata_t *qdata, uint32_t *serial_from)
return KNOT_EOK;
}
-static int ixfr_answer_soa(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+static knot_layer_state_t ixfr_answer_soa(knot_pkt_t *pkt, knotd_qdata_t *qdata)
{
assert(pkt);
assert(qdata);
/* Check query. */
- int state = ixfr_query_check(qdata);
+ knot_layer_state_t state = ixfr_query_check(qdata);
if (state == KNOT_STATE_FAIL) {
return state; /* Malformed query. */
}
@@ -277,7 +277,7 @@ static int ixfr_answer_soa(knot_pkt_t *pkt, knotd_qdata_t *qdata)
return KNOT_STATE_DONE;
}
-int ixfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+knot_layer_state_t ixfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
{
if (pkt == NULL || qdata == NULL) {
return KNOT_STATE_FAIL;
diff --git a/src/knot/nameserver/ixfr.h b/src/knot/nameserver/ixfr.h
index 3012be1..91ce750 100644
--- a/src/knot/nameserver/ixfr.h
+++ b/src/knot/nameserver/ixfr.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -55,9 +55,5 @@ struct ixfr_proc {
/*!
* \brief IXFR query processing module.
- *
- * \retval PRODUCE if it has an answer, but not yet finished.
- * \retval FAIL if it encountered an error.
- * \retval DONE if finished.
*/
-int ixfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
+knot_layer_state_t ixfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
diff --git a/src/knot/nameserver/log.h b/src/knot/nameserver/log.h
index 5a5d5f0..373d65a 100644
--- a/src/knot/nameserver/log.h
+++ b/src/knot/nameserver/log.h
@@ -81,6 +81,8 @@ static inline const char *log_conn_info(knotd_query_proto_t proto, bool pool)
return pool ? " TCP/pool" : " TCP";
case KNOTD_QUERY_PROTO_QUIC:
return pool ? " QUIC/0-RTT" : " QUIC";
+ case KNOTD_QUERY_PROTO_TLS:
+ return " TLS";
default:
return "";
}
diff --git a/src/knot/nameserver/notify.c b/src/knot/nameserver/notify.c
index eaa305a..5b1593f 100644
--- a/src/knot/nameserver/notify.c
+++ b/src/knot/nameserver/notify.c
@@ -29,7 +29,7 @@
LOG_DIRECTION_IN, (qdata)->params->remote, (qdata)->params->proto, \
false, (qdata)->sign.tsig_key.name, fmt)
-static int notify_check_query(knotd_qdata_t *qdata)
+static knot_layer_state_t notify_check_query(knotd_qdata_t *qdata)
{
NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);
NS_NEED_AUTH(qdata, ACL_ACTION_NOTIFY);
@@ -39,14 +39,14 @@ static int notify_check_query(knotd_qdata_t *qdata)
return KNOT_STATE_DONE;
}
-int notify_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+knot_layer_state_t notify_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
{
if (pkt == NULL || qdata == NULL) {
return KNOT_STATE_FAIL;
}
/* Validate notification query. */
- int state = notify_check_query(qdata);
+ knot_layer_state_t state = notify_check_query(qdata);
if (state == KNOT_STATE_FAIL) {
switch (qdata->rcode) {
case KNOT_RCODE_NOTAUTH: /* Not authorized, already logged. */
diff --git a/src/knot/nameserver/notify.h b/src/knot/nameserver/notify.h
index d0bff14..0e60b58 100644
--- a/src/knot/nameserver/notify.h
+++ b/src/knot/nameserver/notify.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,8 +21,5 @@
/*!
* \brief Answer IN class zone NOTIFY message (RFC1996).
- *
- * \retval FAIL if it encountered an error.
- * \retval DONE if finished.
*/
-int notify_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
+knot_layer_state_t notify_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
diff --git a/src/knot/nameserver/nsec_proofs.c b/src/knot/nameserver/nsec_proofs.c
index 71944b1..b67566c 100644
--- a/src/knot/nameserver/nsec_proofs.c
+++ b/src/knot/nameserver/nsec_proofs.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -121,11 +121,11 @@ static const knot_dname_t *get_next_closer(const knot_dname_t *closest_encloser,
size_t ce_labels = knot_dname_labels(closest_encloser, NULL);
size_t qname_labels = knot_dname_labels(name, NULL);
for (int i = 0; i < (qname_labels - ce_labels - 1); ++i) {
- name = knot_wire_next_label(name, NULL);
+ name = knot_dname_next_label(name);
}
// the common labels should match
- assert(knot_dname_is_equal(knot_wire_next_label(name, NULL), closest_encloser));
+ assert(knot_dname_is_equal(knot_dname_next_label(name), closest_encloser));
return name;
}
diff --git a/src/knot/nameserver/process_query.c b/src/knot/nameserver/process_query.c
index 00ec001..beced84 100644
--- a/src/knot/nameserver/process_query.c
+++ b/src/knot/nameserver/process_query.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -30,9 +30,9 @@
#include "knot/nameserver/notify.h"
#include "knot/server/server.h"
#include "libknot/libknot.h"
-#ifdef ENABLE_QUIC
-#include "libknot/quic/quic.h"
-#endif // ENABLE_QUIC
+#include "libknot/quic/quic_conn.h"
+#include "libknot/quic/tls_common.h"
+#include "libknot/quic/tls.h"
#include "contrib/base64.h"
#include "contrib/macros.h"
#include "contrib/mempattern.h"
@@ -147,7 +147,7 @@ static int process_query_in(knot_layer_t *ctx, knot_pkt_t *pkt)
/*!
* \brief Create a response for a given query in the INTERNET class.
*/
-static int query_internet(knot_pkt_t *pkt, knot_layer_t *ctx)
+static knot_layer_state_t query_internet(knot_pkt_t *pkt, knot_layer_t *ctx)
{
knotd_qdata_t *data = QUERY_DATA(ctx);
@@ -167,7 +167,7 @@ static int query_internet(knot_pkt_t *pkt, knot_layer_t *ctx)
/*!
* \brief Create a response for a given query in the CHAOS class.
*/
-static int query_chaos(knot_pkt_t *pkt, knot_layer_t *ctx)
+static knot_layer_state_t query_chaos(knot_pkt_t *pkt, knot_layer_t *ctx)
{
knotd_qdata_t *data = QUERY_DATA(ctx);
@@ -203,7 +203,7 @@ static zone_t *answer_zone_find(const knot_pkt_t *query, knot_zonedb_t *zonedb)
* records are only present in a parent zone.
*/
if (qtype == KNOT_RRTYPE_DS) {
- const knot_dname_t *parent = knot_wire_next_label(qname, NULL);
+ const knot_dname_t *parent = knot_dname_next_label(qname);
zone = knot_zonedb_find_suffix(zonedb, parent);
/* If zone does not exist, search for its parent zone,
this will later result to NODATA answer. */
@@ -364,6 +364,7 @@ static int answer_edns_put(knot_pkt_t *resp, knotd_qdata_t *qdata)
if (knot_pkt_edns_option(qdata->query, KNOT_EDNS_OPTION_EXPIRE) != NULL &&
qdata->extra->contents != NULL && !qdata->extra->zone->is_catalog_flag) {
int64_t timer = qdata->extra->zone->timers.next_expire;
+ timer = knot_time_min(timer, qdata->extra->contents->dnssec_expire); // NOOP if zero
timer = (timer == 0 ? zone_soa_expire(qdata->extra->zone) : timer - time(NULL));
timer = MAX(timer, 0);
uint32_t timer_be;
@@ -389,8 +390,8 @@ static int answer_edns_put(knot_pkt_t *resp, knotd_qdata_t *qdata)
return ret;
}
- /* Align the response if QUIC with EDNS. */
- if (qdata->params->proto == KNOTD_QUERY_PROTO_QUIC) {
+ /* Align the response if QUIC or TLS with EDNS. */
+ if (qdata->params->proto == KNOTD_QUERY_PROTO_QUIC || qdata->params->proto == KNOTD_QUERY_PROTO_TLS) {
int pad_len = knot_pkt_default_padding_size(resp, &qdata->opt_rr);
if (pad_len > -1) {
ret = knot_edns_reserve_option(&qdata->opt_rr, KNOT_EDNS_OPTION_PADDING,
@@ -506,7 +507,7 @@ static void set_rcode_to_packet(knot_pkt_t *pkt, knotd_qdata_t *qdata)
knot_wire_set_rcode(pkt->wire, KNOT_EDNS_RCODE_LO(qdata->rcode));
}
-static int process_query_err(knot_layer_t *ctx, knot_pkt_t *pkt)
+static knot_layer_state_t process_query_err(knot_layer_t *ctx, knot_pkt_t *pkt)
{
assert(ctx && pkt);
@@ -552,7 +553,8 @@ static int process_query_err(knot_layer_t *ctx, knot_pkt_t *pkt)
#define PROCESS_BEGIN(plan, step, next_state, qdata) \
if (plan != NULL) { \
WALK_LIST(step, plan->stage[KNOTD_STAGE_BEGIN]) { \
- next_state = step->process(next_state, pkt, qdata, step->ctx); \
+ assert(step->type == QUERY_HOOK_TYPE_GENERAL); \
+ next_state = step->general_hook(next_state, pkt, qdata, step->ctx); \
if (next_state == KNOT_STATE_FAIL) { \
goto finish; \
} \
@@ -562,7 +564,8 @@ static int process_query_err(knot_layer_t *ctx, knot_pkt_t *pkt)
#define PROCESS_END(plan, step, next_state, qdata) \
if (plan != NULL) { \
WALK_LIST(step, plan->stage[KNOTD_STAGE_END]) { \
- next_state = step->process(next_state, pkt, qdata, step->ctx); \
+ assert(step->type == QUERY_HOOK_TYPE_GENERAL); \
+ next_state = step->general_hook(next_state, pkt, qdata, step->ctx); \
if (next_state == KNOT_STATE_FAIL) { \
next_state = process_query_err(ctx, pkt); \
} \
@@ -705,43 +708,53 @@ bool process_query_acl_check(conf_t *conf, acl_action_t action,
bool automatic = false;
bool allowed = false;
+ struct gnutls_session_int *tls_session;
+ switch (qdata->params->proto) {
+ case KNOTD_QUERY_PROTO_QUIC: tls_session = qdata->params->quic_conn->tls_session; break;
+ case KNOTD_QUERY_PROTO_TLS: tls_session = qdata->params->tls_conn->session; break;
+ default: tls_session = NULL;
+ }
+
if (action != ACL_ACTION_UPDATE) {
// ACL_ACTION_QUERY is used for SOA/refresh query.
assert(action == ACL_ACTION_QUERY || action == ACL_ACTION_NOTIFY ||
action == ACL_ACTION_TRANSFER);
const yp_name_t *item = (action == ACL_ACTION_NOTIFY) ? C_MASTER : C_NOTIFY;
conf_val_t rmts = conf_zone_get(conf, item, zone_name);
- allowed = rmt_allowed(conf, &rmts, query_source, &tsig,
- qdata->params->quic_conn);
+ allowed = rmt_allowed(conf, &rmts, query_source, &tsig, tls_session);
automatic = allowed;
}
if (!allowed) {
conf_val_t acl = conf_zone_get(conf, C_ACL, zone_name);
allowed = acl_allowed(conf, &acl, action, query_source, &tsig,
- zone_name, query, qdata->params->quic_conn);
+ zone_name, query, tls_session);
}
if (log_enabled_debug()) {
int pin_size = 0;
-#ifdef ENABLE_QUIC
- uint8_t bin_pin[KNOT_QUIC_PIN_LEN], pin[2 * KNOT_QUIC_PIN_LEN];
+ uint8_t bin_pin[KNOT_TLS_PIN_LEN], pin[2 * KNOT_TLS_PIN_LEN];
size_t bin_pin_size = sizeof(bin_pin);
- knot_quic_conn_pin(qdata->params->quic_conn, bin_pin, &bin_pin_size, false);
+ knot_tls_pin(tls_session, bin_pin, &bin_pin_size, false);
if (bin_pin_size > 0) {
pin_size = knot_base64_encode(bin_pin, bin_pin_size, pin, sizeof(pin));
}
-#else
- uint8_t pin[1];
-#endif // ENABLE_QUIC
+
+ const char *proto_str;
+ switch (qdata->params->proto) {
+ case KNOTD_QUERY_PROTO_UDP: proto_str = " UDP"; break;
+ case KNOTD_QUERY_PROTO_TCP: proto_str = " TCP"; break;
+ case KNOTD_QUERY_PROTO_QUIC: proto_str = " QUIC"; break;
+ case KNOTD_QUERY_PROTO_TLS: proto_str = " TLS"; break;
+ default: proto_str = "";
+ }
log_zone_debug(zone_name,
"ACL, %s, action %s, remote %s%s%s%s%s%.*s%s",
allowed ? "allowed" : "denied",
(act != NULL) ? act->name : "query",
- addr_str,
+ addr_str, proto_str,
(key_name[0] != '\0') ? ", key " : "",
(key_name[0] != '\0') ? key_name : "",
- (qdata->params->proto == KNOTD_QUERY_PROTO_QUIC) ? ", QUIC" : "",
(pin_size > 0) ? " cert-key " : "",
(pin_size > 0) ? pin_size : 0,
(pin_size > 0) ? (const char *)pin : "",
@@ -994,6 +1007,30 @@ int process_query_put_rr(knot_pkt_t *pkt, knotd_qdata_t *qdata,
return ret;
}
+knotd_proto_state_t process_query_proto(knotd_qdata_params_t *params,
+ const knotd_stage_t stage)
+{
+ assert(params);
+ assert(stage == KNOTD_STAGE_PROTO_BEGIN || stage == KNOTD_STAGE_PROTO_END);
+
+ knotd_proto_state_t state = KNOTD_PROTO_STATE_PASS;
+
+ rcu_read_lock();
+
+ struct query_plan *plan = conf()->query_plan;
+ if (plan != NULL) {
+ struct query_step *step;
+ WALK_LIST(step, plan->stage[stage]) {
+ assert(step->type == QUERY_HOOK_TYPE_PROTO);
+ state = step->proto_hook(state, params, step->ctx);
+ }
+ }
+
+ rcu_read_unlock();
+
+ return state;
+}
+
/*! \brief Module implementation. */
const knot_layer_api_t *process_query_layer(void)
{
diff --git a/src/knot/nameserver/process_query.h b/src/knot/nameserver/process_query.h
index 42e66be..c005b58 100644
--- a/src/knot/nameserver/process_query.h
+++ b/src/knot/nameserver/process_query.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -106,3 +106,14 @@ int process_query_sign_response(knot_pkt_t *pkt, knotd_qdata_t *qdata);
int process_query_put_rr(knot_pkt_t *pkt, knotd_qdata_t *qdata,
const knot_rrset_t *rr, const knot_rrset_t *rrsigs,
uint16_t compr_hint, uint32_t flags);
+
+/*!
+ * \brief Processes all global module protocol callbacks at given stage.
+ *
+ * \param params Query processing parameters.
+ * \param stage Processing stage (KNOTD_STAGE_PROTO_BEGIN or KNOTD_STAGE_PROTO_END).
+ *
+ * \return Resulting state.
+ */
+knotd_proto_state_t process_query_proto(knotd_qdata_params_t *params,
+ const knotd_stage_t stage);
diff --git a/src/knot/nameserver/query_module.c b/src/knot/nameserver/query_module.c
index f02ee1b..0708b17 100644
--- a/src/knot/nameserver/query_module.c
+++ b/src/knot/nameserver/query_module.c
@@ -32,17 +32,6 @@
#include "knot/nameserver/query_module.h"
#include "knot/nameserver/process_query.h"
-#ifdef HAVE_ATOMIC
- #define ATOMIC_ADD(dst, val) __atomic_add_fetch(&(dst), (val), __ATOMIC_RELAXED)
- #define ATOMIC_SUB(dst, val) __atomic_sub_fetch(&(dst), (val), __ATOMIC_RELAXED)
- #define ATOMIC_SET(dst, val) __atomic_store_n(&(dst), (val), __ATOMIC_RELAXED)
-#else
- #warning "Statistics data can be inaccurate"
- #define ATOMIC_ADD(dst, val) ((dst) += (val))
- #define ATOMIC_SUB(dst, val) ((dst) -= (val))
- #define ATOMIC_SET(dst, val) ((dst) = (val))
-#endif
-
_public_
int knotd_conf_check_ref(knotd_conf_check_args_t *args)
{
@@ -79,30 +68,31 @@ void query_plan_free(struct query_plan *plan)
free(plan);
}
-static struct query_step *make_step(query_step_process_f process, void *ctx)
+int query_plan_step(struct query_plan *plan, knotd_stage_t stage,
+ query_hook_type_t type, void *hook, void *ctx)
{
- struct query_step *step = calloc(1, sizeof(struct query_step));
+ struct query_step *step = calloc(1, sizeof(*step));
if (step == NULL) {
- return NULL;
+ return KNOT_ENOMEM;
}
- step->process = process;
+ step->type = type;
+ step->general_hook = hook;
step->ctx = ctx;
- return step;
+ add_tail(&plan->stage[stage], &step->node);
+
+ return KNOT_EOK;
}
-int query_plan_step(struct query_plan *plan, knotd_stage_t stage,
- query_step_process_f process, void *ctx)
+_public_
+int knotd_mod_proto_hook(knotd_mod_t *mod, knotd_stage_t stage, knotd_mod_proto_hook_f hook)
{
- struct query_step *step = make_step(process, ctx);
- if (step == NULL) {
- return KNOT_ENOMEM;
+ if (stage != KNOTD_STAGE_PROTO_BEGIN && stage != KNOTD_STAGE_PROTO_END) {
+ return KNOT_EINVAL;
}
- add_tail(&plan->stage[stage], &step->node);
-
- return KNOT_EOK;
+ return query_plan_step(mod->plan, stage, QUERY_HOOK_TYPE_PROTO, hook, mod);
}
_public_
@@ -112,17 +102,18 @@ int knotd_mod_hook(knotd_mod_t *mod, knotd_stage_t stage, knotd_mod_hook_f hook)
return KNOT_EINVAL;
}
- return query_plan_step(mod->plan, stage, hook, mod);
+ return query_plan_step(mod->plan, stage, QUERY_HOOK_TYPE_GENERAL, hook, mod);
}
_public_
int knotd_mod_in_hook(knotd_mod_t *mod, knotd_stage_t stage, knotd_mod_in_hook_f hook)
{
- if (stage == KNOTD_STAGE_BEGIN || stage == KNOTD_STAGE_END) {
+ if (stage != KNOTD_STAGE_PREANSWER && stage != KNOTD_STAGE_ANSWER &&
+ stage != KNOTD_STAGE_AUTHORITY && stage != KNOTD_STAGE_ADDITIONAL) {
return KNOT_EINVAL;
}
- return query_plan_step(mod->plan, stage, hook, mod);
+ return query_plan_step(mod->plan, stage, QUERY_HOOK_TYPE_IN, hook, mod);
}
knotd_mod_t *query_module_open(conf_t *conf, server_t *server, conf_mod_id_t *mod_id,
@@ -313,8 +304,8 @@ int knotd_mod_stats_add(knotd_mod_t *mod, const char *ctr_name, uint32_t idx_cou
stats += mod->stats_count;
for (unsigned i = 0; i < threads; i++) {
- uint64_t *new_vals = realloc(mod->stats_vals[i],
- (offset + idx_count) * sizeof(*new_vals));
+ knot_atomic_uint64_t *new_vals = realloc(mod->stats_vals[i],
+ (offset + idx_count) * sizeof(*new_vals));
if (new_vals == NULL) {
knotd_mod_stats_free(mod);
return KNOT_ENOMEM;
@@ -627,6 +618,7 @@ uint32_t knotd_qdata_rtt(knotd_qdata_t *qdata)
switch (qdata->params->proto) {
case KNOTD_QUERY_PROTO_TCP:
+ case KNOTD_QUERY_PROTO_TLS:
if (qdata->params->xdp_msg != NULL) {
#ifdef ENABLE_XDP
return qdata->params->measured_rtt;
diff --git a/src/knot/nameserver/query_module.h b/src/knot/nameserver/query_module.h
index 5cc905b..d403b25 100644
--- a/src/knot/nameserver/query_module.h
+++ b/src/knot/nameserver/query_module.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,24 +22,27 @@
#include "knot/dnssec/zone-keys.h"
#include "knot/include/module.h"
#include "knot/server/server.h"
+#include "contrib/atomic.h"
#include "contrib/ucw/lists.h"
-#ifdef HAVE_ATOMIC
- #define ATOMIC_GET(src) __atomic_load_n(&(src), __ATOMIC_RELAXED)
-#else
- #define ATOMIC_GET(src) (src)
-#endif
+#define KNOTD_STAGES (KNOTD_STAGE_PROTO_END + 1)
-#define KNOTD_STAGES (KNOTD_STAGE_END + 1)
+typedef enum {
+ QUERY_HOOK_TYPE_PROTO,
+ QUERY_HOOK_TYPE_GENERAL,
+ QUERY_HOOK_TYPE_IN,
+} query_hook_type_t;
-typedef unsigned (*query_step_process_f)
- (unsigned state, knot_pkt_t *pkt, knotd_qdata_t *qdata, knotd_mod_t *mod);
-
-/*! \brief Single processing step in query processing. */
+/*! \brief Single processing step in query/module processing. */
struct query_step {
node_t node;
+ query_hook_type_t type;
+ union {
+ knotd_mod_proto_hook_f proto_hook;
+ knotd_mod_hook_f general_hook;
+ knotd_mod_in_hook_f in_hook;
+ };
void *ctx;
- query_step_process_f process;
};
/*! Query plan represents a sequence of steps needed for query processing
@@ -58,7 +61,7 @@ void query_plan_free(struct query_plan *plan);
/*! \brief Plan another step for given stage. */
int query_plan_step(struct query_plan *plan, knotd_stage_t stage,
- query_step_process_f process, void *ctx);
+ query_hook_type_t type, void *hook, void *ctx);
/*! \brief Open query module identified by name. */
knotd_mod_t *query_module_open(conf_t *conf, server_t *server, conf_mod_id_t *mod_id,
@@ -91,7 +94,7 @@ struct knotd_mod {
zone_keyset_t *keyset;
zone_sign_ctx_t *sign_ctx;
mod_ctr_t *stats_info;
- uint64_t **stats_vals;
+ knot_atomic_uint64_t **stats_vals;
uint32_t stats_count;
void *ctx;
};
diff --git a/src/knot/nameserver/update.c b/src/knot/nameserver/update.c
index 1168c94..ee884fd 100644
--- a/src/knot/nameserver/update.c
+++ b/src/knot/nameserver/update.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,12 +22,23 @@
#include "knot/query/requestor.h"
#include "contrib/sockaddr.h"
#include "libknot/libknot.h"
+#include "libknot/quic/quic_conn.h"
+#include "libknot/quic/tls.h"
static int update_enqueue(zone_t *zone, knotd_qdata_t *qdata)
{
assert(zone);
assert(qdata);
+ pthread_mutex_lock(&zone->ddns_lock);
+ if (zone->events.ufrozen && zone->ddns_queue_size >= 8) {
+ pthread_mutex_unlock(&zone->ddns_lock);
+ qdata->rcode = KNOT_RCODE_REFUSED;
+ qdata->rcode_ede = KNOT_EDNS_EDE_NOT_READY;
+ return KNOT_ELIMIT;
+ }
+ pthread_mutex_unlock(&zone->ddns_lock);
+
/* Create serialized request. */
knot_request_t *req = calloc(1, sizeof(*req));
if (req == NULL) {
@@ -63,6 +74,22 @@ static int update_enqueue(zone_t *zone, knotd_qdata_t *qdata)
assert(req->sign.tsig_key.algorithm == knot_tsig_rdata_alg(req->query->tsig_rr));
}
+#ifdef ENABLE_QUIC
+ if (qdata->params->quic_conn != NULL) {
+ req->flags |= KNOT_REQUEST_QUIC;
+ req->quic_conn = qdata->params->quic_conn;
+ knot_quic_conn_block(req->quic_conn, true);
+ assert(qdata->params->quic_stream >= 0);
+ req->quic_stream = qdata->params->quic_stream;
+ } else
+#endif // ENABLE_QUIC
+ if (qdata->params->tls_conn != NULL) {
+ req->flags |= KNOT_REQUEST_TLS;
+ req->tls_req_ctx.conn = qdata->params->tls_conn;
+ req->tls_req_ctx.conn->fd_clones_count++;
+ knot_tls_conn_block(req->tls_req_ctx.conn, true);
+ }
+
pthread_mutex_lock(&zone->ddns_lock);
/* Enqueue created request. */
@@ -77,10 +104,10 @@ static int update_enqueue(zone_t *zone, knotd_qdata_t *qdata)
return KNOT_EOK;
}
-int update_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+knot_layer_state_t update_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
{
/* DDNS over XDP not supported. */
- if (qdata->params->xdp_msg != NULL || qdata->params->proto == KNOTD_QUERY_PROTO_QUIC) {
+ if (qdata->params->xdp_msg != NULL) {
qdata->rcode = KNOT_RCODE_SERVFAIL;
return KNOT_STATE_FAIL;
}
@@ -95,8 +122,6 @@ int update_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
NS_NEED_AUTH(qdata, ACL_ACTION_UPDATE);
/* Check expiration. */
NS_NEED_ZONE_CONTENTS(qdata);
- /* Check frozen zone. */
- NS_NEED_NOT_FROZEN(qdata);
/* Store update into DDNS queue. */
int ret = update_enqueue((zone_t *)qdata->extra->zone, qdata);
diff --git a/src/knot/nameserver/update.h b/src/knot/nameserver/update.h
index 609acd9..430b323 100644
--- a/src/knot/nameserver/update.h
+++ b/src/knot/nameserver/update.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,7 +21,5 @@
/*!
* \brief UPDATE query processing module.
- *
- * \return KNOT_STATE_* processing states
*/
-int update_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
+knot_layer_state_t update_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
diff --git a/src/knot/nameserver/xfr.h b/src/knot/nameserver/xfr.h
index e6d06b6..fe8a7a8 100644
--- a/src/knot/nameserver/xfr.h
+++ b/src/knot/nameserver/xfr.h
@@ -38,12 +38,12 @@ static inline
void xfr_log_finished(const knot_dname_t *zone, log_operation_t op,
log_direction_t dir, const struct sockaddr_storage *remote,
knotd_query_proto_t proto, const knot_dname_t *key_name,
- const struct xfr_stats *stats)
+ const char *serial_log, const struct xfr_stats *stats)
{
ns_log(LOG_INFO, zone, op, dir, remote, proto, false, key_name,
- "%sfinished, %0.2f seconds, %u messages, %u bytes",
+ "%sfinished,%s %0.2f seconds, %u messages, %u bytes",
(proto == KNOTD_QUERY_PROTO_QUIC && dir == LOG_DIRECTION_OUT ? "buffering " : ""),
- time_diff_ms(&stats->begin, &stats->end) / 1000.0,
+ serial_log, time_diff_ms(&stats->begin, &stats->end) / 1000.0,
stats->messages, stats->bytes);
}
diff --git a/src/knot/query/quic-requestor.c b/src/knot/query/quic-requestor.c
index 62008f9..7492efd 100644
--- a/src/knot/query/quic-requestor.c
+++ b/src/knot/query/quic-requestor.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,7 +28,6 @@
#include "knot/conf/conf.h" // please use this only for tiny stuff like quic-log
#include "knot/server/handler.h"
#include "libknot/error.h"
-#include "libknot/quic/quic.h"
#define QUIC_BUF_SIZE 4096
@@ -63,6 +62,7 @@ static int quic_exchange(knot_quic_conn_t *conn, knot_quic_reply_t *r, int timeo
int64_t quic_timeout_ms = knot_quic_conn_next_timeout(conn);
quic_timeout_ms = MIN(quic_timeout_ms, timeout_remain);
quic_timeout_ms = MIN(quic_timeout_ms, timeout_ms / 2);
+ quic_timeout_ms = MAX(quic_timeout_ms, 1);
r->in_payload->iov_len = QUIC_BUF_SIZE;
@@ -156,7 +156,7 @@ int knot_qreq_connect(struct knot_quic_reply **out,
int fd,
struct sockaddr_storage *remote,
struct sockaddr_storage *local,
- const struct knot_quic_creds *local_creds,
+ const struct knot_creds *local_creds,
const uint8_t *peer_pin,
uint8_t peer_pin_len,
bool *reused_fd,
@@ -179,8 +179,7 @@ int knot_qreq_connect(struct knot_quic_reply **out,
r->send_reply = qr_send_reply;
r->free_reply = qr_free_reply;
- struct knot_quic_creds *creds = knot_quic_init_creds_peer(local_creds,
- peer_pin, peer_pin_len);
+ struct knot_creds *creds = knot_creds_init_peer(local_creds, peer_pin, peer_pin_len);
if (creds == NULL) {
free(r);
return KNOT_ENOMEM;
@@ -190,7 +189,7 @@ int knot_qreq_connect(struct knot_quic_reply **out,
knot_quic_table_t *table = knot_quic_table_new(1, QUIC_BUF_SIZE,
QUIC_BUF_SIZE, 0, creds);
if (table == NULL) {
- knot_quic_free_creds(creds);
+ knot_creds_free(creds);
free(r);
return KNOT_ENOMEM;
}
@@ -294,7 +293,7 @@ void knot_qreq_close(struct knot_quic_reply *r, bool send_close)
knot_quic_table_rem(conn, table);
knot_quic_cleanup(&conn, 1);
if (table != NULL) {
- knot_quic_free_creds(table->creds);
+ knot_creds_free(table->creds);
}
knot_quic_table_free(table);
free(r);
diff --git a/src/knot/query/quic-requestor.h b/src/knot/query/quic-requestor.h
index b5f479e..083254d 100644
--- a/src/knot/query/quic-requestor.h
+++ b/src/knot/query/quic-requestor.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,15 +17,13 @@
#pragma once
#include "contrib/sockaddr.h"
-
-struct knot_quic_creds;
-struct knot_quic_reply;
+#include "libknot/quic/quic.h"
int knot_qreq_connect(struct knot_quic_reply **out,
int fd,
struct sockaddr_storage *remote,
struct sockaddr_storage *local,
- const struct knot_quic_creds *local_creds,
+ const struct knot_creds *local_creds,
const uint8_t *peer_pin,
uint8_t peer_pin_len,
bool *reused_fd,
diff --git a/src/knot/query/requestor.c b/src/knot/query/requestor.c
index 436f009..125c036 100644
--- a/src/knot/query/requestor.c
+++ b/src/knot/query/requestor.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,12 +18,13 @@
#include <sys/socket.h>
#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+#include "libknot/quic/tls.h"
#include "knot/common/unreachable.h"
#include "knot/query/requestor.h"
#ifdef ENABLE_QUIC
#include "knot/query/quic-requestor.h"
#endif // ENABLE_QUIC
-#include "libknot/errcode.h"
#include "contrib/conn_pool.h"
#include "contrib/mempattern.h"
#include "contrib/net.h"
@@ -39,6 +40,11 @@ static bool use_quic(knot_request_t *request)
return (request->flags & KNOT_REQUEST_QUIC) != 0;
}
+static bool use_tls(knot_request_t *request)
+{
+ return (request->flags & KNOT_REQUEST_TLS) != 0;
+}
+
static bool is_answer_to_query(const knot_pkt_t *query, const knot_pkt_t *answer)
{
return knot_wire_get_id(query->wire) == knot_wire_get_id(answer->wire);
@@ -104,6 +110,19 @@ static int request_ensure_connected(knot_request_t *request, bool *reused_fd, in
#endif // ENABLE_QUIC
}
+ if (use_tls(request)) {
+ assert(!use_quic(request));
+
+ int ret = knot_tls_req_ctx_init(&request->tls_req_ctx, request->fd,
+ request->creds, request->pin,
+ request->pin_len, timeout_ms);
+ if (ret != KNOT_EOK) {
+ close(request->fd);
+ request->fd = -1;
+ return ret;
+ }
+ }
+
return KNOT_EOK;
}
@@ -124,7 +143,9 @@ static int request_send(knot_request_t *request, int timeout_ms, bool *reused_fd
&request->remote : NULL;
/* Send query. */
- if (use_quic(request)) {
+ if (use_tls(request)) {
+ ret = knot_tls_send_dns(request->tls_req_ctx.conn, wire, wire_len);
+ } else if (use_quic(request)) {
#ifdef ENABLE_QUIC
struct iovec tosend = { wire, wire_len };
return knot_qreq_send(request->quic_ctx, &tosend);
@@ -162,7 +183,9 @@ static int request_recv(knot_request_t *request, int timeout_ms)
}
/* Receive it */
- if (use_quic(request)) {
+ if (use_tls(request)) {
+ ret = knot_tls_recv_dns(request->tls_req_ctx.conn, resp->wire, resp->max_size);
+ } else if (use_quic(request)) {
#ifdef ENABLE_QUIC
struct iovec recvd = { resp->wire, resp->max_size };
ret = knot_qreq_recv(request->quic_ctx, &recvd, timeout_ms);
@@ -192,7 +215,7 @@ knot_request_t *knot_request_make_generic(knot_mm_t *mm,
const struct sockaddr_storage *remote,
const struct sockaddr_storage *source,
knot_pkt_t *query,
- const struct knot_quic_creds *creds,
+ const struct knot_creds *creds,
const query_edns_data_t *edns,
const knot_tsig_key_t *tsig_key,
const uint8_t *pin,
@@ -232,7 +255,7 @@ knot_request_t *knot_request_make_generic(knot_mm_t *mm,
request->edns = edns;
request->creds = creds;
- if (flags & KNOT_REQUEST_QUIC && pin_len > 0) {
+ if ((flags & (KNOT_REQUEST_QUIC | KNOT_REQUEST_TLS)) && pin_len > 0) {
request->pin_len = pin_len;
memcpy(request->pin, pin, pin_len);
}
@@ -243,12 +266,15 @@ knot_request_t *knot_request_make_generic(knot_mm_t *mm,
knot_request_t *knot_request_make(knot_mm_t *mm,
const conf_remote_t *remote,
knot_pkt_t *query,
- const struct knot_quic_creds *creds,
+ const struct knot_creds *creds,
const query_edns_data_t *edns,
knot_request_flag_t flags)
{
if (remote->quic) {
+ assert(!remote->tls);
flags |= KNOT_REQUEST_QUIC;
+ } else if (remote->tls) {
+ flags |= KNOT_REQUEST_TLS;
}
return knot_request_make_generic(mm, &remote->addr, &remote->via,
@@ -262,15 +288,25 @@ void knot_request_free(knot_request_t *request, knot_mm_t *mm)
return;
}
- if (request->quic_ctx != NULL) {
+ if (use_quic(request)) {
#ifdef ENABLE_QUIC
- knot_qreq_close(request->quic_ctx, true);
+ if (request->quic_ctx != NULL) {
+ knot_qreq_close(request->quic_ctx, true);
+ }
+ // NOTE synthetic DDNSoQ request is NOOP here
#else
assert(0);
#endif // ENABLE_QUIC
+ } else if (use_tls(request) && request->tls_req_ctx.conn != NULL) {
+ knot_tls_req_ctx_deinit(&request->tls_req_ctx);
+ } else {
+ assert(request->quic_ctx == NULL);
+ assert(request->quic_conn == NULL);
+ assert(request->tls_req_ctx.ctx == NULL);
+ assert(request->tls_req_ctx.conn == NULL);
}
- if (request->fd >= 0 && use_tcp(request) &&
+ if (request->fd >= 0 && use_tcp(request) && !use_tls(request) &&
(request->flags & KNOT_REQUEST_KEEP)) {
request->fd = (int)conn_pool_put(global_conn_pool,
&request->source,
@@ -282,6 +318,7 @@ void knot_request_free(knot_request_t *request, knot_mm_t *mm)
}
knot_pkt_free(request->query);
knot_pkt_free(request->resp);
+ dnssec_binary_free(&request->sign.tsig_key.secret);
tsig_cleanup(&request->tsig);
mm_free(mm, request);
@@ -351,7 +388,7 @@ static int request_produce(knot_requestor_t *req, knot_request_t *last,
if (last->edns != NULL && !last->edns->no_edns) {
ret = query_put_edns(last->query, last->edns,
- (last->flags & KNOT_REQUEST_QUIC));
+ (last->flags & (KNOT_REQUEST_QUIC | KNOT_REQUEST_TLS)));
if (ret != KNOT_EOK) {
return ret;
}
@@ -376,6 +413,9 @@ static int request_produce(knot_requestor_t *req, knot_request_t *last,
if (last->flags & KNOT_REQUEST_QUIC) {
req->layer.flags |= KNOT_REQUESTOR_QUIC;
}
+ if (last->flags & KNOT_REQUEST_TLS) {
+ req->layer.flags |= KNOT_REQUESTOR_TLS;
+ }
}
return ret;
diff --git a/src/knot/query/requestor.h b/src/knot/query/requestor.h
index 65da1ed..241be04 100644
--- a/src/knot/query/requestor.h
+++ b/src/knot/query/requestor.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,26 +23,26 @@
#include "knot/nameserver/tsig_ctx.h"
#include "knot/query/layer.h"
#include "knot/query/query.h"
+#include "knot/query/tls-requestor.h"
#include "libknot/mm_ctx.h"
#include "libknot/rrtype/tsig.h"
-struct knot_quic_creds;
-struct knot_quic_reply;
-
typedef enum {
KNOT_REQUEST_NONE = 0, /*!< Empty flag. */
KNOT_REQUEST_UDP = 1 << 0, /*!< Use UDP for requests. */
KNOT_REQUEST_TFO = 1 << 1, /*!< Enable TCP Fast Open for requests. */
KNOT_REQUEST_KEEP = 1 << 2, /*!< Keep upstream TCP connection in pool for later reuse. */
KNOT_REQUEST_QUIC = 1 << 3, /*!< Use QUIC/UDP for requests. */
- KNOT_REQUEST_FWD = 1 << 4, /*!< Forwarded message, don't modify (TSIG, PADDING). */
+ KNOT_REQUEST_TLS = 1 << 4, /*!< Use DoT for requests. */
+ KNOT_REQUEST_FWD = 1 << 5, /*!< Forwarded message, don't modify (TSIG, PADDING). */
} knot_request_flag_t;
typedef enum {
KNOT_REQUESTOR_CLOSE = 1 << 0, /*!< Close the connection indication. */
KNOT_REQUESTOR_REUSED = 1 << 1, /*!< Reused FD indication (RO). */
KNOT_REQUESTOR_QUIC = 1 << 2, /*!< QUIC used indication (RO). */
- KNOT_REQUESTOR_IOFAIL = 1 << 3, /*!< Encountered error sending/recving data. */
+ KNOT_REQUESTOR_TLS = 1 << 3, /*!< DoT used indication (RO). */
+ KNOT_REQUESTOR_IOFAIL = 1 << 4, /*!< Encountered error sending/recving data. */
} knot_requestor_flag_t;
/*! \brief Requestor structure.
@@ -57,7 +57,14 @@ typedef struct {
/*! \brief Request data (socket, payload, response, TSIG and endpoints). */
typedef struct {
int fd;
- struct knot_quic_reply *quic_ctx;
+ union {
+ struct {
+ struct knot_quic_reply *quic_ctx;
+ struct knot_quic_conn *quic_conn;
+ int64_t quic_stream;
+ };
+ knot_tls_req_ctx_t tls_req_ctx;
+ };
knot_request_flag_t flags;
struct sockaddr_storage remote, source;
knot_pkt_t *query;
@@ -67,11 +74,22 @@ typedef struct {
knot_sign_context_t sign; /*!< Required for async. DDNS processing. */
- const struct knot_quic_creds *creds;
+ const struct knot_creds *creds;
size_t pin_len;
uint8_t pin[];
} knot_request_t;
+static inline knotd_query_proto_t flags2proto(unsigned layer_flags)
+{
+ knotd_query_proto_t proto = KNOTD_QUERY_PROTO_TCP;
+ if ((layer_flags & KNOT_REQUESTOR_QUIC)) {
+ proto = KNOTD_QUERY_PROTO_QUIC;
+ } else if ((layer_flags & KNOT_REQUESTOR_TLS)) {
+ proto = KNOTD_QUERY_PROTO_TLS;
+ }
+ return proto;
+}
+
/*!
* \brief Make request out of endpoints and query.
*
@@ -92,7 +110,7 @@ knot_request_t *knot_request_make_generic(knot_mm_t *mm,
const struct sockaddr_storage *remote,
const struct sockaddr_storage *source,
knot_pkt_t *query,
- const struct knot_quic_creds *creds,
+ const struct knot_creds *creds,
const query_edns_data_t *edns,
const knot_tsig_key_t *tsig_key,
const uint8_t *pin,
@@ -108,7 +126,7 @@ knot_request_t *knot_request_make_generic(knot_mm_t *mm,
knot_request_t *knot_request_make(knot_mm_t *mm,
const conf_remote_t *remote,
knot_pkt_t *query,
- const struct knot_quic_creds *creds,
+ const struct knot_creds *creds,
const query_edns_data_t *edns,
knot_request_flag_t flags);
diff --git a/src/knot/query/tls-requestor.c b/src/knot/query/tls-requestor.c
new file mode 100644
index 0000000..e1a2e8e
--- /dev/null
+++ b/src/knot/query/tls-requestor.c
@@ -0,0 +1,59 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include "knot/query/tls-requestor.h"
+#include "libknot/error.h"
+#include "libknot/quic/tls.h"
+
+int knot_tls_req_ctx_init(knot_tls_req_ctx_t *ctx, int fd,
+ const struct knot_creds *local_creds,
+ const uint8_t *peer_pin, uint8_t peer_pin_len,
+ int io_timeout_ms)
+{
+ struct knot_creds *creds = knot_creds_init_peer(local_creds, peer_pin, peer_pin_len);
+ if (creds == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Use HS = 4x IO timeout, as the RMT IO timeout is usually high.
+ ctx->ctx = knot_tls_ctx_new(creds, io_timeout_ms, 4 * io_timeout_ms, false);
+ if (ctx->ctx == NULL) {
+ knot_creds_free(creds);
+ return KNOT_ENOMEM;
+ }
+
+ ctx->conn = knot_tls_conn_new(ctx->ctx, fd);
+ if (ctx->conn == NULL) {
+ knot_tls_req_ctx_deinit(ctx);
+ return KNOT_ERROR;
+ }
+
+ return KNOT_EOK;
+}
+
+void knot_tls_req_ctx_deinit(knot_tls_req_ctx_t *ctx)
+{
+ if (ctx != NULL) {
+ if (ctx->ctx != NULL) {
+ knot_creds_free(ctx->ctx->creds);
+ }
+ knot_tls_conn_del(ctx->conn);
+ knot_tls_ctx_free(ctx->ctx);
+ memset(ctx, 0, sizeof(*ctx));
+ }
+}
diff --git a/src/knot/query/tls-requestor.h b/src/knot/query/tls-requestor.h
new file mode 100644
index 0000000..535bbe8
--- /dev/null
+++ b/src/knot/query/tls-requestor.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "libknot/quic/tls_common.h"
+
+/*!
+ * \brief TLS requestor context envelope, containing TLS general context and TLS connection.
+ */
+typedef struct knot_tls_req_ctx {
+ struct knot_tls_ctx *ctx;
+ struct knot_tls_conn *conn;
+} knot_tls_req_ctx_t;
+
+/*!
+ * \brief Initialize TLS requestor context.
+ *
+ * \param ctx Context structure to be initialized.
+ * \param fd Opened TCP connection file descriptor.
+ * \param local_creds Local TLS credentials.
+ * \param peer_pin TLS peer pin.
+ * \param peer_pin_len TLS peer pin length.
+ * \param io_timeout_ms Configured io-timeout for TLS connection.
+ *
+ * \return KNOT_E*
+ */
+int knot_tls_req_ctx_init(knot_tls_req_ctx_t *ctx, int fd,
+ const struct knot_creds *local_creds,
+ const uint8_t *peer_pin, uint8_t peer_pin_len,
+ int io_timeout_ms);
+
+/*!
+ * \brief De-initialize TLS requestor context.
+ */
+void knot_tls_req_ctx_deinit(knot_tls_req_ctx_t *ctx);
diff --git a/src/knot/server/handler.c b/src/knot/server/handler.c
index ec7377a..bcd0ec5 100644
--- a/src/knot/server/handler.c
+++ b/src/knot/server/handler.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -97,7 +97,7 @@ static void handle_quic_stream(knot_quic_conn_t *conn, int64_t stream_id, struct
}
void handle_quic_streams(knot_quic_conn_t *conn, knotd_qdata_params_t *params,
- knot_layer_t *layer, void *msg)
+ knot_layer_t *layer)
{
uint8_t ans_buf[KNOT_WIRE_MAX_PKTSIZE];
@@ -108,14 +108,7 @@ void handle_quic_streams(knot_quic_conn_t *conn, knotd_qdata_params_t *params,
assert(stream->inbufs != NULL);
assert(stream->inbufs->n_inbufs > 0);
struct iovec *inbufs = stream->inbufs->inbufs;
- if (msg) {
-#ifdef ENABLE_XDP
- params_xdp_update(params, KNOTD_QUERY_PROTO_QUIC, msg,
- knot_quic_conn_rtt(conn), conn);
-#endif // ENABLE_XDP
- } else {
- params_update(params, knot_quic_conn_rtt(conn), conn);
- }
+ params_update_quic(params, knot_quic_conn_rtt(conn), conn, stream_id);
// NOTE: only the first msg in the stream is used, the rest is dropped.
handle_quic_stream(conn, stream_id, &inbufs[0], layer, params,
ans_buf, sizeof(ans_buf));
diff --git a/src/knot/server/handler.h b/src/knot/server/handler.h
index 0f29988..4b63a0c 100644
--- a/src/knot/server/handler.h
+++ b/src/knot/server/handler.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
#include "knot/query/layer.h"
#include "knot/server/server.h"
#include "libknot/xdp/tcp_iobuf.h"
+#include "libknot/quic/tls.h"
#ifdef ENABLE_QUIC
#include "libknot/quic/quic.h"
@@ -44,18 +45,33 @@ inline static knotd_qdata_params_t params_init(knotd_query_proto_t proto,
.remote = (const struct sockaddr_storage *)remote,
.local = (const struct sockaddr_storage *)local,
.socket = sock,
+ .thread_id = thread_id,
.server = server,
- .thread_id = thread_id
+ .quic_stream = -1,
};
return params;
}
-inline static void params_update(knotd_qdata_params_t *params, uint32_t rtt,
- struct knot_quic_conn *conn)
+inline static void params_update_tcp(knotd_qdata_params_t *params, uint32_t rtt)
{
params->measured_rtt = rtt;
+}
+
+#ifdef ENABLE_QUIC
+inline static void params_update_quic(knotd_qdata_params_t *params, uint32_t rtt,
+ knot_quic_conn_t *conn, int64_t stream_id)
+{
params->quic_conn = conn;
+ params->quic_stream = stream_id;
+ params->measured_rtt = rtt;
+}
+#endif // ENABLE_QUIC
+
+inline static void params_update_tls(knotd_qdata_params_t *params,
+ knot_tls_conn_t *conn)
+{
+ params->tls_conn = conn;
}
#ifdef ENABLE_XDP
@@ -64,8 +80,9 @@ inline static knotd_qdata_params_t params_xdp_init(int sock, server_t *server,
{
knotd_qdata_params_t params = {
.socket = sock,
+ .thread_id = thread_id,
.server = server,
- .thread_id = thread_id
+ .quic_stream = -1,
};
return params;
@@ -73,16 +90,12 @@ inline static knotd_qdata_params_t params_xdp_init(int sock, server_t *server,
inline static void params_xdp_update(knotd_qdata_params_t *params,
knotd_query_proto_t proto,
- struct knot_xdp_msg *msg,
- uint32_t rtt,
- struct knot_quic_conn *conn)
+ struct knot_xdp_msg *msg)
{
params->proto = proto;
params->remote = (struct sockaddr_storage *)&msg->ip_from;
params->local = (struct sockaddr_storage *)&msg->ip_to;
params->xdp_msg = msg;
- params->measured_rtt = rtt;
- params->quic_conn = conn;
}
#endif // ENABLE_XDP
@@ -107,7 +120,7 @@ void handle_udp_reply(knotd_qdata_params_t *params, knot_layer_t *layer,
#ifdef ENABLE_QUIC
void handle_quic_streams(knot_quic_conn_t *conn, knotd_qdata_params_t *params,
- knot_layer_t *layer, void *msg);
+ knot_layer_t *layer);
#endif // ENABLE_QUIC
void log_swept(knot_sweep_stats_t *stats, bool tcp);
diff --git a/src/knot/server/quic-handler.c b/src/knot/server/quic-handler.c
index 5010213..663167e 100644
--- a/src/knot/server/quic-handler.c
+++ b/src/knot/server/quic-handler.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,6 +24,7 @@
#include "contrib/macros.h"
#include "contrib/net.h"
#include "knot/common/log.h"
+#include "knot/nameserver/process_query.h"
#include "knot/server/handler.h"
#include "knot/server/quic-handler.h"
#include "knot/server/server.h"
@@ -92,16 +93,22 @@ void quic_handler(knotd_qdata_params_t *params, knot_layer_t *layer,
rpl.out_payload->iov_len = 0; // prevent send attempt if uq_alloc_reply is not called at all
+ if (process_query_proto(params, KNOTD_STAGE_PROTO_BEGIN) == KNOTD_PROTO_STATE_BLOCK) {
+ return;
+ }
+
knot_quic_conn_t *conn = NULL;
(void)knot_quic_handle(table, &rpl, idle_close, &conn);
if (conn != NULL) {
- handle_quic_streams(conn, params, layer, NULL);
+ handle_quic_streams(conn, params, layer);
(void)knot_quic_send(table, conn, &rpl, QUIC_MAX_SEND_PER_RECV, 0);
knot_quic_cleanup(&conn, 1);
}
+
+ (void)process_query_proto(params, KNOTD_STAGE_PROTO_END);
}
knot_quic_table_t *quic_make_table(struct server *server)
diff --git a/src/knot/server/server.c b/src/knot/server/server.c
index dd0b025..0626337 100644
--- a/src/knot/server/server.c
+++ b/src/knot/server/server.c
@@ -27,8 +27,9 @@
#include "libknot/libknot.h"
#include "libknot/yparser/ypschema.h"
#include "libknot/xdp.h"
+#include "libknot/quic/tls_common.h"
#ifdef ENABLE_QUIC
-#include "libknot/quic/quic.h"
+#include "libknot/quic/quic.h" // knot_quic_session_*
#endif // ENABLE_QUIC
#include "knot/common/log.h"
#include "knot/common/stats.h"
@@ -65,6 +66,8 @@
#define SESSION_TICKET_POOL_TIMEOUT (24 * 3600)
+#define QUIC_LOG "QUIC/TLS, "
+
/*! \brief Minimal send/receive buffer sizes. */
enum {
UDP_MIN_RCVSIZE = 4096,
@@ -236,14 +239,14 @@ static int disable_pmtudisc(int sock, int family)
return KNOT_EOK;
}
-static size_t quic_rmt_count(conf_t *conf)
+static size_t quic_rmt_count(conf_t *conf, const yp_name_t *proto)
{
size_t count = 0;
for (conf_iter_t iter = conf_iter(conf, C_RMT);
iter.code == KNOT_EOK; conf_iter_next(conf, &iter)) {
conf_val_t id = conf_iter_id(conf, &iter);
- conf_val_t rmt_quic = conf_id_get(conf, C_RMT, C_QUIC, &id);
+ conf_val_t rmt_quic = conf_id_get(conf, C_RMT, proto, &id);
if (conf_bool(&rmt_quic)) {
count++;
}
@@ -252,14 +255,11 @@ static size_t quic_rmt_count(conf_t *conf)
return count;
}
+#ifdef ENABLE_XDP
static iface_t *server_init_xdp_iface(struct sockaddr_storage *addr, bool route_check,
bool udp, bool tcp, uint16_t quic, unsigned *thread_id_start,
- bool extra_frames)
+ const knot_xdp_config_t *xdp_config)
{
-#ifndef ENABLE_XDP
- assert(0);
- return NULL;
-#else
conf_xdp_iface_t iface;
int ret = conf_xdp_iface(addr, &iface);
if (ret != KNOT_EOK) {
@@ -296,18 +296,17 @@ static iface_t *server_init_xdp_iface(struct sockaddr_storage *addr, bool route_
xdp_flags |= KNOT_XDP_FILTER_ROUTE;
}
- knot_xdp_config_t xdp_config = { .extra_frames = extra_frames };
for (int i = 0; i < iface.queues; i++) {
knot_xdp_load_bpf_t mode =
(i == 0 ? KNOT_XDP_LOAD_BPF_ALWAYS : KNOT_XDP_LOAD_BPF_NEVER);
ret = knot_xdp_init(new_if->xdp_sockets + i, iface.name, i,
- xdp_flags, iface.port, quic, mode, &xdp_config);
+ xdp_flags, iface.port, quic, mode, xdp_config);
if (ret == -EBUSY && i == 0) {
log_notice("XDP interface %s@%u is busy, retrying initialization",
iface.name, iface.port);
ret = knot_xdp_init(new_if->xdp_sockets + i, iface.name, i,
xdp_flags, iface.port, quic,
- KNOT_XDP_LOAD_BPF_ALWAYS_UNLOAD, &xdp_config);
+ KNOT_XDP_LOAD_BPF_ALWAYS_UNLOAD, xdp_config);
}
if (ret != KNOT_EOK) {
log_warning("failed to initialize XDP interface %s@%u, queue %d (%s)",
@@ -345,8 +344,8 @@ static iface_t *server_init_xdp_iface(struct sockaddr_storage *addr, bool route_
}
return new_if;
-#endif
}
+#endif
/*!
* \brief Create and initialize new interface.
@@ -363,7 +362,7 @@ static iface_t *server_init_xdp_iface(struct sockaddr_storage *addr, bool route_
* \retval Pointer to a new initialized interface.
* \retval NULL if error.
*/
-static iface_t *server_init_iface(struct sockaddr_storage *addr, bool quic,
+static iface_t *server_init_iface(struct sockaddr_storage *addr, bool tls,
int udp_thread_count, int tcp_thread_count,
bool tcp_reuseport, bool socket_affinity)
{
@@ -380,14 +379,14 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr, bool quic,
int udp_socket_count = 1;
int udp_bind_flags = 0;
- int tcp_socket_count = !quic ? 1 : 0;
+ int tcp_socket_count = tcp_thread_count > 0 ? 1 : 0;
int tcp_bind_flags = 0;
#ifdef ENABLE_REUSEPORT
udp_socket_count = udp_thread_count;
udp_bind_flags |= NET_BIND_MULTIPLE;
- if (!quic && tcp_reuseport) {
+ if (tcp_reuseport) {
tcp_socket_count = tcp_thread_count;
tcp_bind_flags |= NET_BIND_MULTIPLE;
}
@@ -459,7 +458,7 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr, bool quic,
warn_flag_misc = false;
}
- if (quic) {
+ if (tls) {
ret = net_cmsg_ecn_enable(sock, addr->ss_family);
if (ret != KNOT_EOK && ret != KNOT_ENOTSUP && warn_ecn) {
log_warning("failed to enable ECN for QUIC");
@@ -561,7 +560,6 @@ static void log_sock_conf(conf_t *conf)
}
}
-#ifdef ENABLE_QUIC
static int check_file(char *path, char *role)
{
if (path == NULL) {
@@ -576,29 +574,29 @@ static int check_file(char *path, char *role)
err_str = "invalid file";
} else if (!S_ISREG(st.st_mode)) {
err_str = "not a file";
+ } else if ((st.st_mode & S_IRUSR) == 0) {
+ err_str = "not readable";
} else {
return KNOT_EOK;
}
- log_error("QUIC, %s file '%s' (%s)", role, path, err_str);
+ log_error(QUIC_LOG "%s file '%s' (%s)", role, path, err_str);
return KNOT_EINVAL;
}
-#endif // ENABLE_QUIC
-static int init_creds(server_t *server, conf_t *conf)
+static int init_creds(conf_t *conf, server_t *server)
{
-#ifdef ENABLE_QUIC
char *cert_file = conf_tls(conf, C_CERT_FILE);
char *key_file = conf_tls(conf, C_KEY_FILE);
int ret = check_file(cert_file, "certificate");
if (ret != KNOT_EOK) {
- return ret;
+ goto failed;
}
ret = check_file(key_file, "key");
if (ret != KNOT_EOK) {
- return ret;
+ goto failed;
}
if (cert_file == NULL) {
@@ -606,35 +604,45 @@ static int init_creds(server_t *server, conf_t *conf)
char *kasp_dir = conf_db(conf, C_KASP_DB);
ret = make_dir(kasp_dir, S_IRWXU | S_IRWXG, true);
if (ret != KNOT_EOK) {
- log_error("QUIC, failed to create directory '%s'", kasp_dir);
+ log_error(QUIC_LOG "failed to create directory '%s'", kasp_dir);
free(kasp_dir);
- return ret;
+ goto failed;
}
key_file = abs_path(DFLT_QUIC_KEY_FILE, kasp_dir);
free(kasp_dir);
- log_debug("QUIC, using self-generated key '%s' with "
+ log_debug(QUIC_LOG "using self-generated key '%s' with "
"one-time certificate", key_file);
}
- server->quic_creds = knot_quic_init_creds(cert_file, key_file);
- free(cert_file);
+
+ uint8_t prev_pin[128];
+ size_t prev_pin_len = server_cert_pin(server, prev_pin, sizeof(prev_pin));
+
if (server->quic_creds == NULL) {
- log_error("QUIC, failed to initialize server credentials with key '%s'",
- key_file);
- free(key_file);
- return KNOT_ERROR;
+ server->quic_creds = knot_creds_init(key_file, cert_file);
+ if (server->quic_creds == NULL) {
+ log_error(QUIC_LOG "failed to initialize server credentials");
+ ret = KNOT_ERROR;
+ goto failed;
+ }
+ } else {
+ ret = knot_creds_update(server->quic_creds, key_file, cert_file);
+ if (ret != KNOT_EOK) {
+ goto failed;
+ }
}
- free(key_file);
- size_t pin_len;
uint8_t pin[128];
- if ((pin_len = server_cert_pin(server, pin, sizeof(pin))) > 0) {
- log_info("QUIC, certificate public key %.*s", (int)pin_len, pin);
+ size_t pin_len = server_cert_pin(server, pin, sizeof(pin));
+ if (pin_len > 0 && (pin_len != prev_pin_len || memcmp(pin, prev_pin, pin_len) != 0)) {
+ log_info(QUIC_LOG "certificate public key %.*s", (int)pin_len, pin);
}
- return KNOT_EOK;
-#else
- return KNOT_ERROR;
-#endif // ENABLE_QUIC
+ ret = KNOT_EOK;
+failed:
+ free(key_file);
+ free(cert_file);
+
+ return ret;
}
/*! \brief Initialize bound sockets according to configuration. */
@@ -646,11 +654,13 @@ static int configure_sockets(conf_t *conf, server_t *s)
conf_val_t listen_val = conf_get(conf, C_SRV, C_LISTEN);
conf_val_t liquic_val = conf_get(conf, C_SRV, C_LISTEN_QUIC);
+ conf_val_t listls_val = conf_get(conf, C_SRV, C_LISTEN_TLS);
conf_val_t lisxdp_val = conf_get(conf, C_XDP, C_LISTEN);
conf_val_t rundir_val = conf_get(conf, C_SRV, C_RUNDIR);
uint16_t convent_quic = conf_val_count(&liquic_val);
+ uint16_t convent_tls = conf_val_count(&listls_val);
- if (listen_val.code == KNOT_EOK || liquic_val.code == KNOT_EOK) {
+ if (listen_val.code == KNOT_EOK || liquic_val.code == KNOT_EOK || listls_val.code == KNOT_EOK) {
log_sock_conf(conf);
} else if (lisxdp_val.code != KNOT_EOK) {
log_warning("no network interface configured");
@@ -676,7 +686,7 @@ static int configure_sockets(conf_t *conf, server_t *s)
size_t real_nifs = 0;
size_t nifs = conf_val_count(&listen_val) + conf_val_count(&liquic_val) +
- conf_val_count(&lisxdp_val);
+ conf_val_count(&listls_val) + conf_val_count(&lisxdp_val);
iface_t *newlist = calloc(nifs, sizeof(*newlist));
if (newlist == NULL) {
log_error("failed to allocate memory for network sockets");
@@ -720,32 +730,52 @@ static int configure_sockets(conf_t *conf, server_t *s)
free(rundir);
return KNOT_ERROR;
}
- new_if->quic = true;
+ new_if->tls = true;
memcpy(&newlist[real_nifs++], new_if, sizeof(*newlist));
free(new_if);
conf_val_next(&liquic_val);
}
+ while (listls_val.code == KNOT_EOK) {
+ struct sockaddr_storage addr = conf_addr(&listls_val, rundir);
+ char addr_str[SOCKADDR_STRLEN] = { 0 };
+ sockaddr_tostr(addr_str, sizeof(addr_str), &addr);
+ log_info("binding to TLS interface %s", addr_str);
+
+ iface_t *new_if = server_init_iface(&addr, true, 0, size_tcp,
+ tcp_reuseport, socket_affinity);
+ if (new_if == NULL) {
+ server_deinit_iface_list(newlist, nifs);
+ free(rundir);
+ return KNOT_ERROR;
+ }
+ new_if->tls = true;
+ memcpy(&newlist[real_nifs++], new_if, sizeof(*newlist));
+ free(new_if);
+
+ conf_val_next(&listls_val);
+ }
free(rundir);
/* XDP sockets. */
- bool xdp_udp = conf->cache.xdp_udp;
- bool xdp_tcp = conf->cache.xdp_tcp;
- uint16_t xdp_quic = conf->cache.xdp_quic;
- bool route_check = conf->cache.xdp_route_check;
+#ifdef ENABLE_XDP
+ knot_xdp_config_t xdp_config = {
+ .ring_size = conf->cache.xdp_ring_size,
+ .busy_poll_budget = conf->cache.xdp_busypoll_budget,
+ .busy_poll_timeout = conf->cache.xdp_busypoll_timeout,
+ };
unsigned thread_id = s->handlers[IO_UDP].handler.unit->size +
s->handlers[IO_TCP].handler.unit->size;
- conf_val_t extra_frames_val = conf_get(conf, C_XDP, C_EXTRA_FRAMES);
- bool extra_frames = conf_bool(&extra_frames_val);
while (lisxdp_val.code == KNOT_EOK) {
struct sockaddr_storage addr = conf_addr(&lisxdp_val, NULL);
char addr_str[SOCKADDR_STRLEN] = { 0 };
sockaddr_tostr(addr_str, sizeof(addr_str), &addr);
log_info("binding to XDP interface %s", addr_str);
- iface_t *new_if = server_init_xdp_iface(&addr, route_check, xdp_udp,
- xdp_tcp, xdp_quic, &thread_id,
- extra_frames);
+ iface_t *new_if = server_init_xdp_iface(&addr, conf->cache.xdp_route_check,
+ conf->cache.xdp_udp, conf->cache.xdp_tcp,
+ conf->cache.xdp_quic, &thread_id,
+ &xdp_config);
if (new_if == NULL) {
server_deinit_iface_list(newlist, nifs);
return KNOT_ERROR;
@@ -755,13 +785,16 @@ static int configure_sockets(conf_t *conf, server_t *s)
conf_val_next(&lisxdp_val);
}
+#endif
+
assert(real_nifs <= nifs);
nifs = real_nifs;
/* QUIC credentials initialization. */
- s->quic_active = xdp_quic > 0 || convent_quic > 0 || quic_rmt_count(conf) > 0;
- if (s->quic_active) {
- if (init_creds(s, conf) != KNOT_EOK) {
+ s->quic_active = conf->cache.xdp_quic > 0 || convent_quic > 0 || quic_rmt_count(conf, C_QUIC) > 0;
+ s->tls_active = convent_tls > 0 || quic_rmt_count(conf, C_TLS) > 0;
+ if (s->quic_active || s->tls_active) {
+ if (init_creds(conf, s) != KNOT_EOK) {
server_deinit_iface_list(newlist, nifs);
return KNOT_ERROR;
}
@@ -810,6 +843,8 @@ int server_init(server_t *server, int bg_workers)
return ret;
}
+ pthread_rwlock_init(&server->ctl_lock, NULL);
+
zone_backups_init(&server->backup_ctxs);
char *catalog_dir = conf_db(conf(), C_CATALOG_DB);
@@ -864,6 +899,9 @@ void server_deinit(server_t *server)
/* Free remaining events. */
evsched_deinit(&server->sched);
+ /* Deinit locks. */
+ pthread_rwlock_destroy(&server->ctl_lock);
+
/* Free catalog zone context. */
catalog_update_clear(&server->catalog_upd);
catalog_update_deinit(&server->catalog_upd);
@@ -885,9 +923,7 @@ void server_deinit(server_t *server)
global_sessticket_pool = NULL;
knot_unreachables_deinit(&global_unreachables);
-#if defined ENABLE_QUIC
- knot_quic_free_creds(server->quic_creds);
-#endif // ENABLE_QUIC
+ knot_creds_free(server->quic_creds);
}
static int server_init_handler(server_t *server, int index, int thread_count,
@@ -1058,9 +1094,10 @@ static bool listen_changed(conf_t *conf, server_t *server)
conf_val_t listen_val = conf_get(conf, C_SRV, C_LISTEN);
conf_val_t liquic_val = conf_get(conf, C_SRV, C_LISTEN_QUIC);
+ conf_val_t listls_val = conf_get(conf, C_SRV, C_LISTEN_TLS);
conf_val_t lisxdp_val = conf_get(conf, C_XDP, C_LISTEN);
size_t new_count = conf_val_count(&listen_val) + conf_val_count(&liquic_val) +
- conf_val_count(&lisxdp_val);
+ conf_val_count(&listls_val) + conf_val_count(&lisxdp_val);
size_t old_count = server->n_ifaces;
if (new_count != old_count) {
return true;
@@ -1075,7 +1112,9 @@ static bool listen_changed(conf_t *conf, server_t *server)
struct sockaddr_storage addr = conf_addr(&listen_val, rundir);
bool found = false;
for (size_t i = 0; i < server->n_ifaces; i++) {
- if (sockaddr_cmp(&addr, &server->ifaces[i].addr, false) == 0) {
+ iface_t *iface = &server->ifaces[i];
+ if (sockaddr_cmp(&addr, &iface->addr, false) == 0 &&
+ !iface->tls && iface->fd_xdp_count == 0) {
matches++;
found = true;
break;
@@ -1090,7 +1129,9 @@ static bool listen_changed(conf_t *conf, server_t *server)
struct sockaddr_storage addr = conf_addr(&liquic_val, rundir);
bool found = false;
for (size_t i = 0; i < server->n_ifaces; i++) {
- if (sockaddr_cmp(&addr, &server->ifaces[i].addr, false) == 0) {
+ iface_t *iface = &server->ifaces[i];
+ if (sockaddr_cmp(&addr, &iface->addr, false) == 0 &&
+ iface->tls && iface->fd_udp_count > 0) {
matches++;
found = true;
break;
@@ -1101,13 +1142,32 @@ static bool listen_changed(conf_t *conf, server_t *server)
}
conf_val_next(&liquic_val);
}
+ while (listls_val.code == KNOT_EOK) {
+ struct sockaddr_storage addr = conf_addr(&listls_val, rundir);
+ bool found = false;
+ for (size_t i = 0; i < server->n_ifaces; i++) {
+ iface_t *iface = &server->ifaces[i];
+ if (sockaddr_cmp(&addr, &iface->addr, false) == 0 &&
+ iface->tls && iface->fd_tcp_count > 0) {
+ matches++;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ break;
+ }
+ conf_val_next(&listls_val);
+ }
free(rundir);
while (lisxdp_val.code == KNOT_EOK) {
struct sockaddr_storage addr = conf_addr(&lisxdp_val, NULL);
bool found = false;
for (size_t i = 0; i < server->n_ifaces; i++) {
- if (sockaddr_cmp(&addr, &server->ifaces[i].addr, false) == 0) {
+ iface_t *iface = &server->ifaces[i];
+ if (sockaddr_cmp(&addr, &iface->addr, false) == 0 &&
+ iface->fd_xdp_count > 0) {
matches++;
found = true;
break;
@@ -1137,6 +1197,9 @@ static void warn_server_reconfigure(conf_t *conf, server_t *server)
static bool warn_xdp_tcp = true;
static bool warn_xdp_quic = true;
static bool warn_route_check = true;
+ static bool warn_ring_size = true;
+ static bool warn_busypoll_budget = true;
+ static bool warn_busypoll_timeout = true;
static bool warn_rmt_pool_limit = true;
if (warn_tcp_reuseport && conf->cache.srv_tcp_reuseport != conf_get_bool(conf, C_SRV, C_TCP_REUSEPORT)) {
@@ -1165,7 +1228,7 @@ static void warn_server_reconfigure(conf_t *conf, server_t *server)
}
if (warn_listen && server->ifaces != NULL && listen_changed(conf, server)) {
- log_warning(msg, "listen(-xdp,-quic)");
+ log_warning(msg, "listen(-xdp,-quic,-tls)");
warn_listen = false;
}
@@ -1195,6 +1258,21 @@ static void warn_server_reconfigure(conf_t *conf, server_t *server)
warn_route_check = false;
}
+ if (warn_ring_size && conf->cache.xdp_ring_size != conf_get_int(conf, C_XDP, C_RING_SIZE)) {
+ log_warning(msg, &C_RING_SIZE[1]);
+ warn_ring_size = false;
+ }
+
+ if (warn_busypoll_budget && conf->cache.xdp_busypoll_budget != conf_get_int(conf, C_XDP, C_BUSYPOLL_BUDGET)) {
+ log_warning(msg, &C_BUSYPOLL_BUDGET[1]);
+ warn_busypoll_budget = false;
+ }
+
+ if (warn_busypoll_timeout && conf->cache.xdp_busypoll_timeout != conf_get_int(conf, C_XDP, C_BUSYPOLL_TIMEOUT)) {
+ log_warning(msg, &C_BUSYPOLL_TIMEOUT[1]);
+ warn_busypoll_timeout = false;
+ }
+
if (warn_rmt_pool_limit && global_conn_pool != NULL &&
global_conn_pool->capacity != conf_get_int(conf, C_SRV, C_RMT_POOL_LIMIT)) {
log_warning(msg, &C_RMT_POOL_LIMIT[1]);
@@ -1395,7 +1473,7 @@ static int reconfigure_remote_pool(conf_t *conf, server_t *server)
#ifdef ENABLE_QUIC
if (global_sessticket_pool == NULL && server->quic_active) {
- size_t rmt_count = quic_rmt_count(conf);
+ size_t rmt_count = quic_rmt_count(conf, C_QUIC);
if (rmt_count > 0) {
size_t max_tickets = conf_bg_threads(conf) * rmt_count * 2; // Two addresses per remote.
conn_pool_t *new_pool =
@@ -1456,6 +1534,12 @@ int server_reconfigure(conf_t *conf, server_t *server)
if (conf_lmdb_readers(conf) > CONF_MAX_DB_READERS) {
log_warning("config, exceeded number of database readers");
}
+ } else {
+ /* Reconfigure TLS credentials. */
+ if ((ret = init_creds(conf, server)) != KNOT_EOK) {
+ log_error("failed to reconfigure server credentials (%s)",
+ knot_strerror(ret));
+ }
}
/* Reconfigure journal DB. */
@@ -1515,14 +1599,13 @@ void server_update_zones(conf_t *conf, server_t *server, reload_t mode)
size_t server_cert_pin(server_t *server, uint8_t *out, size_t out_size)
{
-#ifdef ENABLE_QUIC
int pin_size = 0;
- uint8_t bin_pin[KNOT_QUIC_PIN_LEN];
+ uint8_t bin_pin[KNOT_TLS_PIN_LEN];
size_t bin_pin_size = sizeof(bin_pin);
gnutls_x509_crt_t cert = NULL;
if (server->quic_creds != NULL &&
- knot_quic_creds_cert(server->quic_creds, &cert) == KNOT_EOK &&
+ knot_creds_cert(server->quic_creds, &cert) == KNOT_EOK &&
gnutls_x509_crt_get_key_id(cert, GNUTLS_KEYID_USE_SHA256,
bin_pin, &bin_pin_size) == GNUTLS_E_SUCCESS) {
pin_size = knot_base64_encode(bin_pin, bin_pin_size, out, out_size);
@@ -1530,7 +1613,4 @@ size_t server_cert_pin(server_t *server, uint8_t *out, size_t out_size)
gnutls_x509_crt_deinit(cert);
return (pin_size >= 0) ? pin_size : 0;
-#else
- return 0;
-#endif // ENABLE_QUIC
}
diff --git a/src/knot/server/server.h b/src/knot/server/server.h
index e72e6de..fae06d6 100644
--- a/src/knot/server/server.h
+++ b/src/knot/server/server.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,8 +16,7 @@
#pragma once
-#include <stdatomic.h>
-
+#include "contrib/atomic.h"
#include "knot/conf/conf.h"
#include "knot/catalog/catalog_update.h"
#include "knot/common/evsched.h"
@@ -32,7 +31,7 @@
struct server;
struct knot_xdp_socket;
-struct knot_quic_creds;
+struct knot_creds;
/*!
* \brief I/O handler structure.
@@ -75,7 +74,7 @@ typedef struct {
unsigned fd_xdp_count;
unsigned xdp_first_thread_id;
bool anyaddr;
- bool quic;
+ bool tls;
struct knot_xdp_socket **xdp_sockets;
struct sockaddr_storage addr;
} iface_t;
@@ -120,16 +119,20 @@ typedef struct server {
iface_t *ifaces;
size_t n_ifaces;
bool quic_active;
+ bool tls_active;
+
+ /*! \brief Mutex protecting simultaneous access from concurrent CTL threads. */
+ pthread_rwlock_t ctl_lock;
/*! \brief Pending changes to catalog member zones, update indication. */
catalog_update_t catalog_upd;
- atomic_bool catalog_upd_signal;
+ knot_atomic_bool catalog_upd_signal;
/*! \brief Context of pending zones' backup. */
zone_backup_ctxs_t backup_ctxs;
/*! \brief Crendentials context for QUIC. */
- struct knot_quic_creds *quic_creds;
+ struct knot_creds *quic_creds;
} server_t;
/*!
diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c
index 305b2f4..7af2f58 100644
--- a/src/knot/server/tcp-handler.c
+++ b/src/knot/server/tcp-handler.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -36,6 +36,7 @@
#include "knot/common/fdset.h"
#include "knot/nameserver/process_query.h"
#include "knot/query/layer.h"
+#include "libknot/quic/tls.h"
#include "contrib/macros.h"
#include "contrib/mempattern.h"
#include "contrib/net.h"
@@ -57,6 +58,7 @@ typedef struct tcp_context {
unsigned max_worker_fds; /*!< Max TCP clients per worker configuration + no. of ifaces. */
int idle_timeout; /*!< [s] TCP idle timeout configuration. */
int io_timeout; /*!< [ms] TCP send/recv timeout configuration. */
+ struct knot_tls_ctx *tls_ctx; /*!< DoT answering context. */
} tcp_context_t;
#define TCP_SWEEP_INTERVAL 2 /*!< [secs] granularity of connection sweeping. */
@@ -76,11 +78,22 @@ static void update_tcp_conf(tcp_context_t *tcp)
tcp->idle_timeout = pconf->cache.srv_tcp_idle_timeout;
tcp->io_timeout = pconf->cache.srv_tcp_io_timeout;
rcu_read_unlock();
+
+ if (tcp->tls_ctx != NULL) {
+ tcp->tls_ctx->io_timeout = tcp->io_timeout;
+ }
+}
+
+static void free_tls_ctx(fdset_t *set, int idx)
+{
+ void *tls_conn = *fdset_ctx2(set, idx);
+ knot_tls_conn_del(tls_conn);
}
/*! \brief Sweep TCP connection. */
-static fdset_sweep_state_t tcp_sweep(fdset_t *set, int fd, _unused_ void *data)
+static fdset_sweep_state_t tcp_sweep(fdset_t *set, int idx, _unused_ void *data)
{
+ const int fd = fdset_get_fd(set, idx);
assert(set && fd >= 0);
/* Best-effort, name and shame. */
@@ -92,6 +105,8 @@ static fdset_sweep_state_t tcp_sweep(fdset_t *set, int fd, _unused_ void *data)
log_notice("TCP, terminated inactive client, address %s", addr_str);
}
+ free_tls_ctx(set, idx);
+
return FDSET_SWEEP;
}
@@ -107,15 +122,14 @@ static void tcp_log_error(const struct sockaddr_storage *ss, const char *operati
}
static unsigned tcp_set_ifaces(const iface_t *ifaces, size_t n_ifaces,
- fdset_t *fds, int thread_id)
+ fdset_t *fds, int thread_id, bool *tls)
{
if (n_ifaces == 0) {
return 0;
}
for (const iface_t *i = ifaces; i != ifaces + n_ifaces; i++) {
- if (i->fd_tcp_count == 0 || i->quic) { // Ignore XDP and QUIC interfaces.
- assert(i->fd_xdp_count > 0 || i->quic);
+ if (i->fd_xdp_count > 0 || i->fd_tcp_count == 0) { // Ignore XDP and QUIC interfaces.
continue;
}
@@ -133,31 +147,44 @@ static unsigned tcp_set_ifaces(const iface_t *ifaces, size_t n_ifaces,
if (ret < 0) {
return 0;
}
+ if (i->tls) {
+ *tls = true;
+ }
}
return fdset_get_length(fds);
}
-static int tcp_handle(tcp_context_t *tcp, int fd, const sockaddr_t *remote,
- const sockaddr_t *local, struct iovec *rx, struct iovec *tx)
+static int tcp_handle(tcp_context_t *tcp, knotd_qdata_params_t *params,
+ struct iovec *rx, struct iovec *tx)
{
- /* Create query processing parameter. */
- knotd_qdata_params_t params = params_init(KNOTD_QUERY_PROTO_TCP, remote, local,
- fd, tcp->server, tcp->thread_id);
-
rx->iov_len = KNOT_WIRE_MAX_PKTSIZE;
tx->iov_len = KNOT_WIRE_MAX_PKTSIZE;
/* Receive data. */
- int recv = net_dns_tcp_recv(fd, rx->iov_base, rx->iov_len, tcp->io_timeout);
+ int recv;
+ if (params->tls_conn != NULL) {
+ int ret = knot_tls_handshake(params->tls_conn, true);
+ switch (ret) {
+ case KNOT_EAGAIN: // Unfinished handshake, continue later.
+ return KNOT_EOK;
+ case KNOT_EOK: // Finished handshake, continue with receiving message.
+ recv = knot_tls_recv_dns(params->tls_conn, rx->iov_base, rx->iov_len);
+ break;
+ default: // E.g. handshake timeout.
+ return ret;
+ }
+ } else {
+ recv = net_dns_tcp_recv(params->socket, rx->iov_base, rx->iov_len, tcp->io_timeout);
+ }
if (recv > 0) {
rx->iov_len = recv;
} else {
- tcp_log_error(params.remote, "receive", recv);
+ tcp_log_error(params->remote, "receive", recv);
return KNOT_EOF;
}
- handle_query(&params, &tcp->layer, rx, NULL);
+ handle_query(params, &tcp->layer, rx, NULL);
/* Resolve until NOOP or finished. */
knot_pkt_t *ans = knot_pkt_new(tx->iov_base, tx->iov_len, tcp->layer.mm);
@@ -165,10 +192,15 @@ static int tcp_handle(tcp_context_t *tcp, int fd, const sockaddr_t *remote,
knot_layer_produce(&tcp->layer, ans);
/* Send, if response generation passed and wasn't ignored. */
if (ans->size > 0 && send_state(tcp->layer.state)) {
- int sent = net_dns_tcp_send(fd, ans->wire, ans->size,
- tcp->io_timeout, NULL);
+ int sent;
+ if (params->tls_conn != NULL) {
+ sent = knot_tls_send_dns(params->tls_conn, ans->wire, ans->size);
+ } else {
+ sent = net_dns_tcp_send(params->socket, ans->wire, ans->size,
+ tcp->io_timeout, NULL);
+ }
if (sent != ans->size) {
- tcp_log_error(params.remote, "send", sent);
+ tcp_log_error(params->remote, "send", sent);
handle_finish(&tcp->layer);
return KNOT_EOF;
}
@@ -222,12 +254,42 @@ static int tcp_event_serve(tcp_context_t *tcp, unsigned i, const iface_t *iface)
}
}
- int ret = tcp_handle(tcp, fd, remote, local, &tcp->iov[0], &tcp->iov[1]);
+ knotd_qdata_params_t params = params_init(iface->tls ? KNOTD_QUERY_PROTO_TLS
+ : KNOTD_QUERY_PROTO_TCP,
+ remote, local, fd, tcp->server,
+ tcp->thread_id);
+
+ // NOTE there is no way to avoid calling accept() on unwanted connections:
+ // - it's not possible to read out the remote IP beforehand
+ // - there is no way to pull it out of the queue
+ // So we just accept() those connection (possibly going ahead with the handshake)
+ // and close it immediately.
+ if (process_query_proto(&params, KNOTD_STAGE_PROTO_BEGIN) == KNOTD_PROTO_STATE_BLOCK) {
+ return KNOT_EDENIED; // results in closing connection
+ }
+
+ /* Establish a TLS session. */
+ if (iface->tls) {
+ assert(tcp->tls_ctx != NULL);
+ knot_tls_conn_t *tls_conn = *fdset_ctx2(&tcp->set, i);
+ if (tls_conn == NULL) {
+ tls_conn = knot_tls_conn_new(tcp->tls_ctx, fd);
+ if (tls_conn == NULL) {
+ return KNOT_ENOMEM;
+ }
+ *fdset_ctx2(&tcp->set, i) = tls_conn;
+ }
+ params_update_tls(&params, tls_conn);
+ }
+
+ int ret = tcp_handle(tcp, &params, &tcp->iov[0], &tcp->iov[1]);
if (ret == KNOT_EOK) {
/* Update socket activity timer. */
(void)fdset_set_watchdog(&tcp->set, i, tcp->idle_timeout);
}
+ (void)process_query_proto(&params, KNOTD_STAGE_PROTO_END);
+
return ret;
}
@@ -273,6 +335,7 @@ static void tcp_wait_for_events(tcp_context_t *tcp)
/* Evaluate. */
if (should_close) {
+ free_tls_ctx(set, idx);
fdset_it_remove(&it);
}
}
@@ -325,13 +388,15 @@ int tcp_master(dthread_t *thread)
/* Prepare initial buffer for listening and bound sockets. */
if (fdset_init(&tcp.set, FDSET_RESIZE_STEP) != KNOT_EOK) {
+ ret = KNOT_ENOMEM;
goto finish;
}
/* Set descriptors for the configured interfaces. */
+ bool tls = false;
tcp.client_threshold = tcp_set_ifaces(handler->server->ifaces,
handler->server->n_ifaces,
- &tcp.set, thread_id);
+ &tcp.set, thread_id, &tls);
if (tcp.client_threshold == 0) {
goto finish; /* Terminate on zero interfaces. */
}
@@ -341,6 +406,17 @@ int tcp_master(dthread_t *thread)
update_sweep_timer(&next_sweep);
update_tcp_conf(&tcp);
+ /* Initialize TLS context. */
+ if (tls) {
+ // Set the HS timeout to 8x the RMT IO one as the HS duration can be up to 4*roundtrip.
+ tcp.tls_ctx = knot_tls_ctx_new(handler->server->quic_creds,
+ tcp.io_timeout, 8 * tcp.io_timeout, true);
+ if (tcp.tls_ctx == NULL) {
+ ret = KNOT_ENOMEM;
+ goto finish;
+ }
+ }
+
for (;;) {
/* Check for cancellation. */
if (dt_is_cancelled(thread)) {
@@ -359,6 +435,7 @@ int tcp_master(dthread_t *thread)
}
finish:
+ knot_tls_ctx_free(tcp.tls_ctx);
free(tcp.iov[0].iov_base);
free(tcp.iov[1].iov_base);
mp_delete(mm.ctx);
diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c
index e54ce2c..c5166f9 100644
--- a/src/knot/server/udp-handler.c
+++ b/src/knot/server/udp-handler.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -73,9 +73,15 @@ typedef struct {
static void udp_handler(udp_context_t *udp, knotd_qdata_params_t *params,
struct iovec *rx, struct iovec *tx)
{
+ if (process_query_proto(params, KNOTD_STAGE_PROTO_BEGIN) == KNOTD_PROTO_STATE_BLOCK) {
+ return;
+ }
+
// Prepare a reply.
struct sockaddr_storage proxied_remote;
handle_udp_reply(params, &udp->layer, rx, tx, &proxied_remote);
+
+ (void)process_query_proto(params, KNOTD_STAGE_PROTO_END);
}
typedef struct {
@@ -153,7 +159,7 @@ void cmsg_handle(const struct msghdr *rx, struct msghdr *tx,
}
struct cmsghdr *cmsg = CMSG_FIRSTHDR(tx);
- if (iface->quic) {
+ if (iface->tls) {
*p_ecn = NULL;
while (cmsg != NULL) {
cmsg_handle_ecn(p_ecn, cmsg);
@@ -246,9 +252,9 @@ static void udp_msg_handle(udp_context_t *ctx, const iface_t *iface, void *d)
/* Process received pkt. */
knotd_qdata_params_t params = params_init(
- iface->quic ? KNOTD_QUERY_PROTO_QUIC : KNOTD_QUERY_PROTO_UDP,
+ iface->tls ? KNOTD_QUERY_PROTO_QUIC : KNOTD_QUERY_PROTO_UDP,
&rq->addr, local, rq->fd, ctx->server, ctx->thread_id);
- if (iface->quic) {
+ if (iface->tls) {
#ifdef ENABLE_QUIC
quic_handler(&params, &ctx->layer, ctx->quic_idle_close,
ctx->quic_table, &rq->iov[RX], &rq->msg[TX], p_ecn);
@@ -357,9 +363,9 @@ static void udp_mmsg_handle(udp_context_t *ctx, const iface_t *iface, void *d)
const sockaddr_t *local = local_addr(&ctx->local, iface);
knotd_qdata_params_t params = params_init(
- iface->quic ? KNOTD_QUERY_PROTO_QUIC : KNOTD_QUERY_PROTO_UDP,
+ iface->tls ? KNOTD_QUERY_PROTO_QUIC : KNOTD_QUERY_PROTO_UDP,
&rq->addrs[i], local, rq->fd, ctx->server, ctx->thread_id);
- if (iface->quic) {
+ if (iface->tls) {
#ifdef ENABLE_QUIC
quic_handler(&params, &ctx->layer, ctx->quic_idle_close,
ctx->quic_table, rx->msg_iov, tx, p_ecn);
@@ -432,7 +438,7 @@ static int xdp_mmsg_recv(_unused_ int fd, void *d)
static void xdp_mmsg_handle(udp_context_t *ctx, _unused_ const iface_t *iface, void *d)
{
- assert(!iface->quic);
+ assert(!iface->tls);
xdp_handle_msgs(d, &ctx->layer, ctx->server, ctx->thread_id);
}
@@ -482,7 +488,6 @@ static int iface_udp_fd(const iface_t *iface, int thread_id, bool xdp_thread,
#endif
} else { // UDP thread.
if (iface->fd_udp_count == 0) { // No UDP interfaces.
- assert(iface->fd_xdp_count > 0);
return -1;
}
#ifdef ENABLE_REUSEPORT
@@ -508,7 +513,7 @@ static unsigned udp_set_ifaces(const server_t *server, size_t n_ifaces, fdset_t
#ifndef ENABLE_REUSEPORT
/* If loadbalanced SO_REUSEPORT isn't available, ensure that
* just one (first) UDP worker handles the QUIC sockets. */
- if (i->quic && thread_id > 0) {
+ if (i->tls && thread_id > 0) {
continue;
}
#endif
@@ -520,7 +525,7 @@ static unsigned udp_set_ifaces(const server_t *server, size_t n_ifaces, fdset_t
if (ret < 0) {
return 0;
}
- if (i->quic) {
+ if (i->tls) {
*quic = true;
}
}
diff --git a/src/knot/server/xdp-handler.c b/src/knot/server/xdp-handler.c
index ae8512d..403bb70 100644
--- a/src/knot/server/xdp-handler.c
+++ b/src/knot/server/xdp-handler.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
#include <stdlib.h>
#include <urcu.h>
+#include "knot/nameserver/process_query.h"
#include "knot/server/handler.h"
#include "knot/server/quic-handler.h"
#include "knot/server/xdp-handler.h"
@@ -196,6 +197,12 @@ static void handle_udp(xdp_handle_ctx_t *ctx, knot_layer_t *layer,
continue;
}
+ params_xdp_update(params, KNOTD_QUERY_PROTO_UDP, msg_recv);
+
+ if (process_query_proto(params, KNOTD_STAGE_PROTO_BEGIN) == KNOTD_PROTO_STATE_BLOCK) {
+ continue;
+ }
+
// Try to allocate a buffer for a reply.
if (knot_xdp_reply_alloc(ctx->sock, msg_recv, msg_send) != KNOT_EOK) {
if (log_enabled_debug()) {
@@ -206,36 +213,50 @@ static void handle_udp(xdp_handle_ctx_t *ctx, knot_layer_t *layer,
ctx->msg_udp_count++;
// Prepare a reply.
- params_xdp_update(params, KNOTD_QUERY_PROTO_UDP, msg_recv, 0, NULL);
handle_udp_reply(params, layer, &msg_recv->payload, &msg_send->payload,
&proxied_remote);
+
+ (void)process_query_proto(params, KNOTD_STAGE_PROTO_END);
}
}
static void handle_tcp(xdp_handle_ctx_t *ctx, knot_layer_t *layer,
knotd_qdata_params_t *params)
{
- int ret = knot_tcp_recv(ctx->relays, ctx->msg_recv, ctx->msg_recv_count,
- ctx->tcp_table, ctx->syn_table, XDP_TCP_IGNORE_NONE);
- if (ret != KNOT_EOK) {
- if (log_enabled_debug()) {
- log_debug("TCP/XDP, failed to process some packets (%s)", knot_strerror(ret));
- }
- return;
- } else if (knot_tcp_relay_empty(&ctx->relays[0])) { // no TCP traffic
- return;
- }
uint8_t ans_buf[KNOT_WIRE_MAX_PKTSIZE];
for (uint32_t i = 0; i < ctx->msg_recv_count; i++) {
+ knot_xdp_msg_t *msg_recv = &ctx->msg_recv[i];
knot_tcp_relay_t *rl = &ctx->relays[i];
+ if (!(msg_recv->flags & KNOT_XDP_MSG_TCP)) {
+ continue;
+ }
+
+ params_xdp_update(params, KNOTD_QUERY_PROTO_TCP, msg_recv);
+
+ if (process_query_proto(params, KNOTD_STAGE_PROTO_BEGIN) == KNOTD_PROTO_STATE_BLOCK) {
+ continue;
+ }
+
+ int ret = knot_tcp_recv(rl, msg_recv, ctx->tcp_table,
+ ctx->syn_table, XDP_TCP_IGNORE_NONE);
+ if (ret != KNOT_EOK) {
+ if (log_enabled_debug()) {
+ log_debug("TCP/XDP, failed to process some packets (%s)",
+ knot_strerror(ret));
+ }
+ continue;
+ } else if (knot_tcp_relay_empty(rl)) {
+ continue;
+ }
+
+ params_update_tcp(params, rl->conn->establish_rtt);
+
// Process all complete DNS queries in one TCP stream.
for (size_t j = 0; rl->inbf != NULL && j < rl->inbf->n_inbufs; j++) {
// Consume the query.
- params_xdp_update(params, KNOTD_QUERY_PROTO_TCP, ctx->msg_recv,
- rl->conn->establish_rtt, NULL);
struct iovec *inbufs = rl->inbf->inbufs;
handle_query(params, layer, &inbufs[j], NULL);
@@ -253,6 +274,8 @@ static void handle_tcp(xdp_handle_ctx_t *ctx, knot_layer_t *layer,
handle_finish(layer);
}
+
+ (void)process_query_proto(params, KNOTD_STAGE_PROTO_END);
}
}
@@ -274,6 +297,12 @@ static void handle_quic(xdp_handle_ctx_t *ctx, knot_layer_t *layer,
continue;
}
+ params_xdp_update(params, KNOTD_QUERY_PROTO_QUIC, msg_recv);
+
+ if (process_query_proto(params, KNOTD_STAGE_PROTO_BEGIN) == KNOTD_PROTO_STATE_BLOCK) {
+ continue;
+ }
+
knot_quic_reply_t *reply = &ctx->quic_replies[i];
knot_xdp_msg_t *msg_out = &ctx->msg_send_udp[i];
@@ -289,7 +318,9 @@ static void handle_quic(xdp_handle_ctx_t *ctx, knot_layer_t *layer,
&ctx->quic_relays[i]);
knot_quic_conn_t *conn = ctx->quic_relays[i];
- handle_quic_streams(conn, params, layer, &ctx->msg_recv[i]);
+ handle_quic_streams(conn, params, layer);
+
+ (void)process_query_proto(params, KNOTD_STAGE_PROTO_END);
}
#else
(void)(ctx);
diff --git a/src/knot/updates/acl.c b/src/knot/updates/acl.c
index d297747..456e8bb 100644
--- a/src/knot/updates/acl.c
+++ b/src/knot/updates/acl.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,16 +18,13 @@
#include "contrib/string.h"
#include "contrib/wire_ctx.h"
-#ifdef ENABLE_QUIC
-#include "libknot/quic/quic.h"
-#endif // ENABLE_QUIC
static bool cert_pin_check(const uint8_t *session_pin, size_t session_pin_size,
conf_val_t *pins)
{
if (pins->code == KNOT_ENOENT) { // No certificate pin authentication required.
return true;
- } else if (session_pin_size == 0) { // Not a QUIC connection.
+ } else if (session_pin_size == 0) { // Not a TLS/QUIC connection.
return false;
}
@@ -283,20 +280,15 @@ static bool check_addr_key(conf_t *conf, conf_val_t *addr_val, conf_val_t *key_v
bool acl_allowed(conf_t *conf, conf_val_t *acl, acl_action_t action,
const struct sockaddr_storage *addr, knot_tsig_key_t *tsig,
const knot_dname_t *zone_name, knot_pkt_t *query,
- struct knot_quic_conn *conn)
+ struct gnutls_session_int *tls_session)
{
if (acl == NULL || addr == NULL || tsig == NULL) {
return false;
}
-#ifdef ENABLE_QUIC
- uint8_t session_pin[KNOT_QUIC_PIN_LEN];
+ uint8_t session_pin[KNOT_TLS_PIN_LEN];
size_t session_pin_size = sizeof(session_pin);
- knot_quic_conn_pin(conn, session_pin, &session_pin_size, false);
-#else
- uint8_t session_pin[1];
- size_t session_pin_size = 0;
-#endif // ENABLE_QUIC
+ knot_tls_pin(tls_session, session_pin, &session_pin_size, false);
bool forward = false;
if (action == ACL_ACTION_UPDATE) {
@@ -392,20 +384,15 @@ next_acl:
}
bool rmt_allowed(conf_t *conf, conf_val_t *rmts, const struct sockaddr_storage *addr,
- knot_tsig_key_t *tsig, struct knot_quic_conn *conn)
+ knot_tsig_key_t *tsig, struct gnutls_session_int *tls_session)
{
if (!conf->cache.srv_auto_acl) {
return false;
}
-#ifdef ENABLE_QUIC
- uint8_t session_pin[KNOT_QUIC_PIN_LEN];
+ uint8_t session_pin[KNOT_TLS_PIN_LEN];
size_t session_pin_size = sizeof(session_pin);
- knot_quic_conn_pin(conn, session_pin, &session_pin_size, false);
-#else
- uint8_t session_pin[1];
- size_t session_pin_size = 0;
-#endif // ENABLE_QUIC
+ knot_tls_pin(tls_session, session_pin, &session_pin_size, false);
conf_mix_iter_t iter;
conf_mix_iter_init(conf, rmts, &iter);
diff --git a/src/knot/updates/acl.h b/src/knot/updates/acl.h
index 88d7de5..5072a29 100644
--- a/src/knot/updates/acl.h
+++ b/src/knot/updates/acl.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
#include <stdbool.h>
#include <sys/socket.h>
+#include "libknot/quic/tls_common.h"
#include "libknot/tsig.h"
#include "knot/conf/conf.h"
@@ -51,21 +52,21 @@ typedef enum {
*
* If a proper ACL rule is found and tsig.name is not empty, tsig.secret is filled.
*
- * \param conf Configuration.
- * \param acl Pointer to ACL config multivalued identifier.
- * \param action ACL action.
- * \param addr IP address.
- * \param tsig TSIG parameters.
- * \param zone_name Zone name.
- * \param query Update query.
- * \param conn Possible QUIC connection.
+ * \param conf Configuration.
+ * \param acl Pointer to ACL config multivalued identifier.
+ * \param action ACL action.
+ * \param addr IP address.
+ * \param tsig TSIG parameters.
+ * \param zone_name Zone name.
+ * \param query Update query.
+ * \param tls_session Possible TLS session.
*
* \retval True if authenticated.
*/
bool acl_allowed(conf_t *conf, conf_val_t *acl, acl_action_t action,
const struct sockaddr_storage *addr, knot_tsig_key_t *tsig,
const knot_dname_t *zone_name, knot_pkt_t *query,
- struct knot_quic_conn *conn);
+ struct gnutls_session_int *tls_session);
/*!
* \brief Checks if the address and/or tsig key matches a remote from the list.
@@ -75,13 +76,13 @@ bool acl_allowed(conf_t *conf, conf_val_t *acl, acl_action_t action,
*
* If a proper REMOTE is found and tsig.name is not empty, tsig.secret is filled.
*
- * \param conf Configuration.
- * \param rmts Pointer to REMOTE config multivalued identifier.
- * \param addr IP address.
- * \param tsig TSIG parameters.
- * \param conn Possible QUIC connection.
+ * \param conf Configuration.
+ * \param rmts Pointer to REMOTE config multivalued identifier.
+ * \param addr IP address.
+ * \param tsig TSIG parameters.
+ * \param tls_session Possible TLS session.
*
* \retval True if authenticated.
*/
bool rmt_allowed(conf_t *conf, conf_val_t *rmts, const struct sockaddr_storage *addr,
- knot_tsig_key_t *tsig, struct knot_quic_conn *conn);
+ knot_tsig_key_t *tsig, struct gnutls_session_int *tls_session);
diff --git a/src/knot/updates/ddns.c b/src/knot/updates/ddns.c
index 5ab858f..47e3fc5 100644
--- a/src/knot/updates/ddns.c
+++ b/src/knot/updates/ddns.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -266,21 +266,41 @@ static bool node_contains_rr(const zone_node_t *node,
/*!< \brief Returns true if CNAME is in this node. */
static bool adding_to_cname(const knot_dname_t *owner,
+ zone_update_t *update,
const zone_node_t *node)
{
if (node == NULL) {
- // Node did not exist before update.
+ // Node did not exist before update, juch check DNAMEs above.
+
+ while ((owner = knot_dname_next_label(owner)) != NULL &&
+ (node = zone_update_get_node(update, owner)) == NULL);
+
+ for ( ; node != NULL; node = node->parent) {
+ knot_rrset_t dname = node_rrset(node, KNOT_RRTYPE_DNAME);
+ if (!knot_rrset_empty(&dname)) {
+ // DNAME above
+ return true;
+ }
+ }
+
return false;
}
knot_rrset_t cname = node_rrset(node, KNOT_RRTYPE_CNAME);
- if (knot_rrset_empty(&cname)) {
- // Node did not contain CNAME before update.
- return false;
+ if (!knot_rrset_empty(&cname)) {
+ // CNAME present
+ return true;
}
- // CNAME present
- return true;
+ while ((node = node->parent) != NULL) {
+ knot_rrset_t dname = node_rrset(node, KNOT_RRTYPE_DNAME);
+ if (!knot_rrset_empty(&dname)) {
+ // DNAME above
+ return true;
+ }
+ }
+
+ return false;
}
/*!< \brief Used to ignore SOA deletions and SOAs with lower serial than zone. */
@@ -315,12 +335,29 @@ static int add_rr_to_changeset(const knot_rrset_t *rr, zone_update_t *update)
return zone_update_add(update, rr);
}
-/*!< \brief Processes CNAME addition (replace or ignore) */
+int node_empty_cb(zone_node_t *node, _unused_ void *ctx)
+{
+ return node_empty(node) ? KNOT_EOK : KNOT_ESEMCHECK;
+}
+
+bool subtree_empty(zone_contents_t *zone, const zone_node_t *node)
+{
+ if (node == NULL) {
+ return true;
+ }
+ int ret = zone_tree_sub_apply(zone->nodes, node->owner, true, node_empty_cb, NULL);
+ return (ret == KNOT_EOK);
+}
+
+/*!< \brief Processes CNAME/DNAME addition (replace or ignore) */
static int process_add_cname(const zone_node_t *node,
const knot_rrset_t *rr,
+ uint16_t type,
zone_update_t *update)
{
- knot_rrset_t cname = node_rrset(node, KNOT_RRTYPE_CNAME);
+ assert(type == KNOT_RRTYPE_CNAME || type == KNOT_RRTYPE_DNAME);
+
+ knot_rrset_t cname = node_rrset(node, type);
if (!knot_rrset_empty(&cname)) {
// If they are identical, ignore.
if (knot_rrset_equal(&cname, rr, true)) {
@@ -333,40 +370,24 @@ static int process_add_cname(const zone_node_t *node,
}
return add_rr_to_changeset(rr, update);
- } else if (!node_empty(node)) {
+ } else if (type == KNOT_RRTYPE_CNAME && !node_empty(node)) {
// Other occupied node => ignore.
return KNOT_EOK;
+ } else if (type == KNOT_RRTYPE_DNAME && !subtree_empty(update->new_cont, node)) {
+ // Equivalent to above, ignore.
+ return KNOT_EOK;
+ } else if (type == KNOT_RRTYPE_DNAME && node_rrtype_exists(node, KNOT_RRTYPE_CNAME)) {
+ // RFC 6672 §5.2.
+ return KNOT_EOK;
+ } else if (type == KNOT_RRTYPE_CNAME && adding_to_cname(rr->owner, update, node)) {
+ // DNAME exists above CNAME, ignore.
+ return KNOT_EOK;
} else {
// Can add.
return add_rr_to_changeset(rr, update);
}
}
-/*!< \brief Processes NSEC3PARAM addition (ignore when not removed, or non-apex) */
-static int process_add_nsec3param(const zone_node_t *node,
- const knot_rrset_t *rr,
- zone_update_t *update)
-{
- if (node == NULL || !node_rrtype_exists(node, KNOT_RRTYPE_SOA)) {
- // Ignore non-apex additions
- char *owner = knot_dname_to_str_alloc(rr->owner);
- log_warning("DDNS, refusing to add NSEC3PARAM to non-apex "
- "node '%s'", owner);
- free(owner);
- return KNOT_EDENIED;
- }
- knot_rrset_t param = node_rrset(node, KNOT_RRTYPE_NSEC3PARAM);
- if (knot_rrset_empty(&param)) {
- return add_rr_to_changeset(rr, update);
- }
-
- char *owner = knot_dname_to_str_alloc(rr->owner);
- log_warning("DDNS, refusing to add second NSEC3PARAM to node '%s'", owner);
- free(owner);
-
- return KNOT_EOK;
-}
-
/*!
* \brief Processes SOA addition (ignore when non-apex), lower serials
* dropped before.
@@ -395,7 +416,7 @@ static int process_add_normal(const zone_node_t *node,
const knot_rrset_t *rr,
zone_update_t *update)
{
- if (adding_to_cname(rr->owner, node)) {
+ if (adding_to_cname(rr->owner, update, node)) {
// Adding RR to CNAME node, ignore.
return KNOT_EOK;
}
@@ -415,11 +436,10 @@ static int process_add(const knot_rrset_t *rr,
{
switch(rr->type) {
case KNOT_RRTYPE_CNAME:
- return process_add_cname(node, rr, update);
+ case KNOT_RRTYPE_DNAME:
+ return process_add_cname(node, rr, rr->type, update);
case KNOT_RRTYPE_SOA:
return process_add_soa(node, rr, update);
- case KNOT_RRTYPE_NSEC3PARAM:
- return process_add_nsec3param(node, rr, update);
default:
return process_add_normal(node, rr, update);
}
@@ -531,49 +551,32 @@ static int process_remove(const knot_rrset_t *rr,
}
}
-/*!< \brief Checks whether addition has not violated DNAME rules. */
-static bool sem_check(const knot_rrset_t *rr, const zone_node_t *zone_node,
- zone_update_t *update)
-{
- const zone_node_t *added_node = zone_contents_find_node(update->new_cont, rr->owner);
-
- // we do this sem check AFTER adding the RR, so the node must exist
- assert(added_node != NULL);
-
- for (const zone_node_t *parent = added_node->parent;
- parent != NULL; parent = parent->parent) {
- if (node_rrtype_exists(parent, KNOT_RRTYPE_DNAME)) {
- // Parent has DNAME RRSet, refuse update
- return false;
- }
- }
-
- if (rr->type != KNOT_RRTYPE_DNAME || zone_node == NULL) {
- return true;
- }
-
- // Check that we have not created node with DNAME children.
- if (zone_node->children > 0) {
- // Updated node has children and DNAME was added, refuse update
- return false;
- }
-
- return true;
-}
-
/*!< \brief Checks whether we can accept this RR. */
static int check_update(const knot_rrset_t *rrset, const knot_pkt_t *query,
- uint16_t *rcode)
+ const zone_contents_t *zone, uint16_t *rcode)
{
/* Accept both subdomain and dname match. */
const knot_dname_t *owner = rrset->owner;
const knot_dname_t *qname = knot_pkt_qname(query);
+ assert(knot_dname_is_equal(qname, zone->apex->owner));
const int in_bailiwick = knot_dname_in_bailiwick(owner, qname);
if (in_bailiwick < 0) {
*rcode = KNOT_RCODE_NOTZONE;
return KNOT_EOUTOFZONE;
}
+ if (rrset->type == KNOT_RRTYPE_NSEC3PARAM) {
+ if (!knot_dname_is_equal(rrset->owner, zone->apex->owner)) {
+ log_warning("DDNS, refusing to add NSEC3PARAM to non-apex node");
+ *rcode = KNOT_RCODE_REFUSED;
+ return KNOT_EDENIED;
+ } else if (node_rrtype_exists(zone->apex, rrset->type)) {
+ log_warning("DDNS, refusing to add second NSEC3PARAM to zone apex");
+ *rcode = KNOT_RCODE_REFUSED;
+ return KNOT_EDENIED;
+ }
+ }
+
if (rrset->rclass == knot_pkt_qclass(query)) {
if (knot_rrtype_is_metatype(rrset->type)) {
*rcode = KNOT_RCODE_FORMERR;
@@ -605,13 +608,7 @@ static int process_rr(const knot_rrset_t *rr, zone_update_t *update)
const zone_node_t *node = zone_update_get_node(update, rr->owner);
if (is_addition(rr)) {
- int ret = process_add(rr, node, update);
- if (ret == KNOT_EOK) {
- if (!sem_check(rr, node, update)) {
- return KNOT_EDENIED;
- }
- }
- return ret;
+ return process_add(rr, node, update);
} else if (is_removal(rr)) {
return process_remove(rr, node, update);
} else {
@@ -660,6 +657,27 @@ int ddns_process_prereqs(const knot_pkt_t *query, zone_update_t *update,
return ret;
}
+int ddns_precheck_update(const knot_pkt_t *query, zone_update_t *update,
+ uint16_t *rcode)
+{
+ if (query == NULL || rcode == NULL || update == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Check all RRs in the authority section.
+ const knot_pktsection_t *authority = knot_pkt_section(query, KNOT_AUTHORITY);
+ const knot_rrset_t *authority_rr = (authority->count > 0) ? knot_pkt_rr(authority, 0) : NULL;
+ for (uint16_t i = 0; i < authority->count; ++i) {
+ int ret = check_update(&authority_rr[i], query, update->new_cont, rcode);
+ if (ret != KNOT_EOK) {
+ assert(*rcode != KNOT_RCODE_NOERROR);
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
int ddns_process_update(const knot_pkt_t *query, zone_update_t *update,
uint16_t *rcode)
{
@@ -677,18 +695,11 @@ int ddns_process_update(const knot_pkt_t *query, zone_update_t *update,
const knot_rrset_t *authority_rr = (authority->count > 0) ? knot_pkt_rr(authority, 0) : NULL;
for (uint16_t i = 0; i < authority->count; ++i) {
const knot_rrset_t *rr = &authority_rr[i];
- // Check if RR is correct.
- int ret = check_update(rr, query, rcode);
- if (ret != KNOT_EOK) {
- assert(*rcode != KNOT_RCODE_NOERROR);
- return ret;
- }
-
if (skip_soa(rr, sn_old)) {
continue;
}
- ret = process_rr(rr, update);
+ int ret = process_rr(rr, update);
if (ret != KNOT_EOK) {
*rcode = ret_to_rcode(ret);
return ret;
diff --git a/src/knot/updates/ddns.h b/src/knot/updates/ddns.h
index d5661f5..40f1b37 100644
--- a/src/knot/updates/ddns.h
+++ b/src/knot/updates/ddns.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -32,6 +32,18 @@ int ddns_process_prereqs(const knot_pkt_t *query, zone_update_t *update,
uint16_t *rcode);
/*!
+ * \brief Performs a pre-check of the update'S sanity.
+ *
+ * \param query DNS message containing the update.
+ * \param update Zone to be checked.
+ * \param rcode Returned DNS RCODE.
+ *
+ * \return KNOT_E*
+ */
+int ddns_precheck_update(const knot_pkt_t *query, zone_update_t *update,
+ uint16_t *rcode);
+
+/*!
* \brief Processes DNS update and creates a changeset out of it. Zone is left
* intact.
*
diff --git a/src/knot/updates/zone-update.c b/src/knot/updates/zone-update.c
index 73c9558..4b65a2c 100644
--- a/src/knot/updates/zone-update.c
+++ b/src/knot/updates/zone-update.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,8 +19,8 @@
#include <urcu.h>
#include "knot/catalog/interpret.h"
+#include "knot/common/dbus.h"
#include "knot/common/log.h"
-#include "knot/common/systemd.h"
#include "knot/dnssec/zone-events.h"
#include "knot/server/server.h"
#include "knot/updates/zone-update.h"
@@ -942,26 +942,10 @@ int zone_update_commit(conf_t *conf, zone_update_t *update)
val = conf_zone_get(conf, C_DNSSEC_VALIDATION, update->zone->name);
if (conf_bool(&val)) {
bool incr_valid = update->flags & UPDATE_INCREMENTAL;
- const char *msg_valid = incr_valid ? "incremental " : "";
-
- ret = knot_dnssec_validate_zone(update, conf, 0, incr_valid);
+ ret = knot_dnssec_validate_zone(update, conf, 0, incr_valid, true);
if (ret != KNOT_EOK) {
- log_zone_error(update->zone->name, "DNSSEC, %svalidation failed (%s)",
- msg_valid, knot_strerror(ret));
- char type_str[16];
- knot_dname_txt_storage_t name_str;
- if (knot_dname_to_str(name_str, update->validation_hint.node, sizeof(name_str)) != NULL &&
- knot_rrtype_to_string(update->validation_hint.rrtype, type_str, sizeof(type_str)) >= 0) {
- log_zone_error(update->zone->name, "DNSSEC, validation hint: %s %s",
- name_str, type_str);
- }
discard_adds_tree(update);
- if (conf->cache.srv_dbus_event & DBUS_EVENT_ZONE_INVALID) {
- systemd_emit_zone_invalid(update->zone->name);
- }
return ret;
- } else {
- log_zone_info(update->zone->name, "DNSSEC, %svalidation successful", msg_valid);
}
}
@@ -1023,8 +1007,8 @@ int zone_update_commit(conf_t *conf, zone_update_t *update)
}
if (conf->cache.srv_dbus_event & DBUS_EVENT_ZONE_UPDATED) {
- systemd_emit_zone_updated(update->zone->name,
- zone_contents_serial(update->zone->contents));
+ dbus_emit_zone_updated(update->zone->name,
+ zone_contents_serial(update->zone->contents));
}
memset(update, 0, sizeof(*update));
diff --git a/src/knot/updates/zone-update.h b/src/knot/updates/zone-update.h
index 0499d72..814d0ec 100644
--- a/src/knot/updates/zone-update.h
+++ b/src/knot/updates/zone-update.h
@@ -25,7 +25,9 @@
typedef struct {
knot_dname_storage_t next;
const knot_dname_t *node;
+ uint32_t remaining_secs;
uint16_t rrtype;
+ int warning;
} dnssec_validation_hint_t;
/*! \brief Structure for zone contents updating / querying. */
diff --git a/src/knot/zone/backup.c b/src/knot/zone/backup.c
index 36fd577..5c3038a 100644
--- a/src/knot/zone/backup.c
+++ b/src/knot/zone/backup.c
@@ -85,6 +85,7 @@ int zone_backup_init(bool restore_mode, knot_backup_params_t filters, bool force
ctx->restore_mode = restore_mode;
ctx->backup_params = filters;
ctx->in_backup = 0; // Just to be sure.
+ ctx->arch_match = true;
ctx->forced = forced;
ctx->backup_format = BACKUP_VERSION;
ctx->backup_global = false;
@@ -105,6 +106,11 @@ int zone_backup_init(bool restore_mode, knot_backup_params_t filters, bool force
// For restore, check that there are all required data components in the backup.
if (restore_mode) {
+ if (!ctx->arch_match && filters & BACKUP_PARAM_DB) {
+ free(ctx);
+ return KNOT_ECPUCOMPAT;
+ }
+
// '+kaspdb' in backup provides data also for '+keysonly' restore.
knot_backup_params_t available = ctx->in_backup |
((bool)(ctx->in_backup & BACKUP_PARAM_KASPDB) * BACKUP_PARAM_KEYSONLY);
@@ -369,7 +375,7 @@ static int backup_keystore(conf_t *conf, zone_t *zone, zone_backup_ctx_t *ctx)
return ret;
}
if (backend_type == KEYSTORE_BACKEND_PKCS11) {
- log_zone_notice(zone->name, "private keys from PKCS #11 aren't subject of backup/restore");
+ log_zone_notice(zone->name, "private keys from PKCS #11 are not subject of backup/restore");
(void)dnssec_keystore_deinit(from);
return KNOT_EOK;
}
diff --git a/src/knot/zone/backup.h b/src/knot/zone/backup.h
index 9f2660d..ad0c954 100644
--- a/src/knot/zone/backup.h
+++ b/src/knot/zone/backup.h
@@ -57,6 +57,11 @@ typedef enum {
BACKUP_PARAM_TIMERS | BACKUP_PARAM_KASPDB | \
BACKUP_PARAM_CATALOG)
+/*! \bref Backup components using LMDB databases. */
+#define BACKUP_PARAM_DB (BACKUP_PARAM_JOURNAL | BACKUP_PARAM_TIMERS | \
+ BACKUP_PARAM_KASPDB | BACKUP_PARAM_KEYSONLY | \
+ BACKUP_PARAM_CATALOG)
+
typedef struct {
const char *name;
knot_backup_params_t param;
@@ -70,6 +75,7 @@ typedef struct zone_backup_ctx {
bool forced; // if true, the force flag has been set
knot_backup_params_t backup_params; // bit-mapped list of backup components
knot_backup_params_t in_backup; // bit-mapped list of components available in backup
+ bool arch_match; // match of the system and the backup architectures
bool backup_global; // perform global backup for all zones
ssize_t readers; // when decremented to 0, all zones done, free this context
pthread_mutex_t readers_mutex; // mutex covering readers counter
diff --git a/src/knot/zone/backup_dir.c b/src/knot/zone/backup_dir.c
index 7bf9fd5..b382258 100644
--- a/src/knot/zone/backup_dir.c
+++ b/src/knot/zone/backup_dir.c
@@ -28,11 +28,21 @@
#include "contrib/getline.h"
#include "knot/common/log.h"
+#ifdef ENDIANITY_LITTLE
+ #define ENDIAN_STR "LE"
+#else
+ #define ENDIAN_STR "BE"
+#endif
+#define _STR(x) #x
+#define STR(x) _STR(x)
+#define KNOT_ARCH STR(__LONG_WIDTH__) ENDIAN_STR
+
#define LABEL_FILE "knot_backup.label"
#define LOCK_FILE "lock.knot_backup"
#define LABEL_FILE_HEAD "label: Knot DNS Backup\n"
#define LABEL_FILE_FORMAT "backup_format: %d\n"
+#define LABEL_FILE_ARCH "architecture: "
#define LABEL_FILE_PARAMS "parameters: "
#define LABEL_FILE_BACKUPDIR "backupdir "
#define LABEL_FILE_TIME_FORMAT "%Y-%m-%d %H:%M:%S %Z"
@@ -49,6 +59,7 @@
static const char *label_file_name = LABEL_FILE;
static const char *lock_file_name = LOCK_FILE;
static const char *label_file_head = LABEL_FILE_HEAD;
+static const char *label_file_arch = KNOT_ARCH;
static void get_full_path(zone_backup_ctx_t *ctx, const char *filename,
char *full_path, size_t full_path_size)
@@ -125,6 +136,9 @@ static int make_label_file(zone_backup_ctx_t *ctx)
localtime_r(&now, &tm);
strftime(finished_time, sizeof(finished_time), LABEL_FILE_TIME_FORMAT, &tm);
+ int lmdb_major, lmdb_minor, lmdb_patch;
+ (void)mdb_version(&lmdb_major, &lmdb_minor, &lmdb_patch);
+
// Print the label contents.
char params_str[PARAMS_MAX_LENGTH];
print_params(params_str, ctx->backup_params);
@@ -135,10 +149,14 @@ static int make_label_file(zone_backup_ctx_t *ctx)
"started_time: %s\n"
"finished_time: %s\n"
"knot_version: %s\n"
+ "lmdb_version: %d.%d.%d\n"
+ LABEL_FILE_ARCH "%s\n"
LABEL_FILE_PARAMS "%s+" LABEL_FILE_BACKUPDIR "%s\n"
"zone_count: %d\n",
label_file_head,
- ctx->backup_format, ident, started_time, finished_time, PACKAGE_VERSION,
+ ctx->backup_format, ident, started_time, finished_time,
+ PACKAGE_VERSION,
+ lmdb_major, lmdb_minor, lmdb_patch, label_file_arch,
params_str, ctx->backup_dir,
ctx->zone_count);
@@ -199,6 +217,7 @@ static int get_backup_format(zone_backup_ctx_t *ctx)
unsigned int remain = 3; // Bit-mapped "punch card" for lines to get data from.
while (remain > 0 && knot_getline(&line, &line_size, file) != -1) {
int value;
+ char str[8];
if (sscanf(line, LABEL_FILE_FORMAT, &value) != 0) {
if (value >= BACKUP_FORMAT_TERM) {
ret = KNOT_ENOTSUP;
@@ -212,6 +231,11 @@ static int get_backup_format(zone_backup_ctx_t *ctx)
continue;
}
}
+ if (sscanf(line, LABEL_FILE_ARCH "%7s\n", str) != 0 &&
+ strcmp(str, label_file_arch) != 0) {
+ ctx->arch_match = false;
+ continue;
+ }
if (strncmp(line, LABEL_FILE_PARAMS, sizeof(LABEL_FILE_PARAMS) - 1) == 0) {
ctx->in_backup = parse_params(line + sizeof(LABEL_FILE_PARAMS) - 1);
remain &= ~2;
@@ -240,6 +264,22 @@ int backupdir_init(zone_backup_ctx_t *ctx)
return KNOT_ENOTDIR;
}
} else {
+ if (ctx->forced) {
+ if (stat(ctx->backup_dir, &sb) == 0) {
+ int ret2 = remove_path(ctx->backup_dir, S_ISDIR(sb.st_mode));
+ if (ret2 != KNOT_EOK) {
+ return ret2;
+ }
+ } else if (errno != ENOENT) {
+ return knot_map_errno();
+ } else if (lstat(ctx->backup_dir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
+ // Stale symlink.
+ if (unlink(ctx->backup_dir) != 0) {
+ return knot_map_errno();
+ }
+ } // Omitting lstat() failure check, make_dir() will do it.
+ }
+
ret = make_dir(ctx->backup_dir, S_IRWXU | S_IRWXG, true);
if (ret != KNOT_EOK) {
return ret;
diff --git a/src/knot/zone/contents.c b/src/knot/zone/contents.c
index cba13e8..262136b 100644
--- a/src/knot/zone/contents.c
+++ b/src/knot/zone/contents.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -106,6 +106,12 @@ static zone_node_t *get_nsec3_node(const zone_contents_t *zone,
return zone_tree_get(zone->nsec3_nodes, name);
}
+// UBSAN type punning workaround
+static zone_node_t *node_new_for_contents_wrap(const uint8_t *owner, void *contents)
+{
+ return node_new_for_contents(owner, contents);
+}
+
static int insert_rr(zone_contents_t *z, const knot_rrset_t *rr, zone_node_t **n)
{
if (knot_rrset_empty(rr)) {
@@ -114,7 +120,7 @@ static int insert_rr(zone_contents_t *z, const knot_rrset_t *rr, zone_node_t **n
if (*n == NULL) {
int ret = zone_tree_add_node(zone_contents_tree_for_rr(z, rr), z->apex, rr->owner,
- (zone_tree_new_node_cb_t)node_new_for_contents, z, n);
+ node_new_for_contents_wrap, z, n);
if (ret != KNOT_EOK) {
return ret;
}
diff --git a/src/knot/zone/contents.h b/src/knot/zone/contents.h
index 8f1f160..f344c97 100644
--- a/src/knot/zone/contents.h
+++ b/src/knot/zone/contents.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,6 +16,7 @@
#pragma once
+#include "contrib/time.h"
#include "libdnssec/nsec.h"
#include "libknot/rrtype/nsec3param.h"
#include "knot/zone/node.h"
@@ -38,6 +39,7 @@ typedef struct zone_contents {
size_t size;
uint32_t max_ttl;
bool dnssec;
+ knot_time_t dnssec_expire;
} zone_contents_t;
/*!
diff --git a/src/knot/zone/digest.c b/src/knot/zone/digest.c
index b961f15..a4d50ce 100644
--- a/src/knot/zone/digest.c
+++ b/src/knot/zone/digest.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -60,7 +60,7 @@ static int digest_rrset(knot_rrset_t *rrset, const zone_node_t *node, void *vctx
}
}
- size_t buf_req = knot_rrset_size(rrset);
+ size_t buf_req = knot_rrset_size_estimate(rrset);
if (buf_req > ctx->buf_size) {
uint8_t *newbuf = realloc(ctx->buf, buf_req);
if (newbuf == NULL) {
@@ -71,7 +71,7 @@ static int digest_rrset(knot_rrset_t *rrset, const zone_node_t *node, void *vctx
}
int ret = knot_rrset_to_wire_extra(rrset, ctx->buf, ctx->buf_size, 0,
- NULL, KNOT_PF_ORIGTTL | KNOT_PF_BUFENOUGH);
+ NULL, KNOT_PF_ORIGTTL);
// cleanup apex RRSIGs mess
if (node == ctx->apex && rrset->type == KNOT_RRTYPE_RRSIG) {
diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c
index 92ad29c..3d085d8 100644
--- a/src/knot/zone/semantic-check.c
+++ b/src/knot/zone/semantic-check.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,6 +29,8 @@
static const char *error_messages[SEM_ERR_UNKNOWN + 1] = {
[SEM_ERR_SOA_NONE] =
"missing SOA at the zone apex",
+ [SEM_ERR_SOA_MULTIPLE] =
+ "multiple SOA records",
[SEM_ERR_CNAME_EXTRA_RECORDS] =
"another record exists beside CNAME",
@@ -516,7 +518,7 @@ static sem_error_t err_dnssec2sem(int ret, uint16_t rrtype, char *info, size_t l
static int verify_dnssec(zone_contents_t *zone, sem_handler_t *handler, time_t time)
{
zone_update_t fake_up = { .new_cont = zone, };
- int ret = knot_dnssec_validate_zone(&fake_up, NULL, time, false);
+ int ret = knot_dnssec_validate_zone(&fake_up, NULL, time, false, false);
if (fake_up.validation_hint.node != NULL) { // validation found an issue
char info[64] = "";
sem_error_t err = err_dnssec2sem(ret, fake_up.validation_hint.rrtype, info, sizeof(info));
diff --git a/src/knot/zone/semantic-check.h b/src/knot/zone/semantic-check.h
index 230c709..a0b1d21 100644
--- a/src/knot/zone/semantic-check.h
+++ b/src/knot/zone/semantic-check.h
@@ -35,6 +35,7 @@ typedef enum {
typedef enum {
// Mandatory checks.
SEM_ERR_SOA_NONE,
+ SEM_ERR_SOA_MULTIPLE,
SEM_ERR_CNAME_EXTRA_RECORDS,
SEM_ERR_CNAME_MULTIPLE,
diff --git a/src/knot/zone/zone-tree.c b/src/knot/zone/zone-tree.c
index 87dde18..c3f79e7 100644
--- a/src/knot/zone/zone-tree.c
+++ b/src/knot/zone/zone-tree.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -241,7 +241,7 @@ int zone_tree_add_node(zone_tree_t *tree, zone_node_t *apex, const knot_dname_t
return ret;
}
zone_node_t *parent = NULL;
- ret = zone_tree_add_node(tree, apex, knot_wire_next_label(dname, NULL), new_cb, new_cb_ctx, &parent);
+ ret = zone_tree_add_node(tree, apex, knot_dname_next_label(dname), new_cb, new_cb_ctx, &parent);
if (ret != KNOT_EOK) {
return ret;
}
diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c
index 7c84202..06a8a90 100644
--- a/src/knot/zone/zone.c
+++ b/src/knot/zone/zone.c
@@ -180,6 +180,7 @@ zone_t* zone_new(const knot_dname_t *name)
zone->ddns_queue_size = 0;
init_list(&zone->ddns_queue);
+ pthread_mutex_init(&zone->cu_lock, NULL);
knot_sem_init(&zone->cow_lock, 1);
// Preferred master lock
@@ -222,6 +223,7 @@ void zone_free(zone_t **zone_ptr)
free_ddns_queue(zone);
pthread_mutex_destroy(&zone->ddns_lock);
+ pthread_mutex_destroy(&zone->cu_lock);
knot_sem_destroy(&zone->cow_lock);
/* Control update. */
@@ -273,6 +275,12 @@ void zone_reset(conf_t *conf, zone_t *zone)
} \
}
+// UBSAN type punning workaround
+static bool dname_cmp_sweep_wrap(const uint8_t *zone, void *data)
+{
+ return knot_dname_cmp((const knot_dname_t *)zone, (const knot_dname_t *)data) != 0;
+}
+
int selective_zone_purge(conf_t *conf, zone_t *zone, purge_flag_t params)
{
if (conf == NULL || zone == NULL) {
@@ -291,7 +299,7 @@ int selective_zone_purge(conf_t *conf, zone_t *zone, purge_flag_t params)
zone_timers_sanitize(conf, zone);
zone->zonefile.bootstrap_cnt = 0;
ret = zone_timers_sweep(&zone->server->timerdb,
- (sweep_cb)knot_dname_cmp, zone->name);
+ dname_cmp_sweep_wrap, zone->name);
RETURN_IF_FAILED("timers", KNOT_ENOENT);
}
diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h
index 0527c85..a6046de 100644
--- a/src/knot/zone/zone.h
+++ b/src/knot/zone/zone.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,6 +16,7 @@
#pragma once
+#include "contrib/atomic.h"
#include "contrib/semaphore.h"
#include "knot/catalog/catalog_update.h"
#include "knot/conf/conf.h"
@@ -114,6 +115,7 @@ typedef struct zone
/*! \brief Control update context. */
struct zone_update *control_update;
+ pthread_mutex_t cu_lock;
/*! \brief Ensue one COW transaction on zone's trees at a time. */
knot_sem_t cow_lock;
@@ -122,7 +124,7 @@ typedef struct zone
struct server *server;
/*! \brief Zone backup context (NULL unless backup pending). */
- struct zone_backup_ctx *backup_ctx;
+ knot_atomic_ptr_t backup_ctx;
/*! \brief Catalog-generate feature. */
knot_dname_t *catalog_gen;
diff --git a/src/knot/zone/zonedb-load.c b/src/knot/zone/zonedb-load.c
index d8acd0b..52d9b0f 100644
--- a/src/knot/zone/zonedb-load.c
+++ b/src/knot/zone/zonedb-load.c
@@ -510,7 +510,7 @@ static knot_zonedb_t *create_zonedb(conf_t *conf, server_t *server, reload_t mod
if (forw == NULL) {
knot_dname_txt_storage_t forw_str;
(void)knot_dname_to_str(forw_str, forw_name, sizeof(forw_str));
- log_zone_warning(z->name, "zone to reverse %s doesn't exist",
+ log_zone_warning(z->name, "zone to reverse %s does not exist",
forw_str);
} else {
z->reverse_from = forw;
@@ -587,6 +587,12 @@ catalog_only:
}
}
+// UBSAN type punning workaround
+static void zone_contents_deep_free_wrap(void *contents)
+{
+ zone_contents_deep_free((zone_contents_t *)contents);
+}
+
void zonedb_reload(conf_t *conf, server_t *server, reload_t mode)
{
if (conf == NULL || server == NULL) {
@@ -625,7 +631,7 @@ void zonedb_reload(conf_t *conf, server_t *server, reload_t mode)
/* Wait for readers to finish reading old zone database. */
synchronize_rcu();
- ptrlist_free_custom(&contents_tofree, NULL, (ptrlist_free_cb)zone_contents_deep_free);
+ ptrlist_free_custom(&contents_tofree, NULL, zone_contents_deep_free_wrap);
/* Remove old zone DB. */
remove_old_zonedb(conf, db_old, server, mode);
diff --git a/src/knot/zone/zonedb.c b/src/knot/zone/zonedb.c
index 98cade5..b0388c7 100644
--- a/src/knot/zone/zonedb.c
+++ b/src/knot/zone/zonedb.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -153,7 +153,7 @@ zone_t *knot_zonedb_find_suffix(knot_zonedb_t *db, const knot_dname_t *zone_name
return NULL;
}
- zone_name = knot_wire_next_label(zone_name, NULL);
+ zone_name = knot_dname_next_label(zone_name);
}
}
diff --git a/src/knot/zone/zonefile.c b/src/knot/zone/zonefile.c
index ed075b6..9d0bf97 100644
--- a/src/knot/zone/zonefile.c
+++ b/src/knot/zone/zonefile.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -81,12 +81,6 @@ int zcreator_step(zcreator_t *zc, const knot_rrset_t *rr)
return KNOT_EINVAL;
}
- if (rr->type == KNOT_RRTYPE_SOA &&
- node_rrtype_exists(zc->z->apex, KNOT_RRTYPE_SOA)) {
- // Ignore extra SOA
- return KNOT_EOK;
- }
-
zone_node_t *node = NULL;
int ret = zone_contents_add_rr(zc->z, rr, &node);
if (ret != KNOT_EOK) {
@@ -219,10 +213,11 @@ zone_contents_t *zonefile_load(zloader_t *loader)
goto fail;
}
- if (!node_rrtype_exists(loader->creator->z->apex, KNOT_RRTYPE_SOA)) {
+ knot_rdataset_t *soa = node_rdataset(zc->z->apex, KNOT_RRTYPE_SOA);
+ if (soa == NULL || soa->count != 1) {
+ sem_error_t code = (soa == NULL) ? SEM_ERR_SOA_NONE : SEM_ERR_SOA_MULTIPLE;
loader->err_handler->error = true;
- loader->err_handler->cb(loader->err_handler, zc->z, NULL,
- SEM_ERR_SOA_NONE, NULL);
+ loader->err_handler->cb(loader->err_handler, zc->z, NULL, code, NULL);
goto fail;
}
diff --git a/src/libdnssec/key/algorithm.c b/src/libdnssec/key/algorithm.c
index a9bc3ee..d242442 100644
--- a/src/libdnssec/key/algorithm.c
+++ b/src/libdnssec/key/algorithm.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -93,11 +93,9 @@ gnutls_pk_algorithm_t algorithm_to_gnutls(dnssec_key_algorithm_t dnssec)
return GNUTLS_PK_RSA;
case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256:
case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
- return GNUTLS_PK_EC;
-#ifdef HAVE_ED25519
+ return GNUTLS_PK_ECDSA;
case DNSSEC_KEY_ALGORITHM_ED25519:
return GNUTLS_PK_EDDSA_ED25519;
-#endif
#ifdef HAVE_ED448
case DNSSEC_KEY_ALGORITHM_ED448:
return GNUTLS_PK_EDDSA_ED448;
@@ -119,11 +117,7 @@ bool dnssec_algorithm_reproducible(dnssec_key_algorithm_t algorithm, bool enable
return true; // those are always reproducible
case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256:
case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
-#ifdef HAVE_GNUTLS_REPRODUCIBLE
- return enabled; // Reproducible only if GnuTLS supports && enabled
-#else
- return false;
-#endif
+ return enabled; // reproducible only if GnuTLS supports && enabled
default:
return false;
}
diff --git a/src/libdnssec/key/convert.c b/src/libdnssec/key/convert.c
index 56168f7..d06c25e 100644
--- a/src/libdnssec/key/convert.c
+++ b/src/libdnssec/key/convert.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -104,20 +104,16 @@ static size_t ecdsa_curve_point_size(gnutls_ecc_curve_t curve)
}
}
-#if defined(HAVE_ED25519) || defined(HAVE_ED448)
static size_t eddsa_curve_point_size(gnutls_ecc_curve_t curve)
{
switch (curve) {
-#ifdef HAVE_ED25519
case GNUTLS_ECC_CURVE_ED25519: return 32;
-#endif
#ifdef HAVE_ED448
case GNUTLS_ECC_CURVE_ED448: return 57;
#endif
default: return 0;
}
}
-#endif
/*!
* Convert ECDSA public key to DNSSEC format.
@@ -157,7 +153,6 @@ static int ecdsa_pubkey_to_rdata(gnutls_pubkey_t key, dnssec_binary_t *rdata)
/*!
* Convert EDDSA public key to DNSSEC format.
*/
-#if defined(HAVE_ED25519) || defined(HAVE_ED448)
static int eddsa_pubkey_to_rdata(gnutls_pubkey_t key, dnssec_binary_t *rdata)
{
assert(key);
@@ -187,7 +182,6 @@ static int eddsa_pubkey_to_rdata(gnutls_pubkey_t key, dnssec_binary_t *rdata)
return DNSSEC_EOK;
}
-#endif
/* -- crypto to DNSSEC ------------------------------------------------------*/
@@ -248,20 +242,16 @@ static gnutls_ecc_curve_t ecdsa_curve_from_rdata_size(size_t rdata_size)
/*!
* Get EDDSA curve based on DNSKEY RDATA size.
*/
-#if defined(HAVE_ED25519) || defined(HAVE_ED448)
static gnutls_ecc_curve_t eddsa_curve_from_rdata_size(size_t rdata_size)
{
switch (rdata_size) {
-#ifdef HAVE_ED25519
case 32: return GNUTLS_ECC_CURVE_ED25519;
-#endif
#ifdef HAVE_ED448
case 57: return GNUTLS_ECC_CURVE_ED448;
#endif
default: return GNUTLS_ECC_CURVE_INVALID;
}
}
-#endif
/*!
* Convert ECDSA key in DNSSEC format to crypto key.
@@ -296,7 +286,6 @@ static int ecdsa_rdata_to_pubkey(const dnssec_binary_t *rdata, gnutls_pubkey_t k
/*!
* Convert EDDSA key in DNSSEC format to crypto key.
*/
-#if defined(HAVE_ED25519) || defined(HAVE_ED448)
static int eddsa_rdata_to_pubkey(const dnssec_binary_t *rdata, gnutls_pubkey_t key)
{
assert(rdata);
@@ -320,7 +309,6 @@ static int eddsa_rdata_to_pubkey(const dnssec_binary_t *rdata, gnutls_pubkey_t k
return DNSSEC_EOK;
}
-#endif
/* -- internal API --------------------------------------------------------- */
@@ -339,10 +327,8 @@ int convert_pubkey_to_dnskey(gnutls_pubkey_t key, dnssec_binary_t *rdata)
switch ((gnutls_pk_algorithm_t)algorithm) {
case GNUTLS_PK_RSA: return rsa_pubkey_to_rdata(key, rdata);
- case GNUTLS_PK_EC: return ecdsa_pubkey_to_rdata(key, rdata);
-#ifdef HAVE_ED25519
+ case GNUTLS_PK_ECDSA: return ecdsa_pubkey_to_rdata(key, rdata);
case GNUTLS_PK_EDDSA_ED25519: return eddsa_pubkey_to_rdata(key, rdata);
-#endif
#ifdef HAVE_ED448
case GNUTLS_PK_EDDSA_ED448: return eddsa_pubkey_to_rdata(key, rdata);
#endif
@@ -363,10 +349,8 @@ int convert_dnskey_to_pubkey(uint8_t algorithm, const dnssec_binary_t *rdata,
switch(gnutls_alg) {
case GNUTLS_PK_RSA: return rsa_rdata_to_pubkey(rdata, key);
- case GNUTLS_PK_EC: return ecdsa_rdata_to_pubkey(rdata, key);
-#ifdef HAVE_ED25519
+ case GNUTLS_PK_ECDSA: return ecdsa_rdata_to_pubkey(rdata, key);
case GNUTLS_PK_EDDSA_ED25519: return eddsa_rdata_to_pubkey(rdata, key);
-#endif
#ifdef HAVE_ED448
case GNUTLS_PK_EDDSA_ED448: return eddsa_rdata_to_pubkey(rdata, key);
#endif
diff --git a/src/libdnssec/pem.c b/src/libdnssec/pem.c
index fa463f6..41fd855 100644
--- a/src/libdnssec/pem.c
+++ b/src/libdnssec/pem.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -130,29 +130,10 @@ int dnssec_pem_from_x509(gnutls_x509_privkey_t key, dnssec_binary_t *pem)
static int privkey_export_x509(gnutls_privkey_t key, gnutls_x509_privkey_t *_key)
{
-#ifdef HAVE_EXPORT_X509
if (gnutls_privkey_export_x509(key, _key) != GNUTLS_E_SUCCESS) {
return DNSSEC_KEY_EXPORT_ERROR;
}
-#else // Needed for GnuTLS < 3.4.0 (CentOS 7)
- struct privkey { // Extracted needed items only!
- gnutls_privkey_type_t type;
- gnutls_pk_algorithm_t pk_algorithm;
- gnutls_x509_privkey_t x509;
- };
- struct privkey *pkey = (struct privkey *)key;
- assert(pkey->type == GNUTLS_PRIVKEY_X509);
-
- if (gnutls_x509_privkey_init(_key) != GNUTLS_E_SUCCESS) {
- return DNSSEC_KEY_EXPORT_ERROR;
- }
-
- if (gnutls_x509_privkey_cpy(*_key, pkey->x509) != GNUTLS_E_SUCCESS) {
- gnutls_x509_privkey_deinit(*_key);
- return DNSSEC_KEY_EXPORT_ERROR;
- }
-#endif
return DNSSEC_EOK;
}
diff --git a/src/libdnssec/sign/sign.c b/src/libdnssec/sign/sign.c
index 3a7bcba..727f650 100644
--- a/src/libdnssec/sign/sign.c
+++ b/src/libdnssec/sign/sign.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -202,34 +202,6 @@ static const algorithm_functions_t *get_functions(const dnssec_key_t *key)
}
}
-#ifndef HAVE_SIGN_DATA2
-/*!
- * Get digest algorithm used with a given key.
- */
-static gnutls_digest_algorithm_t get_digest_algorithm(const dnssec_key_t *key)
-{
- uint8_t algorithm = dnssec_key_get_algorithm(key);
-
- switch ((dnssec_key_algorithm_t)algorithm) {
- case DNSSEC_KEY_ALGORITHM_RSA_SHA1:
- case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3:
- return GNUTLS_DIG_SHA1;
- case DNSSEC_KEY_ALGORITHM_RSA_SHA256:
- case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256:
- return GNUTLS_DIG_SHA256;
- case DNSSEC_KEY_ALGORITHM_RSA_SHA512:
- return GNUTLS_DIG_SHA512;
- case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
- return GNUTLS_DIG_SHA384;
- case DNSSEC_KEY_ALGORITHM_ED25519:
- case DNSSEC_KEY_ALGORITHM_ED448:
- return GNUTLS_DIG_SHA512;
- default:
- return GNUTLS_DIG_UNKNOWN;
- }
-}
-#endif
-
static gnutls_sign_algorithm_t algo_dnssec2gnutls(dnssec_key_algorithm_t algorithm)
{
switch (algorithm) {
@@ -244,10 +216,8 @@ static gnutls_sign_algorithm_t algo_dnssec2gnutls(dnssec_key_algorithm_t algorit
return GNUTLS_SIGN_RSA_SHA512;
case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
return GNUTLS_SIGN_ECDSA_SHA384;
-#ifdef HAVE_ED25519
case DNSSEC_KEY_ALGORITHM_ED25519:
return GNUTLS_SIGN_EDDSA_ED25519;
-#endif
#ifdef HAVE_ED448
case DNSSEC_KEY_ALGORITHM_ED448:
return GNUTLS_SIGN_EDDSA_ED448;
@@ -356,24 +326,15 @@ int dnssec_sign_write(dnssec_sign_ctx_t *ctx, dnssec_sign_flags_t flags, dnssec_
};
unsigned gnutls_flags = 0;
-#ifdef HAVE_GNUTLS_REPRODUCIBLE
if (flags & DNSSEC_SIGN_REPRODUCIBLE) {
gnutls_flags |= GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE;
}
-#endif
assert(ctx->key->private_key);
_cleanup_datum_ gnutls_datum_t raw = { 0 };
-#ifdef HAVE_SIGN_DATA2
int result = gnutls_privkey_sign_data2(ctx->key->private_key,
ctx->sign_algorithm,
gnutls_flags, &data, &raw);
-#else
- gnutls_digest_algorithm_t digest_algorithm = get_digest_algorithm(ctx->key);
- int result = gnutls_privkey_sign_data(ctx->key->private_key,
- digest_algorithm,
- gnutls_flags, &data, &raw);
-#endif
if (result < 0) {
return DNSSEC_SIGN_ERROR;
}
diff --git a/src/libdnssec/version.h b/src/libdnssec/version.h
index e72e2bd..cd5bad2 100644
--- a/src/libdnssec/version.h
+++ b/src/libdnssec/version.h
@@ -17,8 +17,8 @@
#pragma once
#define DNSSEC_VERSION_MAJOR 3
-#define DNSSEC_VERSION_MINOR 3
-#define DNSSEC_VERSION_PATCH 0x09
+#define DNSSEC_VERSION_MINOR 4
+#define DNSSEC_VERSION_PATCH 0x00
#define DNSSEC_VERSION_HEX ((DNSSEC_VERSION_MAJOR << 16) | \
(DNSSEC_VERSION_MINOR << 8) | \
diff --git a/src/libknot/Makefile.inc b/src/libknot/Makefile.inc
index f62d836..d09ff55 100755
--- a/src/libknot/Makefile.inc
+++ b/src/libknot/Makefile.inc
@@ -37,6 +37,8 @@ nobase_include_libknot_HEADERS = \
libknot/packet/wire.h \
libknot/probe/data.h \
libknot/probe/probe.h \
+ libknot/quic/tls.h \
+ libknot/quic/tls_common.h \
libknot/rdata.h \
libknot/rdataset.h \
libknot/rrset-dump.h \
@@ -78,6 +80,8 @@ libknot_la_SOURCES = \
libknot/packet/rrset-wire.c \
libknot/probe/data.c \
libknot/probe/probe.c \
+ libknot/quic/tls.c \
+ libknot/quic/tls_common.c \
libknot/rdataset.c \
libknot/rrset-dump.c \
libknot/rrset.c \
diff --git a/src/libknot/attribute.h b/src/libknot/attribute.h
index 525aef3..ce464fc 100644
--- a/src/libknot/attribute.h
+++ b/src/libknot/attribute.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,17 +35,19 @@
/*! \brief GNU C function attributes. */
#if __GNUC__ >= 3
-#define _pure_ __attribute__ ((pure))
-#define _const_ __attribute__ ((const))
-#define _noreturn_ __attribute__ ((noreturn))
-#define _malloc_ __attribute__ ((malloc))
-#define _mustcheck_ __attribute__ ((warn_unused_result))
+#define _pure_ __attribute__ ((pure))
+#define _const_ __attribute__ ((const))
+#define _noreturn_ __attribute__ ((noreturn))
+#define _malloc_ __attribute__ ((malloc))
+#define _mustcheck_ __attribute__ ((warn_unused_result))
+#define _nonnull_(...) __attribute__ ((nonnull(__VA_ARGS__)))
#else
#define _pure_
#define _const_
#define _noreturn_
#define _malloc_
#define _mustcheck_
+#define _nonnull_
#endif
/*! @} */
diff --git a/src/libknot/control/control.c b/src/libknot/control/control.c
index 671896f..8cddd5d 100644
--- a/src/libknot/control/control.c
+++ b/src/libknot/control/control.c
@@ -36,9 +36,6 @@
#define CTL_BUFF_SIZE (256 * 1024)
#endif
-/*! Listen backlog size. */
-#define DEFAULT_LISTEN_BACKLOG 5
-
/*! Default socket operations timeout in milliseconds. */
#define DEFAULT_TIMEOUT (30 * 1000)
@@ -166,6 +163,18 @@ knot_ctl_t* knot_ctl_alloc(void)
}
_public_
+knot_ctl_t* knot_ctl_clone(knot_ctl_t *ctx)
+{
+ knot_ctl_t *res = knot_ctl_alloc();
+ if (res != NULL) {
+ res->timeout = ctx->timeout;
+ res->sock = ctx->sock;
+ ctx->sock = -1;
+ }
+ return res;
+}
+
+_public_
void knot_ctl_free(knot_ctl_t *ctx)
{
if (ctx == NULL) {
@@ -194,13 +203,7 @@ void knot_ctl_set_timeout(knot_ctl_t *ctx, int timeout_ms)
}
_public_
-int knot_ctl_bind(knot_ctl_t *ctx, const char *path)
-{
- return knot_ctl_bind2(ctx, path, DEFAULT_LISTEN_BACKLOG);
-}
-
-_public_
-int knot_ctl_bind2(knot_ctl_t *ctx, const char *path, unsigned backlog)
+int knot_ctl_bind(knot_ctl_t *ctx, const char *path, unsigned backlog)
{
if (ctx == NULL || path == NULL) {
return KNOT_EINVAL;
diff --git a/src/libknot/control/control.h b/src/libknot/control/control.h
index 8ab1e10..f110e3f 100644
--- a/src/libknot/control/control.h
+++ b/src/libknot/control/control.h
@@ -65,6 +65,18 @@ typedef struct knot_ctl knot_ctl_t;
knot_ctl_t* knot_ctl_alloc(void);
/*!
+ * \brief Allocates a control context based on an existing one.
+ *
+ * \param[in,out] ctx Original control context.
+ *
+ * \note The listen_sock is kept at the original, the current sock is taken
+ * by the clone and RESET in the original!
+ *
+ * \return Control context or NULL.
+ */
+knot_ctl_t* knot_ctl_clone(knot_ctl_t *ctx);
+
+/*!
* Deallocates a control context.
*
* \param[in] ctx Control context.
@@ -86,17 +98,13 @@ void knot_ctl_set_timeout(knot_ctl_t *ctx, int timeout_ms);
*
* \note Server operation.
*
- * \param[in] ctx Control context.
- * \param[in] path Control UNIX socket path.
+ * \param[in] ctx Control context.
+ * \param[in] path Control UNIX socket path.
+ * \param[in] backlog Socket listen backlog size.
*
* \return Error code, KNOT_EOK if successful.
*/
-int knot_ctl_bind(knot_ctl_t *ctx, const char *path);
-
-/*!
- * Same as knot_ctl_bind() with socket backlog specification.
- */
-int knot_ctl_bind2(knot_ctl_t *ctx, const char *path, unsigned backlog);
+int knot_ctl_bind(knot_ctl_t *ctx, const char *path, unsigned backlog);
/*!
* Unbinds a control socket.
diff --git a/src/libknot/dname.c b/src/libknot/dname.c
index 31b8a5f..d166f8d 100644
--- a/src/libknot/dname.c
+++ b/src/libknot/dname.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -62,11 +62,11 @@ static int dname_align(const uint8_t **d1, uint8_t d1_labels,
assert(d1 && d2);
for (unsigned j = d1_labels; j < d2_labels; ++j) {
- *d2 = knot_wire_next_label(*d2, NULL);
+ *d2 = knot_dname_next_label(*d2);
}
for (unsigned j = d2_labels; j < d1_labels; ++j) {
- *d1 = knot_wire_next_label(*d1, NULL);
+ *d1 = knot_dname_next_label(*d1);
}
return (d1_labels < d2_labels) ? d1_labels : d2_labels;
@@ -190,7 +190,7 @@ _public_
int knot_dname_unpack(uint8_t *dst, const knot_dname_t *src,
size_t maxlen, const uint8_t *pkt)
{
- if (dst == NULL || src == NULL) {
+ if (dst == NULL || src == NULL || pkt == NULL) {
return KNOT_EINVAL;
}
@@ -533,7 +533,7 @@ size_t knot_dname_size(const knot_dname_t *name)
_public_
size_t knot_dname_realsize(const knot_dname_t *name, const uint8_t *pkt)
{
- if (name == NULL) {
+ if (name == NULL || pkt == NULL) {
return 0;
}
@@ -573,8 +573,8 @@ size_t knot_dname_matched_labels(const knot_dname_t *d1, const knot_dname_t *d2)
}
/* Next label. */
- d1 = knot_wire_next_label(d1, NULL);
- d2 = knot_wire_next_label(d2, NULL);
+ d1 = knot_dname_next_label(d1);
+ d2 = knot_dname_next_label(d2);
--common;
}
@@ -596,7 +596,7 @@ knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *name, unsigned label
}
size_t prefix_lbs = dname_lbs - labels;
- size_t prefix_len = knot_dname_prefixlen(name, prefix_lbs, NULL);
+ size_t prefix_len = knot_dname_prefixlen(name, prefix_lbs);
size_t suffix_len = knot_dname_size(suffix);
if (prefix_len == 0 || suffix_len == 0) {
return NULL;
@@ -614,7 +614,7 @@ knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *name, unsigned label
while (prefix_lbs > 0) {
memcpy(dst, name, *name + 1);
dst += *name + 1;
- name = knot_wire_next_label(name, NULL);
+ name = knot_dname_next_label(name);
--prefix_lbs;
}
@@ -622,7 +622,7 @@ knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *name, unsigned label
while (*suffix != '\0') {
memcpy(dst, suffix, *suffix + 1);
dst += *suffix + 1;
- suffix = knot_wire_next_label(suffix, NULL);
+ suffix = knot_dname_next_label(suffix);
}
*dst = '\0';
@@ -684,8 +684,8 @@ inline static bool dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2
while (*d1 != '\0' || *d2 != '\0') {
if (label_is_equal(d1, d2, no_case)) {
- d1 = knot_wire_next_label(d1, NULL);
- d2 = knot_wire_next_label(d2, NULL);
+ d1 = knot_dname_next_label(d1);
+ d2 = knot_dname_next_label(d2);
} else {
return false;
}
@@ -707,7 +707,7 @@ bool knot_dname_is_case_equal(const knot_dname_t *d1, const knot_dname_t *d2)
}
_public_
-size_t knot_dname_prefixlen(const uint8_t *name, unsigned nlabels, const uint8_t *pkt)
+size_t knot_dname_prefixlen(const uint8_t *name, unsigned nlabels)
{
if (name == NULL) {
return 0;
@@ -718,13 +718,10 @@ size_t knot_dname_prefixlen(const uint8_t *name, unsigned nlabels, const uint8_t
return 0;
}
- /* Seek first real label occurrence. */
- name = knot_wire_seek_label(name, pkt);
-
size_t len = 0;
while (*name != '\0') {
len += *name + 1;
- name = knot_wire_next_label(name, pkt);
+ name = knot_dname_next_label(name);
if (--nlabels == 0) { /* Count N first labels only. */
break;
}
@@ -743,7 +740,8 @@ size_t knot_dname_labels(const uint8_t *name, const uint8_t *pkt)
size_t count = 0;
while (*name != '\0') {
++count;
- name = knot_wire_next_label(name, pkt);
+ name = (pkt == NULL) ? knot_dname_next_label(name) :
+ knot_wire_next_label(name, pkt);
if (name == NULL) {
return 0;
}
@@ -794,7 +792,7 @@ int knot_dname_in_bailiwick(const knot_dname_t *name, const knot_dname_t *bailiw
}
for (int i = 0; i < label_diff; ++i) {
- name = knot_wire_next_label(name, NULL);
+ name = knot_dname_next_label(name);
}
return knot_dname_is_equal(name, bailiwick) ? label_diff : KNOT_EOUTOFZONE;
diff --git a/src/libknot/dname.h b/src/libknot/dname.h
index 5733de9..6d72f90 100644
--- a/src/libknot/dname.h
+++ b/src/libknot/dname.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -48,7 +48,7 @@ typedef char knot_dname_txt_storage_t[KNOT_DNAME_TXT_MAXLEN + 1];
*
* \param name Name on the wire.
* \param endp Name boundary.
- * \param pkt Wire.
+ * \param pkt Wire (can be NULL if not compressed).
*
* \retval (compressed) size of the domain name.
* \retval KNOT_EINVAL
@@ -195,7 +195,7 @@ size_t knot_dname_size(const knot_dname_t *name);
* \brief Returns full size of the given domain name (expanded compression ptrs).
*
* \param name Domain name to get the size of.
- * \param pkt Related packet (or NULL if unpacked)
+ * \param pkt Related packet.
*
* \retval size of the domain name.
* \retval 0 if invalid argument.
@@ -295,12 +295,11 @@ bool knot_dname_is_case_equal(const knot_dname_t *d1, const knot_dname_t *d2);
*
* \param name Domain name.
* \param nlabels First N labels.
- * \param pkt Related packet (or NULL if not compressed).
*
* \return Length of the prefix.
*/
_pure_
-size_t knot_dname_prefixlen(const uint8_t *name, unsigned nlabels, const uint8_t *pkt);
+size_t knot_dname_prefixlen(const uint8_t *name, unsigned nlabels);
/*!
* \brief Return number of labels in the domain name.
diff --git a/src/libknot/errcode.h b/src/libknot/errcode.h
index 6ec4a94..3ee326c 100644
--- a/src/libknot/errcode.h
+++ b/src/libknot/errcode.h
@@ -110,6 +110,7 @@ enum knot_error {
KNOT_EBADCERTKEY,
KNOT_EFACCES,
KNOT_EBACKUPDATA,
+ KNOT_ECPUCOMPAT,
KNOT_GENERAL_ERROR = -900,
@@ -121,6 +122,7 @@ enum knot_error {
KNOT_NET_EADDR,
KNOT_NET_ESOCKET,
KNOT_NET_ECONNECT,
+ KNOT_NET_EHSHAKE,
KNOT_NET_ESEND,
KNOT_NET_ERECV,
KNOT_NET_ETIMEOUT,
@@ -176,6 +178,7 @@ enum knot_error {
KNOT_NO_PUBLIC_KEY,
KNOT_NO_PRIVATE_KEY,
KNOT_NO_READY_KEY,
+ KNOT_ESOON_EXPIRE,
KNOT_DNSSEC_EKEYTAG_LIMIT,
KNOT_DNSSEC_EXTRA_NSEC,
diff --git a/src/libknot/error.c b/src/libknot/error.c
index ae9b973..59f0a7a 100644
--- a/src/libknot/error.c
+++ b/src/libknot/error.c
@@ -109,6 +109,7 @@ static const struct error errors[] = {
{ KNOT_EBADCERTKEY, "unknown certificate key" },
{ KNOT_EFACCES, "file permission denied" },
{ KNOT_EBACKUPDATA, "requested data not in backup" },
+ { KNOT_ECPUCOMPAT, "incompatible CPU architecture" },
{ KNOT_GENERAL_ERROR, "unknown general error" },
@@ -120,6 +121,7 @@ static const struct error errors[] = {
{ KNOT_NET_EADDR, "bad address or host name" },
{ KNOT_NET_ESOCKET, "can't create socket" },
{ KNOT_NET_ECONNECT, "can't connect" },
+ { KNOT_NET_EHSHAKE, "handshake failed" },
{ KNOT_NET_ESEND, "can't send data" },
{ KNOT_NET_ERECV, "can't receive data" },
{ KNOT_NET_ETIMEOUT, "network timeout" },
@@ -175,6 +177,7 @@ static const struct error errors[] = {
{ KNOT_NO_PUBLIC_KEY, "no public key" },
{ KNOT_NO_PRIVATE_KEY, "no private key" },
{ KNOT_NO_READY_KEY, "no key ready for submission" },
+ { KNOT_ESOON_EXPIRE, "oncoming RRSIG expiration" },
{ KNOT_DNSSEC_EKEYTAG_LIMIT, "many keys with equal keytag" },
{ KNOT_DNSSEC_EXTRA_NSEC, "superfluous NSEC(3)" },
diff --git a/src/libknot/packet/pkt.h b/src/libknot/packet/pkt.h
index 383f55e..da69c8c 100644
--- a/src/libknot/packet/pkt.h
+++ b/src/libknot/packet/pkt.h
@@ -52,7 +52,6 @@ enum {
KNOT_PF_NOCANON = 1 << 5, /*!< Don't canonicalize rrsets during parsing. */
KNOT_PF_ORIGTTL = 1 << 6, /*!< Write RRSIGs with their original TTL. */
KNOT_PF_SOAMINTTL = 1 << 7, /*!< Write SOA with its minimum-ttl as TTL. */
- KNOT_PF_BUFENOUGH = 1 << 8, /*!< The output buffer is big enough for the output. */
};
typedef struct knot_pkt knot_pkt_t;
diff --git a/src/libknot/packet/rrset-wire.c b/src/libknot/packet/rrset-wire.c
index ef3a068..1d4f78e 100644
--- a/src/libknot/packet/rrset-wire.c
+++ b/src/libknot/packet/rrset-wire.c
@@ -73,6 +73,7 @@ static bool dname_equal_wire(const knot_dname_t *d1, const knot_dname_t *d2,
{
assert(d1);
assert(d2);
+ assert(wire);
d2 = knot_wire_seek_label(d2, wire);
@@ -80,7 +81,7 @@ static bool dname_equal_wire(const knot_dname_t *d1, const knot_dname_t *d2,
if (!label_is_equal(d1, d2)) {
return false;
}
- d1 = knot_wire_next_label(d1, NULL);
+ d1 = knot_dname_next_label(d1);
d2 = knot_wire_next_label(d2, wire);
}
@@ -170,7 +171,7 @@ static int write_rdata_naptr_header(const uint8_t **src, size_t *src_avail,
written += (len); \
}
-#define CHECK_NEXT_LABEL(res) \
+#define CHECK_WIRE_NEXT_LABEL(res) \
if (res == NULL) { return KNOT_EINVAL; }
/*!
@@ -201,7 +202,7 @@ static int compr_put_dname(const knot_dname_t *dname, uint8_t *dst, uint16_t max
int suffix_labels = compr->suffix.labels;
while (suffix_labels > name_labels) {
suffix = knot_wire_next_label(suffix, compr->wire);
- CHECK_NEXT_LABEL(suffix);
+ CHECK_WIRE_NEXT_LABEL(suffix);
--suffix_labels;
}
@@ -210,8 +211,7 @@ static int compr_put_dname(const knot_dname_t *dname, uint8_t *dst, uint16_t max
uint16_t written = 0;
while (name_labels > suffix_labels) {
WRITE_LABEL(dst, written, dname, max, (*dname + 1));
- dname = knot_wire_next_label(dname, NULL);
- CHECK_NEXT_LABEL(dname);
+ dname = knot_dname_next_label(dname);
--name_labels;
}
@@ -221,10 +221,9 @@ static int compr_put_dname(const knot_dname_t *dname, uint8_t *dst, uint16_t max
const knot_dname_t *compr_ptr = suffix;
while (dname[0] != '\0') {
// Next labels.
- const knot_dname_t *next_dname = knot_wire_next_label(dname, NULL);
- CHECK_NEXT_LABEL(next_dname);
+ const knot_dname_t *next_dname = knot_dname_next_label(dname);
const knot_dname_t *next_suffix = knot_wire_next_label(suffix, compr->wire);
- CHECK_NEXT_LABEL(next_suffix);
+ CHECK_WIRE_NEXT_LABEL(next_suffix);
// Two labels match, extend suffix length.
if (!label_is_equal(dname, suffix)) {
@@ -495,7 +494,7 @@ static int write_rr(const knot_rrset_t *rrset, uint16_t rrset_index,
_public_
int knot_rrset_to_wire_extra(const knot_rrset_t *rrset, uint8_t *wire,
- uint16_t max_size, uint16_t rotate,
+ uint32_t max_size, uint16_t rotate,
knot_compr_t *compr, uint16_t flags)
{
if (rrset == NULL || wire == NULL) {
@@ -511,11 +510,6 @@ int knot_rrset_to_wire_extra(const knot_rrset_t *rrset, uint8_t *wire,
uint8_t *write = wire;
size_t capacity = max_size;
- // FIXME remove this and make the max_size parameter uint32_t in next major libknot release!
- if ((flags & KNOT_PF_BUFENOUGH)) {
- capacity = SIZE_MAX;
- }
-
uint16_t count = rrset->rrs.count;
knot_rdata_t *rdata = rotate > 1 ? knot_rdataset_at(&rrset->rrs, rotate - 1) : rrset->rrs.rdata;
for (int i = rotate; i < count + rotate; i++) {
diff --git a/src/libknot/packet/rrset-wire.h b/src/libknot/packet/rrset-wire.h
index 3be0cba..3247df8 100644
--- a/src/libknot/packet/rrset-wire.h
+++ b/src/libknot/packet/rrset-wire.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -41,12 +41,12 @@
* \return Output size, negative number on error (KNOT_E*).
*/
int knot_rrset_to_wire_extra(const knot_rrset_t *rrset, uint8_t *wire,
- uint16_t max_size, uint16_t rotate,
+ uint32_t max_size, uint16_t rotate,
knot_compr_t *compr, uint16_t flags);
/*! \brief Same as knot_rrset_to_wire_extra but without rrset rotation and flags. */
static inline int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire,
- uint16_t max_size, knot_compr_t *compr)
+ uint32_t max_size, knot_compr_t *compr)
{
return knot_rrset_to_wire_extra(rrset, wire, max_size, 0, compr, 0);
}
diff --git a/src/libknot/packet/wire.h b/src/libknot/packet/wire.h
index 630cd83..cff4b21 100644
--- a/src/libknot/packet/wire.h
+++ b/src/libknot/packet/wire.h
@@ -1025,13 +1025,11 @@ static inline uint16_t knot_wire_get_pointer(const uint8_t *pos)
return (knot_wire_read_u16(pos) - KNOT_WIRE_PTR_BASE); // Return offset.
}
-_pure_ _mustcheck_
+_pure_ _mustcheck_ _nonnull_(2)
static inline const uint8_t *knot_wire_seek_label(const uint8_t *lp, const uint8_t *wire)
{
+ assert(wire);
while (knot_wire_is_pointer(lp)) {
- if (!wire) {
- return NULL;
- }
const uint8_t *new_lp = wire + knot_wire_get_pointer(lp);
if (new_lp >= lp) {
assert(0);
@@ -1042,12 +1040,21 @@ static inline const uint8_t *knot_wire_seek_label(const uint8_t *lp, const uint8
return lp;
}
-_pure_ _mustcheck_
+_pure_ _mustcheck_ _nonnull_(1, 2)
static inline const uint8_t *knot_wire_next_label(const uint8_t *lp, const uint8_t *wire)
{
- if (!lp || !lp[0]) /* No label after final label. */
- return NULL;
+ assert(lp);
+ assert(lp[0] > 0); // Not a terminal label.
return knot_wire_seek_label(lp + (lp[0] + sizeof(uint8_t)), wire);
}
+_pure_ _mustcheck_ _nonnull_(1)
+static inline const uint8_t *knot_dname_next_label(const uint8_t *lp)
+{
+ assert(lp);
+ assert(lp[0] > 0); // Not a terminal label.
+ assert(!knot_wire_is_pointer(lp));
+ return lp + (lp[0] + sizeof(uint8_t));
+}
+
/*! @} */
diff --git a/src/libknot/quic/quic.c b/src/libknot/quic/quic.c
index f9d1d1d..4eb84c3 100644
--- a/src/libknot/quic/quic.c
+++ b/src/libknot/quic/quic.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,7 +33,6 @@
#include "contrib/macros.h"
#include "contrib/sockaddr.h"
-#include "contrib/string.h"
#include "contrib/ucw/lists.h"
#include "libknot/endian.h"
#include "libdnssec/error.h"
@@ -58,19 +57,6 @@
#define TLS_CALLBACK_ERR (-1)
-const gnutls_datum_t doq_alpn = {
- (unsigned char *)"doq", 3
-};
-
-typedef struct knot_quic_creds {
- gnutls_certificate_credentials_t tls_cert;
- gnutls_anti_replay_t tls_anti_replay;
- gnutls_datum_t tls_ticket_key;
- bool peer;
- uint8_t peer_pin_len;
- uint8_t peer_pin[];
-} knot_quic_creds_t;
-
typedef struct knot_quic_session {
node_t n;
gnutls_datum_t tls_session;
@@ -153,223 +139,6 @@ session_free:
return ret;
}
-static int tls_anti_replay_db_add_func(void *dbf, time_t exp_time,
- const gnutls_datum_t *key,
- const gnutls_datum_t *data)
-{
- return 0;
-}
-
-static void tls_session_ticket_key_free(gnutls_datum_t *ticket)
-{
- gnutls_memset(ticket->data, 0, ticket->size);
- gnutls_free(ticket->data);
-}
-
-static int self_key(gnutls_x509_privkey_t *privkey, const char *key_file)
-{
- gnutls_datum_t data = { 0 };
-
- int ret = gnutls_x509_privkey_init(privkey);
- if (ret != GNUTLS_E_SUCCESS) {
- return ret;
- }
-
- int fd = open(key_file, O_RDONLY);
- if (fd != -1) {
- struct stat stat;
- if (fstat(fd, &stat) != 0 ||
- (data.data = gnutls_malloc(stat.st_size)) == NULL ||
- read(fd, data.data, stat.st_size) != stat.st_size) {
- ret = GNUTLS_E_KEYFILE_ERROR;
- goto finish;
- }
-
- data.size = stat.st_size;
- ret = gnutls_x509_privkey_import_pkcs8(*privkey, &data, GNUTLS_X509_FMT_PEM,
- NULL, GNUTLS_PKCS_PLAIN);
- if (ret != GNUTLS_E_SUCCESS) {
- goto finish;
- }
- } else {
- ret = gnutls_x509_privkey_generate(*privkey, GNUTLS_PK_EDDSA_ED25519,
- GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_ED25519), 0);
- if (ret != GNUTLS_E_SUCCESS) {
- goto finish;
- }
-
- ret = gnutls_x509_privkey_export2_pkcs8(*privkey, GNUTLS_X509_FMT_PEM, NULL,
- GNUTLS_PKCS_PLAIN, &data);
- if (ret != GNUTLS_E_SUCCESS ||
- (fd = open(key_file, O_WRONLY | O_CREAT, 0600)) == -1 ||
- write(fd, data.data, data.size) != data.size) {
- ret = GNUTLS_E_KEYFILE_ERROR;
- goto finish;
- }
- }
-
-finish:
- close(fd);
- gnutls_free(data.data);
- if (ret != GNUTLS_E_SUCCESS) {
- gnutls_x509_privkey_deinit(*privkey);
- *privkey = NULL;
- }
- return ret;
-}
-
-static int self_signed_cert(gnutls_certificate_credentials_t tls_cert,
- const char *key_file)
-{
- gnutls_x509_privkey_t privkey = NULL;
- gnutls_x509_crt_t cert = NULL;
-
- char *hostname = sockaddr_hostname();
- if (hostname == NULL) {
- return GNUTLS_E_MEMORY_ERROR;
- }
-
- int ret;
- uint8_t serial[16];
- gnutls_rnd(GNUTLS_RND_NONCE, serial, sizeof(serial));
- // Clear the left-most bit to be a positive number (two's complement form).
- serial[0] &= 0x7F;
-
-#define CHK(cmd) if ((ret = (cmd)) != GNUTLS_E_SUCCESS) { goto finish; }
-#define NOW_DAYS(days) (time(NULL) + 24 * 3600 * (days))
-
- CHK(self_key(&privkey, key_file));
-
- CHK(gnutls_x509_crt_init(&cert));
- CHK(gnutls_x509_crt_set_version(cert, 3));
- CHK(gnutls_x509_crt_set_serial(cert, serial, sizeof(serial)));
- CHK(gnutls_x509_crt_set_activation_time(cert, NOW_DAYS(-1)));
- CHK(gnutls_x509_crt_set_expiration_time(cert, NOW_DAYS(10 * 365)));
- CHK(gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0,
- hostname, strlen(hostname)));
- CHK(gnutls_x509_crt_set_key(cert, privkey));
- CHK(gnutls_x509_crt_sign2(cert, cert, privkey, GNUTLS_DIG_SHA512, 0));
-
- ret = gnutls_certificate_set_x509_key(tls_cert, &cert, 1, privkey);
-
-finish:
- free(hostname);
- gnutls_x509_crt_deinit(cert);
- gnutls_x509_privkey_deinit(privkey);
-
- return ret;
-}
-
-_public_
-struct knot_quic_creds *knot_quic_init_creds(const char *cert_file,
- const char *key_file)
-{
- knot_quic_creds_t *creds = calloc(1, sizeof(*creds));
- if (creds == NULL) {
- return NULL;
- }
-
- int ret = gnutls_certificate_allocate_credentials(&creds->tls_cert);
- if (ret != GNUTLS_E_SUCCESS) {
- goto fail;
- }
-
- ret = gnutls_anti_replay_init(&creds->tls_anti_replay);
- if (ret != GNUTLS_E_SUCCESS) {
- goto fail;
- }
- gnutls_anti_replay_set_add_function(creds->tls_anti_replay, tls_anti_replay_db_add_func);
- gnutls_anti_replay_set_ptr(creds->tls_anti_replay, NULL);
-
- if (cert_file != NULL) {
- ret = gnutls_certificate_set_x509_key_file(creds->tls_cert,
- cert_file, key_file,
- GNUTLS_X509_FMT_PEM);
- } else {
- ret = self_signed_cert(creds->tls_cert, key_file);
- }
- if (ret != GNUTLS_E_SUCCESS) {
- goto fail;
- }
-
- ret = gnutls_session_ticket_key_generate(&creds->tls_ticket_key);
- if (ret != GNUTLS_E_SUCCESS) {
- goto fail;
- }
-
- return creds;
-fail:
- knot_quic_free_creds(creds);
- return NULL;
-}
-
-_public_
-struct knot_quic_creds *knot_quic_init_creds_peer(const struct knot_quic_creds *local_creds,
- const uint8_t *peer_pin,
- uint8_t peer_pin_len)
-{
- knot_quic_creds_t *creds = calloc(1, sizeof(*creds) + peer_pin_len);
- if (creds == NULL) {
- return NULL;
- }
-
- if (local_creds != NULL) {
- creds->peer = true;
- creds->tls_cert = local_creds->tls_cert;
- } else {
- int ret = gnutls_certificate_allocate_credentials(&creds->tls_cert);
- if (ret != GNUTLS_E_SUCCESS) {
- free(creds);
- return NULL;
- }
- }
-
- if (peer_pin_len > 0 && peer_pin != NULL) {
- memcpy(creds->peer_pin, peer_pin, peer_pin_len);
- creds->peer_pin_len = peer_pin_len;
- }
-
- return creds;
-}
-
-_public_
-int knot_quic_creds_cert(struct knot_quic_creds *creds, struct gnutls_x509_crt_int **cert)
-{
- if (creds == NULL || cert == NULL) {
- return KNOT_EINVAL;
- }
-
- gnutls_x509_crt_t *certs;
- unsigned cert_count;
- int ret = gnutls_certificate_get_x509_crt(creds->tls_cert, 0, &certs, &cert_count);
- if (ret == GNUTLS_E_SUCCESS) {
- if (cert_count == 0) {
- gnutls_x509_crt_deinit(*certs);
- return KNOT_ENOENT;
- }
- *cert = *certs;
- free(certs);
- }
- return ret;
-}
-
-_public_
-void knot_quic_free_creds(struct knot_quic_creds *creds)
-{
- if (creds == NULL) {
- return;
- }
-
- if (!creds->peer && creds->tls_cert != NULL) {
- gnutls_certificate_free_credentials(creds->tls_cert);
- }
- gnutls_anti_replay_deinit(creds->tls_anti_replay);
- if (creds->tls_ticket_key.data != NULL) {
- tls_session_ticket_key_free(&creds->tls_ticket_key);
- }
- free(creds);
-}
-
static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
{
return ((knot_quic_conn_t *)conn_ref->user_data)->conn;
@@ -377,51 +146,31 @@ static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
static int tls_init_conn_session(knot_quic_conn_t *conn, bool server)
{
- if (gnutls_init(&conn->tls_session, (server ? GNUTLS_SERVER : GNUTLS_CLIENT) |
- GNUTLS_ENABLE_EARLY_DATA | GNUTLS_NO_AUTO_SEND_TICKET |
- GNUTLS_NO_END_OF_EARLY_DATA) != GNUTLS_E_SUCCESS) {
- return TLS_CALLBACK_ERR;
- }
-
- gnutls_certificate_send_x509_rdn_sequence(conn->tls_session, 1);
- gnutls_certificate_server_set_request(conn->tls_session, GNUTLS_CERT_REQUEST);
-
- if (gnutls_priority_set_direct(conn->tls_session, QUIC_PRIORITIES,
- NULL) != GNUTLS_E_SUCCESS) {
+ int ret = knot_tls_session(&conn->tls_session, conn->quic_table->creds,
+ conn->quic_table->priority, "\x03""doq",
+ true, server);
+ if (ret != KNOT_EOK) {
return TLS_CALLBACK_ERR;
}
- if (server && gnutls_session_ticket_enable_server(conn->tls_session,
- &conn->quic_table->creds->tls_ticket_key) != GNUTLS_E_SUCCESS) {
- return TLS_CALLBACK_ERR;
+ if (server) {
+ ret = ngtcp2_crypto_gnutls_configure_server_session(conn->tls_session);
+ } else {
+ ret = ngtcp2_crypto_gnutls_configure_client_session(conn->tls_session);
}
-
- int ret = ngtcp2_crypto_gnutls_configure_server_session(conn->tls_session);
- if (ret != 0) {
+ if (ret != NGTCP2_NO_ERROR) {
return TLS_CALLBACK_ERR;
}
- gnutls_record_set_max_early_data_size(conn->tls_session, 0xffffffffu);
-
conn->conn_ref = (nc_conn_ref_placeholder_t) {
.get_conn = get_conn,
.user_data = conn
};
- _Static_assert(sizeof(nc_conn_ref_placeholder_t) == sizeof(ngtcp2_crypto_conn_ref), "invalid placeholder for conn_ref");
+ _Static_assert(sizeof(nc_conn_ref_placeholder_t) == sizeof(ngtcp2_crypto_conn_ref),
+ "invalid placeholder for conn_ref");
gnutls_session_set_ptr(conn->tls_session, &conn->conn_ref);
- if (server) {
- gnutls_anti_replay_enable(conn->tls_session, conn->quic_table->creds->tls_anti_replay);
-
- }
- if (gnutls_credentials_set(conn->tls_session, GNUTLS_CRD_CERTIFICATE,
- conn->quic_table->creds->tls_cert) != GNUTLS_E_SUCCESS) {
- return TLS_CALLBACK_ERR;
- }
-
- gnutls_alpn_set_protocols(conn->tls_session, &doq_alpn, 1, GNUTLS_ALPN_MANDATORY);
-
ngtcp2_conn_set_tls_native_handle(conn->conn, conn->tls_session);
return KNOT_EOK;
@@ -477,54 +226,6 @@ uint16_t knot_quic_conn_local_port(knot_quic_conn_t *conn)
return ((const struct sockaddr_in6 *)path->local.addr)->sin6_port;
}
-_public_
-void knot_quic_conn_pin(knot_quic_conn_t *conn, uint8_t *pin, size_t *pin_size, bool local)
-{
- if (conn == NULL) {
- goto error;
- }
-
- const gnutls_datum_t *data = NULL;
- if (local) {
- data = gnutls_certificate_get_ours(conn->tls_session);
- } else {
- unsigned count = 0;
- data = gnutls_certificate_get_peers(conn->tls_session, &count);
- if (count == 0) {
- goto error;
- }
- }
- if (data == NULL) {
- goto error;
- }
-
- gnutls_x509_crt_t cert;
- int ret = gnutls_x509_crt_init(&cert);
- if (ret != GNUTLS_E_SUCCESS) {
- goto error;
- }
-
- ret = gnutls_x509_crt_import(cert, data, GNUTLS_X509_FMT_DER);
- if (ret != GNUTLS_E_SUCCESS) {
- gnutls_x509_crt_deinit(cert);
- goto error;
- }
-
- ret = gnutls_x509_crt_get_key_id(cert, GNUTLS_KEYID_USE_SHA256, pin, pin_size);
- if (ret != GNUTLS_E_SUCCESS) {
- gnutls_x509_crt_deinit(cert);
- goto error;
- }
-
- gnutls_x509_crt_deinit(cert);
-
- return;
-error:
- if (pin_size != NULL) {
- *pin_size = 0;
- }
-}
-
static void knot_quic_rand_cb(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx)
{
(void)rand_ctx;
@@ -602,18 +303,8 @@ static int handshake_completed_cb(ngtcp2_conn *conn, void *user_data)
ctx->flags |= KNOT_QUIC_CONN_HANDSHAKE_DONE;
if (!ngtcp2_conn_is_server(conn)) {
- knot_quic_creds_t *creds = ctx->quic_table->creds;
- if (creds->peer_pin_len == 0) {
- return 0;
- }
- uint8_t pin[KNOT_QUIC_PIN_LEN];
- size_t pin_size = sizeof(pin);
- knot_quic_conn_pin(ctx, pin, &pin_size, false);
- if (pin_size != creds->peer_pin_len ||
- const_time_memcmp(pin, creds->peer_pin, pin_size) != 0) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
- return 0;
+ return knot_tls_pin_check(ctx->tls_session, ctx->quic_table->creds)
+ == KNOT_EOK ? 0 : NGTCP2_ERR_CALLBACK_FAILURE;
}
if (gnutls_session_ticket_send(ctx->tls_session, 1, 0) != GNUTLS_E_SUCCESS) {
@@ -945,6 +636,10 @@ int knot_quic_handle(knot_quic_table_t *table, knot_quic_reply_t *reply,
goto finish;
}
+ if (conn != NULL && (conn->flags & KNOT_QUIC_CONN_BLOCKED)) {
+ return KNOT_EOK;
+ }
+
ngtcp2_path path;
path.remote.addr = (struct sockaddr *)reply->ip_rem;
path.remote.addrlen = addr_len((struct sockaddr_in6 *)reply->ip_rem);
@@ -1249,6 +944,8 @@ int knot_quic_send(knot_quic_table_t *quic_table, knot_quic_conn_t *conn,
return KNOT_EINVAL;
} else if (reply->handle_ret < 0) {
return reply->handle_ret;
+ } else if ((conn->flags & KNOT_QUIC_CONN_BLOCKED) && !(flags & KNOT_QUIC_SEND_IGNORE_BLOCKED)) {
+ return KNOT_EOK;
} else if (reply->handle_ret > 0) {
return send_special(quic_table, reply, conn);
} else if (conn == NULL) {
diff --git a/src/libknot/quic/quic.h b/src/libknot/quic/quic.h
index 29a02e0..b4acb33 100644
--- a/src/libknot/quic/quic.h
+++ b/src/libknot/quic/quic.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,20 +29,18 @@
#include <netinet/in.h>
#include "libknot/quic/quic_conn.h"
-
-#define KNOT_QUIC_PIN_LEN 32
+#include "libknot/quic/tls_common.h"
#define KNOT_QUIC_HANDLE_RET_CLOSE 2000
// RFC 9250
#define KNOT_QUIC_ERR_EXCESSIVE_LOAD 0x4
-struct gnutls_x509_crt_int;
-struct knot_quic_creds;
struct knot_quic_session;
typedef enum {
KNOT_QUIC_SEND_IGNORE_LASTBYTE = (1 << 0),
+ KNOT_QUIC_SEND_IGNORE_BLOCKED = (1 << 1),
} knot_quic_send_flag_t;
typedef struct knot_quic_reply {
@@ -87,45 +85,6 @@ struct knot_quic_session *knot_quic_session_save(knot_quic_conn_t *conn);
int knot_quic_session_load(knot_quic_conn_t *conn, struct knot_quic_session *session);
/*!
- * \brief Init server TLS certificate for DoQ.
- *
- * \param cert_file X509 certificate PEM file path/name (NULL if auto-generated).
- * \param key_file Key PEM file path/name.
- *
- * \return Initialized creds.
- */
-struct knot_quic_creds *knot_quic_init_creds(const char *cert_file,
- const char *key_file);
-
-/*!
- * \brief Init peer TLS certificate for DoQ.
- *
- * \param local_creds Local credentials if server.
- * \param peer_pin Optional peer certificate pin to check.
- * \param peer_pin_len Length of the peer pin. Set 0 if not specified.
- *
- * \return Initialized creds.
- */
-struct knot_quic_creds *knot_quic_init_creds_peer(const struct knot_quic_creds *local_creds,
- const uint8_t *peer_pin,
- uint8_t peer_pin_len);
-
-/*!
- * \brief Gets the certificate from credentials.
- *
- * \param creds TLS credentials.
- * \param cert Output certificate.
- *
- * \return KNOT_E*
- */
-int knot_quic_creds_cert(struct knot_quic_creds *creds, struct gnutls_x509_crt_int **cert);
-
-/*!
- * \brief Deinit server TLS certificate for DoQ.
- */
-void knot_quic_free_creds(struct knot_quic_creds *creds);
-
-/*!
* \brief Returns timeout value for the connection.
*/
uint64_t quic_conn_get_timeout(knot_quic_conn_t *conn);
@@ -156,18 +115,6 @@ uint32_t knot_quic_conn_rtt(knot_quic_conn_t *conn);
uint16_t knot_quic_conn_local_port(knot_quic_conn_t *conn);
/*!
- * \brief Gets local or remote certificate pin.
- *
- * \note Zero output pin_size value means no certificate available or error.
- *
- * \param conn QUIC connection.
- * \param pin Output certificate pin.
- * \param pin_size Input size of the storage / output size of the stored pin.
- * \param local Local or remote certificate indication.
- */
-void knot_quic_conn_pin(knot_quic_conn_t *conn, uint8_t *pin, size_t *pin_size, bool local);
-
-/*!
* \brief Create new outgoing QUIC connection.
*
* \param table QUIC connections table to be added to.
diff --git a/src/libknot/quic/quic_conn.c b/src/libknot/quic/quic_conn.c
index 6616573..1a3b9df 100644
--- a/src/libknot/quic/quic_conn.c
+++ b/src/libknot/quic/quic_conn.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,6 +29,7 @@
#include "libdnssec/random.h"
#include "libknot/attribute.h"
#include "libknot/error.h"
+#include "libknot/quic/tls_common.h"
#include "libknot/quic/quic.h"
#include "libknot/xdp/tcp_iobuf.h"
#include "libknot/wire.h"
@@ -45,7 +46,7 @@ static int cmp_expiry_heap_nodes(void *c1, void *c2)
_public_
knot_quic_table_t *knot_quic_table_new(size_t max_conns, size_t max_ibufs, size_t max_obufs,
- size_t udp_payload, struct knot_quic_creds *creds)
+ size_t udp_payload, struct knot_creds *creds)
{
size_t table_size = max_conns * BUCKETS_PER_CONNS;
@@ -61,9 +62,17 @@ knot_quic_table_t *knot_quic_table_new(size_t max_conns, size_t max_ibufs, size_
res->obufs_max = max_obufs;
res->udp_payload_limit = udp_payload;
+ int ret = gnutls_priority_init2(&res->priority, KNOT_TLS_PRIORITIES, NULL,
+ GNUTLS_PRIORITY_INIT_DEF_APPEND);
+ if (ret != GNUTLS_E_SUCCESS) {
+ free(res);
+ return NULL;
+ }
+
res->expiry_heap = malloc(sizeof(struct heap));
if (res->expiry_heap == NULL || !heap_init(res->expiry_heap, cmp_expiry_heap_nodes, 0)) {
free(res->expiry_heap);
+ gnutls_priority_deinit(res->priority);
free(res);
return NULL;
}
@@ -92,6 +101,7 @@ void knot_quic_table_free(knot_quic_table_t *table)
assert(table->ibufs_size == 0);
assert(table->obufs_size == 0);
+ gnutls_priority_deinit(table->priority);
heap_deinit(table->expiry_heap);
free(table->expiry_heap);
free(table);
@@ -118,7 +128,9 @@ void knot_quic_table_sweep(knot_quic_table_t *table, struct knot_quic_reply *swe
while (!EMPTY_HEAP(table->expiry_heap)) {
knot_quic_conn_t *c = *(knot_quic_conn_t **)HHEAD(table->expiry_heap);
- if (table->usage > table->max_conns) {
+ if ((c->flags & KNOT_QUIC_CONN_BLOCKED)) {
+ break; // highly inprobable
+ } else if (table->usage > table->max_conns) {
knot_sweep_stats_incr(stats, KNOT_SWEEP_CTR_LIMIT_CONN);
send_excessive_load(c, sweep_reply, table);
knot_quic_table_rem(c, table);
@@ -476,7 +488,7 @@ uint8_t *knot_quic_stream_add_data(knot_quic_conn_t *conn, int64_t stream_id,
add_tail((list_t *)&s->outbufs, (node_t *)obuf);
s->obufs_size += obuf->len;
conn->obufs_size += obuf->len;
- conn->quic_table->obufs_size += obuf->len;
+ ATOMIC_ADD(conn->quic_table->obufs_size, obuf->len);
return obuf->buf + prefix;
}
@@ -497,7 +509,7 @@ void knot_quic_stream_ack_data(knot_quic_conn_t *conn, int64_t stream_id,
assert(HEAD(*obs) != first); // help CLANG analyzer understand what rem_node did and that further usage of HEAD(*obs) is safe
s->obufs_size -= first->len;
conn->obufs_size -= first->len;
- conn->quic_table->obufs_size -= first->len;
+ ATOMIC_SUB(conn->quic_table->obufs_size, first->len);
s->first_offset += first->len;
free(first);
if (s->unsent_obuf == first) {
@@ -556,6 +568,19 @@ void knot_quic_stream_mark_sent(knot_quic_conn_t *conn, int64_t stream_id,
}
_public_
+void knot_quic_conn_block(knot_quic_conn_t *conn, bool block)
+{
+ if (block) {
+ conn->flags |= KNOT_QUIC_CONN_BLOCKED;
+ conn->next_expiry = UINT64_MAX;
+ conn_heap_reschedule(conn, conn->quic_table);
+ } else {
+ conn->flags &= ~KNOT_QUIC_CONN_BLOCKED;
+ quic_conn_mark_used(conn, conn->quic_table);
+ }
+}
+
+_public_
void knot_quic_cleanup(knot_quic_conn_t *conns[], size_t n_conns)
{
for (size_t i = 0; i < n_conns; i++) {
diff --git a/src/libknot/quic/quic_conn.h b/src/libknot/quic/quic_conn.h
index 64ead51..49e0631 100644
--- a/src/libknot/quic/quic_conn.h
+++ b/src/libknot/quic/quic_conn.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,10 +29,13 @@
#include <stdint.h>
#include <sys/uio.h>
+#include "contrib/atomic.h"
+
#define MAX_STREAMS_PER_CONN 10 // this limits the number of un-finished streams per conn (i.e. if response has been recvd with FIN, it doesn't count)
+struct gnutls_priority_st;
struct ngtcp2_cid; // declaration taken from wherever in ngtcp2
-struct knot_quic_creds;
+struct knot_creds;
struct knot_quic_reply;
struct knot_sweep_stats;
@@ -70,6 +73,7 @@ typedef struct {
typedef enum {
KNOT_QUIC_CONN_HANDSHAKE_DONE = (1 << 0),
KNOT_QUIC_CONN_SESSION_TAKEN = (1 << 1),
+ KNOT_QUIC_CONN_BLOCKED = (1 << 2),
} knot_quic_conn_flag_t;
typedef struct knot_quic_conn {
@@ -111,12 +115,13 @@ typedef struct knot_quic_table {
size_t ibufs_max;
size_t obufs_max;
size_t ibufs_size;
- size_t obufs_size;
+ knot_atomic_size_t obufs_size;
size_t udp_payload_limit; // for simplicity not distinguishing IPv4/6
void (*log_cb)(const char *);
const char *qlog_dir;
uint64_t hash_secret[4];
- struct knot_quic_creds *creds;
+ struct knot_creds *creds;
+ struct gnutls_priority_st *priority;
struct heap *expiry_heap;
knot_quic_cid_t *conns[];
} knot_quic_table_t;
@@ -133,7 +138,7 @@ typedef struct knot_quic_table {
* \return Allocated table, or NULL.
*/
knot_quic_table_t *knot_quic_table_new(size_t max_conns, size_t max_ibufs, size_t max_obufs,
- size_t udp_payload, struct knot_quic_creds *creds);
+ size_t udp_payload, struct knot_creds *creds);
/*!
* \brief Free QUIC table including its contents.
@@ -306,6 +311,11 @@ void knot_quic_stream_mark_sent(knot_quic_conn_t *conn, int64_t stream_id,
size_t amount_sent);
/*!
+ * \brief (Un)block the connection for incoming/outgoing traffic and sweep.
+ */
+void knot_quic_conn_block(knot_quic_conn_t *conn, bool block);
+
+/*!
* \brief Free rest of resources of closed conns.
*
* \param conns Array with recently used conns (possibly NULLs).
diff --git a/src/libknot/quic/tls.c b/src/libknot/quic/tls.c
new file mode 100644
index 0000000..01172df
--- /dev/null
+++ b/src/libknot/quic/tls.c
@@ -0,0 +1,262 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <gnutls/crypto.h>
+#include <gnutls/gnutls.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "libknot/quic/tls.h"
+
+#include "contrib/macros.h"
+#include "contrib/time.h"
+#include "libknot/attribute.h"
+#include "libknot/error.h"
+#include "libknot/quic/tls_common.h"
+
+_public_
+knot_tls_ctx_t *knot_tls_ctx_new(struct knot_creds *creds, unsigned io_timeout,
+ unsigned hs_timeout, bool server)
+{
+ knot_tls_ctx_t *res = calloc(1, sizeof(*res));
+ if (res == NULL) {
+ return NULL;
+ }
+
+ res->creds = creds;
+ res->handshake_timeout = hs_timeout;
+ res->io_timeout = io_timeout;
+ res->server = server;
+
+ int ret = gnutls_priority_init2(&res->priority, KNOT_TLS_PRIORITIES, NULL,
+ GNUTLS_PRIORITY_INIT_DEF_APPEND);
+ if (ret != GNUTLS_E_SUCCESS) {
+ free(res);
+ return NULL;
+ }
+
+ return res;
+}
+
+_public_
+void knot_tls_ctx_free(knot_tls_ctx_t *ctx)
+{
+ if (ctx != NULL) {
+ gnutls_priority_deinit(ctx->priority);
+ free(ctx);
+ }
+}
+
+_public_
+knot_tls_conn_t *knot_tls_conn_new(knot_tls_ctx_t *ctx, int sock_fd)
+{
+ knot_tls_conn_t *res = calloc(1, sizeof(*res));
+ if (res == NULL) {
+ return NULL;
+ }
+ res->ctx = ctx;
+ res->fd = sock_fd;
+
+ int ret = knot_tls_session(&res->session, ctx->creds, ctx->priority,
+ "\x03""dot", false, ctx->server);
+ if (ret != KNOT_EOK) {
+ goto fail;
+ }
+
+ gnutls_transport_set_int(res->session, sock_fd); // Use internal recv/send/poll.
+ gnutls_handshake_set_timeout(res->session, ctx->handshake_timeout);
+
+ return res;
+fail:
+ gnutls_deinit(res->session);
+ free(res);
+ return NULL;
+}
+
+_public_
+void knot_tls_conn_del(knot_tls_conn_t *conn)
+{
+ if (conn != NULL && conn->fd_clones_count-- < 1) {
+ gnutls_deinit(conn->session);
+ free(conn);
+ }
+}
+
+_public_
+int knot_tls_handshake(knot_tls_conn_t *conn, bool oneshot)
+{
+ if (conn->flags & (KNOT_TLS_CONN_HANDSHAKE_DONE | KNOT_TLS_CONN_BLOCKED)) {
+ return KNOT_EOK;
+ }
+
+ /* Check if NB socket is writeable. */
+ int opt;
+ socklen_t opt_len = sizeof(opt);
+ int ret = getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
+ if (ret < 0 || opt == ECONNREFUSED) {
+ return KNOT_NET_ECONNECT;
+ }
+
+ gnutls_record_set_timeout(conn->session, conn->ctx->io_timeout);
+ do {
+ ret = gnutls_handshake(conn->session);
+ } while (!oneshot && ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ switch (ret) {
+ case GNUTLS_E_SUCCESS:
+ conn->flags |= KNOT_TLS_CONN_HANDSHAKE_DONE;
+ return knot_tls_pin_check(conn->session, conn->ctx->creds);
+ case GNUTLS_E_TIMEDOUT:
+ return KNOT_NET_ETIMEOUT;
+ default:
+ if (gnutls_error_is_fatal(ret) == 0) {
+ return KNOT_EAGAIN;
+ } else {
+ return KNOT_NET_EHSHAKE;
+ }
+ }
+}
+
+#define TIMEOUT_CTX_INIT \
+ struct timespec begin, end; \
+ if (*timeout_ptr > 0) { \
+ clock_gettime(CLOCK_MONOTONIC, &begin); \
+ }
+
+#define TIMEOUT_CTX_UPDATE \
+ if (*timeout_ptr > 0) { \
+ clock_gettime(CLOCK_MONOTONIC, &end); \
+ int running_ms = time_diff_ms(&begin, &end); \
+ *timeout_ptr = MAX(*timeout_ptr - running_ms, 0); \
+ }
+
+static ssize_t recv_data(knot_tls_conn_t *conn, void *data, size_t size, int *timeout_ptr)
+{
+ gnutls_record_set_timeout(conn->session, *timeout_ptr);
+
+ size_t total = 0;
+ ssize_t res;
+ while (total < size) {
+ TIMEOUT_CTX_INIT
+ res = gnutls_record_recv(conn->session, data + total, size - total);
+ if (res > 0) {
+ total += res;
+ } else if (res == 0) {
+ return KNOT_ECONNRESET;
+ } else if (gnutls_error_is_fatal(res) != 0) {
+ return KNOT_NET_ERECV;
+ }
+ TIMEOUT_CTX_UPDATE
+ gnutls_record_set_timeout(conn->session, *timeout_ptr);
+ }
+
+ assert(total == size);
+ return size;
+}
+
+_public_
+ssize_t knot_tls_recv_dns(knot_tls_conn_t *conn, void *data, size_t size)
+{
+ if (conn == NULL || data == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (conn->flags & KNOT_TLS_CONN_BLOCKED) {
+ return 0;
+ }
+
+ ssize_t ret = knot_tls_handshake(conn, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ int timeout = conn->ctx->io_timeout;
+
+ uint16_t msg_len;
+ ret = recv_data(conn, &msg_len, sizeof(msg_len), &timeout);
+ if (ret != sizeof(msg_len)) {
+ return ret;
+ }
+
+ msg_len = ntohs(msg_len);
+ if (size < msg_len) {
+ return KNOT_ESPACE;
+ }
+
+ ret = recv_data(conn, data, msg_len, &timeout);
+ if (ret != size) {
+ return ret;
+ }
+
+ return msg_len;
+}
+
+_public_
+ssize_t knot_tls_send_dns(knot_tls_conn_t *conn, void *data, size_t size)
+{
+ if (conn == NULL || data == NULL || size > UINT16_MAX) {
+ return KNOT_EINVAL;
+ }
+
+ ssize_t res = knot_tls_handshake(conn, false);
+ if (res != KNOT_EOK) {
+ return res;
+ }
+
+ // Enable data buffering.
+ gnutls_record_cork(conn->session);
+
+ uint16_t msg_len = htons(size);
+ res = gnutls_record_send(conn->session, &msg_len, sizeof(msg_len));
+ if (res != sizeof(msg_len)) {
+ return KNOT_NET_ESEND;
+ }
+
+ res = gnutls_record_send(conn->session, data, size);
+ if (res != size) {
+ return KNOT_NET_ESEND;
+ }
+
+ int timeout = conn->ctx->io_timeout, *timeout_ptr = &timeout;
+ gnutls_record_set_timeout(conn->session, timeout);
+
+ // Send the buffered data.
+ while (gnutls_record_check_corked(conn->session) > 0) {
+ TIMEOUT_CTX_INIT
+ int ret = gnutls_record_uncork(conn->session, 0);
+ if (ret < 0 && gnutls_error_is_fatal(ret) != 0) {
+ return ret == GNUTLS_E_TIMEDOUT ? KNOT_ETIMEOUT :
+ KNOT_NET_ESEND;
+ }
+ TIMEOUT_CTX_UPDATE
+ gnutls_record_set_timeout(conn->session, timeout);
+ }
+
+ return size;
+}
+
+_public_
+void knot_tls_conn_block(knot_tls_conn_t *conn, bool block)
+{
+ if (block) {
+ conn->flags |= KNOT_TLS_CONN_BLOCKED;
+ } else {
+ conn->flags &= ~KNOT_TLS_CONN_BLOCKED;
+ }
+}
diff --git a/src/libknot/quic/tls.h b/src/libknot/quic/tls.h
new file mode 100644
index 0000000..7801ca8
--- /dev/null
+++ b/src/libknot/quic/tls.h
@@ -0,0 +1,135 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Pure TLS functionality.
+ *
+ * \addtogroup quic
+ * @{
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+struct gnutls_priority_st;
+
+typedef enum {
+ KNOT_TLS_CONN_HANDSHAKE_DONE = (1 << 0),
+ KNOT_TLS_CONN_SESSION_TAKEN = (1 << 1), // unused, to be implemeted later
+ KNOT_TLS_CONN_BLOCKED = (1 << 2),
+} knot_tls_conn_flag_t;
+
+typedef struct knot_tls_ctx {
+ struct knot_creds *creds;
+ struct gnutls_priority_st *priority;
+ unsigned handshake_timeout;
+ unsigned io_timeout;
+ bool server;
+} knot_tls_ctx_t;
+
+typedef struct knot_tls_conn {
+ struct gnutls_session_int *session;
+ struct knot_tls_ctx *ctx;
+ int fd;
+ unsigned fd_clones_count;
+ knot_tls_conn_flag_t flags;
+} knot_tls_conn_t;
+
+/*!
+ * \brief Initialize DoT answering context.
+ *
+ * \param creds Certificate credentials.
+ * \param io_timeout Connections' IO-timeout (in milliseconds).
+ * \param hs_timeout Handshake timeout (in milliseconds).
+ * \param server Server context (otherwise client).
+ *
+ * \return Initialized context or NULL.
+ */
+knot_tls_ctx_t *knot_tls_ctx_new(struct knot_creds *creds, unsigned io_timeout,
+ unsigned hs_timeout, bool server);
+
+/*!
+ * \brief Free DoT answering context.
+ */
+void knot_tls_ctx_free(knot_tls_ctx_t *ctx);
+
+/*!
+ * \brief Initialize DoT connection.
+ *
+ * \param ctx DoT answering context.
+ * \param sock_fd Opened TCP connection socket.
+ *
+ * \return Connection struct or NULL.
+ */
+knot_tls_conn_t *knot_tls_conn_new(knot_tls_ctx_t *ctx, int sock_fd);
+
+/*!
+ * \brief Free DoT connection struct.
+ *
+ * \note Doesn't close the TCP connection socket.
+ */
+void knot_tls_conn_del(knot_tls_conn_t *conn);
+
+/*!
+ * \brief Perform the TLS handshake (via gnutls_handshake()).
+ *
+ * \note This is also done by the recv/send functions.
+ *
+ * \param conn DoT connection.
+ * \param oneshot If set, don't wait untill the handshake is finished.
+ *
+ * \retval KNOT_EOK Handshake successfully finished.
+ * \retval KNOT_EGAIN Handshake not finished, call me again.
+ * \retval KNOT_NET_EHSHAKE Handshake error.
+ * \retval KNOT_NET_ECONNECT Socket not connected.
+ */
+int knot_tls_handshake(knot_tls_conn_t *conn, bool oneshot);
+
+/*!
+ * \brief Receive a size-word-prefixed DNS message.
+ *
+ * \param conn DoT connection.
+ * \param data Destination buffer.
+ * \param size Maximum buffer size.
+ *
+ * \return Either the DNS message size received or negative error code.
+ *
+ * \note The two-byte-size-prefix is stripped upon reception, not stored to the buffer.
+ */
+ssize_t knot_tls_recv_dns(knot_tls_conn_t *conn, void *data, size_t size);
+
+/*!
+ * \brief Send a size-word-prefixed DNS message.
+ *
+ * \param conn DoT connection.
+ * \param data DNS payload.
+ * \param size Payload size.
+ *
+ * \return Either exactly 'size' or a negative error code.
+ */
+ssize_t knot_tls_send_dns(knot_tls_conn_t *conn, void *data, size_t size);
+
+/*!
+ * \brief Set or unset the conection's BLOCKED flag.
+ */
+void knot_tls_conn_block(knot_tls_conn_t *conn, bool block);
+
+/*! @} */
diff --git a/src/libknot/quic/tls_common.c b/src/libknot/quic/tls_common.c
new file mode 100644
index 0000000..d1647d8
--- /dev/null
+++ b/src/libknot/quic/tls_common.c
@@ -0,0 +1,472 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <fcntl.h>
+#include <gnutls/crypto.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "libknot/quic/tls_common.h"
+
+#include "contrib/atomic.h"
+#include "contrib/sockaddr.h"
+#include "contrib/string.h"
+#include "libknot/attribute.h"
+#include "libknot/error.h"
+
+typedef struct knot_creds {
+ knot_atomic_ptr_t cert_creds; // Current credentials.
+ gnutls_certificate_credentials_t cert_creds_prev; // Previous credentials (for pending connections).
+ gnutls_anti_replay_t tls_anti_replay;
+ gnutls_datum_t tls_ticket_key;
+ bool peer;
+ uint8_t peer_pin_len;
+ uint8_t peer_pin[];
+} knot_creds_t;
+
+static int tls_anti_replay_db_add_func(void *dbf, time_t exp_time,
+ const gnutls_datum_t *key,
+ const gnutls_datum_t *data)
+{
+ return 0;
+}
+
+static void tls_session_ticket_key_free(gnutls_datum_t *ticket)
+{
+ memzero(ticket->data, ticket->size);
+ gnutls_free(ticket->data);
+}
+
+static int self_key(gnutls_x509_privkey_t *privkey, const char *key_file)
+{
+ gnutls_datum_t data = { 0 };
+
+ int ret = gnutls_x509_privkey_init(privkey);
+ if (ret != GNUTLS_E_SUCCESS) {
+ return ret;
+ }
+
+ int fd = open(key_file, O_RDONLY);
+ if (fd != -1) {
+ struct stat stat;
+ if (fstat(fd, &stat) != 0 ||
+ (data.data = gnutls_malloc(stat.st_size)) == NULL ||
+ read(fd, data.data, stat.st_size) != stat.st_size) {
+ ret = GNUTLS_E_KEYFILE_ERROR;
+ goto finish;
+ }
+
+ data.size = stat.st_size;
+ ret = gnutls_x509_privkey_import_pkcs8(*privkey, &data, GNUTLS_X509_FMT_PEM,
+ NULL, GNUTLS_PKCS_PLAIN);
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto finish;
+ }
+ } else {
+ ret = gnutls_x509_privkey_generate(*privkey, GNUTLS_PK_EDDSA_ED25519,
+ GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_ED25519), 0);
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto finish;
+ }
+
+ ret = gnutls_x509_privkey_export2_pkcs8(*privkey, GNUTLS_X509_FMT_PEM, NULL,
+ GNUTLS_PKCS_PLAIN, &data);
+ if (ret != GNUTLS_E_SUCCESS ||
+ (fd = open(key_file, O_WRONLY | O_CREAT, 0600)) == -1 ||
+ write(fd, data.data, data.size) != data.size) {
+ ret = GNUTLS_E_KEYFILE_ERROR;
+ goto finish;
+ }
+ }
+
+finish:
+ if (fd > -1) {
+ close(fd);
+ }
+ gnutls_free(data.data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_x509_privkey_deinit(*privkey);
+ *privkey = NULL;
+ }
+ return ret;
+}
+
+static int self_signed_cert(gnutls_certificate_credentials_t tls_cert,
+ const char *key_file)
+{
+ gnutls_x509_privkey_t privkey = NULL;
+ gnutls_x509_crt_t cert = NULL;
+
+ char *hostname = sockaddr_hostname();
+ if (hostname == NULL) {
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ int ret;
+ uint8_t serial[16];
+ gnutls_rnd(GNUTLS_RND_NONCE, serial, sizeof(serial));
+ // Clear the left-most bit to be a positive number (two's complement form).
+ serial[0] &= 0x7F;
+
+#define CHK(cmd) if ((ret = (cmd)) != GNUTLS_E_SUCCESS) { goto finish; }
+#define NOW_DAYS(days) (time(NULL) + 24 * 3600 * (days))
+
+ CHK(self_key(&privkey, key_file));
+
+ CHK(gnutls_x509_crt_init(&cert));
+ CHK(gnutls_x509_crt_set_version(cert, 3));
+ CHK(gnutls_x509_crt_set_serial(cert, serial, sizeof(serial)));
+ CHK(gnutls_x509_crt_set_activation_time(cert, NOW_DAYS(-1)));
+ CHK(gnutls_x509_crt_set_expiration_time(cert, NOW_DAYS(10 * 365)));
+ CHK(gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0,
+ hostname, strlen(hostname)));
+ CHK(gnutls_x509_crt_set_key(cert, privkey));
+ CHK(gnutls_x509_crt_sign2(cert, cert, privkey, GNUTLS_DIG_SHA512, 0));
+
+ ret = gnutls_certificate_set_x509_key(tls_cert, &cert, 1, privkey);
+
+finish:
+ free(hostname);
+ gnutls_x509_crt_deinit(cert);
+ gnutls_x509_privkey_deinit(privkey);
+
+ return ret;
+}
+
+_public_
+struct knot_creds *knot_creds_init(const char *key_file, const char *cert_file)
+{
+ knot_creds_t *creds = calloc(1, sizeof(*creds));
+ if (creds == NULL) {
+ return NULL;
+ }
+
+ int ret = knot_creds_update(creds, key_file, cert_file);
+ if (ret != KNOT_EOK) {
+ goto fail;
+ }
+
+ ret = gnutls_anti_replay_init(&creds->tls_anti_replay);
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto fail;
+ }
+ gnutls_anti_replay_set_add_function(creds->tls_anti_replay, tls_anti_replay_db_add_func);
+ gnutls_anti_replay_set_ptr(creds->tls_anti_replay, NULL);
+
+ ret = gnutls_session_ticket_key_generate(&creds->tls_ticket_key);
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto fail;
+ }
+
+ return creds;
+fail:
+ knot_creds_free(creds);
+ return NULL;
+}
+
+_public_
+struct knot_creds *knot_creds_init_peer(const struct knot_creds *local_creds,
+ const uint8_t *peer_pin,
+ uint8_t peer_pin_len)
+{
+ knot_creds_t *creds = calloc(1, sizeof(*creds) + peer_pin_len);
+ if (creds == NULL) {
+ return NULL;
+ }
+
+ if (local_creds != NULL) {
+ creds->peer = true;
+ creds->cert_creds = ATOMIC_GET(local_creds->cert_creds);
+ } else {
+ gnutls_certificate_credentials_t new_creds;
+ int ret = gnutls_certificate_allocate_credentials(&new_creds);
+ if (ret != GNUTLS_E_SUCCESS) {
+ free(creds);
+ return NULL;
+ }
+ creds->cert_creds = new_creds;
+ }
+
+ if (peer_pin_len > 0 && peer_pin != NULL) {
+ memcpy(creds->peer_pin, peer_pin, peer_pin_len);
+ creds->peer_pin_len = peer_pin_len;
+ }
+
+ return creds;
+}
+
+static int creds_cert(gnutls_certificate_credentials_t creds,
+ struct gnutls_x509_crt_int **cert)
+{
+ gnutls_x509_crt_t *certs;
+ unsigned cert_count;
+ int ret = gnutls_certificate_get_x509_crt(creds, 0, &certs, &cert_count);
+ if (ret == GNUTLS_E_SUCCESS) {
+ if (cert_count == 0) {
+ gnutls_x509_crt_deinit(*certs);
+ return KNOT_ENOENT;
+ }
+ *cert = *certs;
+ free(certs);
+ return KNOT_EOK;
+ }
+ return KNOT_ERROR;
+}
+
+static int creds_changed(gnutls_certificate_credentials_t creds,
+ gnutls_certificate_credentials_t prev,
+ bool self_cert, bool *changed)
+{
+ if (creds == NULL || prev == NULL) {
+ *changed = true;
+ return KNOT_EOK;
+ }
+
+ gnutls_x509_crt_t cert = NULL, cert_prev = NULL;
+
+ int ret = creds_cert(creds, &cert);
+ if (ret != KNOT_EOK) {
+ goto failed;
+ }
+ ret = creds_cert(prev, &cert_prev);
+ if (ret != KNOT_EOK) {
+ goto failed;
+ }
+
+ if (self_cert) {
+ uint8_t pin[KNOT_TLS_PIN_LEN], pin_prev[KNOT_TLS_PIN_LEN];
+ size_t pin_size = sizeof(pin), pin_prev_size = sizeof(pin_prev);
+
+ ret = gnutls_x509_crt_get_key_id(cert, GNUTLS_KEYID_USE_SHA256,
+ pin, &pin_size);
+ if (ret != KNOT_EOK) {
+ goto failed;
+ }
+ ret = gnutls_x509_crt_get_key_id(cert_prev, GNUTLS_KEYID_USE_SHA256,
+ pin_prev, &pin_prev_size);
+ if (ret != KNOT_EOK) {
+ goto failed;
+ }
+
+ *changed = (pin_size != pin_prev_size) ||
+ memcmp(pin, pin_prev, pin_size) != 0;
+ } else {
+ *changed = (gnutls_x509_crt_equals(cert, cert_prev) == 0);
+ }
+
+ ret = KNOT_EOK;
+failed:
+ gnutls_x509_crt_deinit(cert);
+ gnutls_x509_crt_deinit(cert_prev);
+
+ return ret;
+}
+
+_public_
+int knot_creds_update(struct knot_creds *creds, const char *key_file, const char *cert_file)
+{
+ if (creds == NULL || key_file == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ gnutls_certificate_credentials_t new_creds;
+ int ret = gnutls_certificate_allocate_credentials(&new_creds);
+ if (ret != GNUTLS_E_SUCCESS) {
+ return KNOT_ENOMEM;
+ }
+
+ if (cert_file != NULL) {
+ ret = gnutls_certificate_set_x509_key_file(new_creds,
+ cert_file, key_file,
+ GNUTLS_X509_FMT_PEM);
+ } else {
+ ret = self_signed_cert(new_creds, key_file);
+ }
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_certificate_free_credentials(new_creds);
+ return KNOT_EFILE;
+ }
+
+ bool changed = false;
+ ret = creds_changed(new_creds, ATOMIC_GET(creds->cert_creds),
+ cert_file == NULL, &changed);
+ if (ret != KNOT_EOK) {
+ gnutls_certificate_free_credentials(new_creds);
+ return ret;
+ }
+
+ if (changed) {
+ if (creds->cert_creds_prev != NULL) {
+ gnutls_certificate_free_credentials(creds->cert_creds_prev);
+ }
+ creds->cert_creds_prev = ATOMIC_XCHG(creds->cert_creds, new_creds);
+ } else {
+ gnutls_certificate_free_credentials(new_creds);
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_creds_cert(struct knot_creds *creds, struct gnutls_x509_crt_int **cert)
+{
+ if (creds == NULL || cert == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return creds_cert(ATOMIC_GET(creds->cert_creds), cert);
+}
+
+_public_
+void knot_creds_free(struct knot_creds *creds)
+{
+ if (creds == NULL) {
+ return;
+ }
+
+ if (!creds->peer && creds->cert_creds != NULL) {
+ gnutls_certificate_free_credentials(creds->cert_creds);
+ if (creds->cert_creds_prev != NULL) {
+ gnutls_certificate_free_credentials(creds->cert_creds_prev);
+ }
+ }
+ gnutls_anti_replay_deinit(creds->tls_anti_replay);
+ if (creds->tls_ticket_key.data != NULL) {
+ tls_session_ticket_key_free(&creds->tls_ticket_key);
+ }
+ free(creds);
+}
+
+_public_
+int knot_tls_session(struct gnutls_session_int **session,
+ struct knot_creds *creds,
+ struct gnutls_priority_st *priority,
+ const char *alpn,
+ bool early_data,
+ bool server)
+{
+ if (session == NULL || creds == NULL || priority == NULL || alpn == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ gnutls_init_flags_t flags = GNUTLS_NO_SIGNAL;
+ if (early_data) {
+ flags |= GNUTLS_ENABLE_EARLY_DATA;
+#ifdef ENABLE_QUIC // Next flags aren't available in older GnuTLS versions.
+ flags |= GNUTLS_NO_AUTO_SEND_TICKET | GNUTLS_NO_END_OF_EARLY_DATA;
+#endif
+ }
+
+ int ret = gnutls_init(session, (server ? GNUTLS_SERVER : GNUTLS_CLIENT) | flags);
+ if (ret == GNUTLS_E_SUCCESS) {
+ gnutls_certificate_send_x509_rdn_sequence(*session, 1);
+ gnutls_certificate_server_set_request(*session, GNUTLS_CERT_REQUEST);
+ ret = gnutls_priority_set(*session, priority);
+ }
+ if (server && ret == GNUTLS_E_SUCCESS) {
+ ret = gnutls_session_ticket_enable_server(*session, &creds->tls_ticket_key);
+ }
+ if (ret == GNUTLS_E_SUCCESS) {
+ const gnutls_datum_t alpn_datum = { (void *)&alpn[1], alpn[0] };
+ gnutls_alpn_set_protocols(*session, &alpn_datum, 1, GNUTLS_ALPN_MANDATORY);
+ if (early_data) {
+ gnutls_record_set_max_early_data_size(*session, 0xffffffffu);
+ }
+ if (server) {
+ gnutls_anti_replay_enable(*session, creds->tls_anti_replay);
+ }
+ ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
+ ATOMIC_GET(creds->cert_creds));
+ }
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_deinit(*session);
+ *session = NULL;
+ }
+ return ret == GNUTLS_E_SUCCESS ? KNOT_EOK : KNOT_ERROR;
+}
+
+_public_
+void knot_tls_pin(struct gnutls_session_int *session, uint8_t *pin,
+ size_t *pin_size, bool local)
+{
+ if (session == NULL) {
+ goto error;
+ }
+
+ const gnutls_datum_t *data = NULL;
+ if (local) {
+ data = gnutls_certificate_get_ours(session);
+ } else {
+ unsigned count = 0;
+ data = gnutls_certificate_get_peers(session, &count);
+ if (count == 0) {
+ goto error;
+ }
+ }
+ if (data == NULL) {
+ goto error;
+ }
+
+ gnutls_x509_crt_t cert;
+ int ret = gnutls_x509_crt_init(&cert);
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto error;
+ }
+
+ ret = gnutls_x509_crt_import(cert, data, GNUTLS_X509_FMT_DER);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_x509_crt_deinit(cert);
+ goto error;
+ }
+
+ ret = gnutls_x509_crt_get_key_id(cert, GNUTLS_KEYID_USE_SHA256, pin, pin_size);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_x509_crt_deinit(cert);
+ goto error;
+ }
+
+ gnutls_x509_crt_deinit(cert);
+
+ return;
+error:
+ if (pin_size != NULL) {
+ *pin_size = 0;
+ }
+}
+
+_public_
+int knot_tls_pin_check(struct gnutls_session_int *session,
+ struct knot_creds *creds)
+{
+ if (creds->peer_pin_len == 0) {
+ return KNOT_EOK;
+ }
+
+ uint8_t pin[KNOT_TLS_PIN_LEN];
+ size_t pin_size = sizeof(pin);
+ knot_tls_pin(session, pin, &pin_size, false);
+ if (pin_size != creds->peer_pin_len ||
+ const_time_memcmp(pin, creds->peer_pin, pin_size) != 0) {
+ return KNOT_EBADCERTKEY;
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/quic/tls_common.h b/src/libknot/quic/tls_common.h
new file mode 100644
index 0000000..934f256
--- /dev/null
+++ b/src/libknot/quic/tls_common.h
@@ -0,0 +1,134 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Credentials handling common to QUIC and TLS.
+ *
+ * \addtogroup quic
+ * @{
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#define KNOT_TLS_PIN_LEN 32
+#define KNOT_TLS_PRIORITIES "-VERS-ALL:+VERS-TLS1.3:" \
+ "-GROUP-ALL:+GROUP-X25519:+GROUP-SECP256R1:" \
+ "+GROUP-SECP384R1:+GROUP-SECP521R1"
+
+struct gnutls_priority_st;
+struct gnutls_session_int;
+struct gnutls_x509_crt_int;
+struct knot_creds;
+
+/*!
+ * \brief Init server TLS key and certificate for DoQ.
+ *
+ * \param key_file Key PEM file path/name.
+ * \param cert_file X509 certificate PEM file path/name (NULL if auto-generated).
+ *
+ * \return Initialized creds.
+ */
+struct knot_creds *knot_creds_init(const char *key_file, const char *cert_file);
+
+/*!
+ * \brief Init peer TLS key and certificate for DoQ.
+ *
+ * \param local_creds Local credentials if server.
+ * \param peer_pin Optional peer certificate pin to check.
+ * \param peer_pin_len Length of the peer pin. Set 0 if not specified.
+ *
+ * \return Initialized creds.
+ */
+struct knot_creds *knot_creds_init_peer(const struct knot_creds *local_creds,
+ const uint8_t *peer_pin,
+ uint8_t peer_pin_len);
+
+/*!
+ * \brief Load new server TLS key and certificate for DoQ.
+ *
+ * \param creds Server credentials where key/cert pair will be updated.
+ * \param key_file Key PEM file path/name.
+ * \param cert_file X509 certificate PEM file path/name (NULL if auto-generated).
+ *
+ * \return KNOT_E*
+ */
+int knot_creds_update(struct knot_creds *creds, const char *key_file, const char *cert_file);
+
+/*!
+ * \brief Gets the certificate from credentials.
+ *
+ * \param creds TLS credentials.
+ * \param cert Output certificate.
+ *
+ * \return KNOT_E*
+ */
+int knot_creds_cert(struct knot_creds *creds, struct gnutls_x509_crt_int **cert);
+
+/*!
+ * \brief Deinit server TLS certificate for DoQ.
+ */
+void knot_creds_free(struct knot_creds *creds);
+
+/*!
+ * \brief Initialize GnuTLS session with credentials, ALPN, etc.
+ *
+ * \param session Out: initialized GnuTLS session struct.
+ * \param creds Certificate credentials.
+ * \param priority Session priority configuration.
+ * \param alpn ALPN string, first byte is the string length.
+ * \param early_data Allow early data.
+ * \param server Should be server session (otherwise client).
+ *
+ * \return KNOT_E*
+ */
+int knot_tls_session(struct gnutls_session_int **session,
+ struct knot_creds *creds,
+ struct gnutls_priority_st *priority,
+ const char *alpn,
+ bool early_data,
+ bool server);
+
+/*!
+ * \brief Gets local or remote certificate pin.
+ *
+ * \note Zero output pin_size value means no certificate available or error.
+ *
+ * \param session TLS connection.
+ * \param pin Output certificate pin.
+ * \param pin_size Input size of the storage / output size of the stored pin.
+ * \param local Local or remote certificate indication.
+ */
+void knot_tls_pin(struct gnutls_session_int *session, uint8_t *pin,
+ size_t *pin_size, bool local);
+
+/*!
+ * \brief Checks remote certificate pin in the session against credentials.
+ *
+ * \param session TLS connection.
+ * \param creds TLS credentials.
+ *
+ * \return KNOT_EOK or KNOT_EBADCERTKEY
+ */
+int knot_tls_pin_check(struct gnutls_session_int *session,
+ struct knot_creds *creds);
+
+/*! @} */
diff --git a/src/libknot/rrset-dump.c b/src/libknot/rrset-dump.c
index ea406ca..9fac99d 100644
--- a/src/libknot/rrset-dump.c
+++ b/src/libknot/rrset-dump.c
@@ -508,10 +508,46 @@ static void wire_len_data_encode_to_str(rrset_dump_params_t *p,
}
}
-static void wire_data_omit(rrset_dump_params_t *p)
+static void wire_data_omit(rrset_dump_params_t *p,
+ const size_t len_len,
+ const bool print_len)
{
CHECK_PRET
+ size_t in_len;
+
+ // First len_len bytes are data length.
+ CHECK_INMAX(len_len)
+
+ // Read data length.
+ switch (len_len) {
+ case 0:
+ in_len = p->in_max;
+ break;
+ case 2:
+ in_len = knot_wire_read_u16(p->in);
+ break;
+ default:
+ p->ret = -1;
+ return;
+ }
+
+ // If required print data length.
+ if (print_len == true && len_len != 0) {
+ assert(len_len == 2);
+ wire_num16_to_str(p);
+ CHECK_PRET
+
+ // If something follows, print one space character.
+ if (in_len > 0) {
+ dump_string(p, " ");
+ CHECK_PRET
+ }
+ } else {
+ p->in += len_len;
+ p->in_max -= len_len;
+ }
+
const char *omit_message = "[omitted]";
const size_t omlen = strlen(omit_message);
@@ -527,8 +563,8 @@ static void wire_data_omit(rrset_dump_params_t *p)
STRING_TERMINATION
- p->in += p->in_max;
- p->in_max = 0;
+ p->in += in_len;
+ p->in_max -= in_len;
}
static void wire_dnskey_to_tag(rrset_dump_params_t *p)
@@ -1735,6 +1771,10 @@ static void dnskey_info(const uint8_t *rdata,
#define DUMP_TYPE wire_type_to_str(p); CHECK_RET(p);
#define DUMP_HEX wire_data_encode_to_str(p, &hex_encode, \
&hex_encode_alloc); CHECK_RET(p);
+#define DUMP_OMIT wire_data_omit(p, 0, false); CHECK_RET(p);
+#define DUMP_HEX_OMIT if (p->style->hide_crypto) { DUMP_OMIT; } \
+ else if (p->style->wrap) { WRAP_INIT; DUMP_HEX; WRAP_END; } \
+ else { DUMP_HEX; }
#define DUMP_BASE64 wire_data_encode_to_str(p, &knot_base64_encode, \
&knot_base64_encode_alloc); CHECK_RET(p);
#define DUMP_HASH wire_len_data_encode_to_str(p, &knot_base32hex_encode, \
@@ -1743,9 +1783,9 @@ static void dnskey_info(const uint8_t *rdata,
1, false, "-"); CHECK_RET(p);
#define DUMP_TSIG_DGST wire_len_data_encode_to_str(p, &knot_base64_encode, \
2, true, ""); CHECK_RET(p);
+#define DUMP_TSIG_OMIT wire_data_omit(p, 2, true); CHECK_RET(p);
#define DUMP_TSIG_DATA wire_len_data_encode_to_str(p, &num48_encode, \
2, true, ""); CHECK_RET(p);
-#define DUMP_OMIT wire_data_omit(p); CHECK_RET(p);
#define DUMP_KEY_OMIT wire_dnskey_to_tag(p); CHECK_RET(p);
#define DUMP_TEXT wire_text_to_str1(p, true, false); CHECK_RET(p);
#define DUMP_LONG_TEXT wire_text_to_str(p, p->in_max, NULL, true, false); CHECK_RET(p);
@@ -1923,16 +1963,17 @@ static int dump_naptr(DUMP_PARAMS)
static int dump_cert(DUMP_PARAMS)
{
- if (p->style->wrap) {
- DUMP_NUM16; DUMP_SPACE;
- DUMP_NUM16; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE; WRAP_INIT;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+
+ if (p->style->hide_crypto) {
+ DUMP_OMIT;
+ } else if (p->style->wrap) {
+ WRAP_INIT;
DUMP_BASE64;
WRAP_END;
} else {
- DUMP_NUM16; DUMP_SPACE;
- DUMP_NUM16; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE;
DUMP_BASE64;
}
@@ -1982,34 +2023,19 @@ static int dump_apl(DUMP_PARAMS)
static int dump_ds(DUMP_PARAMS)
{
- if (p->style->wrap) {
- DUMP_NUM16; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE; WRAP_INIT;
- DUMP_HEX;
- WRAP_END;
- } else {
- DUMP_NUM16; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE;
- DUMP_HEX;
- }
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_HEX_OMIT;
DUMP_END;
}
static int dump_sshfp(DUMP_PARAMS)
{
- if (p->style->wrap) {
- DUMP_NUM8; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE; WRAP_INIT;
- DUMP_HEX;
- WRAP_END;
- } else {
- DUMP_NUM8; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE;
- DUMP_HEX;
- }
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_HEX_OMIT;
DUMP_END;
}
@@ -2067,7 +2093,9 @@ static int dump_nsec(DUMP_PARAMS)
static int dump_dhcid(DUMP_PARAMS)
{
- if (p->style->wrap) {
+ if (p->style->hide_crypto) {
+ DUMP_OMIT;
+ } else if (p->style->wrap) {
WRAP_INIT;
DUMP_BASE64;
WRAP_END;
@@ -2112,18 +2140,10 @@ static int dump_nsec3param(DUMP_PARAMS)
static int dump_tlsa(DUMP_PARAMS)
{
- if (p->style->wrap) {
- DUMP_NUM8; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE; WRAP_INIT;
- DUMP_HEX;
- WRAP_END;
- } else {
- DUMP_NUM8; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE;
- DUMP_HEX;
- }
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_HEX_OMIT;
DUMP_END;
}
@@ -2139,18 +2159,10 @@ static int dump_csync(DUMP_PARAMS)
static int dump_zonemd(DUMP_PARAMS)
{
- if (p->style->wrap) {
- DUMP_NUM32; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE; WRAP_INIT;
- DUMP_HEX;
- WRAP_END;
- } else {
- DUMP_NUM32; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE;
- DUMP_NUM8; DUMP_SPACE;
- DUMP_HEX;
- }
+ DUMP_NUM32; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_HEX_OMIT;
DUMP_END;
}
@@ -2184,7 +2196,11 @@ static int dump_tsig(DUMP_PARAMS)
DUMP_DNAME; DUMP_SPACE;
DUMP_NUM48; DUMP_SPACE;
DUMP_NUM16; DUMP_SPACE; WRAP_INIT;
- DUMP_TSIG_DGST; WRAP_LINE;
+ if (p->style->hide_crypto) {
+ DUMP_TSIG_OMIT; WRAP_LINE;
+ } else {
+ DUMP_TSIG_DGST; WRAP_LINE;
+ }
DUMP_NUM16; DUMP_SPACE;
DUMP_TSIG_RCODE; DUMP_SPACE;
DUMP_TSIG_DATA;
@@ -2193,7 +2209,11 @@ static int dump_tsig(DUMP_PARAMS)
DUMP_DNAME; DUMP_SPACE;
DUMP_NUM48; DUMP_SPACE;
DUMP_NUM16; DUMP_SPACE;
- DUMP_TSIG_DGST; DUMP_SPACE;
+ if (p->style->hide_crypto) {
+ DUMP_TSIG_OMIT; DUMP_SPACE;
+ } else {
+ DUMP_TSIG_DGST; DUMP_SPACE;
+ }
DUMP_NUM16; DUMP_SPACE;
DUMP_TSIG_RCODE; DUMP_SPACE;
DUMP_TSIG_DATA;
diff --git a/src/libknot/rrtype/tsig.c b/src/libknot/rrtype/tsig.c
index 83f8436..0002963 100644
--- a/src/libknot/rrtype/tsig.c
+++ b/src/libknot/rrtype/tsig.c
@@ -245,7 +245,11 @@ int knot_tsig_rdata_set_other_data(knot_rrset_t *tsig, uint16_t len,
_public_
const knot_dname_t *knot_tsig_rdata_alg_name(const knot_rrset_t *tsig)
{
- return knot_rdataset_at(&tsig->rrs, 0)->data;
+ const knot_rdata_t *rr_data = knot_rdataset_at(&tsig->rrs, 0);
+ if (!rr_data) {
+ return NULL;
+ }
+ return rr_data->data;
}
_public_
diff --git a/src/libknot/version.h b/src/libknot/version.h
index 38e0b74..8e21f49 100644
--- a/src/libknot/version.h
+++ b/src/libknot/version.h
@@ -17,8 +17,8 @@
#pragma once
#define KNOT_VERSION_MAJOR 3
-#define KNOT_VERSION_MINOR 3
-#define KNOT_VERSION_PATCH 0x09
+#define KNOT_VERSION_MINOR 4
+#define KNOT_VERSION_PATCH 0x00
#define KNOT_VERSION_HEX ((KNOT_VERSION_MAJOR << 16) | \
(KNOT_VERSION_MINOR << 8) | \
diff --git a/src/libknot/xdp/Makefile.in b/src/libknot/xdp/Makefile.in
index 8aa77ce..f065f3e 100644
--- a/src/libknot/xdp/Makefile.in
+++ b/src/libknot/xdp/Makefile.in
@@ -270,6 +270,8 @@ infodir = @infodir@
install_sh = @install_sh@
libbpf_CFLAGS = @libbpf_CFLAGS@
libbpf_LIBS = @libbpf_LIBS@
+libdbus_CFLAGS = @libdbus_CFLAGS@
+libdbus_LIBS = @libdbus_LIBS@
libdir = @libdir@
libdnssec_SONAME = @libdnssec_SONAME@
libdnssec_SOVERSION = @libdnssec_SOVERSION@
@@ -281,8 +283,6 @@ libfstrm_CFLAGS = @libfstrm_CFLAGS@
libfstrm_LIBS = @libfstrm_LIBS@
libidn2_CFLAGS = @libidn2_CFLAGS@
libidn2_LIBS = @libidn2_LIBS@
-libidn_CFLAGS = @libidn_CFLAGS@
-libidn_LIBS = @libidn_LIBS@
libknot_SONAME = @libknot_SONAME@
libknot_SOVERSION = @libknot_SOVERSION@
libknot_VERSION_INFO = @libknot_VERSION_INFO@
@@ -300,7 +300,6 @@ libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@
libprotobuf_c_LIBS = @libprotobuf_c_LIBS@
liburcu_CFLAGS = @liburcu_CFLAGS@
liburcu_LIBS = @liburcu_LIBS@
-liburcu_PKGCONFIG = @liburcu_PKGCONFIG@
libxdp_CFLAGS = @libxdp_CFLAGS@
libxdp_LIBS = @libxdp_LIBS@
libzscanner_SONAME = @libzscanner_SONAME@
diff --git a/src/libknot/xdp/bpf-user.h b/src/libknot/xdp/bpf-user.h
index 37aac61..b76c9d6 100644
--- a/src/libknot/xdp/bpf-user.h
+++ b/src/libknot/xdp/bpf-user.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -60,8 +60,10 @@ struct kxsk_umem {
/*! The memory frames. */
struct umem_frame *frames;
+ /*! Size of RX and TX rings. */
+ uint16_t ring_size;
/*! The number of free frames (for TX). */
- uint32_t tx_free_count;
+ uint16_t tx_free_count;
/*! Stack of indices of the free frames (for TX). */
uint16_t tx_free_indices[];
};
@@ -82,15 +84,15 @@ struct knot_xdp_socket {
/*! If non-NULL, it's a mocked socket with this send function. */
int (*send_mock)(struct knot_xdp_socket *, const knot_xdp_msg_t[], uint32_t, uint32_t *);
- /*! The kernel has to be woken up by a syscall indication. */
- bool kernel_needs_wakeup;
-
/*! The limit of frame size. */
unsigned frame_limit;
/*! Mapping of interface indices to VLAN tags. */
uint16_t *vlan_map;
uint16_t vlan_map_max;
+
+ /*! Enabled preferred busy polling. */
+ bool busy_poll;
};
/*!
diff --git a/src/libknot/xdp/tcp.c b/src/libknot/xdp/tcp.c
index eae73a9..d219db9 100644
--- a/src/libknot/xdp/tcp.c
+++ b/src/libknot/xdp/tcp.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -287,172 +287,162 @@ static void conn_update(knot_tcp_conn_t *conn, const knot_xdp_msg_t *msg)
}
_public_
-int knot_tcp_recv(knot_tcp_relay_t *relays, knot_xdp_msg_t msgs[], uint32_t msg_count,
+int knot_tcp_recv(knot_tcp_relay_t *relay, knot_xdp_msg_t *msg,
knot_tcp_table_t *tcp_table, knot_tcp_table_t *syn_table,
knot_tcp_ignore_t ignore)
{
- if (msg_count == 0) {
- return KNOT_EOK;
- }
- if (relays == NULL || msgs == NULL || tcp_table == NULL) {
+ if (relay == NULL || msg == NULL || tcp_table == NULL) {
return KNOT_EINVAL;
}
- memset(relays, 0, msg_count * sizeof(*relays));
+ memset(relay, 0, sizeof(*relay));
- knot_tcp_relay_t *relay = relays;
int ret = KNOT_EOK;
- for (knot_xdp_msg_t *msg = msgs; msg != msgs + msg_count && ret == KNOT_EOK; msg++) {
- if (!(msg->flags & KNOT_XDP_MSG_TCP)) {
- continue;
- }
+ if (!(msg->flags & KNOT_XDP_MSG_TCP)) {
+ return KNOT_EOK;
+ }
- uint64_t conn_hash = 0;
- knot_tcp_conn_t **pconn = tcp_table_lookup(&msg->ip_from, &msg->ip_to,
- &conn_hash, tcp_table);
- knot_tcp_conn_t *conn = *pconn;
- bool seq_ack_match = check_seq_ack(msg, conn);
- if (seq_ack_match) {
- assert(conn->mss != 0);
- conn_update(conn, msg);
-
- rem_align_pointers(conn, tcp_table);
- rem_node(tcp_conn_node(conn));
- add_tail(tcp_table_timeout(tcp_table), tcp_conn_node(conn));
-
- if (msg->flags & KNOT_XDP_MSG_ACK) {
- conn->acked = msg->ackno;
- knot_tcp_outbufs_ack(&conn->outbufs, msg->ackno, &tcp_table->outbufs_total);
- }
+ uint64_t conn_hash = 0;
+ knot_tcp_conn_t **pconn = tcp_table_lookup(&msg->ip_from, &msg->ip_to,
+ &conn_hash, tcp_table);
+ knot_tcp_conn_t *conn = *pconn;
+ bool seq_ack_match = check_seq_ack(msg, conn);
+ if (seq_ack_match) {
+ assert(conn->mss != 0);
+ conn_update(conn, msg);
+
+ rem_align_pointers(conn, tcp_table);
+ rem_node(tcp_conn_node(conn));
+ add_tail(tcp_table_timeout(tcp_table), tcp_conn_node(conn));
+
+ if (msg->flags & KNOT_XDP_MSG_ACK) {
+ conn->acked = msg->ackno;
+ knot_tcp_outbufs_ack(&conn->outbufs, msg->ackno, &tcp_table->outbufs_total);
}
+ }
- relay->msg = msg;
- relay->conn = conn;
+ relay->msg = msg;
+ relay->conn = conn;
- // process incoming data
- if (seq_ack_match && (msg->flags & KNOT_XDP_MSG_ACK) && msg->payload.iov_len > 0) {
- if (!(ignore & XDP_TCP_IGNORE_DATA_ACK)) {
- relay->auto_answer = KNOT_XDP_MSG_ACK;
- }
- ret = knot_tcp_inbufs_upd(&conn->inbuf, msg->payload, false,
- &relay->inbf, &tcp_table->inbufs_total);
- if (ret != KNOT_EOK) {
- break;
- }
- if (conn->inbuf.iov_len > 0 && tcp_table->next_ibuf == NULL) {
- tcp_table->next_ibuf = conn;
- }
+ // process incoming data
+ if (seq_ack_match && (msg->flags & KNOT_XDP_MSG_ACK) && msg->payload.iov_len > 0) {
+ if (!(ignore & XDP_TCP_IGNORE_DATA_ACK)) {
+ relay->auto_answer = KNOT_XDP_MSG_ACK;
+ }
+ ret = knot_tcp_inbufs_upd(&conn->inbuf, msg->payload, false,
+ &relay->inbf, &tcp_table->inbufs_total);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ if (conn->inbuf.iov_len > 0 && tcp_table->next_ibuf == NULL) {
+ tcp_table->next_ibuf = conn;
}
+ }
- // process TCP connection state
- switch (msg->flags & (KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK |
- KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_RST)) {
- case KNOT_XDP_MSG_SYN:
- case (KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK):
- if (conn == NULL) {
- bool synack = (msg->flags & KNOT_XDP_MSG_ACK);
-
- knot_tcp_table_t *add_table = tcp_table;
- if (syn_table != NULL) {
- if (synack) {
- break; // creating conn based on SYN+ACK is only for kxdpgun, disallow in knotd
- }
- add_table = syn_table;
- if (*tcp_table_lookup(&msg->ip_from, &msg->ip_to, &conn_hash, syn_table) != NULL) {
- break;
- }
- }
+ // process TCP connection state
+ switch (msg->flags & (KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK |
+ KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_RST)) {
+ case KNOT_XDP_MSG_SYN:
+ case (KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK):
+ if (conn == NULL) {
+ bool synack = (msg->flags & KNOT_XDP_MSG_ACK);
- ret = tcp_table_add(msg, conn_hash, add_table, &relay->conn);
- if (ret == KNOT_EOK) {
- relay->action = synack ? XDP_TCP_ESTABLISH : XDP_TCP_SYN;
- if (!(ignore & XDP_TCP_IGNORE_ESTABLISH)) {
- relay->auto_answer = synack ? KNOT_XDP_MSG_ACK : (KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK);
- }
-
- conn = relay->conn;
- conn->state = synack ? XDP_TCP_NORMAL: XDP_TCP_ESTABLISHING;
- conn->mss = MAX(msg->mss, 536); // minimal MSS, most importantly not zero!
- conn->window_scale = msg->win_scale;
- conn_update(conn, msg);
- if (!synack) {
- conn->acked = dnssec_random_uint32_t();
- conn->ackno = conn->acked;
- }
+ knot_tcp_table_t *add_table = tcp_table;
+ if (syn_table != NULL) {
+ if (synack) {
+ break; // creating conn based on SYN+ACK is only for kxdpgun, disallow in knotd
+ }
+ add_table = syn_table;
+ if (*tcp_table_lookup(&msg->ip_from, &msg->ip_to, &conn_hash, syn_table) != NULL) {
+ break;
}
- } else {
- relay->auto_answer = KNOT_XDP_MSG_ACK;
}
- break;
- case KNOT_XDP_MSG_ACK:
- if (!seq_ack_match) {
- if (syn_table != NULL && msg->payload.iov_len == 0 && conn == NULL &&
- (pconn = tcp_table_lookup(&msg->ip_from, &msg->ip_to, &conn_hash, syn_table)) != NULL &&
- (conn = *pconn) != NULL && check_seq_ack(msg, conn)) {
- // move conn from syn_table to tcp_table
- tcp_table_remove(pconn, syn_table);
- tcp_table_insert(conn, conn_hash, tcp_table);
- relay->conn = conn;
- relay->action = XDP_TCP_ESTABLISH;
- conn->state = XDP_TCP_NORMAL;
- conn_update(conn, msg);
+
+ ret = tcp_table_add(msg, conn_hash, add_table, &relay->conn);
+ if (ret == KNOT_EOK) {
+ relay->action = synack ? XDP_TCP_ESTABLISH : XDP_TCP_SYN;
+ if (!(ignore & XDP_TCP_IGNORE_ESTABLISH)) {
+ relay->auto_answer = synack ? KNOT_XDP_MSG_ACK : (KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK);
}
- } else {
- switch (conn->state) {
- case XDP_TCP_NORMAL:
- case XDP_TCP_CLOSING1: // just a mess, ignore
- break;
- case XDP_TCP_ESTABLISHING:
- conn->state = XDP_TCP_NORMAL;
- relay->action = XDP_TCP_ESTABLISH;
- break;
- case XDP_TCP_CLOSING2:
- if (msg->payload.iov_len == 0) { // otherwise ignore close
- tcp_table_remove(pconn, tcp_table);
- relay->answer = XDP_TCP_FREE;
- }
- break;
+
+ conn = relay->conn;
+ conn->state = synack ? XDP_TCP_NORMAL: XDP_TCP_ESTABLISHING;
+ conn->mss = MAX(msg->mss, 536); // minimal MSS, most importantly not zero!
+ conn->window_scale = msg->win_scale;
+ conn_update(conn, msg);
+ if (!synack) {
+ conn->acked = dnssec_random_uint32_t();
+ conn->ackno = conn->acked;
}
}
- break;
- case (KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_ACK):
- if (ignore & XDP_TCP_IGNORE_FIN) {
- break;
+ } else {
+ relay->auto_answer = KNOT_XDP_MSG_ACK;
+ }
+ break;
+ case KNOT_XDP_MSG_ACK:
+ if (!seq_ack_match) {
+ if (syn_table != NULL && msg->payload.iov_len == 0 && conn == NULL &&
+ (pconn = tcp_table_lookup(&msg->ip_from, &msg->ip_to, &conn_hash, syn_table)) != NULL &&
+ (conn = *pconn) != NULL && check_seq_ack(msg, conn)) {
+ // move conn from syn_table to tcp_table
+ tcp_table_remove(pconn, syn_table);
+ tcp_table_insert(conn, conn_hash, tcp_table);
+ relay->conn = conn;
+ relay->action = XDP_TCP_ESTABLISH;
+ conn->state = XDP_TCP_NORMAL;
+ conn_update(conn, msg);
}
- if (!seq_ack_match) {
- if (conn != NULL) {
- relay->auto_answer = KNOT_XDP_MSG_RST;
- relay->auto_seqno = msg->ackno;
- } // else ignore. It would be better and possible, but no big value for the price of CPU.
- } else {
- if (conn->state == XDP_TCP_CLOSING1) {
- relay->action = XDP_TCP_CLOSE;
- relay->auto_answer = KNOT_XDP_MSG_ACK;
- relay->answer = XDP_TCP_FREE;
+ } else {
+ switch (conn->state) {
+ case XDP_TCP_NORMAL:
+ case XDP_TCP_CLOSING1: // just a mess, ignore
+ break;
+ case XDP_TCP_ESTABLISHING:
+ conn->state = XDP_TCP_NORMAL;
+ relay->action = XDP_TCP_ESTABLISH;
+ break;
+ case XDP_TCP_CLOSING2:
+ if (msg->payload.iov_len == 0) { // otherwise ignore close
tcp_table_remove(pconn, tcp_table);
- } else if (msg->payload.iov_len == 0) { // otherwise ignore FIN
- relay->action = XDP_TCP_CLOSE;
- relay->auto_answer = KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_ACK;
- conn->state = XDP_TCP_CLOSING2;
+ relay->answer = XDP_TCP_FREE;
}
+ break;
}
+ }
+ break;
+ case (KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_ACK):
+ if (ignore & XDP_TCP_IGNORE_FIN) {
break;
- case KNOT_XDP_MSG_RST:
- if (conn != NULL && msg->seqno == conn->seqno) {
- relay->action = XDP_TCP_RESET;
- tcp_table_remove(pconn, tcp_table);
- relay->answer = XDP_TCP_FREE;
- } else if (conn != NULL) {
+ }
+ if (!seq_ack_match) {
+ if (conn != NULL) {
+ relay->auto_answer = KNOT_XDP_MSG_RST;
+ relay->auto_seqno = msg->ackno;
+ } // else ignore. It would be better and possible, but no big value for the price of CPU.
+ } else {
+ if (conn->state == XDP_TCP_CLOSING1) {
+ relay->action = XDP_TCP_CLOSE;
relay->auto_answer = KNOT_XDP_MSG_ACK;
+ relay->answer = XDP_TCP_FREE;
+ tcp_table_remove(pconn, tcp_table);
+ } else if (msg->payload.iov_len == 0) { // otherwise ignore FIN
+ relay->action = XDP_TCP_CLOSE;
+ relay->auto_answer = KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_ACK;
+ conn->state = XDP_TCP_CLOSING2;
}
- break;
- default:
- break;
}
-
- if (!knot_tcp_relay_empty(relay)) {
- relay++;
+ break;
+ case KNOT_XDP_MSG_RST:
+ if (conn != NULL && msg->seqno == conn->seqno) {
+ relay->action = XDP_TCP_RESET;
+ tcp_table_remove(pconn, tcp_table);
+ relay->answer = XDP_TCP_FREE;
+ } else if (conn != NULL) {
+ relay->auto_answer = KNOT_XDP_MSG_ACK;
}
+ break;
+ default:
+ break;
}
return ret;
diff --git a/src/libknot/xdp/tcp.h b/src/libknot/xdp/tcp.h
index 09fe652..39a30fd 100644
--- a/src/libknot/xdp/tcp.h
+++ b/src/libknot/xdp/tcp.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -148,18 +148,19 @@ knot_tcp_table_t *knot_tcp_table_new(size_t size, knot_tcp_table_t *secret_share
void knot_tcp_table_free(knot_tcp_table_t *table);
/*!
- * \brief Process received packets, prepare automatic responses (e.g. ACK), pick incoming data.
+ * \brief Process received packet, prepare automatic response (e.g. ACK), pick incoming data.
*
- * \param relays Out: relays to be filled with message/connection details.
- * \param msgs Packets received by knot_xdp_recv().
- * \param msg_count Number of received packets.
+ * \param relay Out: relay to be filled with message/connection details.
+ * \param msg Packet received by knot_xdp_recv().
* \param tcp_table Table of TCP connections.
* \param syn_table Optional: extra table for handling partially established connections.
* \param ignore Ignore specific TCP packets indication.
*
+ * \note resulting relay might be knot_tcp_relay_empty()
+ *
* \return KNOT_E*
*/
-int knot_tcp_recv(knot_tcp_relay_t *relays, knot_xdp_msg_t msgs[], uint32_t msg_count,
+int knot_tcp_recv(knot_tcp_relay_t *relay, knot_xdp_msg_t *msg,
knot_tcp_table_t *tcp_table, knot_tcp_table_t *syn_table,
knot_tcp_ignore_t ignore);
diff --git a/src/libknot/xdp/xdp.c b/src/libknot/xdp/xdp.c
index 8286884..132f5c4 100644
--- a/src/libknot/xdp/xdp.c
+++ b/src/libknot/xdp/xdp.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -38,63 +38,68 @@
#include "contrib/net.h"
#define FRAME_SIZE 2048
-
-#define FRAME_COUNT_TX 2048
-#define FRAME_COUNT_RX 2048
-#define FRAME_COUNT (FRAME_COUNT_TX + FRAME_COUNT_RX)
-
-#define RING_LEN_TX FRAME_COUNT_TX
-#define RING_LEN_CQ FRAME_COUNT_TX
-#define RING_LEN_RX FRAME_COUNT_RX
-/* It's recommended that the FQ ring size >= HW RX ring size + AF_XDP RX ring size. */
-#define RING_LEN_FQ (2 * FRAME_COUNT_RX)
-
-#define ALLOC_RETRY_NUM 15
-#define ALLOC_RETRY_DELAY 20 // In nanoseconds.
-
-/* With recent compilers we statically check #defines for settings that
- * get refused by AF_XDP drivers (in current versions, at least). */
-#if (__STDC_VERSION__ >= 201112L)
-#define IS_POWER_OF_2(n) (((n) & (n - 1)) == 0)
-_Static_assert((FRAME_SIZE == 4096 || FRAME_SIZE == 2048)
- && IS_POWER_OF_2(RING_LEN_TX) && IS_POWER_OF_2(RING_LEN_RX)
- && IS_POWER_OF_2(RING_LEN_CQ) && IS_POWER_OF_2(RING_LEN_FQ)
- && FRAME_COUNT_TX <= (1 << 16) /* see tx_free_indices */
- , "Incorrect #define combination for AF_XDP.");
-#endif
+#define DEFAULT_RING_SIZE 2048
+#define RETRY_DELAY 20 // In nanoseconds.
struct umem_frame {
uint8_t bytes[FRAME_SIZE];
};
-static int configure_xsk_umem(struct kxsk_umem **out_umem, bool extra_frames)
+static bool valid_config(const knot_xdp_config_t *config)
+{
+ if (FRAME_SIZE != 2048 && FRAME_SIZE != 4096) {
+ return false;
+ }
+
+ if (config == NULL) {
+ return true;
+ }
+
+ if ((config->ring_size & (config->ring_size - 1)) != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static uint32_t ring_size(const knot_xdp_config_t *config)
+{
+ return config != NULL ? config->ring_size : DEFAULT_RING_SIZE;
+}
+
+static int configure_xsk_umem(struct kxsk_umem **out_umem, uint32_t ring_size)
{
/* Allocate memory and call driver to create the UMEM. */
struct kxsk_umem *umem = calloc(1,
offsetof(struct kxsk_umem, tx_free_indices)
- + sizeof(umem->tx_free_indices[0]) * FRAME_COUNT_TX);
+ + sizeof(umem->tx_free_indices[0]) * ring_size);
if (umem == NULL) {
return KNOT_ENOMEM;
}
+ umem->ring_size = ring_size;
- size_t frame_count = FRAME_COUNT + (extra_frames ? FRAME_COUNT_RX : 0);
+ /* It's recommended that the FQ ring size >= HW RX ring size + AF_XDP RX ring size.
+ * However, the performance is better if FQ size == AF_XDP RX size. */
+ const uint32_t FQ_SIZE = umem->ring_size;
+ const uint32_t CQ_SIZE = umem->ring_size;
+ const uint32_t FRAMES = FQ_SIZE + CQ_SIZE;
int ret = posix_memalign((void **)&umem->frames, getpagesize(),
- FRAME_SIZE * frame_count);
+ FRAME_SIZE * FRAMES);
if (ret != 0) {
free(umem);
return KNOT_ENOMEM;
}
- const struct xsk_umem_config config = {
- .fill_size = RING_LEN_FQ,
- .comp_size = RING_LEN_CQ,
+ const struct xsk_umem_config umem_config = {
+ .fill_size = FQ_SIZE,
+ .comp_size = CQ_SIZE,
.frame_size = FRAME_SIZE,
.frame_headroom = KNOT_XDP_PKT_ALIGNMENT,
};
- ret = xsk_umem__create(&umem->umem, umem->frames, FRAME_SIZE * frame_count,
- &umem->fq, &umem->cq, &config);
+ ret = xsk_umem__create(&umem->umem, umem->frames, FRAME_SIZE * FRAMES,
+ &umem->fq, &umem->cq, &umem_config);
if (ret != KNOT_EOK) {
free(umem->frames);
free(umem);
@@ -103,23 +108,23 @@ static int configure_xsk_umem(struct kxsk_umem **out_umem, bool extra_frames)
*out_umem = umem;
/* Designate the starting chunk of buffers for TX, and put them onto the stack. */
- umem->tx_free_count = FRAME_COUNT_TX;
- for (uint32_t i = 0; i < FRAME_COUNT_TX; ++i) {
+ umem->tx_free_count = CQ_SIZE;
+ for (uint32_t i = 0; i < CQ_SIZE; ++i) {
umem->tx_free_indices[i] = i;
}
/* Designate the rest of buffers for RX, and pass them to the driver. */
uint32_t idx = 0;
- ret = xsk_ring_prod__reserve(&umem->fq, frame_count - FRAME_COUNT_TX, &idx);
- if (ret != frame_count - FRAME_COUNT_TX) {
+ ret = xsk_ring_prod__reserve(&umem->fq, FQ_SIZE, &idx);
+ if (ret != FQ_SIZE) {
assert(0);
return KNOT_ERROR;
}
assert(idx == 0);
- for (uint32_t i = FRAME_COUNT_TX; i < frame_count; ++i) {
+ for (uint32_t i = CQ_SIZE; i < CQ_SIZE + FQ_SIZE; ++i) {
*xsk_ring_prod__fill_addr(&umem->fq, idx++) = i * FRAME_SIZE;
}
- xsk_ring_prod__submit(&umem->fq, frame_count - FRAME_COUNT_TX);
+ xsk_ring_prod__submit(&umem->fq, FQ_SIZE);
return KNOT_EOK;
}
@@ -131,6 +136,33 @@ static void deconfigure_xsk_umem(struct kxsk_umem *umem)
free(umem);
}
+static int enable_busypoll(int socket, unsigned timeout_us, unsigned budget)
+{
+#if defined(SO_PREFER_BUSY_POLL) && defined(SO_BUSY_POLL_BUDGET)
+ int opt_val = 1;
+ if (setsockopt(socket, SOL_SOCKET, SO_PREFER_BUSY_POLL,
+ &opt_val, sizeof(opt_val)) != 0) {
+ return knot_map_errno();
+ }
+
+ opt_val = timeout_us;
+ if (setsockopt(socket, SOL_SOCKET, SO_BUSY_POLL,
+ &opt_val, sizeof(opt_val)) != 0) {
+ return knot_map_errno();
+ }
+
+ opt_val = budget;
+ if (setsockopt(socket, SOL_SOCKET, SO_BUSY_POLL_BUDGET,
+ &opt_val, sizeof(opt_val)) != 0) {
+ return knot_map_errno();
+ }
+
+ return KNOT_EOK;
+#else
+ return KNOT_ENOTSUP;
+#endif
+}
+
static int configure_xsk_socket(struct kxsk_umem *umem,
const struct kxsk_iface *iface,
knot_xdp_socket_t **out_sock,
@@ -143,14 +175,14 @@ static int configure_xsk_socket(struct kxsk_umem *umem,
xsk_info->iface = iface;
xsk_info->umem = umem;
- uint16_t bind_flags = 0;
+ uint16_t bind_flags = XDP_USE_NEED_WAKEUP;
if (config != NULL && config->force_copy) {
bind_flags |= XDP_COPY;
}
const struct xsk_socket_config sock_conf = {
- .tx_size = RING_LEN_TX,
- .rx_size = RING_LEN_RX,
+ .tx_size = umem->ring_size,
+ .rx_size = umem->ring_size,
.libbpf_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD,
.bind_flags = bind_flags,
};
@@ -163,6 +195,17 @@ static int configure_xsk_socket(struct kxsk_umem *umem,
return ret;
}
+ if (config != NULL && config->busy_poll_budget > 0) {
+ ret = enable_busypoll(xsk_socket__fd(xsk_info->xsk),
+ config->busy_poll_timeout, config->busy_poll_budget);
+ if (ret != KNOT_EOK) {
+ xsk_socket__delete(xsk_info->xsk);
+ free(xsk_info);
+ return ret;
+ }
+ xsk_info->busy_poll = true;
+ }
+
*out_sock = xsk_info;
return KNOT_EOK;
}
@@ -172,7 +215,7 @@ int knot_xdp_init(knot_xdp_socket_t **socket, const char *if_name, int if_queue,
knot_xdp_filter_flag_t flags, uint16_t udp_port, uint16_t quic_port,
knot_xdp_load_bpf_t load_bpf, const knot_xdp_config_t *xdp_config)
{
- if (socket == NULL || if_name == NULL ||
+ if (socket == NULL || if_name == NULL || !valid_config(xdp_config) ||
(udp_port == quic_port && (flags & KNOT_XDP_FILTER_UDP) && (flags & KNOT_XDP_FILTER_QUIC)) ||
(flags & (KNOT_XDP_FILTER_UDP | KNOT_XDP_FILTER_TCP | KNOT_XDP_FILTER_QUIC)) == 0) {
return KNOT_EINVAL;
@@ -187,7 +230,7 @@ int knot_xdp_init(knot_xdp_socket_t **socket, const char *if_name, int if_queue,
/* Initialize shared packet_buffer for umem usage. */
struct kxsk_umem *umem = NULL;
- ret = configure_xsk_umem(&umem, xdp_config->extra_frames);
+ ret = configure_xsk_umem(&umem, ring_size(xdp_config));
if (ret != KNOT_EOK) {
kxsk_iface_free(iface);
return ret;
@@ -266,7 +309,7 @@ static void tx_free_relative(struct kxsk_umem *umem, uint64_t addr_relative)
{
/* The address may not point to *start* of buffer, but `/` solves that. */
uint64_t index = addr_relative / FRAME_SIZE;
- assert(index < FRAME_COUNT);
+ assert(index < umem->ring_size);
umem->tx_free_indices[umem->tx_free_count++] = index;
}
@@ -285,7 +328,7 @@ void knot_xdp_send_prepare(knot_xdp_socket_t *socket)
if (completed == 0) {
return;
}
- assert(umem->tx_free_count + completed <= FRAME_COUNT_TX);
+ assert(umem->tx_free_count + completed <= umem->ring_size);
for (uint32_t i = 0; i < completed; ++i) {
uint64_t addr_relative = *xsk_ring_cons__comp_addr(cq, idx++);
@@ -301,12 +344,13 @@ static struct umem_frame *alloc_tx_frame(knot_xdp_socket_t *socket)
return malloc(sizeof(struct umem_frame));
}
- const struct timespec delay = { .tv_nsec = ALLOC_RETRY_DELAY };
struct kxsk_umem *umem = socket->umem;
- for (int i = 0; unlikely(umem->tx_free_count == 0); i++) {
- if (i == ALLOC_RETRY_NUM) {
- return NULL;
+ const struct timespec delay = { .tv_nsec = RETRY_DELAY };
+ while (unlikely(umem->tx_free_count == 0)) {
+ if (socket->busy_poll || xsk_ring_prod__needs_wakeup(&socket->tx)) {
+ (void)sendto(xsk_socket__fd(socket->xsk), NULL, 0,
+ MSG_DONTWAIT, NULL, 0);
}
nanosleep(&delay, NULL);
knot_xdp_send_prepare(socket);
@@ -381,9 +425,7 @@ int knot_xdp_send(knot_xdp_socket_t *socket, const knot_xdp_msg_t msgs[],
}
if (unlikely(socket->send_mock != NULL)) {
int ret = socket->send_mock(socket, msgs, count, sent);
- for (uint32_t i = 0; i < count; ++i) {
- free_unsent(socket, &msgs[i]);
- }
+ knot_xdp_send_free(socket, msgs, count);
return ret;
}
@@ -393,12 +435,13 @@ int knot_xdp_send(knot_xdp_socket_t *socket, const knot_xdp_msg_t msgs[],
* and the API doesn't allow "cancelling reservations".
* Therefore we handle `socket->tx.cached_prod` by hand.
*/
- if (xsk_prod_nb_free(&socket->tx, count) < count) {
- /* This situation was sometimes observed in the emulated XDP mode. */
- for (uint32_t i = 0; i < count; ++i) {
- free_unsent(socket, &msgs[i]);
+ const struct timespec delay = { .tv_nsec = RETRY_DELAY };
+ while (unlikely(xsk_prod_nb_free(&socket->tx, count) < count)) {
+ if (socket->busy_poll || xsk_ring_prod__needs_wakeup(&socket->tx)) {
+ (void)sendto(xsk_socket__fd(socket->xsk), NULL, 0,
+ MSG_DONTWAIT, NULL, 0);
}
- return KNOT_ENOBUFS;
+ nanosleep(&delay, NULL);
}
uint32_t idx = socket->tx.cached_prod;
@@ -425,7 +468,6 @@ int knot_xdp_send(knot_xdp_socket_t *socket, const knot_xdp_msg_t msgs[],
assert(*sent <= count);
socket->tx.cached_prod = idx;
xsk_ring_prod__submit(&socket->tx, *sent);
- socket->kernel_needs_wakeup = true;
return KNOT_EOK;
}
@@ -446,34 +488,19 @@ int knot_xdp_send_finish(knot_xdp_socket_t *socket)
return KNOT_EINVAL;
}
- /* Trigger sending queued packets. */
- if (!socket->kernel_needs_wakeup) {
+ if (!socket->busy_poll && !xsk_ring_prod__needs_wakeup(&socket->tx)) {
return KNOT_EOK;
}
int ret = sendto(xsk_socket__fd(socket->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0);
- const bool is_ok = (ret >= 0);
- // List of "safe" errors taken from
- // https://github.com/torvalds/linux/blame/master/samples/bpf/xdpsock_user.c
- const bool is_again = !is_ok && (errno == ENOBUFS || errno == EAGAIN
- || errno == EBUSY || errno == ENETDOWN);
- // Some of the !is_ok cases are a little unclear - what to do about the syscall,
- // including how caller of _sendmsg_finish() should react.
- if (is_ok || !is_again) {
- socket->kernel_needs_wakeup = false;
- }
- if (is_again) {
- return KNOT_EAGAIN;
- } else if (is_ok) {
+ if (ret >= 0) {
return KNOT_EOK;
+ } else if (errno == ENOBUFS || errno == EAGAIN || errno == EBUSY ||
+ errno == ENETDOWN) {
+ return KNOT_EAGAIN;
} else {
return -errno;
}
- /* This syscall might be avoided with a newer kernel feature (>= 5.4):
- https://www.kernel.org/doc/html/latest/networking/af_xdp.html#xdp-use-need-wakeup-bind-flag
- Unfortunately it's not easy to continue supporting older kernels
- when using this feature on newer ones.
- */
}
_public_
@@ -518,7 +545,7 @@ int knot_xdp_recv(knot_xdp_socket_t *socket, knot_xdp_msg_t msgs[],
static uint8_t *msg_uframe_ptr(const knot_xdp_msg_t *msg)
{
- return NULL + ((msg->payload.iov_base - NULL) & ~(FRAME_SIZE - 1));
+ return (uint8_t *)((uintptr_t)msg->payload.iov_base & ~(FRAME_SIZE - 1));
}
_public_
@@ -529,30 +556,32 @@ void knot_xdp_recv_finish(knot_xdp_socket_t *socket, const knot_xdp_msg_t msgs[]
return;
}
- const struct timespec delay = { .tv_nsec = ALLOC_RETRY_DELAY };
-
struct kxsk_umem *const umem = socket->umem;
struct xsk_ring_prod *const fq = &umem->fq;
uint32_t idx = 0;
- uint32_t reserved = xsk_ring_prod__reserve(fq, count, &idx);
- for (int i = 0; unlikely(reserved < count); i++) {
- if (i == ALLOC_RETRY_NUM) {
- return;
+ const struct timespec delay = { .tv_nsec = RETRY_DELAY };
+ while (unlikely(xsk_ring_prod__reserve(fq, count, &idx) != count)) {
+ if (socket->busy_poll || xsk_ring_prod__needs_wakeup(fq)) {
+ (void)recvfrom(xsk_socket__fd(socket->xsk), NULL, 0,
+ MSG_DONTWAIT, NULL, NULL);
}
nanosleep(&delay, NULL);
- reserved = xsk_ring_prod__reserve(fq, count, &idx);
}
- for (uint32_t i = 0; i < reserved; ++i) {
+ for (uint32_t i = 0; i < count; ++i) {
uint8_t *uframe_p = msg_uframe_ptr(&msgs[i]);
uint64_t offset = uframe_p - umem->frames->bytes;
*xsk_ring_prod__fill_addr(fq, idx++) = offset;
}
- xsk_ring_prod__submit(fq, reserved);
+ xsk_ring_prod__submit(fq, count);
+ // recvfrom() here slightly worsens the performance, poll is called later anyway.
}
+// The number of busy frames
+#define RING_BUSY(ring) ((*(ring)->producer - *(ring)->consumer) & (ring)->mask)
+
_public_
void knot_xdp_socket_info(const knot_xdp_socket_t *socket, FILE *file)
{
@@ -560,10 +589,6 @@ void knot_xdp_socket_info(const knot_xdp_socket_t *socket, FILE *file)
return;
}
- // The number of busy frames
- #define RING_BUSY(ring) \
- ((*(ring)->producer - *(ring)->consumer) & (ring)->mask)
-
#define RING_PRINFO(name, ring) \
fprintf(file, "Ring %s: size %4d, busy %4d (prod %4d, cons %4d)\n", \
name, (unsigned)(ring)->size, \
@@ -571,11 +596,11 @@ void knot_xdp_socket_info(const knot_xdp_socket_t *socket, FILE *file)
(unsigned)*(ring)->producer, (unsigned)*(ring)->consumer)
const int rx_busyf = RING_BUSY(&socket->umem->fq) + RING_BUSY(&socket->rx);
- fprintf(file, "\nLOST RX frames: %4d", (int)(FRAME_COUNT_RX - rx_busyf));
+ fprintf(file, "\nLOST RX frames: %4d", (int)(socket->umem->ring_size - rx_busyf));
const int tx_busyf = RING_BUSY(&socket->umem->cq) + RING_BUSY(&socket->tx);
const int tx_freef = socket->umem->tx_free_count;
- fprintf(file, "\nLOST TX frames: %4d\n", (int)(FRAME_COUNT_TX - tx_busyf - tx_freef));
+ fprintf(file, "\nLOST TX frames: %4d\n", (int)(socket->umem->ring_size - tx_busyf - tx_freef));
RING_PRINFO("FQ", &socket->umem->fq);
RING_PRINFO("RX", &socket->rx);
@@ -583,3 +608,39 @@ void knot_xdp_socket_info(const knot_xdp_socket_t *socket, FILE *file)
RING_PRINFO("CQ", &socket->umem->cq);
fprintf(file, "TX free frames: %4d\n", tx_freef);
}
+
+_public_
+int knot_xdp_socket_stats(knot_xdp_socket_t *socket, knot_xdp_stats_t *stats)
+{
+ if (socket == NULL || stats == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ memset(stats, 0, sizeof(*stats));
+
+ stats->if_name = socket->iface->if_name;
+ stats->if_index = socket->iface->if_index;
+ stats->if_queue = socket->iface->if_queue;
+
+ struct xdp_statistics xdp_stats;
+ socklen_t optlen = sizeof(xdp_stats);
+
+ int fd = knot_xdp_socket_fd(socket);
+ int ret = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &xdp_stats, &optlen);
+ if (ret != 0) {
+ return knot_map_errno();
+ } else if (optlen != sizeof(xdp_stats)) {
+ return KNOT_EINVAL;
+ }
+
+ size_t common_size = MIN(sizeof(xdp_stats), sizeof(stats->socket));
+ memcpy(&stats->socket, &xdp_stats, common_size);
+
+ stats->rings.tx_busy = socket->umem->ring_size - socket->umem->tx_free_count;
+ stats->rings.fq_fill = RING_BUSY(&socket->umem->fq);
+ stats->rings.rx_fill = RING_BUSY(&socket->rx);
+ stats->rings.tx_fill = RING_BUSY(&socket->tx);
+ stats->rings.cq_fill = RING_BUSY(&socket->umem->cq);
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/xdp/xdp.h b/src/libknot/xdp/xdp.h
index 6c8bb1e..5944d44 100644
--- a/src/libknot/xdp/xdp.h
+++ b/src/libknot/xdp/xdp.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -53,14 +53,54 @@ typedef struct knot_xdp_socket knot_xdp_socket_t;
/*! \brief Configuration of XDP socket. */
struct knot_xdp_config {
- bool force_generic; /*!< Use generic XDP mode (avoid driver/hadrware implementation). */
- bool force_copy; /*!< Force copying packet data between kernel and user-space (avoid zero-copy). */
- bool extra_frames; /*!< Extra FQ frames. */
+ uint16_t ring_size; /*!< Size of RX and TX rings (must be power of 2). */
+ bool force_generic; /*!< Use generic XDP mode (avoid driver/hardware implementation). */
+ bool force_copy; /*!< Force copying packet data between kernel and user-space (avoid zero-copy). */
+ unsigned busy_poll_timeout; /*!< Preferred busy poll budget (0 means disabled). */
+ unsigned busy_poll_budget; /*!< Preferred busy poll timeout (in microseconds) . */
};
/*! \brief Configuration of XDP socket. */
typedef struct knot_xdp_config knot_xdp_config_t;
+/*! \brief Various statistics of an XDP socket (optimally kernel >=5.9). */
+typedef struct {
+ /*! Interface name. */
+ const char *if_name;
+ /*! Interface name index (derived from ifname). */
+ int if_index;
+ /*! Network card queue id. */
+ unsigned if_queue;
+ /*! Counters (xdp_statistics) retrieved from the kernel via XDP_STATISTICS. */
+ struct {
+ /*! Dropped for other reasons. */
+ uint64_t rx_dropped;
+ /*! Dropped due to invalid descriptor. */
+ uint64_t rx_invalid;
+ /*! Dropped due to invalid descriptor. */
+ uint64_t tx_invalid;
+ /*! Dropped due to rx ring being full. */
+ uint64_t rx_full;
+ /*! Failed to retrieve item from fill ring. */
+ uint64_t fq_empty;
+ /*! Failed to retrieve item from tx ring. */
+ uint64_t tx_empty;
+ } socket;
+ /*! States of rings of the XDP socket. */
+ struct {
+ /*! Busy TX buffers. */
+ uint16_t tx_busy;
+ /*! Free buffers to consume from FQ ring. */
+ uint16_t fq_fill;
+ /*! Pending buffers in TX ring. */
+ uint16_t rx_fill;
+ /*! Pending buffers in RX ring. */
+ uint16_t tx_fill;
+ /*! Pending buffers in CQ ring. */
+ uint16_t cq_fill;
+ } rings;
+} knot_xdp_stats_t;
+
/*!
* \brief Initialize XDP socket.
*
@@ -196,4 +236,14 @@ void knot_xdp_recv_finish(knot_xdp_socket_t *socket, const knot_xdp_msg_t msgs[]
*/
void knot_xdp_socket_info(const knot_xdp_socket_t *socket, FILE *file);
+/*!
+ * \brief Gets various statistics of the XDP socket.
+ *
+ * \param socket XDP socket.
+ * \param stats Output structure.
+ *
+ * \return KNOT_E*
+ */
+int knot_xdp_socket_stats(knot_xdp_socket_t *socket, knot_xdp_stats_t *stats);
+
/*! @} */
diff --git a/src/libknot/yparser/ypschema.h b/src/libknot/yparser/ypschema.h
index 57ced72..7fca93e 100644
--- a/src/libknot/yparser/ypschema.h
+++ b/src/libknot/yparser/ypschema.h
@@ -130,6 +130,8 @@ typedef union {
int64_t dflt;
/*! Possible unit type. */
yp_style_t unit;
+ /*! Alternative default value. */
+ int64_t dflt_alt;
} i;
/*! Boolean variables. */
struct {
diff --git a/src/libknot/yparser/yptrafo.c b/src/libknot/yparser/yptrafo.c
index 60b3717..764a5d1 100644
--- a/src/libknot/yparser/yptrafo.c
+++ b/src/libknot/yparser/yptrafo.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -31,25 +31,31 @@
#include "contrib/wire_ctx.h"
enum {
- UNIT_BYTE = 'B',
- UNIT_KILO = 'K',
- UNIT_MEGA = 'M',
- UNIT_GIGA = 'G',
- UNIT_SEC = 's',
- UNIT_MIN = 'm',
- UNIT_HOUR = 'h',
- UNIT_DAY = 'd'
+ UNIT_BYTE = 'B',
+ UNIT_KILO = 'K',
+ UNIT_MEGA = 'M',
+ UNIT_GIGA = 'G',
+ UNIT_SEC = 's',
+ UNIT_MIN = 'm',
+ UNIT_HOUR = 'h',
+ UNIT_DAY = 'd',
+ UNIT_WEEK = 'w',
+ UNIT_MONTH = 'M',
+ UNIT_YEAR = 'y',
};
enum {
- MULTI_BYTE = 1,
- MULTI_KILO = 1024,
- MULTI_MEGA = 1024 * 1024,
- MULTI_GIGA = 1024 * 1024 * 1024,
- MULTI_SEC = 1,
- MULTI_MIN = 60,
- MULTI_HOUR = 3600,
- MULTI_DAY = 24 * 3600
+ MULTI_BYTE = 1,
+ MULTI_KILO = 1024,
+ MULTI_MEGA = 1024 * 1024,
+ MULTI_GIGA = 1024 * 1024 * 1024,
+ MULTI_SEC = 1,
+ MULTI_MIN = 60,
+ MULTI_HOUR = 3600,
+ MULTI_DAY = 24 * 3600,
+ MULTI_WEEK = MULTI_DAY * 7,
+ MULTI_MONTH = MULTI_DAY * 30,
+ MULTI_YEAR = MULTI_DAY * 365,
};
static wire_ctx_t copy_in(
@@ -186,6 +192,15 @@ static int remove_unit(
case UNIT_DAY:
multiplier = MULTI_DAY;
break;
+ case UNIT_WEEK:
+ multiplier = MULTI_WEEK;
+ break;
+ case UNIT_MONTH:
+ multiplier = MULTI_MONTH;
+ break;
+ case UNIT_YEAR:
+ multiplier = MULTI_YEAR;
+ break;
default:
return KNOT_EINVAL;
}
@@ -295,9 +310,18 @@ static void add_unit(
} else if (*number < MULTI_DAY) {
multiplier = MULTI_HOUR;
new_unit = UNIT_HOUR;
- } else {
+ } else if (*number < MULTI_WEEK) {
multiplier = MULTI_DAY;
new_unit = UNIT_DAY;
+ } else if (*number < MULTI_MONTH) {
+ multiplier = MULTI_WEEK;
+ new_unit = UNIT_WEEK;
+ } else if (*number < MULTI_YEAR) {
+ multiplier = MULTI_MONTH;
+ new_unit = UNIT_MONTH;
+ } else {
+ multiplier = MULTI_YEAR;
+ new_unit = UNIT_YEAR;
}
}
diff --git a/src/libzscanner/version.h b/src/libzscanner/version.h
index f24b3a9..05ab471 100644
--- a/src/libzscanner/version.h
+++ b/src/libzscanner/version.h
@@ -17,8 +17,8 @@
#pragma once
#define ZSCANNER_VERSION_MAJOR 3
-#define ZSCANNER_VERSION_MINOR 3
-#define ZSCANNER_VERSION_PATCH 0x09
+#define ZSCANNER_VERSION_MINOR 4
+#define ZSCANNER_VERSION_PATCH 0x00
#define ZSCANNER_VERSION_HEX ((ZSCANNER_VERSION_MAJOR << 16) | \
(ZSCANNER_VERSION_MINOR << 8) | \
diff --git a/src/utils/Makefile.inc b/src/utils/Makefile.inc
index 3097050..1f11282 100644
--- a/src/utils/Makefile.inc
+++ b/src/utils/Makefile.inc
@@ -103,7 +103,11 @@ kxdpgun_SOURCES = \
utils/kxdpgun/ip_route.h \
utils/kxdpgun/load_queries.c \
utils/kxdpgun/load_queries.h \
- utils/kxdpgun/main.c
+ utils/kxdpgun/main.c \
+ utils/kxdpgun/main.h \
+ utils/kxdpgun/stats.c \
+ utils/kxdpgun/stats.h
+
kxdpgun_CPPFLAGS = $(libknotus_la_CPPFLAGS) $(libmnl_CFLAGS)
kxdpgun_LDADD = libknot.la $(libcontrib_LIBS) $(libmnl_LIBS) $(pthread_LIBS)
diff --git a/src/utils/common/msg.h b/src/utils/common/msg.h
index d2ed57e..fbd6c8e 100644
--- a/src/utils/common/msg.h
+++ b/src/utils/common/msg.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,10 +23,10 @@
#define WARNING_ ";; WARNING: "
#define DEBUG_ ";; DEBUG: "
-#define ERR(msg, ...) { fprintf(stderr, ERROR_ msg "\n", ##__VA_ARGS__); fflush(stderr); }
-#define INFO(msg, ...) { fprintf(stdout, INFO_ msg "\n", ##__VA_ARGS__); fflush(stdout); }
-#define WARN(msg, ...) { fprintf(stderr, WARNING_ msg "\n", ##__VA_ARGS__); fflush(stderr); }
-#define DBG(msg, ...) { msg_debug(DEBUG_ msg "\n", ##__VA_ARGS__); fflush(stdout); }
+#define ERR(msg, ...) do { fprintf(stderr, ERROR_ msg "\n", ##__VA_ARGS__); fflush(stderr); } while (0)
+#define INFO(msg, ...) do { fprintf(stdout, INFO_ msg "\n", ##__VA_ARGS__); fflush(stdout); } while (0)
+#define WARN(msg, ...) do { fprintf(stderr, WARNING_ msg "\n", ##__VA_ARGS__); fflush(stderr); } while (0)
+#define DBG(msg, ...) do { msg_debug(DEBUG_ msg "\n", ##__VA_ARGS__); fflush(stdout); } while (0)
/*! \brief Enable/disable debugging. */
int msg_enable_debug(int val);
@@ -37,6 +37,6 @@ int msg_debug(const char *fmt, ...);
/*! \brief Debug message for null input. */
#define DBG_NULL DBG("%s: null parameter", __func__)
-#define ERR2(msg, ...) { fprintf(stderr, "error: " msg "\n", ##__VA_ARGS__); fflush(stderr); }
-#define WARN2(msg, ...) { fprintf(stderr, "warning: " msg "\n", ##__VA_ARGS__); fflush(stderr); }
-#define INFO2(msg, ...) { fprintf(stdout, msg "\n", ##__VA_ARGS__); fflush(stdout); }
+#define ERR2(msg, ...) do { fprintf(stderr, "error: " msg "\n", ##__VA_ARGS__); fflush(stderr); } while (0)
+#define WARN2(msg, ...) do { fprintf(stderr, "warning: " msg "\n", ##__VA_ARGS__); fflush(stderr); } while (0)
+#define INFO2(msg, ...) do { fprintf(stdout, msg "\n", ##__VA_ARGS__); fflush(stdout); } while (0)
diff --git a/src/utils/common/netio.c b/src/utils/common/netio.c
index eed14ee..8ea7b59 100644
--- a/src/utils/common/netio.c
+++ b/src/utils/common/netio.c
@@ -32,6 +32,7 @@
#include "utils/common/msg.h"
#include "utils/common/tls.h"
#include "libknot/libknot.h"
+#include "libknot/quic/tls_common.h"
#include "contrib/net.h"
#include "contrib/proxyv2/proxyv2.h"
#include "contrib/sockaddr.h"
@@ -521,8 +522,8 @@ int net_connect(net_t *net)
#endif //LIBNGHTTP2
{
// Establish TLS connection.
- ret = tls_ctx_setup_remote_endpoint(&net->tls, &dot_alpn, 1, NULL,
- net_get_remote(net));
+ ret = tls_ctx_setup_remote_endpoint(&net->tls, &dot_alpn, 1,
+ KNOT_TLS_PRIORITIES, net_get_remote(net));
if (ret != 0) {
net_close(net);
return ret;
@@ -546,7 +547,7 @@ int net_connect(net_t *net)
return ret;
}
ret = tls_ctx_setup_remote_endpoint(&net->tls,
- &doq_alpn, 1, QUIC_PRIORITY, net_get_remote(net));
+ &doq_alpn, 1, KNOT_TLS_PRIORITIES, net_get_remote(net));
if (ret != 0) {
net_close(net);
return ret;
diff --git a/src/utils/common/params.c b/src/utils/common/params.c
index d16af4c..fe5a854 100644
--- a/src/utils/common/params.c
+++ b/src/utils/common/params.c
@@ -21,7 +21,7 @@
#include <sys/socket.h>
#ifdef LIBIDN
-#include LIBIDN_HEADER
+#include <idn2.h>
#endif
#include "utils/common/params.h"
diff --git a/src/utils/common/params.h b/src/utils/common/params.h
index 8b7565e..bb071aa 100644
--- a/src/utils/common/params.h
+++ b/src/utils/common/params.h
@@ -22,6 +22,7 @@
#include <stdio.h>
#include "libknot/libknot.h"
+#include "contrib/string.h"
#include "contrib/ucw/lists.h"
#define DEFAULT_IPV4_NAME "127.0.0.1"
@@ -31,7 +32,7 @@
#define DEFAULT_DNS_QUIC_PORT "853"
#define DEFAULT_DNS_TLS_PORT "853"
#define DEFAULT_UDP_SIZE 512
-#define DEFAULT_EDNS_SIZE 4096
+#define DEFAULT_EDNS_SIZE 1232
#define MAX_PACKET_SIZE 65535
#define SEP_CHARS "\n\t "
@@ -118,9 +119,15 @@ typedef struct {
param_handle_f handler;
} param_t;
-inline static void print_version(const char *program_name)
+inline static void print_version(const char *prog_name, bool verbose)
{
- printf("%s (Knot DNS), version %s\n", program_name, PACKAGE_VERSION);
+ if (prog_name != NULL) {
+ printf("%s, ", prog_name);
+ }
+ printf("Knot DNS %s\n", PACKAGE_VERSION);
+ if (verbose) {
+ printf("\n%s\n", configure_summary);
+ }
}
/*!
diff --git a/src/utils/common/quic.h b/src/utils/common/quic.h
index fd70d27..2b860c3 100644
--- a/src/utils/common/quic.h
+++ b/src/utils/common/quic.h
@@ -35,10 +35,6 @@ void quic_params_clean(quic_params_t *params);
#include "utils/common/tls.h"
-#define QUIC_DEFAULT_VERSION "-VERS-ALL:+VERS-TLS1.3"
-#define QUIC_DEFAULT_GROUPS "-GROUP-ALL:+GROUP-X25519:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-SECP521R1"
-#define QUIC_PRIORITY "%DISABLE_TLS13_COMPAT_MODE:NORMAL:"QUIC_DEFAULT_VERSION":"QUIC_DEFAULT_GROUPS
-
typedef enum {
CLOSED, // Initialized
CONNECTED, // RTT-0
diff --git a/src/utils/common/tls.c b/src/utils/common/tls.c
index 276ae16..4c9a588 100644
--- a/src/utils/common/tls.c
+++ b/src/utils/common/tls.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -398,7 +398,7 @@ int tls_certificate_verification(tls_ctx_t *ctx)
};
size_t data_count = (ctx->params->hostname != NULL) ? 2 : 1;
if (data_count == 1) {
- WARN("TLS, no hostname provided, will not verify certificate owner")
+ WARN("TLS, no hostname provided, will not verify certificate owner");
}
unsigned int status;
@@ -533,7 +533,8 @@ int tls_ctx_setup_remote_endpoint(tls_ctx_t *ctx, const gnutls_datum_t *alpn,
}
if (priority != NULL) {
- ret = gnutls_priority_set_direct(ctx->session, priority, NULL);
+ ret = gnutls_set_default_priority_append(ctx->session, priority,
+ NULL, 0);
} else {
ret = gnutls_set_default_priority(ctx->session);
}
diff --git a/src/utils/kcatalogprint/main.c b/src/utils/kcatalogprint/main.c
index 0172347..85e50b6 100644
--- a/src/utils/kcatalogprint/main.c
+++ b/src/utils/kcatalogprint/main.c
@@ -108,7 +108,7 @@ int main(int argc, char *argv[])
{ "catalog", required_argument, NULL, 'a' },
{ "member", required_argument, NULL, 'm' },
{ "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
+ { "version", optional_argument, NULL, 'V' },
{ NULL }
};
@@ -116,7 +116,7 @@ int main(int argc, char *argv[])
signal_init_std();
int opt = 0;
- while ((opt = getopt_long(argc, argv, "c:C:D:a:m:hV", opts, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "c:C:D:a:m:hV::", opts, NULL)) != -1) {
switch (opt) {
case 'c':
if (util_conf_init_file(optarg) != KNOT_EOK) {
@@ -147,7 +147,7 @@ int main(int argc, char *argv[])
print_help();
goto success;
case 'V':
- print_version(PROGRAM_NAME);
+ print_version(PROGRAM_NAME, optarg != NULL);
goto success;
default:
print_help();
@@ -155,13 +155,9 @@ int main(int argc, char *argv[])
}
}
- // Backward compatibility.
if (argc - optind > 0) {
- WARN2("obsolete parameter specified");
- if (util_conf_init_justdb("catalog-db", argv[optind]) != KNOT_EOK) {
- goto failure;
- }
- optind++;
+ print_help();
+ goto failure;
}
if (util_conf_init_default(true) != KNOT_EOK) {
diff --git a/src/utils/kdig/kdig_params.c b/src/utils/kdig/kdig_params.c
index c8fd83f..8566848 100644
--- a/src/utils/kdig/kdig_params.c
+++ b/src/utils/kdig/kdig_params.c
@@ -1690,7 +1690,7 @@ query_t *query_create(const char *owner, const query_t *conf)
query->style.style.now = knot_time();
query->idn = true;
query->nsid = false;
- query->edns = -1;
+ query->edns = 0;
query->cc.len = 0;
query->sc.len = 0;
query->badcookie = BADCOOKIE_RETRY_MAX;
@@ -2517,12 +2517,7 @@ static int parse_opt1(const char *opt, const char *value, kdig_params_t *params,
*index += add;
break;
case 'V':
- if (len > 1) {
- ERR("invalid option -%s", opt);
- return KNOT_ENOTSUP;
- }
-
- print_version(PROGRAM_NAME);
+ print_version(PROGRAM_NAME, len > 1);
params->stop = true;
break;
case 'x':
@@ -2598,8 +2593,8 @@ static int parse_opt1(const char *opt, const char *value, kdig_params_t *params,
if (strcmp(opt, "-help") == 0) {
print_help();
params->stop = true;
- } else if (strcmp(opt, "-version") == 0) {
- print_version(PROGRAM_NAME);
+ } else if (strncmp(opt, "-version", 8) == 0) {
+ print_version(PROGRAM_NAME, strlen(opt) > 9);
params->stop = true;
} else {
ERR("invalid option: -%s", opt);
diff --git a/src/utils/keymgr/bind_privkey.c b/src/utils/keymgr/bind_privkey.c
index 9ab895c..bbb61a5 100644
--- a/src/utils/keymgr/bind_privkey.c
+++ b/src/utils/keymgr/bind_privkey.c
@@ -281,9 +281,7 @@ static int rsa_params_to_pem(const bind_privkey_t *params, dnssec_binary_t *pem)
static gnutls_ecc_curve_t choose_ecdsa_curve(size_t pubkey_size)
{
switch (pubkey_size) {
-#ifdef HAVE_ED25519
case 32: return GNUTLS_ECC_CURVE_ED25519;
-#endif
#ifdef HAVE_ED448
case 57: return GNUTLS_ECC_CURVE_ED448;
#endif
@@ -334,7 +332,6 @@ static int ecdsa_params_to_pem(dnssec_key_t *dnskey, const bind_privkey_t *param
return dnssec_pem_from_x509(key, pem);
}
-#if defined(HAVE_ED25519) || defined(HAVE_ED448)
static void eddsa_extract_public_params(dnssec_key_t *key, gnutls_ecc_curve_t *curve,
gnutls_datum_t *x)
{
@@ -371,7 +368,6 @@ static int eddsa_params_to_pem(dnssec_key_t *dnskey, const bind_privkey_t *param
return dnssec_pem_from_x509(key, pem);
}
-#endif
int bind_privkey_to_pem(dnssec_key_t *key, bind_privkey_t *params, dnssec_binary_t *pem)
{
@@ -385,15 +381,11 @@ int bind_privkey_to_pem(dnssec_key_t *key, bind_privkey_t *params, dnssec_binary
case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256:
case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
return ecdsa_params_to_pem(key, params, pem);
-#ifdef HAVE_ED25519
case DNSSEC_KEY_ALGORITHM_ED25519:
-#endif
#ifdef HAVE_ED448
case DNSSEC_KEY_ALGORITHM_ED448:
#endif
-#if defined(HAVE_ED25519) || defined(HAVE_ED448)
return eddsa_params_to_pem(key, params, pem);
-#endif
default:
return DNSSEC_INVALID_KEY_ALGORITHM;
}
diff --git a/src/utils/keymgr/main.c b/src/utils/keymgr/main.c
index b46aaa0..999b5c5 100644
--- a/src/utils/keymgr/main.c
+++ b/src/utils/keymgr/main.c
@@ -333,11 +333,10 @@ int main(int argc, char *argv[])
{ "tsig", required_argument, NULL, 't' },
{ "extended", no_argument, NULL, 'e' },
{ "list", no_argument, NULL, 'l' },
- { "brief", no_argument, NULL, 'b' }, // Legacy.
{ "mono", no_argument, NULL, 'x' },
{ "color", no_argument, NULL, 'X' },
{ "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
+ { "version", optional_argument, NULL, 'V' },
{ "json", no_argument, NULL, 'j' },
{ NULL }
};
@@ -358,7 +357,7 @@ int main(int argc, char *argv[])
list_params.color = isatty(STDOUT_FILENO);
int opt = 0, parm = 0;
- while ((opt = getopt_long(argc, argv, "c:C:D:t:ejlbxXhV", opts, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "c:C:D:t:ejlxXhV::", opts, NULL)) != -1) {
switch (opt) {
case 'c':
if (util_conf_init_file(optarg) != KNOT_EOK) {
@@ -394,9 +393,6 @@ int main(int argc, char *argv[])
case 'l':
just_list = true;
break;
- case 'b':
- WARN2("option '--brief' is deprecated and enabled by default");
- break;
case 'x':
list_params.color = false;
break;
@@ -407,7 +403,7 @@ int main(int argc, char *argv[])
print_help();
goto success;
case 'V':
- print_version(PROGRAM_NAME);
+ print_version(PROGRAM_NAME, optarg != NULL);
goto success;
default:
print_help();
diff --git a/src/utils/keymgr/offline_ksk.c b/src/utils/keymgr/offline_ksk.c
index b4260b9..05b2d2b 100644
--- a/src/utils/keymgr/offline_ksk.c
+++ b/src/utils/keymgr/offline_ksk.c
@@ -37,6 +37,8 @@ static int pregenerate_once(kdnssec_ctx_t *ctx, knot_time_t *next)
{
zone_sign_reschedule_t resch = { 0 };
+ memset(ctx->stats, 0, sizeof(*ctx->stats));
+
// generate ZSKs
int ret = knot_dnssec_key_rollover(ctx, KEY_ROLL_ALLOW_ZSK_ROLL | KEY_ROLL_PRESERVE_FUTURE, &resch);
if (ret != KNOT_EOK) {
@@ -245,6 +247,9 @@ static int ksr_once(kdnssec_ctx_t *ctx, char **buf, size_t *buf_size, knot_time_
{
knot_rrset_t *dnskey = NULL;
zone_keyset_t keyset = { 0 };
+
+ memset(ctx->stats, 0, sizeof(*ctx->stats));
+
int ret = load_dnskey_rrset(ctx, &dnskey, &keyset);
if (ret != KNOT_EOK) {
goto done;
@@ -322,10 +327,10 @@ static int ksr_sign_dnskey(kdnssec_ctx_t *ctx, knot_rrset_t *zsk, knot_time_t no
zone_keyset_t keyset = { 0 };
char *buf = NULL;
size_t buf_size = 4096;
- knot_time_t rrsigs_expire = 0;
ctx->now = now;
ctx->policy->dnskey_ttl = zsk->ttl;
+ memset(ctx->stats, 0, sizeof(*ctx->stats));
knot_timediff_t rrsig_refresh = ctx->policy->rrsig_refresh_before;
if (rrsig_refresh == UINT32_MAX) { // not setting rrsig-refresh prohibited by documentation, but we need to do something
@@ -352,7 +357,7 @@ static int ksr_sign_dnskey(kdnssec_ctx_t *ctx, knot_rrset_t *zsk, knot_time_t no
// no check if the KSK used for signing (in keyset) is contained in DNSKEY record being signed (in KSR) !
for (int i = 0; i < keyset.count; i++) {
- ret = key_records_sign(&keyset.keys[i], &r, ctx, &rrsigs_expire);
+ ret = key_records_sign(&keyset.keys[i], &r, ctx);
if (ret != KNOT_EOK) {
goto done;
}
@@ -362,7 +367,7 @@ static int ksr_sign_dnskey(kdnssec_ctx_t *ctx, knot_rrset_t *zsk, knot_time_t no
print_header("SignedKeyResponse "KSR_SKR_VER, ctx->now, buf);
*next_sign = knot_time_min(
knot_get_next_zone_key_event(&keyset),
- knot_time_add(rrsigs_expire, -rrsig_refresh)
+ knot_time_add(ctx->stats->expire, -rrsig_refresh)
);
}
@@ -446,6 +451,7 @@ static void skr_import_header(zs_scanner_t *sc)
// trailing header without timestamp
next_timestamp = 0;
}
+ knot_time_t validity_ts = next_timestamp != 0 ? next_timestamp : ctx->timestamp;
// delete possibly existing conflicting offline records
ctx->ret = kasp_db_delete_offline_records(
@@ -454,16 +460,11 @@ static void skr_import_header(zs_scanner_t *sc)
// store previous SKR
if (ctx->timestamp > 0 && ctx->ret == KNOT_EOK) {
- ctx->ret = key_records_verify(&ctx->r, ctx->kctx, ctx->timestamp);
+ ctx->ret = key_records_verify(&ctx->r, ctx->kctx, ctx->timestamp, validity_ts);
if (ctx->ret != KNOT_EOK) {
return;
}
- if (next_timestamp > 0) {
- ctx->ret = key_records_verify(&ctx->r, ctx->kctx, next_timestamp - 1);
- if (ctx->ret != KNOT_EOK) {
- return;
- }
- }
+
ctx->ret = kasp_db_store_offline_records(ctx->kctx->kasp_db,
ctx->timestamp, &ctx->r);
key_records_clear_rdatasets(&ctx->r);
@@ -490,20 +491,14 @@ static void skr_validate_header(zs_scanner_t *sc)
// trailing header without timestamp
next_timestamp = 0;
}
+ knot_time_t validity_ts = next_timestamp != 0 ? next_timestamp : ctx->timestamp;
if (ctx->timestamp > 0 && ctx->ret == KNOT_EOK) {
- int ret = key_records_verify(&ctx->r, ctx->kctx, ctx->timestamp);
+ int ret = key_records_verify(&ctx->r, ctx->kctx, ctx->timestamp, validity_ts);
if (ret != KNOT_EOK) { // ctx->ret untouched
ERR2("invalid SignedKeyResponse for %"KNOT_TIME_PRINTF" (%s)",
ctx->timestamp, knot_strerror(ret));
}
- if (next_timestamp > 0) {
- ret = key_records_verify(&ctx->r, ctx->kctx, next_timestamp - 1);
- if (ret != KNOT_EOK) { // ctx->ret untouched
- ERR2("invalid SignedKeyResponse for %"KNOT_TIME_PRINTF" (%s)",
- next_timestamp - 1, knot_strerror(ret));
- }
- }
key_records_clear_rdatasets(&ctx->r);
}
diff --git a/src/utils/khost/khost_params.c b/src/utils/khost/khost_params.c
index 1423e09..de95d9b 100644
--- a/src/utils/khost/khost_params.c
+++ b/src/utils/khost/khost_params.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -251,14 +251,14 @@ int khost_parse(kdig_params_t *params, int argc, char *argv[])
// Long options.
struct option opts[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", optional_argument, NULL, 'V' },
{ NULL }
};
// Command line options processing.
int opt = 0;
- while ((opt = getopt_long(argc, argv, "46adhrsTvVwc:t:R:W:", opts, NULL))
+ while ((opt = getopt_long(argc, argv, "46adhrsTvV::wc:t:R:W:", opts, NULL))
!= -1) {
switch (opt) {
case '4':
@@ -294,7 +294,7 @@ int khost_parse(kdig_params_t *params, int argc, char *argv[])
conf->style.show_footer = true;
break;
case 'V':
- print_version(PROGRAM_NAME);
+ print_version(PROGRAM_NAME, optarg != NULL);
params->stop = false;
return KNOT_EOK;
case 'w':
diff --git a/src/utils/kjournalprint/main.c b/src/utils/kjournalprint/main.c
index 3ba0019..1d633dd 100644
--- a/src/utils/kjournalprint/main.c
+++ b/src/utils/kjournalprint/main.c
@@ -342,11 +342,10 @@ int main(int argc, char *argv[])
{ "zone-list", no_argument, NULL, 'z' },
{ "check", no_argument, NULL, 'H' },
{ "debug", no_argument, NULL, 'd' },
- { "no-color", no_argument, NULL, 'n' },
{ "mono", no_argument, NULL, 'x' },
{ "color", no_argument, NULL, 'X' },
{ "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
+ { "version", optional_argument, NULL, 'V' },
{ NULL }
};
@@ -354,7 +353,7 @@ int main(int argc, char *argv[])
signal_init_std();
int opt = 0;
- while ((opt = getopt_long(argc, argv, "c:C:D:l:s:zHdnxXhV", opts, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "c:C:D:l:s:zHdxXhV::", opts, NULL)) != -1) {
switch (opt) {
case 'c':
if (util_conf_init_file(optarg) != KNOT_EOK) {
@@ -393,7 +392,6 @@ int main(int argc, char *argv[])
case 'd':
params.debug = true;
break;
- case 'n':
case 'x':
params.color = false;
break;
@@ -404,7 +402,7 @@ int main(int argc, char *argv[])
print_help();
goto success;
case 'V':
- print_version(PROGRAM_NAME);
+ print_version(PROGRAM_NAME, optarg != NULL);
goto success;
default:
print_help();
@@ -412,15 +410,6 @@ int main(int argc, char *argv[])
}
}
- // Backward compatibility.
- if ((justlist && (argc - optind > 0)) || (!justlist && (argc - optind > 1))) {
- WARN2("obsolete parameter specified");
- if (util_conf_init_justdb("journal-db", argv[optind]) != KNOT_EOK) {
- goto failure;
- }
- optind++;
- }
-
signal_ctx.color = params.color;
if (util_conf_init_default(true) != KNOT_EOK) {
diff --git a/src/utils/knotc/commands.c b/src/utils/knotc/commands.c
index c2c25a2..5cc1a14 100644
--- a/src/utils/knotc/commands.c
+++ b/src/utils/knotc/commands.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -51,6 +51,7 @@
#define CMD_ZONE_BACKUP "zone-backup"
#define CMD_ZONE_RESTORE "zone-restore"
#define CMD_ZONE_SIGN "zone-sign"
+#define CMD_ZONE_VALIDATE "zone-validate"
#define CMD_ZONE_KEYS_LOAD "zone-keys-load"
#define CMD_ZONE_KEY_ROLL "zone-key-rollover"
#define CMD_ZONE_KSK_SBM "zone-ksk-submitted"
@@ -99,7 +100,7 @@
static int check_args(cmd_args_t *args, int min, int max)
{
if (max == 0 && args->argc > 0) {
- log_error("command doesn't take arguments");
+ log_error("command does not take arguments");
return KNOT_EINVAL;
} else if (min == max && args->argc != min) {
log_error("command requires %i arguments", min);
@@ -272,6 +273,7 @@ static void format_data(cmd_args_t *args, knot_ctl_type_t data_type,
case CTL_ZONE_BACKUP:
case CTL_ZONE_RESTORE:
case CTL_ZONE_SIGN:
+ case CTL_ZONE_VALIDATE:
case CTL_ZONE_KEYS_LOAD:
case CTL_ZONE_KEY_ROLL:
case CTL_ZONE_KSK_SBM:
@@ -411,6 +413,7 @@ static void format_block(ctl_cmd_t cmd, bool failed, bool empty)
case CTL_ZONE_BACKUP:
case CTL_ZONE_RESTORE:
case CTL_ZONE_SIGN:
+ case CTL_ZONE_VALIDATE:
case CTL_ZONE_KEYS_LOAD:
case CTL_ZONE_KEY_ROLL:
case CTL_ZONE_KSK_SBM:
@@ -1294,6 +1297,7 @@ const cmd_desc_t cmd_table[] = {
{ CMD_ZONE_BACKUP, cmd_zone_filter_ctl, CTL_ZONE_BACKUP, CMD_FOPT_ZONE },
{ CMD_ZONE_RESTORE, cmd_zone_filter_ctl, CTL_ZONE_RESTORE, CMD_FOPT_ZONE },
{ CMD_ZONE_SIGN, cmd_zone_ctl, CTL_ZONE_SIGN, CMD_FOPT_ZONE },
+ { CMD_ZONE_VALIDATE, cmd_zone_ctl, CTL_ZONE_VALIDATE, CMD_FOPT_ZONE },
{ CMD_ZONE_KEYS_LOAD, cmd_zone_ctl, CTL_ZONE_KEYS_LOAD, CMD_FOPT_ZONE },
{ CMD_ZONE_KEY_ROLL, cmd_zone_key_roll_ctl, CTL_ZONE_KEY_ROLL, CMD_FREQ_ZONE },
{ CMD_ZONE_KSK_SBM, cmd_zone_ctl, CTL_ZONE_KSK_SBM, CMD_FREQ_ZONE | CMD_FOPT_ZONE },
@@ -1347,6 +1351,7 @@ static const cmd_help_t cmd_help_table[] = {
{ CMD_ZONE_BACKUP, "[<zone>...] [<filter>...] +backupdir <dir>", "Backup zone data and metadata. (#)" },
{ CMD_ZONE_RESTORE, "[<zone>...] [<filter>...] +backupdir <dir>", "Restore zone data and metadata. (#)" },
{ CMD_ZONE_SIGN, "[<zone>...]", "Re-sign the automatically signed zone. (#)" },
+ { CMD_ZONE_VALIDATE, "[<zone>...]", "Trigger a DNSSEC validation of the zone. (#)" },
{ CMD_ZONE_KEYS_LOAD, "[<zone>...]", "Re-load keys from KASP database, sign the zone. (#)" },
{ CMD_ZONE_KEY_ROLL, " <zone> ksk|zsk", "Trigger immediate key rollover. (#)" },
{ CMD_ZONE_KSK_SBM, " <zone>...", "When KSK submission, confirm parent's DS presence. (#)" },
diff --git a/src/utils/knotc/main.c b/src/utils/knotc/main.c
index dad3671..274ab6d 100644
--- a/src/utils/knotc/main.c
+++ b/src/utils/knotc/main.c
@@ -82,7 +82,7 @@ int main(int argc, char **argv)
{ "color", no_argument, NULL, 'X' },
{ "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
+ { "version", optional_argument, NULL, 'V' },
{ NULL }
};
@@ -97,7 +97,7 @@ int main(int argc, char **argv)
/* Parse command line arguments */
int opt = 0;
- while ((opt = getopt_long(argc, argv, "+c:C:m:s:t:befxXvhV", opts, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "+c:C:m:s:t:befxXvhV::", opts, NULL)) != -1) {
switch (opt) {
case 'c':
params.orig_config = optarg;
@@ -147,7 +147,7 @@ int main(int argc, char **argv)
print_help();
return EXIT_SUCCESS;
case 'V':
- print_version(PROGRAM_NAME);
+ print_version(PROGRAM_NAME, optarg != NULL);
return EXIT_SUCCESS;
default:
print_help();
diff --git a/src/utils/knotd/main.c b/src/utils/knotd/main.c
index d4ebd53..e863296 100644
--- a/src/utils/knotd/main.c
+++ b/src/utils/knotd/main.c
@@ -38,15 +38,39 @@
#include "knot/conf/conf.h"
#include "knot/conf/migration.h"
#include "knot/conf/module.h"
+#include "knot/common/dbus.h"
#include "knot/common/log.h"
#include "knot/common/process.h"
#include "knot/common/stats.h"
#include "knot/common/systemd.h"
#include "knot/server/server.h"
#include "knot/server/tcp-handler.h"
+#include "utils/common/params.h"
#define PROGRAM_NAME "knotd"
+typedef enum {
+ CONCURRENT_EMPTY = 0, // fresh cctx without a thread.
+ CONCURRENT_ASSIGNED, // cctx assigned to process a command.
+ CONCURRENT_RUNNING, // ctl command is being processed in the thread.
+ CONCURRENT_IDLE, // command has been processed, waiting for a new one.
+ CONCURRENT_KILLED, // cctx cleanup has started.
+ CONCURRENT_FINISHED, // after having been killed, the thread is being joined.
+} concurrent_ctl_state_t;
+
+typedef struct {
+ concurrent_ctl_state_t state;
+ pthread_mutex_t mutex; // Protects .state.
+ pthread_cond_t cond;
+ knot_ctl_t *ctl;
+ server_t *server;
+ pthread_t thread;
+ sigset_t sigmask;
+ int ret;
+ int thread_idx;
+ bool exclusive;
+} concurrent_ctl_ctx_t;
+
/* Signal flags. */
static volatile bool sig_req_stop = false;
static volatile bool sig_req_reload = false;
@@ -161,13 +185,14 @@ static void setup_signals(void)
sigdelset(&all, SIGBUS);
sigdelset(&all, SIGFPE);
sigdelset(&all, SIGSEGV);
- pthread_sigmask(SIG_SETMASK, &all, NULL);
/* Setup handlers. */
struct sigaction action = { .sa_handler = handle_signal };
for (const struct signal *s = SIGNALS; s->signum > 0; s++) {
sigaction(s->signum, &action, NULL);
}
+
+ pthread_sigmask(SIG_SETMASK, &all, NULL);
}
/*! \brief Unblock server control signals. */
@@ -185,6 +210,24 @@ static void enable_signals(void)
pthread_sigmask(SIG_UNBLOCK, &mask, NULL);
}
+/*! \brief Create a control thread with correct signals setting. */
+static void create_thread_sigmask(pthread_t *thr, void *(*fcn)(void*), void *ctx,
+ sigset_t *out_mask)
+{
+ /* Block all blockable signals. */
+ sigset_t mask;
+ sigfillset(&mask);
+ sigdelset(&mask, SIGBUS);
+ sigdelset(&mask, SIGFPE);
+ sigdelset(&mask, SIGILL);
+ sigdelset(&mask, SIGSEGV);
+ pthread_sigmask(SIG_SETMASK, &mask, out_mask);
+
+ pthread_create(thr, NULL, fcn, ctx);
+
+ pthread_sigmask(SIG_SETMASK, out_mask, NULL);
+}
+
/*! \brief Drop POSIX 1003.1e capabilities. */
static void drop_capabilities(void)
{
@@ -224,6 +267,7 @@ static void check_loaded(server_t *server)
return;
}
+ rcu_read_lock();
knot_zonedb_iter_t *it = knot_zonedb_iter_begin(server->zone_db);
while (!knot_zonedb_iter_finished(it)) {
zone_t *zone = (zone_t *)knot_zonedb_iter_val(it);
@@ -234,9 +278,162 @@ static void check_loaded(server_t *server)
knot_zonedb_iter_next(it);
}
knot_zonedb_iter_free(it);
+ rcu_read_unlock();
finished = true;
- systemd_emit_running(true);
+ dbus_emit_running(true);
+}
+
+static void *ctl_process_thread(void *arg);
+
+/*!
+ * Try to find an empty ctl processing context and if successful,
+ * prepare to lauch the incomming command processing in it.
+ *
+ * \param[in] concurrent_ctxs Configured concurrent control contexts.
+ * \param[in] n_ctxs Number of configured concurrent control contexts.
+ * \param[in] ctl Control context.
+ *
+ * \return Assigned concurrent control context, or NULL.
+ */
+
+static concurrent_ctl_ctx_t *find_free_ctx(concurrent_ctl_ctx_t *concurrent_ctxs,
+ size_t n_ctxs, knot_ctl_t *ctl)
+{
+ concurrent_ctl_ctx_t *res = NULL;
+ for (size_t i = 0; i < n_ctxs && res == NULL; i++) {
+ concurrent_ctl_ctx_t *cctx = &concurrent_ctxs[i];
+ pthread_mutex_lock(&cctx->mutex);
+ if (cctx->exclusive) {
+ while (cctx->state != CONCURRENT_IDLE) {
+ pthread_cond_wait(&cctx->cond, &cctx->mutex);
+ }
+ knot_ctl_free(cctx->ctl);
+ cctx->ctl = knot_ctl_clone(ctl);
+ if (cctx->ctl == NULL) {
+ cctx->exclusive = false;
+ pthread_mutex_unlock(&cctx->mutex);
+ break;
+ }
+ cctx->state = CONCURRENT_ASSIGNED;
+ res = cctx;
+ pthread_cond_broadcast(&cctx->cond);
+ }
+ pthread_mutex_unlock(&cctx->mutex);
+ }
+ for (size_t i = 0; i < n_ctxs && res == NULL; i++) {
+ concurrent_ctl_ctx_t *cctx = &concurrent_ctxs[i];
+ pthread_mutex_lock(&cctx->mutex);
+ switch (cctx->state) {
+ case CONCURRENT_EMPTY:
+ create_thread_sigmask(&cctx->thread, ctl_process_thread, cctx, &cctx->sigmask);
+ break;
+ case CONCURRENT_IDLE:
+ knot_ctl_free(cctx->ctl);
+ pthread_cond_broadcast(&cctx->cond);
+ break;
+ default:
+ pthread_mutex_unlock(&cctx->mutex);
+ continue;
+ }
+ cctx->ctl = knot_ctl_clone(ctl);
+ if (cctx->ctl != NULL) {
+ cctx->state = CONCURRENT_ASSIGNED;
+ res = cctx;
+ }
+ pthread_mutex_unlock(&cctx->mutex);
+ }
+ return res;
+}
+
+static void init_ctxs(concurrent_ctl_ctx_t *concurrent_ctxs, size_t n_ctxs, server_t *server)
+{
+ for (size_t i = 0; i < n_ctxs; i++) {
+ concurrent_ctl_ctx_t *cctx = &concurrent_ctxs[i];
+ pthread_mutex_init(&cctx->mutex, NULL);
+ pthread_cond_init(&cctx->cond, NULL);
+ cctx->server = server;
+ cctx->thread_idx = i + 1;
+ }
+}
+
+static int cleanup_ctxs(concurrent_ctl_ctx_t *concurrent_ctxs, size_t n_ctxs)
+{
+ int ret = KNOT_EOK;
+ for (size_t i = 0; i < n_ctxs; i++) {
+ concurrent_ctl_ctx_t *cctx = &concurrent_ctxs[i];
+ pthread_mutex_lock(&cctx->mutex);
+ if (cctx->state == CONCURRENT_IDLE) {
+ knot_ctl_free(cctx->ctl);
+ cctx->ctl = NULL;
+ if (cctx->ret == KNOT_CTL_ESTOP) {
+ ret = cctx->ret;
+ }
+ }
+ pthread_mutex_unlock(&cctx->mutex);
+ }
+ return ret;
+}
+
+static void finalize_ctxs(concurrent_ctl_ctx_t *concurrent_ctxs, size_t n_ctxs)
+{
+ for (size_t i = 0; i < n_ctxs; i++) {
+ concurrent_ctl_ctx_t *cctx = &concurrent_ctxs[i];
+ pthread_mutex_lock(&cctx->mutex);
+ if (cctx->state == CONCURRENT_EMPTY) {
+ pthread_mutex_unlock(&cctx->mutex);
+ pthread_mutex_destroy(&cctx->mutex);
+ pthread_cond_destroy(&cctx->cond);
+ continue;
+ }
+
+ cctx->state = CONCURRENT_KILLED;
+ pthread_cond_broadcast(&cctx->cond);
+ pthread_mutex_unlock(&cctx->mutex);
+ (void)pthread_join(cctx->thread, NULL);
+
+ assert(cctx->state == CONCURRENT_FINISHED);
+ knot_ctl_free(cctx->ctl);
+ pthread_mutex_destroy(&cctx->mutex);
+ pthread_cond_destroy(&cctx->cond);
+ }
+}
+
+static void *ctl_process_thread(void *arg)
+{
+ concurrent_ctl_ctx_t *ctx = arg;
+ rcu_register_thread();
+ setup_signals(); // in fact, this blocks common signals so that they
+ // arrive to main thread instead of this one
+
+ pthread_mutex_lock(&ctx->mutex);
+ while (ctx->state != CONCURRENT_KILLED) {
+ if (ctx->state != CONCURRENT_ASSIGNED) {
+ pthread_cond_wait(&ctx->cond, &ctx->mutex);
+ continue;
+ }
+ ctx->state = CONCURRENT_RUNNING;
+ bool exclusive = ctx->exclusive;
+ pthread_mutex_unlock(&ctx->mutex);
+
+ // Not IDLE, ctx can be read without locking.
+ int ret = ctl_process(ctx->ctl, ctx->server, ctx->thread_idx, &exclusive);
+
+ pthread_mutex_lock(&ctx->mutex);
+ ctx->ret = ret;
+ ctx->exclusive = exclusive;
+ if (ctx->state == CONCURRENT_RUNNING) { // not KILLED
+ ctx->state = CONCURRENT_IDLE;
+ pthread_cond_broadcast(&ctx->cond);
+ }
+ }
+
+ knot_ctl_close(ctx->ctl);
+
+ ctx->state = CONCURRENT_FINISHED;
+ pthread_mutex_unlock(&ctx->mutex);
+ rcu_unregister_thread();
+ return NULL;
}
/*! \brief Event loop listening for signals and remote commands. */
@@ -274,7 +471,7 @@ static void event_loop(server_t *server, const char *socket, bool daemonize,
/* Bind the control socket. */
uint16_t backlog = conf_get_int(conf(), C_CTL, C_BACKLOG);
- int ret = knot_ctl_bind2(ctl, listen, backlog);
+ int ret = knot_ctl_bind(ctl, listen, backlog);
if (ret != KNOT_EOK) {
knot_ctl_free(ctl);
log_fatal("control, failed to bind socket '%s' (%s)",
@@ -286,6 +483,10 @@ static void event_loop(server_t *server, const char *socket, bool daemonize,
enable_signals();
+ concurrent_ctl_ctx_t concurrent_ctxs[CTL_MAX_CONCURRENT] = { 0 };
+ init_ctxs(concurrent_ctxs, CTL_MAX_CONCURRENT, server);
+ bool main_thread_exclusive = false;
+
/* Notify systemd about successful start. */
systemd_ready_notify();
if (daemonize) {
@@ -299,15 +500,19 @@ static void event_loop(server_t *server, const char *socket, bool daemonize,
/* Interrupts. */
if (sig_req_reload && !sig_req_stop) {
sig_req_reload = false;
+ pthread_rwlock_wrlock(&server->ctl_lock);
server_reload(server, RELOAD_FULL);
+ pthread_rwlock_unlock(&server->ctl_lock);
}
if (sig_req_zones_reload && !sig_req_stop) {
sig_req_zones_reload = false;
reload_t mode = server->catalog_upd_signal ? RELOAD_CATALOG : RELOAD_ZONES;
+ pthread_rwlock_wrlock(&server->ctl_lock);
server->catalog_upd_signal = false;
server_update_zones(conf(), server, mode);
+ pthread_rwlock_unlock(&server->ctl_lock);
}
- if (sig_req_stop) {
+ if (sig_req_stop || cleanup_ctxs(concurrent_ctxs, CTL_MAX_CONCURRENT) == KNOT_CTL_ESTOP) {
break;
}
@@ -325,15 +530,20 @@ static void event_loop(server_t *server, const char *socket, bool daemonize,
continue;
}
- ret = ctl_process(ctl, server);
- knot_ctl_close(ctl);
- if (ret == KNOT_CTL_ESTOP) {
- break;
+ if (main_thread_exclusive ||
+ find_free_ctx(concurrent_ctxs, CTL_MAX_CONCURRENT, ctl) == NULL) {
+ ret = ctl_process(ctl, server, 0, &main_thread_exclusive);
+ knot_ctl_close(ctl);
+ if (ret == KNOT_CTL_ESTOP) {
+ break;
+ }
}
}
+ finalize_ctxs(concurrent_ctxs, CTL_MAX_CONCURRENT);
+
if (conf()->cache.srv_dbus_event & DBUS_EVENT_RUNNING) {
- systemd_emit_running(false);
+ dbus_emit_running(false);
}
/* Unbind the control socket. */
@@ -363,11 +573,6 @@ static void print_help(void)
CONF_MAPSIZE, RUN_DIR "/knot.sock");
}
-static void print_version(void)
-{
- printf("%s (Knot DNS), version %s\n", PROGRAM_NAME, PACKAGE_VERSION);
-}
-
static int set_config(const char *confdb, const char *config, size_t max_conf_size)
{
if (config != NULL && confdb != NULL) {
@@ -440,7 +645,7 @@ int main(int argc, char **argv)
{ "daemonize", optional_argument, NULL, 'd' },
{ "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
+ { "version", optional_argument, NULL, 'V' },
{ NULL }
};
@@ -449,7 +654,7 @@ int main(int argc, char **argv)
/* Parse command line arguments. */
int opt = 0;
- while ((opt = getopt_long(argc, argv, "c:C:m:s:dvhV", opts, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "c:C:m:s:dvhV::", opts, NULL)) != -1) {
switch (opt) {
case 'c':
config = optarg;
@@ -481,7 +686,7 @@ int main(int argc, char **argv)
print_help();
return EXIT_SUCCESS;
case 'V':
- print_version();
+ print_version(PROGRAM_NAME, optarg != NULL);
return EXIT_SUCCESS;
default:
print_help();
@@ -570,14 +775,9 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
- if (conf()->cache.srv_dbus_event != DBUS_EVENT_NONE) {
- ret = systemd_dbus_open();
- if (ret != KNOT_EOK) {
- log_error("d-bus: failed to open system bus (%s)",
- knot_strerror(ret));
- } else {
- log_info("d-bus: connected to system bus");
- }
+ /* Connect to the system D-bus. */
+ if (conf()->cache.srv_dbus_event != DBUS_EVENT_NONE &&
+ dbus_open() == KNOT_EOK) {
int64_t delay = conf_get_int(conf(), C_SRV, C_DBUS_INIT_DELAY);
sleep(delay);
}
@@ -595,7 +795,7 @@ int main(int argc, char **argv)
server_wait(&server);
server_deinit(&server);
conf_free(conf());
- systemd_dbus_close();
+ dbus_close();
log_close();
dnssec_crypto_cleanup();
return EXIT_FAILURE;
@@ -636,7 +836,7 @@ int main(int argc, char **argv)
rcu_unregister_thread();
pid_cleanup();
conf_free(conf());
- systemd_dbus_close();
+ dbus_close();
log_close();
dnssec_crypto_cleanup();
return EXIT_FAILURE;
@@ -660,7 +860,7 @@ int main(int argc, char **argv)
/* Unhook from RCU. */
rcu_unregister_thread();
- systemd_dbus_close();
+ dbus_close();
log_info("shutting down");
log_close();
diff --git a/src/utils/knsec3hash/knsec3hash.c b/src/utils/knsec3hash/knsec3hash.c
index a7bac97..a18498d 100644
--- a/src/utils/knsec3hash/knsec3hash.c
+++ b/src/utils/knsec3hash/knsec3hash.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -36,7 +36,7 @@
*/
static void print_help(void)
{
- printf("Usage: " PROGRAM_NAME " <salt> <algorithm> <iterations> <domain-name>\n");
+ printf("Usage: " PROGRAM_NAME " [-h | -V] <salt> <algorithm> <iterations> <domain-name>\n");
printf("Example: " PROGRAM_NAME " c01dcafe 1 10 knot-dns.cz\n");
printf("Alternative usage: "PROGRAM_NAME " <algorithm> <flags> <iterations> <salt> <domain-name>\n");
printf("Example: " PROGRAM_NAME " 1 0 10 c01dcafe knot-dns.cz\n");
@@ -103,19 +103,19 @@ static bool parse_nsec3_params(dnssec_nsec3_params_t *params, const char *salt_s
int main(int argc, char *argv[])
{
struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", optional_argument, NULL, 'V' },
{ NULL }
};
int opt = 0;
- while ((opt = getopt_long(argc, argv, "hV", options, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "hV::", options, NULL)) != -1) {
switch(opt) {
case 'h':
print_help();
return EXIT_SUCCESS;
case 'V':
- print_version(PROGRAM_NAME);
+ print_version(PROGRAM_NAME, optarg != NULL);
return EXIT_SUCCESS;
default:
print_help();
diff --git a/src/utils/knsupdate/knsupdate_exec.c b/src/utils/knsupdate/knsupdate_exec.c
index e201711..d716ffb 100644
--- a/src/utils/knsupdate/knsupdate_exec.c
+++ b/src/utils/knsupdate/knsupdate_exec.c
@@ -452,15 +452,23 @@ static int pkt_sendrecv(knsupdate_params_t *params)
return -1;
}
+ ret = net_init_crypto(&net, &params->tls_params, NULL, &params->quic_params);
+ if (ret != 0) {
+ ERR("failed to initialize crypto context (%s)", knot_strerror(ret));
+ net_clean(&net);
+ return -1;
+ }
+
ret = net_connect(&net);
- DBG("%s: send_msg = %d", __func__, net.sockfd);
if (ret != KNOT_EOK) {
+ ERR("failed to connect (%s)", knot_strerror(ret));
net_clean(&net);
return -1;
}
ret = net_send(&net, params->query->wire, params->query->size);
if (ret != KNOT_EOK) {
+ ERR("failed to send update (%s)", knot_strerror(ret));
net_close(&net);
net_clean(&net);
return -1;
@@ -471,8 +479,8 @@ static int pkt_sendrecv(knsupdate_params_t *params)
/* Wait for reception. */
int rb = net_receive(&net, params->answer->wire, params->answer->max_size);
- DBG("%s: receive_msg = %d", __func__, rb);
if (rb <= 0) {
+ ERR("failed to receive response (%s)", knot_strerror(rb));
net_close(&net);
net_clean(&net);
return -1;
diff --git a/src/utils/knsupdate/knsupdate_params.c b/src/utils/knsupdate/knsupdate_params.c
index f9fa41f..5aaf808 100644
--- a/src/utils/knsupdate/knsupdate_params.c
+++ b/src/utils/knsupdate/knsupdate_params.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
#include "utils/common/netio.h"
#include "libknot/libknot.h"
#include "libknot/tsig.h"
+#include "contrib/base64.h"
#include "contrib/mempattern.h"
#include "contrib/strtonum.h"
#include "contrib/ucw/mempool.h"
@@ -90,6 +91,8 @@ static int knsupdate_init(knsupdate_params_t *params)
init_list(&params->update_list);
init_list(&params->prereq_list);
+ tls_params_init(&params->tls_params);
+
/* Initialize memory context. */
mm_ctx_mempool(&params->mm, MM_DEFAULT_BLKSIZE);
@@ -142,6 +145,9 @@ void knsupdate_clean(knsupdate_params_t *params)
knot_pkt_free(params->answer);
knot_tsig_key_deinit(&params->tsig_key);
+ tls_params_clean(&params->tls_params);
+ quic_params_clean(&params->quic_params);
+
/* Clean up the structure. */
mp_delete(params->mm.ctx);
memset(params, 0, sizeof(*params));
@@ -172,9 +178,31 @@ void knsupdate_reset(knsupdate_params_t *params)
static void print_help(void)
{
- printf("Usage: %s [-d] [-v] [-k keyfile | -y [hmac:]name:key]\n"
- " [-p port] [-t timeout] [-r retries] [filename]\n",
- PROGRAM_NAME);
+ printf("Usage:\n"
+ " %s [-T] [options] [filename]\n"
+ " %s [-S | -Q] [tls_options] [options] [filename]\n"
+ "\n"
+ "Options:\n"
+ " -T, --tcp Use TCP protocol.\n"
+ " -S, --tls Use TLS protocol.\n"
+ " -Q, --quic Use QUIC protocol.\n"
+ " -p, --port <num> Remote port.\n"
+ " -r, --retry <num> Number of retries over UDP.\n"
+ " -t, --timeout <num> Update timeout.\n"
+ " -y, --tsig <str> TSIG key in the form [alg:]name:key.\n"
+ " -k, --tsigfile <path> Path to a TSIG key file.\n"
+ " -d, --debug Debug mode output.\n"
+ " -h, --help Print the program help.\n"
+ " -V, --version Print the program version.\n"
+ "\n"
+ "QUIC/TLS options:\n"
+ " -H, --hostname <str> Remote hostname validation.\n"
+ " -P, --pin <base64> Certificate key PIN.\n"
+ " -A, --ca [<path>] Path to a CA file.\n"
+ " -E, --certfile <path> Path to a client certificate file.\n"
+ " -K, --keyfile <path> Path to a client key file.\n"
+ " -s, --sni <str> Remote SNI.\n",
+ PROGRAM_NAME, PROGRAM_NAME);
}
int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
@@ -188,40 +216,74 @@ int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
return ret;
}
- // Long options.
+ const char *opts_str = "dhvTSQV::p:r:t:y:k:H:P:A::E:K:s:";
struct option opts[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "tcp", no_argument, NULL, 'T' },
+ { "tls", no_argument, NULL, 'S' },
+ { "quic", no_argument, NULL, 'Q' },
+ { "version", optional_argument, NULL, 'V' },
+ { "port", required_argument, NULL, 'p' },
+ { "retry", required_argument, NULL, 'r' },
+ { "timeout", required_argument, NULL, 't' },
+ { "tsig", required_argument, NULL, 'y' },
+ { "tsigfile", required_argument, NULL, 'k' },
+ { "hostname", required_argument, NULL, 'H' },
+ { "pin", required_argument, NULL, 'P' },
+ { "ca", optional_argument, NULL, 'A' },
+ { "certfile", required_argument, NULL, 'E' },
+ { "keyfile", required_argument, NULL, 'K' },
+ { "sni", required_argument, NULL, 's' },
{ NULL }
};
- /* Command line options processing. */
+ bool default_port = true;
+
int opt = 0;
- while ((opt = getopt_long(argc, argv, "dhDvVp:t:r:y:k:", opts, NULL))
- != -1) {
+ while ((opt = getopt_long(argc, argv, opts_str, opts, NULL)) != -1) {
switch (opt) {
case 'd':
- case 'D': /* Extra debugging. */
msg_enable_debug(1);
break;
case 'h':
print_help();
params->stop = true;
return KNOT_EOK;
- case 'v':
+ case 'v': // Compatibility with nsupdate.
+ case 'T':
params->protocol = PROTO_TCP;
break;
+ case 'S':
+ params->protocol = PROTO_TCP;
+
+ params->tls_params.enable = true;
+
+ if (default_port) {
+ free(params->server->service);
+ params->server->service = strdup(DEFAULT_DNS_TLS_PORT);
+ }
+ break;
+ case 'Q':
+ params->protocol = PROTO_UDP;
+
+ params->tls_params.enable = true;
+ params->quic_params.enable = true;
+
+ if (default_port) {
+ free(params->server->service);
+ params->server->service = strdup(DEFAULT_DNS_QUIC_PORT);
+ }
+ break;
case 'V':
- print_version(PROGRAM_NAME);
+ print_version(PROGRAM_NAME, optarg != NULL);
params->stop = true;
return KNOT_EOK;
case 'p':
+ assert(optarg);
+ default_port = false;
free(params->server->service);
params->server->service = strdup(optarg);
- if (!params->server->service) {
- ERR("failed to set default port '%s'", optarg);
- return KNOT_ENOMEM;
- }
break;
case 'r':
ret = str_to_u32(optarg, &params->retries);
@@ -241,7 +303,7 @@ int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
knot_tsig_key_deinit(&params->tsig_key);
ret = knot_tsig_key_init_str(&params->tsig_key, optarg);
if (ret != KNOT_EOK) {
- ERR("failed to parse key '%s'", optarg);
+ ERR("failed to parse TSIG key '%s'", optarg);
return ret;
}
break;
@@ -249,9 +311,63 @@ int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
knot_tsig_key_deinit(&params->tsig_key);
ret = knot_tsig_key_init_file(&params->tsig_key, optarg);
if (ret != KNOT_EOK) {
- ERR("failed to parse keyfile '%s'", optarg);
+ ERR("failed to parse TSIG keyfile '%s'", optarg);
+ return ret;
+ }
+ break;
+ case 'H':
+ assert(optarg);
+ free(params->tls_params.hostname);
+ params->tls_params.hostname = strdup(optarg);
+ break;
+ case 'P':
+ assert(optarg);
+ uint8_t pin[64] = { 0 };
+ ret = knot_base64_decode((const uint8_t *)optarg, strlen(optarg), pin, sizeof(pin));
+ if (ret < 0) {
+ ERR("invalid certificate pin %s", optarg);
return ret;
+ } else if (ret != CERT_PIN_LEN) { // Check for 256-bit value.
+ ERR("invalid SHA256 hash length of certificate pin %s", optarg);
+ return KNOT_EINVAL;
}
+
+ uint8_t *item = malloc(1 + ret); // 1 ~ leading data length.
+ if (item == NULL) {
+ return KNOT_ENOMEM;
+ }
+ item[0] = ret;
+ memcpy(&item[1], pin, ret);
+
+ if (ptrlist_add(&params->tls_params.pins, item, NULL) == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ break;
+ case 'A':
+ if (optarg == NULL) {
+ params->tls_params.system_ca = true;
+ break;
+ }
+ if (ptrlist_add(&params->tls_params.ca_files, strdup(optarg), NULL) == NULL) {
+ ERR("failed to set CA file '%s'", optarg);
+ return KNOT_ENOMEM;
+ }
+ break;
+ case 'E':
+ assert(optarg);
+ free(params->tls_params.certfile);
+ params->tls_params.certfile = strdup(optarg);
+ break;
+ case 'K':
+ assert(optarg);
+ free(params->tls_params.keyfile);
+ params->tls_params.keyfile = strdup(optarg);
+ break;
+ case 's':
+ assert(optarg);
+ free(params->tls_params.sni);
+ params->tls_params.sni = strdup(optarg);
break;
default:
print_help();
@@ -259,8 +375,8 @@ int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
}
}
- /* No retries for TCP. */
- if (params->protocol == PROTO_TCP) {
+ /* Retries only for UDP. */
+ if (params->protocol == PROTO_TCP || params->quic_params.enable) {
params->retries = 0;
} else {
/* If wait/tries < 1 s, set 1 second for each try. */
@@ -277,7 +393,7 @@ int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
ptrlist_add(&params->qfiles, argv[optind], &params->mm);
}
- return ret;
+ return KNOT_EOK;
}
int knsupdate_set_ttl(knsupdate_params_t *params, const uint32_t ttl)
diff --git a/src/utils/knsupdate/knsupdate_params.h b/src/utils/knsupdate/knsupdate_params.h
index 1933244..fbf30e5 100644
--- a/src/utils/knsupdate/knsupdate_params.h
+++ b/src/utils/knsupdate/knsupdate_params.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,7 +20,9 @@
#include "utils/common/netio.h"
#include "utils/common/params.h"
+#include "utils/common/quic.h"
#include "utils/common/sign.h"
+#include "utils/common/tls.h"
#include "libknot/libknot.h"
#include "libzscanner/scanner.h"
#include "contrib/ucw/lists.h"
@@ -63,6 +65,10 @@ typedef struct {
style_t style;
/*!< Memory context. */
knot_mm_t mm;
+ /*!< TLS params. */
+ tls_params_t tls_params;
+ /*!< QUIC params. */
+ quic_params_t quic_params;
} knsupdate_params_t;
int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[]);
diff --git a/src/utils/kxdpgun/load_queries.c b/src/utils/kxdpgun/load_queries.c
index 8ecac48..fe7c9ae 100644
--- a/src/utils/kxdpgun/load_queries.c
+++ b/src/utils/kxdpgun/load_queries.c
@@ -16,9 +16,11 @@
#include <assert.h>
#include <errno.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <arpa/inet.h>
#include "load_queries.h"
#include "libknot/libknot.h"
@@ -44,106 +46,181 @@ void free_global_payloads(void)
global_payloads = NULL;
}
-bool load_queries(const char *filename, uint16_t edns_size, uint16_t msgid, size_t maxcount)
+typedef struct {
+ char line[KNOT_DNAME_TXT_MAXLEN + 256];
+ char dname_txt[KNOT_DNAME_TXT_MAXLEN + 1];
+ uint8_t dname[KNOT_DNAME_MAXLEN];
+ char type_txt[128];
+ char flags_txt[128];
+} txt_bufs_t;
+
+typedef struct {
+ char line[USHRT_MAX];
+} bin_bufs_t;
+
+static int read_txt(struct pkt_payload **g_payloads_top_p, FILE *f, txt_bufs_t *bufs,
+ uint16_t edns_size, uint16_t msgid)
+{
+ assert(g_payloads_top_p != NULL);
+ struct pkt_payload *g_payloads_top = *g_payloads_top_p;
+ if (fgets(bufs->line, sizeof(bufs->line), f) == NULL) {
+ return 0;
+ }
+ bufs->flags_txt[0] = '\0';
+ int ret = sscanf(bufs->line, "%s%s%s", bufs->dname_txt, bufs->type_txt,
+ bufs->flags_txt);
+ if (ret < 2) {
+ ERR2(ERR_PREFIX "(faulty line): '%.*s'",
+ (int)strcspn(bufs->line, "\n"), bufs->line);
+ return KNOT_EINVAL;
+ }
+
+ void *pret = knot_dname_from_str(bufs->dname, bufs->dname_txt, sizeof(bufs->dname));
+ if (pret == NULL) {
+ ERR2(ERR_PREFIX "(faulty dname): '%s'", bufs->dname_txt);
+ return KNOT_EINVAL;
+ }
+
+ uint16_t type;
+ ret = knot_rrtype_from_string(bufs->type_txt, &type);
+ if (ret < 0) {
+ ERR2(ERR_PREFIX "(faulty type): '%s'", bufs->type_txt);
+ return KNOT_EINVAL;
+ }
+
+ enum qflags flags = 0;
+ switch (bufs->flags_txt[0]) {
+ case '\0':
+ break;
+ case 'e':
+ case 'E':
+ flags |= QFLAG_EDNS;
+ break;
+ case 'd':
+ case 'D':
+ flags |= QFLAG_EDNS | QFLAG_DO;
+ break;
+ default:
+ ERR2(ERR_PREFIX "(faulty flag): '%s'", bufs->flags_txt);
+ return KNOT_EINVAL;
+ }
+
+ size_t dname_len = knot_dname_size(bufs->dname);
+ size_t pkt_len = KNOT_WIRE_HEADER_SIZE + 2 * sizeof(uint16_t) + dname_len;
+ if (flags & QFLAG_EDNS) {
+ pkt_len += KNOT_EDNS_MIN_SIZE;
+ }
+
+ struct pkt_payload *pkt = calloc(1, sizeof(*pkt) + pkt_len);
+ if (pkt == NULL) {
+ ERR2(ERR_PREFIX "(out of memory)");
+ return KNOT_ENOMEM;
+ }
+ pkt->len = pkt_len;
+ memcpy(pkt->payload, &msgid, sizeof(msgid));
+ pkt->payload[2] = 0x01; // RD bit
+ pkt->payload[5] = 0x01; // 1 question
+ pkt->payload[11] = (flags & QFLAG_EDNS) ? 0x01 : 0x00;
+ memcpy(pkt->payload + 12, bufs->dname, dname_len);
+ pkt->payload[dname_len + 12] = type >> 8;
+ pkt->payload[dname_len + 13] = type & 0xff;
+ pkt->payload[dname_len + 15] = KNOT_CLASS_IN;
+ if (flags & QFLAG_EDNS) {
+ pkt->payload[dname_len + 18] = KNOT_RRTYPE_OPT;
+ pkt->payload[dname_len + 19] = edns_size >> 8;
+ pkt->payload[dname_len + 20] = edns_size & 0xff;
+ pkt->payload[dname_len + 23] = (flags & QFLAG_DO) ? 0x80 : 0x00;
+ }
+
+ // add pkt to list global_payloads
+ if (g_payloads_top == NULL) {
+ global_payloads = pkt;
+ *g_payloads_top_p = pkt;
+ } else {
+ g_payloads_top->next = pkt;
+ *g_payloads_top_p = pkt;
+ }
+ return pkt_len;
+}
+
+static int read_bin(struct pkt_payload **g_payloads_top_p, FILE *f, bin_bufs_t *bufs,
+ uint16_t msgid)
+{
+ assert(g_payloads_top_p != NULL);
+ struct pkt_payload *g_payloads_top = *g_payloads_top_p;
+ uint16_t size;
+ if (fread(&size, sizeof(size), 1, f) < 1) {
+ return 0;
+ }
+ size = ntohs(size);
+ if (fread(bufs->line, size, 1, f) < 1) {
+ return KNOT_EINVAL;
+ }
+ struct pkt_payload *pkt = calloc(1, sizeof(*pkt) + size);
+ if (pkt == NULL) {
+ ERR2(ERR_PREFIX "(out of memory)");
+ return KNOT_ENOMEM;
+ }
+ pkt->len = size;
+ memcpy(pkt->payload, &msgid, sizeof(msgid)); // Override msgID
+ memcpy(pkt->payload + 2, bufs->line + 2, size - 2);
+
+ // add pkt to list global_payloads
+ if (g_payloads_top == NULL) {
+ global_payloads = pkt;
+ *g_payloads_top_p = pkt;
+ } else {
+ g_payloads_top->next = pkt;
+ *g_payloads_top_p = pkt;
+ }
+ return size;
+}
+
+bool load_queries(const input_t *input, uint16_t edns_size, uint16_t msgid, size_t maxcount)
{
size_t read = 0;
- FILE *f = fopen(filename, "r");
+ FILE *f = fopen(input->path, (input->format == BIN) ? "rb" : "r");
if (f == NULL) {
- ERR2(ERR_PREFIX "file '%s' (%s)", filename, strerror(errno));
+ ERR2(ERR_PREFIX "file '%s' (%s)", input->path, strerror(errno));
return false;
}
- struct pkt_payload *g_payloads_top = NULL;
- struct {
- char line[KNOT_DNAME_TXT_MAXLEN + 256];
- char dname_txt[KNOT_DNAME_TXT_MAXLEN + 1];
- uint8_t dname[KNOT_DNAME_MAXLEN];
- char type_txt[128];
- char flags_txt[128];
- } *bufs;
- bufs = malloc(sizeof(*bufs)); // avoiding too much stuff on stack
+ void *bufs = NULL;
+ switch (input->format) {
+ case TXT:
+ bufs = malloc(sizeof(txt_bufs_t)); // avoiding too much stuff on stack
+ break;
+ case BIN:
+ bufs = malloc(sizeof(bin_bufs_t));
+ break;
+ default:
+ assert(0);
+ goto fail;
+ }
if (bufs == NULL) {
ERR2(ERR_PREFIX "(out of memory)");
goto fail;
}
+ struct pkt_payload *g_payloads_top = NULL;
while (read < maxcount) {
- if (fgets(bufs->line, sizeof(bufs->line), f) == NULL) {
+ int ret = 0;
+ switch (input->format) {
+ case TXT:
+ ret = read_txt(&g_payloads_top, f, bufs, edns_size, msgid);
break;
- }
- bufs->flags_txt[0] = '\0';
- int ret = sscanf(bufs->line, "%s%s%s", bufs->dname_txt, bufs->type_txt, bufs->flags_txt);
- if (ret < 2) {
- ERR2(ERR_PREFIX "(faulty line): '%.*s'",
- (int)strcspn(bufs->line, "\n"), bufs->line);
- goto fail;
- }
-
- void *pret = knot_dname_from_str(bufs->dname, bufs->dname_txt, sizeof(bufs->dname));
- if (pret == NULL) {
- ERR2(ERR_PREFIX "(faulty dname): '%s'", bufs->dname_txt);
- goto fail;
- }
-
- uint16_t type;
- ret = knot_rrtype_from_string(bufs->type_txt, &type);
- if (ret < 0) {
- ERR2(ERR_PREFIX "(faulty type): '%s'", bufs->type_txt);
- goto fail;
- }
-
- enum qflags flags = 0;
- switch (bufs->flags_txt[0]) {
- case '\0':
- break;
- case 'e':
- case 'E':
- flags |= QFLAG_EDNS;
- break;
- case 'd':
- case 'D':
- flags |= QFLAG_EDNS | QFLAG_DO;
+ case BIN:
+ ret = read_bin(&g_payloads_top, f, bufs, msgid);
break;
default:
- ERR2(ERR_PREFIX "(faulty flag): '%s'", bufs->flags_txt);
- goto fail;
- }
-
- size_t dname_len = knot_dname_size(bufs->dname);
- size_t pkt_len = KNOT_WIRE_HEADER_SIZE + 2 * sizeof(uint16_t) + dname_len;
- if (flags & QFLAG_EDNS) {
- pkt_len += KNOT_EDNS_MIN_SIZE;
+ ret = -1;
+ break;
}
-
- struct pkt_payload *pkt = calloc(1, sizeof(struct pkt_payload) + pkt_len);
- if (pkt == NULL) {
- ERR2(ERR_PREFIX "(out of memory)");
+ if (ret == 0) {
+ break;
+ } else if (ret < 0) {
goto fail;
}
- pkt->len = pkt_len;
- memcpy(pkt->payload, &msgid, sizeof(msgid));
- pkt->payload[2] = 0x01; // RD bit
- pkt->payload[5] = 0x01; // 1 question
- pkt->payload[11] = (flags & QFLAG_EDNS) ? 0x01 : 0x00;
- memcpy(pkt->payload + 12, bufs->dname, dname_len);
- pkt->payload[dname_len + 12] = type >> 8;
- pkt->payload[dname_len + 13] = type & 0xff;
- pkt->payload[dname_len + 15] = KNOT_CLASS_IN;
- if (flags & QFLAG_EDNS) {
- pkt->payload[dname_len + 18] = KNOT_RRTYPE_OPT;
- pkt->payload[dname_len + 19] = edns_size >> 8;
- pkt->payload[dname_len + 20] = edns_size & 0xff;
- pkt->payload[dname_len + 23] = (flags & QFLAG_DO) ? 0x80 : 0x00;
- }
-
- // add pkt to list global_payloads
- if (g_payloads_top == NULL) {
- global_payloads = pkt;
- g_payloads_top = pkt;
- } else {
- g_payloads_top->next = pkt;
- g_payloads_top = pkt;
- }
read++;
}
diff --git a/src/utils/kxdpgun/load_queries.h b/src/utils/kxdpgun/load_queries.h
index 3d7bace..09dfee9 100644
--- a/src/utils/kxdpgun/load_queries.h
+++ b/src/utils/kxdpgun/load_queries.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,6 +19,16 @@
#include <stdbool.h>
#include <stdint.h>
+enum input_format {
+ TXT = 0,
+ BIN
+};
+
+typedef struct {
+ const char *path;
+ enum input_format format;
+} input_t;
+
struct pkt_payload {
struct pkt_payload *next;
size_t len;
@@ -27,6 +37,6 @@ struct pkt_payload {
extern struct pkt_payload *global_payloads;
-bool load_queries(const char *filename, uint16_t edns_size, uint16_t msgid, size_t maxcount);
+bool load_queries(const input_t *input, uint16_t edns_size, uint16_t msgid, size_t maxcount);
void free_global_payloads(void);
diff --git a/src/utils/kxdpgun/main.c b/src/utils/kxdpgun/main.c
index 523f64f..4e3aa31 100644
--- a/src/utils/kxdpgun/main.c
+++ b/src/utils/kxdpgun/main.c
@@ -14,12 +14,15 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <ifaddrs.h>
#include <inttypes.h>
#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
@@ -28,16 +31,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <netdb.h>
-
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <net/if.h>
#include <sys/ioctl.h>
-#include <sys/socket.h>
#include <sys/resource.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <unistd.h>
#include "libknot/libknot.h"
#include "libknot/xdp.h"
@@ -46,101 +44,28 @@
#include <gnutls/gnutls.h>
#include "libknot/quic/quic.h"
#endif // ENABLE_QUIC
-#include "contrib/macros.h"
-#include "contrib/mempattern.h"
-#include "contrib/openbsd/strlcat.h"
+#include "contrib/atomic.h"
#include "contrib/openbsd/strlcpy.h"
#include "contrib/os.h"
#include "contrib/sockaddr.h"
#include "contrib/toeplitz.h"
-#include "contrib/ucw/mempool.h"
#include "utils/common/msg.h"
#include "utils/common/params.h"
#include "utils/kxdpgun/ip_route.h"
#include "utils/kxdpgun/load_queries.h"
-
-#define PROGRAM_NAME "kxdpgun"
-#define SPACE " "
-
-enum {
- KXDPGUN_WAIT,
- KXDPGUN_START,
- KXDPGUN_STOP,
-};
+#include "utils/kxdpgun/main.h"
+#include "utils/kxdpgun/stats.h"
volatile int xdp_trigger = KXDPGUN_WAIT;
-volatile unsigned stats_trigger = 0;
+volatile knot_atomic_uint64_t stats_trigger = 0;
+volatile knot_atomic_bool stats_switch = STATS_SUM;
unsigned global_cpu_aff_start = 0;
unsigned global_cpu_aff_step = 1;
-#define REMOTE_PORT_DEFAULT 53
-#define REMOTE_PORT_DOQ_DEFAULT 853
-#define LOCAL_PORT_MIN 2000
-#define LOCAL_PORT_MAX 65535
-#define QUIC_THREAD_PORTS 100
-
-#define RCODE_MAX (0x0F + 1)
-
-typedef struct {
- size_t collected;
- uint64_t duration;
- uint64_t qry_sent;
- uint64_t synack_recv;
- uint64_t ans_recv;
- uint64_t finack_recv;
- uint64_t rst_recv;
- uint64_t size_recv;
- uint64_t wire_recv;
- uint64_t rcodes_recv[RCODE_MAX];
- pthread_mutex_t mutex;
-} kxdpgun_stats_t;
-
static kxdpgun_stats_t global_stats = { 0 };
-typedef enum {
- KXDPGUN_IGNORE_NONE = 0,
- KXDPGUN_IGNORE_QUERY = (1 << 0),
- KXDPGUN_IGNORE_LASTBYTE = (1 << 1),
- KXDPGUN_IGNORE_CLOSE = (1 << 2),
- KXDPGUN_REUSE_CONN = (1 << 3),
-} xdp_gun_ignore_t;
-
-typedef struct {
- union {
- struct sockaddr_in local_ip4;
- struct sockaddr_in6 local_ip;
- struct sockaddr_storage local_ip_ss;
- };
- union {
- struct sockaddr_in target_ip4;
- struct sockaddr_in6 target_ip;
- struct sockaddr_storage target_ip_ss;
- };
- char dev[IFNAMSIZ];
- uint64_t qps, duration;
- unsigned at_once;
- uint16_t msgid;
- uint16_t edns_size;
- uint16_t vlan_tci;
- uint8_t local_mac[6], target_mac[6];
- uint8_t local_ip_range;
- bool ipv6;
- bool tcp;
- bool quic;
- bool quic_full_handshake;
- const char *qlog_dir;
- const char *sending_mode;
- xdp_gun_ignore_t ignore1;
- knot_tcp_ignore_t ignore2;
- uint16_t target_port;
- knot_xdp_filter_flag_t flags;
- unsigned n_threads, thread_id;
- knot_eth_rss_conf_t *rss_conf;
- knot_xdp_config_t xdp_config;
-} xdp_gun_ctx_t;
-
const static xdp_gun_ctx_t ctx_defaults = {
.dev[0] = '\0',
.edns_size = 1232,
@@ -150,7 +75,9 @@ const static xdp_gun_ctx_t ctx_defaults = {
.sending_mode = "",
.target_port = 0,
.flags = KNOT_XDP_FILTER_UDP | KNOT_XDP_FILTER_PASS,
- .xdp_config = { .extra_frames = true },
+ .xdp_config = { .ring_size = 2048 },
+ .jw = NULL,
+ .stats_period = 0,
};
static void sigterm_handler(int signo)
@@ -163,103 +90,8 @@ static void sigusr_handler(int signo)
{
assert(signo == SIGUSR1);
if (global_stats.collected == 0) {
- stats_trigger++;
- }
-}
-
-static void clear_stats(kxdpgun_stats_t *st)
-{
- pthread_mutex_lock(&st->mutex);
- st->duration = 0;
- st->qry_sent = 0;
- st->synack_recv = 0;
- st->ans_recv = 0;
- st->finack_recv = 0;
- st->rst_recv = 0;
- st->size_recv = 0;
- st->wire_recv = 0;
- st->collected = 0;
- memset(st->rcodes_recv, 0, sizeof(st->rcodes_recv));
- pthread_mutex_unlock(&st->mutex);
-}
-
-static size_t collect_stats(kxdpgun_stats_t *into, const kxdpgun_stats_t *what)
-{
- pthread_mutex_lock(&into->mutex);
- into->duration = MAX(into->duration, what->duration);
- into->qry_sent += what->qry_sent;
- into->synack_recv += what->synack_recv;
- into->ans_recv += what->ans_recv;
- into->finack_recv += what->finack_recv;
- into->rst_recv += what->rst_recv;
- into->size_recv += what->size_recv;
- into->wire_recv += what->wire_recv;
- for (int i = 0; i < RCODE_MAX; i++) {
- into->rcodes_recv[i] += what->rcodes_recv[i];
+ ATOMIC_ADD(stats_trigger, 1);
}
- size_t res = ++into->collected;
- pthread_mutex_unlock(&into->mutex);
- return res;
-}
-
-static void print_stats(kxdpgun_stats_t *st, bool tcp, bool quic, bool recv, uint64_t qps)
-{
- pthread_mutex_lock(&st->mutex);
-
-#define ps(counter) ((typeof(counter))((counter) * 1000 / ((float)st->duration / 1000)))
-#define pct(counter) ((counter) * 100.0 / st->qry_sent)
-
- const char *name = tcp ? "SYNs: " : quic ? "initials:" : "queries: ";
- printf("total %s %"PRIu64" (%"PRIu64" pps) (%f%%)\n", name, st->qry_sent,
- ps(st->qry_sent), 100.0 * st->qry_sent / (st->duration / 1000000.0 * qps));
- if (st->qry_sent > 0 && recv) {
- if (tcp || quic) {
- name = tcp ? "established:" : "handshakes: ";
- printf("total %s %"PRIu64" (%"PRIu64" pps) (%f%%)\n", name,
- st->synack_recv, ps(st->synack_recv), pct(st->synack_recv));
- }
- printf("total replies: %"PRIu64" (%"PRIu64" pps) (%f%%)\n",
- st->ans_recv, ps(st->ans_recv), pct(st->ans_recv));
- if (tcp) {
- printf("total closed: %"PRIu64" (%"PRIu64" pps) (%f%%)\n",
- st->finack_recv, ps(st->finack_recv), pct(st->finack_recv));
- }
- if (st->rst_recv > 0) {
- printf("total reset: %"PRIu64" (%"PRIu64" pps) (%f%%)\n",
- st->rst_recv, ps(st->rst_recv), pct(st->rst_recv));
- }
- printf("average DNS reply size: %"PRIu64" B\n",
- st->ans_recv > 0 ? st->size_recv / st->ans_recv : 0);
- printf("average Ethernet reply rate: %"PRIu64" bps (%.2f Mbps)\n",
- ps(st->wire_recv * 8), ps((float)st->wire_recv * 8 / (1000 * 1000)));
-
- for (int i = 0; i < RCODE_MAX; i++) {
- if (st->rcodes_recv[i] > 0) {
- const knot_lookup_t *rcode = knot_lookup_by_id(knot_rcode_names, i);
- const char *rcname = rcode == NULL ? "unknown" : rcode->name;
- int space = MAX(9 - strlen(rcname), 0);
- printf("responded %s: %.*s%"PRIu64"\n",
- rcname, space, " ", st->rcodes_recv[i]);
- }
- }
- }
- printf("duration: %"PRIu64" s\n", (st->duration / (1000 * 1000)));
-
- pthread_mutex_unlock(&st->mutex);
-}
-
-inline static void timer_start(struct timespec *timesp)
-{
- clock_gettime(CLOCK_MONOTONIC, timesp);
-}
-
-inline static uint64_t timer_end(struct timespec *timesp)
-{
- struct timespec end;
- clock_gettime(CLOCK_MONOTONIC, &end);
- uint64_t res = (end.tv_sec - timesp->tv_sec) * (uint64_t)1000000;
- res += ((int64_t)end.tv_nsec - timesp->tv_nsec) / 1000;
- return res;
}
static unsigned addr_bits(bool ipv6)
@@ -267,7 +99,8 @@ static unsigned addr_bits(bool ipv6)
return ipv6 ? 128 : 32;
}
-static void shuffle_sockaddr4(struct sockaddr_in *dst, struct sockaddr_in *src, uint64_t increment)
+static void shuffle_sockaddr4(struct sockaddr_in *dst, struct sockaddr_in *src,
+ uint64_t increment)
{
memcpy(&dst->sin_addr, &src->sin_addr, sizeof(dst->sin_addr));
if (increment > 0) {
@@ -275,7 +108,8 @@ static void shuffle_sockaddr4(struct sockaddr_in *dst, struct sockaddr_in *src,
}
}
-static void shuffle_sockaddr6(struct sockaddr_in6 *dst, struct sockaddr_in6 *src, uint64_t increment)
+static void shuffle_sockaddr6(struct sockaddr_in6 *dst, struct sockaddr_in6 *src,
+ uint64_t increment)
{
memcpy(&dst->sin6_addr, &src->sin6_addr, sizeof(dst->sin6_addr));
if (increment > 0) {
@@ -293,7 +127,8 @@ static void shuffle_sockaddr(struct sockaddr_in6 *dst, struct sockaddr_in6 *src,
if (src->sin6_family == AF_INET6) {
shuffle_sockaddr6(dst, src, increment);
} else {
- shuffle_sockaddr4((struct sockaddr_in *)dst, (struct sockaddr_in *)src, increment);
+ shuffle_sockaddr4((struct sockaddr_in *)dst, (struct sockaddr_in *)src,
+ increment);
}
}
@@ -311,7 +146,8 @@ static void next_payload(struct pkt_payload **payload, int increment)
}
}
-static void put_dns_payload(struct iovec *put_into, bool zero_copy, xdp_gun_ctx_t *ctx, struct pkt_payload **payl)
+static void put_dns_payload(struct iovec *put_into, bool zero_copy, xdp_gun_ctx_t *ctx,
+ struct pkt_payload **payl)
{
if (zero_copy) {
put_into->iov_base = (*payl)->payload;
@@ -472,19 +308,41 @@ static void quic_free_cb(knot_quic_reply_t *rpl)
}
#endif // ENABLE_QUIC
+static uint64_t timestamp_ns(void)
+{
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ return ((uint64_t)ts.tv_sec * 1000000000) + ts.tv_nsec;
+}
+
+static void timer_start(struct timespec *out)
+{
+ clock_gettime(CLOCK_MONOTONIC, out);
+}
+
+static uint64_t timer_end_ns(const struct timespec *start)
+{
+ struct timespec end;
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ uint64_t res = (end.tv_sec - start->tv_sec) * (uint64_t)1000000000;
+ res += end.tv_nsec - start->tv_nsec;
+ return res;
+}
+
void *xdp_gun_thread(void *_ctx)
{
xdp_gun_ctx_t *ctx = _ctx;
struct knot_xdp_socket *xsk = NULL;
- struct timespec timer;
knot_xdp_msg_t pkts[ctx->at_once];
- uint64_t errors = 0, lost = 0, duration = 0;
- kxdpgun_stats_t local_stats = { 0 };
+ uint64_t duration_us = 0;
+ struct timespec timer;
+ kxdpgun_stats_t local_stats = { 0 }; // cumulative stats of past periods excluding the current
+ kxdpgun_stats_t periodic_stats = { 0 }; // stats for the current period (see -S option)
unsigned stats_triggered = 0;
knot_tcp_table_t *tcp_table = NULL;
#ifdef ENABLE_QUIC
knot_quic_table_t *quic_table = NULL;
- struct knot_quic_creds *quic_creds = NULL;
+ struct knot_creds *quic_creds = NULL;
list_t quic_sessions;
init_list(&quic_sessions);
#endif // ENABLE_QUIC
@@ -501,12 +359,13 @@ void *xdp_gun_thread(void *_ctx)
}
if (ctx->quic) {
#ifdef ENABLE_QUIC
- quic_creds = knot_quic_init_creds_peer(NULL, NULL, 0);
+ quic_creds = knot_creds_init_peer(NULL, NULL, 0);
if (quic_creds == NULL) {
ERR2("failed to initialize QUIC context");
goto cleanup;
}
- quic_table = knot_quic_table_new(ctx->qps * 100, SIZE_MAX, SIZE_MAX, 1232, quic_creds);
+ quic_table = knot_quic_table_new(ctx->qps * 100, SIZE_MAX, SIZE_MAX,
+ 1232, quic_creds);
if (quic_table == NULL) {
ERR2("failed to allocate QUIC connection table");
goto cleanup;
@@ -517,12 +376,12 @@ void *xdp_gun_thread(void *_ctx)
#endif // ENABLE_QUIC
}
- knot_xdp_load_bpf_t mode = (ctx->thread_id == 0 ?
- KNOT_XDP_LOAD_BPF_ALWAYS : KNOT_XDP_LOAD_BPF_NEVER);
+ knot_xdp_load_bpf_t mode =
+ (ctx->thread_id == 0 ? KNOT_XDP_LOAD_BPF_ALWAYS : KNOT_XDP_LOAD_BPF_NEVER);
/*
* This mutex prevents libbpf from logging:
* 'libbpf: can't get link by id (5535): Resource temporarily unavailable'
- */
+ */
pthread_mutex_lock(&global_stats.mutex);
int ret = knot_xdp_init(&xsk, ctx->dev, ctx->thread_id, ctx->flags,
LOCAL_PORT_MIN, LOCAL_PORT_MIN, mode, &ctx->xdp_config);
@@ -534,13 +393,7 @@ void *xdp_gun_thread(void *_ctx)
}
if (ctx->thread_id == 0) {
- INFO2("using interface %s, XDP threads %u, IPv%c/%s%s%s, %s mode",
- ctx->dev, ctx->n_threads, (ctx->ipv6 ? '6' : '4'),
- (ctx->tcp ? "TCP" : ctx->quic ? "QUIC" : "UDP"),
- (ctx->sending_mode[0] != '\0' ? " mode " : ""),
- (ctx->sending_mode[0] != '\0' ? ctx->sending_mode : ""),
- (knot_eth_xdp_mode(if_nametoindex(ctx->dev)) == KNOT_XDP_MODE_FULL ?
- "native" : "emulated"));
+ STATS_HDR(ctx);
}
struct pollfd pfd = { knot_xdp_socket_fd(xsk), POLLIN, 0 };
@@ -576,7 +429,7 @@ void *xdp_gun_thread(void *_ctx)
ctx->target_ip.sin6_port = htobe16(ctx->target_port);
knot_sweep_stats_t sweep_stats = { 0 };
- uint16_t local_ports[QUIC_THREAD_PORTS];
+ uint16_t local_ports[QUIC_THREAD_PORTS] = { 0 };
uint16_t port = LOCAL_PORT_MIN;
for (int i = 0; ctx->quic && i < QUIC_THREAD_PORTS; ++i) {
local_ports[i] = adjust_port(ctx, port);
@@ -586,24 +439,25 @@ void *xdp_gun_thread(void *_ctx)
size_t local_ports_it = 0;
#endif // ENABLE_QUIC
+ local_stats.since = periodic_stats.since = timestamp_ns();
timer_start(&timer);
+ ctx->stats_start_us = local_stats.since / 1000;
- while (duration < ctx->duration + extra_wait) {
-
+ while (duration_us < ctx->duration + extra_wait) {
// sending part
- if (duration < ctx->duration) {
+ if (duration_us < ctx->duration) {
while (1) {
knot_xdp_send_prepare(xsk);
unsigned alloced = alloc_pkts(pkts, xsk, ctx, tick);
if (alloced < ctx->at_once) {
- lost += ctx->at_once - alloced;
+ periodic_stats.lost += ctx->at_once - alloced;
if (alloced == 0) {
break;
}
}
if (ctx->tcp) {
- for (int i = 0; i < alloced; i++) {
+ for (uint32_t i = 0; i < alloced; i++) {
pkts[i].payload.iov_len = 0;
if (!EMPTY_LIST(reuse_conns)) {
@@ -621,7 +475,7 @@ void *xdp_gun_thread(void *_ctx)
}
if (ret == KNOT_EOK) {
pkts[i].flags &= ~KNOT_XDP_MSG_SYN; // skip sending respective packet
- local_stats.qry_sent++;
+ periodic_stats.qry_sent++;
}
free(rl);
}
@@ -670,14 +524,14 @@ void *xdp_gun_thread(void *_ctx)
(ctx->ignore1 & KXDPGUN_IGNORE_LASTBYTE) ? KNOT_QUIC_SEND_IGNORE_LASTBYTE : 0);
}
if (ret == KNOT_EOK) {
- local_stats.qry_sent++;
+ periodic_stats.qry_sent++;
}
}
(void)knot_xdp_send_finish(xsk);
#endif // ENABLE_QUIC
break;
} else {
- for (int i = 0; i < alloced; i++) {
+ for (uint32_t i = 0; i < alloced; i++) {
put_dns_payload(&pkts[i].payload, false,
ctx, &payload_ptr);
}
@@ -685,9 +539,9 @@ void *xdp_gun_thread(void *_ctx)
uint32_t really_sent = 0;
if (knot_xdp_send(xsk, pkts, alloced, &really_sent) != KNOT_EOK) {
- lost += alloced;
+ periodic_stats.lost += alloced;
}
- local_stats.qry_sent += really_sent;
+ periodic_stats.qry_sent += really_sent;
(void)knot_xdp_send_finish(xsk);
break;
@@ -699,7 +553,7 @@ void *xdp_gun_thread(void *_ctx)
while (1) {
ret = poll(&pfd, 1, 0);
if (ret < 0) {
- errors++;
+ periodic_stats.errors++;
break;
}
if (!pfd.revents) {
@@ -714,18 +568,19 @@ void *xdp_gun_thread(void *_ctx)
}
if (ctx->tcp) {
knot_tcp_relay_t relays[recvd];
- ret = knot_tcp_recv(relays, pkts, recvd, tcp_table, NULL, ctx->ignore2);
- if (ret != KNOT_EOK) {
- errors++;
- break;
- }
for (size_t i = 0; i < recvd; i++) {
knot_tcp_relay_t *rl = &relays[i];
+ ret = knot_tcp_recv(rl, &pkts[i], tcp_table, NULL, ctx->ignore2);
+ if (ret != KNOT_EOK) {
+ periodic_stats.errors++;
+ continue;
+ }
+
struct iovec payl;
switch (rl->action) {
case XDP_TCP_ESTABLISH:
- local_stats.synack_recv++;
+ periodic_stats.synack_recv++;
if (ctx->ignore1 & KXDPGUN_IGNORE_QUERY) {
break;
}
@@ -734,20 +589,20 @@ void *xdp_gun_thread(void *_ctx)
(ctx->ignore1 & KXDPGUN_IGNORE_LASTBYTE),
payl.iov_base, payl.iov_len);
if (ret != KNOT_EOK) {
- errors++;
+ periodic_stats.errors++;
}
break;
case XDP_TCP_CLOSE:
- local_stats.finack_recv++;
+ periodic_stats.finack_recv++;
break;
case XDP_TCP_RESET:
- local_stats.rst_recv++;
+ periodic_stats.rst_recv++;
break;
default:
break;
}
for (size_t j = 0; rl->inbf != NULL && j < rl->inbf->n_inbufs; j++) {
- if (check_dns_payload(&rl->inbf->inbufs[j], ctx, &local_stats)) {
+ if (check_dns_payload(&rl->inbf->inbufs[j], ctx, &periodic_stats)) {
if (!(ctx->ignore1 & KXDPGUN_IGNORE_CLOSE)) {
rl->answer = XDP_TCP_CLOSE;
} else if ((ctx->ignore1 & KXDPGUN_REUSE_CONN)) {
@@ -763,7 +618,7 @@ void *xdp_gun_thread(void *_ctx)
ret = knot_tcp_send(xsk, relays, recvd, ctx->at_once);
if (ret != KNOT_EOK) {
- errors++;
+ periodic_stats.errors++;
}
(void)knot_xdp_send_finish(xsk);
@@ -781,11 +636,11 @@ void *xdp_gun_thread(void *_ctx)
ret = knot_quic_handle(quic_table, &quic_reply, 5000000000L, &conn);
if (ret == KNOT_ECONN) {
- local_stats.rst_recv++;
+ periodic_stats.rst_recv++;
knot_quic_cleanup(&conn, 1);
continue;
} else if (ret != 0) {
- errors++;
+ periodic_stats.errors++;
knot_quic_cleanup(&conn, 1);
break;
}
@@ -805,7 +660,7 @@ void *xdp_gun_thread(void *_ctx)
if ((conn->flags & KNOT_QUIC_CONN_HANDSHAKE_DONE) && conn->streams_count == -1) {
conn->streams_count = 1;
- local_stats.synack_recv++;
+ periodic_stats.synack_recv++;
if ((ctx->ignore1 & KXDPGUN_IGNORE_QUERY)) {
knot_quic_table_rem(conn, quic_table);
knot_quic_cleanup(&conn, 1);
@@ -822,14 +677,14 @@ void *xdp_gun_thread(void *_ctx)
if ((ctx->ignore2 & XDP_TCP_IGNORE_ESTABLISH)) {
knot_quic_table_rem(conn, quic_table);
knot_quic_cleanup(&conn, 1);
- local_stats.synack_recv++;
+ periodic_stats.synack_recv++;
continue;
}
int64_t s0id;
knot_quic_stream_t *stream0 = knot_quic_stream_get_process(conn, &s0id);
if (stream0 != NULL && stream0->inbufs != NULL && stream0->inbufs->n_inbufs > 0) {
- check_dns_payload(&stream0->inbufs->inbufs[0], ctx, &local_stats);
+ check_dns_payload(&stream0->inbufs->inbufs[0], ctx, &periodic_stats);
stream0->inbufs->n_inbufs = 0; // signal that data have been read out
if ((ctx->ignore2 & XDP_TCP_IGNORE_DATA_ACK)) {
@@ -837,7 +692,9 @@ void *xdp_gun_thread(void *_ctx)
knot_quic_cleanup(&conn, 1);
continue;
} else if ((ctx->ignore1 & KXDPGUN_REUSE_CONN)) {
- if (conn->streams_count > 1) { // keep the number of outstanding streams below MAX_STREAMS_PER_CONN, while preserving at least one at all times
+ /* keep the number of outstanding streams below MAX_STREAMS_PER_CONN,
+ * while preserving at least one at all times */
+ if (conn->streams_count > 1) {
knot_quic_conn_stream_free(conn, conn->streams_first * 4);
}
ptrlist_add(&reuse_conns, conn, NULL);
@@ -846,30 +703,31 @@ void *xdp_gun_thread(void *_ctx)
ret = knot_quic_send(quic_table, conn, &quic_reply, 4,
(ctx->ignore1 & KXDPGUN_IGNORE_LASTBYTE) ? KNOT_QUIC_SEND_IGNORE_LASTBYTE : 0);
if (ret != KNOT_EOK) {
- errors++;
+ periodic_stats.errors++;
}
- if (!(ctx->ignore1 & KXDPGUN_IGNORE_CLOSE) && (conn->flags & KNOT_QUIC_CONN_SESSION_TAKEN) &&
- stream0 != NULL && stream0->inbufs != NULL && stream0->inbufs->n_inbufs == 0) {
+ if (!(ctx->ignore1 & KXDPGUN_IGNORE_CLOSE)
+ && (conn->flags & KNOT_QUIC_CONN_SESSION_TAKEN)
+ && stream0 != NULL && stream0->inbufs != NULL
+ && stream0->inbufs->n_inbufs == 0) {
assert(!(ctx->ignore2 & XDP_TCP_IGNORE_DATA_ACK));
quic_reply.handle_ret = KNOT_QUIC_HANDLE_RET_CLOSE;
ret = knot_quic_send(quic_table, conn, &quic_reply, 1, 0);
knot_quic_table_rem(conn, quic_table);
knot_quic_cleanup(&conn, 1);
if (ret != KNOT_EOK) {
- errors++;
+ periodic_stats.errors++;
}
}
}
(void)knot_xdp_send_finish(xsk);
#endif // ENABLE_QUIC
} else {
- for (int i = 0; i < recvd; i++) {
- (void)check_dns_payload(&pkts[i].payload, ctx,
- &local_stats);
+ for (uint32_t i = 0; i < recvd; i++) {
+ check_dns_payload(&pkts[i].payload, ctx, &periodic_stats);
}
}
- local_stats.wire_recv += wire;
+ periodic_stats.wire_recv += wire;
knot_xdp_recv_finish(xsk, pkts, recvd);
pfd.revents = 0;
}
@@ -882,47 +740,59 @@ void *xdp_gun_thread(void *_ctx)
#endif // ENABLE_QUIC
// speed and signal part
- uint64_t dura_exp = (local_stats.qry_sent * 1000000) / ctx->qps;
- duration = timer_end(&timer);
- if (xdp_trigger == KXDPGUN_STOP && ctx->duration > duration) {
- ctx->duration = duration;
+ uint64_t duration_ns = timer_end_ns(&timer);
+ duration_us = duration_ns / 1000;
+ uint64_t dura_exp = ((local_stats.qry_sent + periodic_stats.qry_sent) * 1000000) / ctx->qps;
+ if (ctx->thread_id == 0 && ctx->stats_period != 0 && global_stats.collected == 0
+ && (duration_ns - (periodic_stats.since - local_stats.since)) >= ctx->stats_period) {
+ ATOMIC_SET(stats_switch, STATS_PERIODIC);
+ ATOMIC_ADD(stats_trigger, 1);
}
- if (stats_trigger > stats_triggered) {
- assert(stats_trigger == stats_triggered + 1);
- stats_triggered++;
- local_stats.duration = duration;
- size_t collected = collect_stats(&global_stats, &local_stats);
+ if (xdp_trigger == KXDPGUN_STOP && ctx->duration > duration_us) {
+ ctx->duration = duration_us;
+ }
+ uint64_t tmp_stats_trigger = ATOMIC_GET(stats_trigger);
+ if (duration_us < ctx->duration && tmp_stats_trigger > stats_triggered) {
+ bool tmp_stats_switch = ATOMIC_GET(stats_switch);
+ stats_triggered = tmp_stats_trigger;
+
+ local_stats.until = periodic_stats.until = local_stats.since + duration_ns;
+ kxdpgun_stats_t cumulative_stats = periodic_stats;
+ if (tmp_stats_switch == STATS_PERIODIC) {
+ collect_periodic_stats(&local_stats, &periodic_stats);
+ clear_stats(&periodic_stats);
+ periodic_stats.since = local_stats.since + duration_ns;
+ } else {
+ collect_periodic_stats(&cumulative_stats, &local_stats);
+ cumulative_stats.since = local_stats.since;
+ }
+
+ size_t collected = collect_stats(&global_stats, &cumulative_stats);
+
assert(collected <= ctx->n_threads);
if (collected == ctx->n_threads) {
- print_stats(&global_stats, ctx->tcp, ctx->quic,
- !(ctx->flags & KNOT_XDP_FILTER_DROP),
- ctx->qps * ctx->n_threads);
+ STATS_FMT(ctx, &global_stats, tmp_stats_switch);
+ if (!JSON_MODE(*ctx)) {
+ puts(STATS_SECTION_SEP);
+ }
clear_stats(&global_stats);
+ ATOMIC_SET(stats_switch, STATS_SUM);
}
}
- if (dura_exp > duration) {
- usleep(dura_exp - duration);
+ if (dura_exp > duration_us) {
+ usleep(dura_exp - duration_us);
}
- if (duration > ctx->duration) {
+ if (duration_us > ctx->duration) {
usleep(1000);
}
tick++;
}
+ periodic_stats.until = local_stats.since + timer_end_ns(&timer) - extra_wait * 1000;
+ collect_periodic_stats(&local_stats, &periodic_stats);
+
+ STATS_THRD(ctx, &local_stats);
- char recv_str[40] = "", lost_str[40] = "", err_str[40] = "";
- if (!(ctx->flags & KNOT_XDP_FILTER_DROP)) {
- (void)snprintf(recv_str, sizeof(recv_str), ", received %"PRIu64, local_stats.ans_recv);
- }
- if (lost > 0) {
- (void)snprintf(lost_str, sizeof(lost_str), ", lost %"PRIu64, lost);
- }
- if (errors > 0) {
- (void)snprintf(err_str, sizeof(err_str), ", errors %"PRIu64, errors);
- }
- INFO2("thread#%02u: sent %"PRIu64"%s%s%s",
- ctx->thread_id, local_stats.qry_sent, recv_str, lost_str, err_str);
- local_stats.duration = ctx->duration;
collect_stats(&global_stats, &local_stats);
cleanup:
@@ -943,7 +813,7 @@ cleanup:
WALK_LIST_DELSAFE(n, nxt, quic_sessions) {
knot_quic_session_load(NULL, n);
}
- knot_quic_free_creds(quic_creds);
+ knot_creds_free(quic_creds);
#endif // ENABLE_QUIC
return NULL;
@@ -1118,30 +988,33 @@ static void print_help(void)
printf("Usage: %s [options] -i <queries_file> <dest_ip>\n"
"\n"
"Options:\n"
- " -t, --duration <sec> "SPACE"Duration of traffic generation.\n"
- " "SPACE" (default is %"PRIu64" seconds)\n"
- " -T, --tcp[=debug_mode] "SPACE"Send queries over TCP.\n"
- " -U, --quic[=debug_mode] "SPACE"Send queries over QUIC.\n"
- " -Q, --qps <qps> "SPACE"Number of queries-per-second (approximately) to be sent.\n"
- " "SPACE" (default is %"PRIu64" qps)\n"
- " -b, --batch <size> "SPACE"Send queries in a batch of defined size.\n"
- " "SPACE" (default is %d for UDP, %d for TCP)\n"
- " -r, --drop "SPACE"Drop incoming responses (disables response statistics).\n"
- " -p, --port <port> "SPACE"Remote destination port.\n"
- " "SPACE" (default is %d for UDP/TCP, %u for QUIC)\n"
- " -F, --affinity <spec> "SPACE"CPU affinity in the format [<cpu_start>][s<cpu_step>].\n"
- " "SPACE" (default is %s)\n"
- " -i, --infile <file> "SPACE"Path to a file with query templates.\n"
- " -I, --interface <ifname> "SPACE"Override auto-detected interface for outgoing communication.\n"
- " -l, --local <ip[/prefix]>"SPACE"Override auto-detected source IP address or subnet.\n"
- " -L, --local-mac <MAC> "SPACE"Override auto-detected local MAC address.\n"
- " -R, --remote-mac <MAC> "SPACE"Override auto-detected remote MAC address.\n"
- " -v, --vlan <id> "SPACE"Add VLAN 802.1Q header with the given id.\n"
- " -e, --edns-size <size> "SPACE"EDNS UDP payload size, range 512-4096 (default 1232)\n"
- " -m, --mode <mode> "SPACE"Set XDP mode (auto, copy, generic).\n"
- " -G, --qlog <path> "SPACE"Output directory for qlog (useful for QUIC only).\n"
- " -h, --help "SPACE"Print the program help.\n"
- " -V, --version "SPACE"Print the program version.\n"
+ " -t, --duration <sec> "SPACE"Duration of traffic generation.\n"
+ " "SPACE" (default is %"PRIu64" seconds)\n"
+ " -T, --tcp[=debug_mode] "SPACE"Send queries over TCP.\n"
+ " -U, --quic[=debug_mode] "SPACE"Send queries over QUIC.\n"
+ " -Q, --qps <qps> "SPACE"Number of queries-per-second (approximately) to be sent.\n"
+ " "SPACE" (default is %"PRIu64" qps)\n"
+ " -b, --batch <size> "SPACE"Send queries in a batch of defined size.\n"
+ " "SPACE" (default is %d for UDP, %d for TCP)\n"
+ " -r, --drop "SPACE"Drop incoming responses (disables response statistics).\n"
+ " -p, --port <port> "SPACE"Remote destination port.\n"
+ " "SPACE" (default is %d for UDP/TCP, %u for QUIC)\n"
+ " -F, --affinity <spec> "SPACE"CPU affinity in the format [<cpu_start>][s<cpu_step>].\n"
+ " "SPACE" (default is %s)\n"
+ " -I, --interface <ifname> "SPACE"Override auto-detected interface for outgoing communication.\n"
+ " -i, --infile <file> "SPACE"Path to a file with query templates.\n"
+ " -B, --binary "SPACE"Specify that input file is in binary format (<length:2><wire:length>).\n"
+ " -l, --local <ip[/prefix]> "SPACE"Override auto-detected source IP address or subnet.\n"
+ " -L, --local-mac <MAC> "SPACE"Override auto-detected local MAC address.\n"
+ " -R, --remote-mac <MAC> "SPACE"Override auto-detected remote MAC address.\n"
+ " -v, --vlan <id> "SPACE"Add VLAN 802.1Q header with the given id.\n"
+ " -e, --edns-size <size> "SPACE"EDNS UDP payload size, range 512-4096 (default 1232)\n"
+ " -m, --mode <mode> "SPACE"Set XDP mode (auto, copy, generic).\n"
+ " -G, --qlog <path> "SPACE"Output directory for qlog (useful for QUIC only).\n"
+ " -j, --json "SPACE"Output statistics in json.\n"
+ " -S, --stats-period <period>"SPACE"Enable periodic statistics printout in milliseconds.\n"
+ " -h, --help "SPACE"Print the program help.\n"
+ " -V, --version "SPACE"Print the program version.\n"
"\n"
"Parameters:\n"
" <dest_ip> "SPACE"IPv4 or IPv6 address of the remote destination.\n",
@@ -1240,40 +1113,45 @@ static int set_mode(const char *arg, knot_xdp_config_t *config)
static bool get_opts(int argc, char *argv[], xdp_gun_ctx_t *ctx)
{
+ const char *opts_str = "hV::t:Q:b:rp:T::U::F:I:i:Bl:L:R:v:e:m:G:jS:";
struct option opts[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
- { "duration", required_argument, NULL, 't' },
- { "qps", required_argument, NULL, 'Q' },
- { "batch", required_argument, NULL, 'b' },
- { "drop", no_argument, NULL, 'r' },
- { "port", required_argument, NULL, 'p' },
- { "tcp", optional_argument, NULL, 'T' },
- { "quic", optional_argument, NULL, 'U' },
- { "affinity", required_argument, NULL, 'F' },
- { "interface", required_argument, NULL, 'I' },
- { "local", required_argument, NULL, 'l' },
- { "infile", required_argument, NULL, 'i' },
- { "local-mac", required_argument, NULL, 'L' },
- { "remote-mac", required_argument, NULL, 'R' },
- { "vlan", required_argument, NULL, 'v' },
- { "edns-size", required_argument, NULL, 'e' },
- { "mode", required_argument, NULL, 'm' },
- { "qlog", required_argument, NULL, 'G' },
- { NULL }
+ { "help", no_argument, NULL, 'h' },
+ { "version", optional_argument, NULL, 'V' },
+ { "duration", required_argument, NULL, 't' },
+ { "qps", required_argument, NULL, 'Q' },
+ { "batch", required_argument, NULL, 'b' },
+ { "drop", no_argument, NULL, 'r' },
+ { "port", required_argument, NULL, 'p' },
+ { "tcp", optional_argument, NULL, 'T' },
+ { "quic", optional_argument, NULL, 'U' },
+ { "affinity", required_argument, NULL, 'F' },
+ { "interface", required_argument, NULL, 'I' },
+ { "infile", required_argument, NULL, 'i' },
+ { "binary", no_argument, NULL, 'B' },
+ { "local", required_argument, NULL, 'l' },
+ { "local-mac", required_argument, NULL, 'L' },
+ { "remote-mac", required_argument, NULL, 'R' },
+ { "vlan", required_argument, NULL, 'v' },
+ { "edns-size", required_argument, NULL, 'e' },
+ { "mode", required_argument, NULL, 'm' },
+ { "qlog", required_argument, NULL, 'G' },
+ { "json", no_argument, NULL, 'j' },
+ { "stats-period", required_argument, NULL, 'S' },
+ { 0 }
};
int opt = 0, arg;
bool default_at_once = true;
double argf;
- char *argcp, *local_ip = NULL, *filename = NULL;
- while ((opt = getopt_long(argc, argv, "hVt:Q:b:rp:T::U::F:I:l:i:L:R:v:e:m:G:", opts, NULL)) != -1) {
+ char *argcp, *local_ip = NULL;
+ input_t input = { .format = TXT };
+ while ((opt = getopt_long(argc, argv, opts_str, opts, NULL)) != -1) {
switch (opt) {
case 'h':
print_help();
exit(EXIT_SUCCESS);
case 'V':
- print_version(PROGRAM_NAME);
+ print_version(PROGRAM_NAME, optarg != NULL);
exit(EXIT_SUCCESS);
case 't':
assert(optarg);
@@ -1366,12 +1244,15 @@ static bool get_opts(int argc, char *argv[], xdp_gun_ctx_t *ctx)
case 'I':
strlcpy(ctx->dev, optarg, IFNAMSIZ);
break;
+ case 'i':
+ input.path = optarg;
+ break;
+ case 'B':
+ input.format = BIN;
+ break;
case 'l':
local_ip = optarg;
break;
- case 'i':
- filename = optarg;
- break;
case 'L':
if (mac_sscan(optarg, ctx->local_mac) != KNOT_EOK) {
ERR2("invalid local MAC address '%s'", optarg);
@@ -1415,17 +1296,33 @@ static bool get_opts(int argc, char *argv[], xdp_gun_ctx_t *ctx)
case 'G':
ctx->qlog_dir = optarg;
break;
+ case 'S':
+ assert(optarg);
+ arg = atoi(optarg);
+ if (arg > 0) {
+ ctx->stats_period = arg * 1000000; // convert to ns
+ } else {
+ ERR2("period must be a positive integer\n");
+ return false;
+ }
+ break;
+ case 'j':
+ if ((ctx->jw = jsonw_new(stdout, JSON_INDENT)) == NULL) {
+ ERR2("failed to use JSON");
+ return false;
+ }
+ break;
default:
print_help();
return false;
}
}
- if (filename == NULL) {
+ if (input.path == NULL) {
print_help();
return false;
}
size_t qcount = ctx->duration / 1000000 * ctx->qps;
- if (!load_queries(filename, ctx->edns_size, ctx->msgid, qcount)) {
+ if (!load_queries(&input, ctx->edns_size, ctx->msgid, qcount)) {
return false;
}
if (global_payloads == NULL || argc - optind != 1) {
@@ -1452,25 +1349,29 @@ static bool get_opts(int argc, char *argv[], xdp_gun_ctx_t *ctx)
int main(int argc, char *argv[])
{
+ int ecode = EXIT_FAILURE;
+
xdp_gun_ctx_t ctx = ctx_defaults, *thread_ctxs = NULL;
ctx.msgid = time(NULL) % UINT16_MAX;
+ ctx.runid = timestamp_ns() / 1000;
+ ctx.argv = argv;
pthread_t *threads = NULL;
if (!get_opts(argc, argv, &ctx)) {
- free_global_payloads();
- return EXIT_FAILURE;
+ goto err;
+ }
+
+ if (JSON_MODE(ctx)) {
+ jsonw_list(ctx.jw, NULL); // wrap the json in a list, for syntactic correctness
}
thread_ctxs = calloc(ctx.n_threads, sizeof(*thread_ctxs));
threads = calloc(ctx.n_threads, sizeof(*threads));
if (thread_ctxs == NULL || threads == NULL) {
ERR2("out of memory");
- free(thread_ctxs);
- free(threads);
- free_global_payloads();
- return EXIT_FAILURE;
+ goto err;
}
- for (int i = 0; i < ctx.n_threads; i++) {
+ for (uint32_t i = 0; i < ctx.n_threads; i++) {
thread_ctxs[i] = ctx;
thread_ctxs[i].thread_id = i;
}
@@ -1482,8 +1383,7 @@ int main(int argc, char *argv[])
cur_limit.rlim_max != min_limit.rlim_max) {
int ret = setrlimit(RLIMIT_MEMLOCK, &min_limit);
if (ret != 0) {
- WARN2("unable to increase RLIMIT_MEMLOCK: %s",
- strerror(errno));
+ WARN2("unable to increase RLIMIT_MEMLOCK: %s", strerror(errno));
}
}
}
@@ -1509,22 +1409,30 @@ int main(int argc, char *argv[])
usleep(20000);
}
usleep(1000000);
-
xdp_trigger = KXDPGUN_START;
usleep(1000000);
for (size_t i = 0; i < ctx.n_threads; i++) {
pthread_join(threads[i], NULL);
}
- if (global_stats.duration > 0 && global_stats.qry_sent > 0) {
- print_stats(&global_stats, ctx.tcp, ctx.quic, !(ctx.flags & KNOT_XDP_FILTER_DROP), ctx.qps * ctx.n_threads);
+ if (DURATION_US(global_stats) > 0 && global_stats.qry_sent > 0) {
+ if (!JSON_MODE(ctx)) {
+ puts(STATS_SECTION_SEP);
+ }
+ STATS_FMT(&ctx, &global_stats, STATS_SUM);
}
pthread_mutex_destroy(&global_stats.mutex);
+ ecode = EXIT_SUCCESS;
+
+err:
free(ctx.rss_conf);
free(thread_ctxs);
free(threads);
free_global_payloads();
-
- return EXIT_SUCCESS;
+ if (JSON_MODE(ctx)) {
+ jsonw_end(ctx.jw);
+ jsonw_free(&ctx.jw);
+ }
+ return ecode;
}
diff --git a/src/utils/kxdpgun/main.h b/src/utils/kxdpgun/main.h
new file mode 100644
index 0000000..d87aee8
--- /dev/null
+++ b/src/utils/kxdpgun/main.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+
+#include "contrib/json.h"
+#include "libknot/xdp/eth.h"
+#include "libknot/xdp/tcp.h"
+
+#define PROGRAM_NAME "kxdpgun"
+#define SPACE " "
+
+#define REMOTE_PORT_DEFAULT 53
+#define REMOTE_PORT_DOQ_DEFAULT 853
+#define LOCAL_PORT_MIN 2000
+#define LOCAL_PORT_MAX 65535
+#define QUIC_THREAD_PORTS 100
+
+enum {
+ KXDPGUN_WAIT,
+ KXDPGUN_START,
+ KXDPGUN_STOP,
+};
+
+typedef enum {
+ KXDPGUN_IGNORE_NONE = 0,
+ KXDPGUN_IGNORE_QUERY = (1 << 0),
+ KXDPGUN_IGNORE_LASTBYTE = (1 << 1),
+ KXDPGUN_IGNORE_CLOSE = (1 << 2),
+ KXDPGUN_REUSE_CONN = (1 << 3),
+} xdp_gun_ignore_t;
+
+typedef struct xdp_gun_ctx {
+ union {
+ struct sockaddr_in local_ip4;
+ struct sockaddr_in6 local_ip;
+ struct sockaddr_storage local_ip_ss;
+ };
+ union {
+ struct sockaddr_in target_ip4;
+ struct sockaddr_in6 target_ip;
+ struct sockaddr_storage target_ip_ss;
+ };
+ char dev[IFNAMSIZ];
+ uint64_t qps, duration;
+ uint64_t runid;
+ uint64_t stats_start_us;
+ uint32_t stats_period; // 0 means no periodic stats
+ unsigned at_once;
+ uint16_t msgid;
+ uint16_t edns_size;
+ uint16_t vlan_tci;
+ uint8_t local_mac[6], target_mac[6];
+ uint8_t local_ip_range;
+ bool ipv6;
+ bool tcp;
+ bool quic;
+ bool quic_full_handshake;
+ const char *qlog_dir;
+ const char *sending_mode;
+ xdp_gun_ignore_t ignore1;
+ knot_tcp_ignore_t ignore2;
+ uint16_t target_port;
+ knot_xdp_filter_flag_t flags;
+ unsigned n_threads, thread_id;
+ knot_eth_rss_conf_t *rss_conf;
+ jsonw_t *jw;
+ char **argv;
+ knot_xdp_config_t xdp_config;
+} xdp_gun_ctx_t;
diff --git a/src/utils/kxdpgun/stats.c b/src/utils/kxdpgun/stats.c
new file mode 100644
index 0000000..f1e4f43
--- /dev/null
+++ b/src/utils/kxdpgun/stats.c
@@ -0,0 +1,292 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "contrib/macros.h"
+#include "libknot/codes.h"
+#include "libknot/lookup.h"
+#include "utils/common/msg.h"
+#include "utils/kxdpgun/main.h"
+#include "utils/kxdpgun/stats.h"
+
+pthread_mutex_t stdout_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+void clear_stats(kxdpgun_stats_t *st)
+{
+ pthread_mutex_lock(&st->mutex);
+ st->since = 0;
+ st->until = 0;
+ st->qry_sent = 0;
+ st->synack_recv = 0;
+ st->ans_recv = 0;
+ st->finack_recv = 0;
+ st->rst_recv = 0;
+ st->size_recv = 0;
+ st->wire_recv = 0;
+ st->collected = 0;
+ st->lost = 0;
+ st->errors = 0;
+ memset(st->rcodes_recv, 0, sizeof(st->rcodes_recv));
+ pthread_mutex_unlock(&st->mutex);
+}
+
+size_t collect_stats(kxdpgun_stats_t *into, const kxdpgun_stats_t *what)
+{
+ pthread_mutex_lock(&into->mutex);
+ into->since = what->since;
+ collect_periodic_stats(into, what);
+ size_t res = ++into->collected;
+ pthread_mutex_unlock(&into->mutex);
+ return res;
+}
+
+void collect_periodic_stats(kxdpgun_stats_t *into, const kxdpgun_stats_t *what)
+{
+ into->until = what->until;
+ into->qry_sent += what->qry_sent;
+ into->synack_recv += what->synack_recv;
+ into->ans_recv += what->ans_recv;
+ into->finack_recv += what->finack_recv;
+ into->rst_recv += what->rst_recv;
+ into->size_recv += what->size_recv;
+ into->wire_recv += what->wire_recv;
+ into->lost += what->lost;
+ into->errors += what->errors;
+ for (int i = 0; i < RCODE_MAX; i++) {
+ into->rcodes_recv[i] += what->rcodes_recv[i];
+ }
+}
+
+void plain_stats_header(const xdp_gun_ctx_t *ctx)
+{
+ INFO2("using interface %s, XDP threads %u, IPv%c/%s%s%s, %s mode", ctx->dev, ctx->n_threads,
+ (ctx->ipv6 ? '6' : '4'),
+ (ctx->tcp ? "TCP" : ctx->quic ? "QUIC" : "UDP"),
+ (ctx->sending_mode[0] != '\0' ? " mode " : ""),
+ (ctx->sending_mode[0] != '\0' ? ctx->sending_mode : ""),
+ (knot_eth_xdp_mode(if_nametoindex(ctx->dev)) == KNOT_XDP_MODE_FULL ? "native" : "emulated"));
+ puts(STATS_SECTION_SEP);
+}
+
+/* see:
+ * - https://github.com/DNS-OARC/dns-metrics/blob/main/dns-metrics.schema.json
+ * - https://github.com/DNS-OARC/dns-metrics/issues/16#issuecomment-2139462920
+ */
+void json_stats_header(const xdp_gun_ctx_t *ctx)
+{
+ jsonw_t *w = ctx->jw;
+
+ jsonw_object(w, NULL);
+ {
+ jsonw_ulong(w, "runid", ctx->runid);
+ jsonw_str(w, "type", "header");
+ jsonw_int(w, "schema_version", STATS_SCHEMA_VERSION);
+ jsonw_str(w, "generator", PROGRAM_NAME);
+ jsonw_str(w, "generator_version", PACKAGE_VERSION);
+
+ jsonw_list(w, "generator_params");
+ {
+ for (char **it = ctx->argv; *it != NULL; ++it) {
+ jsonw_str(w, NULL, *it);
+ }
+ }
+ jsonw_end(w);
+
+ jsonw_ulong(w, "time_units_per_sec", 1000000000);
+ if (ctx->stats_period > 0) {
+ jsonw_double(w, "stats_interval", ctx->stats_period / 1000.0);
+ }
+ // TODO: timeout
+
+ // mirror the info given by the plaintext printout
+ jsonw_object(w, "additional_info");
+ {
+ jsonw_str(w, "interface", ctx->dev);
+ jsonw_int(w, "xdp_threads", ctx->n_threads);
+ jsonw_int(w, "ip_version", ctx->ipv6 ? 6 : 4);
+ jsonw_str(w, "transport_layer_proto", ctx->tcp ? "TCP" : (ctx->quic ? "QUIC" : "UDP"));
+ jsonw_object(w, "mode_info");
+ {
+ if (ctx->sending_mode[0] != '\0') {
+ jsonw_str(w, "debug", ctx->sending_mode);
+ }
+ jsonw_str(w, "mode", knot_eth_xdp_mode(if_nametoindex(ctx->dev)) == KNOT_XDP_MODE_FULL
+ ? "native"
+ : "emulated");
+ }
+ jsonw_end(w);
+ }
+ jsonw_end(w);
+ }
+ jsonw_end(w);
+}
+
+void plain_thrd_summary(const xdp_gun_ctx_t *ctx, const kxdpgun_stats_t *st)
+{
+ pthread_mutex_lock(&stdout_mtx);
+
+ char recv_str[40] = "", lost_str[40] = "", err_str[40] = "";
+ if (!(ctx->flags & KNOT_XDP_FILTER_DROP)) {
+ (void)snprintf(recv_str, sizeof(recv_str), ", received %"PRIu64, st->ans_recv);
+ }
+ if (st->lost > 0) {
+ (void)snprintf(lost_str, sizeof(lost_str), ", lost %"PRIu64, st->lost);
+ }
+ if (st->errors > 0) {
+ (void)snprintf(err_str, sizeof(err_str), ", errors %"PRIu64, st->errors);
+ }
+ INFO2("thread#%02u: sent %"PRIu64"%s%s%s",
+ ctx->thread_id, st->qry_sent, recv_str, lost_str, err_str);
+
+ pthread_mutex_unlock(&stdout_mtx);
+}
+
+void json_thrd_summary(const xdp_gun_ctx_t *ctx, const kxdpgun_stats_t *st)
+{
+ pthread_mutex_lock(&stdout_mtx);
+
+ jsonw_t *w = ctx->jw;
+
+ jsonw_object(ctx->jw, NULL);
+ {
+ jsonw_str(w, "type", "thread_summary");
+ jsonw_ulong(w, "runid", ctx->runid);
+ jsonw_ulong(w, "subid", ctx->thread_id);
+ jsonw_ulong(w, "qry_sent", st->qry_sent);
+ jsonw_ulong(w, "ans_recv", st->ans_recv);
+ jsonw_ulong(w, "lost", st->lost);
+ jsonw_ulong(w, "errors", st->errors);
+ }
+ jsonw_end(ctx->jw);
+
+ pthread_mutex_unlock(&stdout_mtx);
+}
+
+void plain_stats(const xdp_gun_ctx_t *ctx, kxdpgun_stats_t *st, stats_type_t stt)
+{
+ pthread_mutex_lock(&st->mutex);
+
+ printf("%s metrics:\n", (stt == STATS_SUM) ? "cumulative" : "periodic");
+
+ bool recv = !(ctx->flags & KNOT_XDP_FILTER_DROP);
+ uint64_t duration = DURATION_US(*st);
+ double rel_start_us = (st->since / 1000.0) - ctx->stats_start_us ;
+ double rel_end_us = rel_start_us + duration;
+
+#define ps(counter) ((typeof(counter))((counter) * 1000 / ((float)duration / 1000)))
+#define pct(counter) ((counter) * 100.0 / st->qry_sent)
+
+ const char *name = ctx->tcp ? "SYNs: " : ctx->quic ? "initials:" : "queries: ";
+ printf("total %s %"PRIu64" (%"PRIu64" pps) (%f%%)\n", name, st->qry_sent,
+ ps(st->qry_sent), 100.0 * st->qry_sent / (duration / 1000000.0 * ctx->qps * ctx->n_threads));
+ if (st->qry_sent > 0 && recv) {
+ if (ctx->tcp || ctx->quic) {
+ name = ctx->tcp ? "established:" : "handshakes: ";
+ printf("total %s %"PRIu64" (%"PRIu64" pps) (%f%%)\n", name,
+ st->synack_recv, ps(st->synack_recv), pct(st->synack_recv));
+ }
+ printf("total replies: %"PRIu64" (%"PRIu64" pps) (%f%%)\n",
+ st->ans_recv, ps(st->ans_recv), pct(st->ans_recv));
+ if (ctx->tcp) {
+ printf("total closed: %"PRIu64" (%"PRIu64" pps) (%f%%)\n",
+ st->finack_recv, ps(st->finack_recv), pct(st->finack_recv));
+ }
+ if (st->rst_recv > 0) {
+ printf("total reset: %"PRIu64" (%"PRIu64" pps) (%f%%)\n",
+ st->rst_recv, ps(st->rst_recv), pct(st->rst_recv));
+ }
+ printf("average DNS reply size: %"PRIu64" B\n",
+ st->ans_recv > 0 ? st->size_recv / st->ans_recv : 0);
+ printf("average Ethernet reply rate: %"PRIu64" bps (%.2f Mbps)\n",
+ ps(st->wire_recv * 8), ps((float)st->wire_recv * 8 / (1000 * 1000)));
+
+ for (int i = 0; i < RCODE_MAX; i++) {
+ if (st->rcodes_recv[i] > 0) {
+ const knot_lookup_t *rcode = knot_lookup_by_id(knot_rcode_names, i);
+ const char *rcname = rcode == NULL ? "unknown" : rcode->name;
+ int space = MAX(9 - strlen(rcname), 0);
+ printf("responded %s: %.*s%"PRIu64"\n",
+ rcname, space, " ", st->rcodes_recv[i]);
+ }
+ }
+ }
+ if (stt == STATS_SUM) {
+ printf("duration: %.4f s\n", duration / 1000000.0);
+ } else {
+ printf("since: %.4fs until: %.4fs\n", rel_start_us / 1000000, rel_end_us / 1000000);
+ }
+
+ pthread_mutex_unlock(&st->mutex);
+}
+
+/* see https://github.com/DNS-OARC/dns-metrics/blob/main/dns-metrics.schema.json
+ * and https://github.com/DNS-OARC/dns-metrics/issues/16#issuecomment-2139462920 */
+void json_stats(const xdp_gun_ctx_t *ctx, kxdpgun_stats_t *st, stats_type_t stt)
+{
+ assert(stt == STATS_PERIODIC || stt == STATS_SUM);
+
+ jsonw_t *w = ctx->jw;
+
+ pthread_mutex_lock(&st->mutex);
+
+ jsonw_object(w, NULL);
+ {
+ jsonw_ulong(w, "runid", ctx->runid);
+ jsonw_str(w, "type", (stt == STATS_PERIODIC) ? "stats_periodic" : "stats_sum");
+ jsonw_ulong(w, "since", st->since);
+ jsonw_ulong(w, "until", st->until);
+ jsonw_ulong(w, "queries", st->qry_sent);
+ jsonw_ulong(w, "responses", st->ans_recv);
+
+ jsonw_object(w, "response_rcodes");
+ {
+ for (size_t i = 0; i < RCODE_MAX; ++i) {
+ if (st->rcodes_recv[i] > 0) {
+ const knot_lookup_t *rc = knot_lookup_by_id(knot_rcode_names, i);
+ jsonw_ulong(w, (rc == NULL) ? "unknown" : rc->name, st->rcodes_recv[i]);
+ }
+ }
+ }
+ jsonw_end(w);
+
+ jsonw_object(w, "conn_info");
+ {
+ jsonw_str(w, "type", ctx->tcp ? "tcp" : (ctx->quic ? "quic_conn" : "udp"));
+
+ // TODO:
+ // packets_sent
+ // packets_recieved
+
+ jsonw_ulong(w, "socket_errors", st->errors);
+ if (ctx->tcp || ctx->quic) {
+ jsonw_ulong(w, "handshakes", st->synack_recv);
+ // TODO: handshakes_failed
+ if (ctx->quic) {
+ // TODO: conn_resumption
+ }
+ }
+ }
+ jsonw_end(w);
+ }
+ jsonw_end(w);
+
+ pthread_mutex_unlock(&st->mutex);
+}
diff --git a/src/utils/kxdpgun/stats.h b/src/utils/kxdpgun/stats.h
new file mode 100644
index 0000000..2c62ee3
--- /dev/null
+++ b/src/utils/kxdpgun/stats.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdbool.h>
+
+#include "utils/kxdpgun/main.h"
+
+#define RCODE_MAX (0x0F + 1)
+
+#define STATS_SECTION_SEP "--------------------------------------------------------------"
+
+#define JSON_INDENT " "
+#define STATS_SCHEMA_VERSION 20240530
+
+#define DURATION_US(st) (((st).until - (st).since) / 1000)
+#define DURATION_NS(st) ((st).until - (st).since)
+
+#define JSON_MODE(ctx) ((ctx).jw != NULL)
+
+#define STATS_HDR(ctx) ((JSON_MODE(*(ctx)) ? json_stats_header : plain_stats_header)((ctx)))
+#define STATS_THRD(ctx, stats) \
+ ((JSON_MODE(*ctx) ? json_thrd_summary : plain_thrd_summary)((ctx), (stats)))
+#define STATS_FMT(ctx, stats, stats_type) \
+ ((JSON_MODE(*(ctx)) ? json_stats : plain_stats)((ctx), (stats), (stats_type)))
+
+typedef struct {
+ size_t collected;
+ uint64_t since, until; // nanosecs UNIX
+ uint64_t qry_sent;
+ uint64_t synack_recv;
+ uint64_t ans_recv;
+ uint64_t finack_recv;
+ uint64_t rst_recv;
+ uint64_t size_recv;
+ uint64_t wire_recv;
+ uint64_t errors;
+ uint64_t lost;
+ uint64_t rcodes_recv[RCODE_MAX];
+ pthread_mutex_t mutex;
+} kxdpgun_stats_t;
+
+typedef enum {
+ STATS_PERIODIC,
+ STATS_SUM,
+} stats_type_t;
+
+void clear_stats(kxdpgun_stats_t *st);
+size_t collect_stats(kxdpgun_stats_t *into, const kxdpgun_stats_t *what);
+void collect_periodic_stats(kxdpgun_stats_t *into, const kxdpgun_stats_t *what);
+
+void plain_stats_header(const xdp_gun_ctx_t *ctx);
+void json_stats_header(const xdp_gun_ctx_t *ctx);
+
+void plain_thrd_summary(const xdp_gun_ctx_t *ctx, const kxdpgun_stats_t *st);
+void json_thrd_summary(const xdp_gun_ctx_t *ctx, const kxdpgun_stats_t *st);
+
+void plain_stats(const xdp_gun_ctx_t *ctx, kxdpgun_stats_t *st, stats_type_t stt);
+void json_stats(const xdp_gun_ctx_t *ctx, kxdpgun_stats_t *st, stats_type_t stt);
+
+extern pthread_mutex_t stdout_mtx;
diff --git a/src/utils/kzonecheck/main.c b/src/utils/kzonecheck/main.c
index b602cc9..e8c5868 100644
--- a/src/utils/kzonecheck/main.c
+++ b/src/utils/kzonecheck/main.c
@@ -81,7 +81,7 @@ int main(int argc, char *argv[])
{ "print", no_argument, NULL, 'p' },
{ "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
+ { "version", optional_argument, NULL, 'V' },
{ NULL }
};
@@ -90,7 +90,7 @@ int main(int argc, char *argv[])
/* Parse command line arguments */
int opt = 0;
- while ((opt = getopt_long(argc, argv, "o:t:d:zpvVh", opts, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "o:t:d:zpvV::h", opts, NULL)) != -1) {
switch (opt) {
case 'o':
origin = optarg;
@@ -105,7 +105,7 @@ int main(int argc, char *argv[])
print_help();
return EXIT_SUCCESS;
case 'V':
- print_version(PROGRAM_NAME);
+ print_version(PROGRAM_NAME, optarg != NULL);
return EXIT_SUCCESS;
case 'd':
optional = str2bool(optarg) ? SEMCHECK_DNSSEC_ON : SEMCHECK_DNSSEC_OFF;
diff --git a/src/utils/kzonesign/main.c b/src/utils/kzonesign/main.c
index e70abb6..641acfc 100644
--- a/src/utils/kzonesign/main.c
+++ b/src/utils/kzonesign/main.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -117,7 +117,7 @@ static int zonesign(sign_params_t *params)
goto fail;
}
- ret = knot_dnssec_validate_zone(&up, conf(), params->timestamp, false);
+ ret = knot_dnssec_validate_zone(&up, conf(), params->timestamp, false, false);
if (ret != KNOT_EOK) {
ERR2("DNSSEC validation failed (%s)", knot_strerror(ret));
char type_str[16];
@@ -204,7 +204,7 @@ int main(int argc, char *argv[])
{ "verify" , no_argument, NULL, 'v' },
{ "time", required_argument, NULL, 't' },
{ "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
+ { "version", optional_argument, NULL, 'V' },
{ NULL }
};
@@ -212,7 +212,7 @@ int main(int argc, char *argv[])
signal_init_std();
int opt = 0;
- while ((opt = getopt_long(argc, argv, "c:C:o:rvt:hV", opts, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "c:C:o:rvt:hV::", opts, NULL)) != -1) {
switch (opt) {
case 'c':
if (util_conf_init_file(optarg) != KNOT_EOK) {
@@ -245,7 +245,7 @@ int main(int argc, char *argv[])
print_help();
goto success;
case 'V':
- print_version(PROGRAM_NAME);
+ print_version(PROGRAM_NAME, optarg != NULL);
goto success;
default:
print_help();