]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/auth-packetcache.cc
f28a6fdc37102bd69dce913e2a463d3942cdae38
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
25 #include "auth-packetcache.hh"
28 #include "cachecleaner.hh"
31 const unsigned int AuthPacketCache::s_mincleaninterval
, AuthPacketCache::s_maxcleaninterval
;
33 AuthPacketCache::AuthPacketCache(size_t mapsCount
): d_lastclean(time(nullptr))
35 d_maps
.resize(mapsCount
);
36 for(auto& mc
: d_maps
) {
37 pthread_rwlock_init(&mc
.d_mut
, 0);
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");
46 d_statnumhit
=S
.getPointer("packetcache-hit");
47 d_statnummiss
=S
.getPointer("packetcache-miss");
48 d_statnumentries
=S
.getPointer("packetcache-size");
51 AuthPacketCache::~AuthPacketCache()
54 vector
<WriteLock
*> locks
;
55 for(auto& mc
: d_maps
) {
56 locks
.push_back(new WriteLock(&mc
.d_mut
));
58 for(auto wl
: locks
) {
66 bool AuthPacketCache::get(DNSPacket
*p
, DNSPacket
*cached
)
75 uint32_t hash
= canHashPacket(p
->getString(), false);
80 time_t now
= time(nullptr);
81 auto& mc
= getMap(p
->qdomain
);
83 TryReadLock
rl(&mc
.d_mut
);
85 S
.inc("deferred-packetcache-lookup");
89 haveSomething
= getEntryLocked(mc
.d_map
, hash
, p
->qdomain
, p
->qtype
.getCode(), p
->d_tcp
, now
, value
);
97 if(cached
->noparse(value
.c_str(), value
.size()) < 0) {
102 cached
->spoofQuestion(p
); // for correct case
103 cached
->qdomain
= p
->qdomain
;
104 cached
->qtype
= p
->qtype
;
109 void AuthPacketCache::insert(DNSPacket
*q
, DNSPacket
*r
, unsigned int maxTTL
)
113 if (ntohs(q
->d
.qdcount
) != 1) {
114 return; // do not try to cache packets with multiple questions
117 if (q
->qclass
!= QClass::IN
) // we only cache the INternet
120 uint32_t ourttl
= std::min(d_ttl
, maxTTL
);
125 uint32_t hash
= q
->getHash();
126 time_t now
= time(nullptr);
130 entry
.ttd
= now
+ ourttl
;
131 entry
.qname
= q
->qdomain
;
132 entry
.qtype
= q
->qtype
.getCode();
133 entry
.value
= r
->getString();
134 entry
.tcp
= r
->d_tcp
;
136 auto& mc
= getMap(entry
.qname
);
138 TryWriteLock
l(&mc
.d_mut
);
140 S
.inc("deferred-packetcache-inserts");
144 auto& idx
= mc
.d_map
.get
<HashTag
>();
145 auto range
= idx
.equal_range(hash
);
146 auto iter
= range
.first
;
148 for( ; iter
!= range
.second
; ++iter
) {
149 if (iter
->tcp
!= entry
.tcp
|| iter
->qtype
!= entry
.qtype
|| iter
->qname
!= entry
.qname
)
152 iter
->value
= entry
.value
;
153 iter
->ttd
= now
+ ourttl
;
158 /* no existing entry found to refresh */
159 mc
.d_map
.insert(entry
);
160 (*d_statnumentries
)++;
164 bool AuthPacketCache::getEntryLocked(cmap_t
& map
, uint32_t hash
, const DNSName
&qname
, uint16_t qtype
, bool tcp
, time_t now
, string
& value
)
166 auto& idx
= map
.get
<HashTag
>();
167 auto range
= idx
.equal_range(hash
);
169 for(auto iter
= range
.first
; iter
!= range
.second
; ++iter
) {
173 if (iter
->tcp
!= tcp
|| iter
->qtype
!= qtype
|| iter
->qname
!= qname
)
183 /* clears the entire cache. */
184 uint64_t AuthPacketCache::purge()
186 d_statnumentries
->store(0);
188 return purgeLockedCollectionsVector(d_maps
);
191 uint64_t AuthPacketCache::purgeExact(const DNSName
& qname
)
193 auto& mc
= getMap(qname
);
194 uint64_t delcount
= purgeExactLockedCollection(mc
, qname
);
196 *d_statnumentries
-= delcount
;
201 /* purges entries from the packetcache. If match ends on a $, it is treated as a suffix */
202 uint64_t AuthPacketCache::purge(const string
&match
)
204 uint64_t delcount
= 0;
206 if(ends_with(match
, "$")) {
207 delcount
= purgeLockedCollectionsVector(d_maps
, match
);
208 *d_statnumentries
-= delcount
;
211 delcount
= purgeExact(DNSName(match
));
217 void AuthPacketCache::cleanup()
219 uint64_t maxCached
= d_maxEntries
;
220 uint64_t cacheSize
= *d_statnumentries
;
221 uint64_t totErased
= 0;
223 totErased
= pruneLockedCollectionsVector(d_maps
, maxCached
, cacheSize
);
224 *d_statnumentries
-= totErased
;
226 DLOG(L
<<"Done with cache clean, cacheSize: "<<(*d_statnumentries
)<<", totErased"<<totErased
<<endl
);
230 after d_nextclean operations, we clean. We also adjust the cleaninterval
231 a bit so we slowly move it to a value where we clean roughly every 30 seconds.
233 If d_nextclean has reached its maximum value, we also test if we were called
234 within 30 seconds, and if so, we skip cleaning. This means that under high load,
235 we will not clean more often than every 30 seconds anyhow.
238 void AuthPacketCache::cleanupIfNeeded()
240 if (d_ops
++ == d_nextclean
) {
241 time_t now
= time(nullptr);
242 int timediff
= max((int)(now
- d_lastclean
), 1);
244 DLOG(L
<<"cleaninterval: "<<d_cleaninterval
<<", timediff: "<<timediff
<<endl
);
246 if (d_cleaninterval
== s_maxcleaninterval
&& timediff
< 30) {
247 d_cleanskipped
= true;
248 d_nextclean
+= d_cleaninterval
;
250 DLOG(L
<<"cleaning skipped, timediff: "<<timediff
<<endl
);
255 if(!d_cleanskipped
) {
256 d_cleaninterval
=(int)(0.6*d_cleaninterval
)+(0.4*d_cleaninterval
*(30.0/timediff
));
257 d_cleaninterval
=std::max(d_cleaninterval
, s_mincleaninterval
);
258 d_cleaninterval
=std::min(d_cleaninterval
, s_maxcleaninterval
);
260 DLOG(L
<<"new cleaninterval: "<<d_cleaninterval
<<endl
);
262 d_cleanskipped
= false;
265 d_nextclean
+= d_cleaninterval
;