From: Remi Gacogne Date: Mon, 18 Jan 2021 10:19:40 +0000 (+0100) Subject: dnsdist: Add a test for "Dynamic Block RCode rules messing up the queries count" X-Git-Tag: dnsdist-1.6.0-alpha1~26^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0ed4bb710e1e1a960692fcadd3caaebeaecc41f9;p=thirdparty%2Fpdns.git dnsdist: Add a test for "Dynamic Block RCode rules messing up the queries count" --- diff --git a/pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc b/pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc index 7bbdd02c3e..e817e1916e 100644 --- a/pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc +++ b/pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc @@ -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 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 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);