]>
Commit | Line | Data |
---|---|---|
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" | |
29 | extern StatBag S; | |
30 | ||
31 | const unsigned int AuthPacketCache::s_mincleaninterval, AuthPacketCache::s_maxcleaninterval; | |
32 | ||
33 | AuthPacketCache::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 | ||
51 | AuthPacketCache::~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 | ||
66 | bool AuthPacketCache::get(DNSPacket *p, DNSPacket *cached) | |
67 | { | |
bf269e28 | 68 | if(!d_ttl) { |
bf269e28 RG |
69 | return false; |
70 | } | |
71 | ||
9a037bfa KM |
72 | cleanupIfNeeded(); |
73 | ||
08b02366 | 74 | uint32_t hash = canHashPacket(p->getString()); |
bf269e28 RG |
75 | p->setHash(hash); |
76 | ||
77 | string value; | |
78 | bool haveSomething; | |
79 | time_t now = time(nullptr); | |
80 | auto& mc = getMap(p->qdomain); | |
81 | { | |
82 | TryReadLock rl(&mc.d_mut); | |
83 | if(!rl.gotIt()) { | |
84 | S.inc("deferred-packetcache-lookup"); | |
85 | return false; | |
86 | } | |
87 | ||
08b02366 | 88 | haveSomething = getEntryLocked(mc.d_map, p->getString(), hash, p->qdomain, p->qtype.getCode(), p->d_tcp, now, value); |
bf269e28 RG |
89 | } |
90 | ||
91 | if (!haveSomething) { | |
92 | (*d_statnummiss)++; | |
93 | return false; | |
94 | } | |
95 | ||
96 | if(cached->noparse(value.c_str(), value.size()) < 0) { | |
97 | return false; | |
98 | } | |
99 | ||
100 | (*d_statnumhit)++; | |
101 | cached->spoofQuestion(p); // for correct case | |
102 | cached->qdomain = p->qdomain; | |
103 | cached->qtype = p->qtype; | |
104 | ||
105 | return true; | |
106 | } | |
107 | ||
08b02366 RG |
108 | bool AuthPacketCache::entryMatches(cmap_t::index<HashTag>::type::iterator& iter, const std::string& query, const DNSName& qname, uint16_t qtype, bool tcp) |
109 | { | |
110 | return iter->tcp == tcp && iter->qtype == qtype && iter->qname == qname && queryMatches(iter->query, query, qname); | |
111 | } | |
112 | ||
bf269e28 RG |
113 | void AuthPacketCache::insert(DNSPacket *q, DNSPacket *r, unsigned int maxTTL) |
114 | { | |
9a037bfa KM |
115 | if(!d_ttl) { |
116 | return; | |
117 | } | |
118 | ||
bf269e28 RG |
119 | cleanupIfNeeded(); |
120 | ||
121 | if (ntohs(q->d.qdcount) != 1) { | |
122 | return; // do not try to cache packets with multiple questions | |
123 | } | |
124 | ||
125 | if (q->qclass != QClass::IN) // we only cache the INternet | |
126 | return; | |
127 | ||
128 | uint32_t ourttl = std::min(d_ttl, maxTTL); | |
129 | if (ourttl == 0) { | |
130 | return; | |
131 | } | |
132 | ||
133 | uint32_t hash = q->getHash(); | |
134 | time_t now = time(nullptr); | |
135 | CacheEntry entry; | |
136 | entry.hash = hash; | |
137 | entry.created = now; | |
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; | |
08b02366 | 143 | entry.query = q->getString(); |
bf269e28 RG |
144 | |
145 | auto& mc = getMap(entry.qname); | |
146 | { | |
147 | TryWriteLock l(&mc.d_mut); | |
148 | if (!l.gotIt()) { | |
149 | S.inc("deferred-packetcache-inserts"); | |
150 | return; | |
151 | } | |
152 | ||
153 | auto& idx = mc.d_map.get<HashTag>(); | |
154 | auto range = idx.equal_range(hash); | |
155 | auto iter = range.first; | |
156 | ||
157 | for( ; iter != range.second ; ++iter) { | |
08b02366 | 158 | if (!entryMatches(iter, entry.query, entry.qname, entry.qtype, entry.tcp)) { |
bf269e28 | 159 | continue; |
08b02366 | 160 | } |
bf269e28 RG |
161 | |
162 | iter->value = entry.value; | |
163 | iter->ttd = now + ourttl; | |
164 | iter->created = now; | |
165 | return; | |
166 | } | |
167 | ||
168 | /* no existing entry found to refresh */ | |
169 | mc.d_map.insert(entry); | |
170 | (*d_statnumentries)++; | |
171 | } | |
172 | } | |
173 | ||
08b02366 | 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) |
bf269e28 RG |
175 | { |
176 | auto& idx = map.get<HashTag>(); | |
177 | auto range = idx.equal_range(hash); | |
178 | ||
179 | for(auto iter = range.first; iter != range.second ; ++iter) { | |
08b02366 | 180 | if (iter->ttd < now) { |
bf269e28 | 181 | continue; |
08b02366 | 182 | } |
bf269e28 | 183 | |
08b02366 | 184 | if (!entryMatches(iter, query, qname, qtype, tcp)) { |
bf269e28 | 185 | continue; |
08b02366 | 186 | } |
bf269e28 RG |
187 | |
188 | value = iter->value; | |
189 | return true; | |
190 | } | |
191 | ||
192 | return false; | |
193 | } | |
194 | ||
195 | /* clears the entire cache. */ | |
196 | uint64_t AuthPacketCache::purge() | |
197 | { | |
9a037bfa KM |
198 | if(!d_ttl) { |
199 | return 0; | |
200 | } | |
201 | ||
bf269e28 RG |
202 | d_statnumentries->store(0); |
203 | ||
204 | return purgeLockedCollectionsVector(d_maps); | |
205 | } | |
206 | ||
207 | uint64_t AuthPacketCache::purgeExact(const DNSName& qname) | |
208 | { | |
209 | auto& mc = getMap(qname); | |
210 | uint64_t delcount = purgeExactLockedCollection(mc, qname); | |
211 | ||
212 | *d_statnumentries -= delcount; | |
213 | ||
214 | return delcount; | |
215 | } | |
216 | ||
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) | |
219 | { | |
9a037bfa KM |
220 | if(!d_ttl) { |
221 | return 0; | |
222 | } | |
223 | ||
bf269e28 RG |
224 | uint64_t delcount = 0; |
225 | ||
226 | if(ends_with(match, "$")) { | |
227 | delcount = purgeLockedCollectionsVector(d_maps, match); | |
228 | *d_statnumentries -= delcount; | |
229 | } | |
230 | else { | |
231 | delcount = purgeExact(DNSName(match)); | |
232 | } | |
233 | ||
234 | return delcount; | |
235 | } | |
236 | ||
237 | void AuthPacketCache::cleanup() | |
238 | { | |
239 | uint64_t maxCached = d_maxEntries; | |
240 | uint64_t cacheSize = *d_statnumentries; | |
241 | uint64_t totErased = 0; | |
242 | ||
243 | totErased = pruneLockedCollectionsVector(d_maps, maxCached, cacheSize); | |
244 | *d_statnumentries -= totErased; | |
245 | ||
e6a9dde5 | 246 | DLOG(g_log<<"Done with cache clean, cacheSize: "<<(*d_statnumentries)<<", totErased"<<totErased<<endl); |
bf269e28 RG |
247 | } |
248 | ||
249 | /* the logic: | |
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. | |
252 | ||
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. | |
256 | */ | |
257 | ||
258 | void AuthPacketCache::cleanupIfNeeded() | |
259 | { | |
260 | if (d_ops++ == d_nextclean) { | |
261 | time_t now = time(nullptr); | |
262 | int timediff = max((int)(now - d_lastclean), 1); | |
263 | ||
e6a9dde5 | 264 | DLOG(g_log<<"cleaninterval: "<<d_cleaninterval<<", timediff: "<<timediff<<endl); |
bf269e28 RG |
265 | |
266 | if (d_cleaninterval == s_maxcleaninterval && timediff < 30) { | |
267 | d_cleanskipped = true; | |
268 | d_nextclean += d_cleaninterval; | |
269 | ||
e6a9dde5 | 270 | DLOG(g_log<<"cleaning skipped, timediff: "<<timediff<<endl); |
bf269e28 RG |
271 | |
272 | return; | |
273 | } | |
274 | ||
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); | |
279 | ||
e6a9dde5 | 280 | DLOG(g_log<<"new cleaninterval: "<<d_cleaninterval<<endl); |
bf269e28 RG |
281 | } else { |
282 | d_cleanskipped = false; | |
283 | } | |
284 | ||
285 | d_nextclean += d_cleaninterval; | |
286 | d_lastclean=now; | |
287 | cleanup(); | |
288 | } | |
289 | } |