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 #include "nx_tcp.h"
31 #include "tx_thread.h"
32 #include "nx_ipv6.h"
33
34 /**************************************************************************/
35 /* */
36 /* FUNCTION RELEASE */
37 /* */
38 /* _nx_tcp_socket_disconnect PORTABLE C */
39 /* 6.1.8 */
40 /* AUTHOR */
41 /* */
42 /* Yuxin Zhou, Microsoft Corporation */
43 /* */
44 /* DESCRIPTION */
45 /* */
46 /* This function handles the disconnect request for both active and */
47 /* passive calls. */
48 /* */
49 /* INPUT */
50 /* */
51 /* socket_ptr Pointer to TCP client socket */
52 /* wait_option Suspension option */
53 /* */
54 /* OUTPUT */
55 /* */
56 /* status Completion status */
57 /* */
58 /* CALLS */
59 /* */
60 /* _nx_tcp_connect_cleanup Clear connect suspension */
61 /* _nx_tcp_disconnect_cleanup Clear disconnect suspension */
62 /* _nx_tcp_packet_send_fin Send FIN message */
63 /* _nx_tcp_packet_send_rst Send RST on no timeout */
64 /* _nx_tcp_receive_cleanup Clear receive suspension */
65 /* _nx_tcp_transmit_cleanup Clear transmit suspension */
66 /* _nx_tcp_socket_thread_suspend Suspend calling thread */
67 /* _nx_tcp_socket_transmit_queue_flush Release all transmit packets */
68 /* _nx_tcp_socket_block_cleanup Cleanup the socket block */
69 /* tx_mutex_get Get protection */
70 /* tx_mutex_put Release protection */
71 /* */
72 /* CALLED BY */
73 /* */
74 /* Application Code */
75 /* */
76 /* RELEASE HISTORY */
77 /* */
78 /* DATE NAME DESCRIPTION */
79 /* */
80 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
81 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
82 /* resulting in version 6.1 */
83 /* 08-02-2021 Yuxin Zhou Modified comment(s), and */
84 /* supported TCP/IP offload, */
85 /* resulting in version 6.1.8 */
86 /* */
87 /**************************************************************************/
_nx_tcp_socket_disconnect(NX_TCP_SOCKET * socket_ptr,ULONG wait_option)88 UINT _nx_tcp_socket_disconnect(NX_TCP_SOCKET *socket_ptr, ULONG wait_option)
89 {
90
91 #ifndef NX_DISABLE_RESET_DISCONNECT
92 NX_TCP_HEADER tcp_header;
93 #endif
94 #ifdef NX_ENABLE_TCPIP_OFFLOAD
95 NX_INTERFACE *interface_ptr;
96 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
97 UINT status;
98 NX_IP *ip_ptr;
99
100 /* Setup IP pointer. */
101 ip_ptr = socket_ptr -> nx_tcp_socket_ip_ptr;
102
103 /* If trace is enabled, insert this event into the trace buffer. */
104 NX_TRACE_IN_LINE_INSERT(NX_TRACE_TCP_SOCKET_DISCONNECT, ip_ptr, socket_ptr, wait_option, socket_ptr -> nx_tcp_socket_state, NX_TRACE_TCP_EVENTS, 0, 0);
105
106 /* Default status to success. */
107 status = NX_SUCCESS;
108
109 /* Obtain the IP mutex so we can access socket and IP information. */
110 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
111
112 #ifndef NX_DISABLE_TCP_INFO
113 /* Increment the TCP disconnections count. */
114 ip_ptr -> nx_ip_tcp_disconnections++;
115 #endif
116
117 /* Determine if the socket is in a state not valid for a disconnect. */
118 if ((socket_ptr -> nx_tcp_socket_state != NX_TCP_ESTABLISHED) &&
119 (socket_ptr -> nx_tcp_socket_state != NX_TCP_SYN_SENT) &&
120 (socket_ptr -> nx_tcp_socket_state != NX_TCP_SYN_RECEIVED) &&
121 (socket_ptr -> nx_tcp_socket_state != NX_TCP_CLOSE_WAIT))
122 {
123
124 /* Release protection. */
125 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
126
127 /* Return a not connected error code. */
128 return(NX_NOT_CONNECTED);
129 }
130
131 #ifdef NX_ENABLE_TCP_KEEPALIVE
132 /* Clear the TCP Keepalive timer to disable it for this socket (only needed when
133 the socket is connected. */
134 socket_ptr -> nx_tcp_socket_keepalive_timeout = 0;
135 socket_ptr -> nx_tcp_socket_keepalive_retries = 0;
136 #endif
137
138 #ifdef NX_ENABLE_TCPIP_OFFLOAD
139 interface_ptr = socket_ptr -> nx_tcp_socket_connect_interface;
140 if (interface_ptr &&
141 (interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCPIP_OFFLOAD) &&
142 (interface_ptr -> nx_interface_tcpip_offload_handler))
143 {
144
145 /* Let TCP/IP offload interface close the connection. */
146 interface_ptr -> nx_interface_tcpip_offload_handler(ip_ptr, interface_ptr, socket_ptr,
147 NX_TCPIP_OFFLOAD_TCP_SOCKET_DISCONNECT,
148 NX_NULL, NX_NULL, NX_NULL, 0, NX_NULL,
149 wait_option);
150
151 /* Reset socket state. */
152 socket_ptr -> nx_tcp_socket_state = NX_TCP_CLOSED;
153 }
154 else
155 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
156
157 /* Determine if the connection wasn't fully completed. */
158 if ((socket_ptr -> nx_tcp_socket_state == NX_TCP_SYN_SENT) ||
159 (socket_ptr -> nx_tcp_socket_state == NX_TCP_SYN_RECEIVED))
160 {
161
162 /* Connection wasn't fully completed, reset to the proper socket state. */
163 if (socket_ptr -> nx_tcp_socket_client_type)
164 {
165
166 /* If trace is enabled, insert this event into the trace buffer. */
167 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_STATE_CHANGE, ip_ptr, socket_ptr, socket_ptr -> nx_tcp_socket_state, NX_TCP_CLOSED, NX_TRACE_INTERNAL_EVENTS, 0, 0);
168
169 if (socket_ptr -> nx_tcp_socket_state == NX_TCP_SYN_RECEIVED)
170 {
171
172 /* Setup FIN timeout. */
173 socket_ptr -> nx_tcp_socket_timeout = socket_ptr -> nx_tcp_socket_timeout_rate;
174 socket_ptr -> nx_tcp_socket_timeout_retries = 0;
175
176 /* Increment the sequence number. */
177 socket_ptr -> nx_tcp_socket_tx_sequence++;
178
179 /* Send FIN packet. */
180 _nx_tcp_packet_send_fin(socket_ptr, (socket_ptr -> nx_tcp_socket_tx_sequence - 1));
181 }
182
183 /* Client socket, return to a CLOSED state. */
184 socket_ptr -> nx_tcp_socket_state = NX_TCP_CLOSED;
185 }
186 else
187 {
188
189 /* If trace is enabled, insert this event into the trace buffer. */
190 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_STATE_CHANGE, ip_ptr, socket_ptr, socket_ptr -> nx_tcp_socket_state, NX_TCP_LISTEN_STATE, NX_TRACE_INTERNAL_EVENTS, 0, 0);
191
192 if ((socket_ptr -> nx_tcp_socket_state == NX_TCP_SYN_RECEIVED) &&
193 (socket_ptr -> nx_tcp_socket_connect_interface != NX_NULL))
194 {
195
196 /* Setup FIN timeout. */
197 socket_ptr -> nx_tcp_socket_timeout = socket_ptr -> nx_tcp_socket_timeout_rate;
198 socket_ptr -> nx_tcp_socket_timeout_retries = 0;
199
200 /* Increment the sequence number. */
201 socket_ptr -> nx_tcp_socket_tx_sequence++;
202
203 /* Send FIN packet. */
204 _nx_tcp_packet_send_fin(socket_ptr, (socket_ptr -> nx_tcp_socket_tx_sequence - 1));
205 }
206
207 /* Server socket, return to LISTEN state. */
208 socket_ptr -> nx_tcp_socket_state = NX_TCP_LISTEN_STATE;
209
210 /* Move back the acknowledgment number just in case there is a retry. */
211 socket_ptr -> nx_tcp_socket_rx_sequence--;
212 }
213
214 /* Socket is no longer active. Clear the timeout. */
215 socket_ptr -> nx_tcp_socket_timeout = 0;
216 }
217
218 #ifndef NX_DISABLE_RESET_DISCONNECT
219
220 /* Determine if there is no timeout associated with the disconnect. If this is the case,
221 we will send a RST and simply enter a closed state. */
222 else if (wait_option == NX_NO_WAIT)
223 {
224
225 /* No timeout was specified, simply send a RST and enter a closed or listen state. */
226
227 /* Clear this field so the RST packet handler knows this is a fake header. */
228 tcp_header.nx_tcp_header_word_3 = NX_TCP_ACK_BIT;
229
230 /* Send the RST packet. We just want to create a fake header, so assume this packet is incoming packet. */
231 tcp_header.nx_tcp_acknowledgment_number = socket_ptr -> nx_tcp_socket_tx_sequence;
232 tcp_header.nx_tcp_sequence_number = socket_ptr -> nx_tcp_socket_rx_sequence;
233 _nx_tcp_packet_send_rst(socket_ptr, &tcp_header);
234
235 /* Cleanup the transmission control block. */
236 _nx_tcp_socket_block_cleanup(socket_ptr);
237
238 /* No suspension is requested, just set the return status to in progress. */
239 status = NX_IN_PROGRESS;
240 }
241 #endif
242
243 /* Determine if this is an active disconnect, i.e. initiated by the application rather
244 than in response to a disconnect from the connected socket. */
245 else if (socket_ptr -> nx_tcp_socket_state != NX_TCP_CLOSE_WAIT)
246 {
247
248 /* Yes, this disconnect was initiated by the application. Initiate the disconnect
249 process. */
250
251 /* If trace is enabled, insert this event into the trace buffer. */
252 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_STATE_CHANGE, ip_ptr, socket_ptr, socket_ptr -> nx_tcp_socket_state, NX_TCP_FIN_WAIT_1, NX_TRACE_INTERNAL_EVENTS, 0, 0);
253
254 /* Move the TCP state to FIN WAIT 1 state, the first state of an active close. */
255 socket_ptr -> nx_tcp_socket_state = NX_TCP_FIN_WAIT_1;
256
257 /* Determine if the transmit queue is empty. Only setup a FIN timeout here when
258 there are no more transmit packets waiting to be ACKed. If there are transmit
259 packets still waiting, the FIN timeout will be setup when the transmit queue is completely
260 acknowledged. */
261 if (socket_ptr -> nx_tcp_socket_transmit_sent_head == NX_NULL)
262 {
263
264 /* No transmit packets queue, setup FIN timeout. */
265 socket_ptr -> nx_tcp_socket_timeout = socket_ptr -> nx_tcp_socket_timeout_rate;
266 socket_ptr -> nx_tcp_socket_timeout_retries = 0;
267 }
268
269 /* Increment the sequence number. */
270 socket_ptr -> nx_tcp_socket_tx_sequence++;
271
272 /* Send FIN packet. */
273 _nx_tcp_packet_send_fin(socket_ptr, (socket_ptr -> nx_tcp_socket_tx_sequence - 1));
274 }
275 else
276 {
277
278 /* This disconnect request is coming after the other side of the TCP connection
279 initiated a disconnect. */
280
281 /* If trace is enabled, insert this event into the trace buffer. */
282 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_STATE_CHANGE, ip_ptr, socket_ptr, socket_ptr -> nx_tcp_socket_state, NX_TCP_LAST_ACK, NX_TRACE_INTERNAL_EVENTS, 0, 0);
283
284 /* Move the TCP state to wait for the last ACK message for the complete disconnect. */
285 socket_ptr -> nx_tcp_socket_state = NX_TCP_LAST_ACK;
286
287 /* Send a FIN message back to the side of the connection that initiated the
288 disconnect. */
289
290 /* Determine if the transmit queue is empty. Only setup a FIN timeout here when
291 there are no more transmit packets waiting to be ACKed. If there are transmit
292 packets still waiting, the FIN timeout will be setup when the transmit queue is completely
293 acknowledged. */
294 if (socket_ptr -> nx_tcp_socket_transmit_sent_head == NX_NULL)
295 {
296
297 /* No transmit packets queue, setup FIN timeout. */
298 socket_ptr -> nx_tcp_socket_timeout = socket_ptr -> nx_tcp_socket_timeout_rate;
299 socket_ptr -> nx_tcp_socket_timeout_retries = 0;
300 }
301
302 /* Increment the sequence number. */
303 socket_ptr -> nx_tcp_socket_tx_sequence++;
304
305 /* Send FIN packet. */
306 _nx_tcp_packet_send_fin(socket_ptr, (socket_ptr -> nx_tcp_socket_tx_sequence - 1));
307 }
308
309 /* Optionally suspend the thread. If timeout occurs, return a disconnect timeout status. If
310 immediate response is selected, return a disconnect in progress status. Only on a real
311 disconnect should success be returned. */
312 if ((socket_ptr -> nx_tcp_socket_state != NX_TCP_CLOSED) && (socket_ptr -> nx_tcp_socket_state != NX_TCP_LISTEN_STATE) &&
313 #ifdef NX_DISABLE_RESET_DISCONNECT
314 (wait_option) &&
315 #endif
316 (_tx_thread_current_ptr != &(ip_ptr -> nx_ip_thread)))
317 {
318
319 /* Suspend the thread on socket disconnect. */
320 _nx_tcp_socket_thread_suspend(&(socket_ptr -> nx_tcp_socket_disconnect_suspended_thread), _nx_tcp_disconnect_cleanup, socket_ptr, &(ip_ptr -> nx_ip_protection), wait_option);
321
322 /* Reobtain the IP mutex. */
323 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
324
325 /* Determine if the socket is in the timed wait state. */
326 if (socket_ptr -> nx_tcp_socket_state != NX_TCP_TIMED_WAIT)
327 {
328
329 /* Cleanup the transmission control block. */
330 _nx_tcp_socket_block_cleanup(socket_ptr);
331 }
332
333 /* Use the thread return the completion code. */
334 status = _tx_thread_current_ptr -> tx_thread_suspend_status;
335 }
336
337 /* We now need to check for any remaining sent packets in the transmit queue.
338 If found they need to be released. */
339 if (socket_ptr -> nx_tcp_socket_transmit_sent_head)
340 {
341
342 /* Release all transmit packets. */
343 _nx_tcp_socket_transmit_queue_flush(socket_ptr);
344 }
345
346 /* Clear any connection suspension on this socket. */
347 if (socket_ptr -> nx_tcp_socket_connect_suspended_thread)
348 {
349
350 /* Call the connect thread suspension cleanup routine. */
351 _nx_tcp_connect_cleanup(socket_ptr -> nx_tcp_socket_connect_suspended_thread NX_CLEANUP_ARGUMENT);
352 }
353
354 /* Clear all receive thread suspensions on this socket. */
355 while (socket_ptr -> nx_tcp_socket_receive_suspension_list)
356 {
357
358 /* Call the receive thread suspension cleanup routine. */
359 _nx_tcp_receive_cleanup(socket_ptr -> nx_tcp_socket_receive_suspension_list NX_CLEANUP_ARGUMENT);
360 }
361
362 /* Clear all transmit thread suspensions on this socket. */
363 while (socket_ptr -> nx_tcp_socket_transmit_suspension_list)
364 {
365
366 /* Call the receive thread suspension cleanup routine. */
367 _nx_tcp_transmit_cleanup(socket_ptr -> nx_tcp_socket_transmit_suspension_list NX_CLEANUP_ARGUMENT);
368 }
369
370 /* Release the IP protection. */
371 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
372
373 /* Return in-progress completion status. */
374 return(status);
375 }
376
377