Skip to content

net: core: Rework l3 handling #89248

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

go2sh
Copy link
Contributor

@go2sh go2sh commented Apr 29, 2025

This PR rework the l3 handling from the ethernet l2 driver into the net_core. For that it does:

  • Loopback flag in net_pkt
  • Dedicated l3 handler for ipv4
  • Dedicated l3 handler for ipv6
  • L3 handler for IEEE802.15.4
  • Allow L3 handlers to not specify an l2 to handle DUMMY and VIRTUAL l2.

The idea is to allow virutal intefaces to go throug the l3 handlers. So for example (a potential macsec l2 inteface), the flow could look like: l2 ethernet -> l2 macsec ->l2 vlan -> l3 handler.

@go2sh go2sh force-pushed the rework_l3_handling branch 2 times, most recently from 0e4900b to 77e4245 Compare April 29, 2025 11:03
@go2sh go2sh marked this pull request as draft April 29, 2025 11:32
@go2sh go2sh force-pushed the rework_l3_handling branch 5 times, most recently from f5ddfc9 to 0f13367 Compare April 29, 2025 23:58
@go2sh go2sh marked this pull request as ready for review April 29, 2025 23:59
@github-actions github-actions bot added the Release Notes To be mentioned in the release notes label Apr 29, 2025
@go2sh go2sh force-pushed the rework_l3_handling branch from 0f13367 to 81dd7ff Compare April 30, 2025 00:00
@kartben kartben requested a review from Copilot April 30, 2025 06:35
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR reworks L3 packet handling, moving IPv4/IPv6 (and IEEE802.15.4) processing from the Ethernet L2 driver into the centralized net_core module to support virtual interfaces and new handler registration. Key changes include:

  • Adding loopback flags and associated helper functions in net_pkt.
  • Introducing dedicated L3 handlers for IPv4, IPv6, and IEEE802.15.4.
  • Adjusting tests to explicitly set link layer protocol types for fragmented packet handling.

Reviewed Changes

Copilot reviewed 9 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/net/lib/mdns_responder/src/main.c Updates IPv6 packet allocation parameters and explicitly sets link layer protocol type.
tests/net/ipv6_fragment/src/main.c Ensures IPv6 fragments have the correct ll protocol type.
tests/net/ipv4_fragment/src/main.c Sets the IPv4 link layer protocol type in multiple test cases.
subsys/net/l2/ieee802154/ieee802154.c Introduces a dedicated L3 handler registration for IEEE802.15.4.
subsys/net/l2/ethernet/ethernet.c Removes legacy L3 dispatch logic from the Ethernet driver, reflecting the centralized processing.
subsys/net/ip/net_core.c Centralizes L3 processing logic and refactors handling of loopback and reassembled packets.
subsys/net/ip/ipv6.c & ipv4.c Add new L3 handler functions for IPv6 and IPv4, including protocol-specific error checks.
include/zephyr/net/net_pkt.h Adds the loopback flag field and helper functions, plus updates to interface management macros.
Files not reviewed (2)
  • doc/releases/release-notes-4.2.rst: Language not supported
  • subsys/net/l2/ethernet/Kconfig: Language not supported
Comments suppressed due to low confidence (2)

subsys/net/ip/net_core.c:125

  • [nitpick] The condition used to determine the correct L3 handler is complex. Consider refactoring it (e.g. by using temporary variables or a helper function) to improve readability and maintainability.
