#include "dnsdist-rings.hh"
#include "statnode.hh"
-extern "C" {
+extern "C"
+{
#include "dnsdist-lua-inspection-ffi.h"
}
// dnsdist_ffi_stat_node_t is a lightuserdata
-template<>
-struct LuaContext::Pusher<dnsdist_ffi_stat_node_t*> {
- static const int minSize = 1;
- static const int maxSize = 1;
-
- static PushedObject push(lua_State* state, dnsdist_ffi_stat_node_t* ptr) noexcept {
- lua_pushlightuserdata(state, ptr);
- return PushedObject{state, 1};
- }
+template <>
+struct LuaContext::Pusher<dnsdist_ffi_stat_node_t*>
+{
+ static const int minSize = 1;
+ static const int maxSize = 1;
+
+ static PushedObject push(lua_State* state, dnsdist_ffi_stat_node_t* ptr) noexcept
+ {
+ lua_pushlightuserdata(state, ptr);
+ return PushedObject{state, 1};
+ }
};
using dnsdist_ffi_stat_node_visitor_t = std::function<bool(dnsdist_ffi_stat_node_t*)>;
struct dnsdist_ffi_stat_node_t
{
- dnsdist_ffi_stat_node_t(const StatNode& node_, const StatNode::Stat& self_, const StatNode::Stat& children_, std::optional<std::string>& reason_): node(node_), self(self_), children(children_), reason(reason_)
+ dnsdist_ffi_stat_node_t(const StatNode& node_, const StatNode::Stat& self_, const StatNode::Stat& children_, std::optional<std::string>& reason_) :
+ node(node_), self(self_), children(children_), reason(reason_)
{
}
class DynBlockRulesGroup
{
private:
-
struct Counts
{
std::map<uint8_t, uint64_t> d_rcodeCounts;
struct DynBlockRule
{
- DynBlockRule(): d_enabled(false)
+ DynBlockRule() :
+ d_enabled(false)
{
}
- DynBlockRule(const std::string& blockReason, unsigned int blockDuration, unsigned int rate, unsigned int warningRate, unsigned int seconds, DNSAction::Action action): d_blockReason(blockReason), d_blockDuration(blockDuration), d_rate(rate), d_warningRate(warningRate), d_seconds(seconds), d_action(action), d_enabled(true)
+ DynBlockRule(const std::string& blockReason, unsigned int blockDuration, unsigned int rate, unsigned int warningRate, unsigned int seconds, DNSAction::Action action) :
+ d_blockReason(blockReason), d_blockDuration(blockDuration), d_rate(rate), d_warningRate(warningRate), d_seconds(seconds), d_action(action), d_enabled(true)
{
}
bool d_enabled{false};
};
- struct DynBlockRatioRule: DynBlockRule
+ struct DynBlockRatioRule : DynBlockRule
{
- DynBlockRatioRule(): DynBlockRule()
+ DynBlockRatioRule() :
+ DynBlockRule()
{
}
- DynBlockRatioRule(const std::string& blockReason, unsigned int blockDuration, double ratio, double warningRatio, unsigned int seconds, DNSAction::Action action, size_t minimumNumberOfResponses): DynBlockRule(blockReason, blockDuration, 0, 0, seconds, action), d_minimumNumberOfResponses(minimumNumberOfResponses), d_ratio(ratio), d_warningRatio(warningRatio)
+ DynBlockRatioRule(const std::string& blockReason, unsigned int blockDuration, double ratio, double warningRatio, unsigned int seconds, DNSAction::Action action, size_t minimumNumberOfResponses) :
+ DynBlockRule(blockReason, blockDuration, 0, 0, seconds, action), d_minimumNumberOfResponses(minimumNumberOfResponses), d_ratio(ratio), d_warningRatio(warningRatio)
{
}
}
private:
-
+ void applySMT(const struct timespec& now, StatNode& statNodeRoot);
bool checkIfQueryTypeMatches(const Rings::Query& query);
bool checkIfResponseCodeMatches(const Rings::Response& response);
- void addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange> >& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated, bool warning);
+ void addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange>>& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated, bool warning);
void addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& blocks, const struct timespec& now, const DNSName& name, const DynBlockRule& rule, bool& updated);
- void addBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange> >& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated)
+ void addBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange>>& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated)
{
addOrRefreshBlock(blocks, now, requestor, rule, updated, false);
}
- void handleWarning(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange> >& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated)
+ void handleWarning(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange>>& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated)
{
addOrRefreshBlock(blocks, now, requestor, rule, updated, true);
}
static size_t s_topN;
};
-namespace dnsdist::DynamicBlocks {
+namespace dnsdist::DynamicBlocks
+{
bool addOrRefreshBlock(NetmaskTree<DynBlock, AddressAndPortRange>& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const std::string& reason, unsigned int duration, DNSAction::Action action, bool warning, bool beQuiet);
bool addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& blocks, const struct timespec& now, const DNSName& name, const std::string& reason, unsigned int duration, DNSAction::Action action, bool beQuiet);
}
DNSAction::Action g_dynBlockAction = DNSAction::Action::Drop;
#ifndef DISABLE_DYNBLOCKS
-
void DynBlockRulesGroup::apply(const struct timespec& now)
{
counts_t counts;
return;
}
- boost::optional<NetmaskTree<DynBlock, AddressAndPortRange> > blocks;
+ boost::optional<NetmaskTree<DynBlock, AddressAndPortRange>> blocks;
bool updated = false;
for (const auto& entry : counts) {
g_dynblockNMG.setState(std::move(*blocks));
}
- if (!statNodeRoot.empty()) {
- StatNode::Stat node;
- std::unordered_map<DNSName, std::optional<std::string>> namesToBlock;
- statNodeRoot.visit([this,&namesToBlock](const StatNode* node_, const StatNode::Stat& self, const StatNode::Stat& children) {
- bool block = false;
- std::optional<std::string> reason;
-
- if (d_smtVisitorFFI) {
- dnsdist_ffi_stat_node_t tmp(*node_, self, children, reason);
- block = d_smtVisitorFFI(&tmp);
- }
- else {
- auto ret = d_smtVisitor(*node_, self, children);
- block = std::get<0>(ret);
- if (block) {
- if (boost::optional<std::string> tmp = std::get<1>(ret)) {
- reason = std::move(*tmp);
- }
- }
- }
-
- if (block) {
- namesToBlock.insert({DNSName(node_->fullname), std::move(reason)});
- }
- },
- node);
-
- if (!namesToBlock.empty()) {
- updated = false;
- SuffixMatchTree<DynBlock> smtBlocks = g_dynblockSMT.getCopy();
- for (auto& [name, reason] : namesToBlock) {
- if (reason) {
- DynBlockRule rule(d_suffixMatchRule);
- rule.d_blockReason = std::move(*reason);
- addOrRefreshBlockSMT(smtBlocks, now, std::move(name), std::move(rule), updated);
- }
- else {
- addOrRefreshBlockSMT(smtBlocks, now, std::move(name), d_suffixMatchRule, updated);
+ applySMT(now, statNodeRoot);
+}
+
+void DynBlockRulesGroup::applySMT(const struct timespec& now, StatNode& statNodeRoot)
+{
+ if (statNodeRoot.empty()) {
+ return;
+ }
+
+ bool updated = false;
+ StatNode::Stat node;
+ std::unordered_map<DNSName, std::optional<std::string>> namesToBlock;
+ statNodeRoot.visit([this, &namesToBlock](const StatNode* node_, const StatNode::Stat& self, const StatNode::Stat& children) {
+ bool block = false;
+ std::optional<std::string> reason;
+
+ if (d_smtVisitorFFI) {
+ dnsdist_ffi_stat_node_t tmp(*node_, self, children, reason);
+ block = d_smtVisitorFFI(&tmp);
+ }
+ else {
+ auto ret = d_smtVisitor(*node_, self, children);
+ block = std::get<0>(ret);
+ if (block) {
+ if (boost::optional<std::string> tmp = std::get<1>(ret)) {
+ reason = std::move(*tmp);
}
}
- if (updated) {
- g_dynblockSMT.setState(std::move(smtBlocks));
+ }
+
+ if (block) {
+ namesToBlock.insert({DNSName(node_->fullname), std::move(reason)});
+ }
+ },
+ node);
+
+ if (!namesToBlock.empty()) {
+ updated = false;
+ SuffixMatchTree<DynBlock> smtBlocks = g_dynblockSMT.getCopy();
+ for (auto& [name, reason] : namesToBlock) {
+ if (reason) {
+ DynBlockRule rule(d_suffixMatchRule);
+ rule.d_blockReason = std::move(*reason);
+ addOrRefreshBlockSMT(smtBlocks, now, name, rule, updated);
+ }
+ else {
+ addOrRefreshBlockSMT(smtBlocks, now, name, d_suffixMatchRule, updated);
}
}
+ if (updated) {
+ g_dynblockSMT.setState(std::move(smtBlocks));
+ }
}
}
}
auto ratio = d_rcodeRatioRules.find(response.dh.rcode);
- if (ratio != d_rcodeRatioRules.end() && ratio->second.matches(response.when)) {
- return true;
- }
-
- return false;
+ return ratio != d_rcodeRatioRules.end() && ratio->second.matches(response.when);
}
/* return the actual action that will be taken by that block:
return g_dynBlockAction;
}
-namespace dnsdist::DynamicBlocks {
- bool addOrRefreshBlock(NetmaskTree<DynBlock, AddressAndPortRange>& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const std::string& reason, unsigned int duration, DNSAction::Action action, bool warning, bool beQuiet)
+namespace dnsdist::DynamicBlocks
+{
+bool addOrRefreshBlock(NetmaskTree<DynBlock, AddressAndPortRange>& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const std::string& reason, unsigned int duration, DNSAction::Action action, bool warning, bool beQuiet)
{
unsigned int count = 0;
bool expired = false;
struct timespec until = now;
until.tv_sec += duration;
- DynBlock db{reason, until, DNSName(), warning ? DNSAction::Action::NoOp : action};
- db.warning = warning;
+ DynBlock dblock{reason, until, DNSName(), warning ? DNSAction::Action::NoOp : action};
+ dblock.warning = warning;
const auto& got = blocks.lookup(requestor);
- if (got) {
+ if (got != nullptr) {
bpf = got->second.bpf;
if (warning && !got->second.warning) {
don't override it */
return false;
}
- else if (!warning && got->second.warning) {
+ if (!warning && got->second.warning) {
wasWarning = true;
}
else {
}
}
- db.blocks = count;
+ dblock.blocks = count;
- if (!got || expired || wasWarning) {
- const auto actualAction = getActualAction(db);
- if (g_defaultBPFFilter &&
- ((requestor.isIPv4() && requestor.getBits() == 32) || (requestor.isIPv6() && requestor.getBits() == 128)) &&
- (actualAction == DNSAction::Action::Drop || actualAction == DNSAction::Action::Truncate)) {
+ if (got == nullptr || expired || wasWarning) {
+ const auto actualAction = getActualAction(dblock);
+ if (g_defaultBPFFilter && ((requestor.isIPv4() && requestor.getBits() == 32) || (requestor.isIPv6() && requestor.getBits() == 128)) && (actualAction == DNSAction::Action::Drop || actualAction == DNSAction::Action::Truncate)) {
try {
BPFFilter::MatchAction bpfAction = actualAction == DNSAction::Action::Drop ? BPFFilter::MatchAction::Drop : BPFFilter::MatchAction::Truncate;
if (g_defaultBPFFilter->supportsMatchAction(bpfAction)) {
}
if (!beQuiet) {
- warnlog("Inserting %s%sdynamic block for %s for %d seconds: %s", warning ? "(warning) " :"", bpf ? "eBPF " : "", requestor.toString(), duration, reason);
+ warnlog("Inserting %s%sdynamic block for %s for %d seconds: %s", warning ? "(warning) " : "", bpf ? "eBPF " : "", requestor.toString(), duration, reason);
}
}
- db.bpf = bpf;
+ dblock.bpf = bpf;
- blocks.insert(requestor).second = std::move(db);
+ blocks.insert(requestor).second = std::move(dblock);
return true;
}
struct timespec until = now;
until.tv_sec += duration;
unsigned int count = 0;
- DynBlock db{reason, until, name.makeLowerCase(), action};
+ DynBlock dblock{reason, until, name.makeLowerCase(), action};
/* be careful, if you try to insert a longer suffix
lookup() might return a shorter one if it is
already in the tree as a final node */
const DynBlock* got = blocks.lookup(name);
- if (got && got->domain != name) {
+ if (got != nullptr && got->domain != name) {
got = nullptr;
}
bool expired = false;
- if (got) {
+ if (got != nullptr) {
if (until < got->until) {
// had a longer policy
return false;
}
}
- db.blocks = count;
+ dblock.blocks = count;
- if (!beQuiet && (!got || expired)) {
+ if (!beQuiet && (got == nullptr || expired)) {
warnlog("Inserting dynamic block for %s for %d seconds: %s", name, duration, reason);
}
- blocks.add(name, std::move(db));
+ blocks.add(name, std::move(dblock));
return true;
}
}
-void DynBlockRulesGroup::addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange> >& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated, bool warning)
+void DynBlockRulesGroup::addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange>>& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated, bool warning)
{
/* network exclusions are address-based only (no port) */
if (d_excludedSubnets.match(requestor.getNetwork())) {
}
for (const auto& shard : g_rings.d_shards) {
- auto rl = shard->queryRing.lock();
- for (const auto& c : *rl) {
- if (now < c.when) {
+ auto queryRing = shard->queryRing.lock();
+ for (const auto& ringEntry : *queryRing) {
+ if (now < ringEntry.when) {
continue;
}
- bool qRateMatches = d_queryRateRule.matches(c.when);
- bool typeRuleMatches = checkIfQueryTypeMatches(c);
+ bool qRateMatches = d_queryRateRule.matches(ringEntry.when);
+ bool typeRuleMatches = checkIfQueryTypeMatches(ringEntry);
if (qRateMatches || typeRuleMatches) {
- auto& entry = counts[AddressAndPortRange(c.requestor, c.requestor.isIPv4() ? d_v4Mask : d_v6Mask, d_portMask)];
+ auto& entry = counts[AddressAndPortRange(ringEntry.requestor, ringEntry.requestor.isIPv4() ? d_v4Mask : d_v6Mask, d_portMask)];
if (qRateMatches) {
++entry.queries;
}
if (typeRuleMatches) {
- ++entry.d_qtypeCounts[c.qtype];
+ ++entry.d_qtypeCounts[ringEntry.qtype];
}
}
}
}
for (const auto& shard : g_rings.d_shards) {
- auto rl = shard->respRing.lock();
- for(const auto& c : *rl) {
- if (now < c.when) {
+ auto responseRing = shard->respRing.lock();
+ for (const auto& ringEntry : *responseRing) {
+ if (now < ringEntry.when) {
continue;
}
- if (c.when < responseCutOff) {
+ if (ringEntry.when < responseCutOff) {
continue;
}
- auto& entry = counts[AddressAndPortRange(c.requestor, c.requestor.isIPv4() ? d_v4Mask : d_v6Mask, d_portMask)];
+ auto& entry = counts[AddressAndPortRange(ringEntry.requestor, ringEntry.requestor.isIPv4() ? d_v4Mask : d_v6Mask, d_portMask)];
++entry.responses;
- bool respRateMatches = d_respRateRule.matches(c.when);
- bool suffixMatchRuleMatches = d_suffixMatchRule.matches(c.when);
- bool rcodeRuleMatches = checkIfResponseCodeMatches(c);
+ bool respRateMatches = d_respRateRule.matches(ringEntry.when);
+ bool suffixMatchRuleMatches = d_suffixMatchRule.matches(ringEntry.when);
+ bool rcodeRuleMatches = checkIfResponseCodeMatches(ringEntry);
if (respRateMatches || rcodeRuleMatches) {
if (respRateMatches) {
- entry.respBytes += c.size;
+ entry.respBytes += ringEntry.size;
}
if (rcodeRuleMatches) {
- ++entry.d_rcodeCounts[c.dh.rcode];
+ ++entry.d_rcodeCounts[ringEntry.dh.rcode];
}
}
if (suffixMatchRuleMatches) {
- bool hit = c.ds.sin4.sin_family == 0;
- if (!hit && c.ds.isIPv4() && c.ds.sin4.sin_addr.s_addr == 0 && c.ds.sin4.sin_port == 0) {
+ bool hit = ringEntry.ds.sin4.sin_family == 0;
+ if (!hit && ringEntry.ds.isIPv4() && ringEntry.ds.sin4.sin_addr.s_addr == 0 && ringEntry.ds.sin4.sin_port == 0) {
hit = true;
}
- root.submit(c.name, ((c.dh.rcode == 0 && c.usec == std::numeric_limits<unsigned int>::max()) ? -1 : c.dh.rcode), c.size, hit, boost::none);
+ root.submit(ringEntry.name, ((ringEntry.dh.rcode == 0 && ringEntry.usec == std::numeric_limits<unsigned int>::max()) ? -1 : ringEntry.dh.rcode), ringEntry.size, hit, boost::none);
}
}
}
topsForReason.pop_front();
}
- topsForReason.insert(std::lower_bound(topsForReason.begin(), topsForReason.end(), newEntry, [](const std::pair<AddressAndPortRange, unsigned int>& a, const std::pair<AddressAndPortRange, unsigned int>& b) {
- return a.second < b.second;
- }),
- newEntry);
+ topsForReason.insert(std::lower_bound(topsForReason.begin(), topsForReason.end(), newEntry, [](const std::pair<AddressAndPortRange, unsigned int>& rhs, const std::pair<AddressAndPortRange, unsigned int>& lhs) {
+ return rhs.second < lhs.second;
+ }),
+ newEntry);
}
}
topsForReason.pop_front();
}
- topsForReason.insert(std::lower_bound(topsForReason.begin(), topsForReason.end(), newEntry, [](const std::pair<DNSName, unsigned int>& a, const std::pair<DNSName, unsigned int>& b) {
- return a.second < b.second;
- }),
- newEntry);
+ topsForReason.insert(std::lower_bound(topsForReason.begin(), topsForReason.end(), newEntry, [](const std::pair<DNSName, unsigned int>& rhs, const std::pair<DNSName, unsigned int>& lhs) {
+ return rhs.second < lhs.second;
+ }),
+ newEntry);
}
});
struct DynBlockEntryStat
{
- size_t sum;
+ size_t sum{0};
unsigned int lastSeenValue{0};
};
}
/* do NMG */
- std::map<std::string, std::map<AddressAndPortRange, DynBlockEntryStat>> nm;
+ std::map<std::string, std::map<AddressAndPortRange, DynBlockEntryStat>> netmasks;
for (const auto& reason : s_metricsData.front().nmgData) {
- auto& reasonStat = nm[reason.first];
+ auto& reasonStat = netmasks[reason.first];
/* prepare the counters by scanning the oldest entry (N+1) */
for (const auto& entry : reason.second) {
continue;
}
- auto& nmgData = snap.nmgData;
+ const auto& nmgData = snap.nmgData;
for (const auto& reason : nmgData) {
- auto& reasonStat = nm[reason.first];
+ auto& reasonStat = netmasks[reason.first];
for (const auto& entry : reason.second) {
auto& stat = reasonStat[entry.first];
if (entry.second < stat.lastSeenValue) {
/* now we need to get the top N entries (for each "reason") based on our counters (sum of the last N entries) */
std::map<std::string, std::list<std::pair<AddressAndPortRange, unsigned int>>> topNMGs;
{
- for (const auto& reason : nm) {
+ for (const auto& reason : netmasks) {
auto& topsForReason = topNMGs[reason.first];
for (const auto& entry : reason.second) {
if (topsForReason.size() < s_topN || topsForReason.front().second < entry.second.sum) {
/* Note that this is a gauge, so we need to divide by the number of elapsed seconds */
- auto newEntry = std::pair<AddressAndPortRange, unsigned int>(entry.first, std::round(entry.second.sum / 60.0));
+ auto newEntry = std::pair<AddressAndPortRange, unsigned int>(entry.first, std::round(static_cast<double>(entry.second.sum) / 60.0));
if (topsForReason.size() >= s_topN) {
topsForReason.pop_front();
}
- topsForReason.insert(std::lower_bound(topsForReason.begin(), topsForReason.end(), newEntry, [](const std::pair<AddressAndPortRange, unsigned int>& a, const std::pair<AddressAndPortRange, unsigned int>& b) {
- return a.second < b.second;
- }),
- newEntry);
+ topsForReason.insert(std::lower_bound(topsForReason.begin(), topsForReason.end(), newEntry, [](const std::pair<AddressAndPortRange, unsigned int>& rhs, const std::pair<AddressAndPortRange, unsigned int>& lhs) {
+ return rhs.second < lhs.second;
+ }),
+ newEntry);
}
}
}
continue;
}
- auto& smtData = snap.smtData;
+ const auto& smtData = snap.smtData;
for (const auto& reason : smtData) {
auto& reasonStat = smt[reason.first];
for (const auto& entry : reason.second) {
for (const auto& entry : reason.second) {
if (topsForReason.size() < s_topN || topsForReason.front().second < entry.second.sum) {
/* Note that this is a gauge, so we need to divide by the number of elapsed seconds */
- auto newEntry = std::pair<DNSName, unsigned int>(entry.first, std::round(entry.second.sum / 60.0));
+ auto newEntry = std::pair<DNSName, unsigned int>(entry.first, std::round(static_cast<double>(entry.second.sum) / 60.0));
if (topsForReason.size() >= s_topN) {
topsForReason.pop_front();
}
- topsForReason.insert(std::lower_bound(topsForReason.begin(), topsForReason.end(), newEntry, [](const std::pair<DNSName, unsigned int>& a, const std::pair<DNSName, unsigned int>& b) {
- return a.second < b.second;
- }),
- newEntry);
+ topsForReason.insert(std::lower_bound(topsForReason.begin(), topsForReason.end(), newEntry, [](const std::pair<DNSName, unsigned int>& lhs, const std::pair<DNSName, unsigned int>& rhs) {
+ return lhs.second < rhs.second;
+ }),
+ newEntry);
}
}
}
sleepDelay = std::min(sleepDelay, (nextMetricsGeneration - now));
// coverity[store_truncates_time_t]
- sleep(sleepDelay);
+ std::this_thread::sleep_for(std::chrono::seconds(sleepDelay));
try {
now = time(nullptr);
}
if (s_expiredDynBlocksPurgeInterval > 0 && now >= nextExpiredPurge) {
- struct timespec tspec;
+ struct timespec tspec
+ {
+ };
gettime(&tspec);
purgeExpired(tspec);