7 #include "recpacketcache.hh"
8 #include "cachecleaner.hh"
10 #include "namespaces.hh"
11 #include "rec-taskqueue.hh"
13 unsigned int RecursorPacketCache::s_refresh_ttlperc
{0};
15 void RecursorPacketCache::setShardSizes(size_t shardSize
)
17 for (auto& shard
: d_maps
) {
18 auto lock
= shard
.lock();
19 lock
->d_shardSize
= shardSize
;
23 uint64_t RecursorPacketCache::size() const
26 for (const auto& map
: d_maps
) {
27 count
+= map
.d_entriesCount
;
32 uint64_t RecursorPacketCache::bytes()
35 for (auto& shard
: d_maps
) {
36 auto lock
= shard
.lock();
37 for (const auto& entry
: lock
->d_map
) {
38 sum
+= sizeof(entry
) + entry
.d_packet
.length() + 4;
44 uint64_t RecursorPacketCache::getHits()
47 for (auto& shard
: d_maps
) {
48 auto lock
= shard
.lock();
54 uint64_t RecursorPacketCache::getMisses()
57 for (auto& shard
: d_maps
) {
58 auto lock
= shard
.lock();
59 sum
+= lock
->d_misses
;
64 pair
<uint64_t, uint64_t> RecursorPacketCache::stats()
66 uint64_t contended
= 0;
67 uint64_t acquired
= 0;
68 for (auto& shard
: d_maps
) {
69 auto content
= shard
.lock();
70 contended
+= content
->d_contended_count
;
71 acquired
+= content
->d_acquired_count
;
73 return {contended
, acquired
};
76 uint64_t RecursorPacketCache::doWipePacketCache(const DNSName
& name
, uint16_t qtype
, bool subtree
)
79 for (auto& map
: d_maps
) {
80 auto shard
= map
.lock();
81 auto& idx
= shard
->d_map
.get
<NameTag
>();
82 for (auto iter
= idx
.lower_bound(name
); iter
!= idx
.end();) {
84 if (!iter
->d_name
.isPartOf(name
)) { // this is case insensitive
89 if (iter
->d_name
!= name
) {
93 if (qtype
== 0xffff || iter
->d_type
== qtype
) {
94 iter
= idx
.erase(iter
);
106 bool RecursorPacketCache::qrMatch(const packetCache_t::index
<HashTag
>::type::iterator
& iter
, const std::string
& queryPacket
, const DNSName
& qname
, uint16_t qtype
, uint16_t qclass
)
108 // this ignores checking on the EDNS subnet flags!
109 if (qname
!= iter
->d_name
|| iter
->d_type
!= qtype
|| iter
->d_class
!= qclass
) {
113 static const std::unordered_set
<uint16_t> optionsToSkip
{EDNSOptionCode::COOKIE
, EDNSOptionCode::ECS
};
114 return queryMatches(iter
->d_query
, queryPacket
, qname
, optionsToSkip
);
117 bool RecursorPacketCache::checkResponseMatches(MapCombo::LockedContent
& shard
, std::pair
<packetCache_t::index
<HashTag
>::type::iterator
, packetCache_t::index
<HashTag
>::type::iterator
> range
, const std::string
& queryPacket
, const DNSName
& qname
, uint16_t qtype
, uint16_t qclass
, time_t now
, std::string
* responsePacket
, uint32_t* age
, vState
* valState
, OptPBData
* pbdata
)
119 for (auto iter
= range
.first
; iter
!= range
.second
; ++iter
) {
120 // the possibility is VERY real that we get hits that are not right - birthday paradox
121 if (!qrMatch(iter
, queryPacket
, qname
, qtype
, qclass
)) {
125 if (now
< iter
->d_ttd
) { // it is right, it is fresh!
126 *age
= static_cast<uint32_t>(now
- iter
->d_creation
);
127 // we know ttl is > 0
128 auto ttl
= static_cast<uint32_t>(iter
->d_ttd
- now
);
129 if (s_refresh_ttlperc
> 0 && !iter
->d_submitted
) {
130 const uint32_t deadline
= iter
->getOrigTTL() * s_refresh_ttlperc
/ 100;
131 const bool almostExpired
= ttl
<= deadline
;
133 iter
->d_submitted
= true;
134 pushAlmostExpiredTask(qname
, qtype
, iter
->d_ttd
, Netmask());
137 *responsePacket
= iter
->d_packet
;
138 responsePacket
->replace(0, 2, queryPacket
.c_str(), 2);
139 *valState
= iter
->d_vstate
;
141 const size_t wirelength
= qname
.wirelength();
142 if (responsePacket
->size() > (sizeof(dnsheader
) + wirelength
)) {
143 responsePacket
->replace(sizeof(dnsheader
), wirelength
, queryPacket
, sizeof(dnsheader
), wirelength
);
147 moveCacheItemToBack
<SequencedTag
>(shard
.d_map
, iter
);
149 if (pbdata
!= nullptr) {
150 if (iter
->d_pbdata
) {
151 *pbdata
= iter
->d_pbdata
;
154 *pbdata
= boost::none
;
160 // We used to move the item to the front of "the to be deleted" sequence,
161 // but we very likely will update the entry very soon, so leave it
169 static const std::unordered_set
<uint16_t> s_skipOptions
= {EDNSOptionCode::ECS
, EDNSOptionCode::COOKIE
};
171 bool RecursorPacketCache::getResponsePacket(unsigned int tag
, const std::string
& queryPacket
, const DNSName
& qname
, uint16_t qtype
, uint16_t qclass
, time_t now
,
172 std::string
* responsePacket
, uint32_t* age
, vState
* valState
, uint32_t* qhash
, OptPBData
* pbdata
, bool tcp
)
174 *qhash
= canHashPacket(queryPacket
, s_skipOptions
);
175 auto& map
= getMap(tag
, *qhash
, tcp
);
176 auto shard
= map
.lock();
177 const auto& idx
= shard
->d_map
.get
<HashTag
>();
178 auto range
= idx
.equal_range(std::tie(tag
, *qhash
, tcp
));
180 if (range
.first
== range
.second
) {
185 return checkResponseMatches(*shard
, range
, queryPacket
, qname
, qtype
, qclass
, now
, responsePacket
, age
, valState
, pbdata
);
188 bool RecursorPacketCache::getResponsePacket(unsigned int tag
, const std::string
& queryPacket
, DNSName
& qname
, uint16_t* qtype
, uint16_t* qclass
, time_t now
,
189 std::string
* responsePacket
, uint32_t* age
, vState
* valState
, uint32_t* qhash
, OptPBData
* pbdata
, bool tcp
)
191 *qhash
= canHashPacket(queryPacket
, s_skipOptions
);
192 auto& map
= getMap(tag
, *qhash
, tcp
);
193 auto shard
= map
.lock();
194 const auto& idx
= shard
->d_map
.get
<HashTag
>();
195 auto range
= idx
.equal_range(std::tie(tag
, *qhash
, tcp
));
197 if (range
.first
== range
.second
) {
202 qname
= DNSName(queryPacket
.c_str(), static_cast<int>(queryPacket
.length()), sizeof(dnsheader
), false, qtype
, qclass
);
204 return checkResponseMatches(*shard
, range
, queryPacket
, qname
, *qtype
, *qclass
, now
, responsePacket
, age
, valState
, pbdata
);
207 void RecursorPacketCache::insertResponsePacket(unsigned int tag
, uint32_t qhash
, std::string
&& query
, const DNSName
& qname
, uint16_t qtype
, uint16_t qclass
, std::string
&& responsePacket
, time_t now
, uint32_t ttl
, const vState
& valState
, OptPBData
&& pbdata
, bool tcp
)
209 auto& map
= getMap(tag
, qhash
, tcp
);
210 auto shard
= map
.lock();
211 auto& idx
= shard
->d_map
.get
<HashTag
>();
212 auto range
= idx
.equal_range(std::tie(tag
, qhash
, tcp
));
213 auto iter
= range
.first
;
215 for (; iter
!= range
.second
; ++iter
) {
216 if (iter
->d_type
!= qtype
|| iter
->d_class
!= qclass
|| iter
->d_name
!= qname
) {
220 moveCacheItemToBack
<SequencedTag
>(shard
->d_map
, iter
);
221 iter
->d_packet
= std::move(responsePacket
);
222 iter
->d_query
= std::move(query
);
223 iter
->d_ttd
= now
+ ttl
;
224 iter
->d_creation
= now
;
225 iter
->d_vstate
= valState
;
226 iter
->d_submitted
= false;
228 iter
->d_pbdata
= std::move(*pbdata
);
234 struct Entry
entry(qname
, qtype
, qclass
, std::move(responsePacket
), std::move(query
), tcp
, qhash
, now
+ ttl
, now
, tag
, valState
);
236 entry
.d_pbdata
= std::move(*pbdata
);
239 shard
->d_map
.insert(entry
);
240 map
.d_entriesCount
++;
242 if (shard
->d_map
.size() > shard
->d_shardSize
) {
243 auto& seq_idx
= shard
->d_map
.get
<SequencedTag
>();
244 seq_idx
.erase(seq_idx
.begin());
245 map
.d_entriesCount
--;
247 assert(map
.d_entriesCount
== shard
->d_map
.size()); // XXX
250 void RecursorPacketCache::doPruneTo(size_t maxSize
)
252 size_t cacheSize
= size();
253 pruneMutexCollectionsVector
<SequencedTag
>(*this, d_maps
, maxSize
, cacheSize
);
256 uint64_t RecursorPacketCache::doDump(int file
)
258 int fdupped
= dup(file
);
262 auto filePtr
= std::unique_ptr
<FILE, decltype(&fclose
)>(fdopen(fdupped
, "w"), fclose
);
269 time_t now
= time(nullptr);
272 size_t min
= std::numeric_limits
<size_t>::max();
274 uint64_t maxSize
= 0;
276 for (auto& shard
: d_maps
) {
277 auto lock
= shard
.lock();
278 const auto& sidx
= lock
->d_map
.get
<SequencedTag
>();
279 const auto shardSize
= lock
->d_map
.size();
280 fprintf(filePtr
.get(), "; packetcache shard %zu; size %zu/%zu\n", shardNum
, shardSize
, lock
->d_shardSize
);
281 min
= std::min(min
, shardSize
);
282 max
= std::max(max
, shardSize
);
283 maxSize
+= lock
->d_shardSize
;
285 for (const auto& entry
: sidx
) {
288 fprintf(filePtr
.get(), "%s %" PRId64
" %s ; tag %d %s\n", entry
.d_name
.toString().c_str(), static_cast<int64_t>(entry
.d_ttd
- now
), DNSRecordContent::NumberToType(entry
.d_type
).c_str(), entry
.d_tag
, entry
.d_tcp
? "tcp" : "udp");
291 fprintf(filePtr
.get(), "; error printing '%s'\n", entry
.d_name
.empty() ? "EMPTY" : entry
.d_name
.toString().c_str());
295 fprintf(filePtr
.get(), "; packetcache size: %" PRIu64
"/%" PRIu64
" shards: %zu min/max shard size: %zu/%zu\n", size(), maxSize
, d_maps
.size(), min
, max
);