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 #ifdef FEATURE_NX_IPV6
31 #include "nx_ipv6.h"
32 #endif /* FEATURE_NX_IPV6 */
33 #include "nx_packet.h"
34 #include "nx_tcp.h"
35 #ifdef NX_IPSEC_ENABLE
36 #include "nx_ipsec.h"
37 #endif /* NX_IPSEC_ENABLE */
38 
39 
40 /**************************************************************************/
41 /*                                                                        */
42 /*  FUNCTION                                               RELEASE        */
43 /*                                                                        */
44 /*    _nx_tcp_packet_send_ack                             PORTABLE C      */
45 /*                                                           6.1          */
46 /*  AUTHOR                                                                */
47 /*                                                                        */
48 /*    Yuxin Zhou, Microsoft Corporation                                   */
49 /*                                                                        */
50 /*  DESCRIPTION                                                           */
51 /*                                                                        */
52 /*    This function sends a TCP control packet from the specified socket. */
53 /*                                                                        */
54 /*  INPUT                                                                 */
55 /*                                                                        */
56 /*    socket_ptr                            Pointer to socket             */
57 /*    control_bits                          TCP control bits              */
58 /*    tx_sequence                           Transmit sequence number      */
59 /*    ack_number                            Transmit acknowledge number   */
60 /*    data                                  Data of zero window probe     */
61 /*                                                                        */
62 /*  OUTPUT                                                                */
63 /*                                                                        */
64 /*    None                                                                */
65 /*                                                                        */
66 /*  CALLS                                                                 */
67 /*                                                                        */
68 /*    _nx_packet_allocate                   Allocate a packet             */
69 /*    _nx_ip_checksum_compute               Calculate TCP checksum        */
70 /*    _nx_ip_packet_send                    Send IPv4 packet              */
71 /*    _nx_ipv6_packet_send                  Send IPv6 packet              */
72 /*                                                                        */
73 /*  CALLED BY                                                             */
74 /*                                                                        */
75 /*    _nx_tcp_packet_send_syn               Send SYN packet               */
76 /*    _nx_tcp_packet_send_ack               Send ACK packet               */
77 /*    _nx_tcp_packet_send_fin               Send FIN packet               */
78 /*    _nx_tcp_packet_send_rst               Send RST packet               */
79 /*    _nx_tcp_packet_send_probe             Send zero window probe packet */
80 /*                                                                        */
81 /*  RELEASE HISTORY                                                       */
82 /*                                                                        */
83 /*    DATE              NAME                      DESCRIPTION             */
84 /*                                                                        */
85 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
86 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
87 /*                                            resulting in version 6.1    */
88 /*                                                                        */
89 /**************************************************************************/
_nx_tcp_packet_send_control(NX_TCP_SOCKET * socket_ptr,ULONG control_bits,ULONG tx_sequence,ULONG ack_number,ULONG option_word_1,ULONG option_word_2,UCHAR * data)90 VOID  _nx_tcp_packet_send_control(NX_TCP_SOCKET *socket_ptr, ULONG control_bits, ULONG tx_sequence,
91                                   ULONG ack_number, ULONG option_word_1, ULONG option_word_2, UCHAR *data)
92 {
93 
94 NX_IP         *ip_ptr;
95 NX_PACKET     *packet_ptr;
96 NX_TCP_HEADER *tcp_header_ptr;
97 ULONG          checksum;
98 ULONG          data_offset = 0;
99 ULONG         *source_ip = NX_NULL, *dest_ip = NX_NULL;
100 #if defined(NX_DISABLE_TCP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
101 UINT           compute_checksum = 1;
102 #endif /* defined(NX_DISABLE_TCP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
103 ULONG          header_size;
104 ULONG          window_size;
105 
106 #ifdef NX_DISABLE_TCP_TX_CHECKSUM
107     compute_checksum = 0;
108 #endif /* NX_DISABLE_TCP_TX_CHECKSUM */
109 
110     /* Setup the IP pointer.  */
111     ip_ptr =  socket_ptr -> nx_tcp_socket_ip_ptr;
112 
113     if (control_bits & NX_TCP_SYN_BIT)
114     {
115 
116         /* Set header size. */
117         header_size = NX_TCP_SYN_HEADER;
118         window_size = socket_ptr -> nx_tcp_socket_rx_window_current;
119     }
120     else
121     {
122 
123         /* Set header size. */
124         header_size = NX_TCP_HEADER_SIZE;
125 
126         /* Set window size. */
127 #ifdef NX_ENABLE_TCP_WINDOW_SCALING
128         window_size = socket_ptr -> nx_tcp_socket_rx_window_current >> socket_ptr -> nx_tcp_rcv_win_scale_value;
129 #else
130         window_size = socket_ptr -> nx_tcp_socket_rx_window_current;
131 #endif /* NX_ENABLE_TCP_WINDOW_SCALING */
132     }
133 
134 #ifdef NX_ENABLE_TCP_WINDOW_SCALING
135     /* Make sure the window_size is less than 0xFFFF. */
136     if (window_size > 0xFFFF)
137     {
138         window_size = 0xFFFF;
139     }
140 #endif /* NX_ENABLE_TCP_WINDOW_SCALING */
141 
142 #ifdef NX_IPSEC_ENABLE
143     /* Get data offset from socket directly. */
144     data_offset = socket_ptr -> nx_tcp_socket_egress_sa_data_offset;
145 #endif /* NX_IPSEC_ENABLE */
146 
147 #ifdef NX_ENABLE_DUAL_PACKET_POOL
148     /* Allocate from auxiliary packet pool first. */
149     if (_nx_packet_allocate(ip_ptr -> nx_ip_auxiliary_packet_pool, &packet_ptr, NX_IP_PACKET + data_offset, NX_NO_WAIT))
150     {
151         if (ip_ptr -> nx_ip_auxiliary_packet_pool != ip_ptr -> nx_ip_default_packet_pool)
152 #endif /* NX_ENABLE_DUAL_PACKET_POOL */
153         {
154 
155             /*lint -e{835} -e{845} suppress operating on zero. */
156             if (_nx_packet_allocate(ip_ptr -> nx_ip_default_packet_pool,
157                                     &packet_ptr, NX_IP_PACKET + data_offset, NX_NO_WAIT) != NX_SUCCESS)
158             {
159 
160                 /* Just give up and return.  */
161                 return;
162             }
163         }
164 #ifdef NX_ENABLE_DUAL_PACKET_POOL
165         else
166         {
167 
168             /* Just give up and return.  */
169             return;
170         }
171     }
172 #endif /* NX_ENABLE_DUAL_PACKET_POOL */
173 
174     /* Check to see if the packet has enough room to fill with the max TCP header (SYN + probe data).  */
175     if ((UINT)(packet_ptr -> nx_packet_data_end - packet_ptr -> nx_packet_prepend_ptr) < (NX_TCP_SYN_SIZE + 1))
176     {
177 
178         /* Error getting packet, so just get out!  */
179         _nx_packet_release(packet_ptr);
180         return;
181     }
182 
183     /*lint -e{644} suppress variable might not be initialized, since "packet_ptr" was initialized in _nx_packet_allocate. */
184     packet_ptr -> nx_packet_ip_version = (UCHAR)(socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version);
185 
186     /* Allocate a packet for the control message.  */
187 #ifndef NX_DISABLE_IPV4
188     if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V4)
189     {
190 
191         /* The outgoing interface should have been stored in the socket structure. */
192         packet_ptr -> nx_packet_address.nx_packet_interface_ptr = socket_ptr -> nx_tcp_socket_connect_interface;
193     }
194 #endif /* !NX_DISABLE_IPV4  */
195 
196     /* Add debug information. */
197     NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
198 
199 #ifdef NX_IPSEC_ENABLE
200     packet_ptr -> nx_packet_ipsec_sa_ptr = socket_ptr -> nx_tcp_socket_egress_sa;
201 #endif
202 
203     /* Setup the packet payload pointers and length for a basic TCP packet.  */
204     packet_ptr -> nx_packet_append_ptr =  packet_ptr -> nx_packet_prepend_ptr + sizeof(NX_TCP_HEADER);
205 
206     /* Setup the packet length.  */
207     packet_ptr -> nx_packet_length =  sizeof(NX_TCP_HEADER);
208 
209     /* Pickup the pointer to the head of the TCP packet.  */
210     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
211     tcp_header_ptr =  (NX_TCP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
212 
213     /* Build the control request in the TCP header.  */
214     tcp_header_ptr -> nx_tcp_header_word_0 =        (((ULONG)(socket_ptr -> nx_tcp_socket_port)) << NX_SHIFT_BY_16) | (ULONG)socket_ptr -> nx_tcp_socket_connect_port;
215     tcp_header_ptr -> nx_tcp_sequence_number =      tx_sequence;
216     tcp_header_ptr -> nx_tcp_acknowledgment_number = ack_number;
217     tcp_header_ptr -> nx_tcp_header_word_3 =        header_size | control_bits | window_size;
218     tcp_header_ptr -> nx_tcp_header_word_4 =        0;
219 
220     /* Remember the last ACKed sequence and the last reported window size.  */
221     socket_ptr -> nx_tcp_socket_rx_sequence_acked =    ack_number;
222     socket_ptr -> nx_tcp_socket_rx_window_last_sent =  socket_ptr -> nx_tcp_socket_rx_window_current;
223 
224     /* Endian swapping logic.  If NX_LITTLE_ENDIAN is specified, these macros will
225        swap the endian of the TCP header.  */
226     NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_header_word_0);
227     NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_sequence_number);
228     NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_acknowledgment_number);
229     NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_header_word_3);
230     NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_header_word_4);
231 
232     /* Check whether or not data is set. */
233     if (data)
234     {
235 
236         /* Zero window probe data exist. */
237         *packet_ptr -> nx_packet_append_ptr++ = *data;
238         packet_ptr -> nx_packet_length++;
239     }
240 
241     /* Whether it is a SYN packet. */
242     if (control_bits & NX_TCP_SYN_BIT)
243     {
244 
245         /* Endian swapping logic.  If NX_LITTLE_ENDIAN is specified, these macros will
246            swap the endian of the TCP header.  */
247         NX_CHANGE_ULONG_ENDIAN(option_word_1);
248         NX_CHANGE_ULONG_ENDIAN(option_word_2);
249 
250         /* Set options. */
251         /*lint --e{927} --e{826} suppress cast of pointer to pointer, since it is necessary  */
252         *((ULONG *)packet_ptr -> nx_packet_append_ptr) = option_word_1;
253         *(((ULONG *)packet_ptr -> nx_packet_append_ptr) + 1) = option_word_2;
254 
255         /* Adjust packet information. */
256         packet_ptr -> nx_packet_append_ptr += (sizeof(ULONG) << 1);
257         packet_ptr -> nx_packet_length += (ULONG)(sizeof(ULONG) << 1);
258     }
259 
260 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
261     if (socket_ptr -> nx_tcp_socket_connect_interface -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCP_TX_CHECKSUM)
262     {
263         compute_checksum = 0;
264     }
265 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
266 
267 #ifdef NX_IPSEC_ENABLE
268     if ((packet_ptr -> nx_packet_ipsec_sa_ptr != NX_NULL) && (((NX_IPSEC_SA *)(packet_ptr -> nx_packet_ipsec_sa_ptr)) -> nx_ipsec_sa_encryption_method != NX_CRYPTO_NONE))
269     {
270         compute_checksum = 1;
271     }
272 #endif /* NX_IPSEC_ENABLE */
273 
274 #if defined(NX_DISABLE_TCP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
275     if (compute_checksum)
276 #endif /* defined(NX_DISABLE_TCP_TX_CHECKSUM ) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
277     {
278 
279 
280         /* Set the packet source IP address. */
281 #ifndef NX_DISABLE_IPV4
282         if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V4)
283         {
284 
285             /* For IPv4 the IP instance has only one global address. */
286             source_ip = &socket_ptr -> nx_tcp_socket_connect_interface -> nx_interface_ip_address;
287 
288             /* Set the destination address to the other side of the TCP connection. */
289             dest_ip = &socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v4;
290         }
291 #endif /* !NX_DISABLE_IPV4  */
292 
293 #ifdef FEATURE_NX_IPV6
294         if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V6)
295         {
296 
297             /* For IPv6, use the source address specified in the socket outgoing interface. */
298             source_ip = socket_ptr -> nx_tcp_socket_ipv6_addr -> nxd_ipv6_address;
299 
300             /* Set the destination address to the other side of the TCP connection. */
301             dest_ip = socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v6;
302         }
303 #endif /* FEATURE_NX_IPV6 */
304 
305         /* Calculate the TCP checksum.  */
306         checksum =  _nx_ip_checksum_compute(packet_ptr, NX_PROTOCOL_TCP,
307                                             (UINT)packet_ptr -> nx_packet_length, source_ip, dest_ip);
308 
309         checksum = ~checksum & NX_LOWER_16_MASK;
310 
311         /* Move the checksum into header.  */
312         NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_header_word_4);
313         tcp_header_ptr -> nx_tcp_header_word_4 =  (checksum << NX_SHIFT_BY_16);
314         NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_header_word_4);
315     }
316 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
317     else
318     {
319         packet_ptr -> nx_packet_interface_capability_flag |= NX_INTERFACE_CAPABILITY_TCP_TX_CHECKSUM;
320     }
321 #endif /* NX_ENABLE_INTERFACE_CAPABILITY  */
322 
323 #ifndef NX_DISABLE_IPV4
324     /* Send the TCP packet to the IP component.  */
325     if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V4)
326     {
327 
328         _nx_ip_packet_send(ip_ptr, packet_ptr, socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v4,
329                            socket_ptr -> nx_tcp_socket_type_of_service, socket_ptr -> nx_tcp_socket_time_to_live, NX_IP_TCP,
330                            socket_ptr -> nx_tcp_socket_fragment_enable,
331                            socket_ptr -> nx_tcp_socket_next_hop_address);
332     }
333 #endif /* !NX_DISABLE_IPV4  */
334 
335 #ifdef FEATURE_NX_IPV6
336     if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V6)
337     {
338 
339         /* The IPv6 packet interface must be set before sending. Set to the TCP socket outgoing interface. */
340         packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr = socket_ptr -> nx_tcp_socket_ipv6_addr;
341 
342         _nx_ipv6_packet_send(ip_ptr, packet_ptr, NX_PROTOCOL_TCP, packet_ptr -> nx_packet_length, ip_ptr -> nx_ipv6_hop_limit,
343                              socket_ptr -> nx_tcp_socket_ipv6_addr -> nxd_ipv6_address,
344                              socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v6);
345     }
346 #endif /* FEATURE_NX_IPV6 */
347 }
348 
349