if (l3->ptype != net_pkt_ll_proto_type(pkt) || (l3->l2 != NULL && l3->l2 != (net_pkt_iface(pkt)->if_dev->l2 == &NET_L2_GET_NAME(VIRTUAL) ? net_pkt_orig_iface(pkt)->if_dev->l2 : net_pkt_iface(pkt)->if_dev->l2)) || l3->handler == NULL) {

subsys/net/ip/net_core.c:440

  • [nitpick] The change removes the is_loopback parameter from the process_data function and now sets the loopback flag externally. Please verify that this change maintains consistent loopback processing semantics across all packet reception paths.
net_pkt_set_loopback(pkt, true);

@jukkar
Copy link
Member

jukkar commented Apr 30, 2025

The idea is to allow virutal intefaces to go throug the l3 handlers. So for example (a potential macsec l2 inteface), the flow could look like: l2 ethernet -> l2 macsec ->l2 vlan -> l3 handler

There are now multiple PRs changing things in this area and at least I have some difficulty keeping track of the changes.
I think we should leave the L3 handler changes to later stage and just try to allow your use case of placing the macsec layer between Ethernet and VLAN. As the virtual interfaces can be stacked on top of each other, we just need to adjust the VLAN code so that it does not place itself on top of Ethernet

ret = net_virtual_interface_attach(ctx->iface, iface);
but on top of what you want to attach it to.

@jukkar
Copy link
Member

jukkar commented Apr 30, 2025

@go2sh BTW, I just wonder what is the use case where VLAN would not be run directly on top of Ethernet because VLAN is basically Ethernet header + couple extra bytes in header. This does not sound very standard like behavior. What is the macsec layer doing?

@go2sh
Copy link
Contributor Author

go2sh commented Apr 30, 2025

@jukkar General remark for MACsec. MACsec is a layer 2 authentication and encryption standard, which operates on a point to point bases. Example: An ECU and a switch communicate via MACsec, usually all traffic between those two is either authenticated or encrypted through a symmetric cypher (usually AES-GCM). Each point-to-point link in the network has its on key pair. Depending on the system configuration also the VLAN header is part of the authenticated/encrypted data, sometimes outside, sometimes there is a q-in-q setting. Depending on the requirement of the vehicle architecture.

This pull request should enable a l2 macsec interface to feed packets into the l3 handlers the sameway a ethernet l2 or vlan l2 would do.

The other pull request (#89291) should allow to layer l2 device depending on the network requirement. This would also make it easy to enable q-in-q (s-vlan and c-vlan) support in zephyr.

Maybe on general remark on how I see virtual interfaces here: My idea is to have a tree of virtual interfaces similar to what linux does (eth0, macsec0@eth0, vlan1@macesc0, vlan2@macsec0 and so on). During receiving, the processing goes from the root (ethernet l2) towards an end node depending on the protocol header (vlan and macesec header give guidance which interface to select next). During sending, the processing goes from edge node towards the root node (l2 Ethernet). Here the route is clear from the configuration of each interface. Each layer (either ethernet l2 or virtual l2) removes or inserts the header required for its protocol. With that you have complete freedom how to layer the different protocols (except l2 ethernet is always start or end). And by the way: This layering could also be used for the bridging.

@go2sh
Copy link
Contributor Author

go2sh commented Apr 30, 2025

BTW: This PR doesn't change any observable behavior for the current use cases. The changes to the tests a negatable.

@jukkar
Copy link
Member

jukkar commented Apr 30, 2025

@jukkar
Copy link
Member

jukkar commented Apr 30, 2025

After reading the article it looks like the macsec works similar way as Ethernet extension protocol like LLDP, to me it does not look like a virtual network interface.
How is macsec suppose to be used with VLAN? The article mentions VXLAN which is used to tunnel Ethernet over UDP but that is not supported in zephyr atm.
Is the VLAN Ethernet frame encapsulated inside macsec payload (1), or is the VLAN tags part of the Ethernet header together with macsec header (2)?

  • If (1), then the macsec layer can just decrypt the packet and feed the resulting back to Ethernet for processing. So the macsec processing would be done here
    verdict = l3->handler(iface, type, pkt);
    so similar way for example how LLDP is working
  • If (2), then the VLAN is already handled before macsec layer, in which case the macsec could be a virtual network interface that sits on top of VLAN processing

Sorry for stupid questions, just trying to understand how the macsec works to figure out how it can be bolt into network packet processing pipeline.

@go2sh
Copy link
Contributor Author

go2sh commented Apr 30, 2025

Both (1) and (2) are used, depending on the requirement. The macsec header behaves similar to the vlan header. Its just another l2 header and the first two bytes of the payload contain the new ethertype.

The reason why I think macsec is a virtual interface ist the following:
image
The standard requires two endpoints and the traffic is duplicated: An uncontrolled port for all traffic and a controlled port, which depending on the configuration, only uses macsec frames . (Though the duplication is not required if nothing consumes the macsec frames in the uncontrolled port). Both ports can have different consumer connected and have different ips etc. Its possible to use macsec and non macsec at the same time. An especially in the transmit case, different interfaces are required to know, when to insert a macsec header or not. (Similar to VLAN)

Copy link
Collaborator

@rlubos rlubos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks pretty reasonable, however it seems all L2s other than Ethernet need to be aligned to set ll_proto_type accordingly. After a brief check it seems that only Ethernet L2 does this, all other L2s relied on the net_core behavior which checked the IP header.

go2sh added 6 commits May 9, 2025 10:47
Chaning the l3 processing from the ethernet l2 driver to the net_core
l3 processing. This allows to have a more generic l3 processing
also in conjunction with other l2 drivers.

Signed-off-by: Christoph Seitz <christoph.seitz@infineon.com>
Adding a loopback flag allows to process this information
in l3 handlers.

Signed-off-by: Christoph Seitz <christoph.seitz@infineon.com>
Adding l3 handler for ieee802154 packets to not get droped during l3
processing before packet sockets.

Signed-off-by: Christoph Seitz <christoph.seitz@infineon.com>
Add a dedicated l3 handler to support more flexible
interface architecture.

Signed-off-by: Christoph Seitz <christoph.seitz@infineon.com>
Adding a dedicated ipv6 l3 handler makes processing
from interfaces easier.

Signed-off-by: Christoph Seitz <christoph.seitz@infineon.com>
The length update for ip and ipv6 is done in the
respecive protocol functions.

Signed-off-by: Christoph Seitz <christoph.seitz@infineon.com>
@go2sh go2sh force-pushed the rework_l3_handling branch from 81dd7ff to 981ef0d Compare May 9, 2025 08:54
@go2sh
Copy link
Contributor Author

go2sh commented May 9, 2025

Generally looks pretty reasonable, however it seems all L2s other than Ethernet need to be aligned to set ll_proto_type accordingly. After a brief check it seems that only Ethernet L2 does this, all other L2s relied on the net_core behavior which checked the IP header.

I run all test and added the missing l3 handler, which was only IEEE802.15.4. The CAN L3 handler is in a separat PR.

@go2sh go2sh requested review from pdgendt, rlubos and Copilot May 9, 2025 09:04
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the l3 packet handling by moving responsibilities from the ethernet l2 driver into net_core. Key changes include introducing a loopback flag in net_pkt, creating dedicated l3 handlers for IPv4, IPv6, and IEEE802.15.4, and allowing l3 handlers to operate independently of an l2 layer for DUMMY and VIRTUAL interfaces.

Reviewed Changes

Copilot reviewed 10 out of 12 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/net/lib/mdns_responder/src/main.c Updated pkt allocation to explicitly use AF_INET6 and set LL protocol
tests/net/ipv6_fragment/src/main.c & tests/net/ipv4_fragment/src/main.c Added ll protocol type setting in packet tests
subsys/net/l2/ieee802154/ieee802154.c Introduced a dedicated l3 handler for IEEE802.15.4 via NET_L3_REGISTER
subsys/net/l2/ethernet/ethernet.c Removed internal l3 registration loop and ethernet_update_length helper
subsys/net/ip/net_pkt.c Ensured that loopback flag is propagated when cloning packets
subsys/net/ip/net_core.c Refactored process_data to eliminate the is_loopback parameter and updated L3 handling
subsys/net/ip/ipv6.c & subsys/net/ip/ipv4.c Introduced dedicated l3 handlers for IPv6 and IPv4 respectively
include/zephyr/net/net_pkt.h Added a new loopback bitfield and updated the interface for orig_iface handling
Files not reviewed (2)
  • doc/releases/release-notes-4.2.rst: Language not supported
  • subsys/net/l2/ethernet/Kconfig: Language not supported
Comments suppressed due to low confidence (4)

subsys/net/l2/ethernet/ethernet.c:118

  • Ensure that the removal of the internal l3 registration loop and the ethernet_update_length helper is intentional and that all L3 handling is now properly routed via net_core.
/* Removed ethernet_update_length and L3 registration loop */

tests/net/lib/mdns_responder/src/main.c:299

  • [nitpick] The allocation now explicitly uses AF_INET6 instead of AF_UNSPEC; please confirm that all test cases expecting an IPv6 packet allocation are updated accordingly.
pkt = net_pkt_alloc_with_buffer(iface1, NET_IPV6UDPH_LEN + len, AF_INET6, 0, K_FOREVER);

subsys/net/ip/net_core.c:67

  • Confirm that eliminating the is_loopback parameter in process_data and relying on net_pkt_set_loopback() does not inadvertently affect loopback packet processing.
static inline enum net_verdict process_data(struct net_pkt *pkt)

include/zephyr/net/net_pkt.h:247

  • Double-check that adding the loopback bitfield does not disrupt the existing bitfield alignment or conflicts with other flags in the net_pkt structure.
uint8_t loopback: 1; /** Set to 1 if this packet is a loopback packet */

@@ -244,6 +244,8 @@ struct net_pkt {
uint8_t tx_timestamping : 1; /** Timestamp transmitted packet */
uint8_t rx_timestamping : 1; /** Timestamp received packet */
#endif
uint8_t loopback: 1; /** Set to 1 if this packet is a loopback packet */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previous nit comment not addressed

Suggested change
uint8_t loopback: 1; /** Set to 1 if this packet is a loopback packet */
uint8_t loopback : 1; /** Set to 1 if this packet is a loopback packet */

Copy link

sonarqubecloud bot commented May 9, 2025

@rlubos
Copy link
Collaborator

rlubos commented May 9, 2025

Generally looks pretty reasonable, however it seems all L2s other than Ethernet need to be aligned to set ll_proto_type accordingly. After a brief check it seems that only Ethernet L2 does this, all other L2s relied on the net_core behavior which checked the IP header.

I run all test and added the missing l3 handler, which was only IEEE802.15.4. The CAN L3 handler is in a separat PR.

I wasn't referring to L3 handlers - my point is, that not all L2 implementations currently set the packet proto type to either ETH_P_IP or ETH_P_IPV6. I've verified OpenThread and 802.15.4 L2s and this indeed is an issue - all packets end up dropped. So for example for OpenThread we need:

OpenThread L2 diff
diff --git a/subsys/net/l2/openthread/openthread.c b/subsys/net/l2/openthread/openthread.c
index c82dfdea2d2..00290b7fde3 100644
--- a/subsys/net/l2/openthread/openthread.c
+++ b/subsys/net/l2/openthread/openthread.c
@@ -7,6 +7,7 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(net_l2_openthread, CONFIG_OPENTHREAD_L2_LOG_LEVEL);

+#include <zephyr/net/ethernet.h>
#include <zephyr/net/net_core.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/net_mgmt.h>
@@ -307,6 +308,8 @@ static void ot_receive_handler(otMessage *aMessage, void *context)
               }
       }

+       net_pkt_set_ll_proto_type(pkt, PKT_IS_IPv4(pkt) ? ETH_P_IP : ETH_P_IPV6);
+
       if (!pkt_list_is_full(ot_context)) {
               if (pkt_list_add(ot_context, pkt) != 0) {
                       NET_ERR("pkt_list_add failed");

or for 802.15.4 6lowpan compression:

802.15.4 L2 diff
diff --git a/subsys/net/ip/6lo.c b/subsys/net/ip/6lo.c
index 6a0dfa66758..6d9e48d9fd4 100644
--- a/subsys/net/ip/6lo.c
+++ b/subsys/net/ip/6lo.c
@@ -13,6 +13,7 @@
LOG_MODULE_REGISTER(net_6lo, CONFIG_NET_6LO_LOG_LEVEL);

#include <errno.h>
+#include <zephyr/net/ethernet.h>
#include <zephyr/net/net_core.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_stats.h>
@@ -1420,6 +1421,7 @@ static bool uncompress_IPHC_header(struct net_pkt *pkt)
       /* Version is always 6 */
       ipv6->vtc = 0x60;
       net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN);
+       net_pkt_set_ll_proto_type(pkt, ETH_P_IPV6);

       /* Uncompress Traffic class and Flow label */
       cursor = uncompress_tfl(iphc, cursor, ipv6);
@@ -1556,6 +1558,7 @@ static inline bool uncompress_ipv6_header(struct net_pkt *pkt)
       /* Pull off IPv6 dispatch header and adjust data and length */
       net_buf_pull(pkt->buffer, 1U);
       net_pkt_cursor_init(pkt);
+       net_pkt_set_ll_proto_type(pkt, ETH_P_IPV6);

       return true;
}

PPP L2 likely (haven't tested this) need to set respective proto type somewhere in:

switch (protocol) {

Not sure about dummy L2 - it's mostly used for loopback so probably it's not an issue in this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants