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