Repository: zephyrproject-rtos/zephyr (C)

Issue:
Connect: unable to disconnect a datagram socket
### Describe the bug

I am trying to disconnect a datagram socket the same way that is use on Linux (see [man connect](https://man7.org/linux/man-pages/man2/connect.2.html)) but I get the following error.
```
ASSERTION FAIL [addr->sa_family == net_context_get_family(context)] @ WEST_TOPDIR/zephyr/subsys/net/ip/net_context.c:1361
      Family mismatch 0 should be 1
```

### Regression

- [ ] This is a regression.

### Steps to reproduce

1. mkdir test; cd test
2. copy the following to src/mainc.
```
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(Main, LOG_LEVEL_INF);

#include <zephyr/kernel.h>
#include <stdio.h>
#include <zephyr/posix/sys/socket.h>

int main() {
        int socket1 = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket1 < 0) {
                LOG_ERR("Creation of socket 1 failed");
                return -1;
        }
        LOG_INF("Socket1 was successfully created");
        struct in_addr sin_addr;
        sin_addr.s_addr = 0;
        struct sockaddr_in addr_in;
        addr_in.sin_family = AF_INET;
        addr_in.sin_port = 4000;
        addr_in.sin_addr = sin_addr;

        int res = bind(socket1, &addr_in, sizeof(struct sockaddr_in));
        if (res < 0) {
                LOG_ERR("Binding socket1 failed with %d\n", res);
                return -1;
        }
        LOG_INF("Socket1 was successfully binded");
        int socket2 = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket2 < 0) {
                LOG_ERR("Creation of socket 2 failed");
                return -1;
        }
        LOG_INF("Socket2 was succesfully created");
        addr_in.sin_port = 4001;

        res = bind(socket2, &addr_in, sizeof(struct sockaddr_in));
        if (res < 0) {
                LOG_ERR("Binding socket2 failed with %d\n", res);
                return -1;
        }
        LOG_INF("Socket2 was successfully binded");

        res = connect(socket1, &addr_in, sizeof(struct sockaddr_in));
        if (res < 0) {
                LOG_ERR("Socket1 wasn't able to connect");
                return -1;
        }
        LOG_INF("Socket1 was successfully connected");

        struct sockaddr disconnect;
        disconnect.sa_family = AF_UNSPEC;

        res = connect(socket1, &disconnect, sizeof(struct sockaddr));
        if (res < 0) {
                LOG_ERR("Socket1 failed to disconnect");
                return -1;
        }
        LOG_INF("Socket1 was successfully");
        return 0;
}
```
3. copy the following to CMakeLists.txt
```
cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(test)

target_sources(app PRIVATE src/main.c)
```
4. copy the following to prj.conf
```
CONFIG_NETWORKING=y
CONFIG_NET_UDP=y
CONFIG_NET_TCP=y
CONFIG_NET_IPV4=y
CONFIG_POSIX_API=y
CONFIG_NET_SOCKETS=y
CONFIG_NET_DRIVERS=y
CONFIG_NET_LOOPBACK=y
CONFIG_POSIX_TIMERS=y
CONFIG_XOPEN_STREAMS=y
CONFIG_NET_LOG=y
CONFIG_LOG=y
```
5. build and flash on your card

### Relevant log output

```shell
*** Booting Zephyr OS build v4.2.0-rc1-26-gb15404f99834 ***
[00:00:00.000,000] <inf> Main: Socket1 was successfully created
[00:00:00.000,000] <inf> Main: Socket1 was successfully binded
[00:00:00.000,000] <inf> Main: Socket2 was succesfully created
[00:00:00.000,000] <inf> Main: Socket2 was successfully binded
[00:00:00.000,000] <inf> Main: Socket1 was successfully connected
[00:00:00.000,000] <err> Main: Socket1 failed to disconnect
```

### Impact

Functional Limitation – Some features not working as expected, but system usable.

### Environment

- OS: WSL (Ubuntu 24.02)
- SDK: Zephyr SDK 0.17.0
- Zephyr commit: b15404f998346c65952ffa42ca6f5194ec71065f

### Additional Context

_No response_


Discussion: The assertion on incorrect family type was wrong and fixed with https://github.com/zephyrproject-rtos/zephyr/pull/94115.

But about the "disconnect a datagram socket" (which clearly is just resetting the peer address), the link you provide is a Linux man page and this behavior with AF_UNSPEC apparently is a Linux-specific thing, POSIX man page (https://linux.die.net/man/3/connect) makes no mention of such behavior. Instead, it clearly states how to reset peer address:
> For SOCK_DGRAM sockets, the peer address identifies where all datagrams are sent on subsequent send() functions, and limits the remote sender for subsequent recv() functions. **If address is a null address for the protocol, the socket's peer address shall be reset.**

And that's what we implement, providing the unspecified address to `connect()` will clear the peer address, the datagram socket will no longer be considered "connected".
@rlubos I check the documentation link given for the `zsock_connect` [function](https://docs.zephyrproject.org/apidoc/latest/group__bsd__sockets.html#ga1a70b1d3616341a86977835cc853d81d). An in this one, there is that 

> If the sa_family member of address is AF_UNSPEC, the socket's peer address shall be reset.

Which doc should I trust ? 
Ok, so it seems that they actually changed the POSIX standard in that matter, the link above refers to an older version, and sicne 2008 revision they added the mention `AF_UNSPEC`. If that's the case, I guess we need to implement that.
Should be fixed with https://github.com/zephyrproject-rtos/zephyr/pull/94228



Fix (patch):
diff --git a/subsys/net/ip/connection.c b/subsys/net/ip/connection.c
index b95462f9331a0..2140b84e25b9c 100644
--- a/subsys/net/ip/connection.c
+++ b/subsys/net/ip/connection.c
@@ -42,16 +42,16 @@ LOG_MODULE_REGISTER(net_conn, CONFIG_NET_CONN_LOG_LEVEL);
 /** Local address set */
 #define NET_CONN_LOCAL_ADDR_SET		BIT(2)
 
-/** Local port set */
+/** Remote port set */
 #define NET_CONN_REMOTE_PORT_SPEC	BIT(3)
 
-/** Remote port set */
+/** Local port set */
 #define NET_CONN_LOCAL_PORT_SPEC	BIT(4)
 
-/** Local address specified */
+/** Remote address specified */
 #define NET_CONN_REMOTE_ADDR_SPEC	BIT(5)
 
-/** Remote address specified */
+/** Local address specified */
 #define NET_CONN_LOCAL_ADDR_SPEC	BIT(6)
 
 #define NET_CONN_RANK(_flags)		(_flags & 0x78)
@@ -970,22 +970,22 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
 			/* Is the candidate connection matching the packet's TCP/UDP
 			 * address and port?
 			 */
-			if (net_sin(&conn->remote_addr)->sin_port &&
+			if ((conn->flags & NET_CONN_REMOTE_PORT_SPEC) != 0 &&
 			    net_sin(&conn->remote_addr)->sin_port != src_port) {
 				continue; /* wrong remote port */
 			}
 
-			if (net_sin(&conn->local_addr)->sin_port &&
+			if ((conn->flags & NET_CONN_LOCAL_PORT_SPEC) != 0 &&
 			    net_sin(&conn->local_addr)->sin_port != dst_port) {
 				continue; /* wrong local port */
 			}
 
-			if ((conn->flags & NET_CONN_REMOTE_ADDR_SET) &&
+			if ((conn->flags & NET_CONN_REMOTE_ADDR_SET) != 0 &&
 			    !conn_addr_cmp(pkt, ip_hdr, &conn->remote_addr, true)) {
 				continue; /* wrong remote address */
 			}
 
-			if ((conn->flags & NET_CONN_LOCAL_ADDR_SET) &&
+			if ((conn->flags & NET_CONN_LOCAL_ADDR_SET) != 0 &&
 			    !conn_addr_cmp(pkt, ip_hdr, &conn->local_addr, false)) {
 
 				/* Check if we could do a v4-mapping-to-v6 and the IPv6 socket
diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c
index 703ddacaf2200..f8c057c9348aa 100644
--- a/subsys/net/ip/net_context.c
+++ b/subsys/net/ip/net_context.c
@@ -1357,6 +1357,18 @@ int net_context_connect(struct net_context *context,
 		goto unlock;
 	}
 
+	/* As per POSIX, for non-connection-mode sockets:
+	 * "If the sa_family member of address is AF_UNSPEC, the socket's peer
+	 *  address shall be reset.""
+	 */
+	if (IS_ENABLED(CONFIG_NET_UDP) && addr->sa_family == AF_UNSPEC &&
+	    net_context_get_type(context) == SOCK_DGRAM) {
+		context->flags &= ~NET_CONTEXT_REMOTE_ADDR_SET;
+		memset(&context->remote, 0, sizeof(context->remote));
+		ret = 0;
+		goto unlock;
+	}
+
 	if (addr->sa_family != net_context_get_family(context)) {
 		NET_ERR("Address family %d does not match network context family %d",
 			addr->sa_family, net_context_get_family(context));