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