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