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 #ifdef NX_ENABLE_HTTP_PROXY
30 #include "nx_http_proxy_client.h"
31 #endif /* NX_ENABLE_HTTP_PROXY */
32
33
34 /**************************************************************************/
35 /* */
36 /* FUNCTION RELEASE */
37 /* */
38 /* _nx_tcp_socket_state_syn_sent PORTABLE C */
39 /* 6.2.0 */
40 /* AUTHOR */
41 /* */
42 /* Yuxin Zhou, Microsoft Corporation */
43 /* */
44 /* DESCRIPTION */
45 /* */
46 /* This function processes packets during the SYN SENT state, which is */
47 /* the state of the socket immediately after the initial SYN is sent */
48 /* in the establishment of a TCP connection. We are expecting a SYN */
49 /* and an ACK from the other side of the connection in order to move */
50 /* into an established state. */
51 /* */
52 /* INPUT */
53 /* */
54 /* socket_ptr Pointer to owning socket */
55 /* tcp_header_ptr Pointer to packet header */
56 /* */
57 /* OUTPUT */
58 /* */
59 /* None */
60 /* */
61 /* CALLS */
62 /* */
63 /* _nx_tcp_packet_send_ack Send ACK packet */
64 /* _nx_tcp_packet_send_syn Send SYN packet */
65 /* _nx_tcp_packet_send_rst Send RST packet */
66 /* _nx_tcp_socket_thread_resume Resume suspended thread */
67 /* _nx_http_proxy_client_connect Connect with HTTP Proxy */
68 /* */
69 /* CALLED BY */
70 /* */
71 /* _nx_tcp_socket_packet_process Process TCP packet for socket */
72 /* */
73 /* RELEASE HISTORY */
74 /* */
75 /* DATE NAME DESCRIPTION */
76 /* */
77 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
78 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
79 /* resulting in version 6.1 */
80 /* 10-31-2022 Wenhui Xie Modified comment(s), and */
81 /* supported HTTP Proxy, */
82 /* resulting in version 6.2.0 */
83 /* */
84 /**************************************************************************/
_nx_tcp_socket_state_syn_sent(NX_TCP_SOCKET * socket_ptr,NX_TCP_HEADER * tcp_header_ptr,NX_PACKET * packet_ptr)85 VOID _nx_tcp_socket_state_syn_sent(NX_TCP_SOCKET *socket_ptr, NX_TCP_HEADER *tcp_header_ptr, NX_PACKET *packet_ptr)
86 {
87
88
89 #ifndef TX_ENABLE_EVENT_TRACE
90 NX_PARAMETER_NOT_USED(packet_ptr);
91 #endif /* TX_ENABLE_EVENT_TRACE */
92
93 /* Check if a RST is present. */
94 if (tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_RST_BIT)
95 {
96
97 /* Check if the ACK was acceptable. According to RFC 793, Section 3.9, Page 67. */
98 if ((tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_ACK_BIT) &&
99 (tcp_header_ptr -> nx_tcp_acknowledgment_number == socket_ptr -> nx_tcp_socket_tx_sequence))
100 {
101
102 #ifndef NX_DISABLE_TCP_INFO
103
104 /* Increment the resets received count. */
105 (socket_ptr -> nx_tcp_socket_ip_ptr) -> nx_ip_tcp_resets_received++;
106 #endif
107
108 /* If trace is enabled, insert this event into the trace buffer. */
109 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_RESET_RECEIVE, socket_ptr -> nx_tcp_socket_ip_ptr, socket_ptr, packet_ptr, tcp_header_ptr -> nx_tcp_sequence_number, NX_TRACE_INTERNAL_EVENTS, 0, 0);
110
111 /* Reset connection. */
112 _nx_tcp_socket_connection_reset(socket_ptr);
113 }
114
115 /* Finished processing, simply return! */
116 return;
117 }
118 /* Determine if a valid SYN/ACK is present. */
119 else if ((tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_SYN_BIT) &&
120 (tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_ACK_BIT) &&
121 (tcp_header_ptr -> nx_tcp_acknowledgment_number == socket_ptr -> nx_tcp_socket_tx_sequence))
122 {
123
124 /* Yes, this is a proper SYN/ACK message. We need to send an ACK
125 back the other direction before we go into the ESTABLISHED
126 state. */
127
128 /* Save the sequence number. */
129 socket_ptr -> nx_tcp_socket_rx_sequence = tcp_header_ptr -> nx_tcp_sequence_number + 1;
130
131 /* Save the window size. */
132 socket_ptr -> nx_tcp_socket_tx_window_advertised = tcp_header_ptr -> nx_tcp_header_word_3 & NX_LOWER_16_MASK;
133
134 #ifdef NX_ENABLE_TCP_WINDOW_SCALING
135
136 /* The window size advertised in the SYN packet is NEVER scaled. Therefore there is no
137 need to apply the scale shift. However validate snd_win_scale and rcv_win_scale. */
138 if (socket_ptr -> nx_tcp_snd_win_scale_value == 0xFF)
139 {
140 /* Peer does not support window scale option. */
141 socket_ptr -> nx_tcp_snd_win_scale_value = 0;
142 socket_ptr -> nx_tcp_rcv_win_scale_value = 0;
143
144 /* Since the peer does not offer window scaling feature, make sure
145 our default window size for this connection does not exceed 65535 bytes. */
146 if (socket_ptr -> nx_tcp_socket_rx_window_maximum > 65535)
147 {
148 socket_ptr -> nx_tcp_socket_rx_window_default = 65535;
149 socket_ptr -> nx_tcp_socket_rx_window_current = 65535;
150 }
151 }
152
153 #endif /* NX_ENABLE_TCP_WINDOW_SCALING */
154
155 /* Initialize the slow start threshold to be the advertised window size. */
156 socket_ptr -> nx_tcp_socket_tx_slow_start_threshold = socket_ptr -> nx_tcp_socket_tx_window_advertised;
157
158 /* Set the Initial transmit outstanding byte count. */
159 socket_ptr -> nx_tcp_socket_tx_outstanding_bytes = 0;
160
161 /* Set the initial congestion control window size. */
162 /* Section 3.1, Page 5, RFC5681. */
163 if (socket_ptr -> nx_tcp_socket_timeout_retries > 0)
164 {
165
166 /* Set the initial congestion control window size to be the mss. */
167 socket_ptr -> nx_tcp_socket_tx_window_congestion = socket_ptr -> nx_tcp_socket_connect_mss;
168 }
169 else
170 {
171 socket_ptr -> nx_tcp_socket_tx_window_congestion = (socket_ptr -> nx_tcp_socket_connect_mss << 2);
172 if (socket_ptr -> nx_tcp_socket_connect_mss > 1095)
173 {
174 socket_ptr -> nx_tcp_socket_tx_window_congestion -= socket_ptr -> nx_tcp_socket_connect_mss;
175 }
176 if (socket_ptr -> nx_tcp_socket_connect_mss > 2190)
177 {
178 socket_ptr -> nx_tcp_socket_tx_window_congestion -= socket_ptr -> nx_tcp_socket_connect_mss;
179 }
180 }
181
182 /* Send the ACK. */
183 _nx_tcp_packet_send_ack(socket_ptr, socket_ptr -> nx_tcp_socket_tx_sequence);
184
185 /* If trace is enabled, insert this event into the trace buffer. */
186 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_STATE_CHANGE, socket_ptr -> nx_tcp_socket_ip_ptr, socket_ptr, socket_ptr -> nx_tcp_socket_state, NX_TCP_ESTABLISHED, NX_TRACE_INTERNAL_EVENTS, 0, 0);
187
188 /* Move to the ESTABLISHED state. */
189 socket_ptr -> nx_tcp_socket_state = NX_TCP_ESTABLISHED;
190
191 /* Clear the socket timeout. */
192 socket_ptr -> nx_tcp_socket_timeout = 0;
193
194 #ifdef NX_ENABLE_TCP_KEEPALIVE
195 /* Is the keepalive feature enabled on this socket? */
196 if (socket_ptr -> nx_tcp_socket_keepalive_enabled)
197 {
198 /* Setup the TCP Keepalive timer to initial values. */
199 socket_ptr -> nx_tcp_socket_keepalive_timeout = NX_TCP_KEEPALIVE_INITIAL;
200 socket_ptr -> nx_tcp_socket_keepalive_retries = 0;
201 }
202 #endif
203
204 #ifdef NX_ENABLE_HTTP_PROXY
205
206 /* Check if the HTTP Proxy is started and waiting for TCP socket connection. */
207 if ((socket_ptr -> nx_tcp_socket_ip_ptr -> nx_ip_http_proxy_enable) &&
208 (socket_ptr -> nx_tcp_socket_http_proxy_state == NX_HTTP_PROXY_STATE_WAITING))
209 {
210
211 /* TCP connection established, start the HTTP Proxy connection. */
212 _nx_http_proxy_client_connect(socket_ptr);
213 }
214 else
215 #endif /* NX_ENABLE_HTTP_PROXY */
216 {
217 #ifndef NX_DISABLE_EXTENDED_NOTIFY_SUPPORT
218
219 /* Is a connection completion callback registered with the TCP socket? */
220 if (socket_ptr -> nx_tcp_establish_notify)
221 {
222
223 /* Call the application's establish callback function. */
224 (socket_ptr -> nx_tcp_establish_notify)(socket_ptr);
225 }
226 #endif
227
228 /* Determine if we need to wake a thread suspended on the connection. */
229 if (socket_ptr -> nx_tcp_socket_connect_suspended_thread)
230 {
231
232 /* Resume the suspended thread. */
233 _nx_tcp_socket_thread_resume(&(socket_ptr -> nx_tcp_socket_connect_suspended_thread), NX_SUCCESS);
234 }
235 }
236 }
237 else if ((tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_SYN_BIT) &&
238 (!(tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_ACK_BIT)))
239 {
240
241 /* Simultaneous Connection Synchronization,
242 A SYN message was received. We need to send both a SYN and ACK and move to the SYN RECEIVED state. */
243
244 /* Save the sequence number. */
245 socket_ptr -> nx_tcp_socket_rx_sequence = tcp_header_ptr -> nx_tcp_sequence_number + 1;
246
247 /* Save the window size. */
248 socket_ptr -> nx_tcp_socket_tx_window_advertised = tcp_header_ptr -> nx_tcp_header_word_3 & NX_LOWER_16_MASK;
249
250 #ifdef NX_ENABLE_TCP_WINDOW_SCALING
251 socket_ptr -> nx_tcp_socket_tx_window_advertised <<= socket_ptr -> nx_tcp_rcv_win_scale_value;
252 #endif /* NX_ENABLE_TCP_WINDOW_SCALING */
253
254 /* Initialize the slow start threshold to be the advertised window size. */
255 socket_ptr -> nx_tcp_socket_tx_slow_start_threshold = socket_ptr -> nx_tcp_socket_tx_window_advertised;
256
257 /* Set the initial congestion control window size. */
258 /* Section 3.1, Page 5, RFC5681. */
259 socket_ptr -> nx_tcp_socket_tx_window_congestion = (socket_ptr -> nx_tcp_socket_connect_mss << 2);
260 if (socket_ptr -> nx_tcp_socket_connect_mss > 1095)
261 {
262 socket_ptr -> nx_tcp_socket_tx_window_congestion -= socket_ptr -> nx_tcp_socket_connect_mss;
263 }
264 if (socket_ptr -> nx_tcp_socket_connect_mss > 2190)
265 {
266 socket_ptr -> nx_tcp_socket_tx_window_congestion -= socket_ptr -> nx_tcp_socket_connect_mss;
267 }
268
269 /* Set the Initial transmit outstanding byte count. */
270 socket_ptr -> nx_tcp_socket_tx_outstanding_bytes = 0;
271
272 /* If trace is enabled, insert this event into the trace buffer. */
273 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_STATE_CHANGE, socket_ptr -> nx_tcp_socket_ip_ptr, socket_ptr, socket_ptr -> nx_tcp_socket_state, NX_TCP_SYN_RECEIVED, NX_TRACE_INTERNAL_EVENTS, 0, 0);
274
275 /* Move to the SYN RECEIVED state. */
276 socket_ptr -> nx_tcp_socket_state = NX_TCP_SYN_RECEIVED;
277
278 /* Clear the timeout. */
279 socket_ptr -> nx_tcp_socket_timeout = 0;
280
281 /* Send the SYN packet. */
282 _nx_tcp_packet_send_syn(socket_ptr, (socket_ptr -> nx_tcp_socket_tx_sequence - 1));
283 }
284 /* Check for an invalid response to an attempted connection. */
285 else if ((tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_ACK_BIT) &&
286 (tcp_header_ptr -> nx_tcp_acknowledgment_number != socket_ptr -> nx_tcp_socket_tx_sequence))
287 {
288
289 /* Invalid response was received, it is likely that the other side still
290 thinks a previous connection is active. Send a reset (RST) message to
291 the other side to clear any previous connection. */
292
293 /* Send the RST packet. */
294 _nx_tcp_packet_send_rst(socket_ptr, tcp_header_ptr);
295 }
296 }
297
298