1/* $NetBSD: stats.c,v 1.1 2024/02/18 20:57:34 christos Exp $ */ 2 3/* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16/*! \file */ 17 18#include <inttypes.h> 19#include <stdbool.h> 20 21#include <isc/magic.h> 22#include <isc/mem.h> 23#include <isc/refcount.h> 24#include <isc/stats.h> 25#include <isc/util.h> 26 27#include <dns/log.h> 28#include <dns/opcode.h> 29#include <dns/rdatatype.h> 30#include <dns/stats.h> 31 32#define DNS_STATS_MAGIC ISC_MAGIC('D', 's', 't', 't') 33#define DNS_STATS_VALID(x) ISC_MAGIC_VALID(x, DNS_STATS_MAGIC) 34 35/*% 36 * Statistics types. 37 */ 38typedef enum { 39 dns_statstype_general = 0, 40 dns_statstype_rdtype = 1, 41 dns_statstype_rdataset = 2, 42 dns_statstype_opcode = 3, 43 dns_statstype_rcode = 4, 44 dns_statstype_dnssec = 5 45} dns_statstype_t; 46 47/*% 48 * It doesn't make sense to have 2^16 counters for all possible types since 49 * most of them won't be used. We have counters for the first 256 types. 50 * 51 * A rdtypecounter is now 8 bits for RRtypes and 3 bits for flags: 52 * 53 * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 54 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 55 * | | | | | | S |NX| RRType | 56 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 57 * 58 * If the 8 bits for RRtype are all zero, this is an Other RRtype. 59 */ 60#define RDTYPECOUNTER_MAXTYPE 0x00ff 61 62/* 63 * 64 * Bit 7 is the NXRRSET (NX) flag and indicates whether this is a 65 * positive (0) or a negative (1) RRset. 66 */ 67#define RDTYPECOUNTER_NXRRSET 0x0100 68 69/* 70 * Then bit 5 and 6 mostly tell you if this counter is for an active, 71 * stale, or ancient RRtype: 72 * 73 * S = 0 (0b00) means Active 74 * S = 1 (0b01) means Stale 75 * S = 2 (0b10) means Ancient 76 * 77 * Since a counter cannot be stale and ancient at the same time, we 78 * treat S = 0b11 as a special case to deal with NXDOMAIN counters. 79 */ 80#define RDTYPECOUNTER_STALE (1 << 9) 81#define RDTYPECOUNTER_ANCIENT (1 << 10) 82#define RDTYPECOUNTER_NXDOMAIN ((1 << 9) | (1 << 10)) 83 84/* 85 * S = 0b11 indicates an NXDOMAIN counter and in this case the RRtype 86 * field signals the expiry of this cached item: 87 * 88 * RRType = 0 (0b00) means Active 89 * RRType = 1 (0b01) means Stale 90 * RRType = 2 (0b02) means Ancient 91 * 92 */ 93#define RDTYPECOUNTER_NXDOMAIN_STALE 1 94#define RDTYPECOUNTER_NXDOMAIN_ANCIENT 2 95 96/* 97 * The maximum value for rdtypecounter is for an ancient NXDOMAIN. 98 */ 99#define RDTYPECOUNTER_MAXVAL 0x0602 100 101/* 102 * DNSSEC sign statistics. 103 * 104 * Per key we maintain 3 counters. The first is actually no counter but 105 * a key id reference. The second is the number of signatures the key created. 106 * The third is the number of signatures refreshed by the key. 107 */ 108 109/* Maximum number of keys to keep track of for DNSSEC signing statistics. */ 110static int dnssecsign_num_keys = 4; 111static int dnssecsign_block_size = 3; 112/* Key id mask */ 113#define DNSSECSIGNSTATS_KEY_ID_MASK 0x0000FFFF 114 115struct dns_stats { 116 unsigned int magic; 117 dns_statstype_t type; 118 isc_mem_t *mctx; 119 isc_stats_t *counters; 120 isc_refcount_t references; 121}; 122 123typedef struct rdatadumparg { 124 dns_rdatatypestats_dumper_t fn; 125 void *arg; 126} rdatadumparg_t; 127 128typedef struct opcodedumparg { 129 dns_opcodestats_dumper_t fn; 130 void *arg; 131} opcodedumparg_t; 132 133typedef struct rcodedumparg { 134 dns_rcodestats_dumper_t fn; 135 void *arg; 136} rcodedumparg_t; 137typedef struct dnssecsigndumparg { 138 dns_dnssecsignstats_dumper_t fn; 139 void *arg; 140} dnssecsigndumparg_t; 141 142void 143dns_stats_attach(dns_stats_t *stats, dns_stats_t **statsp) { 144 REQUIRE(DNS_STATS_VALID(stats)); 145 REQUIRE(statsp != NULL && *statsp == NULL); 146 147 isc_refcount_increment(&stats->references); 148 149 *statsp = stats; 150} 151 152void 153dns_stats_detach(dns_stats_t **statsp) { 154 dns_stats_t *stats; 155 156 REQUIRE(statsp != NULL && DNS_STATS_VALID(*statsp)); 157 158 stats = *statsp; 159 *statsp = NULL; 160 161 if (isc_refcount_decrement(&stats->references) == 1) { 162 isc_refcount_destroy(&stats->references); 163 isc_stats_detach(&stats->counters); 164 isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats)); 165 } 166} 167 168/*% 169 * Create methods 170 */ 171static isc_result_t 172create_stats(isc_mem_t *mctx, dns_statstype_t type, int ncounters, 173 dns_stats_t **statsp) { 174 dns_stats_t *stats; 175 isc_result_t result; 176 177 stats = isc_mem_get(mctx, sizeof(*stats)); 178 179 stats->counters = NULL; 180 isc_refcount_init(&stats->references, 1); 181 182 result = isc_stats_create(mctx, &stats->counters, ncounters); 183 if (result != ISC_R_SUCCESS) { 184 goto clean_mutex; 185 } 186 187 stats->magic = DNS_STATS_MAGIC; 188 stats->type = type; 189 stats->mctx = NULL; 190 isc_mem_attach(mctx, &stats->mctx); 191 *statsp = stats; 192 193 return (ISC_R_SUCCESS); 194 195clean_mutex: 196 isc_mem_put(mctx, stats, sizeof(*stats)); 197 198 return (result); 199} 200 201isc_result_t 202dns_generalstats_create(isc_mem_t *mctx, dns_stats_t **statsp, int ncounters) { 203 REQUIRE(statsp != NULL && *statsp == NULL); 204 205 return (create_stats(mctx, dns_statstype_general, ncounters, statsp)); 206} 207 208isc_result_t 209dns_rdatatypestats_create(isc_mem_t *mctx, dns_stats_t **statsp) { 210 REQUIRE(statsp != NULL && *statsp == NULL); 211 212 /* 213 * Create rdtype statistics for the first 255 RRtypes, 214 * plus one additional for other RRtypes. 215 */ 216 return (create_stats(mctx, dns_statstype_rdtype, 217 (RDTYPECOUNTER_MAXTYPE + 1), statsp)); 218} 219 220isc_result_t 221dns_rdatasetstats_create(isc_mem_t *mctx, dns_stats_t **statsp) { 222 REQUIRE(statsp != NULL && *statsp == NULL); 223 224 return (create_stats(mctx, dns_statstype_rdataset, 225 (RDTYPECOUNTER_MAXVAL + 1), statsp)); 226} 227 228isc_result_t 229dns_opcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp) { 230 REQUIRE(statsp != NULL && *statsp == NULL); 231 232 return (create_stats(mctx, dns_statstype_opcode, 16, statsp)); 233} 234 235isc_result_t 236dns_rcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp) { 237 REQUIRE(statsp != NULL && *statsp == NULL); 238 239 return (create_stats(mctx, dns_statstype_rcode, dns_rcode_badcookie + 1, 240 statsp)); 241} 242 243isc_result_t 244dns_dnssecsignstats_create(isc_mem_t *mctx, dns_stats_t **statsp) { 245 REQUIRE(statsp != NULL && *statsp == NULL); 246 247 /* 248 * Create two counters per key, one is the key id, the other two are 249 * the actual counters for creating and refreshing signatures. 250 */ 251 return (create_stats(mctx, dns_statstype_dnssec, 252 dnssecsign_num_keys * dnssecsign_block_size, 253 statsp)); 254} 255 256/*% 257 * Increment/Decrement methods 258 */ 259void 260dns_generalstats_increment(dns_stats_t *stats, isc_statscounter_t counter) { 261 REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general); 262 263 isc_stats_increment(stats->counters, counter); 264} 265 266static isc_statscounter_t 267rdatatype2counter(dns_rdatatype_t type) { 268 if (type > (dns_rdatatype_t)RDTYPECOUNTER_MAXTYPE) { 269 return (0); 270 } 271 return ((isc_statscounter_t)type); 272} 273 274void 275dns_rdatatypestats_increment(dns_stats_t *stats, dns_rdatatype_t type) { 276 isc_statscounter_t counter; 277 278 REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype); 279 280 counter = rdatatype2counter(type); 281 isc_stats_increment(stats->counters, counter); 282} 283 284static void 285update_rdatasetstats(dns_stats_t *stats, dns_rdatastatstype_t rrsettype, 286 bool increment) { 287 isc_statscounter_t counter; 288 289 if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & 290 DNS_RDATASTATSTYPE_ATTR_NXDOMAIN) != 0) 291 { 292 counter = RDTYPECOUNTER_NXDOMAIN; 293 294 /* 295 * This is an NXDOMAIN counter, save the expiry value 296 * (active, stale, or ancient) value in the RRtype part. 297 */ 298 if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & 299 DNS_RDATASTATSTYPE_ATTR_ANCIENT) != 0) 300 { 301 counter |= RDTYPECOUNTER_NXDOMAIN_ANCIENT; 302 } else if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & 303 DNS_RDATASTATSTYPE_ATTR_STALE) != 0) 304 { 305 counter += RDTYPECOUNTER_NXDOMAIN_STALE; 306 } 307 } else { 308 counter = rdatatype2counter(DNS_RDATASTATSTYPE_BASE(rrsettype)); 309 310 if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & 311 DNS_RDATASTATSTYPE_ATTR_NXRRSET) != 0) 312 { 313 counter |= RDTYPECOUNTER_NXRRSET; 314 } 315 316 if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & 317 DNS_RDATASTATSTYPE_ATTR_ANCIENT) != 0) 318 { 319 counter |= RDTYPECOUNTER_ANCIENT; 320 } else if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & 321 DNS_RDATASTATSTYPE_ATTR_STALE) != 0) 322 { 323 counter |= RDTYPECOUNTER_STALE; 324 } 325 } 326 327 if (increment) { 328 isc_stats_increment(stats->counters, counter); 329 } else { 330 isc_stats_decrement(stats->counters, counter); 331 } 332} 333 334void 335dns_rdatasetstats_increment(dns_stats_t *stats, 336 dns_rdatastatstype_t rrsettype) { 337 REQUIRE(DNS_STATS_VALID(stats) && 338 stats->type == dns_statstype_rdataset); 339 340 update_rdatasetstats(stats, rrsettype, true); 341} 342 343void 344dns_rdatasetstats_decrement(dns_stats_t *stats, 345 dns_rdatastatstype_t rrsettype) { 346 REQUIRE(DNS_STATS_VALID(stats) && 347 stats->type == dns_statstype_rdataset); 348 349 update_rdatasetstats(stats, rrsettype, false); 350} 351 352void 353dns_opcodestats_increment(dns_stats_t *stats, dns_opcode_t code) { 354 REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode); 355 356 isc_stats_increment(stats->counters, (isc_statscounter_t)code); 357} 358 359void 360dns_rcodestats_increment(dns_stats_t *stats, dns_rcode_t code) { 361 REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rcode); 362 363 if (code <= dns_rcode_badcookie) { 364 isc_stats_increment(stats->counters, (isc_statscounter_t)code); 365 } 366} 367 368void 369dns_dnssecsignstats_increment(dns_stats_t *stats, dns_keytag_t id, uint8_t alg, 370 dnssecsignstats_type_t operation) { 371 uint32_t kval; 372 int num_keys = isc_stats_ncounters(stats->counters) / 373 dnssecsign_block_size; 374 375 REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_dnssec); 376 377 /* Shift algorithm in front of key tag, which is 16 bits */ 378 kval = (uint32_t)(alg << 16 | id); 379 380 /* Look up correct counter. */ 381 for (int i = 0; i < num_keys; i++) { 382 int idx = i * dnssecsign_block_size; 383 uint32_t counter = isc_stats_get_counter(stats->counters, idx); 384 if (counter == kval) { 385 /* Match */ 386 isc_stats_increment(stats->counters, (idx + operation)); 387 return; 388 } 389 } 390 391 /* No match found. Store key in unused slot. */ 392 for (int i = 0; i < num_keys; i++) { 393 int idx = i * dnssecsign_block_size; 394 uint32_t counter = isc_stats_get_counter(stats->counters, idx); 395 if (counter == 0) { 396 isc_stats_set(stats->counters, kval, idx); 397 isc_stats_increment(stats->counters, (idx + operation)); 398 return; 399 } 400 } 401 402 /* No room, grow stats storage. */ 403 isc_stats_resize(&stats->counters, 404 (num_keys * dnssecsign_block_size * 2)); 405 406 /* Reset counters for new key (new index, nidx). */ 407 int nidx = num_keys * dnssecsign_block_size; 408 isc_stats_set(stats->counters, kval, nidx); 409 isc_stats_set(stats->counters, 0, (nidx + dns_dnssecsignstats_sign)); 410 isc_stats_set(stats->counters, 0, (nidx + dns_dnssecsignstats_refresh)); 411 412 /* And increment the counter for the given operation. */ 413 isc_stats_increment(stats->counters, (nidx + operation)); 414} 415 416void 417dns_dnssecsignstats_clear(dns_stats_t *stats, dns_keytag_t id, uint8_t alg) { 418 uint32_t kval; 419 int num_keys = isc_stats_ncounters(stats->counters) / 420 dnssecsign_block_size; 421 422 REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_dnssec); 423 424 /* Shift algorithm in front of key tag, which is 16 bits */ 425 kval = (uint32_t)(alg << 16 | id); 426 427 /* Look up correct counter. */ 428 for (int i = 0; i < num_keys; i++) { 429 int idx = i * dnssecsign_block_size; 430 uint32_t counter = isc_stats_get_counter(stats->counters, idx); 431 if (counter == kval) { 432 /* Match */ 433 isc_stats_set(stats->counters, 0, idx); 434 isc_stats_set(stats->counters, 0, 435 (idx + dns_dnssecsignstats_sign)); 436 isc_stats_set(stats->counters, 0, 437 (idx + dns_dnssecsignstats_refresh)); 438 return; 439 } 440 } 441} 442 443/*% 444 * Dump methods 445 */ 446void 447dns_generalstats_dump(dns_stats_t *stats, dns_generalstats_dumper_t dump_fn, 448 void *arg, unsigned int options) { 449 REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general); 450 451 isc_stats_dump(stats->counters, (isc_stats_dumper_t)dump_fn, arg, 452 options); 453} 454 455static void 456dump_rdentry(int rdcounter, uint64_t value, dns_rdatastatstype_t attributes, 457 dns_rdatatypestats_dumper_t dump_fn, void *arg) { 458 dns_rdatatype_t rdtype = dns_rdatatype_none; /* sentinel */ 459 dns_rdatastatstype_t type; 460 461 if ((rdcounter & RDTYPECOUNTER_MAXTYPE) == 0) { 462 attributes |= DNS_RDATASTATSTYPE_ATTR_OTHERTYPE; 463 } else { 464 rdtype = (dns_rdatatype_t)(rdcounter & RDTYPECOUNTER_MAXTYPE); 465 } 466 type = DNS_RDATASTATSTYPE_VALUE((dns_rdatastatstype_t)rdtype, 467 attributes); 468 dump_fn(type, value, arg); 469} 470 471static void 472rdatatype_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) { 473 rdatadumparg_t *rdatadumparg = arg; 474 475 dump_rdentry(counter, value, 0, rdatadumparg->fn, rdatadumparg->arg); 476} 477 478void 479dns_rdatatypestats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn, 480 void *arg0, unsigned int options) { 481 rdatadumparg_t arg; 482 REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype); 483 484 arg.fn = dump_fn; 485 arg.arg = arg0; 486 isc_stats_dump(stats->counters, rdatatype_dumpcb, &arg, options); 487} 488 489static void 490rdataset_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) { 491 rdatadumparg_t *rdatadumparg = arg; 492 unsigned int attributes = 0; 493 494 if ((counter & RDTYPECOUNTER_NXDOMAIN) == RDTYPECOUNTER_NXDOMAIN) { 495 attributes |= DNS_RDATASTATSTYPE_ATTR_NXDOMAIN; 496 497 /* 498 * This is an NXDOMAIN counter, check the RRtype part for the 499 * expiry value (active, stale, or ancient). 500 */ 501 if ((counter & RDTYPECOUNTER_MAXTYPE) == 502 RDTYPECOUNTER_NXDOMAIN_STALE) 503 { 504 attributes |= DNS_RDATASTATSTYPE_ATTR_STALE; 505 } else if ((counter & RDTYPECOUNTER_MAXTYPE) == 506 RDTYPECOUNTER_NXDOMAIN_ANCIENT) 507 { 508 attributes |= DNS_RDATASTATSTYPE_ATTR_ANCIENT; 509 } 510 } else { 511 if ((counter & RDTYPECOUNTER_MAXTYPE) == 0) { 512 attributes |= DNS_RDATASTATSTYPE_ATTR_OTHERTYPE; 513 } 514 if ((counter & RDTYPECOUNTER_NXRRSET) != 0) { 515 attributes |= DNS_RDATASTATSTYPE_ATTR_NXRRSET; 516 } 517 518 if ((counter & RDTYPECOUNTER_STALE) != 0) { 519 attributes |= DNS_RDATASTATSTYPE_ATTR_STALE; 520 } else if ((counter & RDTYPECOUNTER_ANCIENT) != 0) { 521 attributes |= DNS_RDATASTATSTYPE_ATTR_ANCIENT; 522 } 523 } 524 525 dump_rdentry(counter, value, attributes, rdatadumparg->fn, 526 rdatadumparg->arg); 527} 528 529void 530dns_rdatasetstats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn, 531 void *arg0, unsigned int options) { 532 rdatadumparg_t arg; 533 534 REQUIRE(DNS_STATS_VALID(stats) && 535 stats->type == dns_statstype_rdataset); 536 537 arg.fn = dump_fn; 538 arg.arg = arg0; 539 isc_stats_dump(stats->counters, rdataset_dumpcb, &arg, options); 540} 541 542static void 543dnssec_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) { 544 dnssecsigndumparg_t *dnssecarg = arg; 545 546 dnssecarg->fn((dns_keytag_t)counter, value, dnssecarg->arg); 547} 548 549static void 550dnssec_statsdump(isc_stats_t *stats, dnssecsignstats_type_t operation, 551 isc_stats_dumper_t dump_fn, void *arg, unsigned int options) { 552 int i, num_keys; 553 554 num_keys = isc_stats_ncounters(stats) / dnssecsign_block_size; 555 for (i = 0; i < num_keys; i++) { 556 int idx = dnssecsign_block_size * i; 557 uint32_t kval, val; 558 dns_keytag_t id; 559 560 kval = isc_stats_get_counter(stats, idx); 561 if (kval == 0) { 562 continue; 563 } 564 565 val = isc_stats_get_counter(stats, (idx + operation)); 566 if ((options & ISC_STATSDUMP_VERBOSE) == 0 && val == 0) { 567 continue; 568 } 569 570 id = (dns_keytag_t)kval & DNSSECSIGNSTATS_KEY_ID_MASK; 571 572 dump_fn((isc_statscounter_t)id, val, arg); 573 } 574} 575 576void 577dns_dnssecsignstats_dump(dns_stats_t *stats, dnssecsignstats_type_t operation, 578 dns_dnssecsignstats_dumper_t dump_fn, void *arg0, 579 unsigned int options) { 580 dnssecsigndumparg_t arg; 581 582 REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_dnssec); 583 584 arg.fn = dump_fn; 585 arg.arg = arg0; 586 587 dnssec_statsdump(stats->counters, operation, dnssec_dumpcb, &arg, 588 options); 589} 590 591static void 592opcode_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) { 593 opcodedumparg_t *opcodearg = arg; 594 595 opcodearg->fn((dns_opcode_t)counter, value, opcodearg->arg); 596} 597 598static void 599rcode_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) { 600 rcodedumparg_t *rcodearg = arg; 601 602 rcodearg->fn((dns_rcode_t)counter, value, rcodearg->arg); 603} 604 605void 606dns_opcodestats_dump(dns_stats_t *stats, dns_opcodestats_dumper_t dump_fn, 607 void *arg0, unsigned int options) { 608 opcodedumparg_t arg; 609 610 REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode); 611 612 arg.fn = dump_fn; 613 arg.arg = arg0; 614 isc_stats_dump(stats->counters, opcode_dumpcb, &arg, options); 615} 616 617void 618dns_rcodestats_dump(dns_stats_t *stats, dns_rcodestats_dumper_t dump_fn, 619 void *arg0, unsigned int options) { 620 rcodedumparg_t arg; 621 622 REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rcode); 623 624 arg.fn = dump_fn; 625 arg.arg = arg0; 626 isc_stats_dump(stats->counters, rcode_dumpcb, &arg, options); 627} 628 629/*** 630 *** Obsolete variables and functions follow: 631 ***/ 632LIBDNS_EXTERNAL_DATA const char *dns_statscounter_names[DNS_STATS_NCOUNTERS] = { 633 "success", "referral", "nxrrset", "nxdomain", 634 "recursion", "failure", "duplicate", "dropped" 635}; 636 637isc_result_t 638dns_stats_alloccounters(isc_mem_t *mctx, uint64_t **ctrp) { 639 int i; 640 uint64_t *p = isc_mem_get(mctx, DNS_STATS_NCOUNTERS * sizeof(uint64_t)); 641 if (p == NULL) { 642 return (ISC_R_NOMEMORY); 643 } 644 for (i = 0; i < DNS_STATS_NCOUNTERS; i++) { 645 p[i] = 0; 646 } 647 *ctrp = p; 648 return (ISC_R_SUCCESS); 649} 650 651void 652dns_stats_freecounters(isc_mem_t *mctx, uint64_t **ctrp) { 653 isc_mem_put(mctx, *ctrp, DNS_STATS_NCOUNTERS * sizeof(uint64_t)); 654 *ctrp = NULL; 655} 656