From: Otto Moerbeek Date: Wed, 27 Mar 2019 11:37:19 +0000 (+0100) Subject: Initial code for ecs-cache-limit-ttl. X-Git-Tag: dnsdist-1.4.0-alpha1~37^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ed9019c97a4f81c42c2e45ab4353022540afdf08;p=thirdparty%2Fpdns.git Initial code for ecs-cache-limit-ttl. --- diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index d4619922d6..712b5dae7f 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -3657,6 +3657,7 @@ static int serviceMain(int argc, char*argv[]) SyncRes::clearECSStats(); SyncRes::s_ecsipv4cachelimit = ::arg().asNum("ecs-ipv4-cache-bits"); SyncRes::s_ecsipv6cachelimit = ::arg().asNum("ecs-ipv6-cache-bits"); + SyncRes::s_ecscachelimitttl = ::arg().asNum("ecs-cache-limit-ttl"); if (!::arg().isEmpty("ecs-scope-zero-address")) { ComboAddress scopeZero(::arg()["ecs-scope-zero-address"]); @@ -4268,6 +4269,8 @@ int main(int argc, char **argv) ::arg().set("ecs-ipv6-bits", "Number of bits of IPv6 address to pass for EDNS Client Subnet")="56"; ::arg().set("ecs-ipv6-cache-bits", "Maximum number of bits of IPv6 mask to cache ECS response")="56"; ::arg().set("ecs-minimum-ttl-override", "Set under adverse conditions, a minimum TTL for records in ECS-specific answers")="0"; + ::arg().set("ecs-cache-limit-ttl", "Minimum TTL to cache ECS response")="0"; + ::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for")=""; ::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for")=""; ::arg().set("ecs-add-for", "List of client netmasks for which EDNS Client Subnet will be added")="0.0.0.0/0, ::/0, " LOCAL_NETS_INVERSE; ::arg().set("ecs-scope-zero-address", "Address to send to whitelisted authoritative servers for incoming queries with ECS prefix-length source of 0")=""; diff --git a/pdns/recursor_cache.cc b/pdns/recursor_cache.cc index 7e0bf054ce..51bfa3663b 100644 --- a/pdns/recursor_cache.cc +++ b/pdns/recursor_cache.cc @@ -236,7 +236,7 @@ int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, return -1; } -void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, const std::vector>& authorityRecs, bool auth, boost::optional ednsmask, vState state) +void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, const std::vector>& authorityRecs, bool auth, boost::optional ednsmask, time_t minTTD, vState state) { d_cachecachevalid = false; // cerr<<"Replacing "<toString() : "everyone") << endl; @@ -305,9 +305,11 @@ void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt for(const auto i : content) { /* Yes, we have altered the d_ttl value by adding time(nullptr) to it prior to calling this function, so the TTL actually holds a TTD. */ - ce.d_ttd=min(maxTTD, static_cast(i.d_ttl)); // XXX this does weird things if TTLs differ in the set - // cerr<<"To store: "<getZoneRepresentation()<<" with ttl/ttd "<(i.d_ttl) >= minTTD) { + ce.d_ttd = min(maxTTD, static_cast(i.d_ttl)); // XXX this does weird things if TTLs differ in the set + // cerr<<"To store: "<getZoneRepresentation()<<" with ttl/ttd "<* res, const ComboAddress& who, vector>* signatures=nullptr, std::vector>* authorityRecs=nullptr, bool* variable=nullptr, vState* state=nullptr, bool* wasAuth=nullptr); - void replace(time_t, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, const std::vector>& authorityRecs, bool auth, boost::optional ednsmask=boost::none, vState state=Indeterminate); + void replace(time_t, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, const std::vector>& authorityRecs, bool auth, boost::optional ednsmask=boost::none, time_t minTTD = 0, vState state=Indeterminate); void doPrune(unsigned int keep); uint64_t doDump(int fd); diff --git a/pdns/recursordist/docs/settings.rst b/pdns/recursordist/docs/settings.rst index d3d0727243..a1b692b49b 100644 --- a/pdns/recursordist/docs/settings.rst +++ b/pdns/recursordist/docs/settings.rst @@ -421,6 +421,15 @@ This setting artificially raises the TTLs of records in the ANSWER section of EC While this is a gross hack, and violates RFCs, under conditions of DoS, it may enable you to continue serving your customers. Can be set at runtime using ``rec_control set-ecs-minimum-ttl 3600``. +.. _setting-ecs-cache-limit-ttl: + +``ecs-cache-limit-ttl`` +----------------------- +- Integer +- Default: 0 (disabled) + +The minumum TTL for an ECS-specific answer to be inserted into the query cache. + .. _setting-ecs-scope-zero-address: ``ecs-scope-zero-address`` diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 04d9499e00..c0412b388b 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -80,6 +80,8 @@ uint8_t SyncRes::s_ecsipv4limit; uint8_t SyncRes::s_ecsipv6limit; uint8_t SyncRes::s_ecsipv4cachelimit; uint8_t SyncRes::s_ecsipv6cachelimit; +unsigned int SyncRes::s_ecscachelimitttl; + bool SyncRes::s_doIPv6; bool SyncRes::s_nopacketcache; bool SyncRes::s_rootNXTrust; @@ -119,8 +121,8 @@ static void accountAuthLatency(int usec, int family) SyncRes::SyncRes(const struct timeval& now) : d_authzonequeries(0), d_outqueries(0), d_tcpoutqueries(0), d_throttledqueries(0), d_timeouts(0), d_unreachables(0), d_totUsec(0), d_now(now), d_cacheonly(false), d_doDNSSEC(false), d_doEDNS0(false), d_lm(s_lm) - -{ + +{ } /** everything begins here - this is the entry point just after receiving a packet */ @@ -433,10 +435,10 @@ uint64_t SyncRes::doDumpThrottleMap(int fd) Another cause of "No answer" may simply be a network condition. Nonsense answers are a clearer indication this host won't be able to do DNSSEC evah. - Previous implementations have suffered from turning off DNSSEC questions for an authoritative server based on timeouts. + Previous implementations have suffered from turning off DNSSEC questions for an authoritative server based on timeouts. A clever idea is to only turn off DNSSEC if we know a domain isn't signed anyhow. The problem with that really - clever idea however is that at this point in PowerDNS, we may simply not know that yet. All the DNSSEC thinking happens - elsewhere. It may not have happened yet. + clever idea however is that at this point in PowerDNS, we may simply not know that yet. All the DNSSEC thinking happens + elsewhere. It may not have happened yet. For now this means we can't be clever, but will turn off DNSSEC if you reply with FormError or gibberish. */ @@ -447,19 +449,19 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con the goal is to get as many remotes as possible on the highest level of EDNS support The levels are: - 0) UNKNOWN Unknown state + 0) UNKNOWN Unknown state 1) EDNS: Honors EDNS0 2) EDNSIGNORANT: Ignores EDNS0, gives replies without EDNS0 3) NOEDNS: Generates FORMERR on EDNS queries Everybody starts out assumed to be '0'. If '0', send out EDNS0 - If you FORMERR us, go to '3', + If you FORMERR us, go to '3', If no EDNS in response, go to '2' If '1', send out EDNS0 If FORMERR, downgrade to 3 If '2', keep on including EDNS0, see what happens - Same behaviour as 0 + Same behaviour as 0 If '3', send bare queries */ @@ -483,7 +485,7 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con int ret; for(int tries = 0; tries < 3; ++tries) { // cerr<<"Remote '"<modeSetAt) ednsstatus->modeSetAt=d_now.tv_sec; - // cerr<<"Result: ret="<d_haveEDNS<<", new mode: "<d_haveEDNS<<", new mode: "<isIpv4() && ednsmask->getBits() <= SyncRes::s_ecsipv4cachelimit) || (ednsmask->isIpv6() && ednsmask->getBits() <= SyncRes::s_ecsipv6cachelimit)) { - t_RC->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, authorityRecs, i->first.type == QType::DS ? true : isAA, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, recordState); + time_t minTTD = 0; + if (ednsmask && SyncRes::s_ecscachelimitttl > 0) { + minTTD = SyncRes::s_ecscachelimitttl + d_now.tv_sec; + } + t_RC->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, authorityRecs, i->first.type == QType::DS ? true : isAA, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, minTTD, recordState); } } @@ -3199,7 +3205,7 @@ int directResolve(const DNSName& qname, const QType& qtype, int qclass, vector