Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions common/src/nxd_udp_socket_send.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,12 @@ UINT compute_checksum = 1;
return(status);
}
}
else if (packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_state != NX_IPV6_ADDR_STATE_VALID)
{

/* The packet carries the address it was received on, which is no longer valid. */
return(NX_NO_INTERFACE_ADDRESS);
}

/* Fill in the IP src/dest address */
ip_dest_addr = &ip_address -> nxd_ip_address.v6[0];
Expand Down
1 change: 1 addition & 0 deletions test/cmake/netxduo/regression/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ set(netxduo_test_cases
${SOURCE_DIR}/netxduo_test/netx_ipv6_disable_test.c
${SOURCE_DIR}/netxduo_test/netx_ipv6_disable_tcp_test.c
${SOURCE_DIR}/netxduo_test/netx_ipv6_dad_failure_notify_send_test.c
${SOURCE_DIR}/netxduo_test/netx_udp_ipv6_reply_invalid_address_test.c
${SOURCE_DIR}/netxduo_test/netx_icmp_multiple_ping6_test1.c
${SOURCE_DIR}/netxduo_test/netx_ip_address_get_test.c
${SOURCE_DIR}/netxduo_test/netx_10_24_02_test.c
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@
/***************************************************************************
* 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 Claude Code (Opus 5.5).
* 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 NetX test concentrates on sending back a received IPv6 UDP datagram
once the address it arrived on is no longer valid.

A received datagram carries the address it arrived on, and
nxd_udp_socket_send uses that address as the source of a reply instead of
looking one up. If the address has been deleted, or IPv6 disabled, the
entry is no longer attached to an interface; if it has been set again it is
still being checked for duplicates. In each case the send must be refused
with NX_NO_INTERFACE_ADDRESS, the packet left to the caller, and the IP
instance must carry on. A deleted entry used as a source would stop the IP
thread for good, with the IP mutex held.

ip_1 sends to ip_0's 2001::4, and ip_0 replies with the packet it received.
The reply is sent by a helper thread and waited for with a bound, so the
test reports an error rather than hanging if the send never returns. After
each refusal ip_0 gets its address back, and a datagram and its reply cross
both ways again. */

#include "tx_api.h"
#include "nx_api.h"

extern void test_control_return(UINT status);

#ifdef FEATURE_NX_IPV6

#define DEMO_STACK_SIZE 2048

static TX_THREAD thread_0;
static TX_THREAD thread_1;

static NX_PACKET_POOL pool_0;
static NX_IP ip_0;
static NX_IP ip_1;
static NX_UDP_SOCKET socket_0;
static NX_UDP_SOCKET socket_1;

static ULONG error_counter;
static NXD_ADDRESS address_0;
static NXD_ADDRESS address_1;

/* The reply handed to thread 1, and what came of it. */
static NX_PACKET *reply_ptr;
static UINT reply_status;
static volatile UINT reply_done;

static void thread_0_entry(ULONG thread_input);
static void thread_1_entry(ULONG thread_input);
static UINT datagram_receive(NX_PACKET **packet_ptr);
static UINT reply_send(NX_PACKET *packet_ptr);
static UINT address_restore(VOID);
static VOID round_trip(VOID);

extern void _nx_ram_network_driver_1500(struct NX_IP_DRIVER_STRUCT *driver_req);

#ifdef CTEST
VOID test_application_define(void *first_unused_memory)
#else
void netx_udp_ipv6_reply_invalid_address_test_application_define(void *first_unused_memory)
#endif
{

CHAR *pointer;
UINT status;

pointer = (CHAR *) first_unused_memory;

error_counter = 0;

tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
pointer, DEMO_STACK_SIZE,
4, 4, TX_NO_TIME_SLICE, TX_AUTO_START);
pointer = pointer + DEMO_STACK_SIZE;

/* Sends the replies; higher priority, so it runs as soon as it is resumed. */
tx_thread_create(&thread_1, "thread 1", thread_1_entry, 0,
pointer, DEMO_STACK_SIZE,
3, 3, TX_NO_TIME_SLICE, TX_DONT_START);
pointer = pointer + DEMO_STACK_SIZE;

nx_system_initialize();

status = nx_packet_pool_create(&pool_0, "NetX Main Packet Pool", 1536, pointer, 1536 * 12);
pointer = pointer + 1536 * 12;
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_1500, pointer, 2048, 1);
pointer = pointer + 2048;
status += nx_ip_create(&ip_1, "NetX IP Instance 1", IP_ADDRESS(1, 2, 3, 5), 0xFFFFFF00UL, &pool_0,
_nx_ram_network_driver_1500, pointer, 2048, 1);
pointer = pointer + 2048;
#ifndef NX_DISABLE_IPV4
status += nx_arp_enable(&ip_0, (void *) pointer, 1024);
pointer = pointer + 1024;
status += nx_arp_enable(&ip_1, (void *) pointer, 1024);
pointer = pointer + 1024;
#endif /* NX_DISABLE_IPV4 */
status += nx_udp_enable(&ip_0);
status += nx_udp_enable(&ip_1);
status += nxd_ipv6_enable(&ip_0);
status += nxd_ipv6_enable(&ip_1);
if (status)
error_counter++;
}


