Change /api/info/cache -> /api/info/metrics and add other metrics such as query reply sources and DHCP metrics

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER 2023-02-11 13:38:37 +01:00
parent bcda8cc29a
commit 83195e678e
No known key found for this signature in database
GPG Key ID: 00135ACBD90B28DD
11 changed files with 345 additions and 187 deletions

View File

@ -203,6 +203,7 @@ set(sources
log.h
main.c
main.h
metrics.h
overTime.c
overTime.h
procps.c

View File

@ -50,7 +50,7 @@ static struct {
{ "/api/info/version", "", api_info_version, { false, true, 0 }, true, HTTP_GET },
{ "/api/info/messages", "/{message_id}", api_info_messages, { false, true, 0 }, true, HTTP_DELETE },
{ "/api/info/messages", "", api_info_messages, { false, true, 0 }, true, HTTP_GET },
{ "/api/info/cache", "", api_info_cache, { false, true, 0 }, true, HTTP_GET },
{ "/api/info/metrics", "", api_info_metrics, { false, true, 0 }, true, HTTP_GET },
{ "/api/logs/dnsmasq", "", api_logs, { false, true, FIFO_DNSMASQ }, true, HTTP_GET },
{ "/api/logs/ftl", "", api_logs, { false, true, FIFO_FTL }, true, HTTP_GET },
{ "/api/logs/http", "", api_logs, { false, true, FIFO_CIVETWEB }, true, HTTP_GET },

View File

@ -54,7 +54,7 @@ int api_info_host(struct ftl_conn *api);
int api_info_sensors(struct ftl_conn *api);
int api_info_version(struct ftl_conn *api);
int api_info_messages(struct ftl_conn *api);
int api_info_cache(struct ftl_conn *api);
int api_info_metrics(struct ftl_conn *api);
// Config methods
int api_config(struct ftl_conn *api);

View File

@ -206,24 +206,24 @@ components:
$ref: 'info.yaml#/components/examples/errors/messages/uri_error'
bad_request:
$ref: 'info.yaml#/components/examples/errors/messages/bad_request'
cache:
metrics:
get:
summary: Get cache info
summary: Get metrics info
tags:
- "FTL information"
operationId: "get_cacheinfo"
operationId: "get_metricsinfo"
description: |
This API hook returns live information about the DNS cache usage.
This API hook returns live information about the DNS and DHCP metrics.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: 'info.yaml#/components/schemas/cache'
$ref: 'info.yaml#/components/schemas/metrics'
examples:
cache:
$ref: 'info.yaml#/components/examples/cache'
metrics:
$ref: 'info.yaml#/components/examples/metrics'
'401':
description: Unauthorized
content:
@ -880,71 +880,134 @@ components:
type: string
description: HTML-formatted message
example: "Client <code>192.168.2.42</code> has been rate-limited for at least 5 seconds (current limit: 1000 queries per 60 seconds)"
cache:
metrics:
type: object
properties:
cache:
metrics:
type: object
description: Cache information
properties:
size:
type: integer
description: Cache size
inserted:
type: integer
description: Number of inserted entries
evicted:
type: integer
description: Number of evicted entries
expired:
type: integer
description: Number of expired entries
immortal:
type: integer
description: Number of immortal entries
optimized:
type: integer
description: Number of queries answered from stale cache entries
local:
type: integer
description: Number of queries answered locally
auth:
type: integer
description: Number of queries for authoritative zones
valid:
dns:
type: object
description: Number of valid entries
description: DNS metrics
properties:
a:
type: integer
description: Number of valid A (IPv4) entries
aaaa:
type: integer
description: Number of valid AAAA (IPv6) entries
cname:
type: integer
description: Number of valid CNAME entries
srv:
type: integer
description: Number of valid SRV entries
ds:
type: integer
description: Number of valid DS entries
dnskey:
type: integer
description: Number of valid DNSKEY entries
other:
type: integer
description: Number of valid other entries
extra:
cache:
type: object
description: Cache information
properties:
size:
type: integer
description: Cache size
inserted:
type: integer
description: Number of inserted entries
evicted:
type: integer
description: Number of evicted entries
expired:
type: integer
description: Number of expired entries
immortal:
type: integer
description: Number of immortal entries
content:
type: object
description: Number of valid DNS cache entries
properties:
a:
type: integer
description: Number of valid A (IPv4) entries
aaaa:
type: integer
description: Number of valid AAAA (IPv6) entries
cname:
type: integer
description: Number of valid CNAME entries
srv:
type: integer
description: Number of valid SRV entries
ds:
type: integer
description: Number of valid DS entries
dnskey:
type: integer
description: Number of valid DNSKEY entries
other:
type: integer
description: Number of valid other entries
replies:
type: object
properties:
forwarded:
type: integer
description: Number of forwarded queries
unanswered:
type: integer
description: Number of unanswered queries
local:
type: integer
description: Number of queries answered from local cache
optimized:
type: integer
description: Number of queries answered from local cache (stale entries)
auth:
type: integer
description: Number of queries for authoritative zones
sum:
type: integer
description: Total number of queries
dhcp:
type: object
description: DHCP metrics
properties:
forwarded:
ack:
type: integer
description: Number of forwarded queries
unanswered:
description: Number of DHCP ACKs
nak:
type: integer
description: Number of unanswered queries
description: Number of DHCP NAKs
decline:
type: integer
description: Number of DHCP declines
offer:
type: integer
description: Number of DHCP offers
discover:
type: integer
description: Number of DHCP discovers
inform:
type: integer
description: Number of DHCP informs
request:
type: integer
description: Number of DHCP requests
release:
type: integer
description: Number of DHCP releases
noanswer:
type: integer
description: Number of DHCP requests without answer
bootp:
type: integer
description: Number of BOOTP requests
pxe:
type: integer
description: Number of PXE requests
leases:
type: object
description: DHCP leases
properties:
allocated_4:
type: integer
description: Number of allocated IPv4 leases
pruned_4:
type: integer
description: Number of pruned IPv4 leases
allocated_6:
type: integer
description: Number of allocated IPv6 leases
pruned_6:
type: integer
description: Number of pruned IPv6 leases
examples:
errors:
messages:
@ -962,29 +1025,48 @@ components:
key: "bad_request"
message: "Invalid ID in path"
hint: "/api/info/messages/486741168746857468758478"
cache:
summary: Cache info
metrics:
summary: Server metrics
value:
cache:
size: 10000
inserted: 4060
evicted: 0
expired: 0
immortal: 0
optimized: 1
local: 84
auth: 0
valid:
a: 212
aaaa: 61
cname: 14
srv: 0
ds: 60
dnskey: 35
other: 14
extra:
forwarded: 46
unanswered: 0
metrics:
dns:
cache:
size: 10000
inserted: 4060
evicted: 0
expired: 0
immortal: 0
content:
a: 212
aaaa: 61
cname: 14
srv: 0
ds: 60
dnskey: 35
other: 14
replies:
optimized: 1
local: 84
auth: 0
forwarded: 46
unanswered: 0
dhcp:
ack: 0
nak: 0
decline: 0
offer: 0
discover: 0
inform: 0
request: 0
release: 0
noanswer: 0
bootp: 0
pxe: 0
leases:
allocated_4: 0
pruned_4: 0
allocated_6: 0
pruned_6: 0
parameters:
logs:

View File

@ -159,8 +159,8 @@ paths:
/info/messages:
$ref: 'info.yaml#/components/paths/messages'
/info/cache:
$ref: 'info.yaml#/components/paths/cache'
/info/metrics:
$ref: 'info.yaml#/components/paths/metrics'
/logs/dnsmasq:
$ref: 'logs.yaml#/components/paths/logs/dnsmasq'

View File

@ -48,8 +48,7 @@
// LONG_MIN, LONG_MAX
#include <limits.h>
// getCacheInformation()
#include "cache_info.h"
#include "metrics.h"
#include <dirent.h>
@ -874,36 +873,69 @@ int api_info_messages(struct ftl_conn *api)
return send_json_error(api, 405, "method_not_allowed", "Method not allowed", NULL);
}
int api_info_cache(struct ftl_conn *api)
int api_info_metrics(struct ftl_conn *api)
{
struct cache_info ci = { 0 };
get_dnsmasq_cache_info(&ci);
struct metrics metrics = { 0 };
get_dnsmasq_metrics(&metrics);
cJSON *cache = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(cache, "size", ci.cache_size);
JSON_ADD_NUMBER_TO_OBJECT(cache, "inserted", ci.cache_inserted);
JSON_ADD_NUMBER_TO_OBJECT(cache, "evicted", ci.cache_live_freed);
JSON_ADD_NUMBER_TO_OBJECT(cache, "optimized", ci.stale_answered);
JSON_ADD_NUMBER_TO_OBJECT(cache, "local", ci.local_answered);
JSON_ADD_NUMBER_TO_OBJECT(cache, "auth", ci.auth_answered);
cJSON *valid = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(valid, "a", ci.valid.a);
JSON_ADD_NUMBER_TO_OBJECT(valid, "aaaa", ci.valid.aaaa);
JSON_ADD_NUMBER_TO_OBJECT(valid, "cname", ci.valid.cname);
JSON_ADD_NUMBER_TO_OBJECT(valid, "srv", ci.valid.srv);
JSON_ADD_NUMBER_TO_OBJECT(valid, "ds", ci.valid.ds);
JSON_ADD_NUMBER_TO_OBJECT(valid, "dnskey", ci.valid.dnskey);
JSON_ADD_NUMBER_TO_OBJECT(valid, "other", ci.valid.other);
JSON_ADD_ITEM_TO_OBJECT(cache, "valid", valid);
JSON_ADD_NUMBER_TO_OBJECT(cache, "size", metrics.dns.cache.size);
JSON_ADD_NUMBER_TO_OBJECT(cache, "inserted", metrics.dns.cache.inserted);
JSON_ADD_NUMBER_TO_OBJECT(cache, "evicted", metrics.dns.cache.live_freed);
JSON_ADD_NUMBER_TO_OBJECT(cache, "expired", metrics.dns.cache.expired);
JSON_ADD_NUMBER_TO_OBJECT(cache, "immortal", metrics.dns.cache.immortal);
JSON_ADD_NUMBER_TO_OBJECT(cache, "expired", ci.expired);
JSON_ADD_NUMBER_TO_OBJECT(cache, "immortal", ci.immortal);
cJSON *content = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(content, "a", metrics.dns.cache.content.a);
JSON_ADD_NUMBER_TO_OBJECT(content, "aaaa", metrics.dns.cache.content.aaaa);
JSON_ADD_NUMBER_TO_OBJECT(content, "cname", metrics.dns.cache.content.cname);
JSON_ADD_NUMBER_TO_OBJECT(content, "srv", metrics.dns.cache.content.srv);
JSON_ADD_NUMBER_TO_OBJECT(content, "ds", metrics.dns.cache.content.ds);
JSON_ADD_NUMBER_TO_OBJECT(content, "dnskey", metrics.dns.cache.content.dnskey);
JSON_ADD_NUMBER_TO_OBJECT(content, "other", metrics.dns.cache.content.other);
JSON_ADD_ITEM_TO_OBJECT(cache, "content", content);
cJSON *extra = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(extra, "forwarded", ci.forwarded_queries);
JSON_ADD_NUMBER_TO_OBJECT(extra, "unanswered", ci.unanswered_queries);
JSON_ADD_ITEM_TO_OBJECT(cache, "extra", extra);
cJSON *replies = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(replies, "local", metrics.dns.local_answered);
JSON_ADD_NUMBER_TO_OBJECT(replies, "forwarded", metrics.dns.forwarded_queries);
JSON_ADD_NUMBER_TO_OBJECT(replies, "optimized", metrics.dns.stale_answered);
JSON_ADD_NUMBER_TO_OBJECT(replies, "unanswered", metrics.dns.unanswered_queries);
JSON_ADD_NUMBER_TO_OBJECT(replies, "auth", metrics.dns.auth_answered);
const int sum = metrics.dns.local_answered
+ metrics.dns.forwarded_queries
+ metrics.dns.stale_answered
+ metrics.dns.unanswered_queries
+ metrics.dns.auth_answered;
JSON_ADD_NUMBER_TO_OBJECT(replies, "sum", sum);
cJSON *dhcp = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "ack", metrics.dhcp.ack);
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "decline", metrics.dhcp.decline);
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "discover", metrics.dhcp.discover);
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "inform", metrics.dhcp.inform);
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "nak", metrics.dhcp.nak);
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "offer", metrics.dhcp.offer);
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "release", metrics.dhcp.release);
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "request", metrics.dhcp.request);
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "noanswer", metrics.dhcp.noanswer);
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "bootp", metrics.dhcp.bootp);
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "pxe", metrics.dhcp.pxe);
cJSON *leases = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(leases, "allocated_4", metrics.dhcp.leases.allocated_4);
JSON_ADD_NUMBER_TO_OBJECT(leases, "pruned_4", metrics.dhcp.leases.pruned_4);
JSON_ADD_NUMBER_TO_OBJECT(leases, "allocated_6", metrics.dhcp.leases.allocated_6);
JSON_ADD_NUMBER_TO_OBJECT(leases, "pruned_6", metrics.dhcp.leases.pruned_6);
JSON_ADD_ITEM_TO_OBJECT(dhcp, "leases", leases);
cJSON *dns = JSON_NEW_OBJECT();
JSON_ADD_ITEM_TO_OBJECT(dns, "cache", cache);
JSON_ADD_ITEM_TO_OBJECT(dns, "replies", replies);
cJSON *json = JSON_NEW_OBJECT();
JSON_ADD_ITEM_TO_OBJECT(json, "cache", cache);
JSON_SEND_OBJECT(json);
JSON_ADD_ITEM_TO_OBJECT(json, "dns", dns);
JSON_ADD_ITEM_TO_OBJECT(json, "dhcp", dhcp);
cJSON *json2 = JSON_NEW_OBJECT();
JSON_ADD_ITEM_TO_OBJECT(json2, "metrics", json);
JSON_SEND_OBJECT(json2);
}

