From: George Thessalonikefs Date: Wed, 17 Feb 2021 12:39:15 +0000 (+0100) Subject: - Fix #417: prefetch and ECS causing cache corruption when used X-Git-Tag: release-1.16.0rc1~9^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F674%2Fhead;p=thirdparty%2Funbound.git - Fix #417: prefetch and ECS causing cache corruption when used together. --- diff --git a/daemon/worker.c b/daemon/worker.c index 270de8975..bf8c5d6b6 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -98,7 +98,7 @@ /** ratelimit for error responses */ #define ERROR_RATELIMIT 100 /* qps */ -/** +/** * seconds to add to prefetch leeway. This is a TTL that expires old rrsets * earlier than they should in order to put the new update into the cache. * This additional value is to make sure that if not all TTLs are equal in @@ -763,11 +763,12 @@ bail_out: /** Reply to client and perform prefetch to keep cache up to date. */ static void -reply_and_prefetch(struct worker* worker, struct query_info* qinfo, +reply_and_prefetch(struct worker* worker, struct query_info* qinfo, uint16_t flags, struct comm_reply* repinfo, time_t leeway, int noreply, - int rpz_passthru) + int rpz_passthru, struct edns_option* opt_list) { - /* first send answer to client to keep its latency + (void)opt_list; + /* first send answer to client to keep its latency * as small as a cachereply */ if(!noreply) { if(repinfo->c->tcp_req_info) { @@ -778,13 +779,23 @@ reply_and_prefetch(struct worker* worker, struct query_info* qinfo, comm_point_send_reply(repinfo); } server_stats_prefetch(&worker->stats, worker); - +#ifdef CLIENT_SUBNET + /* Check if the subnet module is enabled. In that case pass over the + * comm_reply information for ECS generation later. The mesh states are + * unique when subnet is enabled. */ + 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); + return; + } +#endif /* create the prefetch in the mesh as a normal lookup without * client addrs waiting, which has the cache blacklisted (to bypass * the cache and go to the network for the data). */ /* this (potentially) runs the mesh for the new query */ - mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway + - PREFETCH_EXPIRY_ADD, rpz_passthru); + mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway + + PREFETCH_EXPIRY_ADD, rpz_passthru, NULL, NULL); } /** @@ -1240,6 +1251,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, struct lruhash_entry* e; struct query_info qinfo; struct edns_data edns; + struct edns_option* original_edns_list = NULL; enum acl_access acl; struct acl_addr* acladdr; int rc = 0; @@ -1612,6 +1624,11 @@ worker_handle_request(struct comm_point* c, void* arg, int error, cinfo = &cinfo_tmp; } + /* Keep the original edns list around. The pointer could change if there is + * a cached answer (through the inplace callback function there). + * No need to actually copy the contents as they shouldn't change. + * Used while prefetching and subnet is enabled. */ + original_edns_list = edns.opt_list_in; lookup_cache: /* Lookup the cache. In case we chase an intermediate CNAME chain * this is a two-pass operation, and lookup_qinfo is different for @@ -1649,7 +1666,8 @@ lookup_cache: sldns_buffer_read_u16_at(c->buffer, 2), repinfo, leeway, (partial_rep || need_drop), - rpz_passthru); + rpz_passthru, + original_edns_list); if(!partial_rep) { rc = 0; regional_free_all(worker->scratchpad); diff --git a/doc/Changelog b/doc/Changelog index 15f448fd5..f55f4cab0 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +12 May 2022: George + - Fix #417: prefetch and ECS causing cache corruption when used + together. + 11 May 2022: Wouter - Fix #673: DNS over TLS: error: SSL_handshake syscall: No route to host. diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index fcea71c31..25190b040 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); @@ -602,7 +602,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) { @@ -765,7 +765,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, 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 +787,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 +811,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..c877692b4 100644 --- a/edns-subnet/subnetmod.h +++ b/edns-subnet/subnetmod.h @@ -143,4 +143,11 @@ 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/pythonmod/doc/modules/functions.rst b/pythonmod/doc/modules/functions.rst index 951a17f94..76c21d6db 100644 --- a/pythonmod/doc/modules/functions.rst +++ b/pythonmod/doc/modules/functions.rst @@ -60,7 +60,7 @@ EDNS options .. function:: edns_opt_list_remove(list, code); - Remove an ENDS option code from the list. + Remove an EDNS option code from the list. .. note:: All :class:`edns_option` with the code will be removed :param list: linked list of :class:`edns_option` diff --git a/pythonmod/doc/modules/struct.rst b/pythonmod/doc/modules/struct.rst index 310cf5524..6e6a32e3e 100644 --- a/pythonmod/doc/modules/struct.rst +++ b/pythonmod/doc/modules/struct.rst @@ -77,7 +77,7 @@ module_qstate .. attribute:: edns_opts_back_in_iter - Iterator for `ends_opts_back_in`. + Iterator for `edns_opts_back_in`. .. attribute:: edns_opts_front_out diff --git a/pythonmod/examples/inplace_callbacks.py b/pythonmod/examples/inplace_callbacks.py index de375b4e1..2682fbd02 100644 --- a/pythonmod/examples/inplace_callbacks.py +++ b/pythonmod/examples/inplace_callbacks.py @@ -207,7 +207,7 @@ def inplace_servfail_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, """ log_info("python: called back while servfail.") - # Append the example ENDS option + # Append the example EDNS option b = bytearray.fromhex("") edns_opt_list_append(opt_list_out, 65003, b, region) diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index afe4cb71d..03153bd64 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -1158,7 +1158,7 @@ if_is_ssl(const char* ifname, const char* port, int ssl_port, * @param do_auto: use automatic interface detection. * If enabled, then ifname must be the wildcard name. * @param do_udp: if udp should be used. - * @param do_tcp: if udp should be used. + * @param do_tcp: if tcp should be used. * @param hints: for getaddrinfo. family and flags have to be set by caller. * @param port: Port number to use (as string). * @param list: list of open ports, appended to, changed to point to list head. diff --git a/services/mesh.c b/services/mesh.c index 7c767e626..fbaa966bd 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) @@ -736,11 +741,11 @@ static void mesh_schedule_prefetch(struct mesh_area* mesh, /* 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_list_insert(s, &mesh->forever_first, &mesh->forever_last); s->list_select = mesh_forever_list; } else { - mesh_list_insert(s, &mesh->jostle_first, + mesh_list_insert(s, &mesh->jostle_first, &mesh->jostle_last); s->list_select = mesh_jostle_list; } @@ -761,10 +766,114 @@ static void mesh_schedule_prefetch(struct mesh_area* mesh, mesh_run(mesh, s, module_event_new, NULL); } +#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 comm_reply* rep, struct edns_option* edns_list) +{ + 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(edns_list, 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(&rep->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_run(mesh, s, module_event_new, NULL); +} +#endif /* CLIENT_SUBNET */ + 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) { - mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1, rpz_passthru); + (void)opt_list; + (void)rep; +#ifdef CLIENT_SUBNET + if(rep) + mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, leeway, 1, + rpz_passthru, rep, opt_list); + else +#endif + mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1, + rpz_passthru); } void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e, @@ -1561,7 +1670,7 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, struct comm_reply* rep, uint16_t qid, uint16_t qflags, const struct query_info* qinfo) { - struct mesh_reply* r = regional_alloc(s->s.region, + struct mesh_reply* r = regional_alloc(s->s.region, sizeof(struct mesh_reply)); if(!r) return 0; diff --git a/services/mesh.h b/services/mesh.h index 526e679fe..3be9b63fa 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -335,9 +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 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); + uint16_t qflags, time_t leeway, int rpz_passthru, + struct comm_reply* rep, 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 new file mode 100644 index 000000000..7083aba6a --- /dev/null +++ b/testdata/subnet_prefetch.crpl @@ -0,0 +1,215 @@ +; 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. + +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 + 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: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test prefetch option for global cache + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 + ENTRY_BEGIN + MATCH opcode qtype qname ednsdata + ADJUST copy_id + REPLY QR NOERROR + SECTION QUESTION + . IN NS + 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 + + 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 ednsdata + ADJUST copy_id + REPLY QR NOERROR + SECTION QUESTION + com. IN NS + 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 + + 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 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 + 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 ednsdata + ADJUST copy_id copy_ednsdata_assume_clientsubnet + REPLY QR NOERROR + SECTION QUESTION + www.example.com. IN A + SECTION ANSWER + www.example.com. IN A 10.20.30.40 + SECTION AUTHORITY + example.com. IN NS ns.example.com. + SECTION ADDITIONAL + HEX_EDNSDATA_BEGIN + ; client is 127.0.0.1 + 00 08 ; OPC + 00 07 ; option length + 00 01 ; Family + 15 00 ; source mask, scopemask + 7f 00 00 ; address + HEX_EDNSDATA_END + ns.example.com. 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 + +; This answer should be in the global cache +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. 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 + +; Try to trigger a prefetch +STEP 3 TIME_PASSES ELAPSE 11 + +STEP 11 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 +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 +SECTION AUTHORITY +example.com. 3589 IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. 3589 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 + +SCENARIO_END diff --git a/testdata/subnet_prefetch_with_client_ecs.crpl b/testdata/subnet_prefetch_with_client_ecs.crpl new file mode 100644 index 000000000..b0410255e --- /dev/null +++ b/testdata/subnet_prefetch_with_client_ecs.crpl @@ -0,0 +1,221 @@ +; Check if the prefetch option works properly for messages stored in the global +; cache for ECS clients. The prefetch query needs to result in an ECS +; outgoing query using the client's ECS data. + +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 + 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: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test prefetch option for global cache + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 + ENTRY_BEGIN + MATCH opcode qtype qname ednsdata + ADJUST copy_id + REPLY QR NOERROR + SECTION QUESTION + . IN NS + 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 + + 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 ednsdata + ADJUST copy_id + REPLY QR NOERROR + SECTION QUESTION + com. IN NS + 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 + + 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 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 + 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 ednsdata + ADJUST copy_id copy_ednsdata_assume_clientsubnet + REPLY QR NOERROR + SECTION QUESTION + www.example.com. IN A + SECTION ANSWER + www.example.com. IN A 10.20.30.40 + SECTION AUTHORITY + example.com. IN NS ns.example.com. + SECTION ADDITIONAL + HEX_EDNSDATA_BEGIN + ; client is 127.0.0.1 + 00 08 ; OPC + 00 05 ; option length + 00 01 ; Family + 08 00 ; source mask, scopemask + 7f ; address + HEX_EDNSDATA_END + ns.example.com. 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 + +; This answer should be in the global cache +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. 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 + +; Try to trigger a prefetch +STEP 3 TIME_PASSES ELAPSE 11 + +STEP 11 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +www.example.com. IN A +SECTION ADDITIONAL +HEX_EDNSDATA_BEGIN + 00 08 00 05 ; OPC, optlen + 00 01 08 00 ; ip4, source 8, scope 0 + 7f ; 127.0.0.0/8 +HEX_EDNSDATA_END +ENTRY_END + +; This expired record came from the cache and a prefetch is triggered +STEP 12 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ttl +REPLY QR RD RA DO NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. 30 IN A 10.20.30.40 +SECTION AUTHORITY +example.com. 3589 IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. 3589 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 + +SCENARIO_END