]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/auth-packetcache.cc
Merge pull request #8732 from pieterlexis/remote-support-getUnfreshSlaveInfos
[thirdparty/pdns.git] / pdns / auth-packetcache.cc
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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "auth-packetcache.hh"
26 #include "logger.hh"
27 #include "statbag.hh"
28 #include "cachecleaner.hh"
29 extern StatBag S;
30
31 const unsigned int AuthPacketCache::s_mincleaninterval, AuthPacketCache::s_maxcleaninterval;
32
33 AuthPacketCache::AuthPacketCache(size_t mapsCount): d_maps(mapsCount), d_lastclean(time(nullptr))
34 {
35 S.declare("packetcache-hit", "Number of hits on the packet cache");
36 S.declare("packetcache-miss", "Number of misses on the packet cache");
37 S.declare("packetcache-size", "Number of entries in the packet cache");
38 S.declare("deferred-packetcache-inserts","Amount of packet cache inserts that were deferred because of maintenance");
39 S.declare("deferred-packetcache-lookup","Amount of packet cache lookups that were deferred because of maintenance");
40
41 d_statnumhit=S.getPointer("packetcache-hit");
42 d_statnummiss=S.getPointer("packetcache-miss");
43 d_statnumentries=S.getPointer("packetcache-size");
44 }
45
46 AuthPacketCache::~AuthPacketCache()
47 {
48 try {
49 vector<WriteLock*> locks;
50 for(auto& mc : d_maps) {
51 locks.push_back(new WriteLock(&mc.d_mut));
52 }
53 for(auto wl : locks) {
54 delete wl;
55 }
56 }
57 catch(...) {
58 }
59 }
60
61 bool AuthPacketCache::get(DNSPacket& p, DNSPacket& cached)
62 {
63 if(!d_ttl) {
64 return false;
65 }
66
67 cleanupIfNeeded();
68
69 uint32_t hash = canHashPacket(p.getString());
70 p.setHash(hash);
71
72 string value;
73 bool haveSomething;
74 time_t now = time(nullptr);
75 auto& mc = getMap(p.qdomain);
76 {
77 TryReadLock rl(&mc.d_mut);
78 if(!rl.gotIt()) {
79 S.inc("deferred-packetcache-lookup");
80 return false;
81 }
82
83 haveSomething = getEntryLocked(mc.d_map, p.getString(), hash, p.qdomain, p.qtype.getCode(), p.d_tcp, now, value);
84 }
85
86 if (!haveSomething) {
87 (*d_statnummiss)++;
88 return false;
89 }
90
91 if(cached.noparse(value.c_str(), value.size()) < 0) {
92 return false;
93 }
94
95 (*d_statnumhit)++;
96 cached.spoofQuestion(p); // for correct case
97 cached.qdomain = p.qdomain;
98 cached.qtype = p.qtype;
99
100 return true;
101 }
102
103 bool AuthPacketCache::entryMatches(cmap_t::index<HashTag>::type::iterator& iter, const std::string& query, const DNSName& qname, uint16_t qtype, bool tcp)
104 {
105 return iter->tcp == tcp && iter->qtype == qtype && iter->qname == qname && queryMatches(iter->query, query, qname);
106 }
107
108 void AuthPacketCache::insert(DNSPacket& q, DNSPacket& r, unsigned int maxTTL)
109 {
110 if(!d_ttl) {
111 return;
112 }
113
114 cleanupIfNeeded();
115
116 if (ntohs(q.d.qdcount) != 1) {
117 return; // do not try to cache packets with multiple questions
118 }
119
120 if (q.qclass != QClass::IN) // we only cache the INternet
121 return;
122
123 uint32_t ourttl = std::min(d_ttl, maxTTL);
124 if (ourttl == 0) {
125 return;
126 }
127
128 uint32_t hash = q.getHash();
129 time_t now = time(nullptr);
130 CacheEntry entry;
131 entry.hash = hash;
132 entry.created = now;
133 entry.ttd = now + ourttl;
134 entry.qname = q.qdomain;
135 entry.qtype = q.qtype.getCode();
136 entry.value = r.getString();
137 entry.tcp = r.d_tcp;
138 entry.query = q.getString();
139
140 auto& mc = getMap(entry.qname);
141 {
142 TryWriteLock l(&mc.d_mut);
143 if (!l.gotIt()) {
144 S.inc("deferred-packetcache-inserts");
145 return;
146 }
147
148 auto& idx = mc.d_map.get<HashTag>();
149 auto range = idx.equal_range(hash);
150 auto iter = range.first;
151
152 for( ; iter != range.second ; ++iter) {
153 if (!entryMatches(iter, entry.query, entry.qname, entry.qtype, entry.tcp)) {
154 continue;
155 }
156
157 moveCacheItemToBack<SequencedTag>(mc.d_map, iter);
158 iter->value = entry.value;
159 iter->ttd = now + ourttl;
160 iter->created = now;
161 return;
162 }
163
164 /* no existing entry found to refresh */
165 mc.d_map.insert(entry);
166
167 if (*d_statnumentries >= d_maxEntries) {
168 /* remove the least recently inserted or replaced entry */
169 auto& sidx = mc.d_map.get<SequencedTag>();
170 sidx.pop_front();
171 }
172 else {
173 (*d_statnumentries)++;
174 }
175 }
176 }
177
178 bool AuthPacketCache::getEntryLocked(cmap_t& map, const std::string& query, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& value)
179 {
180 auto& idx = map.get<HashTag>();
181 auto range = idx.equal_range(hash);
182
183 for(auto iter = range.first; iter != range.second ; ++iter) {
184 if (iter->ttd < now) {
185 continue;
186 }
187
188 if (!entryMatches(iter, query, qname, qtype, tcp)) {
189 continue;
190 }
191
192 value = iter->value;
193 return true;
194 }
195
196 return false;
197 }
198
199 /* clears the entire cache. */
200 uint64_t AuthPacketCache::purge()
201 {
202 if(!d_ttl) {
203 return 0;
204 }
205
206 d_statnumentries->store(0);
207
208 return purgeLockedCollectionsVector(d_maps);
209 }
210
211 uint64_t AuthPacketCache::purgeExact(const DNSName& qname)
212 {
213 auto& mc = getMap(qname);
214 uint64_t delcount = purgeExactLockedCollection<NameTag>(mc, qname);
215
216 *d_statnumentries -= delcount;
217
218 return delcount;
219 }
220
221 /* purges entries from the packetcache. If match ends on a $, it is treated as a suffix */
222 uint64_t AuthPacketCache::purge(const string &match)
223 {
224 if(!d_ttl) {
225 return 0;
226 }
227
228 uint64_t delcount = 0;
229
230 if(ends_with(match, "$")) {
231 delcount = purgeLockedCollectionsVector<NameTag>(d_maps, match);
232 *d_statnumentries -= delcount;
233 }
234 else {
235 delcount = purgeExact(DNSName(match));
236 }
237
238 return delcount;
239 }
240
241 void AuthPacketCache::cleanup()
242 {
243 uint64_t maxCached = d_maxEntries;
244 uint64_t cacheSize = *d_statnumentries;
245 uint64_t totErased = 0;
246
247 totErased = pruneLockedCollectionsVector<SequencedTag>(d_maps, maxCached, cacheSize);
248 *d_statnumentries -= totErased;
249
250 DLOG(g_log<<"Done with cache clean, cacheSize: "<<(*d_statnumentries)<<", totErased"<<totErased<<endl);
251 }
252
253 /* the logic:
254 after d_nextclean operations, we clean. We also adjust the cleaninterval
255 a bit so we slowly move it to a value where we clean roughly every 30 seconds.
256
257 If d_nextclean has reached its maximum value, we also test if we were called
258 within 30 seconds, and if so, we skip cleaning. This means that under high load,
259 we will not clean more often than every 30 seconds anyhow.
260 */
261
262 void AuthPacketCache::cleanupIfNeeded()
263 {
264 if (d_ops++ == d_nextclean) {
265 time_t now = time(nullptr);
266 int timediff = max((int)(now - d_lastclean), 1);
267
268 DLOG(g_log<<"cleaninterval: "<<d_cleaninterval<<", timediff: "<<timediff<<endl);
269
270 if (d_cleaninterval == s_maxcleaninterval && timediff < 30) {
271 d_cleanskipped = true;
272 d_nextclean += d_cleaninterval;
273
274 DLOG(g_log<<"cleaning skipped, timediff: "<<timediff<<endl);
275
276 return;
277 }
278
279 if(!d_cleanskipped) {
280 d_cleaninterval=(int)(0.6*d_cleaninterval)+(0.4*d_cleaninterval*(30.0/timediff));
281 d_cleaninterval=std::max(d_cleaninterval, s_mincleaninterval);
282 d_cleaninterval=std::min(d_cleaninterval, s_maxcleaninterval);
283
284 DLOG(g_log<<"new cleaninterval: "<<d_cleaninterval<<endl);
285 } else {
286 d_cleanskipped = false;
287 }
288
289 d_nextclean += d_cleaninterval;
290 d_lastclean=now;
291 cleanup();
292 }
293 }