Merge branch 'tilan7663-subnet_cache_prefetch' into subnet_cache_prefetch

This commit is contained in:
George Thessalonikefs 2023-07-07 16:27:49 +02:00
commit a952ac17be
7 changed files with 118 additions and 75 deletions

View file

@ -817,7 +817,8 @@ reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
if(modstack_find(&worker->env.mesh->mods, "subnetcache") != -1 if(modstack_find(&worker->env.mesh->mods, "subnetcache") != -1
&& worker->env.unique_mesh) { && worker->env.unique_mesh) {
mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway + mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway +
PREFETCH_EXPIRY_ADD, rpz_passthru, repinfo, opt_list); PREFETCH_EXPIRY_ADD, rpz_passthru,
&repinfo->client_addr, opt_list);
return; return;
} }
#endif #endif

View file

@ -352,7 +352,7 @@ update_cache(struct module_qstate *qstate, int id)
((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash : ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash :
query_info_hash(&qstate->qinfo, qstate->query_flags); query_info_hash(&qstate->qinfo, qstate->query_flags);
/* Step 1, general qinfo lookup */ /* Step 1, general qinfo lookup */
struct lruhash_entry *lru_entry = slabhash_lookup(subnet_msg_cache, h, struct lruhash_entry* lru_entry = slabhash_lookup(subnet_msg_cache, h,
&qstate->qinfo, 1); &qstate->qinfo, 1);
int need_to_insert = (lru_entry == NULL); int need_to_insert = (lru_entry == NULL);
if (!lru_entry) { if (!lru_entry) {
@ -421,7 +421,7 @@ update_cache(struct module_qstate *qstate, int id)
/** Lookup in cache and reply true iff reply is sent. */ /** Lookup in cache and reply true iff reply is sent. */
static int static int
lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq) lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq, int prefetch)
{ {
struct lruhash_entry *e; struct lruhash_entry *e;
struct module_env *env = qstate->env; struct module_env *env = qstate->env;
@ -473,6 +473,10 @@ lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
INET6_SIZE); INET6_SIZE);
sq->ecs_client_out.subnet_validdata = 1; sq->ecs_client_out.subnet_validdata = 1;
} }
if (prefetch && *qstate->env->now >= ((struct reply_info *)node->elem)->prefetch_ttl) {
qstate->need_refetch = 1;
}
return 1; return 1;
} }
@ -807,7 +811,9 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
if(!sq->started_no_cache_lookup && !qstate->blacklist) { if(!sq->started_no_cache_lookup && !qstate->blacklist) {
lock_rw_wrlock(&sne->biglock); lock_rw_wrlock(&sne->biglock);
if(lookup_and_reply(qstate, id, sq)) { if(qstate->mesh_info->reply_list &&
lookup_and_reply(qstate, id, sq,
qstate->env->cfg->prefetch)) {
sne->num_msg_cache++; sne->num_msg_cache++;
lock_rw_unlock(&sne->biglock); lock_rw_unlock(&sne->biglock);
verbose(VERB_QUERY, "subnetcache: answered from cache"); verbose(VERB_QUERY, "subnetcache: answered from cache");

View file

@ -705,7 +705,7 @@ static void mesh_schedule_prefetch(struct mesh_area* mesh,
* attached its own ECS data. */ * attached its own ECS data. */
static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh, static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh,
struct query_info* qinfo, uint16_t qflags, time_t leeway, int run, struct query_info* qinfo, uint16_t qflags, time_t leeway, int run,
int rpz_passthru, struct comm_reply* rep, struct edns_option* edns_list) int rpz_passthru, struct sockaddr_storage* addr, struct edns_option* edns_list)
{ {
struct mesh_state* s = NULL; struct mesh_state* s = NULL;
struct edns_option* opt = NULL; struct edns_option* opt = NULL;
@ -738,7 +738,7 @@ static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh,
/* Store the client's address. Later in the subnet module, /* Store the client's address. Later in the subnet module,
* it is decided whether to include an ECS option or not. * it is decided whether to include an ECS option or not.
*/ */
s->s.client_addr = rep->client_addr; s->s.client_addr = *addr;
} }
#ifdef UNBOUND_DEBUG #ifdef UNBOUND_DEBUG
n = n =
@ -785,14 +785,14 @@ static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh,
void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, time_t leeway, int rpz_passthru, uint16_t qflags, time_t leeway, int rpz_passthru,
struct comm_reply* rep, struct edns_option* opt_list) struct sockaddr_storage* addr, struct edns_option* opt_list)
{ {
(void)addr;
(void)opt_list; (void)opt_list;
(void)rep;
#ifdef CLIENT_SUBNET #ifdef CLIENT_SUBNET
if(rep) if(addr)
mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, leeway, 1, mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, leeway, 1,
rpz_passthru, rep, opt_list); rpz_passthru, addr, opt_list);
else else
#endif #endif
mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1, mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1,
@ -1794,9 +1794,21 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate,
if(s == module_finished) { if(s == module_finished) {
if(mstate->s.curmod == 0) { if(mstate->s.curmod == 0) {
struct query_info* qinfo = NULL; struct query_info* qinfo = NULL;
struct edns_option* opt_list = NULL;
struct sockaddr_storage addr;
uint16_t qflags; uint16_t qflags;
int rpz_p = 0; int rpz_p = 0;
#ifdef CLIENT_SUBNET
struct edns_option* ecs;
if(mstate->s.need_refetch && mstate->reply_list &&
modstack_find(&mesh->mods, "subnetcache") != -1 &&
mstate->s.env->unique_mesh) {
addr = mstate->reply_list->query_reply.client_addr;
} else
#endif
memset(&addr, 0, sizeof(addr));
mesh_query_done(mstate); mesh_query_done(mstate);
mesh_walk_supers(mesh, mstate); mesh_walk_supers(mesh, mstate);
@ -1806,13 +1818,28 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate,
* we need to make a copy of the query info here. */ * we need to make a copy of the query info here. */
if(mstate->s.need_refetch) { if(mstate->s.need_refetch) {
mesh_copy_qinfo(mstate, &qinfo, &qflags); mesh_copy_qinfo(mstate, &qinfo, &qflags);
#ifdef CLIENT_SUBNET
/* Make also a copy of the ecs option if any */
if((ecs = edns_opt_list_find(
mstate->s.edns_opts_front_in,
mstate->s.env->cfg->client_subnet_opcode)) != NULL) {
(void)edns_opt_list_append(&opt_list,
ecs->opt_code, ecs->opt_len,
ecs->opt_data,
mstate->s.env->scratch);
}
#endif
rpz_p = mstate->s.rpz_passthru; rpz_p = mstate->s.rpz_passthru;
} }
mesh_state_delete(&mstate->s);
if(qinfo) { if(qinfo) {
mesh_schedule_prefetch(mesh, qinfo, qflags, mesh_state_delete(&mstate->s);
0, 1, rpz_p); mesh_new_prefetch(mesh, qinfo, qflags, 0,
rpz_p,
addr.ss_family!=AF_UNSPEC?&addr:NULL,
opt_list);
} else {
mesh_state_delete(&mstate->s);
} }
return 0; return 0;
} }

View file

@ -337,13 +337,13 @@ int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
* @param leeway: TTL leeway what to expire earlier for this update. * @param leeway: TTL leeway what to expire earlier for this update.
* @param rpz_passthru: if true, the rpz passthru was previously found and * @param rpz_passthru: if true, the rpz passthru was previously found and
* further rpz processing is stopped. * further rpz processing is stopped.
* @param rep: comm_reply for the client; to be used when subnet is enabled. * @param addr: sockaddr_storage for the client; to be used with subnet.
* @param opt_list: edns opt_list from the client; to be used when subnet is * @param opt_list: edns opt_list from the client; to be used when subnet is
* enabled. * enabled.
*/ */
void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, time_t leeway, int rpz_passthru, uint16_t qflags, time_t leeway, int rpz_passthru,
struct comm_reply* rep, struct edns_option* opt_list); struct sockaddr_storage* addr, struct edns_option* opt_list);
/** /**
* Handle new event from the wire. A serviced query has returned. * Handle new event from the wire. A serviced query has returned.

View file

@ -1,12 +1,12 @@
; Check if the prefetch option works properly for messages stored in the global ; Check if the prefetch option works properly for messages stored in ECS cache
; cache for non-ECS clients. The prefetch query needs to result in an ECS ; for non-ECS clients.
; outgoing query based on the client's IP.
server: server:
trust-anchor-signaling: no trust-anchor-signaling: no
target-fetch-policy: "0 0 0 0 0" target-fetch-policy: "0 0 0 0 0"
send-client-subnet: 1.2.3.4 send-client-subnet: 1.2.3.4
max-client-subnet-ipv4: 21 max-client-subnet-ipv4: 21
client-subnet-always-forward: yes
module-config: "subnetcache iterator" module-config: "subnetcache iterator"
verbosity: 3 verbosity: 3
access-control: 127.0.0.1 allow_snoop access-control: 127.0.0.1 allow_snoop
@ -19,7 +19,7 @@ stub-zone:
stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
CONFIG_END CONFIG_END
SCENARIO_BEGIN Test prefetch option for global cache with ECS enabled SCENARIO_BEGIN Test prefetch option for ECS cache
; K.ROOT-SERVERS.NET. ; K.ROOT-SERVERS.NET.
RANGE_BEGIN 0 100 RANGE_BEGIN 0 100
@ -78,38 +78,7 @@ RANGE_BEGIN 0 100
RANGE_END RANGE_END
; ns.example.com. ; ns.example.com.
RANGE_BEGIN 0 10 RANGE_BEGIN 0 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
; response to query of interest
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 10.20.30.40
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 11 100
ADDRESS 1.2.3.4 ADDRESS 1.2.3.4
ENTRY_BEGIN ENTRY_BEGIN
MATCH opcode qtype qname MATCH opcode qtype qname
@ -154,7 +123,7 @@ SECTION QUESTION
www.example.com. IN A www.example.com. IN A
ENTRY_END ENTRY_END
; This answer should be in the global cache (because no ECS from upstream) ; This answer will end up in the subnet cache
STEP 2 CHECK_ANSWER STEP 2 CHECK_ANSWER
ENTRY_BEGIN ENTRY_BEGIN
MATCH all MATCH all
@ -172,15 +141,15 @@ ENTRY_END
; Try to trigger a prefetch ; Try to trigger a prefetch
STEP 3 TIME_PASSES ELAPSE 9 STEP 3 TIME_PASSES ELAPSE 9
STEP 11 QUERY STEP 4 QUERY
ENTRY_BEGIN ENTRY_BEGIN
REPLY RD REPLY RD
SECTION QUESTION SECTION QUESTION
www.example.com. IN A www.example.com. IN A
ENTRY_END ENTRY_END
; This record came from the global cache and a prefetch was triggered ; This record came from the cache and a prefetch is triggered
STEP 12 CHECK_ANSWER STEP 5 CHECK_ANSWER
ENTRY_BEGIN ENTRY_BEGIN
MATCH all ttl MATCH all ttl
REPLY QR RD RA NOERROR REPLY QR RD RA NOERROR
@ -194,31 +163,29 @@ SECTION ADDITIONAL
ns.example.com. 3591 IN A 1.2.3.4 ns.example.com. 3591 IN A 1.2.3.4
ENTRY_END ENTRY_END
; Allow time to pass so that the global cache record is expired ; Allow for some time to pass to differentiate from a cached vs resolved answer
STEP 13 TIME_PASSES ELAPSE 2 STEP 6 TIME_PASSES ELAPSE 1
; Query again to verify that the record was prefetched and stored in the ECS STEP 7 QUERY
; cache (because the server replied with ECS this time)
STEP 14 QUERY
ENTRY_BEGIN ENTRY_BEGIN
REPLY RD REPLY RD
SECTION QUESTION SECTION QUESTION
www.example.com. IN A www.example.com. IN A
ENTRY_END ENTRY_END
; This record came from the ECS cache ; This prefetched record came from the ECS cache
STEP 15 CHECK_ANSWER STEP 8 CHECK_ANSWER
ENTRY_BEGIN ENTRY_BEGIN
MATCH all ttl MATCH all ttl
REPLY QR RD RA NOERROR REPLY QR RD RA NOERROR
SECTION QUESTION SECTION QUESTION
www.example.com. IN A www.example.com. IN A
SECTION ANSWER SECTION ANSWER
www.example.com. 8 IN A 10.20.30.40 www.example.com. 9 IN A 10.20.30.40
SECTION AUTHORITY SECTION AUTHORITY
example.com. 3598 IN NS ns.example.com. example.com. 3599 IN NS ns.example.com.
SECTION ADDITIONAL SECTION ADDITIONAL
ns.example.com. 3598 IN A 1.2.3.4 ns.example.com. 3599 IN A 1.2.3.4
ENTRY_END ENTRY_END
SCENARIO_END SCENARIO_END

View file

@ -1224,6 +1224,42 @@ struct edns_option* edns_opt_copy_region(struct edns_option* list,
return result; return result;
} }
struct edns_option* edns_opt_copy_filter_region(struct edns_option* list,
uint16_t* filter_list, size_t filter_list_len, struct regional* region)
{
struct edns_option* result = NULL, *cur = NULL, *s;
size_t i;
while(list) {
for(i=0; i<filter_list_len; i++)
if(filter_list[i] == list->opt_code) goto found;
if(i == filter_list_len) goto next;
found:
/* copy edns option structure */
s = regional_alloc_init(region, list, sizeof(*list));
if(!s) return NULL;
s->next = NULL;
/* copy option data */
if(s->opt_data) {
s->opt_data = regional_alloc_init(region, s->opt_data,
s->opt_len);
if(!s->opt_data)
return NULL;
}
/* link into list */
if(cur)
cur->next = s;
else result = s;
cur = s;
next:
/* examine next element */
list = list->next;
}
return result;
}
int edns_opt_compare(struct edns_option* p, struct edns_option* q) int edns_opt_compare(struct edns_option* p, struct edns_option* q)
{ {
if(!p && !q) return 0; if(!p && !q) return 0;

View file

@ -718,6 +718,12 @@ int inplace_cb_query_response_call(struct module_env* env,
struct edns_option* edns_opt_copy_region(struct edns_option* list, struct edns_option* edns_opt_copy_region(struct edns_option* list,
struct regional* region); struct regional* region);
/**
* Copy a filtered edns option list allocated to the new region
*/
struct edns_option* edns_opt_copy_filter_region(struct edns_option* list,
uint16_t* filter_list, size_t filter_list_len, struct regional* region);
/** /**
* Copy edns option list allocated with malloc * Copy edns option list allocated with malloc
*/ */