1 /* 2 * edns-subnet/subnetmod.c - edns subnet module. Must be called before validator 3 * and iterator. 4 * 5 * Copyright (c) 2013, NLnet Labs. All rights reserved. 6 * 7 * This software is open source. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * Redistributions of source code must retain the above copyright notice, 14 * this list of conditions and the following disclaimer. 15 * 16 * Redistributions in binary form must reproduce the above copyright notice, 17 * this list of conditions and the following disclaimer in the documentation 18 * and/or other materials provided with the distribution. 19 * 20 * Neither the name of the NLNET LABS nor the names of its contributors may 21 * be used to endorse or promote products derived from this software without 22 * specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 30 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 /** 37 * \file 38 * subnet module for unbound. 39 */ 40 41 #include "config.h" 42 43 #ifdef CLIENT_SUBNET /* keeps splint happy */ 44 45 #include "edns-subnet/subnetmod.h" 46 #include "edns-subnet/edns-subnet.h" 47 #include "edns-subnet/addrtree.h" 48 #include "edns-subnet/subnet-whitelist.h" 49 50 #include "services/mesh.h" 51 #include "services/cache/dns.h" 52 #include "util/module.h" 53 #include "util/regional.h" 54 #include "util/fptr_wlist.h" 55 #include "util/storage/slabhash.h" 56 #include "util/config_file.h" 57 #include "util/data/msgreply.h" 58 #include "sldns/sbuffer.h" 59 #include "sldns/wire2str.h" 60 #include "iterator/iter_utils.h" 61 #ifdef USE_CACHEDB 62 #include "cachedb/cachedb.h" 63 #endif 64 65 /** externally called */ 66 void 67 subnet_data_delete(void *d, void *ATTR_UNUSED(arg)) 68 { 69 struct subnet_msg_cache_data *r; 70 r = (struct subnet_msg_cache_data*)d; 71 addrtree_delete(r->tree4); 72 addrtree_delete(r->tree6); 73 free(r); 74 } 75 76 /** externally called */ 77 size_t 78 msg_cache_sizefunc(void *k, void *d) 79 { 80 struct msgreply_entry *q = (struct msgreply_entry*)k; 81 struct subnet_msg_cache_data *r = (struct subnet_msg_cache_data*)d; 82 size_t s = sizeof(struct msgreply_entry) 83 + sizeof(struct subnet_msg_cache_data) 84 + q->key.qname_len + lock_get_mem(&q->entry.lock); 85 s += addrtree_size(r->tree4); 86 s += addrtree_size(r->tree6); 87 return s; 88 } 89 90 /** new query for ecs module */ 91 static int 92 subnet_new_qstate(struct module_qstate *qstate, int id) 93 { 94 struct subnet_qstate *sq = (struct subnet_qstate*)regional_alloc( 95 qstate->region, sizeof(struct subnet_qstate)); 96 if(!sq) 97 return 0; 98 qstate->minfo[id] = sq; 99 memset(sq, 0, sizeof(*sq)); 100 sq->started_no_cache_store = qstate->no_cache_store; 101 sq->started_no_cache_lookup = qstate->no_cache_lookup; 102 return 1; 103 } 104 105 /** Add ecs struct to edns list, after parsing it to wire format. */ 106 void 107 subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list, 108 struct module_qstate *qstate, struct regional *region) 109 { 110 size_t sn_octs, sn_octs_remainder; 111 sldns_buffer* buf = qstate->env->scratch_buffer; 112 113 if(ecs->subnet_validdata) { 114 log_assert(ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 || 115 ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6); 116 log_assert(ecs->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP4 || 117 ecs->subnet_source_mask <= INET_SIZE*8); 118 log_assert(ecs->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP6 || 119 ecs->subnet_source_mask <= INET6_SIZE*8); 120 121 sn_octs = ecs->subnet_source_mask / 8; 122 sn_octs_remainder = 123 (size_t)((ecs->subnet_source_mask % 8)>0?1:0); 124 125 log_assert(sn_octs + sn_octs_remainder <= INET6_SIZE); 126 127 sldns_buffer_clear(buf); 128 sldns_buffer_write_u16(buf, ecs->subnet_addr_fam); 129 sldns_buffer_write_u8(buf, ecs->subnet_source_mask); 130 sldns_buffer_write_u8(buf, ecs->subnet_scope_mask); 131 sldns_buffer_write(buf, ecs->subnet_addr, sn_octs); 132 if(sn_octs_remainder) 133 sldns_buffer_write_u8(buf, ecs->subnet_addr[sn_octs] & 134 ~(0xFF >> (ecs->subnet_source_mask % 8))); 135 sldns_buffer_flip(buf); 136 137 edns_opt_list_append(list, 138 qstate->env->cfg->client_subnet_opcode, 139 sn_octs + sn_octs_remainder + 4, 140 sldns_buffer_begin(buf), region); 141 } 142 } 143 144 int ecs_whitelist_check(struct query_info* qinfo, 145 uint16_t ATTR_UNUSED(flags), struct module_qstate* qstate, 146 struct sockaddr_storage* addr, socklen_t addrlen, 147 uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), 148 struct regional *region, int id, void* ATTR_UNUSED(cbargs)) 149 { 150 struct subnet_qstate *sq; 151 struct subnet_env *sn_env; 152 153 if(!(sq=(struct subnet_qstate*)qstate->minfo[id])) 154 return 1; 155 sn_env = (struct subnet_env*)qstate->env->modinfo[id]; 156 157 if(sq->is_subquery_nonsubnet) { 158 if(sq->is_subquery_scopezero) { 159 /* Check if the result can be stored in the global cache, 160 * this is okay if the address and name are not configured 161 * as subnet address and subnet zone. */ 162 if(!ecs_is_whitelisted(sn_env->whitelist, 163 addr, addrlen, qinfo->qname, qinfo->qname_len, 164 qinfo->qclass)) { 165 verbose(VERB_ALGO, "subnet store subquery global, name and addr have no subnet treatment."); 166 qstate->no_cache_store = 0; 167 } 168 } 169 return 1; 170 } 171 172 /* Cache by default, might be disabled after parsing EDNS option 173 * received from nameserver. */ 174 if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0) 175 && sq->ecs_client_in.subnet_validdata) { 176 qstate->no_cache_store = 0; 177 } 178 179 sq->subnet_sent_no_subnet = 0; 180 if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream && 181 qstate->env->cfg->client_subnet_always_forward) || 182 ecs_is_whitelisted(sn_env->whitelist, 183 addr, addrlen, qinfo->qname, qinfo->qname_len, 184 qinfo->qclass))) { 185 /* Address on whitelist or client query contains ECS option, we 186 * want to sent out ECS. Only add option if it is not already 187 * set. */ 188 if(!edns_opt_list_find(qstate->edns_opts_back_out, 189 qstate->env->cfg->client_subnet_opcode)) { 190 /* if the client is not wanting an EDNS subnet option, 191 * omit it and store that we omitted it but actually 192 * are doing EDNS subnet to the server. */ 193 if(sq->ecs_server_out.subnet_source_mask == 0) { 194 sq->subnet_sent_no_subnet = 1; 195 sq->subnet_sent = 0; 196 return 1; 197 } 198 subnet_ecs_opt_list_append(&sq->ecs_server_out, 199 &qstate->edns_opts_back_out, qstate, region); 200 } 201 sq->subnet_sent = 1; 202 } 203 else { 204 /* Outgoing ECS option is set, but we don't want to sent it to 205 * this address, remove option. */ 206 if(edns_opt_list_find(qstate->edns_opts_back_out, 207 qstate->env->cfg->client_subnet_opcode)) { 208 edns_opt_list_remove(&qstate->edns_opts_back_out, 209 qstate->env->cfg->client_subnet_opcode); 210 } 211 sq->subnet_sent = 0; 212 } 213 return 1; 214 } 215 216 217 void 218 subnet_markdel(void* key) 219 { 220 struct msgreply_entry *e = (struct msgreply_entry*)key; 221 e->key.qtype = 0; 222 e->key.qclass = 0; 223 } 224 225 int 226 subnetmod_init(struct module_env *env, int id) 227 { 228 struct subnet_env *sn_env = (struct subnet_env*)calloc(1, 229 sizeof(struct subnet_env)); 230 if(!sn_env) { 231 log_err("malloc failure"); 232 return 0; 233 } 234 alloc_init(&sn_env->alloc, NULL, 0); 235 env->modinfo[id] = (void*)sn_env; 236 237 /* Warn that serve-expired and prefetch do not work with the subnet 238 * module cache. */ 239 if(env->cfg->serve_expired) 240 log_warn( 241 "subnetcache: serve-expired is set but not working " 242 "for data originating from the subnet module cache."); 243 if(env->cfg->prefetch) 244 log_warn( 245 "subnetcache: prefetch is set but not working " 246 "for data originating from the subnet module cache."); 247 /* Copy msg_cache settings */ 248 sn_env->subnet_msg_cache = slabhash_create(env->cfg->msg_cache_slabs, 249 HASH_DEFAULT_STARTARRAY, env->cfg->msg_cache_size, 250 msg_cache_sizefunc, query_info_compare, query_entry_delete, 251 subnet_data_delete, NULL); 252 if(!sn_env->subnet_msg_cache) { 253 log_err("subnetcache: could not create cache"); 254 free(sn_env); 255 env->modinfo[id] = NULL; 256 return 0; 257 } 258 slabhash_setmarkdel(sn_env->subnet_msg_cache, &subnet_markdel); 259 /* whitelist for edns subnet capable servers */ 260 sn_env->whitelist = ecs_whitelist_create(); 261 if(!sn_env->whitelist || 262 !ecs_whitelist_apply_cfg(sn_env->whitelist, env->cfg)) { 263 log_err("subnetcache: could not create ECS whitelist"); 264 slabhash_delete(sn_env->subnet_msg_cache); 265 free(sn_env); 266 env->modinfo[id] = NULL; 267 return 0; 268 } 269 270 verbose(VERB_QUERY, "subnetcache: option registered (%d)", 271 env->cfg->client_subnet_opcode); 272 /* Create new mesh state for all queries. */ 273 env->unique_mesh = 1; 274 if(!edns_register_option(env->cfg->client_subnet_opcode, 275 env->cfg->client_subnet_always_forward /* bypass cache */, 276 1 /* no aggregation */, env)) { 277 log_err("subnetcache: could not register opcode"); 278 ecs_whitelist_delete(sn_env->whitelist); 279 slabhash_delete(sn_env->subnet_msg_cache); 280 free(sn_env); 281 env->modinfo[id] = NULL; 282 return 0; 283 } 284 inplace_cb_register((void*)ecs_whitelist_check, inplace_cb_query, NULL, 285 env, id); 286 inplace_cb_register((void*)ecs_edns_back_parsed, 287 inplace_cb_edns_back_parsed, NULL, env, id); 288 inplace_cb_register((void*)ecs_query_response, 289 inplace_cb_query_response, NULL, env, id); 290 lock_rw_init(&sn_env->biglock); 291 return 1; 292 } 293 294 void 295 subnetmod_deinit(struct module_env *env, int id) 296 { 297 struct subnet_env *sn_env; 298 if(!env || !env->modinfo[id]) 299 return; 300 sn_env = (struct subnet_env*)env->modinfo[id]; 301 lock_rw_destroy(&sn_env->biglock); 302 inplace_cb_delete(env, inplace_cb_edns_back_parsed, id); 303 inplace_cb_delete(env, inplace_cb_query, id); 304 inplace_cb_delete(env, inplace_cb_query_response, id); 305 ecs_whitelist_delete(sn_env->whitelist); 306 slabhash_delete(sn_env->subnet_msg_cache); 307 alloc_clear(&sn_env->alloc); 308 free(sn_env); 309 env->modinfo[id] = NULL; 310 } 311 312 /** Tells client that upstream has no/improper support */ 313 static void 314 cp_edns_bad_response(struct ecs_data *target, struct ecs_data *source) 315 { 316 target->subnet_scope_mask = 0; 317 target->subnet_source_mask = source->subnet_source_mask; 318 target->subnet_addr_fam = source->subnet_addr_fam; 319 memcpy(target->subnet_addr, source->subnet_addr, INET6_SIZE); 320 target->subnet_validdata = 1; 321 } 322 323 static void 324 delfunc(void *envptr, void *elemptr) { 325 struct reply_info *elem = (struct reply_info *)elemptr; 326 struct subnet_env *env = (struct subnet_env *)envptr; 327 reply_info_parsedelete(elem, &env->alloc); 328 } 329 330 static size_t 331 sizefunc(void *elemptr) { 332 struct reply_info *elem = (struct reply_info *)elemptr; 333 size_t s = sizeof (struct reply_info) - sizeof (struct rrset_ref) 334 + elem->rrset_count * sizeof (struct rrset_ref) 335 + elem->rrset_count * sizeof (struct ub_packed_rrset_key *); 336 size_t i; 337 for (i = 0; i < elem->rrset_count; i++) { 338 struct ub_packed_rrset_key *key = elem->rrsets[i]; 339 struct packed_rrset_data *data = key->entry.data; 340 s += ub_rrset_sizefunc(key, data); 341 } 342 if(elem->reason_bogus_str) 343 s += strlen(elem->reason_bogus_str)+1; 344 return s; 345 } 346 347 /** 348 * Select tree from cache entry based on edns data. 349 * If for address family not present it will create a new one. 350 * NULL on failure to create. */ 351 static struct addrtree* 352 get_tree(struct subnet_msg_cache_data *data, struct ecs_data *edns, 353 struct subnet_env *env, struct config_file* cfg) 354 { 355 struct addrtree *tree; 356 if (edns->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) { 357 if (!data->tree4) 358 data->tree4 = addrtree_create( 359 cfg->max_client_subnet_ipv4, &delfunc, 360 &sizefunc, env, cfg->max_ecs_tree_size_ipv4); 361 tree = data->tree4; 362 } else { 363 if (!data->tree6) 364 data->tree6 = addrtree_create( 365 cfg->max_client_subnet_ipv6, &delfunc, 366 &sizefunc, env, cfg->max_ecs_tree_size_ipv6); 367 tree = data->tree6; 368 } 369 return tree; 370 } 371 372 static void 373 update_cache(struct module_qstate *qstate, int id) 374 { 375 struct msgreply_entry *mrep_entry; 376 struct addrtree *tree; 377 struct reply_info *rep; 378 struct query_info qinf; 379 struct subnet_env *sne = qstate->env->modinfo[id]; 380 struct subnet_qstate *sq = (struct subnet_qstate*)qstate->minfo[id]; 381 struct slabhash *subnet_msg_cache = sne->subnet_msg_cache; 382 struct ecs_data *edns = &sq->ecs_client_in; 383 size_t i; 384 int only_match_scope_zero, diff_size; 385 386 /* We already calculated hash upon lookup (lookup_and_reply) if we were 387 * allowed to look in the ECS cache */ 388 hashvalue_type h = qstate->minfo[id] && 389 ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash_calculated? 390 ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash : 391 query_info_hash(&qstate->qinfo, qstate->query_flags); 392 /* Step 1, general qinfo lookup */ 393 struct lruhash_entry* lru_entry = slabhash_lookup(subnet_msg_cache, h, 394 &qstate->qinfo, 1); 395 int need_to_insert = (lru_entry == NULL); 396 if (!lru_entry) { 397 void* data = calloc(1, 398 sizeof(struct subnet_msg_cache_data)); 399 if(!data) { 400 log_err("malloc failed"); 401 return; 402 } 403 qinf = qstate->qinfo; 404 qinf.qname = memdup(qstate->qinfo.qname, 405 qstate->qinfo.qname_len); 406 if(!qinf.qname) { 407 free(data); 408 log_err("memdup failed"); 409 return; 410 } 411 mrep_entry = query_info_entrysetup(&qinf, data, h); 412 free(qinf.qname); /* if qname 'consumed', it is set to NULL */ 413 if (!mrep_entry) { 414 free(data); 415 log_err("query_info_entrysetup failed"); 416 return; 417 } 418 lru_entry = &mrep_entry->entry; 419 lock_rw_wrlock(&lru_entry->lock); 420 } 421 /* lru_entry->lock is locked regardless of how we got here, 422 * either from the slabhash_lookup, or above in the new allocated */ 423 /* Step 2, find the correct tree */ 424 if (!(tree = get_tree(lru_entry->data, edns, sne, qstate->env->cfg))) { 425 lock_rw_unlock(&lru_entry->lock); 426 log_err("subnetcache: cache insertion failed"); 427 return; 428 } 429 lock_quick_lock(&sne->alloc.lock); 430 rep = reply_info_copy(qstate->return_msg->rep, &sne->alloc, NULL); 431 lock_quick_unlock(&sne->alloc.lock); 432 if (!rep) { 433 lock_rw_unlock(&lru_entry->lock); 434 log_err("subnetcache: cache insertion failed"); 435 return; 436 } 437 438 /* store RRsets */ 439 for(i=0; i<rep->rrset_count; i++) { 440 rep->ref[i].key = rep->rrsets[i]; 441 rep->ref[i].id = rep->rrsets[i]->id; 442 } 443 reply_info_set_ttls(rep, *qstate->env->now); 444 reply_info_sortref(rep); 445 rep->flags |= (BIT_RA | BIT_QR); /* fix flags to be sensible for */ 446 rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache */ 447 if(edns->subnet_source_mask == 0 && edns->subnet_scope_mask == 0) 448 only_match_scope_zero = 1; 449 else only_match_scope_zero = 0; 450 diff_size = (int)tree->size_bytes; 451 addrtree_insert(tree, (addrkey_t*)edns->subnet_addr, 452 edns->subnet_source_mask, sq->max_scope, rep, 453 rep->ttl, *qstate->env->now, only_match_scope_zero); 454 diff_size = (int)tree->size_bytes - diff_size; 455 456 lock_rw_unlock(&lru_entry->lock); 457 if (need_to_insert) { 458 slabhash_insert(subnet_msg_cache, h, lru_entry, lru_entry->data, 459 NULL); 460 } else { 461 slabhash_update_space_used(subnet_msg_cache, h, NULL, 462 diff_size); 463 } 464 } 465 466 /** Lookup in cache and reply true iff reply is sent. */ 467 static int 468 lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq, int prefetch) 469 { 470 struct lruhash_entry *e; 471 struct module_env *env = qstate->env; 472 struct subnet_env *sne = (struct subnet_env*)env->modinfo[id]; 473 hashvalue_type h = query_info_hash(&qstate->qinfo, qstate->query_flags); 474 struct subnet_msg_cache_data *data; 475 struct ecs_data *ecs = &sq->ecs_client_in; 476 struct addrtree *tree; 477 struct addrnode *node; 478 uint8_t scope; 479 480 memset(&sq->ecs_client_out, 0, sizeof(sq->ecs_client_out)); 481 482 if (sq) { 483 sq->qinfo_hash = h; /* Might be useful on cache miss */ 484 sq->qinfo_hash_calculated = 1; 485 } 486 e = slabhash_lookup(sne->subnet_msg_cache, h, &qstate->qinfo, 1); 487 if (!e) return 0; /* qinfo not in cache */ 488 data = e->data; 489 tree = (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4)? 490 data->tree4 : data->tree6; 491 if (!tree) { /* qinfo in cache but not for this family */ 492 lock_rw_unlock(&e->lock); 493 return 0; 494 } 495 node = addrtree_find(tree, (addrkey_t*)ecs->subnet_addr, 496 ecs->subnet_source_mask, *env->now); 497 if (!node) { /* plain old cache miss */ 498 lock_rw_unlock(&e->lock); 499 return 0; 500 } 501 502 qstate->return_msg = tomsg(NULL, &qstate->qinfo, 503 (struct reply_info *)node->elem, qstate->region, *env->now, 0, 504 env->scratch); 505 scope = (uint8_t)node->scope; 506 lock_rw_unlock(&e->lock); 507 508 if (!qstate->return_msg) { /* Failed allocation or expired TTL */ 509 return 0; 510 } 511 512 if (sq->subnet_downstream) { /* relay to interested client */ 513 sq->ecs_client_out.subnet_scope_mask = scope; 514 sq->ecs_client_out.subnet_addr_fam = ecs->subnet_addr_fam; 515 sq->ecs_client_out.subnet_source_mask = ecs->subnet_source_mask; 516 memcpy(&sq->ecs_client_out.subnet_addr, &ecs->subnet_addr, 517 INET6_SIZE); 518 sq->ecs_client_out.subnet_validdata = 1; 519 } 520 521 if (prefetch && *qstate->env->now >= ((struct reply_info *)node->elem)->prefetch_ttl) { 522 qstate->need_refetch = 1; 523 } 524 return 1; 525 } 526 527 /** 528 * Test first bits of addresses for equality. Caller is responsible 529 * for making sure that both a and b are at least net/8 octets long. 530 * @param a: first address. 531 * @param a: seconds address. 532 * @param net: Number of bits to test. 533 * @return: 1 if equal, 0 otherwise. 534 */ 535 static int 536 common_prefix(uint8_t *a, uint8_t *b, uint8_t net) 537 { 538 size_t n = (size_t)net / 8; 539 return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]); 540 } 541 542 /** 543 * Create sub request that looks up the query. 544 * @param qstate: query state 545 * @param id: module id. 546 * @param sq: subnet qstate 547 * @return false on failure. 548 */ 549 static int 550 generate_sub_request(struct module_qstate *qstate, int id, struct subnet_qstate* sq) 551 { 552 struct module_qstate* subq = NULL; 553 uint16_t qflags = 0; /* OPCODE QUERY, no flags */ 554 int prime = 0; 555 int valrec = 0; 556 struct query_info qinf; 557 qinf.qname = qstate->qinfo.qname; 558 qinf.qname_len = qstate->qinfo.qname_len; 559 qinf.qtype = qstate->qinfo.qtype; 560 qinf.qclass = qstate->qinfo.qclass; 561 qinf.local_alias = NULL; 562 563 qflags |= BIT_RD; 564 if((qstate->query_flags & BIT_CD)!=0) { 565 qflags |= BIT_CD; 566 valrec = 1; 567 } 568 569 fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); 570 if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec, 571 &subq)) { 572 return 0; 573 } 574 if(subq) { 575 /* It is possible to access the subquery module state. */ 576 struct subnet_qstate* subsq; 577 if(!subnet_new_qstate(subq, id)) { 578 verbose(VERB_ALGO, "Could not allocate new subnet qstate"); 579 return 0; 580 } 581 subsq = (struct subnet_qstate*)subq->minfo[id]; 582 subsq->is_subquery_nonsubnet = 1; 583 584 /* When the client asks 0.0.0.0/0 and the name is not treated 585 * as subnet, it is to be stored in the global cache. 586 * Store that the client asked for that, if so. */ 587 if(sq->ecs_client_in.subnet_source_mask == 0 && 588 edns_opt_list_find(qstate->edns_opts_front_in, 589 qstate->env->cfg->client_subnet_opcode)) { 590 subq->no_cache_store = 1; 591 subsq->is_subquery_scopezero = 1; 592 } 593 } 594 return 1; 595 } 596 597 /** 598 * Perform the query without subnet 599 * @param qstate: query state 600 * @param id: module id. 601 * @param sq: subnet qstate 602 * @return module state 603 */ 604 static enum module_ext_state 605 generate_lookup_without_subnet(struct module_qstate *qstate, int id, 606 struct subnet_qstate* sq) 607 { 608 verbose(VERB_ALGO, "subnetcache: make subquery to look up without subnet"); 609 if(!generate_sub_request(qstate, id, sq)) { 610 verbose(VERB_ALGO, "Could not generate sub query"); 611 qstate->return_rcode = LDNS_RCODE_SERVFAIL; 612 qstate->return_msg = NULL; 613 return module_finished; 614 } 615 sq->wait_subquery = 1; 616 return module_wait_subquery; 617 } 618 619 static enum module_ext_state 620 eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) 621 { 622 struct subnet_env *sne = qstate->env->modinfo[id]; 623 624 struct ecs_data *c_in = &sq->ecs_client_in; /* rcvd from client */ 625 struct ecs_data *c_out = &sq->ecs_client_out;/* will send to client */ 626 struct ecs_data *s_in = &sq->ecs_server_in; /* rcvd from auth */ 627 struct ecs_data *s_out = &sq->ecs_server_out;/* sent to auth */ 628 629 memset(c_out, 0, sizeof(*c_out)); 630 631 if (!qstate->return_msg) { 632 /* already an answer and its not a message, but retain 633 * the actual rcode, instead of module_error, so send 634 * module_finished */ 635 return module_finished; 636 } 637 638 /* We have not asked for subnet data */ 639 if (!sq->subnet_sent && !sq->subnet_sent_no_subnet) { 640 if (s_in->subnet_validdata) 641 verbose(VERB_QUERY, "subnetcache: received spurious data"); 642 if (sq->subnet_downstream) /* Copy back to client */ 643 cp_edns_bad_response(c_out, c_in); 644 return module_finished; 645 } 646 647 /* subnet sent but nothing came back */ 648 if (!s_in->subnet_validdata && !sq->subnet_sent_no_subnet) { 649 /* The authority indicated no support for edns subnet. As a 650 * consequence the answer ended up in the regular cache. It 651 * is still useful to put it in the edns subnet cache for 652 * when a client explicitly asks for subnet specific answer. */ 653 verbose(VERB_QUERY, "subnetcache: Authority indicates no support"); 654 return generate_lookup_without_subnet(qstate, id, sq); 655 } 656 657 /* Purposefully there was no sent subnet, and there is consequently 658 * no subnet in the answer. If there was, use the subnet in the answer 659 * anyway. But if there is not, treat it as a prefix 0 answer. */ 660 if(sq->subnet_sent_no_subnet && !s_in->subnet_validdata) { 661 /* Fill in 0.0.0.0/0 scope 0, or ::0/0 scope 0, for caching. */ 662 s_in->subnet_addr_fam = s_out->subnet_addr_fam; 663 s_in->subnet_source_mask = 0; 664 s_in->subnet_scope_mask = 0; 665 memset(s_in->subnet_addr, 0, INET6_SIZE); 666 s_in->subnet_validdata = 1; 667 } 668 669 /* Being here means we have asked for and got a subnet specific 670 * answer. Also, the answer from the authority is not yet cached 671 * anywhere. */ 672 673 /* can we accept response? */ 674 if(s_out->subnet_addr_fam != s_in->subnet_addr_fam || 675 s_out->subnet_source_mask != s_in->subnet_source_mask || 676 !common_prefix(s_out->subnet_addr, s_in->subnet_addr, 677 s_out->subnet_source_mask)) 678 { 679 /* we can not accept, perform query without option */ 680 verbose(VERB_QUERY, "subnetcache: forged data"); 681 s_out->subnet_validdata = 0; 682 (void)edns_opt_list_remove(&qstate->edns_opts_back_out, 683 qstate->env->cfg->client_subnet_opcode); 684 sq->subnet_sent = 0; 685 sq->subnet_sent_no_subnet = 0; 686 return generate_lookup_without_subnet(qstate, id, sq); 687 } 688 689 lock_rw_wrlock(&sne->biglock); 690 if(!sq->started_no_cache_store) { 691 update_cache(qstate, id); 692 } 693 sne->num_msg_nocache++; 694 lock_rw_unlock(&sne->biglock); 695 696 /* If there is an expired answer in the global cache, remove that, 697 * because expired answers would otherwise resurface once the ecs data 698 * expires, giving once in a while global data responses for ecs 699 * domains, with serve expired enabled. */ 700 if(qstate->env->cfg->serve_expired) { 701 msg_cache_remove(qstate->env, qstate->qinfo.qname, 702 qstate->qinfo.qname_len, qstate->qinfo.qtype, 703 qstate->qinfo.qclass, 0); 704 #ifdef USE_CACHEDB 705 if(qstate->env->cachedb_enabled) 706 cachedb_msg_remove(qstate); 707 #endif 708 } 709 710 if (sq->subnet_downstream) { 711 /* Client wants to see the answer, echo option back 712 * and adjust the scope. */ 713 c_out->subnet_addr_fam = c_in->subnet_addr_fam; 714 c_out->subnet_source_mask = c_in->subnet_source_mask; 715 memcpy(&c_out->subnet_addr, &c_in->subnet_addr, INET6_SIZE); 716 c_out->subnet_scope_mask = sq->max_scope; 717 /* Limit scope returned to client to scope used for caching. */ 718 if(c_out->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) { 719 if(c_out->subnet_scope_mask > 720 qstate->env->cfg->max_client_subnet_ipv4) { 721 c_out->subnet_scope_mask = 722 qstate->env->cfg->max_client_subnet_ipv4; 723 } 724 } 725 else if(c_out->subnet_scope_mask > 726 qstate->env->cfg->max_client_subnet_ipv6) { 727 c_out->subnet_scope_mask = 728 qstate->env->cfg->max_client_subnet_ipv6; 729 } 730 c_out->subnet_validdata = 1; 731 } 732 return module_finished; 733 } 734 735 /** Parse EDNS opt data containing ECS */ 736 static int 737 parse_subnet_option(struct edns_option* ecs_option, struct ecs_data* ecs) 738 { 739 memset(ecs, 0, sizeof(*ecs)); 740 if (ecs_option->opt_len < 4) 741 return 0; 742 743 ecs->subnet_addr_fam = sldns_read_uint16(ecs_option->opt_data); 744 ecs->subnet_source_mask = ecs_option->opt_data[2]; 745 ecs->subnet_scope_mask = ecs_option->opt_data[3]; 746 /* remaining bytes indicate address */ 747 748 /* validate input*/ 749 /* option length matches calculated length? */ 750 if (ecs_option->opt_len != (size_t)((ecs->subnet_source_mask+7)/8 + 4)) 751 return 0; 752 if (ecs_option->opt_len - 4 > INET6_SIZE || ecs_option->opt_len == 0) 753 return 0; 754 if (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) { 755 if (ecs->subnet_source_mask > 32 || ecs->subnet_scope_mask > 32) 756 return 0; 757 } else if (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6) { 758 if (ecs->subnet_source_mask > 128 || 759 ecs->subnet_scope_mask > 128) 760 return 0; 761 } else 762 return 0; 763 764 /* valid ECS data, write to ecs_data */ 765 if (copy_clear(ecs->subnet_addr, INET6_SIZE, ecs_option->opt_data + 4, 766 ecs_option->opt_len - 4, ecs->subnet_source_mask)) 767 return 0; 768 ecs->subnet_validdata = 1; 769 return 1; 770 } 771 772 void 773 subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs, 774 struct config_file* cfg) 775 { 776 void* sinaddr; 777 778 /* Construct subnet option from original query */ 779 if(((struct sockaddr_in*)ss)->sin_family == AF_INET) { 780 ecs->subnet_source_mask = cfg->max_client_subnet_ipv4; 781 ecs->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP4; 782 sinaddr = &((struct sockaddr_in*)ss)->sin_addr; 783 if (!copy_clear( ecs->subnet_addr, INET6_SIZE, 784 (uint8_t *)sinaddr, INET_SIZE, 785 ecs->subnet_source_mask)) { 786 ecs->subnet_validdata = 1; 787 } 788 } 789 #ifdef INET6 790 else { 791 ecs->subnet_source_mask = cfg->max_client_subnet_ipv6; 792 ecs->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP6; 793 sinaddr = &((struct sockaddr_in6*)ss)->sin6_addr; 794 if (!copy_clear( ecs->subnet_addr, INET6_SIZE, 795 (uint8_t *)sinaddr, INET6_SIZE, 796 ecs->subnet_source_mask)) { 797 ecs->subnet_validdata = 1; 798 } 799 } 800 #else 801 /* We don't know how to handle ip6, just pass */ 802 #endif /* INET6 */ 803 } 804 805 int 806 ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, 807 int id, void* ATTR_UNUSED(cbargs)) 808 { 809 struct subnet_qstate *sq; 810 811 if(!response || !(sq=(struct subnet_qstate*)qstate->minfo[id])) 812 return 1; 813 814 if(sq->subnet_sent && 815 FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_REFUSED) { 816 /* REFUSED response to ECS query, remove ECS option. */ 817 edns_opt_list_remove(&qstate->edns_opts_back_out, 818 qstate->env->cfg->client_subnet_opcode); 819 sq->subnet_sent = 0; 820 sq->subnet_sent_no_subnet = 0; 821 memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out)); 822 } else if (!sq->track_max_scope && 823 FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR && 824 response->rep->an_numrrsets > 0 825 ) { 826 struct ub_packed_rrset_key* s = response->rep->rrsets[0]; 827 if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && 828 query_dname_compare(qstate->qinfo.qname, 829 s->rk.dname) == 0) { 830 /* CNAME response for QNAME. From now on keep track of 831 * longest received ECS prefix for all queries on this 832 * qstate. */ 833 sq->track_max_scope = 1; 834 } 835 } 836 return 1; 837 } 838 839 /** verbose print edns subnet option in pretty print */ 840 static void 841 subnet_log_print(const char* s, struct edns_option* ecs_opt) 842 { 843 if(verbosity >= VERB_ALGO) { 844 char buf[256]; 845 char* str = buf; 846 size_t str_len = sizeof(buf); 847 if(!ecs_opt) { 848 verbose(VERB_ALGO, "%s (null)", s); 849 return; 850 } 851 (void)sldns_wire2str_edns_subnet_print(&str, &str_len, 852 ecs_opt->opt_data, ecs_opt->opt_len); 853 verbose(VERB_ALGO, "%s %s", s, buf); 854 } 855 } 856 857 int 858 ecs_edns_back_parsed(struct module_qstate* qstate, int id, 859 void* ATTR_UNUSED(cbargs)) 860 { 861 struct subnet_qstate *sq; 862 struct edns_option* ecs_opt; 863 864 if(!(sq=(struct subnet_qstate*)qstate->minfo[id])) 865 return 1; 866 if((ecs_opt = edns_opt_list_find( 867 qstate->edns_opts_back_in, 868 qstate->env->cfg->client_subnet_opcode)) && 869 parse_subnet_option(ecs_opt, &sq->ecs_server_in) && 870 sq->subnet_sent && sq->ecs_server_in.subnet_validdata) { 871 subnet_log_print("answer has edns subnet", ecs_opt); 872 /* Only skip global cache store if we sent an ECS option 873 * and received one back. Answers from non-whitelisted 874 * servers will end up in global cache. Answers for 875 * queries with 0 source will not (unless nameserver 876 * does not support ECS). */ 877 qstate->no_cache_store = 1; 878 if(!sq->track_max_scope || (sq->track_max_scope && 879 sq->ecs_server_in.subnet_scope_mask > 880 sq->max_scope)) 881 sq->max_scope = sq->ecs_server_in.subnet_scope_mask; 882 } else if(sq->subnet_sent_no_subnet) { 883 /* The answer can be stored as scope 0, not in global cache. */ 884 qstate->no_cache_store = 1; 885 } else if(sq->subnet_sent) { 886 /* Need another query to be able to store in global cache. */ 887 qstate->no_cache_store = 1; 888 } 889 890 return 1; 891 } 892 893 void 894 subnetmod_operate(struct module_qstate *qstate, enum module_ev event, 895 int id, struct outbound_entry* outbound) 896 { 897 struct subnet_env *sne = qstate->env->modinfo[id]; 898 struct subnet_qstate *sq = (struct subnet_qstate*)qstate->minfo[id]; 899 900 verbose(VERB_QUERY, "subnetcache[module %d] operate: extstate:%s " 901 "event:%s", id, strextstate(qstate->ext_state[id]), 902 strmodulevent(event)); 903 log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo); 904 905 if(sq && sq->wait_subquery_done) { 906 /* The subquery lookup returned. */ 907 if(sq->ecs_client_in.subnet_source_mask == 0 && 908 edns_opt_list_find(qstate->edns_opts_front_in, 909 qstate->env->cfg->client_subnet_opcode)) { 910 if(!sq->started_no_cache_store && 911 qstate->return_msg) { 912 lock_rw_wrlock(&sne->biglock); 913 update_cache(qstate, id); 914 lock_rw_unlock(&sne->biglock); 915 } 916 if (sq->subnet_downstream) 917 cp_edns_bad_response(&sq->ecs_client_out, 918 &sq->ecs_client_in); 919 /* It is a scope zero lookup, append edns subnet 920 * option to the querier. */ 921 subnet_ecs_opt_list_append(&sq->ecs_client_out, 922 &qstate->edns_opts_front_out, qstate, 923 qstate->region); 924 } 925 sq->wait_subquery_done = 0; 926 qstate->ext_state[id] = module_finished; 927 qstate->no_cache_store = sq->started_no_cache_store; 928 qstate->no_cache_lookup = sq->started_no_cache_lookup; 929 return; 930 } 931 if((event == module_event_new || event == module_event_pass) && 932 sq == NULL) { 933 struct edns_option* ecs_opt; 934 if(!subnet_new_qstate(qstate, id)) { 935 qstate->return_msg = NULL; 936 qstate->ext_state[id] = module_finished; 937 return; 938 } 939 940 sq = (struct subnet_qstate*)qstate->minfo[id]; 941 if(sq->wait_subquery) 942 return; /* Wait for that subquery to return */ 943 944 if((ecs_opt = edns_opt_list_find( 945 qstate->edns_opts_front_in, 946 qstate->env->cfg->client_subnet_opcode))) { 947 if(!parse_subnet_option(ecs_opt, &sq->ecs_client_in)) { 948 /* Wrongly formatted ECS option. RFC mandates to 949 * return FORMERROR. */ 950 qstate->return_rcode = LDNS_RCODE_FORMERR; 951 qstate->ext_state[id] = module_finished; 952 return; 953 } 954 subnet_log_print("query has edns subnet", ecs_opt); 955 sq->subnet_downstream = 1; 956 } 957 else if(qstate->mesh_info->reply_list) { 958 subnet_option_from_ss( 959 &qstate->mesh_info->reply_list->query_reply.client_addr, 960 &sq->ecs_client_in, qstate->env->cfg); 961 } 962 else if(qstate->client_addr.ss_family != AF_UNSPEC) { 963 subnet_option_from_ss( 964 &qstate->client_addr, 965 &sq->ecs_client_in, qstate->env->cfg); 966 } 967 968 if(sq->ecs_client_in.subnet_validdata == 0) { 969 /* No clients are interested in result or we could not 970 * parse it, we don't do client subnet */ 971 sq->ecs_server_out.subnet_validdata = 0; 972 if(edns_opt_list_find(qstate->edns_opts_front_in, 973 qstate->env->cfg->client_subnet_opcode)) { 974 /* aggregated this deaggregated state */ 975 qstate->ext_state[id] = 976 generate_lookup_without_subnet( 977 qstate, id, sq); 978 return; 979 } 980 verbose(VERB_ALGO, "subnetcache: pass to next module"); 981 qstate->ext_state[id] = module_wait_module; 982 return; 983 } 984 985 /* Limit to minimum allowed source mask */ 986 if(sq->ecs_client_in.subnet_source_mask != 0 && ( 987 (sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 && 988 sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv4) || 989 (sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6 && 990 sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv6))) { 991 qstate->return_rcode = LDNS_RCODE_REFUSED; 992 qstate->ext_state[id] = module_finished; 993 return; 994 } 995 996 if(!sq->started_no_cache_lookup && !qstate->blacklist) { 997 lock_rw_wrlock(&sne->biglock); 998 if(qstate->mesh_info->reply_list && 999 lookup_and_reply(qstate, id, sq, 1000 qstate->env->cfg->prefetch)) { 1001 sne->num_msg_cache++; 1002 lock_rw_unlock(&sne->biglock); 1003 verbose(VERB_QUERY, "subnetcache: answered from cache"); 1004 qstate->ext_state[id] = module_finished; 1005 1006 subnet_ecs_opt_list_append(&sq->ecs_client_out, 1007 &qstate->edns_opts_front_out, qstate, 1008 qstate->region); 1009 if(verbosity >= VERB_ALGO) { 1010 subnet_log_print("reply has edns subnet", 1011 edns_opt_list_find( 1012 qstate->edns_opts_front_out, 1013 qstate->env->cfg-> 1014 client_subnet_opcode)); 1015 } 1016 return; 1017 } 1018 lock_rw_unlock(&sne->biglock); 1019 } 1020 if(sq->ecs_client_in.subnet_source_mask == 0 && 1021 edns_opt_list_find(qstate->edns_opts_front_in, 1022 qstate->env->cfg->client_subnet_opcode)) { 1023 /* client asked for resolution without edns subnet */ 1024 qstate->ext_state[id] = generate_lookup_without_subnet( 1025 qstate, id, sq); 1026 return; 1027 } 1028 1029 sq->ecs_server_out.subnet_addr_fam = 1030 sq->ecs_client_in.subnet_addr_fam; 1031 sq->ecs_server_out.subnet_source_mask = 1032 sq->ecs_client_in.subnet_source_mask; 1033 /* Limit source prefix to configured maximum */ 1034 if(sq->ecs_server_out.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 1035 && sq->ecs_server_out.subnet_source_mask > 1036 qstate->env->cfg->max_client_subnet_ipv4) 1037 sq->ecs_server_out.subnet_source_mask = 1038 qstate->env->cfg->max_client_subnet_ipv4; 1039 else if(sq->ecs_server_out.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6 1040 && sq->ecs_server_out.subnet_source_mask > 1041 qstate->env->cfg->max_client_subnet_ipv6) 1042 sq->ecs_server_out.subnet_source_mask = 1043 qstate->env->cfg->max_client_subnet_ipv6; 1044 /* Safe to copy completely, even if the source is limited by the 1045 * configuration. subnet_ecs_opt_list_append() will limit the address. 1046 * */ 1047 memcpy(&sq->ecs_server_out.subnet_addr, 1048 sq->ecs_client_in.subnet_addr, INET6_SIZE); 1049 sq->ecs_server_out.subnet_scope_mask = 0; 1050 sq->ecs_server_out.subnet_validdata = 1; 1051 if(sq->ecs_server_out.subnet_source_mask != 0 && 1052 qstate->env->cfg->client_subnet_always_forward && 1053 sq->subnet_downstream) 1054 /* ECS specific data required, do not look at the global 1055 * cache in other modules. */ 1056 qstate->no_cache_lookup = 1; 1057 1058 /* pass request to next module */ 1059 verbose(VERB_ALGO, 1060 "subnetcache: not found in cache. pass to next module"); 1061 qstate->ext_state[id] = module_wait_module; 1062 return; 1063 } 1064 if(sq && sq->wait_subquery) 1065 return; /* Wait for that subquery to return */ 1066 /* Query handed back by next module, we have a 'final' answer */ 1067 if(sq && event == module_event_moddone) { 1068 qstate->ext_state[id] = eval_response(qstate, id, sq); 1069 if(qstate->ext_state[id] == module_finished && 1070 qstate->return_msg) { 1071 subnet_ecs_opt_list_append(&sq->ecs_client_out, 1072 &qstate->edns_opts_front_out, qstate, 1073 qstate->region); 1074 if(verbosity >= VERB_ALGO) { 1075 subnet_log_print("reply has edns subnet", 1076 edns_opt_list_find( 1077 qstate->edns_opts_front_out, 1078 qstate->env->cfg-> 1079 client_subnet_opcode)); 1080 } 1081 } 1082 qstate->no_cache_store = sq->started_no_cache_store; 1083 qstate->no_cache_lookup = sq->started_no_cache_lookup; 1084 return; 1085 } 1086 if(sq && outbound) { 1087 return; 1088 } 1089 /* We are being revisited */ 1090 if(event == module_event_pass || event == module_event_new) { 1091 /* Just pass it on, we already did the work */ 1092 verbose(VERB_ALGO, "subnetcache: pass to next module"); 1093 qstate->ext_state[id] = module_wait_module; 1094 return; 1095 } 1096 if(!sq && (event == module_event_moddone)) { 1097 /* during priming, module done but we never started */ 1098 qstate->ext_state[id] = module_finished; 1099 return; 1100 } 1101 log_err("subnetcache: bad event %s", strmodulevent(event)); 1102 qstate->ext_state[id] = module_error; 1103 return; 1104 } 1105 1106 void 1107 subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate), 1108 int ATTR_UNUSED(id)) 1109 { 1110 /* qstate has no data outside region */ 1111 } 1112 1113 void 1114 subnetmod_inform_super(struct module_qstate *qstate, int id, 1115 struct module_qstate *super) 1116 { 1117 struct subnet_qstate* super_sq = 1118 (struct subnet_qstate*)super->minfo[id]; 1119 log_query_info(VERB_ALGO, "subnetcache inform_super: query", 1120 &super->qinfo); 1121 super_sq->wait_subquery = 0; 1122 super_sq->wait_subquery_done = 1; 1123 if(qstate->return_rcode != LDNS_RCODE_NOERROR || 1124 !qstate->return_msg) { 1125 super->return_msg = NULL; 1126 super->return_rcode = LDNS_RCODE_SERVFAIL; 1127 return; 1128 } 1129 super->return_rcode = LDNS_RCODE_NOERROR; 1130 super->return_msg = dns_copy_msg(qstate->return_msg, super->region); 1131 if(!super->return_msg) { 1132 log_err("subnetcache: copy response, out of memory"); 1133 super->return_rcode = LDNS_RCODE_SERVFAIL; 1134 } 1135 } 1136 1137 size_t 1138 subnetmod_get_mem(struct module_env *env, int id) 1139 { 1140 struct subnet_env *sn_env = env->modinfo[id]; 1141 if (!sn_env) return 0; 1142 return sizeof(*sn_env) + 1143 slabhash_get_mem(sn_env->subnet_msg_cache) + 1144 ecs_whitelist_get_mem(sn_env->whitelist); 1145 } 1146 1147 /** 1148 * The module function block 1149 */ 1150 static struct module_func_block subnetmod_block = { 1151 "subnetcache", 1152 NULL, NULL, &subnetmod_init, &subnetmod_deinit, &subnetmod_operate, 1153 &subnetmod_inform_super, &subnetmod_clear, &subnetmod_get_mem 1154 }; 1155 1156 struct module_func_block* 1157 subnetmod_get_funcblock(void) 1158 { 1159 return &subnetmod_block; 1160 } 1161 1162 /** Wrappers for static functions to unit test */ 1163 size_t 1164 unittest_wrapper_subnetmod_sizefunc(void *elemptr) 1165 { 1166 return sizefunc(elemptr); 1167 } 1168 1169 #endif /* CLIENT_SUBNET */ 1170