diff options
| author | mzgrebnak <[email protected]> | 2026-05-29 16:34:38 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-05-29 10:34:38 -0400 |
| commit | 09ce97f8f9019724407917c197c2a0c0e859bf2d (patch) | |
| tree | 3f0683ac14105149cf0759ea1fa133ed9675cd34 | |
| parent | 53070b69eed6af6840b0a890fe749e73e3f280f2 (diff) | |
Added BSD recvfromto support (#376)
* recvfromto support
* nx_bsd_inet_aton, disabling other format than four numbers separated by dots. Otherwise used in nx_bsd_inet_pton
It accepts unallowed format of IP address.
* bsd: address PR review feedback
- Fix build failure: add missing nx_bsd_recvfromto() prototype to
nxd_bsd.h near the other receive prototypes. The nx_bsd_recvfromto
macro maps to recvfromto, but without a prior declaration the
compiler emits -Werror=missing-declarations.
- Revert inet_aton() change: restore the 1-part, 2-part and 3-part
abbreviated IPv4 address formats (a.b.c and a.b). The PR removed
them, breaking existing tests that rely on these forms.
- Add IPv6 destination address support in recvfromto(): when the
receiving BSD socket is AF_INET6, extract the destination IPv6
address from the IPv6 packet header and populate a sockaddr_in6
in toAddr, mirroring the existing IPv4 path.
- Add regression test netx_bsd_recvfromto_test covering:
* recvfromto() populates fromAddr (sender) and toAddr (destination)
with correct address/port values for IPv4 UDP
* recvfromto() with NULL toAddr/toAddrLen works like recvfrom()
* inet_aton() still accepts abbreviated a.b and a.b.c forms
Register test 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.c | 72 | ||||
| -rw-r--r-- | addons/BSD/nxd_bsd.h | 2 | ||||
| -rw-r--r-- | test/cmake/netxduo/regression/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | test/regression/bsd_test/netx_bsd_recvfromto_test.c | 315 |
4 files changed, 386 insertions, 6 deletions
diff --git a/addons/BSD/nxd_bsd.c b/addons/BSD/nxd_bsd.c index 10a2dee5..b69edf57 100644 --- a/addons/BSD/nxd_bsd.c +++ b/addons/BSD/nxd_bsd.c @@ -164,7 +164,8 @@ static VOID _nxd_bsd_ipv4_packet_send(NX_PACKET *packet_ptr); static INT nx_bsd_send_internal(INT sockID, const CHAR *msg, INT msgLength, INT flags, NXD_ADDRESS *dst_address, USHORT dst_port, UINT local_interface_index); static INT nx_bsd_recv_internal(INT sockID, struct nx_bsd_iovec *iov, size_t iovlen, INT flags, - struct nx_bsd_sockaddr *fromAddr, INT *fromAddrLen); + struct nx_bsd_sockaddr *fromAddr, INT *fromAddrLen, + struct nx_bsd_sockaddr *toAddr, INT *toAddrLen); #ifdef FEATURE_NX_IPV6 static VOID _nxd_bsd_ipv6_packet_send(NX_PACKET *packet_ptr, ULONG *src_addr, ULONG *dest_addr); #endif /* FEATURE_NX_IPV6 */ @@ -3931,7 +3932,7 @@ struct nx_bsd_iovec iov; iov.iov_len = (size_t)bufferLength; /* Call the recv_internal() function. */ - return nx_bsd_recv_internal(sockID, &iov, 1, flags, NX_NULL, NX_NULL); + return nx_bsd_recv_internal(sockID, &iov, 1, flags, NX_NULL, NX_NULL, NX_NULL, NX_NULL); } @@ -3986,7 +3987,21 @@ struct nx_bsd_iovec iov; iov.iov_len = (size_t)bufferLength; /* Call the recv_internal() function. */ - return nx_bsd_recv_internal(sockID, &iov, 1, flags, fromAddr, fromAddrLen); + return nx_bsd_recv_internal(sockID, &iov, 1, flags, fromAddr, fromAddrLen, NX_NULL, NX_NULL); +} + + +INT nx_bsd_recvfromto(INT sockID, CHAR *rcvBuffer, INT bufferLength, INT flags, + struct nx_bsd_sockaddr *fromAddr, INT *fromAddrLen, + struct nx_bsd_sockaddr *toAddr, INT *toAddrLen) +{ +struct nx_bsd_iovec iov; + + iov.iov_base = rcvBuffer; + iov.iov_len = (size_t)bufferLength; + + /* Call the recv_internal() function. */ + return nx_bsd_recv_internal(sockID, &iov, 1, flags, fromAddr, fromAddrLen, toAddr, toAddrLen); } /**************************************************************************/ @@ -4035,7 +4050,7 @@ INT fromAddrLen = 0; fromAddrLen = (INT)(msg -> msg_namelen); } - return(nx_bsd_recv_internal(sockID, msg -> msg_iov, msg -> msg_iovlen, flags, (struct nx_bsd_sockaddr *)(msg -> msg_name), &fromAddrLen)); + return(nx_bsd_recv_internal(sockID, msg -> msg_iov, msg -> msg_iovlen, flags, (struct nx_bsd_sockaddr *)(msg -> msg_name), &fromAddrLen, NX_NULL, NX_NULL)); } /**************************************************************************/ @@ -4102,7 +4117,9 @@ INT fromAddrLen = 0; /* recvfrom */ /* */ /**************************************************************************/ -static INT nx_bsd_recv_internal(INT sockID, struct nx_bsd_iovec *iov, size_t iovlen, INT flags, struct nx_bsd_sockaddr *fromAddr, INT *fromAddrLen) +static INT nx_bsd_recv_internal(INT sockID, struct nx_bsd_iovec *iov, size_t iovlen, INT flags, + struct nx_bsd_sockaddr *fromAddr, INT *fromAddrLen, + struct nx_bsd_sockaddr *toAddr, INT *toAddrLen) { UINT status; NX_PACKET *packet_ptr; @@ -4388,6 +4405,51 @@ struct nx_bsd_sockaddr_in6 /* Get the sender and port from the UDP packet. */ nxd_udp_source_extract(packet_ptr, &bsd_socket_ptr -> nx_bsd_socket_source_ip_address, (UINT *)&bsd_socket_ptr -> nx_bsd_socket_source_port); + + if(toAddr && (*toAddrLen != 0)) + { +#ifndef NX_DISABLE_IPV4 + if(bsd_socket_ptr -> nx_bsd_socket_family == AF_INET) + { + ULONG nx_ip_header_destination_ip = ((NX_IPV4_HEADER *)packet_ptr -> nx_packet_ip_header) -> nx_ip_header_destination_ip; + ULONG *prepend_ptr = (ULONG *)packet_ptr -> nx_packet_prepend_ptr; + USHORT dst_port = (USHORT)(*(prepend_ptr - 2U) & 0xFFFFU); + struct nx_bsd_sockaddr_in destination_address; + + destination_address.sin_family = AF_INET; + destination_address.sin_addr.s_addr = ntohl(nx_ip_header_destination_ip); + destination_address.sin_port = ntohs(dst_port); + + if(*toAddrLen > (INT)sizeof(struct nx_bsd_sockaddr_in)) + { + *toAddrLen = (INT)sizeof(struct nx_bsd_sockaddr_in); + } + memcpy(toAddr, &destination_address, (UINT)(*toAddrLen)); + } +#endif /* NX_DISABLE_IPV4 */ +#ifdef FEATURE_NX_IPV6 + if(bsd_socket_ptr -> nx_bsd_socket_family == AF_INET6) + { + NX_IPV6_HEADER *ipv6_header = (NX_IPV6_HEADER *)packet_ptr -> nx_packet_ip_header; + ULONG *prepend_ptr = (ULONG *)packet_ptr -> nx_packet_prepend_ptr; + USHORT dst_port = (USHORT)(*(prepend_ptr - 2U) & 0xFFFFU); + struct nx_bsd_sockaddr_in6 destination_address6; + + destination_address6.sin6_family = AF_INET6; + destination_address6.sin6_addr._S6_un._S6_u32[0] = ntohl(ipv6_header -> nx_ip_header_destination_ip[0]); + destination_address6.sin6_addr._S6_un._S6_u32[1] = ntohl(ipv6_header -> nx_ip_header_destination_ip[1]); + destination_address6.sin6_addr._S6_un._S6_u32[2] = ntohl(ipv6_header -> nx_ip_header_destination_ip[2]); + destination_address6.sin6_addr._S6_un._S6_u32[3] = ntohl(ipv6_header -> nx_ip_header_destination_ip[3]); + destination_address6.sin6_port = ntohs(dst_port); + + if(*toAddrLen > (INT)sizeof(struct nx_bsd_sockaddr_in6)) + { + *toAddrLen = (INT)sizeof(struct nx_bsd_sockaddr_in6); + } + memcpy(toAddr, &destination_address6, (UINT)(*toAddrLen)); + } +#endif /* FEATURE_NX_IPV6 */ + } } #if defined(NX_BSD_RAW_SUPPORT) || defined(NX_BSD_RAW_PPPOE_SUPPORT) diff --git a/addons/BSD/nxd_bsd.h b/addons/BSD/nxd_bsd.h index 15d1c1ac..5223efd0 100644 --- a/addons/BSD/nxd_bsd.h +++ b/addons/BSD/nxd_bsd.h @@ -238,6 +238,7 @@ extern "C" { #define nx_bsd_inet_ntop inet_ntop #define nx_bsd_listen listen #define nx_bsd_recvfrom recvfrom +#define nx_bsd_recvfromto recvfromto #define nx_bsd_recv recv #define nx_bsd_recvmsg recvmsg #define nx_bsd_sendto sendto @@ -1014,6 +1015,7 @@ VOID nx_bsd_raw_receive_notify(NX_IP *ip_ptr, UINT bsd_socket_index); #endif UINT nx_bsd_socket_set_inherited_settings(UINT master_sock_id, UINT secondary_sock_id); INT nx_bsd_recvfrom(INT sockID, CHAR *buffer, INT buffersize, INT flags,struct nx_bsd_sockaddr *fromAddr, INT *fromAddrLen); +INT nx_bsd_recvfromto(INT sockID, CHAR *rcvBuffer, INT bufferLength, INT flags, struct nx_bsd_sockaddr *fromAddr, INT *fromAddrLen, struct nx_bsd_sockaddr *toAddr, INT *toAddrLen); INT nx_bsd_recv(INT sockID, VOID *rcvBuffer, INT bufferLength, INT flags); INT nx_bsd_recvmsg(INT sockID, struct nx_bsd_msghdr *msg, INT flags); INT nx_bsd_sendto(INT sockID, CHAR *msg, INT msgLength, INT flags, struct nx_bsd_sockaddr *destAddr, INT destAddrLen); diff --git a/test/cmake/netxduo/regression/CMakeLists.txt b/test/cmake/netxduo/regression/CMakeLists.txt index 4dbad145..794d4b36 100644 --- a/test/cmake/netxduo/regression/CMakeLists.txt +++ b/test/cmake/netxduo/regression/CMakeLists.txt @@ -55,7 +55,8 @@ if("-DNX_BSD_ENABLE" IN_LIST ${CMAKE_BUILD_TYPE}) ${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_socket_options_test.c - ${SOURCE_DIR}/bsd_test/netx_bsd_select_improvements_test.c) + ${SOURCE_DIR}/bsd_test/netx_bsd_select_improvements_test.c + ${SOURCE_DIR}/bsd_test/netx_bsd_recvfromto_test.c) if("-DNX_BSD_RAW_SUPPORT" IN_LIST ${CMAKE_BUILD_TYPE}) list( APPEND diff --git a/test/regression/bsd_test/netx_bsd_recvfromto_test.c b/test/regression/bsd_test/netx_bsd_recvfromto_test.c new file mode 100644 index 00000000..223e5c04 --- /dev/null +++ b/test/regression/bsd_test/netx_bsd_recvfromto_test.c @@ -0,0 +1,315 @@ +/*************************************************************************** + * 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 covers the recvfromto() API from PR #376: + * + * 1. recvfromto() populates fromAddr with the sender's address/port and + * toAddr with the destination (local) address/port of the UDP packet. + * + * 2. recvfromto() with NULL toAddr/toAddrLen works like recvfrom(). + * + * 3. inet_aton() still accepts abbreviated IPv4 forms such as a.b.c + * and a.b (regression for the inet_aton revert). + */ + +#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 +#define TEST_PORT 4444 + +static TX_THREAD ntest_0; +static TX_THREAD ntest_1; + +static NX_PACKET_POOL pool_0; +static NX_IP ip_0; /* BSD receiver: 1.2.3.4 */ +static NX_IP ip_1; /* NX sender: 1.2.3.5 */ +static TX_SEMAPHORE sema_rx_ready; +static TX_SEMAPHORE sema_done; + +static ULONG error_counter; + +static ULONG packet_pool_area[(512 + sizeof(NX_PACKET)) * 32 / 4]; +static ULONG bsd_thread_area[DEMO_STACK_SIZE / sizeof(ULONG)]; + +static void ntest_0_entry(ULONG thread_input); +static void ntest_1_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_recvfromto_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; + + tx_thread_create(&ntest_1, "thread 1", ntest_1_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", 512, + 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_ip_create(&ip_1, "NetX IP Instance 1", + IP_ADDRESS(1, 2, 3, 5), 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_arp_enable(&ip_1, (void *)pointer, 1024); + pointer += 1024; + if (status) + error_counter++; + + status = nx_udp_enable(&ip_0); + status += nx_udp_enable(&ip_1); + 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++; + + status = tx_semaphore_create(&sema_rx_ready, "RX READY", 0); + if (status) + error_counter++; + + status = tx_semaphore_create(&sema_done, "DONE", 0); + if (status) + error_counter++; +} + +/* Thread 0: BSD receiver */ +static void ntest_0_entry(ULONG thread_input) +{ +INT sock; +struct nx_bsd_sockaddr_in local_addr; +struct nx_bsd_sockaddr_in from_addr; +struct nx_bsd_sockaddr_in to_addr; +INT from_len; +INT to_len; +CHAR buf[64]; +INT ret; +UINT actual_status; + + /* Wait for IP stack initialisation. */ + nx_ip_status_check(&ip_0, NX_IP_INITIALIZE_DONE, &actual_status, + NX_IP_PERIODIC_RATE); + + /* --- Test 1: recvfromto() populates fromAddr and toAddr --- */ + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) + { + error_counter++; + test_control_return(1); + return; + } + + memset(&local_addr, 0, sizeof(local_addr)); + local_addr.sin_family = AF_INET; + local_addr.sin_port = htons(TEST_PORT); + local_addr.sin_addr.s_addr = htonl(IP_ADDRESS(1, 2, 3, 4)); + + if (bind(sock, (struct nx_bsd_sockaddr *)&local_addr, sizeof(local_addr)) < 0) + { + error_counter++; + soc_close(sock); + test_control_return(1); + return; + } + + /* Signal sender that receiver is ready. */ + tx_semaphore_put(&sema_rx_ready); + + memset(&from_addr, 0, sizeof(from_addr)); + memset(&to_addr, 0, sizeof(to_addr)); + from_len = sizeof(from_addr); + to_len = sizeof(to_addr); + + ret = recvfromto(sock, buf, sizeof(buf), 0, + (struct nx_bsd_sockaddr *)&from_addr, &from_len, + (struct nx_bsd_sockaddr *)&to_addr, &to_len); + if (ret <= 0) + { + error_counter++; + } + else + { + /* Validate fromAddr: must be 1.2.3.5 : any port */ + if (from_addr.sin_family != AF_INET) + error_counter++; + if (ntohl(from_addr.sin_addr.s_addr) != IP_ADDRESS(1, 2, 3, 5)) + error_counter++; + + /* Validate toAddr: must be 1.2.3.4 : TEST_PORT */ + if (to_addr.sin_family != AF_INET) + error_counter++; + if (ntohl(to_addr.sin_addr.s_addr) != IP_ADDRESS(1, 2, 3, 4)) + error_counter++; + if (ntohs(to_addr.sin_port) != TEST_PORT) + error_counter++; + + /* toAddrLen must be trimmed to sizeof(sockaddr_in). */ + if (to_len != (INT)sizeof(struct nx_bsd_sockaddr_in)) + error_counter++; + } + + soc_close(sock); + + /* --- Test 2: recvfromto() with NULL toAddr works like recvfrom() --- */ + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) + { + error_counter++; + test_control_return(1); + return; + } + + memset(&local_addr, 0, sizeof(local_addr)); + local_addr.sin_family = AF_INET; + local_addr.sin_port = htons(TEST_PORT); + local_addr.sin_addr.s_addr = htonl(IP_ADDRESS(1, 2, 3, 4)); + + if (bind(sock, (struct nx_bsd_sockaddr *)&local_addr, sizeof(local_addr)) < 0) + { + error_counter++; + soc_close(sock); + test_control_return(1); + return; + } + + tx_semaphore_put(&sema_rx_ready); + + memset(&from_addr, 0, sizeof(from_addr)); + from_len = sizeof(from_addr); + + ret = recvfromto(sock, buf, sizeof(buf), 0, + (struct nx_bsd_sockaddr *)&from_addr, &from_len, + NX_NULL, NX_NULL); + if (ret <= 0) + error_counter++; + + soc_close(sock); + + /* --- Test 3: inet_aton() still accepts abbreviated IPv4 forms --- */ + { + struct in_addr in_val; + INT rc; + + /* a.b format: 11.657930 == 11.0x0a0a0a == 11.10.10.10 */ + rc = inet_aton("11.657930", &in_val); + if (rc == 0) + error_counter++; + + /* a.b.c format: 11.10.2570 == 11.10.0x0a0a == 11.10.10.10 */ + rc = inet_aton("11.10.2570", &in_val); + if (rc == 0) + error_counter++; + + /* standard dotted-decimal must still work */ + rc = inet_aton("11.10.10.10", &in_val); + if (rc == 0) + error_counter++; + } + + tx_semaphore_put(&sema_done); +} + +/* Thread 1: NX UDP sender */ +static void ntest_1_entry(ULONG thread_input) +{ +NX_UDP_SOCKET udp_socket; +NX_PACKET *packet_ptr; +NXD_ADDRESS dest_addr; +UINT actual_status; + + nx_ip_status_check(&ip_1, NX_IP_INITIALIZE_DONE, &actual_status, + NX_IP_PERIODIC_RATE); + + nx_udp_socket_create(&ip_1, &udp_socket, "UDP sender", + NX_IP_NORMAL, NX_FRAGMENT_OKAY, NX_IP_TIME_TO_LIVE, 8); + nx_udp_socket_bind(&udp_socket, NX_ANY_PORT, NX_WAIT_FOREVER); + + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.nxd_ip_version = NX_IP_VERSION_V4; + dest_addr.nxd_ip_address.v4 = IP_ADDRESS(1, 2, 3, 4); + + /* Send for Test 1 */ + tx_semaphore_get(&sema_rx_ready, NX_IP_PERIODIC_RATE); + + nx_packet_allocate(&pool_0, &packet_ptr, NX_UDP_PACKET, NX_WAIT_FOREVER); + nx_packet_data_append(packet_ptr, "hello", 5, &pool_0, NX_WAIT_FOREVER); + nxd_udp_socket_send(&udp_socket, packet_ptr, &dest_addr, TEST_PORT); + + /* Send for Test 2 */ + tx_semaphore_get(&sema_rx_ready, NX_IP_PERIODIC_RATE); + + nx_packet_allocate(&pool_0, &packet_ptr, NX_UDP_PACKET, NX_WAIT_FOREVER); + nx_packet_data_append(packet_ptr, "world", 5, &pool_0, NX_WAIT_FOREVER); + nxd_udp_socket_send(&udp_socket, packet_ptr, &dest_addr, TEST_PORT); + + nx_udp_socket_unbind(&udp_socket); + nx_udp_socket_delete(&udp_socket); + + /* Wait for receiver to finish then report result. */ + tx_semaphore_get(&sema_done, 5 * NX_IP_PERIODIC_RATE); + test_control_return(error_counter ? 1 : 0); +} + +#else /* !(NX_BSD_ENABLE && !NX_DISABLE_IPV4) */ +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_bsd_recvfromto_test_application_define(void *first_unused_memory) +#endif +{ + NX_PARAMETER_NOT_USED(first_unused_memory); + test_control_return(3); /* skip */ +} +#endif /* NX_BSD_ENABLE && !NX_DISABLE_IPV4 */ |
