]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add a SetTag dynamic rule action
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 7 May 2024 12:53:39 +0000 (14:53 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 16 May 2024 09:10:31 +0000 (11:10 +0200)
pdns/dnsdistdist/dnsdist-dynblocks.cc
pdns/dnsdistdist/dnsdist-dynblocks.hh
pdns/dnsdistdist/dnsdist-lua-ffi.cc
pdns/dnsdistdist/dnsdist-lua-inspection.cc
pdns/dnsdistdist/dnsdist-lua-vars.cc
pdns/dnsdistdist/dnsdist-lua.cc
pdns/dnsdistdist/dnsdist.hh
pdns/dnsdistdist/docs/reference/constants.rst
pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc

index b7baca2be7154118fd49b07e33b9becaccc03623..ce5068ac92f062848f80ed04c2dfed416c20d9da 100644 (file)
@@ -8,7 +8,7 @@ GlobalStateHolder<SuffixMatchTree<DynBlock>> 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<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 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, std::shared_ptr<DynBlock::TagSettings> tagSettings)
 {
   unsigned int count = 0;
   bool expired = false;
@@ -255,6 +255,9 @@ bool addOrRefreshBlock(NetmaskTree<DynBlock, AddressAndPortRange>& 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<DynBlock, AddressAndPortRange>& blocks, const
   return true;
 }
 
-bool addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& blocks, const struct timespec& now, const DNSName& name, const std::string& reason, unsigned int duration, DNSAction::Action action, 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, std::shared_ptr<DynBlock::TagSettings> tagSettings)
 {
   struct timespec until = now;
   until.tv_sec += duration;
@@ -315,6 +318,9 @@ bool addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& 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<NetmaskTree<DynBlock,
     blocks = g_dynblockNMG.getCopy();
   }
 
-  updated = dnsdist::DynamicBlocks::addOrRefreshBlock(*blocks, now, requestor, rule.d_blockReason, rule.d_blockDuration, rule.d_action, warning, d_beQuiet);
+  updated = dnsdist::DynamicBlocks::addOrRefreshBlock(*blocks, now, requestor, rule.d_blockReason, rule.d_blockDuration, rule.d_action, warning, d_beQuiet, rule.d_tagSettings);
   if (updated && d_newBlockHook) {
     try {
       d_newBlockHook(dnsdist_ffi_dynamic_block_type_nmt, requestor.toString().c_str(), rule.d_blockReason.c_str(), static_cast<uint8_t>(rule.d_action), rule.d_blockDuration, warning);
@@ -354,7 +360,7 @@ void DynBlockRulesGroup::addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& 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<uint8_t>(rule.d_action), rule.d_blockDuration, false);
index 1a8b3a68e81fc51ed97df06367b92ba750d93c75..1668aa779abca7fd86872d27db95be454bae5ab5 100644 (file)
@@ -72,17 +72,7 @@ using dnsdist_ffi_dynamic_block_inserted_hook = std::function<void(uint8_t type,
 
 class DynBlockRulesGroup
 {
-private:
-  struct Counts
-  {
-    std::map<uint8_t, uint64_t> d_rcodeCounts;
-    std::map<uint16_t, uint64_t> 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<DynBlock::TagSettings> 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<uint8_t, uint64_t> d_rcodeCounts;
+    std::map<uint16_t, uint64_t> d_qtypeCounts;
+    uint64_t queries{0};
+    uint64_t responses{0};
+    uint64_t respBytes{0};
+    uint64_t cacheMisses{0};
+  };
   using counts_t = std::unordered_map<AddressAndPortRange, Counts, AddressAndPortRange::hash>;
 
 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<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)
+  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<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);
+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, std::shared_ptr<DynBlock::TagSettings> tagSettings);
+bool addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& blocks, const struct timespec& now, const DNSName& name, const std::string& reason, unsigned int duration, DNSAction::Action action, bool beQuiet, std::shared_ptr<DynBlock::TagSettings> tagSettings);
 }
 #endif /* DISABLE_DYNBLOCKS */
