From 52dd497cd110b621ec77c5c1884d58349df377b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Sat, 17 Oct 2026 08:03:09 -0400 Subject: [PATCH] Held back IPv6 TCP segments while the source address is not valid Deleting an IPv6 address, or disabling IPv6, while a TCP connection uses it clears the address entry and detaches it from its interface, but the socket keeps pointing at the entry. The data path refuses to send in that state with NX_NO_INTERFACE_ADDRESS, and once an address is set in the same entry again the connection carries on, which netx_tcp_ipv6_basic_processing_test relies on. The control path (ACK, FIN, RST, keepalive, window probe) and the retransmission path did not check: the next such segment reached _nx_ipv6_packet_send, whose NX_ASSERT on the attached interface puts the sending thread to sleep forever with the IP protection mutex held, deadlocking the IP instance. With NX_DISABLE_ASSERT it is a NULL dereference. _nx_tcp_packet_send_control and _nx_tcp_socket_retransmit now make the data path's check. While the connection's IPv6 source address is not valid, a control segment is dropped, as one lost on the link would be, and a retransmission sends nothing and leaves the packets queued; the retry count still limits how long. Added netx_ipv6_address_delete_tcp_test and netx_ipv6_disable_tcp_test. With the fix reverted both hang to their bound in v6_full_build and v6_only_build; with it they pass in v6_full_build, v6_build, v6_small_build, v6_no_reset_disconn_build and v6_only_build, as do netx_tcp_ipv6_basic_processing_test, netx_ipv6_address_delete_test and netx_ipv6_disable_test. Assisted-by: Claude Code (Opus 5.5) --- common/src/nx_tcp_packet_send_control.c | 13 + common/src/nx_tcp_socket_retransmit.c | 13 + test/cmake/netxduo/regression/CMakeLists.txt | 2 + .../netx_ipv6_address_delete_tcp_test.c | 350 ++++++++++++++++++ .../netxduo_test/netx_ipv6_disable_tcp_test.c | 328 ++++++++++++++++ 5 files changed, 706 insertions(+) create mode 100644 test/regression/netxduo_test/netx_ipv6_address_delete_tcp_test.c create mode 100644 test/regression/netxduo_test/netx_ipv6_disable_tcp_test.c diff --git a/common/src/nx_tcp_packet_send_control.c b/common/src/nx_tcp_packet_send_control.c index f9c469a8a..2923c3669 100644 --- a/common/src/nx_tcp_packet_send_control.c +++ b/common/src/nx_tcp_packet_send_control.c @@ -103,6 +103,19 @@ ULONG window_size; /* Setup the IP pointer. */ ip_ptr = socket_ptr -> nx_tcp_socket_ip_ptr; +#ifdef FEATURE_NX_IPV6 + /* An IPv6 connection cannot send while its source address is not valid: deleted, + or IPv6 disabled, until an address is set in that entry again. A deleted entry is + detached from its interface, which _nx_ipv6_packet_send asserts never happens. + The data path refuses in the same case; a control segment is dropped, as one + lost on the link would be. */ + if ((socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V6) && + (socket_ptr -> nx_tcp_socket_ipv6_addr -> nxd_ipv6_address_state != NX_IPV6_ADDR_STATE_VALID)) + { + return; + } +#endif /* FEATURE_NX_IPV6 */ + if (control_bits & NX_TCP_SYN_BIT) { diff --git a/common/src/nx_tcp_socket_retransmit.c b/common/src/nx_tcp_socket_retransmit.c index e1cfa93f5..4ec2c20a8 100644 --- a/common/src/nx_tcp_socket_retransmit.c +++ b/common/src/nx_tcp_socket_retransmit.c @@ -189,6 +189,19 @@ ULONG window_size; socket_ptr -> nx_tcp_socket_timeout = socket_ptr -> nx_tcp_socket_timeout_rate << (socket_ptr -> nx_tcp_socket_timeout_retries * socket_ptr -> nx_tcp_socket_timeout_shift); +#ifdef FEATURE_NX_IPV6 + /* An IPv6 connection cannot send while its source address is not valid: deleted, + or IPv6 disabled, until an address is set in that entry again. A deleted entry is + detached from its interface, which _nx_ipv6_packet_send asserts never happens. + The data path refuses in the same case. Leave the packets queued, as for an + unanswered retransmission; the retry count above still limits how long. */ + if ((socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V6) && + (socket_ptr -> nx_tcp_socket_ipv6_addr -> nxd_ipv6_address_state != NX_IPV6_ADDR_STATE_VALID)) + { + return; + } +#endif /* FEATURE_NX_IPV6 */ + /* Get available size of packet that can be sent. */ available = socket_ptr -> nx_tcp_socket_tx_window_congestion; diff --git a/test/cmake/netxduo/regression/CMakeLists.txt b/test/cmake/netxduo/regression/CMakeLists.txt index ad5c65013..308026c93 100644 --- a/test/cmake/netxduo/regression/CMakeLists.txt +++ b/test/cmake/netxduo/regression/CMakeLists.txt @@ -295,6 +295,7 @@ set(netxduo_test_cases ${SOURCE_DIR}/netxduo_test/netx_igmp_basic_test.c ${SOURCE_DIR}/netxduo_test/netx_14_19_test.c ${SOURCE_DIR}/netxduo_test/netx_ipv6_address_delete_test.c + ${SOURCE_DIR}/netxduo_test/netx_ipv6_address_delete_tcp_test.c ${SOURCE_DIR}/netxduo_test/netx_2_20_test.c ${SOURCE_DIR}/netxduo_test/netx_arp_no_duplicate_entry_test.c ${SOURCE_DIR}/netxduo_test/netx_ip_raw_loopback_test.c @@ -361,6 +362,7 @@ set(netxduo_test_cases ${SOURCE_DIR}/netxduo_test/netx_ip_nxe_api_test.c ${SOURCE_DIR}/netxduo_test/netx_tcp_socket_listen_test.c ${SOURCE_DIR}/netxduo_test/netx_ipv6_disable_test.c + ${SOURCE_DIR}/netxduo_test/netx_ipv6_disable_tcp_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 diff --git a/test/regression/netxduo_test/netx_ipv6_address_delete_tcp_test.c b/test/regression/netxduo_test/netx_ipv6_address_delete_tcp_test.c new file mode 100644 index 000000000..39b3c7f9b --- /dev/null +++ b/test/regression/netxduo_test/netx_ipv6_address_delete_tcp_test.c @@ -0,0 +1,350 @@ +/*************************************************************************** + * 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 an established IPv6 TCP connection whose + source address is deleted and later set again. + + A TCP socket keeps a pointer to the address entry it sends from, and + nxd_ipv6_address_delete clears that entry, detaching it from its + interface. The connection survives: sends are refused with + NX_NO_INTERFACE_ADDRESS while the entry is empty, and once an address is + set in it again the same connection carries on, as + netx_tcp_ipv6_basic_processing_test also shows. What must not happen in + between is a send. The control path -- the ACK, FIN, RST and window probe + -- and the retransmission path hand their segment to _nx_ipv6_packet_send, + which asserts that the entry is attached; before those paths checked the + address, the first such segment stopped the sending thread with the IP + protection held, and the IP instance with it, and this test never + finished. + + Here an acknowledgment falls due, and a data segment lost on the link + falls due for retransmission, while the address is gone. Both are held + back. When the address is set again the retransmission goes out, the + peer receives the data, and the connection is used and closed normally. */ + +#include "tx_api.h" +#include "nx_api.h" +#include "nx_ram_network_driver_test_1500.h" + +extern void test_control_return(UINT status); + +#if defined(FEATURE_NX_IPV6) + +#define DEMO_STACK_SIZE 2048 +#define SERVER_PORT 80 + +extern void _nx_ram_network_driver_1500(struct NX_IP_DRIVER_STRUCT *driver_req); +extern UINT (*advanced_packet_process_callback)(NX_IP *ip_ptr, NX_PACKET *packet_ptr, + UINT *operation_ptr, UINT *delay_ptr); + +static TX_THREAD thread_0; + +static NX_PACKET_POOL pool_0; +static NX_IP ip_0; +static NX_IP ip_1; + +static NX_TCP_SOCKET client_socket; +static NX_TCP_SOCKET server_socket; + +static NXD_ADDRESS ipv6_address_0; +static NXD_ADDRESS ipv6_address_1; + +static ULONG error_counter; +static UINT drop_next_data; + +static void thread_0_entry(ULONG thread_input); +static UINT link_process(NX_IP *ip_ptr, NX_PACKET *packet_ptr, UINT *operation_ptr, UINT *delay_ptr); +static UINT send_text(NX_TCP_SOCKET *socket_ptr, CHAR *text, UINT length); +static UINT receive_text(NX_TCP_SOCKET *socket_ptr, CHAR *text, UINT length, ULONG wait_option); +static UINT wait_for_valid(NX_IP *ip_ptr); + +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_ipv6_address_delete_tcp_test_application_define(void *first_unused_memory) +#endif +{ + +CHAR *pointer; +UINT status; + + pointer = (CHAR *) first_unused_memory; + + error_counter = 0; + drop_next_data = NX_FALSE; + + 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; + + nx_system_initialize(); + + status = nx_packet_pool_create(&pool_0, "NetX Main Packet Pool", 1536, pointer, 32768); + pointer = pointer + 32768; + 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; + if (status) + error_counter++; + + status = nx_tcp_enable(&ip_0); + status += nx_tcp_enable(&ip_1); + status += nxd_ipv6_enable(&ip_0); + status += nxd_ipv6_enable(&ip_1); + status += nxd_icmp_enable(&ip_0); + status += nxd_icmp_enable(&ip_1); + if (status) + error_counter++; + + ipv6_address_0.nxd_ip_version = NX_IP_VERSION_V6; + ipv6_address_0.nxd_ip_address.v6[0] = 0x20010000; + ipv6_address_0.nxd_ip_address.v6[1] = 0; + ipv6_address_0.nxd_ip_address.v6[2] = 0; + ipv6_address_0.nxd_ip_address.v6[3] = 1; + + ipv6_address_1 = ipv6_address_0; + ipv6_address_1.nxd_ip_address.v6[3] = 2; + + status = nxd_ipv6_address_set(&ip_0, 0, &ipv6_address_0, 64, NX_NULL); + status += nxd_ipv6_address_set(&ip_1, 0, &ipv6_address_1, 64, NX_NULL); + if (status) + error_counter++; +} + + +static void thread_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; +UINT address_index; +NX_PACKET *packet_ptr; + + NX_PARAMETER_NOT_USED(thread_input); + + printf("NetX Test: IPv6 Address Delete TCP Test.............................."); + + if (wait_for_valid(&ip_0) || wait_for_valid(&ip_1)) + error_counter++; + + status = nx_tcp_socket_create(&ip_0, &client_socket, "Client", NX_IP_NORMAL, NX_FRAGMENT_OKAY, + NX_IP_TIME_TO_LIVE, 8192, NX_NULL, NX_NULL); + status += nx_tcp_socket_create(&ip_1, &server_socket, "Server", NX_IP_NORMAL, NX_FRAGMENT_OKAY, + NX_IP_TIME_TO_LIVE, 8192, NX_NULL, NX_NULL); + status += nx_tcp_server_socket_listen(&ip_1, SERVER_PORT, &server_socket, 5, NX_NULL); + status += nx_tcp_client_socket_bind(&client_socket, 60, NX_WAIT_FOREVER); + if (status) + error_counter++; + + /* One thread plays both ends, and the server answers only once it + accepts, so the connect does not wait. */ + status = nxd_tcp_client_socket_connect(&client_socket, &ipv6_address_1, SERVER_PORT, NX_NO_WAIT); + if ((status != NX_SUCCESS) && (status != NX_IN_PROGRESS)) + error_counter++; + status = nx_tcp_server_socket_accept(&server_socket, 5 * NX_IP_PERIODIC_RATE); + if (status) + error_counter++; + for (i = 0; (i < 5 * NX_IP_PERIODIC_RATE) && (client_socket.nx_tcp_socket_state != NX_TCP_ESTABLISHED); i++) + tx_thread_sleep(1); + if (client_socket.nx_tcp_socket_state != NX_TCP_ESTABLISHED) + error_counter++; + + /* Data from the client, lost on the link and waiting to be + retransmitted, then data from the server, received and not yet + acknowledged: the client sends nothing after it that could carry the + acknowledgment. */ + advanced_packet_process_callback = link_process; + drop_next_data = NX_TRUE; + if (send_text(&client_socket, "to server", 9) || (drop_next_data != NX_FALSE)) + error_counter++; + if (client_socket.nx_tcp_socket_transmit_sent_count != 1) + error_counter++; + if (send_text(&server_socket, "to client", 9) || receive_text(&client_socket, "to client", 9, NX_IP_PERIODIC_RATE)) + error_counter++; + + /* The address goes. The connection stays, sends are refused, and the + acknowledgment and the retransmission both fall due and are held. */ + status = nxd_ipv6_address_delete(&ip_0, 0); + if (status) + error_counter++; + status = nx_packet_allocate(&pool_0, &packet_ptr, NX_TCP_PACKET, NX_WAIT_FOREVER); + if (status) + error_counter++; + else + { + nx_packet_data_append(packet_ptr, "refused", 7, &pool_0, NX_WAIT_FOREVER); + if (nx_tcp_socket_send(&client_socket, packet_ptr, NX_NO_WAIT) != NX_NO_INTERFACE_ADDRESS) + error_counter++; + nx_packet_release(packet_ptr); + } + + tx_thread_sleep(3 * NX_IP_PERIODIC_RATE); + if ((client_socket.nx_tcp_socket_state != NX_TCP_ESTABLISHED) || + (client_socket.nx_tcp_socket_transmit_sent_count != 1) || + (client_socket.nx_tcp_socket_timeout_retries == 0)) + error_counter++; + if (receive_text(&server_socket, "to server", 9, NX_NO_WAIT) != NX_NO_PACKET) + error_counter++; + + /* The address is set again, in the same entry. The retransmission goes + out and the connection carries on. */ + status = nxd_ipv6_address_set(&ip_0, 0, &ipv6_address_0, 64, &address_index); + if ((status) || (address_index != 0) || wait_for_valid(&ip_0)) + error_counter++; + if (receive_text(&server_socket, "to server", 9, 10 * NX_IP_PERIODIC_RATE)) + error_counter++; + if (send_text(&client_socket, "again", 5) || receive_text(&server_socket, "again", 5, NX_IP_PERIODIC_RATE)) + error_counter++; + + advanced_packet_process_callback = NX_NULL; + + status = nx_tcp_socket_disconnect(&client_socket, NX_NO_WAIT); + if ((status != NX_SUCCESS) && (status != NX_IN_PROGRESS)) + error_counter++; + + if (error_counter) + { + printf("ERROR!\n"); + test_control_return(1); + } + else + { + printf("SUCCESS!\n"); + test_control_return(0); + } +} + + +/* Loses the next data segment ip_0 sends, when asked. The IPv6 header is + found after any link header. */ +static UINT link_process(NX_IP *ip_ptr, NX_PACKET *packet_ptr, UINT *operation_ptr, UINT *delay_ptr) +{ + +UCHAR *frame; +UCHAR *tcp_header; +ULONG link_length; + + NX_PARAMETER_NOT_USED(delay_ptr); + + if ((ip_ptr != &ip_0) || (drop_next_data == NX_FALSE)) + return(NX_TRUE); + + frame = packet_ptr -> nx_packet_prepend_ptr; + if ((frame[0] >> 4) == NX_IP_VERSION_V6) + link_length = 0; + else if ((frame[12] == 0x86) && (frame[13] == 0xDD)) + link_length = 14; + else if ((frame[12] == 0x81) && (frame[13] == 0x00) && (frame[16] == 0x86) && (frame[17] == 0xDD)) + link_length = 18; + else + return(NX_TRUE); + + if (frame[link_length + 6] != NX_PROTOCOL_TCP) + return(NX_TRUE); + + tcp_header = frame + link_length + 40; + if (packet_ptr -> nx_packet_length > link_length + 40 + (ULONG)((tcp_header[12] >> 4) * 4)) + { + drop_next_data = NX_FALSE; + *operation_ptr = NX_RAMDRIVER_OP_DROP; + } + + return(NX_TRUE); +} + + +static UINT send_text(NX_TCP_SOCKET *socket_ptr, CHAR *text, UINT length) +{ + +UINT status; +NX_PACKET *packet_ptr; + + status = nx_packet_allocate(&pool_0, &packet_ptr, NX_TCP_PACKET, NX_WAIT_FOREVER); + if (status) + return(status); + + status = nx_packet_data_append(packet_ptr, text, length, &pool_0, NX_WAIT_FOREVER); + if (status == NX_SUCCESS) + status = nx_tcp_socket_send(socket_ptr, packet_ptr, NX_IP_PERIODIC_RATE); + if (status) + nx_packet_release(packet_ptr); + + return(status); +} + + +/* Returns NX_SUCCESS if exactly the text arrives in time, the receive + status otherwise. */ +static UINT receive_text(NX_TCP_SOCKET *socket_ptr, CHAR *text, UINT length, ULONG wait_option) +{ + +UINT status; +NX_PACKET *packet_ptr; +ULONG bytes; +UCHAR buffer[32]; + + status = nx_tcp_socket_receive(socket_ptr, &packet_ptr, wait_option); + if (status) + return(status); + + bytes = 0; + status = nx_packet_data_retrieve(packet_ptr, buffer, &bytes); + nx_packet_release(packet_ptr); + + if ((status) || (bytes != length) || (memcmp(buffer, text, length) != 0)) + return(NX_NOT_SUCCESSFUL); + + return(NX_SUCCESS); +} + + +/* Waits a bounded time for address 0 to finish duplicate address + detection. */ +static UINT wait_for_valid(NX_IP *ip_ptr) +{ + +UINT i; + + for (i = 0; i < 10 * NX_IP_PERIODIC_RATE; i++) + { + if (ip_ptr -> nx_ipv6_address[0].nxd_ipv6_address_state == NX_IPV6_ADDR_STATE_VALID) + return(NX_SUCCESS); + tx_thread_sleep(1); + } + + return(NX_NOT_SUCCESSFUL); +} + +#else + +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_ipv6_address_delete_tcp_test_application_define(void *first_unused_memory) +#endif +{ + + /* Print out test information banner. */ + printf("NetX Test: IPv6 Address Delete TCP Test..............................N/A\n"); + test_control_return(3); +} + +#endif diff --git a/test/regression/netxduo_test/netx_ipv6_disable_tcp_test.c b/test/regression/netxduo_test/netx_ipv6_disable_tcp_test.c new file mode 100644 index 000000000..fcf6a559d --- /dev/null +++ b/test/regression/netxduo_test/netx_ipv6_disable_tcp_test.c @@ -0,0 +1,328 @@ +/*************************************************************************** + * 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 disabling IPv6 while an IPv6 TCP connection + is established, with an acknowledgment due on it. + + nxd_ipv6_disable clears every IPv6 address entry, detaching each from its + interface, and a TCP socket keeps a pointer to the entry it sends from. + The connection's control segments -- the ACK that falls due here among + them -- hand their segment to _nx_ipv6_packet_send, which asserts that the + entry is attached; before the control path checked the address, the first + one stopped the sending thread with the IP protection held, and the IP + instance with it, and this test never finished. + + The connection survives, as it does a deleted address: sends are refused + while there is no address, an IPv4 connection on the same instance + carries on throughout, and once IPv6 is enabled again and the address set + in the same entry, the IPv6 connection carries on too. */ + +#include "tx_api.h" +#include "nx_api.h" + +extern void test_control_return(UINT status); + +#if defined(FEATURE_NX_IPV6) + +#define DEMO_STACK_SIZE 2048 +#define PORT_IPV6 80 +#define PORT_IPV4 81 + +extern void _nx_ram_network_driver_1500(struct NX_IP_DRIVER_STRUCT *driver_req); + +static TX_THREAD thread_0; + +static NX_PACKET_POOL pool_0; +static NX_IP ip_0; +static NX_IP ip_1; + +static NX_TCP_SOCKET client_ipv6; +static NX_TCP_SOCKET server_ipv6; +#ifndef NX_DISABLE_IPV4 +static NX_TCP_SOCKET client_ipv4; +static NX_TCP_SOCKET server_ipv4; +#endif + +static NXD_ADDRESS ipv6_address_0; +static NXD_ADDRESS ipv6_address_1; + +static ULONG error_counter; + +static void thread_0_entry(ULONG thread_input); +static UINT connect_pair(NX_TCP_SOCKET *client_ptr, UINT client_port, NX_TCP_SOCKET *server_ptr, + UINT server_port, NXD_ADDRESS *server_address); +static UINT send_and_receive(NX_TCP_SOCKET *from_ptr, NX_TCP_SOCKET *to_ptr); +static UINT wait_for_valid(NX_IP *ip_ptr); + +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_ipv6_disable_tcp_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; + + nx_system_initialize(); + + status = nx_packet_pool_create(&pool_0, "NetX Main Packet Pool", 1536, pointer, 32768); + pointer = pointer + 32768; + 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; + if (status) + error_counter++; + +#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; + if (status) + error_counter++; +#endif + + status = nx_tcp_enable(&ip_0); + status += nx_tcp_enable(&ip_1); + status += nxd_ipv6_enable(&ip_0); + status += nxd_ipv6_enable(&ip_1); + status += nxd_icmp_enable(&ip_0); + status += nxd_icmp_enable(&ip_1); + if (status) + error_counter++; + + ipv6_address_0.nxd_ip_version = NX_IP_VERSION_V6; + ipv6_address_0.nxd_ip_address.v6[0] = 0x20010000; + ipv6_address_0.nxd_ip_address.v6[1] = 0; + ipv6_address_0.nxd_ip_address.v6[2] = 0; + ipv6_address_0.nxd_ip_address.v6[3] = 1; + + ipv6_address_1 = ipv6_address_0; + ipv6_address_1.nxd_ip_address.v6[3] = 2; + + status = nxd_ipv6_address_set(&ip_0, 0, &ipv6_address_0, 64, NX_NULL); + status += nxd_ipv6_address_set(&ip_1, 0, &ipv6_address_1, 64, NX_NULL); + if (status) + error_counter++; +} + + +static void thread_0_entry(ULONG thread_input) +{ + +UINT status; +UINT address_index; +NX_PACKET *packet_ptr; +#ifndef NX_DISABLE_IPV4 +NXD_ADDRESS ipv4_address_1; +#endif + + NX_PARAMETER_NOT_USED(thread_input); + + printf("NetX Test: IPv6 Disable TCP Test....................................."); + + if (wait_for_valid(&ip_0) || wait_for_valid(&ip_1)) + error_counter++; + + status = nx_tcp_socket_create(&ip_0, &client_ipv6, "Client IPv6", NX_IP_NORMAL, NX_FRAGMENT_OKAY, + NX_IP_TIME_TO_LIVE, 8192, NX_NULL, NX_NULL); + status += nx_tcp_socket_create(&ip_1, &server_ipv6, "Server IPv6", NX_IP_NORMAL, NX_FRAGMENT_OKAY, + NX_IP_TIME_TO_LIVE, 8192, NX_NULL, NX_NULL); + if (status) + error_counter++; + +#ifndef NX_DISABLE_IPV4 + /* An IPv4 connection on the same instance, which the disable must not + touch. */ + status = nx_tcp_socket_create(&ip_0, &client_ipv4, "Client IPv4", NX_IP_NORMAL, NX_FRAGMENT_OKAY, + NX_IP_TIME_TO_LIVE, 8192, NX_NULL, NX_NULL); + status += nx_tcp_socket_create(&ip_1, &server_ipv4, "Server IPv4", NX_IP_NORMAL, NX_FRAGMENT_OKAY, + NX_IP_TIME_TO_LIVE, 8192, NX_NULL, NX_NULL); + if (status) + error_counter++; + ipv4_address_1.nxd_ip_version = NX_IP_VERSION_V4; + ipv4_address_1.nxd_ip_address.v4 = IP_ADDRESS(1, 2, 3, 5); + if (connect_pair(&client_ipv4, 61, &server_ipv4, PORT_IPV4, &ipv4_address_1)) + error_counter++; +#endif + + /* The IPv6 connection. Data is received on it last, so the client owes + an acknowledgment when IPv6 is disabled. */ + if (connect_pair(&client_ipv6, 60, &server_ipv6, PORT_IPV6, &ipv6_address_1)) + error_counter++; + if (send_and_receive(&server_ipv6, &client_ipv6)) + error_counter++; + + status = nxd_ipv6_disable(&ip_0); + if (status) + error_counter++; + + /* The acknowledgment falls due and is held. The connection stays, and a + send on it is refused. */ + tx_thread_sleep(NX_IP_PERIODIC_RATE); + if (client_ipv6.nx_tcp_socket_state != NX_TCP_ESTABLISHED) + error_counter++; + status = nx_packet_allocate(&pool_0, &packet_ptr, NX_TCP_PACKET, NX_WAIT_FOREVER); + if (status) + error_counter++; + else + { + nx_packet_data_append(packet_ptr, "refused", 7, &pool_0, NX_WAIT_FOREVER); + if (nx_tcp_socket_send(&client_ipv6, packet_ptr, NX_NO_WAIT) != NX_NO_INTERFACE_ADDRESS) + error_counter++; + nx_packet_release(packet_ptr); + } + +#ifndef NX_DISABLE_IPV4 + /* The IPv4 connection carries on, in both directions. */ + if (send_and_receive(&client_ipv4, &server_ipv4) || send_and_receive(&server_ipv4, &client_ipv4)) + error_counter++; +#endif + + /* IPv6 enabled again and the address set in the same entry: the IPv6 + connection carries on. */ + status = nxd_ipv6_enable(&ip_0); + status += nxd_icmp_enable(&ip_0); + status += nxd_ipv6_address_set(&ip_0, 0, &ipv6_address_0, 64, &address_index); + if ((status) || (address_index != 0) || wait_for_valid(&ip_0)) + error_counter++; + if (send_and_receive(&client_ipv6, &server_ipv6) || send_and_receive(&server_ipv6, &client_ipv6)) + error_counter++; + + if (error_counter) + { + printf("ERROR!\n"); + test_control_return(1); + } + else + { + printf("SUCCESS!\n"); + test_control_return(0); + } +} + + +/* Connects a client on ip_0 to a server on ip_1. One thread plays both ends, + and the server answers only once it accepts, so the connect does not + wait. */ +static UINT connect_pair(NX_TCP_SOCKET *client_ptr, UINT client_port, NX_TCP_SOCKET *server_ptr, + UINT server_port, NXD_ADDRESS *server_address) +{ + +UINT status; +UINT i; + + status = nx_tcp_server_socket_listen(&ip_1, server_port, server_ptr, 5, NX_NULL); + status += nx_tcp_client_socket_bind(client_ptr, client_port, NX_WAIT_FOREVER); + if (status) + return(NX_NOT_SUCCESSFUL); + + status = nxd_tcp_client_socket_connect(client_ptr, server_address, server_port, NX_NO_WAIT); + if ((status != NX_SUCCESS) && (status != NX_IN_PROGRESS)) + return(status); + + status = nx_tcp_server_socket_accept(server_ptr, 5 * NX_IP_PERIODIC_RATE); + if (status) + return(status); + + for (i = 0; i < 5 * NX_IP_PERIODIC_RATE; i++) + { + if (client_ptr -> nx_tcp_socket_state == NX_TCP_ESTABLISHED) + return(NX_SUCCESS); + tx_thread_sleep(1); + } + + return(NX_NOT_CONNECTED); +} + + +static UINT send_and_receive(NX_TCP_SOCKET *from_ptr, NX_TCP_SOCKET *to_ptr) +{ + +UINT status; +NX_PACKET *packet_ptr; + + status = nx_packet_allocate(&pool_0, &packet_ptr, NX_TCP_PACKET, NX_WAIT_FOREVER); + if (status) + return(status); + + status = nx_packet_data_append(packet_ptr, "data", 4, &pool_0, NX_WAIT_FOREVER); + if (status == NX_SUCCESS) + status = nx_tcp_socket_send(from_ptr, packet_ptr, NX_IP_PERIODIC_RATE); + if (status) + { + nx_packet_release(packet_ptr); + return(status); + } + + status = nx_tcp_socket_receive(to_ptr, &packet_ptr, 5 * NX_IP_PERIODIC_RATE); + if (status) + return(status); + + if (packet_ptr -> nx_packet_length != 4) + status = NX_NOT_SUCCESSFUL; + nx_packet_release(packet_ptr); + + return(status); +} + + +/* Waits a bounded time for address 0 to finish duplicate address + detection. */ +static UINT wait_for_valid(NX_IP *ip_ptr) +{ + +UINT i; + + for (i = 0; i < 10 * NX_IP_PERIODIC_RATE; i++) + { + if (ip_ptr -> nx_ipv6_address[0].nxd_ipv6_address_state == NX_IPV6_ADDR_STATE_VALID) + return(NX_SUCCESS); + tx_thread_sleep(1); + } + + return(NX_NOT_SUCCESSFUL); +} + +#else + +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_ipv6_disable_tcp_test_application_define(void *first_unused_memory) +#endif +{ + + /* Print out test information banner. */ + printf("NetX Test: IPv6 Disable TCP Test.....................................N/A\n"); + test_control_return(3); +} + +#endif