diff --git a/common/src/nx_tcp_packet_send_control.c b/common/src/nx_tcp_packet_send_control.c index 57a75e74b..258a9baf4 100644 --- a/common/src/nx_tcp_packet_send_control.c +++ b/common/src/nx_tcp_packet_send_control.c @@ -6,6 +6,8 @@ * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * + * AI Disclosure: Receive-window changes developed with AI assistance. + * Assisted-by: OpenAI Codex * SPDX-License-Identifier: MIT **************************************************************************/ @@ -123,13 +125,16 @@ ULONG window_size; #endif /* NX_ENABLE_TCP_WINDOW_SCALING */ } -#ifdef NX_ENABLE_TCP_WINDOW_SCALING - /* Make sure the window_size is less than 0xFFFF. */ - if (window_size > 0xFFFF) + /* The window is ORed into the header word unmasked. A wrapped (negative) + window advertises zero instead of overwriting the data offset and flags. */ + if ((INT)socket_ptr -> nx_tcp_socket_rx_window_current < 0) + { + window_size = 0; + } + else if (window_size > 0xFFFF) { window_size = 0xFFFF; } -#endif /* NX_ENABLE_TCP_WINDOW_SCALING */ #ifdef NX_IPSEC_ENABLE /* Get data offset from socket directly. */ diff --git a/common/src/nx_tcp_socket_retransmit.c b/common/src/nx_tcp_socket_retransmit.c index a3d964f86..86ea1fd94 100644 --- a/common/src/nx_tcp_socket_retransmit.c +++ b/common/src/nx_tcp_socket_retransmit.c @@ -6,6 +6,8 @@ * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * + * AI Disclosure: Receive-window changes developed with AI assistance. + * Assisted-by: OpenAI Codex * SPDX-License-Identifier: MIT **************************************************************************/ @@ -270,15 +272,20 @@ ULONG window_size; /* Set window size. */ #ifdef NX_ENABLE_TCP_WINDOW_SCALING window_size = socket_ptr -> nx_tcp_socket_rx_window_current >> socket_ptr -> nx_tcp_rcv_win_scale_value; +#else + window_size = socket_ptr -> nx_tcp_socket_rx_window_current; +#endif /* NX_ENABLE_TCP_WINDOW_SCALING */ - /* Make sure the window_size is less than 0xFFFF. */ - if (window_size > 0xFFFF) + /* The window is ORed into the header word unmasked. A wrapped (negative) + window advertises zero instead of overwriting the data offset and flags. */ + if ((INT)socket_ptr -> nx_tcp_socket_rx_window_current < 0) + { + window_size = 0; + } + else if (window_size > 0xFFFF) { window_size = 0xFFFF; } -#else - window_size = socket_ptr -> nx_tcp_socket_rx_window_current; -#endif /* NX_ENABLE_TCP_WINDOW_SCALING */ header_ptr -> nx_tcp_header_word_3 = NX_TCP_HEADER_SIZE | NX_TCP_ACK_BIT | NX_TCP_PSH_BIT | window_size; diff --git a/common/src/nx_tcp_socket_send_internal.c b/common/src/nx_tcp_socket_send_internal.c index db48895c1..7fd6331a8 100644 --- a/common/src/nx_tcp_socket_send_internal.c +++ b/common/src/nx_tcp_socket_send_internal.c @@ -6,6 +6,8 @@ * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * + * AI Disclosure: Receive-window changes developed with AI assistance. + * Assisted-by: OpenAI Codex * SPDX-License-Identifier: MIT **************************************************************************/ @@ -715,15 +717,20 @@ UINT compute_checksum = 1; /* Set window size. */ #ifdef NX_ENABLE_TCP_WINDOW_SCALING window_size = socket_ptr -> nx_tcp_socket_rx_window_current >> socket_ptr -> nx_tcp_rcv_win_scale_value; +#else + window_size = socket_ptr -> nx_tcp_socket_rx_window_current; +#endif /* NX_ENABLE_TCP_WINDOW_SCALING */ - /* Make sure the window_size is less than 0xFFFF. */ - if (window_size > 0xFFFF) + /* The window is ORed into the header word unmasked. A wrapped (negative) + window advertises zero instead of overwriting the data offset and flags. */ + if ((INT)socket_ptr -> nx_tcp_socket_rx_window_current < 0) + { + window_size = 0; + } + else if (window_size > 0xFFFF) { window_size = 0xFFFF; } -#else - window_size = socket_ptr -> nx_tcp_socket_rx_window_current; -#endif /* NX_ENABLE_TCP_WINDOW_SCALING */ header_ptr -> nx_tcp_header_word_3 = NX_TCP_HEADER_SIZE | NX_TCP_ACK_BIT | NX_TCP_PSH_BIT | window_size; header_ptr -> nx_tcp_header_word_4 = 0; diff --git a/common/src/nx_tcp_socket_state_data_check.c b/common/src/nx_tcp_socket_state_data_check.c index 0abbedccc..97f14af97 100644 --- a/common/src/nx_tcp_socket_state_data_check.c +++ b/common/src/nx_tcp_socket_state_data_check.c @@ -6,6 +6,8 @@ * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * + * AI Disclosure: Receive-window changes developed with AI assistance. + * Assisted-by: OpenAI Codex * SPDX-License-Identifier: MIT **************************************************************************/ // Some portions generated by Codex (gpt-5.6-sol). @@ -894,6 +896,16 @@ NX_IP *ip_ptr; do { +#ifdef NX_ENABLE_LOW_WATERMARK + /* The tail is released below, so it must never be acknowledged. The + packet count check at the end of this loop misses the tail when the + pool watermark triggered the drop or a superset packet shrank the queue. */ + if (drop_packet && (search_ptr == socket_ptr -> nx_tcp_socket_receive_queue_tail)) + { + break; + } +#endif /* NX_ENABLE_LOW_WATERMARK */ + /* Setup a pointer to header of this packet in the sent list. */ /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */ search_header_ptr = (NX_TCP_HEADER *)search_ptr -> nx_packet_prepend_ptr; @@ -1028,7 +1040,13 @@ NX_IP *ip_ptr; #endif /* Check if the rx sequence number has been updated. */ - if (original_rx_sequence != socket_ptr -> nx_tcp_socket_rx_sequence) + if ((original_rx_sequence != socket_ptr -> nx_tcp_socket_rx_sequence) +#ifdef NX_ENABLE_LOW_WATERMARK + /* A drop already set the window from the advanced rx_sequence. + Subtracting the advance again would wrap the ULONG window. */ + && (drop_packet == NX_FALSE) +#endif /* NX_ENABLE_LOW_WATERMARK */ + ) { /* Decrease the receive window size since rx_sequence is updated. */ diff --git a/test/cmake/netxduo/CMakeLists.txt b/test/cmake/netxduo/CMakeLists.txt index 0879c9c85..b92f38a87 100644 --- a/test/cmake/netxduo/CMakeLists.txt +++ b/test/cmake/netxduo/CMakeLists.txt @@ -188,7 +188,9 @@ set(HTTP_PROXY -DNX_ENABLE_HTTP_PROXY) set(RAND_ID -DNX_ENABLE_IP_ID_RANDOMIZATION) set(PPP_COMPRESSION -DNX_PPP_COMPRESSION_ENABLE) -set(v4_build ${IPV4} ${PACKET_DEBUG}) +# Exercise low-watermark regressions without and with TCP window scaling. +# AI Disclosure: These low-watermark profile changes used OpenAI Codex assistance. +set(v4_build ${IPV4} ${PACKET_DEBUG} ${LOW_WATERMARK}) set(v4_dns_cache_build ${IPV4} ${DNS_CACHE}) set(v4_no_checksum_build ${IPV4} ${DISABLE_TX_CHECKSUM} ${DISABLE_RX_CHECKSUM}) set(v4_physical_48_build ${IPV4} ${PHY_HEADER} ${MDNS_NO_CLIENT} ${PPPOE} ${MULTI_INTERFACE}) @@ -207,6 +209,7 @@ set(v4_small_build set(v4_packet_pad_build ${IPV4} ${PACKET_ALIGNMENT}) set(v4_full_build ${IPV4} + ${LOW_WATERMARK} ${TCP_KEEPALIVE} ${TCP_WINDOW} ${IP_STATIC_ROUTING} diff --git a/test/cmake/netxduo/regression/CMakeLists.txt b/test/cmake/netxduo/regression/CMakeLists.txt index 1b4514c1b..3057b487a 100644 --- a/test/cmake/netxduo/regression/CMakeLists.txt +++ b/test/cmake/netxduo/regression/CMakeLists.txt @@ -593,6 +593,8 @@ set(netxduo_test_cases ${SOURCE_DIR}/netxduo_test/netx_ip_packet_filter_test.c ${SOURCE_DIR}/netxduo_test/netx_ip_packet_filter_extended_test.c ${SOURCE_DIR}/netxduo_test/netx_icmpv6_invalid_na.c + # AI Disclosure: Regression registration added with OpenAI Codex assistance. + ${SOURCE_DIR}/netxduo_test/netx_tcp_low_watermark_rx_window_test.c ${SOURCE_DIR}/netxduo_test/netx_low_watermark_test.c ${SOURCE_DIR}/netxduo_test/netx_rarp_nxe_api_test.c ${SOURCE_DIR}/netxduo_test/netx_ip_interface_detachment_arp_table_test.c diff --git a/test/regression/netxduo_test/netx_tcp_low_watermark_rx_window_test.c b/test/regression/netxduo_test/netx_tcp_low_watermark_rx_window_test.c new file mode 100644 index 000000000..5f3a967a4 --- /dev/null +++ b/test/regression/netxduo_test/netx_tcp_low_watermark_rx_window_test.c @@ -0,0 +1,340 @@ +/***************************************************************************/ +/* 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 test was largely AI-generated. The AI-generated */ +/* portions are made available under CC0-1.0. */ +/* Assisted-by: OpenAI Codex */ +/* SPDX-License-Identifier: MIT AND CC0-1.0 */ +/***************************************************************************/ + +/* Regression for #433. Inject segments at the TCP reassembly boundary and + inspect packets at the IP driver boundary, using the real stack and pool. */ +#include "tx_api.h" +#include "nx_api.h" +#include "nx_tcp.h" +#include +#include + +extern void test_control_return(UINT status); +#ifdef CTEST +VOID test_application_define(void *first_unused_memory); +#else +void netx_tcp_low_watermark_rx_window_test_application_define(void *first_unused_memory); +#endif + +#if defined(__PRODUCT_NETXDUO__) && defined(NX_ENABLE_LOW_WATERMARK) && !defined(NX_DISABLE_IPV4) + +#define STACK_SIZE 4096 +#define PACKET_SIZE 1536 +#define POOL_SIZE (32 * (PACKET_SIZE + sizeof(NX_PACKET))) +#define BASE 1000UL +#define WINDOW 8192UL +#define HOLE 244UL +#define SEGMENT 980UL +#define OOO(k) (BASE + HOLE + ((ULONG)(k) - 1) * SEGMENT) +#define CHECK(condition) do { if (!(condition)) { \ + printf("ERROR at line %u: %s\n", (UINT)__LINE__, #condition); \ + test_control_return(1); return; } } while (0) + +static NX_IP ip; +static NX_PACKET_POOL pool; +static NX_TCP_SOCKET socket_0; +static TX_THREAD thread_0; +static UCHAR pool_memory[POOL_SIZE]; +static ULONG sent_count; +static ULONG last_word; +static ULONG last_ack; +static UINT header_error; +static const CHAR *scenario; + +static void thread_entry(ULONG input); +static void capture_driver(NX_IP_DRIVER *request); +static void start_case(void); +static void finish_case(void); +static void receive_segment(ULONG sequence, ULONG length); +static void queue_segments(UINT count); +static void check_drop(ULONG sequence, UINT count); +static void check_header(ULONG window, ULONG expected); + +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_tcp_low_watermark_rx_window_test_application_define(void *first_unused_memory) +#endif +{ +CHAR *memory = (CHAR *)first_unused_memory; + + nx_system_initialize(); + CHECK(nx_packet_pool_create(&pool, "Pool", PACKET_SIZE, + pool_memory, sizeof(pool_memory)) == NX_SUCCESS); + CHECK(nx_ip_create(&ip, "IP", IP_ADDRESS(1, 2, 3, 4), 0xFFFFFF00UL, + &pool, capture_driver, memory, STACK_SIZE, 1) == NX_SUCCESS); + CHECK(nx_tcp_enable(&ip) == NX_SUCCESS); + CHECK(tx_thread_create(&thread_0, "Test", thread_entry, 0, + memory + STACK_SIZE, STACK_SIZE, 4, 4, + TX_NO_TIME_SLICE, TX_AUTO_START) == TX_SUCCESS); +} + +/* A sink driver: no peer or timing dependencies. The normal transmit release + keeps data queued for retransmission and returns control packets to the pool. */ +static void capture_driver(NX_IP_DRIVER *request) +{ +NX_PACKET *packet; +NX_TCP_HEADER *header; +ULONG word; + + request -> nx_ip_driver_status = NX_SUCCESS; + switch (request -> nx_ip_driver_command) + { + case NX_LINK_INITIALIZE: + request -> nx_ip_driver_interface -> nx_interface_ip_mtu_size = 1500; + request -> nx_ip_driver_interface -> nx_interface_address_mapping_needed = NX_FALSE; + break; + case NX_LINK_ENABLE: + request -> nx_ip_driver_interface -> nx_interface_link_up = NX_TRUE; + break; + case NX_LINK_DISABLE: + request -> nx_ip_driver_interface -> nx_interface_link_up = NX_FALSE; + break; + case NX_LINK_PACKET_SEND: + packet = request -> nx_ip_driver_packet; + header = (NX_TCP_HEADER *)(packet -> nx_packet_prepend_ptr + packet -> nx_packet_ip_header_length); + word = header -> nx_tcp_header_word_3; + NX_CHANGE_ULONG_ENDIAN(word); + last_word = word; + last_ack = header -> nx_tcp_acknowledgment_number; + NX_CHANGE_ULONG_ENDIAN(last_ack); + sent_count++; + /* Require data offset 5, no reserved bits, and exactly ACK or ACK|PSH. */ + if (((word & ~NX_LOWER_16_MASK) != ((5UL << NX_TCP_HEADER_SHIFT) | NX_TCP_ACK_BIT)) && + ((word & ~NX_LOWER_16_MASK) != ((5UL << NX_TCP_HEADER_SHIFT) | NX_TCP_ACK_BIT | NX_TCP_PSH_BIT))) + { + header_error++; + } + nx_packet_transmit_release(packet); + break; + default: + break; + } +} + +static void start_case(void) +{ + CHECK(nx_packet_pool_low_watermark_set(&pool, 0) == NX_SUCCESS); + CHECK(nx_tcp_socket_create(&ip, &socket_0, "Socket", NX_IP_NORMAL, + NX_FRAGMENT_OKAY, NX_IP_TIME_TO_LIVE, WINDOW, + NX_NULL, NX_NULL) == NX_SUCCESS); + CHECK(nx_tcp_client_socket_bind(&socket_0, 1234, NX_NO_WAIT) == NX_SUCCESS); + CHECK(nx_tcp_socket_receive_queue_max_set(&socket_0, 8) == NX_SUCCESS); + /* Skip the handshake to set up an exact, deterministic receive sequence. */ + socket_0.nx_tcp_socket_state = NX_TCP_ESTABLISHED; + socket_0.nx_tcp_socket_connect_ip.nxd_ip_version = NX_IP_VERSION_V4; + socket_0.nx_tcp_socket_connect_ip.nxd_ip_address.v4 = IP_ADDRESS(1, 2, 3, 5); + socket_0.nx_tcp_socket_connect_port = 4321; + socket_0.nx_tcp_socket_next_hop_address = IP_ADDRESS(1, 2, 3, 5); + socket_0.nx_tcp_socket_connect_interface = &ip.nx_ip_interface[0]; + socket_0.nx_tcp_socket_rx_sequence = BASE; + socket_0.nx_tcp_socket_rx_window_current = WINDOW; + socket_0.nx_tcp_socket_rx_window_last_sent = WINDOW; + socket_0.nx_tcp_socket_tx_window_advertised = WINDOW; + socket_0.nx_tcp_socket_tx_window_congestion = WINDOW; + socket_0.nx_tcp_socket_connect_mss = 1460; + socket_0.nx_tcp_socket_connect_mss2 = 1460UL * 1460UL; +#ifdef NX_ENABLE_TCP_WINDOW_SCALING + /* A nonzero shift also checks that validation precedes scaling of a wrap. */ + socket_0.nx_tcp_rcv_win_scale_value = 2; +#endif + sent_count = 0; + last_word = last_ack = 0; + header_error = 0; +} + +static void finish_case(void) +{ + CHECK(header_error == 0); + _nx_tcp_socket_receive_queue_flush(&socket_0); + _nx_tcp_socket_transmit_queue_flush(&socket_0); + socket_0.nx_tcp_socket_state = NX_TCP_CLOSED; + CHECK(nx_tcp_client_socket_unbind(&socket_0) == NX_SUCCESS); + CHECK(nx_tcp_socket_delete(&socket_0) == NX_SUCCESS); + CHECK(pool.nx_packet_pool_available == pool.nx_packet_pool_total); + printf(" %s: SUCCESS!\n", scenario); +} + +static void receive_segment(ULONG sequence, ULONG length) +{ +NX_PACKET *packet; +NX_TCP_HEADER *header; +ULONG i; + + CHECK(nx_packet_allocate(&pool, &packet, NX_TCP_PACKET, NX_NO_WAIT) == NX_SUCCESS); + packet -> nx_packet_prepend_ptr -= sizeof(NX_TCP_HEADER); + packet -> nx_packet_append_ptr = packet -> nx_packet_prepend_ptr + sizeof(NX_TCP_HEADER) + length; + packet -> nx_packet_length = sizeof(NX_TCP_HEADER) + length; + packet -> nx_packet_ip_version = NX_IP_VERSION_V4; + packet -> nx_packet_ip_header = packet -> nx_packet_prepend_ptr - 20; + header = (NX_TCP_HEADER *)packet -> nx_packet_prepend_ptr; + memset(header, 0, sizeof(*header)); + header -> nx_tcp_header_word_3 = (5UL << NX_TCP_HEADER_SHIFT) | NX_TCP_ACK_BIT; + header -> nx_tcp_sequence_number = sequence; + for (i = 0; i < length; i++) + { + packet -> nx_packet_prepend_ptr[sizeof(NX_TCP_HEADER) + i] = (UCHAR)(sequence + i); + } +#ifdef NX_TCP_ACK_EVERY_N_PACKETS + socket_0.nx_tcp_socket_ack_n_packet_counter = NX_TCP_ACK_EVERY_N_PACKETS; +#endif + CHECK(_nx_tcp_socket_state_data_check(&socket_0, packet) == NX_TRUE); +} + +static void queue_segments(UINT count) +{ +UINT i; + + for (i = 1; i <= count; i++) + { + receive_segment(OOO(i), SEGMENT); + } + CHECK(socket_0.nx_tcp_socket_receive_queue_count == count); + CHECK(socket_0.nx_tcp_socket_rx_sequence == BASE); + CHECK(socket_0.nx_tcp_socket_rx_window_current == WINDOW); +} + +/* Validate both ACK accounting and the actual bytes available to the caller. + The peer must never be told that discarded data was received. */ +static void check_drop(ULONG sequence, UINT count) +{ +NX_PACKET *packet; +ULONG delivered = 0; +ULONG i; + + CHECK(socket_0.nx_tcp_socket_rx_sequence == sequence); + CHECK(socket_0.nx_tcp_socket_receive_queue_count == count); + CHECK(socket_0.nx_tcp_socket_rx_window_current == 0); + CHECK(socket_0.nx_tcp_socket_rx_window_last_sent == 0); + _nx_tcp_packet_send_ack(&socket_0, socket_0.nx_tcp_socket_tx_sequence); + CHECK(sent_count != 0 && last_ack == sequence); + CHECK((last_word & NX_LOWER_16_MASK) == 0 && header_error == 0); + CHECK(pool.nx_packet_pool_available == pool.nx_packet_pool_total - count); + while (nx_tcp_socket_receive(&socket_0, &packet, NX_NO_WAIT) == NX_SUCCESS) + { + for (i = 0; i < packet -> nx_packet_length; i++) + { + CHECK(packet -> nx_packet_prepend_ptr[i] == (UCHAR)(BASE + delivered + i)); + } + delivered += packet -> nx_packet_length; + CHECK(nx_packet_release(packet) == NX_SUCCESS); + } + CHECK(delivered == sequence - BASE); + CHECK(socket_0.nx_tcp_socket_receive_queue_head == NX_NULL); + CHECK(socket_0.nx_tcp_socket_receive_queue_tail == NX_NULL); +} + +/* Exercise control send, new data send, and retransmission for each value. + In particular, retransmission must replace a previously valid window. */ +static void check_header(ULONG window, ULONG expected) +{ +NX_PACKET *packet; +ULONG before; +ULONG data_flags; + + socket_0.nx_tcp_socket_rx_window_current = window; + before = sent_count; + _nx_tcp_packet_send_ack(&socket_0, socket_0.nx_tcp_socket_tx_sequence); + CHECK(sent_count == before + 1); + CHECK(last_word == ((5UL << NX_TCP_HEADER_SHIFT) | NX_TCP_ACK_BIT | expected)); + + CHECK(nx_packet_allocate(&pool, &packet, NX_TCP_PACKET, NX_NO_WAIT) == NX_SUCCESS); + CHECK(nx_packet_data_append(packet, "test", 4, &pool, NX_NO_WAIT) == NX_SUCCESS); + before = sent_count; + CHECK(_nx_tcp_socket_send_internal(&socket_0, packet, NX_NO_WAIT) == NX_SUCCESS); + CHECK(sent_count == before + 1); + data_flags = (5UL << NX_TCP_HEADER_SHIFT) | NX_TCP_ACK_BIT | NX_TCP_PSH_BIT; + CHECK(last_word == (data_flags | expected)); + _nx_tcp_socket_transmit_queue_flush(&socket_0); + + socket_0.nx_tcp_socket_rx_window_current = WINDOW; + CHECK(nx_packet_allocate(&pool, &packet, NX_TCP_PACKET, NX_NO_WAIT) == NX_SUCCESS); + CHECK(nx_packet_data_append(packet, "test", 4, &pool, NX_NO_WAIT) == NX_SUCCESS); + CHECK(_nx_tcp_socket_send_internal(&socket_0, packet, NX_NO_WAIT) == NX_SUCCESS); + socket_0.nx_tcp_socket_rx_window_current = window; + before = sent_count; + _nx_tcp_socket_retransmit(&ip, &socket_0, NX_FALSE); + CHECK(sent_count == before + 1); + CHECK(last_word == (data_flags | expected)); + _nx_tcp_socket_transmit_queue_flush(&socket_0); +} + +static void thread_entry(ULONG input) +{ +ULONG actual; + + (void)input; + printf("NetX Test: TCP low-watermark receive window (#433)\n"); + CHECK(nx_ip_status_check(&ip, NX_IP_INITIALIZE_DONE, &actual, NX_IP_PERIODIC_RATE) == NX_SUCCESS); + /* Internal TCP entry points require the IP mutex. Hold it across each + setup and assertion so periodic processing cannot change the fixture. */ + CHECK(tx_mutex_get(&ip.nx_ip_protection, TX_WAIT_FOREVER) == TX_SUCCESS); + + scenario = "queue-limit hole fill"; + start_case(); + queue_segments(8); + receive_segment(BASE, HOLE); + check_drop(OOO(8), 8); + finish_case(); + + scenario = "queue-limit superset shrinks queue"; + start_case(); + queue_segments(8); + receive_segment(BASE, HOLE + SEGMENT); + check_drop(OOO(8), 7); + finish_case(); + + scenario = "pool-low-watermark short queue"; + start_case(); + queue_segments(2); + CHECK(nx_packet_pool_low_watermark_set(&pool, pool.nx_packet_pool_available) == NX_SUCCESS); + receive_segment(BASE, HOLE); + check_drop(OOO(2), 2); + finish_case(); + + scenario = "pool-low-watermark incoming superset is tail"; + start_case(); + queue_segments(1); + CHECK(nx_packet_pool_low_watermark_set(&pool, pool.nx_packet_pool_available) == NX_SUCCESS); + receive_segment(BASE, HOLE + SEGMENT); + check_drop(BASE, 0); + finish_case(); + + scenario = "three header builders: ordinary, wrapped and oversized windows"; + start_case(); +#ifdef NX_ENABLE_TCP_WINDOW_SCALING + check_header(WINDOW, WINDOW >> 2); + check_header(0UL - 7624UL, 0); + check_header(0x12345UL << 2, 0xFFFF); +#else + check_header(WINDOW, WINDOW); + check_header(0UL - 7624UL, 0); + check_header(0x12345UL, 0xFFFF); +#endif + finish_case(); + CHECK(tx_mutex_put(&ip.nx_ip_protection) == TX_SUCCESS); + test_control_return(0); +} + +#else +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_tcp_low_watermark_rx_window_test_application_define(void *first_unused_memory) +#endif +{ + (void)first_unused_memory; + printf("NetX Test: TCP low-watermark receive window (#433)................N/A\n"); + test_control_return(3); +} +#endif