View File

@ -1,50 +0,0 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2021 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* FTL cache_info prototypes
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#ifndef CACHE_INFO_H
#define CACHE_INFO_H
struct cache_info {
// <cache-size> is obvious
int cache_size;
// It means the resolver handled <cache-inserted> names lookups that
// needed to be sent to upstream servers and that <cache-live-freed>
// was thrown out of the cache before reaching the end of its
// time-to-live, to make room for a newer name.
// For <cache-live-freed>, smaller is better. New queries are always
// cached. If the cache is full with entries which haven't reached
// the end of their time-to-live, then the entry which hasn't been
// looked up for the longest time is evicted.
int cache_live_freed;
int cache_inserted;
int local_answered;
int stale_answered;
int auth_answered;
// <valid> are cache entries with positive remaining TTL
struct valid {
int a;
int aaaa;
int cname;
int srv;
int ds;
int dnskey;
int other;
} valid;
// <expired> cache entries (to be removed when space is needed)
int expired;
// <immortal> cache records never expire (e.g. from /etc/hosts)
int immortal;
// extra properties of the cache
int unanswered_queries;
int forwarded_queries;
};
void get_dnsmasq_cache_info(struct cache_info *ci);
#endif // CACHE_INFO_H

View File

@ -1768,44 +1768,60 @@ static char *sanitise(char *name)
}
/***************** Pi-hole modification *****************/
void get_dnsmasq_cache_info(struct cache_info *ci)
void get_dnsmasq_metrics(struct metrics *ci)
{
memset(ci, 0, sizeof(struct cache_info));
ci->cache_size = daemon->cachesize;
ci->cache_inserted = daemon->metrics[METRIC_DNS_CACHE_INSERTED];
ci->cache_live_freed = daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED];
ci->local_answered = daemon->metrics[METRIC_DNS_LOCAL_ANSWERED];
ci->stale_answered = daemon->metrics[METRIC_DNS_STALE_ANSWERED];
ci->auth_answered = daemon->metrics[METRIC_DNS_AUTH_ANSWERED];
ci->unanswered_queries = daemon->metrics[METRIC_DNS_UNANSWERED_QUERY];
ci->forwarded_queries = daemon->metrics[METRIC_DNS_QUERIES_FORWARDED];
memset(ci, 0, sizeof(struct metrics));
ci->dns.cache.size = daemon->cachesize;
ci->dns.cache.inserted = daemon->metrics[METRIC_DNS_CACHE_INSERTED];
ci->dns.cache.live_freed = daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED];
ci->dns.local_answered = daemon->metrics[METRIC_DNS_LOCAL_ANSWERED];
ci->dns.stale_answered = daemon->metrics[METRIC_DNS_STALE_ANSWERED];
ci->dns.auth_answered = daemon->metrics[METRIC_DNS_AUTH_ANSWERED];
ci->dns.unanswered_queries = daemon->metrics[METRIC_DNS_UNANSWERED_QUERY];
ci->dns.forwarded_queries = daemon->metrics[METRIC_DNS_QUERIES_FORWARDED];
const time_t now = time(NULL);
for (int i=0; i < hash_size; i++)
for (struct crec *cache = hash_table[i]; cache; cache = cache->hash_next)
if(cache->ttd >= now || cache->flags & F_IMMORTAL)
{
if (cache->flags & F_IPV4)
ci->valid.a++;
ci->dns.cache.content.a++;
else if (cache->flags & F_IPV6)
ci->valid.aaaa++;
ci->dns.cache.content.aaaa++;
else if (cache->flags & F_CNAME)
ci->valid.cname++;
ci->dns.cache.content.cname++;
else if (cache->flags & F_SRV)
ci->valid.srv++;
ci->dns.cache.content.srv++;
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
ci->valid.ds++;
ci->dns.cache.content.ds++;
else if (cache->flags & F_DNSKEY)
ci->valid.dnskey++;
ci->dns.cache.content.dnskey++;
#endif
else
ci->valid.other++;
ci->dns.cache.content.other++;
if(cache->flags & F_IMMORTAL)
ci->immortal++;
ci->dns.cache.immortal++;
}
else
ci->expired++;
ci->dns.cache.expired++;
ci->dhcp.bootp = daemon->metrics[METRIC_BOOTP];
ci->dhcp.pxe = daemon->metrics[METRIC_PXE];
ci->dhcp.ack = daemon->metrics[METRIC_DHCPACK];
ci->dhcp.decline = daemon->metrics[METRIC_DHCPDECLINE];
ci->dhcp.discover = daemon->metrics[METRIC_DHCPDISCOVER];
ci->dhcp.inform = daemon->metrics[METRIC_DHCPINFORM];
ci->dhcp.nak = daemon->metrics[METRIC_DHCPNAK];
ci->dhcp.offer = daemon->metrics[METRIC_DHCPOFFER];
ci->dhcp.release = daemon->metrics[METRIC_DHCPRELEASE];
ci->dhcp.request = daemon->metrics[METRIC_DHCPREQUEST];
ci->dhcp.noanswer = daemon->metrics[METRIC_NOANSWER];
ci->dhcp.leases.allocated_4 = daemon->metrics[METRIC_LEASES_ALLOCATED_4];
ci->dhcp.leases.pruned_4 = daemon->metrics[METRIC_LEASES_PRUNED_4];
ci->dhcp.leases.allocated_6 = daemon->metrics[METRIC_LEASES_ALLOCATED_6];
ci->dhcp.leases.pruned_6 = daemon->metrics[METRIC_LEASES_PRUNED_6];
}
/********************************************************/

