From 53cfa7368ff83d91d2ad5de602703aaff4e240cd Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Fri, 1 Nov 2024 15:56:52 +0200 Subject: [PATCH 01/24] net: ip: pmtu: Add generic IP PMTU Discovery support This adds generic code that can be used by both IPv4 and IPv6 Path MTU Discovery mechanism. The actual PMTU support for each protocol family is in subsequent commits. Signed-off-by: Jukka Rissanen --- subsys/net/ip/CMakeLists.txt | 1 + subsys/net/ip/Kconfig | 13 ++ subsys/net/ip/Kconfig.ipv4 | 12 ++ subsys/net/ip/Kconfig.ipv6 | 12 ++ subsys/net/ip/net_core.c | 3 + subsys/net/ip/pmtu.c | 249 +++++++++++++++++++++++++++++++++++ subsys/net/ip/pmtu.h | 144 ++++++++++++++++++++ 7 files changed, 434 insertions(+) create mode 100644 subsys/net/ip/pmtu.c create mode 100644 subsys/net/ip/pmtu.h diff --git a/subsys/net/ip/CMakeLists.txt b/subsys/net/ip/CMakeLists.txt index 23d433930b1f7..961a0c036d65e 100644 --- a/subsys/net/ip/CMakeLists.txt +++ b/subsys/net/ip/CMakeLists.txt @@ -42,6 +42,7 @@ zephyr_library_sources_ifdef(CONFIG_NET_IPV6_PE ipv6_pe.c) zephyr_library_sources_ifdef(CONFIG_NET_IPV6_FRAGMENT ipv6_fragment.c) zephyr_library_sources_ifdef(CONFIG_NET_IPV4_FRAGMENT ipv4_fragment.c) zephyr_library_sources_ifdef(CONFIG_NET_MGMT_EVENT net_mgmt.c) +zephyr_library_sources_ifdef(CONFIG_NET_PMTU pmtu.c) zephyr_library_sources_ifdef(CONFIG_NET_ROUTE route.c) zephyr_library_sources_ifdef(CONFIG_NET_STATISTICS net_stats.c) zephyr_library_sources_ifdef(CONFIG_NET_TCP tcp.c) diff --git a/subsys/net/ip/Kconfig b/subsys/net/ip/Kconfig index b61e2dd74c3cc..70155b82f847c 100644 --- a/subsys/net/ip/Kconfig +++ b/subsys/net/ip/Kconfig @@ -48,6 +48,19 @@ config NET_NATIVE_IPV4 depends on NET_NATIVE default y if NET_IPV4 +config NET_PMTU + bool + default y + depends on NET_IPV6_PMTU || NET_IPV4_PMTU + +if NET_PMTU +module = NET_PMTU +module-dep = NET_LOG +module-str = Log level for PMTU +module-help = Enables PMTU to output debug messages. +source "subsys/net/Kconfig.template.log_config.net" +endif # NET_PMTU + config NET_NATIVE_TCP bool depends on NET_NATIVE diff --git a/subsys/net/ip/Kconfig.ipv4 b/subsys/net/ip/Kconfig.ipv4 index 77c29c8966ced..e2456557caab3 100644 --- a/subsys/net/ip/Kconfig.ipv4 +++ b/subsys/net/ip/Kconfig.ipv4 @@ -151,6 +151,18 @@ config NET_IPV4_FRAGMENT_TIMEOUT How long to wait for IPv4 fragment to arrive before the reassembly will timeout. This value is in seconds. +config NET_IPV4_PMTU + bool "IPv4 Path MTU Discovery" + help + Enables IPv4 Path MTU Discovery (see RFC 1191) + +config NET_IPV4_PMTU_DESTINATION_CACHE_ENTRIES + int "Number of IPv4 PMTU destination cache entries" + default 3 + depends on NET_IPV4_PMTU + help + How many PMTU entries we can track for each destination address. + module = NET_IPV4 module-dep = NET_LOG module-str = Log level for core IPv4 diff --git a/subsys/net/ip/Kconfig.ipv6 b/subsys/net/ip/Kconfig.ipv6 index 33e493d2a237d..2c818bdfb1235 100644 --- a/subsys/net/ip/Kconfig.ipv6 +++ b/subsys/net/ip/Kconfig.ipv6 @@ -46,6 +46,18 @@ config NET_IPV6_MTU The value should normally be 1280 which is the minimum IPv6 packet size that implementations need to support without fragmentation. +config NET_IPV6_PMTU + bool "IPv6 Path MTU Discovery" + help + Enables IPv6 Path MTU Discovery (see RFC 8201) + +config NET_IPV6_PMTU_DESTINATION_CACHE_ENTRIES + int "Number of IPv6 PMTU destination cache entries" + default 3 + depends on NET_IPV6_PMTU + help + How many PMTU entries we can track for each destination address. + config NET_INITIAL_HOP_LIMIT int "Initial IPv6 hop limit value for unicast packets" default 64 diff --git a/subsys/net/ip/net_core.c b/subsys/net/ip/net_core.c index 5259f9c6e6d70..facc4c8ecc7d1 100644 --- a/subsys/net/ip/net_core.c +++ b/subsys/net/ip/net_core.c @@ -40,6 +40,8 @@ LOG_MODULE_REGISTER(net_core, CONFIG_NET_CORE_LOG_LEVEL); #include "net_private.h" #include "shell/net_shell.h" +#include "pmtu.h" + #include "icmpv6.h" #include "ipv6.h" @@ -536,6 +538,7 @@ int net_recv_data(struct net_if *iface, struct net_pkt *pkt) static inline void l3_init(void) { + net_pmtu_init(); net_icmpv4_init(); net_icmpv6_init(); net_ipv4_init(); diff --git a/subsys/net/ip/pmtu.c b/subsys/net/ip/pmtu.c new file mode 100644 index 0000000000000..e1f9d748f5f42 --- /dev/null +++ b/subsys/net/ip/pmtu.c @@ -0,0 +1,249 @@ +/** @file + * @brief IPv4/6 PMTU related functions + */ + +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(net_pmtu, CONFIG_NET_PMTU_LOG_LEVEL); + +#include + +#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) +{ + entry->mtu = mtu; + entry->last_update = k_uptime_get_32(); +} + +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); +} diff --git a/subsys/net/ip/pmtu.h b/subsys/net/ip/pmtu.h new file mode 100644 index 0000000000000..bce20a7762c70 --- /dev/null +++ b/subsys/net/ip/pmtu.h @@ -0,0 +1,144 @@ +/** @file + * @brief IPv4/6 PMTU related functions + */ + +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __NET_PMTU_H +#define __NET_PMTU_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** PTMU destination cache entry */ +struct net_pmtu_entry { + /** Destination address */ + struct net_addr dst; + /** Last time the PMTU was updated */ + uint32_t last_update; + /** MTU for this destination address */ + uint16_t mtu; + /** In use flag */ + bool in_use : 1; +}; + +/** Get PMTU entry for the given destination address + * + * @param dst Destination address + * + * @return PMTU entry if found, NULL otherwise + */ +#if defined(CONFIG_NET_PMTU) +struct net_pmtu_entry *net_pmtu_get_entry(const struct sockaddr *dst); +#else +static inline struct net_pmtu_entry *net_pmtu_get_entry(const struct sockaddr *dst) +{ + ARG_UNUSED(dst); + + return NULL; +} +#endif /* CONFIG_NET_PMTU */ + +/** Get MTU value for the given destination address + * + * @param dst Destination address + * + * @return MTU value (> 0) if found, <0 otherwise + */ +#if defined(CONFIG_NET_PMTU) +int net_pmtu_get_mtu(const struct sockaddr *dst); +#else +static inline int net_pmtu_get_mtu(const struct sockaddr *dst) +{ + ARG_UNUSED(dst); + + return -ENOTSUP; +} +#endif /* CONFIG_NET_PMTU */ + +/** Update PMTU value for the given destination address + * + * @param dst Destination address + * @param mtu New MTU value + * + * @return >0 previous MTU, <0 if error + */ +#if defined(CONFIG_NET_PMTU) +int net_pmtu_update_mtu(const struct sockaddr *dst, uint16_t mtu); +#else +static inline int net_pmtu_update_mtu(const struct sockaddr *dst, uint16_t mtu) +{ + ARG_UNUSED(dst); + ARG_UNUSED(mtu); + + return -ENOTSUP; +} +#endif /* CONFIG_NET_PMTU */ + +/** Update PMTU entry for the given destination address + * + * @param entry PMTU entry + * @param mtu New MTU value + * + * @return >0 previous MTU, <0 if error + */ +#if defined(CONFIG_NET_PMTU) +int net_pmtu_update_entry(struct net_pmtu_entry *entry, uint16_t mtu); +#else +static inline int net_pmtu_update_entry(struct net_pmtu_entry *entry, + uint16_t mtu) +{ + ARG_UNUSED(entry); + ARG_UNUSED(mtu); + + return -ENOTSUP; +} +#endif /* CONFIG_NET_PMTU */ + +/** + * @typedef net_pmtu_cb_t + * @brief Callback used when traversing PMTU destination cache. + * + * @param entry PMTU entry + * @param user_data User specified data + */ +typedef void (*net_pmtu_cb_t)(struct net_pmtu_entry *entry, + void *user_data); + +/** Get PMTU destination cache contents + * + * @param cb PMTU callback to be called for each cache entry. + * @param user_data User specific data. + * + * @return >=0 number of entries in the PMTU destination cache, <0 if error + */ +#if defined(CONFIG_NET_PMTU) +int net_pmtu_foreach(net_pmtu_cb_t cb, void *user_data); +#else +static inline int net_pmtu_foreach(net_pmtu_cb_t cb, void *user_data) +{ + ARG_UNUSED(cb); + ARG_UNUSED(user_data); + + return -ENOTSUP; +} +#endif /* CONFIG_NET_PMTU */ + +/** Initialize PMTU module */ +#if defined(CONFIG_NET_PMTU) +void net_pmtu_init(void); +#else +#define net_pmtu_init(...) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __NET_PMTU_H */ From 0ca36f713fd1f1216cf5ff6406d7f13eb113c354 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Fri, 1 Nov 2024 15:59:07 +0200 Subject: [PATCH 02/24] tests: net: pmtu: Add tests for path MTU discovery Add initial tests for generic part of PMTU discovery code. Signed-off-by: Jukka Rissanen --- tests/net/pmtu/CMakeLists.txt | 9 + tests/net/pmtu/prj.conf | 20 ++ tests/net/pmtu/src/main.c | 352 ++++++++++++++++++++++++++++++++++ tests/net/pmtu/testcase.yaml | 17 ++ 4 files changed, 398 insertions(+) create mode 100644 tests/net/pmtu/CMakeLists.txt create mode 100644 tests/net/pmtu/prj.conf create mode 100644 tests/net/pmtu/src/main.c create mode 100644 tests/net/pmtu/testcase.yaml diff --git a/tests/net/pmtu/CMakeLists.txt b/tests/net/pmtu/CMakeLists.txt new file mode 100644 index 0000000000000..44c347478ef73 --- /dev/null +++ b/tests/net/pmtu/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(mld) + +target_include_directories(app PRIVATE ${ZEPHYR_BASE}/subsys/net/ip) +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/net/pmtu/prj.conf b/tests/net/pmtu/prj.conf new file mode 100644 index 0000000000000..1006a8ccc03d2 --- /dev/null +++ b/tests/net/pmtu/prj.conf @@ -0,0 +1,20 @@ +CONFIG_ZTEST=y +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_NET_TEST=y +CONFIG_NETWORKING=y +CONFIG_NET_UDP=y +CONFIG_NET_TCP=n +CONFIG_NET_IPV6=y +CONFIG_NET_IPV6_PMTU=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV4_PMTU=y +CONFIG_NET_L2_ETHERNET=y +CONFIG_NET_LOG=y +CONFIG_ENTROPY_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_NET_PKT_TX_COUNT=20 +CONFIG_NET_PKT_RX_COUNT=20 +CONFIG_NET_BUF_RX_COUNT=20 +CONFIG_NET_BUF_TX_COUNT=20 +CONFIG_NET_MGMT=y +CONFIG_NET_MGMT_EVENT=y diff --git a/tests/net/pmtu/src/main.c b/tests/net/pmtu/src/main.c new file mode 100644 index 0000000000000..006101e684c74 --- /dev/null +++ b/tests/net/pmtu/src/main.c @@ -0,0 +1,352 @@ +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(net_test, CONFIG_NET_PMTU_LOG_LEVEL); + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pmtu.h" + +#define NET_LOG_ENABLED 1 +#include "net_private.h" + +#if defined(CONFIG_NET_PMTU_LOG_LEVEL_DBG) +#define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__) +#else +#define DBG(fmt, ...) +#endif + +/* Small sleep between tests makes sure that the PMTU destination + * cache entries are separated from each other. + */ +#define SMALL_SLEEP K_MSEC(5) + +static struct in_addr my_ipv4_addr = { { { 192, 0, 2, 1 } } }; +static struct in_addr dest_ipv4_addr1 = { { { 198, 51, 100, 1 } } }; +static struct in_addr dest_ipv4_addr2 = { { { 198, 51, 100, 2 } } }; +static struct in_addr dest_ipv4_addr3 = { { { 198, 51, 100, 3 } } }; +static struct in_addr dest_ipv4_addr4 = { { { 198, 51, 100, 4 } } }; +static struct in_addr any_ipv4_addr = INADDR_ANY_INIT; + +static struct in6_addr my_ipv6_addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; +static struct in6_addr dest_ipv6_addr1 = { { { 0x20, 0x01, 0x0d, 0xb8, 0x01, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; +static struct in6_addr dest_ipv6_addr2 = { { { 0x20, 0x01, 0x0d, 0xb8, 0x01, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x2 } } }; +static struct in6_addr dest_ipv6_addr3 = { { { 0x20, 0x01, 0x0d, 0xb8, 0x01, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x3 } } }; +static struct in6_addr dest_ipv6_addr4 = { { { 0x20, 0x01, 0x0d, 0xb8, 0x01, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x4 } } }; +static struct in6_addr any_ipv6_addr = IN6ADDR_ANY_INIT; + +K_SEM_DEFINE(wait_data, 0, UINT_MAX); + +#define WAIT_TIME 500 +#define WAIT_TIME_LONG MSEC_PER_SEC +#define MY_PORT 1969 +#define PEER_PORT 13856 + +struct net_test_pmtu { + uint8_t mac_addr[sizeof(struct net_eth_addr)]; + struct net_linkaddr ll_addr; +}; + +int net_test_dev_init(const struct device *dev) +{ + return 0; +} + +static uint8_t *net_test_get_mac(const struct device *dev) +{ + struct net_test_pmtu *context = dev->data; + + if (context->mac_addr[2] == 0x00) { + /* 00-00-5E-00-53-xx Documentation RFC 7042 */ + context->mac_addr[0] = 0x00; + context->mac_addr[1] = 0x00; + context->mac_addr[2] = 0x5E; + context->mac_addr[3] = 0x00; + context->mac_addr[4] = 0x53; + context->mac_addr[5] = sys_rand8_get(); + } + + return context->mac_addr; +} + +static void net_test_iface_init(struct net_if *iface) +{ + uint8_t *mac = net_test_get_mac(net_if_get_device(iface)); + + net_if_set_link_addr(iface, mac, sizeof(struct net_eth_addr), + NET_LINK_ETHERNET); +} + +static int tester_send(const struct device *dev, struct net_pkt *pkt) +{ + if (!pkt->buffer) { + TC_ERROR("No data to send!\n"); + return -ENODATA; + } + + return 0; +} + +struct net_test_pmtu net_test_data; + +static struct ethernet_api net_test_if_api = { + .iface_api.init = net_test_iface_init, + .send = tester_send, +}; + +#define _ETH_L2_LAYER ETHERNET_L2 +#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(ETHERNET_L2) + +NET_DEVICE_INIT(net_test_pmtu, "net_test_pmtu", + net_test_dev_init, NULL, &net_test_data, NULL, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &net_test_if_api, _ETH_L2_LAYER, _ETH_L2_CTX_TYPE, + 127); + +ZTEST(net_pmtu_test_suite, test_pmtu_01_ipv4_get_entry) +{ +#if defined(CONFIG_NET_IPV4_PMTU) + struct net_pmtu_entry *entry; + struct sockaddr_in dest_ipv4; + + net_ipaddr_copy(&dest_ipv4.sin_addr, &dest_ipv4_addr1); + dest_ipv4.sin_family = AF_INET; + + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv4); + zassert_is_null(entry, "PMTU IPv4 entry is not NULL"); + + k_sleep(SMALL_SLEEP); +#else + ztest_test_skip(); +#endif +} + +ZTEST(net_pmtu_test_suite, test_pmtu_01_ipv6_get_entry) +{ +#if defined(CONFIG_NET_IPV6_PMTU) + struct net_pmtu_entry *entry; + struct sockaddr_in6 dest_ipv6; + + net_ipaddr_copy(&dest_ipv6.sin6_addr, &dest_ipv6_addr1); + dest_ipv6.sin6_family = AF_INET6; + + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv6); + zassert_is_null(entry, "PMTU IPv6 entry is not NULL"); + + k_sleep(SMALL_SLEEP); +#else + ztest_test_skip(); +#endif +} + +ZTEST(net_pmtu_test_suite, test_pmtu_02_ipv4_update_entry) +{ +#if defined(CONFIG_NET_IPV4_PMTU) + struct sockaddr_in dest_ipv4; + int ret; + + net_ipaddr_copy(&dest_ipv4.sin_addr, &dest_ipv4_addr1); + dest_ipv4.sin_family = AF_INET; + + ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv4, 1300); + zassert_equal(ret, 0, "PMTU IPv4 MTU update failed (%d)", ret); + + k_sleep(SMALL_SLEEP); +#else + ztest_test_skip(); +#endif +} + +ZTEST(net_pmtu_test_suite, test_pmtu_02_ipv6_update_entry) +{ +#if defined(CONFIG_NET_IPV6_PMTU) + struct sockaddr_in6 dest_ipv6; + int ret; + + net_ipaddr_copy(&dest_ipv6.sin6_addr, &dest_ipv6_addr1); + dest_ipv6.sin6_family = AF_INET6; + + ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv6, 1600); + zassert_equal(ret, 0, "PMTU IPv6 MTU update failed (%d)", ret); + + k_sleep(SMALL_SLEEP); +#else + ztest_test_skip(); +#endif +} + +ZTEST(net_pmtu_test_suite, test_pmtu_03_ipv4_create_more_entries) +{ +#if defined(CONFIG_NET_IPV4_PMTU) + struct sockaddr_in dest_ipv4; + struct net_pmtu_entry *entry; + int ret; + + dest_ipv4.sin_family = AF_INET; + + net_ipaddr_copy(&dest_ipv4.sin_addr, &dest_ipv4_addr1); + ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv4, 1300); + zassert_equal(ret, 1300, "PMTU IPv4 MTU update failed (%d)", ret); + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv4); + zassert_equal(entry->mtu, 1300, "PMTU IPv4 MTU is not correct (%d)", + entry->mtu); + + k_sleep(SMALL_SLEEP); + + net_ipaddr_copy(&dest_ipv4.sin_addr, &dest_ipv4_addr2); + ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv4, 1400); + zassert_equal(ret, 0, "PMTU IPv4 MTU update failed (%d)", ret); + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv4); + zassert_equal(entry->mtu, 1400, "PMTU IPv4 MTU is not correct (%d)", + entry->mtu); + + + k_sleep(SMALL_SLEEP); + + net_ipaddr_copy(&dest_ipv4.sin_addr, &dest_ipv4_addr3); + ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv4, 1500); + zassert_equal(ret, 0, "PMTU IPv4 MTU update failed (%d)", ret); + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv4); + zassert_equal(entry->mtu, 1500, "PMTU IPv4 MTU is not correct (%d)", + entry->mtu); +#else + ztest_test_skip(); +#endif +} + +ZTEST(net_pmtu_test_suite, test_pmtu_03_ipv6_create_more_entries) +{ +#if defined(CONFIG_NET_IPV6_PMTU) + struct sockaddr_in6 dest_ipv6; + struct net_pmtu_entry *entry; + int ret; + + dest_ipv6.sin6_family = AF_INET6; + + net_ipaddr_copy(&dest_ipv6.sin6_addr, &dest_ipv6_addr1); + ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv6, 1600); + zassert_equal(ret, 1600, "PMTU IPv6 MTU update failed (%d)", ret); + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv6); + zassert_equal(entry->mtu, 1600, "PMTU IPv6 MTU is not correct (%d)", + entry->mtu); + + k_sleep(SMALL_SLEEP); + + net_ipaddr_copy(&dest_ipv6.sin6_addr, &dest_ipv6_addr2); + ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv6, 1700); + zassert_equal(ret, 0, "PMTU IPv6 MTU update failed (%d)", ret); + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv6); + zassert_equal(entry->mtu, 1700, "PMTU IPv6 MTU is not correct (%d)", + entry->mtu); + + k_sleep(SMALL_SLEEP); + + net_ipaddr_copy(&dest_ipv6.sin6_addr, &dest_ipv6_addr3); + ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv6, 1800); + zassert_equal(ret, 0, "PMTU IPv6 MTU update failed (%d)", ret); + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv6); + zassert_equal(entry->mtu, 1800, "PMTU IPv6 MTU is not correct (%d)", + entry->mtu); +#else + ztest_test_skip(); +#endif +} + +ZTEST(net_pmtu_test_suite, test_pmtu_04_ipv4_overflow) +{ +#if defined(CONFIG_NET_IPV4_PMTU) + struct sockaddr_in dest_ipv4; + struct net_pmtu_entry *entry; + int ret; + + dest_ipv4.sin_family = AF_INET; + + /* Create more entries than we have space */ + net_ipaddr_copy(&dest_ipv4.sin_addr, &dest_ipv4_addr4); + ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv4, 1450); + zassert_equal(ret, 0, "PMTU IPv4 MTU update failed (%d)", ret); + + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv4); + zassert_equal(entry->mtu, 1450, "PMTU IPv4 MTU is not correct (%d)", + entry->mtu); + + k_sleep(SMALL_SLEEP); + + net_ipaddr_copy(&dest_ipv4.sin_addr, &dest_ipv4_addr1); + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv4); + zassert_is_null(entry, "PMTU IPv4 MTU found when it should not be"); +#else + ztest_test_skip(); +#endif +} + +ZTEST(net_pmtu_test_suite, test_pmtu_04_ipv6_overflow) +{ +#if defined(CONFIG_NET_IPV6_PMTU) + struct sockaddr_in6 dest_ipv6; + struct net_pmtu_entry *entry; + int ret; + + dest_ipv6.sin6_family = AF_INET6; + + /* Create more entries than we have space */ + net_ipaddr_copy(&dest_ipv6.sin6_addr, &dest_ipv6_addr4); + ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv6, 1650); + zassert_equal(ret, 0, "PMTU IPv6 MTU update failed (%d)", ret); + + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv6); + zassert_equal(entry->mtu, 1650, "PMTU IPv6 MTU is not correct (%d)", + entry->mtu); + + k_sleep(SMALL_SLEEP); + + /* If we have IPv4 PMTU enabled, then the oldest one is an IPv4 entry. + */ + if (IS_ENABLED(CONFIG_NET_IPV4_PMTU)) { + struct sockaddr_in dest_ipv4; + + dest_ipv4.sin_family = AF_INET; + + net_ipaddr_copy(&dest_ipv4.sin_addr, &dest_ipv4_addr2); + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv4); + } else { + net_ipaddr_copy(&dest_ipv6.sin6_addr, &dest_ipv6_addr1); + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv6); + } + + zassert_is_null(entry, "PMTU IPv6 MTU found when it should not be"); +#else + ztest_test_skip(); +#endif +} + +ZTEST_SUITE(net_pmtu_test_suite, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/net/pmtu/testcase.yaml b/tests/net/pmtu/testcase.yaml new file mode 100644 index 0000000000000..6b0da0a2dfdbd --- /dev/null +++ b/tests/net/pmtu/testcase.yaml @@ -0,0 +1,17 @@ +common: + tags: + - net + - pmtu + depends_on: netif + platform_allow: qemu_x86 +tests: + net.pmtu.ipv4: + extra_configs: + - CONFIG_NET_IPV4_PMTU=y + net.pmtu.ipv6: + extra_configs: + - CONFIG_NET_IPV6_PMTU=y + net.pmtu.all: + extra_configs: + - CONFIG_NET_IPV4_PMTU=y + - CONFIG_NET_IPV6_PMTU=y From 6c5263462a49c515e9a868251638ae62109bee84 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Fri, 1 Nov 2024 16:44:09 +0200 Subject: [PATCH 03/24] net: stats: ipv6: pmtu: Add Path MTU Discovery statistics Add information about PMTU related packets received/sent/dropped for IPv6. Signed-off-by: Jukka Rissanen --- include/zephyr/net/net_stats.h | 30 ++++++++++++++++++++++++++++++ samples/net/stats/src/main.c | 6 ++++++ subsys/net/ip/Kconfig.stats | 7 +++++++ subsys/net/ip/net_stats.c | 17 +++++++++++++++++ subsys/net/ip/net_stats.h | 23 +++++++++++++++++++++++ subsys/net/lib/shell/stats.c | 6 ++++++ 6 files changed, 89 insertions(+) diff --git a/include/zephyr/net/net_stats.h b/include/zephyr/net/net_stats.h index f41bf204e9682..93f34da6236fe 100644 --- a/include/zephyr/net/net_stats.h +++ b/include/zephyr/net/net_stats.h @@ -198,6 +198,20 @@ struct net_stats_ipv6_nd { net_stats_t sent; }; +/** + * @brief IPv6 Path MTU Discovery statistics + */ +struct net_stats_ipv6_pmtu { + /** Number of dropped IPv6 PMTU packets. */ + net_stats_t drop; + + /** Number of received IPv6 PMTU packets. */ + net_stats_t recv; + + /** Number of sent IPv6 PMTU packets. */ + net_stats_t sent; +}; + /** * @brief IPv6 multicast listener daemon statistics */ @@ -379,6 +393,11 @@ struct net_stats { struct net_stats_ipv6_nd ipv6_nd; #endif +#if defined(CONFIG_NET_STATISTICS_IPV6_PMTU) + /** IPv6 Path MTU Discovery statistics */ + struct net_stats_ipv6_pmtu ipv6_pmtu; +#endif + #if defined(CONFIG_NET_STATISTICS_MLD) /** IPv6 MLD statistics */ struct net_stats_ipv6_mld ipv6_mld; @@ -665,6 +684,7 @@ enum net_request_stats_cmd { NET_REQUEST_STATS_CMD_GET_IPV4, NET_REQUEST_STATS_CMD_GET_IPV6, NET_REQUEST_STATS_CMD_GET_IPV6_ND, + NET_REQUEST_STATS_CMD_GET_IPV6_PMTU, NET_REQUEST_STATS_CMD_GET_ICMP, NET_REQUEST_STATS_CMD_GET_UDP, NET_REQUEST_STATS_CMD_GET_TCP, @@ -732,6 +752,16 @@ NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV6_ND); /** @endcond */ #endif /* CONFIG_NET_STATISTICS_IPV6_ND */ +#if defined(CONFIG_NET_STATISTICS_IPV6_PMTU) +/** Request IPv6 Path MTU Discovery statistics */ +#define NET_REQUEST_STATS_GET_IPV6_PMTU \ + (_NET_STATS_BASE | NET_REQUEST_STATS_CMD_GET_IPV6_PMTU) + +/** @cond INTERNAL_HIDDEN */ +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV6_PMTU); +/** @endcond */ +#endif /* CONFIG_NET_STATISTICS_IPV6_PMTU */ + #if defined(CONFIG_NET_STATISTICS_ICMP) /** Request ICMPv4 and ICMPv6 statistics */ #define NET_REQUEST_STATS_GET_ICMP \ diff --git a/samples/net/stats/src/main.c b/samples/net/stats/src/main.c index 171757cac853c..f6e35a89e904e 100644 --- a/samples/net/stats/src/main.c +++ b/samples/net/stats/src/main.c @@ -43,6 +43,12 @@ static void print_stats(struct net_if *iface, struct net_stats *data) GET_STAT(iface, ipv6_nd.sent), GET_STAT(iface, ipv6_nd.drop)); #endif /* CONFIG_NET_IPV6_ND */ +#if defined(CONFIG_NET_IPV6_PMTU) + printk("IPv6 PMTU recv %d\tsent\t%d\tdrop\t%d\n", + GET_STAT(iface, ipv6_pmtu.recv), + GET_STAT(iface, ipv6_pmtu.sent), + GET_STAT(iface, ipv6_pmtu.drop)); +#endif /* CONFIG_NET_IPV6_PMTU */ #if defined(CONFIG_NET_STATISTICS_MLD) printk("IPv6 MLD recv %d\tsent\t%d\tdrop\t%d\n", GET_STAT(iface, ipv6_mld.recv), diff --git a/subsys/net/ip/Kconfig.stats b/subsys/net/ip/Kconfig.stats index ae0d6e95e46b0..9851e5063fd06 100644 --- a/subsys/net/ip/Kconfig.stats +++ b/subsys/net/ip/Kconfig.stats @@ -57,6 +57,13 @@ config NET_STATISTICS_IPV6_ND help Keep track of IPv6 Neighbor Discovery related statistics +config NET_STATISTICS_IPV6_PMTU + bool "IPv6 PMTU statistics" + depends on NET_IPV6_PMTU + default y + help + Keep track of IPv6 Path MTU Discovery related statistics + config NET_STATISTICS_ICMP bool "ICMP statistics" depends on NET_IPV6 || NET_IPV4 diff --git a/subsys/net/ip/net_stats.c b/subsys/net/ip/net_stats.c index 4fe68933a061b..ab6d7855ee0eb 100644 --- a/subsys/net/ip/net_stats.c +++ b/subsys/net/ip/net_stats.c @@ -91,6 +91,12 @@ static inline void stats(struct net_if *iface) GET_STAT(iface, ipv6_nd.sent), GET_STAT(iface, ipv6_nd.drop)); #endif /* CONFIG_NET_STATISTICS_IPV6_ND */ +#if defined(CONFIG_NET_STATISTICS_IPV6_PMTU) + NET_INFO("IPv6 PMTU recv %d\tsent\t%d\tdrop\t%d", + GET_STAT(iface, ipv6_pmtu.recv), + GET_STAT(iface, ipv6_pmtu.sent), + GET_STAT(iface, ipv6_pmtu.drop)); +#endif /* CONFIG_NET_STATISTICS_IPV6_PMTU */ #if defined(CONFIG_NET_STATISTICS_MLD) NET_INFO("IPv6 MLD recv %d\tsent\t%d\tdrop\t%d", GET_STAT(iface, ipv6_mld.recv), @@ -279,6 +285,12 @@ static int net_stats_get(uint32_t mgmt_request, struct net_if *iface, src = GET_STAT_ADDR(iface, ipv6_nd); break; #endif +#if defined(CONFIG_NET_STATISTICS_IPV6_PMTU) + case NET_REQUEST_STATS_CMD_GET_IPV6_PMTU: + len_chk = sizeof(struct net_stats_ipv6_pmtu); + src = GET_STAT_ADDR(iface, ipv6_pmtu); + break; +#endif #if defined(CONFIG_NET_STATISTICS_ICMP) case NET_REQUEST_STATS_CMD_GET_ICMP: len_chk = sizeof(struct net_stats_icmp); @@ -341,6 +353,11 @@ NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV6_ND, net_stats_get); #endif +#if defined(CONFIG_NET_STATISTICS_IPV6_PMTU) +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV6_PMTU, + net_stats_get); +#endif + #if defined(CONFIG_NET_STATISTICS_ICMP) NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_ICMP, net_stats_get); diff --git a/subsys/net/ip/net_stats.h b/subsys/net/ip/net_stats.h index 431461b497eee..54ae8ff3773ee 100644 --- a/subsys/net/ip/net_stats.h +++ b/subsys/net/ip/net_stats.h @@ -113,6 +113,29 @@ static inline void net_stats_update_ipv6_nd_drop(struct net_if *iface) #define net_stats_update_ipv6_nd_drop(iface) #endif /* CONFIG_NET_STATISTICS_IPV6_ND */ +#if defined(CONFIG_NET_STATISTICS_IPV6_PMTU) && defined(CONFIG_NET_NATIVE_IPV6) +/* IPv6 Path MTU Discovery stats */ + +static inline void net_stats_update_ipv6_pmtu_sent(struct net_if *iface) +{ + UPDATE_STAT(iface, stats.ipv6_pmtu.sent++); +} + +static inline void net_stats_update_ipv6_pmtu_recv(struct net_if *iface) +{ + UPDATE_STAT(iface, stats.ipv6_pmtu.recv++); +} + +static inline void net_stats_update_ipv6_pmtu_drop(struct net_if *iface) +{ + UPDATE_STAT(iface, stats.ipv6_pmtu.drop++); +} +#else +#define net_stats_update_ipv6_pmtu_sent(iface) +#define net_stats_update_ipv6_pmtu_recv(iface) +#define net_stats_update_ipv6_pmtu_drop(iface) +#endif /* CONFIG_NET_STATISTICS_IPV6_PMTU */ + #if defined(CONFIG_NET_STATISTICS_IPV4) && defined(CONFIG_NET_NATIVE_IPV4) /* IPv4 stats */ diff --git a/subsys/net/lib/shell/stats.c b/subsys/net/lib/shell/stats.c index 0896add25f997..3bd7f09a6d80a 100644 --- a/subsys/net/lib/shell/stats.c +++ b/subsys/net/lib/shell/stats.c @@ -467,6 +467,12 @@ static void net_shell_print_statistics(struct net_if *iface, void *user_data) GET_STAT(iface, ipv6_nd.sent), GET_STAT(iface, ipv6_nd.drop)); #endif /* CONFIG_NET_STATISTICS_IPV6_ND */ +#if defined(CONFIG_NET_STATISTICS_IPV6_PMTU) + PR("IPv6 PMTU recv %d\tsent\t%d\tdrop\t%d\n", + GET_STAT(iface, ipv6_pmtu.recv), + GET_STAT(iface, ipv6_pmtu.sent), + GET_STAT(iface, ipv6_pmtu.drop)); +#endif /* CONFIG_NET_STATISTICS_IPV6_PMTU */ #if defined(CONFIG_NET_STATISTICS_MLD) PR("IPv6 MLD recv %d\tsent\t%d\tdrop\t%d\n", GET_STAT(iface, ipv6_mld.recv), From b4df05cf5337bea5d5a0a04c5fce6358b2da259d Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 5 Nov 2024 10:33:03 +0200 Subject: [PATCH 04/24] net: ipv6: Print verdict information Print more cases when the packet is dropped, and also print the upper layer verdict for the packet. Signed-off-by: Jukka Rissanen --- subsys/net/ip/ipv6.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/subsys/net/ip/ipv6.c b/subsys/net/ip/ipv6.c index 032794a76680d..54a884dc2ae8a 100644 --- a/subsys/net/ip/ipv6.c +++ b/subsys/net/ip/ipv6.c @@ -574,6 +574,7 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) if (!net_pkt_filter_ip_recv_ok(pkt)) { /* drop the packet */ + NET_DBG("DROP: pkt filter"); return NET_DROP; } @@ -587,6 +588,7 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) * layer. */ if (ipv6_forward_mcast_packet(pkt, hdr) == NET_DROP) { + NET_DBG("DROP: forward mcast"); goto drop; } } @@ -597,6 +599,9 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) return NET_OK; } + NET_DBG("DROP: no such address %s in iface %d", + net_sprint_ipv6_addr((struct in6_addr *)hdr->dst), + net_if_get_by_iface(pkt_iface)); goto drop; } @@ -611,6 +616,7 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) pkt_iface, (struct in6_addr *)hdr->dst)) { ipv6_no_route_info(pkt, (struct in6_addr *)hdr->src, (struct in6_addr *)hdr->dst); + NET_DBG("DROP: cross interface boundary"); goto drop; } } @@ -664,6 +670,7 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) * This is not an error case so do not update drop * statistics. */ + NET_DBG("DROP: none nexthdr"); return NET_DROP; } @@ -671,6 +678,7 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) prev_hdr_offset = net_pkt_get_current_offset(pkt); if (net_pkt_read_u8(pkt, &nexthdr)) { + NET_DBG("DROP: pkt invalid read"); goto drop; } @@ -731,6 +739,7 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) exthdr_len = ipv6_handle_ext_hdr_options(pkt, hdr, pkt_len); if (exthdr_len < 0) { + NET_DBG("DROP: extension hdr len (%d)", exthdr_len); goto drop; } @@ -753,12 +762,16 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) if (proto_hdr.tcp) { verdict = NET_OK; } + + NET_DBG("%s verdict %s", "TCP", net_verdict2str(verdict)); break; case IPPROTO_UDP: proto_hdr.udp = net_udp_input(pkt, &udp_access); if (proto_hdr.udp) { verdict = NET_OK; } + + NET_DBG("%s verdict %s", "UDP", net_verdict2str(verdict)); break; #if defined(CONFIG_NET_L2_IPIP) @@ -787,14 +800,19 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) } if (verdict == NET_DROP) { + NET_DBG("DROP: because verdict"); goto drop; } else if (current_hdr == IPPROTO_ICMPV6) { + NET_DBG("%s verdict %s", "ICMPv6", net_verdict2str(verdict)); return verdict; } ip.ipv6 = hdr; verdict = net_conn_input(pkt, &ip, current_hdr, &proto_hdr); + + NET_DBG("%s verdict %s", "Connection", net_verdict2str(verdict)); + if (verdict != NET_DROP) { return verdict; } From 94766624993697cdea040b35be22dfbd678b28c9 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 5 Nov 2024 10:38:26 +0200 Subject: [PATCH 05/24] net: ipv6: Add PMTU support Catch "Packet Too Big" ICMPv6 messages and update PMTU for a given destination IPv6 address. Use that PMTU when sending data to the destination. Signed-off-by: Jukka Rissanen --- subsys/net/ip/icmpv6.h | 3 + subsys/net/ip/ipv6_nbr.c | 125 +++++++++++++++++++++++++++++++++++++++ subsys/net/ip/tcp.c | 49 ++++++++++----- 3 files changed, 161 insertions(+), 16 deletions(-) diff --git a/subsys/net/ip/icmpv6.h b/subsys/net/ip/icmpv6.h index 64ee1dfaa7bcb..2b68ae8bfaeda 100644 --- a/subsys/net/ip/icmpv6.h +++ b/subsys/net/ip/icmpv6.h @@ -117,6 +117,9 @@ struct net_icmpv6_mld_mcast_record { uint8_t mcast_address[NET_IPV6_ADDR_SIZE]; } __packed; +struct net_icmpv6_ptb { + uint32_t mtu; +} __packed; #define NET_ICMPV6_ND_O_FLAG(flag) ((flag) & 0x40) #define NET_ICMPV6_ND_M_FLAG(flag) ((flag) & 0x80) diff --git a/subsys/net/ip/ipv6_nbr.c b/subsys/net/ip/ipv6_nbr.c index 86e6b1f2173b1..93b0123eca46a 100644 --- a/subsys/net/ip/ipv6_nbr.c +++ b/subsys/net/ip/ipv6_nbr.c @@ -35,6 +35,7 @@ LOG_MODULE_REGISTER(net_ipv6_nd, CONFIG_NET_IPV6_ND_LOG_LEVEL); #include "6lo.h" #include "route.h" #include "net_stats.h" +#include "pmtu.h" /* Timeout value to be used when allocating net buffer during various * neighbor discovery procedures. @@ -903,6 +904,26 @@ enum net_verdict net_ipv6_prepare_for_send(struct net_pkt *pkt) } try_send: + if (IS_ENABLED(CONFIG_NET_IPV6_PMTU)) { + struct net_pmtu_entry *entry; + struct sockaddr_in6 dst = { + .sin6_family = AF_INET6, + }; + + net_ipaddr_copy(&dst.sin6_addr, (struct in6_addr *)ip_hdr->dst); + + entry = net_pmtu_get_entry((struct sockaddr *)&dst); + if (entry == NULL) { + ret = net_pmtu_update_mtu((struct sockaddr *)&dst, + net_if_get_mtu(iface)); + if (ret < 0) { + NET_DBG("Cannot update PMTU for %s (%d)", + net_sprint_ipv6_addr(&dst.sin6_addr), + ret); + } + } + } + net_ipv6_nbr_lock(); nbr = nbr_lookup(&net_neighbor.table, iface, nexthop); @@ -2716,6 +2737,98 @@ static int handle_ra_input(struct net_icmp_ctx *ctx, } #endif /* CONFIG_NET_IPV6_ND */ +#if defined(CONFIG_NET_IPV6_PMTU) +/* Packet format described in RFC 4443 ch 3.2. Packet Too Big Message */ +static int handle_ptb_input(struct net_icmp_ctx *ctx, + struct net_pkt *pkt, + struct net_icmp_ip_hdr *hdr, + struct net_icmp_hdr *icmp_hdr, + void *user_data) +{ + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ptb_access, struct net_icmpv6_ptb); + struct net_ipv6_hdr *ip_hdr = hdr->ipv6; + uint16_t length = net_pkt_get_len(pkt); + struct net_icmpv6_ptb *ptb_hdr; + struct sockaddr_in6 sockaddr_src = { + .sin6_family = AF_INET6, + }; + struct net_pmtu_entry *entry; + uint32_t mtu; + int ret; + + ARG_UNUSED(user_data); + + ptb_hdr = (struct net_icmpv6_ptb *)net_pkt_get_data(pkt, &ptb_access); + if (!ptb_hdr) { + NET_DBG("DROP: NULL PTB header"); + goto drop; + } + + dbg_addr_recv("Packet Too Big", &ip_hdr->src, &ip_hdr->dst, pkt); + + net_stats_update_ipv6_pmtu_recv(net_pkt_iface(pkt)); + + if (length < (sizeof(struct net_ipv6_hdr) + + sizeof(struct net_icmp_hdr) + + sizeof(struct net_icmpv6_ptb))) { + NET_DBG("DROP: length %d too big %zd", + length, sizeof(struct net_ipv6_hdr) + + sizeof(struct net_icmp_hdr) + + sizeof(struct net_icmpv6_ptb)); + goto drop; + } + + net_pkt_acknowledge_data(pkt, &ptb_access); + + mtu = ntohl(ptb_hdr->mtu); + + if (mtu < MIN_IPV6_MTU || mtu > MAX_IPV6_MTU) { + NET_DBG("DROP: Unsupported MTU %u, min is %u, max is %u", + mtu, MIN_IPV6_MTU, MAX_IPV6_MTU); + goto drop; + } + + net_ipaddr_copy(&sockaddr_src.sin6_addr, (struct in6_addr *)&ip_hdr->src); + + entry = net_pmtu_get_entry((struct sockaddr *)&sockaddr_src); + if (entry == NULL) { + NET_DBG("DROP: Cannot find PMTU entry for %s", + net_sprint_ipv6_addr(&ip_hdr->src)); + goto silent_drop; + } + + /* We must not accept larger PMTU value than what we already know. + * RFC 8201 chapter 4 page 8. + */ + if (entry->mtu > 0 && entry->mtu < mtu) { + NET_DBG("DROP: PMTU for %s %u larger than %u", + net_sprint_ipv6_addr(&ip_hdr->src), mtu, + entry->mtu); + goto silent_drop; + } + + ret = net_pmtu_update_entry(entry, mtu); + if (ret > 0) { + NET_DBG("PMTU for %s changed from %u to %u", + net_sprint_ipv6_addr(&ip_hdr->src), ret, mtu); + } + + return 0; +drop: + net_stats_update_ipv6_pmtu_drop(net_pkt_iface(pkt)); + + return -EIO; + +silent_drop: + /* If the event is not really an error then just ignore it and + * return 0 so that icmpv6 module will not complain about it. + */ + net_stats_update_ipv6_pmtu_drop(net_pkt_iface(pkt)); + + return 0; +} +#endif /* CONFIG_NET_IPV6_PMTU */ + #if defined(CONFIG_NET_IPV6_NBR_CACHE) static struct net_icmp_ctx ns_ctx; static struct net_icmp_ctx na_ctx; @@ -2725,6 +2838,10 @@ static struct net_icmp_ctx na_ctx; static struct net_icmp_ctx ra_ctx; #endif /* CONFIG_NET_IPV6_ND */ +#if defined(CONFIG_NET_IPV6_PMTU) +static struct net_icmp_ctx ptb_ctx; +#endif /* CONFIG_NET_IPV6_PMTU */ + void net_ipv6_nbr_init(void) { int ret; @@ -2755,5 +2872,13 @@ void net_ipv6_nbr_init(void) ipv6_nd_reachable_timeout); #endif +#if defined(CONFIG_NET_IPV6_PMTU) + ret = net_icmp_init_ctx(&ptb_ctx, NET_ICMPV6_PACKET_TOO_BIG, 0, handle_ptb_input); + if (ret < 0) { + NET_ERR("Cannot register %s handler (%d)", STRINGIFY(NET_ICMPV6_PACKET_TOO_BIG), + ret); + } +#endif + ARG_UNUSED(ret); } diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index b7dd20d3ffdf8..094990109c2a8 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -25,6 +25,7 @@ LOG_MODULE_REGISTER(net_tcp, CONFIG_NET_TCP_LOG_LEVEL); #include "net_stats.h" #include "net_private.h" #include "tcp_internal.h" +#include "pmtu.h" #define ACK_TIMEOUT_MS tcp_max_timeout_ms #define ACK_TIMEOUT K_MSEC(ACK_TIMEOUT_MS) @@ -4392,6 +4393,32 @@ void net_tcp_foreach(net_tcp_cb_t cb, void *user_data) k_mutex_unlock(&tcp_lock); } +static uint16_t get_ipv6_destination_mtu(struct net_if *iface, + const struct in6_addr *dest) +{ +#if defined(CONFIG_NET_IPV6_PMTU) + int mtu = net_pmtu_get_mtu((struct sockaddr *)&(struct sockaddr_in6){ + .sin6_family = AF_INET6, + .sin6_addr = *dest }); + + if (mtu < 0) { + if (iface != NULL) { + return net_if_get_mtu(iface); + } + + return NET_IPV6_MTU; + } + + return (uint16_t)mtu; +#else + if (iface != NULL) { + return net_if_get_mtu(iface); + } + + return NET_IPV6_MTU; +#endif /* CONFIG_NET_IPV6_PMTU */ +} + uint16_t net_tcp_get_supported_mss(const struct tcp *conn) { sa_family_t family = net_context_get_family(conn->context); @@ -4416,26 +4443,16 @@ uint16_t net_tcp_get_supported_mss(const struct tcp *conn) #else return 0; #endif /* CONFIG_NET_IPV4 */ - } -#if defined(CONFIG_NET_IPV6) - else if (family == AF_INET6) { - struct net_if *iface = net_context_get_iface(conn->context); - int mss = 0; - if (iface && net_if_get_mtu(iface) >= NET_IPV6TCPH_LEN) { - /* Detect MSS based on interface MTU minus "TCP,IP - * header size" - */ - mss = net_if_get_mtu(iface) - NET_IPV6TCPH_LEN; - } + } else if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) { + struct net_if *iface = net_context_get_iface(conn->context); + uint16_t dest_mtu; - if (mss == 0) { - mss = NET_IPV6_MTU - NET_IPV6TCPH_LEN; - } + dest_mtu = get_ipv6_destination_mtu(iface, &conn->dst.sin6.sin6_addr); - return mss; + /* Detect MSS based on interface MTU minus "TCP,IP header size" */ + return dest_mtu - NET_IPV6TCPH_LEN; } -#endif /* CONFIG_NET_IPV6 */ return 0; } From 7ebc1a7fd6d12f4c5b887d0fd5723950d69fa34e Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 5 Nov 2024 10:45:12 +0200 Subject: [PATCH 06/24] tests: net: pmtu: Add IPv6 specific PMTU TCP tests Allow tests to check whether a IPv6 TCP connection MTU is changed. Signed-off-by: Jukka Rissanen --- subsys/net/ip/tcp.c | 38 +++++ tests/net/pmtu/prj.conf | 9 +- tests/net/pmtu/src/main.c | 283 ++++++++++++++++++++++++++++++-------- 3 files changed, 268 insertions(+), 62 deletions(-) diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 094990109c2a8..47f3f4b4104aa 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -4457,6 +4457,44 @@ uint16_t net_tcp_get_supported_mss(const struct tcp *conn) return 0; } +#if defined(CONFIG_NET_TEST) +struct testing_user_data { + struct sockaddr remote; + uint16_t mtu; +}; + +static void testing_find_conn(struct tcp *conn, void *user_data) +{ + struct testing_user_data *data = user_data; + + if (IS_ENABLED(CONFIG_NET_IPV6) && data->remote.sa_family == AF_INET6 && + net_ipv6_addr_cmp(&conn->dst.sin6.sin6_addr, + &net_sin6(&data->remote)->sin6_addr)) { + if (data->mtu > 0) { + /* Set it only once */ + return; + } + + NET_DBG("Found connection %p mtu %u", conn, + net_tcp_get_supported_mss(conn) + NET_IPV6TCPH_LEN); + data->mtu = net_tcp_get_supported_mss(conn) + NET_IPV6TCPH_LEN; + return; + } +} + +uint16_t net_tcp_get_mtu(struct sockaddr *dst) +{ + struct testing_user_data data = { + .remote = *dst, + .mtu = 0, + }; + + net_tcp_foreach(testing_find_conn, &data); + + return data.mtu; +} +#endif /* CONFIG_NET_TEST */ + int net_tcp_set_option(struct net_context *context, enum tcp_conn_option option, const void *value, size_t len) diff --git a/tests/net/pmtu/prj.conf b/tests/net/pmtu/prj.conf index 1006a8ccc03d2..48d1ac83464be 100644 --- a/tests/net/pmtu/prj.conf +++ b/tests/net/pmtu/prj.conf @@ -2,13 +2,15 @@ CONFIG_ZTEST=y CONFIG_MAIN_STACK_SIZE=2048 CONFIG_NET_TEST=y CONFIG_NETWORKING=y -CONFIG_NET_UDP=y -CONFIG_NET_TCP=n +CONFIG_NET_UDP=n +CONFIG_NET_TCP=y CONFIG_NET_IPV6=y CONFIG_NET_IPV6_PMTU=y CONFIG_NET_IPV4=y CONFIG_NET_IPV4_PMTU=y -CONFIG_NET_L2_ETHERNET=y +CONFIG_NET_L2_DUMMY=y +CONFIG_NET_LOOPBACK=y +CONFIG_NET_DRIVERS=y CONFIG_NET_LOG=y CONFIG_ENTROPY_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y @@ -18,3 +20,4 @@ CONFIG_NET_BUF_RX_COUNT=20 CONFIG_NET_BUF_TX_COUNT=20 CONFIG_NET_MGMT=y CONFIG_NET_MGMT_EVENT=y +CONFIG_NET_SOCKETS=y diff --git a/tests/net/pmtu/src/main.c b/tests/net/pmtu/src/main.c index 006101e684c74..7f3dbd8c18907 100644 --- a/tests/net/pmtu/src/main.c +++ b/tests/net/pmtu/src/main.c @@ -23,12 +23,19 @@ LOG_MODULE_REGISTER(net_test, CONFIG_NET_PMTU_LOG_LEVEL); #include #include #include +#include +#include #include #include #include #include +#include "../../socket/socket_helpers.h" + +#include "route.h" +#include "icmpv6.h" +#include "ipv6.h" #include "pmtu.h" #define NET_LOG_ENABLED 1 @@ -40,20 +47,22 @@ LOG_MODULE_REGISTER(net_test, CONFIG_NET_PMTU_LOG_LEVEL); #define DBG(fmt, ...) #endif +/* This is a helper function to get the MTU value for the given destination. + * It is implemented in tcp.c file. + */ +extern uint16_t net_tcp_get_mtu(struct sockaddr *dst); + /* Small sleep between tests makes sure that the PMTU destination * cache entries are separated from each other. */ #define SMALL_SLEEP K_MSEC(5) -static struct in_addr my_ipv4_addr = { { { 192, 0, 2, 1 } } }; static struct in_addr dest_ipv4_addr1 = { { { 198, 51, 100, 1 } } }; static struct in_addr dest_ipv4_addr2 = { { { 198, 51, 100, 2 } } }; static struct in_addr dest_ipv4_addr3 = { { { 198, 51, 100, 3 } } }; static struct in_addr dest_ipv4_addr4 = { { { 198, 51, 100, 4 } } }; -static struct in_addr any_ipv4_addr = INADDR_ANY_INIT; +static struct in_addr dest_ipv4_addr_not_found = { { { 1, 2, 3, 4 } } }; -static struct in6_addr my_ipv6_addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; static struct in6_addr dest_ipv6_addr1 = { { { 0x20, 0x01, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; static struct in6_addr dest_ipv6_addr2 = { { { 0x20, 0x01, 0x0d, 0xb8, 0x01, 0, 0, 0, @@ -62,75 +71,61 @@ static struct in6_addr dest_ipv6_addr3 = { { { 0x20, 0x01, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0x3 } } }; static struct in6_addr dest_ipv6_addr4 = { { { 0x20, 0x01, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x4 } } }; -static struct in6_addr any_ipv6_addr = IN6ADDR_ANY_INIT; +static struct in6_addr dest_ipv6_addr_not_found = { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, + 0xad, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x4 } } }; + +static struct net_if *target_iface; +static char target_iface_name[CONFIG_NET_INTERFACE_NAME_LEN + 1]; K_SEM_DEFINE(wait_data, 0, UINT_MAX); +#define PKT_WAIT_TIME K_MSEC(500) #define WAIT_TIME 500 #define WAIT_TIME_LONG MSEC_PER_SEC #define MY_PORT 1969 -#define PEER_PORT 13856 +#define PEER_PORT 2024 +#define PEER_IPV6_ADDR "::1" +#define MY_IPV6_ADDR "::1" +#define MY_IPV4_ADDR "127.0.0.1" +#define PEER_IPV4_ADDR "127.0.0.1" -struct net_test_pmtu { - uint8_t mac_addr[sizeof(struct net_eth_addr)]; - struct net_linkaddr ll_addr; -}; +#define THREAD_SLEEP 50 /* ms */ -int net_test_dev_init(const struct device *dev) +static const char *iface2str(struct net_if *iface) { - return 0; -} - -static uint8_t *net_test_get_mac(const struct device *dev) -{ - struct net_test_pmtu *context = dev->data; - - if (context->mac_addr[2] == 0x00) { - /* 00-00-5E-00-53-xx Documentation RFC 7042 */ - context->mac_addr[0] = 0x00; - context->mac_addr[1] = 0x00; - context->mac_addr[2] = 0x5E; - context->mac_addr[3] = 0x00; - context->mac_addr[4] = 0x53; - context->mac_addr[5] = sys_rand8_get(); + if (net_if_l2(iface) == &NET_L2_GET_NAME(DUMMY)) { + return "No L2"; } - return context->mac_addr; + return ""; } -static void net_test_iface_init(struct net_if *iface) +static void iface_cb(struct net_if *iface, void *user_data) { - uint8_t *mac = net_test_get_mac(net_if_get_device(iface)); + static int if_count; - net_if_set_link_addr(iface, mac, sizeof(struct net_eth_addr), - NET_LINK_ETHERNET); -} + NET_DBG("Interface %p (%s) [%d]", iface, iface2str(iface), + net_if_get_by_iface(iface)); -static int tester_send(const struct device *dev, struct net_pkt *pkt) -{ - if (!pkt->buffer) { - TC_ERROR("No data to send!\n"); - return -ENODATA; + switch (if_count) { + case 0: + target_iface = iface; + (void)net_if_get_name(iface, target_iface_name, + CONFIG_NET_INTERFACE_NAME_LEN); + break; } - return 0; + if_count++; } -struct net_test_pmtu net_test_data; - -static struct ethernet_api net_test_if_api = { - .iface_api.init = net_test_iface_init, - .send = tester_send, -}; +static void *test_setup(void) +{ + net_if_foreach(iface_cb, NULL); -#define _ETH_L2_LAYER ETHERNET_L2 -#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(ETHERNET_L2) + zassert_not_null(target_iface, "Interface is NULL"); -NET_DEVICE_INIT(net_test_pmtu, "net_test_pmtu", - net_test_dev_init, NULL, &net_test_data, NULL, - CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, - &net_test_if_api, _ETH_L2_LAYER, _ETH_L2_CTX_TYPE, - 127); + return NULL; +} ZTEST(net_pmtu_test_suite, test_pmtu_01_ipv4_get_entry) { @@ -209,6 +204,7 @@ ZTEST(net_pmtu_test_suite, test_pmtu_03_ipv4_create_more_entries) #if defined(CONFIG_NET_IPV4_PMTU) struct sockaddr_in dest_ipv4; struct net_pmtu_entry *entry; + uint16_t mtu; int ret; dest_ipv4.sin_family = AF_INET; @@ -225,10 +221,8 @@ ZTEST(net_pmtu_test_suite, test_pmtu_03_ipv4_create_more_entries) net_ipaddr_copy(&dest_ipv4.sin_addr, &dest_ipv4_addr2); ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv4, 1400); zassert_equal(ret, 0, "PMTU IPv4 MTU update failed (%d)", ret); - entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv4); - zassert_equal(entry->mtu, 1400, "PMTU IPv4 MTU is not correct (%d)", - entry->mtu); - + mtu = net_pmtu_get_mtu((struct sockaddr *)&dest_ipv4); + zassert_equal(mtu, 1400, "PMTU IPv4 MTU is not correct (%d)", mtu); k_sleep(SMALL_SLEEP); @@ -238,6 +232,12 @@ ZTEST(net_pmtu_test_suite, test_pmtu_03_ipv4_create_more_entries) entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv4); zassert_equal(entry->mtu, 1500, "PMTU IPv4 MTU is not correct (%d)", entry->mtu); + + net_ipaddr_copy(&dest_ipv4.sin_addr, &dest_ipv4_addr_not_found); + ret = net_pmtu_get_mtu((struct sockaddr *)&dest_ipv4); + zassert_equal(ret, -ENOENT, "PMTU IPv4 MTU update succeed (%d)", ret); + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv4); + zassert_equal(entry, NULL, "PMTU IPv4 MTU update succeed"); #else ztest_test_skip(); #endif @@ -248,6 +248,7 @@ ZTEST(net_pmtu_test_suite, test_pmtu_03_ipv6_create_more_entries) #if defined(CONFIG_NET_IPV6_PMTU) struct sockaddr_in6 dest_ipv6; struct net_pmtu_entry *entry; + uint16_t mtu; int ret; dest_ipv6.sin6_family = AF_INET6; @@ -264,9 +265,8 @@ ZTEST(net_pmtu_test_suite, test_pmtu_03_ipv6_create_more_entries) net_ipaddr_copy(&dest_ipv6.sin6_addr, &dest_ipv6_addr2); ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv6, 1700); zassert_equal(ret, 0, "PMTU IPv6 MTU update failed (%d)", ret); - entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv6); - zassert_equal(entry->mtu, 1700, "PMTU IPv6 MTU is not correct (%d)", - entry->mtu); + mtu = net_pmtu_get_mtu((struct sockaddr *)&dest_ipv6); + zassert_equal(mtu, 1700, "PMTU IPv6 MTU is not correct (%d)", mtu); k_sleep(SMALL_SLEEP); @@ -276,6 +276,12 @@ ZTEST(net_pmtu_test_suite, test_pmtu_03_ipv6_create_more_entries) entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv6); zassert_equal(entry->mtu, 1800, "PMTU IPv6 MTU is not correct (%d)", entry->mtu); + + net_ipaddr_copy(&dest_ipv6.sin6_addr, &dest_ipv6_addr_not_found); + ret = net_pmtu_get_mtu((struct sockaddr *)&dest_ipv6); + zassert_equal(ret, -ENOENT, "PMTU IPv6 MTU update succeed (%d)", ret); + entry = net_pmtu_get_entry((struct sockaddr *)&dest_ipv6); + zassert_equal(entry, NULL, "PMTU IPv6 MTU update succeed"); #else ztest_test_skip(); #endif @@ -349,4 +355,163 @@ ZTEST(net_pmtu_test_suite, test_pmtu_04_ipv6_overflow) #endif } -ZTEST_SUITE(net_pmtu_test_suite, NULL, NULL, NULL, NULL, NULL); +static void test_bind(int sock, struct sockaddr *addr, socklen_t addrlen) +{ + int ret; + + ret = zsock_bind(sock, addr, addrlen); + zassert_equal(ret, 0, "bind failed with error %d", errno); +} + +static void test_listen(int sock) +{ + zassert_equal(zsock_listen(sock, 1), + 0, + "listen failed with error %d", errno); +} + +static void test_connect(int sock, struct sockaddr *addr, socklen_t addrlen) +{ + zassert_equal(zsock_connect(sock, addr, addrlen), + 0, + "connect failed with error %d", errno); + + if (IS_ENABLED(CONFIG_NET_TC_THREAD_PREEMPTIVE)) { + /* Let the connection proceed */ + k_msleep(THREAD_SLEEP); + } +} + +static void test_accept(int sock, int *new_sock, struct sockaddr *addr, + socklen_t *addrlen) +{ + zassert_not_null(new_sock, "null newsock"); + + *new_sock = zsock_accept(sock, addr, addrlen); + zassert_true(*new_sock >= 0, "accept failed"); +} + +#if defined(CONFIG_NET_IPV6_PMTU) +static int get_v6_send_recv_sock(int *srv_sock, + struct sockaddr_in6 *my_saddr, + struct sockaddr_in6 *peer_saddr) +{ + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + int new_sock; + int c_sock; + int s_sock; + + prepare_sock_tcp_v6(PEER_IPV6_ADDR, PEER_PORT, &s_sock, peer_saddr); + test_bind(s_sock, (struct sockaddr *)peer_saddr, sizeof(*peer_saddr)); + test_listen(s_sock); + + prepare_sock_tcp_v6(MY_IPV6_ADDR, MY_PORT, &c_sock, my_saddr); + test_bind(c_sock, (struct sockaddr *)my_saddr, sizeof(*my_saddr)); + test_connect(c_sock, (struct sockaddr *)peer_saddr, sizeof(*peer_saddr)); + + test_accept(s_sock, &new_sock, &addr, &addrlen); + zassert_equal(addrlen, sizeof(struct sockaddr_in6), "wrong addrlen"); + + *srv_sock = new_sock; + + return c_sock; +} + +static int create_icmpv6_ptb(struct net_if *iface, + struct sockaddr_in6 *src, + struct sockaddr_in6 *dst, + uint32_t mtu, + struct net_pkt **pkt) +{ + struct net_icmpv6_ptb ptb_hdr; + struct net_pkt *ptb_pkt; + struct in6_addr *dest6; + struct in6_addr *src6; + int ret; + + ptb_pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct net_ipv6_hdr) + + sizeof(struct net_icmp_hdr) + + sizeof(struct net_icmpv6_ptb), + AF_INET6, IPPROTO_ICMPV6, + PKT_WAIT_TIME); + if (ptb_pkt == NULL) { + NET_DBG("No buffer"); + return -ENOMEM; + } + + dest6 = &dst->sin6_addr; + src6 = &src->sin6_addr; + + ret = net_ipv6_create(ptb_pkt, src6, dest6); + if (ret < 0) { + LOG_ERR("Cannot create IPv6 pkt (%d)", ret); + return ret; + } + + ret = net_icmpv6_create(ptb_pkt, NET_ICMPV6_PACKET_TOO_BIG, 0); + if (ret < 0) { + LOG_ERR("Cannot create ICMPv6 pkt (%d)", ret); + return ret; + } + + ptb_hdr.mtu = htonl(mtu); + + ret = net_pkt_write(ptb_pkt, &ptb_hdr, sizeof(ptb_hdr)); + if (ret < 0) { + LOG_ERR("Cannot write payload (%d)", ret); + return ret; + } + + net_pkt_cursor_init(ptb_pkt); + net_ipv6_finalize(ptb_pkt, IPPROTO_ICMPV6); + + net_pkt_set_iface(ptb_pkt, iface); + + *pkt = ptb_pkt; + + return 0; +} +#endif + +ZTEST(net_pmtu_test_suite, test_pmtu_05_ipv6_tcp) +{ +#if defined(CONFIG_NET_IPV6_PMTU) + struct sockaddr_in6 dest_ipv6; + struct sockaddr_in6 s_saddr = { 0 }; /* peer */ + struct sockaddr_in6 c_saddr = { 0 }; /* this host */ + struct net_pkt *pkt = NULL; + int client_sock, server_sock; + uint16_t mtu; + int ret; + + dest_ipv6.sin6_family = AF_INET6; + + client_sock = get_v6_send_recv_sock(&server_sock, &c_saddr, &s_saddr); + zassert_true(client_sock >= 0, "Failed to create client socket"); + + /* Set initial MTU for the destination */ + ret = net_pmtu_update_mtu((struct sockaddr *)&c_saddr, 4096); + zassert_true(ret >= 0, "PMTU IPv6 MTU update failed (%d)", ret); + + /* Send an ICMPv6 "Packet too big" message from server to client which + * will update the PMTU entry. + */ + ret = create_icmpv6_ptb(target_iface, &s_saddr, &c_saddr, 2048, &pkt); + zassert_equal(ret, 0, "Failed to create ICMPv6 PTB message"); + + ret = net_send_data(pkt); + zassert_equal(ret, 0, "Failed to send PTB message"); + + /* Check that the PMTU entry has been updated */ + mtu = net_tcp_get_mtu((struct sockaddr *)&s_saddr); + zassert_equal(mtu, 2048, "PMTU IPv6 MTU is not correct (%d)", mtu); + + (void)zsock_close(client_sock); + (void)zsock_close(server_sock); +#else + ztest_test_skip(); +#endif /* CONFIG_NET_IPV6_PMTU */ +} + +ZTEST_SUITE(net_pmtu_test_suite, NULL, test_setup, NULL, NULL, NULL); From 6364a2df51c743b2eafcb50a480c3e1da4f1de55 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 5 Nov 2024 14:47:48 +0200 Subject: [PATCH 07/24] net: shell: Add pmtu command to the net-shell Show information whether PMTU is enabled or not. Show pmtu destination cache content with "net pmtu" command. The "net pmtu flush" can be used to clear the cache. Signed-off-by: Jukka Rissanen --- subsys/net/ip/Kconfig | 5 ++ subsys/net/lib/shell/CMakeLists.txt | 1 + subsys/net/lib/shell/ipv4.c | 2 + subsys/net/lib/shell/ipv6.c | 4 + subsys/net/lib/shell/pmtu.c | 112 ++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 subsys/net/lib/shell/pmtu.c diff --git a/subsys/net/ip/Kconfig b/subsys/net/ip/Kconfig index 70155b82f847c..387c352e8d59b 100644 --- a/subsys/net/ip/Kconfig +++ b/subsys/net/ip/Kconfig @@ -265,6 +265,11 @@ config NET_SHELL_PKT_ALLOC_SUPPORTED default y depends on NET_SHELL_SHOW_DISABLED_COMMANDS || NET_DEBUG_NET_PKT_ALLOC +config NET_SHELL_PMTU_SUPPORTED + bool "PMTU config" + default y + depends on NET_SHELL_SHOW_DISABLED_COMMANDS || NET_PMTU + config NET_SHELL_PPP_SUPPORTED bool "PPP config" default y diff --git a/subsys/net/lib/shell/CMakeLists.txt b/subsys/net/lib/shell/CMakeLists.txt index 05c92897403e0..02e9dde5e283c 100644 --- a/subsys/net/lib/shell/CMakeLists.txt +++ b/subsys/net/lib/shell/CMakeLists.txt @@ -24,6 +24,7 @@ zephyr_library_sources(mem.c) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_IPV6_SUPPORTED nbr.c) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_IP_SUPPORTED ping.c) zephyr_library_sources(pkt.c) +zephyr_library_sources_ifdef(CONFIG_NET_SHELL_PMTU_SUPPORTED pmtu.c) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_PPP_SUPPORTED ppp.c) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_POWER_MANAGEMENT_SUPPORTED resume.c) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_ROUTE_SUPPORTED route.c) diff --git a/subsys/net/lib/shell/ipv4.c b/subsys/net/lib/shell/ipv4.c index d70dc786828be..966783182a657 100644 --- a/subsys/net/lib/shell/ipv4.c +++ b/subsys/net/lib/shell/ipv4.c @@ -68,6 +68,8 @@ static int cmd_net_ipv4(const struct shell *sh, size_t argc, char *argv[]) PR("IPv4 conflict detection support : %s\n", IS_ENABLED(CONFIG_NET_IPV4_ACD) ? "enabled" : "disabled"); + PR("Path MTU Discovery (PMTU) : %s\n", + IS_ENABLED(CONFIG_NET_IPV4_PMTU) ? "enabled" : "disabled"); #endif /* CONFIG_NET_NATIVE_IPV4 */ #if defined(CONFIG_NET_IPV4) diff --git a/subsys/net/lib/shell/ipv6.c b/subsys/net/lib/shell/ipv6.c index eb65185c03ca1..55b100e34c71b 100644 --- a/subsys/net/lib/shell/ipv6.c +++ b/subsys/net/lib/shell/ipv6.c @@ -194,6 +194,10 @@ static int cmd_net_ipv6(const struct shell *sh, size_t argc, char *argv[]) " : %d\n", CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT); #endif /* CONFIG_NET_IPV6_PE */ + + PR("Path MTU Discovery (PMTU) : %s\n", + IS_ENABLED(CONFIG_NET_IPV6_PMTU) ? "enabled" : "disabled"); + #endif /* CONFIG_NET_NATIVE_IPV6 */ #if defined(CONFIG_NET_IPV6) diff --git a/subsys/net/lib/shell/pmtu.c b/subsys/net/lib/shell/pmtu.c new file mode 100644 index 0000000000000..bf8eca403ce84 --- /dev/null +++ b/subsys/net/lib/shell/pmtu.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_DECLARE(net_shell); + +#include "net_shell_private.h" + +#include "pmtu.h" + +#if !defined(CONFIG_NET_PMTU) +static void print_pmtu_error(const struct shell *sh) +{ + PR_INFO("Set %s to enable %s support.\n", + "CONFIG_NET_IPV6_PMTU or CONFIG_NET_IPV4_PMTU", "PMTU"); +} +#endif + +#if defined(CONFIG_NET_PMTU) +static void pmtu_cb(struct net_pmtu_entry *entry, void *user_data) +{ + struct net_shell_user_data *data = user_data; + const struct shell *sh = data->sh; + int *count = data->user_data; + +#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_IPV6) +/* Use the value of NET_IPV6_ADDR_LEN */ +#define ADDR_STR_LEN 40 +#elif defined(CONFIG_NET_IPV4) +#define ADDR_STR_LEN INET_ADDRSTRLEN +#elif defined(CONFIG_NET_IPV6) +#define ADDR_STR_LEN 40 +#else +#define ADDR_STR_LEN INET_ADDRSTRLEN +#endif + + if (!entry->in_use) { + return; + } + + if (*count == 0) { + PR(" %" STRINGIFY(ADDR_STR_LEN) "s MTU Age (sec)\n", + "Destination Address"); + } + + PR("[%2d] %" STRINGIFY(ADDR_STR_LEN) "s %5d %d\n", *count + 1, + net_sprint_addr(entry->dst.family, (void *)&entry->dst.in_addr), + entry->mtu, + (k_uptime_get_32() - entry->last_update) / 1000U); + + (*count)++; +} +#endif /* CONFIG_NET_PMTU */ + +static int cmd_net_pmtu(const struct shell *sh, size_t argc, char *argv[]) +{ +#if defined(CONFIG_NET_PMTU) + struct net_shell_user_data user_data; + int arg = 1; +#endif + + ARG_UNUSED(argc); + +#if defined(CONFIG_NET_PMTU) + if (!argv[arg]) { + /* PMTU destination cache content */ + int count = 0; + + user_data.sh = sh; + user_data.user_data = &count; + + (void)net_pmtu_foreach(pmtu_cb, &user_data); + + if (count == 0) { + PR("PMTU destination cache is empty.\n"); + } + } +#else + print_pmtu_error(sh); +#endif + + return 0; +} + +static int cmd_net_pmtu_flush(const struct shell *sh, size_t argc, char *argv[]) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + +#if defined(CONFIG_NET_PMTU) + PR("Flushing PMTU destination cache.\n"); + net_pmtu_init(); +#else + print_pmtu_error(sh); +#endif + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_pmtu, + SHELL_CMD(flush, NULL, + "Remove all entries from PMTU destination cache.", + cmd_net_pmtu_flush), + SHELL_SUBCMD_SET_END +); + +SHELL_SUBCMD_ADD((net), pmtu, &net_cmd_pmtu, + "Show PMTU information.", + cmd_net_pmtu, 1, 0); From a52c866ea324521731c10a2cf2bf4b9fc457db3e Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 5 Nov 2024 16:53:24 +0200 Subject: [PATCH 08/24] net: stats: ipv4: pmtu: Add Path MTU Discovery statistics Add information about PMTU related packets received/sent/dropped for IPv4. Signed-off-by: Jukka Rissanen --- include/zephyr/net/net_stats.h | 30 ++++++++++++++++++++++++++++++ samples/net/stats/src/main.c | 7 +++++++ subsys/net/ip/Kconfig.stats | 7 +++++++ subsys/net/ip/net_stats.c | 18 ++++++++++++++++++ subsys/net/ip/net_stats.h | 23 +++++++++++++++++++++++ subsys/net/lib/shell/stats.c | 7 +++++++ 6 files changed, 92 insertions(+) diff --git a/include/zephyr/net/net_stats.h b/include/zephyr/net/net_stats.h index 93f34da6236fe..2fb28b6104c71 100644 --- a/include/zephyr/net/net_stats.h +++ b/include/zephyr/net/net_stats.h @@ -212,6 +212,20 @@ struct net_stats_ipv6_pmtu { net_stats_t sent; }; +/** + * @brief IPv4 Path MTU Discovery statistics + */ +struct net_stats_ipv4_pmtu { + /** Number of dropped IPv4 PMTU packets. */ + net_stats_t drop; + + /** Number of received IPv4 PMTU packets. */ + net_stats_t recv; + + /** Number of sent IPv4 PMTU packets. */ + net_stats_t sent; +}; + /** * @brief IPv6 multicast listener daemon statistics */ @@ -398,6 +412,11 @@ struct net_stats { struct net_stats_ipv6_pmtu ipv6_pmtu; #endif +#if defined(CONFIG_NET_STATISTICS_IPV4_PMTU) + /** IPv4 Path MTU Discovery statistics */ + struct net_stats_ipv4_pmtu ipv4_pmtu; +#endif + #if defined(CONFIG_NET_STATISTICS_MLD) /** IPv6 MLD statistics */ struct net_stats_ipv6_mld ipv6_mld; @@ -685,6 +704,7 @@ enum net_request_stats_cmd { NET_REQUEST_STATS_CMD_GET_IPV6, NET_REQUEST_STATS_CMD_GET_IPV6_ND, NET_REQUEST_STATS_CMD_GET_IPV6_PMTU, + NET_REQUEST_STATS_CMD_GET_IPV4_PMTU, NET_REQUEST_STATS_CMD_GET_ICMP, NET_REQUEST_STATS_CMD_GET_UDP, NET_REQUEST_STATS_CMD_GET_TCP, @@ -762,6 +782,16 @@ NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV6_PMTU); /** @endcond */ #endif /* CONFIG_NET_STATISTICS_IPV6_PMTU */ +#if defined(CONFIG_NET_STATISTICS_IPV4_PMTU) +/** Request IPv4 Path MTU Discovery statistics */ +#define NET_REQUEST_STATS_GET_IPV4_PMTU \ + (_NET_STATS_BASE | NET_REQUEST_STATS_CMD_GET_IPV4_PMTU) + +/** @cond INTERNAL_HIDDEN */ +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV4_PMTU); +/** @endcond */ +#endif /* CONFIG_NET_STATISTICS_IPV4_PMTU */ + #if defined(CONFIG_NET_STATISTICS_ICMP) /** Request ICMPv4 and ICMPv6 statistics */ #define NET_REQUEST_STATS_GET_ICMP \ diff --git a/samples/net/stats/src/main.c b/samples/net/stats/src/main.c index f6e35a89e904e..fdfb7e4339c18 100644 --- a/samples/net/stats/src/main.c +++ b/samples/net/stats/src/main.c @@ -74,6 +74,13 @@ static void print_stats(struct net_if *iface, struct net_stats *data) GET_STAT(iface, ip_errors.chkerr), GET_STAT(iface, ip_errors.protoerr)); +#if defined(CONFIG_NET_IPV4_PMTU) + printk("IPv4 PMTU recv %d\tsent\t%d\tdrop\t%d\n", + GET_STAT(iface, ipv4_pmtu.recv), + GET_STAT(iface, ipv4_pmtu.sent), + GET_STAT(iface, ipv4_pmtu.drop)); +#endif /* CONFIG_NET_IPV4_PMTU */ + printk("ICMP recv %d\tsent\t%d\tdrop\t%d\n", GET_STAT(iface, icmp.recv), GET_STAT(iface, icmp.sent), diff --git a/subsys/net/ip/Kconfig.stats b/subsys/net/ip/Kconfig.stats index 9851e5063fd06..cdd59c11685f1 100644 --- a/subsys/net/ip/Kconfig.stats +++ b/subsys/net/ip/Kconfig.stats @@ -64,6 +64,13 @@ config NET_STATISTICS_IPV6_PMTU help Keep track of IPv6 Path MTU Discovery related statistics +config NET_STATISTICS_IPV4_PMTU + bool "IPv4 PMTU statistics" + depends on NET_IPV4_PMTU + default y + help + Keep track of IPv4 Path MTU Discovery related statistics + config NET_STATISTICS_ICMP bool "ICMP statistics" depends on NET_IPV6 || NET_IPV4 diff --git a/subsys/net/ip/net_stats.c b/subsys/net/ip/net_stats.c index ab6d7855ee0eb..69579b9ec6fb5 100644 --- a/subsys/net/ip/net_stats.c +++ b/subsys/net/ip/net_stats.c @@ -122,6 +122,13 @@ static inline void stats(struct net_if *iface) GET_STAT(iface, ip_errors.chkerr), GET_STAT(iface, ip_errors.protoerr)); +#if defined(CONFIG_NET_STATISTICS_IPV4_PMTU) + NET_INFO("IPv4 PMTU recv %d\tsent\t%d\tdrop\t%d", + GET_STAT(iface, ipv4_pmtu.recv), + GET_STAT(iface, ipv4_pmtu.sent), + GET_STAT(iface, ipv4_pmtu.drop)); +#endif /* CONFIG_NET_STATISTICS_IPV4_PMTU */ + NET_INFO("ICMP recv %d\tsent\t%d\tdrop\t%d", GET_STAT(iface, icmp.recv), GET_STAT(iface, icmp.sent), @@ -291,6 +298,12 @@ static int net_stats_get(uint32_t mgmt_request, struct net_if *iface, src = GET_STAT_ADDR(iface, ipv6_pmtu); break; #endif +#if defined(CONFIG_NET_STATISTICS_IPV4_PMTU) + case NET_REQUEST_STATS_CMD_GET_IPV4_PMTU: + len_chk = sizeof(struct net_stats_ipv4_pmtu); + src = GET_STAT_ADDR(iface, ipv4_pmtu); + break; +#endif #if defined(CONFIG_NET_STATISTICS_ICMP) case NET_REQUEST_STATS_CMD_GET_ICMP: len_chk = sizeof(struct net_stats_icmp); @@ -358,6 +371,11 @@ NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV6_PMTU, net_stats_get); #endif +#if defined(CONFIG_NET_STATISTICS_IPV4_PMTU) +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV4_PMTU, + net_stats_get); +#endif + #if defined(CONFIG_NET_STATISTICS_ICMP) NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_ICMP, net_stats_get); diff --git a/subsys/net/ip/net_stats.h b/subsys/net/ip/net_stats.h index 54ae8ff3773ee..60777d06f1e4f 100644 --- a/subsys/net/ip/net_stats.h +++ b/subsys/net/ip/net_stats.h @@ -136,6 +136,29 @@ static inline void net_stats_update_ipv6_pmtu_drop(struct net_if *iface) #define net_stats_update_ipv6_pmtu_drop(iface) #endif /* CONFIG_NET_STATISTICS_IPV6_PMTU */ +#if defined(CONFIG_NET_STATISTICS_IPV4_PMTU) && defined(CONFIG_NET_NATIVE_IPV4) +/* IPv4 Path MTU Discovery stats */ + +static inline void net_stats_update_ipv4_pmtu_sent(struct net_if *iface) +{ + UPDATE_STAT(iface, stats.ipv4_pmtu.sent++); +} + +static inline void net_stats_update_ipv4_pmtu_recv(struct net_if *iface) +{ + UPDATE_STAT(iface, stats.ipv4_pmtu.recv++); +} + +static inline void net_stats_update_ipv4_pmtu_drop(struct net_if *iface) +{ + UPDATE_STAT(iface, stats.ipv4_pmtu.drop++); +} +#else +#define net_stats_update_ipv4_pmtu_sent(iface) +#define net_stats_update_ipv4_pmtu_recv(iface) +#define net_stats_update_ipv4_pmtu_drop(iface) +#endif /* CONFIG_NET_STATISTICS_IPV4_PMTU */ + #if defined(CONFIG_NET_STATISTICS_IPV4) && defined(CONFIG_NET_NATIVE_IPV4) /* IPv4 stats */ diff --git a/subsys/net/lib/shell/stats.c b/subsys/net/lib/shell/stats.c index 3bd7f09a6d80a..3556ec0269a4e 100644 --- a/subsys/net/lib/shell/stats.c +++ b/subsys/net/lib/shell/stats.c @@ -498,6 +498,13 @@ static void net_shell_print_statistics(struct net_if *iface, void *user_data) GET_STAT(iface, ip_errors.chkerr), GET_STAT(iface, ip_errors.protoerr)); +#if defined(CONFIG_NET_STATISTICS_IPV4_PMTU) + PR("IPv4 PMTU recv %d\tsent\t%d\tdrop\t%d\n", + GET_STAT(iface, ipv4_pmtu.recv), + GET_STAT(iface, ipv4_pmtu.sent), + GET_STAT(iface, ipv4_pmtu.drop)); +#endif /* CONFIG_NET_STATISTICS_IPV4_PMTU */ + #if defined(CONFIG_NET_STATISTICS_ICMP) && defined(CONFIG_NET_NATIVE_IPV4) PR("ICMP recv %d\tsent\t%d\tdrop\t%d\n", GET_STAT(iface, icmp.recv), From 7c3cf50f80e5428dea150142412ea8f2d57e1ec8 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 5 Nov 2024 17:50:11 +0200 Subject: [PATCH 09/24] net: ipv4: Add PMTU support Catch "Destination Unreachable" ICMPv4 messages and update PMTU for a given destination IPv4 address. Use that PMTU when sending data to the destination. Signed-off-by: Jukka Rissanen --- include/zephyr/net/net_pkt.h | 30 +++++++++ subsys/net/ip/icmpv4.c | 112 ++++++++++++++++++++++++++++++++++ subsys/net/ip/icmpv4.h | 5 ++ subsys/net/ip/ipv4.c | 38 +++++++++++- subsys/net/ip/ipv4.h | 28 ++++----- subsys/net/ip/ipv4_fragment.c | 2 +- subsys/net/ip/net_context.c | 17 ++++++ subsys/net/ip/net_if.c | 4 +- subsys/net/ip/tcp.c | 48 +++++++++------ 9 files changed, 247 insertions(+), 37 deletions(-) diff --git a/include/zephyr/net/net_pkt.h b/include/zephyr/net/net_pkt.h index 7693f9a79734b..1a3b4a042aeec 100644 --- a/include/zephyr/net/net_pkt.h +++ b/include/zephyr/net/net_pkt.h @@ -342,6 +342,11 @@ struct net_pkt { uint8_t cooked_mode_pkt : 1; #endif /* CONFIG_NET_CAPTURE_COOKED_MODE */ +#if defined(CONFIG_NET_IPV4_PMTU) + /* Path MTU needed for this destination address */ + uint8_t ipv4_pmtu : 1; +#endif /* CONFIG_NET_IPV4_PMTU */ + /* @endcond */ }; @@ -783,6 +788,31 @@ static inline uint16_t net_pkt_ip_opts_len(struct net_pkt *pkt) #endif } +#if defined(CONFIG_NET_IPV4_PMTU) +static inline bool net_pkt_ipv4_pmtu(struct net_pkt *pkt) +{ + return !!pkt->ipv4_pmtu; +} + +static inline void net_pkt_set_ipv4_pmtu(struct net_pkt *pkt, bool value) +{ + pkt->ipv4_pmtu = value; +} +#else +static inline bool net_pkt_ipv4_pmtu(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return false; +} + +static inline void net_pkt_set_ipv4_pmtu(struct net_pkt *pkt, bool value) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(value); +} +#endif /* CONFIG_NET_IPV4_PMTU */ + #if defined(CONFIG_NET_IPV4_FRAGMENT) static inline uint16_t net_pkt_ipv4_fragment_offset(struct net_pkt *pkt) { diff --git a/subsys/net/ip/icmpv4.c b/subsys/net/ip/icmpv4.c index 5e1cb35566c57..cc25e572dd4cf 100644 --- a/subsys/net/ip/icmpv4.c +++ b/subsys/net/ip/icmpv4.c @@ -21,6 +21,7 @@ LOG_MODULE_REGISTER(net_icmpv4, CONFIG_NET_ICMPV4_LOG_LEVEL); #include "ipv4.h" #include "icmpv4.h" #include "net_stats.h" +#include "pmtu.h" #define PKT_WAIT_TIME K_SECONDS(1) @@ -654,6 +655,108 @@ enum net_verdict net_icmpv4_input(struct net_pkt *pkt, return NET_DROP; } +#if defined(CONFIG_NET_IPV4_PMTU) +/* The RFC 1191 chapter 3 says the minimum MTU size is 68 octets. + * This is way too small in modern world, so make the minimum 576 octets. + */ +#define MIN_IPV4_MTU NET_IPV4_MTU + +static int icmpv4_handle_dst_unreach(struct net_icmp_ctx *ctx, + struct net_pkt *pkt, + struct net_icmp_ip_hdr *hdr, + struct net_icmp_hdr *icmp_hdr, + void *user_data) +{ + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(dst_unreach_access, + struct net_icmpv4_dest_unreach); + struct net_icmpv4_dest_unreach *dest_unreach_hdr; + struct net_ipv4_hdr *ip_hdr = hdr->ipv4; + uint16_t length = net_pkt_get_len(pkt); + struct net_pmtu_entry *entry; + struct sockaddr_in sockaddr_src = { + .sin_family = AF_INET, + }; + uint16_t mtu; + int ret; + + ARG_UNUSED(user_data); + + dest_unreach_hdr = (struct net_icmpv4_dest_unreach *) + net_pkt_get_data(pkt, &dst_unreach_access); + if (dest_unreach_hdr == NULL) { + NET_DBG("DROP: NULL ICMPv4 Destination Unreachable header"); + goto drop; + } + + net_stats_update_ipv4_pmtu_recv(net_pkt_iface(pkt)); + + NET_DBG("Received Destination Unreachable from %s to %s", + net_sprint_ipv4_addr(&ip_hdr->src), + net_sprint_ipv4_addr(&ip_hdr->dst)); + + if (length < (sizeof(struct net_ipv4_hdr) + + sizeof(struct net_icmp_hdr) + + sizeof(struct net_icmpv4_dest_unreach))) { + NET_DBG("DROP: length %d too big %zd", + length, sizeof(struct net_ipv4_hdr) + + sizeof(struct net_icmp_hdr) + + sizeof(struct net_icmpv4_dest_unreach)); + goto drop; + } + + net_pkt_acknowledge_data(pkt, &dst_unreach_access); + + mtu = ntohs(dest_unreach_hdr->mtu); + + if (mtu < MIN_IPV4_MTU) { + NET_DBG("DROP: Unsupported MTU %u, min is %u", + mtu, MIN_IPV4_MTU); + goto drop; + } + + net_ipaddr_copy(&sockaddr_src.sin_addr, (struct in_addr *)&ip_hdr->src); + + entry = net_pmtu_get_entry((struct sockaddr *)&sockaddr_src); + if (entry == NULL) { + NET_DBG("DROP: Cannot find PMTU entry for %s", + net_sprint_ipv4_addr(&ip_hdr->src)); + goto silent_drop; + } + + /* We must not accept larger PMTU value than what we already know. + * RFC 1191 chapter 3 page 5. + */ + if (entry->mtu > 0 && entry->mtu < mtu) { + NET_DBG("DROP: PMTU for %s %u larger than %u", + net_sprint_ipv4_addr(&ip_hdr->src), mtu, + entry->mtu); + goto silent_drop; + } + + ret = net_pmtu_update_entry(entry, mtu); + if (ret > 0) { + NET_DBG("PMTU for %s changed from %u to %u", + net_sprint_ipv4_addr(&ip_hdr->src), ret, mtu); + } + + return 0; +drop: + net_stats_update_ipv4_pmtu_drop(net_pkt_iface(pkt)); + + return -EIO; + +silent_drop: + /* If the event is not really an error then just ignore it and + * return 0 so that icmpv4 module will not complain about it. + */ + net_stats_update_ipv4_pmtu_drop(net_pkt_iface(pkt)); + + return 0; +} + +static struct net_icmp_ctx dst_unreach_ctx; +#endif /* CONFIG_NET_IPV4_PMTU */ + void net_icmpv4_init(void) { static struct net_icmp_ctx ctx; @@ -664,4 +767,13 @@ void net_icmpv4_init(void) NET_ERR("Cannot register %s handler (%d)", STRINGIFY(NET_ICMPV4_ECHO_REQUEST), ret); } + +#if defined(CONFIG_NET_IPV4_PMTU) + ret = net_icmp_init_ctx(&dst_unreach_ctx, NET_ICMPV4_DST_UNREACH, 0, + icmpv4_handle_dst_unreach); + if (ret < 0) { + NET_ERR("Cannot register %s handler (%d)", STRINGIFY(NET_ICMPV4_DST_UNREACH), + ret); + } +#endif } diff --git a/subsys/net/ip/icmpv4.h b/subsys/net/ip/icmpv4.h index ae848c9d96415..88f13971c9ca2 100644 --- a/subsys/net/ip/icmpv4.h +++ b/subsys/net/ip/icmpv4.h @@ -34,6 +34,11 @@ struct net_icmpv4_echo_req { uint16_t sequence; } __packed; +struct net_icmpv4_dest_unreach { + uint16_t unused; + uint16_t mtu; +} __packed; + /** * @brief Send ICMPv4 error message. * @param pkt Network packet that this error is related to. diff --git a/subsys/net/ip/ipv4.c b/subsys/net/ip/ipv4.c index a7d74d74a2fd4..7178ba1d1dbb4 100644 --- a/subsys/net/ip/ipv4.c +++ b/subsys/net/ip/ipv4.c @@ -25,6 +25,7 @@ LOG_MODULE_REGISTER(net_ipv4, CONFIG_NET_IPV4_LOG_LEVEL); #include "tcp_internal.h" #include "dhcpv4/dhcpv4_internal.h" #include "ipv4.h" +#include "pmtu.h" BUILD_ASSERT(sizeof(struct in_addr) == NET_IPV4_ADDR_SIZE); @@ -90,13 +91,18 @@ int net_ipv4_create(struct net_pkt *pkt, const struct in_addr *dst) { uint8_t tos = 0; + uint8_t flags = 0U; if (IS_ENABLED(CONFIG_NET_IP_DSCP_ECN)) { net_ipv4_set_dscp(&tos, net_pkt_ip_dscp(pkt)); net_ipv4_set_ecn(&tos, net_pkt_ip_ecn(pkt)); } - return net_ipv4_create_full(pkt, src, dst, tos, 0U, 0U, 0U); + if (IS_ENABLED(CONFIG_NET_IPV4_PMTU) && net_pkt_ipv4_pmtu(pkt)) { + flags = NET_IPV4_DF; + } + + return net_ipv4_create_full(pkt, src, dst, tos, 0U, flags, 0U); } int net_ipv4_finalize(struct net_pkt *pkt, uint8_t next_header_proto) @@ -444,6 +450,36 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt, bool is_loopback) return NET_DROP; } +enum net_verdict net_ipv4_prepare_for_send(struct net_pkt *pkt) +{ + if (IS_ENABLED(CONFIG_NET_IPV4_PMTU)) { + struct net_pmtu_entry *entry; + struct sockaddr_in dst = { + .sin_family = AF_INET, + }; + int ret; + + net_ipv4_addr_copy_raw((uint8_t *)&dst.sin_addr, + NET_IPV4_HDR(pkt)->dst); + entry = net_pmtu_get_entry((struct sockaddr *)&dst); + if (entry == NULL) { + ret = net_pmtu_update_mtu((struct sockaddr *)&dst, + net_if_get_mtu(net_pkt_iface(pkt))); + if (ret < 0) { + NET_DBG("Cannot update PMTU for %s (%d)", + net_sprint_ipv4_addr(&dst.sin_addr), + ret); + } + } + } + +#if defined(CONFIG_NET_IPV4_FRAGMENT) + return net_ipv4_prepare_for_send_fragment(pkt); +#else + return NET_OK; +#endif +} + void net_ipv4_init(void) { if (IS_ENABLED(CONFIG_NET_IPV4_FRAGMENT)) { diff --git a/subsys/net/ip/ipv4.h b/subsys/net/ip/ipv4.h index 8c063952e9fff..e70fae3ff1f7c 100644 --- a/subsys/net/ip/ipv4.h +++ b/subsys/net/ip/ipv4.h @@ -357,6 +357,17 @@ typedef void (*net_ipv4_frag_cb_t)(struct net_ipv4_reassembly *reass, void *user */ void net_ipv4_frag_foreach(net_ipv4_frag_cb_t cb, void *user_data); +/** + * @brief Prepare packet for sending, this will split up a packet that is too large to send into + * multiple fragments so that it can be sent. It will also update PMTU destination cache if it + * is enabled. + * + * @param pkt Network packet + * + * @return Return verdict about the packet. + */ +enum net_verdict net_ipv4_prepare_for_send(struct net_pkt *pkt); + #if defined(CONFIG_NET_NATIVE_IPV4) /** * @brief Initialises IPv4 @@ -384,22 +395,9 @@ static inline enum net_verdict net_ipv4_handle_fragment_hdr(struct net_pkt *pkt, } #endif /* CONFIG_NET_IPV4_FRAGMENT */ -/** - * @brief Prepare packet for sending, this will split up a packet that is too large to send into - * multiple fragments so that it can be sent. - * - * @param pkt Network packet - * - * @return Return verdict about the packet. - */ #if defined(CONFIG_NET_IPV4_FRAGMENT) -enum net_verdict net_ipv4_prepare_for_send(struct net_pkt *pkt); -#else -static inline enum net_verdict net_ipv4_prepare_for_send(struct net_pkt *pkt) -{ - return NET_OK; -} -#endif /* CONFIG_NET_IPV4_FRAGMENT */ +enum net_verdict net_ipv4_prepare_for_send_fragment(struct net_pkt *pkt); +#endif /** * @brief Sets up fragment buffers for usage, should only be called by the SYS_INIT() handler in diff --git a/subsys/net/ip/ipv4_fragment.c b/subsys/net/ip/ipv4_fragment.c index 2fe6fb2033dd3..c095efc4c2d45 100644 --- a/subsys/net/ip/ipv4_fragment.c +++ b/subsys/net/ip/ipv4_fragment.c @@ -607,7 +607,7 @@ int net_ipv4_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt, return 0; } -enum net_verdict net_ipv4_prepare_for_send(struct net_pkt *pkt) +enum net_verdict net_ipv4_prepare_for_send_fragment(struct net_pkt *pkt) { NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv4_access, struct net_ipv4_hdr); struct net_ipv4_hdr *ip_hdr; diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index e6a13874bdef4..357e95061e550 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -37,6 +37,7 @@ LOG_MODULE_REGISTER(net_ctx, CONFIG_NET_CONTEXT_LOG_LEVEL); #include "udp_internal.h" #include "tcp_internal.h" #include "net_stats.h" +#include "pmtu.h" #if defined(CONFIG_NET_TCP) #include "tcp.h" @@ -1139,6 +1140,22 @@ int net_context_create_ipv4_new(struct net_context *context, } #endif + if (IS_ENABLED(CONFIG_NET_IPV4_PMTU)) { + struct net_pmtu_entry *entry; + struct sockaddr_in dst_addr = { + .sin_family = AF_INET, + .sin_addr = *dst, + }; + + entry = net_pmtu_get_entry((struct sockaddr *)&dst_addr); + if (entry == NULL) { + /* Try to figure out the MTU of the path */ + net_pkt_set_ipv4_pmtu(pkt, true); + } else { + net_pkt_set_ipv4_pmtu(pkt, false); + } + } + return net_ipv4_create(pkt, src, dst); } #endif /* CONFIG_NET_IPV4 */ diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index f9f7f68b41428..fa4cb4b29ea62 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -520,11 +520,9 @@ enum net_verdict net_if_send_data(struct net_if *iface, struct net_pkt *pkt) verdict = net_ipv6_prepare_for_send(pkt); } -#if defined(CONFIG_NET_IPV4_FRAGMENT) - if (net_pkt_family(pkt) == AF_INET) { + if (IS_ENABLED(CONFIG_NET_IPV4) && net_pkt_family(pkt) == AF_INET) { verdict = net_ipv4_prepare_for_send(pkt); } -#endif done: /* NET_OK in which case packet has checked successfully. In this case diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 47f3f4b4104aa..e7f5c7d7c95b8 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -4419,30 +4419,44 @@ static uint16_t get_ipv6_destination_mtu(struct net_if *iface, #endif /* CONFIG_NET_IPV6_PMTU */ } +static uint16_t get_ipv4_destination_mtu(struct net_if *iface, + const struct in_addr *dest) +{ +#if defined(CONFIG_NET_IPV4_PMTU) + int mtu = net_pmtu_get_mtu((struct sockaddr *)&(struct sockaddr_in){ + .sin_family = AF_INET, + .sin_addr = *dest }); + + if (mtu < 0) { + if (iface != NULL) { + return net_if_get_mtu(iface); + } + + return NET_IPV4_MTU; + } + + return (uint16_t)mtu; +#else + if (iface != NULL) { + return net_if_get_mtu(iface); + } + + return NET_IPV4_MTU; +#endif /* CONFIG_NET_IPV4_PMTU */ +} + uint16_t net_tcp_get_supported_mss(const struct tcp *conn) { sa_family_t family = net_context_get_family(conn->context); - if (family == AF_INET) { -#if defined(CONFIG_NET_IPV4) + if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) { struct net_if *iface = net_context_get_iface(conn->context); - int mss = 0; - - if (iface && net_if_get_mtu(iface) >= NET_IPV4TCPH_LEN) { - /* Detect MSS based on interface MTU minus "TCP,IP - * header size" - */ - mss = net_if_get_mtu(iface) - NET_IPV4TCPH_LEN; - } + uint16_t dest_mtu; - if (mss == 0) { - mss = NET_IPV4_MTU - NET_IPV4TCPH_LEN; - } + dest_mtu = get_ipv4_destination_mtu(iface, &conn->dst.sin.sin_addr); - return mss; -#else - return 0; -#endif /* CONFIG_NET_IPV4 */ + /* Detect MSS based on interface MTU minus "TCP,IP header size" */ + return dest_mtu - NET_IPV4TCPH_LEN; } else if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) { struct net_if *iface = net_context_get_iface(conn->context); From 09aae0d5e819f35b5b9363657eb1b84284db7ab8 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 5 Nov 2024 18:10:00 +0200 Subject: [PATCH 10/24] tests: net: pmtu: Add IPv4 specific PMTU TCP tests Allow tests to check whether a IPv4 TCP connection MTU is changed. Signed-off-by: Jukka Rissanen --- subsys/net/ip/tcp.c | 14 +++++ tests/net/pmtu/src/main.c | 125 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index e7f5c7d7c95b8..14da3f0cc349b 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -4494,6 +4494,20 @@ static void testing_find_conn(struct tcp *conn, void *user_data) data->mtu = net_tcp_get_supported_mss(conn) + NET_IPV6TCPH_LEN; return; } + + if (IS_ENABLED(CONFIG_NET_IPV4) && data->remote.sa_family == AF_INET && + net_ipv4_addr_cmp(&conn->dst.sin.sin_addr, + &net_sin(&data->remote)->sin_addr)) { + if (data->mtu > 0) { + /* Set it only once */ + return; + } + + NET_DBG("Found connection %p mtu %u", conn, + net_tcp_get_supported_mss(conn) + NET_IPV4TCPH_LEN); + data->mtu = net_tcp_get_supported_mss(conn) + NET_IPV4TCPH_LEN; + return; + } } uint16_t net_tcp_get_mtu(struct sockaddr *dst) diff --git a/tests/net/pmtu/src/main.c b/tests/net/pmtu/src/main.c index 7f3dbd8c18907..542c89519d84d 100644 --- a/tests/net/pmtu/src/main.c +++ b/tests/net/pmtu/src/main.c @@ -35,7 +35,9 @@ LOG_MODULE_REGISTER(net_test, CONFIG_NET_PMTU_LOG_LEVEL); #include "route.h" #include "icmpv6.h" +#include "icmpv4.h" #include "ipv6.h" +#include "ipv4.h" #include "pmtu.h" #define NET_LOG_ENABLED 1 @@ -514,4 +516,127 @@ ZTEST(net_pmtu_test_suite, test_pmtu_05_ipv6_tcp) #endif /* CONFIG_NET_IPV6_PMTU */ } +#if defined(CONFIG_NET_IPV4_PMTU) +static int get_v4_send_recv_sock(int *srv_sock, + struct sockaddr_in *my_saddr, + struct sockaddr_in *peer_saddr) +{ + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + int new_sock; + int c_sock; + int s_sock; + + prepare_sock_tcp_v4(PEER_IPV4_ADDR, PEER_PORT, &s_sock, peer_saddr); + test_bind(s_sock, (struct sockaddr *)peer_saddr, sizeof(*peer_saddr)); + test_listen(s_sock); + + prepare_sock_tcp_v4(MY_IPV4_ADDR, MY_PORT, &c_sock, my_saddr); + test_bind(c_sock, (struct sockaddr *)my_saddr, sizeof(*my_saddr)); + test_connect(c_sock, (struct sockaddr *)peer_saddr, sizeof(*peer_saddr)); + + test_accept(s_sock, &new_sock, &addr, &addrlen); + zassert_equal(addrlen, sizeof(struct sockaddr_in), "wrong addrlen"); + + *srv_sock = new_sock; + + return c_sock; +} + +static int create_icmpv4_dest_unreach(struct net_if *iface, + struct sockaddr_in *src, + struct sockaddr_in *dst, + uint32_t mtu, + struct net_pkt **pkt) +{ + struct net_icmpv4_dest_unreach du_hdr; + struct net_pkt *du_pkt; + struct in_addr *dest4; + struct in_addr *src4; + int ret; + + du_pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct net_ipv4_hdr) + + sizeof(struct net_icmp_hdr) + + sizeof(struct net_icmpv4_dest_unreach), + AF_INET, IPPROTO_ICMP, + PKT_WAIT_TIME); + if (du_pkt == NULL) { + NET_DBG("No buffer"); + return -ENOMEM; + } + + dest4 = &dst->sin_addr; + src4 = &src->sin_addr; + + ret = net_ipv4_create(du_pkt, src4, dest4); + if (ret < 0) { + LOG_ERR("Cannot create IPv4 pkt (%d)", ret); + return ret; + } + + ret = net_icmpv4_create(du_pkt, NET_ICMPV4_DST_UNREACH, 0); + if (ret < 0) { + LOG_ERR("Cannot create ICMPv4 pkt (%d)", ret); + return ret; + } + + du_hdr.mtu = htons(mtu); + + ret = net_pkt_write(du_pkt, &du_hdr, sizeof(du_hdr)); + if (ret < 0) { + LOG_ERR("Cannot write payload (%d)", ret); + return ret; + } + + net_pkt_cursor_init(du_pkt); + net_ipv4_finalize(du_pkt, IPPROTO_ICMP); + + net_pkt_set_iface(du_pkt, iface); + + *pkt = du_pkt; + + return 0; +} +#endif + +ZTEST(net_pmtu_test_suite, test_pmtu_05_ipv4_tcp) +{ +#if defined(CONFIG_NET_IPV4_PMTU) + struct sockaddr_in dest_ipv4; + struct sockaddr_in s_saddr = { 0 }; /* peer */ + struct sockaddr_in c_saddr = { 0 }; /* this host */ + struct net_pkt *pkt = NULL; + int client_sock, server_sock; + uint16_t mtu; + int ret; + + dest_ipv4.sin_family = AF_INET; + + client_sock = get_v4_send_recv_sock(&server_sock, &c_saddr, &s_saddr); + zassert_true(client_sock >= 0, "Failed to create client socket"); + + /* Set initial MTU for the destination */ + ret = net_pmtu_update_mtu((struct sockaddr *)&c_saddr, 4096); + zassert_true(ret >= 0, "PMTU IPv6 MTU update failed (%d)", ret); + + /* Send an ICMPv4 "Destination Unreachable" message from server to client which + * will update the PMTU entry. + */ + ret = create_icmpv4_dest_unreach(target_iface, &s_saddr, &c_saddr, 2048, &pkt); + zassert_equal(ret, 0, "Failed to create ICMPv4 Destination Unrechable message"); + + ret = net_send_data(pkt); + zassert_equal(ret, 0, "Failed to send Destination Unreachable message"); + + /* Check that the PMTU entry has been updated */ + mtu = net_tcp_get_mtu((struct sockaddr *)&s_saddr); + zassert_equal(mtu, 2048, "PMTU IPv4 MTU is not correct (%d)", mtu); + + (void)zsock_close(client_sock); + (void)zsock_close(server_sock); +#else + ztest_test_skip(); +#endif /* CONFIG_NET_IPV4_PMTU */ +} + ZTEST_SUITE(net_pmtu_test_suite, NULL, test_setup, NULL, NULL, NULL); From 1c93c7eb0f08b768dcf503d398e44259b527c4c6 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 6 Nov 2024 14:27:28 +0200 Subject: [PATCH 11/24] net: pmtu: Send net_mgmt event for changed path MTU Send a network management event for a changed path MTU value. Both IPv4 and IPv6 have their own events as we cannot mix these because how the network event numbering space is implemented. Signed-off-by: Jukka Rissanen --- include/zephyr/net/net_event.h | 38 ++++++++++++++++++++++++++++++ subsys/net/ip/Kconfig | 3 +++ subsys/net/ip/pmtu.c | 43 ++++++++++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/include/zephyr/net/net_event.h b/include/zephyr/net/net_event.h index 9bcba06be76eb..f0f8545a713a7 100644 --- a/include/zephyr/net/net_event.h +++ b/include/zephyr/net/net_event.h @@ -74,6 +74,7 @@ enum net_event_ipv6_cmd { NET_EVENT_IPV6_CMD_PE_DISABLED, NET_EVENT_IPV6_CMD_PE_FILTER_ADD, NET_EVENT_IPV6_CMD_PE_FILTER_DEL, + NET_EVENT_IPV6_CMD_PMTU_CHANGED, }; /* IPv4 Events*/ @@ -99,6 +100,7 @@ enum net_event_ipv4_cmd { NET_EVENT_IPV4_CMD_ACD_SUCCEED, NET_EVENT_IPV4_CMD_ACD_FAILED, NET_EVENT_IPV4_CMD_ACD_CONFLICT, + NET_EVENT_IPV4_CMD_PMTU_CHANGED, }; /* L4 network events */ @@ -237,6 +239,10 @@ enum net_event_l4_cmd { #define NET_EVENT_IPV6_PE_FILTER_DEL \ (_NET_EVENT_IPV6_BASE | NET_EVENT_IPV6_CMD_PE_FILTER_DEL) +/** IPv6 Path MTU is changed. */ +#define NET_EVENT_IPV6_PMTU_CHANGED \ + (_NET_EVENT_IPV6_BASE | NET_EVENT_IPV6_CMD_PMTU_CHANGED) + /** Event emitted when an IPv4 address is added to the system. */ #define NET_EVENT_IPV4_ADDR_ADD \ (_NET_EVENT_IPV4_BASE | NET_EVENT_IPV4_CMD_ADDR_ADD) @@ -296,6 +302,10 @@ enum net_event_l4_cmd { #define NET_EVENT_IPV4_ACD_CONFLICT \ (_NET_EVENT_IPV4_BASE | NET_EVENT_IPV4_CMD_ACD_CONFLICT) +/** IPv4 Path MTU is changed. */ +#define NET_EVENT_IPV4_PMTU_CHANGED \ + (_NET_EVENT_IPV4_BASE | NET_EVENT_IPV4_CMD_PMTU_CHANGED) + /** Event emitted when the system is considered to be connected. * The connected in this context means that the network interface is up, * and the interface has either IPv4 or IPv6 address assigned to it. @@ -441,6 +451,34 @@ struct net_event_ipv6_pe_filter { bool is_deny_list; }; +/** + * @brief Network Management event information structure + * Used to pass information on network event + * NET_EVENT_IPV4_PMTU_CHANGED + * when CONFIG_NET_MGMT_EVENT_INFO enabled and event generator pass the + * information. + */ +struct net_event_ipv4_pmtu_info { + /** IPv4 address */ + struct in_addr dst; + /** New MTU */ + uint16_t mtu; +}; + +/** + * @brief Network Management event information structure + * Used to pass information on network event + * NET_EVENT_IPV6_PMTU_CHANGED + * when CONFIG_NET_MGMT_EVENT_INFO enabled and event generator pass the + * information. + */ +struct net_event_ipv6_pmtu_info { + /** IPv6 address */ + struct in6_addr dst; + /** New MTU */ + uint32_t mtu; +}; + #ifdef __cplusplus } #endif diff --git a/subsys/net/ip/Kconfig b/subsys/net/ip/Kconfig index 387c352e8d59b..f123854a7a32c 100644 --- a/subsys/net/ip/Kconfig +++ b/subsys/net/ip/Kconfig @@ -50,6 +50,9 @@ config NET_NATIVE_IPV4 config NET_PMTU bool + select NET_MGMT + select NET_MGMT_EVENT + select NET_MGMT_EVENT_INFO default y depends on NET_IPV6_PMTU || NET_IPV4_PMTU diff --git a/subsys/net/ip/pmtu.c b/subsys/net/ip/pmtu.c index e1f9d748f5f42..be2c3355a6b40 100644 --- a/subsys/net/ip/pmtu.c +++ b/subsys/net/ip/pmtu.c @@ -12,7 +12,9 @@ LOG_MODULE_REGISTER(net_pmtu, CONFIG_NET_PMTU_LOG_LEVEL); #include - +#include +#include +#include #include "pmtu.h" #if defined(CONFIG_NET_IPV4_PMTU) @@ -104,8 +106,45 @@ static struct net_pmtu_entry *get_free_pmtu_entry(void) static void update_pmtu_entry(struct net_pmtu_entry *entry, uint16_t mtu) { - entry->mtu = 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) From c5ed86319c31edba862752d2800e59c72bffed5b Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 6 Nov 2024 14:29:53 +0200 Subject: [PATCH 12/24] tests: net: pmtu: Add network event tests for pmtu Verify that the PMTU changed events are generated and we can catch them. Signed-off-by: Jukka Rissanen --- tests/net/pmtu/src/main.c | 133 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/tests/net/pmtu/src/main.c b/tests/net/pmtu/src/main.c index 542c89519d84d..30b09a2e8a622 100644 --- a/tests/net/pmtu/src/main.c +++ b/tests/net/pmtu/src/main.c @@ -43,6 +43,12 @@ LOG_MODULE_REGISTER(net_test, CONFIG_NET_PMTU_LOG_LEVEL); #define NET_LOG_ENABLED 1 #include "net_private.h" +#if defined(CONFIG_BOARD_NATIVE_SIM) || defined(CONFIG_BOARD_NATIVE_SIM_NATIVE_64) +#define WAIT_PROPERLY 0 +#else +#define WAIT_PROPERLY 1 +#endif + #if defined(CONFIG_NET_PMTU_LOG_LEVEL_DBG) #define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__) #else @@ -93,6 +99,58 @@ K_SEM_DEFINE(wait_data, 0, UINT_MAX); #define THREAD_SLEEP 50 /* ms */ +static K_SEM_DEFINE(wait_pmtu_changed, 0, UINT_MAX); +static bool is_pmtu_changed; + +static void ipv6_pmtu_changed(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, + struct net_if *iface) +{ + ARG_UNUSED(cb); + ARG_UNUSED(iface); + + if (mgmt_event != NET_EVENT_IPV6_PMTU_CHANGED) { + return; + } + + NET_DBG("IPv6 PMTU changed event received"); + + k_sem_give(&wait_pmtu_changed); + is_pmtu_changed = true; + + /* Let the network stack to proceed */ + k_msleep(THREAD_SLEEP); +} + +static void ipv4_pmtu_changed(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, + struct net_if *iface) +{ + ARG_UNUSED(cb); + ARG_UNUSED(iface); + + if (mgmt_event != NET_EVENT_IPV4_PMTU_CHANGED) { + return; + } + + NET_DBG("IPv4 PMTU changed event received"); + + k_sem_give(&wait_pmtu_changed); + is_pmtu_changed = true; + + /* Let the network stack to proceed */ + k_msleep(THREAD_SLEEP); +} + +static struct mgmt_events { + uint32_t event; + net_mgmt_event_handler_t handler; + struct net_mgmt_event_callback cb; +} mgmt_events[] = { + { .event = NET_EVENT_IPV6_PMTU_CHANGED, .handler = ipv6_pmtu_changed }, + { .event = NET_EVENT_IPV4_PMTU_CHANGED, .handler = ipv4_pmtu_changed }, +}; + static const char *iface2str(struct net_if *iface) { if (net_if_l2(iface) == &NET_L2_GET_NAME(DUMMY)) { @@ -120,6 +178,25 @@ static void iface_cb(struct net_if *iface, void *user_data) if_count++; } +static void setup_mgmt_events(void) +{ + static bool setup_done; + + if (setup_done) { + return; + } + + setup_done = true; + + ARRAY_FOR_EACH(mgmt_events, i) { + net_mgmt_init_event_callback(&mgmt_events[i].cb, + mgmt_events[i].handler, + mgmt_events[i].event); + + net_mgmt_add_event_callback(&mgmt_events[i].cb); + } +} + static void *test_setup(void) { net_if_foreach(iface_cb, NULL); @@ -639,4 +716,60 @@ ZTEST(net_pmtu_test_suite, test_pmtu_05_ipv4_tcp) #endif /* CONFIG_NET_IPV4_PMTU */ } +ZTEST(net_pmtu_test_suite, test_pmtu_06_ipv4_event) +{ +#if defined(CONFIG_NET_IPV4_PMTU) && WAIT_PROPERLY + struct sockaddr_in dest_ipv4; + int ret; + + setup_mgmt_events(); + + is_pmtu_changed = false; + + net_ipaddr_copy(&dest_ipv4.sin_addr, &dest_ipv4_addr1); + dest_ipv4.sin_family = AF_INET; + + ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv4, 1200); + zassert_equal(ret, 0, "PMTU IPv4 MTU update failed (%d)", ret); + + if (k_sem_take(&wait_pmtu_changed, K_MSEC(WAIT_TIME))) { + zassert_true(0, "Timeout while waiting pmtu changed event"); + } + + zassert_true(is_pmtu_changed, "Did not catch pmtu changed event"); + + is_pmtu_changed = false; +#else + ztest_test_skip(); +#endif /* CONFIG_NET_IPV4_PMTU */ +} + +ZTEST(net_pmtu_test_suite, test_pmtu_06_ipv6_event) +{ +#if defined(CONFIG_NET_IPV6_PMTU) && WAIT_PROPERLY + struct sockaddr_in6 dest_ipv6; + int ret; + + setup_mgmt_events(); + + is_pmtu_changed = false; + + net_ipaddr_copy(&dest_ipv6.sin6_addr, &dest_ipv6_addr1); + dest_ipv6.sin6_family = AF_INET6; + + ret = net_pmtu_update_mtu((struct sockaddr *)&dest_ipv6, 1500); + zassert_equal(ret, 0, "PMTU IPv6 MTU update failed (%d)", ret); + + if (k_sem_take(&wait_pmtu_changed, K_MSEC(WAIT_TIME))) { + zassert_true(0, "Timeout while waiting pmtu changed event"); + } + + zassert_true(is_pmtu_changed, "Did not catch pmtu changed event"); + + is_pmtu_changed = false; +#else + ztest_test_skip(); +#endif /* CONFIG_NET_IPV6_PMTU */ +} + ZTEST_SUITE(net_pmtu_test_suite, NULL, test_setup, NULL, NULL, NULL); From 722595dd1672060d0ec8f63770f5bca122875625 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 6 Nov 2024 14:31:10 +0200 Subject: [PATCH 13/24] net: shell: events: Print PMTU event values Print the changed PMTU value and IP address in the event monitor. Signed-off-by: Jukka Rissanen --- subsys/net/lib/shell/events.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/subsys/net/lib/shell/events.c b/subsys/net/lib/shell/events.c index 687e136e7cb33..66d3acb190cab 100644 --- a/subsys/net/lib/shell/events.c +++ b/subsys/net/lib/shell/events.c @@ -88,6 +88,11 @@ static char *get_l3_desc(struct event_msg *msg, static const char *desc_unknown = ""; char *info = NULL; +#if defined(CONFIG_NET_PMTU) +#define MAX_PMTU_INFO_STR_LEN sizeof("changed MTU xxxxx for") + static char pmtu_buf[MAX_PMTU_INFO_STR_LEN + 1]; +#endif + *desc = desc_unknown; switch (msg->event) { @@ -267,6 +272,34 @@ static char *get_l3_desc(struct event_msg *msg, info = net_addr_ntop(AF_INET, msg->data, extra_info, extra_info_len); break; + case NET_EVENT_IPV4_PMTU_CHANGED: { +#if defined(CONFIG_NET_IPV4_PMTU) + struct net_event_ipv4_pmtu_info *pmtu_info = + (struct net_event_ipv4_pmtu_info *)msg->data; + + *desc = "IPV4 PMTU"; + *desc2 = pmtu_buf; + snprintk(pmtu_buf, MAX_PMTU_INFO_STR_LEN, + "changed MTU %u for", (uint16_t)pmtu_info->mtu); + info = net_addr_ntop(AF_INET, &pmtu_info->dst, extra_info, + extra_info_len); +#endif + break; + } + case NET_EVENT_IPV6_PMTU_CHANGED: { +#if defined(CONFIG_NET_IPV6_PMTU) + struct net_event_ipv6_pmtu_info *pmtu_info = + (struct net_event_ipv6_pmtu_info *)msg->data; + + *desc = "IPV6 PMTU"; + *desc2 = pmtu_buf; + snprintk(pmtu_buf, MAX_PMTU_INFO_STR_LEN, + "changed MTU %u for", (uint16_t)pmtu_info->mtu); + info = net_addr_ntop(AF_INET6, &pmtu_info->dst, extra_info, + extra_info_len); +#endif + break; + } } return info; From b0145a7ec861903fb4903d4e50f1f3773bbfc79b Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 6 Nov 2024 14:39:09 +0200 Subject: [PATCH 14/24] net: shell: events: Set the command mask correctly Set the commands to monitor correctly. Before this change some events were missed. Signed-off-by: Jukka Rissanen --- subsys/net/lib/shell/events.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/subsys/net/lib/shell/events.c b/subsys/net/lib/shell/events.c index 66d3acb190cab..699db20ef534b 100644 --- a/subsys/net/lib/shell/events.c +++ b/subsys/net/lib/shell/events.c @@ -20,9 +20,9 @@ LOG_MODULE_DECLARE(net_shell); #define THREAD_PRIORITY K_PRIO_COOP(2) #define MAX_EVENT_INFO_SIZE NET_EVENT_INFO_MAX_SIZE #define MONITOR_L2_MASK (_NET_EVENT_IF_BASE) -#define MONITOR_L3_IPV4_MASK (_NET_EVENT_IPV4_BASE) -#define MONITOR_L3_IPV6_MASK (_NET_EVENT_IPV6_BASE) -#define MONITOR_L4_MASK (_NET_EVENT_L4_BASE) +#define MONITOR_L3_IPV4_MASK (_NET_EVENT_IPV4_BASE | NET_MGMT_COMMAND_MASK) +#define MONITOR_L3_IPV6_MASK (_NET_EVENT_IPV6_BASE | NET_MGMT_COMMAND_MASK) +#define MONITOR_L4_MASK (_NET_EVENT_L4_BASE | NET_MGMT_COMMAND_MASK) static bool net_event_monitoring; static bool net_event_shutting_down; From ad693c99b6ebcfc5630c41592c14a107708b3dc4 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 12 Nov 2024 18:18:44 +0200 Subject: [PATCH 15/24] net: Add support for IP_MTU IPv4 socket option Add IP_MTU IPv4 socket option and implement getsockopt() call for the option. The IP_MTU option does not support setsockopt() call. Signed-off-by: Jukka Rissanen --- include/zephyr/net/net_context.h | 1 + include/zephyr/net/socket.h | 6 +++ subsys/net/ip/net_context.c | 53 +++++++++++++++++++++++++++ subsys/net/lib/sockets/sockets_inet.c | 12 ++++++ 4 files changed, 72 insertions(+) diff --git a/include/zephyr/net/net_context.h b/include/zephyr/net/net_context.h index 430c1164f4047..6b7e2a7e233e8 100644 --- a/include/zephyr/net/net_context.h +++ b/include/zephyr/net/net_context.h @@ -1292,6 +1292,7 @@ enum net_context_option { NET_OPT_TTL = 16, /**< IPv4 unicast TTL */ NET_OPT_ADDR_PREFERENCES = 17, /**< IPv6 address preference */ NET_OPT_TIMESTAMPING = 18, /**< Packet timestamping */ + NET_OPT_MTU = 20, /**< IPv4 socket path MTU */ }; /** diff --git a/include/zephyr/net/socket.h b/include/zephyr/net/socket.h index f1a937d42294d..a6285c50774c1 100644 --- a/include/zephyr/net/socket.h +++ b/include/zephyr/net/socket.h @@ -1190,6 +1190,12 @@ struct in_pktinfo { struct in_addr ipi_addr; /**< Header Destination address */ }; +/** Retrieve the current known path MTU of the current socket. Returns an + * integer. IP_MTU is valid only for getsockopt and can be employed only when + * the socket has been connected. + */ +#define IP_MTU 14 + /** Set IPv4 multicast TTL value. */ #define IP_MULTICAST_TTL 33 /** Join IPv4 multicast group. */ diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index 357e95061e550..173000fad0d67 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -1788,6 +1788,48 @@ static int get_context_timestamping(struct net_context *context, #endif } +static int get_context_mtu(struct net_context *context, + void *value, size_t *len) +{ + sa_family_t family = net_context_get_family(context); + struct net_if *iface = NULL; + int mtu; + + if (IS_ENABLED(CONFIG_NET_PMTU)) { + mtu = net_pmtu_get_mtu(&context->remote); + if (mtu > 0) { + goto out; + } + } + + if (net_context_is_bound_to_iface(context)) { + iface = net_context_get_iface(context); + + mtu = net_if_get_mtu(iface); + } else { + if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) { + iface = net_if_ipv6_select_src_iface( + &net_sin6(&context->remote)->sin6_addr); + } else if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) { + iface = net_if_ipv4_select_src_iface( + &net_sin(&context->remote)->sin_addr); + } else { + return -EAFNOSUPPORT; + } + + mtu = net_if_get_mtu(iface); + } + +out: + *((int *)value) = mtu; + + if (len) { + *len = sizeof(int); + } + + return 0; +} + /* If buf is not NULL, then use it. Otherwise read the data to be written * to net_pkt from msghdr. */ @@ -3188,6 +3230,14 @@ int net_context_set_option(struct net_context *context, break; case NET_OPT_TIMESTAMPING: ret = set_context_timestamping(context, value, len); + break; + case NET_OPT_MTU: + /* IPv4 only supports getting the MTU */ + if (IS_ENABLED(CONFIG_NET_IPV4) && + net_context_get_family(context) == AF_INET) { + ret = -EOPNOTSUPP; + } + break; } @@ -3265,6 +3315,9 @@ int net_context_get_option(struct net_context *context, case NET_OPT_TIMESTAMPING: ret = get_context_timestamping(context, value, len); break; + case NET_OPT_MTU: + ret = get_context_mtu(context, value, len); + break; } k_mutex_unlock(&context->lock); diff --git a/subsys/net/lib/sockets/sockets_inet.c b/subsys/net/lib/sockets/sockets_inet.c index acb9d470a5ea9..af57c40e5bc2d 100644 --- a/subsys/net/lib/sockets/sockets_inet.c +++ b/subsys/net/lib/sockets/sockets_inet.c @@ -1840,6 +1840,18 @@ int zsock_getsockopt_ctx(struct net_context *ctx, int level, int optname, } return 0; + + case IP_MTU: + if (IS_ENABLED(CONFIG_NET_IPV4)) { + ret = net_context_get_option(ctx, NET_OPT_MTU, + optval, optlen); + if (ret < 0) { + errno = -ret; + return -1; + } + + return 0; + } } break; From fa8e489a6e90c22729fb0648485417ba52cb694c Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 12 Nov 2024 18:20:06 +0200 Subject: [PATCH 16/24] tests: net: pmtu: Add IP_MTU socket option tests Make sure we can use IP_MTU socket option from application. Signed-off-by: Jukka Rissanen --- tests/net/pmtu/prj.conf | 3 ++ tests/net/pmtu/src/main.c | 61 ++++++++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/tests/net/pmtu/prj.conf b/tests/net/pmtu/prj.conf index 48d1ac83464be..1253c5c7c1c85 100644 --- a/tests/net/pmtu/prj.conf +++ b/tests/net/pmtu/prj.conf @@ -21,3 +21,6 @@ CONFIG_NET_BUF_TX_COUNT=20 CONFIG_NET_MGMT=y CONFIG_NET_MGMT_EVENT=y CONFIG_NET_SOCKETS=y +CONFIG_ZVFS_OPEN_MAX=32 +CONFIG_NET_MAX_CONTEXTS=32 +CONFIG_NET_MAX_CONN=32 diff --git a/tests/net/pmtu/src/main.c b/tests/net/pmtu/src/main.c index 30b09a2e8a622..9c41e8ae5a35b 100644 --- a/tests/net/pmtu/src/main.c +++ b/tests/net/pmtu/src/main.c @@ -473,7 +473,9 @@ static void test_accept(int sock, int *new_sock, struct sockaddr *addr, #if defined(CONFIG_NET_IPV6_PMTU) static int get_v6_send_recv_sock(int *srv_sock, struct sockaddr_in6 *my_saddr, - struct sockaddr_in6 *peer_saddr) + struct sockaddr_in6 *peer_saddr, + uint16_t my_port, + uint16_t peer_port) { struct sockaddr addr; socklen_t addrlen = sizeof(addr); @@ -481,11 +483,11 @@ static int get_v6_send_recv_sock(int *srv_sock, int c_sock; int s_sock; - prepare_sock_tcp_v6(PEER_IPV6_ADDR, PEER_PORT, &s_sock, peer_saddr); + prepare_sock_tcp_v6(PEER_IPV6_ADDR, peer_port, &s_sock, peer_saddr); test_bind(s_sock, (struct sockaddr *)peer_saddr, sizeof(*peer_saddr)); test_listen(s_sock); - prepare_sock_tcp_v6(MY_IPV6_ADDR, MY_PORT, &c_sock, my_saddr); + prepare_sock_tcp_v6(MY_IPV6_ADDR, my_port, &c_sock, my_saddr); test_bind(c_sock, (struct sockaddr *)my_saddr, sizeof(*my_saddr)); test_connect(c_sock, (struct sockaddr *)peer_saddr, sizeof(*peer_saddr)); @@ -566,7 +568,8 @@ ZTEST(net_pmtu_test_suite, test_pmtu_05_ipv6_tcp) dest_ipv6.sin6_family = AF_INET6; - client_sock = get_v6_send_recv_sock(&server_sock, &c_saddr, &s_saddr); + client_sock = get_v6_send_recv_sock(&server_sock, &c_saddr, &s_saddr, + MY_PORT, PEER_PORT); zassert_true(client_sock >= 0, "Failed to create client socket"); /* Set initial MTU for the destination */ @@ -596,7 +599,9 @@ ZTEST(net_pmtu_test_suite, test_pmtu_05_ipv6_tcp) #if defined(CONFIG_NET_IPV4_PMTU) static int get_v4_send_recv_sock(int *srv_sock, struct sockaddr_in *my_saddr, - struct sockaddr_in *peer_saddr) + struct sockaddr_in *peer_saddr, + uint16_t my_port, + uint16_t peer_port) { struct sockaddr addr; socklen_t addrlen = sizeof(addr); @@ -604,11 +609,11 @@ static int get_v4_send_recv_sock(int *srv_sock, int c_sock; int s_sock; - prepare_sock_tcp_v4(PEER_IPV4_ADDR, PEER_PORT, &s_sock, peer_saddr); + prepare_sock_tcp_v4(PEER_IPV4_ADDR, peer_port, &s_sock, peer_saddr); test_bind(s_sock, (struct sockaddr *)peer_saddr, sizeof(*peer_saddr)); test_listen(s_sock); - prepare_sock_tcp_v4(MY_IPV4_ADDR, MY_PORT, &c_sock, my_saddr); + prepare_sock_tcp_v4(MY_IPV4_ADDR, my_port, &c_sock, my_saddr); test_bind(c_sock, (struct sockaddr *)my_saddr, sizeof(*my_saddr)); test_connect(c_sock, (struct sockaddr *)peer_saddr, sizeof(*peer_saddr)); @@ -689,7 +694,8 @@ ZTEST(net_pmtu_test_suite, test_pmtu_05_ipv4_tcp) dest_ipv4.sin_family = AF_INET; - client_sock = get_v4_send_recv_sock(&server_sock, &c_saddr, &s_saddr); + client_sock = get_v4_send_recv_sock(&server_sock, &c_saddr, &s_saddr, + MY_PORT, PEER_PORT); zassert_true(client_sock >= 0, "Failed to create client socket"); /* Set initial MTU for the destination */ @@ -772,4 +778,43 @@ ZTEST(net_pmtu_test_suite, test_pmtu_06_ipv6_event) #endif /* CONFIG_NET_IPV6_PMTU */ } +ZTEST(net_pmtu_test_suite, test_pmtu_07_socket_api_ipv4) +{ +#if defined(CONFIG_NET_IPV4_PMTU) + struct sockaddr_in s_saddr = { 0 }; /* peer */ + struct sockaddr_in c_saddr = { 0 }; /* this host */ + int ret, client_sock, server_sock; + size_t optlen; + int optval; + int err; + + client_sock = get_v4_send_recv_sock(&server_sock, &c_saddr, &s_saddr, + MY_PORT + 1, PEER_PORT + 1); + zassert_true(client_sock >= 0, "Failed to create client socket"); + + /* Set initial MTU for the destination */ + ret = net_pmtu_update_mtu((struct sockaddr *)&c_saddr, 4096); + zassert_true(ret >= 0, "PMTU IPv4 MTU update failed (%d)", ret); + + optval = 0; optlen = sizeof(int); + ret = zsock_getsockopt(client_sock, IPPROTO_IP, IP_MTU, &optval, &optlen); + err = -errno; + zexpect_equal(ret, 0, "setsockopt failed (%d)", err); + zexpect_equal(optlen, sizeof(int), "setsockopt optlen (%d)", optlen); + zexpect_equal(optval, 4096, "setsockopt mtu (%d)", optval); + + optval = 0; optlen = sizeof(int); + ret = zsock_setsockopt(client_sock, IPPROTO_IP, IP_MTU, &optval, optlen); + err = -errno; + zexpect_equal(ret, -1, "setsockopt failed (%d)", err); + zexpect_equal(optlen, sizeof(int), "setsockopt optlen (%d)", optlen); + zexpect_equal(optval, 0, "setsockopt mtu (%d)", optval); + + (void)zsock_close(client_sock); + (void)zsock_close(server_sock); +#else + ztest_test_skip(); +#endif /* CONFIG_NET_IPV4_PMTU */ +} + ZTEST_SUITE(net_pmtu_test_suite, NULL, test_setup, NULL, NULL, NULL); From ca6decbb32a995be5c3f507c1d628106ac74cc6a Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 13 Nov 2024 09:26:53 +0200 Subject: [PATCH 17/24] net: Add support for IPV6_MTU IPv6 socket option Add IPV6_MTU IPv6 socket option and implement getsockopt() and setsockopt() calls for the option. Signed-off-by: Jukka Rissanen --- include/zephyr/net/socket.h | 7 ++++ subsys/net/ip/net_context.c | 52 +++++++++++++++++++++++++++ subsys/net/lib/sockets/sockets_inet.c | 28 +++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/include/zephyr/net/socket.h b/include/zephyr/net/socket.h index a6285c50774c1..2519a186614d4 100644 --- a/include/zephyr/net/socket.h +++ b/include/zephyr/net/socket.h @@ -1242,6 +1242,13 @@ struct ipv6_mreq { int ipv6mr_ifindex; }; +/** For getsockopt(), retrieve the current known IPv6 path MTU of the given socket. + * Valid only when the socket has been connected. + * For setsockopt(), set the MTU to be used for the socket. The MTU is limited by + * the device MTU or the path MTU when path MTU discovery is enabled. + */ +#define IPV6_MTU 24 + /** Don't support IPv4 access */ #define IPV6_V6ONLY 26 diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index 173000fad0d67..0d304e624eb1c 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -3102,6 +3102,55 @@ static int set_context_reuseport(struct net_context *context, #endif } +static int set_context_ipv6_mtu(struct net_context *context, + const void *value, size_t len) +{ +#if defined(CONFIG_NET_IPV6) + struct net_if *iface; + uint16_t mtu; + + if (len != sizeof(int)) { + return -EINVAL; + } + + mtu = *((int *)value); + + if (IS_ENABLED(CONFIG_NET_IPV6_PMTU)) { + int ret; + + ret = net_pmtu_update_mtu(&context->remote, mtu); + if (ret < 0) { + return ret; + } + + return 0; + } + + if (net_context_is_bound_to_iface(context)) { + iface = net_context_get_iface(context); + } else { + sa_family_t family = net_context_get_family(context); + + if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) { + iface = net_if_ipv6_select_src_iface( + &net_sin6(&context->remote)->sin6_addr); + } else { + return -EAFNOSUPPORT; + } + } + + net_if_set_mtu(iface, (uint16_t)mtu); + + return 0; +#else + ARG_UNUSED(context); + ARG_UNUSED(value); + ARG_UNUSED(len); + + return -ENOTSUP; +#endif +} + static int set_context_ipv6_v6only(struct net_context *context, const void *value, size_t len) { @@ -3236,6 +3285,9 @@ int net_context_set_option(struct net_context *context, if (IS_ENABLED(CONFIG_NET_IPV4) && net_context_get_family(context) == AF_INET) { ret = -EOPNOTSUPP; + } else if (IS_ENABLED(CONFIG_NET_IPV6) && + net_context_get_family(context) == AF_INET6) { + ret = set_context_ipv6_mtu(context, value, len); } break; diff --git a/subsys/net/lib/sockets/sockets_inet.c b/subsys/net/lib/sockets/sockets_inet.c index af57c40e5bc2d..8e5673208aa3e 100644 --- a/subsys/net/lib/sockets/sockets_inet.c +++ b/subsys/net/lib/sockets/sockets_inet.c @@ -1858,6 +1858,20 @@ int zsock_getsockopt_ctx(struct net_context *ctx, int level, int optname, case IPPROTO_IPV6: switch (optname) { + case IPV6_MTU: + if (IS_ENABLED(CONFIG_NET_IPV6)) { + ret = net_context_get_option(ctx, NET_OPT_MTU, + optval, optlen); + if (ret < 0) { + errno = -ret; + return -1; + } + + return 0; + } + + break; + case IPV6_V6ONLY: if (IS_ENABLED(CONFIG_NET_IPV4_MAPPING_TO_IPV6)) { ret = net_context_get_option(ctx, @@ -2422,6 +2436,20 @@ int zsock_setsockopt_ctx(struct net_context *ctx, int level, int optname, case IPPROTO_IPV6: switch (optname) { + case IPV6_MTU: + if (IS_ENABLED(CONFIG_NET_IPV6)) { + ret = net_context_set_option(ctx, NET_OPT_MTU, + optval, optlen); + if (ret < 0) { + errno = -ret; + return -1; + } + + return 0; + } + + break; + case IPV6_V6ONLY: if (IS_ENABLED(CONFIG_NET_IPV4_MAPPING_TO_IPV6)) { ret = net_context_set_option(ctx, From a77e506a79c21c4bb5d21414143eeed4b2e7e95b Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 13 Nov 2024 09:28:02 +0200 Subject: [PATCH 18/24] tests: net: pmtu: Add IPV6_MTU socket option tests Make sure we can use IPV6_MTU socket option from application. Signed-off-by: Jukka Rissanen --- tests/net/pmtu/src/main.c | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/net/pmtu/src/main.c b/tests/net/pmtu/src/main.c index 9c41e8ae5a35b..54d225a259e25 100644 --- a/tests/net/pmtu/src/main.c +++ b/tests/net/pmtu/src/main.c @@ -817,4 +817,50 @@ ZTEST(net_pmtu_test_suite, test_pmtu_07_socket_api_ipv4) #endif /* CONFIG_NET_IPV4_PMTU */ } +ZTEST(net_pmtu_test_suite, test_pmtu_08_socket_api_ipv6) +{ +#if defined(CONFIG_NET_IPV6_PMTU) + struct sockaddr_in6 s_saddr = { 0 }; /* peer */ + struct sockaddr_in6 c_saddr = { 0 }; /* this host */ + int ret, client_sock, server_sock; + size_t optlen; + int optval; + int err; + + client_sock = get_v6_send_recv_sock(&server_sock, &c_saddr, &s_saddr, + MY_PORT + 2, PEER_PORT + 2); + zassert_true(client_sock >= 0, "Failed to create client socket"); + + /* Set initial MTU for the destination */ + ret = net_pmtu_update_mtu((struct sockaddr *)&c_saddr, 2048); + zassert_true(ret >= 0, "PMTU IPv6 MTU update failed (%d)", ret); + + optval = 0; optlen = sizeof(int); + ret = zsock_getsockopt(client_sock, IPPROTO_IPV6, IPV6_MTU, &optval, &optlen); + err = -errno; + zexpect_equal(ret, 0, "getsockopt failed (%d)", err); + zexpect_equal(optlen, sizeof(int), "getsockopt optlen (%d)", optlen); + zexpect_equal(optval, 2048, "getsockopt mtu (%d)", optval); + + optval = 1500; optlen = sizeof(int); + ret = zsock_setsockopt(client_sock, IPPROTO_IPV6, IPV6_MTU, &optval, optlen); + err = -errno; + zexpect_equal(ret, 0, "setsockopt failed (%d)", err); + zexpect_equal(optlen, sizeof(int), "setsockopt optlen (%d)", optlen); + zexpect_equal(optval, 1500, "setsockopt mtu (%d)", optval); + + optval = 0; optlen = sizeof(int); + ret = zsock_getsockopt(client_sock, IPPROTO_IPV6, IPV6_MTU, &optval, &optlen); + err = -errno; + zexpect_equal(ret, 0, "getsockopt failed (%d)", err); + zexpect_equal(optlen, sizeof(int), "getsockopt optlen (%d)", optlen); + zexpect_equal(optval, 1500, "getsockopt mtu (%d)", optval); + + (void)zsock_close(client_sock); + (void)zsock_close(server_sock); +#else + ztest_test_skip(); +#endif /* CONFIG_NET_IPV6_PMTU */ +} + ZTEST_SUITE(net_pmtu_test_suite, NULL, test_setup, NULL, NULL, NULL); From b067a2aaf2233652d8acb067ac2370310793a581 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 13 Nov 2024 10:38:13 +0200 Subject: [PATCH 19/24] net: ipv4_fragment: Add PMTU support If PMTU is enabled, then use the MTU value from it instead of always using network interface MTU. Signed-off-by: Jukka Rissanen --- subsys/net/ip/ipv4_fragment.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/subsys/net/ip/ipv4_fragment.c b/subsys/net/ip/ipv4_fragment.c index c095efc4c2d45..69fe952215989 100644 --- a/subsys/net/ip/ipv4_fragment.c +++ b/subsys/net/ip/ipv4_fragment.c @@ -23,6 +23,7 @@ LOG_MODULE_DECLARE(net_ipv4, CONFIG_NET_IPV4_LOG_LEVEL); #include "ipv4.h" #include "route.h" #include "net_stats.h" +#include "pmtu.h" /* Timeout for various buffer allocations in this file. */ #define NET_BUF_TIMEOUT K_MSEC(100) @@ -624,10 +625,26 @@ enum net_verdict net_ipv4_prepare_for_send_fragment(struct net_pkt *pkt) * and we can skip other checks. */ if (ip_hdr->id[0] == 0 && ip_hdr->id[1] == 0) { - uint16_t mtu = net_if_get_mtu(net_pkt_iface(pkt)); size_t pkt_len = net_pkt_get_len(pkt); + uint16_t mtu; - mtu = MAX(NET_IPV4_MTU, mtu); + if (IS_ENABLED(CONFIG_NET_IPV4_PMTU)) { + struct sockaddr_in dst = { + .sin_family = AF_INET, + .sin_addr = *((struct in_addr *)ip_hdr->dst), + }; + + ret = net_pmtu_get_mtu((struct sockaddr *)&dst); + if (ret <= 0) { + goto use_interface_mtu; + } + + mtu = ret; + } else { +use_interface_mtu: + mtu = net_if_get_mtu(net_pkt_iface(pkt)); + mtu = MAX(NET_IPV4_MTU, mtu); + } if (pkt_len > mtu) { ret = net_ipv4_send_fragmented_pkt(net_pkt_iface(pkt), pkt, pkt_len, mtu); From b9c2e7a7d7bb7f041156e64932bd93cba882e8ab Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 13 Nov 2024 10:39:00 +0200 Subject: [PATCH 20/24] tests: net: ipv4_fragment: Add PMTU testing Enable PMTU so that we test it with IPv4 fragmentation code. Signed-off-by: Jukka Rissanen --- tests/net/ipv4_fragment/testcase.yaml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/net/ipv4_fragment/testcase.yaml b/tests/net/ipv4_fragment/testcase.yaml index 30e18231e4941..c45d22699d50a 100644 --- a/tests/net/ipv4_fragment/testcase.yaml +++ b/tests/net/ipv4_fragment/testcase.yaml @@ -5,9 +5,14 @@ # common: depends_on: netif + tags: + - net + - ipv4 + - fragment tests: net.ipv4.fragment: - tags: - - net - - ipv4 - - fragment + extra_configs: + - CONFIG_NET_IPV4_PMTU=n + net.ipv4.fragment.with_pmtu: + extra_configs: + - CONFIG_NET_IPV4_PMTU=y From df788c08db7d3959b12094d6159827f5b1e3a0da Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 13 Nov 2024 10:35:32 +0200 Subject: [PATCH 21/24] tests: net: ipv4_fragment: Make test to run faster Shorten the timeouts so that the tests are run in 6 second instead of 15 seconds. Use only native_sim for the tests so that the tests are run without any extra delays. Signed-off-by: Jukka Rissanen --- tests/net/ipv4_fragment/prj.conf | 2 ++ tests/net/ipv4_fragment/src/main.c | 8 ++++---- tests/net/ipv4_fragment/testcase.yaml | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/net/ipv4_fragment/prj.conf b/tests/net/ipv4_fragment/prj.conf index d8c4d1ff5348e..2cd57fc788160 100644 --- a/tests/net/ipv4_fragment/prj.conf +++ b/tests/net/ipv4_fragment/prj.conf @@ -26,3 +26,5 @@ CONFIG_ZTEST_STACK_SIZE=2048 CONFIG_INIT_STACKS=y CONFIG_NET_STATISTICS=n + +CONFIG_NET_IPV4_FRAGMENT_TIMEOUT=1 diff --git a/tests/net/ipv4_fragment/src/main.c b/tests/net/ipv4_fragment/src/main.c index 9379af361a18c..8e496e8097d1e 100644 --- a/tests/net/ipv4_fragment/src/main.c +++ b/tests/net/ipv4_fragment/src/main.c @@ -31,7 +31,7 @@ LOG_MODULE_REGISTER(net_ipv4_test, CONFIG_NET_IPV4_LOG_LEVEL); #define IPV4_TEST_PACKET_SIZE 2048 /* Wait times for semaphores and buffers */ -#define WAIT_TIME K_SECONDS(2) +#define WAIT_TIME K_MSEC(1100) #define ALLOC_TIMEOUT K_MSEC(500) /* Dummy network addresses, 192.168.8.1 and 192.168.8.2 */ @@ -776,7 +776,7 @@ ZTEST(net_ipv4_fragment, test_fragment_timeout) zassert_equal(packets, 1, "Expected fragment to be present in buffer"); /* Delay briefly and re-check number of pending reassembly packets */ - k_sleep(K_SECONDS(6)); + k_sleep(K_MSEC(1100)); packets = 0; net_ipv4_frag_foreach(reassembly_foreach_cb, &packets); zassert_equal(packets, 0, "Expected fragment to be dropped after timeout"); @@ -790,7 +790,7 @@ ZTEST(net_ipv4_fragment, test_fragment_timeout) zassert_equal(sem_count, 0, "Expected no complete upper-layer packets"); /* Check packet counts are valid */ - k_sleep(K_SECONDS(1)); + k_sleep(K_MSEC(500)); zassert_equal(lower_layer_packet_count, 1, "Expected 1 packet at lower layers"); zassert_equal(upper_layer_packet_count, 0, "Expected no packets at upper layers"); zassert_equal(last_packet_received, 1, "Expected last packet"); @@ -862,7 +862,7 @@ ZTEST(net_ipv4_fragment, test_do_not_fragment) "Expected timeout waiting for packet to be received"); /* Check packet counts are valid */ - k_sleep(K_SECONDS(1)); + k_sleep(K_MSEC(100)); zassert_equal(lower_layer_packet_count, 0, "Expected no packets at lower layers"); zassert_equal(upper_layer_packet_count, 0, "Expected no packets at upper layers"); zassert_equal(last_packet_received, 0, "Did not expect last packet"); diff --git a/tests/net/ipv4_fragment/testcase.yaml b/tests/net/ipv4_fragment/testcase.yaml index c45d22699d50a..34822465bef22 100644 --- a/tests/net/ipv4_fragment/testcase.yaml +++ b/tests/net/ipv4_fragment/testcase.yaml @@ -9,6 +9,7 @@ common: - net - ipv4 - fragment + platform_allow: native_sim tests: net.ipv4.fragment: extra_configs: From 617728500d0761a0bf34b4ddf541f8c92222bcc7 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 13 Nov 2024 11:05:31 +0200 Subject: [PATCH 22/24] net: ipv6_fragment: Add PMTU support If PMTU is enabled, then use the MTU value from it instead of always using network interface MTU. Signed-off-by: Jukka Rissanen --- subsys/net/ip/ipv6_fragment.c | 6 +++--- subsys/net/ip/ipv6_nbr.c | 24 +++++++++++++++++++++--- subsys/net/ip/net_private.h | 2 +- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/subsys/net/ip/ipv6_fragment.c b/subsys/net/ip/ipv6_fragment.c index fda00c59f0390..2915f4c374ee6 100644 --- a/subsys/net/ip/ipv6_fragment.c +++ b/subsys/net/ip/ipv6_fragment.c @@ -686,7 +686,7 @@ static int send_ipv6_fragment(struct net_pkt *pkt, } int net_ipv6_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt, - uint16_t pkt_len) + uint16_t pkt_len, uint16_t mtu) { uint16_t next_hdr_off; uint16_t last_hdr_off; @@ -713,12 +713,12 @@ int net_ipv6_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt, /* The Maximum payload can fit into each packet after IPv6 header, * Extension headers and Fragmentation header. */ - fit_len = NET_IPV6_MTU - NET_IPV6_FRAGH_LEN - + fit_len = (int)mtu - NET_IPV6_FRAGH_LEN - (net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt)); if (fit_len <= 0) { /* Must be invalid extension headers length */ NET_DBG("No room for IPv6 payload MTU %d hdrs_len %d", - NET_IPV6_MTU, NET_IPV6_FRAGH_LEN + + mtu, NET_IPV6_FRAGH_LEN + net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt)); return -EINVAL; } diff --git a/subsys/net/ip/ipv6_nbr.c b/subsys/net/ip/ipv6_nbr.c index 93b0123eca46a..fa78f4221dcca 100644 --- a/subsys/net/ip/ipv6_nbr.c +++ b/subsys/net/ip/ipv6_nbr.c @@ -810,13 +810,31 @@ enum net_verdict net_ipv6_prepare_for_send(struct net_pkt *pkt) * contain a proper value and we can skip other checks. */ if (net_pkt_ipv6_fragment_id(pkt) == 0U) { - uint16_t mtu = net_if_get_mtu(net_pkt_iface(pkt)); size_t pkt_len = net_pkt_get_len(pkt); + uint16_t mtu; + + if (IS_ENABLED(CONFIG_NET_IPV6_PMTU)) { + struct sockaddr_in6 dst = { + .sin6_family = AF_INET6, + }; + + net_ipv6_addr_copy_raw((uint8_t *)&dst.sin6_addr, ip_hdr->dst); + + ret = net_pmtu_get_mtu((struct sockaddr *)&dst); + if (ret <= 0) { + goto use_interface_mtu; + } + + mtu = ret; + } else { +use_interface_mtu: + mtu = net_if_get_mtu(net_pkt_iface(pkt)); + mtu = MAX(NET_IPV6_MTU, mtu); + } - mtu = MAX(NET_IPV6_MTU, mtu); if (mtu < pkt_len) { ret = net_ipv6_send_fragmented_pkt(net_pkt_iface(pkt), - pkt, pkt_len); + pkt, pkt_len, mtu); if (ret < 0) { NET_DBG("Cannot fragment IPv6 pkt (%d)", ret); return NET_DROP; diff --git a/subsys/net/ip/net_private.h b/subsys/net/ip/net_private.h index 04c3205f5aece..9e3878c1d8e24 100644 --- a/subsys/net/ip/net_private.h +++ b/subsys/net/ip/net_private.h @@ -245,7 +245,7 @@ int net_ipv4_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt, #if defined(CONFIG_NET_IPV6_FRAGMENT) int net_ipv6_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt, - uint16_t pkt_len); + uint16_t pkt_len, uint16_t mtu); #endif extern const char *net_verdict2str(enum net_verdict verdict); From 8d61425e593189e86d00f4c5ca95b987b1e4d162 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 13 Nov 2024 11:06:15 +0200 Subject: [PATCH 23/24] tests: net: ipv6_fragment: Add PMTU testing Enable PMTU so that we test it with IPv6 fragmentation code. Signed-off-by: Jukka Rissanen --- tests/net/ipv6_fragment/testcase.yaml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/net/ipv6_fragment/testcase.yaml b/tests/net/ipv6_fragment/testcase.yaml index c5cfd98ada2d5..6095af5f02dd5 100644 --- a/tests/net/ipv6_fragment/testcase.yaml +++ b/tests/net/ipv6_fragment/testcase.yaml @@ -1,8 +1,13 @@ common: depends_on: netif + tags: + - net + - ipv6 + - fragment tests: net.ipv6.fragment: - tags: - - net - - ipv6 - - fragment + extra_configs: + - CONFIG_NET_IPV6_PMTU=n + net.ipv6.fragment.with_pmtu: + extra_configs: + - CONFIG_NET_IPV6_PMTU=y From 32e3339997d9466596b794fb78d056c33b15f893 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 14 Nov 2024 13:51:14 +0200 Subject: [PATCH 24/24] net: ipv6_fragment: Data in one frag must be multiple of 8 After we take the true MTU into account, we need to send proper number of bytes (multiple of 8) in one IPv6 fragment. Signed-off-by: Jukka Rissanen --- subsys/net/ip/ipv6_fragment.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/subsys/net/ip/ipv6_fragment.c b/subsys/net/ip/ipv6_fragment.c index 2915f4c374ee6..f50e5f9e4e21b 100644 --- a/subsys/net/ip/ipv6_fragment.c +++ b/subsys/net/ip/ipv6_fragment.c @@ -715,6 +715,10 @@ int net_ipv6_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt, */ fit_len = (int)mtu - NET_IPV6_FRAGH_LEN - (net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt)); + + /* The data we want to sent in one fragment must be multiple of 8 */ + fit_len = ROUND_DOWN(fit_len, 8); + if (fit_len <= 0) { /* Must be invalid extension headers length */ NET_DBG("No room for IPv6 payload MTU %d hdrs_len %d",