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