udp\-max\-payload\-ipv6: SIZE
edns\-client\-subnet: BOOL
answer\-rotation: BOOL
+ xdp\-route\-check: BOOL
listen: ADDR[@INT] ...
listen\-xdp: STR[@INT] | ADDR[@INT] ...
.ft P
The rotation shift is simply determined by a query ID.
.sp
\fIDefault:\fP off
+.SS xdp\-route\-check
+.sp
+If enabled, routing information from the operating system is considered
+when processing every incoming DNS packet received over the XDP interface:
+.INDENT 0.0
+.IP \(bu 2
+If the outgoing interface of the corresponding DNS response differs from
+the incoming one, the packet is processed normally by UDP workers
+(XDP isn\(aqt used).
+.IP \(bu 2
+If the destination address is blackholed, unreachable, or prohibited,
+the DNS packet is dropped without any response.
+.IP \(bu 2
+The destination MAC address for the response is taken from the routing system.
+.UNINDENT
+.sp
+If disabled, symmetrical routing is applied. It means that the query source
+MAC address is used as a response destination MAC address.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This mode requires forwarding enabled on the loopback interface
+(\fBsysctl \-w net.ipv4.conf.lo.forwarding=1\fP and \fBsysctl \-w net.ipv6.conf.lo.forwarding=1\fP).
+If forwarding is disabled, all incoming DNS packets are dropped!
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP off
.SS listen
.sp
One or more IP addresses where the server listens for incoming queries.
* Dynamic DNS over XDP is not supported.
* MTU higher than 1792 bytes is not supported.
* Multiple BPF filters per one network device are not supported.
-* Symmetrical routing is required (query source MAC/IP addresses and
- reply destination MAC/IP addresses are the same).
* Systems with big-endian byte ordering require special recompilation of the nameserver.
* IPv4 header and UDP checksums are not verified on received DNS messages.
* DNS over XDP traffic is not visible to common system tools (e.g. firewall, tcpdump etc.).
udp-max-payload-ipv6: SIZE
edns-client-subnet: BOOL
answer-rotation: BOOL
+ xdp-route-check: BOOL
listen: ADDR[@INT] ...
listen-xdp: STR[@INT] | ADDR[@INT] ...
*Default:* off
+.. _server_xdp-route-check:
+
+xdp-route-check
+---------------
+
+If enabled, routing information from the operating system is considered
+when processing every incoming DNS packet received over the XDP interface:
+
+- If the outgoing interface of the corresponding DNS response differs from
+ the incoming one, the packet is processed normally by UDP workers
+ (XDP isn't used).
+- If the destination address is blackholed, unreachable, or prohibited,
+ the DNS packet is dropped without any response.
+- The destination MAC address for the response is taken from the routing system.
+
+If disabled, symmetrical routing is applied. It means that the query source
+MAC address is used as a response destination MAC address.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+.. NOTE::
+ This mode requires forwarding enabled on the loopback interface
+ (``sysctl -w net.ipv4.conf.lo.forwarding=1`` and ``sysctl -w net.ipv6.conf.lo.forwarding=1``).
+ If forwarding is disabled, all incoming DNS packets are dropped!
+
+*Default:* off
+
.. _server_listen:
listen
static bool first_init = true;
static bool running_tcp_reuseport;
static bool running_socket_affinity;
+ static bool running_route_check;
static size_t running_udp_threads;
static size_t running_tcp_threads;
static size_t running_xdp_threads;
static size_t running_bg_threads;
if (first_init || reinit_cache) {
- running_tcp_reuseport = conf_tcp_reuseport(conf);
- running_socket_affinity = conf_socket_affinity(conf);
+ running_tcp_reuseport = conf_srv_bool(conf, C_TCP_REUSEPORT);
+ running_socket_affinity = conf_srv_bool(conf, C_SOCKET_AFFINITY);
+ running_route_check = conf_srv_bool(conf, C_XDP_ROUTE_CHECK);
running_udp_threads = conf_udp_threads(conf);
running_tcp_threads = conf_tcp_threads(conf);
running_xdp_threads = conf_xdp_threads(conf);
conf->cache.srv_socket_affinity = running_socket_affinity;
+ conf->cache.srv_xdp_route_check = running_route_check;
+
conf->cache.srv_udp_threads = running_udp_threads;
conf->cache.srv_tcp_threads = running_tcp_threads;
bool srv_tcp_reuseport;
bool srv_tcp_fastopen;
bool srv_socket_affinity;
+ bool srv_xdp_route_check;
size_t srv_udp_threads;
size_t srv_tcp_threads;
size_t srv_xdp_threads;
return conf_get_txn(conf, txn, C_DB, param);
}
-bool conf_tcp_reuseport_txn(
+bool conf_srv_bool_txn(
conf_t *conf,
- knot_db_txn_t *txn)
-{
- conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_TCP_REUSEPORT);
- return conf_bool(&val);
-}
-
-bool conf_socket_affinity_txn(
- conf_t *conf,
- knot_db_txn_t *txn)
+ knot_db_txn_t *txn,
+ const yp_name_t *param)
{
- conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_SOCKET_AFFINITY);
+ conf_val_t val = conf_get_txn(conf, txn, C_SRV, param);
return conf_bool(&val);
}
}
/*!
- * Gets the configured setting of the TCP reuseport switch.
+ * Gets the configured setting of the bool option in server section.
*
- * \param[in] conf Configuration.
- * \param[in] txn Configuration DB transaction.
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] param Parameter name.
*
* \return True if enabled, false otherwise.
*/
-bool conf_tcp_reuseport_txn(
+bool conf_srv_bool_txn(
conf_t *conf,
- knot_db_txn_t *txn
+ knot_db_txn_t *txn,
+ const yp_name_t *param
);
-static inline bool conf_tcp_reuseport(
- conf_t *conf)
-{
- return conf_tcp_reuseport_txn(conf, &conf->read_txn);
-}
-
-/*!
- * Gets the configured setting of the socket affinity switch.
- *
- * \param[in] conf Configuration.
- * \param[in] txn Configuration DB transaction.
- *
- * \return True if enabled, false otherwise.
- */
-bool conf_socket_affinity_txn(
+static inline bool conf_srv_bool(
conf_t *conf,
- knot_db_txn_t *txn
-);
-static inline bool conf_socket_affinity(
- conf_t *conf)
+ const yp_name_t *param)
{
- return conf_socket_affinity_txn(conf, &conf->read_txn);
+ return conf_srv_bool_txn(conf, &conf->read_txn, param);
}
/*!
1232, YP_SSIZE } },
{ C_ECS, YP_TBOOL, YP_VNONE },
{ C_ANS_ROTATION, YP_TBOOL, YP_VNONE },
+ { C_XDP_ROUTE_CHECK, YP_TBOOL, YP_VNONE },
{ C_LISTEN, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI, { check_listen } },
{ C_LISTEN_XDP, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI, { check_xdp } },
{ C_COMMENT, YP_TSTR, YP_VNONE },
#define C_USER "\x04""user"
#define C_VERSION "\x07""version"
#define C_VIA "\x03""via"
+#define C_XDP_ROUTE_CHECK "\x0F""xdp-route-check"
#define C_ZONE "\x04""zone"
#define C_ZONEFILE_LOAD "\x0D""zonefile-load"
#define C_ZONEFILE_SYNC "\x0D""zonefile-sync"
return KNOT_EOK;
}
-static iface_t *server_init_xdp_iface(struct sockaddr_storage *addr, unsigned *thread_id_start)
+static iface_t *server_init_xdp_iface(struct sockaddr_storage *addr, bool route_check,
+ unsigned *thread_id_start)
{
#ifndef ENABLE_XDP
assert(0);
new_if->xdp_first_thread_id = *thread_id_start;
*thread_id_start += iface.queues;
+ uint32_t xdp_flags = route_check ? KNOT_XDP_LISTEN_PORT_ROUTE : 0;
+
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,
- iface.port, mode);
+ iface.port | xdp_flags, mode);
if (ret == -EBUSY && i == 0) {
log_notice("XDP interface %s@%u is busy, retrying initializaion",
iface.name, iface.port);
ret = knot_xdp_init(new_if->xdp_sockets + i, iface.name, i,
- iface.port, KNOT_XDP_LOAD_BPF_ALWAYS_UNLOAD);
+ iface.port | xdp_flags, KNOT_XDP_LOAD_BPF_ALWAYS_UNLOAD);
}
if (ret != KNOT_EOK) {
log_warning("failed to initialize XDP interface %s@%u, queue %d (%s)",
if (ret == KNOT_EOK) {
knot_xdp_mode_t mode = knot_eth_xdp_mode(if_nametoindex(iface.name));
- log_debug("initialized XDP interface %s@%u, queues %d, %s mode",
+ log_debug("initialized XDP interface %s@%u, queues %d, %s mode%s",
iface.name, iface.port, iface.queues,
- (mode == KNOT_XDP_MODE_FULL ? "native" : "emulated"));
+ (mode == KNOT_XDP_MODE_FULL ? "native" : "emulated"),
+ route_check ? ", route check" : "");
}
return new_if;
free(rundir);
/* XDP sockets. */
+ bool route_check = conf->cache.srv_xdp_route_check;
unsigned thread_id = s->handlers[IO_UDP].handler.unit->size +
s->handlers[IO_TCP].handler.unit->size;
while (lisxdp_val.code == KNOT_EOK) {
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, &thread_id);
+ iface_t *new_if = server_init_xdp_iface(&addr, route_check, &thread_id);
if (new_if == NULL) {
server_deinit_iface_list(newlist, nifs);
return KNOT_ERROR;
static bool warn_tcp = true;
static bool warn_bg = true;
static bool warn_listen = true;
+ static bool warn_route_check = true;
- if (warn_tcp_reuseport && conf->cache.srv_tcp_reuseport != conf_tcp_reuseport(conf)) {
+ if (warn_tcp_reuseport && conf->cache.srv_tcp_reuseport != conf_srv_bool(conf, C_TCP_REUSEPORT)) {
log_warning(msg, &C_TCP_REUSEPORT[1]);
warn_tcp_reuseport = false;
}
- if (warn_socket_affinity && conf->cache.srv_socket_affinity != conf_socket_affinity(conf)) {
+ if (warn_socket_affinity && conf->cache.srv_socket_affinity != conf_srv_bool(conf, C_SOCKET_AFFINITY)) {
log_warning(msg, &C_SOCKET_AFFINITY[1]);
warn_socket_affinity = false;
}
log_warning(msg, "listen(-xdp)");
warn_listen = false;
}
+
+ if (warn_route_check && conf->cache.srv_xdp_route_check != conf_srv_bool(conf, C_XDP_ROUTE_CHECK)) {
+ log_warning(msg, &C_XDP_ROUTE_CHECK[1]);
+ warn_route_check = false;
+ }
}
int server_reload(server_t *server)
"server.tcp-max-clients\n"
"server.tcp-reuseport\n"
"server.tcp-fastopen\n"
+ "server.xdp-route-check\n"
"server.socket-affinity\n"
"server.udp-workers\n"
"server.tcp-workers\n"
{ C_TCP_MAX_CLIENTS, YP_TINT, YP_VNONE },
{ C_TCP_REUSEPORT, YP_TBOOL, YP_VNONE },
{ C_TCP_FASTOPEN, YP_TBOOL, YP_VNONE },
+ { C_XDP_ROUTE_CHECK, YP_TBOOL, YP_VNONE },
{ C_SOCKET_AFFINITY, YP_TBOOL, YP_VNONE },
{ C_UDP_WORKERS, YP_TINT, YP_VNONE },
{ C_TCP_WORKERS, YP_TINT, YP_VNONE },