This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push:
new 964099ffc testing/nettest: Add a test of dual-stack UDP sockets.
964099ffc is described below
commit 964099ffc3250bf6c079115bb85180229e2365fe
Author: Marco Casaroli <[email protected]>
AuthorDate: Mon Nov 9 17:27:41 2026 +0100
testing/nettest: Add a test of dual-stack UDP sockets.
Add cmocka_net_udp (testing/nettest/udp, CONFIG_TESTING_NET_UDP, default
y like the other nettest programs). It runs over the loopback device
and checks UDP sockets of AF_INET and AF_INET6 on one port:
- IPV6_V6ONLY: 0 by default; without it an IPv6 socket on [::] gets an
IPv4 datagram from an IPv4-mapped address, with it the socket gets
none and sendto() and connect() to an IPv4-mapped address fail with
ENETUNREACH; it can change only before bind() (EINVAL after). A TCP
socket of AF_INET6 is always IPv6-only: 1 is accepted, 0 fails with
ENOPROTOOPT, an IPv6 listener gets no IPv4 connection.
- Delivery: an IPv4 socket gets no IPv6 datagram, alone on its port and
next to an IPv6 socket; an IPv6 socket on ::1 gets no IPv4 datagram;
after connect() to [::], and with sendto() to [::] or 0.0.0.0, a
datagram goes to the loopback address.
- bind(): a dual-stack IPv6 socket and an IPv4 socket cannot use the
same port (EADDRINUSE in both orders); with IPV6_V6ONLY, with
SO_REUSEADDR on both, or with the IPv6 socket on ::1 they can.
Each test keeps its sockets in the test state, and the teardown closes
them, also when an assertion ends the test.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <[email protected]>
---
testing/nettest/CMakeLists.txt | 23 +++
testing/nettest/Kconfig | 11 ++
testing/nettest/Makefile | 7 +
testing/nettest/udp/test_udp.c | 79 +++++++++
testing/nettest/udp/test_udp.h | 165 ++++++++++++++++++
testing/nettest/udp/test_udp_bind.c | 212 +++++++++++++++++++++++
testing/nettest/udp/test_udp_common.c | 226 ++++++++++++++++++++++++
testing/nettest/udp/test_udp_family.c | 237 +++++++++++++++++++++++++
testing/nettest/udp/test_udp_v6only.c | 317 ++++++++++++++++++++++++++++++++++
9 files changed, 1277 insertions(+)
diff --git a/testing/nettest/CMakeLists.txt b/testing/nettest/CMakeLists.txt
index 2b506689a..760621931 100644
--- a/testing/nettest/CMakeLists.txt
+++ b/testing/nettest/CMakeLists.txt
@@ -50,6 +50,29 @@ if(CONFIG_TESTING_NET_TEST)
${SRCS})
endif()
+ if(CONFIG_TESTING_NET_UDP)
+ set(SRCS
+ ${CMAKE_CURRENT_LIST_DIR}/udp/test_udp.c
+ ${CMAKE_CURRENT_LIST_DIR}/udp/test_udp_common.c
+ ${CMAKE_CURRENT_LIST_DIR}/udp/test_udp_v6only.c
+ ${CMAKE_CURRENT_LIST_DIR}/udp/test_udp_family.c
+ ${CMAKE_CURRENT_LIST_DIR}/udp/test_udp_bind.c)
+
+ nuttx_add_application(
+ NAME
+ cmocka_net_udp
+ PRIORITY
+ ${CONFIG_TESTING_NET_TEST_PRIORITY}
+ STACKSIZE
+ ${CONFIG_TESTING_NET_TEST_STACKSIZE}
+ MODULE
+ ${CONFIG_TESTING_NET_TEST}
+ DEPENDS
+ cmocka
+ SRCS
+ ${SRCS})
+ endif()
+
if(CONFIG_TESTING_NET_LOCAL)
set(SRCS
${CMAKE_CURRENT_LIST_DIR}/local/test_local.c
diff --git a/testing/nettest/Kconfig b/testing/nettest/Kconfig
index c4a089639..4b9aeaf9f 100644
--- a/testing/nettest/Kconfig
+++ b/testing/nettest/Kconfig
@@ -35,6 +35,17 @@ config TESTING_NET_TCP_PORT
endif
+config TESTING_NET_UDP
+ bool "Enable cmocka net udp test"
+ depends on NET_UDP && !NET_UDP_NO_STACK
+ depends on NET_IPv4 && NET_IPv6 && NET_LOOPBACK && NET_SOCKOPTS
+ default y
+ ---help---
+ Test UDP sockets of AF_INET and AF_INET6 on one port over the
+ loopback device: IPV6_V6ONLY (also on TCP sockets), the delivery
+ of IPv4 and IPv6 datagrams, and EADDRINUSE from bind()
+ (cmocka_net_udp).
+
config TESTING_NET_LOCAL
bool "Enable cmocka net local test"
depends on NET_LOCAL && NET_LOCAL_SCM
diff --git a/testing/nettest/Makefile b/testing/nettest/Makefile
index 6e1ab5784..1fff32496 100644
--- a/testing/nettest/Makefile
+++ b/testing/nettest/Makefile
@@ -45,6 +45,13 @@ endif
endif
+ifeq ($(CONFIG_TESTING_NET_UDP),y)
+MAINSRC += udp/test_udp.c
+PROGNAME += cmocka_net_udp
+CSRCS += udp/test_udp_common.c udp/test_udp_v6only.c
+CSRCS += udp/test_udp_family.c udp/test_udp_bind.c
+endif
+
ifeq ($(CONFIG_TESTING_NET_LOCAL),y)
MAINSRC += local/test_local.c
PROGNAME += cmocka_net_local
diff --git a/testing/nettest/udp/test_udp.c b/testing/nettest/udp/test_udp.c
new file mode 100644
index 000000000..584af4f4b
--- /dev/null
+++ b/testing/nettest/udp/test_udp.c
@@ -0,0 +1,79 @@
+/****************************************************************************
+ * apps/testing/nettest/udp/test_udp.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <cmocka.h>
+
+#include "test_udp.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TEST_UDP(f) \
+ cmocka_unit_test_setup_teardown(f, test_udp_setup, test_udp_teardown)
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int main(int argc, FAR char *argv[])
+{
+ const struct CMUnitTest udp_tests[] =
+ {
+ TEST_UDP(test_udp_v6only_default),
+ TEST_UDP(test_udp_v6only_mapped),
+ TEST_UDP(test_udp_v6only_set0),
+ TEST_UDP(test_udp_v6only_set1),
+ TEST_UDP(test_udp_v6only_both),
+ TEST_UDP(test_udp_v6only_send),
+ TEST_UDP(test_udp_v6only_bound),
+#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_TCPBACKLOG)
+ TEST_UDP(test_udp_v6only_tcp_set1),
+ TEST_UDP(test_udp_v6only_tcp_set0),
+ TEST_UDP(test_udp_v6only_tcp_ipv4),
+ TEST_UDP(test_udp_v6only_tcp_bound),
+#endif
+ TEST_UDP(test_udp_family_alone),
+ TEST_UDP(test_udp_family_both),
+ TEST_UDP(test_udp_family_v6addr),
+ TEST_UDP(test_udp_family_connect),
+ TEST_UDP(test_udp_family_sendto6),
+ TEST_UDP(test_udp_family_sendto4),
+ TEST_UDP(test_udp_bind_v6_v4),
+ TEST_UDP(test_udp_bind_v4_v6),
+ TEST_UDP(test_udp_bind_v6only_v4),
+ TEST_UDP(test_udp_bind_v4_v6only),
+ TEST_UDP(test_udp_bind_reuse),
+ TEST_UDP(test_udp_bind_v6addr),
+ };
+
+ return cmocka_run_group_tests(udp_tests, NULL, NULL);
+}
diff --git a/testing/nettest/udp/test_udp.h b/testing/nettest/udp/test_udp.h
new file mode 100644
index 000000000..f7860054d
--- /dev/null
+++ b/testing/nettest/udp/test_udp.h
@@ -0,0 +1,165 @@
+/****************************************************************************
+ * apps/testing/nettest/udp/test_udp.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __APPS_TESTING_NETTEST_UDP_TEST_UDP_H
+#define __APPS_TESTING_NETTEST_UDP_TEST_UDP_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <netinet/in.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TEST_UDP_PORT 5100 /* First port; each test has its own */
+#define TEST_UDP_WAIT_US (200 * 1000) /* Time for lo to deliver */
+#define TEST_UDP_NSOCKS 4 /* Sockets of one test */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* The sockets of one test, closed by test_udp_teardown() also when an
+ * assertion ends the test.
+ */
+
+struct test_udp_state_s
+{
+ int sd[TEST_UDP_NSOCKS];
+ int nsd;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: test_udp_setup, test_udp_teardown
+ ****************************************************************************/
+
+int test_udp_setup(FAR void **state);
+int test_udp_teardown(FAR void **state);
+
+/****************************************************************************
+ * Name: test_udp_set_v6only, test_udp_get_v6only
+ *
+ * Description:
+ * Set IPV6_V6ONLY (the result of setsockopt()), or read it (the value,
+ * or -errno).
+ *
+ ****************************************************************************/
+
+int test_udp_set_v6only(int sd, int val);
+int test_udp_get_v6only(int sd);
+
+/****************************************************************************
+ * Name: test_udp_socket, test_udp_bind
+ *
+ * Description:
+ * A socket of 'domain' and 'type', kept in 'state'. test_udp_socket()
+ * sets IPV6_V6ONLY to 'v6only' (if >= 0) and SO_REUSEADDR (if 'reuse').
+ * test_udp_bind()
+ * binds it to the wildcard address and 'port' and gives the result of
+ * bind(), with errno in 'err'.
+ *
+ ****************************************************************************/
+
+int test_udp_socket(FAR void **state, int domain, int type, int v6only,
+ bool reuse);
+int test_udp_bind(int sd, int domain, int port, FAR int *err);
+
+/****************************************************************************
+ * Name: test_udp_send4, test_udp_send6, test_udp_recv
+ *
+ * Description:
+ * Send one datagram to 127.0.0.1 or ::1 at 'port' and let lo deliver
+ * it. Read one datagram without waiting: its size, or -errno.
+ *
+ ****************************************************************************/
+
+void test_udp_send4(int port);
+void test_udp_send6(int port);
+int test_udp_recv(int sd, FAR struct sockaddr_in6 *from);
+
+/****************************************************************************
+ * Name: test_udp_v6only_*
+ *
+ * Description:
+ * IPV6_V6ONLY on UDP and TCP sockets of AF_INET6.
+ *
+ ****************************************************************************/
+
+void test_udp_v6only_default(FAR void **state);
+void test_udp_v6only_mapped(FAR void **state);
+void test_udp_v6only_set0(FAR void **state);
+void test_udp_v6only_set1(FAR void **state);
+void test_udp_v6only_both(FAR void **state);
+void test_udp_v6only_send(FAR void **state);
+void test_udp_v6only_bound(FAR void **state);
+#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_TCPBACKLOG)
+void test_udp_v6only_tcp_set1(FAR void **state);
+void test_udp_v6only_tcp_set0(FAR void **state);
+void test_udp_v6only_tcp_ipv4(FAR void **state);
+void test_udp_v6only_tcp_bound(FAR void **state);
+#endif
+
+/****************************************************************************
+ * Name: test_udp_family_*
+ *
+ * Description:
+ * A UDP socket of AF_INET gets no IPv6 datagram, a UDP socket of AF_INET6
+ * on ::1 gets no IPv4 datagram, and a datagram to [::] or 0.0.0.0 goes
+ * to the loopback address.
+ *
+ ****************************************************************************/
+
+void test_udp_family_alone(FAR void **state);
+void test_udp_family_both(FAR void **state);
+void test_udp_family_v6addr(FAR void **state);
+void test_udp_family_connect(FAR void **state);
+void test_udp_family_sendto6(FAR void **state);
+void test_udp_family_sendto4(FAR void **state);
+
+/****************************************************************************
+ * Name: test_udp_bind_*
+ *
+ * Description:
+ * A dual-stack UDP socket of AF_INET6 and a UDP socket of AF_INET cannot
+ * bind the same port, in either order, unless the IPv6 socket has
+ * IPV6_V6ONLY or both have SO_REUSEADDR.
+ *
+ ****************************************************************************/
+
+void test_udp_bind_v6_v4(FAR void **state);
+void test_udp_bind_v4_v6(FAR void **state);
+void test_udp_bind_v6only_v4(FAR void **state);
+void test_udp_bind_v4_v6only(FAR void **state);
+void test_udp_bind_reuse(FAR void **state);
+void test_udp_bind_v6addr(FAR void **state);
+
+#endif /* __APPS_TESTING_NETTEST_UDP_TEST_UDP_H */
diff --git a/testing/nettest/udp/test_udp_bind.c
b/testing/nettest/udp/test_udp_bind.c
new file mode 100644
index 000000000..d9ebaa435
--- /dev/null
+++ b/testing/nettest/udp/test_udp_bind.c
@@ -0,0 +1,212 @@
+/****************************************************************************
+ * apps/testing/nettest/udp/test_udp_bind.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <cmocka.h>
+#include <sys/socket.h>
+
+#include "test_udp.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: test_udp_bind_v6_v4
+ *
+ * Description:
+ * A dual-stack IPv6 socket on [::]:p, then an IPv4 socket on p: the
+ * second bind() fails with EADDRINUSE.
+ *
+ ****************************************************************************/
+
+void test_udp_bind_v6_v4(FAR void **state)
+{
+ int port = TEST_UDP_PORT + 20;
+ int r6;
+ int r4;
+ int s6;
+ int s4;
+ int err;
+
+ s6 = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, false);
+ r6 = test_udp_bind(s6, AF_INET6, port, &err);
+ assert_return_code(r6, err);
+ s4 = test_udp_socket(state, AF_INET, SOCK_DGRAM, -1, false);
+ r4 = test_udp_bind(s4, AF_INET, port, &err);
+ assert_int_equal(r4, -1);
+ assert_int_equal(err, EADDRINUSE);
+}
+
+/****************************************************************************
+ * Name: test_udp_bind_v4_v6
+ *
+ * Description:
+ * An IPv4 socket on p, then a dual-stack IPv6 socket on [::]:p: the
+ * second bind() fails with EADDRINUSE.
+ *
+ ****************************************************************************/
+
+void test_udp_bind_v4_v6(FAR void **state)
+{
+ int port = TEST_UDP_PORT + 21;
+ int r6;
+ int r4;
+ int s6;
+ int s4;
+ int err;
+
+ s4 = test_udp_socket(state, AF_INET, SOCK_DGRAM, -1, false);
+ r4 = test_udp_bind(s4, AF_INET, port, &err);
+ assert_return_code(r4, err);
+ s6 = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, false);
+ r6 = test_udp_bind(s6, AF_INET6, port, &err);
+ assert_int_equal(r6, -1);
+ assert_int_equal(err, EADDRINUSE);
+}
+
+/****************************************************************************
+ * Name: test_udp_bind_v6only_v4
+ *
+ * Description:
+ * With IPV6_V6ONLY on the IPv6 socket, both binds succeed (IPv6 first).
+ *
+ ****************************************************************************/
+
+void test_udp_bind_v6only_v4(FAR void **state)
+{
+ int port = TEST_UDP_PORT + 22;
+ int r6;
+ int r4;
+ int s6;
+ int s4;
+ int err;
+
+ s6 = test_udp_socket(state, AF_INET6, SOCK_DGRAM, 1, false);
+ r6 = test_udp_bind(s6, AF_INET6, port, &err);
+ assert_return_code(r6, err);
+ s4 = test_udp_socket(state, AF_INET, SOCK_DGRAM, -1, false);
+ r4 = test_udp_bind(s4, AF_INET, port, &err);
+ assert_return_code(r4, err);
+}
+
+/****************************************************************************
+ * Name: test_udp_bind_v4_v6only
+ *
+ * Description:
+ * With IPV6_V6ONLY on the IPv6 socket, both binds succeed (IPv4 first).
+ *
+ ****************************************************************************/
+
+void test_udp_bind_v4_v6only(FAR void **state)
+{
+ int port = TEST_UDP_PORT + 23;
+ int r6;
+ int r4;
+ int s6;
+ int s4;
+ int err;
+
+ s4 = test_udp_socket(state, AF_INET, SOCK_DGRAM, -1, false);
+ r4 = test_udp_bind(s4, AF_INET, port, &err);
+ assert_return_code(r4, err);
+ s6 = test_udp_socket(state, AF_INET6, SOCK_DGRAM, 1, false);
+ r6 = test_udp_bind(s6, AF_INET6, port, &err);
+ assert_return_code(r6, err);
+}
+
+/****************************************************************************
+ * Name: test_udp_bind_reuse
+ *
+ * Description:
+ * With SO_REUSEADDR on both, a dual-stack IPv6 socket and an IPv4 socket
+ * bind the same port, in both orders.
+ *
+ ****************************************************************************/
+
+void test_udp_bind_reuse(FAR void **state)
+{
+ int port = TEST_UDP_PORT + 24;
+ int r6;
+ int r4;
+ int s6;
+ int s4;
+ int err;
+
+ s6 = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, true);
+ r6 = test_udp_bind(s6, AF_INET6, port, &err);
+ assert_return_code(r6, err);
+ s4 = test_udp_socket(state, AF_INET, SOCK_DGRAM, -1, true);
+ r4 = test_udp_bind(s4, AF_INET, port, &err);
+ assert_return_code(r4, err);
+
+ s4 = test_udp_socket(state, AF_INET, SOCK_DGRAM, -1, true);
+ r4 = test_udp_bind(s4, AF_INET, port + 1, &err);
+ assert_return_code(r4, err);
+ s6 = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, true);
+ r6 = test_udp_bind(s6, AF_INET6, port + 1, &err);
+ assert_return_code(r6, err);
+}
+
+/****************************************************************************
+ * Name: test_udp_bind_v6addr
+ *
+ * Description:
+ * An IPv4 socket on 0.0.0.0:p, then a dual-stack IPv6 socket on [::1]:p:
+ * ::1 takes no IPv4 traffic, so both binds succeed.
+ *
+ ****************************************************************************/
+
+void test_udp_bind_v6addr(FAR void **state)
+{
+ struct sockaddr_in6 addr;
+ int port = TEST_UDP_PORT + 26;
+ int r6;
+ int r4;
+ int s6;
+ int s4;
+ int err;
+
+ s4 = test_udp_socket(state, AF_INET, SOCK_DGRAM, -1, false);
+ r4 = test_udp_bind(s4, AF_INET, port, &err);
+ assert_return_code(r4, err);
+
+ s6 = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, false);
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(port);
+ addr.sin6_addr = in6addr_loopback;
+ r6 = bind(s6, (FAR struct sockaddr *)&addr, sizeof(addr));
+ err = r6 < 0 ? errno : 0;
+ assert_return_code(r6, err);
+}
diff --git a/testing/nettest/udp/test_udp_common.c
b/testing/nettest/udp/test_udp_common.c
new file mode 100644
index 000000000..e6a2e97b9
--- /dev/null
+++ b/testing/nettest/udp/test_udp_common.c
@@ -0,0 +1,226 @@
+/****************************************************************************
+ * apps/testing/nettest/udp/test_udp_common.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <cmocka.h>
+#include <sys/socket.h>
+
+#include "test_udp.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: test_udp_setup
+ ****************************************************************************/
+
+int test_udp_setup(FAR void **state)
+{
+ FAR struct test_udp_state_s *udp_state;
+
+ udp_state = zalloc(sizeof(*udp_state));
+ assert_non_null(udp_state);
+ *state = udp_state;
+ return 0;
+}
+
+/****************************************************************************
+ * Name: test_udp_teardown
+ ****************************************************************************/
+
+int test_udp_teardown(FAR void **state)
+{
+ FAR struct test_udp_state_s *udp_state = *state;
+ int i;
+
+ for (i = 0; i < udp_state->nsd; i++)
+ {
+ close(udp_state->sd[i]);
+ }
+
+ free(udp_state);
+ return 0;
+}
+
+/****************************************************************************
+ * Name: test_udp_set_v6only
+ ****************************************************************************/
+
+int test_udp_set_v6only(int sd, int val)
+{
+ return setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
+}
+
+/****************************************************************************
+ * Name: test_udp_get_v6only
+ ****************************************************************************/
+
+int test_udp_get_v6only(int sd)
+{
+ socklen_t len = sizeof(int);
+ int val = -1;
+
+ if (getsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &val, &len) < 0)
+ {
+ return -errno;
+ }
+
+ return val;
+}
+
+/****************************************************************************
+ * Name: test_udp_socket
+ ****************************************************************************/
+
+int test_udp_socket(FAR void **state, int domain, int type, int v6only,
+ bool reuse)
+{
+ FAR struct test_udp_state_s *udp_state = *state;
+ int one = 1;
+ int ret;
+ int sd;
+
+ assert_true(udp_state->nsd < TEST_UDP_NSOCKS);
+ sd = socket(domain, type | SOCK_CLOEXEC, 0);
+ assert_return_code(sd, errno);
+ udp_state->sd[udp_state->nsd++] = sd;
+
+ if (v6only >= 0)
+ {
+ ret = test_udp_set_v6only(sd, v6only);
+ assert_return_code(ret, errno);
+ }
+
+ if (reuse)
+ {
+ ret = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+ assert_return_code(ret, errno);
+ }
+
+ return sd;
+}
+
+/****************************************************************************
+ * Name: test_udp_bind
+ ****************************************************************************/
+
+int test_udp_bind(int sd, int domain, int port, FAR int *err)
+{
+ struct sockaddr_in6 addr6;
+ struct sockaddr_in addr4;
+ int ret;
+
+ if (domain == AF_INET)
+ {
+ memset(&addr4, 0, sizeof(addr4));
+ addr4.sin_family = AF_INET;
+ addr4.sin_port = htons(port);
+ addr4.sin_addr.s_addr = htonl(INADDR_ANY);
+ ret = bind(sd, (FAR struct sockaddr *)&addr4, sizeof(addr4));
+ }
+ else
+ {
+ memset(&addr6, 0, sizeof(addr6));
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_port = htons(port);
+ addr6.sin6_addr = in6addr_any;
+ ret = bind(sd, (FAR struct sockaddr *)&addr6, sizeof(addr6));
+ }
+
+ *err = ret < 0 ? errno : 0;
+ return ret;
+}
+
+/****************************************************************************
+ * Name: test_udp_send4
+ ****************************************************************************/
+
+void test_udp_send4(int port)
+{
+ struct sockaddr_in addr;
+ int ret;
+ int sd;
+
+ sd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ assert_return_code(sd, errno);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ ret = sendto(sd, "v4", 2, 0, (FAR struct sockaddr *)&addr, sizeof(addr));
+ close(sd);
+ assert_int_equal(ret, 2);
+ usleep(TEST_UDP_WAIT_US);
+}
+
+/****************************************************************************
+ * Name: test_udp_send6
+ ****************************************************************************/
+
+void test_udp_send6(int port)
+{
+ struct sockaddr_in6 addr;
+ int ret;
+ int sd;
+
+ sd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ assert_return_code(sd, errno);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(port);
+ addr.sin6_addr = in6addr_loopback;
+ ret = sendto(sd, "v6", 2, 0, (FAR struct sockaddr *)&addr, sizeof(addr));
+ close(sd);
+ assert_int_equal(ret, 2);
+ usleep(TEST_UDP_WAIT_US);
+}
+
+/****************************************************************************
+ * Name: test_udp_recv
+ ****************************************************************************/
+
+int test_udp_recv(int sd, FAR struct sockaddr_in6 *from)
+{
+ socklen_t len = sizeof(*from);
+ char buf[16];
+ int ret;
+
+ memset(from, 0, sizeof(*from));
+ ret = recvfrom(sd, buf, sizeof(buf), MSG_DONTWAIT,
+ (FAR struct sockaddr *)from, &len);
+ return ret < 0 ? -errno : ret;
+}
diff --git a/testing/nettest/udp/test_udp_family.c
b/testing/nettest/udp/test_udp_family.c
new file mode 100644
index 000000000..d3f0dbffc
--- /dev/null
+++ b/testing/nettest/udp/test_udp_family.c
@@ -0,0 +1,237 @@
+/****************************************************************************
+ * apps/testing/nettest/udp/test_udp_family.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <cmocka.h>
+#include <sys/socket.h>
+
+#include "test_udp.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: test_udp_family_alone
+ *
+ * Description:
+ * An IPv4 socket alone on its port gets no IPv6 datagram.
+ *
+ ****************************************************************************/
+
+void test_udp_family_alone(FAR void **state)
+{
+ struct sockaddr_in6 from;
+ int port = TEST_UDP_PORT + 10;
+ int ret;
+ int err;
+ int s4;
+
+ s4 = test_udp_socket(state, AF_INET, SOCK_DGRAM, -1, false);
+ ret = test_udp_bind(s4, AF_INET, port, &err);
+ assert_return_code(ret, err);
+ test_udp_send6(port);
+ ret = test_udp_recv(s4, &from);
+ assert_int_equal(ret, -EAGAIN);
+}
+
+/****************************************************************************
+ * Name: test_udp_family_both
+ *
+ * Description:
+ * An IPv4 socket, then an IPv6 socket with IPV6_V6ONLY on the same port:
+ * the IPv6 datagram goes to the IPv6 socket only.
+ *
+ ****************************************************************************/
+
+void test_udp_family_both(FAR void **state)
+{
+ struct sockaddr_in6 from;
+ int port = TEST_UDP_PORT + 11;
+ int r4;
+ int r6;
+ int s4;
+ int s6;
+ int err;
+
+ s4 = test_udp_socket(state, AF_INET, SOCK_DGRAM, -1, false);
+ r4 = test_udp_bind(s4, AF_INET, port, &err);
+ assert_return_code(r4, err);
+ s6 = test_udp_socket(state, AF_INET6, SOCK_DGRAM, 1, false);
+ r6 = test_udp_bind(s6, AF_INET6, port, &err);
+ assert_return_code(r6, err);
+ test_udp_send6(port);
+ r6 = test_udp_recv(s6, &from);
+ r4 = test_udp_recv(s4, &from);
+ assert_int_equal(r4, -EAGAIN);
+ assert_int_equal(r6, 2);
+}
+
+/****************************************************************************
+ * Name: test_udp_family_v6addr
+ *
+ * Description:
+ * A UDP socket of AF_INET6 on [::1]:p gets no IPv4 datagram to
+ * 127.0.0.1:p: ::1 is not an IPv4 address.
+ *
+ ****************************************************************************/
+
+void test_udp_family_v6addr(FAR void **state)
+{
+ struct sockaddr_in6 from;
+ struct sockaddr_in6 addr;
+ int port = TEST_UDP_PORT + 12;
+ int ret;
+ int err;
+ int s6;
+
+ s6 = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, false);
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(port);
+ addr.sin6_addr = in6addr_loopback;
+ ret = bind(s6, (FAR struct sockaddr *)&addr, sizeof(addr));
+ err = ret < 0 ? errno : 0;
+ assert_return_code(ret, err);
+ test_udp_send4(port);
+ ret = test_udp_recv(s6, &from);
+ assert_int_equal(ret, -EAGAIN);
+}
+
+/****************************************************************************
+ * Name: test_udp_family_connect
+ *
+ * Description:
+ * connect() to [::]:p connects to ::1, as connect() to 0.0.0.0 connects
+ * to 127.0.0.1: a UDP socket of AF_INET6 on [::]:p gets the datagram.
+ *
+ ****************************************************************************/
+
+void test_udp_family_connect(FAR void **state)
+{
+ struct sockaddr_in6 from;
+ struct sockaddr_in6 addr;
+ int port = TEST_UDP_PORT + 13;
+ int ret;
+ int err;
+ int s6;
+ int sd;
+
+ s6 = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, false);
+ ret = test_udp_bind(s6, AF_INET6, port, &err);
+ assert_return_code(ret, err);
+
+ sd = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, false);
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(port);
+ addr.sin6_addr = in6addr_any;
+ ret = connect(sd, (FAR struct sockaddr *)&addr, sizeof(addr));
+ assert_return_code(ret, errno);
+ ret = send(sd, "v6", 2, 0);
+ assert_return_code(ret, errno);
+ usleep(TEST_UDP_WAIT_US);
+ ret = test_udp_recv(s6, &from);
+ assert_int_equal(ret, 2);
+ assert_true(IN6_IS_ADDR_LOOPBACK(&from.sin6_addr));
+}
+
+/****************************************************************************
+ * Name: test_udp_family_sendto6
+ *
+ * Description:
+ * sendto() to [::]:p on a socket that is not connected sends to ::1.
+ *
+ ****************************************************************************/
+
+void test_udp_family_sendto6(FAR void **state)
+{
+ struct sockaddr_in6 from;
+ struct sockaddr_in6 addr;
+ int port = TEST_UDP_PORT + 14;
+ int ret;
+ int err;
+ int s6;
+ int sd;
+
+ s6 = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, false);
+ ret = test_udp_bind(s6, AF_INET6, port, &err);
+ assert_return_code(ret, err);
+
+ sd = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, false);
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(port);
+ addr.sin6_addr = in6addr_any;
+ ret = sendto(sd, "v6", 2, 0, (FAR struct sockaddr *)&addr, sizeof(addr));
+ assert_return_code(ret, errno);
+ usleep(TEST_UDP_WAIT_US);
+ ret = test_udp_recv(s6, &from);
+ assert_int_equal(ret, 2);
+ assert_true(IN6_IS_ADDR_LOOPBACK(&from.sin6_addr));
+}
+
+/****************************************************************************
+ * Name: test_udp_family_sendto4
+ *
+ * Description:
+ * sendto() to 0.0.0.0:p on a socket that is not connected sends to
+ * 127.0.0.1.
+ *
+ ****************************************************************************/
+
+void test_udp_family_sendto4(FAR void **state)
+{
+ struct sockaddr_in6 from;
+ struct sockaddr_in addr;
+ int port = TEST_UDP_PORT + 15;
+ int ret;
+ int err;
+ int s4;
+ int sd;
+
+ s4 = test_udp_socket(state, AF_INET, SOCK_DGRAM, -1, false);
+ ret = test_udp_bind(s4, AF_INET, port, &err);
+ assert_return_code(ret, err);
+
+ sd = test_udp_socket(state, AF_INET, SOCK_DGRAM, -1, false);
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ ret = sendto(sd, "v4", 2, 0, (FAR struct sockaddr *)&addr, sizeof(addr));
+ assert_return_code(ret, errno);
+ usleep(TEST_UDP_WAIT_US);
+ ret = test_udp_recv(s4, &from);
+ assert_int_equal(ret, 2);
+}
diff --git a/testing/nettest/udp/test_udp_v6only.c
b/testing/nettest/udp/test_udp_v6only.c
new file mode 100644
index 000000000..8d10fe671
--- /dev/null
+++ b/testing/nettest/udp/test_udp_v6only.c
@@ -0,0 +1,317 @@
+/****************************************************************************
+ * apps/testing/nettest/udp/test_udp_v6only.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <cmocka.h>
+#include <sys/socket.h>
+
+#include "test_udp.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: test_udp_v6only_default
+ ****************************************************************************/
+
+void test_udp_v6only_default(FAR void **state)
+{
+ int sd;
+
+ sd = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, false);
+ assert_int_equal(test_udp_get_v6only(sd), 0);
+}
+
+/****************************************************************************
+ * Name: test_udp_v6only_mapped
+ *
+ * Description:
+ * Without the option, an IPv6 socket on [::] gets an IPv4 datagram from
+ * an IPv4-mapped address.
+ *
+ ****************************************************************************/
+
+void test_udp_v6only_mapped(FAR void **state)
+{
+ struct sockaddr_in6 from;
+ int port = TEST_UDP_PORT;
+ int ret;
+ int err;
+ int sd;
+
+ sd = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, false);
+ ret = test_udp_bind(sd, AF_INET6, port, &err);
+ assert_return_code(ret, err);
+ test_udp_send4(port);
+ ret = test_udp_recv(sd, &from);
+ assert_int_equal(ret, 2);
+ assert_true(IN6_IS_ADDR_V4MAPPED(&from.sin6_addr));
+}
+
+/****************************************************************************
+ * Name: test_udp_v6only_set0
+ *
+ * Description:
+ * Set 1, then 0 before bind(): the socket gets IPv4 datagrams.
+ *
+ ****************************************************************************/
+
+void test_udp_v6only_set0(FAR void **state)
+{
+ struct sockaddr_in6 from;
+ int port = TEST_UDP_PORT + 1;
+ int ret;
+ int err;
+ int sd;
+
+ sd = test_udp_socket(state, AF_INET6, SOCK_DGRAM, 1, false);
+ ret = test_udp_set_v6only(sd, 0);
+ assert_return_code(ret, errno);
+ assert_int_equal(test_udp_get_v6only(sd), 0);
+ ret = test_udp_bind(sd, AF_INET6, port, &err);
+ assert_return_code(ret, err);
+ test_udp_send4(port);
+ ret = test_udp_recv(sd, &from);
+ assert_int_equal(ret, 2);
+}
+
+/****************************************************************************
+ * Name: test_udp_v6only_set1
+ *
+ * Description:
+ * With the option, an IPv6 socket on [::] gets no IPv4 datagram.
+ *
+ ****************************************************************************/
+
+void test_udp_v6only_set1(FAR void **state)
+{
+ struct sockaddr_in6 from;
+ int port = TEST_UDP_PORT + 2;
+ int ret;
+ int err;
+ int sd;
+
+ sd = test_udp_socket(state, AF_INET6, SOCK_DGRAM, 1, false);
+ assert_int_equal(test_udp_get_v6only(sd), 1);
+ ret = test_udp_bind(sd, AF_INET6, port, &err);
+ assert_return_code(ret, err);
+ test_udp_send4(port);
+ ret = test_udp_recv(sd, &from);
+ assert_int_equal(ret, -EAGAIN);
+}
+
+/****************************************************************************
+ * Name: test_udp_v6only_both
+ *
+ * Description:
+ * An IPv6 socket with the option, then an IPv4 socket on the same port:
+ * the IPv4 datagram goes to the IPv4 socket.
+ *
+ ****************************************************************************/
+
+void test_udp_v6only_both(FAR void **state)
+{
+ struct sockaddr_in6 from;
+ int port = TEST_UDP_PORT + 3;
+ int r6;
+ int r4;
+ int s6;
+ int s4;
+ int err;
+
+ s6 = test_udp_socket(state, AF_INET6, SOCK_DGRAM, 1, false);
+ r6 = test_udp_bind(s6, AF_INET6, port, &err);
+ assert_return_code(r6, err);
+ s4 = test_udp_socket(state, AF_INET, SOCK_DGRAM, -1, false);
+ r4 = test_udp_bind(s4, AF_INET, port, &err);
+ assert_return_code(r4, err);
+ test_udp_send4(port);
+ r6 = test_udp_recv(s6, &from);
+ r4 = test_udp_recv(s4, &from);
+ assert_int_equal(r6, -EAGAIN);
+ assert_int_equal(r4, 2);
+}
+
+/****************************************************************************
+ * Name: test_udp_v6only_send
+ *
+ * Description:
+ * With the option, sendto() and connect() to ::ffff:127.0.0.1 fail with
+ * ENETUNREACH.
+ *
+ ****************************************************************************/
+
+void test_udp_v6only_send(FAR void **state)
+{
+ struct sockaddr_in6 addr;
+ int ret;
+ int err;
+ int sd;
+
+ sd = test_udp_socket(state, AF_INET6, SOCK_DGRAM, 1, false);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(TEST_UDP_PORT + 4);
+ addr.sin6_addr.s6_addr[10] = 0xff;
+ addr.sin6_addr.s6_addr[11] = 0xff;
+ addr.sin6_addr.s6_addr[12] = 127;
+ addr.sin6_addr.s6_addr[15] = 1;
+
+ ret = sendto(sd, "v6", 2, 0, (FAR struct sockaddr *)&addr, sizeof(addr));
+ err = ret < 0 ? errno : 0;
+ assert_int_equal(ret, -1);
+ assert_int_equal(err, ENETUNREACH);
+
+ ret = connect(sd, (FAR struct sockaddr *)&addr, sizeof(addr));
+ err = ret < 0 ? errno : 0;
+ assert_int_equal(ret, -1);
+ assert_int_equal(err, ENETUNREACH);
+}
+
+/****************************************************************************
+ * Name: test_udp_v6only_bound
+ *
+ * Description:
+ * After bind(), setting the option fails with EINVAL.
+ *
+ ****************************************************************************/
+
+void test_udp_v6only_bound(FAR void **state)
+{
+ int ret;
+ int err;
+ int sd;
+
+ sd = test_udp_socket(state, AF_INET6, SOCK_DGRAM, -1, false);
+ ret = test_udp_bind(sd, AF_INET6, TEST_UDP_PORT + 5, &err);
+ assert_return_code(ret, err);
+ ret = test_udp_set_v6only(sd, 1);
+ err = ret < 0 ? errno : 0;
+ assert_int_equal(ret, -1);
+ assert_int_equal(err, EINVAL);
+}
+
+#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_TCPBACKLOG)
+/****************************************************************************
+ * Name: test_udp_v6only_tcp_set1
+ ****************************************************************************/
+
+void test_udp_v6only_tcp_set1(FAR void **state)
+{
+ int sd;
+
+ sd = test_udp_socket(state, AF_INET6, SOCK_STREAM, 1, false);
+ assert_int_equal(test_udp_get_v6only(sd), 1);
+}
+
+/****************************************************************************
+ * Name: test_udp_v6only_tcp_set0
+ *
+ * Description:
+ * A TCP socket of AF_INET6 is always IPv6-only: 0 fails, get gives 1.
+ *
+ ****************************************************************************/
+
+void test_udp_v6only_tcp_set0(FAR void **state)
+{
+ int ret;
+ int err;
+ int sd;
+
+ sd = test_udp_socket(state, AF_INET6, SOCK_STREAM, -1, false);
+ ret = test_udp_set_v6only(sd, 0);
+ err = ret < 0 ? errno : 0;
+ assert_int_equal(ret, -1);
+ assert_int_equal(err, ENOPROTOOPT);
+ assert_int_equal(test_udp_get_v6only(sd), 1);
+}
+
+/****************************************************************************
+ * Name: test_udp_v6only_tcp_ipv4
+ *
+ * Description:
+ * An IPv6 listener gets no IPv4 connection.
+ *
+ ****************************************************************************/
+
+void test_udp_v6only_tcp_ipv4(FAR void **state)
+{
+ struct sockaddr_in addr;
+ int port = TEST_UDP_PORT + 6;
+ int ret;
+ int err;
+ int ls;
+ int cs;
+
+ ls = test_udp_socket(state, AF_INET6, SOCK_STREAM, -1, false);
+ ret = test_udp_bind(ls, AF_INET6, port, &err);
+ assert_return_code(ret, err);
+ ret = listen(ls, 2);
+ assert_return_code(ret, errno);
+
+ cs = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ assert_return_code(cs, errno);
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ ret = connect(cs, (FAR struct sockaddr *)&addr, sizeof(addr));
+ close(cs);
+ assert_int_equal(ret, -1);
+}
+
+/****************************************************************************
+ * Name: test_udp_v6only_tcp_bound
+ *
+ * Description:
+ * After bind(), setting the option on a TCP socket fails with EINVAL.
+ *
+ ****************************************************************************/
+
+void test_udp_v6only_tcp_bound(FAR void **state)
+{
+ int ret;
+ int err;
+ int sd;
+
+ sd = test_udp_socket(state, AF_INET6, SOCK_STREAM, -1, false);
+ ret = test_udp_bind(sd, AF_INET6, TEST_UDP_PORT + 7, &err);
+ assert_return_code(ret, err);
+ ret = test_udp_set_v6only(sd, 1);
+ err = ret < 0 ? errno : 0;
+ assert_int_equal(ret, -1);
+ assert_int_equal(err, EINVAL);
+}
+#endif