/***************************************************************************/ /* Copyright (c) 2024 Microsoft Corporation */ /* 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. */ /* */ /* SPDX-License-Identifier: MIT */ /***************************************************************************/ /* 16.02 Even if the receive window is closed indefinitely, the sending TCP MUST allow the connection to stay open as long as probes are acknowledged. */ /* Update 1. nx_tcp_fast_periodic_processing.c between line 199 and line 271. 2. nx_tcp_socket_state_ack_check.c between line 436 and 441, line 536. */ /* Procedure 1. Set 'nx_ip_tcp_packet_receive' pointer of client ip instance to 'my_tcp_packet_receive_16_02' to deal with ACK packet. 2. Client sends data to server whose length equals window size of server . 3. Server sends back ACK with window size 0. 4. Client sends probe to server. 5. Server sends back ACK with window size 0. 6. Server receives data and window size is back to original. 7. Check if connection is still open. */ #include "tx_api.h" #include "nx_api.h" #include "nx_tcp.h" extern void test_control_return(UINT status); #if !defined(NX_DISABLE_IPV4) #define DEMO_STACK_SIZE 2048 #define MSG "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" /* Define the ThreadX and NetX object control blocks... */ static TX_THREAD ntest_0; static TX_THREAD ntest_1; 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; /* Define the counters used in the demo application... */ static ULONG error_counter; static ULONG server_window; static ULONG ack_counter; static ULONG data_packet_counter; /* Define thread prototypes. */ static void ntest_0_entry(ULONG thread_input); static void ntest_1_entry(ULONG thread_input); static void ntest_1_connect_received(NX_TCP_SOCKET *server_socket, UINT port); static void ntest_1_disconnect_received(NX_TCP_SOCKET *server_socket); extern void _nx_ram_network_driver(struct NX_IP_DRIVER_STRUCT *driver_req); static void my_tcp_packet_receive_16_02(NX_IP *ip_ptr, NX_PACKET *packet_ptr); /* Define what the initial system looks like. */ #ifdef CTEST VOID test_application_define(void *first_unused_memory) #else void netx_16_02_application_define(void *first_unused_memory) #endif { CHAR *pointer; UINT status; /* Setup the working pointer. */ pointer = (CHAR *) first_unused_memory; error_counter = 0; ack_counter = 0; data_packet_counter = 0; /* Create the main thread. */ tx_thread_create(&ntest_0, "thread 0", ntest_0_entry, 0, pointer, DEMO_STACK_SIZE, 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); pointer = pointer + DEMO_STACK_SIZE; /* Create the main thread. */ 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 = pointer + DEMO_STACK_SIZE; /* Initialize the NetX system. */ nx_system_initialize(); /* Create a packet pool. */ status = nx_packet_pool_create(&pool_0, "NetX Main Packet Pool", 512, pointer, 8192); pointer = pointer + 8192; if(status) error_counter++; /* Create an IP instance. */ status = nx_ip_create(&ip_0, "NetX IP Instance 0", IP_ADDRESS(1, 2, 3, 4), 0xFFFFFF00UL, &pool_0, _nx_ram_network_driver, pointer, 2048, 1); pointer = pointer + 2048; /* Create another IP instance. */ status += nx_ip_create(&ip_1, "NetX IP Instance 1", IP_ADDRESS(1, 2, 3, 5), 0xFFFFFF00UL, &pool_0, _nx_ram_network_driver, pointer, 2048, 1); pointer = pointer + 2048; if(status) error_counter++; /* Enable ARP and supply ARP cache memory for IP Instance 0. */ status = nx_arp_enable(&ip_0, (void *) pointer, 1024); pointer = pointer + 1024; /* Enable ARP and supply ARP cache memory for IP Instance 1. */ status += nx_arp_enable(&ip_1, (void *) pointer, 1024); pointer = pointer + 1024; /* Check ARP enable status. */ if(status) error_counter++; /* Enable TCP processing for both IP instances. */ status = nx_tcp_enable(&ip_0); status += nx_tcp_enable(&ip_1); /* Check TCP enable status. */ if(status) error_counter++; } /* Define the test threads. */ static void ntest_0_entry(ULONG thread_input) { UINT status; NX_PACKET *my_packet; /* Create a socket. */ status = nx_tcp_socket_create(&ip_0, &client_socket, "Client Socket", NX_IP_NORMAL, NX_FRAGMENT_OKAY, NX_IP_TIME_TO_LIVE, 300, NX_NULL, NX_NULL); /* Check for error. */ if(status) error_counter++; /* Bind the socket. */ status = nx_tcp_client_socket_bind(&client_socket, 12, NX_IP_PERIODIC_RATE); /* Check for error. */ if(status) error_counter++; /* Attempt to connect the socket. */ status = nx_tcp_client_socket_connect(&client_socket, IP_ADDRESS(1, 2, 3, 5), 12, NX_IP_PERIODIC_RATE); /* Check for error. */ if(status) error_counter++; /* Setup function pointer to deal with ACK packet. */ ip_0.nx_ip_tcp_packet_receive = my_tcp_packet_receive_16_02; /* Create a packet to send. */ status = nx_packet_allocate(&pool_0, &my_packet, NX_TCP_PACKET, NX_IP_PERIODIC_RATE); /* Check for error. */ if(status) error_counter++; status = nx_packet_data_append(my_packet, MSG, server_window, &pool_0, NX_IP_PERIODIC_RATE); /* Check for error. */ if(status) error_counter++; /* Send packet to server. */ status = nx_tcp_socket_send(&client_socket, my_packet, NX_IP_PERIODIC_RATE); /* Check status. */ if(status) error_counter++; tx_thread_sleep(NX_IP_PERIODIC_RATE); /* Check if connection is still open. */ if((ack_counter != 1) || (client_socket.nx_tcp_socket_state != NX_TCP_ESTABLISHED)) error_counter++; tx_thread_resume(&ntest_1); /* Disconnect this socket. */ status = nx_tcp_socket_disconnect(&client_socket, NX_IP_PERIODIC_RATE); /* Check for error. */ if(status) error_counter++; /* Unbind the socket. */ status = nx_tcp_client_socket_unbind(&client_socket); /* Check for error. */ if(status) error_counter++; /* Delete the socket. */ status = nx_tcp_socket_delete(&client_socket); /* Check for error. */ if(status) error_counter++; /* Determine if the test was successful. */ if((error_counter) || (ack_counter != 1) || (data_packet_counter != 1)) { printf("ERROR!\n"); test_control_return(1); } else { printf("SUCCESS!\n"); test_control_return(0); } } static void ntest_1_entry(ULONG thread_input) { UINT status; ULONG actual_status; NX_PACKET *rcv_packet_ptr; /* Print out test information banner. */ printf("NetX Test: TCP Spec 16.02 Test......................................."); /* Check for earlier error. */ if(error_counter) { printf("ERROR!\n"); test_control_return(1); } /* Ensure the IP instance has been initialized. */ status = nx_ip_status_check(&ip_1, NX_IP_INITIALIZE_DONE, &actual_status, NX_IP_PERIODIC_RATE); /* Check for error. */ if(status) error_counter++; /* Create a socket. */ status = nx_tcp_socket_create(&ip_1, &server_socket, "Server Socket", NX_IP_NORMAL, NX_FRAGMENT_OKAY, NX_IP_TIME_TO_LIVE, 20, NX_NULL, ntest_1_disconnect_received); /* Check for error. */ if(status) error_counter++; /* Setup this thread to listen. */ status = nx_tcp_server_socket_listen(&ip_1, 12, &server_socket, 5, ntest_1_connect_received); /* Check for error. */ if(status) error_counter++; /* If accept return successfully, then it handles an illegal option length for MSS. */ status = nx_tcp_server_socket_accept(&server_socket, NX_IP_PERIODIC_RATE); /* Check for error. */ if(status) error_counter++; server_window = server_socket.nx_tcp_socket_rx_window_current; tx_thread_suspend(&ntest_1); status = nx_tcp_socket_receive(&server_socket, &rcv_packet_ptr, 2 * NX_IP_PERIODIC_RATE); /* Check for error. */ if(status) error_counter++; else { /* Check data length and payload */ if((rcv_packet_ptr -> nx_packet_length == server_window) && (!memcmp(rcv_packet_ptr -> nx_packet_prepend_ptr, MSG, server_window))) data_packet_counter++; /* Release the packet. */ nx_packet_release(rcv_packet_ptr); } /* Disconnect the server socket. */ status = nx_tcp_socket_disconnect(&server_socket, NX_IP_PERIODIC_RATE); /* Check for error. */ if(status) error_counter++; status = nx_tcp_server_socket_unaccept(&server_socket); /* Check for error. */ if(status) error_counter++; /* Unlisten on the server port. */ status = nx_tcp_server_socket_unlisten(&ip_1, 12); /* Check for error. */ if(status) error_counter++; /* Delete the socket. */ status = nx_tcp_socket_delete(&server_socket); /* Check for error. */ if(status) error_counter++; } static void ntest_1_connect_received(NX_TCP_SOCKET *socket_ptr, UINT port) { /* Check for the proper socket and port. */ if((socket_ptr != &server_socket) || (port != 12)) error_counter++; } static void ntest_1_disconnect_received(NX_TCP_SOCKET *socket) { /* Check for proper disconnected socket. */ if(socket != &server_socket) error_counter++; } static void my_tcp_packet_receive_16_02(NX_IP *ip_ptr, NX_PACKET *packet_ptr) { NX_TCP_HEADER *tcp_header_ptr; tcp_header_ptr = (NX_TCP_HEADER *)packet_ptr -> nx_packet_prepend_ptr; NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_header_word_3); /* Check if it is an ACK packet. */ if(tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_ACK_BIT) { /* Check the window size. */ if((tcp_header_ptr -> nx_tcp_header_word_3 & NX_LOWER_16_MASK) == 0) ack_counter++; ip_0.nx_ip_tcp_packet_receive = _nx_tcp_packet_receive; } NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_header_word_3); /* Pass current packet to default function. */ _nx_tcp_packet_receive(ip_ptr, packet_ptr); } #else #ifdef CTEST VOID test_application_define(void *first_unused_memory) #else void netx_16_02_application_define(void *first_unused_memory) #endif { /* Print out test information banner. */ printf("NetX Test: TCP Spec 16.02 Test.......................................N/A\n"); test_control_return(3); } #endif