View File

@ -1312,7 +1312,7 @@ void next_uid(struct crec *crecp);
/********************************************* Pi-hole modification ***********************************************/
#define log_query(flags,name,addr,arg,type) _log_query(flags, name, addr, arg, type, __FILE__, __LINE__)
void _log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type, const char* file, const int line);
#include "../cache_info.h"
#include "../metrics.h"
/******************************************************************************************************************/
char *record_source(unsigned int index);
int cache_find_non_terminal(char *name, time_t now);

View File

@ -14,7 +14,7 @@
#include <stdbool.h>
#include "edns0.h"
#include "cache_info.h"
#include "metrics.h"
extern unsigned char* pihole_privacylevel;
enum protocol { TCP, UDP, INTERNAL };

77
src/metrics.h Normal file
View File

@ -0,0 +1,77 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2021 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* FTL struct metrics definition
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#ifndef METRICS_H
#define METRICS_H
struct metrics
{
struct dns
{
struct cache
{
// <cache-size> is obvious
int size;
// It means the resolver handled <cache-inserted> names lookups that
// needed to be sent to upstream servers and that <cache-live-freed>
// was thrown out of the cache before reaching the end of its
// time-to-live, to make room for a newer name.
// For <cache-live-freed>, smaller is better. New queries are always
// cached. If the cache is full with entries which haven't reached
// the end of their time-to-live, then the entry which hasn't been
// looked up for the longest time is evicted.
int live_freed;
int inserted;
// <expired> cache entries (to be removed when space is needed)
int expired;
// <immortal> cache records never expire (e.g. from /etc/hosts)
int immortal;
// <content> are cache entries with positive remaining TTL
struct content
{
int a;
int aaaa;
int cname;
int srv;
int ds;
int dnskey;
int other;
} content;
} cache;
int local_answered;
int forwarded_queries;
int stale_answered;
int unanswered_queries;
int auth_answered;
} dns;
struct dhcp {
int ack;
int decline;
int discover;
int inform;
int nak;
int offer;
int release;
int request;
int noanswer;
struct leases
{
int allocated_4;
int pruned_4;
int allocated_6;
int pruned_6;
} leases;
int bootp;
int pxe;
} dhcp;
};
void get_dnsmasq_metrics(struct metrics *ci);
#endif // METRICS_H