]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
config: Add additional QUIC configuration and documentation
authorFrantisek Tobias <frantisek.tobias@nic.cz>
Tue, 16 Dec 2025 09:52:32 +0000 (10:52 +0100)
committerFrantisek Tobias <frantisek.tobias@nic.cz>
Wed, 7 Jan 2026 13:44:26 +0000 (14:44 +0100)
20 files changed:
daemon/bindings/net.c
daemon/lua/kres-gen-33.lua
daemon/network.c
daemon/network.h
daemon/quic_common.c
daemon/quic_common.h
daemon/quic_conn.c
daemon/quic_conn.h
daemon/quic_demux.c
daemon/quic_stream.h
doc/_static/config.schema.json
doc/user/config-network-server-tls.rst
doc/user/config-network-server.rst
lib/log.c
python/knot_resolver/datamodel/logging_schema.py
python/knot_resolver/datamodel/network_schema.py
python/knot_resolver/datamodel/templates/network.lua.j2
python/knot_resolver/datamodel/types/__init__.py
python/knot_resolver/datamodel/types/types.py
tests/manager/datamodel/test_network_schema.py

index cb3d7498d97db32c9c52a11f77bffacaf2c0bb4e..e4f6dcba47bb763d52a52232aff10873583d8359 100644 (file)
@@ -8,6 +8,7 @@
 #include "contrib/cleanup.h"
 #include "daemon/network.h"
 #include "daemon/tls.h"
+#include "daemon/quic_common.h"
 #include "lib/utils.h"
 
 #include <stdlib.h>
@@ -620,6 +621,107 @@ static int net_doh_headers(lua_State *L)
        return 0;
 }
 
+static int net_quic_max_conns(lua_State *L)
+{
+       if (kr_fails_assert(the_network)) {
+               return 0;
+       }
+
+       // /* Only return current max conns. */
+       if (lua_gettop(L) == 0) {
+               if (!the_network->quic_params) {
+                       return 0;
+               }
+
+               lua_newtable(L);
+               lua_pushinteger(L, the_network->quic_params->max_conns);
+               return 1;
+       }
+
+       /* Allocate struct if needed */
+       if (!the_network->quic_params && quic_configuration_set() != kr_ok()) {
+               lua_error_p(L, "Out of memory allocating net_quic_params");
+       }
+
+       if (lua_gettop(L) != 1 || !lua_isnumber(L, 1))
+               lua_error_p(L, "net.quic_max_conns requires one integer value within <1,  4096>");
+
+       lua_Integer v = lua_tointeger(L, 1);
+       if (v < 1 || v > 4096)
+               lua_error_p(L, "net.quic_max_conns must be within <1,  4096>");
+
+       the_network->quic_params->max_conns = (uint16_t)v;
+       lua_pushboolean(L, true);
+       return 1;
+}
+
+static int net_quic_max_streams(lua_State *L)
+{
+       if (kr_fails_assert(the_network)) {
+               return 0;
+       }
+
+       // /* Only return current max streams. */
+       if (lua_gettop(L) == 0) {
+               if (!the_network->quic_params) {
+                       return 0;
+               }
+
+               lua_newtable(L);
+               lua_pushinteger(L, the_network->quic_params->max_streams);
+               return 1;
+       }
+
+       /* Allocate struct if needed */
+       if (!the_network->quic_params && quic_configuration_set() != kr_ok()) {
+               lua_error_p(L, "Out of memory allocating net_quic_params");
+       }
+
+       if (lua_gettop(L) != 1 || !lua_isnumber(L, 1))
+               lua_error_p(L, "net.quic_max_streams requires one integer value within <1,  4096>");
+
+       lua_Integer v = lua_tointeger(L, 1);
+       if (v < 1 || v > 4096)
+               lua_error_p(L, "net.quic_max_streams must be within <1,  4096>");
+
+       the_network->quic_params->max_streams = (uint16_t)v;
+       lua_pushboolean(L, true);
+       return 1;
+}
+
+static int net_quic_reqire_retry(lua_State *L)
+{
+       if (kr_fails_assert(the_network)) {
+               return 0;
+       }
+
+       // /* Only return current require_retry. */
+       if (lua_gettop(L) == 0) {
+               if (!the_network->quic_params) {
+                       return 0;
+               }
+
+               lua_newtable(L);
+               lua_pushboolean(L, the_network->quic_params->require_retry);
+               return 1;
+       }
+
+       if (lua_gettop(L) != 1 || !lua_isboolean(L, 1))
+               lua_error_p(L, "net.quic_require_retry requires one boolean value");
+
+       bool v = lua_toboolean(L, 1);
+
+       /* Allocate struct if needed */
+       if (!the_network->quic_params && quic_configuration_set() != kr_ok()) {
+               lua_error_p(L, "Out of memory allocating net_quic_params");
+       }
+
+       the_network->quic_params->require_retry = v;
+       lua_pushboolean(L, true);
+       return 1;
+}
+
+
 /** Return a lua table with TLS authentication parameters.
  * The format is the same as passed to policy.TLS_FORWARD();
  * more precisely, it's in a compatible canonical form. */
