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