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