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 /**   User Datagram Protocol (UDP)                                        */
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_udp.h"
29 #include "nx_packet.h"
30 #include "tx_thread.h"
31 
32 
33 #ifdef NX_ENABLE_TCPIP_OFFLOAD
34 /**************************************************************************/
35 /*                                                                        */
36 /*  FUNCTION                                               RELEASE        */
37 /*                                                                        */
38 /*    _nx_udp_socket_driver_unbind                        PORTABLE C      */
39 /*                                                           6.1.8        */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    Yuxin Zhou, Microsoft Corporation                                   */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This function unbinds a UDP port through TCP/IP offload interface.  */
47 /*                                                                        */
48 /*  INPUT                                                                 */
49 /*                                                                        */
50 /*    socket_ptr                            Pointer to UDP socket         */
51 /*                                                                        */
52 /*  OUTPUT                                                                */
53 /*                                                                        */
54 /*    status                                Completion status             */
55 /*                                                                        */
56 /*  CALLS                                                                 */
57 /*                                                                        */
58 /*    None                                                                */
59 /*                                                                        */
60 /*  CALLED BY                                                             */
61 /*                                                                        */
62 /*    _nx_udp_socket_bind                                                 */
63 /*                                                                        */
64 /*  RELEASE HISTORY                                                       */
65 /*                                                                        */
66 /*    DATE              NAME                      DESCRIPTION             */
67 /*                                                                        */
68 /*  08-02-2021     Yuxin Zhou               Initial Version 6.1.8         */
69 /*                                                                        */
70 /**************************************************************************/
_nx_udp_socket_driver_unbind(NX_UDP_SOCKET * socket_ptr)71 static UINT _nx_udp_socket_driver_unbind(NX_UDP_SOCKET *socket_ptr)
72 {
73 UINT          i;
74 NX_INTERFACE *interface_ptr;
75 NX_IP        *ip_ptr;
76 
77 
78     /* Setup the pointer to the associated IP instance.  */
79     ip_ptr =  socket_ptr -> nx_udp_socket_ip_ptr;
80 
81     /* Loop all interfaces to unbind to ones support TCP/IP offload.  */
82     for (i = 0; i < NX_MAX_IP_INTERFACES; i++)
83     {
84 
85         /* Use a local variable for convenience.  */
86         interface_ptr = &(ip_ptr -> nx_ip_interface[i]);
87 
88         /* Check for valid interfaces.  */
89         if (interface_ptr -> nx_interface_valid == NX_FALSE)
90         {
91 
92             /* Skip interface not valid.  */
93             continue;
94         }
95 
96         /* Check for TCP/IP offload feature.  */
97         if (((interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCPIP_OFFLOAD) == 0) ||
98             (interface_ptr -> nx_interface_tcpip_offload_handler == NX_NULL))
99         {
100 
101             /* Skip interface not support TCP/IP offload.  */
102             continue;
103         }
104 
105         /* Let TCP/IP offload interface unbind port.  Return value is ignored.  */
106         interface_ptr -> nx_interface_tcpip_offload_handler(ip_ptr, interface_ptr,
107                                                             socket_ptr,
108                                                             NX_TCPIP_OFFLOAD_UDP_SOCKET_UNBIND,
109                                                             NX_NULL, NX_NULL, NX_NULL,
110                                                             socket_ptr -> nx_udp_socket_port,
111                                                             NX_NULL, NX_NO_WAIT);
112     }
113 
114     return(NX_SUCCESS);
115 }
116 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
117 
118 
119 /**************************************************************************/
120 /*                                                                        */
121 /*  FUNCTION                                               RELEASE        */
122 /*                                                                        */
123 /*    _nx_udp_socket_unbind                               PORTABLE C      */
124 /*                                                           6.1.8        */
125 /*  AUTHOR                                                                */
126 /*                                                                        */
127 /*    Yuxin Zhou, Microsoft Corporation                                   */
128 /*                                                                        */
129 /*  DESCRIPTION                                                           */
130 /*                                                                        */
131 /*    This function unbinds the UDP socket structure from the previously  */
132 /*    bound UDP port.                                                     */
133 /*                                                                        */
134 /*  INPUT                                                                 */
135 /*                                                                        */
136 /*    socket_ptr                            Pointer to UDP socket         */
137 /*                                                                        */
138 /*  OUTPUT                                                                */
139 /*                                                                        */
140 /*    status                                Completion status             */
141 /*                                                                        */
142 /*  CALLS                                                                 */
143 /*                                                                        */
144 /*    _nx_packet_release                    Release data packet           */
145 /*    _nx_udp_bind_cleanup                  Remove and cleanup bind req   */
146 /*    tx_mutex_get                          Obtain protection mutex       */
147 /*    tx_mutex_put                          Release protection mutex      */
148 /*    _tx_thread_system_resume              Resume suspended thread       */
149 /*    _tx_thread_system_preempt_check       Check for preemption          */
150 /*                                                                        */
151 /*  CALLED BY                                                             */
152 /*                                                                        */
153 /*    Application Code                                                    */
154 /*                                                                        */
155 /*  RELEASE HISTORY                                                       */
156 /*                                                                        */
157 /*    DATE              NAME                      DESCRIPTION             */
158 /*                                                                        */
159 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
160 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
161 /*                                            resulting in version 6.1    */
162 /*  08-02-2021     Yuxin Zhou               Modified comment(s), and      */
163 /*                                            supported TCP/IP offload,   */
164 /*                                            resulting in version 6.1.8  */
165 /*                                                                        */
166 /**************************************************************************/
_nx_udp_socket_unbind(NX_UDP_SOCKET * socket_ptr)167 UINT  _nx_udp_socket_unbind(NX_UDP_SOCKET *socket_ptr)
168 {
169 TX_INTERRUPT_SAVE_AREA
170 
171 UINT           index;
172 UINT           port;
173 NX_IP         *ip_ptr;
174 TX_THREAD     *thread_ptr;
175 NX_UDP_SOCKET *new_socket_ptr;
176 NX_PACKET     *packet_ptr;
177 NX_PACKET     *next_packet_ptr;
178 
179 
180     /* Setup the pointer to the associated IP instance.  */
181     ip_ptr =  socket_ptr -> nx_udp_socket_ip_ptr;
182 
183     /* If trace is enabled, insert this event into the trace buffer.  */
184     NX_TRACE_IN_LINE_INSERT(NX_TRACE_UDP_SOCKET_UNBIND, ip_ptr, socket_ptr, socket_ptr -> nx_udp_socket_port, 0, NX_TRACE_UDP_EVENTS, 0, 0);
185 
186     /* Obtain the IP mutex so we can figure out whether or not the port has already
187        been bound to.  */
188     tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
189 
190     /* Determine if the socket is bound to port.  */
191     if (!socket_ptr -> nx_udp_socket_bound_next)
192     {
193 
194         /* Determine if there is a special condition for the socket not being in
195            a bound condition...  i.e. the socket is in a pending-to-be-bound condition
196            in a call from a different thread.  */
197         if (socket_ptr -> nx_udp_socket_bind_in_progress)
198         {
199 
200             /* Execute the bind suspension cleanup routine.  */
201             _nx_udp_bind_cleanup(socket_ptr -> nx_udp_socket_bind_in_progress NX_CLEANUP_ARGUMENT);
202 
203             /* Release the protection mutex.  */
204             tx_mutex_put(&(ip_ptr -> nx_ip_protection));
205 
206             /* Return success.  */
207             return(NX_SUCCESS);
208         }
209         else
210         {
211 
212             /* Release the protection mutex.  */
213             tx_mutex_put(&(ip_ptr -> nx_ip_protection));
214 
215             /* Return a not bound error code.  */
216             return(NX_NOT_BOUND);
217         }
218     }
219 
220     /* Otherwise, the socket is bound.  We need to remove this socket from the
221        port and check for any other UDP socket bind requests that are queued.  */
222 
223     /* Pickup the port number in the UDP socket structure.  */
224     port =  socket_ptr -> nx_udp_socket_port;
225 
226     /* Calculate the hash index in the UDP port array of the associated IP instance.  */
227     index =  (UINT)((port + (port >> 8)) & NX_UDP_PORT_TABLE_MASK);
228 
229 #ifdef NX_ENABLE_TCPIP_OFFLOAD
230     _nx_udp_socket_driver_unbind(socket_ptr);
231 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
232 
233     /* Disable interrupts while we unlink the current socket.  */
234     TX_DISABLE
235 
236     /* Determine if this is the only socket bound on this port list.  */
237     if (socket_ptr -> nx_udp_socket_bound_next == socket_ptr)
238     {
239 
240         /* Yes, this is the only socket on the port list.  */
241 
242         /* Clear the list head pointer and the next pointer in the socket.  */
243         ip_ptr -> nx_ip_udp_port_table[index] =   NX_NULL;
244         socket_ptr -> nx_udp_socket_bound_next =  NX_NULL;
245     }
246     else
247     {
248 
249         /* Relink the neighbors of this UDP socket.  */
250 
251         /* Update the links of the adjacent sockets.  */
252         (socket_ptr -> nx_udp_socket_bound_next) -> nx_udp_socket_bound_previous =
253             socket_ptr -> nx_udp_socket_bound_previous;
254         (socket_ptr -> nx_udp_socket_bound_previous) -> nx_udp_socket_bound_next =
255             socket_ptr -> nx_udp_socket_bound_next;
256 
257         /* Determine if the head of the port list points to the socket being removed.
258            If so, we need to move the head pointer.  */
259         if (ip_ptr -> nx_ip_udp_port_table[index] == socket_ptr)
260         {
261 
262             /* Yes, we need to move the port list head pointer.  */
263             ip_ptr -> nx_ip_udp_port_table[index] =  socket_ptr -> nx_udp_socket_bound_next;
264         }
265 
266         /* Clear the next pointer in the socket to indicate it is no longer bound.  */
267         socket_ptr -> nx_udp_socket_bound_next =  NX_NULL;
268     }
269 
270     /* Restore interrupts.  */
271     TX_RESTORE
272 
273     /* The socket is off the bound list...  we need to check for queued packets and possible
274        receive suspension.  We need to clean up either of these conditions.  */
275     if (socket_ptr -> nx_udp_socket_receive_count)
276     {
277 
278         /* Setup packet pointer.  */
279         packet_ptr =  socket_ptr -> nx_udp_socket_receive_head;
280 
281         /* Clear the head and the tail pointers.  */
282         socket_ptr -> nx_udp_socket_receive_head =  NX_NULL;
283         socket_ptr -> nx_udp_socket_receive_tail =  NX_NULL;
284 
285         /* Loop to clear all the packets out.  */
286         while (socket_ptr -> nx_udp_socket_receive_count)
287         {
288 
289             /* Pickup the next queued packet.  */
290             next_packet_ptr =  packet_ptr -> nx_packet_queue_next;
291 
292             /* Release the packet.  */
293             _nx_packet_release(packet_ptr);
294 
295             /* Move to the next packet.  */
296             packet_ptr =  next_packet_ptr;
297 
298             /* Decrease the queued packet count.  */
299             socket_ptr -> nx_udp_socket_receive_count--;
300         }
301     }
302     else if (socket_ptr -> nx_udp_socket_receive_suspended_count)
303     {
304 
305         /* Clear out all threads suspended on this socket.  */
306 
307         /* Pickup the first suspended thread.  */
308         thread_ptr =  socket_ptr -> nx_udp_socket_receive_suspension_list;
309 
310         /* Clear the thread receive suspension list.  */
311         socket_ptr -> nx_udp_socket_receive_suspension_list =  NX_NULL;
312 
313         /* Walk through the queue list to resume any and all threads suspended
314            on this queue.  */
315         while (socket_ptr -> nx_udp_socket_receive_suspended_count)
316         {
317 
318             /* Lockout interrupts.  */
319             TX_DISABLE
320 
321             /* Clear the cleanup pointer, this prevents the timeout from doing
322                anything.  */
323             thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
324 
325             /* Temporarily disable preemption again.  */
326             _tx_thread_preempt_disable++;
327 
328             /* Restore interrupts.  */
329             TX_RESTORE
330 
331             /* Set the return status in the thread to NX_SOCKET_UNBOUND.  */
332             thread_ptr -> tx_thread_suspend_status =  NX_SOCKET_UNBOUND;
333 
334             /* Move the thread pointer ahead.  */
335             thread_ptr =  thread_ptr -> tx_thread_suspended_next;
336 
337             /* Resume the thread.  */
338             _tx_thread_system_resume(thread_ptr -> tx_thread_suspended_previous);
339 
340             /* Decrease the suspended count.  */
341             socket_ptr -> nx_udp_socket_receive_suspended_count--;
342         }
343     }
344 
345     /* Disable interrupts again.  */
346     TX_DISABLE
347 
348     /* Determine if there are any threads suspended on trying to bind to the
349        same port.  */
350     thread_ptr =  socket_ptr -> nx_udp_socket_bind_suspension_list;
351     if (thread_ptr)
352     {
353 
354         /* Remove the suspended thread from the list.  */
355 
356         /* See if this is the only suspended thread on the list.  */
357         if (thread_ptr == thread_ptr -> tx_thread_suspended_next)
358         {
359 
360             /* Yes, the only suspended thread.  */
361 
362             /* Update the head pointer.  */
363             socket_ptr -> nx_udp_socket_bind_suspension_list =  NX_NULL;
364         }
365         else
366         {
367 
368             /* At least one more thread is on the same expiration list.  */
369 
370             /* Update the list head pointer.  */
371             socket_ptr -> nx_udp_socket_bind_suspension_list =  thread_ptr -> tx_thread_suspended_next;
372 
373             /* Update the links of the adjacent threads.  */
374             (thread_ptr -> tx_thread_suspended_next) -> tx_thread_suspended_previous =
375                 thread_ptr -> tx_thread_suspended_previous;
376             (thread_ptr -> tx_thread_suspended_previous) -> tx_thread_suspended_next =
377                 thread_ptr -> tx_thread_suspended_next;
378         }
379 
380         /* Decrement the suspension count.  */
381         socket_ptr -> nx_udp_socket_bind_suspended_count--;
382 
383         /* Pickup the new socket structure to link to the port list.  */
384         new_socket_ptr =  (NX_UDP_SOCKET *)thread_ptr -> tx_thread_suspend_control_block;
385 
386         /* Clear the new socket's bind in progress flag.  */
387         new_socket_ptr -> nx_udp_socket_bind_in_progress =  NX_NULL;
388 
389         /* Inherit the suspension list from the previously bound socket.  */
390         new_socket_ptr -> nx_udp_socket_bind_suspension_list =
391             socket_ptr -> nx_udp_socket_bind_suspension_list;
392         socket_ptr -> nx_udp_socket_bind_suspension_list =  NX_NULL;
393 
394         /* Link the new socket to the bound list.  */
395         if (ip_ptr -> nx_ip_udp_port_table[index])
396         {
397 
398             /* There are already sockets on this list... just add this one
399                to the end.  */
400             new_socket_ptr -> nx_udp_socket_bound_next =
401                 ip_ptr -> nx_ip_udp_port_table[index];
402             new_socket_ptr -> nx_udp_socket_bound_previous =
403                 (ip_ptr -> nx_ip_udp_port_table[index]) -> nx_udp_socket_bound_previous;
404             ((ip_ptr -> nx_ip_udp_port_table[index]) -> nx_udp_socket_bound_previous) -> nx_udp_socket_bound_next =
405                 new_socket_ptr;
406             (ip_ptr -> nx_ip_udp_port_table[index]) -> nx_udp_socket_bound_previous =   new_socket_ptr;
407         }
408         else
409         {
410 
411             /* Nothing is on the UDP port list.  Add this UDP socket to an
412                empty list.  */
413             new_socket_ptr -> nx_udp_socket_bound_next =      new_socket_ptr;
414             new_socket_ptr -> nx_udp_socket_bound_previous =  new_socket_ptr;
415             ip_ptr -> nx_ip_udp_port_table[index] =           new_socket_ptr;
416         }
417 
418         /* Prepare for resumption of the first thread.  */
419 
420         /* Clear cleanup routine to avoid timeout.  */
421         thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
422 
423         /* Temporarily disable preemption.  */
424         _tx_thread_preempt_disable++;
425 
426         /* Restore interrupts.  */
427         TX_RESTORE
428 
429         /* Put return status into the thread control block.  */
430         thread_ptr -> tx_thread_suspend_status =  NX_SUCCESS;
431 
432         /* Release the mutex protection.  */
433         tx_mutex_put(&(ip_ptr -> nx_ip_protection));
434 
435         /* Resume thread.  */
436         _tx_thread_system_resume(thread_ptr);
437     }
438     else
439     {
440 
441         /* Restore interrupts.  */
442         TX_RESTORE
443 
444         /* Release the mutex protection.  */
445         tx_mutex_put(&(ip_ptr -> nx_ip_protection));
446 
447         /* Return success to the caller.  */
448         return(NX_SUCCESS);
449     }
450 
451     /* Check for preemption.  */
452     _tx_thread_system_preempt_check();
453 
454     /* Return success.  */
455     return(NX_SUCCESS);
456 }
457 
458