]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsdist-idstate.hh
Merge pull request #12587 from rgacogne/ddist-no-tsan-signal-warning
[thirdparty/pdns.git] / pdns / dnsdist-idstate.hh
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
24 #include "config.h"
25 #include "dnsname.hh"
26 #include "dnsdist-protocols.hh"
27 #include "gettime.hh"
28 #include "iputils.hh"
29 #include "uuid-utils.hh"
30
31 struct ClientState;
32 struct DOHUnit;
33 class DNSCryptQuery;
34 class DNSDistPacketCache;
35
36 using QTag = std::unordered_map<string, string>;
37
38 struct StopWatch
39 {
40 StopWatch(bool realTime = false) :
41 d_needRealTime(realTime)
42 {
43 }
44
45 void start()
46 {
47 d_start = getCurrentTime();
48 }
49
50 void set(const struct timespec& from)
51 {
52 d_start = from;
53 }
54
55 double udiff() const
56 {
57 struct timespec now = getCurrentTime();
58 return 1000000.0 * (now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec) / 1000.0;
59 }
60
61 double udiffAndSet()
62 {
63 struct timespec now = getCurrentTime();
64 auto ret = 1000000.0 * (now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec) / 1000.0;
65 d_start = now;
66 return ret;
67 }
68
69 struct timespec getStartTime() const
70 {
71 return d_start;
72 }
73
74 struct timespec d_start
75 {
76 0, 0
77 };
78
79 private:
80 struct timespec getCurrentTime() const
81 {
82 struct timespec now;
83 if (gettime(&now, d_needRealTime) < 0) {
84 unixDie("Getting timestamp");
85 }
86 return now;
87 }
88
89 bool d_needRealTime;
90 };
91
92 struct InternalQueryState
93 {
94 static void DeleterPlaceHolder(DOHUnit*)
95 {
96 }
97
98 InternalQueryState() :
99 du(std::unique_ptr<DOHUnit, void (*)(DOHUnit*)>(nullptr, DeleterPlaceHolder))
100 {
101 origDest.sin4.sin_family = 0;
102 }
103
104 InternalQueryState(InternalQueryState&& rhs) = default;
105 InternalQueryState& operator=(InternalQueryState&& rhs) = default;
106
107 InternalQueryState(const InternalQueryState& orig) = delete;
108 InternalQueryState& operator=(const InternalQueryState& orig) = delete;
109
110 boost::optional<Netmask> subnet{boost::none}; // 40
111 ComboAddress origRemote; // 28
112 ComboAddress origDest; // 28
113 ComboAddress hopRemote;
114 ComboAddress hopLocal;
115 DNSName qname; // 24
116 std::string poolName; // 24
117 StopWatch queryRealTime{true}; // 16
118 std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; // 16
119 std::unique_ptr<DNSCryptQuery> dnsCryptQuery{nullptr}; // 8
120 std::unique_ptr<QTag> qTag{nullptr}; // 8
121 std::unique_ptr<PacketBuffer> d_packet; // Initial packet, so we can restart the query from the response path if needed // 8
122 boost::optional<uint32_t> tempFailureTTL{boost::none}; // 8
123 ClientState* cs{nullptr}; // 8
124 std::unique_ptr<DOHUnit, void (*)(DOHUnit*)> du; // 8
125 uint32_t cacheKey{0}; // 4
126 uint32_t cacheKeyNoECS{0}; // 4
127 // DoH-only */
128 uint32_t cacheKeyUDP{0}; // 4
129 uint32_t ttlCap{0}; // cap the TTL _after_ inserting into the packet cache // 4
130 int backendFD{-1}; // 4
131 int delayMsec{0};
132 uint16_t qtype{0}; // 2
133 uint16_t qclass{0}; // 2
134 // origID is in network-byte order
135 uint16_t origID{0}; // 2
136 uint16_t origFlags{0}; // 2
137 uint16_t cacheFlags{0}; // DNS flags as sent to the backend // 2
138 uint16_t udpPayloadSize{0}; // Max UDP payload size from the query // 2
139 dnsdist::Protocol protocol; // 1
140 boost::optional<boost::uuids::uuid> uniqueId{boost::none}; // 17 (placed here to reduce the space lost to padding)
141 bool ednsAdded{false};
142 bool ecsAdded{false};
143 bool skipCache{false};
144 bool dnssecOK{false};
145 bool useZeroScope{false};
146 bool forwardedOverUDP{false};
147 bool selfGenerated{false};
148 };
149
150 struct IDState
151 {
152 IDState()
153 {
154 }
155
156 IDState(const IDState& orig) = delete;
157 IDState(IDState&& rhs) noexcept :
158 internal(std::move(rhs.internal))
159 {
160 inUse.store(rhs.inUse.load());
161 age.store(rhs.age.load());
162 }
163
164 IDState& operator=(IDState&& rhs) noexcept
165 {
166 inUse.store(rhs.inUse.load());
167 age.store(rhs.age.load());
168 internal = std::move(rhs.internal);
169 return *this;
170 }
171
172 bool isInUse() const
173 {
174 return inUse;
175 }
176
177 /* For performance reasons we don't want to use a lock here, but that means
178 we need to be very careful when modifying this value. Modifications happen
179 from:
180 - one of the UDP or DoH 'client' threads receiving a query, selecting a backend
181 then picking one of the states associated to this backend (via the idOffset).
182 Most of the time this state should not be in use and usageIndicator is -1, but we
183 might not yet have received a response for the query previously associated to this
184 state, meaning that we will 'reuse' this state and erase the existing state.
185 If we ever receive a response for this state, it will be discarded. This is
186 mostly fine for UDP except that we still need to be careful in order to miss
187 the 'outstanding' counters, which should only be increased when we are picking
188 an empty state, and not when reusing ;
189 For DoH, though, we have dynamically allocated a DOHUnit object that needs to
190 be freed, as well as internal objects internals to libh2o.
191 - one of the UDP receiver threads receiving a response from a backend, picking
192 the corresponding state and sending the response to the client ;
193 - the 'healthcheck' thread scanning the states to actively discover timeouts,
194 mostly to keep some counters like the 'outstanding' one sane.
195
196 We have two flags:
197 - inUse tells us if there currently is a in-flight query whose state is stored
198 in this state
199 - locked tells us whether someone currently owns the state, so no-one else can touch
200 it
201 */
202 InternalQueryState internal;
203 std::atomic<uint16_t> age{0};
204
205 class StateGuard
206 {
207 public:
208 StateGuard(IDState& ids) :
209 d_ids(ids)
210 {
211 }
212 ~StateGuard()
213 {
214 d_ids.release();
215 }
216 StateGuard(const StateGuard&) = delete;
217 StateGuard(StateGuard&&) = delete;
218 StateGuard& operator=(const StateGuard&) = delete;
219 StateGuard& operator=(StateGuard&&) = delete;
220
221 private:
222 IDState& d_ids;
223 };
224
225 [[nodiscard]] std::optional<StateGuard> acquire()
226 {
227 bool expected = false;
228 if (locked.compare_exchange_strong(expected, true)) {
229 return std::optional<StateGuard>(*this);
230 }
231 return std::nullopt;
232 }
233
234 void release()
235 {
236 locked.store(false);
237 }
238
239 std::atomic<bool> inUse{false}; // 1
240
241 private:
242 std::atomic<bool> locked{false}; // 1
243 };