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 "tx_thread.h"
30 
31 
32 #ifdef NX_ENABLE_TCPIP_OFFLOAD
33 /**************************************************************************/
34 /*                                                                        */
35 /*  FUNCTION                                               RELEASE        */
36 /*                                                                        */
37 /*    _nx_udp_socket_driver_bind                          PORTABLE C      */
38 /*                                                           6.1.8        */
39 /*  AUTHOR                                                                */
40 /*                                                                        */
41 /*    Yuxin Zhou, Microsoft Corporation                                   */
42 /*                                                                        */
43 /*  DESCRIPTION                                                           */
44 /*                                                                        */
45 /*    This function binds to a UDP port through TCP/IP offload interface. */
46 /*                                                                        */
47 /*  INPUT                                                                 */
48 /*                                                                        */
49 /*    socket_ptr                            Pointer to UDP socket         */
50 /*    port                                  16-bit UDP port number        */
51 /*    wait_option                           Suspension option             */
52 /*                                                                        */
53 /*  OUTPUT                                                                */
54 /*                                                                        */
55 /*    status                                Completion status             */
56 /*                                                                        */
57 /*  CALLS                                                                 */
58 /*                                                                        */
59 /*    _nx_udp_socket_unbind                 Unbind UDP port               */
60 /*    tx_mutex_get                          Obtain protection mutex       */
61 /*    tx_mutex_put                          Release protection mutex      */
62 /*                                                                        */
63 /*  CALLED BY                                                             */
64 /*                                                                        */
65 /*    _nx_udp_socket_bind                                                 */
66 /*                                                                        */
67 /*  RELEASE HISTORY                                                       */
68 /*                                                                        */
69 /*    DATE              NAME                      DESCRIPTION             */
70 /*                                                                        */
71 /*  08-02-2021     Yuxin Zhou               Initial Version 6.1.8         */
72 /*                                                                        */
73 /**************************************************************************/
_nx_udp_socket_driver_bind(NX_UDP_SOCKET * socket_ptr,UINT port,ULONG wait_option)74 static UINT _nx_udp_socket_driver_bind(NX_UDP_SOCKET *socket_ptr, UINT port, ULONG wait_option)
75 {
76 UINT          status = NX_SUCCESS;
77 UINT          i;
78 NX_INTERFACE *interface_ptr;
79 NX_IP        *ip_ptr;
80 
81 
82     /* Setup the pointer to the associated IP instance.  */
83     ip_ptr =  socket_ptr -> nx_udp_socket_ip_ptr;
84 
85     /* Obtain the IP mutex.  */
86     tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
87 
88     /* Loop all interfaces to bind to ones support TCP/IP offload.  */
89     for (i = 0; i < NX_MAX_IP_INTERFACES; i++)
90     {
91 
92         /* Use a local variable for convenience.  */
93         interface_ptr = &(ip_ptr -> nx_ip_interface[i]);
94 
95         /* Check for valid interfaces.  */
96         if ((interface_ptr -> nx_interface_valid == NX_FALSE) ||
97             (interface_ptr -> nx_interface_link_up == NX_FALSE))
98         {
99 
100             /* Skip interface not valid.  */
101             continue;
102         }
103 
104         /* Check for TCP/IP offload feature.  */
105         if (((interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCPIP_OFFLOAD) == 0) ||
106             (interface_ptr -> nx_interface_tcpip_offload_handler == NX_NULL))
107         {
108 
109             /* Skip interface not support TCP/IP offload.  */
110             continue;
111         }
112 
113         /* Let TCP/IP offload interface bind to port.  */
114         status = interface_ptr -> nx_interface_tcpip_offload_handler(ip_ptr, interface_ptr,
115                                                                      socket_ptr,
116                                                                      NX_TCPIP_OFFLOAD_UDP_SOCKET_BIND,
117                                                                      NX_NULL, NX_NULL, NX_NULL, port,
118                                                                      NX_NULL, wait_option);
119         if (status)
120         {
121 
122             /* At least one of the interface fails to bind to port.  */
123             _nx_udp_socket_unbind(socket_ptr);
124             status = NX_TCPIP_OFFLOAD_ERROR;
125             break;
126         }
127     }
128 
129     /* Release the IP protection.  */
130     tx_mutex_put(&(ip_ptr -> nx_ip_protection));
131 
132     return(status);
133 }
134 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
135 
136 
137 /**************************************************************************/
138 /*                                                                        */
139 /*  FUNCTION                                               RELEASE        */
140 /*                                                                        */
141 /*    _nx_udp_socket_bind                                 PORTABLE C      */
142 /*                                                           6.1.11       */
143 /*  AUTHOR                                                                */
144 /*                                                                        */
145 /*    Yuxin Zhou, Microsoft Corporation                                   */
146 /*                                                                        */
147 /*  DESCRIPTION                                                           */
148 /*                                                                        */
149 /*    This function binds the UDP socket a specific UDP port.  If the     */
150 /*    requested port number is currently bound to another socket,         */
151 /*    this function waits for specified period of time (wait_option)      */
152 /*    for the other socket to unbind the port number.                     */
153 /*                                                                        */
154 /*  INPUT                                                                 */
155 /*                                                                        */
156 /*    socket_ptr                            Pointer to UDP socket         */
157 /*    port                                  16-bit UDP port number        */
158 /*    wait_option                           Suspension option             */
159 /*                                                                        */
160 /*  OUTPUT                                                                */
161 /*                                                                        */
162 /*    status                                Completion status             */
163 /*                                                                        */
164 /*  CALLS                                                                 */
165 /*                                                                        */
166 /*    _nx_udp_free_port_find                Find a free UDP port          */
167 /*    tx_mutex_get                          Obtain protection mutex       */
168 /*    tx_mutex_put                          Release protection mutex      */
169 /*    _tx_thread_system_suspend             Suspend thread                */
170 /*                                                                        */
171 /*  CALLED BY                                                             */
172 /*                                                                        */
173 /*    Application Code                                                    */
174 /*                                                                        */
175 /*  RELEASE HISTORY                                                       */
176 /*                                                                        */
177 /*    DATE              NAME                      DESCRIPTION             */
178 /*                                                                        */
179 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
180 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
181 /*                                            resulting in version 6.1    */
182 /*  08-02-2021     Yuxin Zhou               Modified comment(s), and      */
183 /*                                            supported TCP/IP offload,   */
184 /*                                            resulting in version 6.1.8  */
185 /*  04-25-2022     Yuxin Zhou               Modified comment(s), and      */
186 /*                                            corrected the random value, */
187 /*                                            resulting in version 6.1.11 */
188 /*                                                                        */
189 /**************************************************************************/
_nx_udp_socket_bind(NX_UDP_SOCKET * socket_ptr,UINT port,ULONG wait_option)190 UINT  _nx_udp_socket_bind(NX_UDP_SOCKET *socket_ptr, UINT  port, ULONG wait_option)
191 {
192 
193 TX_INTERRUPT_SAVE_AREA
194 UINT           index;
195 #ifdef NX_NAT_ENABLE
196 UINT           bound;
197 #endif /* NX_NAT_ENABLE */
198 NX_IP         *ip_ptr;
199 TX_THREAD     *thread_ptr;
200 NX_UDP_SOCKET *search_ptr;
201 NX_UDP_SOCKET *end_ptr;
202 UINT           status = NX_SUCCESS;
203 
204 
205     /* Setup the pointer to the associated IP instance.  */
206     ip_ptr =  socket_ptr -> nx_udp_socket_ip_ptr;
207 
208     /* If trace is enabled, insert this event into the trace buffer.  */
209     NX_TRACE_IN_LINE_INSERT(NX_TRACE_UDP_SOCKET_BIND, ip_ptr, socket_ptr, port, wait_option, NX_TRACE_UDP_EVENTS, 0, 0);
210 
211     /* Obtain the IP mutex so we can figure out whether or not the port has already
212        been bound to.  */
213     tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
214 
215     /* Determine if the socket has already been bound to port or if a socket bind is
216        already pending from another thread.  */
217     if ((socket_ptr -> nx_udp_socket_bound_next) ||
218         (socket_ptr -> nx_udp_socket_bind_in_progress))
219     {
220 
221         /* Release the protection mutex.  */
222         tx_mutex_put(&(ip_ptr -> nx_ip_protection));
223 
224         /* Return an already bound error code.  */
225         return(NX_ALREADY_BOUND);
226     }
227 
228     /* Determine if the port needs to be allocated.  */
229     if (port == NX_ANY_PORT)
230     {
231 
232         /* Call the find routine to allocate a UDP port.  */
233         port = NX_SEARCH_PORT_START + (UINT)(((ULONG)NX_RAND()) % ((NX_MAX_PORT + 1) - NX_SEARCH_PORT_START));
234         if (_nx_udp_free_port_find(ip_ptr, port, &port) != NX_SUCCESS)
235         {
236 
237             /* Release the protection mutex.  */
238             tx_mutex_put(&(ip_ptr -> nx_ip_protection));
239 
240             /* There was no free port, return an error code.  */
241             return(NX_NO_FREE_PORTS);
242         }
243     }
244 #ifdef NX_NAT_ENABLE
245     else
246     {
247 
248         /* Check if this IP interface has a NAT service. */
249         if (ip_ptr -> nx_ip_nat_port_verify)
250         {
251 
252             /* Yes, so check the port by NAT handler.  */
253             bound = (ip_ptr -> nx_ip_nat_port_verify)(ip_ptr, NX_PROTOCOL_UDP, port);
254 
255             /* Check to see if the port has been bound by NAT.  */
256             if (bound == NX_TRUE)
257             {
258 
259                 /* Release the protection.  */
260                 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
261 
262                 /* Return the port unavailable error.  */
263                 return(NX_PORT_UNAVAILABLE);
264             }
265         }
266     }
267 #endif
268 
269     /* Save the port number in the UDP socket structure.  */
270     socket_ptr -> nx_udp_socket_port =  port;
271 
272     /* Calculate the hash index in the UDP port array of the associated IP instance.  */
273     index =  (UINT)((port + (port >> 8)) & NX_UDP_PORT_TABLE_MASK);
274 
275     /* Pickup the head of the UDP ports bound list.  */
276     search_ptr =  ip_ptr -> nx_ip_udp_port_table[index];
277 
278     /* Determine if we need to perform a list search.  */
279     if (search_ptr)
280     {
281 
282         /* Walk through the circular list of UDP sockets that are already
283            bound.  */
284         end_ptr = search_ptr;
285         do
286         {
287 
288             /* Determine if this entry is the same as the requested port.  */
289             if (search_ptr -> nx_udp_socket_port == port)
290             {
291 
292                 /* Yes, the port has already been allocated.  */
293                 break;
294             }
295 
296             /* Move to the next entry in the list.  */
297             search_ptr =  search_ptr -> nx_udp_socket_bound_next;
298         } while (search_ptr != end_ptr);
299     }
300 
301     /* Now determine if the port is available.  */
302     if ((search_ptr == NX_NULL) || (search_ptr -> nx_udp_socket_port != port))
303     {
304 
305         /* Place this UDP socket structure on the list of bound ports.  */
306 
307         /* Disable interrupts.  */
308         TX_DISABLE
309 
310         /* Determine if the list is NULL.  */
311         if (search_ptr)
312         {
313 
314             /* There are already sockets on this list... just add this one
315                to the end.  */
316             socket_ptr -> nx_udp_socket_bound_next =       ip_ptr -> nx_ip_udp_port_table[index];
317             socket_ptr -> nx_udp_socket_bound_previous =   (ip_ptr -> nx_ip_udp_port_table[index]) -> nx_udp_socket_bound_previous;
318             ((ip_ptr -> nx_ip_udp_port_table[index]) -> nx_udp_socket_bound_previous) -> nx_udp_socket_bound_next = socket_ptr;
319             (ip_ptr -> nx_ip_udp_port_table[index]) -> nx_udp_socket_bound_previous =   socket_ptr;
320         }
321         else
322         {
323 
324             /* Nothing is on the UDP port list.  Add this UDP socket to an
325                empty list.  */
326             socket_ptr -> nx_udp_socket_bound_next =      socket_ptr;
327             socket_ptr -> nx_udp_socket_bound_previous =  socket_ptr;
328             ip_ptr -> nx_ip_udp_port_table[index] =       socket_ptr;
329         }
330 
331         /* Restore interrupts.  */
332         TX_RESTORE
333 
334         /* Release the mutex protection.  */
335         tx_mutex_put(&(ip_ptr -> nx_ip_protection));
336     }
337     else if (wait_option)
338     {
339 
340         /* Prepare for suspension of this thread.  */
341 
342         /* Disable interrupts.  */
343         TX_DISABLE
344 
345         /* Pickup thread pointer.  */
346         thread_ptr =  _tx_thread_current_ptr;
347 
348         /* Setup cleanup routine pointer.  */
349         thread_ptr -> tx_thread_suspend_cleanup =  _nx_udp_bind_cleanup;
350 
351         /* Setup cleanup information, i.e. this socket control
352            block.  */
353         thread_ptr -> tx_thread_suspend_control_block =  (void *)socket_ptr;
354 
355         /* Also remember the socket that has bound to the port, since the thread
356            is going to be suspended on that socket.  */
357         socket_ptr -> nx_udp_socket_bound_previous =  search_ptr;
358 
359         /* Set the socket bind in progress flag (thread pointer).  */
360         socket_ptr -> nx_udp_socket_bind_in_progress =  thread_ptr;
361 
362         /* Setup suspension list.  */
363         if (search_ptr -> nx_udp_socket_bind_suspension_list)
364         {
365 
366             /* This list is not NULL, add current thread to the end. */
367             thread_ptr -> tx_thread_suspended_next =       search_ptr -> nx_udp_socket_bind_suspension_list;
368             thread_ptr -> tx_thread_suspended_previous =  (search_ptr -> nx_udp_socket_bind_suspension_list) -> tx_thread_suspended_previous;
369             ((search_ptr -> nx_udp_socket_bind_suspension_list) -> tx_thread_suspended_previous) -> tx_thread_suspended_next =  thread_ptr;
370             (search_ptr -> nx_udp_socket_bind_suspension_list) -> tx_thread_suspended_previous =   thread_ptr;
371         }
372         else
373         {
374 
375             /* No other threads are suspended.  Setup the head pointer and
376                just setup this threads pointers to itself.  */
377             search_ptr -> nx_udp_socket_bind_suspension_list =         thread_ptr;
378             thread_ptr -> tx_thread_suspended_next =                   thread_ptr;
379             thread_ptr -> tx_thread_suspended_previous =               thread_ptr;
380         }
381 
382         /* Increment the suspended thread count.  */
383         search_ptr -> nx_udp_socket_bind_suspended_count++;
384 
385         /* Set the state to suspended.  */
386         thread_ptr -> tx_thread_state =  TX_TCP_IP;
387 
388         /* Set the suspending flag.  */
389         thread_ptr -> tx_thread_suspending =  TX_TRUE;
390 
391         /* Temporarily disable preemption.  */
392         _tx_thread_preempt_disable++;
393 
394         /* Save the timeout value.  */
395         thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
396 
397         /* Restore interrupts.  */
398         TX_RESTORE
399 
400         /* Release the mutex protection.  */
401         tx_mutex_put(&(ip_ptr -> nx_ip_protection));
402 
403         /* Call actual thread suspension routine.  */
404         _tx_thread_system_suspend(thread_ptr);
405 
406         /* Return the completion status.  */
407         status = thread_ptr -> tx_thread_suspend_status;
408     }
409     else
410     {
411 
412         /* Release the IP protection.  */
413         tx_mutex_put(&(ip_ptr -> nx_ip_protection));
414 
415         /* Return the port unavailable error.  */
416         status = NX_PORT_UNAVAILABLE;
417     }
418 
419 #ifdef NX_ENABLE_TCPIP_OFFLOAD
420     if (status == NX_SUCCESS)
421     {
422 
423         /* Bind to TCP/IP offload interface.  */
424         status = _nx_udp_socket_driver_bind(socket_ptr, port, wait_option);
425     }
426 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
427 
428     /* Return success to the caller.  */
429     return(status);
430 }
431 
432