#include "dnsdist-rings.hh"
+void Rings::setCapacity(size_t newCapacity, size_t numberOfShards)
+{
+ if (d_initialized) {
+ throw std::runtime_error("Rings::setCapacity() should not be called once the rings have been initialized");
+ }
+ d_capacity = newCapacity;
+ d_numberOfShards = numberOfShards;
+}
+
+void Rings::init()
+{
+ if (d_initialized) {
+ throw std::runtime_error("Rings::init() should only be called once");
+ }
+ d_initialized = true;
+
+ if (d_numberOfShards <= 1) {
+ d_nbLockTries = 0;
+ }
+
+ d_shards.resize(d_numberOfShards);
+
+ /* resize all the rings */
+ for (auto& shard : d_shards) {
+ shard = std::make_unique<Shard>();
+ shard->queryRing.lock()->set_capacity(d_capacity / d_numberOfShards);
+ shard->respRing.lock()->set_capacity(d_capacity / d_numberOfShards);
+ }
+
+ /* we just recreated the shards so they are now empty */
+ d_nbQueryEntries = 0;
+ d_nbResponseEntries = 0;
+}
+
+void Rings::setNumberOfLockRetries(size_t retries)
+{
+ if (d_numberOfShards <= 1) {
+ d_nbLockTries = 0;
+ } else {
+ d_nbLockTries = retries;
+ }
+}
+
size_t Rings::numDistinctRequestors()
{
std::set<ComboAddress, ComboAddress::addressOnlyLessThan> s;
LockGuarded<boost::circular_buffer<Response>> respRing{boost::circular_buffer<Response>()};
};
- Rings(size_t capacity=10000, size_t numberOfShards=10, size_t nbLockTries=5, bool keepLockingStats=false): d_blockingQueryInserts(0), d_blockingResponseInserts(0), d_deferredQueryInserts(0), d_deferredResponseInserts(0), d_nbQueryEntries(0), d_nbResponseEntries(0), d_currentShardId(0), d_numberOfShards(numberOfShards), d_nbLockTries(nbLockTries), d_keepLockingStats(keepLockingStats)
+ Rings(size_t capacity=10000, size_t numberOfShards=10, size_t nbLockTries=5, bool keepLockingStats=false): d_blockingQueryInserts(0), d_blockingResponseInserts(0), d_deferredQueryInserts(0), d_deferredResponseInserts(0), d_nbQueryEntries(0), d_nbResponseEntries(0), d_currentShardId(0), d_capacity(capacity), d_numberOfShards(numberOfShards), d_nbLockTries(nbLockTries), d_keepLockingStats(keepLockingStats)
{
- setCapacity(capacity, numberOfShards);
}
std::unordered_map<int, vector<boost::variant<string,double> > > getTopBandwidth(unsigned int numentries);
size_t numDistinctRequestors();
- /* This function should only be called at configuration time before any query or response has been inserted */
- void setCapacity(size_t newCapacity, size_t numberOfShards)
- {
- if (numberOfShards <= 1) {
- d_nbLockTries = 0;
- }
-
- d_shards.resize(numberOfShards);
- d_numberOfShards = numberOfShards;
+ /* this function should not be called after init() has been called */
+ void setCapacity(size_t newCapacity, size_t numberOfShards);
- /* resize all the rings */
- for (auto& shard : d_shards) {
- shard = std::make_unique<Shard>();
- shard->queryRing.lock()->set_capacity(newCapacity / numberOfShards);
- shard->respRing.lock()->set_capacity(newCapacity / numberOfShards);
- }
-
- /* we just recreated the shards so they are now empty */
- d_nbQueryEntries = 0;
- d_nbResponseEntries = 0;
- }
+ /* This function should only be called at configuration time before any query or response has been inserted */
+ void init();
- void setNumberOfLockRetries(size_t retries)
- {
- if (d_numberOfShards <= 1) {
- d_nbLockTries = 0;
- } else {
- d_nbLockTries = retries;
- }
- }
+ void setNumberOfLockRetries(size_t retries);
size_t getNumberOfShards() const
{
d_deferredResponseInserts.store(0);
}
+ /* this should be called in the unit tests, and never at runtime */
+ void reset()
+ {
+ clear();
+ d_initialized = false;
+ }
+
/* load the content of the ring buffer from a file in the format emitted by grepq(),
only useful for debugging purposes */
size_t loadFromFile(const std::string& filepath, const struct timespec& now);
std::atomic<size_t> d_nbQueryEntries;
std::atomic<size_t> d_nbResponseEntries;
std::atomic<size_t> d_currentShardId;
+ std::atomic<bool> d_initialized{false};
+ size_t d_capacity;
size_t d_numberOfShards;
size_t d_nbLockTries = 5;
bool d_keepLockingStats{false};
g_configurationDone = true;
+ g_rings.init();
+
for(auto& frontend : g_frontends) {
setUpLocalBind(frontend);
const auto action = DNSAction::Action::Drop;
const std::string reason = "Exceeded query rate";
+ g_rings.reset();
+ g_rings.init();
+
DynBlockRulesGroup dbrg;
dbrg.setQuiet(true);
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);
/* 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);
NetmaskTree<DynBlock, AddressAndPortRange> emptyNMG;
/* 100k entries, one shard */
+ g_rings.reset();
g_rings.setCapacity(1000000, 1);
+ g_rings.init();
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);
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);
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);
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);
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);
/* 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 */
const auto action = DNSAction::Action::Drop;
const std::string reason = "Exceeded query rate";
+ g_rings.reset();
/* 10M entries, only one shard */
g_rings.setCapacity(10000000, 1);
+ g_rings.init();
{
DynBlockRulesGroup dbrg;
static void test_ring(size_t maxEntries, size_t numberOfShards, size_t nbLockTries)
{
Rings rings(maxEntries, numberOfShards, nbLockTries);
+ rings.init();
size_t entriesPerShard = maxEntries / numberOfShards;
BOOST_CHECK_EQUAL(rings.getNumberOfShards(), numberOfShards);
dnsdist::Protocol outgoingProtocol = dnsdist::Protocol::DoUDP;
Rings rings(numberOfEntries, numberOfShards, lockAttempts, true);
+ rings.init();
#if defined(DNSDIST_RINGS_WITH_MACADDRESS)
Rings::Query query({requestor, qname, now, dh, size, qtype, protocol, "", false});
#else