2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <unordered_set>
27 #include "dnsdist-rings.hh"
28 #include "statnode.hh"
30 #include "dnsdist-lua-inspection-ffi.hh"
32 // dnsdist_ffi_stat_node_t is a lightuserdata
34 struct LuaContext::Pusher<dnsdist_ffi_stat_node_t*> {
35 static const int minSize = 1;
36 static const int maxSize = 1;
38 static PushedObject push(lua_State* state, dnsdist_ffi_stat_node_t* ptr) noexcept {
39 lua_pushlightuserdata(state, ptr);
40 return PushedObject{state, 1};
44 typedef std::function<bool(dnsdist_ffi_stat_node_t*)> dnsdist_ffi_stat_node_visitor_t;
46 struct dnsdist_ffi_stat_node_t
48 dnsdist_ffi_stat_node_t(const StatNode& node_, const StatNode::Stat& self_, const StatNode::Stat& children_): node(node_), self(self_), children(children_)
53 const StatNode::Stat& self;
54 const StatNode::Stat& children;
57 class DynBlockRulesGroup
63 std::map<uint8_t, uint64_t> d_rcodeCounts;
64 std::map<uint16_t, uint64_t> d_qtypeCounts;
66 uint64_t respBytes{0};
71 DynBlockRule(): d_enabled(false)
75 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)
79 bool matches(const struct timespec& when)
85 if (d_seconds && when < d_cutOff) {
89 if (when < d_minTime) {
96 bool rateExceeded(unsigned int count, const struct timespec& now) const
102 double delta = d_seconds ? d_seconds : DiffTime(now, d_minTime);
103 double limit = delta * d_rate;
104 return (count > limit);
107 bool warningRateExceeded(unsigned int count, const struct timespec& now) const
109 if (d_warningRate == 0) {
113 double delta = d_seconds ? d_seconds : DiffTime(now, d_minTime);
114 double limit = delta * d_warningRate;
115 return (count > limit);
118 bool isEnabled() const
123 std::string toString() const
129 std::stringstream result;
130 if (d_action != DNSAction::Action::None) {
131 result << DNSAction::typeToString(d_action) << " ";
134 result << "Apply the global DynBlock action ";
136 result << "for " << std::to_string(d_blockDuration) << " seconds when over " << std::to_string(d_rate) << " during the last " << d_seconds << " seconds, reason: '" << d_blockReason << "'";
141 std::string d_blockReason;
142 struct timespec d_cutOff;
143 struct timespec d_minTime;
144 unsigned int d_blockDuration{0};
145 unsigned int d_rate{0};
146 unsigned int d_warningRate{0};
147 unsigned int d_seconds{0};
148 DNSAction::Action d_action{DNSAction::Action::None};
149 bool d_enabled{false};
152 struct DynBlockRatioRule: DynBlockRule
154 DynBlockRatioRule(): DynBlockRule()
158 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)
162 bool ratioExceeded(unsigned int total, unsigned int count) const
168 if (total < d_minimumNumberOfResponses) {
172 double allowed = d_ratio * static_cast<double>(total);
173 return (count > allowed);
176 bool warningRatioExceeded(unsigned int total, unsigned int count) const
178 if (d_warningRate == 0) {
182 if (total < d_minimumNumberOfResponses) {
186 double allowed = d_warningRatio * static_cast<double>(total);
187 return (count > allowed);
190 std::string toString() const
196 std::stringstream result;
197 if (d_action != DNSAction::Action::None) {
198 result << DNSAction::typeToString(d_action) << " ";
201 result << "Apply the global DynBlock action ";
203 result << "for " << std::to_string(d_blockDuration) << " seconds when over " << std::to_string(d_ratio) << " ratio during the last " << d_seconds << " seconds, reason: '" << d_blockReason << "'";
208 size_t d_minimumNumberOfResponses{0};
210 double d_warningRatio{0.0};
213 typedef std::unordered_map<ComboAddress, Counts, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual> counts_t;
220 void setQueryRate(unsigned int rate, unsigned int warningRate, unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action)
222 d_queryRateRule = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action);
225 /* rate is in bytes per second */
226 void setResponseByteRate(unsigned int rate, unsigned int warningRate, unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action)
228 d_respRateRule = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action);
231 void setRCodeRate(uint8_t rcode, unsigned int rate, unsigned int warningRate, unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action)
233 auto& entry = d_rcodeRules[rcode];
234 entry = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action);
237 void setRCodeRatio(uint8_t rcode, double ratio, double warningRatio, unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action, size_t minimumNumberOfResponses)
239 auto& entry = d_rcodeRatioRules[rcode];
240 entry = DynBlockRatioRule(reason, blockDuration, ratio, warningRatio, seconds, action, minimumNumberOfResponses);
243 void setQTypeRate(uint16_t qtype, unsigned int rate, unsigned int warningRate, unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action)
245 auto& entry = d_qtypeRules[qtype];
246 entry = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action);
249 typedef std::function<bool(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)> smtVisitor_t;
251 void setSuffixMatchRule(unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action, smtVisitor_t visitor)
253 d_suffixMatchRule = DynBlockRule(reason, blockDuration, 0, 0, seconds, action);
254 d_smtVisitor = visitor;
257 void setSuffixMatchRuleFFI(unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action, dnsdist_ffi_stat_node_visitor_t visitor)
259 d_suffixMatchRule = DynBlockRule(reason, blockDuration, 0, 0, seconds, action);
260 d_smtVisitorFFI = visitor;
271 void apply(const struct timespec& now);
273 void excludeRange(const Netmask& range)
275 d_excludedSubnets.addMask(range);
278 void includeRange(const Netmask& range)
280 d_excludedSubnets.addMask(range, false);
283 void excludeDomain(const DNSName& domain)
285 d_excludedDomains.add(domain);
288 std::string toString() const
290 std::stringstream result;
292 result << "Query rate rule: " << d_queryRateRule.toString() << std::endl;
293 result << "Response rate rule: " << d_respRateRule.toString() << std::endl;
294 result << "SuffixMatch rule: " << d_suffixMatchRule.toString() << std::endl;
295 result << "RCode rules: " << std::endl;
296 for (const auto& rule : d_rcodeRules) {
297 result << "- " << RCode::to_s(rule.first) << ": " << rule.second.toString() << std::endl;
299 for (const auto& rule : d_rcodeRatioRules) {
300 result << "- " << RCode::to_s(rule.first) << ": " << rule.second.toString() << std::endl;
302 result << "QType rules: " << std::endl;
303 for (const auto& rule : d_qtypeRules) {
304 result << "- " << QType(rule.first).getName() << ": " << rule.second.toString() << std::endl;
306 result << "Excluded Subnets: " << d_excludedSubnets.toString() << std::endl;
307 result << "Excluded Domains: " << d_excludedDomains.toString() << std::endl;
312 void setQuiet(bool quiet)
319 bool checkIfQueryTypeMatches(const Rings::Query& query);
320 bool checkIfResponseCodeMatches(const Rings::Response& response);
321 void addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock> >& blocks, const struct timespec& now, const ComboAddress& requestor, const DynBlockRule& rule, bool& updated, bool warning);
322 void addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& blocks, const struct timespec& now, const DNSName& name, const DynBlockRule& rule, bool& updated);
324 void addBlock(boost::optional<NetmaskTree<DynBlock> >& blocks, const struct timespec& now, const ComboAddress& requestor, const DynBlockRule& rule, bool& updated)
326 addOrRefreshBlock(blocks, now, requestor, rule, updated, false);
329 void handleWarning(boost::optional<NetmaskTree<DynBlock> >& blocks, const struct timespec& now, const ComboAddress& requestor, const DynBlockRule& rule, bool& updated)
331 addOrRefreshBlock(blocks, now, requestor, rule, updated, true);
334 bool hasQueryRules() const
336 return d_queryRateRule.isEnabled() || !d_qtypeRules.empty();
339 bool hasResponseRules() const
341 return d_respRateRule.isEnabled() || !d_rcodeRules.empty() || !d_rcodeRatioRules.empty();
344 bool hasSuffixMatchRules() const
346 return d_suffixMatchRule.isEnabled();
349 bool hasRules() const
351 return hasQueryRules() || hasResponseRules();
354 void processQueryRules(counts_t& counts, const struct timespec& now);
355 void processResponseRules(counts_t& counts, StatNode& root, const struct timespec& now);
357 std::map<uint8_t, DynBlockRule> d_rcodeRules;
358 std::map<uint8_t, DynBlockRatioRule> d_rcodeRatioRules;
359 std::map<uint16_t, DynBlockRule> d_qtypeRules;
360 DynBlockRule d_queryRateRule;
361 DynBlockRule d_respRateRule;
362 DynBlockRule d_suffixMatchRule;
363 NetmaskGroup d_excludedSubnets;
364 SuffixMatchNode d_excludedDomains;
365 smtVisitor_t d_smtVisitor;
366 dnsdist_ffi_stat_node_visitor_t d_smtVisitorFFI;
367 bool d_beQuiet{false};