summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormzgrebnak <[email protected]>2026-05-28 22:23:22 +0200
committerGitHub <[email protected]>2026-05-28 16:23:22 -0400
commit57410350d94bbcb5e6885d0fbc40c72b6d7ddd33 (patch)
tree523f6e89bd960a4fbb3f849a87c7162a24639d9b
parent5dca3d2af173318df6e0452bddddcbc34f459146 (diff)
Implemented BSD socket options improvements (#374)
● - initial support - bsd: address PR review feedback - Fix build failure: so_linger was stored on NX_TCP_SOCKET which has no such field. Linger state is now kept in NX_BSD_SOCKET using the new nx_bsd_option_linger field and the existing NX_BSD_SOCKET_ENABLE_OPTION_LINGER flag. - Fix sign-conversion warning in setsockopt(IP_TOS): read the value as INT, validate range 0..0xFF, then cast to ULONG. - Reject out-of-range INT values (< 0 or > 255) in setsockopt(IP_TTL). - Add lower-bound check for IPPROTO_IP options in getsockopt and setsockopt so that SO_* option numbers are no longer silently accepted at the IPPROTO_IP level. - Add null check for raw sockets in IP_TOS and IP_TTL get/set paths; return ENOPROTOOPT instead of dereferencing a NULL pointer. - Validate *option_length before writing in SO_BROADCAST, TCP_NODELAY, SO_LINGER and IP_TOS getsockopt paths; set *option_length to the actual size returned on success. - Separate ENOPROTOOPT (wrong socket type) from EINVAL (wrong length) in SO_LINGER get/set error handling. - Remove unused so_linger local variable from getsockopt. - Replace C++-style // comments in the close/linger path with C99 /* */ comments. - Add regression test netx_bsd_socket_options_test covering SO_LINGER, IP_TOS, IP_TTL, SO_BROADCAST and TCP_NODELAY round-trips and error paths; register in bsd_test_cases in regression/CMakeLists.txt. Co-authored-by: Frédéric Desbiens [email protected] Co-authored-by: Copilot [email protected]
-rw-r--r--addons/BSD/nxd_bsd.c413
-rw-r--r--addons/BSD/nxd_bsd.h5
-rw-r--r--test/cmake/netxduo/regression/CMakeLists.txt3
-rw-r--r--test/regression/bsd_test/netx_bsd_socket_options_test.c335
4 files changed, 731 insertions, 25 deletions
diff --git a/addons/BSD/nxd_bsd.c b/addons/BSD/nxd_bsd.c
index ef27b0dd..202b39be 100644
--- a/addons/BSD/nxd_bsd.c
+++ b/addons/BSD/nxd_bsd.c
@@ -8,8 +8,7 @@
*
* SPDX-License-Identifier: MIT
**************************************************************************/
-
-/**************************************************************************/
+/* Some portions generated by Copilot (claude-sonnet-4.6). */
/**************************************************************************/
/** */
/** BSD 4.3 Socket API Compatible Interface to NetX Duo */
@@ -4891,7 +4890,23 @@ UINT index;
/* Release the mutex while disconnecting. */
tx_mutex_put(nx_bsd_protection_ptr);
- nx_tcp_socket_disconnect(tcp_socket_ptr, timeout);
+ if (!(bsd_socket_ptr -> nx_bsd_socket_option_flags & NX_BSD_SOCKET_ENABLE_OPTION_LINGER))
+ {
+ /* SO_LINGER disabled: normal graceful close. */
+ nx_tcp_socket_disconnect(tcp_socket_ptr, timeout);
+ }
+ else if (bsd_socket_ptr -> nx_bsd_option_linger.l_linger == 0)
+ {
+ /* l_linger == 0: abort immediately with RST. */
+ nx_tcp_socket_disconnect(tcp_socket_ptr, NX_NO_WAIT);
+ }
+ else
+ {
+ /* l_linger > 0: wait up to l_linger seconds for graceful close. */
+ ULONG ticks = (ULONG)(bsd_socket_ptr -> nx_bsd_option_linger.l_linger) * NX_IP_PERIODIC_RATE;
+
+ nx_tcp_socket_disconnect(tcp_socket_ptr, ticks);
+ }
tx_mutex_get(nx_bsd_protection_ptr, NX_BSD_TIMEOUT);
@@ -6092,7 +6107,7 @@ ULONG ticks;
if (option_level == IPPROTO_IP)
{
- if((option_name <= SO_MAX) || (option_name > IP_OPTION_MAX))
+ if ((option_name < IP_TOS) || (option_name > IP_OPTION_MAX))
{
/* Error, one or more invalid arguments. */
@@ -6119,6 +6134,20 @@ ULONG ticks;
return NX_SOC_ERROR;
}
}
+ else if(option_level == IPPROTO_TCP)
+ {
+ if((option_name != TCP_NODELAY))
+ {
+
+ /* Error, one or more invalid arguments. */
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(ENOPROTOOPT);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+ }
else
{
@@ -6155,6 +6184,44 @@ ULONG ticks;
switch (option_name)
{
+ case IP_TOS:
+
+ if (*option_length < (INT)sizeof(INT))
+ {
+
+ tx_mutex_put(nx_bsd_protection_ptr);
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(EINVAL);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+ else if (bsd_socket_ptr -> nx_bsd_socket_tcp_socket)
+ {
+ ULONG tmp_option_value = bsd_socket_ptr -> nx_bsd_socket_tcp_socket -> nx_tcp_socket_type_of_service;
+ *((INT *)option_value) = (INT)(tmp_option_value >> 16);
+ }
+ else if (bsd_socket_ptr -> nx_bsd_socket_udp_socket)
+ {
+ ULONG tmp_option_value = bsd_socket_ptr -> nx_bsd_socket_udp_socket -> nx_udp_socket_type_of_service;
+ *((INT *)option_value) = (INT)(tmp_option_value >> 16);
+ }
+ else
+ {
+ tx_mutex_put(nx_bsd_protection_ptr);
+
+ /* Raw sockets do not support IP_TOS. */
+ nx_bsd_set_errno(ENOPROTOOPT);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+
+ *option_length = (INT)sizeof(INT);
+
+ break;
+
case SO_ERROR:
{
@@ -6195,6 +6262,26 @@ ULONG ticks;
break;
+ case SO_BROADCAST:
+
+ if (*option_length < (INT)sizeof(INT))
+ {
+
+ tx_mutex_put(nx_bsd_protection_ptr);
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(EINVAL);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+
+ /* This is the default behavior of NetX. All sockets have this capability. */
+ *((INT *)option_value) = 1;
+ *option_length = (INT)sizeof(INT);
+
+ break;
+
case SO_KEEPALIVE:
/* Determine if NetX Duo supports keepalive. */
@@ -6222,6 +6309,40 @@ ULONG ticks;
break;
+ case SO_LINGER:
+
+ /* Only available to TCP BSD sockets. */
+ if (!bsd_socket_ptr -> nx_bsd_socket_tcp_socket)
+ {
+ tx_mutex_put(nx_bsd_protection_ptr);
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(ENOPROTOOPT);
+
+ /* Return an error status. */
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+
+ if (*option_length < (INT)sizeof(struct nx_bsd_linger))
+ {
+ tx_mutex_put(nx_bsd_protection_ptr);
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(EINVAL);
+
+ /* Return an error status. */
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+
+ (*((struct nx_bsd_linger *)option_value)).l_onoff =
+ (bsd_socket_ptr -> nx_bsd_socket_option_flags & NX_BSD_SOCKET_ENABLE_OPTION_LINGER) ? 1 : 0;
+ (*((struct nx_bsd_linger *)option_value)).l_linger = bsd_socket_ptr -> nx_bsd_option_linger.l_linger;
+ *option_length = (INT)sizeof(struct nx_bsd_linger);
+
+ break;
+
case SO_RCVTIMEO:
/* Check if the receive time out is set. */
@@ -6250,6 +6371,20 @@ ULONG ticks;
case SO_RCVBUF:
+ /* Only available to TCP sockets. */
+ if (!bsd_socket_ptr -> nx_bsd_socket_tcp_socket)
+ {
+
+ tx_mutex_put(nx_bsd_protection_ptr);
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(ENOPROTOOPT);
+
+ /* Return an error status. */
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+
soc_window_size = (struct nx_bsd_sock_winsize *)option_value;
soc_window_size -> winsize = (INT)(bsd_socket_ptr -> nx_bsd_socket_tcp_socket -> nx_tcp_socket_rx_window_default);
*option_length = sizeof(soc_window_size);
@@ -6277,10 +6412,31 @@ ULONG ticks;
break;
+ case TCP_NODELAY:
+
+ if (*option_length < (INT)sizeof(INT))
+ {
+
+ tx_mutex_put(nx_bsd_protection_ptr);
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(EINVAL);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+
+ /* This is the default behavior of NetX. All sockets have this attribute. */
+ *((INT *)option_value) = 1;
+ *option_length = (INT)sizeof(INT);
+
+ break;
+
+ case IP_TTL:
case IP_MULTICAST_TTL:
/* Validate the option length. */
- if(*option_length != sizeof(UCHAR))
+ if (*option_length < (INT)sizeof(UCHAR))
{
tx_mutex_put(nx_bsd_protection_ptr);
@@ -6305,23 +6461,63 @@ ULONG ticks;
return NX_SOC_ERROR;
}
- /* Make sure socket is UDP type. */
- if(bsd_socket_ptr -> nx_bsd_socket_udp_socket == NX_NULL)
+ /* Socket is UDP type. */
+ if (bsd_socket_ptr -> nx_bsd_socket_udp_socket != NX_NULL)
+ {
+
+ /* Get the TTL value. */
+ *(UCHAR *)option_value = (UCHAR)(bsd_socket_ptr -> nx_bsd_socket_udp_socket -> nx_udp_socket_time_to_live);
+ }
+ /* Socket is TCP type. */
+ else if (bsd_socket_ptr -> nx_bsd_socket_tcp_socket != NX_NULL)
+ {
+
+ if (option_name == IP_MULTICAST_TTL)
+ {
+
+ tx_mutex_put(nx_bsd_protection_ptr);
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(ENOPROTOOPT);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+
+ /* Get the TTL value. */
+ *(UCHAR *)option_value = (UCHAR)(bsd_socket_ptr -> nx_bsd_socket_tcp_socket -> nx_tcp_socket_time_to_live);
+ }
+ else
{
tx_mutex_put(nx_bsd_protection_ptr);
- /* Set the socket error if extended socket options enabled. */
+ /* Raw sockets do not support IP_TTL/IP_MULTICAST_TTL. */
nx_bsd_set_errno(ENOPROTOOPT);
NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
return NX_SOC_ERROR;
}
- /* Set the TTL value. */
- *(UCHAR*)option_value = (UCHAR)(bsd_socket_ptr -> nx_bsd_socket_udp_socket -> nx_udp_socket_time_to_live);
+ *option_length = (INT)sizeof(UCHAR);
+
break;
+ case IP_MULTICAST_IF:
+
+ /* This is not supported yet. */
+
+ tx_mutex_put(nx_bsd_protection_ptr);
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(ENOPROTOOPT);
+
+ /* Return an error status. */
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+
+ break;
+
default:
tx_mutex_put(nx_bsd_protection_ptr);
@@ -6431,7 +6627,7 @@ ULONG physical_addr_lsw;
if (option_level == IPPROTO_IP)
{
- if((option_name <= SO_MAX) || (option_name > IP_OPTION_MAX))
+ if ((option_name < IP_TOS) || (option_name > IP_OPTION_MAX))
{
/* Error, one or more invalid arguments. */
@@ -6507,9 +6703,70 @@ ULONG physical_addr_lsw;
switch (option_name)
{
+ case IP_TOS:
+
+ if (option_length != (INT)sizeof(INT))
+ {
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(EINVAL);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+ else
+ {
+ INT tos_in = *((const INT *)option_value);
+
+ if ((tos_in < 0) || (tos_in > 0xFF))
+ {
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(EINVAL);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+ else
+ {
+ ULONG type_of_service = (ULONG)tos_in << 16;
+
+ if (bsd_socket_ptr -> nx_bsd_socket_tcp_socket)
+ {
+ bsd_socket_ptr -> nx_bsd_socket_tcp_socket -> nx_tcp_socket_type_of_service = type_of_service;
+ }
+ else if (bsd_socket_ptr -> nx_bsd_socket_udp_socket)
+ {
+ bsd_socket_ptr -> nx_bsd_socket_udp_socket -> nx_udp_socket_type_of_service = type_of_service;
+ }
+ else
+ {
+
+ /* Raw sockets do not support IP_TOS. */
+ nx_bsd_set_errno(ENOPROTOOPT);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+ }
+ }
+
+ break;
+
case SO_BROADCAST:
/* This is the default behavior of NetX. All sockets have this capability. */
+ if (*((INT *)option_value) == 0)
+ {
+
+ nx_bsd_set_errno(EINVAL);
+
+ /* Return an error status. */
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+
+ return NX_SOC_ERROR;
+ }
+
break;
case SO_KEEPALIVE:
@@ -6563,10 +6820,56 @@ ULONG physical_addr_lsw;
break;
case SO_LINGER:
+ {
+ INT l_linger_val;
+
+ /* Only available to TCP BSD sockets. */
+ if (!bsd_socket_ptr -> nx_bsd_socket_tcp_socket)
+ {
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(ENOPROTOOPT);
+
+ /* Return an error status. */
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+
+ if (option_length != (INT)sizeof(struct nx_bsd_linger))
+ {
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(EINVAL);
+
+ /* Return an error status. */
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
- /* The Linger socket option is not supported in NetX Duo BSD. */
+ l_linger_val = (*((const struct nx_bsd_linger *)option_value)).l_linger;
+ if (l_linger_val < 0)
+ {
- return NX_NOT_ENABLED;
+ /* Negative linger time is invalid. */
+ nx_bsd_set_errno(EINVAL);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+
+ if ((*((const struct nx_bsd_linger *)option_value)).l_onoff)
+ {
+ bsd_socket_ptr -> nx_bsd_socket_option_flags |= NX_BSD_SOCKET_ENABLE_OPTION_LINGER;
+ }
+ else
+ {
+ bsd_socket_ptr -> nx_bsd_socket_option_flags &= (ULONG)(~NX_BSD_SOCKET_ENABLE_OPTION_LINGER);
+ }
+ bsd_socket_ptr -> nx_bsd_option_linger.l_onoff = (*((const struct nx_bsd_linger *)option_value)).l_onoff;
+ bsd_socket_ptr -> nx_bsd_option_linger.l_linger = l_linger_val;
+
+ break;
+ }
case SO_SNDTIMEO:
@@ -6640,13 +6943,24 @@ ULONG physical_addr_lsw;
case TCP_NODELAY:
/* This is the default behavior of NetX. All sockets have this attribute. */
+ if (*((INT *)option_value) == 0)
+ {
+
+ nx_bsd_set_errno(EINVAL);
+
+ /* Return an error status. */
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+
+ return NX_SOC_ERROR;
+ }
break;
+ case IP_TTL:
case IP_MULTICAST_TTL:
/* Validate the option length. */
- if(option_length != sizeof(UCHAR) && option_length != sizeof(INT))
+ if ((option_length != (INT)sizeof(UCHAR)) && (option_length != (INT)sizeof(INT)))
{
/* Set the socket error if extended socket options enabled. */
@@ -6667,24 +6981,68 @@ ULONG physical_addr_lsw;
return NX_SOC_ERROR;
}
- /* Make sure socket is UDP type. */
- if(bsd_socket_ptr -> nx_bsd_socket_udp_socket == NX_NULL)
+ /* When option_length == sizeof(INT), validate range 0..255. */
+ if (option_length == (INT)sizeof(INT))
{
- /* Set the socket error if extended socket options enabled. */
- nx_bsd_set_errno(ENOPROTOOPT);
+ INT ttl_in = *(const INT *)option_value;
- NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
- return NX_SOC_ERROR;
+ if ((ttl_in < 0) || (ttl_in > 0xFF))
+ {
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(EINVAL);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
}
- /* Set the TTL value. */
- if (option_length == sizeof(UCHAR))
+ /* Socket is UDP type. */
+ if (bsd_socket_ptr -> nx_bsd_socket_udp_socket != NX_NULL)
{
- bsd_socket_ptr -> nx_bsd_socket_udp_socket -> nx_udp_socket_time_to_live = *(UCHAR*)option_value;
+
+ /* Set the TTL value. */
+ if (option_length == (INT)sizeof(UCHAR))
+ {
+ bsd_socket_ptr -> nx_bsd_socket_udp_socket -> nx_udp_socket_time_to_live = *(const UCHAR *)option_value;
+ }
+ else
+ {
+ bsd_socket_ptr -> nx_bsd_socket_udp_socket -> nx_udp_socket_time_to_live = (UCHAR)(*(const INT *)option_value);
+ }
+ }
+ /* Socket is TCP type. */
+ else if (bsd_socket_ptr -> nx_bsd_socket_tcp_socket != NX_NULL)
+ {
+
+ if (option_name == IP_MULTICAST_TTL)
+ {
+
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(ENOPROTOOPT);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+ }
+
+ /* Set the TTL value. */
+ if (option_length == (INT)sizeof(UCHAR))
+ {
+ bsd_socket_ptr -> nx_bsd_socket_tcp_socket -> nx_tcp_socket_time_to_live = *(const UCHAR *)option_value;
+ }
+ else
+ {
+ bsd_socket_ptr -> nx_bsd_socket_tcp_socket -> nx_tcp_socket_time_to_live = (UCHAR)(*(const INT *)option_value);
+ }
}
else
{
- bsd_socket_ptr -> nx_bsd_socket_udp_socket -> nx_udp_socket_time_to_live = (UCHAR)(*(INT*)option_value);
+
+ /* Raw sockets do not support IP_TTL/IP_MULTICAST_TTL. */
+ nx_bsd_set_errno(ENOPROTOOPT);
+
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
}
break;
@@ -6698,6 +7056,13 @@ ULONG physical_addr_lsw;
/* This is not supported yet. */
+ /* Set the socket error if extended socket options enabled. */
+ nx_bsd_set_errno(ENOPROTOOPT);
+
+ /* Return an error status. */
+ NX_BSD_ERROR(NX_SOC_ERROR, __LINE__);
+ return NX_SOC_ERROR;
+
break;
#if defined(NX_BSD_RAW_SUPPORT) && defined(NX_ENABLE_VLAN)
diff --git a/addons/BSD/nxd_bsd.h b/addons/BSD/nxd_bsd.h
index a35fee4d..15d1c1ac 100644
--- a/addons/BSD/nxd_bsd.h
+++ b/addons/BSD/nxd_bsd.h
@@ -300,6 +300,7 @@ extern "C" {
#define AF_INET 2 /* IPv4 socket (UDP, TCP, etc) */
#define AF_INET6 3 /* IPv6 socket (UDP, TCP, etc) */
#define AF_PACKET 4 /* Raw Packet type (Link Layer packets) */
+#define AF_MAX AF_PACKET
/* Protocol families, same as address families. */
#define PF_INET AF_INET
@@ -570,6 +571,8 @@ extern "C" {
/* This second set of socket options take the socket level (category) IPPROTO_IP. */
+#define IP_TOS 25 /* Type Of Service */
+#define IP_TTL 26 /* Specify the TTL value. */
#define IP_MULTICAST_IF 27 /* Specify outgoing multicast interface */
#define IP_MULTICAST_TTL 28 /* Specify the TTL value to use for outgoing multicast packet. */
#define IP_MULTICAST_LOOP 29 /* Whether or not receive the outgoing multicast packet, loopbacloopbackk mode. */
@@ -960,6 +963,8 @@ typedef struct NX_BSD_SOCKET_STRUCT
INT nx_bsd_option_linger_time;
UINT nx_bsd_option_linger_time_closed;
UINT nx_bsd_option_linger_start_close;
+ struct nx_bsd_linger
+ nx_bsd_option_linger;
UINT nx_bsd_socket_time_wait_remaining;
ULONG nx_bsd_option_receive_timeout;
ULONG nx_bsd_option_send_timeout;
diff --git a/test/cmake/netxduo/regression/CMakeLists.txt b/test/cmake/netxduo/regression/CMakeLists.txt
index 23f626d2..7acc2783 100644
--- a/test/cmake/netxduo/regression/CMakeLists.txt
+++ b/test/cmake/netxduo/regression/CMakeLists.txt
@@ -53,7 +53,8 @@ if("-DNX_BSD_ENABLE" IN_LIST ${CMAKE_BUILD_TYPE})
${SOURCE_DIR}/bsd_test/netx_bsd_tcp_getsockname_without_bind_test.c
${SOURCE_DIR}/bsd_test/netx_bsd_tcp_rcvbuf_test.c
${SOURCE_DIR}/bsd_test/netx_bsd_tcp_fionread_test.c
- ${SOURCE_DIR}/bsd_test/netx_bsd_select_spurious_event_test.c)
+ ${SOURCE_DIR}/bsd_test/netx_bsd_select_spurious_event_test.c
+ ${SOURCE_DIR}/bsd_test/netx_bsd_socket_options_test.c)
if("-DNX_BSD_RAW_SUPPORT" IN_LIST ${CMAKE_BUILD_TYPE})
list(
APPEND
diff --git a/test/regression/bsd_test/netx_bsd_socket_options_test.c b/test/regression/bsd_test/netx_bsd_socket_options_test.c
new file mode 100644
index 00000000..bdc8f177
--- /dev/null
+++ b/test/regression/bsd_test/netx_bsd_socket_options_test.c
@@ -0,0 +1,335 @@
+/***************************************************************************
+ * Copyright (C) 2026 Eclipse ThreadX contributors
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the MIT License which is available at
+ * https://opensource.org/licenses/MIT.
+ *
+ * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
+ * The AI-generated portions may be considered public domain (CC0-1.0)
+ * and not subject to the project's licence. The human contributor has
+ * reviewed and verified that the code is correct.
+ *
+ * SPDX-License-Identifier: MIT and CC0-1.0
+ **************************************************************************/
+
+/* This test exercises setsockopt/getsockopt for SO_LINGER, IP_TOS, IP_TTL,
+ SO_BROADCAST and TCP_NODELAY, including error-path coverage introduced
+ by the PR #374 bug-fixes. */
+
+#include "tx_api.h"
+#include "nx_api.h"
+#if defined(NX_BSD_ENABLE) && !defined(NX_DISABLE_IPV4) && defined(__PRODUCT_NETXDUO__)
+#include "nxd_bsd.h"
+
+#define DEMO_STACK_SIZE 4096
+#define BSD_THREAD_PRIORITY 2
+
+static TX_THREAD ntest_0;
+static NX_PACKET_POOL pool_0;
+static NX_IP ip_0;
+static ULONG bsd_thread_area[DEMO_STACK_SIZE / sizeof(ULONG)];
+static ULONG error_counter;
+
+static ULONG packet_pool_area[(256 + sizeof(NX_PACKET)) * 16 / 4];
+
+static void ntest_0_entry(ULONG thread_input);
+extern void test_control_return(UINT status);
+extern void _nx_ram_network_driver_256(struct NX_IP_DRIVER_STRUCT *driver_req);
+
+#ifdef CTEST
+VOID test_application_define(void *first_unused_memory)
+#else
+void netx_bsd_socket_options_test_application_define(void *first_unused_memory)
+#endif
+{
+CHAR *pointer = (CHAR *)first_unused_memory;
+UINT status;
+
+ error_counter = 0;
+
+ tx_thread_create(&ntest_0, "thread 0", ntest_0_entry, 0,
+ pointer, DEMO_STACK_SIZE, 3, 3,
+ TX_NO_TIME_SLICE, TX_AUTO_START);
+ pointer += DEMO_STACK_SIZE;
+
+ nx_system_initialize();
+
+ status = nx_packet_pool_create(&pool_0, "NetX Main Packet Pool", 256,
+ packet_pool_area, sizeof(packet_pool_area));
+ if (status)
+ error_counter++;
+
+ status = nx_ip_create(&ip_0, "NetX IP Instance 0",
+ IP_ADDRESS(1, 2, 3, 4), 0xFFFFFF00UL,
+ &pool_0, _nx_ram_network_driver_256,
+ pointer, 2048, 1);
+ pointer += 2048;
+ if (status)
+ error_counter++;
+
+ status = nx_arp_enable(&ip_0, (void *)pointer, 1024);
+ pointer += 1024;
+ if (status)
+ error_counter++;
+
+ status = nx_tcp_enable(&ip_0);
+ if (status)
+ error_counter++;
+
+ status = nx_udp_enable(&ip_0);
+ if (status)
+ error_counter++;
+
+ status = bsd_initialize(&ip_0, &pool_0, (CHAR *)&bsd_thread_area[0],
+ sizeof(bsd_thread_area), BSD_THREAD_PRIORITY);
+ if (status)
+ error_counter++;
+}
+
+static void ntest_0_entry(ULONG thread_input)
+{
+int tcp_sock;
+int udp_sock;
+struct nx_bsd_linger ling_set;
+struct nx_bsd_linger ling_get;
+INT opt_val;
+INT opt_len;
+INT ret;
+
+ NX_PARAMETER_NOT_USED(thread_input);
+
+ /* Create sockets. */
+ tcp_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (tcp_sock < 0)
+ error_counter++;
+
+ udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (udp_sock < 0)
+ error_counter++;
+
+ /* ================================================================
+ * SO_LINGER: round-trip on TCP socket
+ * ================================================================ */
+ ling_set.l_onoff = 1;
+ ling_set.l_linger = 5;
+ ret = setsockopt(tcp_sock, SOL_SOCKET, SO_LINGER,
+ &ling_set, (INT)sizeof(ling_set));
+ if (ret != 0)
+ error_counter++;
+
+ opt_len = (INT)sizeof(ling_get);
+ ret = getsockopt(tcp_sock, SOL_SOCKET, SO_LINGER,
+ &ling_get, &opt_len);
+ if (ret != 0)
+ error_counter++;
+ if (ling_get.l_onoff != 1)
+ error_counter++;
+ if (ling_get.l_linger != 5)
+ error_counter++;
+ if (opt_len != (INT)sizeof(struct nx_bsd_linger))
+ error_counter++;
+
+ /* Disable linger and verify l_onoff is cleared. */
+ ling_set.l_onoff = 0;
+ ling_set.l_linger = 0;
+ ret = setsockopt(tcp_sock, SOL_SOCKET, SO_LINGER,
+ &ling_set, (INT)sizeof(ling_set));
+ if (ret != 0)
+ error_counter++;
+
+ opt_len = (INT)sizeof(ling_get);
+ ret = getsockopt(tcp_sock, SOL_SOCKET, SO_LINGER,
+ &ling_get, &opt_len);
+ if (ret != 0)
+ error_counter++;
+ if (ling_get.l_onoff != 0)
+ error_counter++;
+
+ /* Negative l_linger must be rejected with EINVAL. */
+ ling_set.l_onoff = 1;
+ ling_set.l_linger = -1;
+ ret = setsockopt(tcp_sock, SOL_SOCKET, SO_LINGER,
+ &ling_set, (INT)sizeof(ling_set));
+ if (ret == 0)
+ error_counter++;
+
+ /* Short output buffer must be rejected with EINVAL. */
+ opt_len = 1;
+ ret = getsockopt(tcp_sock, SOL_SOCKET, SO_LINGER,
+ &ling_get, &opt_len);
+ if (ret == 0)
+ error_counter++;
+
+ /* SO_LINGER on a UDP socket must fail with ENOPROTOOPT. */
+ ling_set.l_onoff = 1;
+ ling_set.l_linger = 1;
+ ret = setsockopt(udp_sock, SOL_SOCKET, SO_LINGER,
+ &ling_set, (INT)sizeof(ling_set));
+ if (ret == 0)
+ error_counter++;
+
+ /* ================================================================
+ * IP_TOS: round-trip on TCP socket
+ * ================================================================ */
+ opt_val = 0x10;
+ opt_len = (INT)sizeof(INT);
+ ret = setsockopt(tcp_sock, IPPROTO_IP, IP_TOS, &opt_val, opt_len);
+ if (ret != 0)
+ error_counter++;
+
+ opt_val = 0;
+ opt_len = (INT)sizeof(INT);
+ ret = getsockopt(tcp_sock, IPPROTO_IP, IP_TOS, &opt_val, &opt_len);
+ if (ret != 0)
+ error_counter++;
+ if (opt_val != 0x10)
+ error_counter++;
+ if (opt_len != (INT)sizeof(INT))
+ error_counter++;
+
+ /* IP_TOS round-trip on UDP socket. */
+ opt_val = 0x28;
+ opt_len = (INT)sizeof(INT);
+ ret = setsockopt(udp_sock, IPPROTO_IP, IP_TOS, &opt_val, opt_len);
+ if (ret != 0)
+ error_counter++;
+
+ opt_val = 0;
+ opt_len = (INT)sizeof(INT);
+ ret = getsockopt(udp_sock, IPPROTO_IP, IP_TOS, &opt_val, &opt_len);
+ if (ret != 0)
+ error_counter++;
+ if (opt_val != 0x28)
+ error_counter++;
+
+ /* IP_TOS: out-of-range values must be rejected. */
+ opt_val = 256;
+ opt_len = (INT)sizeof(INT);
+ ret = setsockopt(tcp_sock, IPPROTO_IP, IP_TOS, &opt_val, opt_len);
+ if (ret == 0)
+ error_counter++;
+
+ opt_val = -1;
+ opt_len = (INT)sizeof(INT);
+ ret = setsockopt(tcp_sock, IPPROTO_IP, IP_TOS, &opt_val, opt_len);
+ if (ret == 0)
+ error_counter++;
+
+ /* A SO_* option number passed to IPPROTO_IP level must be rejected. */
+ opt_val = 1;
+ opt_len = (INT)sizeof(INT);
+ ret = setsockopt(tcp_sock, IPPROTO_IP, SO_BROADCAST, &opt_val, opt_len);
+ if (ret == 0)
+ error_counter++;
+
+ /* ================================================================
+ * IP_TTL: round-trip on TCP socket with INT-sized input
+ * ================================================================ */
+ opt_val = 64;
+ opt_len = (INT)sizeof(INT);
+ ret = setsockopt(tcp_sock, IPPROTO_IP, IP_TTL, &opt_val, opt_len);
+ if (ret != 0)
+ error_counter++;
+
+ {
+ UCHAR ttl_get = 0;
+ INT ttl_len = (INT)sizeof(UCHAR);
+
+ ret = getsockopt(tcp_sock, IPPROTO_IP, IP_TTL, &ttl_get, &ttl_len);
+ if (ret != 0)
+ error_counter++;
+ if (ttl_get != 64)
+ error_counter++;
+ }
+
+ /* IP_TTL: round-trip on UDP socket. */
+ opt_val = 128;
+ opt_len = (INT)sizeof(INT);
+ ret = setsockopt(udp_sock, IPPROTO_IP, IP_TTL, &opt_val, opt_len);
+ if (ret != 0)
+ error_counter++;
+
+ {
+ UCHAR ttl_get = 0;
+ INT ttl_len = (INT)sizeof(UCHAR);
+
+ ret = getsockopt(udp_sock, IPPROTO_IP, IP_TTL, &ttl_get, &ttl_len);
+ if (ret != 0)
+ error_counter++;
+ if (ttl_get != 128)
+ error_counter++;
+ }
+
+ /* IP_TTL: out-of-range INT values must be rejected. */
+ opt_val = 300;
+ opt_len = (INT)sizeof(INT);
+ ret = setsockopt(tcp_sock, IPPROTO_IP, IP_TTL, &opt_val, opt_len);
+ if (ret == 0)
+ error_counter++;
+
+ opt_val = -1;
+ opt_len = (INT)sizeof(INT);
+ ret = setsockopt(tcp_sock, IPPROTO_IP, IP_TTL, &opt_val, opt_len);
+ if (ret == 0)
+ error_counter++;
+
+ /* ================================================================
+ * SO_BROADCAST: getsockopt validates option_length
+ * ================================================================ */
+ opt_val = 0;
+ opt_len = (INT)sizeof(INT);
+ ret = getsockopt(tcp_sock, SOL_SOCKET, SO_BROADCAST, &opt_val, &opt_len);
+ if (ret != 0)
+ error_counter++;
+ if (opt_val != 1)
+ error_counter++;
+ if (opt_len != (INT)sizeof(INT))
+ error_counter++;
+
+ opt_val = 0;
+ opt_len = 1;
+ ret = getsockopt(tcp_sock, SOL_SOCKET, SO_BROADCAST, &opt_val, &opt_len);
+ if (ret == 0)
+ error_counter++;
+
+ /* ================================================================
+ * TCP_NODELAY: getsockopt validates option_length
+ * ================================================================ */
+ opt_val = 0;
+ opt_len = (INT)sizeof(INT);
+ ret = getsockopt(tcp_sock, IPPROTO_TCP, TCP_NODELAY, &opt_val, &opt_len);
+ if (ret != 0)
+ error_counter++;
+ if (opt_val != 1)
+ error_counter++;
+ if (opt_len != (INT)sizeof(INT))
+ error_counter++;
+
+ opt_val = 0;
+ opt_len = 1;
+ ret = getsockopt(tcp_sock, IPPROTO_TCP, TCP_NODELAY, &opt_val, &opt_len);
+ if (ret == 0)
+ error_counter++;
+
+ /* Clean up. */
+ soc_close(tcp_sock);
+ soc_close(udp_sock);
+
+ if (error_counter)
+ test_control_return(1);
+ else
+ test_control_return(0);
+}
+
+#else
+#ifdef CTEST
+VOID test_application_define(void *first_unused_memory)
+#else
+void netx_bsd_socket_options_test_application_define(void *first_unused_memory)
+#endif
+{
+ NX_PARAMETER_NOT_USED(first_unused_memory);
+ test_control_return(3);
+}
+#endif /* NX_BSD_ENABLE && !NX_DISABLE_IPV4 && __PRODUCT_NETXDUO__ */