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
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions doc/releases/release-notes-4.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ Deprecated APIs and options
deprecated, because support for anonymous authentication had been removed from the
hawkBit server in version 0.8.0.

* :kconfig:option:`NET_ETHERNET_FORWARD_UNRECOGNISED_ETHERTYPE` Kconfig option has been
deprecated. All frames are now forwarded by default, and the option is no longer
needed.

New APIs and options
====================

Expand Down Expand Up @@ -161,6 +165,12 @@ New APIs and options

* Networking:

* Core

* All packets are passed through l3 handlers.
* Add new ``loopback`` flag for packets which are directly passed back
with :c:func:`net_pkt_is_loopback` and :c:func:`net_pkt_set_loopback`.

* IPv4

* :kconfig:option:`CONFIG_NET_IPV4_MTU`
Expand Down
18 changes: 15 additions & 3 deletions include/zephyr/net/net_pkt.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ struct net_pkt {
/** Allow placing the packet into sys_slist_t */
sys_snode_t next;
#endif
#if defined(CONFIG_NET_ROUTING) || defined(CONFIG_NET_ETHERNET_BRIDGE)
#if defined(CONFIG_NET_ROUTING) || defined(CONFIG_NET_L2_VIRTUAL)
struct net_if *orig_iface; /* Original network interface */
#endif

Expand Down Expand Up @@ -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 */


/* bitfield byte alignment boundary */

#if defined(CONFIG_NET_IP)
Expand Down Expand Up @@ -405,7 +407,7 @@ static inline void net_pkt_set_iface(struct net_pkt *pkt, struct net_if *iface)

static inline struct net_if *net_pkt_orig_iface(struct net_pkt *pkt)
{
#if defined(CONFIG_NET_ROUTING) || defined(CONFIG_NET_ETHERNET_BRIDGE)
#if defined(CONFIG_NET_ROUTING) || defined(CONFIG_NET_L2_VIRTUAL)
return pkt->orig_iface;
#else
return pkt->iface;
Expand All @@ -415,7 +417,7 @@ static inline struct net_if *net_pkt_orig_iface(struct net_pkt *pkt)
static inline void net_pkt_set_orig_iface(struct net_pkt *pkt,
struct net_if *iface)
{
#if defined(CONFIG_NET_ROUTING) || defined(CONFIG_NET_ETHERNET_BRIDGE)
#if defined(CONFIG_NET_ROUTING) || defined(CONFIG_NET_L2_VIRTUAL)
pkt->orig_iface = iface;
#else
ARG_UNUSED(pkt);
Expand Down Expand Up @@ -575,6 +577,16 @@ static inline void net_pkt_set_chksum_done(struct net_pkt *pkt,
pkt->chksum_done = is_chksum_done;
}

static inline void net_pkt_set_loopback(struct net_pkt *pkt, bool loopback)
{
pkt->loopback = loopback;
}

static inline bool net_pkt_is_loopback(struct net_pkt *pkt)
{
return pkt->loopback;
}

static inline uint8_t net_pkt_ip_hdr_len(struct net_pkt *pkt)
{
#if defined(CONFIG_NET_IP)
Expand Down
45 changes: 45 additions & 0 deletions subsys/net/ip/ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,51 @@ enum net_verdict net_ipv4_prepare_for_send(struct net_pkt *pkt)
#endif
}

/* Drop packet if it has broadcast destination MAC address but the IP
* address is not multicast or broadcast address. See RFC 1122 ch 3.3.6
*/
static inline enum net_verdict ip_check_bcast_addr(struct net_pkt *pkt, struct net_eth_hdr *hdr)
{
if (IS_ENABLED(CONFIG_NET_L2_ETHERNET_ACCEPT_MISMATCH_L3_L2_ADDR)) {
return NET_OK;
}

if (net_eth_is_addr_broadcast(&hdr->dst) &&
!(net_ipv4_is_addr_mcast((struct in_addr *)NET_IPV4_HDR(pkt)->dst) ||
net_ipv4_is_addr_bcast(net_pkt_iface(pkt),
(struct in_addr *)NET_IPV4_HDR(pkt)->dst))) {
return NET_DROP;
}

return NET_OK;
}

static enum net_verdict ip_recv(struct net_if *iface, uint16_t ptype, struct net_pkt *pkt)
{
ARG_UNUSED(iface);
/* IP version and header length. */
uint8_t vhl = NET_IPV4_HDR(pkt)->vhl & 0xf0;

struct net_eth_hdr *hdr = NET_ETH_HDR(pkt);

if (ip_check_bcast_addr(pkt, hdr) == NET_DROP) {
return NET_DROP;
}

net_pkt_set_family(pkt, AF_INET);
if (vhl == 0x40) {
return net_ipv4_input(pkt, net_pkt_is_loopback(pkt));
}

NET_DBG("Unknown IP family packet (0x%x)", NET_IPV4_HDR(pkt)->vhl & 0xf0);
net_stats_update_ip_errors_protoerr(net_pkt_iface(pkt));
net_stats_update_ip_errors_vhlerr(net_pkt_iface(pkt));

return NET_CONTINUE;
}

NET_L3_REGISTER(NULL, IPv4, NET_ETH_PTYPE_IP, ip_recv);

void net_ipv4_init(void)
{
if (IS_ENABLED(CONFIG_NET_IPV4_FRAGMENT)) {
Expand Down
20 changes: 20 additions & 0 deletions subsys/net/ip/ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,26 @@ int net_ipv6_addr_generate_iid(struct net_if *iface,
return 0;
}

static enum net_verdict ipv6_recv(struct net_if *iface, uint16_t ptype, struct net_pkt *pkt)
{
ARG_UNUSED(iface);
/* IP version and header length. */
uint8_t vtc = NET_IPV6_HDR(pkt)->vtc & 0xf0;

net_pkt_set_family(pkt, AF_INET6);
if (vtc == 0x60) {
return net_ipv6_input(pkt, net_pkt_is_loopback(pkt));
}

NET_DBG("Unknown IP family packet (0x%x)", NET_IPV6_HDR(pkt)->vtc & 0xf0);
net_stats_update_ip_errors_protoerr(net_pkt_iface(pkt));
net_stats_update_ip_errors_vhlerr(net_pkt_iface(pkt));

return NET_CONTINUE;
}

NET_L3_REGISTER(NULL, IPv6, NET_ETH_PTYPE_IPV6, ipv6_recv);

void net_ipv6_init(void)
{
net_ipv6_nbr_init();
Expand Down
107 changes: 56 additions & 51 deletions subsys/net/ip/net_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,16 @@ LOG_MODULE_REGISTER(net_core, CONFIG_NET_CORE_LOG_LEVEL);
#include "net_stats.h"

#if defined(CONFIG_NET_NATIVE)
static inline enum net_verdict process_data(struct net_pkt *pkt,
bool is_loopback)
static inline enum net_verdict process_data(struct net_pkt *pkt)
{
int ret;
bool locally_routed = false;

/* If there is no data, then drop the packet. */
if (pkt->frags == NULL) {
NET_DBG("Corrupted packet (frags %p)", pkt->frags);
net_stats_update_processing_error(net_pkt_iface(pkt));
return NET_DROP;
}

net_pkt_set_l2_processed(pkt, false);

Expand All @@ -78,34 +83,27 @@ static inline enum net_verdict process_data(struct net_pkt *pkt,
return ret;
}

/* If the packet is routed back to us when we have reassembled an IPv4 or IPv6 packet,
* then do not pass it to L2 as the packet does not have link layer headers in it.
/* If the packet is routed back to us via loopback or is an IPv4/IPv6 reassembled
* packet then it can be processed by the l3 directly.
*/
if (net_pkt_is_ip_reassembled(pkt)) {
locally_routed = true;
}

/* If there is no data, then drop the packet. */
if (!pkt->frags) {
NET_DBG("Corrupted packet (frags %p)", pkt->frags);
net_stats_update_processing_error(net_pkt_iface(pkt));

return NET_DROP;
if (net_pkt_is_loopback(pkt) || net_pkt_is_ip_reassembled(pkt)) {
goto l3_process;
}

if (!is_loopback && !locally_routed) {
ret = net_if_recv_data(net_pkt_iface(pkt), pkt);
if (ret != NET_CONTINUE) {
if (ret == NET_DROP) {
NET_DBG("Packet %p discarded by L2", pkt);
net_stats_update_processing_error(
net_pkt_iface(pkt));
}

return ret;
ret = net_if_recv_data(net_pkt_iface(pkt), pkt);
if (ret != NET_CONTINUE) {
if (ret == NET_DROP) {
NET_DBG("Packet %p discarded by L2", pkt);
net_stats_update_processing_error(net_pkt_iface(pkt));
}

return ret;
}

l3_process:
/* Start processing l3 handlers. By this point, the buffer of the
* first frag should point to the beginning of the payload.
*/
net_pkt_set_l2_processed(pkt, true);

/* L2 has modified the buffer starting point, it is easier
Expand All @@ -123,35 +121,45 @@ static inline enum net_verdict process_data(struct net_pkt *pkt,
}
}

uint8_t family = net_pkt_family(pkt);

if (IS_ENABLED(CONFIG_NET_IP) && (family == AF_INET || family == AF_INET6 ||
family == AF_UNSPEC || family == AF_PACKET)) {
/* IP version and header length. */
uint8_t vtc_vhl = NET_IPV6_HDR(pkt)->vtc & 0xf0;
STRUCT_SECTION_FOREACH(net_l3_register, l3) {
if (l3->ptype != net_pkt_ll_proto_type(pkt) ||
(l3->l2 != NULL && l3->l2 != net_pkt_orig_iface(pkt)->if_dev->l2) ||
l3->handler == NULL) {
continue;
}

if (IS_ENABLED(CONFIG_NET_IPV6) && vtc_vhl == 0x60) {
return net_ipv6_input(pkt, is_loopback);
} else if (IS_ENABLED(CONFIG_NET_IPV4) && vtc_vhl == 0x40) {
return net_ipv4_input(pkt, is_loopback);
NET_DBG("Calling L3 %s handler for type 0x%04x iface %d (%p)", l3->name,
net_pkt_ll_proto_type(pkt), net_if_get_by_iface(net_pkt_iface(pkt)),
net_pkt_iface(pkt));

ret = l3->handler(net_pkt_iface(pkt), net_pkt_ll_proto_type(pkt), pkt);
if (ret == NET_OK) {
return NET_OK;
} else if (ret == NET_DROP) {
NET_DBG("Dropping frame, packet rejected by %s", l3->name);
net_stats_update_processing_error(net_pkt_iface(pkt));
return NET_DROP;
}

NET_DBG("Unknown IP family packet (0x%x)", NET_IPV6_HDR(pkt)->vtc & 0xf0);
net_stats_update_ip_errors_protoerr(net_pkt_iface(pkt));
net_stats_update_ip_errors_vhlerr(net_pkt_iface(pkt));
return NET_DROP;
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) && family == AF_CAN) {
break;
}

uint8_t family = net_pkt_family(pkt);

if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) && family == AF_CAN) {
return net_canbus_socket_input(pkt);
}

NET_DBG("Unknown protocol family packet (0x%x)", family);
NET_DBG("Unknown hdr type 0x%04x iface %d (%p)", net_pkt_ll_proto_type(pkt),
net_if_get_by_iface(net_pkt_iface(pkt)), net_pkt_iface(pkt));
net_stats_update_processing_error(net_pkt_iface(pkt));
return NET_DROP;
}

static void processing_data(struct net_pkt *pkt, bool is_loopback)
static void processing_data(struct net_pkt *pkt)
{
again:
switch (process_data(pkt, is_loopback)) {
switch (process_data(pkt)) {
case NET_CONTINUE:
if (IS_ENABLED(CONFIG_NET_L2_VIRTUAL)) {
/* If we have a tunneling packet, feed it back
Expand Down Expand Up @@ -426,7 +434,8 @@ int net_try_send_data(struct net_pkt *pkt, k_timeout_t timeout)
* to RX processing.
*/
NET_DBG("Loopback pkt %p back to us", pkt);
processing_data(pkt, true);
net_pkt_set_loopback(pkt, true);
processing_data(pkt);
ret = 0;
goto err;
}
Expand Down Expand Up @@ -486,7 +495,6 @@ int net_try_send_data(struct net_pkt *pkt, k_timeout_t timeout)

static void net_rx(struct net_if *iface, struct net_pkt *pkt)
{
bool is_loopback = false;
size_t pkt_len;

pkt_len = net_pkt_get_len(pkt);
Expand All @@ -498,12 +506,12 @@ static void net_rx(struct net_if *iface, struct net_pkt *pkt)
if (IS_ENABLED(CONFIG_NET_LOOPBACK)) {
#ifdef CONFIG_NET_L2_DUMMY
if (net_if_l2(iface) == &NET_L2_GET_NAME(DUMMY)) {
is_loopback = true;
net_pkt_set_loopback(pkt, true);
}
#endif
}

processing_data(pkt, is_loopback);
processing_data(pkt);

net_print_statistics();
net_pkt_print();
Expand Down Expand Up @@ -584,11 +592,8 @@ int net_recv_data(struct net_if *iface, struct net_pkt *pkt)
NET_DBG("prio %d iface %p pkt %p len %zu", net_pkt_priority(pkt),
iface, pkt, net_pkt_get_len(pkt));

if (IS_ENABLED(CONFIG_NET_ROUTING)) {
net_pkt_set_orig_iface(pkt, iface);
}

net_pkt_set_iface(pkt, iface);
net_pkt_set_orig_iface(pkt, iface);

if (!net_pkt_filter_recv_ok(pkt)) {
/* Silently drop the packet, but update the statistics in order
Expand Down
1 change: 1 addition & 0 deletions subsys/net/ip/net_pkt.c
Original file line number Diff line number Diff line change
Expand Up @@ -2047,6 +2047,7 @@ static void clone_pkt_attributes(struct net_pkt *pkt, struct net_pkt *clone_pkt)
net_pkt_set_l2_bridged(clone_pkt, net_pkt_is_l2_bridged(pkt));
net_pkt_set_l2_processed(clone_pkt, net_pkt_is_l2_processed(pkt));
net_pkt_set_ll_proto_type(clone_pkt, net_pkt_ll_proto_type(pkt));
net_pkt_set_loopback(clone_pkt, net_pkt_is_loopback(pkt));

#if defined(CONFIG_NET_OFFLOAD) || defined(CONFIG_NET_L2_IPIP)
net_pkt_set_remote_address(clone_pkt, net_pkt_remote_address(pkt),
Expand Down
6 changes: 5 additions & 1 deletion subsys/net/l2/ethernet/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,15 @@ config NET_ETHERNET_BRIDGE_SHELL
Enables shell utility to manage bridge configuration interactively.

config NET_ETHERNET_FORWARD_UNRECOGNISED_ETHERTYPE
bool "Forward unrecognized EtherType frames further into net stack"
bool "Forward unrecognized EtherType frames further into net stack [DEPRECATED]"
default y if NET_SOCKETS_PACKET
select DEPRECATED
help
When enabled, the Ethernet L2 will forward even those frames for which
it does not recognize the EtherType in the header. By default, such
frames are dropped at the L2 processing.

All frames will be passed through the stack by default. Through this,
the Kconfig is deprecated and will be removed in Zephyr v4.3.

endif # NET_L2_ETHERNET
Loading