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