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_packet.h"
30 #include "nx_ip.h"
31 #include "nx_tcp.h"
32 #ifdef FEATURE_NX_IPV6
33 #include "nx_ipv6.h"
34 #endif /* FEATURE_NX_IPV6 */
35 #ifdef NX_IPSEC_ENABLE
36 #include "nx_ipsec.h"
37 #endif /* NX_IPSEC_ENABLE */
38 
39 /**************************************************************************/
40 /*                                                                        */
41 /*  FUNCTION                                               RELEASE        */
42 /*                                                                        */
43 /*    _nx_tcp_socket_retransmit                           PORTABLE C      */
44 /*                                                           6.1          */
45 /*  AUTHOR                                                                */
46 /*                                                                        */
47 /*    Yuxin Zhou, Microsoft Corporation                                   */
48 /*                                                                        */
49 /*  DESCRIPTION                                                           */
50 /*                                                                        */
51 /*    This function retransmit a TCP packet.                              */
52 /*                                                                        */
53 /*  INPUT                                                                 */
54 /*                                                                        */
55 /*    ip_ptr                                IP instance pointer           */
56 /*    socket_ptr                            Pointer to owning socket      */
57 /*    need_fast_retransmit                  Need fast retransmit or not   */
58 /*                                                                        */
59 /*  OUTPUT                                                                */
60 /*                                                                        */
61 /*    None                                                                */
62 /*                                                                        */
63 /*  CALLS                                                                 */
64 /*                                                                        */
65 /*    _nx_tcp_packet_send_probe             Send zero window probe        */
66 /*    _nx_ip_checksum_compute               Calculate TCP checksum        */
67 /*    _nx_ip_packet_send                    Resend the transmit packet    */
68 /*    _nx_ipv6_packet_send                  Resend the transmit packet    */
69 /*                                                                        */
70 /*  CALLED BY                                                             */
71 /*                                                                        */
72 /*    _nx_tcp_fast_periodic_processing      Process TCP packet for socket */
73 /*    _nx_tcp_socket_state_ack_check        Process ACK number            */
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),          */
81 /*                                            resulting in version 6.1    */
82 /*                                                                        */
83 /**************************************************************************/
_nx_tcp_socket_retransmit(NX_IP * ip_ptr,NX_TCP_SOCKET * socket_ptr,UINT need_fast_retransmit)84 VOID  _nx_tcp_socket_retransmit(NX_IP *ip_ptr, NX_TCP_SOCKET *socket_ptr, UINT need_fast_retransmit)
85 {
86 NX_PACKET *packet_ptr;
87 ULONG      window;
88 ULONG      original_acknowledgment_number;
89 ULONG      original_header_word_3;
90 ULONG      original_header_word_4;
91 ULONG      available;
92 ULONG      window_size;
93 
94     /* If the receiver winodw is zero, we enter the zero window probe phase
95        RFC 793 Sec 3.7, p42: keep send new data.
96 
97        In the zero window probe phase, we send the zero window probe, and increase
98        exponentially the interval between successive probes.
99        RFC 1122 Sec 4.2.2.17, p92.  */
100     if (socket_ptr -> nx_tcp_socket_tx_window_advertised == 0)
101     {
102 
103         /* Pickup the head of the transmit queue.  */
104         packet_ptr =  socket_ptr -> nx_tcp_socket_transmit_sent_head;
105 
106         if (packet_ptr)
107         {
108 
109         /* Get one byte from send queue. */
110         /* Pick up the pointer to the head of the TCP packet.  */
111         /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
112         NX_TCP_HEADER *header_ptr =  (NX_TCP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
113 
114             NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_3);
115             NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_sequence_number);
116 
117             /* Get sequence number and first byte. */
118             socket_ptr -> nx_tcp_socket_zero_window_probe_data = *(packet_ptr -> nx_packet_prepend_ptr + ((header_ptr -> nx_tcp_header_word_3 >> 28) << 2));
119 
120             /* Now set zero window probe started. */
121             socket_ptr -> nx_tcp_socket_zero_window_probe_has_data = NX_TRUE;
122             socket_ptr -> nx_tcp_socket_zero_window_probe_sequence = header_ptr -> nx_tcp_sequence_number;
123             socket_ptr -> nx_tcp_socket_zero_window_probe_failure = 0;
124 
125             NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_sequence_number);
126             NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_3);
127         }
128         else if (socket_ptr -> nx_tcp_socket_zero_window_probe_has_data == NX_FALSE)
129         {
130             return;
131         }
132 
133         /* In the zero window probe phase, we send the zero window probe, and increase
134            exponentially the interval between successive probes.  */
135 
136         /* Increment the retry counter.  */
137         socket_ptr -> nx_tcp_socket_timeout_retries++;
138         socket_ptr -> nx_tcp_socket_zero_window_probe_failure++;
139 
140         /* Setup the next timeout.  */
141         socket_ptr -> nx_tcp_socket_timeout = socket_ptr -> nx_tcp_socket_timeout_rate <<
142             (socket_ptr -> nx_tcp_socket_timeout_retries * socket_ptr -> nx_tcp_socket_timeout_shift);
143 
144         /* Send the zero window probe.  */
145         _nx_tcp_packet_send_probe(socket_ptr, socket_ptr -> nx_tcp_socket_zero_window_probe_sequence,
146                                   socket_ptr -> nx_tcp_socket_zero_window_probe_data);
147 
148         return;
149     }
150     else if (socket_ptr -> nx_tcp_socket_zero_window_probe_has_data == NX_TRUE)
151     {
152 
153         /* If advertised window isn't zero, reset zero window probe flag. */
154         socket_ptr -> nx_tcp_socket_zero_window_probe_has_data = NX_FALSE;
155     }
156 
157     /* Increment the retry counter only if the receiver window is open. */
158     /* Increment the retry counter.  */
159     socket_ptr -> nx_tcp_socket_timeout_retries++;
160 
161     if ((need_fast_retransmit == NX_TRUE) || (socket_ptr -> nx_tcp_socket_fast_recovery == NX_FALSE))
162     {
163 
164         /* Timed out on an outgoing packet.  Enter slow start mode. */
165         /* Compute the flight size / 2 value. */
166         window = socket_ptr -> nx_tcp_socket_tx_outstanding_bytes >> 1;
167 
168         /* Make sure we have at least 2 * MSS */
169         if (window < (socket_ptr -> nx_tcp_socket_connect_mss << 1))
170         {
171             window = socket_ptr -> nx_tcp_socket_connect_mss << 1;
172         }
173 
174         /* Set the slow_start_threshold */
175         socket_ptr -> nx_tcp_socket_tx_slow_start_threshold = window;
176 
177         /* Set the current window to be MSS size. */
178         socket_ptr -> nx_tcp_socket_tx_window_congestion = socket_ptr -> nx_tcp_socket_connect_mss;
179 
180         /* Determine if this socket needs fast retransmit.  */
181         if (need_fast_retransmit == NX_TRUE)
182         {
183 
184             /* Update cwnd to ssthreshold plus 3 * MSS.  */
185             socket_ptr -> nx_tcp_socket_tx_window_congestion += window + (socket_ptr -> nx_tcp_socket_connect_mss << 1);
186 
187             /* Now TCP is in fast recovery procedure. */
188             socket_ptr -> nx_tcp_socket_fast_recovery = NX_TRUE;
189 
190             /* Update the transmit sequence that enters fast transmit. */
191             socket_ptr -> nx_tcp_socket_tx_sequence_recover = socket_ptr -> nx_tcp_socket_tx_sequence - 1;
192         }
193     }
194 
195     /* Setup the next timeout.  */
196     socket_ptr -> nx_tcp_socket_timeout = socket_ptr -> nx_tcp_socket_timeout_rate <<
197         (socket_ptr -> nx_tcp_socket_timeout_retries * socket_ptr -> nx_tcp_socket_timeout_shift);
198 
199     /* Get available size of packet that can be sent. */
200     available = socket_ptr -> nx_tcp_socket_tx_window_congestion;
201 
202     /* Pickup the head of the transmit queue.  */
203     packet_ptr =  socket_ptr -> nx_tcp_socket_transmit_sent_head;
204 
205     /* Determine if the packet has been released by the
206        application I/O driver.  */
207     /*lint -e{923} suppress cast of ULONG to pointer.  */
208     while (packet_ptr && (packet_ptr -> nx_packet_queue_next == (NX_PACKET *)NX_DRIVER_TX_DONE))
209     {
210 
211     /* Update the ACK number in case it has changed since the data was originally transmitted. */
212     ULONG          checksum;
213     NX_TCP_HEADER *header_ptr;
214     ULONG         *source_ip = NX_NULL, *dest_ip = NX_NULL;
215     NX_PACKET     *next_ptr;
216 #if defined(NX_DISABLE_TCP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
217     UINT           compute_checksum = 1;
218 #endif /* defined(NX_DISABLE_TCP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
219 
220 #ifdef NX_DISABLE_TCP_TX_CHECKSUM
221         compute_checksum = 0;
222 #endif /* NX_DISABLE_TCP_TX_CHECKSUM */
223 
224         if (packet_ptr -> nx_packet_length > (available + sizeof(NX_TCP_HEADER)))
225         {
226 
227             /* This packet can not be sent. */
228             break;
229         }
230 
231         /* Decrease the available size. */
232         available -= (packet_ptr -> nx_packet_length - (ULONG)sizeof(NX_TCP_HEADER));
233 
234         /* Pickup next packet. */
235         next_ptr = packet_ptr -> nx_packet_union_next.nx_packet_tcp_queue_next;
236 
237 #ifndef NX_DISABLE_IPV4
238         /* Is this an IPv4 connection? */
239         if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V4)
240         {
241 
242             packet_ptr -> nx_packet_ip_version = NX_IP_VERSION_V4;
243 
244             /* Get the source and destination addresses. */
245             source_ip = &socket_ptr -> nx_tcp_socket_connect_interface -> nx_interface_ip_address;
246             dest_ip = &socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v4;
247         }
248 #endif /* !NX_DISABLE_IPV4  */
249 
250 #ifdef FEATURE_NX_IPV6
251         if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V6)
252         {
253 
254             /* Set the packet for IPv6 connectivity. */
255             packet_ptr -> nx_packet_ip_version = NX_IP_VERSION_V6;
256 
257             /* Get the source and destination addresses. */
258             source_ip = socket_ptr -> nx_tcp_socket_ipv6_addr -> nxd_ipv6_address;
259             dest_ip = socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v6;
260         }
261 #endif /* FEATURE_NX_IPV6 */
262 
263         /* Pick up the pointer to the head of the TCP packet.  */
264         /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
265         header_ptr =  (NX_TCP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
266 
267         /* Record the original data.  */
268         original_acknowledgment_number = header_ptr -> nx_tcp_acknowledgment_number;
269         original_header_word_3 = header_ptr -> nx_tcp_header_word_3;
270         original_header_word_4 = header_ptr -> nx_tcp_header_word_4;
271 
272         /* Update the ACK number in the TCP header.  */
273         header_ptr -> nx_tcp_acknowledgment_number = socket_ptr -> nx_tcp_socket_rx_sequence;
274 
275         /* Convert to network byte order for checksum */
276         NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_acknowledgment_number);
277 
278         /* Set window size. */
279 #ifdef NX_ENABLE_TCP_WINDOW_SCALING
280         window_size = socket_ptr -> nx_tcp_socket_rx_window_current >> socket_ptr -> nx_tcp_rcv_win_scale_value;
281 
282         /* Make sure the window_size is less than 0xFFFF. */
283         if (window_size > 0xFFFF)
284         {
285             window_size = 0xFFFF;
286         }
287 #else
288         window_size = socket_ptr -> nx_tcp_socket_rx_window_current;
289 #endif /* NX_ENABLE_TCP_WINDOW_SCALING */
290 
291         header_ptr -> nx_tcp_header_word_3 =        NX_TCP_HEADER_SIZE | NX_TCP_ACK_BIT | NX_TCP_PSH_BIT | window_size;
292 
293         /* Swap the content to network byte order. */
294         NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_3);
295 
296         /* Convert back to host byte order to so we can zero out the checksum. */
297         NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_4);
298 
299         /* Remember the last ACKed sequence and the last reported window size.  */
300         socket_ptr -> nx_tcp_socket_rx_sequence_acked =    socket_ptr -> nx_tcp_socket_rx_sequence;
301         socket_ptr -> nx_tcp_socket_rx_window_last_sent =  socket_ptr -> nx_tcp_socket_rx_window_current;
302 
303         /* Zero out existing checksum before computing new one. */
304         header_ptr -> nx_tcp_header_word_4 = header_ptr -> nx_tcp_header_word_4 & 0x0000FFFF;
305 
306         /* Convert back to network byte order to so we can do the checksum. */
307         NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_4);
308 
309 
310 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
311         if (socket_ptr -> nx_tcp_socket_connect_interface -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCP_TX_CHECKSUM)
312         {
313             compute_checksum = 0;
314         }
315 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
316 
317 #ifdef NX_IPSEC_ENABLE
318         if ((packet_ptr -> nx_packet_ipsec_sa_ptr != NX_NULL) &&
319             (((NX_IPSEC_SA *)(packet_ptr -> nx_packet_ipsec_sa_ptr)) -> nx_ipsec_sa_encryption_method != NX_CRYPTO_NONE))
320         {
321             compute_checksum = 1;
322         }
323 #endif /* NX_IPSEC_ENABLE */
324 
325 #if defined(NX_DISABLE_TCP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
326         if (compute_checksum)
327 #endif /* defined(NX_DISABLE_TCP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
328         {
329             /* Calculate the TCP checksum without protection.  */
330             checksum =  _nx_ip_checksum_compute(packet_ptr, NX_PROTOCOL_TCP,
331                                                 packet_ptr -> nx_packet_length,
332                                                 source_ip, dest_ip);
333             checksum = ~checksum & NX_LOWER_16_MASK;
334 
335             /* Convert back to host byte order */
336             NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_4);
337 
338             /* Move the checksum into header.  */
339             header_ptr -> nx_tcp_header_word_4 =  header_ptr -> nx_tcp_header_word_4 | (checksum << NX_SHIFT_BY_16);
340 
341             /* Convert back to network byte order for transmit. */
342             NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_4);
343         }
344 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
345         else
346         {
347             packet_ptr -> nx_packet_interface_capability_flag |= NX_INTERFACE_CAPABILITY_TCP_TX_CHECKSUM;
348         }
349 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
350 
351         /* Determine if the retransmitted packet is identical to the original packet.
352            RFC1122, Section3.2.1.5, Page32-33. RFC1122, Section4.2.2.15, Page90-91.  */
353         if ((header_ptr -> nx_tcp_acknowledgment_number == original_acknowledgment_number) &&
354             (header_ptr -> nx_tcp_header_word_3 == original_header_word_3) &&
355             (header_ptr -> nx_tcp_header_word_4 == original_header_word_4))
356         {
357 
358             /* Yes, identical packet, update the identification flag.  */
359             packet_ptr -> nx_packet_identical_copy = NX_TRUE;
360         }
361 
362 
363 #ifndef NX_DISABLE_TCP_INFO
364         /* Increment the TCP retransmit count.  */
365         ip_ptr -> nx_ip_tcp_retransmit_packets++;
366 
367         /* Increment the TCP retransmit count for the socket.  */
368         socket_ptr -> nx_tcp_socket_retransmit_packets++;
369 #endif
370 
371         /* If trace is enabled, insert this event into the trace buffer.  */
372         NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_RETRY, ip_ptr, socket_ptr, packet_ptr, socket_ptr -> nx_tcp_socket_timeout_retries, NX_TRACE_INTERNAL_EVENTS, 0, 0);
373 
374         /* Clear the queue next pointer.  */
375         packet_ptr -> nx_packet_queue_next =  NX_NULL;
376 
377         /* Yes, the driver has finished with the packet at the head of the
378            transmit sent list... so it can be sent again!  */
379 
380 #ifndef NX_DISABLE_IPV4
381         /* Is this an IPv4 connection? */
382         if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V4)
383         {
384             _nx_ip_packet_send(ip_ptr, packet_ptr,
385                                socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v4,
386                                socket_ptr -> nx_tcp_socket_type_of_service,
387                                socket_ptr -> nx_tcp_socket_time_to_live, NX_IP_TCP,
388                                socket_ptr -> nx_tcp_socket_fragment_enable,
389                                socket_ptr -> nx_tcp_socket_next_hop_address);
390         }
391 #endif /* !NX_DISABLE_IPV4  */
392 
393 #ifdef FEATURE_NX_IPV6
394         if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V6)
395         {
396 
397             /* Handle for an IPv6 connection. */
398             /* Set the packet transmit interface before sending. */
399             packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr = socket_ptr -> nx_tcp_socket_ipv6_addr;
400 
401             _nx_ipv6_packet_send(ip_ptr, packet_ptr, NX_PROTOCOL_TCP,
402                                  packet_ptr -> nx_packet_length, ip_ptr -> nx_ipv6_hop_limit,
403                                  socket_ptr -> nx_tcp_socket_ipv6_addr -> nxd_ipv6_address,
404                                  socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v6);
405         }
406 #endif /* FEATURE_NX_IPV6 */
407 
408         /* Move to next packet. */
409         /* During fast recovery, only one packet is retransmitted at once. */
410         /* After a timeout, the sending data can be at most one SMSS. */
411         if ((next_ptr == (NX_PACKET *)NX_PACKET_ENQUEUED) ||
412             (socket_ptr -> nx_tcp_socket_fast_recovery == NX_TRUE))
413         {
414             break;
415         }
416         else
417         {
418             packet_ptr = next_ptr;
419         }
420     }
421 }
422 
423