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