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