net: bridge: handle RX in bridge_input.c

Current bridge function is very simple which does forwarding for all
packets. There will be more and more features of bridge RX handling.

Let's move bridge RX handling to bridge_input.c for next IPv4/IPv6
protocols support for virtual bridge interface.

And currently there is no path to call bridge_iface_recv function.
Let's rework it for proper function returning NET_CONTINUE.

Also fixed another issue of link local address checking. Because
net_linkaddr structure changed. So fixed

is_link_local_addr((struct net_eth_addr *)net_pkt_lladdr_dst(pkt))

to

is_link_local_addr((struct net_eth_addr *)(net_pkt_lladdr_dst(pkt)->addr))

Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
diff --git a/include/zephyr/net/ethernet_bridge.h b/include/zephyr/net/ethernet_bridge.h
index 2afae63..a49236a 100644
--- a/include/zephyr/net/ethernet_bridge.h
+++ b/include/zephyr/net/ethernet_bridge.h
@@ -9,6 +9,7 @@
 /*
  * Copyright (c) 2021 BayLibre SAS
  * Copyright (c) 2024 Nordic Semiconductor
+ * SPDX-FileCopyrightText: Copyright 2025 NXP
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -178,6 +179,16 @@
 }
 
 /**
+ * @brief Process pkt received on bridged interface.
+ *
+ * @param iface Pointer to bridged iface
+ * @param pkt Pointer to pkt
+ *
+ * @return net_verdict.
+ */
+enum net_verdict eth_bridge_input_process(struct net_if *iface, struct net_pkt *pkt);
+
+/**
  * @}
  */
 
diff --git a/subsys/net/l2/ethernet/bridge/CMakeLists.txt b/subsys/net/l2/ethernet/bridge/CMakeLists.txt
index b3567e4..e5d9aef 100644
--- a/subsys/net/l2/ethernet/bridge/CMakeLists.txt
+++ b/subsys/net/l2/ethernet/bridge/CMakeLists.txt
@@ -4,4 +4,5 @@
 zephyr_library_include_directories(. ${ZEPHYR_BASE}/subsys/net/ip)
 
 zephyr_library_sources(bridge.c)
+zephyr_library_sources(bridge_input.c)
 zephyr_library_sources_ifdef(CONFIG_NET_ETHERNET_BRIDGE_SHELL bridge_shell.c)
diff --git a/subsys/net/l2/ethernet/bridge/bridge.c b/subsys/net/l2/ethernet/bridge/bridge.c
index 68384b6..db33769 100644
--- a/subsys/net/l2/ethernet/bridge/bridge.c
+++ b/subsys/net/l2/ethernet/bridge/bridge.c
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2021 BayLibre SAS
  * Copyright (c) 2024 Nordic Semiconductor
+ * SPDX-FileCopyrightText: Copyright 2025 NXP
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -234,20 +235,6 @@
 	return 0;
 }
 
-static inline bool is_link_local_addr(struct net_eth_addr *addr)
-{
-	if (addr->addr[0] == 0x01 &&
-	    addr->addr[1] == 0x80 &&
-	    addr->addr[2] == 0xc2 &&
-	    addr->addr[3] == 0x00 &&
-	    addr->addr[4] == 0x00 &&
-	    (addr->addr[5] & 0x0f) == 0x00) {
-		return true;
-	}
-
-	return false;
-}
-
 static void random_linkaddr(uint8_t *linkaddr, size_t len)
 {
 	sys_rand_get(linkaddr, len);
@@ -350,22 +337,18 @@
 	return 0;
 }
 
