Reimplement DNS cache metrics in the light of arbitrary RRTYPE caching

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER 2023-04-01 16:10:49 +02:00
parent a1332582ff
commit c05502f24a
No known key found for this signature in database
GPG Key ID: 00135ACBD90B28DD
4 changed files with 110 additions and 51 deletions

View File

@ -914,27 +914,20 @@ components:
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
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
type: array
description: Array of valid DNS cache entries
items:
type: object
properties:
type:
type: integer
description: RR type of cache entry
name:
type: string
description: RR name of cache entry
count:
type: string
description: Number of cache entries for this RR type
replies:
type: object
properties:
@ -1038,12 +1031,24 @@ components:
expired: 0
immortal: 0
content:
a: 212
aaaa: 61
cname: 14
ds: 60
dnskey: 35
other: 14
- type: 0
name: "OTHER"
count: 0
- type: 1
name: "A"
count: 17
- type: 28
name: "AAAA"
count: 5
- type: 5
name: "CNAME"
count: 2
- type: 43
name: "DS"
count: 15
- type: 48
name: "DNSKEY"
count: 16
replies:
optimized: 1
local: 84

View File

@ -920,13 +920,20 @@ int api_info_metrics(struct ftl_conn *api)
JSON_ADD_NUMBER_TO_OBJECT(cache, "expired", metrics.dns.cache.expired);
JSON_ADD_NUMBER_TO_OBJECT(cache, "immortal", metrics.dns.cache.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, "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);
cJSON *content = JSON_NEW_ARRAY();
for(unsigned int i = 0; i < RRTYPES; i++)
{
// Skip empty entries
if(metrics.dns.cache.content[i].count == 0)
continue;
// Add this entry to the array
cJSON *obj = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(obj, "type", metrics.dns.cache.content[i].type);
JSON_REF_STR_IN_OBJECT(obj, "name", rrtype_name(metrics.dns.cache.content[i].type));
JSON_ADD_NUMBER_TO_OBJECT(obj, "count", metrics.dns.cache.content[i].count);
JSON_ADD_ITEM_TO_ARRAY(content, obj);
}
JSON_ADD_ITEM_TO_OBJECT(cache, "content", content);
cJSON *replies = JSON_NEW_OBJECT();

View File

@ -146,6 +146,21 @@ unsigned short rrtype(char *in)
return 0;
}
/* Pi-hole function: return name of RR type */
const char *rrtype_name(unsigned short type)
{
unsigned int i;
if(type == 0)
return "OTHER";
for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
if (typestr[i].type == type)
return typestr[i].name;
return NULL;
}
void next_uid(struct crec *crecp)
{
static unsigned int uid = 0;
@ -1865,7 +1880,16 @@ static void dump_cache_entry(struct crec *cache, time_t now)
/***************** Pi-hole modification *****************/
void get_dnsmasq_metrics(struct metrics *ci)
{
// Prepare the metrics struct
memset(ci, 0, sizeof(struct metrics));
ci->dns.cache.content[RRTYPE_OTHER].type = 0;
ci->dns.cache.content[RRTYPE_A].type = T_A; // A
ci->dns.cache.content[RRTYPE_AAAA].type = T_AAAA; // AAAA
ci->dns.cache.content[RRTYPE_CNAME].type = T_CNAME; // CNAME
ci->dns.cache.content[RRTYPE_DS].type = T_DS; // DNSKEY
ci->dns.cache.content[RRTYPE_DNSKEY].type = T_DNSKEY; // DNSKEY
// General DNS cache 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];
@ -1874,30 +1898,44 @@ void get_dnsmasq_metrics(struct metrics *ci)
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];
// DNS cache content metrics
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->dns.cache.content.a++;
ci->dns.cache.content[RRTYPE_A].count++;
else if (cache->flags & F_IPV6)
ci->dns.cache.content.aaaa++;
ci->dns.cache.content[RRTYPE_AAAA].count++;
else if (cache->flags & F_CNAME)
ci->dns.cache.content.cname++;
ci->dns.cache.content[RRTYPE_CNAME].count++;
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
ci->dns.cache.content.ds++;
ci->dns.cache.content[RRTYPE_DS].count++;
else if (cache->flags & F_DNSKEY)
ci->dns.cache.content.dnskey++;
ci->dns.cache.content[RRTYPE_DNSKEY].count++;
#endif
else if(cache->flags & F_RR)
{
// Find the first empty slot or the slot with the same type
for(unsigned int i = RRTYPE_MAX; i < RRTYPES; i++)
{
if(ci->dns.cache.content[i].type == cache->addr.rr.rrtype || ci->dns.cache.content[i].type == 0)
{
ci->dns.cache.content[i].type = cache->addr.rr.rrtype;
ci->dns.cache.content[i].count++;
break;
}
}
}
else
ci->dns.cache.content.other++;
// cache->flags & F_RR --> cache->addr.rr.rrtype
// Needs (USHRT_MAX*sizeof(int)) bytes of memory
ci->dns.cache.content[RRTYPE_OTHER].count++;
if(cache->flags & F_IMMORTAL)
ci->dns.cache.immortal++;
}
else
ci->dns.cache.expired++;

View File

@ -10,6 +10,20 @@
#ifndef METRICS_H
#define METRICS_H
// defined in src/dnsmasq/cache.c
const char *rrtype_name(unsigned short type);
enum rrtype_array {
RRTYPE_OTHER = 0,
RRTYPE_A,
RRTYPE_AAAA,
RRTYPE_CNAME,
RRTYPE_DS,
RRTYPE_DNSKEY,
RRTYPE_MAX
};
#define RRTYPES RRTYPE_MAX+10
struct metrics
{
struct dns
@ -33,15 +47,10 @@ struct metrics
// <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 ds;
int dnskey;
int other;
} content;
struct content {
uint16_t type;
int count;
} content[RRTYPES];
} cache;
int local_answered;
int forwarded_queries;