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