Repository: zephyrproject-rtos/zephyr
Pull request #89204: net: sockets: packet: Make it possible to receive packets when unbound 
Author: login: rlubos
Labels: name: area: Networking
name: area: Sockets
description: Networking sockets
Created: 2025-04-28T13:39:42Z | Merged: 2025-04-30T14:26:16Z

Packet sockets should receive packets from all interfaces if not explicitly bound. This PR introduces changes needed to make this happen and associated test cases.

Fixes #89047

Linked issue #89047: Packet sockets require binding
<!--
**Notes**
Github Discussions (https://github.com/zephyrproject-rtos/zephyr/discussions)
are available to first verify that the issue is a genuine Zephyr bug and not a
consequence of Zephyr services misuse.

This issue list is only for bugs in the main Zephyr code base
(https://github.com/zephyrproject-rtos/zephyr/). If the bug is for a project
fork (such as NCS) specific feature, please open an issue in the fork project
instead.
-->

**Describe the bug**
<!--
A clear and concise description of what the bug is.

Please also mention any information which could help others to understand
the problem you're facing:
 - What target platform are you using?
 - What have you tried to diagnose or workaround this issue?
 - Is this a regression? If yes, have you been able to "git bisect" it to a
   specific commit?
 - ...
-->
Using the packet sockets require binding of the socket to an interface to receive packets. See https://github.com/zephyrproject-rtos/zephyr/blob/b469fc188b4fc87c64e598618c07d717bc570c9d/subsys/net/ip/connection.c#L686

**To Reproduce**
<!--
Steps to reproduce the behavior:
1. mkdir build; cd build
2. cmake -DBOARD=board\_xyz
3. make
4. See error
-->
Create a packet socket and try to receive a packet without binding. 

**Expected behavior**
<!--
A clear and concise description of what you expected to happen.
-->
Packets from all Interfaces should be received.

**Impact**
<!--
What impact does this issue have on your progress (e.g., annoyance, showstopper)
-->
Medium 

**Logs and console output**
<!--
If applicable, add console logs or other types of debug information
e.g Wireshark capture or Logic analyzer capture (upload in zip archive).
copy-and-paste text and put a code fence (\`\`\`) before and after, to help
explain the issue. (if unable to obtain text log, add a screenshot)
-->

**Environment (please complete the following information):**

 - OS: Linux
 - Toolchain clang
 - Commit SHA main

**Additional context**
<!--
Add any other context that could be relevant to your issue, such as pin setting,
target configuration, ...
-->


Linked issue comments: user: login: go2sh
created_at: 2025-04-24T16:06:20Z
body: Cc @rlubos 
author_association: CONTRIBUTOR

Diff:
diff --git a/subsys/net/ip/connection.c b/subsys/net/ip/connection.c
index caab187e3f65c..e3d4bd8b58b1c 100644
--- a/subsys/net/ip/connection.c
+++ b/subsys/net/ip/connection.c
@@ -322,48 +322,22 @@ static int net_conn_change_remote(struct net_conn *conn,
 	return 0;
 }
 
-int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
-		      const struct sockaddr *remote_addr,
-		      const struct sockaddr *local_addr,
-		      uint16_t remote_port,
-		      uint16_t local_port,
-		      struct net_context *context,
-		      net_conn_cb_t cb,
-		      void *user_data,
-		      struct net_conn_handle **handle)
+static int net_conn_change_local(struct net_conn *conn,
+				 const struct sockaddr *local_addr,
+				 uint16_t local_port)
 {
-	struct net_conn *conn;
-	uint8_t flags = 0U;
-	int ret;
-
-	conn = conn_find_handler(context != NULL ? net_context_get_iface(context) : NULL,
-				 proto, family, remote_addr, local_addr,
-				 remote_port, local_port,
-				 context != NULL ?
-					net_context_is_reuseport_set(context) :
-					false);
-	if (conn) {
-		NET_ERR("Identical connection handler %p already found.", conn);
-		return -EADDRINUSE;
-	}
-
-	conn = conn_get_unused();
-	if (!conn) {
-		NET_ERR("Not enough connection contexts. "
-			"Consider increasing CONFIG_NET_MAX_CONN.");
-		return -ENOENT;
-	}
+	NET_DBG("[%zu] connection handler %p changed local",
+		conn - conns, conn);
 
-	if (local_addr) {
+	if (local_addr != NULL) {
 		if (IS_ENABLED(CONFIG_NET_IPV6) &&
 		    local_addr->sa_family == AF_INET6) {
 			memcpy(&conn->local_addr, local_addr,
 			       sizeof(struct sockaddr_in6));
 
 			if (!net_ipv6_is_addr_unspecified(
-				    &net_sin6(local_addr)->
-				    sin6_addr)) {
-				flags |= NET_CONN_LOCAL_ADDR_SPEC;
+					&net_sin6(local_addr)->sin6_addr)) {
+				conn->flags |= NET_CONN_LOCAL_ADDR_SPEC;
 			}
 		} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
 			   local_addr->sa_family == AF_INET) {
@@ -371,7 +345,7 @@ int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
 			       sizeof(struct sockaddr_in));
 
 			if (net_sin(local_addr)->sin_addr.s_addr) {
-				flags |= NET_CONN_LOCAL_ADDR_SPEC;
+				conn->flags |= NET_CONN_LOCAL_ADDR_SPEC;
 			}
 		} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) &&
 			   local_addr->sa_family == AF_CAN) {
@@ -383,10 +357,54 @@ int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
 			       sizeof(struct sockaddr_ll));
 		} else {
 			NET_ERR("Local address family not set");
-			goto error;
+			return -EINVAL;
 		}
 
-		flags |= NET_CONN_LOCAL_ADDR_SET;
+		conn->flags |= NET_CONN_LOCAL_ADDR_SET;
+	} else {
+		conn->flags &= ~NET_CONN_LOCAL_ADDR_SPEC;
+		conn->flags &= ~NET_CONN_LOCAL_ADDR_SET;
+	}
+
+	if (local_port > 0U) {
+		conn->flags |= NET_CONN_LOCAL_PORT_SPEC;
+		net_sin(&conn->local_addr)->sin_port = htons(local_port);
+	} else {
+		conn->flags &= ~NET_CONN_LOCAL_PORT_SPEC;
+	}
+
+	return 0;
+}
+
+int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
+		      const struct sockaddr *remote_addr,
+		      const struct sockaddr *local_addr,
+		      uint16_t remote_port,
+		      uint16_t local_port,
+		      struct net_context *context,
+		      net_conn_cb_t cb,
+		      void *user_data,
+		      struct net_conn_handle **handle)
+{
+	struct net_conn *conn;
+	int ret;
+
+	conn = conn_find_handler(context != NULL ? net_context_get_iface(context) : NULL,
+				 proto, family, remote_addr, local_addr,
+				 remote_port, local_port,
+				 context != NULL ?
+					net_context_is_reuseport_set(context) :
+					false);
+	if (conn != NULL) {
+		NET_ERR("Identical connection handler %p already found.", conn);
+		return -EADDRINUSE;
+	}
+
+	conn = conn_get_unused();
+	if (conn == NULL) {
+		NET_ERR("Not enough connection contexts. "
+			"Consider increasing CONFIG_NET_MAX_CONN.");
+		return -ENOENT;
 	}
 
 	if (remote_addr && local_addr) {
@@ -396,25 +414,20 @@ int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
 		}
 	}
 
-	if (local_port) {
-		flags |= NET_CONN_LOCAL_PORT_SPEC;
-		net_sin(&conn->local_addr)->sin_port = htons(local_port);
-	}
-
 	net_conn_change_callback(conn, cb, user_data);
 
-	conn->flags = flags;
 	conn->proto = proto;
 	conn->type = type;
 	conn->family = family;
 	conn->context = context;
 
-	/*
-	 * Since the net_conn_change_remote() updates the flags in connection,
-	 * must to be called after set the flags to connection.
-	 */
+	ret = net_conn_change_local(conn, local_addr, local_port);
+	if (ret < 0) {
+		goto error;
+	}
+
 	ret = net_conn_change_remote(conn, remote_addr, remote_port);
-	if (ret) {
+	if (ret < 0) {
 		goto error;
 	}
 
@@ -461,7 +474,9 @@ int net_conn_update(struct net_conn_handle *handle,
 		    net_conn_cb_t cb,
 		    void *user_data,
 		    const struct sockaddr *remote_addr,
-		    uint16_t remote_port)
+		    uint16_t remote_port,
+		    const struct sockaddr *local_addr,
+		    uint16_t local_port)
 {
 	struct net_conn *conn = (struct net_conn *)handle;
 	int ret;
@@ -476,6 +491,11 @@ int net_conn_update(struct net_conn_handle *handle,
 
 	net_conn_change_callback(conn, cb, user_data);
 
+	ret = net_conn_change_local(conn, local_addr, local_port);
+	if (ret < 0) {
+		return ret;
+	}
+
 	ret = net_conn_change_remote(conn, remote_addr, remote_port);
 
 	return ret;
@@ -679,13 +699,13 @@ enum net_verdict net_conn_packet_input(struct net_pkt *pkt, uint16_t proto)
 
 		/* Apply protocol-specific matching criteria... */
 
-		if (!(conn->flags & NET_CONN_LOCAL_ADDR_SET)) {
-			continue;
-		}
-
-		local = (struct sockaddr_ll *)&conn->local_addr;
-		if (local->sll_ifindex != net_if_get_by_iface(net_pkt_iface(pkt))) {
-			continue;
+		/* Unbound sockets should collect packets from all interfaces. */
+		if ((conn->flags & NET_CONN_LOCAL_ADDR_SET) != 0U) {
+			local = (struct sockaddr_ll *)&conn->local_addr;
+			if (local->sll_ifindex != 0 &&
+			    local->sll_ifindex != net_if_get_by_iface(net_pkt_iface(pkt))) {
+				continue;
+			}
 		}
 
 		conn_raw_socket_deliver(pkt, conn, false);
@@ -755,7 +775,7 @@ enum net_verdict net_conn_raw_ip_input(struct net_pkt *pkt,
 
 		/* Apply protocol-specific matching criteria... */
 
-		if ((conn->flags & NET_CONN_LOCAL_ADDR_SET) &&
+		if (((conn->flags & NET_CONN_LOCAL_ADDR_SET) != 0U) &&
 		    !conn_addr_cmp(pkt, ip_hdr, &conn->local_addr, false)) {
 			continue; /* wrong local address */
 		}
diff --git a/subsys/net/ip/connection.h b/subsys/net/ip/connection.h
index ab0afc8101fba..a6660cad6bbca 100644
--- a/subsys/net/ip/connection.h
+++ b/subsys/net/ip/connection.h
@@ -170,6 +170,8 @@ static inline int net_conn_unregister(struct net_conn_handle *handle)
  * @param user_data User data supplied by caller.
  * @param remote_addr Remote address
  * @param remote_port Remote port
+ * @param local_addr Local address
+ * @param local_port Local port
  *
  * @return Return 0 if the change succeed, <0 otherwise.
  */
@@ -177,7 +179,9 @@ int net_conn_update(struct net_conn_handle *handle,
 		    net_conn_cb_t cb,
 		    void *user_data,
 		    const struct sockaddr *remote_addr,
-		    uint16_t remote_port);
+		    uint16_t remote_port,
+		    const struct sockaddr *local_addr,
+		    uint16_t local_port);
 
 /**
  * @brief Called by net_core.c when a network packet is received
diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c
index 0890a22764c6a..366ab7aa28a38 100644
--- a/subsys/net/ip/net_context.c
+++ b/subsys/net/ip/net_context.c
@@ -770,13 +770,9 @@ static int bind_default(struct net_context *context)
 			return 0;
 		}
 
-		if (iface == NULL) {
-			iface = net_if_get_default();
-		}
-
 		ll_addr.sll_family = AF_PACKET;
-		ll_addr.sll_protocol = htons(ETH_P_ALL);
-		ll_addr.sll_ifindex = net_if_get_by_iface(iface);
+		ll_addr.sll_protocol = htons(net_context_get_proto(context));
+		ll_addr.sll_ifindex = (iface == NULL) ? 0 : net_if_get_by_iface(iface);
 
 		return net_context_bind(context, (struct sockaddr *)&ll_addr,
 					sizeof(ll_addr));
@@ -867,11 +863,13 @@ int net_context_bind(struct net_context *context, const struct sockaddr *addr,
 	/* If we already have connection handler, then it effectively
 	 * means that it's already bound to an interface/port, and we
 	 * don't support rebinding connection to new address/port in
-	 * the code below.
+	 * the code below. Doesn't apply for packet sockets.
 	 * TODO: Support rebinding.
 	 */
-	if (context->conn_handler) {
-		return -EISCONN;
+	if (addr->sa_family != AF_PACKET) {
+		if (context->conn_handler != NULL) {
+			return -EISCONN;
+		}
 	}
 
 	if (IS_ENABLED(CONFIG_NET_IPV6) && addr->sa_family == AF_INET6) {
@@ -1103,13 +1101,8 @@ int net_context_bind(struct net_context *context, const struct sockaddr *addr,
 		}
 
 		iface = net_if_get_by_index(ll_addr->sll_ifindex);
-		if (!iface) {
-			NET_ERR("Cannot bind to interface index %d",
-				ll_addr->sll_ifindex);
-			return -EADDRNOTAVAIL;
-		}
 
-		if (IS_ENABLED(CONFIG_NET_OFFLOAD) &&
+		if (IS_ENABLED(CONFIG_NET_OFFLOAD) && iface != NULL &&
 		    net_if_is_ip_offloaded(iface)) {
 			net_context_set_iface(context, iface);
 
@@ -1121,20 +1114,22 @@ int net_context_bind(struct net_context *context, const struct sockaddr *addr,
 
 		k_mutex_lock(&context->lock, K_FOREVER);
 
-		net_context_set_iface(context, iface);
-
 		net_sll_ptr(&context->local)->sll_family = AF_PACKET;
 		net_sll_ptr(&context->local)->sll_ifindex =
 			ll_addr->sll_ifindex;
 		net_sll_ptr(&context->local)->sll_protocol =
 			ll_addr->sll_protocol;
 
-		net_if_lock(iface);
-		net_sll_ptr(&context->local)->sll_addr =
-			net_if_get_link_addr(iface)->addr;
-		net_sll_ptr(&context->local)->sll_halen =
-			net_if_get_link_addr(iface)->len;
-		net_if_unlock(iface);
+		if (iface != NULL) {
+			net_context_set_iface(context, iface);
+
+			net_if_lock(iface);
+			net_sll_ptr(&context->local)->sll_addr =
+				net_if_get_link_addr(iface)->addr;
+			net_sll_ptr(&context->local)->sll_halen =
+				net_if_get_link_addr(iface)->len;
+			net_if_unlock(iface);
+		}
 
 		NET_DBG("Context %p bind to type 0x%04x iface[%d] %p addr %s",
 			context, htons(net_context_get_proto(context)),
@@ -2948,26 +2943,6 @@ static int recv_dgram(struct net_context *context,
 
 	ARG_UNUSED(timeout);
 
-	/* If the context already has a connection handler, it means it's
-	 * already registered. In that case, all we have to do is 1) update
-	 * the callback registered in the net_context and 2) update the
-	 * user_data and remote address and port using net_conn_update().
-	 *
-	 * The callback function passed to net_conn_update() must be the same
-	 * function as the one passed to net_conn_register(), not the callback
-	 * set for the net context passed by recv_udp().
-	 */
-	if (context->conn_handler) {
-		context->recv_cb = cb;
-		ret = net_conn_update(context->conn_handler,
-				      net_context_packet_received,
-				      user_data,
-				      context->flags & NET_CONTEXT_REMOTE_ADDR_SET ?
-						&context->remote : NULL,
-				      ntohs(net_sin(&context->remote)->sin_port));
-		return ret;
-	}
-
 	ret = bind_default(context);
 	if (ret) {
 		return ret;
@@ -2999,6 +2974,26 @@ static int recv_dgram(struct net_context *context,
 
 	context->recv_cb = cb;
 
+	/* If the context already has a connection handler, it means it's
+	 * already registered. In that case, all we have to do is 1) update
+	 * the callback registered in the net_context and 2) update the
+	 * user_data and local/remote address and port using net_conn_update().
+	 *
+	 * The callback function passed to net_conn_update() must be the same
+	 * function as the one passed to net_conn_register(), not the callback
+	 * set for the net context passed by recv_udp().
+	 */
+	if (context->conn_handler != NULL) {
+		ret = net_conn_update(context->conn_handler,
+				      net_context_packet_received,
+				      user_data,
+				      context->flags & NET_CONTEXT_REMOTE_ADDR_SET ?
+						&context->remote : NULL,
+				      ntohs(net_sin(&context->remote)->sin_port),
+				      laddr, ntohs(lport));
+		return ret;
+	}
+
 	ret = net_conn_register(net_context_get_proto(context),
 				net_context_get_type(context),
 				net_context_get_family(context),
@@ -3060,10 +3055,12 @@ static int recv_raw(struct net_context *context,
 
 	ARG_UNUSED(timeout);
 
+	context->recv_cb = cb;
+
 	/* If the context already has a connection handler, it means it's
 	 * already registered. In that case, all we have to do is 1) update
 	 * the callback registered in the net_context and 2) update the
-	 * user_data using net_conn_update().
+	 * user_data and local address using net_conn_update().
 	 *
 	 * The callback function passed to net_conn_update() must be the same
 	 * function as the one passed to net_conn_register(), not the callback
@@ -3074,17 +3071,10 @@ static int recv_raw(struct net_context *context,
 		ret = net_conn_update(context->conn_handler,
 				      net_context_raw_packet_received,
 				      user_data,
-				      NULL, 0);
-		return ret;
-	}
-
-	ret = bind_default(context);
-	if (ret) {
+				      NULL, 0, local_addr, 0);
 		return ret;
 	}
 
-	context->recv_cb = cb;
-
 	ret = net_conn_register(net_context_get_proto(context),
 				net_context_get_type(context),
 				net_context_get_family(context),
@@ -3137,7 +3127,12 @@ int net_context_recv(struct net_context *context,
 	} else {
 		if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
 		    family == AF_PACKET) {
-			struct sockaddr_ll addr;
+			struct sockaddr_ll addr = { 0 };
+
+			ret = bind_default(context);
+			if (ret < 0) {
+				goto unlock;
+			}
 
 			addr.sll_family = AF_PACKET;
 			addr.sll_ifindex =
@@ -3147,9 +3142,11 @@ int net_context_recv(struct net_context *context,
 			addr.sll_halen =
 				net_sll_ptr(&context->local)->sll_halen;
 
-			memcpy(addr.sll_addr,
-			       net_sll_ptr(&context->local)->sll_addr,
-			       MIN(addr.sll_halen, sizeof(addr.sll_addr)));
+			if (net_sll_ptr(&context->local)->sll_addr != NULL) {
+				memcpy(addr.sll_addr,
+				       net_sll_ptr(&context->local)->sll_addr,
+				       MIN(addr.sll_halen, sizeof(addr.sll_addr)));
+			}
 
 			ret = recv_raw(context, cb, timeout,
 				       (struct sockaddr *)&addr, user_data);
@@ -3159,6 +3156,11 @@ int net_context_recv(struct net_context *context,
 				.can_family = AF_CAN,
 			};
 
+			ret = bind_default(context);
+			if (ret < 0) {
+				goto unlock;
+			}
+
 			ret = recv_raw(context, cb, timeout,
 				       (struct sockaddr *)&local_addr,
 				       user_data);
diff --git a/subsys/net/lib/sockets/sockets_packet.c b/subsys/net/lib/sockets/sockets_packet.c
index 508520c430d64..5ea90a00272e8 100644
--- a/subsys/net/lib/sockets/sockets_packet.c
+++ b/subsys/net/lib/sockets/sockets_packet.c
@@ -40,6 +40,43 @@ static inline int k_fifo_wait_non_empty(struct k_fifo *fifo,
 	return k_poll(events, ARRAY_SIZE(events), timeout);
 }
 
+static void zpacket_received_cb(struct net_context *ctx,
+				struct net_pkt *pkt,
+				union net_ip_header *ip_hdr,
+				union net_proto_header *proto_hdr,
+				int status,
+				void *user_data)
+{
+	NET_DBG("ctx=%p, pkt=%p, st=%d, user_data=%p", ctx, pkt, status,
+		user_data);
+
+	/* if pkt is NULL, EOF */
+	if (pkt == NULL) {
+		struct net_pkt *last_pkt = k_fifo_peek_tail(&ctx->recv_q);
+
+		if (last_pkt == NULL) {
+			/* If there're no packets in the queue, recv() may
+			 * be blocked waiting on it to become non-empty,
+			 * so cancel that wait.
+			 */
+			sock_set_eof(ctx);
+			k_fifo_cancel_wait(&ctx->recv_q);
+			NET_DBG("Marked socket %p as peer-closed", ctx);
+		} else {
+			net_pkt_set_eof(last_pkt, true);
+			NET_DBG("Set EOF flag on pkt %p", ctx);
+		}
+
+		return;
+	}
+
+	/* Normal packet */
+	net_pkt_set_eof(pkt, false);
+
+	k_fifo_put(&ctx->recv_q, pkt);
+}
+
+
 static int zpacket_socket(int family, int type, int proto)
 {
 	struct net_context *ctx;
@@ -73,46 +110,23 @@ static int zpacket_socket(int family, int type, int proto)
 
 	/* recv_q and accept_q are in union */
 	k_fifo_init(&ctx->recv_q);
-	zvfs_finalize_typed_fd(fd, ctx, (const struct fd_op_vtable *)&packet_sock_fd_op_vtable,
-			    ZVFS_MODE_IFSOCK);
-
-	return fd;
-}
-
-static void zpacket_received_cb(struct net_context *ctx,
-				struct net_pkt *pkt,
-				union net_ip_header *ip_hdr,
-				union net_proto_header *proto_hdr,
-				int status,
-				void *user_data)
-{
-	NET_DBG("ctx=%p, pkt=%p, st=%d, user_data=%p", ctx, pkt, status,
-		user_data);
-
-	/* if pkt is NULL, EOF */
-	if (!pkt) {
-		struct net_pkt *last_pkt = k_fifo_peek_tail(&ctx->recv_q);
 
-		if (!last_pkt) {
-			/* If there're no packets in the queue, recv() may
-			 * be blocked waiting on it to become non-empty,
-			 * so cancel that wait.
-			 */
-			sock_set_eof(ctx);
-			k_fifo_cancel_wait(&ctx->recv_q);
-			NET_DBG("Marked socket %p as peer-closed", ctx);
-		} else {
-			net_pkt_set_eof(last_pkt, true);
-			NET_DBG("Set EOF flag on pkt %p", ctx);
-		}
-
-		return;
+	/* Register the callback so that the socket is able to receive packets
+	 * as soon as it's created.
+	 */
+	ret = net_context_recv(ctx, zpacket_received_cb, K_NO_WAIT,
+			       ctx->user_data);
+	if (ret < 0) {
+		net_context_put(ctx);
+		zvfs_free_fd(fd);
+		errno = -ret;
+		return -1;
 	}
 
-	/* Normal packet */
-	net_pkt_set_eof(pkt, false);
+	zvfs_finalize_typed_fd(fd, ctx, (const struct fd_op_vtable *)&packet_sock_fd_op_vtable,
+			       ZVFS_MODE_IFSOCK);
 
-	k_fifo_put(&ctx->recv_q, pkt);
+	return fd;
 }
 
 static int zpacket_bind_ctx(struct net_context *ctx,
@@ -234,7 +248,6 @@ ssize_t zpacket_sendto_ctx(struct net_context *ctx, const void *buf, size_t len,
 			   int flags, const struct sockaddr *dest_addr,
 			   socklen_t addrlen)
 {
-	const struct sockaddr_ll *ll_addr = (const struct sockaddr_ll *)dest_addr;
 	k_timeout_t timeout = K_FOREVER;
 	int status;
 
@@ -249,32 +262,6 @@ ssize_t zpacket_sendto_ctx(struct net_context *ctx, const void *buf, size_t len,
 		net_context_get_option(ctx, NET_OPT_SNDTIMEO, &timeout, NULL);
 	}
 
-	/* If no interface was set on the context yet, use the one provided in
-	 * the destination address for default binding, unless 0 (any interface)
-	 * was set, in that case let the stack choose the default.
-	 */
-	if (net_context_get_iface(ctx) == NULL && ll_addr->sll_ifindex != 0) {
-		struct net_if *iface = net_if_get_by_index(ll_addr->sll_ifindex);
-
-		if (iface == NULL) {
-			errno = EDESTADDRREQ;
-			return -1;
-		}
-
-		net_context_set_iface(ctx, iface);
-	}
-
-	/* Register the callback before sending in order to receive the response
-	 * from the peer.
-	 */
-
-	status = net_context_recv(ctx, zpacket_received_cb, K_NO_WAIT,
-				  ctx->user_data);
-	if (status < 0) {
-		errno = -status;
-		return -1;
-	}
-
 	status = net_context_sendto(ctx, buf, len, dest_addr, addrlen,
 				    NULL, timeout, ctx->user_data);
 	if (status < 0) {
diff --git a/tests/net/socket/af_packet/src/main.c b/tests/net/socket/af_packet/src/main.c
index 68b5c58e79ffc..cc5ec997556b6 100644
--- a/tests/net/socket/af_packet/src/main.c
+++ b/tests/net/socket/af_packet/src/main.c
@@ -72,12 +72,16 @@ LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
  *   * (AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) - The packet is received for all protocols.
  *     The L2 header is not removed from the data:
  *     - test_raw_sock_recv_proto_wildcard
+ *     - test_raw_sock_recv_proto_wildcard_bound_other_iface
  *     - test_raw_sock_recvfrom_proto_wildcard
+ *     - test_raw_sock_recvfrom_proto_wildcard_unbound
  *
  *   * (AF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL)) - The packet is received for all protocols.
  *     The L2 header is removed from the data:
  *     - test_dgram_sock_recv_proto_wildcard
+ *     - test_dgram_sock_recv_proto_wildcard_bound_other_iface
  *     - test_dgram_sock_recvfrom_proto_wildcard
+ *     - test_dgram_sock_recvfrom_proto_wildcard_unbound
  */
 
 #if defined(CONFIG_NET_SOCKETS_LOG_LEVEL_DBG)
@@ -128,14 +132,16 @@ static int eth_fake_send(const struct device *dev, struct net_pkt *pkt)
 	DBG("Sending data (%d bytes) to iface %d\n",
 	    net_pkt_get_len(pkt), net_if_get_by_iface(net_pkt_iface(pkt)));
 
-	recv_pkt = net_pkt_rx_clone(pkt, K_NO_WAIT);
-
 	if (memcmp(pkt->frags->data, lladdr1, sizeof(lladdr1)) == 0) {
 		target_iface = eth_fake_data1.iface;
-	} else {
+	} else if (memcmp(pkt->frags->data, lladdr2, sizeof(lladdr2)) == 0) {
 		target_iface = eth_fake_data2.iface;
+	} else {
+		return 0;
 	}
 
+	recv_pkt = net_pkt_rx_clone(pkt, K_NO_WAIT);
+
 	net_pkt_set_iface(recv_pkt, target_iface);
 
 	k_sleep(K_MSEC(10)); /* Let the receiver run */
@@ -179,8 +185,7 @@ ETH_NET_DEVICE_INIT(eth_fake2, "eth_fake2", NULL, NULL, &eth_fake_data2, NULL,
 		    CONFIG_ETH_INIT_PRIORITY, &eth_fake_api_funcs,
 		    NET_ETH_MTU);
 
-static void setup_packet_socket(int *sock, struct net_if *iface, int type,
-				int proto)
+static void setup_packet_socket(int *sock, int type, int proto)
 {
 	struct timeval optval = {
 		.tv_usec = 100000,
@@ -202,7 +207,7 @@ static void bind_packet_socket(int sock, struct net_if *iface)
 
 	memset(&addr, 0, sizeof(addr));
 
-	addr.sll_ifindex = net_if_get_by_iface(iface);
+	addr.sll_ifindex = (iface == NULL) ? 0 : net_if_get_by_iface(iface);
 	addr.sll_family = AF_PACKET;
 
 	ret = zsock_bind(sock, (struct sockaddr *)&addr, sizeof(addr));
@@ -212,7 +217,7 @@ static void bind_packet_socket(int sock, struct net_if *iface)
 static void prepare_packet_socket(int *sock, struct net_if *iface, int type,
 				  int proto)
 {
-	setup_packet_socket(sock, iface, type, proto);
+	setup_packet_socket(sock, type, proto);
 	bind_packet_socket(*sock, iface);
 }
 
@@ -657,7 +662,7 @@ static void test_sendto_common(int sock_type, int proto, bool do_bind,
 	uint16_t pkt_len;
 	int ret;
 
-	setup_packet_socket(&packet_sock_1, ud.second, sock_type, htons(proto));
+	setup_packet_socket(&packet_sock_1, sock_type, htons(proto));
 	if (do_bind) {
 		bind_packet_socket(packet_sock_1, ud.second);
 	}
@@ -717,7 +722,7 @@ static void test_sendmsg_common(int sock_type, int proto)
 	uint16_t pkt_len;
 	int ret;
 
-	setup_packet_socket(&packet_sock_1, ud.second, sock_type, htons(proto));
+	setup_packet_socket(&packet_sock_1, sock_type, htons(proto));
 	prepare_udp_socket(&udp_sock_1, &ip_src, DST_PORT);
 	prepare_test_packet(sock_type, ETH_P_IP, lladdr2, lladdr1, &pkt_len);
 	prepare_test_dst_lladdr(&ll_dst, ETH_P_IP, lladdr1, ud.second);
@@ -810,11 +815,11 @@ static void test_recv_common(int sock_type, int proto, bool success)
 	int ret;
 
 	/* Transmitting sock */
-	setup_packet_socket(&packet_sock_1, ud.second, SOCK_RAW, 0);
+	setup_packet_socket(&packet_sock_1, SOCK_RAW, 0);
 	prepare_test_packet(SOCK_RAW, ETH_P_IP, lladdr2, lladdr1, &pkt_len);
 	prepare_test_dst_lladdr(&ll_dst, ETH_P_IP, lladdr1, ud.second);
 	/* Receiving sock */
-	setup_packet_socket(&packet_sock_2, ud.first, sock_type, htons(proto));
+	setup_packet_socket(&packet_sock_2, sock_type, htons(proto));
 	bind_packet_socket(packet_sock_2, ud.first);
 
 	ret = zsock_sendto(packet_sock_1, tx_buf, pkt_len, 0,
@@ -867,6 +872,20 @@ ZTEST(socket_packet, test_raw_sock_recv_proto_wildcard)
 	test_recv_common(SOCK_RAW, ETH_P_ALL, true);
 }
 
+static void validate_recvfrom_addr(struct sockaddr_ll *ll_rx, socklen_t addrlen,
+				   int iface, uint8_t *lladdr)
+{
+	zassert_equal(addrlen, sizeof(struct sockaddr_ll),
+		      "Invalid address length (%u)", addrlen);
+	zassert_equal(ll_rx->sll_family, AF_PACKET, "Invalid family");
+	zassert_equal(ll_rx->sll_protocol, htons(ETH_P_IP), "Invalid protocol");
+	zassert_equal(ll_rx->sll_ifindex, iface, "Invalid interface");
+	zassert_equal(ll_rx->sll_hatype, ARPHRD_ETHER, "Invalid hardware type");
+	zassert_equal(ll_rx->sll_pkttype, PACKET_OTHERHOST, "Invalid packet type");
+	zassert_equal(ll_rx->sll_halen, NET_ETH_ADDR_LEN, "Invalid address length");
+	zassert_mem_equal(ll_rx->sll_addr, lladdr, NET_ETH_ADDR_LEN, "Invalid address");
+}
+
 static void test_recvfrom_common(int sock_type, int proto)
 {
 	struct sockaddr_ll ll_dst;
@@ -877,11 +896,11 @@ static void test_recvfrom_common(int sock_type, int proto)
 	int ret;
 
 	/* Transmitting sock */
-	setup_packet_socket(&packet_sock_1, ud.second, SOCK_RAW, 0);
+	setup_packet_socket(&packet_sock_1, SOCK_RAW, 0);
 	prepare_test_packet(SOCK_RAW, ETH_P_IP, lladdr2, lladdr1, &pkt_len);
 	prepare_test_dst_lladdr(&ll_dst, ETH_P_IP, lladdr1, ud.second);
 	/* Receiving sock */
-	setup_packet_socket(&packet_sock_2, ud.first, sock_type, htons(proto));
+	setup_packet_socket(&packet_sock_2, sock_type, htons(proto));
 	bind_packet_socket(packet_sock_2, ud.first);
 
 	ret = zsock_sendto(packet_sock_1, tx_buf, pkt_len, 0,
@@ -894,6 +913,8 @@ static void test_recvfrom_common(int sock_type, int proto)
 		offset = sizeof(struct net_eth_hdr);
 	}
 
+	pkt_len -= offset;
+
 	ret = zsock_recvfrom(packet_sock_2, rx_buf, sizeof(rx_buf), 0,
 			     (struct sockaddr *)&ll_rx, &addrlen);
 	zassert_not_equal(ret, -1, "Failed to receive packet (%d)", errno);
@@ -904,15 +925,8 @@ static void test_recvfrom_common(int sock_type, int proto)
 			  "Invalid payload received");
 	zassert_equal(addrlen, sizeof(struct sockaddr_ll),
 		      "Invalid address length (%u)", addrlen);
-	zassert_equal(ll_rx.sll_family, AF_PACKET, "Invalid family");
-	zassert_equal(ll_rx.sll_protocol, htons(ETH_P_IP), "Invalid protocol");
-	zassert_equal(ll_rx.sll_ifindex, net_if_get_by_iface(ud.first),
-		      "Invalid interface");
-	zassert_equal(ll_rx.sll_hatype, ARPHRD_ETHER, "Invalid hardware type");
-	zassert_equal(ll_rx.sll_pkttype, PACKET_OTHERHOST, "Invalid packet type");
-	zassert_equal(ll_rx.sll_halen, NET_ETH_ADDR_LEN, "Invalid address length");
-	zassert_mem_equal(ll_rx.sll_addr, &lladdr2, NET_ETH_ADDR_LEN,
-			  "Invalid address");
+	validate_recvfrom_addr(&ll_rx, addrlen, net_if_get_by_iface(ud.first),
+			       lladdr2);
 }
 
 ZTEST(socket_packet, test_raw_sock_recvfrom_proto_wildcard)
@@ -927,7 +941,148 @@ ZTEST(socket_packet, test_dgram_sock_recv_proto_wildcard)
 
 ZTEST(socket_packet, test_dgram_sock_recvfrom_proto_wildcard)
 {
-	test_recvfrom_common(SOCK_RAW, ETH_P_ALL);
+	test_recvfrom_common(SOCK_DGRAM, ETH_P_ALL);
+}
+
+static void test_recvfrom_unbound_round(int tx_sock, int rx_sock, int sock_type,
+					uint8_t *src_addr, struct net_if *src_iface,
+					uint8_t *dst_addr, struct net_if *dst_iface)
+{
+	uint16_t offset = (sock_type == SOCK_DGRAM) ? sizeof(struct net_eth_hdr) : 0;
+	struct sockaddr_ll ll_dst;
+	struct sockaddr_ll ll_rx = { 0 };
+	socklen_t addrlen = sizeof(ll_rx);
+	uint16_t pkt_len;
+	int ret;
+
+	prepare_test_packet(SOCK_RAW, ETH_P_IP, src_addr, dst_addr, &pkt_len);
+	prepare_test_dst_lladdr(&ll_dst, ETH_P_IP, dst_addr, src_iface);
+
+	ret = zsock_sendto(packet_sock_1, tx_buf, pkt_len, 0,
+			   (struct sockaddr *)&ll_dst,
+			   sizeof(struct sockaddr_ll));
+	zassert_not_equal(ret, -1, "Failed to send (%d)", errno);
+	zassert_equal(ret, pkt_len, "Invalid data length sent (%d/%d)", ret, pkt_len);
+
+	pkt_len -= offset;
+
+	ret = zsock_recvfrom(packet_sock_2, rx_buf, sizeof(rx_buf), 0,
+			     (struct sockaddr *)&ll_rx, &addrlen);
+	zassert_not_equal(ret, -1, "Failed to receive packet (%d)", errno);
+	zassert_equal(ret, pkt_len,
+		      "Invalid data size received (%d, expected %d)",
+		      ret, pkt_len);
+	zassert_mem_equal(rx_buf, tx_buf + offset, pkt_len,
+			  "Invalid payload received");
+	validate_recvfrom_addr(&ll_rx, addrlen, net_if_get_by_iface(dst_iface),
+			       src_addr);
+}
+
+static void test_recvfrom_common_unbound(int sock_type, bool bind_iface_0)
+{
+	struct sockaddr_ll ll_dst;
+	static uint8_t dummy_lladdr[] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
+	uint16_t pkt_len;
+	int ret;
+
+	/* Transmitting sock */
+	setup_packet_socket(&packet_sock_1, SOCK_RAW, 0);
+	/* Receiving sock */
+	setup_packet_socket(&packet_sock_2, sock_type, htons(ETH_P_ALL));
+
+	if (bind_iface_0) {
+		bind_packet_socket(packet_sock_2, NULL);
+	}
+
+	/* Verify we get packet from iface 1 */
+	test_recvfrom_unbound_round(packet_sock_1, packet_sock_2, sock_type,
+				    lladdr2, ud.second, lladdr1, ud.first);
+
+	/* Verify we get packet from iface 2 */
+	test_recvfrom_unbound_round(packet_sock_1, packet_sock_2, sock_type,
+				    lladdr1, ud.first, lladdr2, ud.second);
+
+	/* Send some dummy data into the void on the "receiving" socket and make
+	 * sure it doesn't get automatically "bound" to the target iface.
+	 */
+	prepare_test_dst_lladdr(&ll_dst, ETH_P_IP, dummy_lladdr, ud.second);
+	prepare_test_packet(sock_type, ETH_P_IP, lladdr2, dummy_lladdr, &pkt_len);
+	ret = zsock_sendto(packet_sock_2, tx_buf, pkt_len, 0,
+			   (struct sockaddr *)&ll_dst,
+			   sizeof(struct sockaddr_ll));
+	zassert_not_equal(ret, -1, "Failed to send (%d)", errno);
+	zassert_equal(ret, pkt_len, "Invalid data length sent (%d/%d)", ret, pkt_len);
+
+	/* And try to receive again. */
+	/* Verify we get packet from iface 1 */
+	test_recvfrom_unbound_round(packet_sock_1, packet_sock_2, sock_type,
+				    lladdr2, ud.second, lladdr1, ud.first);
+
+	/* Verify we get packet from iface 2 */
+	test_recvfrom_unbound_round(packet_sock_1, packet_sock_2, sock_type,
+				    lladdr1, ud.first, lladdr2, ud.second);
+}
+
+ZTEST(socket_packet, test_raw_sock_recvfrom_proto_wildcard_unbound)
+{
+	test_recvfrom_common_unbound(SOCK_RAW, false);
+}
+
+ZTEST(socket_packet, test_dgram_sock_recvfrom_proto_wildcard_unbound)
+{
+	test_recvfrom_common_unbound(SOCK_DGRAM, false);
+}
+
+ZTEST(socket_packet, test_raw_sock_recvfrom_proto_wildcard_bound_iface_0)
+{
+	test_recvfrom_common_unbound(SOCK_RAW, true);
+}
+
+ZTEST(socket_packet, test_dgram_sock_recvfrom_proto_wildcard_bound_iface_0)
+{
+	test_recvfrom_common_unbound(SOCK_DGRAM, true);
+}
+
+static void test_recv_common_bound_other_iface(int sock_type)
+{
+	struct sockaddr_ll ll_dst;
+	struct sockaddr_in ip_src;
+	uint16_t pkt_len;
+	int ret;
+
+	/* Transmitting sock */
+	setup_packet_socket(&packet_sock_1, SOCK_RAW, 0);
+	prepare_test_packet(SOCK_RAW, ETH_P_IP, lladdr1, lladdr2, &pkt_len);
+	prepare_test_dst_lladdr(&ll_dst, ETH_P_IP, lladdr2, ud.first);
+	/* Receiving sock */
+	setup_packet_socket(&packet_sock_2, sock_type, htons(ETH_P_ALL));
+	bind_packet_socket(packet_sock_2, ud.first);
+	prepare_udp_socket(&udp_sock_1, &ip_src, DST_PORT);
+
+	ret = zsock_sendto(packet_sock_1, tx_buf, pkt_len, 0,
+			   (struct sockaddr *)&ll_dst,
+			   sizeof(struct sockaddr_ll));
+	zassert_not_equal(ret, -1, "Failed to send (%d)", errno);
+	zassert_equal(ret, pkt_len, "Invalid data length sent (%d/%d)", ret, pkt_len);
+
+	/* Packet socket should not get the packet due to different binding */
+	ret = zsock_recv(packet_sock_2, rx_buf, sizeof(rx_buf), 0);
+	zassert_equal(ret, -1, "Recv should fail");
+	zassert_equal(errno, EAGAIN, "Wrong errno");
+
+	/* But UDP socket should get the packet just fine. */
+	ret = zsock_recv(udp_sock_1, rx_buf, sizeof(rx_buf), 0);
+	zassert_not_equal(ret, -1, "Failed to receive UDP packet (%d)", errno);
+}
+
+ZTEST(socket_packet, test_raw_sock_recv_proto_wildcard_bound_other_iface)
+{
+	test_recv_common_bound_other_iface(SOCK_RAW);
+}
+
+ZTEST(socket_packet, test_dgram_sock_recv_proto_wildcard_bound_other_iface)
+{
+	test_recv_common_bound_other_iface(SOCK_DGRAM);
 }
 
 ZTEST(socket_packet, test_raw_dgram_udp_socks_recv)
@@ -939,13 +1094,13 @@ ZTEST(socket_packet, test_raw_dgram_udp_socks_recv)
 	int ret;
 
 	/* Transmitting sock */
-	setup_packet_socket(&packet_sock_1, ud.second, SOCK_RAW, 0);
+	setup_packet_socket(&packet_sock_1, SOCK_RAW, 0);
 	prepare_test_packet(SOCK_RAW, ETH_P_IP, lladdr2, lladdr1, &pkt_len);
 	prepare_test_dst_lladdr(&ll_dst, ETH_P_IP, lladdr1, ud.second);
 	/* Receiving sockets */
-	setup_packet_socket(&packet_sock_2, ud.first, SOCK_RAW, htons(ETH_P_ALL));
+	setup_packet_socket(&packet_sock_2, SOCK_RAW, htons(ETH_P_ALL));
 	bind_packet_socket(packet_sock_2, ud.first);
-	setup_packet_socket(&packet_sock_3, ud.first, SOCK_DGRAM, htons(ETH_P_ALL));
+	setup_packet_socket(&packet_sock_3, SOCK_DGRAM, htons(ETH_P_ALL));
 	bind_packet_socket(packet_sock_3, ud.first);
 	prepare_udp_socket(&udp_sock_1, &ip_src, DST_PORT);
 


Reviews:
user: login: jukkar
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-28T14:10:34Z
user: login: pdgendt
body: Sorry to be nitpicking again, we should try to find a way to check this rule in the compliance checks...

I'll still approve as the file already has many similar issues...
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-29T09:44:25Z
user: login: pdgendt
body: A few more
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-29T12:56:52Z
user: login: pdgendt
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-29T13:02:27Z
user: login: jukkar
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-29T13:44:40Z

Review comments:
diff_hunk: @@ -679,13 +699,13 @@ enum net_verdict net_conn_packet_input(struct net_pkt *pkt, uint16_t proto)
 
 		/* Apply protocol-specific matching criteria... */
 
-		if (!(conn->flags & NET_CONN_LOCAL_ADDR_SET)) {
-			continue;
-		}
-
-		local = (struct sockaddr_ll *)&conn->local_addr;
-		if (local->sll_ifindex != net_if_get_by_iface(net_pkt_iface(pkt))) {
-			continue;
+		/* Unbound sockets should collect packets from all interfaces. */
+		if (conn->flags & NET_CONN_LOCAL_ADDR_SET) {
path: subsys/net/ip/connection.c
user: login: pdgendt
body: Strictly speaking this falls under the [required Misra 14.4 rule](https://docs.zephyrproject.org/latest/contribute/coding_guidelines/index.html).

```suggestion
		if ((conn->flags & NET_CONN_LOCAL_ADDR_SET) != 0U) {
```
created_at: 2025-04-29T09:39:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -40,6 +40,43 @@ static inline int k_fifo_wait_non_empty(struct k_fifo *fifo,
 	return k_poll(events, ARRAY_SIZE(events), timeout);
 }
 
+static void zpacket_received_cb(struct net_context *ctx,
+				struct net_pkt *pkt,
+				union net_ip_header *ip_hdr,
+				union net_proto_header *proto_hdr,
+				int status,
+				void *user_data)
+{
+	NET_DBG("ctx=%p, pkt=%p, st=%d, user_data=%p", ctx, pkt, status,
+		user_data);
+
+	/* if pkt is NULL, EOF */
+	if (!pkt) {
path: subsys/net/lib/sockets/sockets_packet.c
user: login: pdgendt
body: ```suggestion
	if (pkt == NULL) {
```
created_at: 2025-04-29T09:40:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -40,6 +40,43 @@ static inline int k_fifo_wait_non_empty(struct k_fifo *fifo,
 	return k_poll(events, ARRAY_SIZE(events), timeout);
 }
 
+static void zpacket_received_cb(struct net_context *ctx,
+				struct net_pkt *pkt,
+				union net_ip_header *ip_hdr,
+				union net_proto_header *proto_hdr,
+				int status,
+				void *user_data)
+{
+	NET_DBG("ctx=%p, pkt=%p, st=%d, user_data=%p", ctx, pkt, status,
+		user_data);
+
+	/* if pkt is NULL, EOF */
+	if (!pkt) {
+		struct net_pkt *last_pkt = k_fifo_peek_tail(&ctx->recv_q);
+
+		if (!last_pkt) {
path: subsys/net/lib/sockets/sockets_packet.c
user: login: pdgendt
body: ```suggestion
		if (last_pkt == NULL) {
```
created_at: 2025-04-29T09:41:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2999,6 +2974,26 @@ static int recv_dgram(struct net_context *context,
 
 	context->recv_cb = cb;
 
+	/* If the context already has a connection handler, it means it's
+	 * already registered. In that case, all we have to do is 1) update
+	 * the callback registered in the net_context and 2) update the
+	 * user_data and local/remote address and port using net_conn_update().
+	 *
+	 * The callback function passed to net_conn_update() must be the same
+	 * function as the one passed to net_conn_register(), not the callback
+	 * set for the net context passed by recv_udp().
+	 */
+	if (context->conn_handler) {
path: subsys/net/ip/net_context.c
user: login: pdgendt
body: ```suggestion
	if (context->conn_handler != NULL) {
```
created_at: 2025-04-29T09:42:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -867,11 +863,13 @@ int net_context_bind(struct net_context *context, const struct sockaddr *addr,
 	/* If we already have connection handler, then it effectively
 	 * means that it's already bound to an interface/port, and we
 	 * don't support rebinding connection to new address/port in
-	 * the code below.
+	 * the code below. Doesn't apply for packet sockets.
 	 * TODO: Support rebinding.
 	 */
-	if (context->conn_handler) {
-		return -EISCONN;
+	if (addr->sa_family != AF_PACKET) {
+		if (context->conn_handler) {
path: subsys/net/ip/net_context.c
user: login: pdgendt
body: ```suggestion
		if (context->conn_handler != NULL) {
```
created_at: 2025-04-29T09:42:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -396,23 +414,18 @@ int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
 		}
 	}
 
-	if (local_port) {
-		flags |= NET_CONN_LOCAL_PORT_SPEC;
-		net_sin(&conn->local_addr)->sin_port = htons(local_port);
-	}
-
 	net_conn_change_callback(conn, cb, user_data);
 
-	conn->flags = flags;
 	conn->proto = proto;
 	conn->type = type;
 	conn->family = family;
 	conn->context = context;
 
-	/*
-	 * Since the net_conn_change_remote() updates the flags in connection,
-	 * must to be called after set the flags to connection.
-	 */
+	ret = net_conn_change_local(conn, local_addr, local_port);
+	if (ret) {
path: subsys/net/ip/connection.c
user: login: pdgendt
body: ```suggestion
	if (ret < 0) {
```
created_at: 2025-04-29T09:43:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -476,6 +491,11 @@ int net_conn_update(struct net_conn_handle *handle,
 
 	net_conn_change_callback(conn, cb, user_data);
 
+	ret = net_conn_change_local(conn, local_addr, local_port);
+	if (ret) {
path: subsys/net/ip/connection.c
user: login: pdgendt
body: ```suggestion
	if (ret < 0) {
```
created_at: 2025-04-29T09:43:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -383,10 +357,54 @@ int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
 			       sizeof(struct sockaddr_ll));
 		} else {
 			NET_ERR("Local address family not set");
-			goto error;
+			return -EINVAL;
 		}
 
-		flags |= NET_CONN_LOCAL_ADDR_SET;
+		conn->flags |= NET_CONN_LOCAL_ADDR_SET;
+	} else {
+		conn->flags &= ~NET_CONN_LOCAL_ADDR_SPEC;
+		conn->flags &= ~NET_CONN_LOCAL_ADDR_SET;
+	}
+
+	if (local_port) {
path: subsys/net/ip/connection.c
user: login: pdgendt
body: ```suggestion
	if (local_port > 0U) {
```
created_at: 2025-04-29T12:54:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -322,37 +322,12 @@ static int net_conn_change_remote(struct net_conn *conn,
 	return 0;
 }
 
-int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
-		      const struct sockaddr *remote_addr,
-		      const struct sockaddr *local_addr,
-		      uint16_t remote_port,
-		      uint16_t local_port,
-		      struct net_context *context,
-		      net_conn_cb_t cb,
-		      void *user_data,
-		      struct net_conn_handle **handle)
+static int net_conn_change_local(struct net_conn *conn,
+				 const struct sockaddr *local_addr,
+				 uint16_t local_port)
 {
-	struct net_conn *conn;
-	uint8_t flags = 0U;
-	int ret;
-
-	conn = conn_find_handler(context != NULL ? net_context_get_iface(context) : NULL,
-				 proto, family, remote_addr, local_addr,
-				 remote_port, local_port,
-				 context != NULL ?
-					net_context_is_reuseport_set(context) :
-					false);
-	if (conn) {
-		NET_ERR("Identical connection handler %p already found.", conn);
-		return -EADDRINUSE;
-	}
-
-	conn = conn_get_unused();
-	if (!conn) {
-		NET_ERR("Not enough connection contexts. "
-			"Consider increasing CONFIG_NET_MAX_CONN.");
-		return -ENOENT;
-	}
+	NET_DBG("[%zu] connection handler %p changed local",
+		conn - conns, conn);
 
 	if (local_addr) {
path: subsys/net/ip/connection.c
user: login: pdgendt
body: ```suggestion
	if (local_addr != NULL) {
```
created_at: 2025-04-29T12:54:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -383,10 +357,54 @@ int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
 			       sizeof(struct sockaddr_ll));
 		} else {
 			NET_ERR("Local address family not set");
-			goto error;
+			return -EINVAL;
 		}
 
-		flags |= NET_CONN_LOCAL_ADDR_SET;
+		conn->flags |= NET_CONN_LOCAL_ADDR_SET;
+	} else {
+		conn->flags &= ~NET_CONN_LOCAL_ADDR_SPEC;
+		conn->flags &= ~NET_CONN_LOCAL_ADDR_SET;
+	}
+
+	if (local_port) {
+		conn->flags |= NET_CONN_LOCAL_PORT_SPEC;
+		net_sin(&conn->local_addr)->sin_port = htons(local_port);
+	} else {
+		conn->flags &= ~NET_CONN_LOCAL_PORT_SPEC;
+	}
+
+	return 0;
+}
+
+int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
+		      const struct sockaddr *remote_addr,
+		      const struct sockaddr *local_addr,
+		      uint16_t remote_port,
+		      uint16_t local_port,
+		      struct net_context *context,
+		      net_conn_cb_t cb,
+		      void *user_data,
+		      struct net_conn_handle **handle)
+{
+	struct net_conn *conn;
+	int ret;
+
+	conn = conn_find_handler(context != NULL ? net_context_get_iface(context) : NULL,
+				 proto, family, remote_addr, local_addr,
+				 remote_port, local_port,
+				 context != NULL ?
+					net_context_is_reuseport_set(context) :
+					false);
+	if (conn) {
path: subsys/net/ip/connection.c
user: login: pdgendt
body: ```suggestion
	if (conn != NULL) {
```
created_at: 2025-04-29T12:54:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -383,10 +357,54 @@ int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
 			       sizeof(struct sockaddr_ll));
 		} else {
 			NET_ERR("Local address family not set");
-			goto error;
+			return -EINVAL;
 		}
 
-		flags |= NET_CONN_LOCAL_ADDR_SET;
+		conn->flags |= NET_CONN_LOCAL_ADDR_SET;
+	} else {
+		conn->flags &= ~NET_CONN_LOCAL_ADDR_SPEC;
+		conn->flags &= ~NET_CONN_LOCAL_ADDR_SET;
+	}
+
+	if (local_port) {
+		conn->flags |= NET_CONN_LOCAL_PORT_SPEC;
+		net_sin(&conn->local_addr)->sin_port = htons(local_port);
+	} else {
+		conn->flags &= ~NET_CONN_LOCAL_PORT_SPEC;
+	}
+
+	return 0;
+}
+
+int net_conn_register(uint16_t proto, enum net_sock_type type, uint8_t family,
+		      const struct sockaddr *remote_addr,
+		      const struct sockaddr *local_addr,
+		      uint16_t remote_port,
+		      uint16_t local_port,
+		      struct net_context *context,
+		      net_conn_cb_t cb,
+		      void *user_data,
+		      struct net_conn_handle **handle)
+{
+	struct net_conn *conn;
+	int ret;
+
+	conn = conn_find_handler(context != NULL ? net_context_get_iface(context) : NULL,
+				 proto, family, remote_addr, local_addr,
+				 remote_port, local_port,
+				 context != NULL ?
+					net_context_is_reuseport_set(context) :
+					false);
+	if (conn) {
+		NET_ERR("Identical connection handler %p already found.", conn);
+		return -EADDRINUSE;
+	}
+
+	conn = conn_get_unused();
+	if (!conn) {
path: subsys/net/ip/connection.c
user: login: pdgendt
body: ```suggestion
	if (conn == NULL) {
```
created_at: 2025-04-29T12:55:40Z
author_association: CONTRIBUTOR

PR comments:
user: login: rlubos
created_at: 2025-04-28T13:40:33Z
body: @go2sh FYI, if you have some time, please review
author_association: CONTRIBUTOR
user: login: rlubos
created_at: 2025-04-29T12:52:28Z
body: @pdgendt Agreed, would be nice if compliance would check for those. All comments should be addressed, hopefully I did not mess up what goes where.
author_association: CONTRIBUTOR
user: login: rlubos
created_at: 2025-04-29T13:01:36Z
body: @pdgendt Updated
author_association: CONTRIBUTOR