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
* 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;
/* 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;
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,
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);
* 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);
}
-; 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:
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
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
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
RANGE_END
; ns.example.com.
-RANGE_BEGIN 0 10
- 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
-
- ; 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
+RANGE_BEGIN 0 100
ADDRESS 1.2.3.4
ENTRY_BEGIN
MATCH opcode qtype qname
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
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
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
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 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
-; 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
+; 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