]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- For #664: easier code flow for subnetcache prefetching.
authorGeorge Thessalonikefs <george@nlnetlabs.nl>
Thu, 6 Jul 2023 19:57:27 +0000 (21:57 +0200)
committerGeorge Thessalonikefs <george@nlnetlabs.nl>
Thu, 6 Jul 2023 20:22:21 +0000 (22:22 +0200)
- For #664: add testcase.

daemon/worker.c
edns-subnet/subnetmod.c
services/mesh.c
services/mesh.h
testdata/subnet_prefetch.crpl
util/data/msgreply.c
util/data/msgreply.h

index bf8c5d6b6763290594b8c3342a65d7c880d4a7c6..d85e1f6e65d8d049a52fbc92867b05697110ef0e 100644 (file)
@@ -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
index 02994b3ab0372d47dfbc1e419a796ac9c3c9592e..6ec3be49770b4002d41526be46509f282af01a55 100644 (file)
@@ -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;
index 520678734381ad7afb0bead54a8f1c2206f0cd9c..2eea0b558be881da13cf07485dce6498497dc693 100644 (file)
@@ -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);
                        }
index 3be9b63faeddcabed3c40e1ac04d691219d487c4..eff3e73fdecab31fbbe0aa15f25ebd6049fadc2a 100644 (file)
@@ -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.
index 7083aba6a5637f37bb69e2cafa080adb42fecce6..93410381165117a8cb734b1ce7c960d75b8d5136 100644 (file)
@@ -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,41 +78,7 @@ RANGE_BEGIN 0 100
 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
@@ -130,9 +89,6 @@ RANGE_BEGIN 11 100
                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 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
index e3ee607b15400048033dba21d2d4065818c53e57..0b4c0b534b3c16fe0fe77adb7884c17d80d63461 100644 (file)
@@ -1195,10 +1195,45 @@ 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) {
+               /* 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;
+
+               /* examine next element */
+               list = list->next;
+       }
+       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;
@@ -1218,6 +1253,7 @@ struct edns_option* edns_opt_copy_region(struct edns_option* list,
                else    result = s;
                cur = s;
 
+next:
                /* examine next element */
                list = list->next;
        }
index 9538adc5a8b2bf7d48b02c72b2c53af14bfa9777..820090a5423d6563b1abf81f5f5e5e3298f8c6a1 100644 (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 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
  */