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