Compare commits

...
Author SHA1 Message Date
W.C.A. Wijngaards c66c86e95a - add-prometheus-metrics, fix unit test. 2026-04-02 10:41:33 +02:00
W.C.A. Wijngaards b2590c2022 - add-prometheus-metrics, set metrics to type 'counter', for number of query
metrics, and do not reset the stats. There is a warning when
  statistics-cumulative has the wrong value. But the stats are not reset
  from the metrics endpoint regardless. The contrib/metrics.awk script
  is also updated, and the documentation recommends the cumulative setting.
2026-04-02 10:37:56 +02:00
W.C.A. Wijngaards 286494366c - add-prometheus-metrics, fix shm stats to also use the mem info function. 2026-03-30 16:23:25 +02:00
W.C.A. Wijngaards 53724c9e7b Merge branch 'master' into add-prometheus-metrics 2026-03-30 16:19:11 +02:00
W.C.A. Wijngaards 9a01a32176 - add-prometheus-metrics, fix clang analyzer warning about allocation size. 2026-03-27 14:05:14 +01:00
W.C.A. Wijngaards 08e4560065 Merge branch 'master' into add-prometheus-metrics 2026-03-27 11:48:16 +01:00
W.C.A. Wijngaards 86d4414a22 - add-prometheus-metrics, allow interface names, and ip@port notation.
add documentation in man page, and in the example config file.
2026-03-27 11:47:20 +01:00
W.C.A. Wijngaards 1f6e67e653 Merge branch 'master' into add-prometheus-metrics 2026-03-04 16:41:54 +01:00
W.C.A. Wijngaards b96d80a5d3 - add-prometheus-metrics, consolidate mem stats. 2026-03-04 10:02:09 +01:00
W.C.A. Wijngaards 0b42d5ba13 Merge branch 'master' into add-prometheus-metrics 2026-03-03 17:51:03 +01:00
W.C.A. Wijngaards efc0c1e990 - add-prometheus-metrics, implement further test checks. 2026-03-03 16:54:48 +01:00
W.C.A. Wijngaards b0b8a3f144 - add-prometheus-metrics, fix by rcode text. 2026-03-03 16:24:24 +01:00
W.C.A. Wijngaards 172e4a9edb Merge branch 'master' into add-prometheus-metrics 2026-03-03 16:07:29 +01:00
W.C.A. Wijngaards 2c5e96f86c - add-prometheus-metrics, print extended statistics. 2026-03-03 16:07:03 +01:00
W.C.A. Wijngaards d3902c0e70 Merge branch 'master' into add-prometheus-metrics 2026-02-27 17:12:44 +01:00
W.C.A. Wijngaards 35e3dcb701 - add-prometheus-metrics, print histogram of reply time stats. 2026-02-27 17:12:09 +01:00
W.C.A. Wijngaards 415a83e9d5 - add-prometheus-metrics, print memory usage stats. 2026-02-27 17:05:44 +01:00
W.C.A. Wijngaards 19751ec179 - add-prometheus-metrics, fix testbound link for metrics functions. 2026-02-06 17:05:45 +01:00
W.C.A. Wijngaards 6468917fb8 Merge branch 'master' into add-prometheus-metrics 2026-02-06 16:26:38 +01:00
W.C.A. Wijngaards 747ce6ba47 - add-prometheus-metrics, print per-thread stats and the summed totals. 2026-02-06 16:26:17 +01:00
W.C.A. Wijngaards 831a805cd4 Merge branch 'master' into add-prometheus-metrics 2026-02-03 15:22:08 +01:00
W.C.A. Wijngaards a00cac5094 - add-prometheus-metrics, testbound implementation, and if metrics are disabled
there is no http service created.
2026-02-03 12:01:36 +01:00
W.C.A. Wijngaards 705d180096 - add-prometheus-metrics, respect stat-cumulative option, print num queries. 2026-02-03 11:48:55 +01:00
W.C.A. Wijngaards ece48de2bc - add-prometheus-metrics, add metrics-enable, metrics-interface, metrics-port,
and metrics-path options.
2026-01-30 16:20:23 +01:00
W.C.A. Wijngaards 90c2ca4c55 - add-prometheus-metrics, adjust configure.ac, Makefile.in and add
daemon/metrics.c and daemon/metrics.h for statistics in prometheus metrics.
2026-01-30 09:38:00 +01:00
31 changed files with 1724 additions and 99 deletions
+3 -2
View File
@@ -187,9 +187,9 @@ unittcpreuse.lo unitdoq.lo unitinfra.lo
UNITTEST_OBJ_LINK=$(UNITTEST_OBJ) worker_cb.lo $(COMMON_OBJ) $(SLDNS_OBJ) \
$(COMPAT_OBJ)
DAEMON_SRC=daemon/acl_list.c daemon/cachedump.c daemon/daemon.c \
daemon/remote.c daemon/stats.c daemon/unbound.c daemon/worker.c @WIN_DAEMON_SRC@
daemon/remote.c daemon/stats.c daemon/metrics.c daemon/unbound.c daemon/worker.c @WIN_DAEMON_SRC@
DAEMON_OBJ=acl_list.lo cachedump.lo daemon.lo \
shm_main.lo remote.lo stats.lo unbound.lo \
shm_main.lo remote.lo stats.lo metrics.lo unbound.lo \
worker.lo @WIN_DAEMON_OBJ@
DAEMON_OBJ_LINK=$(DAEMON_OBJ) $(COMMON_OBJ_ALL_SYMBOLS) $(SLDNS_OBJ) \
$(COMPAT_OBJ) @WIN_DAEMON_OBJ_LINK@
@@ -721,6 +721,7 @@ depend:
ipset.lo ipset.o: $(srcdir)/ipset/ipset.c
doqclient.lo doqclient.o: $(srcdir)/testcode/doqclient.c
unitdoq.lo unitdoq.o: $(srcdir)/testcode/unitdoq.c
metrics.lo metrics.o: $(srcdir)/daemon/metrics.c
# Dependencies
dns.lo dns.o: $(srcdir)/services/cache/dns.c config.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/util/log.h \
+10
View File
@@ -237,6 +237,9 @@
/* Define to 1 if you have the <event.h> header file. */
#undef HAVE_EVENT_H
/* Define to 1 if you have the `evhttp_free' function. */
#undef HAVE_EVHTTP_FREE
/* Define to 1 if you have the `EVP_aes_256_cbc' function. */
#undef HAVE_EVP_AES_256_CBC
@@ -986,6 +989,9 @@
/* define this to enable debug checks. */
#undef UNBOUND_DEBUG
/* Define the default metrics HTTP endpoint port. */
#undef UNBOUND_METRICS_PORT
/* Define to 1 to use cachedb support */
#undef USE_CACHEDB
@@ -1033,6 +1039,10 @@
distributions) the use of non-ephemeral ports. */
#undef USE_LINUX_IP_LOCAL_PORT_RANGE
/* Define this to expose Unbound statistics via a prometheus metrics HTTP
endpoint. */
#undef USE_METRICS
/* Define if you want to use internal select based events */
#undef USE_MINI_EVENT
Vendored
+27
View File
@@ -22169,6 +22169,31 @@ else $as_nop
fi
printf "%s\n" "#define HAVE_DECL_EVSIGNAL_ASSIGN $ac_have_decl" >>confdefs.h
# prometheus metrics depend on libevent 2.0 and later, and is therefore
# only enabled when the required version is found and used
for ac_func in evhttp_free
do :
ac_fn_c_check_func "$LINENO" "evhttp_free" "ac_cv_func_evhttp_free"
if test "x$ac_cv_func_evhttp_free" = xyes
then :
printf "%s\n" "#define HAVE_EVHTTP_FREE 1" >>confdefs.h
printf "%s\n" "#define USE_METRICS /**/" >>confdefs.h
printf "%s\n" "#define UNBOUND_METRICS_PORT 9101" >>confdefs.h
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: disabling prometheus metrics" >&5
printf "%s\n" "$as_me: disabling prometheus metrics" >&6;}
fi
done
PC_LIBEVENT_DEPENDENCY="libevent"
if test -n "$BAK_LDFLAGS_SET"; then
@@ -22178,6 +22203,8 @@ else
printf "%s\n" "#define USE_MINI_EVENT 1" >>confdefs.h
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: Prometheus metrics are disabled with the builtin libevent alternative" >&5
printf "%s\n" "$as_me: Prometheus metrics are disabled with the builtin libevent alternative" >&6;}
fi
# check for libexpat
+9
View File
@@ -1498,6 +1498,14 @@ large outgoing port ranges. ])
# include "event2/event.h"
#endif
])
# prometheus metrics depend on libevent 2.0 and later, and is therefore
# only enabled when the required version is found and used
AC_CHECK_FUNCS([evhttp_free], [
AC_DEFINE_UNQUOTED([USE_METRICS], [], [Define this to expose Unbound statistics via a prometheus metrics HTTP endpoint.])
AC_DEFINE_UNQUOTED([UNBOUND_METRICS_PORT], [9101], [Define the default metrics HTTP endpoint port.])
], [
AC_MSG_NOTICE([disabling prometheus metrics])
])
PC_LIBEVENT_DEPENDENCY="libevent"
AC_SUBST(PC_LIBEVENT_DEPENDENCY)
if test -n "$BAK_LDFLAGS_SET"; then
@@ -1505,6 +1513,7 @@ large outgoing port ranges. ])
fi
else
AC_DEFINE(USE_MINI_EVENT, 1, [Define if you want to use internal select based events])
AC_MSG_NOTICE([Prometheus metrics are disabled with the builtin libevent alternative])
fi
# check for libexpat
+8 -8
View File
@@ -2,7 +2,7 @@
# and output prometheus metrics style output.
# use these options:
# server: extended-statistics: yes
# statistics-cumulative: no
# statistics-cumulative: yes
# statistics-interval: 0
# remote-control: control-enable: yes
# Can use it like unbound-control stats | awk -f "metrics.awk"
@@ -17,7 +17,7 @@ BEGIN {
# print the output metrics
END {
print "# HELP unbound_hits_queries Unbound DNS traffic and cache hits"
print "# TYPE unbound_hits_queries gauge"
print "# TYPE unbound_hits_queries counter"
print "unbound_hits_queries{type=\"total.num.queries\"} " val["total.num.queries"];
for (x=0; x<99; x++) {
if(val["thread" $x ".num.queries"] != "") {
@@ -70,7 +70,7 @@ END {
print ""
print "# HELP unbound_by_type_queries Unbound DNS queries by type"
print "# TYPE unbound_by_type_queries gauge"
print "# TYPE unbound_by_type_queries counter"
for(x in val) {
if(x ~ /^num.query.type./) {
if(val[x] != "") {
@@ -82,7 +82,7 @@ END {
print ""
print "# HELP unbound_by_class_queries Unbound DNS queries by class"
print "# TYPE unbound_by_class_queries gauge"
print "# TYPE unbound_by_class_queries counter"
for(x in val) {
if(x ~ /^num.query.class./) {
if(val[x] != "") {
@@ -94,7 +94,7 @@ END {
print ""
print "# HELP unbound_by_opcode_queries Unbound DNS queries by opcode"
print "# TYPE unbound_by_opcode_queries gauge"
print "# TYPE unbound_by_opcode_queries counter"
for(x in val) {
if(x ~ /^num.query.opcode./) {
if(val[x] != "") {
@@ -106,7 +106,7 @@ END {
print ""
print "# HELP unbound_by_rcode_queries Unbound DNS answers by rcode"
print "# TYPE unbound_by_rcode_queries gauge"
print "# TYPE unbound_by_rcode_queries counter"
for(x in val) {
if(x ~ /^num.answer.rcode./) {
if(val[x] != "") {
@@ -118,7 +118,7 @@ END {
print ""
print "# HELP unbound_by_flags_queries Unbound DNS queries by flags"
print "# TYPE unbound_by_flags_queries gauge"
print "# TYPE unbound_by_flags_queries counter"
for(x in val) {
if(x ~ /^num.query.flags./) {
if(val[x] != "") {
@@ -136,7 +136,7 @@ END {
print ""
print "# HELP unbound_histogram_seconds Unbound DNS histogram of reply time"
print "# TYPE unbound_histogram_seconds gauge"
print "# TYPE unbound_histogram_seconds counter"
print "unbound_histogram_seconds{bucket=\"000000.000000.to.000000.000001\"} " val["histogram.000000.000000.to.000000.000001"];
print "unbound_histogram_seconds{bucket=\"000000.000001.to.000000.000002\"} " val["histogram.000000.000001.to.000000.000002"];
print "unbound_histogram_seconds{bucket=\"000000.000002.to.000000.000004\"} " val["histogram.000000.000002.to.000000.000004"];
+38
View File
@@ -69,6 +69,7 @@
#include "daemon/daemon.h"
#include "daemon/worker.h"
#include "daemon/remote.h"
#include "daemon/metrics.h"
#include "daemon/acl_list.h"
#include "util/log.h"
#include "util/config_file.h"
@@ -547,10 +548,25 @@ daemon_init(void)
if(gettimeofday(&daemon->time_boot, NULL) < 0)
log_err("gettimeofday: %s", strerror(errno));
daemon->time_last_stat = daemon->time_boot;
#ifdef USE_METRICS
if(!(daemon->metrics = daemon_metrics_create())) {
acl_list_delete(daemon->acl_interface);
acl_list_delete(daemon->acl);
tcl_list_delete(daemon->tcl);
edns_known_options_delete(daemon->env);
free(daemon->env);
free(daemon);
return NULL;
}
daemon->metrics_port = -1;
#endif /* USE_METRICS */
if((daemon->env->auth_zones = auth_zones_create()) == 0) {
acl_list_delete(daemon->acl_interface);
acl_list_delete(daemon->acl);
tcl_list_delete(daemon->tcl);
#ifdef USE_METRICS
daemon_metrics_delete(daemon->metrics);
#endif
edns_known_options_delete(daemon->env);
free(daemon->env);
free(daemon);
@@ -561,6 +577,9 @@ daemon_init(void)
acl_list_delete(daemon->acl_interface);
acl_list_delete(daemon->acl);
tcl_list_delete(daemon->tcl);
#ifdef USE_METRICS
daemon_metrics_delete(daemon->metrics);
#endif
edns_known_options_delete(daemon->env);
free(daemon->env);
free(daemon);
@@ -686,6 +705,19 @@ daemon_open_shared_ports(struct daemon* daemon)
return 0;
daemon->rc_port = daemon->cfg->control_port;
}
#ifdef USE_METRICS
if(!daemon->cfg->metrics_enable && daemon->metrics_port != -1) {
daemon_metrics_close_ports(daemon->metrics);
daemon->metrics_port = -1;
}
if(daemon->cfg->metrics_enable &&
daemon->cfg->metrics_port != daemon->metrics_port) {
daemon_metrics_close_ports(daemon->metrics);
if(!daemon_metrics_open_ports(daemon->metrics, daemon->cfg))
return 0;
daemon->metrics_port = daemon->cfg->metrics_port;
}
#endif /* USE_METRICS */
return 1;
}
@@ -1171,6 +1203,9 @@ daemon_cleanup(struct daemon* daemon)
auth_zones_cleanup(daemon->env->auth_zones);
/* key cache is cleared by module deinit during next daemon_fork() */
daemon_remote_clear(daemon->rc);
#ifdef USE_METRICS
daemon_metrics_detach(daemon->metrics);
#endif
if(daemon->fast_reload_thread)
fast_reload_thread_stop(daemon->fast_reload_thread);
if(daemon->fast_reload_printq_list)
@@ -1213,6 +1248,9 @@ daemon_delete(struct daemon* daemon)
modstack_call_destartup(&daemon->mods, daemon->env);
modstack_free(&daemon->mods);
daemon_remote_delete(daemon->rc);
#ifdef USE_METRICS
daemon_metrics_delete(daemon->metrics);
#endif
for(i = 0; i < daemon->num_ports; i++)
listening_ports_free(daemon->ports[i]);
free(daemon->ports);
+5
View File
@@ -56,6 +56,7 @@ struct local_zones;
struct views;
struct ub_randstate;
struct daemon_remote;
struct daemon_metrics;
struct respip_set;
struct shm_main_info;
struct doq_table;
@@ -99,6 +100,10 @@ struct daemon {
struct listen_port* rc_ports;
/** remote control connections management (for first worker) */
struct daemon_remote* rc;
/** port number for metrics that has ports opened. */
int metrics_port;
/** metrics endpoint connections management (for first worker) */
struct daemon_metrics* metrics;
/** ssl context for listening to dnstcp over ssl */
void* listen_dot_sslctx;
/** ssl context for connecting to dnstcp over ssl */
+877
View File
@@ -0,0 +1,877 @@
/*
* daemon/metrics.c - prometheus metrics endpoint.
*
* Copyright (c) 2026, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* The statistics output provides metrics to prometheus.
*/
#include "config.h"
#include "daemon/metrics.h"
#include "daemon/daemon.h"
#include "daemon/worker.h"
#include "daemon/stats.h"
#include "util/config_file.h"
#include "util/net_help.h"
#include "util/ub_event.h"
#include "util/timeval_func.h"
#include "services/listen_dnsport.h"
#include "services/cache/rrset.h"
#include "services/rpz.h"
#include "sldns/parseutil.h"
#include "sldns/wire2str.h"
/* If there is no metrics enabled, do not add the code. */
#ifdef USE_METRICS
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <event2/event.h>
#include <event2/http.h>
#include <event2/buffer.h>
/** The prefix for the unbound statistics. */
#define METRICS_PREFIX "unbound_"
/** The callback that handles a metrics http request. */
static void metrics_http_callback(struct evhttp_request *req, void *p);
struct daemon_metrics*
daemon_metrics_create(void)
{
struct daemon_metrics* metrics = (struct daemon_metrics*)calloc(
sizeof(*metrics), 1);
if(!metrics) {
log_err("out of memory");
return NULL;
}
return metrics;
}
void
daemon_metrics_delete(struct daemon_metrics* metrics)
{
if(!metrics) return;
daemon_metrics_detach(metrics);
daemon_metrics_close_ports(metrics);
free(metrics);
}
void
daemon_metrics_close_ports(struct daemon_metrics* metrics)
{
struct metrics_acceptlist *h, *nh;
if(!metrics) return;
/* close listen sockets */
h = metrics->accept_list;
while(h) {
nh = h->next;
close(h->accept_fd);
free(h->ident);
free(h);
h = nh;
}
metrics->accept_list = NULL;
}
void
daemon_metrics_detach(struct daemon_metrics* metrics)
{
if(!metrics) return;
if (metrics->http_server) {
evhttp_free(metrics->http_server);
metrics->http_server = NULL;
}
}
/**
* Add and open a new metrics port
* @param metrics: metrics with result list.
* @param cfg: config options.
* @param ip: ip str
* @param nr: port nr
* @param noproto_is_err: if lack of protocol support is an error.
* @return false on failure.
*/
static int
metrics_add_open(struct daemon_metrics* metrics, struct config_file* cfg,
const char* ip, int nr, int noproto_is_err)
{
struct addrinfo hints;
struct addrinfo* res;
struct metrics_acceptlist* hl;
int noproto = 0;
int fd, r;
char port[15];
snprintf(port, sizeof(port), "%d", nr);
port[sizeof(port)-1]=0;
memset(&hints, 0, sizeof(hints));
assert(ip);
if(ip[0] == '/') {
/* This looks like a local socket */
fd = create_local_accept_sock(ip, &noproto, cfg->use_systemd);
/*
* Change socket ownership and permissions so users other
* than root can access it provided they are in the same
* group as the user we run as.
*/
if(fd != -1) {
#ifdef HAVE_CHOWN
if(chmod(ip, (mode_t)(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) {
verbose(VERB_QUERY, "cannot chmod metrics socket %s: %s", ip, strerror(errno));
}
if (cfg->username && cfg->username[0] &&
cfg_uid != (uid_t)-1) {
if(chown(ip, cfg_uid, cfg_gid) == -1)
verbose(VERB_QUERY, "cannot chown metrics socket %u.%u %s: %s",
(unsigned)cfg_uid, (unsigned)cfg_gid,
ip, strerror(errno));
}
#else
(void)cfg;
#endif
}
} else {
char* s = strchr(ip, '@');
char newif[128];
if(s) {
/* override port with ifspec@port */
int portnr;
if((size_t)(s-ip) >= sizeof(newif)) {
log_err("ifname too long: %s", ip);
return -1;
}
portnr = atoi(s+1);
if(portnr < 0 || 0 == portnr || portnr > 65535) {
log_err("invalid portnumber in metrics-interface: %s", ip);
return -1;
}
(void)strlcpy(newif, ip, sizeof(newif));
newif[s-ip] = 0;
ip = newif;
snprintf(port, sizeof(port), "%d", portnr);
port[sizeof(port)-1]=0;
}
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
/* if we had no interface ip name, "default" is what we
* would do getaddrinfo for. */
if((r = getaddrinfo(ip, port, &hints, &res)) != 0 || !res) {
#ifdef USE_WINSOCK
if(!noproto_is_err && r == EAI_NONAME) {
/* tried to lookup the address as name */
return 1; /* return success, but do nothing */
}
#endif /* USE_WINSOCK */
log_err("metrics interface %s:%s getaddrinfo: %s %s",
ip, port, gai_strerror(r),
#ifdef EAI_SYSTEM
r==EAI_SYSTEM?(char*)strerror(errno):""
#else
""
#endif
);
return 0;
}
/* open fd */
fd = create_tcp_accept_sock(res, 1, &noproto, 0,
cfg->ip_transparent, 0, 0, cfg->ip_freebind,
cfg->use_systemd, cfg->ip_dscp, "metrics");
freeaddrinfo(res);
}
if(fd == -1 && noproto) {
if(!noproto_is_err)
return 1; /* return success, but do nothing */
log_err("cannot open metrics interface %s %d : "
"protocol not supported", ip, nr);
return 0;
}
if(fd == -1) {
log_err("cannot open metrics interface %s %d", ip, nr);
return 0;
}
/* alloc */
hl = (struct metrics_acceptlist*)calloc(1, sizeof(*hl));
if(!hl) {
sock_close(fd);
log_err("out of memory");
return 0;
}
hl->metrics = metrics;
hl->ident = strdup(ip);
if(!hl->ident) {
log_err("out of memory");
sock_close(fd);
free(hl);
return 0;
}
hl->next = metrics->accept_list;
metrics->accept_list = hl;
hl->accept_fd = fd;
return 1;
}
int
daemon_metrics_open_ports(struct daemon_metrics* metrics,
struct config_file* cfg)
{
assert(cfg->metrics_enable);
if(!cfg->stat_cumulative)
log_warn("metrics-enable: yes but statistics-cumulative: no, access to control command 'stats' would reset the stat counters, perhaps set 'statistics-cumulative: yes'.");
if(cfg->metrics_ifs.first) {
char** rcif = NULL;
int i, num_rcif = 0;
if(!resolve_interface_names(NULL, 0, cfg->metrics_ifs.first,
&rcif, &num_rcif)) {
return 0;
}
for(i=0; i<num_rcif; i++) {
if(!metrics_add_open(metrics, cfg, rcif[i],
cfg->metrics_port, 1)) {
config_del_strarray(rcif, num_rcif);
return 0;
}
}
config_del_strarray(rcif, num_rcif);
} else {
/* defaults */
if(cfg->do_ip6 && !metrics_add_open(metrics, cfg, "::1",
cfg->metrics_port, 0)) {
return 0;
}
if(cfg->do_ip4 &&
!metrics_add_open(metrics, cfg, "127.0.0.1",
cfg->metrics_port, 1)) {
return 0;
}
}
return 1;
}
int
daemon_metrics_attach(struct daemon_metrics* metrics, struct worker* worker)
{
int fd;
struct metrics_acceptlist* p;
if(!metrics) return 1;
metrics->worker = worker;
if(!metrics->accept_list)
return 1;
metrics->http_server = evhttp_new(ub_libevent_get_event_base(
comm_base_internal(worker->base)));
if(!metrics->http_server) {
log_err("out of memory, evhttp_new failed");
return 0;
}
for(p = metrics->accept_list; p; p = p->next) {
fd = p->accept_fd;
if (evhttp_accept_socket(metrics->http_server, fd)) {
log_err("metrics: cannot set http server to accept socket");
return 0;
}
/* only handle requests to metrics_path, anything else returns 404 */
evhttp_set_cb(metrics->http_server,
worker->daemon->cfg->metrics_path,
metrics_http_callback, p);
/* evhttp_set_gencb(metrics->http_server, metrics_http_callback_generic, p); */
}
return 1;
}
/* Add help and type printout of a metric. */
static void
print_metric_help_and_type(struct evbuffer *buf, char *prefix, char *name,
char *help, char *type)
{
evbuffer_add_printf(buf, "# HELP %s%s %s\n# TYPE %s%s %s\n",
prefix, name, help, prefix, name, type);
}
/* print help and type for main list of metrics */
static int
metrics_print_types(struct evbuffer *reply)
{
char* prefix = METRICS_PREFIX;
print_metric_help_and_type(reply, prefix, "hits_queries",
"Unbound DNS traffic and cache hits", "counter");
print_metric_help_and_type(reply, prefix, "queue_queries",
"Unbound requestlist size", "gauge");
print_metric_help_and_type(reply, prefix, "recursion_time",
"Unbound recursion time, in seconds", "gauge");
print_metric_help_and_type(reply, prefix, "query_queue_time",
"Unbound query queue time, in msec", "gauge");
print_metric_help_and_type(reply, prefix, "socket_count",
"Unbound socket count", "gauge");
return 1;
}
/* metrics print of stat block */
static int
metrics_print_stats(struct evbuffer* reply, const char* nm,
struct ub_stats_info* s)
{
char* prefix = METRICS_PREFIX;
struct timeval sumwait, avg;
/* print to reply buffer the stat for prefix mt
* of type nm.snm and long long output svar. */
#define INFO_STATS(mt, snm, svar) \
evbuffer_add_printf(reply, \
"%s" mt "{type=\"%s." snm "\"} " ARG_LL "d\n", \
prefix, nm, (long long)(svar))
INFO_STATS("hits_queries", "num.queries", s->svr.num_queries);
INFO_STATS("hits_queries", "num.queries_ip_ratelimited",
s->svr.num_queries_ip_ratelimited);
INFO_STATS("hits_queries", "num.queries_cookie_valid",
s->svr.num_queries_cookie_valid);
INFO_STATS("hits_queries", "num.queries_cookie_client",
s->svr.num_queries_cookie_client);
INFO_STATS("hits_queries", "num.queries_cookie_invalid",
s->svr.num_queries_cookie_invalid);
INFO_STATS("hits_queries", "num.queries_discard_timeout",
s->svr.num_queries_discard_timeout);
INFO_STATS("hits_queries", "num.queries_replyaddr_limit",
s->svr.num_queries_replyaddr_limit);
INFO_STATS("hits_queries", "num.queries_wait_limit",
s->svr.num_queries_wait_limit);
INFO_STATS("hits_queries", "num.cachehits",
s->svr.num_queries - s->svr.num_queries_missed_cache);
INFO_STATS("hits_queries", "num.cachemiss",
s->svr.num_queries_missed_cache);
INFO_STATS("hits_queries", "num.prefetch",
s->svr.num_queries_prefetch);
INFO_STATS("hits_queries", "num.queries_timed_out",
s->svr.num_queries_timed_out);
INFO_STATS("hits_queries", "num.expired", s->svr.ans_expired);
INFO_STATS("hits_queries", "num.recursivereplies",
s->mesh_replies_sent);
#ifdef USE_DNSCRYPT
INFO_STATS("hits_queries", "num.dnscrypt.crypted",
s->svr.num_query_dnscrypt_crypted);
INFO_STATS("hits_queries", "num.dnscrypt.cert",
s->svr.num_query_dnscrypt_cert);
INFO_STATS("hits_queries", "num.dnscrypt.cleartext",
s->svr.num_query_dnscrypt_cleartext);
INFO_STATS("hits_queries", "num.dnscrypt.malformed",
s->svr.num_query_dnscrypt_crypted_malformed);
#endif
INFO_STATS("hits_queries", "num.dns_error_reports",
s->svr.num_dns_error_reports);
evbuffer_add_printf(reply,
"%squeue_queries{type=\"%s.requestlist.avg\"} %g\n",
prefix, nm,
(s->svr.num_queries_missed_cache+s->svr.num_queries_prefetch)?
(double)s->svr.sum_query_list_size/
(double)(s->svr.num_queries_missed_cache+
s->svr.num_queries_prefetch) : 0.0);
INFO_STATS("queue_queries", "requestlist.max",
s->svr.max_query_list_size);
INFO_STATS("queue_queries", "requestlist.overwritten",
s->mesh_jostled);
INFO_STATS("queue_queries", "requestlist.exceeded",
s->mesh_dropped);
INFO_STATS("queue_queries", "requestlist.current.all",
s->mesh_num_states);
INFO_STATS("queue_queries", "requestlist.current.user",
s->mesh_num_reply_states);
INFO_STATS("queue_queries", "requestlist.current.replies",
s->mesh_num_reply_addrs);
sumwait.tv_sec = s->mesh_replies_sum_wait_sec;
sumwait.tv_usec = s->mesh_replies_sum_wait_usec;
timeval_divide(&avg, &sumwait, s->mesh_replies_sent);
evbuffer_add_printf(reply,
"%srecursion_time{type=\"%s.recursion.time.avg\"} " ARG_LL
"d.%6.6d\n", prefix, nm,
(long long)avg.tv_sec, (int)avg.tv_usec);
evbuffer_add_printf(reply,
"%srecursion_time{type=\"%s.recursion.time.median\"} %g\n",
prefix, nm, s->mesh_time_median);
INFO_STATS("query_queue_time", "query.queue_time_us.max",
s->svr.max_query_time_us);
INFO_STATS("socket_count", "tcpusage", s->svr.tcp_accept_usage);
return 1;
}
/* metrics print of thread stats */
static int
metrics_print_thread_stats(struct evbuffer* reply, int i,
struct ub_stats_info* s)
{
char nm[32];
snprintf(nm, sizeof(nm), "thread%d", i);
nm[sizeof(nm)-1]=0;
return metrics_print_stats(reply, nm, s);
}
/* metrics print of uptime stats */
static int
metrics_print_uptime(struct evbuffer* reply, struct worker* worker,
struct timeval* stattime, struct timeval* time_last_stat)
{
char* prefix = METRICS_PREFIX;
struct timeval up, dt;
timeval_subtract(&up, stattime, &worker->daemon->time_boot);
timeval_subtract(&dt, stattime, time_last_stat);
print_metric_help_and_type(reply, prefix, "time_now_seconds",
"Time of the statistics printout, in seconds.", "untyped");
evbuffer_add_printf(reply, "%stime_now_seconds " ARG_LL "d.%6.6u\n",
prefix, (long long)stattime->tv_sec,
(unsigned)stattime->tv_usec);
print_metric_help_and_type(reply, prefix, "time_up_seconds_total",
"Uptime since server boot in seconds.", "counter");
evbuffer_add_printf(reply,
"%stime_up_seconds_total " ARG_LL "d.%6.6u\n",
prefix, (long long)up.tv_sec, (unsigned)up.tv_usec);
print_metric_help_and_type(reply, prefix, "time_elapsed_seconds",
"Time since last statistics printout and "
"reset (by unbound-control stats) in seconds.",
"untyped");
evbuffer_add_printf(reply,
"%stime_elapsed_seconds " ARG_LL "d.%6.6u\n",
prefix, (long long)dt.tv_sec, (unsigned)dt.tv_usec);
return 1;
}
/** metrics print of mem stats */
static int
metrics_print_mem(struct evbuffer* reply, struct worker* worker,
struct ub_stats_info* s)
{
char* prefix = METRICS_PREFIX;
struct ub_mem_stat_info mem;
stats_get_mem_info(worker, &mem);
/* print to reply buffer the stat for prefix mt
* of type snm and long long output svar. */
#define INFO_LL_STATS(mt, snm, svar) \
evbuffer_add_printf(reply, \
"%s" mt "{type=\"" snm "\"} " ARG_LL "d\n", \
prefix, (long long)(svar))
print_metric_help_and_type(reply, prefix, "memory_bytes",
"Unbound memory usage, in bytes", "gauge");
INFO_LL_STATS("memory_bytes", "mem.cache.rrset", mem.rrset);
INFO_LL_STATS("memory_bytes", "mem.cache.message", mem.msg);
INFO_LL_STATS("memory_bytes", "mem.mod.iterator", mem.iter);
INFO_LL_STATS("memory_bytes", "mem.mod.validator", mem.val);
INFO_LL_STATS("memory_bytes", "mem.mod.respip", mem.respip);
#ifdef CLIENT_SUBNET
INFO_LL_STATS("memory_bytes", "mem.mod.subnet", mem.subnet);
#endif /* CLIENT_SUBNET */
#ifdef USE_IPSECMOD
INFO_LL_STATS("memory_bytes", "mem.mod.ipsecmod", mem.ipsecmod);
#endif /* USE_IPSECMOD */
#ifdef USE_DNSCRYPT
INFO_LL_STATS("memory_bytes", "mem.cache.dnscrypt_shared_secret",
mem.dnscrypt_shared_secret);
INFO_LL_STATS("memory_bytes", "mem.cache.dnscrypt_nonce",
mem.dnscrypt_nonce);
#endif /* USE_DNSCRYPT */
#ifdef WITH_DYNLIBMODULE
INFO_LL_STATS("memory_bytes", "mem.mod.dynlibmod", mem.dynlib);
#endif /* WITH_DYNLIBMODULE */
INFO_LL_STATS("memory_bytes", "mem.streamwait",
s->svr.mem_stream_wait);
INFO_LL_STATS("memory_bytes", "mem.http.query_buffer",
s->svr.mem_http2_query_buffer);
INFO_LL_STATS("memory_bytes", "mem.http.response_buffer",
s->svr.mem_http2_response_buffer);
#ifdef HAVE_NGTCP2
INFO_LL_STATS("memory_bytes", "mem.quic", s->svr.mem_quic);
#endif /* HAVE_NGTCP2 */
return 1;
}
/* metrics print of histogram */
static int
metrics_print_hist(struct evbuffer* reply, struct ub_stats_info* s)
{
char* prefix = METRICS_PREFIX;
struct timehist* hist;
size_t i;
print_metric_help_and_type(reply, prefix, "histogram_seconds",
"Unbound DNS histogram of reply time", "counter");
hist = timehist_setup();
if(!hist) {
log_err("out of memory");
return 0;
}
timehist_import(hist, s->svr.hist, NUM_BUCKETS_HIST);
for(i=0; i<hist->num; i++) {
evbuffer_add_printf(reply, "%shistogram_seconds"
"{bucket=\"%6.6d.%6.6d.to.%6.6d.%6.6d\"} %lu\n",
prefix,
(int)hist->buckets[i].lower.tv_sec,
(int)hist->buckets[i].lower.tv_usec,
(int)hist->buckets[i].upper.tv_sec,
(int)hist->buckets[i].upper.tv_usec,
(unsigned long)hist->buckets[i].count);
}
timehist_delete(hist);
return 1;
}
/* metrics print of extended stats */
static int
metrics_print_ext(struct evbuffer* reply, struct ub_stats_info* s,
int inhibit_zero)
{
char* prefix = METRICS_PREFIX;
int i;
char nm[32];
const sldns_rr_descriptor* desc;
const sldns_lookup_table* lt;
/* Print stats for metric mt, where type 'sortnm' is "snm" string,
* with value svar. */
#define INFO_EXT_STATS(mt, sortnm, snm, svar) \
evbuffer_add_printf(reply, "%s%s{%s=\"%s\"} " ARG_LL "d\n", \
prefix, mt, sortnm, snm, svar);
/* TYPE */
print_metric_help_and_type(reply, prefix, "by_type_queries",
"Unbound DNS queries by type", "counter");
for(i=0; i<UB_STATS_QTYPE_NUM; i++) {
if(inhibit_zero && s->svr.qtype[i] == 0)
continue;
desc = sldns_rr_descript((uint16_t)i);
if(desc && desc->_name) {
snprintf(nm, sizeof(nm), "%s", desc->_name);
} else if (i == LDNS_RR_TYPE_IXFR) {
snprintf(nm, sizeof(nm), "IXFR");
} else if (i == LDNS_RR_TYPE_AXFR) {
snprintf(nm, sizeof(nm), "AXFR");
} else if (i == LDNS_RR_TYPE_MAILA) {
snprintf(nm, sizeof(nm), "MAILA");
} else if (i == LDNS_RR_TYPE_MAILB) {
snprintf(nm, sizeof(nm), "MAILB");
} else if (i == LDNS_RR_TYPE_ANY) {
snprintf(nm, sizeof(nm), "ANY");
} else {
snprintf(nm, sizeof(nm), "TYPE%d", i);
}
INFO_EXT_STATS("by_type_queries", "type", nm, s->svr.qtype[i]);
}
if(!inhibit_zero || s->svr.qtype_big) {
INFO_EXT_STATS("by_type_queries", "type", "other",
s->svr.qtype_big);
}
/* CLASS */
print_metric_help_and_type(reply, prefix, "by_class_queries",
"Unbound DNS queries by class", "counter");
for(i=0; i<UB_STATS_QCLASS_NUM; i++) {
if(inhibit_zero && s->svr.qclass[i] == 0)
continue;
lt = sldns_lookup_by_id(sldns_rr_classes, i);
if(lt && lt->name) {
snprintf(nm, sizeof(nm), "%s", lt->name);
} else {
snprintf(nm, sizeof(nm), "CLASS%d", i);
}
INFO_EXT_STATS("by_class_queries", "class", nm,
s->svr.qclass[i]);
}
if(!inhibit_zero || s->svr.qclass_big) {
INFO_EXT_STATS("by_class_queries", "class", "other",
s->svr.qclass_big);
}
/* OPCODE */
print_metric_help_and_type(reply, prefix, "by_opcode_queries",
"Unbound DNS queries by opcode", "counter");
for(i=0; i<UB_STATS_OPCODE_NUM; i++) {
if(inhibit_zero && s->svr.qopcode[i] == 0)
continue;
lt = sldns_lookup_by_id(sldns_opcodes, i);
if(lt && lt->name) {
snprintf(nm, sizeof(nm), "%s", lt->name);
} else {
snprintf(nm, sizeof(nm), "OPCODE%d", i);
}
INFO_EXT_STATS("by_opcode_queries", "opcode", nm,
s->svr.qopcode[i]);
}
/* RCODE */
print_metric_help_and_type(reply, prefix, "by_rcode_queries",
"Unbound DNS answers by rcode", "counter");
for(i=0; i<UB_STATS_RCODE_NUM; i++) {
/* Always include RCODEs 0-5 */
if(inhibit_zero && i > LDNS_RCODE_REFUSED && s->svr.ans_rcode[i] == 0)
continue;
lt = sldns_lookup_by_id(sldns_rcodes, i);
if(lt && lt->name) {
snprintf(nm, sizeof(nm), "%s", lt->name);
} else {
snprintf(nm, sizeof(nm), "RCODE%d", i);
}
INFO_EXT_STATS("by_rcode_queries", "rcode", nm,
s->svr.ans_rcode[i]);
}
if(!inhibit_zero || s->svr.ans_rcode_nodata) {
INFO_EXT_STATS("by_rcode_queries", "rcode", "nodata",
s->svr.ans_rcode_nodata);
}
/* FLAGS */
print_metric_help_and_type(reply, prefix, "by_flags_queries",
"Unbound DNS queries by flags", "counter");
INFO_EXT_STATS("by_flags_queries", "flag", "QR", s->svr.qbit_QR);
INFO_EXT_STATS("by_flags_queries", "flag", "AA", s->svr.qbit_AA);
INFO_EXT_STATS("by_flags_queries", "flag", "TC", s->svr.qbit_TC);
INFO_EXT_STATS("by_flags_queries", "flag", "RD", s->svr.qbit_RD);
INFO_EXT_STATS("by_flags_queries", "flag", "RA", s->svr.qbit_RA);
INFO_EXT_STATS("by_flags_queries", "flag", "Z", s->svr.qbit_Z);
INFO_EXT_STATS("by_flags_queries", "flag", "AD", s->svr.qbit_AD);
INFO_EXT_STATS("by_flags_queries", "flag", "CD", s->svr.qbit_CD);
INFO_EXT_STATS("by_flags_queries", "flag", "edns.present",
s->svr.qEDNS);
INFO_EXT_STATS("by_flags_queries", "flag", "edns.DO",
s->svr.qEDNS_DO);
/* transport */
print_metric_help_and_type(reply, prefix, "by_transport_queries",
"Unbound DNS queries by transport", "counter");
INFO_EXT_STATS("by_transport_queries", "transport", "tcp",
s->svr.qtcp);
INFO_EXT_STATS("by_transport_queries", "transport", "tcpout",
s->svr.qtcp_outgoing);
INFO_EXT_STATS("by_transport_queries", "transport", "udpout",
s->svr.qudp_outgoing);
INFO_EXT_STATS("by_transport_queries", "transport", "tls",
s->svr.qtls);
INFO_EXT_STATS("by_transport_queries", "transport", "tls.resume",
s->svr.qtls_resume);
INFO_EXT_STATS("by_transport_queries", "transport", "ipv6",
s->svr.qipv6);
INFO_EXT_STATS("by_transport_queries", "transport", "https",
s->svr.qhttps);
#ifdef HAVE_NGTCP2
INFO_EXT_STATS("by_transport_queries", "transport", "quic",
s->svr.qquic);
#endif /* HAVE_NGTCP2 */
/* iteration */
print_metric_help_and_type(reply, prefix, "ratelimited_queries",
"Unbound DNS queries ratelimited", "counter");
INFO_EXT_STATS("ratelimited_queries", "type", "ratelimited",
s->svr.queries_ratelimited);
/* validation */
print_metric_help_and_type(reply, prefix, "validation_queries",
"Unbound DNS queries DNSSEC validated", "counter");
INFO_EXT_STATS("validation_queries", "type", "secure",
s->svr.ans_secure);
INFO_EXT_STATS("validation_queries", "type", "bogus",
s->svr.ans_bogus);
INFO_EXT_STATS("validation_queries", "type", "rrset.bogus",
s->svr.rrset_bogus);
INFO_EXT_STATS("validation_queries", "type", "valops",
s->svr.val_ops);
INFO_EXT_STATS("validation_queries", "type", "aggressive.NOERROR",
s->svr.num_neg_cache_noerror);
INFO_EXT_STATS("validation_queries", "type", "aggressive.NXDOMAIN",
s->svr.num_neg_cache_nxdomain);
/* threat detection */
print_metric_help_and_type(reply, prefix, "threat_queries",
"Unbound DNS queries threats", "counter");
INFO_EXT_STATS("threat_queries", "type", "unwanted.queries",
s->svr.unwanted_queries);
INFO_EXT_STATS("threat_queries", "type", "unwanted.replies",
s->svr.unwanted_replies);
/* cache counts */
print_metric_help_and_type(reply, prefix, "cache_items",
"Unbound DNS cache counts", "gauge");
INFO_EXT_STATS("cache_items", "count", "msg.cache",
s->svr.msg_cache_count);
INFO_EXT_STATS("cache_items", "count", "rrset.cache",
s->svr.rrset_cache_count);
INFO_EXT_STATS("cache_items", "count", "infra.cache",
s->svr.infra_cache_count);
INFO_EXT_STATS("cache_items", "count", "key.cache",
s->svr.key_cache_count);
/* max collisions */
INFO_EXT_STATS("cache_items", "count", "msg.cache.max_collisions",
s->svr.msg_cache_max_collisions);
INFO_EXT_STATS("cache_items", "count", "rrset.cache.max_collisions",
s->svr.rrset_cache_max_collisions);
/* applied RPZ actions */
print_metric_help_and_type(reply, prefix, "rpz_actions",
"Unbound DNS RPZ actions", "counter");
for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++) {
if(i == RPZ_NO_OVERRIDE_ACTION)
continue;
if(inhibit_zero && s->svr.rpz_action[i] == 0)
continue;
INFO_EXT_STATS("rpz_actions", "action",
rpz_action_to_string(i), s->svr.rpz_action[i]);
}
/* handling mechanism */
print_metric_help_and_type(reply, prefix, "handled_queries",
"Unbound DNS queries by handling mechanism", "counter");
#ifdef USE_DNSCRYPT
INFO_EXT_STATS("cache_items", "count", "dnscrypt_shared_secret.cache",
s->svr.shared_secret_cache_count);
INFO_EXT_STATS("cache_items", "count", "dnscrypt_nonce.cache",
s->svr.nonce_cache_count);
INFO_EXT_STATS("handled_queries", "type",
"dnscrypt.shared_secret.cachemiss",
s->svr.num_query_dnscrypt_secret_missed_cache);
INFO_EXT_STATS("handled_queries", "type", "dnscrypt.replay",
s->svr.num_query_dnscrypt_replay);
#endif /* USE_DNSCRYPT */
INFO_EXT_STATS("handled_queries", "type", "authzone.up",
s->svr.num_query_authzone_up);
INFO_EXT_STATS("handled_queries", "type", "authzone.down",
s->svr.num_query_authzone_down);
#ifdef CLIENT_SUBNET
INFO_EXT_STATS("handled_queries", "type", "subnet",
s->svr.num_query_subnet);
INFO_EXT_STATS("handled_queries", "type", "subnet_cache",
s->svr.num_query_subnet_cache);
#endif /* CLIENT_SUBNET */
#ifdef USE_CACHEDB
INFO_EXT_STATS("handled_queries", "type", "cachedb",
s->svr.num_query_cachedb);
#endif /* USE_CACHEDB */
return 1;
}
/* process statistics */
static void
do_metrics_stats(struct evbuffer* reply, struct worker* worker, int reset)
{
struct daemon* daemon = worker->daemon;
struct ub_stats_info total;
struct ub_stats_info s;
int i;
struct timeval stattime, time_last_stat;
memset(&total, 0, sizeof(total));
log_assert(daemon->num > 0);
if(!metrics_print_types(reply))
return;
/* gather all thread statistics in one place */
for(i=0; i<daemon->num; i++) {
server_stats_obtain(worker, daemon->workers[i], &s, reset);
if(!metrics_print_thread_stats(reply, i, &s))
return;
if(i == 0)
total = s;
else server_stats_add(&total, &s);
}
total.mesh_time_median /= (double)daemon->num;
if(gettimeofday(&stattime, NULL) < 0)
log_err("gettimeofday: %s", strerror(errno));
time_last_stat = worker->daemon->time_last_stat;
if(reset) {
worker->daemon->time_last_stat = stattime;
}
/* print the statistics */
if(!metrics_print_stats(reply, "total", &total))
return;
if(!metrics_print_uptime(reply, worker, &stattime, &time_last_stat))
return;
if(daemon->cfg->stat_extended) {
if(!metrics_print_mem(reply, worker, &total))
return;
if(!metrics_print_hist(reply, &total))
return;
if(!metrics_print_ext(reply, &total,
daemon->cfg->stat_inhibit_zero))
return;
}
}
/* Callback for handling the active http request to the specific URI */
static void
metrics_http_callback(struct evhttp_request *req, void *p)
{
struct evbuffer *reply = NULL;
struct daemon_metrics *metrics = ((struct metrics_acceptlist *)p)->metrics;
/* currently only GET requests are supported/allowed */
enum evhttp_cmd_type cmd = evhttp_request_get_command(req);
if (cmd != EVHTTP_REQ_GET /* && cmd != EVHTTP_REQ_HEAD */) {
evhttp_send_error(req, HTTP_BADMETHOD, 0);
return;
}
reply = evbuffer_new();
if (!reply) {
evhttp_send_error(req, HTTP_INTERNAL, 0);
log_err("metrics: failed to allocate reply buffer\n");
return;
}
evhttp_add_header(evhttp_request_get_output_headers(req),
"Content-Type", "text/plain; version=0.0.4");
do_metrics_stats(reply, metrics->worker, 0 /* no reset */);
evhttp_send_reply(req, HTTP_OK, NULL, reply);
verbose(VERB_DETAIL, "metrics operation completed, response sent");
evbuffer_free(reply);
}
#endif /* USE_METRICS */
+120
View File
@@ -0,0 +1,120 @@
/*
* daemon/metrics.h - prometheus metrics endpoint.
*
* Copyright (c) 2026, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* The statistics output provides metrics to prometheus.
*/
#ifndef DAEMON_METRICS_H
#define DAEMON_METRICS_H
struct daemon_metrics;
struct config_file;
struct worker;
struct evhttp;
/* the metrics daemon needs little backlog */
#define TCP_BACKLOG_METRICS 16 /* listen() tcp backlog */
/**
* list of connection accepting file descriptors
*/
struct metrics_acceptlist {
struct metrics_acceptlist* next;
int accept_fd;
char* ident;
struct daemon_metrics* metrics;
};
/**
* The metrics daemon state.
*/
struct daemon_metrics {
/** The worker for this metrics endpoint */
struct worker* worker;
/** commpoints for accepting HTTP connections */
struct metrics_acceptlist* accept_list;
/** libevent http server */
struct evhttp *http_server;
};
/**
* Create new metrics endpoint for the daemon.
* Does not open the ports, for that call the open ports routine, and
* later the attach routine on the worker event base.
* @return new state, or NULL on failure.
*/
struct daemon_metrics* daemon_metrics_create(void);
/**
* Delete metrics daemon and close HTTP listeners.
* @param m: daemon to delete.
*/
void daemon_metrics_delete(struct daemon_metrics* m);
/**
* Close metrics HTTP listener ports.
* Does not delete the object itself.
* @param m: state to close.
*/
void daemon_metrics_close_ports(struct daemon_metrics* m);
/**
* Detach the metrics listener from the event base.
* Does not delete the object itself.
* @param m: state to detach.
*/
void daemon_metrics_detach(struct daemon_metrics* m);
/**
* Open and create HTTP listeners for metrics daemon.
* @param m: metrics state that contains list of accept sockets.
* @param cfg: config options.
* @return false on failure.
*/
int daemon_metrics_open_ports(struct daemon_metrics* m,
struct config_file* cfg);
/**
* Setup HTTP listener.
* @param m: state
* @param worker: The worker thread that hosts the endpoint.
* The HTTP listener is attached to its event base.
* @return false on failure.
*/
int daemon_metrics_attach(struct daemon_metrics* m, struct worker* worker);
#endif /* DAEMON_METRICS_H */
+31 -48
View File
@@ -910,73 +910,39 @@ print_longnum(RES* ssl, const char* desc, size_t x)
/** print mem stats */
static int
print_mem(RES* ssl, struct worker* worker, struct daemon* daemon,
struct ub_stats_info* s)
print_mem(RES* ssl, struct worker* worker, struct ub_stats_info* s)
{
size_t msg, rrset, val, iter, respip;
#ifdef CLIENT_SUBNET
size_t subnet = 0;
#endif /* CLIENT_SUBNET */
#ifdef USE_IPSECMOD
size_t ipsecmod = 0;
#endif /* USE_IPSECMOD */
#ifdef USE_DNSCRYPT
size_t dnscrypt_shared_secret = 0;
size_t dnscrypt_nonce = 0;
#endif /* USE_DNSCRYPT */
#ifdef WITH_DYNLIBMODULE
size_t dynlib = 0;
#endif /* WITH_DYNLIBMODULE */
msg = slabhash_get_mem(daemon->env->msg_cache);
rrset = slabhash_get_mem(&daemon->env->rrset_cache->table);
val = mod_get_mem(&worker->env, "validator");
iter = mod_get_mem(&worker->env, "iterator");
respip = mod_get_mem(&worker->env, "respip");
#ifdef CLIENT_SUBNET
subnet = mod_get_mem(&worker->env, "subnetcache");
#endif /* CLIENT_SUBNET */
#ifdef USE_IPSECMOD
ipsecmod = mod_get_mem(&worker->env, "ipsecmod");
#endif /* USE_IPSECMOD */
#ifdef USE_DNSCRYPT
if(daemon->dnscenv) {
dnscrypt_shared_secret = slabhash_get_mem(
daemon->dnscenv->shared_secrets_cache);
dnscrypt_nonce = slabhash_get_mem(daemon->dnscenv->nonces_cache);
}
#endif /* USE_DNSCRYPT */
#ifdef WITH_DYNLIBMODULE
dynlib = mod_get_mem(&worker->env, "dynlib");
#endif /* WITH_DYNLIBMODULE */
struct ub_mem_stat_info mem;
stats_get_mem_info(worker, &mem);
if(!print_longnum(ssl, "mem.cache.rrset"SQ, rrset))
if(!print_longnum(ssl, "mem.cache.rrset"SQ, (size_t)mem.rrset))
return 0;
if(!print_longnum(ssl, "mem.cache.message"SQ, msg))
if(!print_longnum(ssl, "mem.cache.message"SQ, (size_t)mem.msg))
return 0;
if(!print_longnum(ssl, "mem.mod.iterator"SQ, iter))
if(!print_longnum(ssl, "mem.mod.iterator"SQ, (size_t)mem.iter))
return 0;
if(!print_longnum(ssl, "mem.mod.validator"SQ, val))
if(!print_longnum(ssl, "mem.mod.validator"SQ, (size_t)mem.val))
return 0;
if(!print_longnum(ssl, "mem.mod.respip"SQ, respip))
if(!print_longnum(ssl, "mem.mod.respip"SQ, (size_t)mem.respip))
return 0;
#ifdef CLIENT_SUBNET
if(!print_longnum(ssl, "mem.mod.subnet"SQ, subnet))
if(!print_longnum(ssl, "mem.mod.subnet"SQ, (size_t)mem.subnet))
return 0;
#endif /* CLIENT_SUBNET */
#ifdef USE_IPSECMOD
if(!print_longnum(ssl, "mem.mod.ipsecmod"SQ, ipsecmod))
if(!print_longnum(ssl, "mem.mod.ipsecmod"SQ, (size_t)mem.ipsecmod))
return 0;
#endif /* USE_IPSECMOD */
#ifdef USE_DNSCRYPT
if(!print_longnum(ssl, "mem.cache.dnscrypt_shared_secret"SQ,
dnscrypt_shared_secret))
(size_t)mem.dnscrypt_shared_secret))
return 0;
if(!print_longnum(ssl, "mem.cache.dnscrypt_nonce"SQ,
dnscrypt_nonce))
(size_t)mem.dnscrypt_nonce))
return 0;
#endif /* USE_DNSCRYPT */
#ifdef WITH_DYNLIBMODULE
if(!print_longnum(ssl, "mem.mod.dynlibmod"SQ, dynlib))
if(!print_longnum(ssl, "mem.mod.dynlibmod"SQ, (size_t)mem.dynlib))
return 0;
#endif /* WITH_DYNLIBMODULE */
if(!print_longnum(ssl, "mem.streamwait"SQ,
@@ -1264,7 +1230,7 @@ do_stats(RES* ssl, struct worker* worker, int reset)
if(!print_uptime(ssl, worker, reset))
return;
if(daemon->cfg->stat_extended) {
if(!print_mem(ssl, worker, daemon, &total))
if(!print_mem(ssl, worker, &total))
return;
if(!print_hist(ssl, &total))
return;
@@ -4980,6 +4946,12 @@ fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg)
FR_CHECK_CHANGED_CFG_STRLIST("tls-additional-port", tls_additional_port, changed_str);
FR_CHECK_CHANGED_CFG_STR("interface-automatic-ports", if_automatic_ports, changed_str);
FR_CHECK_CHANGED_CFG("udp-upstream-without-downstream", udp_upstream_without_downstream, changed_str);
#ifdef USE_METRICS
FR_CHECK_CHANGED_CFG("metrics-enable", metrics_enable, changed_str);
FR_CHECK_CHANGED_CFG("metrics-port", metrics_port, changed_str);
FR_CHECK_CHANGED_CFG_STR("metrics-path", metrics_path, changed_str);
FR_CHECK_CHANGED_CFG_STRLIST("metrics-interface", metrics_ifs.first, changed_str);
#endif
if(changed_str[0] != 0) {
/* The new config changes some items that do not work with
@@ -5307,6 +5279,10 @@ config_file_getmem(struct config_file* cfg)
m += getmem_str(cfg->dnstap_tls_client_cert_file);
m += getmem_str(cfg->dnstap_identity);
m += getmem_str(cfg->dnstap_version);
#ifdef USE_METRICS
m += getmem_config_strlist(cfg->metrics_ifs.first);
m += getmem_str(cfg->metrics_path);
#endif
m += getmem_config_str2list(cfg->ratelimit_for_domain);
m += getmem_config_str2list(cfg->ratelimit_below_domain);
m += getmem_config_str2list(cfg->edns_client_strings);
@@ -6291,6 +6267,13 @@ fr_atomic_copy_cfg(struct config_file* oldcfg, struct config_file* cfg,
COPY_VAR_int(dnstap_log_forwarder_query_messages);
COPY_VAR_int(dnstap_log_forwarder_response_messages);
COPY_VAR_int(disable_dnssec_lame_check);
#ifdef USE_METRICS
COPY_VAR_int(metrics_enable);
COPY_VAR_ptr(metrics_ifs.first);
COPY_VAR_ptr(metrics_ifs.last);
COPY_VAR_int(metrics_port);
COPY_VAR_ptr(metrics_path);
#endif
COPY_VAR_int(ip_ratelimit);
COPY_VAR_int(ip_ratelimit_cookie);
COPY_VAR_size_t(ip_ratelimit_slabs);
+39
View File
@@ -611,3 +611,42 @@ void server_stats_downstream_cookie(struct ub_server_stats* stats,
stats->num_queries_cookie_invalid++;
}
}
void stats_get_mem_info(struct worker* worker, struct ub_mem_stat_info* mem)
{
struct daemon* daemon = worker->daemon;
mem->msg = slabhash_get_mem(daemon->env->msg_cache);
mem->rrset = slabhash_get_mem(&daemon->env->rrset_cache->table);
mem->val = mod_get_mem(&worker->env, "validator");
mem->iter = mod_get_mem(&worker->env, "iterator");
mem->respip = mod_get_mem(&worker->env, "respip");
#ifdef CLIENT_SUBNET
mem->subnet = mod_get_mem(&worker->env, "subnetcache");
#else
mem->subnet = 0;
#endif /* CLIENT_SUBNET */
#ifdef USE_IPSECMOD
mem->ipsecmod = mod_get_mem(&worker->env, "ipsecmod");
#else
mem->ipsecmod = 0;
#endif /* USE_IPSECMOD */
#ifdef USE_DNSCRYPT
if(daemon->dnscenv) {
mem->dnscrypt_shared_secret = slabhash_get_mem(
daemon->dnscenv->shared_secrets_cache);
mem->dnscrypt_nonce = slabhash_get_mem(
daemon->dnscenv->nonces_cache);
} else {
mem->dnscrypt_shared_secret = 0;
mem->dnscrypt_nonce = 0;
}
#else
mem->dnscrypt_shared_secret = 0;
mem->dnscrypt_nonce = 0;
#endif /* USE_DNSCRYPT */
#ifdef WITH_DYNLIBMODULE
mem->dynlib = mod_get_mem(&worker->env, "dynlib");
#else
mem->dynlib = 0;
#endif /* WITH_DYNLIBMODULE */
}
+8
View File
@@ -49,6 +49,7 @@ struct comm_point;
struct comm_reply;
struct edns_data;
struct sldns_buffer;
struct ub_mem_stat_info;
/* stats struct */
#include "libunbound/unbound.h"
@@ -133,4 +134,11 @@ void server_stats_insrcode(struct ub_server_stats* stats, struct sldns_buffer* b
*/
void server_stats_downstream_cookie(struct ub_server_stats* stats,
struct edns_data* edns);
/** Get the memory statistics for the program.
* @param worker: with worker env and ptr to daemon.
* @param mem: filled with memory usage value statistics.
*/
void stats_get_mem_info(struct worker* worker, struct ub_mem_stat_info* mem);
#endif /* DAEMON_STATS_H */
+7
View File
@@ -46,6 +46,7 @@
#include "daemon/worker.h"
#include "daemon/daemon.h"
#include "daemon/remote.h"
#include "daemon/metrics.h"
#include "daemon/acl_list.h"
#include "util/netevent.h"
#include "util/config_file.h"
@@ -2276,6 +2277,12 @@ worker_init(struct worker* worker, struct config_file *cfg,
worker_delete(worker);
return 0;
}
#ifdef USE_METRICS
if(!daemon_metrics_attach(worker->daemon->metrics, worker)) {
worker_delete(worker);
return 0;
}
#endif /* USE METRICS */
#ifdef UB_ON_WINDOWS
wsvc_setup_worker(worker);
#endif /* UB_ON_WINDOWS */
+17
View File
@@ -1159,6 +1159,23 @@ server:
# Timeout in milliseconds for TCP queries to auth servers.
# tcp-auth-query-timeout: 3000
# Enable the prometheus metrics HTTP endpoint. Default is no.
# metrics-enable: no
# Interfaces to expose the HTTP endpoint on, default is on localhost.
# Interfaces can be specified by IP address or interface name.
# With an interface name, all IP addresses associated with that
# interface are used. Default is 127.0.0.1 and ::1.
# metrics-interface: 127.0.0.1
# metrics-interface: ::1
# metrics-interface: lo
# Port number for the HTTP metrics endpoint. Default is 9100.
# metrics-port: 9100
# HTTP path for the metrics endpoint. Default is "/metrics".
# metrics-path: "/metrics"
# Python config section. To enable:
# o use --with-pythonmodule to configure before compiling.
+52
View File
@@ -247,6 +247,8 @@ These options are part of the ``server:`` section.
:doc:`unbound-control(8)</manpages/unbound-control>`.
The counters are listed in
:doc:`unbound-control(8)</manpages/unbound-control>`.
The counters are also available from the metrics interface,
:ref:`metrics-enable<unbound.conf.metrics-enable>` .
Keeping track of more statistics takes time.
Default: no
@@ -3433,6 +3435,56 @@ These options are part of the ``server:`` section.
Default: no
@@UAHL@unbound.conf@metrics-enable@@: *<yes or no>*
Enable the prometheus metrics HTTP endpoint.
It exposes the same statistics as the
:ref:`stats_noreset<unbound-control.commands.stats_noreset>`,
command, but with metric names
following the prometheus specification. (Requires libevent2)
Use it with settings, extended-statistics: yes that collects more
information,
:ref:`extended-statistics<unbound.conf.extended-statistics>` .
And set statistics-cumulative: yes, because the metrics are
defined as cumulative counters for the number of queries,
:ref:`statistics-cumulative<unbound.conf.statistics-cumulative>` .
Access from the metrics endpoint does not reset the statistics.
Beware, if statistics-cumulative is disabled, that when using
:ref:`stats<unbound-control.commands.stats`
(instead of stats_noreset), the statistics will be reset for
the HTTP metrics endpoint as well.
With statistics-cumulative enabled, the stats (and stats_noreset)
command can be used to also get a look at the statistics information.
Default: no
@@UAHL@unbound.conf@metrics-interface@@: *<ip4 or ip6[@port] | interface name>*
Unbound will bind to the listed addresses or interfaces to serve the
prometheus metrics.
Can be given multiple times to bind multiple ip-addresses.
Use 0.0.0.0 and ::0 to bind to the wildcard interface.
If an interface name is used instead of ip4 or ip6, the list of IP
addresses associated with that interface is picked up and used at
server start.
Default is 127.0.0.1 and ::1.
@@UAHL@unbound.conf@metrics-port@@: *<number>*
The port number for the HTTP service.
Default is 9100.
@@UAHL@unbound.conf@metrics-path@@: *<string>*
The HTTP path to expose the metrics at.
Default is "/metrics".
.. _unbound.conf.remote:
Remote Control Options
+17 -12
View File
@@ -649,6 +649,22 @@ int ub_ctx_data_remove(struct ub_ctx* ctx, const char *data);
*/
const char* ub_version(void);
/**
* Memory statistics values. The values describe memory usage (in bytes).
*/
struct ub_mem_stat_info {
long long msg;
long long rrset;
long long val;
long long iter;
long long subnet;
long long ipsecmod;
long long respip;
long long dnscrypt_shared_secret;
long long dnscrypt_nonce;
long long dynlib;
};
/**
* Some global statistics that are not in struct stats_info,
* this struct is shared on a shm segment (shm-key in unbound.conf)
@@ -662,18 +678,7 @@ struct ub_shm_stat_info {
long long elapsed_sec, elapsed_usec;
} time;
struct {
long long msg;
long long rrset;
long long val;
long long iter;
long long subnet;
long long ipsecmod;
long long respip;
long long dnscrypt_shared_secret;
long long dnscrypt_nonce;
long long dynlib;
} mem;
struct ub_mem_stat_info mem;
};
/** number of qtype that is stored for in array */
+37
View File
@@ -49,6 +49,7 @@
#include "daemon/remote.h"
#include "libunbound/worker.h"
#include "daemon/worker.h"
#include "daemon/metrics.h"
#include "util/config_file.h"
#include "sldns/keyraw.h"
#ifdef UB_ON_WINDOWS
@@ -664,6 +665,42 @@ void remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg))
log_assert(0);
}
/* fake metrics */
struct daemon_metrics* daemon_metrics_create(void)
{
return (struct daemon_metrics*)calloc(1, sizeof(struct daemon_metrics));
}
void daemon_metrics_delete(struct daemon_metrics* m)
{
if(!m) return;
free(m);
}
void daemon_metrics_close_ports(struct daemon_metrics* ATTR_UNUSED(m))
{
/* nothing */
}
void daemon_metrics_detach(struct daemon_metrics* ATTR_UNUSED(m))
{
/* nothing */
}
int daemon_metrics_open_ports(struct daemon_metrics* ATTR_UNUSED(m),
struct config_file* ATTR_UNUSED(cfg))
{
/* nothing */
return 1;
}
int daemon_metrics_attach(struct daemon_metrics* ATTR_UNUSED(m),
struct worker* ATTR_UNUSED(worker))
{
/* nothing */
return 1;
}
#ifdef UB_ON_WINDOWS
void wsvc_command_option(const char* ATTR_UNUSED(wopt),
const char* ATTR_UNUSED(cfgfile), int ATTR_UNUSED(v),
@@ -0,0 +1,33 @@
server:
verbosity: 5
num-threads: 1
interface: 127.0.0.1@@PORT@
use-syslog: no
directory: ""
pidfile: "unbound.pid"
chroot: ""
username: ""
do-not-query-localhost: no
metrics-enable: yes
metrics-path: "/metrics"
metrics-interface: 127.0.0.1
metrics-port: @METRICSPORT@
statistics-cumulative: yes
extended-statistics: yes
statistics-inhibit-zero: yes
statistics-interval: 0
local-data: "www.example.com. IN A 192.0.2.10"
local-data: 'a.example.com. IN TXT "abcdef text"'
remote-control:
control-enable: yes
control-interface: 127.0.0.1
# control-interface: ::1
control-port: @CONTROL_PORT@
server-key-file: "unbound_server.key"
server-cert-file: "unbound_server.pem"
control-key-file: "unbound_control.key"
control-cert-file: "unbound_control.pem"
+16
View File
@@ -0,0 +1,16 @@
BaseName: prometheus_metrics
Version: 1.0
Description: Test prometheus metrics
CreationDate: Fri 30 Jan 13:22:03 CET 2026
Maintainer: dr. Wouter Wijngaards
Category:
Component:
CmdDepends:
Depends:
Help:
Pre: prometheus_metrics.pre
Post: prometheus_metrics.post
Test: prometheus_metrics.test
AuxFiles: prometheus_metrics.conf, prometheus_metrics.zone
Passed:
Failure:
@@ -0,0 +1,11 @@
# #-- prometheus_metrics.post --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# source the test var file when it's there
[ -f .tpkg.var.test ] && source .tpkg.var.test
#
# do your teardown here
. ../common.sh
kill_pid $UNBOUND_PID
cat unbound.log
exit 0
+33
View File
@@ -0,0 +1,33 @@
# #-- prometheus_metrics.pre--#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
. ../common.sh
PRE="../.."
if grep "define USE_METRICS" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; fi
# Is curl available
if test -f "$(which curl 2>&1)"; then
echo "curl available, do test"
else
skip_test "curl not available, skip test"
fi
get_random_port 3
UNBOUND_PORT=$RND_PORT
CONTROL_PORT=$(($RND_PORT + 1))
METRICS_PORT=$(($RND_PORT + 2))
echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
echo "CONTROL_PORT=$CONTROL_PORT" >> .tpkg.var.test
echo "METRICS_PORT=$METRICS_PORT" >> .tpkg.var.test
# make config file
sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@METRICSPORT\@/'$METRICS_PORT'/' -e 's/@CONTROL_PORT\@/'$CONTROL_PORT'/' < prometheus_metrics.conf > ub.conf
# start unbound in the background
$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
UNBOUND_PID=$!
echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
cat .tpkg.var.test
wait_unbound_up unbound.log
+113
View File
@@ -0,0 +1,113 @@
# #-- prometheus_metrics.test --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
. ../common.sh
PRE="../.."
NUM_A_QUERIES=5
NUM_TXT_QUERIES=3
# query server a few times
for i in $(seq 1 $NUM_A_QUERIES); do
dig @127.0.0.1 -p "$UNBOUND_PORT" www.example.com. A IN | tee out2
if grep "192.0.2.10" out2; then
echo "OK"
else
echo "data not present"
exit 1
fi
done
for i in $(seq 1 $NUM_TXT_QUERIES); do
dig @127.0.0.1 -p "$UNBOUND_PORT" a.example.com. TXT IN | tee out2
if grep "abcdef text" out2; then
echo "OK"
else
echo "data not present"
exit 1
fi
done
# stats no reset for comparison
echo ""
echo ">> unbound-control stats"
$PRE/unbound-control -c ub.conf stats_noreset | tee stats
# check metrics
if ! curl -Ssi "http://127.0.0.1:$METRICS_PORT/metrics" -o metrics.out; then
echo "FAIL curl failed"
exit 1
fi
echo ""
echo ">> metrics output"
cat metrics.out
echo ""
echo ">> checks"
# more tests
if grep -Fx "unbound_hits_queries{type=\"total.num.queries\"} $((NUM_TXT_QUERIES+NUM_A_QUERIES))" metrics.out; then
echo "OK"
else
echo "FAIL total num"
exit 1
fi
if grep -Fx "unbound_by_type_queries{type=\"TXT\"} $NUM_TXT_QUERIES" metrics.out; then
echo "OK"
else
echo "FAIL txt num"
exit 1
fi
if grep -Fx "unbound_by_type_queries{type=\"A\"} $NUM_A_QUERIES" metrics.out; then
echo "OK"
else
echo "FAIL A num"
exit 1
fi
# and check statistics are the same as metrics
if grep "total.num.queries=$((NUM_TXT_QUERIES+NUM_A_QUERIES))" stats; then echo "OK"; else echo "FAIL"; exit 1; fi
if grep "num.query.type.TXT=$NUM_TXT_QUERIES" stats; then echo "OK"; else echo "FAIL"; exit 1; fi
if grep "num.query.type.A=$NUM_A_QUERIES" stats; then echo "OK"; else echo "FAIL"; exit 1; fi
# check that metrics shows no reset to stats after using nsd-control stats
# check metrics again
if ! curl -Ssi "http://127.0.0.1:$METRICS_PORT/metrics" -o metrics.out2; then
echo "FAIL to curl again"
exit 1
fi
echo ""
echo ">> metrics output"
cat metrics.out2
echo ""
echo ">> checks"
if grep -Fx "unbound_hits_queries{type=\"total.num.queries\"} $((NUM_TXT_QUERIES+NUM_A_QUERIES))" metrics.out2; then
echo "OK"
else
echo "FAIL total num again"
exit 1
fi
if grep -Fx "unbound_by_flags_queries{flag=\"RD\"} $((NUM_TXT_QUERIES+NUM_A_QUERIES))" metrics.out2; then
echo "OK"
else
echo "FAIL RD num again"
exit 1
fi
if grep -Fx "unbound_hits_queries{type=\"total.num.cachehits\"} $((NUM_TXT_QUERIES+NUM_A_QUERIES))" metrics.out2; then
echo "OK"
else
echo "FAIL cachehits num again"
exit 1
fi
exit 0
+39
View File
@@ -0,0 +1,39 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
+22
View File
@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDszCCAhsCFGD5193whHQ2bVdzbaQfdf1gc4SkMA0GCSqGSIb3DQEBCwUAMBIx
EDAOBgNVBAMMB3VuYm91bmQwHhcNMjAwNzA4MTMzMjMwWhcNNDAwMzI1MTMzMjMw
WjAaMRgwFgYDVQQDDA91bmJvdW5kLWNvbnRyb2wwggGiMA0GCSqGSIb3DQEBAQUA
A4IBjwAwggGKAoIBgQCy0Sn4/KHxcau1nvsDgWFiO9t4Hd0xrtDasZbGhvOUD2mv
OEDVoKEC9S0I4C4z6sHo1M0HxhZ9kltAdrDIw2mYLvtyDq9ctgIZRAdnICqJ03Tj
1EkXlPuPg3xWeK/XsuJF2vRLqzDLg9G8Scg89XjcRAdmoLmNW8ewqSEh+YEX8OPl
NdHKl5rBZX+bmN5Tr/gdFgx/K0Fl07WY9e5YuHSWBtDhIJuCs5RUPeueIyTXJkht
A6a+UMdip6SMNv+85bYrFRAsbThpkwnWZZ91vAbQIpCRKMhIQNnWXvq+EzqZMzR/
Bdsi/IVn3I88KV7WYXlpKXFLZyYa4ZVJFBrDxcX197dr0AIklOvfcd1KnJpmQpEB
TMtwqosKXfbd1oq/c3DABPTdOPiJAzjXoBIyiLJ8Vr/YZZMpBWN127wKtqvu0Zp/
nGuWKDgMGasG91gDeePLvb9EPvWEvCaVGgIZTt1G4Ov07ooBJSbVGL3U41n+p2My
+i/XmXa+PgZX4pdHTR0CAwEAATANBgkqhkiG9w0BAQsFAAOCAYEAd++Wen6l8Ifj
4h3p/y16PhSsWJWuJ4wdNYy3/GM84S26wGjzlEEwiW76HpH6VJzPOiBAeWnFKE83
hFyetEIxgJeIPbcs9ZP/Uoh8GZH9tRISBSN9Hgk2Slr9llo4t1H0g/XTgA5HqMQU
9YydlBh43G7Vw3FVwh09OM6poNOGQKNc/tq2/QdKeUMtyBbLWpRmjH5XcCT35fbn
ZiVOUldqSHD4kKrFO4nJYXZyipRbcXybsLiX9GP0GLemc3IgIvOXyJ2RPp06o/SJ
pzlMlkcAfLJaSuEW57xRakhuNK7m051TKKzJzIEX+NFYOVdafFHS8VwGrYsdrFvD
72tMfu+Fu55y3awdWWGc6YlaGogZiuMnJkvQphwgn+5qE/7CGEckoKEsH601rqIZ
muaIc85+nEcHJeijd/ZlBN9zeltjFoMuqTUENgmv8+tUAdVm/UMY9Vjme6b43ydP
uv6DS02+k9z8toxXworLiPr94BGaiGV1NxgwZKLZigYJt/Fi2Qte
-----END CERTIFICATE-----
+39
View File
@@ -0,0 +1,39 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
+22
View File
@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDqzCCAhMCFBHWXeQ6ZIa9QcQbXLFfC6tj+KA+MA0GCSqGSIb3DQEBCwUAMBIx
EDAOBgNVBAMMB3VuYm91bmQwHhcNMjAwNzA4MTMzMjI5WhcNNDAwMzI1MTMzMjI5
WjASMRAwDgYDVQQDDAd1bmJvdW5kMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
igKCAYEAvjSVSN2QMXudpzukdLCqgg/IOhCX8KYkD0FFFfWcQjgKq5wI0x41iG32
a6wbGanre4IX7VxaSPu9kkHfnGgynCk5nwDRedE/FLFhAU78PoT0+NqqGRS7XVQ2
4vLmIz9Hqc2Ozx1um1BXBTmIT0UfN2e22I0LWQ6a3seZlEDRj45gnk7Zuh9MDgot
aBdm+v1JAbupSf6Zis4VEH3JNdvVGE3O1DHEIeuuz/3BDhpf6WBDH+8KWaBe1ca4
TZHr9ThL2gEMEfAQl0wXDwRWRoi3NjNMH+mw0L1rjwThI5GXqNIee7o5FzUReSXZ
uTdFMyGe3Owcx+XoYnwi6cplSNoGsDBu4B9bKKglR9YleJVw4L4Xi8xPq6O9UPj4
+nypHk/DOoC7DIM3ufN0yxPBsFo5TVowxfhdjZXJbbftd2TZv7AH8+XLA5UoZgRz
XgzECelXSCTBFlMTnT48LfA9pMLydyjAz2UdPHs5Iv+TK5nnI+aJoeaP7kFZSngx
dy1+A/bNAgMBAAEwDQYJKoZIhvcNAQELBQADggGBABunf93MKaCUHiZgnoOTinsW
84/EgInrgtKzAyH+BhnKkJOhhR0kkIAx5d9BpDlaSiRTACFon9moWCgDIIsK/Ar7
JE0Kln9cV//wiiNoFU0O4mnzyGUIMvlaEX6QHMJJQYvL05+w/3AAcf5XmMJtR5ca
fJ8FqvGC34b2WxX9lTQoyT52sRt+1KnQikiMEnEyAdKktMG+MwKsFDdOwDXyZhZg
XZhRrfX3/NVJolqB6EahjWIGXDeKuSSKZVtCyib6LskyeMzN5lcRfvubKDdlqFVF
qlD7rHBsKhQUWK/IO64mGf7y/de+CgHtED5vDvr/p2uj/9sABATfbrOQR3W/Of25
sLBj4OEfrJ7lX8hQgFaxkMI3x6VFT3W8dTCp7xnQgb6bgROWB5fNEZ9jk/gjSRmD
yIU+r0UbKe5kBk/CmZVFXL2TyJ92V5NYEQh8V4DGy19qZ6u/XKYyNJL4ocs35GGe
CA8SBuyrmdhx38h1RHErR2Skzadi1S7MwGf1y431fQ==
-----END CERTIFICATE-----
+24
View File
@@ -346,6 +346,14 @@ config_create(void)
cfg->dnstap_bidirectional = 1;
cfg->dnstap_tls = 1;
cfg->disable_dnssec_lame_check = 0;
#ifdef USE_METRICS
cfg->metrics_enable = 0;
cfg->metrics_ifs.first = NULL;
cfg->metrics_ifs.last = NULL;
cfg->metrics_port = UNBOUND_METRICS_PORT;
if(!(cfg->metrics_path = strdup("/metrics")))
goto error_exit;
#endif /* USE_METRICS */
cfg->ip_ratelimit_cookie = 0;
cfg->ip_ratelimit = 0;
cfg->ratelimit = 0;
@@ -845,6 +853,12 @@ int config_set_option(struct config_file* cfg, const char* opt,
else S_YNO("dnstap-log-forwarder-response-messages:",
dnstap_log_forwarder_response_messages)
#endif
#ifdef USE_METRICS
else S_YNO("metrics-enable:", metrics_enable)
else S_STRLIST_APPEND("metrics-interface:", metrics_ifs)
else S_NUMBER_NONZERO("metrics-port:", metrics_port)
else S_STR("metrics-path:", metrics_path)
#endif /* USE_METRICS */
#ifdef USE_DNSCRYPT
else S_YNO("dnscrypt-enable:", dnscrypt)
else S_NUMBER_NONZERO("dnscrypt-port:", dnscrypt_port)
@@ -1338,6 +1352,12 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_YNO(opt, "dnstap-log-forwarder-response-messages",
dnstap_log_forwarder_response_messages)
#endif
#ifdef USE_METRICS
else O_YNO(opt, "metrics-enable", metrics_enable)
else O_LST(opt, "metrics-interface", metrics_ifs.first)
else O_DEC(opt, "metrics-port", metrics_port)
else O_STR(opt, "metrics-path", metrics_path)
#endif /* USE_METRICS */
#ifdef USE_DNSCRYPT
else O_YNO(opt, "dnscrypt-enable", dnscrypt)
else O_DEC(opt, "dnscrypt-port", dnscrypt_port)
@@ -1835,6 +1855,10 @@ config_delete(struct config_file* cfg)
free(cfg->dnstap_tls_client_cert_file);
free(cfg->dnstap_identity);
free(cfg->dnstap_version);
#ifdef USE_METRICS
config_delstrlist(cfg->metrics_ifs.first);
free(cfg->metrics_path);
#endif /* USE_METRICS */
config_deldblstrlist(cfg->ratelimit_for_domain);
config_deldblstrlist(cfg->ratelimit_below_domain);
config_delstrlist(cfg->python_script);
+11
View File
@@ -625,6 +625,17 @@ struct config_file {
/** true to disable DNSSEC lameness check in iterator */
int disable_dnssec_lame_check;
#ifdef USE_METRICS
/** metrics section. enable toggle. */
int metrics_enable;
/** the interfaces the metrics endpoint should listen on */
struct config_strlist_head metrics_ifs;
/** port number for the metrics endpoint */
int metrics_port;
/** HTTP path for the metrics endpoint */
char* metrics_path;
#endif /* USE_METRICS */
/** ratelimit for ip addresses. 0 is off, otherwise qps (unless overridden) */
int ip_ratelimit;
/** ratelimit for ip addresses with a valid DNS Cookie. 0 is off,
+4
View File
@@ -491,6 +491,10 @@ interface-tag-action{COLON} { YDVAR(3, VAR_INTERFACE_TAG_ACTION) }
interface-tag-data{COLON} { YDVAR(3, VAR_INTERFACE_TAG_DATA) }
interface-view{COLON} { YDVAR(2, VAR_INTERFACE_VIEW) }
local-zone-override{COLON} { YDVAR(3, VAR_LOCAL_ZONE_OVERRIDE) }
metrics-enable{COLON} { YDVAR(1, VAR_METRICS_ENABLE) }
metrics-interface{COLON} { YDVAR(1, VAR_METRICS_INTERFACE) }
metrics-port{COLON} { YDVAR(1, VAR_METRICS_PORT) }
metrics-path{COLON} { YDVAR(1, VAR_METRICS_PATH) }
dnstap{COLON} { YDVAR(0, VAR_DNSTAP) }
dnstap-enable{COLON} { YDVAR(1, VAR_DNSTAP_ENABLE) }
dnstap-bidirectional{COLON} { YDVAR(1, VAR_DNSTAP_BIDIRECTIONAL) }
+51 -1
View File
@@ -126,6 +126,8 @@ extern struct config_parser_state* cfg_parser;
%token VAR_INFRA_CACHE_MIN_RTT VAR_INFRA_CACHE_MAX_RTT VAR_INFRA_KEEP_PROBING
%token VAR_DNS64_PREFIX VAR_DNS64_SYNTHALL VAR_DNS64_IGNORE_AAAA
%token VAR_NAT64_PREFIX
%token VAR_METRICS_ENABLE VAR_METRICS_INTERFACE VAR_METRICS_PORT
%token VAR_METRICS_PATH
%token VAR_DNSTAP VAR_DNSTAP_ENABLE VAR_DNSTAP_SOCKET_PATH VAR_DNSTAP_IP
%token VAR_DNSTAP_TLS VAR_DNSTAP_TLS_SERVER_NAME VAR_DNSTAP_TLS_CERT_BUNDLE
%token VAR_DNSTAP_TLS_CLIENT_KEY_FILE VAR_DNSTAP_TLS_CLIENT_CERT_FILE
@@ -359,7 +361,9 @@ content_server: server_num_threads | server_verbosity | server_port |
server_harden_unknown_additional | server_disable_edns_do |
server_log_destaddr | server_cookie_secret_file |
server_iter_scrub_ns | server_iter_scrub_cname | server_max_global_quota |
server_harden_unverified_glue | server_log_time_iso | server_iter_scrub_promiscuous
server_harden_unverified_glue | server_log_time_iso | server_iter_scrub_promiscuous |
server_metrics_enable | server_metrics_interface |
server_metrics_port | server_metrics_path
;
stub_clause: stubstart contents_stub
{
@@ -2748,6 +2752,52 @@ server_response_ip_tag: VAR_RESPONSE_IP_TAG STRING_ARG STRING_ARG
}
}
;
server_metrics_enable: VAR_METRICS_ENABLE STRING_ARG
{
OUTYY(("P(server_metrics_enable:%s)\n", $2));
#ifdef USE_METRICS
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->metrics_enable = (strcmp($2, "yes")==0);
#else
if(strcmp($2, "yes")==0)
log_warn("%s:%d the server is not compiled with "
"prometheus metrics.", cfg_parser->filename,
cfg_parser->line);
#endif
free($2);
};
server_metrics_interface: VAR_METRICS_INTERFACE STRING_ARG
{
OUTYY(("P(server_metrics_interface:%s)\n", $2));
#ifdef USE_METRICS
if(!cfg_strlist_append(&cfg_parser->cfg->metrics_ifs, $2))
yyerror("out of memory");
#else
free($2);
#endif
};
server_metrics_port: VAR_METRICS_PORT STRING_ARG
{
OUTYY(("P(server_metrics_port:%s)\n", $2));
#ifdef USE_METRICS
if(atoi($2) == 0 && strcmp($2,"0")!=0)
yyerror("port number expected");
else
cfg_parser->cfg->metrics_port = atoi($2);
#endif
free($2);
};
server_metrics_path: VAR_METRICS_PATH STRING_ARG
{
OUTYY(("P(server_metrics_path:%s)\n", $2));
#ifdef USE_METRICS
free(cfg_parser->cfg->metrics_path);
cfg_parser->cfg->metrics_path = $2;
#else
free($2);
#endif
};
server_ip_ratelimit: VAR_IP_RATELIMIT STRING_ARG
{
OUTYY(("P(server_ip_ratelimit:%s)\n", $2));
+1 -28
View File
@@ -249,40 +249,13 @@ shm_general_info(struct worker* worker)
stat_timeval_subtract(&shm_stat->time.up_sec, &shm_stat->time.up_usec, worker->env.now_tv, &worker->daemon->time_boot);
stat_timeval_subtract(&shm_stat->time.elapsed_sec, &shm_stat->time.elapsed_usec, worker->env.now_tv, &worker->daemon->time_last_stat);
shm_stat->mem.msg = (long long)slabhash_get_mem(worker->env.msg_cache);
shm_stat->mem.rrset = (long long)slabhash_get_mem(&worker->env.rrset_cache->table);
shm_stat->mem.dnscrypt_shared_secret = 0;
#ifdef USE_DNSCRYPT
if(worker->daemon->dnscenv) {
shm_stat->mem.dnscrypt_shared_secret = (long long)slabhash_get_mem(
worker->daemon->dnscenv->shared_secrets_cache);
shm_stat->mem.dnscrypt_nonce = (long long)slabhash_get_mem(
worker->daemon->dnscenv->nonces_cache);
}
#endif
shm_stat->mem.val = (long long)mod_get_mem(&worker->env, "validator");
shm_stat->mem.iter = (long long)mod_get_mem(&worker->env, "iterator");
shm_stat->mem.respip = (long long)mod_get_mem(&worker->env, "respip");
/* subnet mem value is available in shm, also when not enabled,
* to make the struct easier to memmap by other applications,
* independent of the configuration of unbound */
shm_stat->mem.subnet = 0;
#ifdef CLIENT_SUBNET
shm_stat->mem.subnet = (long long)mod_get_mem(&worker->env,
"subnetcache");
#endif
/* ipsecmod mem value is available in shm, also when not enabled,
* to make the struct easier to memmap by other applications,
* independent of the configuration of unbound */
shm_stat->mem.ipsecmod = 0;
#ifdef USE_IPSECMOD
shm_stat->mem.ipsecmod = (long long)mod_get_mem(&worker->env,
"ipsecmod");
#endif
#ifdef WITH_DYNLIBMODULE
shm_stat->mem.dynlib = (long long)mod_get_mem(&worker->env, "dynlib");
#endif
stats_get_mem_info(worker, &shm_stat->mem);
}
/** See if the thread is first. Caller has lock. */