1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** NetX Component                                                        */
16 /**                                                                       */
17 /**   Transmission Control Protocol (TCP)                                 */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define NX_SOURCE_CODE
23 
24 
25 /* Include necessary system files.  */
26 
27 #include "nx_api.h"
28 #include "nx_ip.h"
29 #include "nx_packet.h"
30 #include "nx_tcp.h"
31 
32 #ifdef FEATURE_NX_IPV6
33 #include "nx_ipv6.h"
34 #endif
35 
36 
37 /**************************************************************************/
38 /*                                                                        */
39 /*  FUNCTION                                               RELEASE        */
40 /*                                                                        */
41 /*    _nx_tcp_socket_state_ack_check                      PORTABLE C      */
42 /*                                                           6.1.10       */
43 /*  AUTHOR                                                                */
44 /*                                                                        */
45 /*    Yuxin Zhou, Microsoft Corporation                                   */
46 /*                                                                        */
47 /*  DESCRIPTION                                                           */
48 /*                                                                        */
49 /*    This function checks for ACK conditions in various states of the    */
50 /*    TCP socket.  ACK messages are examined against the queued transmit  */
51 /*    packets in order to see if one or more transmit packets may be      */
52 /*    removed from the socket's transmit queue.                           */
53 /*                                                                        */
54 /*  INPUT                                                                 */
55 /*                                                                        */
56 /*    socket_ptr                            Pointer to owning socket      */
57 /*    tcp_header_ptr                        Pointer to packet header      */
58 /*                                                                        */
59 /*  OUTPUT                                                                */
60 /*                                                                        */
61 /*    NX_TRUE                               The ACK number is acceptable  */
62 /*    NX_FALSE                              The ACK number is unacceptable*/
63 /*                                                                        */
64 /*  CALLS                                                                 */
65 /*                                                                        */
66 /*    _nx_tcp_packet_send_ack               Send ACK message              */
67 /*    _nx_packet_release                    Packet release function       */
68 /*    _nx_tcp_socket_retransmit             Retransmit packet             */
69 /*                                                                        */
70 /*  CALLED BY                                                             */
71 /*                                                                        */
72 /*    _nx_tcp_socket_packet_process         Process TCP packet for socket */
73 /*                                                                        */
74 /*  RELEASE HISTORY                                                       */
75 /*                                                                        */
76 /*    DATE              NAME                      DESCRIPTION             */
77 /*                                                                        */
78 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
79 /*  09-30-2020     Yuxin Zhou               Modified comment(s), and      */
80 /*                                            corrected the calculation of*/
81 /*                                            ending packet sequence,     */
82 /*                                            resulting in version 6.1    */
83 /*  06-02-2021     Yuxin Zhou               Modified comment(s),          */
84 /*                                            fixed compiler warnings,    */
85 /*                                            resulting in version 6.1.7  */
86 /*  10-15-2021     Yuxin Zhou               Modified comment(s),          */
87 /*                                            fixed the bug of race       */
88 /*                                            condition, removed useless  */
89 /*                                            code,                       */
90 /*                                            resulting in version 6.1.9  */
91 /*  01-31-2022     Yuxin Zhou               Modified comment(s), and      */
92 /*                                            fixed unsigned integers     */
93 /*                                            comparison,                 */
94 /*                                            resulting in version 6.1.10 */
95 /*                                                                        */
96 /**************************************************************************/
_nx_tcp_socket_state_ack_check(NX_TCP_SOCKET * socket_ptr,NX_TCP_HEADER * tcp_header_ptr)97 UINT  _nx_tcp_socket_state_ack_check(NX_TCP_SOCKET *socket_ptr, NX_TCP_HEADER *tcp_header_ptr)
98 {
99 
100 TX_INTERRUPT_SAVE_AREA
101 
102 NX_TCP_HEADER *search_header_ptr = NX_NULL;
103 NX_PACKET     *search_ptr;
104 NX_PACKET     *previous_ptr;
105 ULONG          header_length;
106 ULONG          search_sequence;
107 ULONG          temp;
108 ULONG          packet_release_count;
109 ULONG          ending_packet_sequence;
110 ULONG          starting_tx_sequence;
111 ULONG          ending_tx_sequence;
112 ULONG          ending_rx_sequence;
113 ULONG          acked_bytes;
114 ULONG          tcp_payload_length;
115 UINT           wrapped_flag = NX_FALSE;
116 
117 
118     /* Determine if the header has an ACK bit set.  This is an
119        acknowledgement of a previous transmission.  */
120     if (tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_ACK_BIT)
121     {
122 
123         /* Initialize tx sequence. */
124         if (socket_ptr -> nx_tcp_socket_zero_window_probe_has_data)
125         {
126             ending_tx_sequence = socket_ptr -> nx_tcp_socket_tx_sequence + 1;
127         }
128         else
129         {
130             ending_tx_sequence = socket_ptr -> nx_tcp_socket_tx_sequence;
131         }
132         starting_tx_sequence = socket_ptr -> nx_tcp_socket_tx_sequence - socket_ptr -> nx_tcp_socket_tx_outstanding_bytes;
133 
134         /* Initialize ending rx sequence. */
135         if (socket_ptr -> nx_tcp_socket_receive_queue_tail)
136         {
137             search_ptr = socket_ptr -> nx_tcp_socket_receive_queue_tail;
138 
139             /* Setup a pointer to header of this packet in the sent list.  */
140 #ifndef NX_DISABLE_IPV4
141             if (search_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4)
142             {
143 
144                 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
145                 search_header_ptr =  (NX_TCP_HEADER *)(search_ptr -> nx_packet_ip_header +
146                                                        sizeof(NX_IPV4_HEADER));
147             }
148             else
149 #endif /* NX_DISABLE_IPV4 */
150 #ifdef FEATURE_NX_IPV6
151             if (search_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6)
152             {
153 
154                 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
155                 search_header_ptr =  (NX_TCP_HEADER *)(search_ptr -> nx_packet_ip_header +
156                                                        sizeof(NX_IPV6_HEADER));
157             }
158             else
159 #endif /* FEATURE_NX_IPV6 */
160             {
161                 return(NX_FALSE);
162             }
163 
164             /* Determine the size of the TCP header.  */
165             temp =  search_header_ptr -> nx_tcp_header_word_3;
166             header_length =  (temp >> NX_TCP_HEADER_SHIFT) * (ULONG)sizeof(ULONG);
167 
168             /* Determine the sequence number in the TCP header.  */
169             search_sequence =  search_header_ptr -> nx_tcp_sequence_number;
170 
171             /* Calculate the payload length of TCP. */
172             tcp_payload_length = (search_ptr -> nx_packet_length -
173                                   (header_length +
174                                    (ULONG)((ALIGN_TYPE)search_header_ptr -
175                                            (ALIGN_TYPE)search_ptr -> nx_packet_prepend_ptr)));
176 
177             /* Calculate the ending packet sequence.  */
178             ending_rx_sequence =  search_sequence + tcp_payload_length;
179         }
180         else
181         {
182             ending_rx_sequence = socket_ptr -> nx_tcp_socket_rx_sequence;
183         }
184 
185 #ifdef NX_ENABLE_TCP_KEEPALIVE
186         /* Determine if the socket is in the established state.  */
187         if (socket_ptr -> nx_tcp_socket_state == NX_TCP_ESTABLISHED)
188         {
189 
190             /* Is the keepalive feature enabled on this socket? */
191             if (socket_ptr -> nx_tcp_socket_keepalive_enabled)
192             {
193                 /* Yes, reset the TCP Keepalive timer to initial values.  */
194                 socket_ptr -> nx_tcp_socket_keepalive_timeout =  NX_TCP_KEEPALIVE_INITIAL;
195                 socket_ptr -> nx_tcp_socket_keepalive_retries =  0;
196 
197                 /* Determine if we have received a Keepalive ACK request from the other side
198                    of the connection.  */
199                 if (tcp_header_ptr -> nx_tcp_sequence_number == (socket_ptr -> nx_tcp_socket_rx_sequence - 1))
200                 {
201 
202                     /* Yes, a Keepalive ACK probe is present.  Respond with an ACK to let the other
203                        side of the connection know that we are still alive.  */
204                     _nx_tcp_packet_send_ack(socket_ptr, ending_tx_sequence);
205                 }
206             }
207         }
208 #endif
209 
210         /* First, determine if incoming ACK matches our transmit sequence.  */
211         /*lint -e{923} suppress cast of pointer to ULONG.  */
212         if (tcp_header_ptr -> nx_tcp_acknowledgment_number == ending_tx_sequence)
213         {
214 
215             /* In this case, everything on the transmit list is acknowledged.  Simply set the packet
216                release count to the number of packets in the transmit queue.  */
217             packet_release_count =  socket_ptr -> nx_tcp_socket_transmit_sent_count;
218 
219             /* Set the previous pointer to the socket transmit tail pointer.  */
220             previous_ptr =  socket_ptr -> nx_tcp_socket_transmit_sent_tail;
221 
222             /* Is this ACK to FIN? */
223             if (socket_ptr -> nx_tcp_socket_state >= NX_TCP_FIN_WAIT_1)
224             {
225 
226                 /* Yes it is. */
227                 socket_ptr -> nx_tcp_socket_fin_acked = NX_TRUE;
228             }
229         }
230         else
231         {
232 
233             /* Calculate the start and end of the transmit sequence.  */
234 
235             /* Pickup the head of the transmit queue.  */
236             search_ptr =    socket_ptr -> nx_tcp_socket_transmit_sent_head;
237 
238             /* Determine if there is a packet on the transmit queue... and determine if the packet has been
239                transmitted.  */
240             /*lint -e{923} suppress cast of ULONG to pointer.  */
241             if ((search_ptr) && (search_ptr -> nx_packet_queue_next == ((NX_PACKET *)NX_DRIVER_TX_DONE)))
242             {
243 
244                 /* Determine if the incoming ACK matches the front of our transmit queue. */
245                 if (tcp_header_ptr -> nx_tcp_acknowledgment_number == starting_tx_sequence)
246                 {
247 
248                     /* Handle duplicated ACK packet.  */
249                     socket_ptr -> nx_tcp_socket_duplicated_ack_received++;
250 
251                     if (socket_ptr -> nx_tcp_socket_duplicated_ack_received == 3)
252                     {
253                         if ((INT)((tcp_header_ptr -> nx_tcp_acknowledgment_number - 1) -
254                                   socket_ptr -> nx_tcp_socket_tx_sequence_recover) > 0)
255                         {
256 
257                             /* Cumulative acknowledge covers more than recover. */
258                             /* Section 3.2, Page 5, RFC6582. */
259                             /* Retransmit packet immediately. */
260                             _nx_tcp_socket_retransmit(socket_ptr -> nx_tcp_socket_ip_ptr, socket_ptr, NX_TRUE);
261                         }
262                         else if ((socket_ptr -> nx_tcp_socket_tx_window_congestion > socket_ptr -> nx_tcp_socket_connect_mss) &&
263                                  ((INT)(tcp_header_ptr -> nx_tcp_acknowledgment_number - (socket_ptr -> nx_tcp_socket_previous_highest_ack +
264                                                                                           (socket_ptr -> nx_tcp_socket_connect_mss << 2))) < 0))
265                         {
266 
267                             /* Congestion window is greater than SMSS bytes and
268                                the difference between highest_ack and prev_highest_ack is at most 4*SMSS bytes.*/
269                             /* Section 4.1, Page 5, RFC6582. */
270                             /* Retransmit packet immediately. */
271                             _nx_tcp_socket_retransmit(socket_ptr -> nx_tcp_socket_ip_ptr, socket_ptr, NX_TRUE);
272                         }
273                     }
274                     else if ((socket_ptr -> nx_tcp_socket_duplicated_ack_received > 3) &&
275                              (socket_ptr -> nx_tcp_socket_fast_recovery == NX_TRUE))
276                     {
277 
278                         /* CWND += MSS  */
279                         socket_ptr -> nx_tcp_socket_tx_window_congestion += socket_ptr -> nx_tcp_socket_connect_mss;
280                     }
281                 }
282 
283                 /* Determine if the transmit queue has wrapped.  */
284                 if (ending_tx_sequence > starting_tx_sequence)
285                 {
286 
287                     /* Clear the wrapped flag.  */
288                     wrapped_flag =  NX_FALSE;
289                 }
290                 else
291                 {
292 
293                     /* Set the wrapped flag.  */
294                     wrapped_flag =  NX_TRUE;
295                 }
296             }
297 
298             /* Initialize the packet release count.  */
299             packet_release_count =  0;
300 
301             /* See if we can find the sequence number in the sent queue for this
302                socket.  */
303             previous_ptr =  NX_NULL;
304             while (search_ptr)
305             {
306 
307                 /* Determine if the packet has been transmitted.  */
308                 /*lint -e{923} suppress cast of ULONG to pointer.  */
309                 if (search_ptr -> nx_packet_queue_next != ((NX_PACKET *)NX_DRIVER_TX_DONE))
310                 {
311 
312                     /* Setup a pointer to header of this packet in the sent list.  */
313                     search_header_ptr =  (NX_TCP_HEADER *)(search_ptr -> nx_packet_ip_header +
314                                                            search_ptr -> nx_packet_ip_header_length);
315                 }
316                 else
317                 {
318 
319                     /* Setup a pointer to header of this packet in the sent list.  */
320                     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
321                     search_header_ptr =  (NX_TCP_HEADER *)search_ptr -> nx_packet_prepend_ptr;
322                 }
323 
324                 /* Determine the size of the TCP header.  */
325                 temp =  search_header_ptr -> nx_tcp_header_word_3;
326                 NX_CHANGE_ULONG_ENDIAN(temp);
327                 header_length =  (temp >> NX_TCP_HEADER_SHIFT) * (ULONG)sizeof(ULONG);
328 
329                 /* Determine the sequence number in the TCP header.  */
330                 search_sequence =  search_header_ptr -> nx_tcp_sequence_number;
331                 NX_CHANGE_ULONG_ENDIAN(search_sequence);
332 
333                 /* Calculate the payload length of TCP. */
334                 tcp_payload_length = (search_ptr -> nx_packet_length -
335                                       (header_length +
336                                        (ULONG)((ALIGN_TYPE)search_header_ptr -
337                                                (ALIGN_TYPE)search_ptr -> nx_packet_prepend_ptr)));
338 
339                 /* Calculate the ending packet sequence.  */
340                 ending_packet_sequence =  search_sequence + tcp_payload_length;
341 
342                 /* Determine if the transmit window is wrapped.  */
343                 if (wrapped_flag == NX_FALSE)
344                 {
345 
346                     /* No, the transmit window is not wrapped. Perform a simple compare to determine if the ACK
347                        covers the current search packet.  */
348 
349                     /* Is this ACK before the current search packet or after the transmit sequence?  */
350                     if ((tcp_header_ptr -> nx_tcp_acknowledgment_number < ending_packet_sequence) ||
351                         (tcp_header_ptr -> nx_tcp_acknowledgment_number > ending_tx_sequence))
352                     {
353 
354                         /* The ACK is less than the current packet, just get out of the loop!  */
355                         break;
356                     }
357                 }
358                 else
359                 {
360 
361                     /* Yes, the transmit window has wrapped.  We need to now check for all the wrap conditions to
362                        determine if ACK covers the current search packet.  */
363 
364                     /* Is the search packet's ending sequence number in the wrapped part of the window.  */
365                     if (ending_packet_sequence < starting_tx_sequence)
366                     {
367 
368                         /* The search packet ends in the wrapped portion of the window.  Determine if the ACK
369                            sequence in the wrapped portion as well.  */
370                         if (tcp_header_ptr -> nx_tcp_acknowledgment_number < starting_tx_sequence)
371                         {
372 
373                             /* Yes, the ACK sequence is in the wrapped portion as well. Simply compare the ACK
374                                sequence with the search packet sequence.  */
375                             if (tcp_header_ptr -> nx_tcp_acknowledgment_number < ending_packet_sequence)
376                             {
377 
378                                 /* ACK does not cover the search packet. Break out of the loop.  */
379                                 break;
380                             }
381                         }
382                         else
383                         {
384 
385                             /* The ACK sequence is in the non-wrapped portion of the window and the ending sequence
386                                of the search packet is in the wrapped portion - so the ACK doesn't cover the search
387                                packet.  Break out of the loop!  */
388                             break;
389                         }
390                     }
391                     else
392                     {
393 
394                         /* The search packet is in the non-wrapped portion of the window.  Determine if the ACK
395                            sequence is in the non-wrapped portion as well.  */
396                         if (tcp_header_ptr -> nx_tcp_acknowledgment_number >= starting_tx_sequence)
397                         {
398 
399                             /* Yes, the ACK sequence is in the non-wrapped portion of the window. Simply compare the ACK
400                                sequence with the search packet sequence.  */
401                             if (tcp_header_ptr -> nx_tcp_acknowledgment_number < ending_packet_sequence)
402                             {
403 
404                                 /* ACK does not cover the search packet. Break out of the loop.  */
405                                 break;
406                             }
407                         }
408                     }
409                 }
410 
411                 /* At this point we know that the ACK received covers the search packet.  */
412 
413                 /* Increase the packet release count.  */
414                 packet_release_count++;
415 
416                 /* Move the search and previous pointers forward.  */
417                 previous_ptr =  search_ptr;
418                 search_ptr =  search_ptr -> nx_packet_union_next.nx_packet_tcp_queue_next;
419 
420                 /* Determine if we are at the end of the TCP queue.  */
421                 /*lint -e{923} suppress cast of ULONG to pointer.  */
422                 if (search_ptr == ((NX_PACKET *)NX_PACKET_ENQUEUED))
423                 {
424 
425                     /* Yes, set the search pointer to NULL.  */
426                     search_ptr =  NX_NULL;
427                 }
428             }
429         }
430 
431         /* Determine if anything needs to be released.  */
432         if (!packet_release_count)
433         {
434 
435             /* No, check and see if the ACK is valid.  */
436             /* If the ACK acks something not yet sent (SEG.ACK > SND.NXT) then send an ACK, drop the segment */
437             /* Page 72, section 3.9, RFC 793.*/
438             if (tcp_header_ptr -> nx_tcp_acknowledgment_number != ending_tx_sequence)
439             {
440 
441                 /* If the ACK is a duplicate, it can be ignored. */
442                 if ((INT)(tcp_header_ptr -> nx_tcp_acknowledgment_number - ending_tx_sequence) > 0)
443                 {
444 
445                     /* The ACK sequence is invalid. Respond with an ACK to let the other
446                        side of the connection figure out if everything is still okay.  */
447                     _nx_tcp_packet_send_ack(socket_ptr, ending_tx_sequence);
448                     return(NX_FALSE);
449                 }
450             }
451             else if ((socket_ptr -> nx_tcp_socket_rx_window_current == 0) &&
452                      (tcp_header_ptr -> nx_tcp_sequence_number == socket_ptr -> nx_tcp_socket_rx_sequence))
453             {
454 
455                 /* Response to zero window probe.  */
456                 _nx_tcp_packet_send_ack(socket_ptr, ending_tx_sequence);
457             }
458         }
459         else
460         {
461 
462             /* Congestion window adjustment during slow start and congestion avoidance is executed
463                on every incoming ACK that acknowledges new data. RFC5681, Section3.1, Page4-8.  */
464 
465             /* Check whether the socket is in fast recovery procedure. */
466             if (socket_ptr -> nx_tcp_socket_fast_recovery == NX_TRUE)
467             {
468 
469                 /* Yes. */
470                 if ((INT)(tcp_header_ptr -> nx_tcp_acknowledgment_number -
471                           socket_ptr -> nx_tcp_socket_tx_sequence_recover) > 0)
472                 {
473 
474                     /* All packets sent before entering fast recovery are ACKed. */
475                     /* Exit fast recovery procedure. */
476                     socket_ptr -> nx_tcp_socket_fast_recovery = NX_FALSE;
477                     socket_ptr -> nx_tcp_socket_tx_window_congestion = socket_ptr -> nx_tcp_socket_tx_slow_start_threshold;
478                 }
479             }
480 
481             if ((INT)(socket_ptr -> nx_tcp_socket_tx_sequence_recover -
482                       (tcp_header_ptr -> nx_tcp_acknowledgment_number - 2)) < 0)
483             {
484 
485                 /* Update the transmit sequence that entered fast transmit. */
486                 socket_ptr -> nx_tcp_socket_tx_sequence_recover = tcp_header_ptr -> nx_tcp_acknowledgment_number - 2;
487             }
488 
489             /* Reset the duplicated ACK counter. */
490             socket_ptr -> nx_tcp_socket_duplicated_ack_received = 0;
491 
492             /* Set previous cumulative acknowlesgement. */
493             socket_ptr -> nx_tcp_socket_previous_highest_ack = starting_tx_sequence;
494 
495             /* Calculate ACKed length. */
496             acked_bytes = tcp_header_ptr -> nx_tcp_acknowledgment_number - starting_tx_sequence;
497 
498             if (socket_ptr -> nx_tcp_socket_fast_recovery == NX_TRUE)
499             {
500 
501                 /* Process cwnd in fast recovery procedure. */
502                 socket_ptr -> nx_tcp_socket_tx_window_congestion -= acked_bytes;
503                 if (acked_bytes > socket_ptr -> nx_tcp_socket_connect_mss)
504                 {
505                     socket_ptr -> nx_tcp_socket_tx_window_congestion += socket_ptr -> nx_tcp_socket_connect_mss;
506                 }
507             }
508             else
509             {
510 
511                 /* Adjust the transmit window.  In slow start phase, the transmit window is incremented for every ACK.
512                    In Congestion Avoidance phase, the window is incremented for every RTT. Section 3.1, Page 4-7, RFC5681.  */
513                 if (socket_ptr -> nx_tcp_socket_tx_window_congestion >= socket_ptr -> nx_tcp_socket_tx_slow_start_threshold)
514                 {
515 
516                     /* In Congestion avoidance phase, for every ACK it receives, increase the window size using the
517                        following approximation:
518                        cwnd = cwnd + MSS * MSS / cwnd;  */
519                     temp = socket_ptr -> nx_tcp_socket_connect_mss2 / socket_ptr -> nx_tcp_socket_tx_window_congestion;
520 
521                     /* If the above formula yields 0, the result SHOULD be rounded up to 1 byte.  */
522                     if (temp == 0)
523                     {
524                         temp = 1;
525                     }
526                     socket_ptr -> nx_tcp_socket_tx_window_congestion = socket_ptr -> nx_tcp_socket_tx_window_congestion + temp;
527                 }
528                 else
529                 {
530 
531                     /* In Slow start phase:
532                        cwnd += min (N, SMSS),
533                        where N is the number of ACKed bytes. */
534                     if (acked_bytes < socket_ptr -> nx_tcp_socket_connect_mss)
535                     {
536 
537                         /* In Slow start phase. Increase the cwnd by acked bytes.*/
538                         socket_ptr -> nx_tcp_socket_tx_window_congestion += acked_bytes;
539                     }
540                     else
541                     {
542 
543                         /* In Slow start phase. Increase the cwnd by full MSS for every ack.*/
544                         socket_ptr -> nx_tcp_socket_tx_window_congestion += socket_ptr -> nx_tcp_socket_connect_mss;
545                     }
546                 }
547             }
548         }
549 
550         /* Update the window only when
551          * 1. SND.UNA < SEG.ACK =< SND.NXT or
552          * 2. SND.WL1 < SEG.SEQ or
553          * 3. SND.WL1 = SEG.SEQ and SND.WL2 =< SEG.ACK
554          * RFC793, Section 3.9, Page72. */
555         if ((((INT)(tcp_header_ptr -> nx_tcp_acknowledgment_number - starting_tx_sequence) > 0) &&
556              ((INT)(tcp_header_ptr -> nx_tcp_acknowledgment_number - ending_tx_sequence) <= 0)) ||
557             ((INT)(tcp_header_ptr -> nx_tcp_sequence_number - ending_rx_sequence) > 0) ||
558             (((INT)(tcp_header_ptr -> nx_tcp_sequence_number == ending_rx_sequence)) &&
559              ((INT)(tcp_header_ptr -> nx_tcp_acknowledgment_number - starting_tx_sequence) >= 0)))
560         {
561 
562             /* Update this socket's transmit window with the advertised window size in the ACK message.  */
563             socket_ptr -> nx_tcp_socket_tx_window_advertised =  (tcp_header_ptr -> nx_tcp_header_word_3) & NX_LOWER_16_MASK;
564 
565 #ifdef NX_ENABLE_TCP_WINDOW_SCALING
566             socket_ptr -> nx_tcp_socket_tx_window_advertised <<= socket_ptr -> nx_tcp_snd_win_scale_value;
567 #endif /* NX_ENABLE_TCP_WINDOW_SCALING */
568         }
569 
570         /* Check advertised window. */
571         if ((socket_ptr -> nx_tcp_socket_tx_window_advertised <= socket_ptr -> nx_tcp_socket_tx_outstanding_bytes) &&
572             (tcp_header_ptr -> nx_tcp_acknowledgment_number >= socket_ptr -> nx_tcp_socket_zero_window_probe_sequence))
573         {
574 
575             /* It is an ACK to Zero Window Probe. Reset the zero window probe failure. */
576             socket_ptr -> nx_tcp_socket_zero_window_probe_failure = 0;
577         }
578 
579         if (!packet_release_count)
580         {
581             /* Done, return to caller. */
582             return(NX_TRUE);
583         }
584 
585         /* Once we get this line, packet_release_count is non-zero,
586            which indicates the transmit_sent_count value will be reduced. */
587 
588 #ifdef NX_ENABLE_TCP_QUEUE_DEPTH_UPDATE_NOTIFY
589 
590         /* Is NetX Duo set up with a transmit socket queue depth update? */
591         if (socket_ptr -> nx_tcp_socket_queue_depth_notify)
592         {
593 
594             /* Yes it is. Verify that the packets on the transmit queue exceed the max queue depth, and that
595                the packets to release bring that queue size below the max queue depth. */
596             if ((socket_ptr -> nx_tcp_socket_transmit_sent_count >= socket_ptr -> nx_tcp_socket_transmit_queue_maximum) &&
597                 ((socket_ptr -> nx_tcp_socket_transmit_sent_count - packet_release_count) < socket_ptr -> nx_tcp_socket_transmit_queue_maximum))
598             {
599 
600                 /* Yes to both; Call the queue depth notify callback. */
601                 (socket_ptr -> nx_tcp_socket_queue_depth_notify)(socket_ptr);
602             }
603         }
604 #endif
605 
606         /* Save the front of the of the transmit queue.  */
607         search_ptr =  socket_ptr -> nx_tcp_socket_transmit_sent_head;
608 
609         /* Okay so now the packet after the previous pointer needs to be the front of the
610            queue.  */
611         if (previous_ptr != socket_ptr -> nx_tcp_socket_transmit_sent_tail)
612         {
613 
614             /* Just update the head pointer.  */
615             socket_ptr -> nx_tcp_socket_transmit_sent_head  =  previous_ptr -> nx_packet_union_next.nx_packet_tcp_queue_next;
616 
617             /* And decrease the transmit queue count accordingly.  */
618             socket_ptr -> nx_tcp_socket_transmit_sent_count =   socket_ptr -> nx_tcp_socket_transmit_sent_count - packet_release_count;
619 
620             /* Setup a new transmit timeout.  */
621             socket_ptr -> nx_tcp_socket_timeout =          socket_ptr -> nx_tcp_socket_timeout_rate;
622             socket_ptr -> nx_tcp_socket_timeout_retries =  0;
623         }
624         else
625         {
626 
627             /* The transmit list is now cleared, just set the head and tail pointers to
628                NULL.  */
629             socket_ptr -> nx_tcp_socket_transmit_sent_head  =  NX_NULL;
630             socket_ptr -> nx_tcp_socket_transmit_sent_tail  =  NX_NULL;
631 
632             /* Clear the transmit queue count.  */
633             socket_ptr -> nx_tcp_socket_transmit_sent_count =  0;
634 
635             /* Determine if a disconnect FIN has been sent from this side of the connection.  */
636             if ((socket_ptr -> nx_tcp_socket_state == NX_TCP_FIN_WAIT_1) ||
637                 (socket_ptr -> nx_tcp_socket_state == NX_TCP_CLOSING)    ||
638                 (socket_ptr -> nx_tcp_socket_state == NX_TCP_LAST_ACK))
639             {
640 
641                 /* Yes, setup timeout such that the FIN can be retried if it is lost.  */
642                 socket_ptr -> nx_tcp_socket_timeout =          socket_ptr -> nx_tcp_socket_timeout_rate;
643                 socket_ptr -> nx_tcp_socket_timeout_retries =  0;
644             }
645             else if (socket_ptr -> nx_tcp_socket_tx_window_advertised != 0)
646             {
647 
648                 /* Otherwise, a FIN has not been sent, simply clear the transmit timeout.  */
649                 socket_ptr -> nx_tcp_socket_timeout =  0;
650             }
651         }
652 
653         /* Now walk through the packets to release and set them
654            free.  */
655         while (packet_release_count--)
656         {
657 
658             /* Use the previous pointer as the release pointer.  */
659             previous_ptr =  search_ptr;
660 
661             /* Move to the next packet in the queue before we clip the
662                next pointer.  */
663             search_ptr =  search_ptr -> nx_packet_union_next.nx_packet_tcp_queue_next;
664 
665             /* Disable interrupts temporarily.  */
666             TX_DISABLE
667 
668             /* Set the packet to allocated to indicate it is no longer part of the TCP queue.  */
669             /*lint -e{923} suppress cast of ULONG to pointer.  */
670             previous_ptr -> nx_packet_union_next.nx_packet_tcp_queue_next =  ((NX_PACKET *)NX_PACKET_ALLOCATED);
671 
672             /* Has the packet been transmitted? This is only pertinent if a retransmit of
673                the packet occurred prior to receiving the ACK. If so, the packet could be
674                in an ARP queue or in a driver queue waiting for transmission so we can't
675                release it directly at this point.  The driver or the ARP processing will
676                release it when finished.  */
677             /*lint -e{923} suppress cast of ULONG to pointer.  */
678             if (previous_ptr -> nx_packet_queue_next ==  ((NX_PACKET *)NX_DRIVER_TX_DONE))
679             {
680 
681                 /* Restore interrupts.  */
682                 TX_RESTORE
683 
684                 /* Yes, the driver has already released the packet.  */
685 
686                 /* Open up the transmit window. */
687                 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
688                 search_header_ptr = (NX_TCP_HEADER *)previous_ptr -> nx_packet_prepend_ptr;
689 
690                 temp = search_header_ptr -> nx_tcp_header_word_3;
691                 NX_CHANGE_ULONG_ENDIAN(temp);
692                 header_length = (temp >> NX_TCP_HEADER_SHIFT) * (ULONG)sizeof(ULONG);
693                 if (socket_ptr -> nx_tcp_socket_tx_outstanding_bytes > (previous_ptr -> nx_packet_length - header_length))
694                 {
695                     socket_ptr -> nx_tcp_socket_tx_outstanding_bytes -= previous_ptr -> nx_packet_length - header_length;
696                 }
697                 else
698                 {
699                     socket_ptr -> nx_tcp_socket_tx_outstanding_bytes = 0;
700                 }
701                 /* Release the packet.  */
702                 _nx_packet_release(previous_ptr);
703             }
704             else
705             {
706 
707                 /* No, the driver has not released the packet.  */
708                 /* Open up the transmit window. */
709                 search_header_ptr =  (NX_TCP_HEADER *)(previous_ptr -> nx_packet_ip_header +
710                                                        previous_ptr -> nx_packet_ip_header_length);
711 
712                 temp = search_header_ptr -> nx_tcp_header_word_3;
713                 NX_CHANGE_ULONG_ENDIAN(temp);
714                 header_length = (temp >> NX_TCP_HEADER_SHIFT) * (ULONG)sizeof(ULONG);
715                 tcp_payload_length = (previous_ptr -> nx_packet_length -
716                                       (header_length +
717                                        (ULONG)((ALIGN_TYPE)search_header_ptr -
718                                                (ALIGN_TYPE)(previous_ptr -> nx_packet_prepend_ptr))));
719                 if (socket_ptr -> nx_tcp_socket_tx_outstanding_bytes > tcp_payload_length)
720                 {
721                     socket_ptr -> nx_tcp_socket_tx_outstanding_bytes -= tcp_payload_length;
722                 }
723                 else
724                 {
725                     socket_ptr -> nx_tcp_socket_tx_outstanding_bytes = 0;
726                 }
727 
728                 /* Restore interrupts.  */
729                 TX_RESTORE
730             }
731         }
732 
733         if (socket_ptr -> nx_tcp_socket_fast_recovery == NX_TRUE)
734         {
735 
736             /* Only partial data are ACKed. Retransmit packet immediately. */
737             _nx_tcp_socket_retransmit(socket_ptr -> nx_tcp_socket_ip_ptr, socket_ptr, NX_FALSE);
738         }
739 
740         return(NX_TRUE);
741     }
742     else
743     {
744 
745         /* The ACK bit is off drop the segment and return.  */
746         /* RFC793, Section3.9, Page72.  */
747         return(NX_FALSE);
748     }
749 }
750 
751