Skip to content

Commit bb47630

Browse files
authored
Merge pull request zephyrproject-rtos#12 from converge-io/lucas/dns-cache
net: dns: Add DNS cache for improved performance
2 parents 6f84f6a + d7e7f6e commit bb47630

File tree

5 files changed

+321
-10
lines changed

5 files changed

+321
-10
lines changed

subsys/net/lib/dns/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ zephyr_library_sources(dns_pack.c)
77

88
zephyr_library_sources_ifdef(CONFIG_DNS_RESOLVER resolve.c)
99
zephyr_library_sources_ifdef(CONFIG_DNS_SD dns_sd.c)
10+
zephyr_library_sources_ifdef(CONFIG_DNS_RESOLVER_CACHE dns_cache.c)
1011

1112
if(CONFIG_MDNS_RESPONDER)
1213
zephyr_library_sources(mdns_responder.c)

subsys/net/lib/dns/Kconfig

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ config DNS_RESOLVER_MAX_SERVERS
6262
DNS server is enough. Each connection to DNS server will use one
6363
network context.
6464

65+
config DNS_RESOLVER_MAX_QUERY_LEN
66+
int "Max length of a DNS query"
67+
range 1 255
68+
default 255
69+
help
70+
Max length of a DNS query that should be looked up including the
71+
trailing 0. So e.g. "example.com" would have a query len of 12.
72+
6573
menuconfig DNS_SERVER_IP_ADDRESSES
6674
bool "Set DNS server IP addresses"
6775
help
@@ -121,6 +129,26 @@ module-str = Log level for DNS resolver
121129
module-help = Enables DNS resolver code to output debug messages.
122130
source "subsys/net/Kconfig.template.log_config.net"
123131

132+
menuconfig DNS_RESOLVER_CACHE
133+
bool "DNS resolver cache"
134+
help
135+
This option enables the dns resolver cache. DNS queries
136+
will be cached based on TTL and delivered from cache
137+
whenever possible. This reduces network usage.
138+
139+
if DNS_RESOLVER_CACHE
140+
141+
config DNS_RESOLVER_CACHE_MAX_ENTRIES
142+
int "Number of cache entries supported by the dns cache"
143+
default 6
144+
help
145+
This defines how many entries the DNS cache can hold. If
146+
not enough entries for caching are available the oldest
147+
entry gets replaced. Adjusting this value will affect
148+
RAM usage.
149+
150+
endif # DNS_RESOLVER_CACHE
151+
124152
endif # DNS_RESOLVER
125153

126154
config MDNS_RESPONDER

