#include "contrib/cleanup.h"
#include "daemon/network.h"
#include "daemon/tls.h"
+#include "daemon/quic_common.h"
#include "lib/utils.h"
#include <stdlib.h>
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. */
{ "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 },
_Bool tls;
_Bool http;
_Bool xdp;
- _Bool doq;
+ _Bool quic;
_Bool freebind;
const char *kind;
} endpoint_flags_t;
} 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 {
#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"
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)
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
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;
/** 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. */
*/
#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)
{
#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 {
#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
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);
*/
#include "quic_conn.h"
+#include "network.h"
#include "quic_common.h"
#include "quic_stream.h"
#include "libdnssec/random.h"
/* 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;
#include "quic_common.h"
#include "daemon/tls.h"
-#define QUIC_MAX_OPEN_CONNS 1024
-
/** QUIC parameters. */
typedef struct {
/*! Use QUIC indicator. */
#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);
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);
}
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()) {
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);
#include "quic_conn.h"
#include "session2.h"
-#define QUIC_MAX_SEND_PER_RECV 4
-
struct kr_quic_obuf {
struct node node;
size_t len;
"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.",
.. _config-network-server-tls:
DoT, DoH and DoQ (encrypted DNS)
----------------------------
+--------------------------------
.. warning::
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
.. _dot-doh-doq-config-options:
Configuration options for DoT, DoH and DoQ
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. note::
.. _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
Port number to listen on.
- .. option:: kind: dns|xdp|dot|doh2|doh-legacy
+ .. option:: kind: dns|xdp|dot|doh2|doh-legacy|doq
:default: dns
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.");
"rules",
"prlayr",
"defer",
+ "doq",
+ "ngtcp2",
# "reqdbg",... (non-displayed section of the enum)
]
from knot_resolver.datamodel.types import (
EscapedStr32B,
Int0_512,
+ Int1_4096,
Int0_65535,
InterfaceOptionalPort,
IPAddress,
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):
"""
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.
"""
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}),
{%- 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')
IDPattern,
Int0_32,
Int0_512,
+ Int1_4096,
Int0_65535,
InterfaceName,
InterfaceOptionalPort,
"IDPattern",
"Int0_32",
"Int0_512",
+ "Int1_4096",
"Int0_65535",
"InterfaceName",
"InterfaceOptionalPort",
_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
({"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]):