From: Remi Gacogne Date: Thu, 23 Sep 2021 14:31:38 +0000 (+0200) Subject: dnsdist: Add support for v4/v6 masks in dynamic blocks X-Git-Tag: rec-4.6.0-beta1~39^2~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6d4de1286266259dadf9b3e8161ec75a75266bc8;p=thirdparty%2Fpdns.git dnsdist: Add support for v4/v6 masks in dynamic blocks --- diff --git a/pdns/dnsdist-dynblocks.hh b/pdns/dnsdist-dynblocks.hh index 2833bfe422..c8a7aaa8c6 100644 --- a/pdns/dnsdist-dynblocks.hh +++ b/pdns/dnsdist-dynblocks.hh @@ -212,7 +212,7 @@ private: double d_warningRatio{0.0}; }; - typedef std::unordered_map counts_t; + typedef std::unordered_map counts_t; public: DynBlockRulesGroup() @@ -262,6 +262,12 @@ public: d_smtVisitorFFI = visitor; } + void setMasks(uint8_t v4, uint8_t v6) + { + d_v4Mask = v4; + d_v6Mask = v6; + } + void apply() { struct timespec now; @@ -330,15 +336,15 @@ private: bool checkIfQueryTypeMatches(const Rings::Query& query); bool checkIfResponseCodeMatches(const Rings::Response& response); - void addOrRefreshBlock(boost::optional >& blocks, const struct timespec& now, const ComboAddress& requestor, const DynBlockRule& rule, bool& updated, bool warning); + void addOrRefreshBlock(boost::optional >& blocks, const struct timespec& now, const Netmask& requestor, const DynBlockRule& rule, bool& updated, bool warning); void addOrRefreshBlockSMT(SuffixMatchTree& blocks, const struct timespec& now, const DNSName& name, const DynBlockRule& rule, bool& updated); - void addBlock(boost::optional >& blocks, const struct timespec& now, const ComboAddress& requestor, const DynBlockRule& rule, bool& updated) + void addBlock(boost::optional >& blocks, const struct timespec& now, const Netmask& requestor, const DynBlockRule& rule, bool& updated) { addOrRefreshBlock(blocks, now, requestor, rule, updated, false); } - void handleWarning(boost::optional >& blocks, const struct timespec& now, const ComboAddress& requestor, const DynBlockRule& rule, bool& updated) + void handleWarning(boost::optional >& blocks, const struct timespec& now, const Netmask& requestor, const DynBlockRule& rule, bool& updated) { addOrRefreshBlock(blocks, now, requestor, rule, updated, true); } @@ -376,6 +382,8 @@ private: SuffixMatchNode d_excludedDomains; smtVisitor_t d_smtVisitor; dnsdist_ffi_stat_node_visitor_t d_smtVisitorFFI; + uint8_t d_v6Mask{128}; + uint8_t d_v4Mask{32}; bool d_beQuiet{false}; }; @@ -417,3 +425,110 @@ private: static std::list s_metricsData; static size_t s_topN; }; + +class AddressAndPortRange +{ +public: + AddressAndPortRange(): d_addrMask(0), d_portMask(0) + { + d_addr.sin4.sin_family = 0; // disable this doing anything useful + d_addr.sin4.sin_port = 0; // this guarantees d_network compares identical + } + + AddressAndPortRange(ComboAddress ca, uint8_t addrMask, uint8_t portMask): d_addr(std::move(ca)), d_addrMask(addrMask), d_portMask(portMask) + { + cerr<<"creating a address and port range "<= 0, + the index is relative to the LSB starting at index zero. When the index < 0, + the index is relative to the MSB starting at index -1 and counting down. + */ + bool getBit(int index) const + { + cerr<<"in getBit "<= getFullBits()) { + cerr<<"flse 1"<setQTypeRate(qtype, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); } }); + luaCtx.registerFunction::*)(uint8_t, uint8_t)>("setMasks", [](std::shared_ptr& group, uint8_t v4, uint8_t v6) { + if (group) { + group->setMasks(v4, v6); + } + }); luaCtx.registerFunction::*)(boost::variant>, NetmaskGroup>)>("excludeRange", [](std::shared_ptr& group, boost::variant>, NetmaskGroup> ranges) { if (ranges.type() == typeid(std::vector>)) { for (const auto& range : *boost::get>>(&ranges)) { diff --git a/pdns/dnsdistdist/dnsdist-dynblocks.cc b/pdns/dnsdistdist/dnsdist-dynblocks.cc index 4381b4a396..2b73743b9d 100644 --- a/pdns/dnsdistdist/dnsdist-dynblocks.cc +++ b/pdns/dnsdistdist/dnsdist-dynblocks.cc @@ -174,9 +174,9 @@ bool DynBlockRulesGroup::checkIfResponseCodeMatches(const Rings::Response& respo return false; } -void DynBlockRulesGroup::addOrRefreshBlock(boost::optional >& blocks, const struct timespec& now, const ComboAddress& requestor, const DynBlockRule& rule, bool& updated, bool warning) +void DynBlockRulesGroup::addOrRefreshBlock(boost::optional >& blocks, const struct timespec& now, const Netmask& requestor, const DynBlockRule& rule, bool& updated, bool warning) { - if (d_excludedSubnets.match(requestor)) { + if (d_excludedSubnets.match(requestor.getMaskedNetwork())) { /* do not add a block for excluded subnets */ return; } @@ -187,7 +187,7 @@ void DynBlockRulesGroup::addOrRefreshBlock(boost::optional struct timespec until = now; until.tv_sec += rule.d_blockDuration; unsigned int count = 0; - const auto& got = blocks->lookup(Netmask(requestor)); + const auto& got = blocks->lookup(requestor.getMaskedNetwork()); bool expired = false; bool wasWarning = false; bool bpf = false; @@ -223,9 +223,10 @@ void DynBlockRulesGroup::addOrRefreshBlock(boost::optional db.blocks = count; db.warning = warning; if (!got || expired || wasWarning) { - if (db.action == DNSAction::Action::Drop && g_defaultBPFFilter) { + if (db.action == DNSAction::Action::Drop && g_defaultBPFFilter && + ((requestor.isIPv4() && requestor.getBits() == 32) || (requestor.isIPv6() && requestor.getBits() == 128))) { try { - g_defaultBPFFilter->block(requestor); + g_defaultBPFFilter->block(requestor.getMaskedNetwork()); bpf = true; } catch (const std::exception& e) { @@ -240,7 +241,7 @@ void DynBlockRulesGroup::addOrRefreshBlock(boost::optional db.bpf = bpf; - blocks->insert(Netmask(requestor)).second = std::move(db); + blocks->insert(requestor).second = std::move(db); updated = true; } @@ -314,7 +315,7 @@ void DynBlockRulesGroup::processQueryRules(counts_t& counts, const struct timesp bool typeRuleMatches = checkIfQueryTypeMatches(c); if (qRateMatches || typeRuleMatches) { - auto& entry = counts[c.requestor]; + auto& entry = counts[Netmask(c.requestor, c.requestor.isIPv4() ? d_v4Mask : d_v6Mask)]; if (qRateMatches) { ++entry.queries; } @@ -373,7 +374,7 @@ void DynBlockRulesGroup::processResponseRules(counts_t& counts, StatNode& root, continue; } - auto& entry = counts[c.requestor]; + auto& entry = counts[Netmask(c.requestor, c.requestor.isIPv4() ? d_v4Mask : d_v6Mask)]; ++entry.responses; bool respRateMatches = d_respRateRule.matches(c.when); diff --git a/pdns/dnsdistdist/docs/reference/config.rst b/pdns/dnsdistdist/docs/reference/config.rst index aa43e908ee..73557c4891 100644 --- a/pdns/dnsdistdist/docs/reference/config.rst +++ b/pdns/dnsdistdist/docs/reference/config.rst @@ -1258,6 +1258,17 @@ faster than the existing rules. Represents a group of dynamic block rules. + .. method:: DynBlockRulesGroup:setMasks(v4, v6) + + .. versionadded:: 1.7.0 + + Set the number of bits to keep in the IP address when inserting a block. The default is 32 for IPv4 and 128 for IPv6, meaning + that only the exact address is blocked, but in some scenarios it might make sense to block a whole /64 IPv6 range instead of a + single address, for example. + + :param int v4: Number of bits of to keep for IPv4 addresses. Default is 32 + :param int v6: Number of bits of to keep for IPv6 addresses. Default is 128 + .. method:: DynBlockRulesGroup:setQueryRate(rate, seconds, reason, blockingTime [, action [, warningRate]]) Adds a query rate-limiting rule, equivalent to: diff --git a/pdns/iputils.hh b/pdns/iputils.hh index 45690731a7..2ec43220f3 100644 --- a/pdns/iputils.hh +++ b/pdns/iputils.hh @@ -637,6 +637,11 @@ public: return d_network.getBits(); } + uint8_t getFullBits() const + { + return getAddressBits(); + } + /** Get the value of the bit at the provided bit index. When the index >= 0, the index is relative to the LSB starting at index zero. When the index < 0, the index is relative to the MSB starting at index -1 and counting down. @@ -658,6 +663,15 @@ public: } return d_network.getBit(bit); } + + struct hash + { + uint32_t operator()(const Netmask& nm) const + { + ComboAddress::addressOnlyHash hashOp; + return hashOp(nm.d_network); + } + }; private: ComboAddress d_network; uint32_t d_mask; @@ -685,12 +699,12 @@ private: * Please see NetmaskGroup for example of simple use case. Other usecases can be found * from GeoIPBackend and Sortlist, and from dnsdist. */ -template +template class NetmaskTree { public: class Iterator; - typedef Netmask key_type; + typedef K key_type; typedef T value_type; typedef std::pair node_type; typedef size_t size_type; @@ -706,7 +720,7 @@ private: } explicit TreeNode(const key_type& key) noexcept : parent(nullptr), node({key.getNormalized(), value_type()}), - assigned(false), d_bits(key.getAddressBits()) { + assigned(false), d_bits(key.getFullBits()) { } //