]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/auth-packetcache.cc
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
)
74 uint32_t hash
= canHashPacket(p
->getString());
79 time_t now
= time(nullptr);
80 auto& mc
= getMap(p
->qdomain
);
82 TryReadLock
rl(&mc
.d_mut
);
84 S
.inc("deferred-packetcache-lookup");
88 haveSomething
= getEntryLocked(mc
.d_map
, p
->getString(), hash
, p
->qdomain
, p
->qtype
.getCode(), p
->d_tcp
, now
, value
);
96 if(cached
->noparse(value
.c_str(), value
.size()) < 0) {
101 cached
->spoofQuestion(p
); // for correct case
102 cached
->qdomain
= p
->qdomain
;
103 cached
->qtype
= p
->qtype
;
108 bool AuthPacketCache::entryMatches(cmap_t::index
<HashTag
>::type::iterator
& iter
, const std::string
& query
, const DNSName
& qname
, uint16_t qtype
, bool tcp
)
110 return iter
->tcp
== tcp
&& iter
->qtype
== qtype
&& iter
->qname
== qname
&& queryMatches(iter
->query
, query
, qname
);
113 void AuthPacketCache::insert(DNSPacket
*q
, DNSPacket
*r
, unsigned int maxTTL
)
121 if (ntohs(q
->d
.qdcount
) != 1) {
122 return; // do not try to cache packets with multiple questions
125 if (q
->qclass
!= QClass::IN
) // we only cache the INternet
128 uint32_t ourttl
= std::min(d_ttl
, maxTTL
);
133 uint32_t hash
= q
->getHash();
134 time_t now
= time(nullptr);
138 entry
.ttd
= now
+ ourttl
;
139 entry
.qname
= q
->qdomain
;
140 entry
.qtype
= q
->qtype
.getCode();
141 entry
.value
= r
->getString();
142 entry
.tcp
= r
->d_tcp
;
143 entry
.query
= q
->getString();
145 auto& mc
= getMap(entry
.qname
);
147 TryWriteLock
l(&mc
.d_mut
);
149 S
.inc("deferred-packetcache-inserts");
153 auto& idx
= mc
.d_map
.get
<HashTag
>();
154 auto range
= idx
.equal_range(hash
);
155 auto iter
= range
.first
;
157 for( ; iter
!= range
.second
; ++iter
) {
158 if (!entryMatches(iter
, entry
.query
, entry
.qname
, entry
.qtype
, entry
.tcp
)) {
162 iter
->value
= entry
.value
;
163 iter
->ttd
= now
+ ourttl
;
168 /* no existing entry found to refresh */
169 mc
.d_map
.insert(entry
);
170 (*d_statnumentries
)++;
174 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
)
176 auto& idx
= map
.get
<HashTag
>();
177 auto range
= idx
.equal_range(hash
);
179 for(auto iter
= range
.first
; iter
!= range
.second
; ++iter
) {
180 if (iter
->ttd
< now
) {
184 if (!entryMatches(iter
, query
, qname
, qtype
, tcp
)) {
195 /* clears the entire cache. */
196 uint64_t AuthPacketCache::purge()
202 d_statnumentries
->store(0);
204 return purgeLockedCollectionsVector(d_maps
);
207 uint64_t AuthPacketCache::purgeExact(const DNSName
& qname
)
209 auto& mc
= getMap(qname
);
210 uint64_t delcount
= purgeExactLockedCollection(mc
, qname
);
212 *d_statnumentries
-= delcount
;
217 /* purges entries from the packetcache. If match ends on a $, it is treated as a suffix */
218 uint64_t AuthPacketCache::purge(const string
&match
)
224 uint64_t delcount
= 0;
226 if(ends_with(match
, "$")) {
227 delcount
= purgeLockedCollectionsVector(d_maps
, match
);
228 *d_statnumentries
-= delcount
;
231 delcount
= purgeExact(DNSName(match
));
237 void AuthPacketCache::cleanup()
239 uint64_t maxCached
= d_maxEntries
;
240 uint64_t cacheSize
= *d_statnumentries
;
241 uint64_t totErased
= 0;
243 totErased
= pruneLockedCollectionsVector(d_maps
, maxCached
, cacheSize
);
244 *d_statnumentries
-= totErased
;
246 DLOG(g_log
<<"Done with cache clean, cacheSize: "<<(*d_statnumentries
)<<", totErased"<<totErased
<<endl
);
250 after d_nextclean operations, we clean. We also adjust the cleaninterval
251 a bit so we slowly move it to a value where we clean roughly every 30 seconds.
253 If d_nextclean has reached its maximum value, we also test if we were called
254 within 30 seconds, and if so, we skip cleaning. This means that under high load,
255 we will not clean more often than every 30 seconds anyhow.
258 void AuthPacketCache::cleanupIfNeeded()
260 if (d_ops
++ == d_nextclean
) {
261 time_t now
= time(nullptr);
262 int timediff
= max((int)(now
- d_lastclean
), 1);
264 DLOG(g_log
<<"cleaninterval: "<<d_cleaninterval
<<", timediff: "<<timediff
<<endl
);
266 if (d_cleaninterval
== s_maxcleaninterval
&& timediff
< 30) {
267 d_cleanskipped
= true;
268 d_nextclean
+= d_cleaninterval
;
270 DLOG(g_log
<<"cleaning skipped, timediff: "<<timediff
<<endl
);
275 if(!d_cleanskipped
) {
276 d_cleaninterval
=(int)(0.6*d_cleaninterval
)+(0.4*d_cleaninterval
*(30.0/timediff
));
277 d_cleaninterval
=std::max(d_cleaninterval
, s_mincleaninterval
);
278 d_cleaninterval
=std::min(d_cleaninterval
, s_maxcleaninterval
);
280 DLOG(g_log
<<"new cleaninterval: "<<d_cleaninterval
<<endl
);
282 d_cleanskipped
= false;
285 d_nextclean
+= d_cleaninterval
;