Fix prefetch and stickyness.

git-svn-id: file:///svn/unbound/trunk@2632 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2012-02-16 11:04:53 +00:00
parent 682ff957ed
commit 773d8e3b84
12 changed files with 291 additions and 42 deletions

View file

@ -765,7 +765,7 @@ load_msg(SSL* ssl, ldns_buffer* buf, struct worker* worker)
if(!go_on)
return 1; /* skip this one, not all references satisfied */
if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, NULL)) {
if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, 0, NULL)) {
log_warn("error out of memory");
return 0;
}

View file

@ -1,5 +1,10 @@
16 February 2012: Wouter
- iter_hints is now thread-owned in module env, and thus threadsafe.
- Fix prefetch and sticky NS, now the prefetch works. It picks
nameservers that 'would be valid in the future', and if this makes
the NS timeout, it updates that NS by asking delegation from the
parent again. If child NS has longer TTL, that TTL does not get
refreshed from the lookup to the child nameserver.
15 February 2012: Wouter
- Fix forward-zone memory, uses malloc and frees original root dp.

View file

@ -419,11 +419,11 @@ dns_copy_msg(struct dns_msg* from, struct regional* region)
int
iter_dns_store(struct module_env* env, struct query_info* msgqinf,
struct reply_info* msgrep, int is_referral, uint32_t leeway,
struct reply_info* msgrep, int is_referral, uint32_t leeway, int pside,
struct regional* region)
{
return dns_cache_store(env, msgqinf, msgrep, is_referral, leeway,
region);
pside, region);
}
int

View file

@ -121,11 +121,13 @@ struct dns_msg* dns_copy_msg(struct dns_msg* from, struct regional* regional);
* @param is_referral: If true, then the given message to be stored is a
* referral. The cache implementation may use this as a hint.
* @param leeway: prefetch TTL leeway to expire old rrsets quicker.
* @param pside: true if dp is parentside, thus message is 'fresh' and NS
* can be prefetch-updates.
* @param region: to copy modified (cache is better) rrs back to.
* @return 0 on alloc error (out of memory).
*/
int iter_dns_store(struct module_env* env, struct query_info* qinf,
struct reply_info* rep, int is_referral, uint32_t leeway,
struct reply_info* rep, int is_referral, uint32_t leeway, int pside,
struct regional* region);
/**

View file

@ -259,7 +259,7 @@ error_response_cache(struct module_qstate* qstate, int id, int rcode)
/* do not waste time trying to validate this servfail */
err.security = sec_status_indeterminate;
verbose(VERB_ALGO, "store error response in message cache");
if(!iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, NULL)) {
if(!iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL)) {
log_err("error_response_cache: could not store error (nomem)");
}
return error_response(qstate, id, rcode);
@ -1040,7 +1040,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
if(delname)
iq->dp = dns_cache_find_delegation(qstate->env, delname,
delnamelen, iq->qchase.qtype, iq->qchase.qclass,
qstate->region, &iq->deleg_msg, *qstate->env->now);
qstate->region, &iq->deleg_msg,
*qstate->env->now+qstate->prefetch_leeway);
else iq->dp = NULL;
/* If the cache has returned nothing, then we have a
@ -1258,7 +1259,8 @@ generate_parentside_target_query(struct module_qstate* qstate,
} else {
subiq->dp = dns_cache_find_delegation(qstate->env,
name, namelen, qtype, qclass, subq->region,
&subiq->deleg_msg, *qstate->env->now);
&subiq->deleg_msg,
*qstate->env->now+subq->prefetch_leeway);
/* if no dp, then it's from root, refetch unneeded */
if(subiq->dp) {
subiq->dnssec_expected = iter_indicates_dnssec(
@ -1830,6 +1832,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
}
if(!iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 0, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
qstate->region))
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
/* close down outstanding requests to be discarded */
@ -1869,7 +1872,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
/* Store the referral under the current query */
/* no prefetch-leeway, since its not the answer */
if(!iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 1, 0, NULL))
iq->response->rep, 1, 0, 0, NULL))
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
if(iq->store_parent_NS)
@ -1955,7 +1958,9 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
* the partial query answer (CNAME only). */
/* prefetchleeway applied because this updates answer parts */
if(!iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 1, qstate->prefetch_leeway, NULL))
iq->response->rep, 1, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
NULL))
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
/* set the current request's qname to the new value. */
iq->qchase.qname = sname;
@ -2432,6 +2437,7 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
if(qstate->query_flags&BIT_RD) {
if(!iter_dns_store(qstate->env, &qstate->qinfo,
iq->response->rep, 0, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
qstate->region))
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);

