]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Accept a NMG to fill DynBlockRulesGroup ranges 10015/head
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 26 Jan 2021 16:39:00 +0000 (17:39 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 26 Jan 2021 16:39:00 +0000 (17:39 +0100)
pdns/dnsdist-dynblocks.hh
pdns/dnsdist-lua-inspection.cc
pdns/dnsdistdist/docs/reference/config.rst
pdns/dnsdistdist/docs/reference/ebpf.rst
pdns/iputils.hh
regression-tests.dnsdist/test_DynBlocks.py

index 8f031f457a09148c1cbe24c65e6c208aa82d4c51..80a0dca6034a9899faa391feb34c0802e4a46f28 100644 (file)
@@ -276,11 +276,21 @@ public:
     d_excludedSubnets.addMask(range);
   }
 
+  void excludeRange(const NetmaskGroup& group)
+  {
+    d_excludedSubnets.addMasks(group, true);
+  }
+
   void includeRange(const Netmask& range)
   {
     d_excludedSubnets.addMask(range, false);
   }
 
+  void includeRange(const NetmaskGroup& group)
+  {
+    d_excludedSubnets.addMasks(group, false);
+  }
+
   void excludeDomain(const DNSName& domain)
   {
     d_excludedDomains.add(domain);
index 3cdc790d917b70b3765c0e06341242027fb4890a..f0915d6c82b5c6ba9a7ff06355eab6f65f8c9a13 100644 (file)
@@ -772,22 +772,28 @@ void setupLuaInspection(LuaContext& luaCtx)
         group->setQTypeRate(qtype, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
       }
     });
-  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("excludeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
+  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>, NetmaskGroup>)>("excludeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>, NetmaskGroup> ranges) {
       if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
         for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
           group->excludeRange(Netmask(range.second));
         }
       }
+      else if (ranges.type() == typeid(NetmaskGroup)) {
+        group->excludeRange(*boost::get<NetmaskGroup>(&ranges));
+      }
       else {
         group->excludeRange(Netmask(*boost::get<std::string>(&ranges)));
       }
     });
-  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("includeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
+  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>, NetmaskGroup>)>("includeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>, NetmaskGroup> ranges) {
       if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
         for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
           group->includeRange(Netmask(range.second));
         }
       }
+      else if (ranges.type() == typeid(NetmaskGroup)) {
+        group->includeRange(*boost::get<NetmaskGroup>(&ranges));
+      }
       else {
         group->includeRange(Netmask(*boost::get<std::string>(&ranges)));
       }
index 7ce970272020f71a7d7a4c3e619d55d53dbb60f0..cc23fb72d93312f1e13690e43c0663792239addc 100644 (file)
@@ -1417,17 +1417,23 @@ faster than the existing rules.
 
     .. versionadded:: 1.3.1
 
+    .. versionchanged:: 1.6.0
+      This method now accepts a :class:`NetmaskGroup` object.
+
     Exclude this range, or list of ranges, meaning that no dynamic block will ever be inserted for clients in that range. Default to empty, meaning rules are applied to all ranges. When used in combination with :meth:`DynBlockRulesGroup:includeRange`, the more specific entry wins.
 
-    :param list netmasks: A netmask, or list of netmasks, as strings, like for example "192.0.2.1/24"
+    :param list netmasks: A :class:`NetmaskGroup` object, or a netmask or list of netmasks as strings, like for example "192.0.2.1/24"
 
   .. method:: DynBlockRulesGroup:includeRange(netmasks)
 
     .. versionadded:: 1.3.1
 
+    .. versionchanged:: 1.6.0
+      This method now accepts a :class:`NetmaskGroup` object.
+
     Include this range, or list of ranges, meaning that rules will be applied to this range. When used in combination with :meth:`DynBlockRulesGroup:excludeRange`, the more specific entry wins.
 
-    :param list netmasks: A netmask, or list of netmasks, as strings, like for example "192.0.2.1/24"
+    :param list netmasks: A :class:`NetmaskGroup` object, or a netmask or list of netmasks as strings, like for example "192.0.2.1/24"
 
   .. method:: DynBlockRulesGroup:toString()
 
index 689dddd481da339a4c6322582cbdd6020c69a2e8..48782ed3eb6c8fdb64a34c77d0f9ea6c1f072b1e 100644 (file)
@@ -89,7 +89,7 @@ These are all the functions, objects and methods related to the :doc:`../advance
 
 .. class:: DynBPFFilter
 
-  Represents an dynamic eBPF filter, allowing the use of ephemeral rules to an existing eBPF filter.
+  Represents an dynamic eBPF filter, allowing the use of ephemeral rules to an existing eBPF filter. Note that since 1.6.0 the default BPF filter set via :func:`setDefaultBPFFilter` will automatically be used by a :ref:`DynBlockRulesGroup`, becoming the preferred way of dealing with ephemeral rules.
 
   .. method:: DynBPFFilter:purgeExpired()
 
index 2d9f5af444e35246192b6fa516e80449fd978166..6b36d1e19d1093bf3131d0c421d9ed9cf9ce0617 100644 (file)
@@ -1326,6 +1326,13 @@ public:
     tree.insert(nm).second=positive;
   }
 
+  void addMasks(const NetmaskGroup& group, boost::optional<bool> positive)
+  {
+    for (const auto& entry : group.tree) {
+      addMask(entry.first, positive ? *positive : entry.second);
+    }
+  }
+
   //! Delete this Netmask from the list of possible matches
   void deleteMask(const Netmask& nm)
   {
index c7abead05ab0812cafc29f18c1153ac1c5a58031..44ad7598d7597cc18b04616a162abe7b8b702abd 100644 (file)
@@ -1070,6 +1070,68 @@ class TestDynBlockGroupExcluded(DynBlocksTest):
         self.assertEquals(query, receivedQuery)
         self.assertEquals(receivedResponse, receivedResponse)
 
+class TestDynBlockGroupExcludedViaNMG(DynBlocksTest):
+
+    _dynBlockQPS = 10
+    _dynBlockPeriod = 2
+    _dynBlockDuration = 5
+    _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+    _config_template = """
+    local nmg = newNMG()
+    nmg:addMask("127.0.0.1/32")
+
+    local dbr = dynBlockRulesGroup()
+    dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
+    dbr:excludeRange(nmg)
+
+    function maintenance()
+           dbr:apply()
+    end
+
+    newServer{address="127.0.0.1:%s"}
+    """
+
+    def testExcluded(self):
+        """
+        Dyn Blocks (group) : Excluded (via NMG) from the dynamic block rules
+        """
+        name = 'excluded-nmg.group.dynblocks.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '192.0.2.1')
+        response.answer.append(rrset)
+
+        allowed = 0
+        sent = 0
+        for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1):
+            (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+            sent = sent + 1
+            if receivedQuery:
+                receivedQuery.id = query.id
+                self.assertEquals(query, receivedQuery)
+                self.assertEquals(response, receivedResponse)
+                allowed = allowed + 1
+            else:
+                # the query has not reached the responder,
+                # let's clear the response queue
+                self.clearToResponderQueue()
+
+        # we should not have been blocked
+        self.assertEqual(allowed, sent)
+
+        # wait for the maintenance function to run
+        time.sleep(2)
+
+        # we should still not be blocked
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        receivedQuery.id = query.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(receivedResponse, receivedResponse)
+
 class TestDynBlockGroupNoOp(DynBlocksTest):
 
     _dynBlockQPS = 10