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