]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add regression tests for eBPF blocks (static / dynamic)
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 1 Feb 2024 13:44:45 +0000 (14:44 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 5 Feb 2024 10:32:17 +0000 (11:32 +0100)
regression-tests.dnsdist/dnsdistDynBlockTests.py
regression-tests.dnsdist/dnsdisttests.py
regression-tests.dnsdist/test_DynBlocksEBPF.py [new file with mode: 0644]

index 67f986b585e14f8a69c98b1014bd6c16b078d0f8..27b4f3cfa98af0ddcdc6cb9eb0c280c51c347eea 100644 (file)
@@ -23,7 +23,7 @@ class DynBlocksTest(DNSDistTest):
     _dynBlockDuration = _maintenanceWaitTime + 2
     _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
 
-    def doTestDynBlockViaAPI(self, ipRange, reason, minSeconds, maxSeconds, minBlocks, maxBlocks):
+    def doTestDynBlockViaAPI(self, ipRange, reason, minSeconds, maxSeconds, minBlocks, maxBlocks, ebpf=False):
         headers = {'x-api-key': self._webServerAPIKey}
         url = 'http://127.0.0.1:' + str(self._webServerPort) + '/jsonstat?command=dynblocklist'
         r = requests.get(url, headers=headers, timeout=self._webTimeout)
@@ -35,7 +35,7 @@ class DynBlocksTest(DNSDistTest):
         self.assertIn(ipRange, content)
 
         values = content[ipRange]
-        for key in ['reason', 'seconds', 'blocks', 'action']:
+        for key in ['reason', 'seconds', 'blocks', 'action', 'ebpf']:
             self.assertIn(key, values)
 
         self.assertEqual(values['reason'], reason)
@@ -43,8 +43,9 @@ class DynBlocksTest(DNSDistTest):
         self.assertLessEqual(values['seconds'], maxSeconds)
         self.assertGreaterEqual(values['blocks'], minBlocks)
         self.assertLessEqual(values['blocks'], maxBlocks)
+        self.assertEqual(values['ebpf'], True if ebpf else False)
 
-    def doTestQRate(self, name, testViaAPI=True):
+    def doTestQRate(self, name, testViaAPI=True, ebpf=False):
         query = dns.message.make_query(name, 'A', 'IN')
         response = dns.message.make_response(query)
         rrset = dns.rrset.from_text(name,
@@ -81,7 +82,7 @@ class DynBlocksTest(DNSDistTest):
         self.assertEqual(receivedResponse, None)
 
         if testViaAPI:
-            self.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', 1, self._dynBlockDuration, (sent-allowed)+1, (sent-allowed)+1)
+            self.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', 1, self._dynBlockDuration, (sent-allowed)+1, (sent-allowed)+1, ebpf)
 
         # wait until we are not blocked anymore
         time.sleep(self._dynBlockDuration + self._dynBlockPeriod)
index d8e0cd9fff7e5368759c7fa2a083c2c7c388c1cd..ef2dc35364bdc1feeb1badd8b44fecb23426181b 100644 (file)
@@ -88,6 +88,7 @@ class DNSDistTest(AssertEqualDNSMessageMixin, unittest.TestCase):
     _answerUnexpected = True
     _checkConfigExpectedOutput = None
     _verboseMode = False
+    _sudoMode = False
     _skipListeningOnCL = False
     _alternateListeningAddr = None
     _alternateListeningPort = None
@@ -150,6 +151,10 @@ class DNSDistTest(AssertEqualDNSMessageMixin, unittest.TestCase):
 
         if cls._verboseMode:
             dnsdistcmd.append('-v')
+        if cls._sudoMode:
+            if 'LD_LIBRARY_PATH' in os.environ:
+                dnsdistcmd.insert(0, 'LD_LIBRARY_PATH=' + os.environ['LD_LIBRARY_PATH'])
+            dnsdistcmd.insert(0, 'sudo')
 
         for acl in cls._acl:
             dnsdistcmd.extend(['--acl', acl])
@@ -693,7 +698,11 @@ class DNSDistTest(AssertEqualDNSMessageMixin, unittest.TestCase):
         if useQueue:
             cls._toResponderQueue.put(response, True, timeout)
 
-        sock = cls.openTCPConnection(timeout)
+        try:
+            sock = cls.openTCPConnection(timeout)
+        except socket.timeout as e:
+            print("Timeout while opening TCP connection: %s" % (str(e)))
+            return (None, None)
 
         try:
             cls.sendTCPQueryOverConnection(sock, query, rawQuery)
diff --git a/regression-tests.dnsdist/test_DynBlocksEBPF.py b/regression-tests.dnsdist/test_DynBlocksEBPF.py
new file mode 100644 (file)
index 0000000..8260649
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+import dns
+import os
+import unittest
+from dnsdisttests import DNSDistTest
+from dnsdistDynBlockTests import DynBlocksTest
+
+class EBPFTest(object):
+    pass
+
+@unittest.skipUnless('ENABLE_SUDO_TESTS' in os.environ, "sudo is not available")
+class TestDynBlockEBPFQPS(DynBlocksTest):
+
+    _config_template = """
+    bpf = newBPFFilter({ipv4MaxItems=10, ipv6MaxItems=10, qnamesMaxItems=10})
+    setDefaultBPFFilter(bpf)
+    local dbr = dynBlockRulesGroup()
+    dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
+    function maintenance()
+        dbr:apply()
+    end
+
+    -- not going to wait 60s!
+    setDynBlocksPurgeInterval(1)
+
+    -- exercise the manual blocking methods
+    bpf:block(newCA("2001:DB8::42"))
+    bpf:blockQName(newDNSName("powerdns.com."), 255)
+    bpf:getStats()
+    bpf:unblock(newCA("2001:DB8::42"))
+    bpf:unblockQName(newDNSName("powerdns.com."), 255)
+
+    newServer{address="127.0.0.1:%s"}
+    webserver("127.0.0.1:%s")
+    setWebserverConfig({password="%s", apiKey="%s"})
+    """
+    _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+    _sudoMode = True
+
+    def testDynBlocksQRate(self):
+        """
+        Dyn Blocks: QRate
+        """
+        name = 'qrate.dynblocks.tests.powerdns.com.'
+        self.doTestQRate(name, ebpf=True)