static void thread_0_entry(ULONG thread_input)
{

NX_PACKET *packet_ptr;

NX_PARAMETER_NOT_USED(thread_input);

printf("NetX Test: UDP IPv6 Reply Invalid Address Test......................");

/* Let the IP threads bring the links up. */
tx_thread_sleep(NX_IP_PERIODIC_RATE);

/* The addresses are valid at once, since ICMPv6 is not yet enabled. */
address_0.nxd_ip_version = NX_IP_VERSION_V6;
address_0.nxd_ip_address.v6[0] = 0x20010000;
address_0.nxd_ip_address.v6[1] = 0;
address_0.nxd_ip_address.v6[2] = 0;
address_0.nxd_ip_address.v6[3] = 4;
address_1 = address_0;
address_1.nxd_ip_address.v6[3] = 5;
if (nxd_ipv6_address_set(&ip_0, 0, &address_0, 64, NX_NULL) ||
nxd_ipv6_address_set(&ip_1, 0, &address_1, 64, NX_NULL) ||
nxd_icmp_enable(&ip_0) || nxd_icmp_enable(&ip_1))
error_counter++;

if (nx_udp_socket_create(&ip_0, &socket_0, "Socket 0", NX_IP_NORMAL, NX_FRAGMENT_OKAY, 0x80, 5) ||
nx_udp_socket_bind(&socket_0, 0x88, NX_NO_WAIT) ||
nx_udp_socket_create(&ip_1, &socket_1, "Socket 1", NX_IP_NORMAL, NX_FRAGMENT_OKAY, 0x80, 5) ||
nx_udp_socket_bind(&socket_1, 0x89, NX_NO_WAIT))
error_counter++;

/* Both ways work to begin with. */
round_trip();

/* 1. The address deleted. */
if (datagram_receive(&packet_ptr) == NX_SUCCESS)
{
if (nxd_ipv6_address_delete(&ip_0, 0))
error_counter++;
if (reply_send(packet_ptr) != NX_NO_INTERFACE_ADDRESS)
error_counter++;
nx_packet_release(packet_ptr);
if (address_restore() == NX_SUCCESS)
round_trip();
}

/* 2. IPv6 disabled. */
if (datagram_receive(&packet_ptr) == NX_SUCCESS)
{
if (nxd_ipv6_disable(&ip_0))
error_counter++;
if (reply_send(packet_ptr) != NX_NO_INTERFACE_ADDRESS)
error_counter++;
nx_packet_release(packet_ptr);

/* Disabling IPv6 also stopped ICMPv6. */
if (nxd_ipv6_enable(&ip_0) || nxd_icmp_enable(&ip_0))
error_counter++;
if (address_restore() == NX_SUCCESS)
round_trip();
}

#ifndef NX_DISABLE_IPV6_DAD
/* 3. The address deleted and set again: it is checked for duplicates
before it is valid. Once it is, the refused packet goes out. */
if (datagram_receive(&packet_ptr) == NX_SUCCESS)
{
if (nxd_ipv6_address_delete(&ip_0, 0) ||
nxd_ipv6_address_set(&ip_0, 0, &address_0, 64, NX_NULL))
error_counter++;
if (ip_0.nx_ipv6_address[0].nxd_ipv6_address_state != NX_IPV6_ADDR_STATE_TENTATIVE)
error_counter++;
if (reply_send(packet_ptr) != NX_NO_INTERFACE_ADDRESS)
{
error_counter++;
}
else if (address_restore() == NX_SUCCESS)
{
if (reply_send(packet_ptr) != NX_SUCCESS)
error_counter++;
else if (nx_udp_socket_receive(&socket_1, &packet_ptr, NX_IP_PERIODIC_RATE))
error_counter++;
else
nx_packet_release(packet_ptr);
}
}
#endif /* NX_DISABLE_IPV6_DAD */

if (error_counter)
{
printf("ERROR!\n");
test_control_return(1);
}
else
{
printf("SUCCESS!\n");
test_control_return(0);
}
}


