forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request zephyrproject-rtos#12 from converge-io/lucas/dns-c…
…ache net: dns: Add DNS cache for improved performance
- Loading branch information
Showing
5 changed files
with
321 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
/* | ||
* Copyright (c) 2024 Endress+Hauser AG | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <zephyr/net/dns_resolve.h> | ||
#include "dns_cache.h" | ||
|
||
LOG_MODULE_REGISTER(net_dns_cache, CONFIG_DNS_RESOLVER_LOG_LEVEL); | ||
|
||
static void dns_cache_clean(struct dns_cache const *cache); | ||
|
||
int dns_cache_flush(struct dns_cache *cache) | ||
{ | ||
k_mutex_lock(cache->lock, K_FOREVER); | ||
for (size_t i = 0; i < cache->size; i++) { | ||
cache->entries[i].in_use = false; | ||
} | ||
k_mutex_unlock(cache->lock); | ||
|
||
return 0; | ||
} | ||
|
||
int dns_cache_add(struct dns_cache *cache, char const *query, struct dns_addrinfo const *addrinfo, | ||
uint32_t ttl) | ||
{ | ||
k_timepoint_t closest_to_expiry = sys_timepoint_calc(K_FOREVER); | ||
size_t index_to_replace = 0; | ||
bool found_empty = false; | ||
|
||
if (cache == NULL || query == NULL || addrinfo == NULL || ttl == 0) { | ||
return -EINVAL; | ||
} | ||
|
||
if (strlen(query) >= CONFIG_DNS_RESOLVER_MAX_QUERY_LEN) { | ||
NET_WARN("Query string to big to be processed %u >= " | ||
"CONFIG_DNS_RESOLVER_MAX_QUERY_LEN", | ||
strlen(query)); | ||
return -EINVAL; | ||
} | ||
|
||
k_mutex_lock(cache->lock, K_FOREVER); | ||
|
||
NET_DBG("Add \"%s\" with TTL %" PRIu32, query, ttl); | ||
|
||
dns_cache_clean(cache); | ||
|
||
for (size_t i = 0; i < cache->size; i++) { | ||
if (!cache->entries[i].in_use) { | ||
index_to_replace = i; | ||
found_empty = true; | ||
break; | ||
} else if (sys_timepoint_cmp(closest_to_expiry, cache->entries[i].expiry) > 0) { | ||
index_to_replace = i; | ||
closest_to_expiry = cache->entries[i].expiry; | ||
} | ||
} | ||
|
||
if (!found_empty) { | ||
NET_DBG("Overwrite \"%s\"", cache->entries[index_to_replace].query); | ||
} | ||
|
||
strncpy(cache->entries[index_to_replace].query, query, | ||
CONFIG_DNS_RESOLVER_MAX_QUERY_LEN - 1); | ||
cache->entries[index_to_replace].data = *addrinfo; | ||
cache->entries[index_to_replace].expiry = sys_timepoint_calc(K_SECONDS(ttl)); | ||
cache->entries[index_to_replace].in_use = true; | ||
|
||
k_mutex_unlock(cache->lock); | ||
|
||
return 0; | ||
} | ||
|
||
int dns_cache_remove(struct dns_cache *cache, char const *query) | ||
{ | ||
NET_DBG("Remove all entries with query \"%s\"", query); | ||
if (strlen(query) >= CONFIG_DNS_RESOLVER_MAX_QUERY_LEN) { | ||
NET_WARN("Query string to big to be processed %u >= " | ||
"CONFIG_DNS_RESOLVER_MAX_QUERY_LEN", | ||
strlen(query)); | ||
return -EINVAL; | ||
} | ||
|
||
k_mutex_lock(cache->lock, K_FOREVER); | ||
|
||
dns_cache_clean(cache); | ||
|
||
for (size_t i = 0; i < cache->size; i++) { | ||
if (cache->entries[i].in_use && strcmp(cache->entries[i].query, query) == 0) { | ||
cache->entries[i].in_use = false; | ||
} | ||
} | ||
|
||
k_mutex_unlock(cache->lock); | ||
|
||
return 0; | ||
} | ||
|
||
int dns_cache_find(struct dns_cache const *cache, const char *query, struct dns_addrinfo *addrinfo, | ||
size_t addrinfo_array_len) | ||
{ | ||
size_t found = 0; | ||
|
||
NET_DBG("Find \"%s\"", query); | ||
if (cache == NULL || query == NULL || addrinfo == NULL || addrinfo_array_len <= 0) { | ||
return -EINVAL; | ||
} | ||
if (strlen(query) >= CONFIG_DNS_RESOLVER_MAX_QUERY_LEN) { | ||
NET_WARN("Query string to big to be processed %u >= " | ||
"CONFIG_DNS_RESOLVER_MAX_QUERY_LEN", | ||
strlen(query)); | ||
return -EINVAL; | ||
} | ||
|
||
k_mutex_lock(cache->lock, K_FOREVER); | ||
|
||
dns_cache_clean(cache); | ||
|
||
for (size_t i = 0; i < cache->size; i++) { | ||
if (!cache->entries[i].in_use) { | ||
continue; | ||
} | ||
if (strcmp(cache->entries[i].query, query) != 0) { | ||
continue; | ||
} | ||
if (found >= addrinfo_array_len) { | ||
NET_WARN("Found \"%s\" but not enough space in provided buffer.", query); | ||
found++; | ||
} else { | ||
addrinfo[found] = cache->entries[i].data; | ||
found++; | ||
NET_DBG("Found \"%s\"", query); | ||
} | ||
} | ||
|
||
k_mutex_unlock(cache->lock); | ||
|
||
if (found > addrinfo_array_len) { | ||
return -ENOSR; | ||
} | ||
|
||
if (found == 0) { | ||
NET_DBG("Could not find \"%s\"", query); | ||
} | ||
return found; | ||
} | ||
|
||
/* Needs to be called when lock is already acquired */ | ||
static void dns_cache_clean(struct dns_cache const *cache) | ||
{ | ||
for (size_t i = 0; i < cache->size; i++) { | ||
if (!cache->entries[i].in_use) { | ||
continue; | ||
} | ||
|
||
if (sys_timepoint_expired(cache->entries[i].expiry)) { | ||
NET_DBG("Remove \"%s\"", cache->entries[i].query); | ||
cache->entries[i].in_use = false; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/** @file | ||
* @brief DNS cache | ||
* | ||
* An cache holding dns records for faster dns resolving. | ||
*/ | ||
|
||
/* | ||
* Copyright (c) 2024 Endress+Hauser AG | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#ifndef ZEPHYR_INCLUDE_NET_DNS_CACHE_H_ | ||
#define ZEPHYR_INCLUDE_NET_DNS_CACHE_H_ | ||
|
||
#include <stdint.h> | ||
#include <zephyr/net/dns_resolve.h> | ||
#include <zephyr/kernel.h> | ||
#include <zephyr/sys_clock.h> | ||
|
||
struct dns_cache_entry { | ||
char query[CONFIG_DNS_RESOLVER_MAX_QUERY_LEN]; | ||
struct dns_addrinfo data; | ||
k_timepoint_t expiry; | ||
bool in_use; | ||
}; | ||
|
||
struct dns_cache { | ||
size_t size; | ||
struct dns_cache_entry *entries; | ||
struct k_mutex *lock; | ||
}; | ||
|
||
/** | ||
* @brief Statically define and initialize a DNS queue. | ||
* | ||
* The cache can be accessed outside the module where it is defined using: | ||
* | ||
* @code extern struct dns_cache <name>; @endcode | ||
* | ||
* @param name Name of the cache. | ||
*/ | ||
#define DNS_CACHE_DEFINE(name, cache_size) \ | ||
static K_MUTEX_DEFINE(name##_mutex); \ | ||
static struct dns_cache_entry name##_entries[cache_size]; \ | ||
static struct dns_cache name = { \ | ||
.entries = name##_entries, .size = cache_size, .lock = &name##_mutex}; | ||
|
||
/** | ||
* @brief Flushes the dns cache removing all its entries. | ||
* | ||
* @param cache Cache to be flushed | ||
* @retval 0 on success | ||
* @retval On error, a negative value is returned. | ||
*/ | ||
int dns_cache_flush(struct dns_cache *cache); | ||
|
||
/** | ||
* @brief Adds a new entry to the dns cache removing the one closest to expiry | ||
* if no free space is available. | ||
* | ||
* @param cache Cache where the entry should be added. | ||
* @param query Query which should be persisted in the cache. | ||
* @param addrinfo Addrinfo resulting from the query which will be returned | ||
* upon cache hit. | ||
* @param ttl Time to live for the entry in seconds. This usually represents | ||
* the TTL of the RR. | ||
* @retval 0 on success | ||
* @retval On error, a negative value is returned. | ||
*/ | ||
int dns_cache_add(struct dns_cache *cache, char const *query, struct dns_addrinfo const *addrinfo, | ||
uint32_t ttl); | ||
|
||
/** | ||
* @brief Removes all entries with the given query | ||
* | ||
* @param cache Cache where the entries should be removed. | ||
* @param query Query which should be searched for. | ||
* @retval 0 on success | ||
* @retval On error, a negative value is returned. | ||
*/ | ||
int dns_cache_remove(struct dns_cache *cache, char const *query); | ||
|
||
/** | ||
* @brief Tries to find the specified query entry within the cache. | ||
* | ||
* @param cache Cache where the entry should be searched. | ||
* @param query Query which should be searched for. | ||
* @param addrinfo dns_addrinfo array which will be written if the query was found. | ||
* @param addrinfo_array_len Array size of the dns_addrinfo array | ||
* @retval on success the amount of dns_addrinfo written into the addrinfo array will be returned. | ||
* A cache miss will therefore return a 0. | ||
* @retval On error a negative value is returned. | ||
* -ENOSR means there was not enough space in the addrinfo array to accommodate all cache hits the | ||
* array will however be filled with valid data. | ||
*/ | ||
int dns_cache_find(struct dns_cache const *cache, const char *query, struct dns_addrinfo *addrinfo, | ||
size_t addrinfo_array_len); | ||
|
||
#endif /* ZEPHYR_INCLUDE_NET_DNS_CACHE_H_ */ |
Oops, something went wrong.