From e658bc1b2baa4191d836a04f19a9cded819e1c1f Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 15 Jan 2025 17:17:15 +0200 Subject: [PATCH] net: Extend the protocol handling in Ethernet Allow user to specify protocol extensions when receiving data from Ethernet network. This means that user can register L3 protocol handler using NET_L3_REGISTER() with the desired protocol type. Ethernet code will then call the handler if such a protocol type packet is received. This is currently only implemented for Ethernet. The original IPv4 and IPv6 handling is left intact even if they can be considered to be L3 layer protocol. This could be changed in the future if needed so that IPv4 and IPv6 handling could be made pluggable protocols. Signed-off-by: Jukka Rissanen --- cmake/linker_script/common/common-rom.cmake | 4 + .../linker/common-rom/common-rom-net.ld | 4 + include/zephyr/net/ethernet.h | 10 ++ include/zephyr/net/lldp.h | 10 -- include/zephyr/net/net_core.h | 35 +++++ modules/hostap/src/supp_main.c | 13 ++ subsys/net/ip/net_private.h | 10 -- subsys/net/l2/ethernet/arp.c | 26 ++++ subsys/net/l2/ethernet/ethernet.c | 127 ++++++++++-------- subsys/net/l2/ethernet/gptp/gptp.c | 14 +- subsys/net/l2/ethernet/lldp/lldp.c | 11 +- subsys/net/lib/ptp/ptp.c | 13 ++ 12 files changed, 199 insertions(+), 78 deletions(-) diff --git a/cmake/linker_script/common/common-rom.cmake b/cmake/linker_script/common/common-rom.cmake index 868e9f382d93..cb877ef366d9 100644 --- a/cmake/linker_script/common/common-rom.cmake +++ b/cmake/linker_script/common/common-rom.cmake @@ -91,6 +91,10 @@ zephyr_linker_section_configure( KEEP SORT NAME ) +if(CONFIG_NETWORKING) + zephyr_iterable_section(NAME net_l3_register KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN ${CONFIG_LINKER_ITERABLE_SUBALIGN}) +endif() + if(CONFIG_NET_SOCKETS) zephyr_iterable_section(NAME net_socket_register KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN ${CONFIG_LINKER_ITERABLE_SUBALIGN}) endif() diff --git a/include/zephyr/linker/common-rom/common-rom-net.ld b/include/zephyr/linker/common-rom/common-rom-net.ld index 5305f9cf873c..2b779e469939 100644 --- a/include/zephyr/linker/common-rom/common-rom-net.ld +++ b/include/zephyr/linker/common-rom/common-rom-net.ld @@ -2,6 +2,10 @@ #include +#if defined(CONFIG_NETWORKING) + ITERABLE_SECTION_ROM(net_l3_register, Z_LINK_ITERABLE_SUBALIGN) +#endif + #if defined(CONFIG_NET_SOCKETS) ITERABLE_SECTION_ROM(net_socket_register, Z_LINK_ITERABLE_SUBALIGN) #endif diff --git a/include/zephyr/net/ethernet.h b/include/zephyr/net/ethernet.h index 320bf6103299..3e361cb87031 100644 --- a/include/zephyr/net/ethernet.h +++ b/include/zephyr/net/ethernet.h @@ -1261,6 +1261,16 @@ static inline bool net_eth_is_vlan_interface(struct net_if *iface) #define ETH_NET_DEVICE_DT_INST_DEFINE(inst, ...) \ ETH_NET_DEVICE_DT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__) +/** + * @brief Ethernet L3 protocol register macro. + * + * @param name Name of the L3 protocol. + * @param ptype Ethernet protocol type. + * @param handler Handler function for this protocol type. + */ +#define ETH_NET_L3_REGISTER(name, ptype, handler) \ + NET_L3_REGISTER(&NET_L2_GET_NAME(ETHERNET), name, ptype, handler) + /** * @brief Inform ethernet L2 driver that ethernet carrier is detected. * This happens when cable is connected. diff --git a/include/zephyr/net/lldp.h b/include/zephyr/net/lldp.h index 0d638f2783e4..2b447653712c 100644 --- a/include/zephyr/net/lldp.h +++ b/include/zephyr/net/lldp.h @@ -225,16 +225,6 @@ typedef enum net_verdict (*net_lldp_recv_cb_t)(struct net_if *iface, */ int net_lldp_register_callback(struct net_if *iface, net_lldp_recv_cb_t cb); -/** - * @brief Parse LLDP packet - * - * @param iface Network interface - * @param pkt Network packet - * - * @return Return the policy for network buffer - */ -enum net_verdict net_lldp_recv(struct net_if *iface, struct net_pkt *pkt); - /** * @brief Set LLDP protocol data unit (LLDPDU) for the network interface. * diff --git a/include/zephyr/net/net_core.h b/include/zephyr/net/net_core.h index 4b07aaa3e94e..1b4bf0479bda 100644 --- a/include/zephyr/net/net_core.h +++ b/include/zephyr/net/net_core.h @@ -21,6 +21,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -154,6 +155,40 @@ int net_send_data(struct net_pkt *pkt); #define NET_TC_COUNT 0 #endif /* CONFIG_NET_TC_TX_COUNT && CONFIG_NET_TC_RX_COUNT */ +/** + * @brief Registration information for a given L3 handler. Note that + * the layer number (L3) just refers to something that is on top + * of L2. So for example IPv6 is L3 and IPv4 is L3, but Ethernet + * based LLDP, gPTP are more in the layer 2.5 but we consider them + * as L3 here for simplicity. + */ +struct net_l3_register { + /** Store also the name of the L3 type in order to be able to + * print it later. + */ + const char * const name; + /** What L2 layer this is for */ + const struct net_l2 * const l2; + /** Handler function for the given protocol type */ + enum net_verdict (*handler)(struct net_if *iface, + uint16_t ptype, + struct net_pkt *pkt); + /** Protocol type */ + uint16_t ptype; +}; + +#define NET_L3_GET_NAME(l3_name, ptype) __net_l3_register_##l3_name##_##ptype + +#define NET_L3_REGISTER(_l2_type, _name, _ptype, _handler) \ + static const STRUCT_SECTION_ITERABLE(net_l3_register, \ + NET_L3_GET_NAME(_name, _ptype)) = { \ + .ptype = _ptype, \ + .handler = _handler, \ + .name = STRINGIFY(_name), \ + .l2 = _l2_type, \ + }; \ + BUILD_ASSERT((_handler) != NULL, "Handler is not defined") + /* @endcond */ /** diff --git a/modules/hostap/src/supp_main.c b/modules/hostap/src/supp_main.c index 09bf22141f9d..4fdc144d435e 100644 --- a/modules/hostap/src/supp_main.c +++ b/modules/hostap/src/supp_main.c @@ -1219,3 +1219,16 @@ static int init(void) } SYS_INIT(init, APPLICATION, 0); + +static enum net_verdict eapol_recv(struct net_if *iface, uint16_t ptype, + struct net_pkt *pkt) +{ + ARG_UNUSED(iface); + ARG_UNUSED(ptype); + + net_pkt_set_family(pkt, AF_UNSPEC); + + return NET_CONTINUE; +} + +ETH_NET_L3_REGISTER(EAPOL, NET_ETH_PTYPE_EAPOL, eapol_recv); diff --git a/subsys/net/ip/net_private.h b/subsys/net/ip/net_private.h index cc1774a48b7d..ac54ddcb1425 100644 --- a/subsys/net/ip/net_private.h +++ b/subsys/net/ip/net_private.h @@ -239,18 +239,8 @@ void net_if_ipv6_start_dad(struct net_if *iface, * @brief Initialize Precision Time Protocol Layer. */ void net_gptp_init(void); - -/** - * @brief Process a ptp message. - * - * @param buf Buffer with a valid PTP Ethernet type. - * - * @return Return the policy for network buffer. - */ -enum net_verdict net_gptp_recv(struct net_if *iface, struct net_pkt *pkt); #else #define net_gptp_init() -#define net_gptp_recv(iface, pkt) NET_DROP #endif /* CONFIG_NET_GPTP */ #if defined(CONFIG_NET_IPV4_FRAGMENT) diff --git a/subsys/net/l2/ethernet/arp.c b/subsys/net/l2/ethernet/arp.c index bd7ec3be61e0..df9a9286b1a4 100644 --- a/subsys/net/l2/ethernet/arp.c +++ b/subsys/net/l2/ethernet/arp.c @@ -19,6 +19,7 @@ LOG_MODULE_REGISTER(net_arp, CONFIG_NET_ARP_LOG_LEVEL); #include #include "arp.h" +#include "ipv4.h" #include "net_private.h" #define NET_BUF_TIMEOUT K_MSEC(100) @@ -1013,3 +1014,28 @@ void net_arp_init(void) K_SECONDS(CONFIG_NET_ARP_GRATUITOUS_INTERVAL)); #endif /* defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION) */ } + +static enum net_verdict arp_recv(struct net_if *iface, + uint16_t ptype, + struct net_pkt *pkt) +{ + struct net_eth_hdr *hdr = NET_ETH_HDR(pkt); + + ARG_UNUSED(iface); + ARG_UNUSED(ptype); + + net_pkt_set_family(pkt, AF_INET); + + NET_DBG("ARP packet from %s received", + net_sprint_ll_addr((uint8_t *)hdr->src.addr, + sizeof(struct net_eth_addr))); + + if (IS_ENABLED(CONFIG_NET_IPV4_ACD) && + net_ipv4_acd_input(iface, pkt) == NET_DROP) { + return NET_DROP; + } + + return net_arp_input(pkt, hdr); +} + +ETH_NET_L3_REGISTER(ARP, NET_ETH_PTYPE_ARP, arp_recv); diff --git a/subsys/net/l2/ethernet/ethernet.c b/subsys/net/l2/ethernet/ethernet.c index fd68a71ff0f7..71eb9fcda248 100644 --- a/subsys/net/l2/ethernet/ethernet.c +++ b/subsys/net/l2/ethernet/ethernet.c @@ -236,11 +236,13 @@ static enum net_verdict ethernet_recv(struct net_if *iface, struct net_pkt *pkt) { struct ethernet_context *ctx = net_if_l2_data(iface); - struct net_eth_hdr *hdr = NET_ETH_HDR(pkt); uint8_t hdr_len = sizeof(struct net_eth_hdr); - uint16_t type; - struct net_linkaddr *lladdr; + struct net_eth_hdr *hdr = NET_ETH_HDR(pkt); + enum net_verdict verdict = NET_CONTINUE; bool is_vlan_pkt = false; + bool handled = false; + struct net_linkaddr *lladdr; + uint16_t type; /* This expects that the Ethernet header is in the first net_buf * fragment. This is a safe expectation here as it would not make @@ -279,7 +281,6 @@ static enum net_verdict ethernet_recv(struct net_if *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); @@ -310,39 +311,6 @@ static enum net_verdict ethernet_recv(struct net_if *iface, } } - switch (type) { - case NET_ETH_PTYPE_IP: - case NET_ETH_PTYPE_ARP: - net_pkt_set_family(pkt, AF_INET); - break; - case NET_ETH_PTYPE_IPV6: - net_pkt_set_family(pkt, AF_INET6); - break; - case NET_ETH_PTYPE_EAPOL: - break; -#if defined(CONFIG_NET_L2_PTP) - case NET_ETH_PTYPE_PTP: - break; -#endif - case NET_ETH_PTYPE_LLDP: -#if defined(CONFIG_NET_LLDP) - net_buf_pull(pkt->frags, hdr_len); - return net_lldp_recv(iface, pkt); -#else - NET_DBG("LLDP Rx agent not enabled"); - goto drop; -#endif - default: - if (IS_ENABLED(CONFIG_NET_ETHERNET_FORWARD_UNRECOGNISED_ETHERTYPE)) { - break; - } - - NET_DBG("Unknown hdr type 0x%04x iface %d (%p)", type, - net_if_get_by_iface(iface), iface); - eth_stats_update_unknown_protocol(iface); - return NET_DROP; - } - /* Set the pointers to ll src and dst addresses */ lladdr = net_pkt_lladdr_src(pkt); lladdr->addr = hdr->src.addr; @@ -370,9 +338,7 @@ static enum net_verdict ethernet_recv(struct net_if *iface, if (!net_eth_is_addr_broadcast((struct net_eth_addr *)lladdr->addr) && !net_eth_is_addr_multicast((struct net_eth_addr *)lladdr->addr) && - !net_eth_is_addr_lldp_multicast( - (struct net_eth_addr *)lladdr->addr) && - !net_eth_is_addr_ptp_multicast((struct net_eth_addr *)lladdr->addr) && + !net_eth_is_addr_group((struct net_eth_addr *)lladdr->addr) && !net_linkaddr_cmp(net_if_get_link_addr(iface), lladdr)) { /* The ethernet frame is not for me as the link addresses * are different. @@ -383,43 +349,92 @@ static enum net_verdict ethernet_recv(struct net_if *iface, goto drop; } + /* Get rid of the Ethernet header. */ net_buf_pull(pkt->frags, hdr_len); - if (IS_ENABLED(CONFIG_NET_IPV4) && type == NET_ETH_PTYPE_IP && - ethernet_check_ipv4_bcast_addr(pkt, hdr) == NET_DROP) { - goto drop; - } + STRUCT_SECTION_FOREACH(net_l3_register, l3) { + if (l3->ptype != type || l3->l2 != &NET_L2_GET_NAME(ETHERNET)) { + continue; + } - ethernet_update_rx_stats(iface, hdr, net_pkt_get_len(pkt) + hdr_len); + NET_DBG("Calling L3 %s handler for type 0x%04x iface %d (%p)", + l3->name, type, net_if_get_by_iface(iface), iface); - if (IS_ENABLED(CONFIG_NET_ARP) && type == NET_ETH_PTYPE_ARP) { - NET_DBG("ARP packet from %s received", - net_sprint_ll_addr((uint8_t *)hdr->src.addr, - sizeof(struct net_eth_addr))); + verdict = l3->handler(iface, type, pkt); + if (verdict == NET_DROP) { + NET_DBG("Dropping frame, packet rejected by %s", l3->name); + goto drop; + } - if (IS_ENABLED(CONFIG_NET_IPV4_ACD) && - net_ipv4_acd_input(iface, pkt) == NET_DROP) { + handled = true; + break; + } + + if (!handled) { + if (IS_ENABLED(CONFIG_NET_ETHERNET_FORWARD_UNRECOGNISED_ETHERTYPE)) { + net_pkt_set_family(pkt, AF_UNSPEC); + } else { + NET_DBG("Unknown hdr type 0x%04x iface %d (%p)", type, + net_if_get_by_iface(iface), iface); + eth_stats_update_unknown_protocol(iface); return NET_DROP; } - - return net_arp_input(pkt, hdr); } - if (IS_ENABLED(CONFIG_NET_GPTP) && type == NET_ETH_PTYPE_PTP) { - return net_gptp_recv(iface, pkt); + /* FIXME: ARP eats the packet and the pkt is no longer a valid one, + * so we cannot update the stats here. Fix the code to be more generic + * so that we do not need this check. + */ + if (type == NET_ETH_PTYPE_ARP) { + return verdict; } + ethernet_update_rx_stats(iface, hdr, net_pkt_get_len(pkt) + hdr_len); + if (type != NET_ETH_PTYPE_EAPOL) { ethernet_update_length(iface, pkt); } - return NET_CONTINUE; + return verdict; drop: eth_stats_update_errors_rx(iface); return NET_DROP; } +#if defined(CONFIG_NET_IPV4) || defined(CONFIG_NET_IPV6) +static enum net_verdict ethernet_ip_recv(struct net_if *iface, + uint16_t ptype, + struct net_pkt *pkt) +{ + ARG_UNUSED(iface); + + if (ptype == NET_ETH_PTYPE_IP) { + struct net_eth_hdr *hdr = NET_ETH_HDR(pkt); + + if (ethernet_check_ipv4_bcast_addr(pkt, hdr) == NET_DROP) { + return NET_DROP; + } + + net_pkt_set_family(pkt, AF_INET); + } else if (ptype == NET_ETH_PTYPE_IPV6) { + net_pkt_set_family(pkt, AF_INET6); + } else { + return NET_DROP; + } + + return NET_CONTINUE; +} +#endif /* CONFIG_NET_IPV4 || CONFIG_NET_IPV6 */ + #ifdef CONFIG_NET_IPV4 +ETH_NET_L3_REGISTER(IPv4, NET_ETH_PTYPE_IP, ethernet_ip_recv); +#endif + +#if defined(CONFIG_NET_IPV6) +ETH_NET_L3_REGISTER(IPv6, NET_ETH_PTYPE_IPV6, ethernet_ip_recv); +#endif /* CONFIG_NET_IPV6 */ + +#if defined(CONFIG_NET_IPV4) static inline bool ethernet_ipv4_dst_is_broadcast_or_mcast(struct net_pkt *pkt) { if (net_ipv4_is_addr_bcast(net_pkt_iface(pkt), diff --git a/subsys/net/l2/ethernet/gptp/gptp.c b/subsys/net/l2/ethernet/gptp/gptp.c index 81fda33f1131..67eb3296fbd5 100644 --- a/subsys/net/l2/ethernet/gptp/gptp.c +++ b/subsys/net/l2/ethernet/gptp/gptp.c @@ -320,10 +320,20 @@ static void gptp_handle_msg(struct net_pkt *pkt) } } -enum net_verdict net_gptp_recv(struct net_if *iface, struct net_pkt *pkt) +static enum net_verdict net_gptp_recv(struct net_if *iface, uint16_t ptype, + struct net_pkt *pkt) { struct gptp_hdr *hdr = GPTP_HDR(pkt); + ARG_UNUSED(ptype); + + if (!(net_eth_is_addr_ptp_multicast( + (struct net_eth_addr *)net_pkt_lladdr_dst(pkt)->addr) || + net_eth_is_addr_lldp_multicast( + (struct net_eth_addr *)net_pkt_lladdr_dst(pkt)->addr))) { + return NET_DROP; + } + if ((hdr->ptp_version != GPTP_VERSION) || (hdr->transport_specific != GPTP_TRANSPORT_802_1_AS)) { /* The stack only supports PTP V2 and transportSpecific set @@ -346,6 +356,8 @@ enum net_verdict net_gptp_recv(struct net_if *iface, struct net_pkt *pkt) return NET_DROP; } +ETH_NET_L3_REGISTER(gPTP, NET_ETH_PTYPE_PTP, net_gptp_recv); + static void gptp_init_clock_ds(void) { struct gptp_global_ds *global_ds; diff --git a/subsys/net/l2/ethernet/lldp/lldp.c b/subsys/net/l2/ethernet/lldp/lldp.c index cacac95ba349..da179f728236 100644 --- a/subsys/net/l2/ethernet/lldp/lldp.c +++ b/subsys/net/l2/ethernet/lldp/lldp.c @@ -265,12 +265,19 @@ static int lldp_start(struct net_if *iface, uint32_t mgmt_event) return 0; } -enum net_verdict net_lldp_recv(struct net_if *iface, struct net_pkt *pkt) +static enum net_verdict net_lldp_recv(struct net_if *iface, uint16_t ptype, struct net_pkt *pkt) { struct ethernet_context *ctx; net_lldp_recv_cb_t recv_cb; int ret; + ARG_UNUSED(ptype); + + if (!net_eth_is_addr_lldp_multicast( + (struct net_eth_addr *)net_pkt_lladdr_dst(pkt)->addr)) { + return NET_DROP; + } + ret = lldp_check_iface(iface); if (ret < 0) { return NET_DROP; @@ -291,6 +298,8 @@ enum net_verdict net_lldp_recv(struct net_if *iface, struct net_pkt *pkt) return NET_DROP; } +ETH_NET_L3_REGISTER(LLDP, NET_ETH_PTYPE_LLDP, net_lldp_recv); + int net_lldp_register_callback(struct net_if *iface, net_lldp_recv_cb_t recv_cb) { struct ethernet_context *ctx; diff --git a/subsys/net/lib/ptp/ptp.c b/subsys/net/lib/ptp/ptp.c index 8f7024929517..2097121072d8 100644 --- a/subsys/net/lib/ptp/ptp.c +++ b/subsys/net/lib/ptp/ptp.c @@ -114,3 +114,16 @@ static int ptp_init(void) } SYS_INIT(ptp_init, APPLICATION, CONFIG_PTP_INIT_PRIO); + +static enum net_verdict ptp_recv(struct net_if *iface, uint16_t ptype, + struct net_pkt *pkt) +{ + ARG_UNUSED(iface); + ARG_UNUSED(ptype); + + net_pkt_set_family(pkt, AF_UNSPEC); + + return NET_CONTINUE; +} + +ETH_NET_L3_REGISTER(PTP, NET_ETH_PTYPE_PTP, ptp_recv);