-static enum net_verdict bridge_iface_process(struct net_if *iface,
-					     struct net_pkt *pkt,
-					     bool is_send)
+/*
+ * For direct TX, send pkt to all ifaces.
+ * For forward TX (pkt from an original iface), send to all other ifaces.
+ */
+static enum net_verdict bridge_iface_send_process(struct net_if *iface,
+						  struct net_pkt *pkt)
 {
 	struct eth_bridge_iface_context *ctx = net_if_get_device(iface)->data;
 	struct net_if *orig_iface;
 	struct net_pkt *send_pkt = pkt;
 	int fwd_iface_num = 0;
 
-	/* Drop all link-local packets for now. */
-	if (is_link_local_addr((struct net_eth_addr *)net_pkt_lladdr_dst(pkt))) {
-		NET_DBG("DROP: lladdr");
-		net_pkt_unref(pkt);
-		return NET_OK;
-	}
-
 	lock_bridge(ctx);
 
 	orig_iface = net_pkt_orig_iface(pkt);
@@ -404,8 +387,7 @@
 			net_pkt_set_iface(send_pkt, ctx->eth_iface[i]);
 			net_if_queue_tx(ctx->eth_iface[i], send_pkt);
 
-			NET_DBG("%s iface %d pkt %p (ref %d)",
-				is_send ? "Send" : "Recv",
+			NET_DBG("Send iface %d pkt %p (ref %d)",
 				net_if_get_by_iface(ctx->eth_iface[i]),
 				send_pkt, (int)atomic_get(&send_pkt->atomic_ref));
 		}
@@ -432,24 +414,28 @@
 		net_pkt_hexdump(pkt, str);
 	}
 
-	(void)bridge_iface_process(iface, pkt, true);
+	(void)bridge_iface_send_process(iface, pkt);
 
 	return 0;
 }
 
-static enum net_verdict bridge_iface_recv(struct net_if *iface,
-					  struct net_pkt *pkt)
+static enum net_verdict bridge_iface_recv(struct net_if *iface, struct net_pkt *pkt)
 {
+	struct eth_bridge_iface_context *ctx = net_if_get_device(iface)->data;
+
 	if (DEBUG_RX) {
-		char str[sizeof("RX iface xx")];
+		char str[sizeof("RX bridge xx")];
 
-		snprintk(str, sizeof(str), "RX iface %d",
-			 net_if_get_by_iface(net_pkt_iface(pkt)));
-
+		snprintk(str, sizeof(str), "RX bridge %d", net_if_get_by_iface(iface));
 		net_pkt_hexdump(pkt, str);
 	}
 
-	return bridge_iface_process(iface, pkt, false);
+	if (!ctx->status) {
+		NET_DBG("Bridge interface %d not active", net_if_get_by_iface(iface));
+		return NET_DROP;
+	}
+
+	return NET_CONTINUE;
 }
 
 /* We cannot attach the bridge interface to Ethernet interface because
diff --git a/subsys/net/l2/ethernet/bridge/bridge_input.c b/subsys/net/l2/ethernet/bridge/bridge_input.c
new file mode 100644
index 0000000..6e1956f
--- /dev/null
+++ b/subsys/net/l2/ethernet/bridge/bridge_input.c
@@ -0,0 +1,65 @@
+/*
+ * SPDX-FileCopyrightText: Copyright 2025 NXP
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(net_eth_bridge_input, CONFIG_NET_ETHERNET_BRIDGE_LOG_LEVEL);
+
+#include <zephyr/net/ethernet_bridge.h>
+
+static int eth_bridge_forward(struct net_if *bridge, struct net_if *orig_iface, struct net_pkt *pkt)
+{
+	struct net_pkt *out_pkt;
+
+	out_pkt = net_pkt_clone(pkt, K_NO_WAIT);
+	if (out_pkt == NULL) {
+		return -ENOMEM;
+	}
+
+	net_pkt_set_l2_bridged(out_pkt, true);
+	net_pkt_set_iface(out_pkt, bridge);
+	net_pkt_set_orig_iface(out_pkt, orig_iface);
+
+	net_if_queue_tx(bridge, out_pkt);
+
+	NET_DBG("Passing rx pkt %p (orig %p) to bridge %d tx path from %d",
+		out_pkt, pkt, net_if_get_by_iface(bridge),
+		net_if_get_by_iface(orig_iface));
+
+	return 0;
+}
+
+static inline bool is_link_local_addr(struct net_eth_addr *addr)
+{
+	if (addr->addr[0] == 0x01 &&
+	    addr->addr[1] == 0x80 &&
+	    addr->addr[2] == 0xc2 &&
+	    addr->addr[3] == 0x00 &&
+	    addr->addr[4] == 0x00 &&
+	    (addr->addr[5] & 0x0f) == 0x00) {
+		return true;
+	}
+
+	return false;
+}
+
+enum net_verdict eth_bridge_input_process(struct net_if *iface, struct net_pkt *pkt)
+{
+	struct ethernet_context *ctx = net_if_l2_data(iface);
+	struct net_if *bridge = net_eth_get_bridge(ctx);
+	struct net_eth_addr *dst_addr = (struct net_eth_addr *)(net_pkt_lladdr_dst(pkt)->addr);
+
+	/* Drop all link-local packets for now. */
+	if (is_link_local_addr(dst_addr)) {
+		NET_DBG("DROP: lladdr");
+		return NET_DROP;
+	}
+
+	if (eth_bridge_forward(bridge, iface, pkt) != 0) {
+		return NET_DROP;
+	}
+
+	return NET_CONTINUE;
+}
diff --git a/subsys/net/l2/ethernet/ethernet.c b/subsys/net/l2/ethernet/ethernet.c
index 6302bfa..a54ceca 100644
--- a/subsys/net/l2/ethernet/ethernet.c
+++ b/subsys/net/l2/ethernet/ethernet.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2016-2018 Intel Corporation.
+ * SPDX-FileCopyrightText: Copyright 2025 NXP
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -276,25 +277,18 @@
 		goto drop;
 	}
 
