diff --git a/Knot.files b/Knot.files index 38f94403f7ee94879fd47914589b5fd7a7ce2ff1..38fcd5a77724ebf1517ad57844614e4280db1df6 100644 --- a/Knot.files +++ b/Knot.files @@ -403,16 +403,16 @@ src/libknot/tsig-op.h src/libknot/tsig.c src/libknot/tsig.h src/libknot/wire.h -src/libknot/xdp/af_xdp.c -src/libknot/xdp/af_xdp.h src/libknot/xdp/bpf-consts.h src/libknot/xdp/bpf-kernel-obj.c src/libknot/xdp/bpf-kernel-obj.h src/libknot/xdp/bpf-kernel.c src/libknot/xdp/bpf-user.c src/libknot/xdp/bpf-user.h -src/libknot/xdp/eth-tools.c -src/libknot/xdp/eth-tools.h +src/libknot/xdp/eth.c +src/libknot/xdp/eth.h +src/libknot/xdp/xdp.c +src/libknot/xdp/xdp.h src/libknot/yparser/yparser.c src/libknot/yparser/yparser.h src/libknot/yparser/ypbody.c diff --git a/src/knot/conf/base.c b/src/knot/conf/base.c index d724e68d21e4a3192bbf5c59719a78649f1e0c35..98fdc454de2508c47e56c55cce912c75bfef2272 100644 --- a/src/knot/conf/base.c +++ b/src/knot/conf/base.c @@ -116,7 +116,8 @@ static void init_cache( conf_t *conf, bool reinit_cache) { - /* For UDP, TCP and background workers, cache the numbers of running + /* + * For UDP, TCP, XDP, and background workers, cache the number of running * workers. Cache the setting of TCP reuseport too. These values * can't change in runtime, while config data can. */ @@ -125,12 +126,14 @@ static void init_cache( static bool running_tcp_reuseport; 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_udp_threads = conf_udp_threads(conf); running_tcp_threads = conf_tcp_threads(conf); + running_xdp_threads = conf_xdp_threads(conf); running_bg_threads = conf_bg_threads(conf); first_init = false; @@ -184,6 +187,8 @@ static void init_cache( conf->cache.srv_tcp_threads = running_tcp_threads; + conf->cache.srv_xdp_threads = running_xdp_threads; + conf->cache.srv_bg_threads = running_bg_threads; conf->cache.srv_tcp_max_clients = conf_tcp_max_clients(conf); diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c index 1f289529cf90b5cb8db5aa6eb128ea49eacd92e7..3f0ce9a0ae2e6d067013a6f5cfa94eabcf7994e0 100644 --- a/src/knot/conf/conf.c +++ b/src/knot/conf/conf.c @@ -28,9 +28,11 @@ #include "libknot/yparser/yptrafo.h" #include "contrib/macros.h" #include "contrib/sockaddr.h" +#include "contrib/strtonum.h" #include "contrib/string.h" #include "contrib/wire_ctx.h" #include "contrib/openbsd/strlcat.h" +#include "contrib/openbsd/strlcpy.h" #define DBG_LOG(err) CONF_LOG(LOG_DEBUG, "%s (%s)", __func__, knot_strerror((err))); @@ -1127,6 +1129,26 @@ size_t conf_tcp_threads_txn( return workers; } +size_t conf_xdp_threads_txn( + conf_t *conf, + knot_db_txn_t *txn) +{ + size_t workers = 0; + + conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_LISTEN_XDP); + while (val.code == KNOT_EOK) { + struct sockaddr_storage addr = conf_addr(&val, NULL); + conf_xdp_iface_t iface; + int ret = conf_xdp_iface(&addr, &iface); + if (ret == KNOT_EOK) { + workers += iface.queues; + } + conf_val_next(&val); + } + + return workers; +} + size_t conf_bg_threads_txn( conf_t *conf, knot_db_txn_t *txn) @@ -1277,3 +1299,53 @@ conf_remote_t conf_remote_txn( return out; } + +int conf_xdp_iface( + struct sockaddr_storage *addr, + conf_xdp_iface_t *iface) +{ +#ifndef ENABLE_XDP + return KNOT_ENOTSUP; +#else + if (addr == NULL || iface == NULL) { + return KNOT_EINVAL; + } + + if (addr->ss_family == AF_UNIX) { + const char *addr_str = ((struct sockaddr_un *)addr)->sun_path; + strlcpy(iface->name, addr_str, sizeof(iface->name)); + + const char *port = strchr(addr_str, '@'); + if (port != NULL) { + iface->name[port - addr_str] = '\0'; + int ret = str_to_u16(port + 1, &iface->port); + if (ret != KNOT_EOK) { + return ret; + } else if (iface->port == 0) { + return KNOT_EINVAL; + } + } else { + iface->port = 53; + } + } else { + int ret = knot_eth_name_from_addr(addr, iface->name, sizeof(iface->name)); + if (ret != KNOT_EOK) { + return ret; + } + ret = sockaddr_port(addr); + if (ret < 1) { + return KNOT_EINVAL; + } + iface->port = ret; + } + + int queues = knot_eth_queues(iface->name); + if (queues <= 0) { + assert(queues != 0); + return queues; + } + iface->queues = queues; + + return KNOT_EOK; +#endif +} diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h index a44e879684122521e883873004a0d3f24dd7f86c..c6521e1ab10beb9df1c5f60eef5b965ddd212b38 100644 --- a/src/knot/conf/conf.h +++ b/src/knot/conf/conf.h @@ -695,6 +695,24 @@ static inline size_t conf_tcp_threads( return conf_tcp_threads_txn(conf, &conf->read_txn); } +/*! + * Gets the number of used XDP threads. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * + * \return Number of threads. + */ +size_t conf_xdp_threads_txn( + conf_t *conf, + knot_db_txn_t *txn +); +static inline size_t conf_xdp_threads( + conf_t *conf) +{ + return conf_xdp_threads_txn(conf, &conf->read_txn); +} + /*! * Gets the configured number of worker threads. * @@ -777,5 +795,27 @@ static inline conf_remote_t conf_remote( size_t index) { return conf_remote_txn(conf, &conf->read_txn, id, index); - } + +/*! XDP interface parameters. */ +typedef struct { + /*! Interface name. */ + char name[32]; + /*! UDP port to listen on. */ + uint16_t port; + /*! Number of active IO queues. */ + uint16_t queues; +} conf_xdp_iface_t; + +/*! + * Gets the XDP interface parameters for a given configuration value. + * + * \param[in] addr XDP interface name stored in the configuration. + * \param[out] iface Interface parameters. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_xdp_iface( + struct sockaddr_storage *addr, + conf_xdp_iface_t *iface +); diff --git a/src/knot/conf/schema.c b/src/knot/conf/schema.c index 03bc54653a49db22bd283b0cf0dc96123134874f..98bb2627345a073470f04349703137a2bb160836 100644 --- a/src/knot/conf/schema.c +++ b/src/knot/conf/schema.c @@ -173,7 +173,7 @@ static const yp_item_t desc_server[] = { KNOT_EDNS_MAX_UDP_PAYLOAD, 1232, YP_SSIZE } }, { C_LISTEN, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI }, - { C_LISTEN_XDP, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI }, + { C_LISTEN_XDP, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI, { check_xdp } }, { C_ECS, YP_TBOOL, YP_VNONE }, { C_ANS_ROTATION, YP_TBOOL, YP_VNONE }, { C_COMMENT, YP_TSTR, YP_VNONE }, diff --git a/src/knot/conf/tools.c b/src/knot/conf/tools.c index 833ccc99596889ae7fcfaf32d54b11e66096e5ad..08d4eb85dce27856fb0156bc6c8d1ed827bc47ec 100644 --- a/src/knot/conf/tools.c +++ b/src/knot/conf/tools.c @@ -227,6 +227,43 @@ int check_ref_dflt( return KNOT_EOK; } +int check_xdp( + knotd_conf_check_args_t *args) +{ +#ifndef ENABLE_XDP + args->err_str = "XDP is not available"; + return KNOT_ENOTSUP; +#else + bool no_port; + struct sockaddr_storage ss = yp_addr(args->data, &no_port); + conf_xdp_iface_t if_new; + int ret = conf_xdp_iface(&ss, &if_new); + if (ret != KNOT_EOK) { + args->err_str = "invalid XDP interface specification"; + return ret; + } + + conf_val_t xdp = conf_get_txn(args->extra->conf, args->extra->txn, C_SRV, + C_LISTEN_XDP); + size_t count = conf_val_count(&xdp); + while (xdp.code == KNOT_EOK && count-- > 1) { + struct sockaddr_storage addr = conf_addr(&xdp, NULL); + conf_xdp_iface_t if_prev; + ret = conf_xdp_iface(&addr, &if_prev); + if (ret != KNOT_EOK) { + return ret; + } + if (strcmp(if_new.name, if_prev.name) == 0) { + args->err_str = "duplicate XDP interface specification"; + return KNOT_EINVAL; + } + conf_val_next(&xdp); + } + + return KNOT_EOK; +#endif +} + int check_modref( knotd_conf_check_args_t *args) { @@ -303,7 +340,7 @@ int check_server( conf_val_t hshake = conf_get_txn(args->extra->conf, args->extra->txn, C_SRV, C_TCP_HSHAKE_TIMEOUT); if (hshake.code == KNOT_EOK) { - CONF_LOG(LOG_NOTICE, "option 'tcp-handshake-timeout' is no longer supported"); + CONF_LOG(LOG_NOTICE, "option 'server.tcp-handshake-timeout' is no longer supported"); } CHECK_LEGACY_NAME(C_SRV, C_TCP_REPLY_TIMEOUT, C_TCP_RMT_IO_TIMEOUT); diff --git a/src/knot/conf/tools.h b/src/knot/conf/tools.h index 5d6900fe67445c421dfcfbd5ffc38fc2ae4fd31b..1f36261072e4b36231c87353d873d432f3d73deb 100644 --- a/src/knot/conf/tools.h +++ b/src/knot/conf/tools.h @@ -66,6 +66,10 @@ int check_ref_dflt( knotd_conf_check_args_t *args ); +int check_xdp( + knotd_conf_check_args_t *args +); + int check_modref( knotd_conf_check_args_t *args ); diff --git a/src/knot/ctl/commands.c b/src/knot/ctl/commands.c index 0ffab8c4c76f38b625f01c3d1b803a4a30a96181..d65f9546d2d9ef3602e07f42492cb252340926a3 100644 --- a/src/knot/ctl/commands.c +++ b/src/knot/ctl/commands.c @@ -1384,10 +1384,11 @@ static int server_status(ctl_args_t *args) } else if (strcasecmp(type, "workers") == 0) { int running_bkg_wrk, wrk_queue; worker_pool_status(args->server->workers, &running_bkg_wrk, &wrk_queue); - ret = snprintf(buff, sizeof(buff), "UDP workers: %zu, TCP workers %zu, " - "background workers: %zu (running: %d, pending: %d)", + ret = snprintf(buff, sizeof(buff), "UDP workers: %zu, TCP workers: %zu, " + "XDP workers: %zu, background workers: %zu (running: %d, pending: %d)", conf()->cache.srv_udp_threads, conf()->cache.srv_tcp_threads, - conf()->cache.srv_bg_threads, running_bkg_wrk, wrk_queue); + 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); } else { diff --git a/src/knot/modules/dnstap/dnstap.c b/src/knot/modules/dnstap/dnstap.c index 637e6cd5a39403a995b0513c4d03c0645f852146..630c5dd88663b643bec5845b184f53bc457ea707 100644 --- a/src/knot/modules/dnstap/dnstap.c +++ b/src/knot/modules/dnstap/dnstap.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2020 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 @@ -275,7 +275,7 @@ int dnstap_load(knotd_mod_t *mod) knotd_conf_t udp = knotd_conf_env(mod, KNOTD_CONF_ENV_WORKERS_UDP); knotd_conf_t xdp = knotd_conf_env(mod, KNOTD_CONF_ENV_WORKERS_XDP); knotd_conf_t tcp = knotd_conf_env(mod, KNOTD_CONF_ENV_WORKERS_TCP); - size_t qcount = udp.single.integer + tcp.single.integer + xdp.single.integer; + size_t qcount = udp.single.integer + xdp.single.integer + tcp.single.integer; fstrm_iothr_options_set_num_input_queues(opt, qcount); /* Create the I/O thread. */ diff --git a/src/knot/modules/noudp/noudp.c b/src/knot/modules/noudp/noudp.c index 91a4924cd93de87041ad2aac4033b6b5b2ad7dab..891c4c044fa0213071428581c71ac36aca947cd6 100644 --- a/src/knot/modules/noudp/noudp.c +++ b/src/knot/modules/noudp/noudp.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2020 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,9 +60,9 @@ int noudp_load(knotd_mod_t *mod) ctx->rate = conf.single.integer; if (ctx->rate > 0) { knotd_conf_t udp = knotd_conf_env(mod, KNOTD_CONF_ENV_WORKERS_UDP); - // TODO xdp ? - size_t udp_workers = udp.single.integer; - ctx->counters = calloc(udp_workers, sizeof(uint32_t)); + knotd_conf_t xdp = knotd_conf_env(mod, KNOTD_CONF_ENV_WORKERS_XDP); + size_t workers = udp.single.integer + xdp.single.integer; + ctx->counters = calloc(workers, sizeof(uint32_t)); if (ctx->counters == NULL) { free(ctx); return KNOT_ENOMEM; diff --git a/src/knot/nameserver/query_module.c b/src/knot/nameserver/query_module.c index 6768e498a072bec9d753c5d165446ffc1a0c080f..3954558a862e784326930a689fd933d5852fdafc 100644 --- a/src/knot/nameserver/query_module.c +++ b/src/knot/nameserver/query_module.c @@ -348,6 +348,9 @@ knotd_conf_t knotd_conf_env(knotd_mod_t *mod, knotd_conf_env_t env) case KNOTD_CONF_ENV_WORKERS_TCP: out.single.integer = config->cache.srv_tcp_threads; break; + case KNOTD_CONF_ENV_WORKERS_XDP: + out.single.integer = config->cache.srv_xdp_threads; + break; default: return out; } diff --git a/src/knot/server/server.c b/src/knot/server/server.c index a6455873f03ba73d0f19d95b353356785c687da1..67a61596f3107de238f2555a33d4e121c3c7c02d 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -16,17 +16,10 @@ #define __APPLE_USE_RFC_3542 -#include <stdlib.h> #include <assert.h> -#include <ifaddrs.h> -#include <netinet/tcp.h> #include <sys/resource.h> -#include "libknot/errcode.h" -#ifdef ENABLE_XDP -#include "libknot/xdp/af_xdp.h" -#include "libknot/xdp/eth-tools.h" -#endif +#include "libknot/libknot.h" #include "libknot/yparser/ypschema.h" #include "knot/common/log.h" #include "knot/common/stats.h" @@ -54,7 +47,7 @@ enum { }; /*! \brief Unbind interface and clear the structure. */ -static void server_deinit_iface(iface_t *iface) +static void server_deinit_iface(iface_t *iface, bool dealloc) { assert(iface); @@ -87,6 +80,10 @@ static void server_deinit_iface(iface_t *iface) } free(iface->fd_tcp); } + + if (dealloc) { + free(iface); + } } /*! \brief Deinit server interface list. */ @@ -94,7 +91,7 @@ static void server_deinit_iface_list(iface_t *ifaces, size_t n) { if (ifaces != NULL) { for (size_t i = 0; i < n; i++) { - server_deinit_iface(ifaces + i); + server_deinit_iface(ifaces + i, false); } free(ifaces); } @@ -201,148 +198,54 @@ static int enable_fastopen(int sock, int backlog) return KNOT_EOK; } -__attribute__((unused)) // ifndef ENABLE_REUSEPORT -static int iface_addr2name(const struct sockaddr_storage *add, char **out) -{ - struct ifaddrs *ifas = NULL, *orig = NULL; - if (getifaddrs(&orig)) { - return -errno; - } - - size_t matches = 0; - char *match_name = NULL; - - for (ifas = orig; ifas != NULL; ifas = ifas->ifa_next) { - const struct sockaddr_storage *ifss = (struct sockaddr_storage *)ifas->ifa_addr; // ?? - if (!ifss) { // allowed; observed on interfaces without any address - continue; - } - - if ((ifss->ss_family == add->ss_family && sockaddr_is_any(add)) || - sockaddr_net_match(ifss, add, 64)) { // sockaddr_cmp() compares also port numbers - matches++; - match_name = ifas->ifa_name; - } - } - - if (matches == 1) { - *out = strdup(match_name); - freeifaddrs(orig); - return *out == NULL ? KNOT_ENOMEM : KNOT_EOK; - } - - freeifaddrs(orig); - *out = malloc(64); - if (*out == NULL) { - return KNOT_ENOMEM; - } - sockaddr_tostr(*out, 64, add); - return matches == 0 ? KNOT_EADDRNOTAVAIL : KNOT_ELIMIT; -} - -// returns >= 0: the number of RX queues; < 0 : error -static int get_xdp_iface(struct sockaddr_storage *addr, char **out_devname, uint16_t *out_port) -{ -#ifndef ENABLE_XDP - return 0; -#else - - char *dev = NULL; - int port = 0; - if (addr->ss_family == AF_UNIX) { - dev = strdup(((struct sockaddr_un *)addr)->sun_path); - if (dev == NULL) { - log_warning("%s", knot_strerror(KNOT_ENOMEM)); - return KNOT_ENOMEM; - } - char *sport = strchr(dev, '@'); - if (sport == NULL || (port = atoi(sport + 1)) < 1 || port > 0xffff) { - log_warning("Wrong format of XDP listen, expected 'dev@port': '%s'", dev); - free(dev); - return KNOT_EINVAL; - } - *sport = '\0'; - } else { - int ret = iface_addr2name(addr, &dev); - if (ret != KNOT_EOK) { - if (dev != NULL) { - log_warning("XDP failed: address %s corresponds to %s net devices", dev, ret == KNOT_EADDRNOTAVAIL ? "none" : "more"); - } else { - log_warning("failed to find dev for address (%s)", knot_strerror(ret)); - } - return ret; - } - port = sockaddr_port(addr); - if (port < 1 || port > 0xffff) { - log_warning("port out of range"); - free(dev); - return KNOT_ELIMIT; - } - } - - int rx_queues = knot_eth_get_rx_queues(dev); - if (rx_queues < 0) { - log_warning("failed to obtain RX queue count for iface %s (%s)\n", dev, knot_strerror(rx_queues)); - free(dev); - return rx_queues; - } - - if (out_devname != NULL) { - *out_devname = dev; - } else { - free(dev); - } - if (out_port != NULL) { - *out_port = port; - } - return rx_queues; -#endif -} - static iface_t *server_init_xdp_iface(struct sockaddr_storage *addr, unsigned *thread_id_start) { #ifndef ENABLE_XDP assert(0); return NULL; #else - char *dev = NULL; - uint16_t port = 0; - int rx_queues = get_xdp_iface(addr, &dev, &port); - if (rx_queues < 0) { + conf_xdp_iface_t iface; + int ret = conf_xdp_iface(addr, &iface); + if (ret != KNOT_EOK) { + log_error("failed to initialize XDP interface (%s)", + knot_strerror(ret)); return NULL; } iface_t *new_if = calloc(1, sizeof(*new_if)); if (new_if == NULL) { - log_error("failed to initialize interface"); - free(dev); + log_error("failed to initialize XDP interface (%s)", + knot_strerror(KNOT_ENOMEM)); return NULL; } memcpy(&new_if->addr, addr, sizeof(*addr)); - new_if->fd_xdp = malloc(rx_queues * sizeof(int)); - new_if->sock_xdp = calloc(rx_queues, sizeof(*new_if->sock_xdp)); + new_if->fd_xdp = calloc(iface.queues, sizeof(int)); + new_if->sock_xdp = calloc(iface.queues, sizeof(*new_if->sock_xdp)); if (new_if->fd_xdp == NULL || new_if->sock_xdp == NULL) { - log_warning("failed to init XDP: not enough memory\n"); + log_error("failed to initialize XDP interface (%s)", + knot_strerror(KNOT_ENOMEM)); free(new_if); - free(dev); return NULL; } new_if->fd_thread_ids = *thread_id_start; - *thread_id_start += rx_queues; - - for (int i = 0; i < rx_queues; i++) { - int ret = knot_xdp_init(new_if->sock_xdp + i, dev, i, port, i == 0); + *thread_id_start += iface.queues; + for (int i = 0; i < iface.queues; i++) { + ret = knot_xdp_init(new_if->sock_xdp + i, iface.name, i, + iface.port, i == 0); if (ret != KNOT_EOK) { - log_warning("failed to init XDP in dev %s queue %d (%s)", dev, i, knot_strerror(ret)); + log_warning("failed to initialize XDP interface %s@%u, queue %d (%s)", + iface.name, iface.port, i, knot_strerror(ret)); new_if->fd_xdp[i] = -1; } else { new_if->fd_xdp[i] = knot_xdp_socket_fd(new_if->sock_xdp[i]); - } new_if->fd_xdp_count++; } + + log_debug("initialized XDP interface %s@%u, queues %d", + iface.name, iface.port, iface.queues); return new_if; #endif } @@ -392,10 +295,9 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr, new_if->fd_udp = malloc(udp_socket_count * sizeof(int)); new_if->fd_tcp = malloc(tcp_socket_count * sizeof(int)); - if (new_if->fd_udp == NULL || new_if->fd_tcp == NULL) { log_error("failed to initialize interface"); - server_deinit_iface(new_if); + server_deinit_iface(new_if, true); return NULL; } @@ -419,7 +321,7 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr, if (sock < 0) { log_error("cannot bind address %s UDP (%s)", addr_str, knot_strerror(sock)); - server_deinit_iface(new_if); + server_deinit_iface(new_if, true); return NULL; } @@ -465,7 +367,7 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr, if (sock < 0) { log_error("cannot bind address %s TCP (%s)", addr_str, knot_strerror(sock)); - server_deinit_iface(new_if); + server_deinit_iface(new_if, true); return NULL; } @@ -482,7 +384,7 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr, int ret = listen(sock, TCP_BACKLOG_SIZE); if (ret < 0) { log_error("failed to listen on TCP interface %s", addr_str); - server_deinit_iface(new_if); + server_deinit_iface(new_if, true); return NULL; } @@ -518,38 +420,44 @@ static int configure_sockets(conf_t *conf, server_t *s) if (lisxdp_val.code == KNOT_EOK) { struct rlimit no_limit = { RLIM_INFINITY, RLIM_INFINITY }; int ret = setrlimit(RLIMIT_MEMLOCK, &no_limit); - if (ret) { + if (ret != 0) { + log_error("failed to set RLIMIT_MEMLOCK resource limit (%s)", + knot_strerror(ret)); return -errno; } } - size_t nifs = conf_val_count(&listen_val) + conf_val_count(&lisxdp_val), real_n = 0; + size_t real_nifs = 0; + size_t nifs = conf_val_count(&listen_val) + conf_val_count(&lisxdp_val); iface_t *newlist = calloc(nifs, sizeof(*newlist)); if (newlist == NULL) { return KNOT_ENOMEM; } + /* Normal UDP and TCP sockets. */ + unsigned size_udp = s->handlers[IO_UDP].handler.unit->size; + unsigned size_tcp = s->handlers[IO_TCP].handler.unit->size; + bool tcp_reuseport = conf->cache.srv_tcp_reuseport; char *rundir = conf_abs_path(&rundir_val, NULL); while (listen_val.code == KNOT_EOK) { - /* Log interface binding. */ struct sockaddr_storage addr = conf_addr(&listen_val, rundir); char addr_str[SOCKADDR_STRLEN] = { 0 }; sockaddr_tostr(addr_str, sizeof(addr_str), &addr); log_info("binding to interface %s", addr_str); - /* Create new interface. */ - unsigned size_udp = s->handlers[IO_UDP].handler.unit->size; - unsigned size_tcp = s->handlers[IO_TCP].handler.unit->size; - bool tcp_reuseport = conf->cache.srv_tcp_reuseport; - iface_t *new_if = server_init_iface(&addr, size_udp, size_tcp, tcp_reuseport); + iface_t *new_if = server_init_iface(&addr, size_udp, size_tcp, + tcp_reuseport); if (new_if != NULL) { - memcpy(&newlist[real_n++], new_if, sizeof(*newlist)); + memcpy(&newlist[real_nifs++], new_if, sizeof(*newlist)); free(new_if); } - conf_val_next(&listen_val); } - unsigned thread_id = s->handlers[IO_UDP].handler.unit->size + s->handlers[IO_TCP].handler.unit->size; + free(rundir); + + /* XDP sockets. */ + unsigned thread_id = s->handlers[IO_UDP].handler.unit->size + + s->handlers[IO_TCP].handler.unit->size; while (lisxdp_val.code == KNOT_EOK) { struct sockaddr_storage addr = conf_addr(&lisxdp_val, NULL); char addr_str[SOCKADDR_STRLEN] = { 0 }; @@ -558,20 +466,19 @@ static int configure_sockets(conf_t *conf, server_t *s) iface_t *new_if = server_init_xdp_iface(&addr, &thread_id); if (new_if != NULL) { - memcpy(&newlist[real_n++], new_if, sizeof(*newlist)); + memcpy(&newlist[real_nifs++], new_if, sizeof(*newlist)); free(new_if); } conf_val_next(&lisxdp_val); } - assert(real_n <= nifs); - nifs = real_n; - free(rundir); + assert(real_nifs <= nifs); + nifs = real_nifs; /* Publish new list. */ s->ifaces = newlist; s->n_ifaces = nifs; - /* Set the ID's (thread_id) of both the TCP and UDP threads. */ + /* Set the thread IDs. */ unsigned thread_count = 0; for (unsigned proto = IO_UDP; proto <= IO_XDP; ++proto) { dt_unit_t *tu = s->handlers[proto].handler.unit; @@ -583,22 +490,6 @@ static int configure_sockets(conf_t *conf, server_t *s) return KNOT_EOK; } -int server_count_xdp_threads(conf_t *conf) -{ - int res = 0, ret; - conf_val_t lisxdp_val = conf_get(conf, C_SRV, C_LISTEN_XDP); - while (lisxdp_val.code == KNOT_EOK) { - struct sockaddr_storage addr = conf_addr(&lisxdp_val, NULL); - ret = get_xdp_iface(&addr, NULL, NULL); - if (ret < 0) { - return ret; - } - res += ret; - conf_val_next(&lisxdp_val); - } - return res; -} - int server_init(server_t *server, int bg_workers) { if (server == NULL) { @@ -819,13 +710,14 @@ static int reload_conf(conf_t *new_conf) return KNOT_EOK; } -/*! \brief Check if parameter listen has been changed since knotd started. */ -static bool listen_changed(conf_t *conf, server_t *server, const yp_name_t *item) +/*! \brief Check if parameter listen(-xdp) has been changed since knotd started. */ +static bool listen_changed(conf_t *conf, server_t *server) { assert(server->ifaces); - conf_val_t listen_val = conf_get(conf, C_SRV, item); - size_t new_count = conf_val_count(&listen_val); + conf_val_t listen_val = conf_get(conf, C_SRV, C_LISTEN); + conf_val_t lisxdp_val = conf_get(conf, C_SRV, C_LISTEN_XDP); + size_t new_count = conf_val_count(&listen_val) + conf_val_count(&lisxdp_val); size_t old_count = server->n_ifaces; if (new_count != old_count) { return true; @@ -846,15 +738,29 @@ static bool listen_changed(conf_t *conf, server_t *server, const yp_name_t *item break; } } - if (!found) { break; } conf_val_next(&listen_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) { + matches++; + found = true; + break; + } + } + if (!found) { + break; + } + conf_val_next(&lisxdp_val); + } + return matches != old_count; } @@ -868,7 +774,6 @@ static void warn_server_reconfigure(conf_t *conf, server_t *server) static bool warn_tcp = true; static bool warn_bg = true; static bool warn_listen = true; - static bool warn_listen_xdp = true; if (warn_tcp_reuseport && conf->cache.srv_tcp_reuseport != conf_tcp_reuseport(conf)) { log_warning(msg, &C_TCP_REUSEPORT[1]); @@ -890,15 +795,10 @@ static void warn_server_reconfigure(conf_t *conf, server_t *server) warn_bg = false; } - if (warn_listen && listen_changed(conf, server, C_LISTEN)) { - log_warning(msg, &C_LISTEN[1]); + if (warn_listen && listen_changed(conf, server)) { + log_warning(msg, "listen(-xdp)"); warn_listen = false; } - - if (warn_listen_xdp && listen_changed(conf, server, C_LISTEN_XDP)) { - log_warning(msg, &C_LISTEN_XDP[1]); - warn_listen_xdp = false; - } } int server_reload(server_t *server) @@ -1003,16 +903,15 @@ static int set_handler(server_t *server, int index, unsigned size, runnable_t ru return KNOT_EOK; } -/*! \brief Reconfigure UDP and TCP query processing threads. */ -static int configure_threads(conf_t *conf, server_t *server, size_t xdp_threads) +static int configure_threads(conf_t *conf, server_t *server) { int ret = set_handler(server, IO_UDP, conf->cache.srv_udp_threads, udp_master); if (ret != KNOT_EOK) { return ret; } - if (xdp_threads > 0) { - ret = set_handler(server, IO_XDP, xdp_threads, udp_master); + if (conf->cache.srv_xdp_threads > 0) { + ret = set_handler(server, IO_XDP, conf->cache.srv_xdp_threads, udp_master); if (ret != KNOT_EOK) { return ret; } @@ -1078,14 +977,8 @@ void server_reconfigure(conf_t *conf, server_t *server) knot_db_lmdb_get_path(conf->db)); } - if ((ret = server_count_xdp_threads(conf)) < 0) { - log_error("failed to configure XDP thread count (%s)", - knot_strerror(ret)); - ret = 0; - } - /* Configure server threads. */ - if ((ret = configure_threads(conf, server, ret)) != KNOT_EOK) { + if ((ret = configure_threads(conf, server)) != KNOT_EOK) { log_error("failed to configure server threads (%s)", knot_strerror(ret)); } diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c index 4ca76886158f623ecabbffce0ce27c8c8295e507..0b8a843738bd2673887f39eae31e5d6fb83870ff 100644 --- a/src/knot/server/udp-handler.c +++ b/src/knot/server/udp-handler.c @@ -38,9 +38,6 @@ #include "knot/query/layer.h" #include "knot/server/server.h" #include "knot/server/udp-handler.h" -#ifdef ENABLE_XDP -#include "libknot/xdp/af_xdp.h" -#endif /* ENABLE_XDP */ /* Buffer identifiers. */ enum { diff --git a/src/libknot/Makefile.inc b/src/libknot/Makefile.inc index de2424f7bb63a1fd388ea79cc50f26716ce34ea3..6fe35f495f1c773b872b2a8f19d4b1d25b3dfa19 100644 --- a/src/libknot/Makefile.inc +++ b/src/libknot/Makefile.inc @@ -81,17 +81,17 @@ libknot_la_CPPFLAGS += $(libbpf_CFLAGS) libknot_la_LIBADD += $(libbpf_LIBS) nobase_include_libknot_HEADERS += \ - libknot/xdp/af_xdp.h \ libknot/xdp/bpf-consts.h \ - libknot/xdp/eth-tools.h + libknot/xdp/eth.h \ + libknot/xdp/xdp.h libknot_la_SOURCES += \ - libknot/xdp/af_xdp.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-tools.c + libknot/xdp/eth.c \ + libknot/xdp/xdp.c endif ENABLE_XDP DIST_SUBDIRS = libknot/xdp diff --git a/src/libknot/error.c b/src/libknot/error.c index 7a58f8bc4f3e5a4b1a6705b86d698c2956699501..af412b4d683a562d1045f9f25482f872019728a3 100644 --- a/src/libknot/error.c +++ b/src/libknot/error.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2020 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 @@ -72,7 +72,7 @@ static const struct error errors[] = { { KNOT_ETIMEOUT, "connection timeout" }, { KNOT_ENODIFF, "cannot create zone diff" }, { KNOT_ENOTSIG, "expected a TSIG or SIG(0)" }, - { KNOT_ELIMIT, "exceeded response rate limit" }, + { KNOT_ELIMIT, "exceeded limit" }, { KNOT_EZONESIZE, "zone size exceeded" }, { KNOT_EOF, "end of file" }, { KNOT_ESYSTEM, "system error" }, diff --git a/src/libknot/libknot.h b/src/libknot/libknot.h index e15e627e9911c45f424544e0b6a1417740aaae34..6d8a3e34eb9c249506c9995edcfaba5cd593fa0b 100644 --- a/src/libknot/libknot.h +++ b/src/libknot/libknot.h @@ -62,8 +62,10 @@ #include "libknot/rrtype/soa.h" #include "libknot/rrtype/tsig.h" #include "libknot/wire.h" -#include "libknot/xdp/af_xdp.h" +#ifdef ENABLE_XDP +#include "libknot/xdp/xdp.h" #include "libknot/xdp/bpf-consts.h" -#include "libknot/xdp/eth-tools.h" +#include "libknot/xdp/eth.h" +#endif /*! @} */ diff --git a/src/libknot/xdp/bpf-user.h b/src/libknot/xdp/bpf-user.h index ecdd2ed6b8b43191a213544dc595dcef0352a9d0..ce30582aaedc3e2ae657eb86de0484c40f42ddbb 100644 --- a/src/libknot/xdp/bpf-user.h +++ b/src/libknot/xdp/bpf-user.h @@ -18,7 +18,7 @@ #include <bpf/xsk.h> -#include "libknot/xdp/af_xdp.h" +#include "libknot/xdp/xdp.h" struct kxsk_iface { /*! Interface name. */ diff --git a/src/libknot/xdp/eth-tools.c b/src/libknot/xdp/eth.c similarity index 55% rename from src/libknot/xdp/eth-tools.c rename to src/libknot/xdp/eth.c index a996d6272f3d0f426f1d310ce84aa7fbc3957635..a2249e5b7c4f3202e89049babbf9961113a4a21f 100644 --- a/src/libknot/xdp/eth-tools.c +++ b/src/libknot/xdp/eth.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2020 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,6 +15,7 @@ */ #include <errno.h> +#include <ifaddrs.h> #include <linux/ethtool.h> #include <linux/if.h> #include <linux/sockios.h> @@ -22,11 +23,13 @@ #include <unistd.h> #include "contrib/openbsd/strlcpy.h" +#include "contrib/sockaddr.h" #include "libknot/attribute.h" #include "libknot/errcode.h" +#include "libknot/xdp/eth.h" _public_ -int knot_eth_get_rx_queues(const char *devname) +int knot_eth_queues(const char *devname) { if (devname == NULL) { return KNOT_EINVAL; @@ -63,3 +66,42 @@ int knot_eth_get_rx_queues(const char *devname) close(fd); return ret; } + +_public_ +int knot_eth_name_from_addr(const struct sockaddr_storage *addr, char *out, + size_t out_len) +{ + if (addr == NULL || out == NULL) { + return KNOT_EINVAL; + } + + struct ifaddrs *ifaces = NULL; + if (getifaddrs(&ifaces) != 0) { + return -errno; + } + + size_t matches = 0; + char *match_name = NULL; + + for (struct ifaddrs *ifa = ifaces; ifa != NULL; ifa = ifa->ifa_next) { + const struct sockaddr_storage *ifss = (struct sockaddr_storage *)ifa->ifa_addr; + if (ifss == NULL) { // Observed on interfaces without any address. + continue; + } + + if ((ifss->ss_family == addr->ss_family && sockaddr_is_any(addr)) || + sockaddr_cmp(ifss, addr, true) == 0) { + matches++; + match_name = ifa->ifa_name; + } + } + + if (matches == 1) { + size_t len = strlcpy(out, match_name, out_len); + freeifaddrs(ifaces); + return (len >= out_len) ? KNOT_ESPACE : KNOT_EOK; + } + + freeifaddrs(ifaces); + return matches == 0 ? KNOT_EADDRNOTAVAIL : KNOT_ELIMIT; +} diff --git a/src/libknot/xdp/eth-tools.h b/src/libknot/xdp/eth.h similarity index 61% rename from src/libknot/xdp/eth-tools.h rename to src/libknot/xdp/eth.h index 538d067b9ca968869c1a05b9091738fa2fc0c571..b5fc7ff259859ea4653b7a12593c8639cffcfadd 100644 --- a/src/libknot/xdp/eth-tools.h +++ b/src/libknot/xdp/eth.h @@ -16,13 +16,27 @@ #pragma once +#include <stddef.h> + /*! - * \brief Get number of RX queues of a network iface. + * \brief Get number of combined queues of a network interface. * - * \param devname Name of the ethdev (e.g. eth1). + * \param devname Name of the ethdev (e.g. eth1). * * \retval < 0 KNOT_E* if error. * \retval 1 Default no of queues if the dev does not support. * \return > 0 Number of queues. */ -int knot_eth_get_rx_queues(const char *devname); +int knot_eth_queues(const char *devname); + +/*! + * \brief Get the corresponding network interface name for the address. + * + * \param addr Address of the inteface. + * \param out Output buffer for the interface name. + * \param out_len Size of the output buffer. + * + * \return KNOT_E* + */ +int knot_eth_name_from_addr(const struct sockaddr_storage *addr, char *out, + size_t out_len); diff --git a/src/libknot/xdp/af_xdp.c b/src/libknot/xdp/xdp.c similarity index 99% rename from src/libknot/xdp/af_xdp.c rename to src/libknot/xdp/xdp.c index 1e67b0d542e9b25b6713d5f2be2116796733a84f..9236998e627d9c1562b6ac5843c2cf8caf025ae0 100644 --- a/src/libknot/xdp/af_xdp.c +++ b/src/libknot/xdp/xdp.c @@ -28,8 +28,8 @@ #include "libknot/attribute.h" #include "libknot/endian.h" #include "libknot/errcode.h" -#include "libknot/xdp/af_xdp.h" #include "libknot/xdp/bpf-user.h" +#include "libknot/xdp/xdp.h" #include "contrib/macros.h" /* Don't fragment flag. */ diff --git a/src/libknot/xdp/af_xdp.h b/src/libknot/xdp/xdp.h similarity index 98% rename from src/libknot/xdp/af_xdp.h rename to src/libknot/xdp/xdp.h index a00781582cd176ae857bfd403e512848e30eafe3..dc0538666cbafdf73d58bb1a58e3a5ebcb4397a1 100644 --- a/src/libknot/xdp/af_xdp.h +++ b/src/libknot/xdp/xdp.h @@ -23,6 +23,10 @@ #include "libknot/xdp/bpf-consts.h" +#ifdef ENABLE_XDP +#define KNOT_XDP_AVAILABLE 1 +#endif + /*! \brief A packet with src & dst MAC & IP addrs + UDP payload. */ typedef struct { struct sockaddr_in6 ip_from; diff --git a/src/utils/xdp-gun/main.c b/src/utils/xdp-gun/main.c index 6cc8dddbd151f90ff1b8740c6fe6a5b3c0ab110b..3ddee0e4819dd9599f024becbeea8ffaee423569 100644 --- a/src/utils/xdp-gun/main.c +++ b/src/utils/xdp-gun/main.c @@ -34,10 +34,7 @@ #include <sys/socket.h> #include <sys/resource.h> -#include "libknot/endian.h" -#include "libknot/error.h" -#include "libknot/xdp/af_xdp.h" -#include "libknot/xdp/eth-tools.h" +#include "libknot/libknot.h" #include "contrib/openbsd/strlcpy.h" #include "load_queries.h" @@ -459,7 +456,7 @@ int main(int argc, char *argv[]) goto pusage; } - arg = knot_eth_get_rx_queues(ctx.dev); + arg = knot_eth_queues(ctx.dev); if (arg >= 0) { ctx.n_threads = arg; if (ctx.qps < ctx.n_threads) {