165b390aaSDag-Erling Smørgrav /* 265b390aaSDag-Erling Smørgrav * edns-subnet/subnetmod.c - edns subnet module. Must be called before validator 365b390aaSDag-Erling Smørgrav * and iterator. 465b390aaSDag-Erling Smørgrav * 565b390aaSDag-Erling Smørgrav * Copyright (c) 2013, NLnet Labs. All rights reserved. 665b390aaSDag-Erling Smørgrav * 765b390aaSDag-Erling Smørgrav * This software is open source. 865b390aaSDag-Erling Smørgrav * 965b390aaSDag-Erling Smørgrav * Redistribution and use in source and binary forms, with or without 1065b390aaSDag-Erling Smørgrav * modification, are permitted provided that the following conditions 1165b390aaSDag-Erling Smørgrav * are met: 1265b390aaSDag-Erling Smørgrav * 1365b390aaSDag-Erling Smørgrav * Redistributions of source code must retain the above copyright notice, 1465b390aaSDag-Erling Smørgrav * this list of conditions and the following disclaimer. 1565b390aaSDag-Erling Smørgrav * 1665b390aaSDag-Erling Smørgrav * Redistributions in binary form must reproduce the above copyright notice, 1765b390aaSDag-Erling Smørgrav * this list of conditions and the following disclaimer in the documentation 1865b390aaSDag-Erling Smørgrav * and/or other materials provided with the distribution. 1965b390aaSDag-Erling Smørgrav * 2065b390aaSDag-Erling Smørgrav * Neither the name of the NLNET LABS nor the names of its contributors may 2165b390aaSDag-Erling Smørgrav * be used to endorse or promote products derived from this software without 2265b390aaSDag-Erling Smørgrav * specific prior written permission. 2365b390aaSDag-Erling Smørgrav * 2465b390aaSDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2565b390aaSDag-Erling Smørgrav * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2665b390aaSDag-Erling Smørgrav * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2765b390aaSDag-Erling Smørgrav * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2865b390aaSDag-Erling Smørgrav * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2965b390aaSDag-Erling Smørgrav * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 3065b390aaSDag-Erling Smørgrav * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 3165b390aaSDag-Erling Smørgrav * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 3265b390aaSDag-Erling Smørgrav * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 3365b390aaSDag-Erling Smørgrav * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 3465b390aaSDag-Erling Smørgrav * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3565b390aaSDag-Erling Smørgrav */ 3665b390aaSDag-Erling Smørgrav /** 3765b390aaSDag-Erling Smørgrav * \file 3865b390aaSDag-Erling Smørgrav * subnet module for unbound. 3965b390aaSDag-Erling Smørgrav */ 4065b390aaSDag-Erling Smørgrav 4165b390aaSDag-Erling Smørgrav #include "config.h" 4265b390aaSDag-Erling Smørgrav 4365b390aaSDag-Erling Smørgrav #ifdef CLIENT_SUBNET /* keeps splint happy */ 4465b390aaSDag-Erling Smørgrav 4565b390aaSDag-Erling Smørgrav #include "edns-subnet/subnetmod.h" 4665b390aaSDag-Erling Smørgrav #include "edns-subnet/edns-subnet.h" 4765b390aaSDag-Erling Smørgrav #include "edns-subnet/addrtree.h" 4865b390aaSDag-Erling Smørgrav #include "edns-subnet/subnet-whitelist.h" 4965b390aaSDag-Erling Smørgrav 5065b390aaSDag-Erling Smørgrav #include "services/mesh.h" 5165b390aaSDag-Erling Smørgrav #include "services/cache/dns.h" 5265b390aaSDag-Erling Smørgrav #include "util/module.h" 5365b390aaSDag-Erling Smørgrav #include "util/regional.h" 5465b390aaSDag-Erling Smørgrav #include "util/storage/slabhash.h" 5565b390aaSDag-Erling Smørgrav #include "util/config_file.h" 5665b390aaSDag-Erling Smørgrav #include "util/data/msgreply.h" 5765b390aaSDag-Erling Smørgrav #include "sldns/sbuffer.h" 58865f46b2SCy Schubert #include "sldns/wire2str.h" 59e86b9096SDag-Erling Smørgrav #include "iterator/iter_utils.h" 60335c7cdaSCy Schubert #ifdef USE_CACHEDB 61335c7cdaSCy Schubert #include "cachedb/cachedb.h" 62335c7cdaSCy Schubert #endif 6365b390aaSDag-Erling Smørgrav 6465b390aaSDag-Erling Smørgrav /** externally called */ 6565b390aaSDag-Erling Smørgrav void 6665b390aaSDag-Erling Smørgrav subnet_data_delete(void *d, void *ATTR_UNUSED(arg)) 6765b390aaSDag-Erling Smørgrav { 6865b390aaSDag-Erling Smørgrav struct subnet_msg_cache_data *r; 6965b390aaSDag-Erling Smørgrav r = (struct subnet_msg_cache_data*)d; 7065b390aaSDag-Erling Smørgrav addrtree_delete(r->tree4); 7165b390aaSDag-Erling Smørgrav addrtree_delete(r->tree6); 7265b390aaSDag-Erling Smørgrav free(r); 7365b390aaSDag-Erling Smørgrav } 7465b390aaSDag-Erling Smørgrav 7565b390aaSDag-Erling Smørgrav /** externally called */ 7665b390aaSDag-Erling Smørgrav size_t 7765b390aaSDag-Erling Smørgrav msg_cache_sizefunc(void *k, void *d) 7865b390aaSDag-Erling Smørgrav { 7965b390aaSDag-Erling Smørgrav struct msgreply_entry *q = (struct msgreply_entry*)k; 8065b390aaSDag-Erling Smørgrav struct subnet_msg_cache_data *r = (struct subnet_msg_cache_data*)d; 8165b390aaSDag-Erling Smørgrav size_t s = sizeof(struct msgreply_entry) 8265b390aaSDag-Erling Smørgrav + sizeof(struct subnet_msg_cache_data) 8365b390aaSDag-Erling Smørgrav + q->key.qname_len + lock_get_mem(&q->entry.lock); 8465b390aaSDag-Erling Smørgrav s += addrtree_size(r->tree4); 8565b390aaSDag-Erling Smørgrav s += addrtree_size(r->tree6); 8665b390aaSDag-Erling Smørgrav return s; 8765b390aaSDag-Erling Smørgrav } 8865b390aaSDag-Erling Smørgrav 8965b390aaSDag-Erling Smørgrav /** new query for ecs module */ 9065b390aaSDag-Erling Smørgrav static int 9165b390aaSDag-Erling Smørgrav subnet_new_qstate(struct module_qstate *qstate, int id) 9265b390aaSDag-Erling Smørgrav { 9365b390aaSDag-Erling Smørgrav struct subnet_qstate *sq = (struct subnet_qstate*)regional_alloc( 9465b390aaSDag-Erling Smørgrav qstate->region, sizeof(struct subnet_qstate)); 9565b390aaSDag-Erling Smørgrav if(!sq) 9665b390aaSDag-Erling Smørgrav return 0; 9765b390aaSDag-Erling Smørgrav qstate->minfo[id] = sq; 9865b390aaSDag-Erling Smørgrav memset(sq, 0, sizeof(*sq)); 99e86b9096SDag-Erling Smørgrav sq->started_no_cache_store = qstate->no_cache_store; 1000a92a9fcSCy Schubert sq->started_no_cache_lookup = qstate->no_cache_lookup; 10165b390aaSDag-Erling Smørgrav return 1; 10265b390aaSDag-Erling Smørgrav } 10365b390aaSDag-Erling Smørgrav 10465b390aaSDag-Erling Smørgrav /** Add ecs struct to edns list, after parsing it to wire format. */ 105a39a5a69SCy Schubert void 106a39a5a69SCy Schubert subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list, 1070a92a9fcSCy Schubert struct module_qstate *qstate, struct regional *region) 10865b390aaSDag-Erling Smørgrav { 10965b390aaSDag-Erling Smørgrav size_t sn_octs, sn_octs_remainder; 11065b390aaSDag-Erling Smørgrav sldns_buffer* buf = qstate->env->scratch_buffer; 11165b390aaSDag-Erling Smørgrav 11265b390aaSDag-Erling Smørgrav if(ecs->subnet_validdata) { 11365b390aaSDag-Erling Smørgrav log_assert(ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 || 11465b390aaSDag-Erling Smørgrav ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6); 11565b390aaSDag-Erling Smørgrav log_assert(ecs->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP4 || 11665b390aaSDag-Erling Smørgrav ecs->subnet_source_mask <= INET_SIZE*8); 11765b390aaSDag-Erling Smørgrav log_assert(ecs->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP6 || 11865b390aaSDag-Erling Smørgrav ecs->subnet_source_mask <= INET6_SIZE*8); 11965b390aaSDag-Erling Smørgrav 12065b390aaSDag-Erling Smørgrav sn_octs = ecs->subnet_source_mask / 8; 12165b390aaSDag-Erling Smørgrav sn_octs_remainder = 12265b390aaSDag-Erling Smørgrav (size_t)((ecs->subnet_source_mask % 8)>0?1:0); 12365b390aaSDag-Erling Smørgrav 12465b390aaSDag-Erling Smørgrav log_assert(sn_octs + sn_octs_remainder <= INET6_SIZE); 12565b390aaSDag-Erling Smørgrav 12665b390aaSDag-Erling Smørgrav sldns_buffer_clear(buf); 12765b390aaSDag-Erling Smørgrav sldns_buffer_write_u16(buf, ecs->subnet_addr_fam); 12865b390aaSDag-Erling Smørgrav sldns_buffer_write_u8(buf, ecs->subnet_source_mask); 12965b390aaSDag-Erling Smørgrav sldns_buffer_write_u8(buf, ecs->subnet_scope_mask); 13065b390aaSDag-Erling Smørgrav sldns_buffer_write(buf, ecs->subnet_addr, sn_octs); 13165b390aaSDag-Erling Smørgrav if(sn_octs_remainder) 13265b390aaSDag-Erling Smørgrav sldns_buffer_write_u8(buf, ecs->subnet_addr[sn_octs] & 13365b390aaSDag-Erling Smørgrav ~(0xFF >> (ecs->subnet_source_mask % 8))); 13465b390aaSDag-Erling Smørgrav sldns_buffer_flip(buf); 13565b390aaSDag-Erling Smørgrav 13665b390aaSDag-Erling Smørgrav edns_opt_list_append(list, 13765b390aaSDag-Erling Smørgrav qstate->env->cfg->client_subnet_opcode, 13865b390aaSDag-Erling Smørgrav sn_octs + sn_octs_remainder + 4, 1390a92a9fcSCy Schubert sldns_buffer_begin(buf), region); 14065b390aaSDag-Erling Smørgrav } 14165b390aaSDag-Erling Smørgrav } 14265b390aaSDag-Erling Smørgrav 143c7f4d7adSDag-Erling Smørgrav int ecs_whitelist_check(struct query_info* qinfo, 14465b390aaSDag-Erling Smørgrav uint16_t ATTR_UNUSED(flags), struct module_qstate* qstate, 14565b390aaSDag-Erling Smørgrav struct sockaddr_storage* addr, socklen_t addrlen, 14665b390aaSDag-Erling Smørgrav uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), 1470a92a9fcSCy Schubert struct regional *region, int id, void* ATTR_UNUSED(cbargs)) 14865b390aaSDag-Erling Smørgrav { 14965b390aaSDag-Erling Smørgrav struct subnet_qstate *sq; 15065b390aaSDag-Erling Smørgrav struct subnet_env *sn_env; 15165b390aaSDag-Erling Smørgrav 15265b390aaSDag-Erling Smørgrav if(!(sq=(struct subnet_qstate*)qstate->minfo[id])) 15365b390aaSDag-Erling Smørgrav return 1; 15465b390aaSDag-Erling Smørgrav sn_env = (struct subnet_env*)qstate->env->modinfo[id]; 15565b390aaSDag-Erling Smørgrav 15665b390aaSDag-Erling Smørgrav /* Cache by default, might be disabled after parsing EDNS option 15765b390aaSDag-Erling Smørgrav * received from nameserver. */ 158335c7cdaSCy Schubert if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)) { 15965b390aaSDag-Erling Smørgrav qstate->no_cache_store = 0; 160e86b9096SDag-Erling Smørgrav } 16165b390aaSDag-Erling Smørgrav 162103ba509SCy Schubert sq->subnet_sent_no_subnet = 0; 16365b390aaSDag-Erling Smørgrav if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream && 16465b390aaSDag-Erling Smørgrav qstate->env->cfg->client_subnet_always_forward) || 165c7f4d7adSDag-Erling Smørgrav ecs_is_whitelisted(sn_env->whitelist, 166c7f4d7adSDag-Erling Smørgrav addr, addrlen, qinfo->qname, qinfo->qname_len, 167c7f4d7adSDag-Erling Smørgrav qinfo->qclass))) { 16865b390aaSDag-Erling Smørgrav /* Address on whitelist or client query contains ECS option, we 16965b390aaSDag-Erling Smørgrav * want to sent out ECS. Only add option if it is not already 17065b390aaSDag-Erling Smørgrav * set. */ 171a39a5a69SCy Schubert if(!edns_opt_list_find(qstate->edns_opts_back_out, 172a39a5a69SCy Schubert qstate->env->cfg->client_subnet_opcode)) { 173103ba509SCy Schubert /* if the client is not wanting an EDNS subnet option, 174103ba509SCy Schubert * omit it and store that we omitted it but actually 175103ba509SCy Schubert * are doing EDNS subnet to the server. */ 176103ba509SCy Schubert if(sq->ecs_server_out.subnet_source_mask == 0) { 177103ba509SCy Schubert sq->subnet_sent_no_subnet = 1; 178103ba509SCy Schubert sq->subnet_sent = 0; 179103ba509SCy Schubert return 1; 180103ba509SCy Schubert } 181a39a5a69SCy Schubert subnet_ecs_opt_list_append(&sq->ecs_server_out, 1820a92a9fcSCy Schubert &qstate->edns_opts_back_out, qstate, region); 183a39a5a69SCy Schubert } 18465b390aaSDag-Erling Smørgrav sq->subnet_sent = 1; 18565b390aaSDag-Erling Smørgrav } 186a39a5a69SCy Schubert else { 18765b390aaSDag-Erling Smørgrav /* Outgoing ECS option is set, but we don't want to sent it to 18865b390aaSDag-Erling Smørgrav * this address, remove option. */ 189a39a5a69SCy Schubert if(edns_opt_list_find(qstate->edns_opts_back_out, 190a39a5a69SCy Schubert qstate->env->cfg->client_subnet_opcode)) { 19165b390aaSDag-Erling Smørgrav edns_opt_list_remove(&qstate->edns_opts_back_out, 19265b390aaSDag-Erling Smørgrav qstate->env->cfg->client_subnet_opcode); 193a39a5a69SCy Schubert } 19465b390aaSDag-Erling Smørgrav sq->subnet_sent = 0; 19565b390aaSDag-Erling Smørgrav } 19665b390aaSDag-Erling Smørgrav return 1; 19765b390aaSDag-Erling Smørgrav } 19865b390aaSDag-Erling Smørgrav 19965b390aaSDag-Erling Smørgrav 200e86b9096SDag-Erling Smørgrav void 201e86b9096SDag-Erling Smørgrav subnet_markdel(void* key) 202e86b9096SDag-Erling Smørgrav { 203e86b9096SDag-Erling Smørgrav struct msgreply_entry *e = (struct msgreply_entry*)key; 204e86b9096SDag-Erling Smørgrav e->key.qtype = 0; 205e86b9096SDag-Erling Smørgrav e->key.qclass = 0; 206e86b9096SDag-Erling Smørgrav } 207e86b9096SDag-Erling Smørgrav 20865b390aaSDag-Erling Smørgrav int 20965b390aaSDag-Erling Smørgrav subnetmod_init(struct module_env *env, int id) 21065b390aaSDag-Erling Smørgrav { 21165b390aaSDag-Erling Smørgrav struct subnet_env *sn_env = (struct subnet_env*)calloc(1, 21265b390aaSDag-Erling Smørgrav sizeof(struct subnet_env)); 21365b390aaSDag-Erling Smørgrav if(!sn_env) { 21465b390aaSDag-Erling Smørgrav log_err("malloc failure"); 21565b390aaSDag-Erling Smørgrav return 0; 21665b390aaSDag-Erling Smørgrav } 21765b390aaSDag-Erling Smørgrav alloc_init(&sn_env->alloc, NULL, 0); 21865b390aaSDag-Erling Smørgrav env->modinfo[id] = (void*)sn_env; 2191838dec3SCy Schubert 2201838dec3SCy Schubert /* Warn that serve-expired and prefetch do not work with the subnet 2211838dec3SCy Schubert * module cache. */ 2221838dec3SCy Schubert if(env->cfg->serve_expired) 2231838dec3SCy Schubert log_warn( 2241838dec3SCy Schubert "subnetcache: serve-expired is set but not working " 2251838dec3SCy Schubert "for data originating from the subnet module cache."); 2261838dec3SCy Schubert if(env->cfg->prefetch) 2271838dec3SCy Schubert log_warn( 2281838dec3SCy Schubert "subnetcache: prefetch is set but not working " 2291838dec3SCy Schubert "for data originating from the subnet module cache."); 23065b390aaSDag-Erling Smørgrav /* Copy msg_cache settings */ 23165b390aaSDag-Erling Smørgrav sn_env->subnet_msg_cache = slabhash_create(env->cfg->msg_cache_slabs, 23265b390aaSDag-Erling Smørgrav HASH_DEFAULT_STARTARRAY, env->cfg->msg_cache_size, 23365b390aaSDag-Erling Smørgrav msg_cache_sizefunc, query_info_compare, query_entry_delete, 23465b390aaSDag-Erling Smørgrav subnet_data_delete, NULL); 235e86b9096SDag-Erling Smørgrav slabhash_setmarkdel(sn_env->subnet_msg_cache, &subnet_markdel); 23665b390aaSDag-Erling Smørgrav if(!sn_env->subnet_msg_cache) { 2375469a995SCy Schubert log_err("subnetcache: could not create cache"); 23865b390aaSDag-Erling Smørgrav free(sn_env); 23965b390aaSDag-Erling Smørgrav env->modinfo[id] = NULL; 24065b390aaSDag-Erling Smørgrav return 0; 24165b390aaSDag-Erling Smørgrav } 24265b390aaSDag-Erling Smørgrav /* whitelist for edns subnet capable servers */ 243c7f4d7adSDag-Erling Smørgrav sn_env->whitelist = ecs_whitelist_create(); 244c7f4d7adSDag-Erling Smørgrav if(!sn_env->whitelist || 245c7f4d7adSDag-Erling Smørgrav !ecs_whitelist_apply_cfg(sn_env->whitelist, env->cfg)) { 2465469a995SCy Schubert log_err("subnetcache: could not create ECS whitelist"); 24765b390aaSDag-Erling Smørgrav slabhash_delete(sn_env->subnet_msg_cache); 24865b390aaSDag-Erling Smørgrav free(sn_env); 24965b390aaSDag-Erling Smørgrav env->modinfo[id] = NULL; 25065b390aaSDag-Erling Smørgrav return 0; 25165b390aaSDag-Erling Smørgrav } 25265b390aaSDag-Erling Smørgrav 2535469a995SCy Schubert verbose(VERB_QUERY, "subnetcache: option registered (%d)", 25465b390aaSDag-Erling Smørgrav env->cfg->client_subnet_opcode); 25565b390aaSDag-Erling Smørgrav /* Create new mesh state for all queries. */ 25665b390aaSDag-Erling Smørgrav env->unique_mesh = 1; 25765b390aaSDag-Erling Smørgrav if(!edns_register_option(env->cfg->client_subnet_opcode, 25865b390aaSDag-Erling Smørgrav env->cfg->client_subnet_always_forward /* bypass cache */, 259a39a5a69SCy Schubert 1 /* no aggregation */, env)) { 2605469a995SCy Schubert log_err("subnetcache: could not register opcode"); 261c7f4d7adSDag-Erling Smørgrav ecs_whitelist_delete(sn_env->whitelist); 26265b390aaSDag-Erling Smørgrav slabhash_delete(sn_env->subnet_msg_cache); 26365b390aaSDag-Erling Smørgrav free(sn_env); 26465b390aaSDag-Erling Smørgrav env->modinfo[id] = NULL; 26565b390aaSDag-Erling Smørgrav return 0; 26665b390aaSDag-Erling Smørgrav } 26765b390aaSDag-Erling Smørgrav inplace_cb_register((void*)ecs_whitelist_check, inplace_cb_query, NULL, 26865b390aaSDag-Erling Smørgrav env, id); 26965b390aaSDag-Erling Smørgrav inplace_cb_register((void*)ecs_edns_back_parsed, 27065b390aaSDag-Erling Smørgrav inplace_cb_edns_back_parsed, NULL, env, id); 27165b390aaSDag-Erling Smørgrav inplace_cb_register((void*)ecs_query_response, 27265b390aaSDag-Erling Smørgrav inplace_cb_query_response, NULL, env, id); 27365b390aaSDag-Erling Smørgrav lock_rw_init(&sn_env->biglock); 27465b390aaSDag-Erling Smørgrav return 1; 27565b390aaSDag-Erling Smørgrav } 27665b390aaSDag-Erling Smørgrav 27765b390aaSDag-Erling Smørgrav void 27865b390aaSDag-Erling Smørgrav subnetmod_deinit(struct module_env *env, int id) 27965b390aaSDag-Erling Smørgrav { 28065b390aaSDag-Erling Smørgrav struct subnet_env *sn_env; 28165b390aaSDag-Erling Smørgrav if(!env || !env->modinfo[id]) 28265b390aaSDag-Erling Smørgrav return; 28365b390aaSDag-Erling Smørgrav sn_env = (struct subnet_env*)env->modinfo[id]; 28465b390aaSDag-Erling Smørgrav lock_rw_destroy(&sn_env->biglock); 28565b390aaSDag-Erling Smørgrav inplace_cb_delete(env, inplace_cb_edns_back_parsed, id); 28665b390aaSDag-Erling Smørgrav inplace_cb_delete(env, inplace_cb_query, id); 287c7f4d7adSDag-Erling Smørgrav inplace_cb_delete(env, inplace_cb_query_response, id); 288c7f4d7adSDag-Erling Smørgrav ecs_whitelist_delete(sn_env->whitelist); 28965b390aaSDag-Erling Smørgrav slabhash_delete(sn_env->subnet_msg_cache); 29065b390aaSDag-Erling Smørgrav alloc_clear(&sn_env->alloc); 29165b390aaSDag-Erling Smørgrav free(sn_env); 29265b390aaSDag-Erling Smørgrav env->modinfo[id] = NULL; 29365b390aaSDag-Erling Smørgrav } 29465b390aaSDag-Erling Smørgrav 29565b390aaSDag-Erling Smørgrav /** Tells client that upstream has no/improper support */ 29665b390aaSDag-Erling Smørgrav static void 29765b390aaSDag-Erling Smørgrav cp_edns_bad_response(struct ecs_data *target, struct ecs_data *source) 29865b390aaSDag-Erling Smørgrav { 29965b390aaSDag-Erling Smørgrav target->subnet_scope_mask = 0; 30065b390aaSDag-Erling Smørgrav target->subnet_source_mask = source->subnet_source_mask; 30165b390aaSDag-Erling Smørgrav target->subnet_addr_fam = source->subnet_addr_fam; 30265b390aaSDag-Erling Smørgrav memcpy(target->subnet_addr, source->subnet_addr, INET6_SIZE); 30365b390aaSDag-Erling Smørgrav target->subnet_validdata = 1; 30465b390aaSDag-Erling Smørgrav } 30565b390aaSDag-Erling Smørgrav 30665b390aaSDag-Erling Smørgrav static void 30765b390aaSDag-Erling Smørgrav delfunc(void *envptr, void *elemptr) { 30865b390aaSDag-Erling Smørgrav struct reply_info *elem = (struct reply_info *)elemptr; 30965b390aaSDag-Erling Smørgrav struct subnet_env *env = (struct subnet_env *)envptr; 31065b390aaSDag-Erling Smørgrav reply_info_parsedelete(elem, &env->alloc); 31165b390aaSDag-Erling Smørgrav } 31265b390aaSDag-Erling Smørgrav 31365b390aaSDag-Erling Smørgrav static size_t 31465b390aaSDag-Erling Smørgrav sizefunc(void *elemptr) { 31565b390aaSDag-Erling Smørgrav struct reply_info *elem = (struct reply_info *)elemptr; 316335c7cdaSCy Schubert size_t s = sizeof (struct reply_info) - sizeof (struct rrset_ref) 31765b390aaSDag-Erling Smørgrav + elem->rrset_count * sizeof (struct rrset_ref) 31865b390aaSDag-Erling Smørgrav + elem->rrset_count * sizeof (struct ub_packed_rrset_key *); 319335c7cdaSCy Schubert size_t i; 320335c7cdaSCy Schubert for (i = 0; i < elem->rrset_count; i++) { 321335c7cdaSCy Schubert struct ub_packed_rrset_key *key = elem->rrsets[i]; 322335c7cdaSCy Schubert struct packed_rrset_data *data = key->entry.data; 323335c7cdaSCy Schubert s += ub_rrset_sizefunc(key, data); 324335c7cdaSCy Schubert } 325335c7cdaSCy Schubert if(elem->reason_bogus_str) 326335c7cdaSCy Schubert s += strlen(elem->reason_bogus_str)+1; 327335c7cdaSCy Schubert return s; 32865b390aaSDag-Erling Smørgrav } 32965b390aaSDag-Erling Smørgrav 33065b390aaSDag-Erling Smørgrav /** 33165b390aaSDag-Erling Smørgrav * Select tree from cache entry based on edns data. 33265b390aaSDag-Erling Smørgrav * If for address family not present it will create a new one. 33365b390aaSDag-Erling Smørgrav * NULL on failure to create. */ 33465b390aaSDag-Erling Smørgrav static struct addrtree* 33565b390aaSDag-Erling Smørgrav get_tree(struct subnet_msg_cache_data *data, struct ecs_data *edns, 33665b390aaSDag-Erling Smørgrav struct subnet_env *env, struct config_file* cfg) 33765b390aaSDag-Erling Smørgrav { 33865b390aaSDag-Erling Smørgrav struct addrtree *tree; 33965b390aaSDag-Erling Smørgrav if (edns->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) { 34065b390aaSDag-Erling Smørgrav if (!data->tree4) 34165b390aaSDag-Erling Smørgrav data->tree4 = addrtree_create( 34265b390aaSDag-Erling Smørgrav cfg->max_client_subnet_ipv4, &delfunc, 343e86b9096SDag-Erling Smørgrav &sizefunc, env, cfg->max_ecs_tree_size_ipv4); 34465b390aaSDag-Erling Smørgrav tree = data->tree4; 34565b390aaSDag-Erling Smørgrav } else { 34665b390aaSDag-Erling Smørgrav if (!data->tree6) 34765b390aaSDag-Erling Smørgrav data->tree6 = addrtree_create( 34865b390aaSDag-Erling Smørgrav cfg->max_client_subnet_ipv6, &delfunc, 349e86b9096SDag-Erling Smørgrav &sizefunc, env, cfg->max_ecs_tree_size_ipv6); 35065b390aaSDag-Erling Smørgrav tree = data->tree6; 35165b390aaSDag-Erling Smørgrav } 35265b390aaSDag-Erling Smørgrav return tree; 35365b390aaSDag-Erling Smørgrav } 35465b390aaSDag-Erling Smørgrav 35565b390aaSDag-Erling Smørgrav static void 35665b390aaSDag-Erling Smørgrav update_cache(struct module_qstate *qstate, int id) 35765b390aaSDag-Erling Smørgrav { 35865b390aaSDag-Erling Smørgrav struct msgreply_entry *mrep_entry; 35965b390aaSDag-Erling Smørgrav struct addrtree *tree; 36065b390aaSDag-Erling Smørgrav struct reply_info *rep; 36165b390aaSDag-Erling Smørgrav struct query_info qinf; 36265b390aaSDag-Erling Smørgrav struct subnet_env *sne = qstate->env->modinfo[id]; 36365b390aaSDag-Erling Smørgrav struct subnet_qstate *sq = (struct subnet_qstate*)qstate->minfo[id]; 36465b390aaSDag-Erling Smørgrav struct slabhash *subnet_msg_cache = sne->subnet_msg_cache; 36565b390aaSDag-Erling Smørgrav struct ecs_data *edns = &sq->ecs_client_in; 36665b390aaSDag-Erling Smørgrav size_t i; 367335c7cdaSCy Schubert int only_match_scope_zero, diff_size; 36865b390aaSDag-Erling Smørgrav 3690a92a9fcSCy Schubert /* We already calculated hash upon lookup (lookup_and_reply) if we were 3700a92a9fcSCy Schubert * allowed to look in the ECS cache */ 3710a92a9fcSCy Schubert hashvalue_type h = qstate->minfo[id] && 3720a92a9fcSCy Schubert ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash_calculated? 37365b390aaSDag-Erling Smørgrav ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash : 37465b390aaSDag-Erling Smørgrav query_info_hash(&qstate->qinfo, qstate->query_flags); 37565b390aaSDag-Erling Smørgrav /* Step 1, general qinfo lookup */ 37665b390aaSDag-Erling Smørgrav struct lruhash_entry* lru_entry = slabhash_lookup(subnet_msg_cache, h, 37765b390aaSDag-Erling Smørgrav &qstate->qinfo, 1); 378e86b9096SDag-Erling Smørgrav int need_to_insert = (lru_entry == NULL); 37965b390aaSDag-Erling Smørgrav if (!lru_entry) { 380e86b9096SDag-Erling Smørgrav void* data = calloc(1, 381e86b9096SDag-Erling Smørgrav sizeof(struct subnet_msg_cache_data)); 382e86b9096SDag-Erling Smørgrav if(!data) { 383e86b9096SDag-Erling Smørgrav log_err("malloc failed"); 384e86b9096SDag-Erling Smørgrav return; 385e86b9096SDag-Erling Smørgrav } 38665b390aaSDag-Erling Smørgrav qinf = qstate->qinfo; 38765b390aaSDag-Erling Smørgrav qinf.qname = memdup(qstate->qinfo.qname, 38865b390aaSDag-Erling Smørgrav qstate->qinfo.qname_len); 38965b390aaSDag-Erling Smørgrav if(!qinf.qname) { 390e86b9096SDag-Erling Smørgrav free(data); 39165b390aaSDag-Erling Smørgrav log_err("memdup failed"); 39265b390aaSDag-Erling Smørgrav return; 39365b390aaSDag-Erling Smørgrav } 394e86b9096SDag-Erling Smørgrav mrep_entry = query_info_entrysetup(&qinf, data, h); 39565b390aaSDag-Erling Smørgrav free(qinf.qname); /* if qname 'consumed', it is set to NULL */ 39665b390aaSDag-Erling Smørgrav if (!mrep_entry) { 397e86b9096SDag-Erling Smørgrav free(data); 39865b390aaSDag-Erling Smørgrav log_err("query_info_entrysetup failed"); 39965b390aaSDag-Erling Smørgrav return; 40065b390aaSDag-Erling Smørgrav } 40165b390aaSDag-Erling Smørgrav lru_entry = &mrep_entry->entry; 40257bddd21SDag-Erling Smørgrav lock_rw_wrlock(&lru_entry->lock); 40365b390aaSDag-Erling Smørgrav } 404e86b9096SDag-Erling Smørgrav /* lru_entry->lock is locked regardless of how we got here, 405e86b9096SDag-Erling Smørgrav * either from the slabhash_lookup, or above in the new allocated */ 40665b390aaSDag-Erling Smørgrav /* Step 2, find the correct tree */ 40765b390aaSDag-Erling Smørgrav if (!(tree = get_tree(lru_entry->data, edns, sne, qstate->env->cfg))) { 408e86b9096SDag-Erling Smørgrav lock_rw_unlock(&lru_entry->lock); 4095469a995SCy Schubert log_err("subnetcache: cache insertion failed"); 41065b390aaSDag-Erling Smørgrav return; 41165b390aaSDag-Erling Smørgrav } 41257bddd21SDag-Erling Smørgrav lock_quick_lock(&sne->alloc.lock); 41365b390aaSDag-Erling Smørgrav rep = reply_info_copy(qstate->return_msg->rep, &sne->alloc, NULL); 41457bddd21SDag-Erling Smørgrav lock_quick_unlock(&sne->alloc.lock); 41565b390aaSDag-Erling Smørgrav if (!rep) { 416e86b9096SDag-Erling Smørgrav lock_rw_unlock(&lru_entry->lock); 4175469a995SCy Schubert log_err("subnetcache: cache insertion failed"); 41865b390aaSDag-Erling Smørgrav return; 41965b390aaSDag-Erling Smørgrav } 42065b390aaSDag-Erling Smørgrav 42165b390aaSDag-Erling Smørgrav /* store RRsets */ 42265b390aaSDag-Erling Smørgrav for(i=0; i<rep->rrset_count; i++) { 42365b390aaSDag-Erling Smørgrav rep->ref[i].key = rep->rrsets[i]; 42465b390aaSDag-Erling Smørgrav rep->ref[i].id = rep->rrsets[i]->id; 42565b390aaSDag-Erling Smørgrav } 42665b390aaSDag-Erling Smørgrav reply_info_set_ttls(rep, *qstate->env->now); 427335c7cdaSCy Schubert reply_info_sortref(rep); 42865b390aaSDag-Erling Smørgrav rep->flags |= (BIT_RA | BIT_QR); /* fix flags to be sensible for */ 42965b390aaSDag-Erling Smørgrav rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache */ 430865f46b2SCy Schubert if(edns->subnet_source_mask == 0 && edns->subnet_scope_mask == 0) 431865f46b2SCy Schubert only_match_scope_zero = 1; 432865f46b2SCy Schubert else only_match_scope_zero = 0; 433335c7cdaSCy Schubert diff_size = (int)tree->size_bytes; 43465b390aaSDag-Erling Smørgrav addrtree_insert(tree, (addrkey_t*)edns->subnet_addr, 43525039b37SCy Schubert edns->subnet_source_mask, sq->max_scope, rep, 436865f46b2SCy Schubert rep->ttl, *qstate->env->now, only_match_scope_zero); 437335c7cdaSCy Schubert diff_size = (int)tree->size_bytes - diff_size; 438e86b9096SDag-Erling Smørgrav 43965b390aaSDag-Erling Smørgrav lock_rw_unlock(&lru_entry->lock); 440e86b9096SDag-Erling Smørgrav if (need_to_insert) { 44165b390aaSDag-Erling Smørgrav slabhash_insert(subnet_msg_cache, h, lru_entry, lru_entry->data, 44265b390aaSDag-Erling Smørgrav NULL); 443335c7cdaSCy Schubert } else { 444335c7cdaSCy Schubert slabhash_update_space_used(subnet_msg_cache, h, NULL, 445335c7cdaSCy Schubert diff_size); 44665b390aaSDag-Erling Smørgrav } 44765b390aaSDag-Erling Smørgrav } 44865b390aaSDag-Erling Smørgrav 44965b390aaSDag-Erling Smørgrav /** Lookup in cache and reply true iff reply is sent. */ 45065b390aaSDag-Erling Smørgrav static int 4518f76bb7dSCy Schubert lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq, int prefetch) 45265b390aaSDag-Erling Smørgrav { 45365b390aaSDag-Erling Smørgrav struct lruhash_entry *e; 45465b390aaSDag-Erling Smørgrav struct module_env *env = qstate->env; 45565b390aaSDag-Erling Smørgrav struct subnet_env *sne = (struct subnet_env*)env->modinfo[id]; 45665b390aaSDag-Erling Smørgrav hashvalue_type h = query_info_hash(&qstate->qinfo, qstate->query_flags); 45765b390aaSDag-Erling Smørgrav struct subnet_msg_cache_data *data; 45865b390aaSDag-Erling Smørgrav struct ecs_data *ecs = &sq->ecs_client_in; 45965b390aaSDag-Erling Smørgrav struct addrtree *tree; 46065b390aaSDag-Erling Smørgrav struct addrnode *node; 46165b390aaSDag-Erling Smørgrav uint8_t scope; 46265b390aaSDag-Erling Smørgrav 46365b390aaSDag-Erling Smørgrav memset(&sq->ecs_client_out, 0, sizeof(sq->ecs_client_out)); 46465b390aaSDag-Erling Smørgrav 4650a92a9fcSCy Schubert if (sq) { 4660a92a9fcSCy Schubert sq->qinfo_hash = h; /* Might be useful on cache miss */ 4670a92a9fcSCy Schubert sq->qinfo_hash_calculated = 1; 4680a92a9fcSCy Schubert } 46965b390aaSDag-Erling Smørgrav e = slabhash_lookup(sne->subnet_msg_cache, h, &qstate->qinfo, 1); 47065b390aaSDag-Erling Smørgrav if (!e) return 0; /* qinfo not in cache */ 47165b390aaSDag-Erling Smørgrav data = e->data; 47265b390aaSDag-Erling Smørgrav tree = (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4)? 47365b390aaSDag-Erling Smørgrav data->tree4 : data->tree6; 47465b390aaSDag-Erling Smørgrav if (!tree) { /* qinfo in cache but not for this family */ 47565b390aaSDag-Erling Smørgrav lock_rw_unlock(&e->lock); 47665b390aaSDag-Erling Smørgrav return 0; 47765b390aaSDag-Erling Smørgrav } 47865b390aaSDag-Erling Smørgrav node = addrtree_find(tree, (addrkey_t*)ecs->subnet_addr, 47965b390aaSDag-Erling Smørgrav ecs->subnet_source_mask, *env->now); 48065b390aaSDag-Erling Smørgrav if (!node) { /* plain old cache miss */ 48165b390aaSDag-Erling Smørgrav lock_rw_unlock(&e->lock); 48265b390aaSDag-Erling Smørgrav return 0; 48365b390aaSDag-Erling Smørgrav } 48465b390aaSDag-Erling Smørgrav 48565b390aaSDag-Erling Smørgrav qstate->return_msg = tomsg(NULL, &qstate->qinfo, 486091e9e46SCy Schubert (struct reply_info *)node->elem, qstate->region, *env->now, 0, 48765b390aaSDag-Erling Smørgrav env->scratch); 48865b390aaSDag-Erling Smørgrav scope = (uint8_t)node->scope; 48965b390aaSDag-Erling Smørgrav lock_rw_unlock(&e->lock); 49065b390aaSDag-Erling Smørgrav 49165b390aaSDag-Erling Smørgrav if (!qstate->return_msg) { /* Failed allocation or expired TTL */ 49265b390aaSDag-Erling Smørgrav return 0; 49365b390aaSDag-Erling Smørgrav } 49465b390aaSDag-Erling Smørgrav 49565b390aaSDag-Erling Smørgrav if (sq->subnet_downstream) { /* relay to interested client */ 49665b390aaSDag-Erling Smørgrav sq->ecs_client_out.subnet_scope_mask = scope; 49765b390aaSDag-Erling Smørgrav sq->ecs_client_out.subnet_addr_fam = ecs->subnet_addr_fam; 49865b390aaSDag-Erling Smørgrav sq->ecs_client_out.subnet_source_mask = ecs->subnet_source_mask; 49965b390aaSDag-Erling Smørgrav memcpy(&sq->ecs_client_out.subnet_addr, &ecs->subnet_addr, 50065b390aaSDag-Erling Smørgrav INET6_SIZE); 50165b390aaSDag-Erling Smørgrav sq->ecs_client_out.subnet_validdata = 1; 50265b390aaSDag-Erling Smørgrav } 5038f76bb7dSCy Schubert 5048f76bb7dSCy Schubert if (prefetch && *qstate->env->now >= ((struct reply_info *)node->elem)->prefetch_ttl) { 5058f76bb7dSCy Schubert qstate->need_refetch = 1; 5068f76bb7dSCy Schubert } 50765b390aaSDag-Erling Smørgrav return 1; 50865b390aaSDag-Erling Smørgrav } 50965b390aaSDag-Erling Smørgrav 51065b390aaSDag-Erling Smørgrav /** 51165b390aaSDag-Erling Smørgrav * Test first bits of addresses for equality. Caller is responsible 51265b390aaSDag-Erling Smørgrav * for making sure that both a and b are at least net/8 octets long. 51365b390aaSDag-Erling Smørgrav * @param a: first address. 51465b390aaSDag-Erling Smørgrav * @param a: seconds address. 51565b390aaSDag-Erling Smørgrav * @param net: Number of bits to test. 51665b390aaSDag-Erling Smørgrav * @return: 1 if equal, 0 otherwise. 51765b390aaSDag-Erling Smørgrav */ 51865b390aaSDag-Erling Smørgrav static int 51965b390aaSDag-Erling Smørgrav common_prefix(uint8_t *a, uint8_t *b, uint8_t net) 52065b390aaSDag-Erling Smørgrav { 52165b390aaSDag-Erling Smørgrav size_t n = (size_t)net / 8; 52265b390aaSDag-Erling Smørgrav return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]); 52365b390aaSDag-Erling Smørgrav } 52465b390aaSDag-Erling Smørgrav 52565b390aaSDag-Erling Smørgrav static enum module_ext_state 52665b390aaSDag-Erling Smørgrav eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) 52765b390aaSDag-Erling Smørgrav { 52865b390aaSDag-Erling Smørgrav struct subnet_env *sne = qstate->env->modinfo[id]; 52965b390aaSDag-Erling Smørgrav 53065b390aaSDag-Erling Smørgrav struct ecs_data *c_in = &sq->ecs_client_in; /* rcvd from client */ 53165b390aaSDag-Erling Smørgrav struct ecs_data *c_out = &sq->ecs_client_out;/* will send to client */ 53265b390aaSDag-Erling Smørgrav struct ecs_data *s_in = &sq->ecs_server_in; /* rcvd from auth */ 53365b390aaSDag-Erling Smørgrav struct ecs_data *s_out = &sq->ecs_server_out;/* sent to auth */ 53465b390aaSDag-Erling Smørgrav 53565b390aaSDag-Erling Smørgrav memset(c_out, 0, sizeof(*c_out)); 53665b390aaSDag-Erling Smørgrav 5374c75e3aaSDag-Erling Smørgrav if (!qstate->return_msg) { 5384c75e3aaSDag-Erling Smørgrav /* already an answer and its not a message, but retain 5394c75e3aaSDag-Erling Smørgrav * the actual rcode, instead of module_error, so send 5404c75e3aaSDag-Erling Smørgrav * module_finished */ 5414c75e3aaSDag-Erling Smørgrav return module_finished; 5424c75e3aaSDag-Erling Smørgrav } 54365b390aaSDag-Erling Smørgrav 54465b390aaSDag-Erling Smørgrav /* We have not asked for subnet data */ 545103ba509SCy Schubert if (!sq->subnet_sent && !sq->subnet_sent_no_subnet) { 54665b390aaSDag-Erling Smørgrav if (s_in->subnet_validdata) 5475469a995SCy Schubert verbose(VERB_QUERY, "subnetcache: received spurious data"); 54865b390aaSDag-Erling Smørgrav if (sq->subnet_downstream) /* Copy back to client */ 54965b390aaSDag-Erling Smørgrav cp_edns_bad_response(c_out, c_in); 55065b390aaSDag-Erling Smørgrav return module_finished; 55165b390aaSDag-Erling Smørgrav } 55265b390aaSDag-Erling Smørgrav 55365b390aaSDag-Erling Smørgrav /* subnet sent but nothing came back */ 554103ba509SCy Schubert if (!s_in->subnet_validdata && !sq->subnet_sent_no_subnet) { 55565b390aaSDag-Erling Smørgrav /* The authority indicated no support for edns subnet. As a 55665b390aaSDag-Erling Smørgrav * consequence the answer ended up in the regular cache. It 55724e36522SCy Schubert * is still useful to put it in the edns subnet cache for 55865b390aaSDag-Erling Smørgrav * when a client explicitly asks for subnet specific answer. */ 5595469a995SCy Schubert verbose(VERB_QUERY, "subnetcache: Authority indicates no support"); 560e86b9096SDag-Erling Smørgrav if(!sq->started_no_cache_store) { 56165b390aaSDag-Erling Smørgrav lock_rw_wrlock(&sne->biglock); 56265b390aaSDag-Erling Smørgrav update_cache(qstate, id); 56365b390aaSDag-Erling Smørgrav lock_rw_unlock(&sne->biglock); 564e86b9096SDag-Erling Smørgrav } 56565b390aaSDag-Erling Smørgrav if (sq->subnet_downstream) 56665b390aaSDag-Erling Smørgrav cp_edns_bad_response(c_out, c_in); 56765b390aaSDag-Erling Smørgrav return module_finished; 56865b390aaSDag-Erling Smørgrav } 56965b390aaSDag-Erling Smørgrav 570103ba509SCy Schubert /* Purposefully there was no sent subnet, and there is consequently 571103ba509SCy Schubert * no subnet in the answer. If there was, use the subnet in the answer 572103ba509SCy Schubert * anyway. But if there is not, treat it as a prefix 0 answer. */ 573103ba509SCy Schubert if(sq->subnet_sent_no_subnet && !s_in->subnet_validdata) { 574103ba509SCy Schubert /* Fill in 0.0.0.0/0 scope 0, or ::0/0 scope 0, for caching. */ 575103ba509SCy Schubert s_in->subnet_addr_fam = s_out->subnet_addr_fam; 576103ba509SCy Schubert s_in->subnet_source_mask = 0; 577103ba509SCy Schubert s_in->subnet_scope_mask = 0; 578103ba509SCy Schubert memset(s_in->subnet_addr, 0, INET6_SIZE); 579103ba509SCy Schubert s_in->subnet_validdata = 1; 580103ba509SCy Schubert } 581103ba509SCy Schubert 58265b390aaSDag-Erling Smørgrav /* Being here means we have asked for and got a subnet specific 58365b390aaSDag-Erling Smørgrav * answer. Also, the answer from the authority is not yet cached 58465b390aaSDag-Erling Smørgrav * anywhere. */ 58565b390aaSDag-Erling Smørgrav 58665b390aaSDag-Erling Smørgrav /* can we accept response? */ 58765b390aaSDag-Erling Smørgrav if(s_out->subnet_addr_fam != s_in->subnet_addr_fam || 58865b390aaSDag-Erling Smørgrav s_out->subnet_source_mask != s_in->subnet_source_mask || 58965b390aaSDag-Erling Smørgrav !common_prefix(s_out->subnet_addr, s_in->subnet_addr, 59065b390aaSDag-Erling Smørgrav s_out->subnet_source_mask)) 59165b390aaSDag-Erling Smørgrav { 59265b390aaSDag-Erling Smørgrav /* we can not accept, restart query without option */ 5935469a995SCy Schubert verbose(VERB_QUERY, "subnetcache: forged data"); 59465b390aaSDag-Erling Smørgrav s_out->subnet_validdata = 0; 59565b390aaSDag-Erling Smørgrav (void)edns_opt_list_remove(&qstate->edns_opts_back_out, 59665b390aaSDag-Erling Smørgrav qstate->env->cfg->client_subnet_opcode); 59765b390aaSDag-Erling Smørgrav sq->subnet_sent = 0; 598103ba509SCy Schubert sq->subnet_sent_no_subnet = 0; 59965b390aaSDag-Erling Smørgrav return module_restart_next; 60065b390aaSDag-Erling Smørgrav } 60165b390aaSDag-Erling Smørgrav 60265b390aaSDag-Erling Smørgrav lock_rw_wrlock(&sne->biglock); 603e86b9096SDag-Erling Smørgrav if(!sq->started_no_cache_store) { 60465b390aaSDag-Erling Smørgrav update_cache(qstate, id); 605e86b9096SDag-Erling Smørgrav } 6064c75e3aaSDag-Erling Smørgrav sne->num_msg_nocache++; 60765b390aaSDag-Erling Smørgrav lock_rw_unlock(&sne->biglock); 60865b390aaSDag-Erling Smørgrav 609335c7cdaSCy Schubert /* If there is an expired answer in the global cache, remove that, 610335c7cdaSCy Schubert * because expired answers would otherwise resurface once the ecs data 611335c7cdaSCy Schubert * expires, giving once in a while global data responses for ecs 612335c7cdaSCy Schubert * domains, with serve expired enabled. */ 613335c7cdaSCy Schubert if(qstate->env->cfg->serve_expired) { 614335c7cdaSCy Schubert msg_cache_remove(qstate->env, qstate->qinfo.qname, 615335c7cdaSCy Schubert qstate->qinfo.qname_len, qstate->qinfo.qtype, 616335c7cdaSCy Schubert qstate->qinfo.qclass, 0); 617335c7cdaSCy Schubert #ifdef USE_CACHEDB 618335c7cdaSCy Schubert if(qstate->env->cachedb_enabled) 619335c7cdaSCy Schubert cachedb_msg_remove(qstate); 620335c7cdaSCy Schubert #endif 621335c7cdaSCy Schubert } 622335c7cdaSCy Schubert 62365b390aaSDag-Erling Smørgrav if (sq->subnet_downstream) { 62465b390aaSDag-Erling Smørgrav /* Client wants to see the answer, echo option back 62565b390aaSDag-Erling Smørgrav * and adjust the scope. */ 62665b390aaSDag-Erling Smørgrav c_out->subnet_addr_fam = c_in->subnet_addr_fam; 62765b390aaSDag-Erling Smørgrav c_out->subnet_source_mask = c_in->subnet_source_mask; 62865b390aaSDag-Erling Smørgrav memcpy(&c_out->subnet_addr, &c_in->subnet_addr, INET6_SIZE); 62925039b37SCy Schubert c_out->subnet_scope_mask = sq->max_scope; 630e86b9096SDag-Erling Smørgrav /* Limit scope returned to client to scope used for caching. */ 631e86b9096SDag-Erling Smørgrav if(c_out->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) { 632e86b9096SDag-Erling Smørgrav if(c_out->subnet_scope_mask > 633e86b9096SDag-Erling Smørgrav qstate->env->cfg->max_client_subnet_ipv4) { 634e86b9096SDag-Erling Smørgrav c_out->subnet_scope_mask = 635e86b9096SDag-Erling Smørgrav qstate->env->cfg->max_client_subnet_ipv4; 636e86b9096SDag-Erling Smørgrav } 637e86b9096SDag-Erling Smørgrav } 638e86b9096SDag-Erling Smørgrav else if(c_out->subnet_scope_mask > 639e86b9096SDag-Erling Smørgrav qstate->env->cfg->max_client_subnet_ipv6) { 640e86b9096SDag-Erling Smørgrav c_out->subnet_scope_mask = 641e86b9096SDag-Erling Smørgrav qstate->env->cfg->max_client_subnet_ipv6; 642e86b9096SDag-Erling Smørgrav } 64365b390aaSDag-Erling Smørgrav c_out->subnet_validdata = 1; 64465b390aaSDag-Erling Smørgrav } 64565b390aaSDag-Erling Smørgrav return module_finished; 64665b390aaSDag-Erling Smørgrav } 64765b390aaSDag-Erling Smørgrav 64865b390aaSDag-Erling Smørgrav /** Parse EDNS opt data containing ECS */ 64965b390aaSDag-Erling Smørgrav static int 65065b390aaSDag-Erling Smørgrav parse_subnet_option(struct edns_option* ecs_option, struct ecs_data* ecs) 65165b390aaSDag-Erling Smørgrav { 65265b390aaSDag-Erling Smørgrav memset(ecs, 0, sizeof(*ecs)); 65365b390aaSDag-Erling Smørgrav if (ecs_option->opt_len < 4) 65465b390aaSDag-Erling Smørgrav return 0; 65565b390aaSDag-Erling Smørgrav 65665b390aaSDag-Erling Smørgrav ecs->subnet_addr_fam = sldns_read_uint16(ecs_option->opt_data); 65765b390aaSDag-Erling Smørgrav ecs->subnet_source_mask = ecs_option->opt_data[2]; 65865b390aaSDag-Erling Smørgrav ecs->subnet_scope_mask = ecs_option->opt_data[3]; 6598a384985SDag-Erling Smørgrav /* remaining bytes indicate address */ 66065b390aaSDag-Erling Smørgrav 66165b390aaSDag-Erling Smørgrav /* validate input*/ 66265b390aaSDag-Erling Smørgrav /* option length matches calculated length? */ 66365b390aaSDag-Erling Smørgrav if (ecs_option->opt_len != (size_t)((ecs->subnet_source_mask+7)/8 + 4)) 66465b390aaSDag-Erling Smørgrav return 0; 66565b390aaSDag-Erling Smørgrav if (ecs_option->opt_len - 4 > INET6_SIZE || ecs_option->opt_len == 0) 66665b390aaSDag-Erling Smørgrav return 0; 66765b390aaSDag-Erling Smørgrav if (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) { 66865b390aaSDag-Erling Smørgrav if (ecs->subnet_source_mask > 32 || ecs->subnet_scope_mask > 32) 66965b390aaSDag-Erling Smørgrav return 0; 67065b390aaSDag-Erling Smørgrav } else if (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6) { 67165b390aaSDag-Erling Smørgrav if (ecs->subnet_source_mask > 128 || 67265b390aaSDag-Erling Smørgrav ecs->subnet_scope_mask > 128) 67365b390aaSDag-Erling Smørgrav return 0; 67465b390aaSDag-Erling Smørgrav } else 67565b390aaSDag-Erling Smørgrav return 0; 67665b390aaSDag-Erling Smørgrav 67765b390aaSDag-Erling Smørgrav /* valid ECS data, write to ecs_data */ 67865b390aaSDag-Erling Smørgrav if (copy_clear(ecs->subnet_addr, INET6_SIZE, ecs_option->opt_data + 4, 67965b390aaSDag-Erling Smørgrav ecs_option->opt_len - 4, ecs->subnet_source_mask)) 68065b390aaSDag-Erling Smørgrav return 0; 68165b390aaSDag-Erling Smørgrav ecs->subnet_validdata = 1; 68265b390aaSDag-Erling Smørgrav return 1; 68365b390aaSDag-Erling Smørgrav } 68465b390aaSDag-Erling Smørgrav 685a39a5a69SCy Schubert void 68665b390aaSDag-Erling Smørgrav subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs, 68765b390aaSDag-Erling Smørgrav struct config_file* cfg) 68865b390aaSDag-Erling Smørgrav { 68965b390aaSDag-Erling Smørgrav void* sinaddr; 69065b390aaSDag-Erling Smørgrav 69165b390aaSDag-Erling Smørgrav /* Construct subnet option from original query */ 69265b390aaSDag-Erling Smørgrav if(((struct sockaddr_in*)ss)->sin_family == AF_INET) { 69365b390aaSDag-Erling Smørgrav ecs->subnet_source_mask = cfg->max_client_subnet_ipv4; 69465b390aaSDag-Erling Smørgrav ecs->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP4; 69565b390aaSDag-Erling Smørgrav sinaddr = &((struct sockaddr_in*)ss)->sin_addr; 69665b390aaSDag-Erling Smørgrav if (!copy_clear( ecs->subnet_addr, INET6_SIZE, 69765b390aaSDag-Erling Smørgrav (uint8_t *)sinaddr, INET_SIZE, 69865b390aaSDag-Erling Smørgrav ecs->subnet_source_mask)) { 69965b390aaSDag-Erling Smørgrav ecs->subnet_validdata = 1; 70065b390aaSDag-Erling Smørgrav } 70165b390aaSDag-Erling Smørgrav } 70265b390aaSDag-Erling Smørgrav #ifdef INET6 70365b390aaSDag-Erling Smørgrav else { 70465b390aaSDag-Erling Smørgrav ecs->subnet_source_mask = cfg->max_client_subnet_ipv6; 70565b390aaSDag-Erling Smørgrav ecs->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP6; 70665b390aaSDag-Erling Smørgrav sinaddr = &((struct sockaddr_in6*)ss)->sin6_addr; 70765b390aaSDag-Erling Smørgrav if (!copy_clear( ecs->subnet_addr, INET6_SIZE, 70865b390aaSDag-Erling Smørgrav (uint8_t *)sinaddr, INET6_SIZE, 70965b390aaSDag-Erling Smørgrav ecs->subnet_source_mask)) { 71065b390aaSDag-Erling Smørgrav ecs->subnet_validdata = 1; 71165b390aaSDag-Erling Smørgrav } 71265b390aaSDag-Erling Smørgrav } 71365b390aaSDag-Erling Smørgrav #else 71465b390aaSDag-Erling Smørgrav /* We don't know how to handle ip6, just pass */ 71565b390aaSDag-Erling Smørgrav #endif /* INET6 */ 71665b390aaSDag-Erling Smørgrav } 71765b390aaSDag-Erling Smørgrav 71865b390aaSDag-Erling Smørgrav int 71965b390aaSDag-Erling Smørgrav ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, 72065b390aaSDag-Erling Smørgrav int id, void* ATTR_UNUSED(cbargs)) 72165b390aaSDag-Erling Smørgrav { 72265b390aaSDag-Erling Smørgrav struct subnet_qstate *sq; 72365b390aaSDag-Erling Smørgrav 72465b390aaSDag-Erling Smørgrav if(!response || !(sq=(struct subnet_qstate*)qstate->minfo[id])) 72565b390aaSDag-Erling Smørgrav return 1; 72665b390aaSDag-Erling Smørgrav 72765b390aaSDag-Erling Smørgrav if(sq->subnet_sent && 72865b390aaSDag-Erling Smørgrav FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_REFUSED) { 7298a384985SDag-Erling Smørgrav /* REFUSED response to ECS query, remove ECS option. */ 73065b390aaSDag-Erling Smørgrav edns_opt_list_remove(&qstate->edns_opts_back_out, 73165b390aaSDag-Erling Smørgrav qstate->env->cfg->client_subnet_opcode); 73265b390aaSDag-Erling Smørgrav sq->subnet_sent = 0; 733103ba509SCy Schubert sq->subnet_sent_no_subnet = 0; 73465b390aaSDag-Erling Smørgrav memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out)); 73525039b37SCy Schubert } else if (!sq->track_max_scope && 73625039b37SCy Schubert FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR && 73725039b37SCy Schubert response->rep->an_numrrsets > 0 73825039b37SCy Schubert ) { 73925039b37SCy Schubert struct ub_packed_rrset_key* s = response->rep->rrsets[0]; 74025039b37SCy Schubert if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && 74125039b37SCy Schubert query_dname_compare(qstate->qinfo.qname, 74225039b37SCy Schubert s->rk.dname) == 0) { 74325039b37SCy Schubert /* CNAME response for QNAME. From now on keep track of 74425039b37SCy Schubert * longest received ECS prefix for all queries on this 74525039b37SCy Schubert * qstate. */ 74625039b37SCy Schubert sq->track_max_scope = 1; 74725039b37SCy Schubert } 74865b390aaSDag-Erling Smørgrav } 74965b390aaSDag-Erling Smørgrav return 1; 75065b390aaSDag-Erling Smørgrav } 75165b390aaSDag-Erling Smørgrav 752865f46b2SCy Schubert /** verbose print edns subnet option in pretty print */ 753865f46b2SCy Schubert static void 754865f46b2SCy Schubert subnet_log_print(const char* s, struct edns_option* ecs_opt) 755865f46b2SCy Schubert { 756865f46b2SCy Schubert if(verbosity >= VERB_ALGO) { 757865f46b2SCy Schubert char buf[256]; 758865f46b2SCy Schubert char* str = buf; 759865f46b2SCy Schubert size_t str_len = sizeof(buf); 760865f46b2SCy Schubert if(!ecs_opt) { 761865f46b2SCy Schubert verbose(VERB_ALGO, "%s (null)", s); 762865f46b2SCy Schubert return; 763865f46b2SCy Schubert } 764865f46b2SCy Schubert (void)sldns_wire2str_edns_subnet_print(&str, &str_len, 765865f46b2SCy Schubert ecs_opt->opt_data, ecs_opt->opt_len); 766865f46b2SCy Schubert verbose(VERB_ALGO, "%s %s", s, buf); 767865f46b2SCy Schubert } 768865f46b2SCy Schubert } 769865f46b2SCy Schubert 77065b390aaSDag-Erling Smørgrav int 77165b390aaSDag-Erling Smørgrav ecs_edns_back_parsed(struct module_qstate* qstate, int id, 77265b390aaSDag-Erling Smørgrav void* ATTR_UNUSED(cbargs)) 77365b390aaSDag-Erling Smørgrav { 77465b390aaSDag-Erling Smørgrav struct subnet_qstate *sq; 77565b390aaSDag-Erling Smørgrav struct edns_option* ecs_opt; 77665b390aaSDag-Erling Smørgrav 77765b390aaSDag-Erling Smørgrav if(!(sq=(struct subnet_qstate*)qstate->minfo[id])) 77865b390aaSDag-Erling Smørgrav return 1; 77965b390aaSDag-Erling Smørgrav if((ecs_opt = edns_opt_list_find( 78065b390aaSDag-Erling Smørgrav qstate->edns_opts_back_in, 78125039b37SCy Schubert qstate->env->cfg->client_subnet_opcode)) && 78225039b37SCy Schubert parse_subnet_option(ecs_opt, &sq->ecs_server_in) && 78325039b37SCy Schubert sq->subnet_sent && sq->ecs_server_in.subnet_validdata) { 784865f46b2SCy Schubert subnet_log_print("answer has edns subnet", ecs_opt); 78565b390aaSDag-Erling Smørgrav /* Only skip global cache store if we sent an ECS option 78665b390aaSDag-Erling Smørgrav * and received one back. Answers from non-whitelisted 7878a384985SDag-Erling Smørgrav * servers will end up in global cache. Answers for 78865b390aaSDag-Erling Smørgrav * queries with 0 source will not (unless nameserver 78965b390aaSDag-Erling Smørgrav * does not support ECS). */ 79065b390aaSDag-Erling Smørgrav qstate->no_cache_store = 1; 79125039b37SCy Schubert if(!sq->track_max_scope || (sq->track_max_scope && 79225039b37SCy Schubert sq->ecs_server_in.subnet_scope_mask > 79325039b37SCy Schubert sq->max_scope)) 79425039b37SCy Schubert sq->max_scope = sq->ecs_server_in.subnet_scope_mask; 795103ba509SCy Schubert } else if(sq->subnet_sent_no_subnet) { 796103ba509SCy Schubert /* The answer can be stored as scope 0, not in global cache. */ 797103ba509SCy Schubert qstate->no_cache_store = 1; 79865b390aaSDag-Erling Smørgrav } 79965b390aaSDag-Erling Smørgrav 80065b390aaSDag-Erling Smørgrav return 1; 80165b390aaSDag-Erling Smørgrav } 80265b390aaSDag-Erling Smørgrav 80365b390aaSDag-Erling Smørgrav void 80465b390aaSDag-Erling Smørgrav subnetmod_operate(struct module_qstate *qstate, enum module_ev event, 80565b390aaSDag-Erling Smørgrav int id, struct outbound_entry* outbound) 80665b390aaSDag-Erling Smørgrav { 80765b390aaSDag-Erling Smørgrav struct subnet_env *sne = qstate->env->modinfo[id]; 80865b390aaSDag-Erling Smørgrav struct subnet_qstate *sq = (struct subnet_qstate*)qstate->minfo[id]; 80965b390aaSDag-Erling Smørgrav 8105469a995SCy Schubert verbose(VERB_QUERY, "subnetcache[module %d] operate: extstate:%s " 81165b390aaSDag-Erling Smørgrav "event:%s", id, strextstate(qstate->ext_state[id]), 81265b390aaSDag-Erling Smørgrav strmodulevent(event)); 8135469a995SCy Schubert log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo); 81465b390aaSDag-Erling Smørgrav 81565b390aaSDag-Erling Smørgrav if((event == module_event_new || event == module_event_pass) && 81665b390aaSDag-Erling Smørgrav sq == NULL) { 81765b390aaSDag-Erling Smørgrav struct edns_option* ecs_opt; 81865b390aaSDag-Erling Smørgrav if(!subnet_new_qstate(qstate, id)) { 81965b390aaSDag-Erling Smørgrav qstate->return_msg = NULL; 82065b390aaSDag-Erling Smørgrav qstate->ext_state[id] = module_finished; 82165b390aaSDag-Erling Smørgrav return; 82265b390aaSDag-Erling Smørgrav } 82365b390aaSDag-Erling Smørgrav 82465b390aaSDag-Erling Smørgrav sq = (struct subnet_qstate*)qstate->minfo[id]; 82565b390aaSDag-Erling Smørgrav 82665b390aaSDag-Erling Smørgrav if((ecs_opt = edns_opt_list_find( 82765b390aaSDag-Erling Smørgrav qstate->edns_opts_front_in, 82865b390aaSDag-Erling Smørgrav qstate->env->cfg->client_subnet_opcode))) { 82965b390aaSDag-Erling Smørgrav if(!parse_subnet_option(ecs_opt, &sq->ecs_client_in)) { 83065b390aaSDag-Erling Smørgrav /* Wrongly formatted ECS option. RFC mandates to 83165b390aaSDag-Erling Smørgrav * return FORMERROR. */ 83265b390aaSDag-Erling Smørgrav qstate->return_rcode = LDNS_RCODE_FORMERR; 83365b390aaSDag-Erling Smørgrav qstate->ext_state[id] = module_finished; 83465b390aaSDag-Erling Smørgrav return; 83565b390aaSDag-Erling Smørgrav } 836865f46b2SCy Schubert subnet_log_print("query has edns subnet", ecs_opt); 83765b390aaSDag-Erling Smørgrav sq->subnet_downstream = 1; 83865b390aaSDag-Erling Smørgrav } 83965b390aaSDag-Erling Smørgrav else if(qstate->mesh_info->reply_list) { 84065b390aaSDag-Erling Smørgrav subnet_option_from_ss( 841865f46b2SCy Schubert &qstate->mesh_info->reply_list->query_reply.client_addr, 84265b390aaSDag-Erling Smørgrav &sq->ecs_client_in, qstate->env->cfg); 84365b390aaSDag-Erling Smørgrav } 8448f76bb7dSCy Schubert else if(qstate->client_addr.ss_family != AF_UNSPEC) { 8458f76bb7dSCy Schubert subnet_option_from_ss( 8468f76bb7dSCy Schubert &qstate->client_addr, 8478f76bb7dSCy Schubert &sq->ecs_client_in, qstate->env->cfg); 8488f76bb7dSCy Schubert } 84965b390aaSDag-Erling Smørgrav 85065b390aaSDag-Erling Smørgrav if(sq->ecs_client_in.subnet_validdata == 0) { 85165b390aaSDag-Erling Smørgrav /* No clients are interested in result or we could not 85265b390aaSDag-Erling Smørgrav * parse it, we don't do client subnet */ 85365b390aaSDag-Erling Smørgrav sq->ecs_server_out.subnet_validdata = 0; 8545469a995SCy Schubert verbose(VERB_ALGO, "subnetcache: pass to next module"); 85565b390aaSDag-Erling Smørgrav qstate->ext_state[id] = module_wait_module; 85665b390aaSDag-Erling Smørgrav return; 85765b390aaSDag-Erling Smørgrav } 85865b390aaSDag-Erling Smørgrav 859e86b9096SDag-Erling Smørgrav /* Limit to minimum allowed source mask */ 860e86b9096SDag-Erling Smørgrav if(sq->ecs_client_in.subnet_source_mask != 0 && ( 861e86b9096SDag-Erling Smørgrav (sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 && 862e86b9096SDag-Erling Smørgrav sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv4) || 863e86b9096SDag-Erling Smørgrav (sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6 && 864e86b9096SDag-Erling Smørgrav sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv6))) { 865e86b9096SDag-Erling Smørgrav qstate->return_rcode = LDNS_RCODE_REFUSED; 866e86b9096SDag-Erling Smørgrav qstate->ext_state[id] = module_finished; 867e86b9096SDag-Erling Smørgrav return; 868e86b9096SDag-Erling Smørgrav } 869e86b9096SDag-Erling Smørgrav 8700a92a9fcSCy Schubert if(!sq->started_no_cache_lookup && !qstate->blacklist) { 87165b390aaSDag-Erling Smørgrav lock_rw_wrlock(&sne->biglock); 8728f76bb7dSCy Schubert if(qstate->mesh_info->reply_list && 8738f76bb7dSCy Schubert lookup_and_reply(qstate, id, sq, 8748f76bb7dSCy Schubert qstate->env->cfg->prefetch)) { 8754c75e3aaSDag-Erling Smørgrav sne->num_msg_cache++; 87665b390aaSDag-Erling Smørgrav lock_rw_unlock(&sne->biglock); 8775469a995SCy Schubert verbose(VERB_QUERY, "subnetcache: answered from cache"); 87865b390aaSDag-Erling Smørgrav qstate->ext_state[id] = module_finished; 87965b390aaSDag-Erling Smørgrav 880a39a5a69SCy Schubert subnet_ecs_opt_list_append(&sq->ecs_client_out, 8810a92a9fcSCy Schubert &qstate->edns_opts_front_out, qstate, 8820a92a9fcSCy Schubert qstate->region); 883865f46b2SCy Schubert if(verbosity >= VERB_ALGO) { 884865f46b2SCy Schubert subnet_log_print("reply has edns subnet", 885865f46b2SCy Schubert edns_opt_list_find( 886865f46b2SCy Schubert qstate->edns_opts_front_out, 887865f46b2SCy Schubert qstate->env->cfg-> 888865f46b2SCy Schubert client_subnet_opcode)); 889865f46b2SCy Schubert } 89065b390aaSDag-Erling Smørgrav return; 89165b390aaSDag-Erling Smørgrav } 89265b390aaSDag-Erling Smørgrav lock_rw_unlock(&sne->biglock); 8930a92a9fcSCy Schubert } 89465b390aaSDag-Erling Smørgrav 89565b390aaSDag-Erling Smørgrav sq->ecs_server_out.subnet_addr_fam = 89665b390aaSDag-Erling Smørgrav sq->ecs_client_in.subnet_addr_fam; 89765b390aaSDag-Erling Smørgrav sq->ecs_server_out.subnet_source_mask = 89865b390aaSDag-Erling Smørgrav sq->ecs_client_in.subnet_source_mask; 89965b390aaSDag-Erling Smørgrav /* Limit source prefix to configured maximum */ 90065b390aaSDag-Erling Smørgrav if(sq->ecs_server_out.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 90165b390aaSDag-Erling Smørgrav && sq->ecs_server_out.subnet_source_mask > 90265b390aaSDag-Erling Smørgrav qstate->env->cfg->max_client_subnet_ipv4) 90365b390aaSDag-Erling Smørgrav sq->ecs_server_out.subnet_source_mask = 90465b390aaSDag-Erling Smørgrav qstate->env->cfg->max_client_subnet_ipv4; 90565b390aaSDag-Erling Smørgrav else if(sq->ecs_server_out.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6 90665b390aaSDag-Erling Smørgrav && sq->ecs_server_out.subnet_source_mask > 90765b390aaSDag-Erling Smørgrav qstate->env->cfg->max_client_subnet_ipv6) 90865b390aaSDag-Erling Smørgrav sq->ecs_server_out.subnet_source_mask = 90965b390aaSDag-Erling Smørgrav qstate->env->cfg->max_client_subnet_ipv6; 91065b390aaSDag-Erling Smørgrav /* Safe to copy completely, even if the source is limited by the 911a39a5a69SCy Schubert * configuration. subnet_ecs_opt_list_append() will limit the address. 91265b390aaSDag-Erling Smørgrav * */ 91365b390aaSDag-Erling Smørgrav memcpy(&sq->ecs_server_out.subnet_addr, 91465b390aaSDag-Erling Smørgrav sq->ecs_client_in.subnet_addr, INET6_SIZE); 91565b390aaSDag-Erling Smørgrav sq->ecs_server_out.subnet_scope_mask = 0; 91665b390aaSDag-Erling Smørgrav sq->ecs_server_out.subnet_validdata = 1; 91765b390aaSDag-Erling Smørgrav if(sq->ecs_server_out.subnet_source_mask != 0 && 9188a384985SDag-Erling Smørgrav qstate->env->cfg->client_subnet_always_forward && 91965b390aaSDag-Erling Smørgrav sq->subnet_downstream) 92065b390aaSDag-Erling Smørgrav /* ECS specific data required, do not look at the global 92165b390aaSDag-Erling Smørgrav * cache in other modules. */ 92265b390aaSDag-Erling Smørgrav qstate->no_cache_lookup = 1; 92365b390aaSDag-Erling Smørgrav 92465b390aaSDag-Erling Smørgrav /* pass request to next module */ 92565b390aaSDag-Erling Smørgrav verbose(VERB_ALGO, 9265469a995SCy Schubert "subnetcache: not found in cache. pass to next module"); 92765b390aaSDag-Erling Smørgrav qstate->ext_state[id] = module_wait_module; 92865b390aaSDag-Erling Smørgrav return; 92965b390aaSDag-Erling Smørgrav } 93065b390aaSDag-Erling Smørgrav /* Query handed back by next module, we have a 'final' answer */ 93165b390aaSDag-Erling Smørgrav if(sq && event == module_event_moddone) { 93265b390aaSDag-Erling Smørgrav qstate->ext_state[id] = eval_response(qstate, id, sq); 9334c75e3aaSDag-Erling Smørgrav if(qstate->ext_state[id] == module_finished && 9344c75e3aaSDag-Erling Smørgrav qstate->return_msg) { 935a39a5a69SCy Schubert subnet_ecs_opt_list_append(&sq->ecs_client_out, 9360a92a9fcSCy Schubert &qstate->edns_opts_front_out, qstate, 9370a92a9fcSCy Schubert qstate->region); 938865f46b2SCy Schubert if(verbosity >= VERB_ALGO) { 939865f46b2SCy Schubert subnet_log_print("reply has edns subnet", 940865f46b2SCy Schubert edns_opt_list_find( 941865f46b2SCy Schubert qstate->edns_opts_front_out, 942865f46b2SCy Schubert qstate->env->cfg-> 943865f46b2SCy Schubert client_subnet_opcode)); 944865f46b2SCy Schubert } 94565b390aaSDag-Erling Smørgrav } 946e86b9096SDag-Erling Smørgrav qstate->no_cache_store = sq->started_no_cache_store; 9470a92a9fcSCy Schubert qstate->no_cache_lookup = sq->started_no_cache_lookup; 94865b390aaSDag-Erling Smørgrav return; 94965b390aaSDag-Erling Smørgrav } 95065b390aaSDag-Erling Smørgrav if(sq && outbound) { 95165b390aaSDag-Erling Smørgrav return; 95265b390aaSDag-Erling Smørgrav } 95365b390aaSDag-Erling Smørgrav /* We are being revisited */ 95465b390aaSDag-Erling Smørgrav if(event == module_event_pass || event == module_event_new) { 95565b390aaSDag-Erling Smørgrav /* Just pass it on, we already did the work */ 9565469a995SCy Schubert verbose(VERB_ALGO, "subnetcache: pass to next module"); 95765b390aaSDag-Erling Smørgrav qstate->ext_state[id] = module_wait_module; 95865b390aaSDag-Erling Smørgrav return; 95965b390aaSDag-Erling Smørgrav } 96065b390aaSDag-Erling Smørgrav if(!sq && (event == module_event_moddone)) { 96165b390aaSDag-Erling Smørgrav /* during priming, module done but we never started */ 96265b390aaSDag-Erling Smørgrav qstate->ext_state[id] = module_finished; 96365b390aaSDag-Erling Smørgrav return; 96465b390aaSDag-Erling Smørgrav } 9655469a995SCy Schubert log_err("subnetcache: bad event %s", strmodulevent(event)); 96665b390aaSDag-Erling Smørgrav qstate->ext_state[id] = module_error; 96765b390aaSDag-Erling Smørgrav return; 96865b390aaSDag-Erling Smørgrav } 96965b390aaSDag-Erling Smørgrav 97065b390aaSDag-Erling Smørgrav void 97165b390aaSDag-Erling Smørgrav subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate), 97265b390aaSDag-Erling Smørgrav int ATTR_UNUSED(id)) 97365b390aaSDag-Erling Smørgrav { 97465b390aaSDag-Erling Smørgrav /* qstate has no data outside region */ 97565b390aaSDag-Erling Smørgrav } 97665b390aaSDag-Erling Smørgrav 97765b390aaSDag-Erling Smørgrav void 97865b390aaSDag-Erling Smørgrav subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate), 97965b390aaSDag-Erling Smørgrav int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super)) 98065b390aaSDag-Erling Smørgrav { 98165b390aaSDag-Erling Smørgrav /* Not used */ 98265b390aaSDag-Erling Smørgrav } 98365b390aaSDag-Erling Smørgrav 98465b390aaSDag-Erling Smørgrav size_t 98565b390aaSDag-Erling Smørgrav subnetmod_get_mem(struct module_env *env, int id) 98665b390aaSDag-Erling Smørgrav { 98765b390aaSDag-Erling Smørgrav struct subnet_env *sn_env = env->modinfo[id]; 98865b390aaSDag-Erling Smørgrav if (!sn_env) return 0; 98965b390aaSDag-Erling Smørgrav return sizeof(*sn_env) + 99065b390aaSDag-Erling Smørgrav slabhash_get_mem(sn_env->subnet_msg_cache) + 991c7f4d7adSDag-Erling Smørgrav ecs_whitelist_get_mem(sn_env->whitelist); 99265b390aaSDag-Erling Smørgrav } 99365b390aaSDag-Erling Smørgrav 99465b390aaSDag-Erling Smørgrav /** 99565b390aaSDag-Erling Smørgrav * The module function block 99665b390aaSDag-Erling Smørgrav */ 99765b390aaSDag-Erling Smørgrav static struct module_func_block subnetmod_block = { 998*56850988SCy Schubert "subnetcache", 999*56850988SCy Schubert NULL, NULL, &subnetmod_init, &subnetmod_deinit, &subnetmod_operate, 100065b390aaSDag-Erling Smørgrav &subnetmod_inform_super, &subnetmod_clear, &subnetmod_get_mem 100165b390aaSDag-Erling Smørgrav }; 100265b390aaSDag-Erling Smørgrav 100365b390aaSDag-Erling Smørgrav struct module_func_block* 100465b390aaSDag-Erling Smørgrav subnetmod_get_funcblock(void) 100565b390aaSDag-Erling Smørgrav { 100665b390aaSDag-Erling Smørgrav return &subnetmod_block; 100765b390aaSDag-Erling Smørgrav } 100865b390aaSDag-Erling Smørgrav 100965b390aaSDag-Erling Smørgrav /** Wrappers for static functions to unit test */ 101065b390aaSDag-Erling Smørgrav size_t 101165b390aaSDag-Erling Smørgrav unittest_wrapper_subnetmod_sizefunc(void *elemptr) 101265b390aaSDag-Erling Smørgrav { 101365b390aaSDag-Erling Smørgrav return sizefunc(elemptr); 101465b390aaSDag-Erling Smørgrav } 101565b390aaSDag-Erling Smørgrav 101665b390aaSDag-Erling Smørgrav #endif /* CLIENT_SUBNET */ 1017