Instead of using 32 bit enum values for event numbers, convert the code to use 64 bit long bit fields. This means that the user API is changed to use 64 bit event values instead of 32 bit event values. Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
288 lines
5.5 KiB
C
288 lines
5.5 KiB
C
/** @file
|
|
* @brief IPv4/6 PMTU related functions
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2024 Nordic Semiconductor
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(net_pmtu, CONFIG_NET_PMTU_LOG_LEVEL);
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/net/net_mgmt.h>
|
|
#include <zephyr/net/net_if.h>
|
|
#include "pmtu.h"
|
|
|
|
#if defined(CONFIG_NET_IPV4_PMTU)
|
|
#define NET_IPV4_PMTU_ENTRIES CONFIG_NET_IPV4_PMTU_DESTINATION_CACHE_ENTRIES
|
|
#else
|
|
#define NET_IPV4_PMTU_ENTRIES 0
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_IPV6_PMTU)
|
|
#define NET_IPV6_PMTU_ENTRIES CONFIG_NET_IPV6_PMTU_DESTINATION_CACHE_ENTRIES
|
|
#else
|
|
#define NET_IPV6_PMTU_ENTRIES 0
|
|
#endif
|
|
|
|
#define NET_PMTU_MAX_ENTRIES (NET_IPV4_PMTU_ENTRIES + NET_IPV6_PMTU_ENTRIES)
|
|
|
|
static struct net_pmtu_entry pmtu_entries[NET_PMTU_MAX_ENTRIES];
|
|
|
|
static K_MUTEX_DEFINE(lock);
|
|
|
|
static struct net_pmtu_entry *get_pmtu_entry(const struct sockaddr *dst)
|
|
{
|
|
struct net_pmtu_entry *entry = NULL;
|
|
int i;
|
|
|
|
k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(pmtu_entries); i++) {
|
|
switch (dst->sa_family) {
|
|
case AF_INET:
|
|
if (IS_ENABLED(CONFIG_NET_IPV4_PMTU) &&
|
|
pmtu_entries[i].dst.family == AF_INET &&
|
|
net_ipv4_addr_cmp(&pmtu_entries[i].dst.in_addr,
|
|
&net_sin(dst)->sin_addr)) {
|
|
entry = &pmtu_entries[i];
|
|
goto out;
|
|
}
|
|
break;
|
|
|
|
case AF_INET6:
|
|
if (IS_ENABLED(CONFIG_NET_IPV6_PMTU) &&
|
|
pmtu_entries[i].dst.family == AF_INET6 &&
|
|
net_ipv6_addr_cmp(&pmtu_entries[i].dst.in6_addr,
|
|
&net_sin6(dst)->sin6_addr)) {
|
|
entry = &pmtu_entries[i];
|
|
goto out;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
out:
|
|
k_mutex_unlock(&lock);
|
|
|
|
return entry;
|
|
}
|
|
|
|
static struct net_pmtu_entry *get_free_pmtu_entry(void)
|
|
{
|
|
struct net_pmtu_entry *entry = NULL;
|
|
uint32_t oldest = 0U;
|
|
int i;
|
|
|
|
k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(pmtu_entries); i++) {
|
|
if (!pmtu_entries[i].in_use) {
|
|
pmtu_entries[i].in_use = true;
|
|
pmtu_entries[i].last_update = k_uptime_get_32();
|
|
|
|
entry = &pmtu_entries[i];
|
|
goto out;
|
|
}
|
|
|
|
if (oldest == 0U || pmtu_entries[i].last_update < oldest) {
|
|
oldest = pmtu_entries[i].last_update;
|
|
entry = &pmtu_entries[i];
|
|
}
|
|
}
|
|
|
|
out:
|
|
k_mutex_unlock(&lock);
|
|
|
|
return entry;
|
|
}
|
|
|
|
static void update_pmtu_entry(struct net_pmtu_entry *entry, uint16_t mtu)
|
|
{
|
|
bool changed = false;
|
|
|
|
if (entry->mtu != mtu) {
|
|
changed = true;
|
|
entry->mtu = mtu;
|
|
}
|
|
|
|
entry->last_update = k_uptime_get_32();
|
|
|
|
if (changed) {
|
|
struct net_if *iface;
|
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV4_PMTU) && entry->dst.family == AF_INET) {
|
|
struct net_event_ipv4_pmtu_info info;
|
|
|
|
net_ipaddr_copy(&info.dst, &entry->dst.in_addr);
|
|
info.mtu = mtu;
|
|
|
|
iface = net_if_ipv4_select_src_iface(&info.dst);
|
|
|
|
net_mgmt_event_notify_with_info(NET_EVENT_IPV4_PMTU_CHANGED,
|
|
iface,
|
|
(const void *)&info,
|
|
sizeof(struct net_event_ipv4_pmtu_info));
|
|
|
|
} else if (IS_ENABLED(CONFIG_NET_IPV6_PMTU) && entry->dst.family == AF_INET6) {
|
|
struct net_event_ipv6_pmtu_info info;
|
|
|
|
net_ipaddr_copy(&info.dst, &entry->dst.in6_addr);
|
|
info.mtu = mtu;
|
|
|
|
iface = net_if_ipv6_select_src_iface(&info.dst);
|
|
|
|
net_mgmt_event_notify_with_info(NET_EVENT_IPV6_PMTU_CHANGED,
|
|
iface,
|
|
(const void *)&info,
|
|
sizeof(struct net_event_ipv6_pmtu_info));
|
|
}
|
|
}
|
|
}
|
|
|
|
struct net_pmtu_entry *net_pmtu_get_entry(const struct sockaddr *dst)
|
|
{
|
|
struct net_pmtu_entry *entry;
|
|
|
|
entry = get_pmtu_entry(dst);
|
|
|
|
return entry;
|
|
}
|
|
|
|
int net_pmtu_get_mtu(const struct sockaddr *dst)
|
|
{
|
|
struct net_pmtu_entry *entry;
|
|
|
|
entry = get_pmtu_entry(dst);
|
|
if (entry == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
return entry->mtu;
|
|
}
|
|
|
|
static struct net_pmtu_entry *add_entry(const struct sockaddr *dst, bool *old_entry)
|
|
{
|
|
struct net_pmtu_entry *entry;
|
|
|
|
entry = get_pmtu_entry(dst);
|
|
if (entry != NULL) {
|
|
*old_entry = true;
|
|
return entry;
|
|
}
|
|
|
|
entry = get_free_pmtu_entry();
|
|
if (entry == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
switch (dst->sa_family) {
|
|
case AF_INET:
|
|
if (IS_ENABLED(CONFIG_NET_IPV4_PMTU)) {
|
|
entry->dst.family = AF_INET;
|
|
net_ipaddr_copy(&entry->dst.in_addr, &net_sin(dst)->sin_addr);
|
|
} else {
|
|
entry->in_use = false;
|
|
goto unlock_fail;
|
|
}
|
|
break;
|
|
|
|
case AF_INET6:
|
|
if (IS_ENABLED(CONFIG_NET_IPV6_PMTU)) {
|
|
entry->dst.family = AF_INET6;
|
|
net_ipaddr_copy(&entry->dst.in6_addr, &net_sin6(dst)->sin6_addr);
|
|
} else {
|
|
entry->in_use = false;
|
|
goto unlock_fail;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
entry->in_use = false;
|
|
goto unlock_fail;
|
|
}
|
|
|
|
k_mutex_unlock(&lock);
|
|
return entry;
|
|
|
|
unlock_fail:
|
|
*old_entry = false;
|
|
|
|
k_mutex_unlock(&lock);
|
|
return NULL;
|
|
}
|
|
|
|
int net_pmtu_update_mtu(const struct sockaddr *dst, uint16_t mtu)
|
|
{
|
|
struct net_pmtu_entry *entry;
|
|
uint16_t old_mtu = 0U;
|
|
bool updated = false;
|
|
|
|
entry = add_entry(dst, &updated);
|
|
if (entry == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (updated) {
|
|
old_mtu = entry->mtu;
|
|
}
|
|
|
|
update_pmtu_entry(entry, mtu);
|
|
|
|
return (int)old_mtu;
|
|
}
|
|
|
|
int net_pmtu_update_entry(struct net_pmtu_entry *entry, uint16_t mtu)
|
|
{
|
|
uint16_t old_mtu;
|
|
|
|
if (entry == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (entry->mtu == mtu) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
old_mtu = entry->mtu;
|
|
|
|
update_pmtu_entry(entry, mtu);
|
|
|
|
return (int)old_mtu;
|
|
}
|
|
|
|
int net_pmtu_foreach(net_pmtu_cb_t cb, void *user_data)
|
|
{
|
|
int ret = 0;
|
|
|
|
k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
ARRAY_FOR_EACH(pmtu_entries, i) {
|
|
ret++;
|
|
cb(&pmtu_entries[i], user_data);
|
|
}
|
|
|
|
k_mutex_unlock(&lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void net_pmtu_init(void)
|
|
{
|
|
k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
ARRAY_FOR_EACH(pmtu_entries, i) {
|
|
pmtu_entries[i].in_use = false;
|
|
}
|
|
|
|
k_mutex_unlock(&lock);
|
|
}
|