]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add a test for "Dynamic Block RCode rules messing up the queries count"
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 18 Jan 2021 10:19:40 +0000 (11:19 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 18 Jan 2021 10:19:40 +0000 (11:19 +0100)
pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc

index 7bbdd02c3e9a201455c72df20a1925072981a5f0..e817e1916e90fe0c7d43fb41d067da54ca3039fc 100644 (file)
@@ -17,11 +17,14 @@ BOOST_AUTO_TEST_SUITE(dnsdistdynblocks_hh)
 
 BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate) {
   dnsheader dh;
+  memset(&dh, 0, sizeof(dh));
   DNSName qname("rings.powerdns.com.");
   ComboAddress requestor1("192.0.2.1");
   ComboAddress requestor2("192.0.2.2");
+  ComboAddress backend("192.0.2.42");
   uint16_t qtype = QType::AAAA;
   uint16_t size = 42;
+  unsigned int responseTime = 0;
   struct timespec now;
   gettime(&now);
   NetmaskTree<DynBlock> emptyNMG;
@@ -47,7 +50,11 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate) {
 
     for (size_t idx = 0; idx < numberOfQueries; idx++) {
       g_rings.insertQuery(now, requestor1, qname, qtype, size, dh);
+      /* we do not care about the response during that test, but we want to make sure
+         these do not interfere with the computation */
+      g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend);
     }
+    BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), numberOfQueries);
     BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries);
 
     dbrg.apply(now);
@@ -65,6 +72,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate) {
 
     for (size_t idx = 0; idx < numberOfQueries; idx++) {
       g_rings.insertQuery(now, requestor1, qname, qtype, size, dh);
+      g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend);
     }
     BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries);
 
@@ -96,6 +104,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate) {
         struct timespec when = now;
         when.tv_sec -= (9 - timeIdx);
         g_rings.insertQuery(when, requestor1, qname, qtype, size, dh);
+        g_rings.insertResponse(when, requestor1, qname, qtype, responseTime, size, dh, backend);
       }
     }
     BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries * numberOfSeconds);
@@ -137,7 +146,68 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate) {
     later.tv_sec += 6;
     dbrg.apply(later);
     BOOST_CHECK_EQUAL(g_dynblockNMG.getLocal()->size(), 0U);
+  }
 }
+
+BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_responses) {
+  /* check that the responses are not accounted as queries when a
+     rcode rate rule is defined (sounds very specific but actually happened) */
+  dnsheader dh;
+  memset(&dh, 0, sizeof(dh));
+  DNSName qname("rings.powerdns.com.");
+  ComboAddress requestor1("192.0.2.1");
+  ComboAddress requestor2("192.0.2.2");
+  ComboAddress backend("192.0.2.42");
+  uint16_t qtype = QType::AAAA;
+  uint16_t size = 42;
+  unsigned int responseTime = 0;
+  struct timespec now;
+  gettime(&now);
+  NetmaskTree<DynBlock> emptyNMG;
+
+  /* 100k entries, one shard */
+  g_rings.setCapacity(1000000, 1);
+
+  size_t numberOfSeconds = 10;
+  size_t blockDuration = 60;
+  const auto action = DNSAction::Action::Drop;
+  const std::string reason = "Exceeded query rate";
+
+  /* 100k entries, one shard */
+  g_rings.setCapacity(1000000, 1);
+
+  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);
+
+  {
+    /* insert 45 qps (including responses) from a given client for the last 100s
+       this should not trigger the rule */
+    size_t numberOfQueries = 45;
+    g_rings.clear();
+    BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), 0U);
+    g_dynblockNMG.setState(emptyNMG);
+
+    for (size_t timeIdx = 0; timeIdx < 100; timeIdx++) {
+      struct timespec when = now;
+      when.tv_sec -= (99 - timeIdx);
+      for (size_t idx = 0; idx < numberOfQueries; idx++) {
+        g_rings.insertQuery(when, requestor1, qname, qtype, size, dh);
+        /* we do not care about the response during that test, but we want to make sure
+           these do not interfere with the computation */
+        g_rings.insertResponse(when, requestor1, qname, qtype, responseTime, size, dh, backend);
+      }
+    }
+    BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), numberOfQueries * 100);
+    BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries * 100);
+
+    dbrg.apply(now);
+    BOOST_CHECK_EQUAL(g_dynblockNMG.getLocal()->size(), 0U);
+    BOOST_CHECK(g_dynblockNMG.getLocal()->lookup(requestor1) == nullptr);
+  }
 }
 
 BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QTypeRate) {
@@ -400,7 +470,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRatio) {
 
     dbrg.apply(now);
     BOOST_CHECK_EQUAL(g_dynblockNMG.getLocal()->size(), 1U);
-    BOOST_CHECK(g_dynblockNMG.getLocal()->lookup(requestor1) != nullptr);
+    BOOST_REQUIRE(g_dynblockNMG.getLocal()->lookup(requestor1) != nullptr);
     BOOST_CHECK(g_dynblockNMG.getLocal()->lookup(requestor2) == nullptr);
     const auto& block = g_dynblockNMG.getLocal()->lookup(requestor1)->second;
     BOOST_CHECK_EQUAL(block.reason, reason);