-	if (IS_ENABLED(CONFIG_NET_ETHERNET_BRIDGE) &&
-	    net_eth_iface_is_bridged(ctx) && !net_pkt_is_l2_bridged(pkt)) {
-		struct net_if *bridge = net_eth_get_bridge(ctx);
-		struct net_pkt *out_pkt;
+	/* Set the pointers to ll src and dst addresses */
+	(void)net_linkaddr_create(net_pkt_lladdr_src(pkt), hdr->src.addr,
+				  sizeof(struct net_eth_addr), NET_LINK_ETHERNET);
 
-		out_pkt = net_pkt_clone(pkt, K_NO_WAIT);
-		if (out_pkt == NULL) {
+	(void)net_linkaddr_create(net_pkt_lladdr_dst(pkt), hdr->dst.addr,
+				  sizeof(struct net_eth_addr), NET_LINK_ETHERNET);
+
+	if (IS_ENABLED(CONFIG_NET_ETHERNET_BRIDGE) && net_eth_iface_is_bridged(ctx)) {
+		verdict = eth_bridge_input_process(iface, pkt);
+		if (verdict == NET_DROP) {
 			goto drop;
 		}
-
-		net_pkt_set_l2_bridged(out_pkt, true);
-		net_pkt_set_iface(out_pkt, bridge);
-		net_pkt_set_orig_iface(out_pkt, iface);
-
-		NET_DBG("Passing pkt %p (orig %p) to bridge %d from %d",
-			out_pkt, pkt, net_if_get_by_iface(bridge),
-			net_if_get_by_iface(iface));
-
-		(void)net_if_queue_tx(bridge, out_pkt);
 	}
 
 	type = net_ntohs(hdr->type);
@@ -343,13 +337,6 @@
 		}
 	}
 
-	/* Set the pointers to ll src and dst addresses */
-	(void)net_linkaddr_create(net_pkt_lladdr_src(pkt), hdr->src.addr,
-				  sizeof(struct net_eth_addr), NET_LINK_ETHERNET);
-
-	(void)net_linkaddr_create(net_pkt_lladdr_dst(pkt), hdr->dst.addr,
-				  sizeof(struct net_eth_addr), NET_LINK_ETHERNET);
-
 	lladdr = net_pkt_lladdr_dst(pkt);
 
 	net_pkt_set_ll_proto_type(pkt, type);