From: Remi Gacogne Date: Fri, 23 Jan 2026 11:57:25 +0000 (+0100) Subject: dnsdist: Add a dynamic block unit test with sampling enabled X-Git-Tag: dnsdist-2.1.0-alpha1~6^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9205177caea615b87ea6fbbdf471720fc138ed98;p=thirdparty%2Fpdns.git dnsdist: Add a dynamic block unit test with sampling enabled Signed-off-by: Remi Gacogne --- diff --git a/pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc b/pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc index 42123b5ea6..a79db4422e 100644 --- a/pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc +++ b/pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc @@ -36,6 +36,26 @@ struct TestFixture } }; +static size_t s_samplingRate{10}; + +struct TestFixtureWithSampling +{ + TestFixtureWithSampling() + { + g_rings.reset(); + Rings::RingsConfiguration config { + .capacity = 10000U, + .numberOfShards = 10U, + .samplingRate = s_samplingRate, + }; + g_rings.init(config); + } + ~TestFixtureWithSampling() + { + g_rings.reset(); + } +}; + BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_QueryRate, TestFixture) { dnsheader dnsHeader{}; memset(&dnsHeader, 0, sizeof(dnsHeader)); @@ -669,6 +689,102 @@ BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_RCodeRate, TestFixture) { } +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_RCodeRate_With_Sampling, TestFixtureWithSampling) { + dnsheader dnsHeader{}; + memset(&dnsHeader, 0, sizeof(dnsHeader)); + 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; + dnsdist::Protocol outgoingProtocol = dnsdist::Protocol::DoUDP; + unsigned int responseTime = 100 * 1000; /* 100ms */ + struct timespec now; + gettime(&now); + NetmaskTree emptyNMG; + + size_t numberOfSeconds = 10; + size_t blockDuration = 60; + const auto action = DNSAction::Action::Drop; + const std::string reason = "Exceeded query rate"; + const uint16_t rcode = RCode::ServFail; + + DynBlockRulesGroup dbrg; + dbrg.setQuiet(true); + + { + /* 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 + this should not trigger the rule */ + size_t numberOfResponses = 45 * numberOfSeconds; + g_rings.clear(); + BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); + dnsdist::DynamicBlocks::clearClientAddressDynamicRules(); + + dnsHeader.rcode = rcode; + for (size_t idx = 0; idx < numberOfResponses; idx++) { + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); + } + BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), numberOfResponses / s_samplingRate); + + dbrg.apply(now); + BOOST_CHECK_EQUAL(dnsdist::DynamicBlocks::getClientAddressDynamicRules().size(), 0U); + BOOST_CHECK(dnsdist::DynamicBlocks::getClientAddressDynamicRules().lookup(requestor1) == nullptr); + } + + { + /* insert just above 50 FormErr/s from a given client in the last 10s */ + size_t numberOfResponses = 50 * numberOfSeconds + s_samplingRate; + g_rings.clear(); + BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); + dnsdist::DynamicBlocks::clearClientAddressDynamicRules(); + + dnsHeader.rcode = RCode::FormErr; + for (size_t idx = 0; idx < numberOfResponses; idx++) { + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); + } + BOOST_CHECK_GE(g_rings.getNumberOfResponseEntries(), (numberOfResponses / s_samplingRate)); + + dbrg.apply(now); + BOOST_CHECK_EQUAL(dnsdist::DynamicBlocks::getClientAddressDynamicRules().size(), 0U); + BOOST_CHECK(dnsdist::DynamicBlocks::getClientAddressDynamicRules().lookup(requestor1) == nullptr); + } + + { + /* insert just above 50 ServFail/s from a given client in the last 10s + this should trigger the rule this time */ + size_t numberOfResponses = 50 * numberOfSeconds + s_samplingRate; + g_rings.clear(); + BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); + dnsdist::DynamicBlocks::clearClientAddressDynamicRules(); + + dnsHeader.rcode = rcode; + for (size_t idx = 0; idx < numberOfResponses; idx++) { + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); + } + BOOST_CHECK_GE(g_rings.getNumberOfResponseEntries(), (numberOfResponses / s_samplingRate)); + + dbrg.apply(now); + BOOST_CHECK_EQUAL(dnsdist::DynamicBlocks::getClientAddressDynamicRules().size(), 1U); + BOOST_REQUIRE(dnsdist::DynamicBlocks::getClientAddressDynamicRules().lookup(requestor1) != nullptr); + BOOST_CHECK(dnsdist::DynamicBlocks::getClientAddressDynamicRules().lookup(requestor2) == nullptr); + const auto& block = dnsdist::DynamicBlocks::getClientAddressDynamicRules().lookup(requestor1)->second; + BOOST_CHECK_EQUAL(block.reason, reason); + BOOST_CHECK_EQUAL(static_cast(block.until.tv_sec), now.tv_sec + blockDuration); + BOOST_CHECK(block.domain.empty()); + BOOST_CHECK(block.action == action); + BOOST_CHECK_EQUAL(block.blocks, 0U); + BOOST_CHECK_EQUAL(block.warning, false); + } + +} + BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_RCodeRatio, TestFixture) { dnsheader dnsHeader{}; memset(&dnsHeader, 0, sizeof(dnsHeader));