diff --git a/common/src/nx_tcp_packet_send_control.c b/common/src/nx_tcp_packet_send_control.c index f9c469a8..2923c366 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 e1cfa93f..4ec2c20a 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 ad5c6501..308026c9 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 00000000..39b3c7f9 --- /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 00000000..fcf6a559 --- /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