/*************************************************************************** * 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__ */