]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-dynblocks.hh
rec: Update new b-root-server.net addresses in built-in hints.
[thirdparty/pdns.git] / pdns / dnsdist-dynblocks.hh
CommitLineData
dc2fd93a
RG
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
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.
8 *
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.
12 *
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.
17 *
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.
21 */
22#pragma once
23
3dec2251 24#ifndef DISABLE_DYNBLOCKS
23adffab
RG
25#include <unordered_set>
26
dc2fd93a 27#include "dolog.hh"
03b00917 28#include "dnsdist-rings.hh"
23adffab 29#include "statnode.hh"
dc2fd93a 30
b4fbe20d
RG
31extern "C" {
32#include "dnsdist-lua-inspection-ffi.h"
33}
861ce85b
RG
34
35// dnsdist_ffi_stat_node_t is a lightuserdata
36template<>
37struct LuaContext::Pusher<dnsdist_ffi_stat_node_t*> {
38 static const int minSize = 1;
39 static const int maxSize = 1;
40
41 static PushedObject push(lua_State* state, dnsdist_ffi_stat_node_t* ptr) noexcept {
42 lua_pushlightuserdata(state, ptr);
43 return PushedObject{state, 1};
44 }
45};
46
47typedef std::function<bool(dnsdist_ffi_stat_node_t*)> dnsdist_ffi_stat_node_visitor_t;
48
49struct dnsdist_ffi_stat_node_t
50{
7179dab0 51 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_)
861ce85b
RG
52 {
53 }
54
55 const StatNode& node;
56 const StatNode::Stat& self;
57 const StatNode::Stat& children;
7179dab0 58 std::optional<std::string>& reason;
861ce85b
RG
59};
60
dc2fd93a
RG
61class DynBlockRulesGroup
62{
63private:
64
65 struct Counts
66 {
67 std::map<uint8_t, uint64_t> d_rcodeCounts;
68 std::map<uint16_t, uint64_t> d_qtypeCounts;
69 uint64_t queries{0};
3814053e 70 uint64_t responses{0};
dc2fd93a
RG
71 uint64_t respBytes{0};
72 };
73
74 struct DynBlockRule
75 {
76 DynBlockRule(): d_enabled(false)
77 {
78 }
79
1d3ba133 80 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)
dc2fd93a
RG
81 {
82 }
83
84 bool matches(const struct timespec& when)
85 {
86 if (!d_enabled) {
87 return false;
88 }
89
90 if (d_seconds && when < d_cutOff) {
91 return false;
92 }
93
94 if (when < d_minTime) {
95 d_minTime = when;
96 }
97
98 return true;
99 }
100
101 bool rateExceeded(unsigned int count, const struct timespec& now) const
102 {
103 if (!d_enabled) {
104 return false;
105 }
106
107 double delta = d_seconds ? d_seconds : DiffTime(now, d_minTime);
108 double limit = delta * d_rate;
109 return (count > limit);
110 }
111
1d3ba133
RG
112 bool warningRateExceeded(unsigned int count, const struct timespec& now) const
113 {
b1fd5841
DF
114 if (!d_enabled) {
115 return false;
116 }
117
15e60f9f 118 if (d_warningRate == 0) {
1d3ba133
RG
119 return false;
120 }
121
122 double delta = d_seconds ? d_seconds : DiffTime(now, d_minTime);
123 double limit = delta * d_warningRate;
124 return (count > limit);
125 }
126
dc2fd93a
RG
127 bool isEnabled() const
128 {
15e60f9f 129 return d_enabled;
dc2fd93a
RG
130 }
131
b718792f
RG
132 std::string toString() const
133 {
134 if (!isEnabled()) {
135 return "";
136 }
137
138 std::stringstream result;
139 if (d_action != DNSAction::Action::None) {
140 result << DNSAction::typeToString(d_action) << " ";
141 }
142 else {
143 result << "Apply the global DynBlock action ";
144 }
145 result << "for " << std::to_string(d_blockDuration) << " seconds when over " << std::to_string(d_rate) << " during the last " << d_seconds << " seconds, reason: '" << d_blockReason << "'";
146
147 return result.str();
148 }
149
dc2fd93a
RG
150 std::string d_blockReason;
151 struct timespec d_cutOff;
152 struct timespec d_minTime;
153 unsigned int d_blockDuration{0};
154 unsigned int d_rate{0};
1d3ba133 155 unsigned int d_warningRate{0};
dc2fd93a
RG
156 unsigned int d_seconds{0};
157 DNSAction::Action d_action{DNSAction::Action::None};
158 bool d_enabled{false};
159 };
160
838c2f00
RG
161 struct DynBlockRatioRule: DynBlockRule
162 {
163 DynBlockRatioRule(): DynBlockRule()
164 {
165 }
166
167 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)
168 {
169 }
170
171 bool ratioExceeded(unsigned int total, unsigned int count) const
172 {
173 if (!d_enabled) {
174 return false;
175 }
176
177 if (total < d_minimumNumberOfResponses) {
178 return false;
179 }
180
181 double allowed = d_ratio * static_cast<double>(total);
182 return (count > allowed);
183 }
184
185 bool warningRatioExceeded(unsigned int total, unsigned int count) const
186 {
b1fd5841
DF
187 if (!d_enabled) {
188 return false;
189 }
190
191 if (d_warningRatio == 0.0) {
838c2f00
RG
192 return false;
193 }
194
195 if (total < d_minimumNumberOfResponses) {
196 return false;
197 }
198
199 double allowed = d_warningRatio * static_cast<double>(total);
200 return (count > allowed);
201 }
202
203 std::string toString() const
204 {
205 if (!isEnabled()) {
206 return "";
207 }
208
209 std::stringstream result;
210 if (d_action != DNSAction::Action::None) {
211 result << DNSAction::typeToString(d_action) << " ";
212 }
213 else {
214 result << "Apply the global DynBlock action ";
215 }
216 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 << "'";
217
218 return result.str();
219 }
220
221 size_t d_minimumNumberOfResponses{0};
222 double d_ratio{0.0};
223 double d_warningRatio{0.0};
224 };
225
c173228a 226 typedef std::unordered_map<AddressAndPortRange, Counts, AddressAndPortRange::hash> counts_t;
dc2fd93a
RG
227
228public:
229 DynBlockRulesGroup()
230 {
231 }
232
26512612 233 void setQueryRate(unsigned int rate, unsigned int warningRate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action)
dc2fd93a 234 {
1d3ba133 235 d_queryRateRule = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action);
dc2fd93a
RG
236 }
237
1d3ba133 238 /* rate is in bytes per second */
26512612 239 void setResponseByteRate(unsigned int rate, unsigned int warningRate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action)
dc2fd93a 240 {
1d3ba133 241 d_respRateRule = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action);
dc2fd93a
RG
242 }
243
26512612 244 void setRCodeRate(uint8_t rcode, unsigned int rate, unsigned int warningRate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action)
dc2fd93a
RG
245 {
246 auto& entry = d_rcodeRules[rcode];
1d3ba133 247 entry = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action);
dc2fd93a
RG
248 }
249
26512612 250 void setRCodeRatio(uint8_t rcode, double ratio, double warningRatio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action, size_t minimumNumberOfResponses)
838c2f00
RG
251 {
252 auto& entry = d_rcodeRatioRules[rcode];
253 entry = DynBlockRatioRule(reason, blockDuration, ratio, warningRatio, seconds, action, minimumNumberOfResponses);
254 }
255
26512612 256 void setQTypeRate(uint16_t qtype, unsigned int rate, unsigned int warningRate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action)
dc2fd93a
RG
257 {
258 auto& entry = d_qtypeRules[qtype];
1d3ba133 259 entry = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action);
dc2fd93a
RG
260 }
261
7179dab0 262 typedef std::function<std::tuple<bool, boost::optional<std::string>>(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)> smtVisitor_t;
23adffab 263
26512612 264 void setSuffixMatchRule(unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action, smtVisitor_t visitor)
23adffab
RG
265 {
266 d_suffixMatchRule = DynBlockRule(reason, blockDuration, 0, 0, seconds, action);
c92e6020 267 d_smtVisitor = std::move(visitor);
23adffab
RG
268 }
269
26512612 270 void setSuffixMatchRuleFFI(unsigned int seconds, const std::string& reason, unsigned int blockDuration, DNSAction::Action action, dnsdist_ffi_stat_node_visitor_t visitor)
861ce85b
RG
271 {
272 d_suffixMatchRule = DynBlockRule(reason, blockDuration, 0, 0, seconds, action);
c92e6020 273 d_smtVisitorFFI = std::move(visitor);
861ce85b
RG
274 }
275
c173228a 276 void setMasks(uint8_t v4, uint8_t v6, uint8_t port)
6d4de128
RG
277 {
278 d_v4Mask = v4;
279 d_v6Mask = v6;
c173228a 280 d_portMask = port;
6d4de128
RG
281 }
282
dc2fd93a 283 void apply()
1d3ba133
RG
284 {
285 struct timespec now;
286 gettime(&now);
287
288 apply(now);
289 }
290
838c2f00 291 void apply(const struct timespec& now);
dc2fd93a 292
b718792f
RG
293 void excludeRange(const Netmask& range)
294 {
295 d_excludedSubnets.addMask(range);
296 }
297
5a2f3287
RG
298 void excludeRange(const NetmaskGroup& group)
299 {
300 d_excludedSubnets.addMasks(group, true);
301 }
302
b718792f
RG
303 void includeRange(const Netmask& range)
304 {
305 d_excludedSubnets.addMask(range, false);
306 }
307
5a2f3287
RG
308 void includeRange(const NetmaskGroup& group)
309 {
310 d_excludedSubnets.addMasks(group, false);
311 }
312
23adffab
RG
313 void excludeDomain(const DNSName& domain)
314 {
315 d_excludedDomains.add(domain);
316 }
317
b718792f
RG
318 std::string toString() const
319 {
320 std::stringstream result;
321
322 result << "Query rate rule: " << d_queryRateRule.toString() << std::endl;
323 result << "Response rate rule: " << d_respRateRule.toString() << std::endl;
23adffab 324 result << "SuffixMatch rule: " << d_suffixMatchRule.toString() << std::endl;
b718792f
RG
325 result << "RCode rules: " << std::endl;
326 for (const auto& rule : d_rcodeRules) {
327 result << "- " << RCode::to_s(rule.first) << ": " << rule.second.toString() << std::endl;
328 }
838c2f00
RG
329 for (const auto& rule : d_rcodeRatioRules) {
330 result << "- " << RCode::to_s(rule.first) << ": " << rule.second.toString() << std::endl;
331 }
b718792f
RG
332 result << "QType rules: " << std::endl;
333 for (const auto& rule : d_qtypeRules) {
d5fcd583 334 result << "- " << QType(rule.first).toString() << ": " << rule.second.toString() << std::endl;
b718792f
RG
335 }
336 result << "Excluded Subnets: " << d_excludedSubnets.toString() << std::endl;
23adffab 337 result << "Excluded Domains: " << d_excludedDomains.toString() << std::endl;
b718792f
RG
338
339 return result.str();
340 }
341
1d3ba133
RG
342 void setQuiet(bool quiet)
343 {
344 d_beQuiet = quiet;
345 }
346
dc2fd93a 347private:
dc2fd93a 348
838c2f00
RG
349 bool checkIfQueryTypeMatches(const Rings::Query& query);
350 bool checkIfResponseCodeMatches(const Rings::Response& response);
c173228a 351 void addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange> >& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated, bool warning);
838c2f00 352 void addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& blocks, const struct timespec& now, const DNSName& name, const DynBlockRule& rule, bool& updated);
23adffab 353
c173228a 354 void addBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange> >& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated)
1d3ba133
RG
355 {
356 addOrRefreshBlock(blocks, now, requestor, rule, updated, false);
357 }
358
c173228a 359 void handleWarning(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange> >& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated)
1d3ba133
RG
360 {
361 addOrRefreshBlock(blocks, now, requestor, rule, updated, true);
362 }
363
dc2fd93a
RG
364 bool hasQueryRules() const
365 {
366 return d_queryRateRule.isEnabled() || !d_qtypeRules.empty();
367 }
368
369 bool hasResponseRules() const
370 {
838c2f00 371 return d_respRateRule.isEnabled() || !d_rcodeRules.empty() || !d_rcodeRatioRules.empty();
dc2fd93a
RG
372 }
373
23adffab
RG
374 bool hasSuffixMatchRules() const
375 {
376 return d_suffixMatchRule.isEnabled();
377 }
378
dc2fd93a
RG
379 bool hasRules() const
380 {
381 return hasQueryRules() || hasResponseRules();
382 }
383
838c2f00
RG
384 void processQueryRules(counts_t& counts, const struct timespec& now);
385 void processResponseRules(counts_t& counts, StatNode& root, const struct timespec& now);
dc2fd93a
RG
386
387 std::map<uint8_t, DynBlockRule> d_rcodeRules;
838c2f00 388 std::map<uint8_t, DynBlockRatioRule> d_rcodeRatioRules;
dc2fd93a
RG
389 std::map<uint16_t, DynBlockRule> d_qtypeRules;
390 DynBlockRule d_queryRateRule;
391 DynBlockRule d_respRateRule;
23adffab 392 DynBlockRule d_suffixMatchRule;
b718792f 393 NetmaskGroup d_excludedSubnets;
23adffab
RG
394 SuffixMatchNode d_excludedDomains;
395 smtVisitor_t d_smtVisitor;
861ce85b 396 dnsdist_ffi_stat_node_visitor_t d_smtVisitorFFI;
6d4de128
RG
397 uint8_t d_v6Mask{128};
398 uint8_t d_v4Mask{32};
c173228a 399 uint8_t d_portMask{0};
1d3ba133 400 bool d_beQuiet{false};
dc2fd93a 401};
a4244240 402
59b37d25 403class DynBlockMaintenance
a4244240
RG
404{
405public:
59b37d25 406 static void run();
a4244240 407
59b37d25 408 /* return the (cached) number of hits per second for the top offenders, averaged over 60s */
c173228a 409 static std::map<std::string, std::list<std::pair<AddressAndPortRange, unsigned int>>> getHitsForTopNetmasks();
59b37d25
RG
410 static std::map<std::string, std::list<std::pair<DNSName, unsigned int>>> getHitsForTopSuffixes();
411
412 /* get the the top offenders based on the current value of the counters */
c173228a 413 static std::map<std::string, std::list<std::pair<AddressAndPortRange, unsigned int>>> getTopNetmasks(size_t topN);
3814053e 414 static std::map<std::string, std::list<std::pair<DNSName, unsigned int>>> getTopSuffixes(size_t topN);
59b37d25 415 static void purgeExpired(const struct timespec& now);
a4244240 416
b03d051c
RG
417 static time_t s_expiredDynBlocksPurgeInterval;
418
a4244240 419private:
59b37d25
RG
420 static void collectMetrics();
421 static void generateMetrics();
a4244240 422
59b37d25
RG
423 struct MetricsSnapshot
424 {
c173228a 425 std::map<std::string, std::list<std::pair<AddressAndPortRange, unsigned int>>> nmgData;
59b37d25
RG
426 std::map<std::string, std::list<std::pair<DNSName, unsigned int>>> smtData;
427 };
428
01830997
RG
429 struct Tops
430 {
c173228a 431 std::map<std::string, std::list<std::pair<AddressAndPortRange, unsigned int>>> topNMGsByReason;
01830997
RG
432 std::map<std::string, std::list<std::pair<DNSName, unsigned int>>> topSMTsByReason;
433 };
434
435 static LockGuarded<Tops> s_tops;
436 /* s_metricsData should only be accessed by the dynamic blocks maintenance thread so it does not need a lock */
59b37d25
RG
437 // need N+1 datapoints to be able to do the diff after a collection point has been reached
438 static std::list<MetricsSnapshot> s_metricsData;
59b37d25
RG
439 static size_t s_topN;
440};
3dec2251
RG
441
442#endif /* DISABLE_DYNBLOCKS */