1 /* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2
3 This program is free software: you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation, either version 3 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17 #include "contrib/macros.h"
18 #include "contrib/wire_ctx.h"
19 #include "knot/include/module.h"
20 #include "knot/nameserver/xfr.h" // Dependency on qdata->extra!
21
22 #define MOD_PROTOCOL "\x10""request-protocol"
23 #define MOD_OPERATION "\x10""server-operation"
24 #define MOD_REQ_BYTES "\x0D""request-bytes"
25 #define MOD_RESP_BYTES "\x0E""response-bytes"
26 #define MOD_EDNS "\x0D""edns-presence"
27 #define MOD_FLAG "\x0D""flag-presence"
28 #define MOD_RCODE "\x0D""response-code"
29 #define MOD_REQ_EOPT "\x13""request-edns-option"
30 #define MOD_RESP_EOPT "\x14""response-edns-option"
31 #define MOD_NODATA "\x0C""reply-nodata"
32 #define MOD_QTYPE "\x0A""query-type"
33 #define MOD_QSIZE "\x0A""query-size"
34 #define MOD_RSIZE "\x0A""reply-size"
35
36 #define OTHER "other"
37
38 const yp_item_t stats_conf[] = {
39 { MOD_PROTOCOL, YP_TBOOL, YP_VBOOL = { true } },
40 { MOD_OPERATION, YP_TBOOL, YP_VBOOL = { true } },
41 { MOD_REQ_BYTES, YP_TBOOL, YP_VBOOL = { true } },
42 { MOD_RESP_BYTES, YP_TBOOL, YP_VBOOL = { true } },
43 { MOD_EDNS, YP_TBOOL, YP_VNONE },
44 { MOD_FLAG, YP_TBOOL, YP_VNONE },
45 { MOD_RCODE, YP_TBOOL, YP_VBOOL = { true } },
46 { MOD_REQ_EOPT, YP_TBOOL, YP_VNONE },
47 { MOD_RESP_EOPT, YP_TBOOL, YP_VNONE },
48 { MOD_NODATA, YP_TBOOL, YP_VNONE },
49 { MOD_QTYPE, YP_TBOOL, YP_VNONE },
50 { MOD_QSIZE, YP_TBOOL, YP_VNONE },
51 { MOD_RSIZE, YP_TBOOL, YP_VNONE },
52 { NULL }
53 };
54
55 enum {
56 CTR_PROTOCOL,
57 CTR_OPERATION,
58 CTR_REQ_BYTES,
59 CTR_RESP_BYTES,
60 CTR_EDNS,
61 CTR_FLAG,
62 CTR_RCODE,
63 CTR_REQ_EOPT,
64 CTR_RESP_EOPT,
65 CTR_NODATA,
66 CTR_QTYPE,
67 CTR_QSIZE,
68 CTR_RSIZE,
69 };
70
71 typedef struct {
72 bool protocol;
73 bool operation;
74 bool req_bytes;
75 bool resp_bytes;
76 bool edns;
77 bool flag;
78 bool rcode;
79 bool req_eopt;
80 bool resp_eopt;
81 bool nodata;
82 bool qtype;
83 bool qsize;
84 bool rsize;
85 } stats_t;
86
87 typedef struct {
88 yp_name_t *conf_name;
89 size_t conf_offset;
90 uint32_t count;
91 knotd_mod_idx_to_str_f fcn;
92 } ctr_desc_t;
93
94 enum {
95 OPERATION_QUERY = 0,
96 OPERATION_UPDATE,
97 OPERATION_NOTIFY,
98 OPERATION_AXFR,
99 OPERATION_IXFR,
100 OPERATION_INVALID,
101 OPERATION__COUNT
102 };
103
operation_to_str(uint32_t idx,uint32_t count)104 static char *operation_to_str(uint32_t idx, uint32_t count)
105 {
106 switch (idx) {
107 case OPERATION_QUERY: return strdup("query");
108 case OPERATION_UPDATE: return strdup("update");
109 case OPERATION_NOTIFY: return strdup("notify");
110 case OPERATION_AXFR: return strdup("axfr");
111 case OPERATION_IXFR: return strdup("ixfr");
112 case OPERATION_INVALID: return strdup("invalid");
113 default: assert(0); return NULL;
114 }
115 }
116
117 enum {
118 PROTOCOL_UDP4 = 0,
119 PROTOCOL_TCP4,
120 PROTOCOL_UDP6,
121 PROTOCOL_TCP6,
122 PROTOCOL_UDP4_XDP,
123 PROTOCOL_TCP4_XDP,
124 PROTOCOL_UDP6_XDP,
125 PROTOCOL_TCP6_XDP,
126 PROTOCOL__COUNT
127 };
128
protocol_to_str(uint32_t idx,uint32_t count)129 static char *protocol_to_str(uint32_t idx, uint32_t count)
130 {
131 switch (idx) {
132 case PROTOCOL_UDP4: return strdup("udp4");
133 case PROTOCOL_TCP4: return strdup("tcp4");
134 case PROTOCOL_UDP6: return strdup("udp6");
135 case PROTOCOL_TCP6: return strdup("tcp6");
136 case PROTOCOL_UDP4_XDP: return strdup("udp4-xdp");
137 case PROTOCOL_TCP4_XDP: return strdup("tcp4-xdp");
138 case PROTOCOL_UDP6_XDP: return strdup("udp6-xdp");
139 case PROTOCOL_TCP6_XDP: return strdup("tcp6-xdp");
140 default: assert(0); return NULL;
141 }
142 }
143
144 enum {
145 REQ_BYTES_QUERY = 0,
146 REQ_BYTES_UPDATE,
147 REQ_BYTES_OTHER,
148 REQ_BYTES__COUNT
149 };
150
req_bytes_to_str(uint32_t idx,uint32_t count)151 static char *req_bytes_to_str(uint32_t idx, uint32_t count)
152 {
153 switch (idx) {
154 case REQ_BYTES_QUERY: return strdup("query");
155 case REQ_BYTES_UPDATE: return strdup("update");
156 case REQ_BYTES_OTHER: return strdup(OTHER);
157 default: assert(0); return NULL;
158 }
159 }
160
161 enum {
162 RESP_BYTES_REPLY = 0,
163 RESP_BYTES_TRANSFER,
164 RESP_BYTES_OTHER,
165 RESP_BYTES__COUNT
166 };
167
resp_bytes_to_str(uint32_t idx,uint32_t count)168 static char *resp_bytes_to_str(uint32_t idx, uint32_t count)
169 {
170 switch (idx) {
171 case RESP_BYTES_REPLY: return strdup("reply");
172 case RESP_BYTES_TRANSFER: return strdup("transfer");
173 case RESP_BYTES_OTHER: return strdup(OTHER);
174 default: assert(0); return NULL;
175 }
176 }
177
178 enum {
179 EDNS_REQ = 0,
180 EDNS_RESP,
181 EDNS__COUNT
182 };
183
edns_to_str(uint32_t idx,uint32_t count)184 static char *edns_to_str(uint32_t idx, uint32_t count)
185 {
186 switch (idx) {
187 case EDNS_REQ: return strdup("request");
188 case EDNS_RESP: return strdup("response");
189 default: assert(0); return NULL;
190 }
191 }
192
193 enum {
194 FLAG_DO = 0,
195 FLAG_TC,
196 FLAG__COUNT
197 };
198
flag_to_str(uint32_t idx,uint32_t count)199 static char *flag_to_str(uint32_t idx, uint32_t count)
200 {
201 switch (idx) {
202 case FLAG_TC: return strdup("TC");
203 case FLAG_DO: return strdup("DO");
204 default: assert(0); return NULL;
205 }
206 }
207
208 enum {
209 NODATA_A = 0,
210 NODATA_AAAA,
211 NODATA_OTHER,
212 NODATA__COUNT
213 };
214
nodata_to_str(uint32_t idx,uint32_t count)215 static char *nodata_to_str(uint32_t idx, uint32_t count)
216 {
217 switch (idx) {
218 case NODATA_A: return strdup("A");
219 case NODATA_AAAA: return strdup("AAAA");
220 case NODATA_OTHER: return strdup(OTHER);
221 default: assert(0); return NULL;
222 }
223 }
224
225 #define RCODE_BADSIG 15 // Unassigned code internally used for BADSIG.
226 #define RCODE_OTHER (KNOT_RCODE_BADCOOKIE + 1) // Other RCODES.
227
rcode_to_str(uint32_t idx,uint32_t count)228 static char *rcode_to_str(uint32_t idx, uint32_t count)
229 {
230 const knot_lookup_t *rcode = NULL;
231
232 switch (idx) {
233 case RCODE_BADSIG:
234 rcode = knot_lookup_by_id(knot_tsig_rcode_names, KNOT_RCODE_BADSIG);
235 break;
236 case RCODE_OTHER:
237 return strdup(OTHER);
238 default:
239 rcode = knot_lookup_by_id(knot_rcode_names, idx);
240 break;
241 }
242
243 if (rcode != NULL) {
244 return strdup(rcode->name);
245 } else {
246 return NULL;
247 }
248 }
249
250 #define EOPT_OTHER (KNOT_EDNS_MAX_OPTION_CODE + 1)
251 #define req_eopt_to_str eopt_to_str
252 #define resp_eopt_to_str eopt_to_str
253
eopt_to_str(uint32_t idx,uint32_t count)254 static char *eopt_to_str(uint32_t idx, uint32_t count)
255 {
256 if (idx >= EOPT_OTHER) {
257 return strdup(OTHER);
258 }
259
260 char str[32];
261 if (knot_opt_code_to_string(idx, str, sizeof(str)) < 0) {
262 return NULL;
263 } else {
264 return strdup(str);
265 }
266 }
267
268 enum {
269 QTYPE_OTHER = 0,
270 QTYPE_MIN1 = 1,
271 QTYPE_MAX1 = 65,
272 QTYPE_MIN2 = 99,
273 QTYPE_MAX2 = 110,
274 QTYPE_MIN3 = 255,
275 QTYPE_MAX3 = 260,
276 QTYPE_SHIFT2 = QTYPE_MIN2 - QTYPE_MAX1 - 1,
277 QTYPE_SHIFT3 = QTYPE_SHIFT2 + QTYPE_MIN3 - QTYPE_MAX2 - 1,
278 QTYPE__COUNT = QTYPE_MAX3 - QTYPE_SHIFT3 + 1
279 };
280
qtype_to_str(uint32_t idx,uint32_t count)281 static char *qtype_to_str(uint32_t idx, uint32_t count)
282 {
283 if (idx == QTYPE_OTHER) {
284 return strdup(OTHER);
285 }
286
287 uint16_t qtype;
288
289 if (idx <= QTYPE_MAX1) {
290 qtype = idx;
291 assert(qtype >= QTYPE_MIN1 && qtype <= QTYPE_MAX1);
292 } else if (idx <= QTYPE_MAX2 - QTYPE_SHIFT2) {
293 qtype = idx + QTYPE_SHIFT2;
294 assert(qtype >= QTYPE_MIN2 && qtype <= QTYPE_MAX2);
295 } else {
296 qtype = idx + QTYPE_SHIFT3;
297 assert(qtype >= QTYPE_MIN3 && qtype <= QTYPE_MAX3);
298 }
299
300 char str[32];
301 if (knot_rrtype_to_string(qtype, str, sizeof(str)) < 0) {
302 return NULL;
303 } else {
304 return strdup(str);
305 }
306 }
307
308 #define BUCKET_SIZE 16
309 #define QSIZE_MAX_IDX (288 / BUCKET_SIZE)
310 #define RSIZE_MAX_IDX (4096 / BUCKET_SIZE)
311
size_to_str(uint32_t idx,uint32_t count)312 static char *size_to_str(uint32_t idx, uint32_t count)
313 {
314 char str[16];
315
316 int ret;
317 if (idx < count - 1) {
318 ret = snprintf(str, sizeof(str), "%u-%u", idx * BUCKET_SIZE,
319 (idx + 1) * BUCKET_SIZE - 1);
320 } else {
321 ret = snprintf(str, sizeof(str), "%u-65535", idx * BUCKET_SIZE);
322 }
323
324 if (ret <= 0 || (size_t)ret >= sizeof(str)) {
325 return NULL;
326 } else {
327 return strdup(str);
328 }
329 }
330
qsize_to_str(uint32_t idx,uint32_t count)331 static char *qsize_to_str(uint32_t idx, uint32_t count)
332 {
333 return size_to_str(idx, count);
334 }
335
rsize_to_str(uint32_t idx,uint32_t count)336 static char *rsize_to_str(uint32_t idx, uint32_t count)
337 {
338 return size_to_str(idx, count);
339 }
340
341 static const ctr_desc_t ctr_descs[] = {
342 #define item(macro, name, count) \
343 [CTR_##macro] = { MOD_##macro, offsetof(stats_t, name), (count), name##_to_str }
344 item(PROTOCOL, protocol, PROTOCOL__COUNT),
345 item(OPERATION, operation, OPERATION__COUNT),
346 item(REQ_BYTES, req_bytes, REQ_BYTES__COUNT),
347 item(RESP_BYTES, resp_bytes, RESP_BYTES__COUNT),
348 item(EDNS, edns, EDNS__COUNT),
349 item(FLAG, flag, FLAG__COUNT),
350 item(RCODE, rcode, RCODE_OTHER + 1),
351 item(REQ_EOPT, req_eopt, EOPT_OTHER + 1),
352 item(RESP_EOPT, resp_eopt, EOPT_OTHER + 1),
353 item(NODATA, nodata, NODATA__COUNT),
354 item(QTYPE, qtype, QTYPE__COUNT),
355 item(QSIZE, qsize, QSIZE_MAX_IDX + 1),
356 item(RSIZE, rsize, RSIZE_MAX_IDX + 1),
357 { NULL }
358 };
359
incr_edns_option(knotd_mod_t * mod,unsigned thr_id,const knot_pkt_t * pkt,unsigned ctr_name)360 static void incr_edns_option(knotd_mod_t *mod, unsigned thr_id, const knot_pkt_t *pkt, unsigned ctr_name)
361 {
362 if (!knot_pkt_has_edns(pkt)) {
363 return;
364 }
365
366 knot_rdata_t *rdata = pkt->opt_rr->rrs.rdata;
367 if (rdata == NULL || rdata->len == 0) {
368 return;
369 }
370
371 wire_ctx_t wire = wire_ctx_init_const(rdata->data, rdata->len);
372 while (wire_ctx_available(&wire) > 0) {
373 uint16_t opt_code = wire_ctx_read_u16(&wire);
374 uint16_t opt_len = wire_ctx_read_u16(&wire);
375 wire_ctx_skip(&wire, opt_len);
376 if (wire.error != KNOT_EOK) {
377 break;
378 }
379 knotd_mod_stats_incr(mod, thr_id, ctr_name, MIN(opt_code, EOPT_OTHER), 1);
380 }
381 }
382
update_counters(knotd_state_t state,knot_pkt_t * pkt,knotd_qdata_t * qdata,knotd_mod_t * mod)383 static knotd_state_t update_counters(knotd_state_t state, knot_pkt_t *pkt,
384 knotd_qdata_t *qdata, knotd_mod_t *mod)
385 {
386 assert(pkt && qdata);
387
388 stats_t *stats = knotd_mod_ctx(mod);
389
390 uint16_t operation;
391 unsigned xfr_packets = 0;
392 unsigned tid = qdata->params->thread_id;
393
394 // Get the server operation.
395 switch (qdata->type) {
396 case KNOTD_QUERY_TYPE_NORMAL:
397 operation = OPERATION_QUERY;
398 break;
399 case KNOTD_QUERY_TYPE_UPDATE:
400 operation = OPERATION_UPDATE;
401 break;
402 case KNOTD_QUERY_TYPE_NOTIFY:
403 operation = OPERATION_NOTIFY;
404 break;
405 case KNOTD_QUERY_TYPE_AXFR:
406 operation = OPERATION_AXFR;
407 if (qdata->extra->ext != NULL) {
408 xfr_packets = ((struct xfr_proc *)qdata->extra->ext)->stats.messages;
409 }
410 break;
411 case KNOTD_QUERY_TYPE_IXFR:
412 operation = OPERATION_IXFR;
413 if (qdata->extra->ext != NULL) {
414 xfr_packets = ((struct xfr_proc *)qdata->extra->ext)->stats.messages;
415 }
416 break;
417 default:
418 operation = OPERATION_INVALID;
419 break;
420 }
421
422 // Count request bytes.
423 if (stats->req_bytes) {
424 switch (operation) {
425 case OPERATION_QUERY:
426 knotd_mod_stats_incr(mod, tid, CTR_REQ_BYTES, REQ_BYTES_QUERY,
427 knot_pkt_size(qdata->query));
428 break;
429 case OPERATION_UPDATE:
430 knotd_mod_stats_incr(mod, tid, CTR_REQ_BYTES, REQ_BYTES_UPDATE,
431 knot_pkt_size(qdata->query));
432 break;
433 default:
434 if (xfr_packets <= 1) {
435 knotd_mod_stats_incr(mod, tid, CTR_REQ_BYTES, REQ_BYTES_OTHER,
436 knot_pkt_size(qdata->query));
437 }
438 break;
439 }
440 }
441
442 // Count response bytes.
443 if (stats->resp_bytes && state != KNOTD_STATE_NOOP) {
444 switch (operation) {
445 case OPERATION_QUERY:
446 knotd_mod_stats_incr(mod, tid, CTR_RESP_BYTES, RESP_BYTES_REPLY,
447 knot_pkt_size(pkt));
448 break;
449 case OPERATION_AXFR:
450 case OPERATION_IXFR:
451 knotd_mod_stats_incr(mod, tid, CTR_RESP_BYTES, RESP_BYTES_TRANSFER,
452 knot_pkt_size(pkt));
453 break;
454 default:
455 knotd_mod_stats_incr(mod, tid, CTR_RESP_BYTES, RESP_BYTES_OTHER,
456 knot_pkt_size(pkt));
457 break;
458 }
459 }
460
461 // Get the extended response code.
462 uint16_t rcode = qdata->rcode;
463 if (qdata->rcode_tsig != KNOT_RCODE_NOERROR) {
464 rcode = qdata->rcode_tsig;
465 }
466
467 // Count the response code.
468 if (stats->rcode && state != KNOTD_STATE_NOOP) {
469 if (xfr_packets <= 1 || rcode != KNOT_RCODE_NOERROR) {
470 if (xfr_packets > 1) {
471 assert(rcode != KNOT_RCODE_NOERROR);
472 // Ignore the leading XFR message NOERROR.
473 knotd_mod_stats_decr(mod, tid, CTR_RCODE,
474 KNOT_RCODE_NOERROR, 1);
475 }
476
477 if (qdata->rcode_tsig == KNOT_RCODE_BADSIG) {
478 knotd_mod_stats_incr(mod, tid, CTR_RCODE, RCODE_BADSIG, 1);
479 } else {
480 knotd_mod_stats_incr(mod, tid, CTR_RCODE,
481 MIN(rcode, RCODE_OTHER), 1);
482 }
483 }
484 }
485
486 // Return if non-first transfer message.
487 if (xfr_packets > 1) {
488 return state;
489 }
490
491 // Count the server opearation.
492 if (stats->operation) {
493 knotd_mod_stats_incr(mod, tid, CTR_OPERATION, operation, 1);
494 }
495
496 // Count the request protocol.
497 if (stats->protocol) {
498 bool xdp = qdata->params->xdp_msg != NULL;
499 if (knotd_qdata_remote_addr(qdata)->ss_family == AF_INET) {
500 if (qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE) {
501 if (xdp) {
502 knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
503 PROTOCOL_UDP4_XDP, 1);
504 } else {
505 knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
506 PROTOCOL_UDP4, 1);
507 }
508 } else {
509 if (xdp) {
510 knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
511 PROTOCOL_TCP4_XDP, 1);
512 } else {
513 knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
514 PROTOCOL_TCP4, 1);
515 }
516 }
517 } else {
518 if (qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE) {
519 if (xdp) {
520 knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
521 PROTOCOL_UDP6_XDP, 1);
522 } else {
523 knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
524 PROTOCOL_UDP6, 1);
525 }
526 } else {
527 if (xdp) {
528 knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
529 PROTOCOL_TCP6_XDP, 1);
530 } else {
531 knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
532 PROTOCOL_TCP6, 1);
533 }
534 }
535 }
536 }
537
538 // Count EDNS occurrences.
539 if (stats->edns) {
540 if (knot_pkt_has_edns(qdata->query)) {
541 knotd_mod_stats_incr(mod, tid, CTR_EDNS, EDNS_REQ, 1);
542 }
543 if (knot_pkt_has_edns(pkt) && state != KNOTD_STATE_NOOP) {
544 knotd_mod_stats_incr(mod, tid, CTR_EDNS, EDNS_RESP, 1);
545 }
546 }
547
548 // Count interesting message header flags.
549 if (stats->flag) {
550 if (state != KNOTD_STATE_NOOP && knot_wire_get_tc(pkt->wire)) {
551 knotd_mod_stats_incr(mod, tid, CTR_FLAG, FLAG_TC, 1);
552 }
553 if (knot_pkt_has_dnssec(pkt)) {
554 knotd_mod_stats_incr(mod, tid, CTR_FLAG, FLAG_DO, 1);
555 }
556 }
557
558 // Count EDNS options.
559 if (stats->req_eopt) {
560 incr_edns_option(mod, tid, qdata->query, CTR_REQ_EOPT);
561 }
562 if (stats->resp_eopt) {
563 incr_edns_option(mod, tid, pkt, CTR_RESP_EOPT);
564 }
565
566 // Return if not query operation.
567 if (operation != OPERATION_QUERY) {
568 return state;
569 }
570
571 // Count NODATA reply (RFC 2308, Section 2.2).
572 if (stats->nodata && rcode == KNOT_RCODE_NOERROR && state != KNOTD_STATE_NOOP &&
573 knot_wire_get_ancount(pkt->wire) == 0 && !knot_wire_get_tc(pkt->wire) &&
574 (knot_wire_get_nscount(pkt->wire) == 0 ||
575 knot_pkt_rr(knot_pkt_section(pkt, KNOT_AUTHORITY), 0)->type == KNOT_RRTYPE_SOA)) {
576 switch (knot_pkt_qtype(qdata->query)) {
577 case KNOT_RRTYPE_A:
578 knotd_mod_stats_incr(mod, tid, CTR_NODATA, NODATA_A, 1);
579 break;
580 case KNOT_RRTYPE_AAAA:
581 knotd_mod_stats_incr(mod, tid, CTR_NODATA, NODATA_AAAA, 1);
582 break;
583 default:
584 knotd_mod_stats_incr(mod, tid, CTR_NODATA, NODATA_OTHER, 1);
585 break;
586 }
587 }
588
589 // Count the query type.
590 if (stats->qtype) {
591 uint16_t qtype = knot_pkt_qtype(qdata->query);
592
593 uint16_t idx;
594 switch (qtype) {
595 case QTYPE_MIN1 ... QTYPE_MAX1: idx = qtype; break;
596 case QTYPE_MIN2 ... QTYPE_MAX2: idx = qtype - QTYPE_SHIFT2; break;
597 case QTYPE_MIN3 ... QTYPE_MAX3: idx = qtype - QTYPE_SHIFT3; break;
598 default: idx = QTYPE_OTHER; break;
599 }
600
601 knotd_mod_stats_incr(mod, tid, CTR_QTYPE, idx, 1);
602 }
603
604 // Count the query size.
605 if (stats->qsize) {
606 uint64_t idx = knot_pkt_size(qdata->query) / BUCKET_SIZE;
607 knotd_mod_stats_incr(mod, tid, CTR_QSIZE, MIN(idx, QSIZE_MAX_IDX), 1);
608 }
609
610 // Count the reply size.
611 if (stats->rsize && state != KNOTD_STATE_NOOP) {
612 uint64_t idx = knot_pkt_size(pkt) / BUCKET_SIZE;
613 knotd_mod_stats_incr(mod, tid, CTR_RSIZE, MIN(idx, RSIZE_MAX_IDX), 1);
614 }
615
616 return state;
617 }
618
stats_load(knotd_mod_t * mod)619 int stats_load(knotd_mod_t *mod)
620 {
621 stats_t *stats = calloc(1, sizeof(*stats));
622 if (stats == NULL) {
623 return KNOT_ENOMEM;
624 }
625
626 for (const ctr_desc_t *desc = ctr_descs; desc->conf_name != NULL; desc++) {
627 knotd_conf_t conf = knotd_conf_mod(mod, desc->conf_name);
628 bool enabled = conf.single.boolean;
629
630 // Initialize corresponding configuration item.
631 *(bool *)((uint8_t *)stats + desc->conf_offset) = enabled;
632
633 int ret = knotd_mod_stats_add(mod, enabled ? desc->conf_name + 1 : NULL,
634 enabled ? desc->count : 1, desc->fcn);
635 if (ret != KNOT_EOK) {
636 free(stats);
637 return ret;
638 }
639 }
640
641 knotd_mod_ctx_set(mod, stats);
642
643 return knotd_mod_hook(mod, KNOTD_STAGE_END, update_counters);
644 }
645
stats_unload(knotd_mod_t * mod)646 void stats_unload(knotd_mod_t *mod)
647 {
648 free(knotd_mod_ctx(mod));
649 }
650
651 KNOTD_MOD_API(stats, KNOTD_MOD_FLAG_SCOPE_ANY | KNOTD_MOD_FLAG_OPT_CONF,
652 stats_load, stats_unload, stats_conf, NULL);
653