From 787d91805e518a76a1fa04198410d5c5e774d0ad Mon Sep 17 00:00:00 2001 From: Robert Lubos Date: Mon, 15 May 2023 11:43:27 +0200 Subject: [PATCH 01/18] [nrf fromtree] net: arp: Add support for gratuitous ARP transmission Add support for gratuitous ARP transmission by Zephyr network stack. This allows to prematurely fill the peer ARP table, so there's no need to send an explicit request when peer needs to send an actual packet. The gratuitous ARP is send when the network interface is brought up (joins the network) or a new IP address is added. The gratuitous ARP request is also sent periodically, with a configurable interval time. The gratuitous ARP should also be sent whenever MAC address of the interface is changed, but as Zephyr only allows to do this when interface is down, this is already covered by the first case (interface brought up). Signed-off-by: Robert Lubos (cherry picked from commit 6551e6f5baea82cbf7f98cb5bbf5ca5c3c7a901f) --- subsys/net/l2/ethernet/Kconfig | 13 +++ subsys/net/l2/ethernet/arp.c | 157 +++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+) diff --git a/subsys/net/l2/ethernet/Kconfig b/subsys/net/l2/ethernet/Kconfig index bc4d9a4886a..5413ab4d549 100644 --- a/subsys/net/l2/ethernet/Kconfig +++ b/subsys/net/l2/ethernet/Kconfig @@ -73,6 +73,19 @@ config NET_ARP_GRATUITOUS ff:ff:ff:ff:ff:ff. Ordinarily, no reply packet will occur. A gratuitous ARP reply is a reply to which no request has been made. +config NET_ARP_GRATUITOUS_TRANSMISSION + bool "Transmit gratuitous ARP requests" + depends on NET_ARP_GRATUITOUS + depends on NET_MGMT_EVENT + depends on NET_MGMT_EVENT_INFO + help + Transmit gratuitous ARP requests, as defined in RFC 5227. + +config NET_ARP_GRATUITOUS_INTERVAL + int "Time interval (in seconds) between sending gratuitous ARP requests" + depends on NET_ARP_GRATUITOUS_TRANSMISSION + default 60 + if NET_ARP module = NET_ARP module-dep = NET_LOG diff --git a/subsys/net/l2/ethernet/arp.c b/subsys/net/l2/ethernet/arp.c index 6be7255b19b..da23a578f16 100644 --- a/subsys/net/l2/ethernet/arp.c +++ b/subsys/net/l2/ethernet/arp.c @@ -16,6 +16,7 @@ LOG_MODULE_REGISTER(net_arp, CONFIG_NET_ARP_LOG_LEVEL); #include #include #include +#include #include "arp.h" #include "net_private.h" @@ -34,6 +35,12 @@ static struct k_work_delayable arp_request_timer; static struct k_mutex arp_mutex; +#if defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION) +static struct net_mgmt_event_callback iface_event_cb; +static struct net_mgmt_event_callback ipv4_event_cb; +static struct k_work_delayable arp_gratuitous_work; +#endif /* defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION) */ + static void arp_entry_cleanup(struct arp_entry *entry, bool pending) { NET_DBG("%p", entry); @@ -457,6 +464,141 @@ static void arp_gratuitous(struct net_if *iface, } } +#if defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION) +static void arp_gratuitous_send(struct net_if *iface, + struct in_addr *ipaddr) +{ + struct net_arp_hdr *hdr; + struct net_pkt *pkt; + + pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct net_arp_hdr), + AF_UNSPEC, 0, NET_BUF_TIMEOUT); + if (!pkt) { + return; + } + + net_buf_add(pkt->buffer, sizeof(struct net_arp_hdr)); + net_pkt_set_vlan_tag(pkt, net_eth_get_vlan_tag(iface)); + + hdr = NET_ARP_HDR(pkt); + + hdr->hwtype = htons(NET_ARP_HTYPE_ETH); + hdr->protocol = htons(NET_ETH_PTYPE_IP); + hdr->hwlen = sizeof(struct net_eth_addr); + hdr->protolen = sizeof(struct in_addr); + hdr->opcode = htons(NET_ARP_REQUEST); + + memcpy(&hdr->dst_hwaddr.addr, net_eth_broadcast_addr(), + sizeof(struct net_eth_addr)); + memcpy(&hdr->src_hwaddr.addr, net_if_get_link_addr(iface)->addr, + sizeof(struct net_eth_addr)); + + net_ipv4_addr_copy_raw(hdr->dst_ipaddr, (uint8_t *)ipaddr); + net_ipv4_addr_copy_raw(hdr->src_ipaddr, (uint8_t *)ipaddr); + + net_pkt_lladdr_src(pkt)->addr = net_if_get_link_addr(iface)->addr; + net_pkt_lladdr_src(pkt)->len = sizeof(struct net_eth_addr); + + net_pkt_lladdr_dst(pkt)->addr = (uint8_t *)net_eth_broadcast_addr(); + net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_eth_addr); + + NET_DBG("Sending gratuitous ARP pkt %p", pkt); + + if (net_if_send_data(iface, pkt) == NET_DROP) { + net_pkt_unref(pkt); + } +} + +static void notify_all_ipv4_addr(struct net_if *iface) +{ + struct net_if_ipv4 *ipv4 = iface->config.ip.ipv4; + int i; + + if (!ipv4) { + return; + } + + for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { + if (ipv4->unicast[i].ipv4.is_used && + ipv4->unicast[i].ipv4.address.family == AF_INET && + ipv4->unicast[i].ipv4.addr_state == NET_ADDR_PREFERRED) { + arp_gratuitous_send(iface, + &ipv4->unicast[i].ipv4.address.in_addr); + } + } +} + +static void iface_event_handler(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, struct net_if *iface) +{ + ARG_UNUSED(cb); + + if (!(net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET) || + net_eth_is_vlan_interface(iface))) { + return; + } + + if (mgmt_event != NET_EVENT_IF_UP) { + return; + } + + notify_all_ipv4_addr(iface); +} + +static void ipv4_event_handler(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, struct net_if *iface) +{ + struct in_addr *ipaddr; + + if (!(net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET) || + net_eth_is_vlan_interface(iface))) { + return; + } + + if (!net_if_is_up(iface)) { + return; + } + + if (mgmt_event != NET_EVENT_IPV4_ADDR_ADD) { + return; + } + + if (cb->info_length != sizeof(struct in_addr)) { + return; + } + + ipaddr = (struct in_addr *)cb->info; + + arp_gratuitous_send(iface, ipaddr); +} + +static void iface_cb(struct net_if *iface, void *user_data) +{ + ARG_UNUSED(user_data); + + if (!(net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET) || + net_eth_is_vlan_interface(iface))) { + return; + } + + if (!net_if_is_up(iface)) { + return; + } + + notify_all_ipv4_addr(iface); +} + +static void arp_gratuitous_work_handler(struct k_work *work) +{ + ARG_UNUSED(work); + + net_if_foreach(iface_cb, NULL); + + k_work_reschedule(&arp_gratuitous_work, + K_SECONDS(CONFIG_NET_ARP_GRATUITOUS_INTERVAL)); +} +#endif /* defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION) */ + void net_arp_update(struct net_if *iface, struct in_addr *src, struct net_eth_addr *hwaddr, @@ -823,4 +965,19 @@ void net_arp_init(void) k_mutex_init(&arp_mutex); arp_cache_initialized = true; + +#if defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION) + net_mgmt_init_event_callback(&iface_event_cb, iface_event_handler, + NET_EVENT_IF_UP); + net_mgmt_init_event_callback(&ipv4_event_cb, ipv4_event_handler, + NET_EVENT_IPV4_ADDR_ADD); + + net_mgmt_add_event_callback(&iface_event_cb); + net_mgmt_add_event_callback(&ipv4_event_cb); + + k_work_init_delayable(&arp_gratuitous_work, + arp_gratuitous_work_handler); + k_work_reschedule(&arp_gratuitous_work, + K_SECONDS(CONFIG_NET_ARP_GRATUITOUS_INTERVAL)); +#endif /* defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION) */ } From 028af1e8ed78587409b736fd7f2e271817bd85d2 Mon Sep 17 00:00:00 2001 From: Robert Lubos Date: Wed, 24 Apr 2024 13:48:51 +0200 Subject: [PATCH 02/18] [nrf fromtree] tests: net: all: Make sure gratuitous ARP transmission is enabled Add you config to the build-all net tests, to make sure everything build properly. Signed-off-by: Robert Lubos (cherry picked from commit f003087e38521cc51cce978ec4740ee03a858c0e) --- tests/net/all/prj.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/net/all/prj.conf b/tests/net/all/prj.conf index 3538a834048..02161c12214 100644 --- a/tests/net/all/prj.conf +++ b/tests/net/all/prj.conf @@ -228,6 +228,7 @@ CONFIG_NET_ARP=y CONFIG_NET_ARP_TABLE_SIZE=3 CONFIG_NET_ARP_LOG_LEVEL_DBG=y CONFIG_NET_ARP_GRATUITOUS=y +CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION=y # Logging CONFIG_NET_LOG=y From eb76ae61f909b7961a0571be9797394a1741f1a2 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 31 Jan 2024 17:39:44 +0200 Subject: [PATCH 03/18] [nrf fromtree] net: ipv4: Make netmask IPv4 address specific The netmask should be tied to the IPv4 address instead of being global for the network interface. If there is only one IPv4 address specified to the network interface, nothing changes from user point of view. But if there are more than one IPv4 address / network interface, the netmask must be specified to each address separately. This means that net_if_ipv4_get_netmask() and net_if_ipv4_set_netmask() functions should not be used as they only work reliably if there is only one IPv4 address in the network interface. The new net_if_ipv4_get_netmask_by_addr() and net_if_ipv4_set_netmask_by_addr() functions should be used as they make sure that the netmask is tied to correct IPv4 address in the network interface. Signed-off-by: Jukka Rissanen (cherry picked from commit 1b0f9e865e35a6b3e1ca8aad7a67f7cfbfc2e666) --- include/zephyr/net/net_if.h | 55 +++++- subsys/net/ip/dhcpv4.c | 4 +- subsys/net/ip/ipv4_autoconf.c | 4 +- subsys/net/ip/net_if.c | 241 +++++++++++++++++++++----- subsys/net/l2/ethernet/arp.c | 10 +- subsys/net/lib/capture/capture.c | 4 +- subsys/net/lib/config/init.c | 10 +- subsys/net/lib/dhcpv4/dhcpv4_server.c | 2 +- subsys/net/lib/shell/iface.c | 8 +- subsys/net/lib/shell/ipv4.c | 17 +- subsys/net/lib/shell/tcp.c | 2 +- 11 files changed, 286 insertions(+), 71 deletions(-) diff --git a/include/zephyr/net/net_if.h b/include/zephyr/net/net_if.h index f1e5be67cf7..67c73d9ec15 100644 --- a/include/zephyr/net/net_if.h +++ b/include/zephyr/net/net_if.h @@ -354,9 +354,21 @@ struct net_if_dhcpv6 { #endif /** @endcond */ +/** + * @brief Network Interface unicast IPv4 address and netmask + * + * Stores the unicast IPv4 address and related netmask. + */ +struct net_if_addr_ipv4 { + /** IPv4 address */ + struct net_if_addr ipv4; + /** Netmask */ + struct in_addr netmask; +}; + struct net_if_ipv4 { /** Unicast IP addresses */ - struct net_if_addr unicast[NET_IF_MAX_IPV4_ADDR]; + struct net_if_addr_ipv4 unicast[NET_IF_MAX_IPV4_ADDR]; /** Multicast IP addresses */ struct net_if_mcast_addr mcast[NET_IF_MAX_IPV4_MADDR]; @@ -364,9 +376,6 @@ struct net_if_ipv4 { /** Gateway */ struct in_addr gw; - /** Netmask */ - struct in_addr netmask; - /** IPv4 time-to-live */ uint8_t ttl; }; @@ -2217,6 +2226,18 @@ struct in_addr *net_if_ipv4_get_ll(struct net_if *iface, struct in_addr *net_if_ipv4_get_global_addr(struct net_if *iface, enum net_addr_state addr_state); +/** + * @brief Get IPv4 netmask related to an address of an interface. + * + * @param iface Interface to use. + * @param addr IPv4 address to check. + * + * @return The netmask set on the interface related to the give address, + * unspecified address if not found. + */ +struct in_addr net_if_ipv4_get_netmask_by_addr(struct net_if *iface, + const struct in_addr *addr); + /** * @brief Get IPv4 netmask of an interface. * @@ -2246,6 +2267,32 @@ void net_if_ipv4_set_netmask(struct net_if *iface, __syscall bool net_if_ipv4_set_netmask_by_index(int index, const struct in_addr *netmask); +/** + * @brief Set IPv4 netmask for an interface index for a given address. + * + * @param index Network interface index + * @param addr IPv4 address related to this netmask + * @param netmask IPv4 netmask + * + * @return True if netmask was added, false otherwise. + */ +__syscall bool net_if_ipv4_set_netmask_by_addr_by_index(int index, + const struct in_addr *addr, + const struct in_addr *netmask); + +/** + * @brief Set IPv4 netmask for an interface index for a given address. + * + * @param iface Network interface + * @param addr IPv4 address related to this netmask + * @param netmask IPv4 netmask + * + * @return True if netmask was added, false otherwise. + */ +bool net_if_ipv4_set_netmask_by_addr(struct net_if *iface, + const struct in_addr *addr, + const struct in_addr *netmask); + /** * @brief Set IPv4 gateway for an interface. * diff --git a/subsys/net/ip/dhcpv4.c b/subsys/net/ip/dhcpv4.c index 918b71d1d6b..df130a28efb 100644 --- a/subsys/net/ip/dhcpv4.c +++ b/subsys/net/ip/dhcpv4.c @@ -738,7 +738,9 @@ static bool dhcpv4_parse_options(struct net_pkt *pkt, return false; } - net_if_ipv4_set_netmask(iface, &netmask); + net_if_ipv4_set_netmask_by_addr(iface, + &iface->config.dhcpv4.requested_ip, + &netmask); NET_DBG("options_subnet_mask %s", net_sprint_ipv4_addr(&netmask)); break; diff --git a/subsys/net/ip/ipv4_autoconf.c b/subsys/net/ip/ipv4_autoconf.c index aa18a27ef63..e34b51523c3 100644 --- a/subsys/net/ip/ipv4_autoconf.c +++ b/subsys/net/ip/ipv4_autoconf.c @@ -182,7 +182,9 @@ static inline void ipv4_autoconf_addr_set(struct net_if_ipv4_autoconf *ipv4auto) return; } - net_if_ipv4_set_netmask(ipv4auto->iface, &netmask); + net_if_ipv4_set_netmask_by_addr(ipv4auto->iface, + &ipv4auto->requested_ip, + &netmask); ipv4auto->state = NET_IPV4_AUTOCONF_ASSIGNED; } diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index 1a8199ad62c..a87514c0b2e 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -3191,16 +3191,17 @@ bool net_if_ipv4_addr_mask_cmp(struct net_if *iface, goto out; } - subnet = UNALIGNED_GET(&addr->s_addr) & ipv4->netmask.s_addr; - for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { - if (!ipv4->unicast[i].is_used || - ipv4->unicast[i].address.family != AF_INET) { + if (!ipv4->unicast[i].ipv4.is_used || + ipv4->unicast[i].ipv4.address.family != AF_INET) { continue; } - if ((ipv4->unicast[i].address.in_addr.s_addr & - ipv4->netmask.s_addr) == subnet) { + subnet = UNALIGNED_GET(&addr->s_addr) & + ipv4->unicast[i].netmask.s_addr; + + if ((ipv4->unicast[i].ipv4.address.in_addr.s_addr & + ipv4->unicast[i].netmask.s_addr) == subnet) { ret = true; goto out; } @@ -3217,6 +3218,7 @@ static bool ipv4_is_broadcast_address(struct net_if *iface, { struct net_if_ipv4 *ipv4; bool ret = false; + struct in_addr bcast; net_if_lock(iface); @@ -3226,15 +3228,19 @@ static bool ipv4_is_broadcast_address(struct net_if *iface, goto out; } - if (!net_if_ipv4_addr_mask_cmp(iface, addr)) { - ret = false; - goto out; - } + for (int i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { + if (!ipv4->unicast[i].ipv4.is_used || + ipv4->unicast[i].ipv4.address.family != AF_INET) { + continue; + } - if ((UNALIGNED_GET(&addr->s_addr) & ~ipv4->netmask.s_addr) == - ~ipv4->netmask.s_addr) { - ret = true; - goto out; + bcast.s_addr = ipv4->unicast[i].ipv4.address.in_addr.s_addr | + ~ipv4->unicast[i].netmask.s_addr; + + if (bcast.s_addr == addr->s_addr) { + ret = true; + goto out; + } } out: @@ -3319,14 +3325,14 @@ static struct in_addr *net_if_ipv4_get_best_match(struct net_if *iface, } for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { - if (!is_proper_ipv4_address(&ipv4->unicast[i])) { + if (!is_proper_ipv4_address(&ipv4->unicast[i].ipv4)) { continue; } - len = get_diff_ipv4(dst, &ipv4->unicast[i].address.in_addr); + len = get_diff_ipv4(dst, &ipv4->unicast[i].ipv4.address.in_addr); if (len >= *best_so_far) { *best_so_far = len; - src = &ipv4->unicast[i].address.in_addr; + src = &ipv4->unicast[i].ipv4.address.in_addr; } } @@ -3355,14 +3361,14 @@ static struct in_addr *if_ipv4_get_addr(struct net_if *iface, } for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { - if (!ipv4->unicast[i].is_used || + if (!ipv4->unicast[i].ipv4.is_used || (addr_state != NET_ADDR_ANY_STATE && - ipv4->unicast[i].addr_state != addr_state) || - ipv4->unicast[i].address.family != AF_INET) { + ipv4->unicast[i].ipv4.addr_state != addr_state) || + ipv4->unicast[i].ipv4.address.family != AF_INET) { continue; } - if (net_ipv4_is_ll_addr(&ipv4->unicast[i].address.in_addr)) { + if (net_ipv4_is_ll_addr(&ipv4->unicast[i].ipv4.address.in_addr)) { if (!ll) { continue; } @@ -3372,7 +3378,7 @@ static struct in_addr *if_ipv4_get_addr(struct net_if *iface, } } - addr = &ipv4->unicast[i].address.in_addr; + addr = &ipv4->unicast[i].ipv4.address.in_addr; goto out; } @@ -3481,19 +3487,19 @@ struct net_if_addr *net_if_ipv4_addr_lookup(const struct in_addr *addr, } for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { - if (!ipv4->unicast[i].is_used || - ipv4->unicast[i].address.family != AF_INET) { + if (!ipv4->unicast[i].ipv4.is_used || + ipv4->unicast[i].ipv4.address.family != AF_INET) { continue; } if (UNALIGNED_GET(&addr->s4_addr32[0]) == - ipv4->unicast[i].address.in_addr.s_addr) { + ipv4->unicast[i].ipv4.address.in_addr.s_addr) { if (ret) { *ret = iface; } - ifaddr = &ipv4->unicast[i]; + ifaddr = &ipv4->unicast[i].ipv4; net_if_unlock(iface); goto out; } @@ -3532,9 +3538,97 @@ static inline int z_vrfy_net_if_ipv4_addr_lookup_by_index( #include #endif +struct in_addr net_if_ipv4_get_netmask_by_addr(struct net_if *iface, + const struct in_addr *addr) +{ + struct in_addr netmask = { 0 }; + struct net_if_ipv4 *ipv4; + uint32_t subnet; + + net_if_lock(iface); + + if (net_if_config_ipv4_get(iface, NULL) < 0) { + goto out; + } + + ipv4 = iface->config.ip.ipv4; + if (ipv4 == NULL) { + goto out; + } + + for (int i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { + if (!ipv4->unicast[i].ipv4.is_used || + ipv4->unicast[i].ipv4.address.family != AF_INET) { + continue; + } + + subnet = UNALIGNED_GET(&addr->s_addr) & + ipv4->unicast[i].netmask.s_addr; + + if ((ipv4->unicast[i].ipv4.address.in_addr.s_addr & + ipv4->unicast[i].netmask.s_addr) == subnet) { + netmask = ipv4->unicast[i].netmask; + goto out; + } + } + +out: + net_if_unlock(iface); + + return netmask; +} + +bool net_if_ipv4_set_netmask_by_addr(struct net_if *iface, + const struct in_addr *addr, + const struct in_addr *netmask) +{ + struct net_if_ipv4 *ipv4; + uint32_t subnet; + bool ret = false; + + net_if_lock(iface); + + if (net_if_config_ipv4_get(iface, NULL) < 0) { + goto out; + } + + ipv4 = iface->config.ip.ipv4; + if (ipv4 == NULL) { + goto out; + } + + for (int i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { + if (!ipv4->unicast[i].ipv4.is_used || + ipv4->unicast[i].ipv4.address.family != AF_INET) { + continue; + } + + subnet = UNALIGNED_GET(&addr->s_addr) & + ipv4->unicast[i].netmask.s_addr; + + if ((ipv4->unicast[i].ipv4.address.in_addr.s_addr & + ipv4->unicast[i].netmask.s_addr) == subnet) { + ipv4->unicast[i].netmask = *netmask; + ret = true; + goto out; + } + } + +out: + net_if_unlock(iface); + + return ret; +} + +/* Using this function is problematic as if we have multiple + * addresses configured, which one to return. Use heuristic + * in this case and return the first one found. Please use + * net_if_ipv4_get_netmask_by_addr() instead. + */ struct in_addr net_if_ipv4_get_netmask(struct net_if *iface) { struct in_addr netmask = { 0 }; + struct net_if_ipv4 *ipv4; net_if_lock(iface); @@ -3542,31 +3636,58 @@ struct in_addr net_if_ipv4_get_netmask(struct net_if *iface) goto out; } - if (!iface->config.ip.ipv4) { + ipv4 = iface->config.ip.ipv4; + if (ipv4 == NULL) { goto out; } - netmask = iface->config.ip.ipv4->netmask; + for (int i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { + if (!ipv4->unicast[i].ipv4.is_used || + ipv4->unicast[i].ipv4.address.family != AF_INET) { + continue; + } + + netmask = iface->config.ip.ipv4->unicast[i].netmask; + break; + } + out: net_if_unlock(iface); return netmask; } +/* Using this function is problematic as if we have multiple + * addresses configured, which one to set. Use heuristic + * in this case and set the first one found. Please use + * net_if_ipv4_set_netmask_by_addr() instead. + */ void net_if_ipv4_set_netmask(struct net_if *iface, const struct in_addr *netmask) { + struct net_if_ipv4 *ipv4; + net_if_lock(iface); if (net_if_config_ipv4_get(iface, NULL) < 0) { goto out; } - if (!iface->config.ip.ipv4) { + ipv4 = iface->config.ip.ipv4; + if (ipv4 == NULL) { goto out; } - net_ipaddr_copy(&iface->config.ip.ipv4->netmask, netmask); + for (int i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { + if (!ipv4->unicast[i].ipv4.is_used || + ipv4->unicast[i].ipv4.address.family != AF_INET) { + continue; + } + + net_ipaddr_copy(&ipv4->unicast[i].netmask, netmask); + break; + } + out: net_if_unlock(iface); } @@ -3586,6 +3707,22 @@ bool z_impl_net_if_ipv4_set_netmask_by_index(int index, return true; } +bool z_impl_net_if_ipv4_set_netmask_by_addr_by_index(int index, + const struct in_addr *addr, + const struct in_addr *netmask) +{ + struct net_if *iface; + + iface = net_if_get_by_index(index); + if (!iface) { + return false; + } + + net_if_ipv4_set_netmask_by_addr(iface, addr, netmask); + + return true; +} + #ifdef CONFIG_USERSPACE bool z_vrfy_net_if_ipv4_set_netmask_by_index(int index, const struct in_addr *netmask) @@ -3605,6 +3742,30 @@ bool z_vrfy_net_if_ipv4_set_netmask_by_index(int index, } #include + +bool z_vrfy_net_if_ipv4_set_netmask_by_addr_by_index(int index, + const struct in_addr *addr, + const struct in_addr *netmask) +{ + struct in_addr ipv4_addr, netmask_addr; + struct net_if *iface; + + iface = z_vrfy_net_if_get_by_index(index); + if (!iface) { + return false; + } + + K_OOPS(k_usermode_from_copy(&ipv4_addr, (void *)addr, + sizeof(ipv4_addr))); + K_OOPS(k_usermode_from_copy(&netmask_addr, (void *)netmask, + sizeof(netmask_addr))); + + return z_impl_net_if_ipv4_set_netmask_by_addr_by_index(index, + &ipv4_addr, + &netmask_addr); +} + +#include #endif /* CONFIG_USERSPACE */ void net_if_ipv4_set_gw(struct net_if *iface, const struct in_addr *gw) @@ -3666,13 +3827,13 @@ static struct net_if_addr *ipv4_addr_find(struct net_if *iface, int i; for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { - if (!ipv4->unicast[i].is_used) { + if (!ipv4->unicast[i].ipv4.is_used) { continue; } if (net_ipv4_addr_cmp(addr, - &ipv4->unicast[i].address.in_addr)) { - return &ipv4->unicast[i]; + &ipv4->unicast[i].ipv4.address.in_addr)) { + return &ipv4->unicast[i].ipv4; } } @@ -3701,7 +3862,7 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface, } for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { - struct net_if_addr *cur = &ipv4->unicast[i]; + struct net_if_addr *cur = &ipv4->unicast[i].ipv4; if (addr_type == NET_ADDR_DHCP && cur->addr_type == NET_ADDR_OVERRIDABLE) { @@ -3709,7 +3870,7 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface, break; } - if (!ipv4->unicast[i].is_used) { + if (!ipv4->unicast[i].ipv4.is_used) { ifaddr = cur; break; } @@ -3765,23 +3926,23 @@ bool net_if_ipv4_addr_rm(struct net_if *iface, const struct in_addr *addr) } for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { - if (!ipv4->unicast[i].is_used) { + if (!ipv4->unicast[i].ipv4.is_used) { continue; } - if (!net_ipv4_addr_cmp(&ipv4->unicast[i].address.in_addr, + if (!net_ipv4_addr_cmp(&ipv4->unicast[i].ipv4.address.in_addr, addr)) { continue; } - ipv4->unicast[i].is_used = false; + ipv4->unicast[i].ipv4.is_used = false; NET_DBG("[%d] interface %p address %s removed", i, iface, net_sprint_ipv4_addr(addr)); net_mgmt_event_notify_with_info( NET_EVENT_IPV4_ADDR_DEL, iface, - &ipv4->unicast[i].address.in_addr, + &ipv4->unicast[i].ipv4.address.in_addr, sizeof(struct in_addr)); ret = true; @@ -3886,7 +4047,7 @@ void net_if_ipv4_addr_foreach(struct net_if *iface, net_if_ip_addr_cb_t cb, } for (int i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { - struct net_if_addr *if_addr = &ipv4->unicast[i]; + struct net_if_addr *if_addr = &ipv4->unicast[i].ipv4; if (!if_addr->is_used) { continue; diff --git a/subsys/net/l2/ethernet/arp.c b/subsys/net/l2/ethernet/arp.c index da23a578f16..0f33e68b31a 100644 --- a/subsys/net/l2/ethernet/arp.c +++ b/subsys/net/l2/ethernet/arp.c @@ -234,13 +234,13 @@ static inline struct in_addr *if_get_addr(struct net_if *iface, } for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { - if (ipv4->unicast[i].is_used && - ipv4->unicast[i].address.family == AF_INET && - ipv4->unicast[i].addr_state == NET_ADDR_PREFERRED && + if (ipv4->unicast[i].ipv4.is_used && + ipv4->unicast[i].ipv4.address.family == AF_INET && + ipv4->unicast[i].ipv4.addr_state == NET_ADDR_PREFERRED && (!addr || net_ipv4_addr_cmp(addr, - &ipv4->unicast[i].address.in_addr))) { - return &ipv4->unicast[i].address.in_addr; + &ipv4->unicast[i].ipv4.address.in_addr))) { + return &ipv4->unicast[i].ipv4.address.in_addr; } } diff --git a/subsys/net/lib/capture/capture.c b/subsys/net/lib/capture/capture.c index 91ed775ad45..27114ca7e67 100644 --- a/subsys/net/lib/capture/capture.c +++ b/subsys/net/lib/capture/capture.c @@ -230,7 +230,9 @@ static int setup_iface(struct net_if *iface, const char *ipaddr, /* Set the netmask so that we do not get IPv4 traffic routed * into this interface. */ - net_if_ipv4_set_netmask(iface, &netmask); + net_if_ipv4_set_netmask_by_addr(iface, + &net_sin(addr)->sin_addr, + &netmask); *addr_len = sizeof(struct sockaddr_in); } else { diff --git a/subsys/net/lib/config/init.c b/subsys/net/lib/config/init.c index 192cd3f5685..65b15fcd91c 100644 --- a/subsys/net/lib/config/init.c +++ b/subsys/net/lib/config/init.c @@ -70,7 +70,7 @@ static void ipv4_addr_add_handler(struct net_mgmt_event_callback *cb, for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { struct net_if_addr *if_addr = - &iface->config.ip.ipv4->unicast[i]; + &iface->config.ip.ipv4->unicast[i].ipv4; if (if_addr->addr_type != NET_ADDR_DHCP || !if_addr->is_used) { continue; @@ -85,7 +85,7 @@ static void ipv4_addr_add_handler(struct net_mgmt_event_callback *cb, iface->config.dhcpv4.lease_time); NET_INFO("Subnet: %s", net_addr_ntop(AF_INET, - &iface->config.ip.ipv4->netmask, + &iface->config.ip.ipv4->unicast[i].netmask, hr_addr, sizeof(hr_addr))); NET_INFO("Router: %s", net_addr_ntop(AF_INET, @@ -141,7 +141,7 @@ static void setup_ipv4(struct net_if *iface) #if CONFIG_NET_CONFIG_LOG_LEVEL >= LOG_LEVEL_INF char hr_addr[NET_IPV4_ADDR_LEN]; #endif - struct in_addr addr; + struct in_addr addr, netmask; if (sizeof(CONFIG_NET_CONFIG_MY_IPV4_ADDR) == 1) { /* Empty address, skip setting ANY address in this case */ @@ -177,11 +177,11 @@ static void setup_ipv4(struct net_if *iface) if (sizeof(CONFIG_NET_CONFIG_MY_IPV4_NETMASK) > 1) { /* If not empty */ if (net_addr_pton(AF_INET, CONFIG_NET_CONFIG_MY_IPV4_NETMASK, - &addr)) { + &netmask)) { NET_ERR("Invalid netmask: %s", CONFIG_NET_CONFIG_MY_IPV4_NETMASK); } else { - net_if_ipv4_set_netmask(iface, &addr); + net_if_ipv4_set_netmask_by_addr(iface, &addr, &netmask); } } diff --git a/subsys/net/lib/dhcpv4/dhcpv4_server.c b/subsys/net/lib/dhcpv4/dhcpv4_server.c index fdaab75c950..9925bda1548 100644 --- a/subsys/net/lib/dhcpv4/dhcpv4_server.c +++ b/subsys/net/lib/dhcpv4/dhcpv4_server.c @@ -1466,7 +1466,7 @@ int net_dhcpv4_server_start(struct net_if *iface, struct in_addr *base_addr) return -EINVAL; } - netmask = net_if_ipv4_get_netmask(iface); + netmask = net_if_ipv4_get_netmask_by_addr(iface, server_addr); if (net_ipv4_is_addr_unspecified(&netmask)) { LOG_ERR("Failed to obtain subnet mask."); return -EINVAL; diff --git a/subsys/net/lib/shell/iface.c b/subsys/net/lib/shell/iface.c index 53d4e99bd21..6149861ca90 100644 --- a/subsys/net/lib/shell/iface.c +++ b/subsys/net/lib/shell/iface.c @@ -437,14 +437,16 @@ static void iface_cb(struct net_if *iface, void *user_data) PR("IPv4 unicast addresses (max %d):\n", NET_IF_MAX_IPV4_ADDR); for (i = 0; ipv4 && i < NET_IF_MAX_IPV4_ADDR; i++) { - unicast = &ipv4->unicast[i]; + unicast = &ipv4->unicast[i].ipv4; if (!unicast->is_used) { continue; } - PR("\t%s %s %s%s\n", + PR("\t%s/%s %s %s%s\n", net_sprint_ipv4_addr(&unicast->address.in_addr), + net_sprint_ipv4_addr(&ipv4->unicast[i].netmask), + addrtype2str(unicast->addr_type), addrstate2str(unicast->addr_state), unicast->is_infinite ? " infinite" : ""); @@ -480,8 +482,6 @@ static void iface_cb(struct net_if *iface, void *user_data) if (ipv4) { PR("IPv4 gateway : %s\n", net_sprint_ipv4_addr(&ipv4->gw)); - PR("IPv4 netmask : %s\n", - net_sprint_ipv4_addr(&ipv4->netmask)); } #endif /* CONFIG_NET_IPV4 */ diff --git a/subsys/net/lib/shell/ipv4.c b/subsys/net/lib/shell/ipv4.c index e8584b205fb..a804c995431 100644 --- a/subsys/net/lib/shell/ipv4.c +++ b/subsys/net/lib/shell/ipv4.c @@ -36,18 +36,18 @@ static void ip_address_lifetime_cb(struct net_if *iface, void *user_data) PR("Type \tState \tLifetime (sec)\tAddress\n"); for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { - if (!ipv4->unicast[i].is_used || - ipv4->unicast[i].address.family != AF_INET) { + if (!ipv4->unicast[i].ipv4.is_used || + ipv4->unicast[i].ipv4.address.family != AF_INET) { continue; } PR("%s \t%s \t%12s/%12s\n", - addrtype2str(ipv4->unicast[i].addr_type), - addrstate2str(ipv4->unicast[i].addr_state), + addrtype2str(ipv4->unicast[i].ipv4.addr_type), + addrstate2str(ipv4->unicast[i].ipv4.addr_state), net_sprint_ipv4_addr( - &ipv4->unicast[i].address.in_addr), + &ipv4->unicast[i].ipv4.address.in_addr), net_sprint_ipv4_addr( - &ipv4->netmask)); + &ipv4->unicast[i].netmask)); } } #endif /* CONFIG_NET_NATIVE_IPV4 */ @@ -126,6 +126,7 @@ static int cmd_net_ip_add(const struct shell *sh, size_t argc, char *argv[]) } } else { struct net_if_addr *ifaddr; + struct in_addr netmask; if (argc < 4) { PR_ERROR("Netmask is missing.\n"); @@ -139,12 +140,12 @@ static int cmd_net_ip_add(const struct shell *sh, size_t argc, char *argv[]) return -ENOMEM; } - if (net_addr_pton(AF_INET, argv[3], &addr)) { + if (net_addr_pton(AF_INET, argv[3], &netmask)) { PR_ERROR("Invalid netmask: %s", argv[3]); return -EINVAL; } - net_if_ipv4_set_netmask(iface, &addr); + net_if_ipv4_set_netmask_by_addr(iface, &addr, &netmask); } #else /* CONFIG_NET_NATIVE_IPV4 */ diff --git a/subsys/net/lib/shell/tcp.c b/subsys/net/lib/shell/tcp.c index e2839af36ba..300f0f3fa52 100644 --- a/subsys/net/lib/shell/tcp.c +++ b/subsys/net/lib/shell/tcp.c @@ -51,7 +51,7 @@ static void get_my_ipv4_addr(struct net_if *iface, #if defined(CONFIG_NET_NATIVE_IPV4) /* Just take the first IPv4 address of an interface. */ memcpy(&net_sin(myaddr)->sin_addr, - &iface->config.ip.ipv4->unicast[0].address.in_addr, + &iface->config.ip.ipv4->unicast[0].ipv4.address.in_addr, sizeof(struct in_addr)); net_sin(myaddr)->sin_port = 0U; /* let the IP stack to select */ From dc71e8da1c419c162493b4d0e2075ef0bd9a9613 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 1 Feb 2024 15:44:23 +0200 Subject: [PATCH 04/18] [nrf fromtree] net: ipv4: Deprecate the old netmask set/get routines This commit deprecates these legacy netmask get/set routines net_if_ipv4_set_netmask() net_if_ipv4_set_netmask_by_index() net_if_ipv4_get_netmask() as they do not work well if there are multiple IPv4 address assigned to the network interface. User should use these functions instead net_if_ipv4_set_netmask_by_addr() net_if_ipv4_set_netmask_by_addr_by_index() net_if_ipv4_get_netmask_by_addr() as they make sure the netmask it bound to correct IPv4 address. Signed-off-by: Jukka Rissanen (cherry picked from commit 58b2a3dfd4464f9d924e9fe445db4f42828077e1) --- include/zephyr/net/net_if.h | 16 +++++++++++----- subsys/net/ip/net_if.c | 12 +++++++++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/include/zephyr/net/net_if.h b/include/zephyr/net/net_if.h index 67c73d9ec15..415bc9949db 100644 --- a/include/zephyr/net/net_if.h +++ b/include/zephyr/net/net_if.h @@ -2241,31 +2241,37 @@ struct in_addr net_if_ipv4_get_netmask_by_addr(struct net_if *iface, /** * @brief Get IPv4 netmask of an interface. * + * @deprecated Use net_if_ipv4_get_netmask_by_addr() instead. + * * @param iface Interface to use. * * @return The netmask set on the interface, unspecified address if not found. */ -struct in_addr net_if_ipv4_get_netmask(struct net_if *iface); +__deprecated struct in_addr net_if_ipv4_get_netmask(struct net_if *iface); /** * @brief Set IPv4 netmask for an interface. * + * @deprecated Use net_if_ipv4_set_netmask_by_addr() instead. + * * @param iface Interface to use. * @param netmask IPv4 netmask */ -void net_if_ipv4_set_netmask(struct net_if *iface, - const struct in_addr *netmask); +__deprecated void net_if_ipv4_set_netmask(struct net_if *iface, + const struct in_addr *netmask); /** * @brief Set IPv4 netmask for an interface index. * + * @deprecated Use net_if_ipv4_set_netmask_by_addr() instead. + * * @param index Network interface index * @param netmask IPv4 netmask * * @return True if netmask was added, false otherwise. */ -__syscall bool net_if_ipv4_set_netmask_by_index(int index, - const struct in_addr *netmask); +__deprecated __syscall bool net_if_ipv4_set_netmask_by_index(int index, + const struct in_addr *netmask); /** * @brief Set IPv4 netmask for an interface index for a given address. diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index a87514c0b2e..8fc3b58e31f 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -3662,8 +3662,8 @@ struct in_addr net_if_ipv4_get_netmask(struct net_if *iface) * in this case and set the first one found. Please use * net_if_ipv4_set_netmask_by_addr() instead. */ -void net_if_ipv4_set_netmask(struct net_if *iface, - const struct in_addr *netmask) +static void net_if_ipv4_set_netmask_deprecated(struct net_if *iface, + const struct in_addr *netmask) { struct net_if_ipv4 *ipv4; @@ -3692,6 +3692,12 @@ void net_if_ipv4_set_netmask(struct net_if *iface, net_if_unlock(iface); } +void net_if_ipv4_set_netmask(struct net_if *iface, + const struct in_addr *netmask) +{ + net_if_ipv4_set_netmask_deprecated(iface, netmask); +} + bool z_impl_net_if_ipv4_set_netmask_by_index(int index, const struct in_addr *netmask) { @@ -3702,7 +3708,7 @@ bool z_impl_net_if_ipv4_set_netmask_by_index(int index, return false; } - net_if_ipv4_set_netmask(iface, netmask); + net_if_ipv4_set_netmask_deprecated(iface, netmask); return true; } From e66fc077638e4b5a3c25d6b944dfd79cf31d0fb9 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 6 Mar 2024 18:14:21 +0200 Subject: [PATCH 05/18] [nrf fromtree] net: dhcpv4: Network interface netmask was set too early When we receive the subnet mask option from the server, we cannot yet set the netmask to the network interface as the mask is tied to the IP address we received from the server. We need to delay the setting of netmask until we have added the requested IP address to the interface. Signed-off-by: Jukka Rissanen (cherry picked from commit 7072e75162452bf8a7a5cf04a4e1552b7371ec35) --- include/zephyr/net/net_if.h | 3 +++ subsys/net/ip/dhcpv4.c | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/zephyr/net/net_if.h b/include/zephyr/net/net_if.h index 415bc9949db..9b72fdb8cd5 100644 --- a/include/zephyr/net/net_if.h +++ b/include/zephyr/net/net_if.h @@ -408,6 +408,9 @@ struct net_if_dhcpv4 { /** Requested IP addr */ struct in_addr requested_ip; + /** Received netmask from the server */ + struct in_addr netmask; + /** * DHCPv4 client state in the process of network * address allocation. diff --git a/subsys/net/ip/dhcpv4.c b/subsys/net/ip/dhcpv4.c index df130a28efb..0f564305037 100644 --- a/subsys/net/ip/dhcpv4.c +++ b/subsys/net/ip/dhcpv4.c @@ -738,9 +738,8 @@ static bool dhcpv4_parse_options(struct net_pkt *pkt, return false; } - net_if_ipv4_set_netmask_by_addr(iface, - &iface->config.dhcpv4.requested_ip, - &netmask); + iface->config.dhcpv4.netmask = netmask; + NET_DBG("options_subnet_mask %s", net_sprint_ipv4_addr(&netmask)); break; @@ -965,6 +964,10 @@ static void dhcpv4_handle_msg_ack(struct net_if *iface) return; } + net_if_ipv4_set_netmask_by_addr(iface, + &iface->config.dhcpv4.requested_ip, + &iface->config.dhcpv4.netmask); + dhcpv4_enter_bound(iface); break; From 620e8d0c61b9d20c275766669d886b09f7d0c4f5 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Mon, 15 Jan 2024 22:22:10 +1000 Subject: [PATCH 06/18] [nrf fromtree] wifi: esp_at: compile without `NET_NATIVE_IPV4` Allow the driver to compile without `CONFIG_NET_NATIVE_IPV4`. This can happen if the driver is only desired for SSID scanning. Signed-off-by: Jordan Yates (cherry picked from commit 7e7ab495c36ff6704fd1f764f0e7e2c10ae95870) --- drivers/wifi/esp_at/esp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/wifi/esp_at/esp.c b/drivers/wifi/esp_at/esp.c index 1ac21abc560..bd410bd6dcc 100644 --- a/drivers/wifi/esp_at/esp.c +++ b/drivers/wifi/esp_at/esp.c @@ -443,7 +443,9 @@ static void esp_mgmt_disconnect_work(struct k_work *work) esp_flags_clear(dev, EDF_STA_CONNECTED); esp_mode_switch_submit_if_needed(dev); +#if defined(CONFIG_NET_NATIVE_IPV4) net_if_ipv4_addr_rm(dev->net_iface, &dev->ip); +#endif net_if_dormant_on(dev->net_iface); wifi_mgmt_raise_disconnect_result_event(dev->net_iface, 0); } @@ -509,6 +511,7 @@ static void esp_ip_addr_work(struct k_work *work) return; } +#if defined(CONFIG_NET_NATIVE_IPV4) /* update interface addresses */ net_if_ipv4_set_gw(dev->net_iface, &dev->gw); net_if_ipv4_set_netmask(dev->net_iface, &dev->nm); @@ -516,6 +519,7 @@ static void esp_ip_addr_work(struct k_work *work) net_if_ipv4_addr_add(dev->net_iface, &dev->ip, NET_ADDR_MANUAL, 0); #else net_if_ipv4_addr_add(dev->net_iface, &dev->ip, NET_ADDR_DHCP, 0); +#endif #endif if (IS_ENABLED(CONFIG_WIFI_ESP_AT_DNS_USE)) { @@ -761,7 +765,9 @@ MODEM_CMD_DEFINE(on_cmd_ready) dev->flags = 0; dev->mode = 0; +#if defined(CONFIG_NET_NATIVE_IPV4) net_if_ipv4_addr_rm(dev->net_iface, &dev->ip); +#endif k_work_submit_to_queue(&dev->workq, &dev->init_work); return 0; From a68f33e69b570c72e61a59103499ac1f98d8d215 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Fri, 2 Feb 2024 10:04:42 +0200 Subject: [PATCH 07/18] [nrf fromtree] drivers: Change drivers to support new IPv4 netmask setting API Make sure that the drivers use the new IPv4 netmask setting API. Signed-off-by: Jukka Rissanen (cherry picked from commit 8860a81cfeeae9db20e518a1960123ac51938f8f) --- drivers/modem/hl7800.c | 4 +++- drivers/net/loopback.c | 2 +- drivers/wifi/esp_at/esp.c | 4 ++-- drivers/wifi/eswifi/eswifi_core.c | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/modem/hl7800.c b/drivers/modem/hl7800.c index c635016197e..3020c0825d2 100644 --- a/drivers/modem/hl7800.c +++ b/drivers/modem/hl7800.c @@ -2105,7 +2105,9 @@ static bool on_cmd_atcmdinfo_ipaddr(struct net_buf **buf, uint16_t len) LOG_ERR("Cannot set iface IPv4 addr"); } - net_if_ipv4_set_netmask(iface_ctx.iface, &iface_ctx.subnet); + net_if_ipv4_set_netmask_by_addr(iface_ctx.iface, + &new_ipv4_addr, + &iface_ctx.subnet); net_if_ipv4_set_gw(iface_ctx.iface, &iface_ctx.gateway); #endif /* store the new IP addr */ diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index d8ae5ca1b05..ebe15685573 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -50,7 +50,7 @@ static void loopback_init(struct net_if *iface) LOG_ERR("Failed to register IPv4 loopback address"); } - net_if_ipv4_set_netmask(iface, &netmask); + net_if_ipv4_set_netmask_by_addr(iface, &ipv4_loopback, &netmask); } if (IS_ENABLED(CONFIG_NET_IPV6)) { diff --git a/drivers/wifi/esp_at/esp.c b/drivers/wifi/esp_at/esp.c index bd410bd6dcc..c94ab10dccb 100644 --- a/drivers/wifi/esp_at/esp.c +++ b/drivers/wifi/esp_at/esp.c @@ -513,13 +513,13 @@ static void esp_ip_addr_work(struct k_work *work) #if defined(CONFIG_NET_NATIVE_IPV4) /* update interface addresses */ - net_if_ipv4_set_gw(dev->net_iface, &dev->gw); - net_if_ipv4_set_netmask(dev->net_iface, &dev->nm); #if defined(CONFIG_WIFI_ESP_AT_IP_STATIC) net_if_ipv4_addr_add(dev->net_iface, &dev->ip, NET_ADDR_MANUAL, 0); #else net_if_ipv4_addr_add(dev->net_iface, &dev->ip, NET_ADDR_DHCP, 0); #endif + net_if_ipv4_set_gw(dev->net_iface, &dev->gw); + net_if_ipv4_set_netmask_by_addr(dev->net_iface, &dev->ip, &dev->nm); #endif if (IS_ENABLED(CONFIG_WIFI_ESP_AT_DNS_USE)) { diff --git a/drivers/wifi/eswifi/eswifi_core.c b/drivers/wifi/eswifi/eswifi_core.c index 0beecd4a4fc..27e7a75797d 100644 --- a/drivers/wifi/eswifi/eswifi_core.c +++ b/drivers/wifi/eswifi/eswifi_core.c @@ -676,8 +676,8 @@ static int eswifi_mgmt_ap_enable(const struct device *dev, /* Set IP Address */ for (i = 0; ipv4 && i < NET_IF_MAX_IPV4_ADDR; i++) { - if (ipv4->unicast[i].is_used) { - unicast = &ipv4->unicast[i]; + if (ipv4->unicast[i].ipv4.is_used) { + unicast = &ipv4->unicast[i].ipv4; break; } } From 0f63f856ddea7ad97b0216b2dbd32cae94053fd8 Mon Sep 17 00:00:00 2001 From: Robert Lubos Date: Thu, 30 Nov 2023 13:45:35 +0100 Subject: [PATCH 08/18] [nrf fromtree] net: l2: ethernet: Fix error handling after ARP prepare Commit 55802e5e8637949b113a9f225c9f9a11f7f9b49d fixed error handling of TX errors, in case ARP request was generated. There are however also other places where post-ARP cleanup should be done on the TX path (like running out of buffers for Ethernet L2 header allocation). This commit fixes those cases in ethernet_send(), where function would exit early and report error after ARP prepare stage. Signed-off-by: Robert Lubos (cherry picked from commit 67082289e1380294dc3266549db55468736814f8) --- subsys/net/l2/ethernet/ethernet.c | 36 +++++++++++++++++-------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/subsys/net/l2/ethernet/ethernet.c b/subsys/net/l2/ethernet/ethernet.c index 26c5ddcb3cc..a067e677b37 100644 --- a/subsys/net/l2/ethernet/ethernet.c +++ b/subsys/net/l2/ethernet/ethernet.c @@ -704,7 +704,7 @@ static int ethernet_send(struct net_if *iface, struct net_pkt *pkt) (IS_ENABLED(CONFIG_NET_GPTP_VLAN) || ptype != htons(NET_ETH_PTYPE_PTP))) { if (set_vlan_tag(ctx, iface, pkt) == NET_DROP) { ret = -EINVAL; - goto error; + goto arp_error; } set_vlan_priority(ctx, pkt); @@ -714,7 +714,7 @@ static int ethernet_send(struct net_if *iface, struct net_pkt *pkt) */ if (!ethernet_fill_header(ctx, pkt, ptype)) { ret = -ENOMEM; - goto error; + goto arp_error; } net_pkt_cursor_init(pkt); @@ -724,20 +724,7 @@ static int ethernet_send(struct net_if *iface, struct net_pkt *pkt) if (ret != 0) { eth_stats_update_errors_tx(iface); ethernet_remove_l2_header(pkt); - if (IS_ENABLED(CONFIG_NET_ARP) && ptype == htons(NET_ETH_PTYPE_ARP)) { - /* Original packet was added to ARP's pending Q, so, to avoid it - * being freed, take a reference, the reference is dropped when we - * clear the pending Q in ARP and then it will be freed by net_if. - */ - net_pkt_ref(orig_pkt); - if (net_arp_clear_pending(iface, - (struct in_addr *)NET_IPV4_HDR(pkt)->dst)) { - NET_DBG("Could not find pending ARP entry"); - } - /* Free the ARP request */ - net_pkt_unref(pkt); - } - goto error; + goto arp_error; } ethernet_update_tx_stats(iface, pkt); @@ -748,6 +735,23 @@ static int ethernet_send(struct net_if *iface, struct net_pkt *pkt) net_pkt_unref(pkt); error: return ret; + +arp_error: + if (IS_ENABLED(CONFIG_NET_ARP) && ptype == htons(NET_ETH_PTYPE_ARP)) { + /* Original packet was added to ARP's pending Q, so, to avoid it + * being freed, take a reference, the reference is dropped when we + * clear the pending Q in ARP and then it will be freed by net_if. + */ + net_pkt_ref(orig_pkt); + if (net_arp_clear_pending( + iface, (struct in_addr *)NET_IPV4_HDR(pkt)->dst)) { + NET_DBG("Could not find pending ARP entry"); + } + /* Free the ARP request */ + net_pkt_unref(pkt); + } + + return ret; } static inline int ethernet_enable(struct net_if *iface, bool state) From dfaba89fc73df532c29bcc1afc02e6fdf92231a2 Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Fri, 23 Feb 2024 12:11:48 +0100 Subject: [PATCH 09/18] [nrf fromtree] net: ethernet: Register multicast monitor for MAC filtering Register a multicast monitor for ethernet if HW MAC filtering is supported Signed-off-by: Pieter De Gendt (cherry picked from commit af8ba2a361adb7fb9d544258428f4de61765f2f9) --- subsys/net/l2/ethernet/ethernet.c | 54 +++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/subsys/net/l2/ethernet/ethernet.c b/subsys/net/l2/ethernet/ethernet.c index a067e677b37..985b84bf801 100644 --- a/subsys/net/l2/ethernet/ethernet.c +++ b/subsys/net/l2/ethernet/ethernet.c @@ -37,6 +37,10 @@ static const struct net_eth_addr multicast_eth_addr __unused = { static const struct net_eth_addr broadcast_eth_addr = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#if defined(CONFIG_NET_NATIVE_IP) && !defined(CONFIG_NET_RAW_MODE) +static struct net_if_mcast_monitor mcast_monitor; +#endif + const struct net_eth_addr *net_eth_broadcast_addr(void) { return &broadcast_eth_addr; @@ -187,6 +191,50 @@ enum net_verdict ethernet_check_ipv4_bcast_addr(struct net_pkt *pkt, return NET_OK; } +#if defined(CONFIG_NET_NATIVE_IP) && !defined(CONFIG_NET_RAW_MODE) +static void ethernet_mcast_monitor_cb(struct net_if *iface, const struct net_addr *addr, + bool is_joined) +{ + struct ethernet_config cfg = { + .filter = { + .set = is_joined, + .type = ETHERNET_FILTER_TYPE_DST_MAC_ADDRESS, + }, + }; + const struct device *dev; + const struct ethernet_api *api; + + /* Make sure we're an ethernet device */ + if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) { + return; + } + + dev = net_if_get_device(iface); + api = dev->api; + + if (!(api->get_capabilities(dev) & ETHERNET_HW_FILTERING) || api->set_config == NULL) { + return; + } + + switch (addr->family) { +#if defined(CONFIG_NET_IPV4) + case AF_INET: + net_eth_ipv4_mcast_to_mac_addr(&addr->in_addr, &cfg.filter.mac_address); + break; +#endif /* CONFIG_NET_IPV4 */ +#if defined(CONFIG_NET_IPV6) + case AF_INET6: + net_eth_ipv6_mcast_to_mac_addr(&addr->in6_addr, &cfg.filter.mac_address); + break; +#endif /* CONFIG_NET_IPV6 */ + default: + return; + } + + api->set_config(dev, ETHERNET_CONFIG_TYPE_FILTER, &cfg); +} +#endif + static enum net_verdict ethernet_recv(struct net_if *iface, struct net_pkt *pkt) { @@ -1237,6 +1285,12 @@ void ethernet_init(struct net_if *iface) ctx->ethernet_l2_flags |= NET_L2_PROMISC_MODE; } +#if defined(CONFIG_NET_NATIVE_IP) && !defined(CONFIG_NET_RAW_MODE) + if (net_eth_get_hw_capabilities(iface) & ETHERNET_HW_FILTERING) { + net_if_mcast_mon_register(&mcast_monitor, NULL, ethernet_mcast_monitor_cb); + } +#endif + #if defined(CONFIG_NET_VLAN) if (!(net_eth_get_hw_capabilities(iface) & ETHERNET_HW_VLAN)) { return; From 828b36ea97bedab647db806b34d573322c177138 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 7 Feb 2024 11:50:06 +0200 Subject: [PATCH 10/18] [nrf fromtree] drivers: ethernet: Disable drivers if tests are enabled The network tests at tests/net use simulated network interfaces and set CONFIG_NET_TEST to indicate that. If the config option is set, then we do not want any extra Ethernet driver to complicate the testing scenario so all external Ethernet network interfaces should be disabled. Signed-off-by: Jukka Rissanen (cherry picked from commit bf31b0985efe0007ebea731ecd906e1b0e948e64) --- drivers/ethernet/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ethernet/Kconfig b/drivers/ethernet/Kconfig index 69b60e09bc2..f019de90b82 100644 --- a/drivers/ethernet/Kconfig +++ b/drivers/ethernet/Kconfig @@ -7,6 +7,7 @@ menuconfig ETH_DRIVER bool "Ethernet drivers" default y depends on NET_L2_ETHERNET + depends on !NET_TEST if ETH_DRIVER From e60235363ac5aac80eb384a6083372705b85c0b2 Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Mon, 11 Mar 2024 14:58:58 +0100 Subject: [PATCH 11/18] [nrf fromtree] include: net: ethernet: Add ETH_NET_DEVICE_INIT_INSTANCE Add a macro variant for ethernet devices with instances. Signed-off-by: Pieter De Gendt (cherry picked from commit 253139455c2dfa069de809017414531debcf94d6) --- include/zephyr/net/ethernet.h | 56 ++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/include/zephyr/net/ethernet.h b/include/zephyr/net/ethernet.h index faa6cf7fc0b..7ac97b747b3 100644 --- a/include/zephyr/net/ethernet.h +++ b/include/zephyr/net/ethernet.h @@ -933,23 +933,35 @@ static inline bool net_eth_get_vlan_status(struct net_if *iface) #endif #if defined(CONFIG_NET_VLAN) -#define Z_ETH_NET_DEVICE_INIT(node_id, dev_id, name, init_fn, pm, data, \ - config, prio, api, mtu) \ + +#define Z_ETH_NET_DEVICE_INIT_INSTANCE(node_id, dev_id, name, instance, \ + init_fn, pm, data, config, prio, \ + api, mtu) \ Z_DEVICE_STATE_DEFINE(dev_id); \ Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, pm, data, \ config, POST_KERNEL, prio, api, \ &Z_DEVICE_STATE_NAME(dev_id)); \ - NET_L2_DATA_INIT(dev_id, 0, NET_L2_GET_CTX_TYPE(ETHERNET_L2)); \ - NET_IF_INIT(dev_id, 0, ETHERNET_L2, mtu, NET_VLAN_MAX_COUNT) + NET_L2_DATA_INIT(dev_id, instance, \ + NET_L2_GET_CTX_TYPE(ETHERNET_L2)); \ + NET_IF_INIT(dev_id, instance, ETHERNET_L2, mtu, \ + NET_VLAN_MAX_COUNT) #else /* CONFIG_NET_VLAN */ +#define Z_ETH_NET_DEVICE_INIT_INSTANCE(node_id, dev_id, name, instance, \ + init_fn, pm, data, config, prio, \ + api, mtu) \ + Z_NET_DEVICE_INIT_INSTANCE(node_id, dev_id, name, instance, \ + init_fn, pm, data, config, prio, \ + api, ETHERNET_L2, \ + NET_L2_GET_CTX_TYPE(ETHERNET_L2), mtu) +#endif /* CONFIG_NET_VLAN */ + #define Z_ETH_NET_DEVICE_INIT(node_id, dev_id, name, init_fn, pm, data, \ config, prio, api, mtu) \ - Z_NET_DEVICE_INIT(node_id, dev_id, name, init_fn, pm, data, \ - config, prio, api, ETHERNET_L2, \ - NET_L2_GET_CTX_TYPE(ETHERNET_L2), mtu) -#endif /* CONFIG_NET_VLAN */ + Z_ETH_NET_DEVICE_INIT_INSTANCE(node_id, dev_id, name, 0, \ + init_fn, pm, data, config, prio, \ + api, mtu) /** * @brief Create an Ethernet network interface and bind it to network device. @@ -973,6 +985,34 @@ static inline bool net_eth_get_vlan_status(struct net_if *iface) Z_ETH_NET_DEVICE_INIT(DT_INVALID_NODE, dev_id, name, init_fn, \ pm, data, config, prio, api, mtu) +/** + * @brief Create multiple Ethernet network interfaces and bind them to network + * devices. + * If your network device needs more than one instance of a network interface, + * use this macro below and provide a different instance suffix each time + * (0, 1, 2, ... or a, b, c ... whatever works for you) + * + * @param dev_id Network device id. + * @param name The name this instance of the driver exposes to + * the system. + * @param instance Instance identifier. + * @param init_fn Address to the init function of the driver. + * @param pm Reference to struct pm_device associated with the device. + * (optional). + * @param data Pointer to the device's private data. + * @param config The address to the structure containing the + * configuration information for this instance of the driver. + * @param prio The initialization level at which configuration occurs. + * @param api Provides an initial pointer to the API function struct + * used by the driver. Can be NULL. + * @param mtu Maximum transfer unit in bytes for this network interface. + */ +#define ETH_NET_DEVICE_INIT_INSTANCE(dev_id, name, instance, init_fn, \ + pm, data, config, prio, api, mtu) \ + Z_ETH_NET_DEVICE_INIT_INSTANCE(DT_INVALID_NODE, dev_id, name, \ + instance, init_fn, pm, data, \ + config, prio, api, mtu) + /** * @brief Like ETH_NET_DEVICE_INIT but taking metadata from a devicetree. * Create an Ethernet network interface and bind it to network device. From a2514b0b9b0bd81e0d7ec38423d8d831305e19aa Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 22 Nov 2023 15:07:39 +0200 Subject: [PATCH 12/18] [nrf fromtree] net: context: Add support for setting receive pktinfo option Add low level support for setting IP_PKTINFO or IPV6_RECVPKTINFO socket options. The support is disabled by default. Signed-off-by: Jukka Rissanen (cherry picked from commit c3acd56e27b8305a76390c24d2689783424a8723) --- include/zephyr/net/net_context.h | 4 ++ subsys/net/ip/Kconfig | 8 ++++ subsys/net/ip/net_context.c | 73 ++++++++++++++++++++++++++++++++ subsys/net/ip/net_private.h | 6 +++ 4 files changed, 91 insertions(+) diff --git a/include/zephyr/net/net_context.h b/include/zephyr/net/net_context.h index ea116a24846..7e73bf8eee7 100644 --- a/include/zephyr/net/net_context.h +++ b/include/zephyr/net/net_context.h @@ -331,6 +331,9 @@ __net_socket struct net_context { #endif #if defined(CONFIG_NET_IPV4_MAPPING_TO_IPV6) bool ipv6_v6only; +#endif +#if defined(CONFIG_NET_CONTEXT_RECV_PKTINFO) + bool recv_pktinfo; #endif } options; @@ -1102,6 +1105,7 @@ enum net_context_option { NET_OPT_REUSEADDR = 9, NET_OPT_REUSEPORT = 10, NET_OPT_IPV6_V6ONLY = 11, + NET_OPT_RECV_PKTINFO = 12, }; /** diff --git a/subsys/net/ip/Kconfig b/subsys/net/ip/Kconfig index a5a6b7ba01e..fc9fa56796c 100644 --- a/subsys/net/ip/Kconfig +++ b/subsys/net/ip/Kconfig @@ -737,6 +737,14 @@ config NET_CONTEXT_REUSEPORT Allow to set the SO_REUSEPORT flag on a socket. This enables multiple sockets to bind to the same local IP address and port combination. +config NET_CONTEXT_RECV_PKTINFO + bool "Add receive PKTINFO support to net_context" + depends on NET_UDP + help + Allow to set the IP_PKTINFO or IPV6_RECVPKTINFO flags on a socket. + This way user can get extra information about the received data in the + socket. + config NET_TEST bool "Network Testing" help diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index a628297f650..f92cd5c0875 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -89,6 +89,17 @@ bool net_context_is_v6only_set(struct net_context *context) #endif } +bool net_context_is_recv_pktinfo_set(struct net_context *context) +{ +#if defined(CONFIG_NET_CONTEXT_RECV_PKTINFO) + return context->options.recv_pktinfo; +#else + ARG_UNUSED(context); + + return false; +#endif +} + #if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_TCP) static inline bool is_in_tcp_listen_state(struct net_context *context) { @@ -1558,6 +1569,36 @@ static int get_context_ipv6_v6only(struct net_context *context, #endif } +static int get_context_recv_pktinfo(struct net_context *context, + void *value, size_t *len) +{ +#if defined(CONFIG_NET_CONTEXT_RECV_PKTINFO) + if (!value || !len) { + return -EINVAL; + } + + if (*len != sizeof(int)) { + return -EINVAL; + } + + if (context->options.recv_pktinfo == true) { + *((int *)value) = (int) true; + } else { + *((int *)value) = (int) false; + } + + *len = sizeof(int); + + return 0; +#else + ARG_UNUSED(context); + ARG_UNUSED(value); + ARG_UNUSED(len); + + return -ENOTSUP; +#endif +} + /* If buf is not NULL, then use it. Otherwise read the data to be written * to net_pkt from msghdr. */ @@ -2697,6 +2738,32 @@ static int set_context_ipv6_v6only(struct net_context *context, #endif } +static int set_context_recv_pktinfo(struct net_context *context, + const void *value, size_t len) +{ +#if defined(CONFIG_NET_CONTEXT_RECV_PKTINFO) + bool pktinfo = false; + + if (len != sizeof(int)) { + return -EINVAL; + } + + if (*((int *) value) != 0) { + pktinfo = true; + } + + context->options.recv_pktinfo = pktinfo; + + return 0; +#else + ARG_UNUSED(context); + ARG_UNUSED(value); + ARG_UNUSED(len); + + return -ENOTSUP; +#endif +} + int net_context_set_option(struct net_context *context, enum net_context_option option, const void *value, size_t len) @@ -2745,6 +2812,9 @@ int net_context_set_option(struct net_context *context, case NET_OPT_IPV6_V6ONLY: ret = set_context_ipv6_v6only(context, value, len); break; + case NET_OPT_RECV_PKTINFO: + ret = set_context_recv_pktinfo(context, value, len); + break; } k_mutex_unlock(&context->lock); @@ -2800,6 +2870,9 @@ int net_context_get_option(struct net_context *context, case NET_OPT_IPV6_V6ONLY: ret = get_context_ipv6_v6only(context, value, len); break; + case NET_OPT_RECV_PKTINFO: + ret = get_context_recv_pktinfo(context, value, len); + break; } k_mutex_unlock(&context->lock); diff --git a/subsys/net/ip/net_private.h b/subsys/net/ip/net_private.h index 0906a7a28b1..7790ce260cf 100644 --- a/subsys/net/ip/net_private.h +++ b/subsys/net/ip/net_private.h @@ -72,6 +72,7 @@ extern const char *net_context_state(struct net_context *context); extern bool net_context_is_reuseaddr_set(struct net_context *context); extern bool net_context_is_reuseport_set(struct net_context *context); extern bool net_context_is_v6only_set(struct net_context *context); +extern bool net_context_is_recv_pktinfo_set(struct net_context *context); extern void net_pkt_init(void); extern void net_tc_tx_init(void); extern void net_tc_rx_init(void); @@ -95,6 +96,11 @@ static inline bool net_context_is_reuseport_set(struct net_context *context) ARG_UNUSED(context); return false; } +static inline bool net_context_is_recv_pktinfo_set(struct net_context *context) +{ + ARG_UNUSED(context); + return false; +} #endif #if defined(CONFIG_NET_NATIVE) From cf483a34341eebacd82fd7af43fadfff652e65a4 Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Mon, 11 Mar 2024 11:18:18 +0100 Subject: [PATCH 13/18] [nrf fromtree] drivers: ethernet: Introduce ETH_DRIVER_RAW_MODE option Add a Kconfig symbol to allow building ethernet device driver without an L2 layer. Signed-off-by: Pieter De Gendt (cherry picked from commit e99b5228a1f9dd162dd3d6be1eda42b26d762da3) --- drivers/ethernet/Kconfig | 15 ++++++++++++--- drivers/ethernet/phy/Kconfig | 9 ++++++--- drivers/net/Kconfig | 1 - include/zephyr/net/ethernet.h | 12 ++++++++++++ subsys/net/ip/Kconfig | 12 ++++++------ 5 files changed, 36 insertions(+), 13 deletions(-) diff --git a/drivers/ethernet/Kconfig b/drivers/ethernet/Kconfig index f019de90b82..fd02aed31be 100644 --- a/drivers/ethernet/Kconfig +++ b/drivers/ethernet/Kconfig @@ -5,12 +5,21 @@ menuconfig ETH_DRIVER bool "Ethernet drivers" - default y - depends on NET_L2_ETHERNET + depends on NETWORKING depends on !NET_TEST + default y if NET_L2_ETHERNET if ETH_DRIVER +config ETH_DRIVER_RAW_MODE + bool "Ethernet driver without the MAC stack" + select NET_RAW_MODE + depends on !NET_L2_ETHERNET + help + This option enables using the drivers in a so-called "raw" mode, + i.e. without a MAC stack (the net L2 layer for ethernet will not + be built). + module = ETHERNET module-dep = LOG module-str = Log level for Ethernet driver @@ -72,7 +81,7 @@ endif # "Ethernet Drivers" config ETH_INIT_PRIORITY int "Ethernet driver init priority" default 80 - depends on NET_L2_ETHERNET + depends on NET_L2_ETHERNET || ETH_DRIVER help Ethernet device driver initialization priority. Do not mess with it unless you know what you are doing. diff --git a/drivers/ethernet/phy/Kconfig b/drivers/ethernet/phy/Kconfig index ae1229de1fc..32974fbb67a 100644 --- a/drivers/ethernet/phy/Kconfig +++ b/drivers/ethernet/phy/Kconfig @@ -3,8 +3,11 @@ # Copyright (c) 2021 IP-Logix Inc. # SPDX-License-Identifier: Apache-2.0 -menu "Ethernet PHY Drivers" - depends on NET_L2_ETHERNET +menuconfig ETH_PHY_DRIVER + bool "Ethernet PHY drivers" + default y if NET_L2_ETHERNET || ETH_DRIVER + +if ETH_PHY_DRIVER module = PHY module-dep = LOG @@ -54,4 +57,4 @@ config PHY_MONITOR_PERIOD periodically executed to detect and report any changes in the PHY link status to the operating system. -endmenu # "Ethernet PHY Drivers" +endif # "Ethernet PHY Drivers" diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index f6e0faa334a..00c7f3233a7 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -156,7 +156,6 @@ config SLIP_STATISTICS config SLIP_TAP bool "Use TAP interface to host" default y - select NET_L2_ETHERNET help In TAP the Ethernet frames are transferred over SLIP. diff --git a/include/zephyr/net/ethernet.h b/include/zephyr/net/ethernet.h index 7ac97b747b3..5fcbbb731ad 100644 --- a/include/zephyr/net/ethernet.h +++ b/include/zephyr/net/ethernet.h @@ -932,6 +932,7 @@ static inline bool net_eth_get_vlan_status(struct net_if *iface) } #endif +#if !defined(CONFIG_ETH_DRIVER_RAW_MODE) #if defined(CONFIG_NET_VLAN) #define Z_ETH_NET_DEVICE_INIT_INSTANCE(node_id, dev_id, name, instance, \ @@ -957,6 +958,17 @@ static inline bool net_eth_get_vlan_status(struct net_if *iface) NET_L2_GET_CTX_TYPE(ETHERNET_L2), mtu) #endif /* CONFIG_NET_VLAN */ +#else /* CONFIG_ETH_DRIVER_RAW_MODE */ + +#define Z_ETH_NET_DEVICE_INIT_INSTANCE(node_id, dev_id, name, instance, \ + init_fn, pm, data, config, prio, \ + api, mtu) \ + Z_DEVICE_STATE_DEFINE(dev_id); \ + Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, pm, data, \ + config, POST_KERNEL, prio, api, \ + &Z_DEVICE_STATE_NAME(dev_id)); +#endif /* CONFIG_ETH_DRIVER_RAW_MODE */ + #define Z_ETH_NET_DEVICE_INIT(node_id, dev_id, name, init_fn, pm, data, \ config, prio, api, mtu) \ Z_ETH_NET_DEVICE_INIT_INSTANCE(node_id, dev_id, name, 0, \ diff --git a/subsys/net/ip/Kconfig b/subsys/net/ip/Kconfig index fc9fa56796c..94c08218ead 100644 --- a/subsys/net/ip/Kconfig +++ b/subsys/net/ip/Kconfig @@ -745,12 +745,6 @@ config NET_CONTEXT_RECV_PKTINFO This way user can get extra information about the received data in the socket. -config NET_TEST - bool "Network Testing" - help - Used for self-contained networking tests that do not require a - network device. - config NET_SLIP_TAP bool "TAP SLIP driver" depends on NET_QEMU_SLIP @@ -768,6 +762,12 @@ config NET_SLIP_TAP endif # NET_RAW_MODE +config NET_TEST + bool "Network Testing" + help + Used for self-contained networking tests that do not require a + network device. + config NET_PKT_RX_COUNT int "How many packet receives can be pending at the same time" default 14 if NET_L2_ETHERNET From 3864ded91a618f35f8d8c8e66c35978728a1e981 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Sun, 17 Mar 2024 15:32:29 +0200 Subject: [PATCH 14/18] [nrf fromtree] net: ethernet: VLAN overhaul and refactoring Re-implement the VLAN support inside the network stack. All the user facing APIs stay as is but internally the VLANs are implemented using the L2 virtual interfaces. Signed-off-by: Jukka Rissanen (cherry picked from commit 2058d8f139e9b7755fdf6b4fac59e10f1edea960) --- include/zephyr/net/ethernet.h | 94 ++-- include/zephyr/net/virtual.h | 3 + subsys/net/l2/ethernet/CMakeLists.txt | 1 + subsys/net/l2/ethernet/Kconfig | 10 +- subsys/net/l2/ethernet/arp.c | 10 +- subsys/net/l2/ethernet/ethernet.c | 467 +++---------------- subsys/net/l2/ethernet/vlan.c | 640 ++++++++++++++++++++++++++ 7 files changed, 764 insertions(+), 461 deletions(-) create mode 100644 subsys/net/l2/ethernet/vlan.c diff --git a/include/zephyr/net/ethernet.h b/include/zephyr/net/ethernet.h index 5fcbbb731ad..ef13537e27a 100644 --- a/include/zephyr/net/ethernet.h +++ b/include/zephyr/net/ethernet.h @@ -592,17 +592,6 @@ struct ethernet_context { */ atomic_t flags; -#if defined(CONFIG_NET_VLAN) - struct ethernet_vlan vlan[NET_VLAN_MAX_COUNT]; - - /** Array that will help when checking if VLAN is enabled for - * some specific network interface. Requires that VLAN count - * NET_VLAN_MAX_COUNT is not smaller than the actual number - * of network interfaces. - */ - ATOMIC_DEFINE(interfaces, NET_VLAN_MAX_COUNT); -#endif - #if defined(CONFIG_NET_ETHERNET_BRIDGE) struct eth_bridge_iface_context bridge; #endif @@ -651,14 +640,6 @@ struct ethernet_context { dsa_send_t dsa_send; #endif -#if defined(CONFIG_NET_VLAN) - /** Flag that tells whether how many VLAN tags are enabled for this - * context. The same information can be dug from the vlan array but - * this saves some time in RX path. - */ - int8_t vlan_enabled; -#endif - /** Is network carrier up */ bool is_net_carrier_up : 1; @@ -839,6 +820,9 @@ int net_eth_vlan_enable(struct net_if *iface, uint16_t tag); #else static inline int net_eth_vlan_enable(struct net_if *iface, uint16_t tag) { + ARG_UNUSED(iface); + ARG_UNUSED(tag); + return -EINVAL; } #endif @@ -856,14 +840,20 @@ int net_eth_vlan_disable(struct net_if *iface, uint16_t tag); #else static inline int net_eth_vlan_disable(struct net_if *iface, uint16_t tag) { + ARG_UNUSED(iface); + ARG_UNUSED(tag); + return -EINVAL; } #endif /** - * @brief Return VLAN tag specified to network interface + * @brief Return VLAN tag specified to network interface. + * + * Note that the interface parameter must be the VLAN interface, + * and not the Ethernet one. * - * @param iface Network interface. + * @param iface VLAN network interface. * * @return VLAN tag for this interface or NET_VLAN_TAG_UNSPEC if VLAN * is not configured for that interface. @@ -873,6 +863,8 @@ uint16_t net_eth_get_vlan_tag(struct net_if *iface); #else static inline uint16_t net_eth_get_vlan_tag(struct net_if *iface) { + ARG_UNUSED(iface); + return NET_VLAN_TAG_UNSPEC; } #endif @@ -880,8 +872,7 @@ static inline uint16_t net_eth_get_vlan_tag(struct net_if *iface) /** * @brief Return network interface related to this VLAN tag * - * @param iface Master network interface. This is used to get the - * pointer to ethernet L2 context + * @param iface Main network interface (not the VLAN one). * @param tag VLAN tag * * @return Network interface related to this tag or NULL if no such interface @@ -893,17 +884,46 @@ struct net_if *net_eth_get_vlan_iface(struct net_if *iface, uint16_t tag); static inline struct net_if *net_eth_get_vlan_iface(struct net_if *iface, uint16_t tag) { + ARG_UNUSED(iface); + ARG_UNUSED(tag); + + return NULL; +} +#endif + +/** + * @brief Return main network interface that is attached to this VLAN tag. + * + * @param iface VLAN network interface. This is used to get the + * pointer to ethernet L2 context + * + * @return Network interface related to this tag or NULL if no such interface + * exists. + */ +#if defined(CONFIG_NET_VLAN) +struct net_if *net_eth_get_vlan_main(struct net_if *iface); +#else +static inline +struct net_if *net_eth_get_vlan_main(struct net_if *iface) +{ + ARG_UNUSED(iface); + return NULL; } #endif /** - * @brief Check if VLAN is enabled for a specific network interface. + * @brief Check if there are any VLAN interfaces enabled to this specific + * Ethernet network interface. + * + * Note that the iface must be the actual Ethernet interface and not the + * virtual VLAN interface. * * @param ctx Ethernet context - * @param iface Network interface + * @param iface Ethernet network interface * - * @return True if VLAN is enabled for this network interface, false if not. + * @return True if there are enabled VLANs for this network interface, + * false if not. */ #if defined(CONFIG_NET_VLAN) bool net_eth_is_vlan_enabled(struct ethernet_context *ctx, @@ -912,6 +932,9 @@ bool net_eth_is_vlan_enabled(struct ethernet_context *ctx, static inline bool net_eth_is_vlan_enabled(struct ethernet_context *ctx, struct net_if *iface) { + ARG_UNUSED(ctx); + ARG_UNUSED(iface); + return false; } #endif @@ -928,26 +951,13 @@ bool net_eth_get_vlan_status(struct net_if *iface); #else static inline bool net_eth_get_vlan_status(struct net_if *iface) { + ARG_UNUSED(iface); + return false; } #endif #if !defined(CONFIG_ETH_DRIVER_RAW_MODE) -#if defined(CONFIG_NET_VLAN) - -#define Z_ETH_NET_DEVICE_INIT_INSTANCE(node_id, dev_id, name, instance, \ - init_fn, pm, data, config, prio, \ - api, mtu) \ - Z_DEVICE_STATE_DEFINE(dev_id); \ - Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, pm, data, \ - config, POST_KERNEL, prio, api, \ - &Z_DEVICE_STATE_NAME(dev_id)); \ - NET_L2_DATA_INIT(dev_id, instance, \ - NET_L2_GET_CTX_TYPE(ETHERNET_L2)); \ - NET_IF_INIT(dev_id, instance, ETHERNET_L2, mtu, \ - NET_VLAN_MAX_COUNT) - -#else /* CONFIG_NET_VLAN */ #define Z_ETH_NET_DEVICE_INIT_INSTANCE(node_id, dev_id, name, instance, \ init_fn, pm, data, config, prio, \ @@ -956,7 +966,6 @@ static inline bool net_eth_get_vlan_status(struct net_if *iface) init_fn, pm, data, config, prio, \ api, ETHERNET_L2, \ NET_L2_GET_CTX_TYPE(ETHERNET_L2), mtu) -#endif /* CONFIG_NET_VLAN */ #else /* CONFIG_ETH_DRIVER_RAW_MODE */ @@ -967,6 +976,7 @@ static inline bool net_eth_get_vlan_status(struct net_if *iface) Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, pm, data, \ config, POST_KERNEL, prio, api, \ &Z_DEVICE_STATE_NAME(dev_id)); + #endif /* CONFIG_ETH_DRIVER_RAW_MODE */ #define Z_ETH_NET_DEVICE_INIT(node_id, dev_id, name, init_fn, pm, data, \ diff --git a/include/zephyr/net/virtual.h b/include/zephyr/net/virtual.h index 4509ff351ff..0362ffa5ee6 100644 --- a/include/zephyr/net/virtual.h +++ b/include/zephyr/net/virtual.h @@ -38,6 +38,9 @@ enum virtual_interface_caps { /** IPIP tunnel */ VIRTUAL_INTERFACE_IPIP = BIT(1), + /** Virtual LAN interface (VLAN) */ + VIRTUAL_INTERFACE_VLAN = BIT(2), + /** @cond INTERNAL_HIDDEN */ /* Marker for capabilities - must be at the end of the enum. * It is here because the capability list cannot be empty. diff --git a/subsys/net/l2/ethernet/CMakeLists.txt b/subsys/net/l2/ethernet/CMakeLists.txt index aa23df860f6..2330d18dfd3 100644 --- a/subsys/net/l2/ethernet/CMakeLists.txt +++ b/subsys/net/l2/ethernet/CMakeLists.txt @@ -17,6 +17,7 @@ zephyr_library_sources_ifdef(CONFIG_NET_L2_ETHERNET_MGMT ethernet_mgmt.c) if(CONFIG_NET_NATIVE) zephyr_library_sources_ifdef(CONFIG_NET_ARP arp.c) +zephyr_library_sources_ifdef(CONFIG_NET_VLAN vlan.c) zephyr_library_sources_ifdef(CONFIG_NET_STATISTICS_ETHERNET ethernet_stats.c) zephyr_library_sources_ifdef(CONFIG_NET_ETHERNET_BRIDGE bridge.c) zephyr_library_sources_ifdef(CONFIG_NET_ETHERNET_BRIDGE_SHELL bridge_shell.c) diff --git a/subsys/net/l2/ethernet/Kconfig b/subsys/net/l2/ethernet/Kconfig index 5413ab4d549..5d558738eca 100644 --- a/subsys/net/l2/ethernet/Kconfig +++ b/subsys/net/l2/ethernet/Kconfig @@ -33,7 +33,8 @@ config NET_L2_ETHERNET_ACCEPT_MISMATCH_L3_L2_ADDR buggy devices that do not follow the RFC. config NET_VLAN - bool "Virtual lan support" + bool "Virtual LAN support" + select NET_L2_VIRTUAL help Enables virtual lan (VLAN) support for Ethernet. @@ -45,6 +46,13 @@ config NET_VLAN_COUNT help How many VLAN tags can be configured. +config NET_VLAN_TXRX_DEBUG + bool "Debug received and sent packets in VLAN" + depends on NET_L2_ETHERNET_LOG_LEVEL_DBG && NET_VLAN + help + Enables printing of received and sent network packets. + This can produce lot of output so it is disabled by default. + config NET_ARP bool "ARP" default y diff --git a/subsys/net/l2/ethernet/arp.c b/subsys/net/l2/ethernet/arp.c index 0f33e68b31a..bae967079e9 100644 --- a/subsys/net/l2/ethernet/arp.c +++ b/subsys/net/l2/ethernet/arp.c @@ -274,9 +274,11 @@ static inline struct net_pkt *arp_prepare(struct net_if *iface, if (IS_ENABLED(CONFIG_NET_CAPTURE) && pending) { net_pkt_set_captured(pkt, net_pkt_is_captured(pending)); } - } - net_pkt_set_vlan_tag(pkt, net_eth_get_vlan_tag(iface)); + if (IS_ENABLED(CONFIG_NET_VLAN) && pending) { + net_pkt_set_vlan_tag(pkt, net_pkt_vlan_tag(pending)); + } + } net_buf_add(pkt->buffer, sizeof(struct net_arp_hdr)); @@ -706,7 +708,9 @@ static inline struct net_pkt *arp_prepare_reply(struct net_if *iface, hdr = NET_ARP_HDR(pkt); query = NET_ARP_HDR(req); - net_pkt_set_vlan_tag(pkt, net_pkt_vlan_tag(req)); + if (IS_ENABLED(CONFIG_NET_VLAN)) { + net_pkt_set_vlan_tag(pkt, net_pkt_vlan_tag(req)); + } hdr->hwtype = htons(NET_ARP_HTYPE_ETH); hdr->protocol = htons(NET_ETH_PTYPE_IP); diff --git a/subsys/net/l2/ethernet/ethernet.c b/subsys/net/l2/ethernet/ethernet.c index 985b84bf801..0fefc7d8032 100644 --- a/subsys/net/l2/ethernet/ethernet.c +++ b/subsys/net/l2/ethernet/ethernet.c @@ -243,7 +243,8 @@ static enum net_verdict ethernet_recv(struct net_if *iface, uint8_t hdr_len = sizeof(struct net_eth_hdr); uint16_t type; struct net_linkaddr *lladdr; - sa_family_t family; + sa_family_t family = AF_UNSPEC; + bool is_vlan_pkt = false; /* This expects that the Ethernet header is in the first net_buf * fragment. This is a safe expectation here as it would not make @@ -269,15 +270,33 @@ static enum net_verdict ethernet_recv(struct net_if *iface, type = ntohs(hdr->type); - if (net_eth_is_vlan_enabled(ctx, iface) && - type == NET_ETH_PTYPE_VLAN && - !eth_is_vlan_tag_stripped(iface)) { - struct net_eth_vlan_hdr *hdr_vlan = - (struct net_eth_vlan_hdr *)NET_ETH_HDR(pkt); - - net_pkt_set_vlan_tci(pkt, ntohs(hdr_vlan->vlan.tci)); - type = ntohs(hdr_vlan->type); - hdr_len = sizeof(struct net_eth_vlan_hdr); + if (IS_ENABLED(CONFIG_NET_VLAN) && type == NET_ETH_PTYPE_VLAN) { + if (net_eth_is_vlan_enabled(ctx, iface) && + !eth_is_vlan_tag_stripped(iface)) { + struct net_eth_vlan_hdr *hdr_vlan = + (struct net_eth_vlan_hdr *)NET_ETH_HDR(pkt); + enum net_verdict verdict; + + net_pkt_set_vlan_tci(pkt, ntohs(hdr_vlan->vlan.tci)); + type = ntohs(hdr_vlan->type); + hdr_len = sizeof(struct net_eth_vlan_hdr); + is_vlan_pkt = true; + + net_pkt_set_iface(pkt, + net_eth_get_vlan_iface(iface, + net_pkt_vlan_tag(pkt))); + + /* We could call VLAN interface directly but then the + * interface statistics would not get updated so route + * the call via Virtual L2 layer. + */ + if (net_if_l2(net_pkt_iface(pkt))->recv != NULL) { + verdict = net_if_l2(net_pkt_iface(pkt))->recv(iface, pkt); + if (verdict == NET_DROP) { + goto drop; + } + } + } } switch (type) { @@ -330,20 +349,12 @@ static enum net_verdict ethernet_recv(struct net_if *iface, net_pkt_set_ll_proto_type(pkt, type); - if (net_eth_is_vlan_enabled(ctx, iface)) { - if (type == NET_ETH_PTYPE_VLAN || - (eth_is_vlan_tag_stripped(iface) && - net_pkt_vlan_tci(pkt))) { - print_vlan_ll_addrs(pkt, type, net_pkt_vlan_tci(pkt), - net_pkt_get_len(pkt), - net_pkt_lladdr_src(pkt), - net_pkt_lladdr_dst(pkt), - eth_is_vlan_tag_stripped(iface)); - } else { - print_ll_addrs(pkt, type, net_pkt_get_len(pkt), - net_pkt_lladdr_src(pkt), - net_pkt_lladdr_dst(pkt)); - } + if (is_vlan_pkt) { + print_vlan_ll_addrs(pkt, type, net_pkt_vlan_tci(pkt), + net_pkt_get_len(pkt), + net_pkt_lladdr_src(pkt), + net_pkt_lladdr_dst(pkt), + eth_is_vlan_tag_stripped(iface)); } else { print_ll_addrs(pkt, type, net_pkt_get_len(pkt), net_pkt_lladdr_src(pkt), @@ -430,6 +441,16 @@ static bool ethernet_fill_in_dst_on_ipv4_mcast(struct net_pkt *pkt, static struct net_pkt *ethernet_ll_prepare_on_ipv4(struct net_if *iface, struct net_pkt *pkt) { + struct ethernet_context *ctx = net_if_l2_data(iface); + + if (IS_ENABLED(CONFIG_NET_VLAN) && + net_pkt_vlan_tag(pkt) != NET_VLAN_TAG_UNSPEC && + net_eth_is_vlan_enabled(ctx, net_pkt_iface(pkt))) { + iface = net_eth_get_vlan_iface(iface, + net_pkt_vlan_tag(pkt)); + net_pkt_set_iface(pkt, iface); + } + if (ethernet_ipv4_dst_is_broadcast_or_mcast(pkt)) { return pkt; } @@ -482,79 +503,8 @@ static bool ethernet_fill_in_dst_on_ipv6_mcast(struct net_pkt *pkt, #define ethernet_fill_in_dst_on_ipv6_mcast(...) false #endif /* CONFIG_NET_IPV6 */ -#if defined(CONFIG_NET_VLAN) -static enum net_verdict set_vlan_tag(struct ethernet_context *ctx, - struct net_if *iface, - struct net_pkt *pkt) -{ - int i; - - if (net_pkt_vlan_tag(pkt) != NET_VLAN_TAG_UNSPEC) { - return NET_OK; - } - -#if defined(CONFIG_NET_IPV6) - if (net_pkt_family(pkt) == AF_INET6) { - struct net_if *target; - - if (net_if_ipv6_addr_lookup((struct in6_addr *)NET_IPV6_HDR(pkt)->src, - &target)) { - if (target != iface) { - NET_DBG("Iface %p should be %p", iface, - target); - - iface = target; - } - } - } -#endif - -#if defined(CONFIG_NET_IPV4) - if (net_pkt_family(pkt) == AF_INET) { - struct net_if *target; - - if (net_if_ipv4_addr_lookup((struct in_addr *)NET_IPV4_HDR(pkt)->src, - &target)) { - if (target != iface) { - NET_DBG("Iface %p should be %p", iface, - target); - iface = target; - } - } - } -#endif - - for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) { - if (ctx->vlan[i].tag == NET_VLAN_TAG_UNSPEC || - ctx->vlan[i].iface != iface) { - continue; - } - - /* Depending on source address, use the proper network - * interface when sending. - */ - net_pkt_set_vlan_tag(pkt, ctx->vlan[i].tag); - - return NET_OK; - } - - return NET_DROP; -} - -static void set_vlan_priority(struct ethernet_context *ctx, - struct net_pkt *pkt) -{ - uint8_t vlan_priority; - - vlan_priority = net_priority2vlan(net_pkt_priority(pkt)); - net_pkt_set_vlan_priority(pkt, vlan_priority); -} -#else -#define set_vlan_tag(...) NET_DROP -#define set_vlan_priority(...) -#endif /* CONFIG_NET_VLAN */ - static struct net_buf *ethernet_fill_header(struct ethernet_context *ctx, + struct net_if *iface, struct net_pkt *pkt, uint32_t ptype) { @@ -570,7 +520,8 @@ static struct net_buf *ethernet_fill_header(struct ethernet_context *ctx, } if (IS_ENABLED(CONFIG_NET_VLAN) && - net_eth_is_vlan_enabled(ctx, net_pkt_iface(pkt)) && + net_eth_is_vlan_enabled(ctx, iface) && + net_pkt_vlan_tag(pkt) != NET_VLAN_TAG_UNSPEC && (IS_ENABLED(CONFIG_NET_GPTP_VLAN) || ptype != htons(NET_ETH_PTYPE_PTP))) { struct net_eth_vlan_hdr *hdr_vlan; @@ -747,20 +698,11 @@ static int ethernet_send(struct net_if *iface, struct net_pkt *pkt) net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_eth_addr); } - if (IS_ENABLED(CONFIG_NET_VLAN) && - net_eth_is_vlan_enabled(ctx, iface) && - (IS_ENABLED(CONFIG_NET_GPTP_VLAN) || ptype != htons(NET_ETH_PTYPE_PTP))) { - if (set_vlan_tag(ctx, iface, pkt) == NET_DROP) { - ret = -EINVAL; - goto arp_error; - } - - set_vlan_priority(ctx, pkt); - } - - /* Then set the ethernet header. + /* Then set the ethernet header. Note that the iface parameter tells + * where we are actually sending the packet. The interface in net_pkt + * is used to determine if the VLAN header is added to Ethernet frame. */ - if (!ethernet_fill_header(ctx, pkt, ptype)) { + if (!ethernet_fill_header(ctx, iface, pkt, ptype)) { ret = -ENOMEM; goto arp_error; } @@ -834,287 +776,6 @@ enum net_l2_flags ethernet_flags(struct net_if *iface) return ctx->ethernet_l2_flags; } -#if defined(CONFIG_NET_VLAN) -struct net_if *net_eth_get_vlan_iface(struct net_if *iface, uint16_t tag) -{ - struct ethernet_context *ctx = net_if_l2_data(iface); - struct net_if *first_non_vlan_iface = NULL; - int i; - - for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) { - if (ctx->vlan[i].tag == NET_VLAN_TAG_UNSPEC) { - if (!first_non_vlan_iface) { - first_non_vlan_iface = ctx->vlan[i].iface; - } - - continue; - } - - if (ctx->vlan[i].tag != tag) { - continue; - } - - NET_DBG("[%d] vlan tag %d -> iface %p", i, tag, - ctx->vlan[i].iface); - - return ctx->vlan[i].iface; - } - - return first_non_vlan_iface; -} - -static bool enable_vlan_iface(struct ethernet_context *ctx, - struct net_if *iface) -{ - int iface_idx = net_if_get_by_iface(iface); - - if (iface_idx < 0) { - return false; - } - - atomic_set_bit(ctx->interfaces, iface_idx); - - return true; -} - -static bool disable_vlan_iface(struct ethernet_context *ctx, - struct net_if *iface) -{ - int iface_idx = net_if_get_by_iface(iface); - - if (iface_idx < 0) { - return false; - } - - atomic_clear_bit(ctx->interfaces, iface_idx); - - return true; -} - -static bool is_vlan_enabled_for_iface(struct ethernet_context *ctx, - struct net_if *iface) -{ - int iface_idx = net_if_get_by_iface(iface); - - if (iface_idx < 0) { - return false; - } - - return !!atomic_test_bit(ctx->interfaces, iface_idx); -} - -bool net_eth_is_vlan_enabled(struct ethernet_context *ctx, - struct net_if *iface) -{ - if (ctx->vlan_enabled) { - if (ctx->vlan_enabled == NET_VLAN_MAX_COUNT) { - /* All network interface are using VLAN, no need - * to check further. - */ - return true; - } - - if (is_vlan_enabled_for_iface(ctx, iface)) { - return true; - } - } - - return false; -} - -uint16_t net_eth_get_vlan_tag(struct net_if *iface) -{ - struct ethernet_context *ctx = net_if_l2_data(iface); - int i; - - for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) { - if (ctx->vlan[i].iface == iface) { - return ctx->vlan[i].tag; - } - } - - return NET_VLAN_TAG_UNSPEC; -} - -bool net_eth_get_vlan_status(struct net_if *iface) -{ - struct ethernet_context *ctx = net_if_l2_data(iface); - - if (ctx->vlan_enabled && - net_eth_get_vlan_tag(iface) != NET_VLAN_TAG_UNSPEC) { - return true; - } - - return false; -} - -static struct ethernet_vlan *get_vlan(struct ethernet_context *ctx, - struct net_if *iface, - uint16_t vlan_tag) -{ - int i; - - for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) { - if (ctx->vlan[i].iface == iface && - ctx->vlan[i].tag == vlan_tag) { - return &ctx->vlan[i]; - } - } - - return NULL; -} - -static void setup_ipv6_link_local_addr(struct net_if *iface) -{ - struct net_linkaddr link_addr; - struct net_if_addr *ifaddr; - struct in6_addr addr; - uint32_t entropy; - uint8_t mac_addr[6]; - - entropy = sys_rand32_get(); - mac_addr[0] = entropy >> 0; - mac_addr[1] = entropy >> 8; - mac_addr[2] = entropy >> 16; - - entropy = sys_rand32_get(); - mac_addr[3] = entropy >> 0; - mac_addr[4] = entropy >> 8; - mac_addr[5] = entropy >> 16; - - mac_addr[0] |= 0x02; /* force LAA bit */ - - link_addr.len = sizeof(mac_addr); - link_addr.type = NET_LINK_ETHERNET; - link_addr.addr = mac_addr; - - net_ipv6_addr_create_iid(&addr, &link_addr); - - ifaddr = net_if_ipv6_addr_add(iface, &addr, NET_ADDR_AUTOCONF, 0); - if (!ifaddr) { - NET_DBG("Cannot add %s address to VLAN interface %p", - net_sprint_ipv6_addr(&addr), iface); - } -} - -int net_eth_vlan_enable(struct net_if *iface, uint16_t tag) -{ - struct ethernet_context *ctx = net_if_l2_data(iface); - const struct ethernet_api *eth = - net_if_get_device(iface)->api; - struct ethernet_vlan *vlan; - int i; - - if (!eth) { - return -ENOENT; - } - - if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) { - return -EINVAL; - } - - if (!ctx->is_init) { - return -EPERM; - } - - if (tag >= NET_VLAN_TAG_UNSPEC) { - return -EBADF; - } - - vlan = get_vlan(ctx, iface, tag); - if (vlan) { - return -EALREADY; - } - - for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) { - if (ctx->vlan[i].iface != iface) { - continue; - } - - if (ctx->vlan[i].tag != NET_VLAN_TAG_UNSPEC) { - continue; - } - - NET_DBG("[%d] Adding vlan tag %d to iface %p", i, tag, iface); - - ctx->vlan[i].tag = tag; - - /* Add a link local IPv6 address to VLAN interface here. - * Each network interface needs LL address, but as there is - * only one link (MAC) address defined for all the master and - * slave interfaces, the VLAN interface might be left without - * a LL address. In order to solve this issue, we create a - * random LL address and set it to the VLAN network interface. - */ - if (IS_ENABLED(CONFIG_NET_IPV6)) { - setup_ipv6_link_local_addr(iface); - } - - enable_vlan_iface(ctx, iface); - - if (eth->vlan_setup) { - eth->vlan_setup(net_if_get_device(iface), - iface, tag, true); - } - - ctx->vlan_enabled++; - if (ctx->vlan_enabled > NET_VLAN_MAX_COUNT) { - ctx->vlan_enabled = NET_VLAN_MAX_COUNT; - } - - ethernet_mgmt_raise_vlan_enabled_event(iface, tag); - - return 0; - } - - return -ENOSPC; -} - -int net_eth_vlan_disable(struct net_if *iface, uint16_t tag) -{ - struct ethernet_context *ctx = net_if_l2_data(iface); - const struct ethernet_api *eth = - net_if_get_device(iface)->api; - struct ethernet_vlan *vlan; - - if (!eth) { - return -ENOENT; - } - - if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) { - return -EINVAL; - } - - if (tag == NET_VLAN_TAG_UNSPEC) { - return -EBADF; - } - - vlan = get_vlan(ctx, iface, tag); - if (!vlan) { - return -ESRCH; - } - - NET_DBG("Removing vlan tag %d from iface %p", vlan->tag, vlan->iface); - - vlan->tag = NET_VLAN_TAG_UNSPEC; - - disable_vlan_iface(ctx, iface); - - if (eth->vlan_setup) { - eth->vlan_setup(net_if_get_device(iface), iface, tag, false); - } - - ethernet_mgmt_raise_vlan_disabled_event(iface, tag); - - ctx->vlan_enabled--; - if (ctx->vlan_enabled < 0) { - ctx->vlan_enabled = 0; - } - - return 0; -} -#endif /* CONFIG_NET_VLAN */ - NET_L2_INIT(ETHERNET_L2, ethernet_recv, ethernet_send, ethernet_enable, ethernet_flags); @@ -1271,10 +932,6 @@ void ethernet_init(struct net_if *iface) { struct ethernet_context *ctx = net_if_l2_data(iface); -#if defined(CONFIG_NET_VLAN) - int i; -#endif - NET_DBG("Initializing Ethernet L2 %p for iface %p", ctx, iface); ctx->ethernet_l2_flags = NET_L2_MULTICAST; @@ -1291,26 +948,6 @@ void ethernet_init(struct net_if *iface) } #endif -#if defined(CONFIG_NET_VLAN) - if (!(net_eth_get_hw_capabilities(iface) & ETHERNET_HW_VLAN)) { - return; - } - - for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) { - if (!ctx->vlan[i].iface) { - NET_DBG("[%d] alloc ctx %p iface %p", i, ctx, iface); - ctx->vlan[i].tag = NET_VLAN_TAG_UNSPEC; - ctx->vlan[i].iface = iface; - - if (!ctx->is_init) { - atomic_clear(ctx->interfaces); - } - - break; - } - } -#endif - net_arp_init(); ctx->is_init = true; diff --git a/subsys/net/l2/ethernet/vlan.c b/subsys/net/l2/ethernet/vlan.c new file mode 100644 index 00000000000..844a6bbd2ef --- /dev/null +++ b/subsys/net/l2/ethernet/vlan.c @@ -0,0 +1,640 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(net_ethernet_vlan, CONFIG_NET_L2_ETHERNET_LOG_LEVEL); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "net_private.h" + +#if defined(CONFIG_NET_VLAN_TXRX_DEBUG) +#define DEBUG_TX 1 +#define DEBUG_RX 1 +#else +#define DEBUG_TX 0 +#define DEBUG_RX 0 +#endif + +#define MAX_VLAN_NAME_LEN MIN(sizeof("VLAN-<#####>"), \ + CONFIG_NET_INTERFACE_NAME_LEN) +#define MAX_VIRT_NAME_LEN MIN(sizeof(""), \ + CONFIG_NET_L2_VIRTUAL_MAX_NAME_LEN) + +static void vlan_iface_init(struct net_if *iface); +static int vlan_interface_attach(struct net_if *vlan_iface, + struct net_if *iface); +static enum net_verdict vlan_interface_recv(struct net_if *iface, + struct net_pkt *pkt); +static int vlan_interface_send(struct net_if *iface, struct net_pkt *pkt); +static int vlan_interface_stop(const struct device *dev); +static enum virtual_interface_caps vlan_get_capabilities(struct net_if *iface); +static int vlan_interface_start(const struct device *dev); +static int virt_dev_init(const struct device *dev); + +static K_MUTEX_DEFINE(lock); + +struct vlan_context { + struct net_if *iface; + struct net_if *attached_to; + uint16_t tag; + bool status : 1; /* Is the interface enabled or not */ + bool is_used : 1; /* Is there active config on this context */ + bool init_done : 1; /* Is interface init called for this context */ +}; + +static const struct virtual_interface_api vlan_iface_api = { + .iface_api.init = vlan_iface_init, + + .get_capabilities = vlan_get_capabilities, + .start = vlan_interface_start, + .stop = vlan_interface_stop, + .send = vlan_interface_send, + .recv = vlan_interface_recv, + .attach = vlan_interface_attach, +}; + +#define ETH_DEFINE_VLAN(x, _) \ + static struct vlan_context vlan_context_data_##x = { \ + .tag = NET_VLAN_TAG_UNSPEC, \ + }; \ + NET_VIRTUAL_INTERFACE_INIT_INSTANCE(vlan_##x, \ + "VLAN_" #x, \ + x, \ + virt_dev_init, \ + NULL, \ + &vlan_context_data_##x, \ + NULL, /* config */ \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &vlan_iface_api, \ + NET_ETH_MTU) + +LISTIFY(CONFIG_NET_VLAN_COUNT, ETH_DEFINE_VLAN, (;), _); + +#define INIT_VLAN_CONTEXT_PTR(x, _) \ + [x] = &vlan_context_data_##x \ + +static struct vlan_context *vlan_ctx[] = { + LISTIFY(CONFIG_NET_VLAN_COUNT, INIT_VLAN_CONTEXT_PTR, (,), _) +}; + +#define INIT_VLAN_CONTEXT_IFACE(x, _) \ + vlan_context_data_##x.iface = NET_IF_GET(vlan_##x, x) + +static void init_context_iface(void) +{ + static bool init_done; + + if (init_done) { + return; + } + + init_done = true; + + LISTIFY(CONFIG_NET_VLAN_COUNT, INIT_VLAN_CONTEXT_IFACE, (;), _); +} + +static int virt_dev_init(const struct device *dev) +{ + ARG_UNUSED(dev); + + init_context_iface(); + + return 0; +} + +static struct vlan_context *get_vlan_ctx(struct net_if *main_iface, + uint16_t vlan_tag, + bool any_tag) +{ + struct virtual_interface_context *vctx, *tmp; + sys_slist_t *interfaces; + struct vlan_context *ctx; + + interfaces = &main_iface->config.virtual_interfaces; + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, vctx, tmp, node) { + enum virtual_interface_caps caps; + + if (vctx->virtual_iface == NULL) { + continue; + } + + caps = net_virtual_get_iface_capabilities(vctx->virtual_iface); + if (!(caps & VIRTUAL_INTERFACE_VLAN)) { + continue; + } + + ctx = net_if_get_device(vctx->virtual_iface)->data; + NET_ASSERT(vctx != NULL); + + if (any_tag) { + if (ctx->tag != NET_VLAN_TAG_UNSPEC) { + return ctx; + } + } else { + if ((vlan_tag == NET_VLAN_TAG_UNSPEC || + vlan_tag == ctx->tag)) { + return ctx; + } + } + } + + return NULL; +} + +static struct vlan_context *get_vlan(struct net_if *iface, + uint16_t vlan_tag) +{ + struct vlan_context *ctx = NULL; + + k_mutex_lock(&lock, K_FOREVER); + + /* If the interface is NULL, then get the VLAN that has the tag */ + if (iface == NULL) { + ARRAY_FOR_EACH(vlan_ctx, i) { + if (vlan_ctx[i] == NULL || !vlan_ctx[i]->is_used) { + continue; + } + + if (vlan_tag == vlan_ctx[i]->tag) { + ctx = vlan_ctx[i]; + break; + } + } + + goto out; + } + + /* If the interface is the main Ethernet one, then we only need + * to go through its attached virtual interfaces. + */ + if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) { + + ctx = get_vlan_ctx(iface, vlan_tag, false); + goto out; + + } + + if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) { + goto out; + } + + /* If the interface is virtual, then it should be be the VLAN one. + * Just get the Ethernet interface it points to to get the context. + */ + ctx = get_vlan_ctx(net_virtual_get_iface(iface), vlan_tag, false); + +out: + k_mutex_unlock(&lock); + + return ctx; +} + +static void set_priority(struct net_pkt *pkt) +{ + uint8_t vlan_priority; + + vlan_priority = net_priority2vlan(net_pkt_priority(pkt)); + net_pkt_set_vlan_priority(pkt, vlan_priority); +} + +struct net_if *net_eth_get_vlan_iface(struct net_if *iface, uint16_t tag) +{ + struct vlan_context *ctx; + + ctx = get_vlan(iface, tag); + if (ctx == NULL) { + return NULL; + } + + return ctx->iface; +} + +struct net_if *net_eth_get_vlan_main(struct net_if *iface) +{ + struct vlan_context *ctx; + + ctx = get_vlan(iface, NET_VLAN_TAG_UNSPEC); + if (ctx == NULL) { + return NULL; + } + + return ctx->attached_to; +} + +static bool enable_vlan_iface(struct vlan_context *ctx, + struct net_if *iface) +{ + int iface_idx = net_if_get_by_iface(iface); + char name[MAX(MAX_VLAN_NAME_LEN, MAX_VIRT_NAME_LEN)]; + int ret; + + if (iface_idx < 0) { + return false; + } + + ret = net_virtual_interface_attach(ctx->iface, iface); + if (ret < 0) { + NET_DBG("Cannot attach iface %d to %d", + net_if_get_by_iface(ctx->iface), + net_if_get_by_iface(ctx->attached_to)); + return false; + } + + ctx->is_used = true; + + snprintk(name, sizeof(name), "VLAN-%d", ctx->tag); + net_if_set_name(ctx->iface, name); + + snprintk(name, sizeof(name), "VLAN to %d", + net_if_get_by_iface(ctx->attached_to)); + net_virtual_set_name(ctx->iface, name); + + return true; +} + +static bool disable_vlan_iface(struct vlan_context *ctx, + struct net_if *iface) +{ + int iface_idx = net_if_get_by_iface(iface); + char name[MAX(MAX_VLAN_NAME_LEN, MAX_VIRT_NAME_LEN)]; + + if (iface_idx < 0) { + return false; + } + + (void)net_virtual_interface_attach(iface, NULL); + ctx->is_used = false; + + snprintk(name, sizeof(name), "VLAN-"); + net_if_set_name(iface, name); + + snprintk(name, sizeof(name), ""); + net_virtual_set_name(iface, name); + + return true; +} + +static bool is_vlan_enabled_for_iface(struct net_if *iface) +{ + int iface_idx = net_if_get_by_iface(iface); + struct vlan_context *ctx; + bool ret = false; + + if (iface_idx < 0) { + return false; + } + + k_mutex_lock(&lock, K_FOREVER); + + ctx = get_vlan_ctx(iface, NET_VLAN_TAG_UNSPEC, true); + ret = (ctx != NULL); + + k_mutex_unlock(&lock); + + return ret; +} + +bool net_eth_is_vlan_enabled(struct ethernet_context *ctx, + struct net_if *iface) +{ + ARG_UNUSED(ctx); + + return is_vlan_enabled_for_iface(iface); +} + +uint16_t net_eth_get_vlan_tag(struct net_if *iface) +{ + uint16_t tag = NET_VLAN_TAG_UNSPEC; + struct vlan_context *ctx; + + k_mutex_lock(&lock, K_FOREVER); + + ctx = get_vlan_ctx(iface, tag, true); + if (ctx != NULL) { + /* The Ethernet interface does not have a tag so if user + * tried to use the main interface, then do not return + * the tag. + */ + if (ctx->attached_to != iface) { + tag = ctx->tag; + } + } + + k_mutex_unlock(&lock); + + return tag; +} + +bool net_eth_get_vlan_status(struct net_if *iface) +{ + bool status = false; + struct vlan_context *ctx; + + k_mutex_lock(&lock, K_FOREVER); + + ctx = get_vlan_ctx(iface, NET_VLAN_TAG_UNSPEC, true); + if (ctx != NULL) { + status = ctx->status; + } + + k_mutex_unlock(&lock); + + return status; +} + +static void setup_link_address(struct vlan_context *ctx) +{ + struct net_linkaddr *ll_addr; + + ll_addr = net_if_get_link_addr(ctx->attached_to); + + (void)net_if_set_link_addr(ctx->iface, + ll_addr->addr, + ll_addr->len, + ll_addr->type); +} + +int net_eth_vlan_enable(struct net_if *iface, uint16_t tag) +{ + struct ethernet_context *ctx = net_if_l2_data(iface); + const struct ethernet_api *eth = net_if_get_device(iface)->api; + struct vlan_context *vlan; + int ret; + + if (!eth) { + return -ENOENT; + } + + if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) { + return -EINVAL; + } + + if (!(net_eth_get_hw_capabilities(iface) & ETHERNET_HW_VLAN)) { + NET_DBG("Interface %d does not support VLAN", + net_if_get_by_iface(iface)); + return -ENOTSUP; + } + + if (!ctx->is_init) { + return -EPERM; + } + + if (tag >= NET_VLAN_TAG_UNSPEC) { + return -EBADF; + } + + vlan = get_vlan(iface, tag); + if (vlan != NULL) { + return -EALREADY; + } + + /* This will make sure that the tag is not yet in use by some + * other interface. + */ + vlan = get_vlan(NULL, tag); + if (vlan != NULL) { + return -EALREADY; + } + + ret = -ENOSPC; + + k_mutex_lock(&lock, K_FOREVER); + + ARRAY_FOR_EACH(vlan_ctx, i) { + if (vlan_ctx[i] == NULL || vlan_ctx[i]->is_used) { + continue; + } + + vlan = vlan_ctx[i]; + vlan->tag = tag; + + if (!enable_vlan_iface(vlan, iface)) { + continue; + } + + NET_DBG("[%d] Adding vlan tag %d to iface %d (%p) attached to %d (%p)", + i, vlan->tag, net_if_get_by_iface(vlan->iface), vlan->iface, + net_if_get_by_iface(iface), iface); + + /* Use MAC address of the attached Ethernet interface so that + * packet reception works without any tweaks. + */ + setup_link_address(vlan); + + if (eth->vlan_setup) { + eth->vlan_setup(net_if_get_device(iface), + iface, vlan->tag, true); + } + + ethernet_mgmt_raise_vlan_enabled_event(vlan->iface, vlan->tag); + + ret = 0; + break; + } + + k_mutex_unlock(&lock); + + return ret; +} + +int net_eth_vlan_disable(struct net_if *iface, uint16_t tag) +{ + const struct ethernet_api *eth; + struct vlan_context *vlan; + + if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET) && + net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) { + return -EINVAL; + } + + if (tag == NET_VLAN_TAG_UNSPEC) { + return -EBADF; + } + + vlan = get_vlan(iface, tag); + if (!vlan) { + return -ESRCH; + } + + eth = net_if_get_device(vlan->attached_to)->api; + + k_mutex_lock(&lock, K_FOREVER); + + NET_DBG("Removing vlan tag %d from VLAN iface %d (%p) attached to %d (%p)", + vlan->tag, net_if_get_by_iface(vlan->iface), vlan->iface, + net_if_get_by_iface(vlan->attached_to), vlan->attached_to); + + vlan->tag = NET_VLAN_TAG_UNSPEC; + + if (eth->vlan_setup) { + eth->vlan_setup(net_if_get_device(vlan->attached_to), + vlan->attached_to, tag, false); + } + + ethernet_mgmt_raise_vlan_disabled_event(vlan->iface, tag); + + (void)disable_vlan_iface(vlan, vlan->iface); + + k_mutex_unlock(&lock); + + return 0; +} + +static enum virtual_interface_caps vlan_get_capabilities(struct net_if *iface) +{ + ARG_UNUSED(iface); + + return VIRTUAL_INTERFACE_VLAN; +} + +static int vlan_interface_start(const struct device *dev) +{ + struct vlan_context *ctx = dev->data; + + if (!ctx->is_used) { + NET_DBG("VLAN interface %d not configured yet.", + net_if_get_by_iface(ctx->iface)); + return -ENOENT; + } + + if (ctx->status) { + return -EALREADY; + } + + ctx->status = true; + + NET_DBG("Starting iface %d", net_if_get_by_iface(ctx->iface)); + + /* You can implement here any special action that is needed + * when the network interface is coming up. + */ + + return 0; +} + +static int vlan_interface_stop(const struct device *dev) +{ + struct vlan_context *ctx = dev->data; + + if (!ctx->is_used) { + NET_DBG("VLAN interface %d not configured yet.", + net_if_get_by_iface(ctx->iface)); + return -ENOENT; + } + + if (!ctx->status) { + return -EALREADY; + } + + ctx->status = false; + + NET_DBG("Stopping iface %d", net_if_get_by_iface(ctx->iface)); + + /* You can implement here any special action that is needed + * when the network interface is going down. + */ + + return 0; +} + +static int vlan_interface_send(struct net_if *iface, struct net_pkt *pkt) +{ + struct vlan_context *ctx = net_if_get_device(iface)->data; + + if (ctx->attached_to == NULL) { + return -ENOENT; + } + + net_pkt_set_vlan_tag(pkt, ctx->tag); + net_pkt_set_iface(pkt, ctx->attached_to); + set_priority(pkt); + + if (DEBUG_TX) { + char str[sizeof("TX iface xx (tag xxxx)")]; + + snprintk(str, sizeof(str), "TX iface %d (tag %d)", + net_if_get_by_iface(net_pkt_iface(pkt)), + ctx->tag); + + net_pkt_hexdump(pkt, str); + } + + return net_send_data(pkt); +} + +static enum net_verdict vlan_interface_recv(struct net_if *iface, + struct net_pkt *pkt) +{ + struct vlan_context *ctx = net_if_get_device(iface)->data; + + if (net_pkt_vlan_tag(pkt) != ctx->tag) { + return NET_CONTINUE; + } + + if (DEBUG_RX) { + char str[sizeof("RX iface xx (tag xxxx)")]; + + snprintk(str, sizeof(str), "RX iface %d (tag %d)", + net_pkt_vlan_tag(pkt), + net_if_get_by_iface(iface)); + + net_pkt_hexdump(pkt, str); + } + + return NET_OK; +} + +static int vlan_interface_attach(struct net_if *vlan_iface, + struct net_if *iface) +{ + struct vlan_context *ctx = net_if_get_device(vlan_iface)->data; + + if (iface == NULL) { + NET_DBG("VLAN interface %d (%p) detached from %d (%p)", + net_if_get_by_iface(vlan_iface), vlan_iface, + net_if_get_by_iface(ctx->attached_to), ctx->attached_to); + } else { + NET_DBG("VLAN interface %d (%p) attached to %d (%p)", + net_if_get_by_iface(vlan_iface), vlan_iface, + net_if_get_by_iface(iface), iface); + } + + ctx->attached_to = iface; + + return 0; +} + +static void vlan_iface_init(struct net_if *iface) +{ + struct vlan_context *ctx = net_if_get_device(iface)->data; + char name[MAX(MAX_VLAN_NAME_LEN, MAX_VIRT_NAME_LEN)]; + + if (ctx->init_done) { + return; + } + + ctx->iface = iface; + net_if_flag_set(iface, NET_IF_NO_AUTO_START); + + snprintk(name, sizeof(name), "VLAN-"); + net_if_set_name(iface, name); + + snprintk(name, sizeof(name), "not attached"); + net_virtual_set_name(iface, name); + + (void)net_virtual_set_flags(ctx->iface, NET_L2_MULTICAST); + + ctx->init_done = true; +} From 35b66b3e08c1022cd40c4b494966e7838ff1ec35 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 19 Mar 2024 15:29:52 +0200 Subject: [PATCH 15/18] [nrf fromtree] tests: net: vlan: Update because of VLAN changes Update the tests because of the VLAN overhaul. Signed-off-by: Jukka Rissanen (cherry picked from commit c28ca1ce1fa1f00ce0626f6c6b1eddc0e5f11371) --- tests/net/vlan/prj.conf | 9 +- tests/net/vlan/src/main.c | 395 +++++++++++++++++++++----------------- 2 files changed, 225 insertions(+), 179 deletions(-) diff --git a/tests/net/vlan/prj.conf b/tests/net/vlan/prj.conf index 48fa390cded..a36eb23780c 100644 --- a/tests/net/vlan/prj.conf +++ b/tests/net/vlan/prj.conf @@ -16,16 +16,17 @@ CONFIG_NET_PKT_TX_COUNT=15 CONFIG_NET_PKT_RX_COUNT=15 CONFIG_NET_BUF_RX_COUNT=15 CONFIG_NET_BUF_TX_COUNT=15 -CONFIG_NET_IF_MAX_IPV6_COUNT=6 +CONFIG_NET_IF_MAX_IPV6_COUNT=8 CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=6 CONFIG_NET_IPV6_ND=n CONFIG_NET_VLAN=y CONFIG_NET_VLAN_COUNT=4 CONFIG_NET_CONTEXT_PRIORITY=y +CONFIG_NET_SOCKETS=y CONFIG_ZTEST=y CONFIG_NET_CONFIG_SETTINGS=n CONFIG_NET_SHELL=n -# Disable internal ethernet drivers as the test is self contained -# and does not need the on board driver to function. -CONFIG_ETH_DRIVER=n +# The address check must be disabled so that packet +# is properly routed via Ethernet L2. +CONFIG_NET_IP_ADDR_CHECK=n diff --git a/tests/net/vlan/src/main.c b/tests/net/vlan/src/main.c index cfcab203193..5d53bfd4779 100644 --- a/tests/net/vlan/src/main.c +++ b/tests/net/vlan/src/main.c @@ -22,6 +22,7 @@ LOG_MODULE_REGISTER(net_test, NET_LOG_LEVEL); #include +#include #include #include #include @@ -30,6 +31,7 @@ LOG_MODULE_REGISTER(net_test, NET_LOG_LEVEL); #include #include "ipv6.h" +#include "../../socket/socket_helpers.h" #define NET_LOG_ENABLED 1 #include "net_private.h" @@ -40,7 +42,9 @@ LOG_MODULE_REGISTER(net_test, NET_LOG_LEVEL); #define DBG(fmt, ...) #endif -#define TEST_PORT 9999 +#define ANY_PORT 0 +#define SERVER_PORT 4242 +#define CLIENT_PORT 9898 #define VLAN_TAG_1 100 #define VLAN_TAG_2 200 @@ -48,7 +52,10 @@ LOG_MODULE_REGISTER(net_test, NET_LOG_LEVEL); #define VLAN_TAG_4 400 #define VLAN_TAG_5 500 -static char *test_data = "Test data to be sent"; +#define NET_ETH_MAX_COUNT 2 + +#define MY_IPV6_ADDR "2001:db8:200::2" +#define MY_IPV6_ADDR_SRV "2001:db8:200::1" /* Interface 1 addresses */ static struct in6_addr my_addr1 = { { { 0x20, 0x01, 0x0d, 0xb8, 1, 0, 0, 0, @@ -58,32 +65,39 @@ static struct in6_addr my_addr1 = { { { 0x20, 0x01, 0x0d, 0xb8, 1, 0, 0, 0, static struct in6_addr my_addr2 = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; -/* Interface 3 addresses */ +/* VLAN Interface 3 addresses */ static struct in6_addr my_addr3 = { { { 0x20, 0x01, 0x0d, 0xb8, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; -/* Destination address for test packets */ -static struct in6_addr dst_addr = { { { 0x20, 0x01, 0x0d, 0xb8, 9, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; - /* Extra address is assigned to ll_addr */ static struct in6_addr ll_addr = { { { 0xfe, 0x80, 0x43, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0xf2, 0xaa, 0x29, 0x02, 0x04 } } }; /* Keep track of all ethernet interfaces */ -static struct net_if *eth_interfaces[NET_VLAN_MAX_COUNT + 1]; +static struct net_if *eth_interfaces[NET_ETH_MAX_COUNT]; +static struct net_if *vlan_interfaces[NET_VLAN_MAX_COUNT]; static struct net_if *dummy_interfaces[2]; -static struct net_if *extra_eth; - -static struct net_context *udp_v6_ctx; static bool test_failed; static bool test_started; static K_SEM_DEFINE(wait_data, 0, UINT_MAX); -#define WAIT_TIME K_SECONDS(1) +#define BUF_AND_SIZE(buf) buf, sizeof(buf) - 1 +#define STRLEN(buf) (sizeof(buf) - 1) + +#define TEST_STR_SMALL "test" + +/* More than 256 bytes, to use >1 net_buf. */ +#define TEST_STR2 \ + "The Zephyr Project, a Linux Foundation hosted Collaboration " \ + "Project, is an open source collaborative effort uniting leaders " \ + "from across the industry to build a best-in-breed small, scalable, " \ + "real-time operating system (RTOS) optimized for resource-" \ + "constrained devices, across multiple architectures." + +#define WAIT_TIME K_MSEC(500) struct eth_context { struct net_if *iface; @@ -109,6 +123,7 @@ static void eth_vlan_iface_init(struct net_if *iface) static int eth_tx(const struct device *dev, struct net_pkt *pkt) { struct eth_context *context = dev->data; + int ret; zassert_equal_ptr(ð_vlan_context, context, "Context pointers do not match (%p vs %p)", @@ -125,7 +140,7 @@ static int eth_tx(const struct device *dev, struct net_pkt *pkt) zassert_equal(context->expecting_tag, net_pkt_vlan_tag(pkt), - "Invalid VLAN tag (%d vs %d) in TX pkt\n", + "Invalid VLAN tag (%d vs %d) in TX pkt", net_pkt_vlan_tag(pkt), context->expecting_tag); @@ -133,10 +148,15 @@ static int eth_tx(const struct device *dev, struct net_pkt *pkt) net_eth_vlan_get_vid(ntohs(hdr->vlan.tci)), "Invalid VLAN tag in ethernet header"); + k_sleep(K_MSEC(10)); k_sem_give(&wait_data); } - return 0; + ret = net_recv_data(net_pkt_iface(pkt), + net_pkt_clone(pkt, K_NO_WAIT)); + zassert_false(ret < 0, "Cannot receive data (%d)", ret); + + return ret; } static enum ethernet_hw_caps eth_capabilities(const struct device *dev) @@ -185,14 +205,9 @@ static int eth_init(const struct device *dev) return 0; } -/* Create one ethernet interface that does not have VLAN support. This - * is quite unlikely that this would be done in real life but for testing - * purposes create it here. - */ -NET_DEVICE_INIT(eth_test, "eth_test", eth_init, NULL, - ð_vlan_context, NULL, CONFIG_ETH_INIT_PRIORITY, - &api_funcs, ETHERNET_L2, NET_L2_GET_CTX_TYPE(ETHERNET_L2), - NET_ETH_MTU); +ETH_NET_DEVICE_INIT(eth_test, "eth_test", eth_init, NULL, + ð_vlan_context, NULL, CONFIG_ETH_INIT_PRIORITY, + &api_funcs, NET_ETH_MTU); struct net_if_test { uint8_t idx; /* not used for anything, just a dummy value */ @@ -204,15 +219,13 @@ static uint8_t *net_iface_get_mac(const struct device *dev) { struct net_if_test *data = dev->data; - if (data->mac_addr[2] == 0x00) { - /* 00-00-5E-00-53-xx Documentation RFC 7042 */ - data->mac_addr[0] = 0x00; - data->mac_addr[1] = 0x00; - data->mac_addr[2] = 0x5E; - data->mac_addr[3] = 0x00; - data->mac_addr[4] = 0x53; - data->mac_addr[5] = sys_rand32_get(); - } + /* 00-00-5E-00-53-xx Documentation RFC 7042 */ + data->mac_addr[0] = 0x00; + data->mac_addr[1] = 0x00; + data->mac_addr[2] = 0x5E; + data->mac_addr[3] = 0x00; + data->mac_addr[4] = 0x53; + data->mac_addr[5] = sys_rand32_get(); data->ll_addr.addr = data->mac_addr; data->ll_addr.len = 6U; @@ -272,6 +285,7 @@ NET_DEVICE_INIT_INSTANCE(net_iface2_test, struct user_data { int eth_if_count; + int vlan_if_count; int dummy_if_count; int total_if_count; }; @@ -303,14 +317,14 @@ static void iface_cb(struct net_if *iface, void *user_data) net_if_get_by_iface(iface)); if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) { - if (PART_OF_ARRAY(NET_IF_GET_NAME(eth_test, 0), iface)) { - if (!extra_eth) { - /* Just use the first interface */ - extra_eth = iface; - } - } else { - eth_interfaces[ud->eth_if_count++] = iface; - } + eth_interfaces[ud->eth_if_count++] = iface; + } + + if (net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) { + vlan_interfaces[ud->vlan_if_count++] = iface; + + zassert_true(ud->vlan_if_count <= NET_VLAN_MAX_COUNT, + "Too many VLAN interfaces"); } if (net_if_l2(iface) == &NET_L2_GET_NAME(DUMMY)) { @@ -329,25 +343,22 @@ static void iface_cb(struct net_if *iface, void *user_data) static void test_vlan_setup(void) { struct user_data ud = { 0 }; + int remaining; /* Make sure we have enough virtual interfaces */ net_if_foreach(iface_cb, &ud); /* One extra eth interface without vlan support */ - zassert_equal(ud.eth_if_count, NET_VLAN_MAX_COUNT, - "Invalid number of VLANs %d vs %d\n", - ud.eth_if_count, NET_VLAN_MAX_COUNT); - - zassert_equal(ud.total_if_count, NET_VLAN_MAX_COUNT + 1 + 2, - "Invalid number of interfaces"); - - /* Put the extra non-vlan ethernet interface to last */ - eth_interfaces[4] = extra_eth; - zassert_not_null(extra_eth, "Extra interface missing"); - zassert_equal_ptr(net_if_l2(extra_eth), &NET_L2_GET_NAME(ETHERNET), - "Invalid L2 type %p for iface %p (should be %p)\n", - net_if_l2(extra_eth), extra_eth, - &NET_L2_GET_NAME(ETHERNET)); + zassert_equal(ud.vlan_if_count, NET_VLAN_MAX_COUNT, + "Invalid number of VLANs %d vs %d", + ud.vlan_if_count, NET_VLAN_MAX_COUNT); + + remaining = ud.total_if_count - NET_VLAN_MAX_COUNT - + ud.eth_if_count - ud.dummy_if_count; + zassert_equal(remaining, 0, + "Invalid number of interfaces expecting %d got %d+%d+%d", + ud.total_if_count, NET_VLAN_MAX_COUNT, + ud.eth_if_count, ud.dummy_if_count); } static void test_address_setup(void) @@ -357,7 +368,7 @@ static void test_address_setup(void) iface1 = eth_interfaces[1]; /* This has VLAN enabled */ iface2 = eth_interfaces[0]; /* and this one not */ - iface3 = eth_interfaces[3]; /* and this one has VLAN enabled */ + iface3 = vlan_interfaces[0]; /* and this is the virtual VLAN interface */ zassert_not_null(iface1, "Interface 1"); zassert_not_null(iface2, "Interface 2"); @@ -402,9 +413,10 @@ static void test_address_setup(void) zassert_not_null(ifaddr, "addr3"); } + ifaddr->addr_state = NET_ADDR_PREFERRED; + net_if_up(iface1); net_if_up(iface2); - net_if_up(iface3); /* The interface might receive data which might fail the checks * in the iface sending function, so we need to reset the failure @@ -517,43 +529,51 @@ static void test_vlan_enable(void) struct net_if *iface; int ret; - ret = net_eth_vlan_enable(eth_interfaces[1], VLAN_TAG_1); - zassert_equal(ret, 0, "Cannot enable %d (%d)\n", VLAN_TAG_1, ret); - ret = net_eth_vlan_enable(eth_interfaces[3], VLAN_TAG_2); - zassert_equal(ret, 0, "Cannot enable %d (%d)\n", VLAN_TAG_2, ret); + ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_1); + zassert_equal(ret, 0, "Cannot enable %d (%d)", VLAN_TAG_1, ret); + ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_2); + zassert_equal(ret, 0, "Cannot enable %d (%d)", VLAN_TAG_2, ret); + ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_3); + zassert_equal(ret, 0, "Cannot enable %d (%d)", VLAN_TAG_3, ret); + ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_4); + zassert_equal(ret, 0, "Cannot enable %d (%d)", VLAN_TAG_4, ret); eth_ctx = net_if_l2_data(eth_interfaces[0]); iface = net_eth_get_vlan_iface(eth_interfaces[0], VLAN_TAG_1); - zassert_equal_ptr(iface, eth_interfaces[1], - "Invalid interface for tag %d (%p vs %p)\n", - VLAN_TAG_1, iface, eth_interfaces[1]); + zassert_equal_ptr(iface, vlan_interfaces[0], + "Invalid interface for tag %d (%p vs %p)", + VLAN_TAG_1, iface, vlan_interfaces[0]); iface = net_eth_get_vlan_iface(eth_interfaces[0], VLAN_TAG_2); - zassert_equal_ptr(iface, eth_interfaces[3], - "Invalid interface for tag %d (%p vs %p)\n", - VLAN_TAG_2, iface, eth_interfaces[3]); + zassert_equal_ptr(iface, vlan_interfaces[1], + "Invalid interface for tag %d (%p vs %p)", + VLAN_TAG_2, iface, vlan_interfaces[1]); - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[0]); - zassert_equal(ret, false, "VLAN enabled for interface 0"); + iface = net_eth_get_vlan_iface(eth_interfaces[0], VLAN_TAG_3); + zassert_equal_ptr(iface, vlan_interfaces[2], + "Invalid interface for tag %d (%p vs %p)", + VLAN_TAG_3, iface, vlan_interfaces[2]); - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[1]); - zassert_equal(ret, true, "VLAN disabled for interface 1"); + iface = net_eth_get_vlan_iface(eth_interfaces[0], VLAN_TAG_4); + zassert_equal_ptr(iface, vlan_interfaces[3], + "Invalid interface for tag %d (%p vs %p)", + VLAN_TAG_4, iface, vlan_interfaces[3]); - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[2]); - zassert_equal(ret, false, "VLAN enabled for interface 2"); + ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[0]); + zassert_equal(ret, true, "VLAN enabled for interface 0"); - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[3]); - zassert_equal(ret, true, "VLAN disabled for interface 3"); + ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[1]); + zassert_equal(ret, false, "VLAN enabled for interface 1"); iface = eth_interfaces[0]; ret = net_eth_vlan_enable(iface, NET_VLAN_TAG_UNSPEC); - zassert_equal(ret, -EBADF, "Invalid VLAN tag value %d\n", ret); + zassert_equal(ret, -EBADF, "Invalid VLAN tag value %d", ret); iface = eth_interfaces[1]; ret = net_eth_vlan_enable(iface, VLAN_TAG_1); - zassert_equal(ret, -EALREADY, "VLAN tag %d enabled for iface 1\n", - VLAN_TAG_1); + zassert_equal(ret, -EALREADY, "VLAN tag %d enabled for iface 1 (%d)", + VLAN_TAG_1, ret); } static void test_vlan_disable(void) @@ -563,21 +583,27 @@ static void test_vlan_disable(void) int ret; ret = net_eth_vlan_disable(eth_interfaces[1], VLAN_TAG_1); - zassert_equal(ret, 0, "Cannot disable %d (%d)\n", VLAN_TAG_1, ret); - ret = net_eth_vlan_disable(eth_interfaces[3], VLAN_TAG_2); - zassert_equal(ret, 0, "Cannot disable %d (%d)\n", VLAN_TAG_2, ret); + zassert_not_equal(ret, 0, "Could disable %d (%d)", VLAN_TAG_1, ret); + + ret = net_eth_vlan_disable(eth_interfaces[0], VLAN_TAG_1); + zassert_equal(ret, 0, "Cannot disable %d (%d)", VLAN_TAG_1, ret); + + ret = net_eth_vlan_disable(eth_interfaces[0], VLAN_TAG_2); + zassert_equal(ret, 0, "Cannot disable %d (%d)", VLAN_TAG_2, ret); + + ret = net_eth_vlan_disable(eth_interfaces[0], VLAN_TAG_3); + zassert_equal(ret, 0, "Cannot disable %d (%d)", VLAN_TAG_2, ret); + + ret = net_eth_vlan_disable(vlan_interfaces[3], VLAN_TAG_4); + zassert_equal(ret, 0, "Cannot disable %d (%d)", VLAN_TAG_2, ret); eth_ctx = net_if_l2_data(eth_interfaces[0]); iface = net_eth_get_vlan_iface(eth_interfaces[0], VLAN_TAG_1); - zassert_equal_ptr(iface, eth_interfaces[0], - "Invalid interface for tag %d (%p vs %p)\n", - VLAN_TAG_1, iface, eth_interfaces[0]); + zassert_equal_ptr(iface, NULL, "Valid interface for tag %d", VLAN_TAG_1); iface = net_eth_get_vlan_iface(eth_interfaces[0], VLAN_TAG_2); - zassert_equal_ptr(iface, eth_interfaces[0], - "Invalid interface for tag %d (%p vs %p)\n", - VLAN_TAG_2, iface, eth_interfaces[0]); + zassert_equal_ptr(iface, NULL, "Valid interface for tag %d", VLAN_TAG_2); ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[0]); zassert_equal(ret, false, "VLAN enabled for interface 0"); @@ -585,19 +611,13 @@ static void test_vlan_disable(void) ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[1]); zassert_equal(ret, false, "VLAN enabled for interface 1"); - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[2]); - zassert_equal(ret, false, "VLAN enabled for interface 2"); - - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[3]); - zassert_equal(ret, false, "VLAN enabled for interface 3"); - iface = eth_interfaces[0]; ret = net_eth_vlan_disable(iface, NET_VLAN_TAG_UNSPEC); - zassert_equal(ret, -EBADF, "Invalid VLAN tag value %d\n", ret); + zassert_equal(ret, -EBADF, "Invalid VLAN tag value %d", ret); iface = eth_interfaces[1]; ret = net_eth_vlan_disable(iface, VLAN_TAG_1); - zassert_equal(ret, -ESRCH, "VLAN tag %d disabled for iface 1\n", + zassert_equal(ret, -ESRCH, "VLAN tag %d disabled for iface 1", VLAN_TAG_1); } @@ -608,27 +628,27 @@ static void test_vlan_enable_all(void) int ret; ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_1); - zassert_equal(ret, 0, "Cannot enable %d\n", VLAN_TAG_1); - ret = net_eth_vlan_enable(eth_interfaces[1], VLAN_TAG_2); - zassert_equal(ret, 0, "Cannot enable %d\n", VLAN_TAG_2); - ret = net_eth_vlan_enable(eth_interfaces[2], VLAN_TAG_3); - zassert_equal(ret, 0, "Cannot enable %d\n", VLAN_TAG_3); - ret = net_eth_vlan_enable(eth_interfaces[3], VLAN_TAG_4); - zassert_equal(ret, 0, "Cannot enable %d\n", VLAN_TAG_4); + zassert_equal(ret, 0, "Cannot enable %d", VLAN_TAG_1); + ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_2); + zassert_equal(ret, 0, "Cannot enable %d", VLAN_TAG_2); + ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_3); + zassert_equal(ret, 0, "Cannot enable %d", VLAN_TAG_3); + ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_4); + zassert_equal(ret, 0, "Cannot enable %d", VLAN_TAG_4); eth_ctx = net_if_l2_data(eth_interfaces[0]); ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[0]); zassert_equal(ret, true, "VLAN disabled for interface 0"); - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[1]); - zassert_equal(ret, true, "VLAN disabled for interface 1"); + ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[0]); + zassert_equal(ret, true, "VLAN disabled for interface 0"); - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[2]); - zassert_equal(ret, true, "VLAN disabled for interface 2"); + ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[0]); + zassert_equal(ret, true, "VLAN disabled for interface 0"); - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[3]); - zassert_equal(ret, true, "VLAN disabled for interface 3"); + ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[0]); + zassert_equal(ret, true, "VLAN disabled for interface 0"); iface = net_if_get_first_by_type(&NET_L2_GET_NAME(DUMMY)); zassert_not_null(iface, "No dummy iface found"); @@ -637,7 +657,7 @@ static void test_vlan_enable_all(void) "Not a dummy interface"); ret = net_eth_vlan_enable(iface, VLAN_TAG_5); - zassert_equal(ret, -EINVAL, "Wrong iface type (%d)\n", ret); + zassert_equal(ret, -EINVAL, "Wrong iface type (%d)", ret); } static void test_vlan_disable_all(void) @@ -647,26 +667,26 @@ static void test_vlan_disable_all(void) int ret; ret = net_eth_vlan_disable(eth_interfaces[0], VLAN_TAG_1); - zassert_equal(ret, 0, "Cannot disable %d\n", VLAN_TAG_1); - ret = net_eth_vlan_disable(eth_interfaces[1], VLAN_TAG_2); - zassert_equal(ret, 0, "Cannot disable %d\n", VLAN_TAG_2); - ret = net_eth_vlan_disable(eth_interfaces[2], VLAN_TAG_3); - zassert_equal(ret, 0, "Cannot disable %d\n", VLAN_TAG_3); - ret = net_eth_vlan_disable(eth_interfaces[3], VLAN_TAG_4); - zassert_equal(ret, 0, "Cannot disable %d\n", VLAN_TAG_4); + zassert_equal(ret, 0, "Cannot disable %d", VLAN_TAG_1); + ret = net_eth_vlan_disable(eth_interfaces[0], VLAN_TAG_2); + zassert_equal(ret, 0, "Cannot disable %d", VLAN_TAG_2); + ret = net_eth_vlan_disable(eth_interfaces[0], VLAN_TAG_3); + zassert_equal(ret, 0, "Cannot disable %d", VLAN_TAG_3); + ret = net_eth_vlan_disable(eth_interfaces[0], VLAN_TAG_4); + zassert_equal(ret, 0, "Cannot disable %d", VLAN_TAG_4); eth_ctx = net_if_l2_data(eth_interfaces[0]); - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[0]); + ret = net_eth_is_vlan_enabled(eth_ctx, vlan_interfaces[0]); zassert_equal(ret, false, "VLAN enabled for interface 0"); - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[1]); + ret = net_eth_is_vlan_enabled(eth_ctx, vlan_interfaces[1]); zassert_equal(ret, false, "VLAN enabled for interface 1"); - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[2]); + ret = net_eth_is_vlan_enabled(eth_ctx, vlan_interfaces[2]); zassert_equal(ret, false, "VLAN enabled for interface 2"); - ret = net_eth_is_vlan_enabled(eth_ctx, eth_interfaces[3]); + ret = net_eth_is_vlan_enabled(eth_ctx, vlan_interfaces[3]); zassert_equal(ret, false, "VLAN enabled for interface 3"); iface = net_if_get_first_by_type(&NET_L2_GET_NAME(DUMMY)); @@ -676,27 +696,17 @@ static void test_vlan_disable_all(void) "Not a dummy interface"); ret = net_eth_vlan_disable(iface, VLAN_TAG_5); - zassert_equal(ret, -EINVAL, "Wrong iface type (%d)\n", ret); + zassert_equal(ret, -EINVAL, "Wrong iface type (%d)", ret); } static bool add_neighbor(struct net_if *iface, struct in6_addr *addr) { - struct net_linkaddr_storage llstorage; - struct net_linkaddr lladdr; + struct net_linkaddr *ll_addr; struct net_nbr *nbr; - llstorage.addr[0] = 0x01; - llstorage.addr[1] = 0x02; - llstorage.addr[2] = 0x33; - llstorage.addr[3] = 0x44; - llstorage.addr[4] = 0x05; - llstorage.addr[5] = 0x06; - - lladdr.len = 6U; - lladdr.addr = llstorage.addr; - lladdr.type = NET_LINK_ETHERNET; + ll_addr = net_if_get_link_addr(iface); - nbr = net_ipv6_nbr_add(iface, addr, &lladdr, false, + nbr = net_ipv6_nbr_add(iface, addr, ll_addr, false, NET_IPV6_NBR_STATE_REACHABLE); if (!nbr) { DBG("Cannot add dst %s to neighbor cache\n", @@ -704,69 +714,104 @@ static bool add_neighbor(struct net_if *iface, struct in6_addr *addr) return false; } + DBG("Adding dst %s as [%s] to nbr cache\n", + net_sprint_ipv6_addr(addr), + net_sprint_ll_addr(ll_addr->addr, 6)); + return true; } -ZTEST(net_vlan, test_vlan_send_data) +static void comm_sendto_recvfrom(int client_sock, + struct sockaddr *client_addr, + socklen_t client_addrlen, + int server_sock, + struct sockaddr *server_addr, + socklen_t server_addrlen) +{ + ssize_t sent = 0; + + ARG_UNUSED(server_sock); + ARG_UNUSED(client_addr); + ARG_UNUSED(client_addrlen); + + zassert_not_null(client_addr, "null client addr"); + zassert_not_null(server_addr, "null server addr"); + + /* + * Test client -> server sending + */ + + sent = zsock_sendto(client_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), + 0, server_addr, server_addrlen); + zassert_equal(sent, strlen(TEST_STR_SMALL), "sendto failed (%d vs %d)", + sent, strlen(TEST_STR_SMALL)); + + if (k_sem_take(&wait_data, WAIT_TIME)) { + DBG("Timeout while waiting interface data\n"); + zassert_false(true, "Timeout"); + } + + /* The data verification cannot be done easily because we are in the + * same link and our send/recv MAC addresses are the same and the + * packet is dropped by core stack. + * We cannot use loopback as then VLAN would not be used. + * One option would be to make a fully functional network but that is + * out of scope for this test application (at least for now). + * So do not try to verify data, just check the received tag value. + */ +} + +ZTEST(net_vlan, test_vlan_ipv6_sendto_recvfrom) { - struct ethernet_context *eth_ctx; /* This is L2 context */ - struct eth_context *ctx; /* This is interface context */ struct net_if *iface; int ret; - struct sockaddr_in6 dst_addr6 = { - .sin6_family = AF_INET6, - .sin6_port = htons(TEST_PORT), - }; - struct sockaddr_in6 src_addr6 = { - .sin6_family = AF_INET6, - .sin6_port = 0, - }; + int client_sock; + int server_sock; + struct sockaddr_in6 client_addr; + struct sockaddr_in6 server_addr; + struct eth_context *ctx; /* Setup the interfaces */ test_vlan_enable(); + test_vlan_disable_all(); - ret = net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, - &udp_v6_ctx); - zassert_equal(ret, 0, "Create IPv6 UDP context failed"); - - memcpy(&src_addr6.sin6_addr, &my_addr1, sizeof(struct in6_addr)); - memcpy(&dst_addr6.sin6_addr, &dst_addr, sizeof(struct in6_addr)); - - ret = net_context_bind(udp_v6_ctx, (struct sockaddr *)&src_addr6, - sizeof(struct sockaddr_in6)); - zassert_equal(ret, 0, "Context bind failure test failed"); + ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_1); + zassert_equal(ret, 0, "Could not enable %d (%d)", VLAN_TAG_1, ret); - iface = eth_interfaces[1]; /* This is the VLAN interface */ - ctx = net_if_get_device(iface)->data; - eth_ctx = net_if_l2_data(iface); - ret = net_eth_is_vlan_enabled(eth_ctx, iface); - zassert_equal(ret, true, "VLAN disabled for interface 1"); + iface = net_eth_get_vlan_iface(eth_interfaces[0], VLAN_TAG_1); + ret = net_eth_is_vlan_enabled(NULL, eth_interfaces[0]); + zassert_equal(ret, true, "VLAN not enabled for interface 1"); + ctx = net_if_get_device(eth_interfaces[0])->data; ctx->expecting_tag = VLAN_TAG_1; - iface = eth_interfaces[3]; /* This is also VLAN interface */ - ctx = net_if_get_device(iface)->data; - eth_ctx = net_if_l2_data(iface); - ret = net_eth_is_vlan_enabled(eth_ctx, iface); - zassert_equal(ret, true, "VLAN disabled for interface 1"); + net_if_up(eth_interfaces[0]); + net_if_up(vlan_interfaces[0]); - test_started = true; + prepare_sock_udp_v6(MY_IPV6_ADDR, ANY_PORT, &client_sock, &client_addr); + prepare_sock_udp_v6(MY_IPV6_ADDR_SRV, SERVER_PORT, &server_sock, &server_addr); - ret = add_neighbor(iface, &dst_addr); + ret = add_neighbor(iface, &server_addr.sin6_addr); zassert_true(ret, "Cannot add neighbor"); - ret = net_context_sendto(udp_v6_ctx, test_data, strlen(test_data), - (struct sockaddr *)&dst_addr6, - sizeof(struct sockaddr_in6), - NULL, K_NO_WAIT, NULL); - zassert_true(ret > 0, "Send UDP pkt failed"); + ret = bind(server_sock, + (struct sockaddr *)&server_addr, + sizeof(server_addr)); + zassert_equal(ret, 0, "bind failed"); - if (k_sem_take(&wait_data, WAIT_TIME)) { - DBG("Timeout while waiting interface data\n"); - zassert_false(true, "Timeout"); - } + test_started = true; - net_context_unref(udp_v6_ctx); + comm_sendto_recvfrom(client_sock, + (struct sockaddr *)&client_addr, + sizeof(client_addr), + server_sock, + (struct sockaddr *)&server_addr, + sizeof(server_addr)); + + ret = close(client_sock); + zassert_equal(ret, 0, "close failed"); + ret = close(server_sock); + zassert_equal(ret, 0, "close failed"); } static void *setup(void) From 99aa25e1384b76351bf78042a359b0c7d55be25c Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 10 Apr 2024 11:26:15 +0300 Subject: [PATCH 16/18] [nrf fromtree] net: vlan: Fix net_eth_get_vlan_tag() to check correct interface The network interface parameter for net_eth_get_vlan_tag() should be the VLAN interface so use the search loop properly. Earlier the main interface could be checked. Add also test cases for this so that we can catch that the func works properly. Signed-off-by: Jukka Rissanen (cherry picked from commit d40abe8c0f880c53bdfb759895c2a5c910892646) --- subsys/net/l2/ethernet/vlan.c | 17 ++++++++--------- tests/net/vlan/src/main.c | 25 +++++++++++++++++++++---- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/subsys/net/l2/ethernet/vlan.c b/subsys/net/l2/ethernet/vlan.c index 844a6bbd2ef..9a6d660aeb6 100644 --- a/subsys/net/l2/ethernet/vlan.c +++ b/subsys/net/l2/ethernet/vlan.c @@ -317,18 +317,17 @@ bool net_eth_is_vlan_enabled(struct ethernet_context *ctx, uint16_t net_eth_get_vlan_tag(struct net_if *iface) { uint16_t tag = NET_VLAN_TAG_UNSPEC; - struct vlan_context *ctx; k_mutex_lock(&lock, K_FOREVER); - ctx = get_vlan_ctx(iface, tag, true); - if (ctx != NULL) { - /* The Ethernet interface does not have a tag so if user - * tried to use the main interface, then do not return - * the tag. - */ - if (ctx->attached_to != iface) { - tag = ctx->tag; + ARRAY_FOR_EACH(vlan_ctx, i) { + if (vlan_ctx[i] == NULL || !vlan_ctx[i]->is_used) { + continue; + } + + if (vlan_ctx[i]->iface == iface) { + tag = vlan_ctx[i]->tag; + break; } } diff --git a/tests/net/vlan/src/main.c b/tests/net/vlan/src/main.c index 5d53bfd4779..6cf6c07ffc1 100644 --- a/tests/net/vlan/src/main.c +++ b/tests/net/vlan/src/main.c @@ -574,6 +574,23 @@ static void test_vlan_enable(void) ret = net_eth_vlan_enable(iface, VLAN_TAG_1); zassert_equal(ret, -EALREADY, "VLAN tag %d enabled for iface 1 (%d)", VLAN_TAG_1, ret); + + for (int i = VLAN_TAG_1; i <= VLAN_TAG_5; i += 100) { + iface = net_eth_get_vlan_iface(NULL, i); + + ARRAY_FOR_EACH_PTR(vlan_interfaces, vlan_iface) { + uint16_t tag; + + if (*vlan_iface == iface) { + tag = net_eth_get_vlan_tag(*vlan_iface); + + zassert_equal(tag, i, + "Could not get the VLAN interface (%d)", + net_if_get_by_iface(*vlan_iface)); + break; + } + } + } } static void test_vlan_disable(void) @@ -628,13 +645,13 @@ static void test_vlan_enable_all(void) int ret; ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_1); - zassert_equal(ret, 0, "Cannot enable %d", VLAN_TAG_1); + zassert_true(ret == 0 || ret == -EALREADY, "Cannot enable %d", VLAN_TAG_1); ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_2); - zassert_equal(ret, 0, "Cannot enable %d", VLAN_TAG_2); + zassert_true(ret == 0 || ret == -EALREADY, "Cannot enable %d", VLAN_TAG_2); ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_3); - zassert_equal(ret, 0, "Cannot enable %d", VLAN_TAG_3); + zassert_true(ret == 0 || ret == -EALREADY, "Cannot enable %d", VLAN_TAG_3); ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_4); - zassert_equal(ret, 0, "Cannot enable %d", VLAN_TAG_4); + zassert_true(ret == 0 || ret == -EALREADY, "Cannot enable %d", VLAN_TAG_4); eth_ctx = net_if_l2_data(eth_interfaces[0]); From f8773e2a61c5015a931f31b7fd8e653b8124eec7 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 25 Apr 2024 09:47:23 +0300 Subject: [PATCH 17/18] [nrf fromtree] net: vlan: Add a function to check if interface is VLAN one We were missing a helper function that can be used to check whether the given function is the virtual VLAN interface. Signed-off-by: Jukka Rissanen (cherry picked from commit 07599e3a539f4e6563144cada8cfbb50eae3d93c) --- include/zephyr/net/ethernet.h | 18 ++++++++++++++++++ subsys/net/l2/ethernet/vlan.c | 16 ++++++++++++++++ tests/net/vlan/src/main.c | 5 +++++ 3 files changed, 39 insertions(+) diff --git a/include/zephyr/net/ethernet.h b/include/zephyr/net/ethernet.h index ef13537e27a..40ebffbe662 100644 --- a/include/zephyr/net/ethernet.h +++ b/include/zephyr/net/ethernet.h @@ -957,6 +957,24 @@ static inline bool net_eth_get_vlan_status(struct net_if *iface) } #endif +/** + * @brief Check if the given interface is a VLAN interface. + * + * @param iface Network interface + * + * @return True if this network interface is VLAN one, false if not. + */ +#if defined(CONFIG_NET_VLAN) +bool net_eth_is_vlan_interface(struct net_if *iface); +#else +static inline bool net_eth_is_vlan_interface(struct net_if *iface) +{ + ARG_UNUSED(iface); + + return false; +} +#endif + #if !defined(CONFIG_ETH_DRIVER_RAW_MODE) #define Z_ETH_NET_DEVICE_INIT_INSTANCE(node_id, dev_id, name, instance, \ diff --git a/subsys/net/l2/ethernet/vlan.c b/subsys/net/l2/ethernet/vlan.c index 9a6d660aeb6..f8e21efbbb0 100644 --- a/subsys/net/l2/ethernet/vlan.c +++ b/subsys/net/l2/ethernet/vlan.c @@ -336,6 +336,22 @@ uint16_t net_eth_get_vlan_tag(struct net_if *iface) return tag; } +bool net_eth_is_vlan_interface(struct net_if *iface) +{ + enum virtual_interface_caps caps; + + if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) { + return false; + } + + caps = net_virtual_get_iface_capabilities(iface); + if (!(caps & VIRTUAL_INTERFACE_VLAN)) { + return false; + } + + return true; +} + bool net_eth_get_vlan_status(struct net_if *iface) { bool status = false; diff --git a/tests/net/vlan/src/main.c b/tests/net/vlan/src/main.c index 6cf6c07ffc1..08ccaa25715 100644 --- a/tests/net/vlan/src/main.c +++ b/tests/net/vlan/src/main.c @@ -581,6 +581,11 @@ static void test_vlan_enable(void) ARRAY_FOR_EACH_PTR(vlan_interfaces, vlan_iface) { uint16_t tag; + ret = net_eth_is_vlan_interface(*vlan_iface); + zassert_equal(ret, true, + "Not identified as VLAN interface %d", + net_if_get_by_iface(*vlan_iface)); + if (*vlan_iface == iface) { tag = net_eth_get_vlan_tag(*vlan_iface); From b47daca900c7b782f294c2f78206c270e08690ac Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Fri, 2 Feb 2024 10:13:19 +0200 Subject: [PATCH 18/18] [nrf fromtree] samples: net: Change samples to support new IPv4 netmask setting API Make sure that the samples use the new IPv4 netmask setting API. Signed-off-by: Jukka Rissanen (cherry picked from commit 3c0fe6b04b9d67c6a90640d9e4292135cc3704c2) --- samples/boards/nxp_s32/netc/src/main.c | 6 ++++-- samples/net/dhcpv4_client/src/main.c | 6 +++--- samples/net/dns_resolve/src/main.c | 8 +++++--- samples/net/ipv4_autoconf/src/main.c | 8 ++++++-- samples/net/mdns_responder/src/vlan.c | 6 ++++-- samples/net/sockets/echo_server/src/vlan.c | 6 ++++-- samples/net/virtual/src/main.c | 6 ++++-- samples/subsys/mgmt/hawkbit/src/dhcp.c | 2 +- 8 files changed, 31 insertions(+), 17 deletions(-) diff --git a/samples/boards/nxp_s32/netc/src/main.c b/samples/boards/nxp_s32/netc/src/main.c index 3b3848a1ef1..8399a7dd9b5 100644 --- a/samples/boards/nxp_s32/netc/src/main.c +++ b/samples/boards/nxp_s32/netc/src/main.c @@ -54,12 +54,14 @@ static int setup_iface(struct net_if *iface, const char *ipv6_addr, LOG_INF("IPv4 address: %s", ipv4_addr); if (netmask && netmask[0]) { - if (net_addr_pton(AF_INET, netmask, &addr4)) { + struct in_addr nm; + + if (net_addr_pton(AF_INET, netmask, &nm)) { LOG_ERR("Invalid netmask: %s", netmask); return -EINVAL; } - net_if_ipv4_set_netmask(iface, &addr4); + net_if_ipv4_set_netmask_by_addr(iface, &addr4, &nm); } } diff --git a/samples/net/dhcpv4_client/src/main.c b/samples/net/dhcpv4_client/src/main.c index 68d6abc2b91..00d48949d73 100644 --- a/samples/net/dhcpv4_client/src/main.c +++ b/samples/net/dhcpv4_client/src/main.c @@ -50,18 +50,18 @@ static void handler(struct net_mgmt_event_callback *cb, for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { char buf[NET_IPV4_ADDR_LEN]; - if (iface->config.ip.ipv4->unicast[i].addr_type != + if (iface->config.ip.ipv4->unicast[i].ipv4.addr_type != NET_ADDR_DHCP) { continue; } LOG_INF(" Address[%d]: %s", net_if_get_by_iface(iface), net_addr_ntop(AF_INET, - &iface->config.ip.ipv4->unicast[i].address.in_addr, + &iface->config.ip.ipv4->unicast[i].ipv4.address.in_addr, buf, sizeof(buf))); LOG_INF(" Subnet[%d]: %s", net_if_get_by_iface(iface), net_addr_ntop(AF_INET, - &iface->config.ip.ipv4->netmask, + &iface->config.ip.ipv4->unicast[i].netmask, buf, sizeof(buf))); LOG_INF(" Router[%d]: %s", net_if_get_by_iface(iface), net_addr_ntop(AF_INET, diff --git a/samples/net/dns_resolve/src/main.c b/samples/net/dns_resolve/src/main.c index 0aa5f2a9886..5eec113ead3 100644 --- a/samples/net/dns_resolve/src/main.c +++ b/samples/net/dns_resolve/src/main.c @@ -168,6 +168,7 @@ static void print_dhcpv4_addr(struct net_if *iface, struct net_if_addr *if_addr, { bool *found = (bool *)user_data; char hr_addr[NET_IPV4_ADDR_LEN]; + struct in_addr netmask; if (*found) { return; @@ -181,10 +182,11 @@ static void print_dhcpv4_addr(struct net_if *iface, struct net_if_addr *if_addr, net_addr_ntop(AF_INET, &if_addr->address.in_addr, hr_addr, NET_IPV4_ADDR_LEN)); LOG_INF("Lease time: %u seconds", iface->config.dhcpv4.lease_time); + + netmask = net_if_ipv4_get_netmask_by_addr(iface, + &if_addr->address.in_addr); LOG_INF("Subnet: %s", - net_addr_ntop(AF_INET, - &iface->config.ip.ipv4->netmask, - hr_addr, NET_IPV4_ADDR_LEN)); + net_addr_ntop(AF_INET, &netmask, hr_addr, NET_IPV4_ADDR_LEN)); LOG_INF("Router: %s", net_addr_ntop(AF_INET, &iface->config.ip.ipv4->gw, diff --git a/samples/net/ipv4_autoconf/src/main.c b/samples/net/ipv4_autoconf/src/main.c index 865c91659dd..b9f9c00379d 100644 --- a/samples/net/ipv4_autoconf/src/main.c +++ b/samples/net/ipv4_autoconf/src/main.c @@ -41,13 +41,17 @@ static void handler(struct net_mgmt_event_callback *cb, for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { char buf[NET_IPV4_ADDR_LEN]; - if (cfg->ip.ipv4->unicast[i].addr_type != NET_ADDR_AUTOCONF) { + if (cfg->ip.ipv4->unicast[i].ipv4.addr_type != NET_ADDR_AUTOCONF) { continue; } LOG_INF("Your address: %s", net_addr_ntop(AF_INET, - &cfg->ip.ipv4->unicast[i].address.in_addr, + &cfg->ip.ipv4->unicast[i].ipv4.address.in_addr, + buf, sizeof(buf))); + LOG_INF("Your netmask: %s", + net_addr_ntop(AF_INET, + &cfg->ip.ipv4->unicast[i].netmask, buf, sizeof(buf))); } } diff --git a/samples/net/mdns_responder/src/vlan.c b/samples/net/mdns_responder/src/vlan.c index 534662c3c64..7458343eaee 100644 --- a/samples/net/mdns_responder/src/vlan.c +++ b/samples/net/mdns_responder/src/vlan.c @@ -86,12 +86,14 @@ static int setup_iface(struct net_if *iface, const char *ipv6_addr, } if (netmask && netmask[0]) { - if (net_addr_pton(AF_INET, netmask, &addr4)) { + struct in_addr nm; + + if (net_addr_pton(AF_INET, netmask, &nm)) { LOG_ERR("Invalid netmask: %s", ipv4_addr); return -EINVAL; } - net_if_ipv4_set_netmask(iface, &addr4); + net_if_ipv4_set_netmask_by_addr(iface, &addr4, &nm); } } diff --git a/samples/net/sockets/echo_server/src/vlan.c b/samples/net/sockets/echo_server/src/vlan.c index 4f2a52034e2..2115a5f3ae5 100644 --- a/samples/net/sockets/echo_server/src/vlan.c +++ b/samples/net/sockets/echo_server/src/vlan.c @@ -86,12 +86,14 @@ static int setup_iface(struct net_if *iface, const char *ipv6_addr, } if (netmask && netmask[0]) { - if (net_addr_pton(AF_INET, netmask, &addr4)) { + struct in_addr nm; + + if (net_addr_pton(AF_INET, netmask, &nm)) { LOG_ERR("Invalid netmask: %s", ipv4_addr); return -EINVAL; } - net_if_ipv4_set_netmask(iface, &addr4); + net_if_ipv4_set_netmask_by_addr(iface, &addr4, &nm); } } diff --git a/samples/net/virtual/src/main.c b/samples/net/virtual/src/main.c index 0c086e0726c..bbc803cd4a3 100644 --- a/samples/net/virtual/src/main.c +++ b/samples/net/virtual/src/main.c @@ -302,12 +302,14 @@ static int setup_iface(struct net_if *iface, } if (netmask) { - if (net_addr_pton(AF_INET, netmask, &addr4)) { + struct in_addr nm; + + if (net_addr_pton(AF_INET, netmask, &nm)) { LOG_ERR("Invalid netmask: %s", netmask); return -EINVAL; } - net_if_ipv4_set_netmask(iface, &addr4); + net_if_ipv4_set_netmask_by_addr(iface, &addr4, &nm); } if (!peer4addr || *peer4addr == '\0') { diff --git a/samples/subsys/mgmt/hawkbit/src/dhcp.c b/samples/subsys/mgmt/hawkbit/src/dhcp.c index 3f72a03f3f3..ec433dae7e1 100644 --- a/samples/subsys/mgmt/hawkbit/src/dhcp.c +++ b/samples/subsys/mgmt/hawkbit/src/dhcp.c @@ -30,7 +30,7 @@ static void handler(struct net_mgmt_event_callback *cb, } for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { - if (iface->config.ip.ipv4->unicast[i].addr_type != + if (iface->config.ip.ipv4->unicast[i].ipv4.addr_type != NET_ADDR_DHCP) { continue; }