From: Remi Gacogne Date: Fri, 27 Dec 2024 14:54:43 +0000 (+0100) Subject: dnsdist: Implement XSK and eBPF via YAML X-Git-Tag: dnsdist-2.0.0-alpha1~160^2~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f74a03f5b5ef2cf6eb6d0df092314f2a0862020a;p=thirdparty%2Fpdns.git dnsdist: Implement XSK and eBPF via YAML --- diff --git a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc index da6dae586b..bd0b6553cf 100644 --- a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc +++ b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc @@ -37,10 +37,12 @@ #include "dnsdist-rules-factory.hh" #include "dnsdist-kvs.hh" #include "dnsdist-web.hh" +#include "dnsdist-xsk.hh" #include "doh.hh" #include "fstrm_logger.hh" #include "iputils.hh" #include "remote_logger.hh" +#include "xsk.hh" #include "rust/cxx.h" #include "rust/lib.rs.h" @@ -53,7 +55,9 @@ namespace dnsdist::configuration::yaml { #if defined(HAVE_YAML_CONFIGURATION) -using RegisteredTypes = std::variant, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr>; +using XSKMap = std::vector>; + +using RegisteredTypes = std::variant, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr>; static LockGuarded> s_registeredTypesMap; static std::atomic s_inConfigCheckMode; static std::atomic s_inClientMode; @@ -423,14 +427,25 @@ static std::shared_ptr createBackendFromConfiguration(const dns backendConfig.remote = ComboAddress(std::string(config.address), serverPort); -#warning handle XSK - if (protocol == "dot" || protocol == "doh") { tlsCtx = getTLSContext(backendConfig.d_tlsParams); } auto downstream = std::make_shared(std::move(backendConfig), std::move(tlsCtx), !configCheck); +#if defined(HAVE_XSK) + if (!config.xsk.empty()) { + auto xskMap = getRegisteredTypeByName(config.xsk); + if (!xskMap) { + throw std::runtime_error("XSK map " + std::string(config.xsk) + " attached to backend " + std::string(config.address) + " not found"); + } + downstream->registerXsk(*xskMap); + if (!configCheck) { + infolog("Added downstream server %s via XSK in %s mode", std::string(config.address), xskMap->at(0)->getXDPMode()); + } + } +#endif /* defined(HAVE_XSK) */ + const auto& autoUpgradeConf = config.auto_upgrade; if (autoUpgradeConf.enabled && downstream->getProtocol() != dnsdist::Protocol::DoT && downstream->getProtocol() != dnsdist::Protocol::DoH) { dnsdist::ServiceDiscovery::addUpgradeableServer(downstream, autoUpgradeConf.interval, std::string(autoUpgradeConf.pool), autoUpgradeConf.doh_key, autoUpgradeConf.keep); @@ -490,6 +505,44 @@ bool loadConfigurationFromFile(const std::string& fileName, bool isClient, bool }); } +#if defined(HAVE_EBPF) + if (!configCheck && globalConfig.ebpf.ipv4.max_entries > 0 && globalConfig.ebpf.ipv6.max_entries > 0 && globalConfig.ebpf.qnames.max_entries > 0) { + BPFFilter::MapFormat format = globalConfig.ebpf.external ? BPFFilter::MapFormat::WithActions : BPFFilter::MapFormat::Legacy; + std::unordered_map mapsConfig; + + const auto convertParamsToConfig = [&mapsConfig](const std::string& name, BPFFilter::MapType type, const dnsdist::rust::settings::EbpfMapConfiguration& mapConfig) { + if (mapConfig.max_entries == 0) { + return; + } + BPFFilter::MapConfiguration config; + config.d_type = type; + config.d_maxItems = mapConfig.max_entries; + config.d_pinnedPath = std::string(mapConfig.pinned_path); + mapsConfig[name] = std::move(config); + }; + + convertParamsToConfig("ipv4", BPFFilter::MapType::IPv4, globalConfig.ebpf.ipv4); + convertParamsToConfig("ipv6", BPFFilter::MapType::IPv6, globalConfig.ebpf.ipv6); + convertParamsToConfig("qnames", BPFFilter::MapType::QNames, globalConfig.ebpf.qnames); + convertParamsToConfig("cidr4", BPFFilter::MapType::CIDR4, globalConfig.ebpf.cidr_ipv4); + convertParamsToConfig("cidr6", BPFFilter::MapType::CIDR6, globalConfig.ebpf.cidr_ipv6); + auto filter = std::make_shared(mapsConfig, format, globalConfig.ebpf.external); + g_defaultBPFFilter = std::move(filter); + } +#endif /* defined(HAVE_EBPF) */ + +#if defined(HAVE_XSK) + for (const auto& xskEntry : globalConfig.xsk) { + auto map = std::shared_ptr(); + for (size_t counter = 0; counter < xskEntry.queues; ++counter) { + auto socket = std::make_shared(xskEntry.frames, std::string(xskEntry.interface), counter, std::string(xskEntry.map_path)); + dnsdist::xsk::g_xsk.push_back(socket); + map->push_back(std::move(socket)); + } + registerType(map, xskEntry.name); + } +#endif /* defined(HAVE_XSK) */ + for (const auto& bind : globalConfig.binds) { updateImmutableConfiguration([&bind](ImmutableConfiguration& config) { auto protocol = boost::to_lower_copy(std::string(bind.protocol)); @@ -502,6 +555,17 @@ bool loadConfigurationFromFile(const std::string& fileName, bool isClient, bool } ComboAddress listeningAddress(std::string(bind.listen_address), defaultPort); auto cpus = getCPUPiningFromStr("binds", std::string(bind.cpus)); + std::shared_ptr xskMap; + if (!bind.xsk.empty()) { + xskMap = getRegisteredTypeByName(bind.xsk); + if (!xskMap) { + throw std::runtime_error("XSK map " + std::string(bind.xsk) + " attached to bind " + std::string(bind.listen_address) + " not found"); + } + if (xskMap->size() != bind.threads) { + throw std::runtime_error("XSK map " + std::string(bind.xsk) + " attached to bind " + std::string(bind.listen_address) + " has less queues than the number of threads of the bind"); + } + } + for (size_t idx = 0; idx < bind.threads; idx++) { #if defined(HAVE_DNSCRYPT) std::shared_ptr dnsCryptContext; @@ -552,6 +616,17 @@ bool loadConfigurationFromFile(const std::string& fileName, bool isClient, bool #if defined(HAVE_DNSCRYPT) state->dnscryptCtx = dnsCryptContext; #endif /* defined(HAVE_DNSCRYPT) */ +#if defined(HAVE_XSK) + if (xskMap) { + auto xsk = xskMap->at(idx); + state->xskInfo = XskWorker::create(XskWorker::Type::Bidirectional, xsk->sharedEmptyFrameOffset); + xsk->addWorker(state->xskInfo); + xsk->addWorkerRoute(state->xskInfo, listeningAddress); + state->xskInfoResponder = XskWorker::create(XskWorker::Type::OutgoingOnly, xsk->sharedEmptyFrameOffset); + xsk->addWorker(state->xskInfoResponder); + vinfolog("Enabling XSK in %s mode for incoming UDP packets to %s", xsk->getXDPMode(), listeningAddress.toStringWithPort()); + } +#endif /* defined(HAVE_XSK) */ config.d_frontends.emplace_back(std::move(state)); } } diff --git a/pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs b/pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs index 2a45c57a05..f975c906a6 100644 --- a/pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs +++ b/pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs @@ -1115,6 +1115,7 @@ mod dnsdistsettings { console: ConsoleConfiguration, dynamic_rules: Vec, dynamic_rules_settings: DynamicRulesSettingsConfiguration, + ebpf: EbpfConfiguration, edns_client_subnet: EdnsClientSubnetConfiguration, general: GeneralConfiguration, key_value_stores: KeyValueStoresConfiguration, @@ -1135,6 +1136,7 @@ mod dnsdistsettings { tuning: TuningConfiguration, webserver: WebserverConfiguration, xfr_response_rules: Vec, + xsk: Vec, } #[derive(Deserialize, Serialize, Debug, PartialEq)] @@ -1336,6 +1338,32 @@ mod dnsdistsettings { max_concurrent_connections: u64, } + #[derive(Deserialize, Serialize, Debug, PartialEq)] + #[serde(deny_unknown_fields)] + struct EbpfMapConfiguration { + #[serde(rename = "max-entries", default, skip_serializing_if = "crate::is_default")] + max_entries: u32, + #[serde(rename = "pinned-path", default, skip_serializing_if = "crate::is_default")] + pinned_path: String, + } + + #[derive(Deserialize, Serialize, Debug, PartialEq)] + #[serde(deny_unknown_fields)] + struct EbpfConfiguration { + #[serde(default, skip_serializing_if = "crate::is_default")] + ipv4: EbpfMapConfiguration, + #[serde(default, skip_serializing_if = "crate::is_default")] + ipv6: EbpfMapConfiguration, + #[serde(rename = "cidr-ipv4", default, skip_serializing_if = "crate::is_default")] + cidr_ipv4: EbpfMapConfiguration, + #[serde(rename = "cidr-ipv6", default, skip_serializing_if = "crate::is_default")] + cidr_ipv6: EbpfMapConfiguration, + #[serde(default, skip_serializing_if = "crate::is_default")] + qnames: EbpfMapConfiguration, + #[serde(default, skip_serializing_if = "crate::is_default")] + external: bool, + } + #[derive(Deserialize, Serialize, Debug, PartialEq)] #[serde(deny_unknown_fields)] struct EdnsClientSubnetConfiguration { @@ -1644,6 +1672,8 @@ mod dnsdistsettings { dnscrypt: IncomingDnscryptConfiguration, #[serde(rename = "additional-addresses", default, skip_serializing_if = "crate::is_default")] additional_addresses: Vec, + #[serde(default, skip_serializing_if = "crate::is_default")] + xsk: String, } #[derive(Deserialize, Serialize, Debug, PartialEq)] @@ -1792,6 +1822,8 @@ mod dnsdistsettings { mac_address: String, #[serde(default, skip_serializing_if = "crate::is_default")] cpus: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + xsk: String, } #[derive(Deserialize, Serialize, Debug, PartialEq)] @@ -2040,6 +2072,18 @@ mod dnsdistsettings { action: SharedDNSResponseAction, } + #[derive(Deserialize, Serialize, Debug, PartialEq)] + #[serde(deny_unknown_fields)] + struct XskConfiguration { + name: String, + interface: String, + queues: u16, + #[serde(default = "crate::U32::<65536>::value", skip_serializing_if = "crate::U32::<65536>::is_equal")] + frames: u32, + #[serde(rename = "map-path", default = "crate::default_value_xsk_map_path", skip_serializing_if = "crate::default_value_equal_xsk_map_path")] + map_path: String, + } + /* * Functions callable from Rust (actions and selectors) @@ -2281,6 +2325,8 @@ impl dnsdistsettings::SharedDNSResponseAction { dynamic_rules: Vec, #[serde(rename = "dynamic-rules-settings", default, skip_serializing_if = "crate::is_default")] dynamic_rules_settings: dnsdistsettings::DynamicRulesSettingsConfiguration, + #[serde(default, skip_serializing_if = "crate::is_default")] + ebpf: dnsdistsettings::EbpfConfiguration, #[serde(rename = "edns-client-subnet", default, skip_serializing_if = "crate::is_default")] edns_client_subnet: dnsdistsettings::EdnsClientSubnetConfiguration, #[serde(default, skip_serializing_if = "crate::is_default")] @@ -2321,6 +2367,8 @@ impl dnsdistsettings::SharedDNSResponseAction { webserver: dnsdistsettings::WebserverConfiguration, #[serde(rename = "xfr-response-rules", default, skip_serializing_if = "crate::is_default")] xfr_response_rules: Vec, + #[serde(default, skip_serializing_if = "crate::is_default")] + xsk: Vec, } #[derive(Default, Serialize, Deserialize, Debug, PartialEq)] @@ -2579,6 +2627,22 @@ impl Default for dnsdistsettings::ConsoleConfiguration { } +impl Default for dnsdistsettings::EbpfMapConfiguration { + fn default() -> Self { + let deserialized: dnsdistsettings::EbpfMapConfiguration = serde_yaml::from_str("").unwrap(); + deserialized + } +} + + +impl Default for dnsdistsettings::EbpfConfiguration { + fn default() -> Self { + let deserialized: dnsdistsettings::EbpfConfiguration = serde_yaml::from_str("").unwrap(); + deserialized + } +} + + impl Default for dnsdistsettings::EdnsClientSubnetConfiguration { fn default() -> Self { let deserialized: dnsdistsettings::EdnsClientSubnetConfiguration = serde_yaml::from_str("").unwrap(); @@ -3053,6 +3117,23 @@ impl Default for dnsdistsettings::LoadBalancingPoliciesConfiguration { } +// DEFAULT HANDLING for xsk_map_path +fn default_value_xsk_map_path() -> String { + String::from("/sys/fs/bpf/dnsdist/xskmap") +} +fn default_value_equal_xsk_map_path(value: &str)-> bool { + value == default_value_xsk_map_path() +} + + +impl Default for dnsdistsettings::XskConfiguration { + fn default() -> Self { + let deserialized: dnsdistsettings::XskConfiguration = serde_yaml::from_str("").unwrap(); + deserialized + } +} + + impl Default for GlobalConfigurationSerde { fn default() -> Self { let deserialized: GlobalConfigurationSerde = serde_yaml::from_str("").unwrap(); @@ -3084,6 +3165,7 @@ impl dnsdistsettings::GlobalConfiguration { sub_type.validate()?; } self.dynamic_rules_settings.validate()?; + self.ebpf.validate()?; self.edns_client_subnet.validate()?; self.general.validate()?; self.key_value_stores.validate()?; @@ -3114,6 +3196,9 @@ impl dnsdistsettings::GlobalConfiguration { self.webserver.validate()?; for sub_type in &self.xfr_response_rules { sub_type.validate()?; + } + for sub_type in &self.xsk { + sub_type.validate()?; } Ok(()) } @@ -3229,6 +3314,21 @@ impl dnsdistsettings::ConsoleConfiguration { Ok(()) } } +impl dnsdistsettings::EbpfMapConfiguration { + fn validate(&self) -> Result<(), ValidationError> { + Ok(()) + } +} +impl dnsdistsettings::EbpfConfiguration { + fn validate(&self) -> Result<(), ValidationError> { + self.ipv4.validate()?; + self.ipv6.validate()?; + self.cidr_ipv4.validate()?; + self.cidr_ipv6.validate()?; + self.qnames.validate()?; + Ok(()) + } +} impl dnsdistsettings::EdnsClientSubnetConfiguration { fn validate(&self) -> Result<(), ValidationError> { Ok(()) @@ -3472,6 +3572,11 @@ impl dnsdistsettings::ResponseRuleConfiguration { Ok(()) } } +impl dnsdistsettings::XskConfiguration { + fn validate(&self) -> Result<(), ValidationError> { + Ok(()) + } +} impl GlobalConfigurationSerde { fn validate(&self) -> Result<(), ValidationError> { for sub_type in &self.backends { @@ -3495,6 +3600,7 @@ impl GlobalConfigurationSerde { sub_type.validate()?; } self.dynamic_rules_settings.validate()?; + self.ebpf.validate()?; self.edns_client_subnet.validate()?; self.general.validate()?; self.key_value_stores.validate()?; @@ -3525,6 +3631,9 @@ impl GlobalConfigurationSerde { self.webserver.validate()?; for sub_type in &self.xfr_response_rules { sub_type.validate()?; + } + for sub_type in &self.xsk { + sub_type.validate()?; } Ok(()) } diff --git a/pdns/dnsdistdist/dnsdist-settings-definitions.yml b/pdns/dnsdistdist/dnsdist-settings-definitions.yml index 513d1f12ac..25910f860e 100644 --- a/pdns/dnsdistdist/dnsdist-settings-definitions.yml +++ b/pdns/dnsdistdist/dnsdist-settings-definitions.yml @@ -45,6 +45,10 @@ global: type: "DynamicRulesSettingsConfiguration" default: true description: "Dynamic rules-related settings" + - name: "ebpf" + type: "EbpfConfiguration" + default: true + description: "EBPF settings" - name: "edns-client-subnet" type: "EdnsClientSubnetConfiguration" default: true @@ -130,6 +134,10 @@ global: default: true skip-serde: true description: "List of rules executed when a XFR response is received" + - name: "xsk" + type: "Vec" + default: true + description: "List of AF_XDP / XSK objects" metrics: parameters: @@ -392,6 +400,36 @@ console: internal-field-name: "d_consoleMaxConcurrentConnections" runtime-configurable: false +ebpf-map: + parameters: + - name: "max-entries" + type: "u32" + default: 0 + - name: "pinned-path" + type: "String" + default: "" + +ebpf: + parameters: + - name: "ipv4" + type: "EbpfMapConfiguration" + default: true + - name: "ipv6" + type: "EbpfMapConfiguration" + default: true + - name: "cidr-ipv4" + type: "EbpfMapConfiguration" + default: true + - name: "cidr-ipv6" + type: "EbpfMapConfiguration" + default: true + - name: "qnames" + type: "EbpfMapConfiguration" + default: true + - name: "external" + type: "bool" + default: "false" + edns-client-subnet: parameters: - name: "override-existing" @@ -815,6 +853,9 @@ bind: - name: "additional-addresses" type: "Vec" default: true + - name: "xsk" + type: "String" + default: "" outgoing-tcp: parameters: @@ -1012,6 +1053,9 @@ backend: - name: "cpus" type: "String" default: "" + - name: "xsk" + type: "String" + default: "" tuning: parameters: @@ -1473,3 +1517,18 @@ response-rule: type: "Selector" - name: "action" type: "ResponseAction" + +xsk: + parameters: + - name: "name" + type: "String" + - name: "interface" + type: "String" + - name: "queues" + type: "u16" + - name: "frames" + type: "u32" + default: 65536 + - name: "map-path" + type: "String" + default: "/sys/fs/bpf/dnsdist/xskmap" diff --git a/pdns/dnsdistdist/docs/reference/yaml-settings.rst b/pdns/dnsdistdist/docs/reference/yaml-settings.rst index ee4c8d37c7..8da8a2424f 100644 --- a/pdns/dnsdistdist/docs/reference/yaml-settings.rst +++ b/pdns/dnsdistdist/docs/reference/yaml-settings.rst @@ -30,6 +30,7 @@ GlobalConfiguration - **console**: :ref:`ConsoleConfiguration ` - Console-related settings - **dynamic-rules**: Sequence of :ref:`DynamicRulesConfiguration ` - List of dynamic rules - **dynamic-rules-settings**: :ref:`DynamicRulesSettingsConfiguration ` - Dynamic rules-related settings +- **ebpf**: :ref:`EbpfConfiguration ` - EBPF settings - **edns-client-subnet**: :ref:`EdnsClientSubnetConfiguration ` - EDNS Client Subnet-related settings - **general**: :ref:`GeneralConfiguration ` - General settings - **key-value-stores**: :ref:`KeyValueStoresConfiguration ` - Key-Value stores @@ -50,6 +51,7 @@ GlobalConfiguration - **tuning**: :ref:`TuningConfiguration ` - Performance-related settings - **webserver**: :ref:`WebserverConfiguration ` - Internal web server configuration - **xfr-response-rules**: Sequence of :ref:`ResponseRuleConfiguration ` - List of rules executed when a XFR response is received +- **xsk**: Sequence of :ref:`XskConfiguration ` - List of AF_XDP / XSK objects @@ -87,6 +89,7 @@ BackendConfiguration - **xsk-sockets**: Sequence of String - **mac-address**: String ``("")`` - **cpus**: String ``("")`` +- **xsk**: String ``("")`` .. _yaml-settings-BindConfiguration: @@ -108,6 +111,7 @@ BindConfiguration - **quic**: :ref:`IncomingQuicConfiguration ` - **dnscrypt**: :ref:`IncomingDnscryptConfiguration ` - **additional-addresses**: Sequence of String +- **xsk**: String ``("")`` .. _yaml-settings-CDBKVStoreConfiguration: @@ -240,6 +244,28 @@ DynamicRulesSettingsConfiguration - **default-action**: String ``(Drop)`` +.. _yaml-settings-EbpfConfiguration: + +EbpfConfiguration +----------------- + +- **ipv4**: :ref:`EbpfMapConfiguration ` +- **ipv6**: :ref:`EbpfMapConfiguration ` +- **cidr-ipv4**: :ref:`EbpfMapConfiguration ` +- **cidr-ipv6**: :ref:`EbpfMapConfiguration ` +- **qnames**: :ref:`EbpfMapConfiguration ` +- **external**: Boolean ``(false)`` + + +.. _yaml-settings-EbpfMapConfiguration: + +EbpfMapConfiguration +-------------------- + +- **max-entries**: Unsigned integer ``(0)`` +- **pinned-path**: String ``("")`` + + .. _yaml-settings-EdnsClientSubnetConfiguration: EdnsClientSubnetConfiguration @@ -785,3 +811,15 @@ WebserverConfiguration - **api-read-write**: Boolean ``(false)`` +.. _yaml-settings-XskConfiguration: + +XskConfiguration +---------------- + +- **name**: String +- **interface**: String +- **queues**: Unsigned integer +- **frames**: Unsigned integer ``(65536)`` +- **map-path**: String ``(/sys/fs/bpf/dnsdist/xskmap)`` + +