/* Sends each reply it is given back to ip_1, then waits for the next. */
static void thread_1_entry(ULONG thread_input)
{

NX_PARAMETER_NOT_USED(thread_input);

while (1)
{
reply_status = nxd_udp_socket_send(&socket_0, reply_ptr, &address_1, 0x89);
reply_done = NX_TRUE;
tx_thread_suspend(&thread_1);
}
}


/* ip_1 sends a datagram to ip_0, which receives it. */
static UINT datagram_receive(NX_PACKET **packet_ptr)
{

NX_PACKET *send_ptr;

if (nx_packet_allocate(&pool_0, &send_ptr, NX_IPv6_UDP_PACKET, NX_NO_WAIT) ||
nx_packet_data_append(send_ptr, "request", 7, &pool_0, NX_NO_WAIT))
{
error_counter++;
return(NX_NOT_SUCCESSFUL);
}
if (nxd_udp_socket_send(&socket_1, send_ptr, &address_0, 0x88))
{
nx_packet_release(send_ptr);
error_counter++;
return(NX_NOT_SUCCESSFUL);
}
if (nx_udp_socket_receive(&socket_0, packet_ptr, NX_IP_PERIODIC_RATE))
{
error_counter++;
return(NX_NOT_SUCCESSFUL);
}
return(NX_SUCCESS);
}


/* Has thread 1 send the packet as a reply. If the send has not returned
within a second, the IP instance is stuck: the test stops there. A refused
packet is still the caller's; a sent one is not, so the pointer is cleared. */
static UINT reply_send(NX_PACKET *packet_ptr)
{

UINT i;

reply_ptr = packet_ptr;
reply_done = NX_FALSE;
tx_thread_resume(&thread_1);
for (i = 0; (i < NX_IP_PERIODIC_RATE) && (reply_done == NX_FALSE); i++)
tx_thread_sleep(1);

if (reply_done == NX_FALSE)
{
printf("ERROR!\n");
test_control_return(1);
}

return(reply_status);
}


/* Gives ip_0 its address back and waits, bounded, until it is valid. */
static UINT address_restore(VOID)
{

UINT i;

if ((ip_0.nx_ipv6_address[0].nxd_ipv6_address_state == NX_IPV6_ADDR_STATE_UNKNOWN) &&
nxd_ipv6_address_set(&ip_0, 0, &address_0, 64, NX_NULL))
{
error_counter++;
return(NX_NOT_SUCCESSFUL);
}
for (i = 0; (i < 10 * NX_IP_PERIODIC_RATE) &&
(ip_0.nx_ipv6_address[0].nxd_ipv6_address_state != NX_IPV6_ADDR_STATE_VALID); i++)
tx_thread_sleep(1);
if (ip_0.nx_ipv6_address[0].nxd_ipv6_address_state != NX_IPV6_ADDR_STATE_VALID)
{
error_counter++;
return(NX_NOT_SUCCESSFUL);
}
return(NX_SUCCESS);
}


/* A datagram from ip_1 to ip_0, and the received packet sent back as the
reply, from the address it arrived on. */
static VOID round_trip(VOID)
{

NX_PACKET *packet_ptr;
NXD_ADDRESS source;
UINT port;

if (datagram_receive(&packet_ptr))
return;

if (reply_send(packet_ptr) != NX_SUCCESS)
{
nx_packet_release(packet_ptr);
error_counter++;
return;
}

if (nx_udp_socket_receive(&socket_1, &packet_ptr, NX_IP_PERIODIC_RATE))
{
error_counter++;
return;
}
if (nxd_udp_packet_info_extract(packet_ptr, &source, NX_NULL, &port, NX_NULL) ||
(source.nxd_ip_version != NX_IP_VERSION_V6) ||
(source.nxd_ip_address.v6[0] != 0x20010000) || (source.nxd_ip_address.v6[3] != 4) ||
(port != 0x88))
error_counter++;
nx_packet_release(packet_ptr);
}

#else

#ifdef CTEST
VOID test_application_define(void *first_unused_memory)
#else
void netx_udp_ipv6_reply_invalid_address_test_application_define(void *first_unused_memory)
#endif
{

/* Print out test information banner. */
printf("NetX Test: UDP IPv6 Reply Invalid Address Test......................N/A\n");
test_control_return(3);
}

#endif
Loading