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