diff options
Diffstat (limited to 'test-dnsdistdynblocks_hh.cc')
-rw-r--r-- | test-dnsdistdynblocks_hh.cc | 355 |
1 files changed, 236 insertions, 119 deletions
diff --git a/test-dnsdistdynblocks_hh.cc b/test-dnsdistdynblocks_hh.cc index dda6ff8..fbd24dd 100644 --- a/test-dnsdistdynblocks_hh.cc +++ b/test-dnsdistdynblocks_hh.cc @@ -1,11 +1,15 @@ +#ifndef BOOST_TEST_DYN_LINK #define BOOST_TEST_DYN_LINK +#endif + #define BOOST_TEST_NO_MAIN #include <boost/test/unit_test.hpp> #include "dnsdist.hh" #include "dnsdist-dynblocks.hh" +#include "dnsdist-metrics.hh" #include "dnsdist-rings.hh" Rings g_rings; @@ -15,9 +19,22 @@ shared_ptr<BPFFilter> g_defaultBPFFilter{nullptr}; BOOST_AUTO_TEST_SUITE(dnsdistdynblocks_hh) -BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate) { - dnsheader dh; - memset(&dh, 0, sizeof(dh)); +struct TestFixture +{ + TestFixture() + { + g_rings.reset(); + g_rings.init(); + } + ~TestFixture() + { + g_rings.reset(); + } +}; + +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_QueryRate, TestFixture) { + dnsheader dnsHeader{}; + memset(&dnsHeader, 0, sizeof(dnsHeader)); DNSName qname("rings.powerdns.com."); ComboAddress requestor1("192.0.2.1"); ComboAddress requestor2("192.0.2.2"); @@ -36,9 +53,6 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate) { const auto action = DNSAction::Action::Drop; const std::string reason = "Exceeded query rate"; - g_rings.reset(); - g_rings.init(); - DynBlockRulesGroup dbrg; dbrg.setQuiet(true); @@ -54,10 +68,10 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate) { g_dynblockNMG.setState(emptyNMG); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor1, qname, qtype, size, dnsHeader, protocol); /* 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, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), numberOfQueries); BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -76,8 +90,8 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate) { g_dynblockNMG.setState(emptyNMG); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, qtype, size, dh, protocol); - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertQuery(now, requestor1, qname, qtype, size, dnsHeader, protocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -108,8 +122,8 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate) { for (size_t idx = 0; idx < numberOfQueries; idx++) { struct timespec when = now; when.tv_sec -= (9 - timeIdx); - g_rings.insertQuery(when, requestor1, qname, qtype, size, dh, protocol); - g_rings.insertResponse(when, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertQuery(when, requestor1, qname, qtype, size, dnsHeader, protocol); + g_rings.insertResponse(when, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries * numberOfSeconds); @@ -154,11 +168,11 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate) { } } -BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_RangeV6) { +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_QueryRate_RangeV6, TestFixture) { /* Check that we correctly group IPv6 addresses from the same /64 subnet into the same dynamic block entry, if instructed to do so */ - dnsheader dh; - memset(&dh, 0, sizeof(dh)); + dnsheader dnsHeader{}; + memset(&dnsHeader, 0, sizeof(dnsHeader)); DNSName qname("rings.powerdns.com."); ComboAddress requestor1("2001:db8::1"); ComboAddress backend("2001:0db8:ffff:ffff:ffff:ffff:ffff:ffff"); @@ -180,9 +194,6 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_RangeV6) { dbrg.setQuiet(true); dbrg.setMasks(32, 64, 0); - g_rings.reset(); - g_rings.init(); - /* block above 50 qps for numberOfSeconds seconds, no warning */ dbrg.setQueryRate(50, 0, numberOfSeconds, reason, blockDuration, action); @@ -195,10 +206,10 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_RangeV6) { g_dynblockNMG.setState(emptyNMG); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor1, qname, qtype, size, dnsHeader, protocol); /* 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, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), numberOfQueries); BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -218,8 +229,8 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_RangeV6) { for (size_t idx = 0; idx < numberOfQueries; idx++) { ComboAddress requestor("2001:db8::" + std::to_string(idx)); - g_rings.insertQuery(now, requestor, qname, qtype, size, dh, protocol); - g_rings.insertResponse(now, requestor, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertQuery(now, requestor, qname, qtype, size, dnsHeader, protocol); + g_rings.insertResponse(now, requestor, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -257,10 +268,10 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_RangeV6) { } } -BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_V4Ports) { +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_QueryRate_V4Ports, TestFixture) { /* Check that we correctly split IPv4 addresses based on port ranges, when instructed to do so */ - dnsheader dh; - memset(&dh, 0, sizeof(dh)); + dnsheader dnsHeader{}; + memset(&dnsHeader, 0, sizeof(dnsHeader)); DNSName qname("rings.powerdns.com."); ComboAddress requestor1("192.0.2.1:42"); ComboAddress backend("192.0.2.254"); @@ -283,9 +294,6 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_V4Ports) { /* split v4 by ports using a /2 (0 - 16383, 16384 - 32767, 32768 - 49151, 49152 - 65535) */ dbrg.setMasks(32, 128, 2); - g_rings.reset(); - g_rings.init(); - /* block above 50 qps for numberOfSeconds seconds, no warning */ dbrg.setQueryRate(50, 0, numberOfSeconds, reason, blockDuration, action); @@ -298,10 +306,10 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_V4Ports) { g_dynblockNMG.setState(emptyNMG); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor1, qname, qtype, size, dnsHeader, protocol); /* 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, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), numberOfQueries); BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -321,8 +329,8 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_V4Ports) { for (size_t idx = 0; idx < numberOfQueries; idx++) { ComboAddress requestor("192.0.2.1:" + std::to_string(idx)); - g_rings.insertQuery(now, requestor, qname, qtype, size, dh, protocol); - g_rings.insertResponse(now, requestor, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertQuery(now, requestor, qname, qtype, size, dnsHeader, protocol); + g_rings.insertResponse(now, requestor, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -370,8 +378,8 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_V4Ports) { for (size_t idx = 0; idx < numberOfQueries; idx++) { ComboAddress requestor("192.0.2.1:" + std::to_string(idx)); - g_rings.insertQuery(now, requestor, qname, qtype, size, dh, protocol); - g_rings.insertResponse(now, requestor, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertQuery(now, requestor, qname, qtype, size, dnsHeader, protocol); + g_rings.insertResponse(now, requestor, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -391,11 +399,11 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_V4Ports) { } } -BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_responses) { +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_QueryRate_responses, TestFixture) { /* 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)); + dnsheader dnsHeader{}; + memset(&dnsHeader, 0, sizeof(dnsHeader)); DNSName qname("rings.powerdns.com."); ComboAddress requestor1("192.0.2.1"); ComboAddress requestor2("192.0.2.2"); @@ -438,10 +446,10 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_responses) { 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, protocol); + g_rings.insertQuery(when, requestor1, qname, qtype, size, dnsHeader, protocol); /* 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, outgoingProtocol); + g_rings.insertResponse(when, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), numberOfQueries * 100); @@ -453,9 +461,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_responses) { } } -BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QTypeRate) { - dnsheader dh; - memset(&dh, 0, sizeof(dh)); +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_QTypeRate, TestFixture) { + dnsheader dnsHeader{}; + memset(&dnsHeader, 0, sizeof(dnsHeader)); DNSName qname("rings.powerdns.com."); ComboAddress requestor1("192.0.2.1"); ComboAddress requestor2("192.0.2.2"); @@ -473,8 +481,6 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QTypeRate) { DynBlockRulesGroup dbrg; dbrg.setQuiet(true); - g_rings.reset(); - g_rings.init(); /* block above 50 qps for numberOfSeconds seconds, no warning */ dbrg.setQTypeRate(QType::AAAA, 50, 0, numberOfSeconds, reason, blockDuration, action); @@ -488,7 +494,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QTypeRate) { g_dynblockNMG.setState(emptyNMG); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor1, qname, qtype, size, dnsHeader, protocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -506,7 +512,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QTypeRate) { g_dynblockNMG.setState(emptyNMG); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, QType::A, size, dh, protocol); + g_rings.insertQuery(now, requestor1, qname, QType::A, size, dnsHeader, protocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -524,7 +530,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QTypeRate) { g_dynblockNMG.setState(emptyNMG); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor1, qname, qtype, size, dnsHeader, protocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -543,9 +549,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QTypeRate) { } -BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRate) { - dnsheader dh; - memset(&dh, 0, sizeof(dh)); +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_RCodeRate, TestFixture) { + dnsheader dnsHeader{}; + memset(&dnsHeader, 0, sizeof(dnsHeader)); DNSName qname("rings.powerdns.com."); ComboAddress requestor1("192.0.2.1"); ComboAddress requestor2("192.0.2.2"); @@ -578,9 +584,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRate) { BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); g_dynblockNMG.setState(emptyNMG); - dh.rcode = rcode; + dnsHeader.rcode = rcode; for (size_t idx = 0; idx < numberOfResponses; idx++) { - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), numberOfResponses); @@ -596,9 +602,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRate) { BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); g_dynblockNMG.setState(emptyNMG); - dh.rcode = RCode::FormErr; + dnsHeader.rcode = RCode::FormErr; for (size_t idx = 0; idx < numberOfResponses; idx++) { - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), numberOfResponses); @@ -615,9 +621,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRate) { BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); g_dynblockNMG.setState(emptyNMG); - dh.rcode = rcode; + dnsHeader.rcode = rcode; for (size_t idx = 0; idx < numberOfResponses; idx++) { - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), numberOfResponses); @@ -636,9 +642,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRate) { } -BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRatio) { - dnsheader dh; - memset(&dh, 0, sizeof(dh)); +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_RCodeRatio, TestFixture) { + dnsheader dnsHeader{}; + memset(&dnsHeader, 0, sizeof(dnsHeader)); DNSName qname("rings.powerdns.com."); ComboAddress requestor1("192.0.2.1"); ComboAddress requestor2("192.0.2.2"); @@ -660,9 +666,6 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRatio) { DynBlockRulesGroup dbrg; dbrg.setQuiet(true); - g_rings.reset(); - g_rings.init(); - /* block above 0.2 ServFail/Total ratio over numberOfSeconds seconds, no warning, minimum number of queries should be at least 51 */ dbrg.setRCodeRatio(rcode, 0.2, 0, numberOfSeconds, reason, blockDuration, action, 51); @@ -673,13 +676,13 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRatio) { BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); g_dynblockNMG.setState(emptyNMG); - dh.rcode = rcode; + dnsHeader.rcode = rcode; for (size_t idx = 0; idx < 20; idx++) { - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } - dh.rcode = RCode::NoError; + dnsHeader.rcode = RCode::NoError; for (size_t idx = 0; idx < 80; idx++) { - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 100U); @@ -694,9 +697,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRatio) { BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); g_dynblockNMG.setState(emptyNMG); - dh.rcode = RCode::FormErr; + dnsHeader.rcode = RCode::FormErr; for (size_t idx = 0; idx < 50; idx++) { - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 50U); @@ -712,13 +715,13 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRatio) { BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); g_dynblockNMG.setState(emptyNMG); - dh.rcode = rcode; + dnsHeader.rcode = rcode; for (size_t idx = 0; idx < 21; idx++) { - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } - dh.rcode = RCode::NoError; + dnsHeader.rcode = RCode::NoError; for (size_t idx = 0; idx < 79; idx++) { - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 100U); @@ -742,13 +745,13 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRatio) { BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); g_dynblockNMG.setState(emptyNMG); - dh.rcode = rcode; + dnsHeader.rcode = rcode; for (size_t idx = 0; idx < 11; idx++) { - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } - dh.rcode = RCode::NoError; + dnsHeader.rcode = RCode::NoError; for (size_t idx = 0; idx < 39; idx++) { - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 50U); @@ -758,9 +761,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_RCodeRatio) { } } -BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_ResponseByteRate) { - dnsheader dh; - memset(&dh, 0, sizeof(dh)); +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_ResponseByteRate, TestFixture) { + dnsheader dnsHeader{}; + memset(&dnsHeader, 0, sizeof(dnsHeader)); DNSName qname("rings.powerdns.com."); ComboAddress requestor1("192.0.2.1"); ComboAddress requestor2("192.0.2.2"); @@ -782,9 +785,6 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_ResponseByteRate) { DynBlockRulesGroup dbrg; dbrg.setQuiet(true); - g_rings.reset(); - g_rings.init(); - /* block above 10kB/s for numberOfSeconds seconds, no warning */ dbrg.setResponseByteRate(10000, 0, numberOfSeconds, reason, blockDuration, action); @@ -796,9 +796,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_ResponseByteRate) { BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); g_dynblockNMG.setState(emptyNMG); - dh.rcode = rcode; + dnsHeader.rcode = rcode; for (size_t idx = 0; idx < numberOfResponses; idx++) { - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), numberOfResponses); @@ -814,9 +814,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_ResponseByteRate) { BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); g_dynblockNMG.setState(emptyNMG); - dh.rcode = rcode; + dnsHeader.rcode = rcode; for (size_t idx = 0; idx < numberOfResponses; idx++) { - g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dh, backend, outgoingProtocol); + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), numberOfResponses); @@ -832,12 +832,133 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_ResponseByteRate) { BOOST_CHECK_EQUAL(block.blocks, 0U); BOOST_CHECK_EQUAL(block.warning, false); } +} + +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_CacheMissRatio, TestFixture) { + 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"); + ComboAddress cacheHit; + 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<DynBlock, AddressAndPortRange> emptyNMG; + + time_t numberOfSeconds = 10; + unsigned int blockDuration = 60; + const auto action = DNSAction::Action::Drop; + const std::string reason = "Exceeded cache-miss ratio"; + + DynBlockRulesGroup dbrg; + dbrg.setQuiet(true); + + /* block above 0.5 Cache-Miss/Total ratio over numberOfSeconds seconds, no warning, minimum number of queries should be at least 51, global cache hit at least 80% */ + dnsdist::metrics::g_stats.cacheHits.store(80); + dnsdist::metrics::g_stats.cacheMisses.store(20); + dbrg.setCacheMissRatio(0.5, 0, numberOfSeconds, reason, blockDuration, action, 51, 0.8); + + { + /* insert 50 cache misses and 50 cache hits from a given client in the last 10s + this should not trigger the rule */ + g_rings.clear(); + BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); + g_dynblockNMG.setState(emptyNMG); + + for (size_t idx = 0; idx < 20; idx++) { + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); + } + for (size_t idx = 0; idx < 80; idx++) { + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, cacheHit, outgoingProtocol); + } + BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 100U); + + dbrg.apply(now); + BOOST_CHECK_EQUAL(g_dynblockNMG.getLocal()->size(), 0U); + BOOST_CHECK(g_dynblockNMG.getLocal()->lookup(requestor1) == nullptr); + } + + { + /* insert 51 cache misses and 49 hits from a given client in the last 10s + this should trigger the rule this time */ + g_rings.clear(); + BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); + g_dynblockNMG.setState(emptyNMG); + + for (size_t idx = 0; idx < 51; idx++) { + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); + } + for (size_t idx = 0; idx < 49; idx++) { + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, cacheHit, outgoingProtocol); + } + BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 100U); + + dbrg.apply(now); + BOOST_CHECK_EQUAL(g_dynblockNMG.getLocal()->size(), 1U); + 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); + BOOST_CHECK_EQUAL(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); + } + { + /* insert 40 misses and 10 hits from a given client in the last 10s + this should NOT trigger the rule since we don't have more than 50 queries */ + g_rings.clear(); + BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); + g_dynblockNMG.setState(emptyNMG); + + for (size_t idx = 0; idx < 40; idx++) { + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); + } + for (size_t idx = 0; idx < 10; idx++) { + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, cacheHit, outgoingProtocol); + } + BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 50U); + + dbrg.apply(now); + BOOST_CHECK_EQUAL(g_dynblockNMG.getLocal()->size(), 0U); + BOOST_CHECK(g_dynblockNMG.getLocal()->lookup(requestor1) == nullptr); + } + + /* the global cache-hit rate is too low, should not trigger */ + dnsdist::metrics::g_stats.cacheHits.store(60); + dnsdist::metrics::g_stats.cacheMisses.store(40); + { + /* insert 51 cache misses and 49 hits from a given client in the last 10s */ + g_rings.clear(); + BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 0U); + g_dynblockNMG.setState(emptyNMG); + + for (size_t idx = 0; idx < 51; idx++) { + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, backend, outgoingProtocol); + } + for (size_t idx = 0; idx < 49; idx++) { + g_rings.insertResponse(now, requestor1, qname, qtype, responseTime, size, dnsHeader, cacheHit, outgoingProtocol); + } + BOOST_CHECK_EQUAL(g_rings.getNumberOfResponseEntries(), 100U); + + dbrg.apply(now); + BOOST_CHECK_EQUAL(g_dynblockNMG.getLocal()->size(), 0U); + BOOST_REQUIRE(g_dynblockNMG.getLocal()->lookup(requestor1) == nullptr); + } } -BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_Warning) { - dnsheader dh; - memset(&dh, 0, sizeof(dh)); +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_Warning, TestFixture) { + dnsheader dnsHeader{}; + memset(&dnsHeader, 0, sizeof(dnsHeader)); DNSName qname("rings.powerdns.com."); ComboAddress requestor1("192.0.2.1"); ComboAddress requestor2("192.0.2.2"); @@ -856,9 +977,6 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_Warning) { DynBlockRulesGroup dbrg; dbrg.setQuiet(true); - g_rings.reset(); - g_rings.init(); - /* warn above 20 qps for numberOfSeconds seconds, block above 50 qps */ dbrg.setQueryRate(50, 20, numberOfSeconds, reason, blockDuration, action); @@ -871,7 +989,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_Warning) { g_dynblockNMG.setState(emptyNMG); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor1, qname, qtype, size, dnsHeader, protocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -889,7 +1007,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_Warning) { g_dynblockNMG.setState(emptyNMG); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor1, qname, qtype, size, dnsHeader, protocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -917,7 +1035,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_Warning) { BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), 0U); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor1, qname, qtype, size, dnsHeader, protocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -946,7 +1064,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_Warning) { BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), 0U); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor1, qname, qtype, size, dnsHeader, protocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -977,7 +1095,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_Warning) { g_dynblockNMG.setState(emptyNMG); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor1, qname, qtype, size, dnsHeader, protocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries); @@ -998,9 +1116,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_Warning) { } } -BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_Ranges) { - dnsheader dh; - memset(&dh, 0, sizeof(dh)); +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesGroup_Ranges, TestFixture) { + dnsheader dnsHeader{}; + memset(&dnsHeader, 0, sizeof(dnsHeader)); DNSName qname("rings.powerdns.com."); ComboAddress requestor1("192.0.2.1"); ComboAddress requestor2("192.0.2.42"); @@ -1026,9 +1144,6 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_Ranges) { /* block above 50 qps for numberOfSeconds seconds, no warning */ dbrg.setQueryRate(50, 0, numberOfSeconds, reason, blockDuration, action); - g_rings.reset(); - g_rings.init(); - { /* insert just above 50 qps from the two clients in the last 10s this should trigger the rule for the first one but not the second one */ @@ -1038,8 +1153,8 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_Ranges) { g_dynblockNMG.setState(emptyNMG); for (size_t idx = 0; idx < numberOfQueries; idx++) { - g_rings.insertQuery(now, requestor1, qname, qtype, size, dh, protocol); - g_rings.insertQuery(now, requestor2, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor1, qname, qtype, size, dnsHeader, protocol); + g_rings.insertQuery(now, requestor2, qname, qtype, size, dnsHeader, protocol); } BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries * 2); @@ -1058,9 +1173,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_Ranges) { } -BOOST_AUTO_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN) { - dnsheader dh; - memset(&dh, 0, sizeof(dh)); +BOOST_FIXTURE_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN, TestFixture) { + dnsheader dnsHeader{}; + memset(&dnsHeader, 0, sizeof(dnsHeader)); DNSName qname("rings.powerdns.com."); uint16_t qtype = QType::AAAA; uint16_t size = 42; @@ -1094,7 +1209,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN) { */ for (size_t idx = 0; idx < 256; idx++) { const ComboAddress requestor("192.0.2." + std::to_string(idx)); - g_rings.insertQuery(now, requestor, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor, qname, qtype, size, dnsHeader, protocol); } /* we apply the rules, all clients should be blocked */ @@ -1142,15 +1257,15 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN) { dbrg.setSuffixMatchRule(numberOfSeconds, reason, blockDuration, action, [](const StatNode& node, const StatNode::Stat& self, const StatNode::Stat& children) { if (self.queries > 0) { - return std::tuple<bool, boost::optional<std::string>>(true, boost::none); + return std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(true, boost::none, boost::none); } - return std::tuple<bool, boost::optional<std::string>>(false, boost::none); + return std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(false, boost::none, boost::none); }); /* insert one fake response for 255 DNS names */ const ComboAddress requestor("192.0.2.1"); for (size_t idx = 0; idx < 256; idx++) { - g_rings.insertResponse(now, requestor, DNSName(std::to_string(idx)) + qname, qtype, 1000 /*usec*/, size, dh, requestor /* backend, technically, but we don't care */, outgoingProtocol); + g_rings.insertResponse(now, requestor, DNSName(std::to_string(idx)) + qname, qtype, 1000 /*usec*/, size, dnsHeader, requestor /* backend, technically, but we don't care */, outgoingProtocol); } /* we apply the rules, all suffixes should be blocked */ @@ -1160,6 +1275,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN) { const DNSName name(DNSName(std::to_string(idx)) + qname); const auto* block = g_dynblockSMT.getLocal()->lookup(name); BOOST_REQUIRE(block != nullptr); + BOOST_REQUIRE(block->action == action); /* simulate that: - 1.rings.powerdns.com. got 1 query ... @@ -1198,15 +1314,15 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN) { dbrg.setSuffixMatchRule(numberOfSeconds, reason, blockDuration, action, [](const StatNode& node, const StatNode::Stat& self, const StatNode::Stat& children) { if (self.queries > 0) { - return std::tuple<bool, boost::optional<std::string>>(true, "blocked for a different reason"); + return std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(true, "blocked for a different reason", static_cast<int>(DNSAction::Action::Truncate)); } - return std::tuple<bool, boost::optional<std::string>>(false, boost::none); + return std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(false, boost::none, boost::none); }); /* insert one fake response for 255 DNS names */ const ComboAddress requestor("192.0.2.1"); for (size_t idx = 0; idx < 256; idx++) { - g_rings.insertResponse(now, requestor, DNSName(std::to_string(idx)) + qname, qtype, 1000 /*usec*/, size, dh, requestor /* backend, technically, but we don't care */, dnsdist::Protocol::DoUDP); + g_rings.insertResponse(now, requestor, DNSName(std::to_string(idx)) + qname, qtype, 1000 /*usec*/, size, dnsHeader, requestor /* backend, technically, but we don't care */, dnsdist::Protocol::DoUDP); } /* we apply the rules, all suffixes should be blocked */ @@ -1216,6 +1332,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN) { const DNSName name(DNSName(std::to_string(idx)) + qname); const auto* block = g_dynblockSMT.getLocal()->lookup(name); BOOST_REQUIRE(block != nullptr); + BOOST_REQUIRE(block->action == DNSAction::Action::Truncate); /* simulate that: - 1.rings.powerdns.com. got 1 query ... @@ -1255,9 +1372,9 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN) { dbrg.setSuffixMatchRule(numberOfSeconds, reason, blockDuration, action, [](const StatNode& node, const StatNode::Stat& self, const StatNode::Stat& children) { if (self.queries > 0) { - return std::tuple<bool, boost::optional<std::string>>(true, boost::none); + return std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(true, boost::none, boost::none); } - return std::tuple<bool, boost::optional<std::string>>(false, boost::none); + return std::tuple<bool, boost::optional<std::string>, boost::optional<int>>(false, boost::none, boost::none); }); bool done = false; @@ -1266,7 +1383,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN) { for (size_t idxC = 0; !done && idxC < 256; idxC++) { for (size_t idxD = 0; !done && idxD < 256; idxD++) { const DNSName victim(std::to_string(idxB) + "." + std::to_string(idxC) + "." + std::to_string(idxD) + qname.toString()); - g_rings.insertResponse(now, requestor, victim, qtype, 1000 /*usec*/, size, dh, requestor /* backend, technically, but we don't care */, outgoingProtocol); + g_rings.insertResponse(now, requestor, victim, qtype, 1000 /*usec*/, size, dnsHeader, requestor /* backend, technically, but we don't care */, outgoingProtocol); if (g_rings.getNumberOfQueryEntries() == 1000000) { done = true; break; @@ -1311,7 +1428,7 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesMetricsCache_GetTopN) { for (size_t idxC = 0; !done && idxC < 256; idxC++) { for (size_t idxD = 0; !done && idxD < 256; idxD++) { const ComboAddress requestor("192." + std::to_string(idxB) + "." + std::to_string(idxC) + "." + std::to_string(idxD)); - g_rings.insertQuery(now, requestor, qname, qtype, size, dh, protocol); + g_rings.insertQuery(now, requestor, qname, qtype, size, dnsHeader, protocol); if (g_rings.getNumberOfQueryEntries() == 1000000) { done = true; break; |