]>
Commit | Line | Data |
---|---|---|
12471842 PL |
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 | */ | |
df111b53 | 22 | #pragma once |
11e1e08b | 23 | #include "config.h" |
df111b53 | 24 | #include "ext/luawrapper/include/LuaContext.hpp" |
25 | #include <time.h> | |
26 | #include "misc.hh" | |
27 | #include "iputils.hh" | |
28 | #include "dnsname.hh" | |
29 | #include <atomic> | |
30 | #include <boost/circular_buffer.hpp> | |
e16fd59c | 31 | #include <boost/variant.hpp> |
df111b53 | 32 | #include <mutex> |
33 | #include <thread> | |
bffca8b9 | 34 | #include <unistd.h> |
ecbe9133 | 35 | #include "sholder.hh" |
11e1e08b | 36 | #include "dnscrypt.hh" |
886e2cf2 | 37 | #include "dnsdist-cache.hh" |
85c7ca75 | 38 | #include "gettime.hh" |
87b515ed RG |
39 | #include "dnsdist-dynbpf.hh" |
40 | #include "bpf-filter.hh" | |
26a6373d SO |
41 | #include <string> |
42 | #include <unordered_map> | |
26a6373d SO |
43 | |
44 | ||
d8c19b98 RG |
45 | #ifdef HAVE_PROTOBUF |
46 | #include <boost/uuid/uuid.hpp> | |
47 | #include <boost/uuid/uuid_generators.hpp> | |
48 | #endif | |
49 | ||
42fae326 | 50 | void* carbonDumpThread(); |
61d1b966 | 51 | uint64_t uptimeOfProcess(const std::string& str); |
bd1c631b | 52 | |
7b925432 RG |
53 | extern uint16_t g_ECSSourcePrefixV4; |
54 | extern uint16_t g_ECSSourcePrefixV6; | |
55 | extern bool g_ECSOverride; | |
56 | ||
26a6373d SO |
57 | class QTag |
58 | { | |
26a6373d | 59 | public: |
5b8255ba RG |
60 | QTag() |
61 | { | |
62 | } | |
26a6373d | 63 | |
5b8255ba RG |
64 | ~QTag() |
65 | { | |
66 | } | |
26a6373d | 67 | |
a76b0d63 | 68 | void add(const std::string& strLabel, const std::string& strValue) |
5b8255ba | 69 | { |
a76b0d63 | 70 | tagData.insert({strLabel, strValue}); |
5b8255ba | 71 | return; |
741ebe08 | 72 | } |
26a6373d | 73 | |
5b8255ba RG |
74 | std::string getMatch(const std::string& strLabel) const |
75 | { | |
a76b0d63 RG |
76 | const auto got = tagData.find(strLabel); |
77 | if (got == tagData.cend()) { | |
5b8255ba | 78 | return ""; |
741ebe08 | 79 | } |
a76b0d63 RG |
80 | |
81 | return got->second; | |
741ebe08 | 82 | } |
e6956c91 | 83 | |
5b8255ba RG |
84 | std::string getEntry(size_t iEntry) const |
85 | { | |
86 | std::string strEntry; | |
87 | size_t iCounter = 0; | |
88 | ||
89 | for (const auto& itr : tagData) { | |
90 | iCounter++; | |
91 | if(iCounter == iEntry) { | |
92 | strEntry = itr.first; | |
93 | strEntry += strSep; | |
94 | strEntry += itr.second; | |
95 | break; | |
96 | } | |
97 | } | |
26a6373d | 98 | |
5b8255ba RG |
99 | return strEntry; |
100 | } | |
26a6373d | 101 | |
5b8255ba RG |
102 | size_t count() const |
103 | { | |
104 | return tagData.size(); | |
105 | } | |
26a6373d | 106 | |
5b8255ba RG |
107 | std::string dumpString() const |
108 | { | |
109 | std::string strRet; | |
e6956c91 | 110 | |
5b8255ba RG |
111 | for (const auto& itr : tagData) { |
112 | strRet += itr.first; | |
113 | strRet += strSep; | |
114 | strRet += itr.second; | |
115 | strRet += "\n"; | |
116 | } | |
117 | return strRet; | |
741ebe08 | 118 | } |
26a6373d | 119 | |
a76b0d63 | 120 | std::unordered_map<std::string, std::string> tagData; |
26a6373d SO |
121 | |
122 | private: | |
c959e01a | 123 | static constexpr char const *strSep = "\t"; |
26a6373d SO |
124 | }; |
125 | ||
0beaa5c8 RG |
126 | #ifdef HAVE_PROTOBUF |
127 | extern thread_local boost::uuids::random_generator t_uuidGenerator; | |
128 | #endif | |
26a6373d | 129 | |
7b925432 RG |
130 | struct DNSQuestion |
131 | { | |
132 | DNSQuestion(const DNSName* name, uint16_t type, uint16_t class_, const ComboAddress* lc, const ComboAddress* rem, struct dnsheader* header, size_t bufferSize, uint16_t queryLen, bool isTcp): qname(name), qtype(type), qclass(class_), local(lc), remote(rem), dh(header), size(bufferSize), len(queryLen), ecsPrefixLength(rem->sin4.sin_family == AF_INET ? g_ECSSourcePrefixV4 : g_ECSSourcePrefixV6), tcp(isTcp), ecsOverride(g_ECSOverride) { } | |
133 | ||
134 | #ifdef HAVE_PROTOBUF | |
ec48a28d | 135 | boost::optional<boost::uuids::uuid> uniqueId; |
7b925432 RG |
136 | #endif |
137 | const DNSName* qname; | |
138 | const uint16_t qtype; | |
139 | const uint16_t qclass; | |
140 | const ComboAddress* local; | |
141 | const ComboAddress* remote; | |
e6956c91 | 142 | std::shared_ptr<QTag> qTag; |
7b925432 RG |
143 | struct dnsheader* dh; |
144 | size_t size; | |
145 | uint16_t len; | |
146 | uint16_t ecsPrefixLength; | |
147 | const bool tcp; | |
148 | bool skipCache{false}; | |
149 | bool ecsOverride; | |
5b8255ba | 150 | bool useECS{true}; |
7b925432 RG |
151 | }; |
152 | ||
153 | struct DNSResponse : DNSQuestion | |
154 | { | |
2b3eefc3 | 155 | DNSResponse(const DNSName* name, uint16_t type, uint16_t class_, const ComboAddress* lc, const ComboAddress* rem, struct dnsheader* header, size_t bufferSize, uint16_t responseLen, bool isTcp, const struct timespec* queryTime_): DNSQuestion(name, type, class_, lc, rem, header, bufferSize, responseLen, isTcp), queryTime(queryTime_) { } |
7b925432 RG |
156 | |
157 | const struct timespec* queryTime; | |
158 | }; | |
159 | ||
160 | /* so what could you do: | |
161 | drop, | |
162 | fake up nxdomain, | |
163 | provide actual answer, | |
164 | allow & and stop processing, | |
165 | continue processing, | |
166 | modify header: (servfail|refused|notimp), set TC=1, | |
167 | send to pool */ | |
168 | ||
169 | class DNSAction | |
170 | { | |
171 | public: | |
c4f5aeff | 172 | enum class Action { Drop, Nxdomain, Refused, Spoof, Allow, HeaderModify, Pool, Delay, Truncate, None}; |
7b925432 | 173 | virtual Action operator()(DNSQuestion*, string* ruleresult) const =0; |
205f2081 RG |
174 | virtual ~DNSAction() |
175 | { | |
176 | } | |
7b925432 RG |
177 | virtual string toString() const = 0; |
178 | virtual std::unordered_map<string, double> getStats() const | |
179 | { | |
180 | return {{}}; | |
181 | } | |
182 | }; | |
183 | ||
184 | class DNSResponseAction | |
185 | { | |
186 | public: | |
187 | enum class Action { Allow, Delay, Drop, HeaderModify, None }; | |
188 | virtual Action operator()(DNSResponse*, string* ruleresult) const =0; | |
205f2081 RG |
189 | virtual ~DNSResponseAction() |
190 | { | |
191 | } | |
7b925432 RG |
192 | virtual string toString() const = 0; |
193 | }; | |
194 | ||
78ffa782 | 195 | struct DynBlock |
196 | { | |
197 | DynBlock& operator=(const DynBlock& rhs) | |
198 | { | |
199 | reason=rhs.reason; | |
200 | until=rhs.until; | |
71c94675 | 201 | domain=rhs.domain; |
7b925432 | 202 | action=rhs.action; |
78ffa782 | 203 | blocks.store(rhs.blocks); |
204 | return *this; | |
205 | } | |
71c94675 | 206 | |
78ffa782 | 207 | string reason; |
208 | struct timespec until; | |
71c94675 | 209 | DNSName domain; |
7b925432 | 210 | DNSAction::Action action; |
78ffa782 | 211 | mutable std::atomic<unsigned int> blocks; |
212 | }; | |
213 | ||
214 | extern GlobalStateHolder<NetmaskTree<DynBlock>> g_dynblockNMG; | |
f758857a | 215 | |
216 | extern vector<pair<struct timeval, std::string> > g_confDelta; | |
217 | ||
e48090d1 | 218 | struct DNSDistStats |
219 | { | |
6ad8b29a | 220 | using stat_t=std::atomic<uint64_t>; // aww yiss ;-) |
e48090d1 | 221 | stat_t responses{0}; |
222 | stat_t servfailResponses{0}; | |
223 | stat_t queries{0}; | |
e73ec7d3 | 224 | stat_t nonCompliantQueries{0}; |
d08b1cdf | 225 | stat_t nonCompliantResponses{0}; |
643a182a | 226 | stat_t rdQueries{0}; |
2efd427d | 227 | stat_t emptyQueries{0}; |
e48090d1 | 228 | stat_t aclDrops{0}; |
bd1c631b | 229 | stat_t dynBlocked{0}; |
e48090d1 | 230 | stat_t ruleDrop{0}; |
231 | stat_t ruleNXDomain{0}; | |
dd46e5e3 | 232 | stat_t ruleRefused{0}; |
e48090d1 | 233 | stat_t selfAnswered{0}; |
234 | stat_t downstreamTimeouts{0}; | |
235 | stat_t downstreamSendErrors{0}; | |
6ad8b29a | 236 | stat_t truncFail{0}; |
b8bc7e61 | 237 | stat_t noPolicy{0}; |
886e2cf2 RG |
238 | stat_t cacheHits{0}; |
239 | stat_t cacheMisses{0}; | |
42fae326 | 240 | stat_t latency0_1{0}, latency1_10{0}, latency10_50{0}, latency50_100{0}, latency100_1000{0}, latencySlow{0}; |
e48090d1 | 241 | |
e16fd59c | 242 | double latencyAvg100{0}, latencyAvg1000{0}, latencyAvg10000{0}, latencyAvg1000000{0}; |
a1a787dc | 243 | typedef std::function<uint64_t(const std::string&)> statfunction_t; |
244 | typedef boost::variant<stat_t*, double*, statfunction_t> entry_t; | |
e16fd59c | 245 | std::vector<std::pair<std::string, entry_t>> entries{ |
dd46e5e3 RG |
246 | {"responses", &responses}, |
247 | {"servfail-responses", &servfailResponses}, | |
248 | {"queries", &queries}, | |
249 | {"acl-drops", &aclDrops}, | |
dd46e5e3 RG |
250 | {"rule-drop", &ruleDrop}, |
251 | {"rule-nxdomain", &ruleNXDomain}, | |
252 | {"rule-refused", &ruleRefused}, | |
253 | {"self-answered", &selfAnswered}, | |
254 | {"downstream-timeouts", &downstreamTimeouts}, | |
255 | {"downstream-send-errors", &downstreamSendErrors}, | |
256 | {"trunc-failures", &truncFail}, | |
257 | {"no-policy", &noPolicy}, | |
258 | {"latency0-1", &latency0_1}, | |
259 | {"latency1-10", &latency1_10}, | |
260 | {"latency10-50", &latency10_50}, | |
261 | {"latency50-100", &latency50_100}, | |
262 | {"latency100-1000", &latency100_1000}, | |
263 | {"latency-slow", &latencySlow}, | |
264 | {"latency-avg100", &latencyAvg100}, | |
265 | {"latency-avg1000", &latencyAvg1000}, | |
266 | {"latency-avg10000", &latencyAvg10000}, | |
267 | {"latency-avg1000000", &latencyAvg1000000}, | |
61d1b966 | 268 | {"uptime", uptimeOfProcess}, |
a9b6db56 | 269 | {"real-memory-usage", getRealMemoryUsage}, |
a2aa00ed | 270 | {"noncompliant-queries", &nonCompliantQueries}, |
d08b1cdf | 271 | {"noncompliant-responses", &nonCompliantResponses}, |
643a182a | 272 | {"rdqueries", &rdQueries}, |
2efd427d | 273 | {"empty-queries", &emptyQueries}, |
886e2cf2 RG |
274 | {"cache-hits", &cacheHits}, |
275 | {"cache-misses", &cacheMisses}, | |
4f99f3d3 RG |
276 | {"cpu-user-msec", getCPUTimeUser}, |
277 | {"cpu-sys-msec", getCPUTimeSystem}, | |
dd46e5e3 RG |
278 | {"fd-usage", getOpenFileDescriptors}, |
279 | {"dyn-blocked", &dynBlocked}, | |
bd1c631b | 280 | {"dyn-block-nmg-size", [](const std::string&) { return g_dynblockNMG.getLocal()->size(); }} |
42fae326 | 281 | }; |
e48090d1 | 282 | }; |
283 | ||
e16fd59c | 284 | |
e48090d1 | 285 | extern struct DNSDistStats g_stats; |
286 | ||
638184e9 | 287 | |
df111b53 | 288 | struct StopWatch |
289 | { | |
58307a85 RG |
290 | StopWatch(bool realTime=false): d_needRealTime(realTime) |
291 | { | |
292 | } | |
df111b53 | 293 | struct timespec d_start{0,0}; |
58307a85 RG |
294 | bool d_needRealTime{false}; |
295 | ||
df111b53 | 296 | void start() { |
58307a85 | 297 | if(gettime(&d_start, d_needRealTime) < 0) |
df111b53 | 298 | unixDie("Getting timestamp"); |
299 | ||
300 | } | |
cf48b0ce RG |
301 | |
302 | void set(const struct timespec& from) { | |
303 | d_start = from; | |
304 | } | |
df111b53 | 305 | |
306 | double udiff() const { | |
307 | struct timespec now; | |
58307a85 | 308 | if(gettime(&now, d_needRealTime) < 0) |
df111b53 | 309 | unixDie("Getting timestamp"); |
310 | ||
311 | return 1000000.0*(now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec)/1000.0; | |
312 | } | |
313 | ||
314 | double udiffAndSet() { | |
315 | struct timespec now; | |
58307a85 | 316 | if(gettime(&now, d_needRealTime) < 0) |
df111b53 | 317 | unixDie("Getting timestamp"); |
318 | ||
319 | auto ret= 1000000.0*(now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec)/1000.0; | |
320 | d_start = now; | |
321 | return ret; | |
322 | } | |
323 | ||
324 | }; | |
325 | ||
326 | class QPSLimiter | |
327 | { | |
328 | public: | |
329 | QPSLimiter() | |
330 | { | |
331 | } | |
332 | ||
333 | QPSLimiter(unsigned int rate, unsigned int burst) : d_rate(rate), d_burst(burst), d_tokens(burst) | |
334 | { | |
335 | d_passthrough=false; | |
336 | d_prev.start(); | |
337 | } | |
338 | ||
339 | unsigned int getRate() const | |
340 | { | |
341 | return d_passthrough? 0 : d_rate; | |
342 | } | |
343 | ||
344 | int getPassed() const | |
345 | { | |
346 | return d_passed; | |
347 | } | |
348 | int getBlocked() const | |
349 | { | |
350 | return d_blocked; | |
351 | } | |
352 | ||
ecbe9133 | 353 | bool check() const // this is not quite fair |
df111b53 | 354 | { |
355 | if(d_passthrough) | |
356 | return true; | |
357 | auto delta = d_prev.udiffAndSet(); | |
358 | ||
359 | d_tokens += 1.0*d_rate * (delta/1000000.0); | |
360 | ||
361 | if(d_tokens > d_burst) | |
362 | d_tokens = d_burst; | |
363 | ||
364 | bool ret=false; | |
365 | if(d_tokens >= 1.0) { // we need this because burst=1 is weird otherwise | |
366 | ret=true; | |
367 | --d_tokens; | |
368 | d_passed++; | |
369 | } | |
370 | else | |
371 | d_blocked++; | |
372 | ||
373 | return ret; | |
374 | } | |
375 | private: | |
376 | bool d_passthrough{true}; | |
377 | unsigned int d_rate; | |
378 | unsigned int d_burst; | |
ecbe9133 | 379 | mutable double d_tokens; |
380 | mutable StopWatch d_prev; | |
381 | mutable unsigned int d_passed{0}; | |
382 | mutable unsigned int d_blocked{0}; | |
df111b53 | 383 | }; |
384 | ||
b5b93e0b RG |
385 | struct ClientState; |
386 | ||
df111b53 | 387 | struct IDState |
388 | { | |
58307a85 | 389 | IDState() : origFD(-1), sentTime(true), delayMsec(0) { origDest.sin4.sin_family = 0;} |
df111b53 | 390 | IDState(const IDState& orig) |
391 | { | |
392 | origFD = orig.origFD; | |
393 | origID = orig.origID; | |
394 | origRemote = orig.origRemote; | |
549d63c9 | 395 | origDest = orig.origDest; |
7b3865cd | 396 | delayMsec = orig.delayMsec; |
df111b53 | 397 | age.store(orig.age.load()); |
398 | } | |
399 | ||
2bf26975 | 400 | int origFD; // set to <0 to indicate this state is empty // 4 |
401 | ||
402 | ComboAddress origRemote; // 28 | |
549d63c9 | 403 | ComboAddress origDest; // 28 |
2bf26975 | 404 | StopWatch sentTime; // 16 |
405 | DNSName qname; // 80 | |
11e1e08b RG |
406 | #ifdef HAVE_DNSCRYPT |
407 | std::shared_ptr<DnsCryptQuery> dnsCryptQuery{0}; | |
d8c19b98 RG |
408 | #endif |
409 | #ifdef HAVE_PROTOBUF | |
ec48a28d | 410 | boost::optional<boost::uuids::uuid> uniqueId; |
11e1e08b | 411 | #endif |
886e2cf2 | 412 | std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; |
a76b0d63 | 413 | std::shared_ptr<QTag> qTag{nullptr}; |
b5b93e0b | 414 | const ClientState* cs{nullptr}; |
886e2cf2 | 415 | uint32_t cacheKey; // 8 |
2bf26975 | 416 | std::atomic<uint16_t> age; // 4 |
417 | uint16_t qtype; // 2 | |
886e2cf2 | 418 | uint16_t qclass; // 2 |
2bf26975 | 419 | uint16_t origID; // 2 |
aeb36780 | 420 | uint16_t origFlags; // 2 |
7b3865cd | 421 | int delayMsec; |
ca404e94 | 422 | bool ednsAdded{false}; |
ff73f02b | 423 | bool ecsAdded{false}; |
886e2cf2 | 424 | bool skipCache{false}; |
7cea4e39 | 425 | bool destHarvested{false}; // if true, origDest holds the original dest addr, otherwise the listening addr |
df111b53 | 426 | }; |
427 | ||
428 | struct Rings { | |
e4c24bb3 | 429 | Rings(size_t capacity=10000) |
df111b53 | 430 | { |
e4c24bb3 RG |
431 | queryRing.set_capacity(capacity); |
432 | respRing.set_capacity(capacity); | |
0e41337b | 433 | pthread_rwlock_init(&queryLock, 0); |
df111b53 | 434 | } |
0ba5eecf | 435 | struct Query |
436 | { | |
437 | struct timespec when; | |
438 | ComboAddress requestor; | |
439 | DNSName name; | |
03ebf8b2 | 440 | uint16_t size; |
0ba5eecf | 441 | uint16_t qtype; |
3fcaeeac | 442 | struct dnsheader dh; |
0ba5eecf | 443 | }; |
444 | boost::circular_buffer<Query> queryRing; | |
df111b53 | 445 | struct Response |
446 | { | |
80a216c9 | 447 | struct timespec when; |
448 | ComboAddress requestor; | |
df111b53 | 449 | DNSName name; |
450 | uint16_t qtype; | |
df111b53 | 451 | unsigned int usec; |
80a216c9 | 452 | unsigned int size; |
3fcaeeac | 453 | struct dnsheader dh; |
2d11d1b2 | 454 | ComboAddress ds; // who handled it |
df111b53 | 455 | }; |
456 | boost::circular_buffer<Response> respRing; | |
457 | std::mutex respMutex; | |
0e41337b | 458 | pthread_rwlock_t queryLock; |
03ebf8b2 | 459 | |
7fc00937 | 460 | std::unordered_map<int, vector<boost::variant<string,double> > > getTopBandwidth(unsigned int numentries); |
a683e8bd | 461 | size_t numDistinctRequestors(); |
e4c24bb3 RG |
462 | void setCapacity(size_t newCapacity) |
463 | { | |
464 | { | |
465 | WriteLock wl(&queryLock); | |
466 | queryRing.set_capacity(newCapacity); | |
467 | } | |
468 | { | |
469 | std::lock_guard<std::mutex> lock(respMutex); | |
470 | respRing.set_capacity(newCapacity); | |
471 | } | |
472 | } | |
df111b53 | 473 | }; |
474 | ||
0e41337b | 475 | extern Rings g_rings; |
df111b53 | 476 | |
786e4d8c RS |
477 | typedef std::unordered_map<string, unsigned int> QueryCountRecords; |
478 | typedef std::function<std::tuple<bool, string>(DNSQuestion dq)> QueryCountFilter; | |
479 | struct QueryCount { | |
480 | QueryCount() | |
481 | { | |
482 | pthread_rwlock_init(&queryLock, 0); | |
483 | } | |
484 | QueryCountRecords records; | |
485 | QueryCountFilter filter; | |
486 | pthread_rwlock_t queryLock; | |
487 | bool enabled{false}; | |
488 | }; | |
489 | ||
490 | extern QueryCount g_qcount; | |
491 | ||
8a5d5053 | 492 | struct ClientState |
493 | { | |
f0e4dcba | 494 | std::set<int> cpus; |
8a5d5053 | 495 | ComboAddress local; |
11e1e08b RG |
496 | #ifdef HAVE_DNSCRYPT |
497 | DnsCryptContext* dnscryptCtx{0}; | |
498 | #endif | |
963bef8d | 499 | std::atomic<uint64_t> queries{0}; |
a36ce055 RG |
500 | int udpFD{-1}; |
501 | int tcpFD{-1}; | |
b5b93e0b | 502 | bool muted{false}; |
8429ad04 RG |
503 | |
504 | int getSocket() const | |
505 | { | |
506 | return udpFD != -1 ? udpFD : tcpFD; | |
507 | } | |
508 | ||
509 | #ifdef HAVE_EBPF | |
510 | shared_ptr<BPFFilter> d_filter; | |
511 | ||
512 | void detachFilter() | |
513 | { | |
514 | if (d_filter) { | |
515 | d_filter->removeSocket(getSocket()); | |
516 | d_filter = nullptr; | |
517 | } | |
518 | } | |
519 | ||
520 | void attachFilter(shared_ptr<BPFFilter> bpf) | |
521 | { | |
522 | detachFilter(); | |
523 | ||
524 | bpf->addSocket(getSocket()); | |
525 | d_filter = bpf; | |
526 | } | |
527 | #endif /* HAVE_EBPF */ | |
8a5d5053 | 528 | }; |
529 | ||
530 | class TCPClientCollection { | |
531 | std::vector<int> d_tcpclientthreads; | |
ded1985a | 532 | std::atomic<uint64_t> d_numthreads{0}; |
a9bf3ec4 | 533 | std::atomic<uint64_t> d_pos{0}; |
ded1985a | 534 | std::atomic<uint64_t> d_queued{0}; |
6c1ca990 | 535 | uint64_t d_maxthreads{0}; |
ded1985a | 536 | std::mutex d_mutex; |
edbda1ad RG |
537 | int d_singlePipe[2]; |
538 | bool d_useSinglePipe; | |
ded1985a | 539 | public: |
8a5d5053 | 540 | |
b79e4996 RG |
541 | TCPClientCollection(size_t maxThreads, bool useSinglePipe=false): d_maxthreads(maxThreads), d_singlePipe{-1,-1}, d_useSinglePipe(useSinglePipe) |
542 | ||
8a5d5053 | 543 | { |
a9bf3ec4 | 544 | d_tcpclientthreads.reserve(maxThreads); |
edbda1ad RG |
545 | |
546 | if (d_useSinglePipe) { | |
547 | if (pipe(d_singlePipe) < 0) { | |
548 | throw std::runtime_error("Error creating the TCP single communication pipe: " + string(strerror(errno))); | |
549 | } | |
550 | if (!setNonBlocking(d_singlePipe[1])) { | |
551 | int err = errno; | |
552 | close(d_singlePipe[0]); | |
553 | close(d_singlePipe[1]); | |
554 | throw std::runtime_error("Error setting the TCP single communication pipe non-blocking: " + string(strerror(err))); | |
555 | } | |
556 | } | |
8a5d5053 | 557 | } |
a9bf3ec4 | 558 | int getThread() |
8a5d5053 | 559 | { |
6c1ca990 | 560 | uint64_t pos = d_pos++; |
8a5d5053 | 561 | ++d_queued; |
562 | return d_tcpclientthreads[pos % d_numthreads]; | |
563 | } | |
ded1985a RG |
564 | bool hasReachedMaxThreads() const |
565 | { | |
566 | return d_numthreads >= d_maxthreads; | |
567 | } | |
568 | uint64_t getThreadsCount() const | |
569 | { | |
570 | return d_numthreads; | |
571 | } | |
572 | uint64_t getQueuedCount() const | |
573 | { | |
574 | return d_queued; | |
575 | } | |
576 | void decrementQueuedCount() | |
577 | { | |
578 | --d_queued; | |
579 | } | |
8a5d5053 | 580 | void addTCPClientThread(); |
581 | }; | |
582 | ||
a9bf3ec4 | 583 | extern std::shared_ptr<TCPClientCollection> g_tcpclientthreads; |
8a5d5053 | 584 | |
df111b53 | 585 | struct DownstreamState |
586 | { | |
fbe2a2e0 RG |
587 | DownstreamState(const ComboAddress& remote_, const ComboAddress& sourceAddr_, unsigned int sourceItf); |
588 | DownstreamState(const ComboAddress& remote_): DownstreamState(remote_, ComboAddress(), 0) {} | |
6a62c0e3 RG |
589 | ~DownstreamState() |
590 | { | |
591 | if (fd >= 0) | |
592 | close(fd); | |
593 | } | |
df111b53 | 594 | |
f99e3aaf | 595 | int fd{-1}; |
df111b53 | 596 | std::thread tid; |
597 | ComboAddress remote; | |
598 | QPSLimiter qps; | |
599 | vector<IDState> idStates; | |
fbe2a2e0 RG |
600 | ComboAddress sourceAddr; |
601 | DNSName checkName{"a.root-servers.net."}; | |
602 | QType checkType{QType::A}; | |
de9f7157 | 603 | uint16_t checkClass{QClass::IN}; |
df111b53 | 604 | std::atomic<uint64_t> idOffset{0}; |
605 | std::atomic<uint64_t> sendErrors{0}; | |
606 | std::atomic<uint64_t> outstanding{0}; | |
607 | std::atomic<uint64_t> reuseds{0}; | |
608 | std::atomic<uint64_t> queries{0}; | |
609 | struct { | |
610 | std::atomic<uint64_t> sendErrors{0}; | |
611 | std::atomic<uint64_t> reuseds{0}; | |
612 | std::atomic<uint64_t> queries{0}; | |
613 | } prev; | |
18eeccc9 | 614 | string name; |
df111b53 | 615 | double queryLoad{0.0}; |
616 | double dropRate{0.0}; | |
617 | double latencyUsec{0.0}; | |
618 | int order{1}; | |
619 | int weight{1}; | |
b40cffe7 | 620 | int tcpConnectTimeout{5}; |
3f6d07a4 RG |
621 | int tcpRecvTimeout{30}; |
622 | int tcpSendTimeout{30}; | |
fbe2a2e0 | 623 | unsigned int sourceItf{0}; |
3f6d07a4 | 624 | uint16_t retries{5}; |
9e87dcb8 RG |
625 | uint8_t currentCheckFailures{0}; |
626 | uint8_t maxCheckFailures{1}; | |
df111b53 | 627 | StopWatch sw; |
628 | set<string> pools; | |
629 | enum class Availability { Up, Down, Auto} availability{Availability::Auto}; | |
fbe2a2e0 | 630 | bool mustResolve{false}; |
df111b53 | 631 | bool upStatus{false}; |
ca404e94 | 632 | bool useECS{false}; |
21830638 | 633 | bool setCD{false}; |
7565f4e6 | 634 | std::atomic<bool> connected{false}; |
284d460c | 635 | bool tcpFastOpen{false}; |
5602f131 | 636 | bool ipBindAddrNoPort{true}; |
df111b53 | 637 | bool isUp() const |
638 | { | |
639 | if(availability == Availability::Down) | |
640 | return false; | |
641 | if(availability == Availability::Up) | |
642 | return true; | |
643 | return upStatus; | |
644 | } | |
645 | void setUp() { availability = Availability::Up; } | |
646 | void setDown() { availability = Availability::Down; } | |
647 | void setAuto() { availability = Availability::Auto; } | |
18eeccc9 RG |
648 | string getName() const { |
649 | if (name.empty()) { | |
650 | return remote.toStringWithPort(); | |
651 | } | |
652 | return name; | |
653 | } | |
a7940c06 | 654 | string getNameWithAddr() const { |
655 | if (name.empty()) { | |
656 | return remote.toStringWithPort(); | |
657 | } | |
658 | return name + " (" + remote.toStringWithPort()+ ")"; | |
659 | } | |
9f4eb5cc RG |
660 | string getStatus() const |
661 | { | |
662 | string status; | |
663 | if(availability == DownstreamState::Availability::Up) | |
664 | status = "UP"; | |
665 | else if(availability == DownstreamState::Availability::Down) | |
666 | status = "DOWN"; | |
667 | else | |
668 | status = (upStatus ? "up" : "down"); | |
669 | return status; | |
670 | } | |
b58f08e5 | 671 | void reconnect(); |
df111b53 | 672 | }; |
673 | using servers_t =vector<std::shared_ptr<DownstreamState>>; | |
df111b53 | 674 | |
da4e7813 | 675 | template <class T> using NumberedVector = std::vector<std::pair<unsigned int, T> >; |
676 | ||
677 | void* responderThread(std::shared_ptr<DownstreamState> state); | |
678 | extern std::mutex g_luamutex; | |
679 | extern LuaContext g_lua; | |
680 | extern std::string g_outputBuffer; // locking for this is ok, as locked by g_luamutex | |
681 | ||
0940e4eb | 682 | class DNSRule |
683 | { | |
684 | public: | |
205f2081 RG |
685 | virtual ~DNSRule () |
686 | { | |
687 | } | |
497a6e3a | 688 | virtual bool matches(const DNSQuestion* dq) const =0; |
0940e4eb | 689 | virtual string toString() const = 0; |
690 | mutable std::atomic<uint64_t> d_matches{0}; | |
691 | }; | |
692 | ||
da4e7813 | 693 | using NumberedServerVector = NumberedVector<shared_ptr<DownstreamState>>; |
497a6e3a | 694 | typedef std::function<shared_ptr<DownstreamState>(const NumberedServerVector& servers, const DNSQuestion*)> policyfunc_t; |
df111b53 | 695 | |
696 | struct ServerPolicy | |
697 | { | |
698 | string name; | |
70a57b05 | 699 | policyfunc_t policy; |
df111b53 | 700 | }; |
701 | ||
886e2cf2 RG |
702 | struct ServerPool |
703 | { | |
704 | const std::shared_ptr<DNSDistPacketCache> getCache() const { return packetCache; }; | |
705 | ||
706 | NumberedVector<shared_ptr<DownstreamState>> servers; | |
707 | std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; | |
b9f8a6c8 | 708 | std::shared_ptr<ServerPolicy> policy{nullptr}; |
886e2cf2 RG |
709 | }; |
710 | using pools_t=map<std::string,std::shared_ptr<ServerPool>>; | |
742c079a | 711 | void setPoolPolicy(pools_t& pools, const string& poolName, std::shared_ptr<ServerPolicy> policy); |
886e2cf2 RG |
712 | void addServerToPool(pools_t& pools, const string& poolName, std::shared_ptr<DownstreamState> server); |
713 | void removeServerFromPool(pools_t& pools, const string& poolName, std::shared_ptr<DownstreamState> server); | |
714 | ||
42fae326 | 715 | struct CarbonConfig |
716 | { | |
d617b22c | 717 | ComboAddress server; |
42fae326 | 718 | std::string ourname; |
d617b22c | 719 | unsigned int interval; |
42fae326 | 720 | }; |
721 | ||
ca404e94 RG |
722 | enum ednsHeaderFlags { |
723 | EDNS_HEADER_FLAG_NONE = 0, | |
724 | EDNS_HEADER_FLAG_DO = 32768 | |
725 | }; | |
726 | ||
71c94675 | 727 | extern GlobalStateHolder<SuffixMatchTree<DynBlock>> g_dynblockSMT; |
dd46e5e3 | 728 | extern DNSAction::Action g_dynBlockAction; |
71c94675 | 729 | |
d617b22c | 730 | extern GlobalStateHolder<vector<CarbonConfig> > g_carbon; |
ecbe9133 | 731 | extern GlobalStateHolder<ServerPolicy> g_policy; |
732 | extern GlobalStateHolder<servers_t> g_dstates; | |
886e2cf2 | 733 | extern GlobalStateHolder<pools_t> g_pools; |
0940e4eb | 734 | extern GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSAction> > > > g_rulactions; |
8146444b | 735 | extern GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > g_resprulactions; |
cf48b0ce | 736 | extern GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > g_cachehitresprulactions; |
638184e9 | 737 | extern GlobalStateHolder<NetmaskGroup> g_ACL; |
2e72cc0e | 738 | |
ecbe9133 | 739 | extern ComboAddress g_serverControl; // not changed during runtime |
740 | ||
f0e4dcba | 741 | extern std::vector<std::tuple<ComboAddress, bool, bool, int, std::string, std::set<int>>> g_locals; // not changed at runtime (we hope XXX) |
963bef8d | 742 | extern vector<ClientState*> g_frontends; |
ecbe9133 | 743 | extern std::string g_key; // in theory needs locking |
6ad8b29a | 744 | extern bool g_truncateTC; |
b29edbee | 745 | extern bool g_fixupCase; |
3f6d07a4 RG |
746 | extern int g_tcpRecvTimeout; |
747 | extern int g_tcpSendTimeout; | |
e0b5e49d | 748 | extern int g_udpTimeout; |
e41f8165 RG |
749 | extern uint16_t g_maxOutstanding; |
750 | extern std::atomic<bool> g_configurationDone; | |
6c1ca990 RG |
751 | extern uint64_t g_maxTCPClientThreads; |
752 | extern uint64_t g_maxTCPQueuedConnections; | |
9396d955 RG |
753 | extern size_t g_maxTCPQueriesPerConn; |
754 | extern size_t g_maxTCPConnectionDuration; | |
755 | extern size_t g_maxTCPConnectionsPerClient; | |
886e2cf2 | 756 | extern std::atomic<uint16_t> g_cacheCleaningDelay; |
f65ea0c2 | 757 | extern std::atomic<uint16_t> g_cacheCleaningPercentage; |
9e87dcb8 | 758 | extern bool g_verboseHealthChecks; |
1ea747c0 | 759 | extern uint32_t g_staleCacheEntriesTTL; |
56d68fad RG |
760 | extern bool g_apiReadWrite; |
761 | extern std::string g_apiConfigDirectory; | |
26a3cdb7 | 762 | extern bool g_servFailOnNoPolicy; |
36e763fa | 763 | extern uint32_t g_hashperturb; |
edbda1ad | 764 | extern bool g_useTCPSinglePipe; |
840ed663 | 765 | extern std::atomic<uint16_t> g_downstreamTCPCleanupInterval; |
0beaa5c8 | 766 | extern size_t g_udpVectorSize; |
ca404e94 | 767 | |
ca4252e0 RG |
768 | struct ConsoleKeyword { |
769 | std::string name; | |
770 | bool function; | |
771 | std::string parameters; | |
772 | std::string description; | |
773 | std::string toString() const | |
774 | { | |
775 | std::string res(name); | |
776 | if (function) { | |
777 | res += "(" + parameters + ")"; | |
778 | } | |
779 | res += ": "; | |
780 | res += description; | |
781 | return res; | |
782 | } | |
783 | }; | |
784 | extern const std::vector<ConsoleKeyword> g_consoleKeywords; | |
506bb661 | 785 | extern bool g_logConsoleConnections; |
ca4252e0 | 786 | |
87b515ed RG |
787 | #ifdef HAVE_EBPF |
788 | extern shared_ptr<BPFFilter> g_defaultBPFFilter; | |
8429ad04 | 789 | extern std::vector<std::shared_ptr<DynBPFFilter> > g_dynBPFFilters; |
87b515ed RG |
790 | #endif /* HAVE_EBPF */ |
791 | ||
0beaa5c8 RG |
792 | struct LocalHolders |
793 | { | |
794 | LocalHolders(): acl(g_ACL.getLocal()), policy(g_policy.getLocal()), rulactions(g_rulactions.getLocal()), cacheHitRespRulactions(g_cachehitresprulactions.getLocal()), servers(g_dstates.getLocal()), dynNMGBlock(g_dynblockNMG.getLocal()), dynSMTBlock(g_dynblockSMT.getLocal()), pools(g_pools.getLocal()) | |
795 | { | |
796 | } | |
797 | ||
798 | LocalStateHolder<NetmaskGroup> acl; | |
799 | LocalStateHolder<ServerPolicy> policy; | |
800 | LocalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSAction> > > > rulactions; | |
801 | LocalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > cacheHitRespRulactions; | |
802 | LocalStateHolder<servers_t> servers; | |
803 | LocalStateHolder<NetmaskTree<DynBlock> > dynNMGBlock; | |
804 | LocalStateHolder<SuffixMatchTree<DynBlock> > dynSMTBlock; | |
805 | LocalStateHolder<pools_t> pools; | |
806 | }; | |
807 | ||
ecbe9133 | 808 | struct dnsheader; |
809 | ||
810 | void controlThread(int fd, ComboAddress local); | |
839f3021 | 811 | vector<std::function<void(void)>> setupLua(bool client, const std::string& config); |
886e2cf2 RG |
812 | std::shared_ptr<ServerPool> getPool(const pools_t& pools, const std::string& poolName); |
813 | std::shared_ptr<ServerPool> createPoolIfNotExists(pools_t& pools, const string& poolName); | |
814 | const NumberedServerVector& getDownstreamCandidates(const pools_t& pools, const std::string& poolName); | |
da4e7813 | 815 | |
497a6e3a | 816 | std::shared_ptr<DownstreamState> firstAvailable(const NumberedServerVector& servers, const DNSQuestion* dq); |
ecbe9133 | 817 | |
497a6e3a RG |
818 | std::shared_ptr<DownstreamState> leastOutstanding(const NumberedServerVector& servers, const DNSQuestion* dq); |
819 | std::shared_ptr<DownstreamState> wrandom(const NumberedServerVector& servers, const DNSQuestion* dq); | |
820 | std::shared_ptr<DownstreamState> whashed(const NumberedServerVector& servers, const DNSQuestion* dq); | |
821 | std::shared_ptr<DownstreamState> roundrobin(const NumberedServerVector& servers, const DNSQuestion* dq); | |
520eb5a0 | 822 | int getEDNSZ(const char* packet, unsigned int len); |
ca404e94 | 823 | uint16_t getEDNSOptionCode(const char * packet, size_t len); |
002decab | 824 | void dnsdistWebserverThread(int sock, const ComboAddress& local, const string& password, const string& apiKey, const boost::optional<std::map<std::string, std::string> >&); |
6885d4bf | 825 | bool getMsgLen32(int fd, uint32_t* len); |
826 | bool putMsgLen32(int fd, uint32_t len); | |
d8d85a30 | 827 | void* tcpAcceptorThread(void* p); |
80a216c9 | 828 | |
ffb07158 | 829 | void doClient(ComboAddress server, const std::string& command); |
830 | void doConsole(); | |
831 | void controlClientThread(int fd, ComboAddress client); | |
832 | extern "C" { | |
833 | char** my_completion( const char * text , int start, int end); | |
834 | } | |
f758857a | 835 | void setLuaNoSideEffect(); // if nothing has been declared, set that there are no side effects |
836 | void setLuaSideEffect(); // set to report a side effect, cancelling all _no_ side effect calls | |
837 | bool getLuaNoSideEffect(); // set if there were only explicit declarations of _no_ side effect | |
838 | void resetLuaSideEffect(); // reset to indeterminate state | |
11e1e08b | 839 | |
fcffc585 | 840 | bool responseContentMatches(const char* response, const uint16_t responseLen, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const ComboAddress& remote); |
0beaa5c8 | 841 | bool processQuery(LocalHolders& holders, DNSQuestion& dq, string& poolname, int* delayMsec, const struct timespec& now); |
788c3243 | 842 | bool processResponse(LocalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > >& localRespRulactions, DNSResponse& dr, int* delayMsec); |
ff73f02b | 843 | bool fixUpResponse(char** response, uint16_t* responseLen, size_t* responseSize, const DNSName& qname, uint16_t origFlags, bool ednsAdded, bool ecsAdded, std::vector<uint8_t>& rewrittenResponse, uint16_t addRoom); |
0f72fd5c | 844 | void restoreFlags(struct dnsheader* dh, uint16_t origFlags); |
0beaa5c8 | 845 | bool checkQueryHeaders(const struct dnsheader* dh); |
fcffc585 | 846 | |
11e1e08b | 847 | #ifdef HAVE_DNSCRYPT |
f0e4dcba | 848 | extern std::vector<std::tuple<ComboAddress,DnsCryptContext,bool,int, std::string, std::set<int>>> g_dnsCryptLocals; |
11e1e08b | 849 | |
3596a52b | 850 | int handleDnsCryptQuery(DnsCryptContext* ctx, char* packet, uint16_t len, std::shared_ptr<DnsCryptQuery>& query, uint16_t* decryptedQueryLen, bool tcp, std::vector<uint8_t>& response); |
57847d65 | 851 | bool encryptResponse(char* response, uint16_t* responseLen, size_t responseSize, bool tcp, std::shared_ptr<DnsCryptQuery> dnsCryptQuery, dnsheader** dh, dnsheader* dhCopy); |
11e1e08b | 852 | #endif |
9f4eb5cc RG |
853 | |
854 | #include "dnsdist-snmp.hh" | |
855 | ||
856 | extern bool g_snmpEnabled; | |
857 | extern bool g_snmpTrapsEnabled; | |
858 | extern DNSDistSNMPAgent* g_snmpAgent; |