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