diff options
Diffstat (limited to 'dnsdist-dynblocks.hh')
-rw-r--r-- | dnsdist-dynblocks.hh | 221 |
1 files changed, 81 insertions, 140 deletions
diff --git a/dnsdist-dynblocks.hh b/dnsdist-dynblocks.hh index c9b1e4a..1a8b3a6 100644 --- a/dnsdist-dynblocks.hh +++ b/dnsdist-dynblocks.hh @@ -28,40 +28,51 @@ #include "dnsdist-rings.hh" #include "statnode.hh" -extern "C" { +extern "C" +{ #include "dnsdist-lua-inspection-ffi.h" } // dnsdist_ffi_stat_node_t is a lightuserdata -template<> -struct LuaContext::Pusher<dnsdist_ffi_stat_node_t*> { - static const int minSize = 1; - static const int maxSize = 1; - - static PushedObject push(lua_State* state, dnsdist_ffi_stat_node_t* ptr) noexcept { - lua_pushlightuserdata(state, ptr); - return PushedObject{state, 1}; - } +template <> +struct LuaContext::Pusher<dnsdist_ffi_stat_node_t*> +{ + static const int minSize = 1; + static const int maxSize = 1; + + static PushedObject push(lua_State* state, dnsdist_ffi_stat_node_t* ptr) noexcept + { + lua_pushlightuserdata(state, ptr); + return PushedObject{state, 1}; + } }; -typedef std::function<bool(dnsdist_ffi_stat_node_t*)> dnsdist_ffi_stat_node_visitor_t; +using dnsdist_ffi_stat_node_visitor_t = std::function<bool(dnsdist_ffi_stat_node_t*)>; + +struct SMTBlockParameters +{ + std::optional<std::string> d_reason; + std::optional<DNSAction::Action> d_action; +}; struct dnsdist_ffi_stat_node_t { - dnsdist_ffi_stat_node_t(const StatNode& node_, const StatNode::Stat& self_, const StatNode::Stat& children_, std::optional<std::string>& reason_): node(node_), self(self_), children(children_), reason(reason_) + dnsdist_ffi_stat_node_t(const StatNode& node_, const StatNode::Stat& self_, const StatNode::Stat& children_, SMTBlockParameters& blockParameters) : + node(node_), self(self_), children(children_), d_blockParameters(blockParameters) { } const StatNode& node; const StatNode::Stat& self; const StatNode::Stat& children; - std::optional<std::string>& reason; + SMTBlockParameters& d_blockParameters; }; +using dnsdist_ffi_dynamic_block_inserted_hook = std::function<void(uint8_t type, const char* key, const char* reason, uint8_t action, uint64_t duration, bool warning)>; + class DynBlockRulesGroup { private: - struct Counts { std::map<uint8_t, uint64_t> d_rcodeCounts; @@ -69,83 +80,27 @@ private: uint64_t queries{0}; uint64_t responses{0}; uint64_t respBytes{0}; + uint64_t cacheMisses{0}; }; struct DynBlockRule { - DynBlockRule(): d_enabled(false) + DynBlockRule() = default; + DynBlockRule(const std::string& blockReason, unsigned int blockDuration, unsigned int rate, unsigned int warningRate, unsigned int seconds, DNSAction::Action action) : + d_blockReason(blockReason), d_blockDuration(blockDuration), d_rate(rate), d_warningRate(warningRate), d_seconds(seconds), d_action(action), d_enabled(true) { } - DynBlockRule(const std::string& blockReason, unsigned int blockDuration, unsigned int rate, unsigned int warningRate, unsigned int seconds, DNSAction::Action action): d_blockReason(blockReason), d_blockDuration(blockDuration), d_rate(rate), d_warningRate(warningRate), d_seconds(seconds), d_action(action), d_enabled(true) - { - } - - bool matches(const struct timespec& when) - { - if (!d_enabled) { - return false; - } - - if (d_seconds && when < d_cutOff) { - return false; - } - - if (when < d_minTime) { - d_minTime = when; - } - - return true; - } - - bool rateExceeded(unsigned int count, const struct timespec& now) const - { - if (!d_enabled) { - return false; - } - - double delta = d_seconds ? d_seconds : DiffTime(now, d_minTime); - double limit = delta * d_rate; - return (count > limit); - } - - bool warningRateExceeded(unsigned int count, const struct timespec& now) const - { - if (!d_enabled) { - return false; - } - - if (d_warningRate == 0) { - return false; - } - - double delta = d_seconds ? d_seconds : DiffTime(now, d_minTime); - double limit = delta * d_warningRate; - return (count > limit); - } + bool matches(const struct timespec& when); + bool rateExceeded(unsigned int count, const struct timespec& now) const; + bool warningRateExceeded(unsigned int count, const struct timespec& now) const; bool isEnabled() const { return d_enabled; } - std::string toString() const - { - if (!isEnabled()) { - return ""; - } - - std::stringstream result; - if (d_action != DNSAction::Action::None) { - result << DNSAction::typeToString(d_action) << " "; - } - else { - result << "Apply the global DynBlock action "; - } - result << "for " << std::to_string(d_blockDuration) << " seconds when over " << std::to_string(d_rate) << " during the last " << d_seconds << " seconds, reason: '" << d_blockReason << "'"; - - return result.str(); - } + std::string toString() const; std::string d_blockReason; struct timespec d_cutOff; @@ -158,72 +113,40 @@ private: bool d_enabled{false}; }; - struct DynBlockRatioRule: DynBlockRule + struct DynBlockRatioRule : DynBlockRule { - DynBlockRatioRule(): DynBlockRule() - { - } - - DynBlockRatioRule(const std::string& blockReason, unsigned int blockDuration, double ratio, double warningRatio, unsigned int seconds, DNSAction::Action action, size_t minimumNumberOfResponses): DynBlockRule(blockReason, blockDuration, 0, 0, seconds, action), d_minimumNumberOfResponses(minimumNumberOfResponses), d_ratio(ratio), d_warningRatio(warningRatio) + DynBlockRatioRule() = default; + DynBlockRatioRule(const std::string& blockReason, unsigned int blockDuration, double ratio, double warningRatio, unsigned int seconds, DNSAction::Action action, size_t minimumNumberOfResponses) : + DynBlockRule(blockReason, blockDuration, 0, 0, seconds, action), d_minimumNumberOfResponses(minimumNumberOfResponses), d_ratio(ratio), d_warningRatio(warningRatio) { } - bool ratioExceeded(unsigned int total, unsigned int count) const - { - if (!d_enabled) { - return false; - } - - if (total < d_minimumNumberOfResponses) { - return false; - } + bool ratioExceeded(unsigned int total, unsigned int count) const; + bool warningRatioExceeded(unsigned int total, unsigned int count) const; + std::string toString() const; - double allowed = d_ratio * static_cast<double>(total); - return (count > allowed); - } + size_t d_minimumNumberOfResponses{0}; + double d_ratio{0.0}; + double d_warningRatio{0.0}; + }; - bool warningRatioExceeded(unsigned int total, unsigned int count) const + struct DynBlockCacheMissRatioRule : public DynBlockRatioRule + { + DynBlockCacheMissRatioRule() = default; + DynBlockCacheMissRatioRule(const std::string& blockReason, unsigned int blockDuration, double ratio, double warningRatio, unsigned int seconds, DNSAction::Action action, size_t minimumNumberOfResponses, double minimumGlobalCacheHitRatio) : + DynBlockRatioRule(blockReason, blockDuration, ratio, warningRatio, seconds, action, minimumNumberOfResponses), d_minimumGlobalCacheHitRatio(minimumGlobalCacheHitRatio) { - if (!d_enabled) { - return false; - } - - if (d_warningRatio == 0.0) { - return false; - } - - if (total < d_minimumNumberOfResponses) { - return false; - } - - double allowed = d_warningRatio * static_cast<double>(total); - return (count > allowed); } - std::string toString() const - { - if (!isEnabled()) { - return ""; - } - - std::stringstream result; - if (d_action != DNSAction::Action::None) { - result << DNSAction::typeToString(d_action) << " "; - } - else { - result << "Apply the global DynBlock action "; - } - result << "for " << std::to_string(d_blockDuration) << " seconds when over " << std::to_string(d_ratio) << " ratio during the last " << d_seconds << " seconds, reason: '" << d_blockReason << "'"; - - return result.str(); - } + bool checkGlobalCacheHitRatio() const; + bool ratioExceeded(unsigned int total, unsigned int count) const; + bool warningRatioExceeded(unsigned int total, unsigned int count) const; + std::string toString() const; - size_t d_minimumNumberOfResponses{0}; - double d_ratio{0.0}; - double d_warningRatio{0.0}; + double d_minimumGlobalCacheHitRatio{0.0}; }; - typedef std::unordered_map<AddressAndPortRange, Counts, AddressAndPortRange::hash> counts_t; + using counts_t = std::unordered_map<AddressAndPortRange, Counts, AddressAndPortRange::hash>; public: DynBlockRulesGroup() @@ -259,18 +182,28 @@ public: entry = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action); } - typedef std::function<std::tuple<bool, boost::optional<std::string>>(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)> smtVisitor_t; + void setCacheMissRatio(double ratio, double warningRatio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action, size_t minimumNumberOfResponses, double minimumGlobalCacheHitRatio) + { + d_respCacheMissRatioRule = DynBlockCacheMissRatioRule(reason, blockDuration, ratio, warningRatio, seconds, action, minimumNumberOfResponses, minimumGlobalCacheHitRatio); + } + + using smtVisitor_t = std::function<std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)>; void setSuffixMatchRule(unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action, smtVisitor_t visitor) { d_suffixMatchRule = DynBlockRule(reason, blockDuration, 0, 0, seconds, action); - d_smtVisitor = visitor; + d_smtVisitor = std::move(visitor); } void setSuffixMatchRuleFFI(unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action, dnsdist_ffi_stat_node_visitor_t visitor) { d_suffixMatchRule = DynBlockRule(reason, blockDuration, 0, 0, seconds, action); - d_smtVisitorFFI = visitor; + d_smtVisitorFFI = std::move(visitor); + } + + void setNewBlockHook(const dnsdist_ffi_dynamic_block_inserted_hook& callback) + { + d_newBlockHook = callback; } void setMasks(uint8_t v4, uint8_t v6, uint8_t port) @@ -332,6 +265,7 @@ public: result << "Query rate rule: " << d_queryRateRule.toString() << std::endl; result << "Response rate rule: " << d_respRateRule.toString() << std::endl; result << "SuffixMatch rule: " << d_suffixMatchRule.toString() << std::endl; + result << "Response cache-miss ratio rule: " << d_respCacheMissRatioRule.toString() << std::endl; result << "RCode rules: " << std::endl; for (const auto& rule : d_rcodeRules) { result << "- " << RCode::to_s(rule.first) << ": " << rule.second.toString() << std::endl; @@ -355,18 +289,18 @@ public: } private: - + void applySMT(const struct timespec& now, StatNode& statNodeRoot); bool checkIfQueryTypeMatches(const Rings::Query& query); bool checkIfResponseCodeMatches(const Rings::Response& response); - void addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange> >& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated, bool warning); + void addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange>>& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated, bool warning); void addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& blocks, const struct timespec& now, const DNSName& name, const DynBlockRule& rule, bool& updated); - void addBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange> >& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated) + void addBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange>>& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated) { addOrRefreshBlock(blocks, now, requestor, rule, updated, false); } - void handleWarning(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange> >& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated) + void handleWarning(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange>>& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated) { addOrRefreshBlock(blocks, now, requestor, rule, updated, true); } @@ -378,7 +312,7 @@ private: bool hasResponseRules() const { - return d_respRateRule.isEnabled() || !d_rcodeRules.empty() || !d_rcodeRatioRules.empty(); + return d_respRateRule.isEnabled() || !d_rcodeRules.empty() || !d_rcodeRatioRules.empty() || d_respCacheMissRatioRule.isEnabled(); } bool hasSuffixMatchRules() const @@ -400,10 +334,12 @@ private: DynBlockRule d_queryRateRule; DynBlockRule d_respRateRule; DynBlockRule d_suffixMatchRule; + DynBlockCacheMissRatioRule d_respCacheMissRatioRule; NetmaskGroup d_excludedSubnets; SuffixMatchNode d_excludedDomains; smtVisitor_t d_smtVisitor; dnsdist_ffi_stat_node_visitor_t d_smtVisitorFFI; + dnsdist_ffi_dynamic_block_inserted_hook d_newBlockHook; uint8_t d_v6Mask{128}; uint8_t d_v4Mask{32}; uint8_t d_portMask{0}; @@ -449,4 +385,9 @@ private: static size_t s_topN; }; +namespace dnsdist::DynamicBlocks +{ +bool addOrRefreshBlock(NetmaskTree<DynBlock, AddressAndPortRange>& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const std::string& reason, unsigned int duration, DNSAction::Action action, bool warning, bool beQuiet); +bool addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& blocks, const struct timespec& now, const DNSName& name, const std::string& reason, unsigned int duration, DNSAction::Action action, bool beQuiet); +} #endif /* DISABLE_DYNBLOCKS */ |