From 8afbc0944f054a0a60d0e19e3db9852efb43f4ba Mon Sep 17 00:00:00 2001 From: Tian Lan Date: Fri, 15 Apr 2022 15:26:16 -0400 Subject: [PATCH 1/2] Add prefetch support for subnet cache entries - Entries in the subnet cache should now be prefetched. - Rename testdata subnet_*.crpl to subnet_*.rpl so they are visible to make test Signed-off-by: Tian Lan --- edns-subnet/subnetmod.c | 46 +++++++++------ edns-subnet/subnetmod.h | 8 +++ services/mesh.c | 127 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 161 insertions(+), 20 deletions(-) diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index fcea71c31..02994b3ab 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -97,8 +97,8 @@ subnet_new_qstate(struct module_qstate *qstate, int id) } /** Add ecs struct to edns list, after parsing it to wire format. */ -static void -ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list, +void +subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list, struct module_qstate *qstate) { size_t sn_octs, sn_octs_remainder; @@ -164,7 +164,7 @@ int ecs_whitelist_check(struct query_info* qinfo, * set. */ if(!edns_opt_list_find(qstate->edns_opts_back_out, qstate->env->cfg->client_subnet_opcode)) { - ecs_opt_list_append(&sq->ecs_server_out, + subnet_ecs_opt_list_append(&sq->ecs_server_out, &qstate->edns_opts_back_out, qstate); } sq->subnet_sent = 1; @@ -231,7 +231,7 @@ subnetmod_init(struct module_env *env, int id) env->unique_mesh = 1; if(!edns_register_option(env->cfg->client_subnet_opcode, env->cfg->client_subnet_always_forward /* bypass cache */, - 0 /* no aggregation */, env)) { + 1 /* no aggregation */, env)) { log_err("subnetcache: could not register opcode"); ecs_whitelist_delete(sn_env->whitelist); slabhash_delete(sn_env->subnet_msg_cache); @@ -330,11 +330,15 @@ update_cache(struct module_qstate *qstate, int id) struct slabhash *subnet_msg_cache = sne->subnet_msg_cache; struct ecs_data *edns = &sq->ecs_client_in; size_t i; + hashvalue_type h; + + /* qinfo_hash is not set if it is prefetch request */ + if (qstate->minfo[id] && ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash) { + h = ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash; + } else { + h = query_info_hash(&qstate->qinfo, qstate->query_flags); + } - /* We already calculated hash upon lookup */ - hashvalue_type h = qstate->minfo[id] ? - ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash : - query_info_hash(&qstate->qinfo, qstate->query_flags); /* Step 1, general qinfo lookup */ struct lruhash_entry *lru_entry = slabhash_lookup(subnet_msg_cache, h, &qstate->qinfo, 1); @@ -380,7 +384,7 @@ update_cache(struct module_qstate *qstate, int id) log_err("subnetcache: cache insertion failed"); return; } - + /* store RRsets */ for(i=0; irrset_count; i++) { rep->ref[i].key = rep->rrsets[i]; @@ -402,7 +406,7 @@ update_cache(struct module_qstate *qstate, int id) /** Lookup in cache and reply true iff reply is sent. */ 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 module_env *env = qstate->env; @@ -451,6 +455,10 @@ lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq) INET6_SIZE); 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; } @@ -487,7 +495,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) * module_finished */ return module_finished; } - + /* We have not asked for subnet data */ if (!sq->subnet_sent) { if (s_in->subnet_validdata) @@ -496,7 +504,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) cp_edns_bad_response(c_out, c_in); return module_finished; } - + /* subnet sent but nothing came back */ if (!s_in->subnet_validdata) { /* The authority indicated no support for edns subnet. As a @@ -513,11 +521,11 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) cp_edns_bad_response(c_out, c_in); return module_finished; } - + /* Being here means we have asked for and got a subnet specific * answer. Also, the answer from the authority is not yet cached * anywhere. */ - + /* can we accept response? */ if(s_out->subnet_addr_fam != s_in->subnet_addr_fam || s_out->subnet_source_mask != s_in->subnet_source_mask || @@ -602,7 +610,7 @@ parse_subnet_option(struct edns_option* ecs_option, struct ecs_data* ecs) return 1; } -static void +void subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs, struct config_file* cfg) { @@ -759,13 +767,13 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, } 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++; lock_rw_unlock(&sne->biglock); verbose(VERB_QUERY, "subnetcache: answered from cache"); qstate->ext_state[id] = module_finished; - ecs_opt_list_append(&sq->ecs_client_out, + subnet_ecs_opt_list_append(&sq->ecs_client_out, &qstate->edns_opts_front_out, qstate); return; } @@ -787,7 +795,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, sq->ecs_server_out.subnet_source_mask = qstate->env->cfg->max_client_subnet_ipv6; /* Safe to copy completely, even if the source is limited by the - * configuration. ecs_opt_list_append() will limit the address. + * configuration. subnet_ecs_opt_list_append() will limit the address. * */ memcpy(&sq->ecs_server_out.subnet_addr, sq->ecs_client_in.subnet_addr, INET6_SIZE); @@ -811,7 +819,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, qstate->ext_state[id] = eval_response(qstate, id, sq); if(qstate->ext_state[id] == module_finished && qstate->return_msg) { - ecs_opt_list_append(&sq->ecs_client_out, + subnet_ecs_opt_list_append(&sq->ecs_client_out, &qstate->edns_opts_front_out, qstate); } qstate->no_cache_store = sq->started_no_cache_store; diff --git a/edns-subnet/subnetmod.h b/edns-subnet/subnetmod.h index 27ba2ee74..8e6ccd66e 100644 --- a/edns-subnet/subnetmod.h +++ b/edns-subnet/subnetmod.h @@ -143,4 +143,12 @@ int ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, /** mark subnet msg to be deleted */ void subnet_markdel(void* key); +/** Add ecs struct to edns list, after parsing it to wire format. */ +void subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list, + struct module_qstate *qstate); + +/** Create ecs_data from the sockaddr_storage information. */ +void subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs, + struct config_file* cfg); + #endif /* SUBNETMOD_H */ diff --git a/services/mesh.c b/services/mesh.c index 4b022d47f..1f3eadad2 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -64,6 +64,11 @@ #include "respip/respip.h" #include "services/listen_dnsport.h" +#ifdef CLIENT_SUBNET +#include "edns-subnet/subnetmod.h" +#include "edns-subnet/edns-subnet.h" +#endif + /** subtract timers and the values do not overflow or become negative */ static void timeval_subtract(struct timeval* d, const struct timeval* end, const struct timeval* start) @@ -683,6 +688,107 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, return 1; } +#ifdef CLIENT_SUBNET +/* Same logic as mesh_schedule_prefetch but tailored to the subnet module logic + * like passing along the comm_reply info. This will be faked into an EDNS + * option for processing by the subnet module if the client has not already + * attached its own ECS data. */ +static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh, + struct query_info* qinfo, uint16_t qflags, time_t leeway, int run, + int rpz_passthru, struct mesh_state* mstate, + struct sockaddr_storage *client_addr) +{ + struct mesh_state* s = NULL; + struct edns_option* opt = NULL; +#ifdef UNBOUND_DEBUG + struct rbnode_type* n; +#endif + + if(!mesh_make_new_space(mesh, NULL)) { + verbose(VERB_ALGO, "Too many queries. dropped prefetch."); + mesh->stats_dropped ++; + return; + } + + s = mesh_state_create(mesh->env, qinfo, NULL, + qflags&(BIT_RD|BIT_CD), 0, 0); + if(!s) { + log_err("prefetch_subnet mesh_state_create: out of memory"); + return; + } + + mesh_state_make_unique(s); + + opt = edns_opt_list_find(mstate->s.edns_opts_front_in, mesh->env->cfg->client_subnet_opcode); + if(opt) { + /* Use the client's ECS data */ + if(!edns_opt_list_append(&s->s.edns_opts_front_in, opt->opt_code, + opt->opt_len, opt->opt_data, s->s.region)) { + log_err("prefetch_subnet edns_opt_list_append: out of memory"); + return; + } + } else { + /* Fake the ECS data from the client's IP */ + struct ecs_data ecs; + memset(&ecs, 0, sizeof(ecs)); + subnet_option_from_ss(client_addr, &ecs, mesh->env->cfg); + + if(ecs.subnet_validdata == 0) { + log_err("prefetch_subnet subnet_option_from_ss: invalid data"); + return; + } + + subnet_ecs_opt_list_append(&ecs, &s->s.edns_opts_front_in, &s->s); + if(!s->s.edns_opts_front_in) { + log_err("prefetch_subnet subnet_ecs_opt_list_append: out of memory"); + return; + } + } +#ifdef UNBOUND_DEBUG + n = +#else + (void) +#endif + rbtree_insert(&mesh->all, &s->node); + log_assert(n != NULL); + /* set detached (it is now) */ + mesh->num_detached_states++; + /* make it ignore the cache */ + sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region); + s->s.prefetch_leeway = leeway; + + if(s->list_select == mesh_no_list) { + /* move to either the forever or the jostle_list */ + if(mesh->num_forever_states < mesh->max_forever_states) { + mesh->num_forever_states ++; + mesh_list_insert(s, &mesh->forever_first, + &mesh->forever_last); + s->list_select = mesh_forever_list; + } else { + mesh_list_insert(s, &mesh->jostle_first, + &mesh->jostle_last); + s->list_select = mesh_jostle_list; + } + } + + s->s.rpz_passthru = rpz_passthru; + + if(!run) { +#ifdef UNBOUND_DEBUG + n = +#else + (void) +#endif + rbtree_insert(&mesh->run, &s->run_node); + log_assert(n != NULL); + return; + } + + mesh_state_delete(&mstate->s); + mesh_run(mesh, s, module_event_new, NULL); +} +#endif /* CLIENT_SUBNET */ + /* Internal backend routine of mesh_new_prefetch(). It takes one additional * parameter, 'run', which controls whether to run the prefetch state * immediately. When this function is called internally 'run' could be @@ -1699,6 +1805,11 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, struct query_info* qinfo = NULL; uint16_t qflags; int rpz_p = 0; + struct sockaddr_storage client_addr; + + if (mstate->reply_list) { + client_addr = mstate->reply_list->query_reply.addr; + } mesh_query_done(mstate); mesh_walk_supers(mesh, mstate); @@ -1712,10 +1823,24 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, rpz_p = mstate->s.rpz_passthru; } - mesh_state_delete(&mstate->s); if(qinfo) { +#ifdef CLIENT_SUBNET + if(modstack_find(&mesh->mods, "subnetcache") != -1 ) { + mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, + 0, 1, rpz_p, mstate, &client_addr); + } + else { + mesh_state_delete(&mstate->s); + mesh_schedule_prefetch(mesh, qinfo, qflags, + 0, 1, rpz_p); + } +#else + mesh_state_delete(&mstate->s); mesh_schedule_prefetch(mesh, qinfo, qflags, 0, 1, rpz_p); +#endif + } else { + mesh_state_delete(&mstate->s); } return 0; } From 40e47bf767f1dd235cd242fd4ea0c7b9ebde59da Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Thu, 6 Jul 2023 21:57:27 +0200 Subject: [PATCH 2/2] - For #664: easier code flow for subnetcache prefetching. - For #664: add testcase. --- daemon/worker.c | 2 +- edns-subnet/subnetmod.c | 8 +- services/mesh.c | 156 +++++++--------------------------- services/mesh.h | 4 +- testdata/subnet_prefetch.crpl | 100 +++++++++------------- util/data/msgreply.c | 38 ++++++++- util/data/msgreply.h | 6 ++ 7 files changed, 120 insertions(+), 194 deletions(-) diff --git a/daemon/worker.c b/daemon/worker.c index bf8c5d6b6..d85e1f6e6 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -786,7 +786,7 @@ reply_and_prefetch(struct worker* worker, struct query_info* qinfo, if(modstack_find(&worker->env.mesh->mods, "subnetcache") != -1 && worker->env.unique_mesh) { mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway + - PREFETCH_EXPIRY_ADD, rpz_passthru, repinfo, opt_list); + PREFETCH_EXPIRY_ADD, rpz_passthru, &repinfo->addr, opt_list); return; } #endif diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index 02994b3ab..6ec3be497 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -331,6 +331,8 @@ update_cache(struct module_qstate *qstate, int id) struct ecs_data *edns = &sq->ecs_client_in; size_t i; hashvalue_type h; + struct lruhash_entry* lru_entry; + int need_to_insert; /* qinfo_hash is not set if it is prefetch request */ if (qstate->minfo[id] && ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash) { @@ -340,9 +342,9 @@ update_cache(struct module_qstate *qstate, int id) } /* Step 1, general qinfo lookup */ - struct lruhash_entry *lru_entry = slabhash_lookup(subnet_msg_cache, h, + lru_entry = slabhash_lookup(subnet_msg_cache, h, &qstate->qinfo, 1); - int need_to_insert = (lru_entry == NULL); + need_to_insert = (lru_entry == NULL); if (!lru_entry) { void* data = calloc(1, sizeof(struct subnet_msg_cache_data)); @@ -456,7 +458,7 @@ lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq, sq->ecs_client_out.subnet_validdata = 1; } - if (prefetch && *qstate->env->now > ((struct reply_info *)node->elem)->prefetch_ttl) { + if (prefetch && *qstate->env->now >= ((struct reply_info *)node->elem)->prefetch_ttl) { qstate->need_refetch = 1; } return 1; diff --git a/services/mesh.c b/services/mesh.c index 520678734..2eea0b558 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -688,107 +688,6 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, return 1; } -#ifdef CLIENT_SUBNET -/* Same logic as mesh_schedule_prefetch but tailored to the subnet module logic - * like passing along the comm_reply info. This will be faked into an EDNS - * option for processing by the subnet module if the client has not already - * attached its own ECS data. */ -static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh, - struct query_info* qinfo, uint16_t qflags, time_t leeway, int run, - int rpz_passthru, struct mesh_state* mstate, - struct sockaddr_storage *client_addr) -{ - struct mesh_state* s = NULL; - struct edns_option* opt = NULL; -#ifdef UNBOUND_DEBUG - struct rbnode_type* n; -#endif - - if(!mesh_make_new_space(mesh, NULL)) { - verbose(VERB_ALGO, "Too many queries. dropped prefetch."); - mesh->stats_dropped ++; - return; - } - - s = mesh_state_create(mesh->env, qinfo, NULL, - qflags&(BIT_RD|BIT_CD), 0, 0); - if(!s) { - log_err("prefetch_subnet mesh_state_create: out of memory"); - return; - } - - mesh_state_make_unique(s); - - opt = edns_opt_list_find(mstate->s.edns_opts_front_in, mesh->env->cfg->client_subnet_opcode); - if(opt) { - /* Use the client's ECS data */ - if(!edns_opt_list_append(&s->s.edns_opts_front_in, opt->opt_code, - opt->opt_len, opt->opt_data, s->s.region)) { - log_err("prefetch_subnet edns_opt_list_append: out of memory"); - return; - } - } else { - /* Fake the ECS data from the client's IP */ - struct ecs_data ecs; - memset(&ecs, 0, sizeof(ecs)); - subnet_option_from_ss(client_addr, &ecs, mesh->env->cfg); - - if(ecs.subnet_validdata == 0) { - log_err("prefetch_subnet subnet_option_from_ss: invalid data"); - return; - } - - subnet_ecs_opt_list_append(&ecs, &s->s.edns_opts_front_in, &s->s); - if(!s->s.edns_opts_front_in) { - log_err("prefetch_subnet subnet_ecs_opt_list_append: out of memory"); - return; - } - } -#ifdef UNBOUND_DEBUG - n = -#else - (void) -#endif - rbtree_insert(&mesh->all, &s->node); - log_assert(n != NULL); - /* set detached (it is now) */ - mesh->num_detached_states++; - /* make it ignore the cache */ - sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region); - s->s.prefetch_leeway = leeway; - - if(s->list_select == mesh_no_list) { - /* move to either the forever or the jostle_list */ - if(mesh->num_forever_states < mesh->max_forever_states) { - mesh->num_forever_states ++; - mesh_list_insert(s, &mesh->forever_first, - &mesh->forever_last); - s->list_select = mesh_forever_list; - } else { - mesh_list_insert(s, &mesh->jostle_first, - &mesh->jostle_last); - s->list_select = mesh_jostle_list; - } - } - - s->s.rpz_passthru = rpz_passthru; - - if(!run) { -#ifdef UNBOUND_DEBUG - n = -#else - (void) -#endif - rbtree_insert(&mesh->run, &s->run_node); - log_assert(n != NULL); - return; - } - - mesh_state_delete(&mstate->s); - mesh_run(mesh, s, module_event_new, NULL); -} -#endif /* CLIENT_SUBNET */ - /* Internal backend routine of mesh_new_prefetch(). It takes one additional * parameter, 'run', which controls whether to run the prefetch state * immediately. When this function is called internally 'run' could be @@ -874,7 +773,7 @@ static void mesh_schedule_prefetch(struct mesh_area* mesh, * attached its own ECS data. */ static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh, 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 edns_option* opt = NULL; @@ -907,7 +806,7 @@ static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh, /* Fake the ECS data from the client's IP */ struct ecs_data ecs; memset(&ecs, 0, sizeof(ecs)); - subnet_option_from_ss(&rep->addr, &ecs, mesh->env->cfg); + subnet_option_from_ss(addr, &ecs, mesh->env->cfg); if(ecs.subnet_validdata == 0) { log_err("prefetch_subnet subnet_option_from_ss: invalid data"); return; @@ -963,14 +862,14 @@ static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh, void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, 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)rep; #ifdef CLIENT_SUBNET - if(rep) + if(addr) mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, leeway, 1, - rpz_passthru, rep, opt_list); + rpz_passthru, addr, opt_list); else #endif mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1, @@ -1939,13 +1838,19 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, if(s == module_finished) { if(mstate->s.curmod == 0) { struct query_info* qinfo = NULL; + struct edns_option* opt_list = NULL, *ecs; + struct sockaddr_storage addr; uint16_t qflags; int rpz_p = 0; - struct sockaddr_storage client_addr; - if (mstate->reply_list) { - client_addr = mstate->reply_list->query_reply.addr; - } +#ifdef CLIENT_SUBNET + 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.addr; + } else +#endif + memset(&addr, 0, sizeof(addr)); mesh_query_done(mstate); mesh_walk_supers(mesh, mstate); @@ -1956,25 +1861,26 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, * we need to make a copy of the query info here. */ if(mstate->s.need_refetch) { 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; } if(qinfo) { -#ifdef CLIENT_SUBNET - if(modstack_find(&mesh->mods, "subnetcache") != -1 ) { - mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, - 0, 1, rpz_p, mstate, &client_addr); - } - else { - mesh_state_delete(&mstate->s); - mesh_schedule_prefetch(mesh, qinfo, qflags, - 0, 1, rpz_p); - } -#else mesh_state_delete(&mstate->s); - mesh_schedule_prefetch(mesh, qinfo, qflags, - 0, 1, rpz_p); -#endif + mesh_new_prefetch(mesh, qinfo, qflags, 0, + rpz_p, + addr.ss_family!=AF_UNSPEC?&addr:NULL, + opt_list); } else { mesh_state_delete(&mstate->s); } diff --git a/services/mesh.h b/services/mesh.h index 3be9b63fa..eff3e73fd 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -335,13 +335,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 rpz_passthru: if true, the rpz passthru was previously found and * 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 * enabled. */ void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, 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. diff --git a/testdata/subnet_prefetch.crpl b/testdata/subnet_prefetch.crpl index 7083aba6a..934103811 100644 --- a/testdata/subnet_prefetch.crpl +++ b/testdata/subnet_prefetch.crpl @@ -1,18 +1,17 @@ -; Check if the prefetch option works properly for messages stored in the global -; cache for non-ECS clients. The prefetch query needs to result in an ECS -; outgoing query based on the client's IP. +; Check if the prefetch option works properly for messages stored in ECS cache +; for non-ECS clients. server: trust-anchor-signaling: no target-fetch-policy: "0 0 0 0 0" send-client-subnet: 1.2.3.4 max-client-subnet-ipv4: 21 + client-subnet-always-forward: yes module-config: "subnetcache iterator" verbosity: 3 access-control: 127.0.0.1 allow_snoop qname-minimisation: no minimal-responses: no - serve-expired: yes prefetch: yes stub-zone: @@ -20,7 +19,7 @@ stub-zone: stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. CONFIG_END -SCENARIO_BEGIN Test prefetch option for global cache +SCENARIO_BEGIN Test prefetch option for ECS cache ; K.ROOT-SERVERS.NET. RANGE_BEGIN 0 100 @@ -34,9 +33,6 @@ RANGE_BEGIN 0 100 SECTION ANSWER . IN NS K.ROOT-SERVERS.NET. SECTION ADDITIONAL - HEX_EDNSDATA_BEGIN - ;; we expect to receive empty - HEX_EDNSDATA_END K.ROOT-SERVERS.NET. IN A 193.0.14.129 ENTRY_END @@ -65,9 +61,6 @@ RANGE_BEGIN 0 100 SECTION ANSWER com. IN NS a.gtld-servers.net. SECTION ADDITIONAL - HEX_EDNSDATA_BEGIN - ;; we expect to receive empty - HEX_EDNSDATA_END a.gtld-servers.net. IN A 192.5.6.30 ENTRY_END @@ -85,7 +78,7 @@ RANGE_BEGIN 0 100 RANGE_END ; ns.example.com. -RANGE_BEGIN 0 10 +RANGE_BEGIN 0 100 ADDRESS 1.2.3.4 ENTRY_BEGIN MATCH opcode qtype qname @@ -96,43 +89,6 @@ RANGE_BEGIN 0 10 SECTION ANSWER example.com. IN NS ns.example.com. SECTION ADDITIONAL - HEX_EDNSDATA_BEGIN - ;; we expect to receive empty - HEX_EDNSDATA_END - 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 - 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 - HEX_EDNSDATA_BEGIN - ;; we expect to receive empty - HEX_EDNSDATA_END ns.example.com. IN A 1.2.3.4 ENTRY_END @@ -144,7 +100,7 @@ RANGE_BEGIN 11 100 SECTION QUESTION www.example.com. IN A SECTION ANSWER - www.example.com. IN A 10.20.30.40 + www.example.com. 10 IN A 10.20.30.40 SECTION AUTHORITY example.com. IN NS ns.example.com. SECTION ADDITIONAL @@ -167,7 +123,7 @@ SECTION QUESTION www.example.com. IN A ENTRY_END -; This answer should be in the global cache +; This answer will end up in the subnet cache STEP 2 CHECK_ANSWER ENTRY_BEGIN MATCH all @@ -183,33 +139,53 @@ ns.example.com. IN A 1.2.3.4 ENTRY_END ; Try to trigger a prefetch -STEP 3 TIME_PASSES ELAPSE 11 +STEP 3 TIME_PASSES ELAPSE 9 -STEP 11 QUERY +STEP 4 QUERY ENTRY_BEGIN REPLY RD SECTION QUESTION www.example.com. IN A ENTRY_END -; This expired record came from the cache and a prefetch is triggered -STEP 12 CHECK_ANSWER +; This record came from the cache and a prefetch is triggered +STEP 5 CHECK_ANSWER ENTRY_BEGIN MATCH all ttl REPLY QR RD RA NOERROR SECTION QUESTION www.example.com. IN A SECTION ANSWER -www.example.com. 30 IN A 10.20.30.40 +www.example.com. 1 IN A 10.20.30.40 SECTION AUTHORITY -example.com. 3589 IN NS ns.example.com. +example.com. 3591 IN NS ns.example.com. SECTION ADDITIONAL -ns.example.com. 3589 IN A 1.2.3.4 +ns.example.com. 3591 IN A 1.2.3.4 ENTRY_END -; Allow upstream to reply to the prefetch query. -; It can only be answered if correct ECS was derived from the client's IP. -; Otherwise the test will fail with "messages pending". -STEP 13 TRAFFIC +; Allow for some time to pass to differentiate from a cached vs resolved answer +STEP 6 TIME_PASSES ELAPSE 1 + +STEP 7 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +; This prefetched record came from the ECS cache +STEP 8 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ttl +REPLY QR RD RA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. 9 IN A 10.20.30.40 +SECTION AUTHORITY +example.com. 3599 IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. 3599 IN A 1.2.3.4 +ENTRY_END SCENARIO_END diff --git a/util/data/msgreply.c b/util/data/msgreply.c index e3ee607b1..0b4c0b534 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -1195,7 +1195,7 @@ int inplace_cb_query_response_call(struct module_env* env, } struct edns_option* edns_opt_copy_region(struct edns_option* list, - struct regional* region) + struct regional* region) { struct edns_option* result = NULL, *cur = NULL, *s; while(list) { @@ -1224,6 +1224,42 @@ struct edns_option* edns_opt_copy_region(struct edns_option* list, 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; iopt_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) { if(!p && !q) return 0; diff --git a/util/data/msgreply.h b/util/data/msgreply.h index 9538adc5a..820090a54 100644 --- a/util/data/msgreply.h +++ b/util/data/msgreply.h @@ -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 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 */