subsys/net/lib/dns/dns_cache.c

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (c) 2024 Endress+Hauser AG
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/net/dns_resolve.h>
8+
#include "dns_cache.h"
9+
10+
LOG_MODULE_REGISTER(net_dns_cache, CONFIG_DNS_RESOLVER_LOG_LEVEL);
11+
12+
static void dns_cache_clean(struct dns_cache const *cache);
13+
14+
int dns_cache_flush(struct dns_cache *cache)
15+
{
16+
k_mutex_lock(cache->lock, K_FOREVER);
17+
for (size_t i = 0; i < cache->size; i++) {
18+
cache->entries[i].in_use = false;
19+
}
20+
k_mutex_unlock(cache->lock);
21+
22+
return 0;
23+
}
24+
25+
int dns_cache_add(struct dns_cache *cache, char const *query, struct dns_addrinfo const *addrinfo,
26+
uint32_t ttl)
27+
{
28+
k_timepoint_t closest_to_expiry = sys_timepoint_calc(K_FOREVER);
29+
size_t index_to_replace = 0;
30+
bool found_empty = false;
31+
32+
if (cache == NULL || query == NULL || addrinfo == NULL || ttl == 0) {
33+
return -EINVAL;
34+
}
35+
36+
if (strlen(query) >= CONFIG_DNS_RESOLVER_MAX_QUERY_LEN) {
37+
NET_WARN("Query string to big to be processed %u >= "
38+
"CONFIG_DNS_RESOLVER_MAX_QUERY_LEN",
39+
strlen(query));
40+
return -EINVAL;
41+
}
42+
43+
k_mutex_lock(cache->lock, K_FOREVER);
44+
45+
NET_DBG("Add \"%s\" with TTL %" PRIu32, query, ttl);
46+
47+
dns_cache_clean(cache);
48+
49+
for (size_t i = 0; i < cache->size; i++) {
50+
if (!cache->entries[i].in_use) {
51+
index_to_replace = i;
52+
found_empty = true;
53+
break;
54+
} else if (sys_timepoint_cmp(closest_to_expiry, cache->entries[i].expiry) > 0) {
55+
index_to_replace = i;
56+
closest_to_expiry = cache->entries[i].expiry;
57+
}
58+
}
59+
60+
if (!found_empty) {
61+
NET_DBG("Overwrite \"%s\"", cache->entries[index_to_replace].query);
62+
}
63+
64+
strncpy(cache->entries[index_to_replace].query, query,
65+
CONFIG_DNS_RESOLVER_MAX_QUERY_LEN - 1);
66+
cache->entries[index_to_replace].data = *addrinfo;
67+
cache->entries[index_to_replace].expiry = sys_timepoint_calc(K_SECONDS(ttl));
68+
cache->entries[index_to_replace].in_use = true;
69+
70+
k_mutex_unlock(cache->lock);
71+
72+
return 0;
73+
}
74+
75+
int dns_cache_remove(struct dns_cache *cache, char const *query)
76+
{
77+
NET_DBG("Remove all entries with query \"%s\"", query);
78+
if (strlen(query) >= CONFIG_DNS_RESOLVER_MAX_QUERY_LEN) {
79+
NET_WARN("Query string to big to be processed %u >= "
80+
"CONFIG_DNS_RESOLVER_MAX_QUERY_LEN",
81+
strlen(query));
82+
return -EINVAL;
83+
}
84+
85+
k_mutex_lock(cache->lock, K_FOREVER);
86+
87+
dns_cache_clean(cache);
88+
89+
for (size_t i = 0; i < cache->size; i++) {
90+
if (cache->entries[i].in_use && strcmp(cache->entries[i].query, query) == 0) {
91+
cache->entries[i].in_use = false;
92+
}
93+
}
94+
95+
k_mutex_unlock(cache->lock);
96+
97+
return 0;
98+
}
99+
100+
int dns_cache_find(struct dns_cache const *cache, const char *query, struct dns_addrinfo *addrinfo,
101+
size_t addrinfo_array_len)
102+
{
103+
size_t found = 0;
104+
105+
NET_DBG("Find \"%s\"", query);
106+
if (cache == NULL || query == NULL || addrinfo == NULL || addrinfo_array_len <= 0) {
107+
return -EINVAL;
108+
}
109+
if (strlen(query) >= CONFIG_DNS_RESOLVER_MAX_QUERY_LEN) {
110+
NET_WARN("Query string to big to be processed %u >= "
111+
"CONFIG_DNS_RESOLVER_MAX_QUERY_LEN",
112+
strlen(query));
113+
return -EINVAL;
114+
}
115+
116+
k_mutex_lock(cache->lock, K_FOREVER);
117+
118+
dns_cache_clean(cache);
119+
120+
for (size_t i = 0; i < cache->size; i++) {
121+
if (!cache->entries[i].in_use) {
122+
continue;
123+
}
124+
if (strcmp(cache->entries[i].query, query) != 0) {
125+
continue;
126+
}
127+
if (found >= addrinfo_array_len) {
128+
NET_WARN("Found \"%s\" but not enough space in provided buffer.", query);
129+
found++;
130+
} else {
131+
addrinfo[found] = cache->entries[i].data;
132+
found++;
133+
NET_DBG("Found \"%s\"", query);
134+
}
135+
}
136+
137+
k_mutex_unlock(cache->lock);
138+
139+
if (found > addrinfo_array_len) {
140+
return -ENOSR;
141+
}
142+
143+
if (found == 0) {
144+
NET_DBG("Could not find \"%s\"", query);
145+
}
146+
return found;
147+
}
148+
149+
/* Needs to be called when lock is already acquired */
150+
static void dns_cache_clean(struct dns_cache const *cache)
151+
{
152+
for (size_t i = 0; i < cache->size; i++) {
153+
if (!cache->entries[i].in_use) {
154+
continue;
155+
}
156+
157+
if (sys_timepoint_expired(cache->entries[i].expiry)) {
158+
NET_DBG("Remove \"%s\"", cache->entries[i].query);
159+
cache->entries[i].in_use = false;
160+
}
161+
}
162+
}