index 6c08cfc1bc2456b314cc980d1823ca78354671a7..65336d1e44c53a6ef89fdc38a0a2995f2f96f522 100644 (file)
@@ -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<DNSAction::Action>(action), false, false)) {
+#warning FIXME: need to handle tags
+    if (dnsdist::DynamicBlocks::addOrRefreshBlock(slow, now, target, message, duration, static_cast<DNSAction::Action>(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<DNSAction::Action>(action), false)) {
+#warning FIXME: need to handle tags
+    if (dnsdist::DynamicBlocks::addOrRefreshBlockSMT(slow, now, domain, message, duration, static_cast<DNSAction::Action>(action), false, nullptr)) {
       g_dynblockSMT.setState(slow);
       return true;
     }
index 2f0e6fa37b727ae8c2b9962310e18cdaa56653cb..23368734ca61e43bc33afddd7594c22a6ef46ec6 100644 (file)
@@ -349,6 +349,29 @@ static bool ringEntryMatches(const GrepQParams& params, const C& entry)
   return nmmatch && dnmatch && msecmatch;
 }
 
+#ifndef DISABLE_DYNBLOCKS
+using DynamicActionOptionalParameters = boost::optional<LuaAssociativeTable<std::string>>;
+
+static void parseDynamicActionOptionalParameters(const std::string& directive, DynBlockRulesGroup::DynBlockRule& rule, const boost::optional<DNSAction::Action>& 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<DynBlock::TagSettings>();
+    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<DynBlockRulesGroup>(); });
-  luaCtx.registerFunction<void (std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setQueryRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
+  luaCtx.registerFunction<void (std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>, DynamicActionOptionalParameters)>("setQueryRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> 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<void (std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setResponseByteRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
+  luaCtx.registerFunction<void (std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>, DynamicActionOptionalParameters)>("setResponseByteRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> 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<void (std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, DynBlockRulesGroup::smtVisitor_t)>("setSuffixMatchRule", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, DynBlockRulesGroup::smtVisitor_t visitor) {
+  luaCtx.registerFunction<void (std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, DynBlockRulesGroup::smtVisitor_t, DynamicActionOptionalParameters)>("setSuffixMatchRule", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> 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<void (std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, dnsdist_ffi_stat_node_visitor_t)>("setSuffixMatchRuleFFI", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, dnsdist_ffi_stat_node_visitor_t visitor) {
+  luaCtx.registerFunction<void (std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, dnsdist_ffi_stat_node_visitor_t, DynamicActionOptionalParameters)>("setSuffixMatchRuleFFI", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> 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<void (std::shared_ptr<DynBlockRulesGroup>::*)(const dnsdist_ffi_dynamic_block_inserted_hook&)>("setNewBlockInsertedHook", [](std::shared_ptr<DynBlockRulesGroup>& group, const dnsdist_ffi_dynamic_block_inserted_hook& hook) {
@@ -897,24 +928,32 @@ void setupLuaInspection(LuaContext& luaCtx)
       group->setNewBlockHook(hook);
     }
   });
-  luaCtx.registerFunction<void (std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setRCodeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
+  luaCtx.registerFunction<void (std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>, DynamicActionOptionalParameters)>("setRCodeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> 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<void (std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, double, unsigned int, const std::string&, unsigned int, size_t, boost::optional<DNSAction::Action>, boost::optional<double>)>("setRCodeRatio", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, boost::optional<DNSAction::Action> action, boost::optional<double> warningRatio) {
+  luaCtx.registerFunction<void (std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, double, unsigned int, const std::string&, unsigned int, size_t, boost::optional<DNSAction::Action>, boost::optional<double>, DynamicActionOptionalParameters)>("setRCodeRatio", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, boost::optional<DNSAction::Action> action, boost::optional<double> 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<void (std::shared_ptr<DynBlockRulesGroup>::*)(uint16_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setQTypeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint16_t qtype, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
+  luaCtx.registerFunction<void (std::shared_ptr<DynBlockRulesGroup>::*)(uint16_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>, DynamicActionOptionalParameters)>("setQTypeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint16_t qtype, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> 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<void (std::shared_ptr<DynBlockRulesGroup>::*)(double, unsigned int, const std::string&, unsigned int, size_t, double, boost::optional<DNSAction::Action>, boost::optional<double>)>("setCacheMissRatio", [](std::shared_ptr<DynBlockRulesGroup>& group, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, double minimumGlobalCacheHitRatio, boost::optional<DNSAction::Action> action, boost::optional<double> warningRatio) {
+  luaCtx.registerFunction<void (std::shared_ptr<DynBlockRulesGroup>::*)(double, unsigned int, const std::string&, unsigned int, size_t, double, boost::optional<DNSAction::Action>, boost::optional<double>, DynamicActionOptionalParameters)>("setCacheMissRatio", [](std::shared_ptr<DynBlockRulesGroup>& group, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, double minimumGlobalCacheHitRatio, boost::optional<DNSAction::Action> action, boost::optional<double> 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<void (std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, uint8_t, uint8_t)>("setMasks", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t v4addr, uint8_t v6addr, uint8_t port) {
index 9fc3f521b5c78dc58e2cf7aea6278f10002d2444..7c0457a6349382dfdbadbe486533a0293b0770a6 100644 (file)
@@ -27,7 +27,7 @@
 
 void setupLuaVars(LuaContext& luaCtx)
 {
-  luaCtx.writeVariable("DNSAction", LuaAssociativeTable<int>{{"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<int>{{"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<int>{{"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}});
 
index 55075c8efcd55a54459a880d1d59cf2d68b9b3a2..81ace57ee9592809792df225a8717e530c7a8fdd 100644 (file)
@@ -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);
                          }
                        });
index 76444e6c640f5a8bb4e1faa3255ff9170e719058..4291218f5ef7d3d306b2dffcd54c7051982b34a3 100644 (file)
@@ -380,7 +380,7 @@ struct DynBlock
   string reason;
   DNSName domain;
   timespec until{};
-  std::unique_ptr<TagSettings> tagSettings{nullptr};
+  std::shared_ptr<TagSettings> tagSettings{nullptr};
   mutable std::atomic<uint32_t> blocks{0};
   DNSAction::Action action{DNSAction::Action::None};
   bool warning{false};
index 576acce0fae07faef53a5332154fffcd9d27a729..a5040c7598cf23c3d58dce9ad93653df8f6c2864 100755 (executable)
@@ -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:
 
index fbd24dd973e92c33fb40259b646a9a32b7fbae27..0bcbb98128957e014dbd33ca9dcf19525ee1e717 100644 (file)
@@ -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<bool, boost::optional<std::string>, boost::optional<int>>(true, boost::none, boost::none);
-      }
-      return std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(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<bool, boost::optional<std::string>, boost::optional<int>>(true, boost::none, boost::none);
+        }
+        return std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(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<bool, boost::optional<std::string>, boost::optional<int>>(true, "blocked for a different reason", static_cast<int>(DNSAction::Action::Truncate));
-      }
-      return std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(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<bool, boost::optional<std::string>, boost::optional<int>>(true, "blocked for a different reason", static_cast<int>(DNSAction::Action::Truncate));
+        }
+        return std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(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<bool, boost::optional<std::string>, boost::optional<int>>(true, boost::none, boost::none);
-      }
-      return std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(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<bool, boost::optional<std::string>, boost::optional<int>>(true, boost::none, boost::none);
+        }
+        return std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(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++) {