20
services/cache/dns.c vendored
View file

@ -60,12 +60,15 @@
* updated with a new full TTL.
* Type NS does not get this, because it must not be refreshed from the
* child domain, but keep counting down properly.
* @param pside: if from parentside discovered NS, so that its NS is okay
* in a prefetch situation to be updated (without becoming sticky).
* @param qrep: update rrsets here if cache is better
* @param region: for qrep allocs.
*/
static void
store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now,
uint32_t leeway, struct reply_info* qrep, struct regional* region)
uint32_t leeway, int pside, struct reply_info* qrep,
struct regional* region)
{
size_t i;
/* see if rrset already exists in cache, if not insert it. */
@ -75,7 +78,7 @@ store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now,
/* update ref if it was in the cache */
switch(rrset_cache_update(env->rrset_cache, &rep->ref[i],
env->alloc, now + ((ntohs(rep->ref[i].key->rk.type)==
LDNS_RR_TYPE_NS)?0:leeway))) {
LDNS_RR_TYPE_NS && !pside)?0:leeway))) {
case 0: /* ref unchanged, item inserted */
break;
case 2: /* ref updated, cache is superior */
@ -102,7 +105,7 @@ store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now,
void
dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
hashvalue_t hash, struct reply_info* rep, uint32_t leeway,
hashvalue_t hash, struct reply_info* rep, uint32_t leeway, int pside,
struct reply_info* qrep, struct regional* region)
{
struct msgreply_entry* e;
@ -118,7 +121,7 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
/* there was a reply_info_sortref(rep) here but it seems to be
* unnecessary, because the cache gets locked per rrset. */
reply_info_set_ttls(rep, *env->now);
store_rrsets(env, rep, *env->now, leeway, qrep, region);
store_rrsets(env, rep, *env->now, leeway, pside, qrep, region);
if(ttl == 0) {
/* we do not store the message, but we did store the RRs,
* which could be useful for delegation information */
@ -736,7 +739,7 @@ dns_cache_lookup(struct module_env* env,
int
dns_cache_store(struct module_env* env, struct query_info* msgqinf,
struct reply_info* msgrep, int is_referral, uint32_t leeway,
struct reply_info* msgrep, int is_referral, uint32_t leeway, int pside,
struct regional* region)
{
struct reply_info* rep = NULL;
@ -761,8 +764,8 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
/* no leeway for typeNS */
(void)rrset_cache_update(env->rrset_cache, &ref,
env->alloc, *env->now +
((ntohs(ref.key->rk.type)==LDNS_RR_TYPE_NS)?
0:leeway));
((ntohs(ref.key->rk.type)==LDNS_RR_TYPE_NS
&& !pside) ? 0:leeway));
}
free(rep);
return 1;
@ -783,7 +786,8 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
rep->flags |= (BIT_RA | BIT_QR);
rep->flags &= ~(BIT_AA | BIT_CD);
h = query_info_hash(&qinf);
dns_cache_store_msg(env, &qinf, h, rep, leeway, msgrep, region);
dns_cache_store_msg(env, &qinf, h, rep, leeway, pside, msgrep,
region);
/* qname is used inside query_info_entrysetup, and set to
* NULL. If it has not been used, free it. free(0) is safe. */
free(qinf.qname);

10
services/cache/dns.h vendored
View file

@ -74,12 +74,15 @@ struct dns_msg {
* It will store only the RRsets, not the message.
* @param leeway: TTL value, if not 0, other rrsets are considered expired
* that many seconds before actual TTL expiry.
* @param pside: if true, information came from a server which was fetched
* from the parentside of the zonecut. This means that the type NS
* can be updated to full TTL even in prefetch situations.
* @param region: region to allocate better entries from cache into.
* (used when is_referral is false).
* @return 0 on alloc error (out of memory).
*/
int dns_cache_store(struct module_env* env, struct query_info* qinf,
struct reply_info* rep, int is_referral, uint32_t leeway,
struct reply_info* rep, int is_referral, uint32_t leeway, int pside,
struct regional* region);
/**
@ -95,11 +98,14 @@ int dns_cache_store(struct module_env* env, struct query_info* qinf,
* Adjusts the reply info TTLs to absolute time.
* @param leeway: TTL value, if not 0, other rrsets are considered expired
* that many seconds before actual TTL expiry.
* @param pside: if true, information came from a server which was fetched
* from the parentside of the zonecut. This means that the type NS
* can be updated to full TTL even in prefetch situations.
* @param qrep: message that can be altered with better rrs from cache.
* @param region: to allocate into for qmsg.
*/
void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
hashvalue_t hash, struct reply_info* rep, uint32_t leeway,
hashvalue_t hash, struct reply_info* rep, uint32_t leeway, int pside,
struct reply_info* qrep, struct regional* region);
/**

View file

@ -217,8 +217,8 @@ www.example.com. IN A
SECTION ANSWER
www.example.com. 3600 IN A 10.20.30.40
SECTION AUTHORITY
; NS rrset TTL not updated to avoid sticky-NS (ghost domain) problem.
example.com. 360 IN NS ns.example.com.
; NS rrset picked up from parent-NS (the child-NS timed out at now+prefetch)
example.com. 3600 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 3600 IN A 1.2.3.4
ENTRY_END

View file

@ -256,11 +256,11 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 300 IN A 10.1.1.1
www.example.com. 86400 IN A 10.2.2.2
SECTION AUTHORITY
example.com. 30 IN NS old-ns.example.com.
example.com. 86400 IN NS new-ns.example.com.
SECTION ADDITIONAL
old-ns.example.com. 300 IN A 192.168.0.1
new-ns.example.com. 86400 IN A 172.16.0.1
ENTRY_END
; the NS record times out after 31 seconds.
@ -281,11 +281,11 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 86400 IN A 10.2.2.2
www.example.com. 86369 IN A 10.2.2.2
SECTION AUTHORITY
example.com. 86400 IN NS new-ns.example.com.
example.com. 86369 IN NS new-ns.example.com.
SECTION ADDITIONAL
new-ns.example.com. 86400 IN A 172.16.0.1
new-ns.example.com. 86369 IN A 172.16.0.1
ENTRY_END
; a reply from cache
@ -303,11 +303,11 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 86400 IN A 10.2.2.2
www.example.com. 86369 IN A 10.2.2.2
SECTION AUTHORITY
example.com. 86400 IN NS new-ns.example.com.
example.com. 86369 IN NS new-ns.example.com.
SECTION ADDITIONAL
new-ns.example.com. 86400 IN A 172.16.0.1
new-ns.example.com. 86369 IN A 172.16.0.1
ENTRY_END
SCENARIO_END

227
testdata/iter_prefetch_childns.rpl vendored Normal file
View file

@ -0,0 +1,227 @@
; config options
server:
target-fetch-policy: "0 0 0 0 0"
prefetch: "yes"
stub-zone:
name: "."
stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
CONFIG_END
SCENARIO_BEGIN Test resolver prefetch from child nameserver
; child NS record has longer TTL than A record and is thus valid for prefetch.
; K.ROOT-SERVERS.NET.
RANGE_BEGIN 0 100
ADDRESS 193.0.14.129
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
. IN NS
SECTION ANSWER
. IN NS K.ROOT-SERVERS.NET.
SECTION ADDITIONAL
K.ROOT-SERVERS.NET. IN A 193.0.14.129
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION AUTHORITY
com. IN NS a.gtld-servers.net.
SECTION ADDITIONAL
a.gtld-servers.net. IN A 192.5.6.30
ENTRY_END
RANGE_END
; a.gtld-servers.net.
RANGE_BEGIN 0 100
ADDRESS 192.5.6.30
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
com. IN NS
SECTION ANSWER
com. IN NS a.gtld-servers.net.
SECTION ADDITIONAL
a.gtld-servers.net. IN A 192.5.6.30
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. IN A 1.2.3.4
ENTRY_END
RANGE_END
; ns.example.com.
RANGE_BEGIN 0 40
ADDRESS 1.2.3.4
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
example.com. IN NS
SECTION ANSWER
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. IN A 1.2.3.4
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 1800 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 3600 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 3600 IN A 1.2.3.4
ENTRY_END
RANGE_END
; ns.example.com.
RANGE_BEGIN 50 100
ADDRESS 1.2.3.4
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
example.com. IN NS
SECTION ANSWER
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. IN A 1.2.3.4
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 1800 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 3600 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 3600 IN A 1.2.3.4
ENTRY_END
RANGE_END
STEP 1 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
www.example.com. IN A
ENTRY_END
; recursion happens here.
STEP 10 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 1800 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 3600 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 3600 IN A 1.2.3.4
ENTRY_END
; after 900 secs still the cached answer
STEP 20 TIME_PASSES ELAPSE 900
STEP 30 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
www.example.com. IN A
ENTRY_END
; recursion happens here.
STEP 40 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 900 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 2700 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 2700 IN A 1.2.3.4
ENTRY_END
; after 720 we are 180 seconds before the expiry
; (the authority changes behind the scenes to detect new lookup)
STEP 50 TIME_PASSES ELAPSE 720
STEP 60 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
www.example.com. IN A
ENTRY_END
; recursion happens here.
STEP 70 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 180 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 1980 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 1980 IN A 1.2.3.4
ENTRY_END
STEP 80 TRAFFIC
; let traffic flow for prefetch to happen
; above a cache reply with 10% of the original TTL
; but the actual cache is changed, try to get that
STEP 120 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
www.example.com. IN A
ENTRY_END
; recursion happens here.
STEP 130 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 1800 IN A 10.20.30.40
SECTION AUTHORITY
; The NS rrset (from the child-side NS) is not updated but keeps counting down
example.com. 1980 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 1980 IN A 1.2.3.4
ENTRY_END
SCENARIO_END

View file

@ -26,11 +26,11 @@ K.ROOT-SERVERS.NET. IN A 193.0.14.129
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
MATCH opcode subdomain
ADJUST copy_id copy_query
REPLY QR NOERROR
SECTION QUESTION
www.example.com. IN A
example.com. IN A
SECTION AUTHORITY
com. IN NS a.gtld-servers.net.
SECTION ADDITIONAL
@ -273,6 +273,8 @@ SECTION QUESTION
example.com. IN NS
ENTRY_END
; recursion happens here.
; because the prefetch+current makes old-NS expired, new delegation is picked up
STEP 91 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
@ -280,19 +282,14 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
example.com. IN NS
SECTION ANSWER
; this record is unchanged even though it now points to the
; new registrant. Thus it keeps counting down.
example.com. 360 IN NS ns.example.com.
example.com. 3600 IN NS ns.example.com.
SECTION AUTHORITY
SECTION ADDITIONAL
ns.example.com. 3600 IN A 8.8.8.8
ENTRY_END
; after 360 + 2000 we are after the change to new owner.
STEP 100 TIME_PASSES ELAPSE 2360
; the NS record should have timed out.
STEP 120 QUERY
ENTRY_BEGIN
REPLY RD
@ -309,7 +306,7 @@ www.example.com. IN A
SECTION ANSWER
www.example.com. 3600 IN A 88.88.88.88
SECTION AUTHORITY
example.com. 3600 IN NS ns.example.com.
example.com. 1240 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 1240 IN A 8.8.8.8
ENTRY_END

View file

@ -1977,15 +1977,17 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
/* store results in cache */
if(qstate->query_flags&BIT_RD) {
/* if secure, this will override cache anyway, no need
* to check if from parentNS */
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
vq->orig_msg->rep, 0, qstate->prefetch_leeway, NULL)) {
vq->orig_msg->rep, 0, qstate->prefetch_leeway, 0, NULL)) {
log_err("out of memory caching validator results");
}
} else {
/* for a referral, store the verified RRsets */
/* and this does not get prefetched, so no leeway */
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
vq->orig_msg->rep, 1, 0, NULL)) {
vq->orig_msg->rep, 1, 0, 0, NULL)) {
log_err("out of memory caching validator results");
}
}