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_tcp.h"
30 #include "tx_thread.h"
31 #include "nx_ipv6.h"
32 #include "nx_ipv4.h"
33 #include "nx_ip.h"
34 #ifdef NX_ENABLE_HTTP_PROXY
35 #include "nx_http_proxy_client.h"
36 #endif /* NX_ENABLE_HTTP_PROXY */
37
38
39 #ifdef NX_ENABLE_TCPIP_OFFLOAD
40 /**************************************************************************/
41 /* */
42 /* FUNCTION RELEASE */
43 /* */
44 /* _nxd_tcp_client_socket_driver_connect PORTABLE C */
45 /* 6.1.8 */
46 /* AUTHOR */
47 /* */
48 /* Yuxin Zhou, Microsoft Corporation */
49 /* */
50 /* DESCRIPTION */
51 /* */
52 /* This function handles the connect request for TCP/IP offload */
53 /* interface. */
54 /* */
55 /* INPUT */
56 /* */
57 /* socket_ptr Pointer to TCP client socket */
58 /* server_ip IP address of server */
59 /* server_port Port number of server */
60 /* wait_option Suspension option */
61 /* */
62 /* OUTPUT */
63 /* */
64 /* status Completion status */
65 /* */
66 /* CALLS */
67 /* */
68 /* tx_mutex_get Obtain protection */
69 /* tx_mutex_put Release protection */
70 /* */
71 /* CALLED BY */
72 /* */
73 /* _nxd_tcp_client_socket_connect */
74 /* */
75 /* RELEASE HISTORY */
76 /* */
77 /* DATE NAME DESCRIPTION */
78 /* */
79 /* 08-02-2021 Yuxin Zhou Initial Version 6.1.8 */
80 /* */
81 /**************************************************************************/
_nxd_tcp_client_socket_driver_connect(NX_TCP_SOCKET * socket_ptr,NXD_ADDRESS * server_ip,UINT server_port,ULONG wait_option)82 static UINT _nxd_tcp_client_socket_driver_connect(NX_TCP_SOCKET *socket_ptr,
83 NXD_ADDRESS *server_ip,
84 UINT server_port,
85 ULONG wait_option)
86 {
87 UINT status;
88 NX_INTERFACE *interface_ptr;
89 NX_IP *ip_ptr;
90
91
92 /* Setup the pointer to the associated IP instance. */
93 ip_ptr = socket_ptr -> nx_tcp_socket_ip_ptr;
94
95 /* Clear the socket timeout. */
96 socket_ptr -> nx_tcp_socket_timeout = 0;
97
98 /* Let TCP/IP offload interface make the connection. */
99 interface_ptr = socket_ptr -> nx_tcp_socket_connect_interface;
100 status = interface_ptr -> nx_interface_tcpip_offload_handler(ip_ptr, interface_ptr, socket_ptr,
101 NX_TCPIP_OFFLOAD_TCP_CLIENT_SOCKET_CONNECT,
102 NX_NULL, NX_NULL, server_ip,
103 socket_ptr -> nx_tcp_socket_port,
104 &server_port, wait_option);
105 if ((status == NX_SUCCESS) || (status == NX_IN_PROGRESS))
106 {
107
108 /* Set MSS. */
109 #ifndef NX_DISABLE_IPV4
110 if (server_ip -> nxd_ip_version == NX_IP_VERSION_V4)
111 {
112 socket_ptr -> nx_tcp_socket_mss =
113 (ULONG)((interface_ptr -> nx_interface_ip_mtu_size - sizeof(NX_IPV4_HEADER)) - sizeof(NX_TCP_HEADER));
114 }
115 #endif /* !NX_DISABLE_IPV4 */
116 #ifdef FEATURE_NX_IPV6
117 if (server_ip -> nxd_ip_version == NX_IP_VERSION_V6)
118 {
119 socket_ptr -> nx_tcp_socket_mss =
120 (ULONG)((interface_ptr -> nx_interface_ip_mtu_size - sizeof(NX_IPV6_HEADER)) - sizeof(NX_TCP_HEADER));
121 }
122 #endif /* FEATURE_NX_IPV6 */
123 socket_ptr -> nx_tcp_socket_connect_mss = socket_ptr -> nx_tcp_socket_mss;
124 socket_ptr -> nx_tcp_socket_peer_mss = socket_ptr -> nx_tcp_socket_mss;
125
126 if (status == NX_SUCCESS)
127 {
128
129 /* Connected to server. */
130 socket_ptr -> nx_tcp_socket_state = NX_TCP_ESTABLISHED;
131 #ifndef NX_DISABLE_EXTENDED_NOTIFY_SUPPORT
132
133 /* Is a connection completion callback registered with the TCP socket? */
134 if (socket_ptr -> nx_tcp_establish_notify)
135 {
136
137 /* Call the application's establish callback function. */
138 (socket_ptr -> nx_tcp_establish_notify)(socket_ptr);
139 }
140 #endif
141
142 /* Release the IP protection. */
143 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
144 return(NX_SUCCESS);
145 }
146 else
147 {
148
149 /* Connected to server. */
150 socket_ptr -> nx_tcp_socket_state = NX_TCP_SYN_SENT;
151 }
152 }
153 else
154 {
155
156 /* Unable to connect to server. */
157
158 #ifndef NX_DISABLE_TCP_INFO
159
160 /* Reduce the active connections count. */
161 ip_ptr -> nx_ip_tcp_active_connections--;
162
163 /* Reduce the TCP connections count. */
164 ip_ptr -> nx_ip_tcp_connections--;
165 #endif
166
167 /* Restore the socket state. */
168 socket_ptr -> nx_tcp_socket_state = NX_TCP_CLOSED;
169
170 /* Reset server port and server IP address. */
171 memset(&socket_ptr -> nx_tcp_socket_connect_ip, 0, sizeof(NXD_ADDRESS));
172 socket_ptr -> nx_tcp_socket_connect_port = 0;
173
174 /* Reset the next_hop_address information. */
175 socket_ptr -> nx_tcp_socket_next_hop_address = 0;
176
177 /* Release the IP protection. */
178 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
179 return(NX_TCPIP_OFFLOAD_ERROR);
180 }
181
182 /* Release the IP protection. */
183 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
184 return(NX_SUCCESS);
185 }
186 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
187
188
189 /**************************************************************************/
190 /* */
191 /* FUNCTION RELEASE */
192 /* */
193 /* _nxd_tcp_client_socket_connect PORTABLE C */
194 /* 6.2.0 */
195 /* AUTHOR */
196 /* */
197 /* Yuxin Zhou, Microsoft Corporation */
198 /* */
199 /* DESCRIPTION */
200 /* */
201 /* This function handles the connect request for the supplied socket. */
202 /* If bound and not connected, this function will send the first SYN */
203 /* message to the specified server to initiate the connection process. */
204 /* */
205 /* INPUT */
206 /* */
207 /* socket_ptr Pointer to TCP client socket */
208 /* server_ip IP address of server */
209 /* server_port Port number of server */
210 /* wait_option Suspension option */
211 /* */
212 /* OUTPUT */
213 /* */
214 /* status Completion status */
215 /* */
216 /* CALLS */
217 /* */
218 /* _nx_tcp_socket_thread_suspend Suspend thread for connection */
219 /* _nx_tcp_packet_send_syn Send SYN packet */
220 /* _nx_ip_route_find Find a suitable outgoing */
221 /* interface. */
222 /* tx_mutex_get Obtain protection */
223 /* tx_mutex_put Release protection */
224 /* _nx_http_proxy_client_initialize Initialize the HTTP Proxy */
225 /* */
226 /* CALLED BY */
227 /* */
228 /* Application Code */
229 /* */
230 /* RELEASE HISTORY */
231 /* */
232 /* DATE NAME DESCRIPTION */
233 /* */
234 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
235 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
236 /* resulting in version 6.1 */
237 /* 08-02-2021 Yuxin Zhou Modified comment(s), and */
238 /* supported TCP/IP offload, */
239 /* resulting in version 6.1.8 */
240 /* 10-31-2022 Wenhui Xie Modified comment(s), and */
241 /* supported HTTP Proxy, */
242 /* resulting in version 6.2.0 */
243 /* */
244 /**************************************************************************/
_nxd_tcp_client_socket_connect(NX_TCP_SOCKET * socket_ptr,NXD_ADDRESS * server_ip,UINT server_port,ULONG wait_option)245 UINT _nxd_tcp_client_socket_connect(NX_TCP_SOCKET *socket_ptr,
246 NXD_ADDRESS *server_ip,
247 UINT server_port,
248 ULONG wait_option)
249 {
250
251 UINT ip_header_size = 0;
252 NX_IP *ip_ptr;
253 NX_INTERFACE *outgoing_interface = NX_NULL;
254
255 #ifdef FEATURE_NX_IPV6
256 UINT status;
257 #endif /* FEATURE_NX_IPV6 */
258
259 #ifdef TX_ENABLE_EVENT_TRACE
260 ULONG ip_address_log = 0;
261 #endif /* TX_ENABLE_EVENT_TRACE */
262
263 /* Setup IP pointer. */
264 ip_ptr = socket_ptr -> nx_tcp_socket_ip_ptr;
265
266 #ifdef NX_ENABLE_HTTP_PROXY
267 if (ip_ptr -> nx_ip_http_proxy_enable)
268 {
269
270 /* Initialize the HTTP Proxy info and replace the peer IP and port with HTTP proxy server's IP and port. */
271 _nx_http_proxy_client_initialize(socket_ptr, &server_ip, &server_port);
272 }
273 #endif /* NX_ENABLE_HTTP_PROXY */
274
275 /* Make sure the server IP address is accesible. */
276 #ifndef NX_DISABLE_IPV4
277 if (server_ip -> nxd_ip_version == NX_IP_VERSION_V4)
278 {
279 if (_nx_ip_route_find(ip_ptr, server_ip -> nxd_ip_address.v4, &outgoing_interface, &socket_ptr -> nx_tcp_socket_next_hop_address) != NX_SUCCESS)
280 {
281 /* Return an IP address error code. */
282 return(NX_IP_ADDRESS_ERROR);
283 }
284 }
285 #endif /* !NX_DISABLE_IPV4 */
286
287 #ifdef FEATURE_NX_IPV6
288 /* For IPv6 connections, find a suitable outgoing interface, based on the TCP peer address. */
289 if (server_ip -> nxd_ip_version == NX_IP_VERSION_V6)
290 {
291
292 status = _nxd_ipv6_interface_find(ip_ptr, server_ip -> nxd_ip_address.v6,
293 &socket_ptr -> nx_tcp_socket_ipv6_addr,
294 NX_NULL);
295
296 if (status != NX_SUCCESS)
297 {
298 return(status);
299 }
300
301 outgoing_interface = socket_ptr -> nx_tcp_socket_ipv6_addr -> nxd_ipv6_address_attached;
302 }
303 #endif /* FEATURE_NX_IPV6 */
304
305 #ifdef TX_ENABLE_EVENT_TRACE
306 #ifndef NX_DISABLE_IPV4
307 if (server_ip -> nxd_ip_version == NX_IP_VERSION_V4)
308 {
309 ip_address_log = server_ip -> nxd_ip_address.v4;
310 }
311 #endif /* !NX_DISABLE_IPV4 */
312
313 #ifdef FEATURE_NX_IPV6
314 if (server_ip -> nxd_ip_version == NX_IP_VERSION_V6)
315 {
316 ip_address_log = server_ip -> nxd_ip_address.v6[3];
317 }
318 #endif /* FEATURE_NX_IPV6 */
319
320 /* If trace is enabled, insert this event into the trace buffer. */
321 NX_TRACE_IN_LINE_INSERT(NX_TRACE_TCP_CLIENT_SOCKET_CONNECT, ip_ptr, socket_ptr, ip_address_log, server_port, NX_TRACE_TCP_EVENTS, 0, 0);
322 #endif /* TX_ENABLE_EVENT_TRACE */
323
324 /* Obtain the IP mutex so we initiate the connect. */
325 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
326
327 /* Determine if the socket has already been bound to port or if a socket bind is
328 already pending from another thread. */
329 if (!socket_ptr -> nx_tcp_socket_bound_next)
330 {
331
332 /* Release protection. */
333 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
334
335 /* Return a not bound error code. */
336 return(NX_NOT_BOUND);
337 }
338
339 /* Determine if the socket is in a pre-connection state. */
340 if ((socket_ptr -> nx_tcp_socket_state != NX_TCP_CLOSED) && (socket_ptr -> nx_tcp_socket_state != NX_TCP_TIMED_WAIT))
341 {
342
343 /* Release protection. */
344 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
345
346 /* Return a not closed error code. */
347 return(NX_NOT_CLOSED);
348 }
349
350 #ifndef NX_DISABLE_TCP_INFO
351
352 /* Increment the active connections count. */
353 ip_ptr -> nx_ip_tcp_active_connections++;
354
355 /* Increment the TCP connections count. */
356 ip_ptr -> nx_ip_tcp_connections++;
357 #endif
358
359 /* If trace is enabled, insert this event into the trace buffer. */
360 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_STATE_CHANGE, ip_ptr, socket_ptr, socket_ptr -> nx_tcp_socket_state, NX_TCP_SYN_SENT, NX_TRACE_INTERNAL_EVENTS, 0, 0);
361
362 /* Move the TCP state to Sequence Sent, the next state of an active open. */
363 socket_ptr -> nx_tcp_socket_state = NX_TCP_SYN_SENT;
364
365 /* Save the server port and server IP address. */
366 socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version = server_ip -> nxd_ip_version;
367
368 #ifndef NX_DISABLE_IPV4
369 if (server_ip -> nxd_ip_version == NX_IP_VERSION_V4)
370 {
371
372 /* copy the IPv4 address */
373 socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v4 = server_ip -> nxd_ip_address.v4;
374
375 ip_header_size = (UINT)sizeof(NX_IPV4_HEADER);
376 }
377 #endif /* !NX_DISABLE_IPV4 */
378
379 #ifdef FEATURE_NX_IPV6
380 if (server_ip -> nxd_ip_version == NX_IP_VERSION_V6)
381 {
382 COPY_IPV6_ADDRESS(server_ip -> nxd_ip_address.v6,
383 socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v6);
384
385 ip_header_size = (UINT)sizeof(NX_IPV6_HEADER);
386 }
387 #endif /* FEATURE_NX_IPV6 */
388
389 socket_ptr -> nx_tcp_socket_connect_port = server_port;
390
391 /* Outgoing interface must not be null. */
392 NX_ASSERT(outgoing_interface != NX_NULL);
393
394 /* Initialize the maximum segment size based on the interface MTU. */
395 /*lint -e{644} suppress variable might not be initialized, since "outgoing_interface" was initialized by _nx_ip_route_find or _nxd_ipv6_interface_find. */
396 if (outgoing_interface -> nx_interface_ip_mtu_size < (ip_header_size + NX_TCP_SYN_SIZE))
397 {
398
399 /* Interface MTU size is smaller than IP and TCP header size. Invalid interface! */
400
401 #ifndef NX_DISABLE_TCP_INFO
402
403 /* Reduce the active connections count. */
404 ip_ptr -> nx_ip_tcp_active_connections--;
405
406 /* Reduce the TCP connections count. */
407 ip_ptr -> nx_ip_tcp_connections--;
408 #endif
409
410 /* Restore the socket state. */
411 socket_ptr -> nx_tcp_socket_state = NX_TCP_CLOSED;
412
413 /* Reset server port and server IP address. */
414 memset(&socket_ptr -> nx_tcp_socket_connect_ip, 0, sizeof(NXD_ADDRESS));
415 socket_ptr -> nx_tcp_socket_connect_port = 0;
416
417 /* Reset the next_hop_address information. */
418 socket_ptr -> nx_tcp_socket_next_hop_address = 0;
419
420 /* Release protection. */
421 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
422
423
424 /* Return an IP address error code. */
425 return(NX_INVALID_INTERFACE);
426 }
427
428 socket_ptr -> nx_tcp_socket_connect_interface = outgoing_interface;
429
430 /* Setup the initial sequence number. */
431 if (socket_ptr -> nx_tcp_socket_tx_sequence == 0)
432 {
433 socket_ptr -> nx_tcp_socket_tx_sequence = (((ULONG)NX_RAND()) << NX_SHIFT_BY_16) & 0xFFFFFFFF;
434 socket_ptr -> nx_tcp_socket_tx_sequence |= (ULONG)NX_RAND();
435 }
436 else
437 {
438 socket_ptr -> nx_tcp_socket_tx_sequence = socket_ptr -> nx_tcp_socket_tx_sequence + ((ULONG)(((ULONG)0x10000))) + ((ULONG)NX_RAND());
439 }
440
441 /* Ensure the rx window size logic is reset. */
442 socket_ptr -> nx_tcp_socket_rx_window_current = socket_ptr -> nx_tcp_socket_rx_window_default;
443 socket_ptr -> nx_tcp_socket_rx_window_last_sent = socket_ptr -> nx_tcp_socket_rx_window_default;
444
445 /* Clear the FIN received flag. */
446 socket_ptr -> nx_tcp_socket_fin_received = NX_FALSE;
447 socket_ptr -> nx_tcp_socket_fin_acked = NX_FALSE;
448
449 /* Increment the sequence number. */
450 socket_ptr -> nx_tcp_socket_tx_sequence++;
451
452 /* Setup a timeout so the connection attempt can be sent again. */
453 socket_ptr -> nx_tcp_socket_timeout = socket_ptr -> nx_tcp_socket_timeout_rate;
454 socket_ptr -> nx_tcp_socket_timeout_retries = 0;
455
456 /* CLEANUP: In case any existing packets on socket's receive queue. */
457 if (socket_ptr -> nx_tcp_socket_receive_queue_count)
458 {
459
460 /* Remove all packets on the socket's receive queue. */
461 _nx_tcp_socket_receive_queue_flush(socket_ptr);
462 }
463
464 /* CLEANUP: Clean up any existing socket data before making a new connection. */
465 socket_ptr -> nx_tcp_socket_tx_window_congestion = 0;
466 socket_ptr -> nx_tcp_socket_tx_outstanding_bytes = 0;
467 socket_ptr -> nx_tcp_socket_packets_sent = 0;
468 socket_ptr -> nx_tcp_socket_bytes_sent = 0;
469 socket_ptr -> nx_tcp_socket_packets_received = 0;
470 socket_ptr -> nx_tcp_socket_bytes_received = 0;
471 socket_ptr -> nx_tcp_socket_retransmit_packets = 0;
472 socket_ptr -> nx_tcp_socket_checksum_errors = 0;
473 socket_ptr -> nx_tcp_socket_transmit_sent_head = NX_NULL;
474 socket_ptr -> nx_tcp_socket_transmit_sent_tail = NX_NULL;
475 socket_ptr -> nx_tcp_socket_transmit_sent_count = 0;
476 socket_ptr -> nx_tcp_socket_receive_queue_count = 0;
477 socket_ptr -> nx_tcp_socket_receive_queue_head = NX_NULL;
478 socket_ptr -> nx_tcp_socket_receive_queue_tail = NX_NULL;
479
480 #ifdef NX_ENABLE_TCPIP_OFFLOAD
481 if ((outgoing_interface -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCPIP_OFFLOAD) &&
482 (outgoing_interface -> nx_interface_tcpip_offload_handler))
483 {
484
485 /* This interface supports TCP/IP offload. */
486 return(_nxd_tcp_client_socket_driver_connect(socket_ptr, server_ip, server_port, wait_option));
487 }
488 else
489 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
490 {
491
492 /* Send the SYN message. */
493 _nx_tcp_packet_send_syn(socket_ptr, (socket_ptr -> nx_tcp_socket_tx_sequence - 1));
494 }
495
496 #ifdef NX_ENABLE_HTTP_PROXY
497 if (ip_ptr -> nx_ip_http_proxy_enable)
498 {
499
500 /* Set HTTP Proxy state as waiting for TCP connection. */
501 socket_ptr -> nx_tcp_socket_http_proxy_state = NX_HTTP_PROXY_STATE_WAITING;
502 }
503 #endif /* NX_ENABLE_HTTP_PROXY */
504
505 /* Optionally suspend the thread. If timeout occurs, return a connection timeout status. If
506 immediate response is selected, return a connection in progress status. Only on a real
507 connection should success be returned. */
508 if ((wait_option) && (_tx_thread_current_ptr != &(ip_ptr -> nx_ip_thread)))
509 {
510
511 /* Suspend the thread on this socket's connection attempt. */
512 /* Note: the IP protection mutex is released inside _nx_tcp_socket_thread_suspend(). */
513
514 _nx_tcp_socket_thread_suspend(&(socket_ptr -> nx_tcp_socket_connect_suspended_thread), _nx_tcp_connect_cleanup, socket_ptr, &(ip_ptr -> nx_ip_protection), wait_option);
515
516 /* Just return the completion code. */
517 return(_tx_thread_current_ptr -> tx_thread_suspend_status);
518 }
519 else
520 {
521
522 /* No suspension is request, just release protection and return to the caller. */
523
524 /* Release the IP protection. */
525 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
526
527 /* Return in-progress completion status. */
528 return(NX_IN_PROGRESS);
529 }
530 }
531
532