@@ -1243,6 +1345,9 @@ int kr_bindings_net(lua_State *L)
                { "tls_padding",  net_tls_padding },
                { "tls_sticket_secret", net_tls_sticket_secret_string },
                { "tls_sticket_secret_file", net_tls_sticket_secret_file },
+               { "quic_max_conns", net_quic_max_conns },
+               { "quic_max_streams", net_quic_max_streams },
+               { "quic_require_retry", net_quic_reqire_retry },
                { "outgoing_v4",  net_outgoing_v4 },
                { "outgoing_v6",  net_outgoing_v6 },
                { "tcp_in_idle",  net_tcp_in_idle },
index e999b89c3c2398d19bd449d4e51559b467d73b59..54f01bb67fa2ae8f6ab9d82bdde75df345514199 100644 (file)
@@ -555,7 +555,7 @@ typedef struct {
        _Bool tls;
        _Bool http;
        _Bool xdp;
-       _Bool doq;
+       _Bool quic;
        _Bool freebind;
        const char *kind;
 } endpoint_flags_t;
@@ -631,6 +631,7 @@ struct network {
        } listen_tcp_buflens;
        _Bool enable_connect_udp;
        uint16_t min_udp_source_port;
+       struct net_quic_params *quic_params;
 };
 struct args *the_args;
 struct endpoint {
index dd5fa3073751834e20a2a11c5a699df800f827bd..18b1159b7f40722300ce4ecdd4495ea631e457ec 100644 (file)
@@ -8,6 +8,7 @@
 #include "daemon/bindings/impl.h"
 #include "daemon/io.h"
 #include "daemon/tls.h"
+#include "daemon/quic_common.h"
 #include "daemon/worker.h"
 #include "lib/utils.h"
 
@@ -80,6 +81,7 @@ void network_init(uv_loop_t *loop, int tcp_backlog)
        the_network->tcp_backlog = tcp_backlog;
        the_network->enable_connect_udp = true;
        the_network->min_udp_source_port = 1024;
+       the_network->quic_params = NULL;
 
        // On Linux, unset means some auto-tuning mechanism also depending on RAM,
        // which might be OK default (together with the user_timeout above)
@@ -310,6 +312,7 @@ void network_deinit(void)
        trie_free(the_network->proxy_addrs6);
 
        tls_credentials_free(the_network->tls_credentials);
+       quic_configuration_free(the_network->quic_params);
        tls_client_params_free(the_network->tls_client_params);
        tls_session_ticket_ctx_destroy(the_network->tls_session_ticket_ctx);
 #ifndef NDEBUG
index 39f5b7f76970ad5dd3465efd2ed737ff8e6d3013..6f21068110605eca91fe0e2fc78be2cc36410410 100644 (file)
@@ -70,6 +70,12 @@ struct net_proxy_data {
        uint8_t netmask;   /**< Number of bits to be matched */
 };
 
+struct net_quic_params {
+       uint16_t max_conns;
+       uint16_t max_streams;
+       bool require_retry;
+};
+
 struct network {
        uv_loop_t *loop;
 
@@ -116,6 +122,8 @@ struct network {
 
        /** Low source port (e.g. 53) might be useful for attacks with spoofed source IPs. */
        uint16_t min_udp_source_port;
+
+       struct net_quic_params *quic_params;
 };
 
 /** Pointer to the singleton network state. NULL if not initialized. */
index 7ba462c75496a2ef50b638039585cd0f4563e0aa..23f27388777574d5a39bd200884b30eba00e5b7e 100644 (file)
@@ -3,10 +3,46 @@
  */
 #include <ngtcp2/ngtcp2.h>
 #include "contrib/openbsd/siphash.h"
-#include "quic_common.h"
 #include "libdnssec/random.h"
-#include "session2.h"
+
+#include "quic_common.h"
 #include "quic_conn.h"
+#include "session2.h"
+#include "network.h"
+
+int quic_configuration_set(void)
+{
+       if (kr_fails_assert(the_network)) {
+               return kr_error(EINVAL);
+       }
+
+       if (the_network->quic_params) {
+               return kr_ok();
+       }
+
+       struct net_quic_params *quic_params = calloc(1, sizeof(*quic_params));
+       if (quic_params == NULL) {
+               return kr_error(ENOMEM);
+       }
+
+       the_network->quic_params = quic_params;
+       /* Default values */
+       the_network->quic_params->require_retry = false;
+       the_network->quic_params->max_streams = 1024;
+       the_network->quic_params->max_conns = 1024;
+       return kr_ok();
+}
+
+int quic_configuration_free(struct net_quic_params *quic_params)
+{
+       if (quic_params == NULL){
+               return kr_ok();
+       }
+
+       free(quic_params);
+
+       return kr_ok();
+}
 
 uint64_t quic_timestamp(void)
 {
index 674247716119ff2950b4000f938b898e7384c95a..675ec60f945fa3b19488e6b812569e6d4cd83cea 100644 (file)
@@ -13,6 +13,7 @@
 #include <gnutls/crypto.h>
 
 #include "session2.h"
+#include "network.h"
 
 /** RFC 9250 4.3 DoQ Error Codes for use as application protocol error codes */
 typedef enum {
@@ -48,12 +49,6 @@ typedef enum {
 
 #define BUCKETS_PER_CONNS 8
 
-/* Application is responsible for extending the stream limit.
- * This mainly means that this value limits the number of concurrent streams
- * because only once a stream is closed is this max_streams frame sent to the
- * remote endpoint. */
-#define MAX_STREAMS_BIDI 1024
-
 #define MAX_QUIC_PKT_SIZE 65536
 #define MAX_QUIC_FRAME_SIZE 65536
 #define QUIC_MAX_SEND_PER_RECV 4
@@ -110,6 +105,8 @@ struct kr_quic_stream_param {
        struct comm_info comm_storage;
 };
 
+int quic_configuration_set(void);
+int quic_configuration_free(struct net_quic_params *quic_params);
 bool kr_quic_conn_timeout(struct pl_quic_conn_sess_data *conn, uint64_t *now);
 uint64_t cid2hash(const ngtcp2_cid *cid, kr_quic_table_t *table);
 bool init_unique_cid(ngtcp2_cid *cid, size_t len, kr_quic_table_t *table);
index b7db39508a23322370e9d302ce3afd433fe26e14..9f0fab2f5940238387a1a858d992db29d36f1415 100644 (file)
@@ -3,6 +3,7 @@
  */
 
 #include "quic_conn.h"
+#include "network.h"
 #include "quic_common.h"
 #include "quic_stream.h"
 #include "libdnssec/random.h"
@@ -327,7 +328,12 @@ static int conn_new_handler(ngtcp2_conn **pconn, const ngtcp2_path *path,
 
        /* We have no use for unidirectional streams */
        params.initial_max_streams_uni = 0;
-       params.initial_max_streams_bidi = MAX_STREAMS_BIDI;
+
+       if (unlikely(kr_fails_assert(the_network && the_network->quic_params))) {
+               kr_log_debug(DOQ, "Missing network struct or network quic_parameters\n");
+               return kr_error(EINVAL);
+       }
+       params.initial_max_streams_bidi = the_network->quic_params->max_streams;
        params.initial_max_stream_data_bidi_local = MAX_QUIC_FRAME_SIZE;
        params.initial_max_stream_data_bidi_remote = MAX_QUIC_FRAME_SIZE;
        params.initial_max_data = MAX_QUIC_PKT_SIZE;
index 38597221d4f63418b5d06fe35518102fa7f4a843..1fd3873ea3bd85179c57c11fc8bb86573c492d83 100644 (file)
@@ -12,8 +12,6 @@
 #include "quic_common.h"
 #include "daemon/tls.h"
 
-#define QUIC_MAX_OPEN_CONNS 1024
-
 /** QUIC parameters. */
 typedef struct {
        /*! Use QUIC indicator. */
index 2d79dd49febd12266ed458a6dcbed73392214463..e5b8348fb791e9330d3422fe6a0224e9fac82c36 100644 (file)
@@ -7,11 +7,11 @@
 #include "quic_conn.h"
 #include "quic_demux.h"
 #include "libdnssec/random.h"
+#include <stdlib.h>
 
 /* Toggle sending retry for new connections. This is a way to validate the
  * client address, but it adds 1 round trip to the connection establishment
  * potentially hindering performance */
-#define DOQ_REQUIRE_RETRY false
 #define BUCKETS_PER_CONNS 8 // Each connecion has several dCIDs, and each CID takes one hash table bucket.
 
 void kr_quic_table_rem(struct pl_quic_conn_sess_data *conn, kr_quic_table_t *table);
@@ -198,12 +198,14 @@ void kr_quic_table_sweep(struct kr_quic_table *table,
                        int ret = ngtcp2_conn_handle_expiry(c->conn, now);
                        if (ret != NGTCP2_NO_ERROR) {
                                quic_doq_error_t doq_error = DOQ_NO_ERROR;
-                               send_special(&c->dec_cids, c->table_ref,
-                                               ctx, QUIC_SEND_CONN_CLOSE,
-                                               c, c->h.session, &doq_error);
+                               /* see https://nghttp2.org/ngtcp2/ngtcp2_conn_handle_expiry.html */
+                               if (ret != NGTCP2_ERR_IDLE_CLOSE) {
+                                       send_special(&c->dec_cids, c->table_ref,
+                                                       ctx, QUIC_SEND_CONN_CLOSE,
+                                                       c, c->h.session, &doq_error);
+                               }
                                session2_event(c->h.session->transport.parent,
-                                               PROTOLAYER_EVENT_DISCONNECT,
-                                               c);
+                                               PROTOLAYER_EVENT_DISCONNECT, c);
                        } else {
                                // quic_conn_mark_used(c, table);
                        }
@@ -279,8 +281,8 @@ static enum protolayer_iter_cb_result pl_quic_demux_unwrap(void *sess_data,
                        return protolayer_break(ctx, kr_ok());
                }
 
-               /* additional RTT seems quite expensive for all new connections */
-               if (header.tokenlen == 0 && DOQ_REQUIRE_RETRY) {
+               if (header.tokenlen == 0 && the_network->quic_params
+                               && the_network->quic_params->require_retry) {
                        if (send_special(&dec_cids, demux->conn_table, ctx,
                                        QUIC_SEND_RETRY, NULL,
                                        demux->h.session, NULL) != kr_ok()) {
@@ -458,10 +460,20 @@ static int pl_quic_demux_sess_init(struct session2 *session, void *sess_data, vo
 
        struct tls_credentials *creds = the_network->tls_credentials;
 
+       /* kresd process was run without a manager and no quic configuration
+        * which would set defaults was provided -> init and set defaults */
+       if (!the_network->quic_params) {
+               int ret = 0;
+               if ((ret = quic_configuration_set()) != kr_ok()) {
+                       kr_log_error(DOQ, "Failed to allocate quic defaults\n");
+                       return ret;
+               }
+       }
+
        if (!quic->conn_table) {
-               quic->conn_table = kr_quic_table_new(QUIC_MAX_OPEN_CONNS,
+               quic->conn_table = kr_quic_table_new(
+                               the_network->quic_params->max_conns,
                                NGTCP2_MAX_UDP_PAYLOAD_SIZE, creds);
-
                if (!quic->conn_table) {
                        kr_log_error(DOQ, "Failed to create QUIC connection table\n");
                        return kr_error(ENOMEM);
index 9920271b3d44901d2ef99cfeb4275c6fa6df2a81..33808d9b0e9e18b2fdf004dbf0a6a0b63dd7f4c9 100644 (file)
@@ -9,8 +9,6 @@
 #include "quic_conn.h"
 #include "session2.h"
 
-#define QUIC_MAX_SEND_PER_RECV 4
-
 struct kr_quic_obuf {
        struct node node;
        size_t len;
index aa58df77c57bbb34638615d465cd7cb49da8a023..70786d74bd2a2f995f7c22a855138a59b0a21da2 100644 (file)
                     "default": null
                 },
                 "tls": {
-                    "description": "TLS configuration, also affects DNS over TLS and DNS over HTTPS.",
+                    "description": "TLS configuration, also affects DNS over TLS, DNS over HTTPS and DNS over QUIC.",
                     "type": "object",
                     "properties": {
                         "watchdog": {
                         "padding": true
                     }
                 },
+                "quic": {
+                    "description": "DNS over QUIC configuration.",
+                    "type": "object",
+                    "properties": {
+                        "max-conns": {
+                            "type": "integer",
+                            "minimum": 1,
+                            "maximum": 4096,
+                            "description": "Maximum number of active connections a single worker is allowed to accept.",
+                            "default": 1024
+                        },
+                        "max-streams": {
+                            "type": "integer",
+                            "minimum": 1,
+                            "maximum": 4096,
+                            "description": "Maximum number of concurrent streams a connection is allowed to open.",
+                            "default": 1024
+                        },
+                        "require-retry": {
+                            "type": "boolean",
+                            "description": "Require address validation for unknown source addresses. Adds a 1-RTT delay to connection establishment.",
+                            "default": false
+                        }
+                    },
+                    "default": {
+                        "max_conns": 1024,
+                        "max_streams": 1024,
+                        "require_retry": false
+                    }
+                },
                 "proxy-protocol": {
                     "description": "PROXYv2 protocol configuration.",
                     "type": "object",
                     "sticket_secret_file": null,
                     "padding": true
                 },
+                "quic": {
+                    "max_conns": 1024,
+                    "max_streams": 1024,
+                    "require_retry": false
+                },
                 "proxy_protocol": {
                     "enable": false,
                     "allow": null
                             "exterr",
                             "rules",
                             "prlayr",
-                            "defer"
+                            "defer",
+                            "doq",
+                            "ngtcp2"
                         ]
                     },
                     "description": "List of groups for which 'debug' logging level is set.",
index 34cf9c9e32b1573d0dda17ad5abd7b724349cb5b..143a0d9ff196c70f585350bea7cc0fff0e828c20 100644 (file)
@@ -3,7 +3,7 @@
 .. _config-network-server-tls:
 
 DoT, DoH and DoQ (encrypted DNS)
----------------------------
+--------------------------------
 
 .. warning::
 
@@ -13,7 +13,7 @@ DoT, DoH and DoQ (encrypted DNS)
    See `slides <https://irtf.org/anrw/2019/slides-anrw19-final44.pdf>`_
    or `the article itself <https://dl.acm.org/authorize?N687437>`_.
 
-DoT and DoH encrypt DNS traffic with Transport Layer Security (TLS) protocol
+DoT, DoH and DoQ encrypt DNS traffic with Transport Layer Security (TLS) protocol
 and thus protects DNS traffic from certain types of attacks.
 
 You can learn more about DoT and DoH and their implementation in Knot Resolver
@@ -90,7 +90,7 @@ the following status codes:
 .. _dot-doh-doq-config-options:
 
 Configuration options for DoT, DoH and DoQ
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. note::
 
@@ -210,11 +210,46 @@ policies.
 .. _dns-over-quic:
 
 DNS-over-QUIC (DoQ)
-^^^^^^^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^^^
+
 .. note::
-   Forwarding over QUIC is not currently supported.
+    DoQ is currently considered an experimental feature. If you encounter any
+    issues or have any questions, please do not hesitate to contact us via the
+    standard communication channels.
+
 
 DNS-over-QUIC server (:rfc:`9250`) can be configured using ``doq`` kind in
 :option:`network/listen <network/listen: <list>>`.
 
 For certificate configuration, refer to :ref:`dot-doh-doq-config-options`.
+
+.. option:: network/quic:
+
+    .. option:: max_conns: <1-4096>
+
+    :default: 1024
+
+    The maximum number of active connections a worker is permitted to accept.
+    Setting this value too low or too high may negatively affect performance.
+    Changing this value requires a worker restart.
+
+    .. option:: max_streams: <1-4096>
+
+    :default: 1024
+
+    The maximum number of concurrent streams a connection can open.
+    Setting this value too low or too high may negatively affect performance.
+    Changing this value requires a worker restart.
+
+    .. option:: require_retry: true|false
+
+    :default: false
+
+    Require address validation from unknown addresses. Retry requests might
+    be sent under certain conditions regardless of this setting.
+    Note that enabling this option imposes a 1-RTT delay for verifying
+    the return routability of the source address of a client.
+
+    For further details see:
+    https://datatracker.ietf.org/doc/html/rfc9000#name-address-validation-using-re
+    and https://datatracker.ietf.org/doc/html/rfc9250#name-address-validation
index 71d1645838bac14cdc73f9958e1d26d5f58f9c81..a3b5a9bf7b79b227f69b17c2be021dc968ae21dd 100644 (file)
@@ -55,7 +55,7 @@ address + port combination.
 
       Port number to listen on.
 
-   .. option:: kind: dns|xdp|dot|doh2|doh-legacy
+   .. option:: kind: dns|xdp|dot|doh2|doh-legacy|doq
 
       :default: dns
 
index 38c62f29b7c32dccece4ab5fbc942f7a34d9532a..0ad69926311b04d26782fe6778d5df1e3f454c48 100644 (file)
--- a/lib/log.c
+++ b/lib/log.c
@@ -81,9 +81,9 @@ const log_group_names_t log_group_names[] = {
        GRP_NAME_ITEM(LOG_GRP_RULES),
        GRP_NAME_ITEM(LOG_GRP_PROTOLAYER),
        GRP_NAME_ITEM(LOG_GRP_DEFER),
-       GRP_NAME_ITEM(LOG_GRP_REQDBG),
        GRP_NAME_ITEM(LOG_GRP_DOQ),
        GRP_NAME_ITEM(LOG_GRP_DOQ_LIBNGTCP2),
+       GRP_NAME_ITEM(LOG_GRP_REQDBG),
        { NULL, LOG_GRP_UNKNOWN },
 };
 static_assert(LOG_GRP_REQDBG <= 8 * sizeof(kr_log_groups), "Too many log groups.");
index d97a5694a31f7f55ba82f8e5baf361bcb20b1678..61fbd1f4cc67ff5e6041d29f1846daa746870d8b 100644 (file)
@@ -71,6 +71,8 @@ LogGroupsKresdEnum = Literal[
     "rules",
     "prlayr",
     "defer",
+    "doq",
+    "ngtcp2",
     # "reqdbg",... (non-displayed section of the enum)
 ]
 
index 662cd9d280ae8e27614b0e7521c626bfbe666cf5..d6712f2875a9533559393fbd07705760c01441f5 100644 (file)
@@ -4,6 +4,7 @@ from knot_resolver.constants import WATCHDOG_LIB
 from knot_resolver.datamodel.types import (
     EscapedStr32B,
     Int0_512,
+    Int1_4096,
     Int0_65535,
     InterfaceOptionalPort,
     IPAddress,
@@ -48,6 +49,27 @@ class AddressRenumberingSchema(ConfigSchema):
     destination: Union[IPAddressEM, IPAddress]
 
 
+class QUICSchema(ConfigSchema):
+    class Raw(ConfigSchema):
+        """
+        Optional DoQ configuration
+
+        ---
+        max_conns: Maximum number of active connections a single worker is allowed to accept.
+        max_streams: Maximum number of concurrent streams a connection is allowed to open.
+        require_retry: Require address validation for unknown source addresses. Adds a 1-RTT delay to connection establishment.
+        """
+
+        max_conns: Int1_4096 = Int1_4096(1024)
+        max_streams: Int1_4096 = Int1_4096(1024)
+        require_retry: bool = False;
+
+    _LAYER = Raw
+
+    max_conns: Int1_4096 = Int1_4096(1024)
+    max_streams: Int1_4096 = Int1_4096(1024)
+    require_retry: bool = False;
+
 class TLSSchema(ConfigSchema):
     class Raw(ConfigSchema):
         """
@@ -182,7 +204,8 @@ class NetworkSchema(ConfigSchema):
     edns_tcp_keepalive: Allows clients to discover the connection timeout. (RFC 7828)
     edns_buffer_size: Maximum EDNS payload size advertised in DNS packets. Different values can be configured for communication downstream (towards clients) and upstream (towards other DNS servers).
     address_renumbering: Renumbers addresses in answers to different address space.
-    tls: TLS configuration, also affects DNS over TLS and DNS over HTTPS.
+    tls: TLS configuration, also affects DNS over TLS, DNS over HTTPS and DNS over QUIC.
+    quic: DNS over QUIC configuration.
     proxy_protocol: PROXYv2 protocol configuration.
     listen: List of interfaces to listen to and its configuration.
     """
@@ -197,6 +220,7 @@ class NetworkSchema(ConfigSchema):
     address_renumbering: Optional[List[AddressRenumberingSchema]] = None
     tls: TLSSchema = TLSSchema()
     proxy_protocol: ProxyProtocolSchema = ProxyProtocolSchema()
+    quic: QUICSchema = QUICSchema()
     listen: List[ListenSchema] = [
         ListenSchema({"interface": "127.0.0.1"}),
         ListenSchema({"interface": "::1", "freebind": True}),
index 81ec14595edcc68560524e066d1afa3f30e1ea6d..aee0060617fa3d54ad1570297e040a99d9a0c38f 100644 (file)
@@ -57,6 +57,25 @@ false
 {%- endif -%}
 )
 
+-- network.quic.max_conns
+{% if cfg.network.quic.max_conns %}
+net.quic_max_conns({{ cfg.network.quic.max_conns }})
+{% endif %}
+
+-- network.quic.max_streams
+{% if cfg.network.quic.max_streams %}
+net.quic_max_streams({{ cfg.network.quic.max_streams }})
+{% endif %}
+
+-- network.quic.require_retry
+net.quic_require_retry(
+{%- if cfg.network.quic.require_retry == true -%}
+true
+{%- else -%}
+false
+{%- endif -%}
+)
+
 {% if cfg.network.address_renumbering %}
 -- network.address-renumbering
 modules.load('renumber')
index 7e5cab417a7aedd8d25e27ad0c1fda9f792977c8..bf6439c972fa6313feef6005a93b55d5deca65e5 100644 (file)
@@ -9,6 +9,7 @@ from .types import (
     IDPattern,
     Int0_32,
     Int0_512,
+    Int1_4096,
     Int0_65535,
     InterfaceName,
     InterfaceOptionalPort,
@@ -42,6 +43,7 @@ __all__ = [
     "IDPattern",
     "Int0_32",
     "Int0_512",
+    "Int1_4096",
     "Int0_65535",
     "InterfaceName",
     "InterfaceOptionalPort",
index fb22169effa61a2e31870eaad61a4c5c39d6df84..9ade24816dcc69be7875e7c837158de46353388f 100644 (file)
@@ -31,6 +31,11 @@ class Int0_512(IntRangeBase):  # noqa: N801
     _max: int = 512
 
 
+class Int1_4096(IntRangeBase):  # noqa: N801
+    _min: int = 1
+    _max: int = 4096
+
+
 class Int0_65535(IntRangeBase):  # noqa: N801
     _min: int = 0
     _max: int = 65_535
index 1204e01154676afb40c07b0a8fa5230c1096f859..3723fbdb1005b64155f23f926a229e1a04244c20 100644 (file)
@@ -33,6 +33,7 @@ def test_listen_defaults():
         ({"interface": ["::1"], "kind": "dot"}, 853),
         ({"interface": ["::1"], "kind": "doh-legacy"}, 443),
         ({"interface": ["::1"], "kind": "doh2"}, 443),
+        ({"interface": ["::1"], "kind": "doq"}, 853),
     ],
 )
 def test_listen_port_defaults(listen: Dict[str, Any], port: Optional[int]):