diff --git a/drivers/ethernet/Kconfig b/drivers/ethernet/Kconfig index 69b60e09bc2..fd02aed31be 100644 --- a/drivers/ethernet/Kconfig +++ b/drivers/ethernet/Kconfig @@ -5,11 +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 @@ -71,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/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/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/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 1ac21abc560..c94ab10dccb 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,14 +511,16 @@ 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); #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)) { ret = esp_cmd_send(dev, dns_cmds, ARRAY_SIZE(dns_cmds), @@ -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; 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; } } diff --git a/include/zephyr/net/ethernet.h b/include/zephyr/net/ethernet.h index faa6cf7fc0b..40ebffbe662 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. * - * @param iface Network interface. + * Note that the interface parameter must be the VLAN interface, + * and not the Ethernet one. + * + * @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,28 +951,57 @@ 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 +/** + * @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) -#define Z_ETH_NET_DEVICE_INIT(node_id, dev_id, name, init_fn, pm, data, \ - config, prio, api, mtu) \ +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, \ + 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) + +#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)); \ - 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) + &Z_DEVICE_STATE_NAME(dev_id)); -#else /* CONFIG_NET_VLAN */ +#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_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 +1025,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. 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/include/zephyr/net/net_if.h b/include/zephyr/net/net_if.h index f1e5be67cf7..9b72fdb8cd5 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; }; @@ -399,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. @@ -2217,34 +2229,78 @@ 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. * + * @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. + */ +__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. + * * @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. */ -__syscall bool net_if_ipv4_set_netmask_by_index(int index, - const struct in_addr *netmask); +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/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/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; } diff --git a/subsys/net/ip/Kconfig b/subsys/net/ip/Kconfig index a5a6b7ba01e..94c08218ead 100644 --- a/subsys/net/ip/Kconfig +++ b/subsys/net/ip/Kconfig @@ -737,11 +737,13 @@ 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_TEST - bool "Network Testing" +config NET_CONTEXT_RECV_PKTINFO + bool "Add receive PKTINFO support to net_context" + depends on NET_UDP help - Used for self-contained networking tests that do not require a - network device. + 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_SLIP_TAP bool "TAP SLIP driver" @@ -760,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 diff --git a/subsys/net/ip/dhcpv4.c b/subsys/net/ip/dhcpv4.c index 918b71d1d6b..0f564305037 100644 --- a/subsys/net/ip/dhcpv4.c +++ b/subsys/net/ip/dhcpv4.c @@ -738,7 +738,8 @@ static bool dhcpv4_parse_options(struct net_pkt *pkt, return false; } - net_if_ipv4_set_netmask(iface, &netmask); + iface->config.dhcpv4.netmask = netmask; + NET_DBG("options_subnet_mask %s", net_sprint_ipv4_addr(&netmask)); break; @@ -963,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; 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_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_if.c b/subsys/net/ip/net_if.c index 1a8199ad62c..8fc3b58e31f 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,35 +3636,68 @@ 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; } -void net_if_ipv4_set_netmask(struct net_if *iface, - const struct in_addr *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. + */ +static void net_if_ipv4_set_netmask_deprecated(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); } +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) { @@ -3581,7 +3708,23 @@ 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; +} + +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; } @@ -3605,6 +3748,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 +3833,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 +3868,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 +3876,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 +3932,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 +4053,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/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) 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 bc4d9a4886a..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 @@ -73,6 +81,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..bae967079e9 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); @@ -227,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; } } @@ -267,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)); @@ -457,6 +466,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, @@ -564,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); @@ -823,4 +969,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) */ } diff --git a/subsys/net/l2/ethernet/ethernet.c b/subsys/net/l2/ethernet/ethernet.c index 26c5ddcb3cc..0fefc7d8032 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) { @@ -195,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 @@ -221,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) { @@ -282,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), @@ -382,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; } @@ -434,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) { @@ -522,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; @@ -699,22 +698,13 @@ 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 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 error; + goto arp_error; } net_pkt_cursor_init(pkt); @@ -724,20 +714,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 +725,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) @@ -782,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); @@ -1219,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; @@ -1233,23 +942,9 @@ void ethernet_init(struct net_if *iface) ctx->ethernet_l2_flags |= NET_L2_PROMISC_MODE; } -#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; - } +#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 diff --git a/subsys/net/l2/ethernet/vlan.c b/subsys/net/l2/ethernet/vlan.c new file mode 100644 index 00000000000..f8e21efbbb0 --- /dev/null +++ b/subsys/net/l2/ethernet/vlan.c @@ -0,0 +1,655 @@ +/* + * 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; + + k_mutex_lock(&lock, K_FOREVER); + + 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; + } + } + + k_mutex_unlock(&lock); + + 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; + 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; +} 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 */ 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 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..08ccaa25715 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,73 @@ 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); + + 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; + + 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); + + 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) @@ -563,21 +605,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 +633,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 +650,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_true(ret == 0 || ret == -EALREADY, "Cannot enable %d", VLAN_TAG_1); + ret = net_eth_vlan_enable(eth_interfaces[0], 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_true(ret == 0 || ret == -EALREADY, "Cannot enable %d", VLAN_TAG_3); + ret = net_eth_vlan_enable(eth_interfaces[0], VLAN_TAG_4); + zassert_true(ret == 0 || ret == -EALREADY, "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 +679,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 +689,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 +718,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; + ll_addr = net_if_get_link_addr(iface); - lladdr.len = 6U; - lladdr.addr = llstorage.addr; - lladdr.type = NET_LINK_ETHERNET; - - 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 +736,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)