From: Remi Gacogne Date: Tue, 7 May 2024 12:53:39 +0000 (+0200) Subject: dnsdist: Add a SetTag dynamic rule action X-Git-Tag: rec-5.1.0-beta1~27^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3a7310ffac4ad11d12193ecce3c90f0c955e0207;p=thirdparty%2Fpdns.git dnsdist: Add a SetTag dynamic rule action --- diff --git a/pdns/dnsdistdist/dnsdist-dynblocks.cc b/pdns/dnsdistdist/dnsdist-dynblocks.cc index b7baca2be7..ce5068ac92 100644 --- a/pdns/dnsdistdist/dnsdist-dynblocks.cc +++ b/pdns/dnsdistdist/dnsdist-dynblocks.cc @@ -8,7 +8,7 @@ GlobalStateHolder> g_dynblockSMT; DNSAction::Action g_dynBlockAction = DNSAction::Action::Drop; #ifndef DISABLE_DYNBLOCKS -void DynBlockRulesGroup::apply(const struct timespec& now) +void DynBlockRulesGroup::apply(const timespec& now) { counts_t counts; StatNode statNodeRoot; @@ -214,7 +214,7 @@ static DNSAction::Action getActualAction(const DynBlock& block) namespace dnsdist::DynamicBlocks { -bool addOrRefreshBlock(NetmaskTree& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const std::string& reason, unsigned int duration, DNSAction::Action action, bool warning, bool beQuiet) + bool addOrRefreshBlock(NetmaskTree& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const std::string& reason, unsigned int duration, DNSAction::Action action, bool warning, bool beQuiet, std::shared_ptr tagSettings) { unsigned int count = 0; bool expired = false; @@ -255,6 +255,9 @@ bool addOrRefreshBlock(NetmaskTree& blocks, const } dblock.blocks = count; + if (action == DNSAction::Action::SetTag) { + dblock.tagSettings = std::move(tagSettings); + } if (got == nullptr || expired || wasWarning) { const auto actualAction = getActualAction(dblock); @@ -284,7 +287,7 @@ bool addOrRefreshBlock(NetmaskTree& blocks, const return true; } -bool addOrRefreshBlockSMT(SuffixMatchTree& blocks, const struct timespec& now, const DNSName& name, const std::string& reason, unsigned int duration, DNSAction::Action action, bool beQuiet) +bool addOrRefreshBlockSMT(SuffixMatchTree& blocks, const struct timespec& now, const DNSName& name, const std::string& reason, unsigned int duration, DNSAction::Action action, bool beQuiet, std::shared_ptr tagSettings) { struct timespec until = now; until.tv_sec += duration; @@ -315,6 +318,9 @@ bool addOrRefreshBlockSMT(SuffixMatchTree& blocks, const struct timesp } dblock.blocks = count; + if (action == DNSAction::Action::SetTag) { + dblock.tagSettings = std::move(tagSettings); + } if (!beQuiet && (got == nullptr || expired)) { warnlog("Inserting dynamic block for %s for %d seconds: %s", name, duration, reason); @@ -336,7 +342,7 @@ void DynBlockRulesGroup::addOrRefreshBlock(boost::optional(rule.d_action), rule.d_blockDuration, warning); @@ -354,7 +360,7 @@ void DynBlockRulesGroup::addOrRefreshBlockSMT(SuffixMatchTree& blocks, return; } - updated = dnsdist::DynamicBlocks::addOrRefreshBlockSMT(blocks, now, name, rule.d_blockReason, rule.d_blockDuration, rule.d_action, d_beQuiet); + updated = dnsdist::DynamicBlocks::addOrRefreshBlockSMT(blocks, now, name, rule.d_blockReason, rule.d_blockDuration, rule.d_action, d_beQuiet, rule.d_tagSettings); if (updated && d_newBlockHook) { try { d_newBlockHook(dnsdist_ffi_dynamic_block_type_smt, name.toString().c_str(), rule.d_blockReason.c_str(), static_cast(rule.d_action), rule.d_blockDuration, false); diff --git a/pdns/dnsdistdist/dnsdist-dynblocks.hh b/pdns/dnsdistdist/dnsdist-dynblocks.hh index 1a8b3a68e8..1668aa779a 100644 --- a/pdns/dnsdistdist/dnsdist-dynblocks.hh +++ b/pdns/dnsdistdist/dnsdist-dynblocks.hh @@ -72,17 +72,7 @@ using dnsdist_ffi_dynamic_block_inserted_hook = std::function d_rcodeCounts; - std::map d_qtypeCounts; - uint64_t queries{0}; - uint64_t responses{0}; - uint64_t respBytes{0}; - uint64_t cacheMisses{0}; - }; - +public: struct DynBlockRule { DynBlockRule() = default; @@ -103,6 +93,7 @@ private: std::string toString() const; std::string d_blockReason; + std::shared_ptr d_tagSettings; struct timespec d_cutOff; struct timespec d_minTime; unsigned int d_blockDuration{0}; @@ -146,6 +137,16 @@ private: double d_minimumGlobalCacheHitRatio{0.0}; }; +private: + struct Counts + { + std::map d_rcodeCounts; + std::map d_qtypeCounts; + uint64_t queries{0}; + uint64_t responses{0}; + uint64_t respBytes{0}; + uint64_t cacheMisses{0}; + }; using counts_t = std::unordered_map; public: @@ -153,51 +154,48 @@ public: { } - void setQueryRate(unsigned int rate, unsigned int warningRate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action) + void setQueryRate(DynBlockRule&& rule) { - d_queryRateRule = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action); + d_queryRateRule = std::move(rule); } /* rate is in bytes per second */ - void setResponseByteRate(unsigned int rate, unsigned int warningRate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action) + void setResponseByteRate(DynBlockRule&& rule) { - d_respRateRule = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action); + d_respRateRule = std::move(rule); } - void setRCodeRate(uint8_t rcode, unsigned int rate, unsigned int warningRate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action) + void setRCodeRate(uint8_t rcode, DynBlockRule&& rule) { - auto& entry = d_rcodeRules[rcode]; - entry = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action); + d_rcodeRules[rcode] = std::move(rule); } - void setRCodeRatio(uint8_t rcode, double ratio, double warningRatio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action, size_t minimumNumberOfResponses) + void setRCodeRatio(uint8_t rcode, DynBlockRatioRule&& rule) { - auto& entry = d_rcodeRatioRules[rcode]; - entry = DynBlockRatioRule(reason, blockDuration, ratio, warningRatio, seconds, action, minimumNumberOfResponses); + d_rcodeRatioRules[rcode] = std::move(rule); } - void setQTypeRate(uint16_t qtype, unsigned int rate, unsigned int warningRate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action) + void setQTypeRate(uint16_t qtype, DynBlockRule&& rule) { - auto& entry = d_qtypeRules[qtype]; - entry = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action); + d_qtypeRules[qtype] = std::move(rule); } - void setCacheMissRatio(double ratio, double warningRatio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action, size_t minimumNumberOfResponses, double minimumGlobalCacheHitRatio) + void setCacheMissRatio(DynBlockCacheMissRatioRule&& rule) { - d_respCacheMissRatioRule = DynBlockCacheMissRatioRule(reason, blockDuration, ratio, warningRatio, seconds, action, minimumNumberOfResponses, minimumGlobalCacheHitRatio); + d_respCacheMissRatioRule = std::move(rule); } using smtVisitor_t = std::function, boost::optional>(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) + void setSuffixMatchRule(DynBlockRule&& rule, smtVisitor_t visitor) { - d_suffixMatchRule = DynBlockRule(reason, blockDuration, 0, 0, seconds, action); + d_suffixMatchRule = std::move(rule); 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) + void setSuffixMatchRuleFFI(DynBlockRule&& rule, dnsdist_ffi_stat_node_visitor_t visitor) { - d_suffixMatchRule = DynBlockRule(reason, blockDuration, 0, 0, seconds, action); + d_suffixMatchRule = std::move(rule); d_smtVisitorFFI = std::move(visitor); } @@ -215,13 +213,13 @@ public: void apply() { - struct timespec now; + timespec now{}; gettime(&now); apply(now); } - void apply(const struct timespec& now); + void apply(const timespec& now); void excludeRange(const Netmask& range) { @@ -387,7 +385,7 @@ private: namespace dnsdist::DynamicBlocks { -bool addOrRefreshBlock(NetmaskTree& 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& blocks, const struct timespec& now, const DNSName& name, const std::string& reason, unsigned int duration, DNSAction::Action action, bool beQuiet); +bool addOrRefreshBlock(NetmaskTree& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const std::string& reason, unsigned int duration, DNSAction::Action action, bool warning, bool beQuiet, std::shared_ptr tagSettings); +bool addOrRefreshBlockSMT(SuffixMatchTree& blocks, const struct timespec& now, const DNSName& name, const std::string& reason, unsigned int duration, DNSAction::Action action, bool beQuiet, std::shared_ptr tagSettings); } #endif /* DISABLE_DYNBLOCKS */ diff --git a/pdns/dnsdistdist/dnsdist-lua-ffi.cc b/pdns/dnsdistdist/dnsdist-lua-ffi.cc index 6c08cfc1bc..65336d1e44 100644 --- a/pdns/dnsdistdist/dnsdist-lua-ffi.cc +++ b/pdns/dnsdistdist/dnsdist-lua-ffi.cc @@ -1839,12 +1839,11 @@ bool dnsdist_ffi_dynamic_blocks_add(const char* address, const char* message, ui AddressAndPortRange target(clientIPCA, clientIPMask, clientIPPortMask); - struct timespec now - { - }; + timespec now{}; gettime(&now); auto slow = g_dynblockNMG.getCopy(); - if (dnsdist::DynamicBlocks::addOrRefreshBlock(slow, now, target, message, duration, static_cast(action), false, false)) { +#warning FIXME: need to handle tags + if (dnsdist::DynamicBlocks::addOrRefreshBlock(slow, now, target, message, duration, static_cast(action), false, false, nullptr)) { g_dynblockNMG.setState(slow); return true; } @@ -1878,12 +1877,11 @@ bool dnsdist_ffi_dynamic_blocks_smt_add(const char* suffix, const char* message, return false; } - struct timespec now - { - }; + timespec now{}; gettime(&now); auto slow = g_dynblockSMT.getCopy(); - if (dnsdist::DynamicBlocks::addOrRefreshBlockSMT(slow, now, domain, message, duration, static_cast(action), false)) { +#warning FIXME: need to handle tags + if (dnsdist::DynamicBlocks::addOrRefreshBlockSMT(slow, now, domain, message, duration, static_cast(action), false, nullptr)) { g_dynblockSMT.setState(slow); return true; } diff --git a/pdns/dnsdistdist/dnsdist-lua-inspection.cc b/pdns/dnsdistdist/dnsdist-lua-inspection.cc index 2f0e6fa37b..23368734ca 100644 --- a/pdns/dnsdistdist/dnsdist-lua-inspection.cc +++ b/pdns/dnsdistdist/dnsdist-lua-inspection.cc @@ -349,6 +349,29 @@ static bool ringEntryMatches(const GrepQParams& params, const C& entry) return nmmatch && dnmatch && msecmatch; } +#ifndef DISABLE_DYNBLOCKS +using DynamicActionOptionalParameters = boost::optional>; + +static void parseDynamicActionOptionalParameters(const std::string& directive, DynBlockRulesGroup::DynBlockRule& rule, const boost::optional& action, const DynamicActionOptionalParameters& optionalParameters) +{ + if (action && *action == DNSAction::Action::SetTag) { + if (!optionalParameters) { + throw std::runtime_error("SetTag action passed to " + directive + " without additional parameters"); + } + const auto& paramNameIt = optionalParameters->find("tagName"); + if (paramNameIt == optionalParameters->end()) { + throw std::runtime_error("SetTag action passed to " + directive + " without a tag name"); + } + rule.d_tagSettings = std::make_shared(); + rule.d_tagSettings->d_name = paramNameIt->second; + const auto& paramValueIt = optionalParameters->find("tagValue"); + if (paramValueIt != optionalParameters->end()) { + rule.d_tagSettings->d_value = paramValueIt->second; + } + } +} +#endif /* DISABLE_DYNBLOCKS */ + // NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold void setupLuaInspection(LuaContext& luaCtx) { @@ -872,24 +895,32 @@ void setupLuaInspection(LuaContext& luaCtx) /* DynBlockRulesGroup */ luaCtx.writeFunction("dynBlockRulesGroup", []() { return std::make_shared(); }); - luaCtx.registerFunction::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional)>("setQueryRate", [](std::shared_ptr& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate) { + luaCtx.registerFunction::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional, DynamicActionOptionalParameters)>("setQueryRate", [](std::shared_ptr& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate, DynamicActionOptionalParameters optionalParameters) { if (group) { - group->setQueryRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, rate, warningRate ? *warningRate : 0, seconds, action ? *action : DNSAction::Action::None); + parseDynamicActionOptionalParameters("setQueryRate", rule, action, optionalParameters); + group->setQueryRate(std::move(rule)); } }); - luaCtx.registerFunction::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional)>("setResponseByteRate", [](std::shared_ptr& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate) { + luaCtx.registerFunction::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional, DynamicActionOptionalParameters)>("setResponseByteRate", [](std::shared_ptr& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate, DynamicActionOptionalParameters optionalParameters) { if (group) { - group->setResponseByteRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, rate, warningRate ? *warningRate : 0, seconds, action ? *action : DNSAction::Action::None); + parseDynamicActionOptionalParameters("setResponseByteRate", rule, action, optionalParameters); + group->setResponseByteRate(std::move(rule)); } }); - luaCtx.registerFunction::*)(unsigned int, const std::string&, unsigned int, boost::optional, DynBlockRulesGroup::smtVisitor_t)>("setSuffixMatchRule", [](std::shared_ptr& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, DynBlockRulesGroup::smtVisitor_t visitor) { + luaCtx.registerFunction::*)(unsigned int, const std::string&, unsigned int, boost::optional, DynBlockRulesGroup::smtVisitor_t, DynamicActionOptionalParameters)>("setSuffixMatchRule", [](std::shared_ptr& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, DynBlockRulesGroup::smtVisitor_t visitor, DynamicActionOptionalParameters optionalParameters) { if (group) { - group->setSuffixMatchRule(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, std::move(visitor)); + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 0, 0, seconds, action ? *action : DNSAction::Action::None); + parseDynamicActionOptionalParameters("setSuffixMatchRule", rule, action, optionalParameters); + group->setSuffixMatchRule(std::move(rule), std::move(visitor)); } }); - luaCtx.registerFunction::*)(unsigned int, const std::string&, unsigned int, boost::optional, dnsdist_ffi_stat_node_visitor_t)>("setSuffixMatchRuleFFI", [](std::shared_ptr& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, dnsdist_ffi_stat_node_visitor_t visitor) { + luaCtx.registerFunction::*)(unsigned int, const std::string&, unsigned int, boost::optional, dnsdist_ffi_stat_node_visitor_t, DynamicActionOptionalParameters)>("setSuffixMatchRuleFFI", [](std::shared_ptr& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, dnsdist_ffi_stat_node_visitor_t visitor, DynamicActionOptionalParameters optionalParameters) { if (group) { - group->setSuffixMatchRuleFFI(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, std::move(visitor)); + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 0, 0, seconds, action ? *action : DNSAction::Action::None); + parseDynamicActionOptionalParameters("setSuffixMatchRuleFFI", rule, action, optionalParameters); + group->setSuffixMatchRuleFFI(std::move(rule), std::move(visitor)); } }); luaCtx.registerFunction::*)(const dnsdist_ffi_dynamic_block_inserted_hook&)>("setNewBlockInsertedHook", [](std::shared_ptr& group, const dnsdist_ffi_dynamic_block_inserted_hook& hook) { @@ -897,24 +928,32 @@ void setupLuaInspection(LuaContext& luaCtx) group->setNewBlockHook(hook); } }); - luaCtx.registerFunction::*)(uint8_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional)>("setRCodeRate", [](std::shared_ptr& group, uint8_t rcode, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate) { + luaCtx.registerFunction::*)(uint8_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional, DynamicActionOptionalParameters)>("setRCodeRate", [](std::shared_ptr& group, uint8_t rcode, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate, DynamicActionOptionalParameters optionalParameters) { if (group) { - group->setRCodeRate(rcode, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, rate, warningRate ? *warningRate : 0, seconds, action ? *action : DNSAction::Action::None); + parseDynamicActionOptionalParameters("setRCodeRate", rule, action, optionalParameters); + group->setRCodeRate(rcode, std::move(rule)); } }); - luaCtx.registerFunction::*)(uint8_t, double, unsigned int, const std::string&, unsigned int, size_t, boost::optional, boost::optional)>("setRCodeRatio", [](std::shared_ptr& group, uint8_t rcode, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, boost::optional action, boost::optional warningRatio) { + luaCtx.registerFunction::*)(uint8_t, double, unsigned int, const std::string&, unsigned int, size_t, boost::optional, boost::optional, DynamicActionOptionalParameters)>("setRCodeRatio", [](std::shared_ptr& group, uint8_t rcode, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, boost::optional action, boost::optional warningRatio, DynamicActionOptionalParameters optionalParameters) { if (group) { - group->setRCodeRatio(rcode, ratio, warningRatio ? *warningRatio : 0.0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, minimumNumberOfResponses); + DynBlockRulesGroup::DynBlockRatioRule rule(reason, blockDuration, ratio, warningRatio ? *warningRatio : 0.0, seconds, action ? *action : DNSAction::Action::None, minimumNumberOfResponses); + parseDynamicActionOptionalParameters("setRCodeRatio", rule, action, optionalParameters); + group->setRCodeRatio(rcode, std::move(rule)); } }); - luaCtx.registerFunction::*)(uint16_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional)>("setQTypeRate", [](std::shared_ptr& group, uint16_t qtype, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate) { + luaCtx.registerFunction::*)(uint16_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional, boost::optional, DynamicActionOptionalParameters)>("setQTypeRate", [](std::shared_ptr& group, uint16_t qtype, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional action, boost::optional warningRate, DynamicActionOptionalParameters optionalParameters) { if (group) { - group->setQTypeRate(qtype, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None); + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, rate, warningRate ? *warningRate : 0.0, seconds, action ? *action : DNSAction::Action::None); + parseDynamicActionOptionalParameters("setQTypeRate", rule, action, optionalParameters); + group->setQTypeRate(qtype, std::move(rule)); } }); - luaCtx.registerFunction::*)(double, unsigned int, const std::string&, unsigned int, size_t, double, boost::optional, boost::optional)>("setCacheMissRatio", [](std::shared_ptr& group, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, double minimumGlobalCacheHitRatio, boost::optional action, boost::optional warningRatio) { + luaCtx.registerFunction::*)(double, unsigned int, const std::string&, unsigned int, size_t, double, boost::optional, boost::optional, DynamicActionOptionalParameters)>("setCacheMissRatio", [](std::shared_ptr& group, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, double minimumGlobalCacheHitRatio, boost::optional action, boost::optional warningRatio, DynamicActionOptionalParameters optionalParameters) { if (group) { - group->setCacheMissRatio(ratio, warningRatio ? *warningRatio : 0.0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, minimumNumberOfResponses, minimumGlobalCacheHitRatio); + DynBlockRulesGroup::DynBlockCacheMissRatioRule rule(reason, blockDuration, ratio, warningRatio ? *warningRatio : 0.0, seconds, action ? *action : DNSAction::Action::None, minimumNumberOfResponses, minimumGlobalCacheHitRatio); + parseDynamicActionOptionalParameters("setCacheMissRatio", rule, action, optionalParameters); + group->setCacheMissRatio(std::move(rule)); } }); luaCtx.registerFunction::*)(uint8_t, uint8_t, uint8_t)>("setMasks", [](std::shared_ptr& group, uint8_t v4addr, uint8_t v6addr, uint8_t port) { diff --git a/pdns/dnsdistdist/dnsdist-lua-vars.cc b/pdns/dnsdistdist/dnsdist-lua-vars.cc index 9fc3f521b5..7c0457a634 100644 --- a/pdns/dnsdistdist/dnsdist-lua-vars.cc +++ b/pdns/dnsdistdist/dnsdist-lua-vars.cc @@ -27,7 +27,7 @@ void setupLuaVars(LuaContext& luaCtx) { - luaCtx.writeVariable("DNSAction", LuaAssociativeTable{{"Drop", (int)DNSAction::Action::Drop}, {"Nxdomain", (int)DNSAction::Action::Nxdomain}, {"Refused", (int)DNSAction::Action::Refused}, {"Spoof", (int)DNSAction::Action::Spoof}, {"SpoofPacket", (int)DNSAction::Action::SpoofPacket}, {"SpoofRaw", (int)DNSAction::Action::SpoofRaw}, {"Allow", (int)DNSAction::Action::Allow}, {"HeaderModify", (int)DNSAction::Action::HeaderModify}, {"Pool", (int)DNSAction::Action::Pool}, {"None", (int)DNSAction::Action::None}, {"NoOp", (int)DNSAction::Action::NoOp}, {"Delay", (int)DNSAction::Action::Delay}, {"Truncate", (int)DNSAction::Action::Truncate}, {"ServFail", (int)DNSAction::Action::ServFail}, {"NoRecurse", (int)DNSAction::Action::NoRecurse}}); + luaCtx.writeVariable("DNSAction", LuaAssociativeTable{{"Drop", (int)DNSAction::Action::Drop}, {"Nxdomain", (int)DNSAction::Action::Nxdomain}, {"Refused", (int)DNSAction::Action::Refused}, {"Spoof", (int)DNSAction::Action::Spoof}, {"SpoofPacket", (int)DNSAction::Action::SpoofPacket}, {"SpoofRaw", (int)DNSAction::Action::SpoofRaw}, {"Allow", (int)DNSAction::Action::Allow}, {"HeaderModify", (int)DNSAction::Action::HeaderModify}, {"Pool", (int)DNSAction::Action::Pool}, {"None", (int)DNSAction::Action::None}, {"NoOp", (int)DNSAction::Action::NoOp}, {"Delay", (int)DNSAction::Action::Delay}, {"Truncate", (int)DNSAction::Action::Truncate}, {"ServFail", (int)DNSAction::Action::ServFail}, {"NoRecurse", (int)DNSAction::Action::NoRecurse}, {"SetTag", (int)DNSAction::Action::SetTag}}); luaCtx.writeVariable("DNSResponseAction", LuaAssociativeTable{{"Allow", (int)DNSResponseAction::Action::Allow}, {"Delay", (int)DNSResponseAction::Action::Delay}, {"Drop", (int)DNSResponseAction::Action::Drop}, {"HeaderModify", (int)DNSResponseAction::Action::HeaderModify}, {"ServFail", (int)DNSResponseAction::Action::ServFail}, {"Truncate", (int)DNSResponseAction::Action::Truncate}, {"None", (int)DNSResponseAction::Action::None}}); diff --git a/pdns/dnsdistdist/dnsdist-lua.cc b/pdns/dnsdistdist/dnsdist-lua.cc index 55075c8efc..81ace57ee9 100644 --- a/pdns/dnsdistdist/dnsdist-lua.cc +++ b/pdns/dnsdistdist/dnsdist-lua.cc @@ -1651,8 +1651,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) for (const auto& capair : names) { DNSName domain(capair.second); domain.makeUsLowerCase(); - - if (dnsdist::DynamicBlocks::addOrRefreshBlockSMT(slow, now, domain, msg, actualSeconds, action ? *action : DNSAction::Action::None, false)) { +#warning FIXME: need to handle tags + if (dnsdist::DynamicBlocks::addOrRefreshBlockSMT(slow, now, domain, msg, actualSeconds, action ? *action : DNSAction::Action::None, false, nullptr)) { needUpdate = true; } } @@ -1690,7 +1690,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) timespec now{}; gettime(&now); auto slow = g_dynblockNMG.getCopy(); - if (dnsdist::DynamicBlocks::addOrRefreshBlock(slow, now, target, msg, actualSeconds, action ? *action : DNSAction::Action::None, false, false)) { +#warning FIXME: need to handle tags + if (dnsdist::DynamicBlocks::addOrRefreshBlock(slow, now, target, msg, actualSeconds, action ? *action : DNSAction::Action::None, false, false, nullptr)) { g_dynblockNMG.setState(slow); } }); diff --git a/pdns/dnsdistdist/dnsdist.hh b/pdns/dnsdistdist/dnsdist.hh index 76444e6c64..4291218f5e 100644 --- a/pdns/dnsdistdist/dnsdist.hh +++ b/pdns/dnsdistdist/dnsdist.hh @@ -380,7 +380,7 @@ struct DynBlock string reason; DNSName domain; timespec until{}; - std::unique_ptr tagSettings{nullptr}; + std::shared_ptr tagSettings{nullptr}; mutable std::atomic blocks{0}; DNSAction::Action action{DNSAction::Action::None}; bool warning{false}; diff --git a/pdns/dnsdistdist/docs/reference/constants.rst b/pdns/dnsdistdist/docs/reference/constants.rst index 576acce0fa..a5040c7598 100755 --- a/pdns/dnsdistdist/docs/reference/constants.rst +++ b/pdns/dnsdistdist/docs/reference/constants.rst @@ -111,6 +111,9 @@ DNSAction .. versionchanged:: 1.8.0 ``DNSAction.SpoofPacket`` has been added. +.. versionchanged:: 2.0.0 + ``DNSAction.SetTag`` has been added. + These constants represent an Action that can be returned from :func:`LuaAction` functions. * ``DNSAction.Allow``: let the query pass, skipping other rules @@ -119,15 +122,16 @@ These constants represent an Action that can be returned from :func:`LuaAction` * ``DNSAction.HeaderModify``: indicate that the query has been turned into a response * ``DNSAction.None``: continue to the next rule * ``DNSAction.NoOp``: continue to the next rule (used for Dynamic Block actions where None has a different meaning) + * ``DNSAction.NoRecurse``: set rd=0 on the query * ``DNSAction.Nxdomain``: return a response with a NXDomain rcode * ``DNSAction.Pool``: use the specified pool to forward this query * ``DNSAction.Refused``: return a response with a Refused rcode * ``DNSAction.ServFail``: return a response with a ServFail rcode + * ``DNSAction.SetTag``: set a tag, see :function:`SetTagAction` (only used for Dynamic Block actions, see meth:`DNSQuestion:setTag` to set a tag from Lua) * ``DNSAction.Spoof``: spoof the response using the supplied IPv4 (A), IPv6 (AAAA) or string (CNAME) value. TTL will be 60 seconds. * ``DNSAction.SpoofPacket``: spoof the response using the supplied raw packet * ``DNSAction.SpoofRaw``: spoof the response using the supplied raw value as record data (see also :meth:`DNSQuestion:spoof` and :func:`dnsdist_ffi_dnsquestion_spoof_raw` to spoof multiple values) * ``DNSAction.Truncate``: truncate the response - * ``DNSAction.NoRecurse``: set rd=0 on the query .. _DNSQType: diff --git a/pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc b/pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc index fbd24dd973..0bcbb98128 100644 --- a/pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc +++ b/pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc @@ -56,8 +56,11 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_QueryRate, TestFixture) { DynBlockRulesGroup dbrg; dbrg.setQuiet(true); - /* block above 50 qps for numberOfSeconds seconds, no warning */ - dbrg.setQueryRate(50, 0, numberOfSeconds, reason, blockDuration, action); + { + /* block above 50 qps for numberOfSeconds seconds, no warning */ + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 50, 0, numberOfSeconds, action); + dbrg.setQueryRate(std::move(rule)); + } { /* insert 45 qps from a given client in the last 10s @@ -194,8 +197,11 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_QueryRate_RangeV6, TestFixture) dbrg.setQuiet(true); dbrg.setMasks(32, 64, 0); - /* block above 50 qps for numberOfSeconds seconds, no warning */ - dbrg.setQueryRate(50, 0, numberOfSeconds, reason, blockDuration, action); + { + /* block above 50 qps for numberOfSeconds seconds, no warning */ + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 50, 0, numberOfSeconds, action); + dbrg.setQueryRate(std::move(rule)); + } { /* insert 45 qps from a given client in the last 10s @@ -294,8 +300,11 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_QueryRate_V4Ports, TestFixture) /* split v4 by ports using a /2 (0 - 16383, 16384 - 32767, 32768 - 49151, 49152 - 65535) */ dbrg.setMasks(32, 128, 2); - /* block above 50 qps for numberOfSeconds seconds, no warning */ - dbrg.setQueryRate(50, 0, numberOfSeconds, reason, blockDuration, action); + { + /* block above 50 qps for numberOfSeconds seconds, no warning */ + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 50, 0, numberOfSeconds, action); + dbrg.setQueryRate(std::move(rule)); + } { /* insert 45 qps from a given client in the last 10s @@ -430,9 +439,15 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_QueryRate_responses, TestFixture DynBlockRulesGroup dbrg; dbrg.setQuiet(true); - /* block above 50 qps for numberOfSeconds seconds, no warning */ - dbrg.setQueryRate(50, 0, numberOfSeconds, reason, blockDuration, action); - dbrg.setRCodeRate(RCode::ServFail, 50, 40, 5, "Exceeded ServFail rate", 60, DNSAction::Action::Drop); + { + /* block above 50 qps for numberOfSeconds seconds, no warning */ + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 50, 0, numberOfSeconds, action); + dbrg.setQueryRate(std::move(rule)); + } + { + DynBlockRulesGroup::DynBlockRule rule("Exceeded ServFail rate", 60, 50, 40, 5, DNSAction::Action::Drop); + dbrg.setRCodeRate(RCode::ServFail, std::move(rule)); + } { /* insert 45 qps (including responses) from a given client for the last 100s @@ -482,8 +497,11 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_QTypeRate, TestFixture) { DynBlockRulesGroup dbrg; dbrg.setQuiet(true); - /* block above 50 qps for numberOfSeconds seconds, no warning */ - dbrg.setQTypeRate(QType::AAAA, 50, 0, numberOfSeconds, reason, blockDuration, action); + { + /* block above 50 qps for numberOfSeconds seconds, no warning */ + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 50, 0, numberOfSeconds, action); + dbrg.setQTypeRate(QType::AAAA, std::move(rule)); + } { /* insert 45 qps from a given client in the last 10s @@ -573,8 +591,11 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_RCodeRate, TestFixture) { DynBlockRulesGroup dbrg; dbrg.setQuiet(true); - /* block above 50 ServFail/s for numberOfSeconds seconds, no warning */ - dbrg.setRCodeRate(rcode, 50, 0, numberOfSeconds, reason, blockDuration, action); + { + /* block above 50 ServFail/s for numberOfSeconds seconds, no warning */ + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 50, 0, numberOfSeconds, action); + dbrg.setRCodeRate(rcode, std::move(rule)); + } { /* insert 45 ServFail/s from a given client in the last 10s @@ -666,8 +687,11 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_RCodeRatio, TestFixture) { DynBlockRulesGroup dbrg; dbrg.setQuiet(true); - /* block above 0.2 ServFail/Total ratio over numberOfSeconds seconds, no warning, minimum number of queries should be at least 51 */ - dbrg.setRCodeRatio(rcode, 0.2, 0, numberOfSeconds, reason, blockDuration, action, 51); + { + /* block above 0.2 ServFail/Total ratio over numberOfSeconds seconds, no warning, minimum number of queries should be at least 51 */ + DynBlockRulesGroup::DynBlockRatioRule rule(reason, blockDuration, 0.2, 0.0, numberOfSeconds, action, 51); + dbrg.setRCodeRatio(rcode, std::move(rule)); + } { /* insert 20 ServFail and 80 NoErrors from a given client in the last 10s @@ -785,8 +809,11 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_ResponseByteRate, TestFixture) { DynBlockRulesGroup dbrg; dbrg.setQuiet(true); - /* block above 10kB/s for numberOfSeconds seconds, no warning */ - dbrg.setResponseByteRate(10000, 0, numberOfSeconds, reason, blockDuration, action); + { + /* block above 10kB/s for numberOfSeconds seconds, no warning */ + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 10000, 0, numberOfSeconds, action); + dbrg.setResponseByteRate(std::move(rule)); + } { /* insert 99 answers of 100 bytes per second from a given client in the last 10s @@ -863,7 +890,10 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_CacheMissRatio, TestFixture) { /* block above 0.5 Cache-Miss/Total ratio over numberOfSeconds seconds, no warning, minimum number of queries should be at least 51, global cache hit at least 80% */ dnsdist::metrics::g_stats.cacheHits.store(80); dnsdist::metrics::g_stats.cacheMisses.store(20); - dbrg.setCacheMissRatio(0.5, 0, numberOfSeconds, reason, blockDuration, action, 51, 0.8); + { + DynBlockRulesGroup::DynBlockCacheMissRatioRule rule(reason, blockDuration, 0.5, 0.0, numberOfSeconds, action, 51, 0.8); + dbrg.setCacheMissRatio(std::move(rule)); + } { /* insert 50 cache misses and 50 cache hits from a given client in the last 10s @@ -977,8 +1007,11 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_Warning, TestFixture) { DynBlockRulesGroup dbrg; dbrg.setQuiet(true); - /* warn above 20 qps for numberOfSeconds seconds, block above 50 qps */ - dbrg.setQueryRate(50, 20, numberOfSeconds, reason, blockDuration, action); + { + /* warn above 20 qps for numberOfSeconds seconds, block above 50 qps */ + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 50, 20, numberOfSeconds, action); + dbrg.setQueryRate(std::move(rule)); + } { /* insert 20 qps from a given client in the last 10s @@ -1141,8 +1174,11 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_Ranges, TestFixture) { /* but exclude 192.0.2.42 only */ dbrg.excludeRange(Netmask("192.0.2.42/32")); - /* block above 50 qps for numberOfSeconds seconds, no warning */ - dbrg.setQueryRate(50, 0, numberOfSeconds, reason, blockDuration, action); + { + /* block above 50 qps for numberOfSeconds seconds, no warning */ + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 50, 0, numberOfSeconds, action); + dbrg.setQueryRate(std::move(rule)); + } { /* insert just above 50 qps from the two clients in the last 10s @@ -1202,8 +1238,11 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN, TestFixture) { g_rings.clear(); g_dynblockNMG.setState(emptyNMG); - /* block above 0 qps for numberOfSeconds seconds, no warning */ - dbrg.setQueryRate(0, 0, numberOfSeconds, reason, blockDuration, action); + { + /* block above 0 qps for numberOfSeconds seconds, no warning */ + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 0, 0, numberOfSeconds, action); + dbrg.setQueryRate(std::move(rule)); + } /* insert one fake query from 255 clients: */ @@ -1255,12 +1294,15 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN, TestFixture) { g_dynblockNMG.setState(emptyNMG); g_dynblockSMT.setState(emptySMT); - dbrg.setSuffixMatchRule(numberOfSeconds, reason, blockDuration, action, [](const StatNode& node, const StatNode::Stat& self, const StatNode::Stat& children) { - if (self.queries > 0) { - return std::tuple, boost::optional>(true, boost::none, boost::none); - } - return std::tuple, boost::optional>(false, boost::none, boost::none); - }); + { + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 0, 0, numberOfSeconds, action); + dbrg.setSuffixMatchRule(std::move(rule), [](const StatNode& node, const StatNode::Stat& self, const StatNode::Stat& children) { + if (self.queries > 0) { + return std::tuple, boost::optional>(true, boost::none, boost::none); + } + return std::tuple, boost::optional>(false, boost::none, boost::none); + }); + } /* insert one fake response for 255 DNS names */ const ComboAddress requestor("192.0.2.1"); @@ -1312,12 +1354,15 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN, TestFixture) { g_dynblockNMG.setState(emptyNMG); g_dynblockSMT.setState(emptySMT); - dbrg.setSuffixMatchRule(numberOfSeconds, reason, blockDuration, action, [](const StatNode& node, const StatNode::Stat& self, const StatNode::Stat& children) { - if (self.queries > 0) { - return std::tuple, boost::optional>(true, "blocked for a different reason", static_cast(DNSAction::Action::Truncate)); - } - return std::tuple, boost::optional>(false, boost::none, boost::none); - }); + { + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 0, 0, numberOfSeconds, action); + dbrg.setSuffixMatchRule(std::move(rule), [](const StatNode& node, const StatNode::Stat& self, const StatNode::Stat& children) { + if (self.queries > 0) { + return std::tuple, boost::optional>(true, "blocked for a different reason", static_cast(DNSAction::Action::Truncate)); + } + return std::tuple, boost::optional>(false, boost::none, boost::none); + }); + } /* insert one fake response for 255 DNS names */ const ComboAddress requestor("192.0.2.1"); @@ -1370,12 +1415,15 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN, TestFixture) { g_dynblockNMG.setState(emptyNMG); g_dynblockSMT.setState(emptySMT); - dbrg.setSuffixMatchRule(numberOfSeconds, reason, blockDuration, action, [](const StatNode& node, const StatNode::Stat& self, const StatNode::Stat& children) { - if (self.queries > 0) { - return std::tuple, boost::optional>(true, boost::none, boost::none); - } - return std::tuple, boost::optional>(false, boost::none, boost::none); - }); + { + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 0, 0, numberOfSeconds, action); + dbrg.setSuffixMatchRule(std::move(rule), [](const StatNode& node, const StatNode::Stat& self, const StatNode::Stat& children) { + if (self.queries > 0) { + return std::tuple, boost::optional>(true, boost::none, boost::none); + } + return std::tuple, boost::optional>(false, boost::none, boost::none); + }); + } bool done = false; const ComboAddress requestor("192.0.2.1"); @@ -1421,7 +1469,10 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN, TestFixture) { g_rings.clear(); g_dynblockNMG.setState(emptyNMG); g_dynblockSMT.setState(emptySMT); - dbrg.setQueryRate(0, 0, numberOfSeconds, reason, blockDuration, action); + { + DynBlockRulesGroup::DynBlockRule rule(reason, blockDuration, 0, 0, numberOfSeconds, action); + dbrg.setQueryRate(std::move(rule)); + } bool done = false; for (size_t idxB = 0; !done && idxB < 256; idxB++) {