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