From: Remi Gacogne Date: Thu, 28 Oct 2021 15:58:53 +0000 (+0200) Subject: dnsdist: Add a new eBPF map format, support external eBPF programs X-Git-Tag: dnsdist-1.7.0-beta1^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=053a020a8c8a23c9c7471a9d6e1161606738b6a6;p=thirdparty%2Fpdns.git dnsdist: Add a new eBPF map format, support external eBPF programs Supporting external eBPF programs makes it possible to populate the eBPF tables from dnsdist, manually or via our dynamic blocking mechanisms, but to actually do the filtering in an external program, like an XDP one. We cannot increase the size of eBPF programs if we want to stay below 4k instructions for older kernels, so this commit implements a compatibility layer with the new map format. The 4k limit for unprivileged was removed in 5.2 but the complexity limit remains: The complexity limit was actually changed several times since the 32k value from its introduction in Linux 3.18: it was raised to 64k in Linux 4.7, then to 96k in Linux 4.12, again to 128k in Linux 4.14, and at last to 1M in Linux 5.2. --- diff --git a/pdns/bpf-filter.cc b/pdns/bpf-filter.cc index 55dc241d9a..2b5df7a07f 100644 --- a/pdns/bpf-filter.cc +++ b/pdns/bpf-filter.cc @@ -178,13 +178,25 @@ struct QNameKey uint8_t qname[255]; }; -struct QNameValue +struct QNameAndQTypeKey { - uint64_t counter; + uint8_t qname[255]; uint16_t qtype; }; -BPFFilter::Map::Map(const BPFFilter::MapConfiguration& config): d_config(config) +struct QNameValue +{ + uint64_t counter{0}; + uint16_t qtype{0}; +}; + +struct CounterAndActionValue +{ + uint64_t counter{0}; + BPFFilter::MatchAction action{BPFFilter::MatchAction::Pass}; +}; + +BPFFilter::Map::Map(const BPFFilter::MapConfiguration& config, BPFFilter::MapFormat format): d_config(config) { if (d_config.d_type == BPFFilter::MapType::Filters) { /* special case, this is a map of eBPF programs */ @@ -196,21 +208,41 @@ BPFFilter::Map::Map(const BPFFilter::MapConfiguration& config): d_config(config) else { int keySize = 0; int valueSize = 0; - switch (d_config.d_type) { - case MapType::IPv4: - keySize = sizeof(uint32_t); - valueSize = sizeof(uint64_t); - break; - case MapType::IPv6: - keySize = sizeof(KeyV6); - valueSize = sizeof(uint64_t); - break; - case MapType::QNames: - keySize = sizeof(QNameKey); - valueSize = sizeof(QNameValue); - break; - default: - throw std::runtime_error("Unsupported eBPF map type: " + std::to_string(static_cast(d_config.d_type))); + if (format == MapFormat::Legacy) { + switch (d_config.d_type) { + case MapType::IPv4: + keySize = sizeof(uint32_t); + valueSize = sizeof(uint64_t); + break; + case MapType::IPv6: + keySize = sizeof(KeyV6); + valueSize = sizeof(uint64_t); + break; + case MapType::QNames: + keySize = sizeof(QNameKey); + valueSize = sizeof(QNameValue); + break; + default: + throw std::runtime_error("Unsupported eBPF map type: " + std::to_string(static_cast(d_config.d_type))); + } + } + else { + switch (d_config.d_type) { + case MapType::IPv4: + keySize = sizeof(uint32_t); + valueSize = sizeof(CounterAndActionValue); + break; + case MapType::IPv6: + keySize = sizeof(KeyV6); + valueSize = sizeof(CounterAndActionValue); + break; + case MapType::QNames: + keySize = sizeof(QNameAndQTypeKey); + valueSize = sizeof(CounterAndActionValue); + break; + default: + throw std::runtime_error("Unsupported eBPF map type: " + std::to_string(static_cast(d_config.d_type))); + } } if (!d_config.d_pinnedPath.empty()) { @@ -238,12 +270,23 @@ BPFFilter::Map::Map(const BPFFilter::MapConfiguration& config): d_config(config) } } else if (d_config.d_type == MapType::QNames) { - QNameKey key; - memset(&key, 0, sizeof(key)); - int res = bpf_get_next_key(d_fd.getHandle(), &key, &key); - while (res == 0) { - ++d_count; - res = bpf_get_next_key(d_fd.getHandle(), &key, &key); + if (format == MapFormat::Legacy) { + QNameKey key; + memset(&key, 0, sizeof(key)); + int res = bpf_get_next_key(d_fd.getHandle(), &key, &key); + while (res == 0) { + ++d_count; + res = bpf_get_next_key(d_fd.getHandle(), &key, &key); + } + } + else { + QNameAndQTypeKey key; + memset(&key, 0, sizeof(key)); + int res = bpf_get_next_key(d_fd.getHandle(), &key, &key); + while (res == 0) { + ++d_count; + res = bpf_get_next_key(d_fd.getHandle(), &key, &key); + } } } } @@ -278,47 +321,53 @@ static FDWrapper loadProgram(const struct bpf_insn* filter, size_t filterSize) } -BPFFilter::BPFFilter(const BPFFilter::MapConfiguration& v4, const BPFFilter::MapConfiguration& v6, const BPFFilter::MapConfiguration& qnames) +BPFFilter::BPFFilter(const BPFFilter::MapConfiguration& v4, const BPFFilter::MapConfiguration& v6, const BPFFilter::MapConfiguration& qnames, BPFFilter::MapFormat format, bool external): d_mapFormat(format), d_external(external) { + if (d_mapFormat != BPFFilter::MapFormat::Legacy && !d_external) { + throw std::runtime_error("Unsupported eBPF map format, the current internal implemenation only supports the legacy format"); + } + auto maps = d_maps.lock(); - maps->d_v4 = BPFFilter::Map(v4); - maps->d_v6 = BPFFilter::Map(v6); - maps->d_qnames = BPFFilter::Map(qnames); - BPFFilter::MapConfiguration filters; - filters.d_maxItems = 1; - filters.d_type = BPFFilter::MapType::Filters; - maps->d_filters = BPFFilter::Map(filters); + maps->d_v4 = BPFFilter::Map(v4, d_mapFormat); + maps->d_v6 = BPFFilter::Map(v6, d_mapFormat); + maps->d_qnames = BPFFilter::Map(qnames, d_mapFormat); + if (!external) { + BPFFilter::MapConfiguration filters; + filters.d_maxItems = 1; + filters.d_type = BPFFilter::MapType::Filters; + maps->d_filters = BPFFilter::Map(filters, d_mapFormat); - const struct bpf_insn main_filter[] = { + const struct bpf_insn main_filter[] = { #include "bpf-filter.main.ebpf" - }; + }; - const struct bpf_insn qname_filter[] = { + const struct bpf_insn qname_filter[] = { #include "bpf-filter.qname.ebpf" - }; + }; - try { - d_mainfilter = loadProgram(main_filter, - sizeof(main_filter)); - } - catch (const std::exception& e) { - throw std::runtime_error("Error load the main eBPF filter: " + std::string(e.what())); - } + try { + d_mainfilter = loadProgram(main_filter, + sizeof(main_filter)); + } + catch (const std::exception& e) { + throw std::runtime_error("Error load the main eBPF filter: " + std::string(e.what())); + } - try { - d_qnamefilter = loadProgram(qname_filter, - sizeof(qname_filter)); - } - catch (const std::exception& e) { - throw std::runtime_error("Error load the qname eBPF filter: " + std::string(e.what())); - } + try { + d_qnamefilter = loadProgram(qname_filter, + sizeof(qname_filter)); + } + catch (const std::exception& e) { + throw std::runtime_error("Error load the qname eBPF filter: " + std::string(e.what())); + } - uint32_t key = 0; - int qnamefd = d_qnamefilter.getHandle(); - int res = bpf_update_elem(maps->d_filters.d_fd.getHandle(), &key, &qnamefd, BPF_ANY); - if (res != 0) { - throw std::runtime_error("Error updating BPF filters map: " + stringerror()); + uint32_t key = 0; + int qnamefd = d_qnamefilter.getHandle(); + int res = bpf_update_elem(maps->d_filters.d_fd.getHandle(), &key, &qnamefd, BPF_ANY); + if (res != 0) { + throw std::runtime_error("Error updating BPF filters map: " + stringerror()); + } } } @@ -342,9 +391,12 @@ void BPFFilter::removeSocket(int sock) } } -void BPFFilter::block(const ComboAddress& addr) +void BPFFilter::block(const ComboAddress& addr, BPFFilter::MatchAction action) { - uint64_t counter = 0; + CounterAndActionValue value; + value.counter = 0; + value.action = action; + int res = 0; if (addr.isIPv4()) { uint32_t key = htonl(addr.sin4.sin_addr.s_addr); @@ -354,12 +406,12 @@ void BPFFilter::block(const ComboAddress& addr) throw std::runtime_error("Table full when trying to block " + addr.toString()); } - res = bpf_lookup_elem(map.d_fd.getHandle(), &key, &counter); + res = bpf_lookup_elem(map.d_fd.getHandle(), &key, &value); if (res != -1) { throw std::runtime_error("Trying to block an already blocked address: " + addr.toString()); } - res = bpf_update_elem(map.d_fd.getHandle(), &key, &counter, BPF_NOEXIST); + res = bpf_update_elem(map.d_fd.getHandle(), &key, &value, BPF_NOEXIST); if (res == 0) { ++map.d_count; } @@ -377,12 +429,12 @@ void BPFFilter::block(const ComboAddress& addr) throw std::runtime_error("Table full when trying to block " + addr.toString()); } - res = bpf_lookup_elem(map.d_fd.getHandle(), &key, &counter); + res = bpf_lookup_elem(map.d_fd.getHandle(), &key, &value); if (res != -1) { throw std::runtime_error("Trying to block an already blocked address: " + addr.toString()); } - res = bpf_update_elem(map.d_fd.getHandle(), key, &counter, BPF_NOEXIST); + res = bpf_update_elem(map.d_fd.getHandle(), key, &value, BPF_NOEXIST); if (res == 0) { map.d_count++; } @@ -425,20 +477,34 @@ void BPFFilter::unblock(const ComboAddress& addr) } } -void BPFFilter::block(const DNSName& qname, uint16_t qtype) +void BPFFilter::block(const DNSName& qname, BPFFilter::MatchAction action, uint16_t qtype) { - struct QNameKey key; - struct QNameValue value; + CounterAndActionValue cadvalue; + QNameValue qvalue; + void* value = nullptr; + + if (d_external) { + memset(&cadvalue, 0, sizeof(cadvalue)); + cadvalue.counter = 0; + cadvalue.action = action; + value = &cadvalue; + } + else { + memset(&qvalue, 0, sizeof(qvalue)); + qvalue.counter = 0; + qvalue.qtype = qtype; + value = &qvalue; + } + + QNameAndQTypeKey key; memset(&key, 0, sizeof(key)); - memset(&value, 0, sizeof(value)); - value.counter = 0; - value.qtype = qtype; std::string keyStr = qname.toDNSStringLC(); if (keyStr.size() > sizeof(key.qname)) { throw std::runtime_error("Invalid QName to block " + qname.toLogString()); } memcpy(key.qname, keyStr.c_str(), keyStr.size()); + key.qtype = qtype; { auto maps = d_maps.lock(); @@ -465,14 +531,15 @@ void BPFFilter::block(const DNSName& qname, uint16_t qtype) void BPFFilter::unblock(const DNSName& qname, uint16_t qtype) { - struct QNameKey key = { { 0 } }; + QNameAndQTypeKey key; + memset(&key, 0, sizeof(key)); std::string keyStr = qname.toDNSStringLC(); - (void) qtype; if (keyStr.size() > sizeof(key.qname)) { throw std::runtime_error("Invalid QName to block " + qname.toLogString()); } memcpy(key.qname, keyStr.c_str(), keyStr.size()); + key.qtype = qtype; { auto maps = d_maps.lock(); @@ -501,7 +568,7 @@ std::vector > BPFFilter::getAddrStats() uint32_t v4Key = 0; uint32_t nextV4Key; - uint64_t value; + CounterAndActionValue value; uint8_t v6Key[16]; uint8_t nextV6Key[16]; @@ -522,7 +589,7 @@ std::vector > BPFFilter::getAddrStats() v4Key = nextV4Key; if (bpf_lookup_elem(map.d_fd.getHandle(), &v4Key, &value) == 0) { v4Addr.sin_addr.s_addr = ntohl(v4Key); - result.emplace_back(ComboAddress(&v4Addr), value); + result.emplace_back(ComboAddress(&v4Addr), value.counter); } res = bpf_get_next_key(map.d_fd.getHandle(), &v4Key, &nextV4Key); @@ -537,7 +604,7 @@ std::vector > BPFFilter::getAddrStats() if (bpf_lookup_elem(map.d_fd.getHandle(), &nextV6Key, &value) == 0) { memcpy(&v6Addr.sin6_addr.s6_addr, &nextV6Key, sizeof(nextV6Key)); - result.emplace_back(ComboAddress(&v6Addr), value); + result.emplace_back(ComboAddress(&v6Addr), value.counter); } res = bpf_get_next_key(map.d_fd.getHandle(), &nextV6Key, &nextV6Key); @@ -551,29 +618,54 @@ std::vector > BPFFilter::getQNameStats() { std::vector > result; - struct QNameKey key = { { 0 } }; - struct QNameKey nextKey = { { 0 } }; - struct QNameValue value; + if (d_mapFormat == MapFormat::Legacy) { + QNameKey key = { { 0 } }; + QNameKey nextKey = { { 0 } }; + QNameValue value; - auto maps = d_maps.lock(); - auto& map = maps->d_qnames; - result.reserve(map.d_count); - int res = bpf_get_next_key(map.d_fd.getHandle(), &key, &nextKey); + auto maps = d_maps.lock(); + auto& map = maps->d_qnames; + result.reserve(map.d_count); + int res = bpf_get_next_key(map.d_fd.getHandle(), &key, &nextKey); - while (res == 0) { - if (bpf_lookup_elem(map.d_fd.getHandle(), &nextKey, &value) == 0) { - nextKey.qname[sizeof(nextKey.qname) - 1 ] = '\0'; - result.push_back(std::make_tuple(DNSName((const char*) nextKey.qname, sizeof(nextKey.qname), 0, false), value.qtype, value.counter)); + while (res == 0) { + if (bpf_lookup_elem(map.d_fd.getHandle(), &nextKey, &value) == 0) { + nextKey.qname[sizeof(nextKey.qname) - 1 ] = '\0'; + result.push_back(std::make_tuple(DNSName(reinterpret_cast(nextKey.qname), sizeof(nextKey.qname), 0, false), value.qtype, value.counter)); + } + + res = bpf_get_next_key(map.d_fd.getHandle(), &nextKey, &nextKey); } + } + else { + QNameAndQTypeKey key; + QNameAndQTypeKey nextKey; + memset(&key, 0, sizeof(key)); + memset(&nextKey, 0, sizeof(nextKey)); + CounterAndActionValue value; + + auto maps = d_maps.lock(); + auto& map = maps->d_qnames; + result.reserve(map.d_count); + int res = bpf_get_next_key(map.d_fd.getHandle(), &key, &nextKey); - res = bpf_get_next_key(map.d_fd.getHandle(), &nextKey, &nextKey); + while (res == 0) { + if (bpf_lookup_elem(map.d_fd.getHandle(), &nextKey, &value) == 0) { + nextKey.qname[sizeof(nextKey.qname) - 1 ] = '\0'; + result.push_back(std::make_tuple(DNSName(reinterpret_cast(nextKey.qname), sizeof(nextKey.qname), 0, false), key.qtype, value.counter)); + } + + res = bpf_get_next_key(map.d_fd.getHandle(), &nextKey, &nextKey); + } } + return result; } uint64_t BPFFilter::getHits(const ComboAddress& requestor) { - uint64_t counter = 0; + CounterAndActionValue counter; + if (requestor.isIPv4()) { uint32_t key = htonl(requestor.sin4.sin_addr.s_addr); @@ -581,7 +673,7 @@ uint64_t BPFFilter::getHits(const ComboAddress& requestor) auto& map = maps->d_v4; int res = bpf_lookup_elem(map.d_fd.getHandle(), &key, &counter); if (res == 0) { - return counter; + return counter.counter; } } else if (requestor.isIPv6()) { @@ -595,7 +687,7 @@ uint64_t BPFFilter::getHits(const ComboAddress& requestor) auto& map = maps->d_v6; int res = bpf_lookup_elem(map.d_fd.getHandle(), &key, &counter); if (res == 0) { - return counter; + return counter.counter; } } @@ -604,7 +696,7 @@ uint64_t BPFFilter::getHits(const ComboAddress& requestor) #else -BPFFilter::BPFFilter(const BPFFilter::MapConfiguration&, const BPFFilter::MapConfiguration&, const BPFFilter::MapConfiguration&) +BPFFilter::BPFFilter(const BPFFilter::MapConfiguration&, const BPFFilter::MapConfiguration&, const BPFFilter::MapConfiguration&, BPFFilter::MapFormat, bool) { } @@ -618,7 +710,7 @@ void BPFFilter::removeSocket(int) throw std::runtime_error("eBPF support not enabled"); } -void BPFFilter::block(const ComboAddress&) +void BPFFilter::block(const ComboAddress&, BPFFilter::MatchAction) { throw std::runtime_error("eBPF support not enabled"); } @@ -628,7 +720,7 @@ void BPFFilter::unblock(const ComboAddress&) throw std::runtime_error("eBPF support not enabled"); } -void BPFFilter::block(const DNSName&, uint16_t) +void BPFFilter::block(const DNSName&, BPFFilter::MatchAction, uint16_t) { throw std::runtime_error("eBPF support not enabled"); } @@ -655,3 +747,22 @@ uint64_t BPFFilter::getHits(const ComboAddress&) return 0; } #endif /* HAVE_EBPF */ + +bool BPFFilter::supportsMatchAction(MatchAction action) const +{ +#ifdef HAVE_EBPF + if (action == BPFFilter::MatchAction::Drop) { + return true; + } + return d_mapFormat == BPFFilter::MapFormat::WithActions; +#endif /* HAVE_EBPF */ + return false; +} + +bool BPFFilter::isExternal() const +{ +#ifdef HAVE_EBPF + return d_external; +#endif /* HAVE_EBPF */ + return false; +} diff --git a/pdns/bpf-filter.hh b/pdns/bpf-filter.hh index 6dce2dbc97..b56bd45266 100644 --- a/pdns/bpf-filter.hh +++ b/pdns/bpf-filter.hh @@ -35,6 +35,17 @@ public: Filters }; + enum class MapFormat : uint8_t { + Legacy = 0, + WithActions = 1 + }; + + enum class MatchAction : uint32_t { + Pass = 0, + Drop = 1, + Truncate = 2 + }; + struct MapConfiguration { std::string d_pinnedPath; @@ -42,7 +53,8 @@ public: MapType d_type; }; - BPFFilter(const BPFFilter::MapConfiguration& v4, const BPFFilter::MapConfiguration& v6, const BPFFilter::MapConfiguration& qnames); + + BPFFilter(const BPFFilter::MapConfiguration& v4, const BPFFilter::MapConfiguration& v6, const BPFFilter::MapConfiguration& qnames, BPFFilter::MapFormat format, bool external); BPFFilter(const BPFFilter&) = delete; BPFFilter(BPFFilter&&) = delete; BPFFilter& operator=(const BPFFilter&) = delete; @@ -50,8 +62,8 @@ public: void addSocket(int sock); void removeSocket(int sock); - void block(const ComboAddress& addr); - void block(const DNSName& qname, uint16_t qtype=255); + void block(const ComboAddress& addr, MatchAction action); + void block(const DNSName& qname, MatchAction action, uint16_t qtype=255); void unblock(const ComboAddress& addr); void unblock(const DNSName& qname, uint16_t qtype=255); @@ -60,6 +72,9 @@ public: uint64_t getHits(const ComboAddress& requestor); + bool supportsMatchAction(MatchAction action) const; + bool isExternal() const; + private: #ifdef HAVE_EBPF struct Map @@ -67,7 +82,7 @@ private: Map() { } - Map(const MapConfiguration&); + Map(const MapConfiguration&, MapFormat); MapConfiguration d_config; uint32_t d_count{0}; FDWrapper d_fd; @@ -90,5 +105,20 @@ private: FDWrapper d_mainfilter; /* qname filtering program */ FDWrapper d_qnamefilter; + + /* whether the maps are in the 'old' format, which we need + to keep to prevent going over the 4k instructions per eBPF + program limit in kernels < 5.2, as well as the complexity limit: + - 32k in Linux 3.18 + - 64k in Linux 4.7 + - 96k in Linux 4.12 + - 128k in Linux 4.14, + - 1M in Linux 5.2 */ + MapFormat d_mapFormat; + + /* whether the filter is internal, using our own eBPF programs, + or external where we only update the maps but the filtering is + done by an external program. */ + bool d_external; #endif /* HAVE_EBPF */ }; diff --git a/pdns/dnsdist-dynbpf.cc b/pdns/dnsdist-dynbpf.cc index a11c928f95..ec549c32f9 100644 --- a/pdns/dnsdist-dynbpf.cc +++ b/pdns/dnsdist-dynbpf.cc @@ -38,7 +38,7 @@ bool DynBPFFilter::block(const ComboAddress& addr, const struct timespec& until) } } else { - data->d_bpf->block(addr); + data->d_bpf->block(addr, BPFFilter::MatchAction::Drop); data->d_entries.insert(BlockEntry(addr, until)); inserted = true; } diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdist-lua-bindings.cc index 3830e9d875..43c3d04122 100644 --- a/pdns/dnsdist-lua-bindings.cc +++ b/pdns/dnsdist-lua-bindings.cc @@ -398,10 +398,11 @@ void setupLuaBindings(LuaContext& luaCtx, bool client) /* BPF Filter */ #ifdef HAVE_EBPF using bpfFilterMapParams = boost::variant>>; - luaCtx.writeFunction("newBPFFilter", [client](bpfFilterMapParams v4Params, bpfFilterMapParams v6Params, bpfFilterMapParams qnameParams) { + luaCtx.writeFunction("newBPFFilter", [client](bpfFilterMapParams v4Params, bpfFilterMapParams v6Params, bpfFilterMapParams qnameParams, boost::optional external) { if (client) { return std::shared_ptr(nullptr); } + BPFFilter::MapConfiguration v4Config, v6Config, qnameConfig; auto convertParamsToConfig = [](bpfFilterMapParams& params, BPFFilter::MapType type, BPFFilter::MapConfiguration& config) { @@ -424,18 +425,63 @@ void setupLuaBindings(LuaContext& luaCtx, bool client) convertParamsToConfig(v6Params, BPFFilter::MapType::IPv6, v6Config); convertParamsToConfig(qnameParams, BPFFilter::MapType::QNames, qnameConfig); - return std::make_shared(v4Config, v6Config, qnameConfig); + BPFFilter::MapFormat format = BPFFilter::MapFormat::Legacy; + if (external && *external) { + format = BPFFilter::MapFormat::WithActions; + } + + return std::make_shared(v4Config, v6Config, qnameConfig, format, external.value_or(false)); }); - luaCtx.registerFunction::*)(const ComboAddress& ca)>("block", [](std::shared_ptr bpf, const ComboAddress& ca) { + luaCtx.registerFunction::*)(const ComboAddress& ca, boost::optional action)>("block", [](std::shared_ptr bpf, const ComboAddress& ca, boost::optional action) { if (bpf) { - return bpf->block(ca); + if (!action) { + return bpf->block(ca, BPFFilter::MatchAction::Drop); + } + else { + BPFFilter::MatchAction match; + + switch (*action) { + case 0: + match = BPFFilter::MatchAction::Pass; + break; + case 1: + match = BPFFilter::MatchAction::Drop; + break; + case 2: + match = BPFFilter::MatchAction::Truncate; + break; + default: + throw std::runtime_error("Unsupported action for BPFFilter::block"); + } + return bpf->block(ca, match); + } } }); - luaCtx.registerFunction::*)(const DNSName& qname, boost::optional qtype)>("blockQName", [](std::shared_ptr bpf, const DNSName& qname, boost::optional qtype) { + luaCtx.registerFunction::*)(const DNSName& qname, boost::optional qtype, boost::optional action)>("blockQName", [](std::shared_ptr bpf, const DNSName& qname, boost::optional qtype, boost::optional action) { if (bpf) { - return bpf->block(qname, qtype ? *qtype : 255); + if (!action) { + return bpf->block(qname, BPFFilter::MatchAction::Drop, qtype.value_or(255)); + } + else { + BPFFilter::MatchAction match; + + switch (*action) { + case 0: + match = BPFFilter::MatchAction::Pass; + break; + case 1: + match = BPFFilter::MatchAction::Drop; + break; + case 2: + match = BPFFilter::MatchAction::Truncate; + break; + default: + throw std::runtime_error("Unsupported action for BPFFilter::block"); + } + return bpf->block(qname, match, qtype.value_or(255)); + } } }); diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 4f9027585f..4d43eadec2 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -2088,7 +2088,7 @@ static void setUpLocalBind(std::unique_ptr& cs) } #ifdef HAVE_EBPF - if (g_defaultBPFFilter) { + if (g_defaultBPFFilter && !g_defaultBPFFilter->isExternal()) { cs->attachFilter(g_defaultBPFFilter); vinfolog("Attaching default BPF Filter to %s frontend %s", (!cs->tcp ? "UDP" : "TCP"), cs->local.toStringWithPort()); } diff --git a/pdns/dnsdistdist/dnsdist-dynblocks.cc b/pdns/dnsdistdist/dnsdist-dynblocks.cc index dccf2aa8c1..3aa0078308 100644 --- a/pdns/dnsdistdist/dnsdist-dynblocks.cc +++ b/pdns/dnsdistdist/dnsdist-dynblocks.cc @@ -224,12 +224,16 @@ void DynBlockRulesGroup::addOrRefreshBlock(boost::optionalblock(requestor.getNetwork()); - bpf = true; + BPFFilter::MatchAction action = db.action == DNSAction::Action::Drop ? BPFFilter::MatchAction::Drop : BPFFilter::MatchAction::Truncate; + if (g_defaultBPFFilter->supportsMatchAction(action)) { + /* the current BPF filter implementation only supports full addresses (/32 or /128) and no port */ + g_defaultBPFFilter->block(requestor.getNetwork(), action); + bpf = true; + } } catch (const std::exception& e) { vinfolog("Unable to insert eBPF dynamic block for %s, falling back to regular dynamic block: %s", requestor.toString(), e.what());