subsys/net/lib/dns/dns_cache.h

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/** @file
2+
* @brief DNS cache
3+
*
4+
* An cache holding dns records for faster dns resolving.
5+
*/
6+
7+
/*
8+
* Copyright (c) 2024 Endress+Hauser AG
9+
*
10+
* SPDX-License-Identifier: Apache-2.0
11+
*/
12+
13+
#ifndef ZEPHYR_INCLUDE_NET_DNS_CACHE_H_
14+
#define ZEPHYR_INCLUDE_NET_DNS_CACHE_H_
15+
16+
#include <stdint.h>
17+
#include <zephyr/net/dns_resolve.h>
18+
#include <zephyr/kernel.h>
19+
#include <zephyr/sys_clock.h>
20+
21+
struct dns_cache_entry {
22+
char query[CONFIG_DNS_RESOLVER_MAX_QUERY_LEN];
23+
struct dns_addrinfo data;
24+
k_timepoint_t expiry;
25+
bool in_use;
26+
};
27+
28+
struct dns_cache {
29+
size_t size;
30+
struct dns_cache_entry *entries;
31+
struct k_mutex *lock;
32+
};
33+
34+
/**
35+
* @brief Statically define and initialize a DNS queue.
36+
*
37+
* The cache can be accessed outside the module where it is defined using:
38+
*
39+
* @code extern struct dns_cache <name>; @endcode
40+
*
41+
* @param name Name of the cache.
42+
*/
43+
#define DNS_CACHE_DEFINE(name, cache_size) \
44+
static K_MUTEX_DEFINE(name##_mutex); \
45+
static struct dns_cache_entry name##_entries[cache_size]; \
46+
static struct dns_cache name = { \
47+
.entries = name##_entries, .size = cache_size, .lock = &name##_mutex};
48+
49+
/**
50+
* @brief Flushes the dns cache removing all its entries.
51+
*
52+
* @param cache Cache to be flushed
53+
* @retval 0 on success
54+
* @retval On error, a negative value is returned.
55+
*/
56+
int dns_cache_flush(struct dns_cache *cache);
57+
58+
/**
59+
* @brief Adds a new entry to the dns cache removing the one closest to expiry
60+
* if no free space is available.
61+
*
62+
* @param cache Cache where the entry should be added.
63+
* @param query Query which should be persisted in the cache.
64+
* @param addrinfo Addrinfo resulting from the query which will be returned
65+
* upon cache hit.
66+
* @param ttl Time to live for the entry in seconds. This usually represents
67+
* the TTL of the RR.
68+
* @retval 0 on success
69+
* @retval On error, a negative value is returned.
70+
*/
71+
int dns_cache_add(struct dns_cache *cache, char const *query, struct dns_addrinfo const *addrinfo,
72+
uint32_t ttl);
73+
74+
/**
75+
* @brief Removes all entries with the given query
76+
*
77+
* @param cache Cache where the entries should be removed.
78+
* @param query Query which should be searched for.
79+
* @retval 0 on success
80+
* @retval On error, a negative value is returned.
81+
*/
82+
int dns_cache_remove(struct dns_cache *cache, char const *query);
83+
84+
/**
85+
* @brief Tries to find the specified query entry within the cache.
86+
*
87+
* @param cache Cache where the entry should be searched.
88+
* @param query Query which should be searched for.
89+
* @param addrinfo dns_addrinfo array which will be written if the query was found.
90+
* @param addrinfo_array_len Array size of the dns_addrinfo array
91+
* @retval on success the amount of dns_addrinfo written into the addrinfo array will be returned.
92+
* A cache miss will therefore return a 0.
93+
* @retval On error a negative value is returned.
94+
* -ENOSR means there was not enough space in the addrinfo array to accommodate all cache hits the
95+
* array will however be filled with valid data.
96+
*/
97+
int dns_cache_find(struct dns_cache const *cache, const char *query, struct dns_addrinfo *addrinfo,
98+
size_t addrinfo_array_len);
99+
100+
#endif /* ZEPHYR_INCLUDE_NET_DNS_CACHE_H_ */

0 commit comments

Comments
 (0)