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 "tx_thread.h"
29 #include "nx_udp.h"
30 #include "nx_packet.h"
31 #include "nx_icmpv4.h"
32
33 #ifdef FEATURE_NX_IPV6
34 /* for ICMPv6 destination unreachable */
35 #include "nx_ipv6.h"
36 #include "nx_icmpv6.h"
37
38 #endif /* FEATURE_NX_IPV6 */
39
40
41 /**************************************************************************/
42 /* */
43 /* FUNCTION RELEASE */
44 /* */
45 /* _nx_udp_packet_receive PORTABLE C */
46 /* 6.1 */
47 /* AUTHOR */
48 /* */
49 /* Yuxin Zhou, Microsoft Corporation */
50 /* */
51 /* DESCRIPTION */
52 /* */
53 /* This function receives a UDP packet from the IP receive processing */
54 /* and places on the appropriate socket's input queue. */
55 /* */
56 /* INPUT */
57 /* */
58 /* ip_ptr Pointer to IP control block */
59 /* packet_ptr Pointer to packet received */
60 /* */
61 /* OUTPUT */
62 /* */
63 /* None */
64 /* */
65 /* CALLS */
66 /* */
67 /* _nx_packet_release Packet release function */
68 /* tx_mutex_get Get protection mutex */
69 /* tx_mutex_put Release protection mutex */
70 /* _tx_thread_system_resume Resume suspended thread */
71 /* (nx_udp_receive_callback) Packet receive notify function*/
72 /* */
73 /* CALLED BY */
74 /* */
75 /* _nx_ip_packet_receive Dispatch received IP packets */
76 /* */
77 /* RELEASE HISTORY */
78 /* */
79 /* DATE NAME DESCRIPTION */
80 /* */
81 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
82 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
83 /* resulting in version 6.1 */
84 /* */
85 /**************************************************************************/
_nx_udp_packet_receive(NX_IP * ip_ptr,NX_PACKET * packet_ptr)86 VOID _nx_udp_packet_receive(NX_IP *ip_ptr, NX_PACKET *packet_ptr)
87 {
88
89 TX_INTERRUPT_SAVE_AREA
90 VOID (*receive_callback)(struct NX_UDP_SOCKET_STRUCT *socket_ptr);
91 UINT index;
92 UINT port;
93 TX_THREAD *thread_ptr;
94 NX_UDP_SOCKET *socket_ptr;
95 NX_UDP_HEADER *udp_header_ptr;
96
97 /* Add debug information. */
98 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
99
100 #ifndef NX_DISABLE_UDP_INFO
101
102 /* Increment the total UDP receive packets count. */
103 ip_ptr -> nx_ip_udp_packets_received++;
104 #endif
105
106 #ifndef NX_DISABLE_RX_SIZE_CHECKING
107
108 /* Check for valid packet length. */
109 if (packet_ptr -> nx_packet_length < sizeof(NX_UDP_HEADER))
110 {
111
112 #ifndef NX_DISABLE_UDP_INFO
113
114 /* Increment the UDP invalid packet error. */
115 ip_ptr -> nx_ip_udp_invalid_packets++;
116 #endif
117
118 /* Invalid packet length, just release it. */
119 _nx_packet_release(packet_ptr);
120
121 /* The function is complete, just return! */
122 return;
123 }
124 #endif
125
126 /* Pickup the pointer to the head of the UDP packet. */
127 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
128 udp_header_ptr = (NX_UDP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
129
130 /* Endian swapping logic. If NX_LITTLE_ENDIAN is specified, these macros will
131 swap the endian of the UDP header. */
132 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_0);
133 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_1);
134
135 #ifndef NX_DISABLE_RX_SIZE_CHECKING
136
137 /* Check for valid packet length. */
138 if (packet_ptr -> nx_packet_length < (((udp_header_ptr -> nx_udp_header_word_1) >> NX_SHIFT_BY_16) & NX_LOWER_16_MASK))
139 {
140
141 #ifndef NX_DISABLE_UDP_INFO
142
143 /* Increment the UDP invalid packet error. */
144 ip_ptr -> nx_ip_udp_invalid_packets++;
145 #endif
146
147 /* Invalid packet length, just release it. */
148 _nx_packet_release(packet_ptr);
149
150 /* The function is complete, just return! */
151 return;
152 }
153 #endif
154
155 #ifdef NX_IPSEC_ENABLE
156 /* Recompute the packet length in case TFC padding is present. */
157 packet_ptr -> nx_packet_length = (((udp_header_ptr -> nx_udp_header_word_1) >> NX_SHIFT_BY_16) & NX_LOWER_16_MASK);
158 #endif /* NX_IPSEC_ENABLE */
159
160 /* Pickup the destination UDP port. */
161 port = (UINT)(udp_header_ptr -> nx_udp_header_word_0 & NX_LOWER_16_MASK);
162
163 /* Calculate the hash index in the UDP port array of the associated IP instance. */
164 index = (UINT)((port + (port >> 8)) & NX_UDP_PORT_TABLE_MASK);
165
166 /* Determine if the caller is a thread. If so, we should use the protection mutex
167 to avoid having the port list examined while we are traversing it. If this routine
168 is called from an ISR nothing needs to be done since bind/unbind are not allowed
169 from ISRs. */
170 if ((_tx_thread_current_ptr) && (TX_THREAD_GET_SYSTEM_STATE() == 0))
171 {
172
173 /* Get mutex protection. */
174 tx_mutex_get(&(ip_ptr -> nx_ip_protection), NX_WAIT_FOREVER);
175 }
176
177 /* Search the bound sockets in this index for the particular port. */
178 socket_ptr = ip_ptr -> nx_ip_udp_port_table[index];
179
180 /* Determine if there are any sockets bound on this port index. */
181 if (!socket_ptr)
182 {
183
184 #ifndef NX_DISABLE_IPV4
185 #ifndef NX_DISABLE_ICMPV4_ERROR_MESSAGE
186 /* If ICMPv4 is enabled, send Destination unreachable. */
187 if ((packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4) &&
188 (ip_ptr -> nx_ip_icmpv4_packet_process))
189 {
190
191 /* Restore UDP header. */
192 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_0);
193 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_1);
194
195 /* Send out ICMP error message if dest is not multicast. */
196 NX_ICMPV4_SEND_DEST_UNREACHABLE(ip_ptr, packet_ptr, NX_ICMP_PORT_UNREACH_CODE);
197 }
198 #endif /* NX_DISABLE_ICMPV4_ERROR_MESSAGE */
199 #endif /* !NX_DISABLE_IPV4 */
200
201 #ifdef FEATURE_NX_IPV6
202 #ifndef NX_DISABLE_ICMPV6_ERROR_MESSAGE
203 /* If ICMPv6 is enabled, send Destination unreachable. */
204 if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6)
205 {
206 NX_IPV6_HEADER *ip_header;
207
208 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
209 ip_header = (NX_IPV6_HEADER *)packet_ptr -> nx_packet_ip_header;
210
211 if ((ip_header -> nx_ip_header_destination_ip[0] & (ULONG)0xFF000000) != (ULONG)0xFF000000)
212 {
213
214 /* Restore UDP header. */
215 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_0);
216 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_1);
217
218 /* Send out ICMP error message if dest is not multicast. */
219 NX_ICMPV6_SEND_DEST_UNREACHABLE(ip_ptr, packet_ptr, NX_ICMPV6_DEST_UNREACHABLE_CODE);
220 }
221 }
222 #endif /* NX_DISABLE_ICMPV6_ERROR_MESSAGE */
223 #endif /* FEATURE_NX_IPV6 */
224
225 #ifndef NX_DISABLE_UDP_INFO
226
227 /* Increment the no port for delivery count. */
228 ip_ptr -> nx_ip_udp_no_port_for_delivery++;
229
230 /* Increment the total UDP receive packets dropped count. */
231 ip_ptr -> nx_ip_udp_receive_packets_dropped++;
232 #endif
233
234 /* Determine if the caller is a thread. If so, release the mutex protection previously setup. */
235 if ((_tx_thread_current_ptr) && (TX_THREAD_GET_SYSTEM_STATE() == 0))
236 {
237
238 /* Release mutex protection. */
239 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
240 }
241
242 /* Release the packet. */
243 _nx_packet_release(packet_ptr);
244
245 /* Just return. */
246 return;
247 }
248
249 /* Loop to examine the list of bound ports on this index. */
250 do
251 {
252
253 /* Determine if the port has been found. */
254 if (socket_ptr -> nx_udp_socket_port == port)
255 {
256
257 /* Yes, we have a match. */
258
259 #ifndef NX_DISABLE_UDP_INFO
260
261 /* Increment the total number of packets received for this socket. */
262 socket_ptr -> nx_udp_socket_packets_received++;
263
264 /* Increment the total UDP receive bytes. */
265 ip_ptr -> nx_ip_udp_bytes_received += packet_ptr -> nx_packet_length - (ULONG)sizeof(NX_UDP_HEADER);
266 socket_ptr -> nx_udp_socket_bytes_received += packet_ptr -> nx_packet_length - (ULONG)sizeof(NX_UDP_HEADER);
267 #endif
268
269 /* If trace is enabled, insert this event into the trace buffer. */
270 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_UDP_RECEIVE, ip_ptr, socket_ptr, packet_ptr, udp_header_ptr -> nx_udp_header_word_0, NX_TRACE_INTERNAL_EVENTS, 0, 0);
271
272 /* Get out of the search loop. */
273 break;
274 }
275 else
276 {
277
278 /* Move to the next entry in the bound index. */
279 socket_ptr = socket_ptr -> nx_udp_socket_bound_next;
280 }
281 } while (socket_ptr != ip_ptr -> nx_ip_udp_port_table[index]);
282
283 /* Determine if the caller is a thread. If so, release the mutex protection previously setup. */
284 if ((_tx_thread_current_ptr) && (TX_THREAD_GET_SYSTEM_STATE() == 0))
285 {
286
287 /* Release mutex protection. */
288 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
289 }
290
291 /* Determine if a match was found. */
292 if (socket_ptr -> nx_udp_socket_port != port)
293 {
294
295 #ifndef NX_DISABLE_UDP_INFO
296
297 /* Increment the no port for delivery count. */
298 ip_ptr -> nx_ip_udp_no_port_for_delivery++;
299
300 /* Increment the total UDP receive packets dropped count. */
301 ip_ptr -> nx_ip_udp_receive_packets_dropped++;
302 #endif
303
304 #if !defined(NX_DISABLE_IPV4) && !defined(NX_DISABLE_ICMPV4_ERROR_MESSAGE)
305 /* If ICMPv4 is enabled, send Destination unreachable. */
306 if ((packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4) &&
307 (ip_ptr -> nx_ip_icmpv4_packet_process))
308 {
309
310 /* Restore UDP header. */
311 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_0);
312 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_1);
313
314 /* Send out ICMP error message if dest is not multicast. */
315 NX_ICMPV4_SEND_DEST_UNREACHABLE(ip_ptr, packet_ptr, NX_ICMP_PORT_UNREACH_CODE);
316 }
317 #endif /* !NX_DISABLE_IPV4 && !NX_DISABLE_ICMPV4_ERROR_MESSAGE */
318
319 #if defined(FEATURE_NX_IPV6) && !defined(NX_DISABLE_ICMPV6_ERROR_MESSAGE)
320 /* If ICMPv6 is enabled, send Destination unreachable. */
321 if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6)
322 {
323
324 NX_IPV6_HEADER *ip_header;
325
326 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
327 ip_header = (NX_IPV6_HEADER *)packet_ptr -> nx_packet_ip_header;
328
329 /* Send out ICMP error message if dest is not multicast. */
330 if ((ip_header -> nx_ip_header_destination_ip[0] & (ULONG)0xFF000000) != (ULONG)0xFF000000)
331 {
332
333 /* Restore UDP header. */
334 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_0);
335 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_1);
336
337 NX_ICMPV6_SEND_DEST_UNREACHABLE(ip_ptr, packet_ptr, NX_ICMPV6_DEST_UNREACHABLE_CODE);
338 }
339 }
340 #endif /* FEATURE_NX_IPV6 && !NX_DISABLE_ICMPV6_ERROR_MESSAGE */
341
342 /* No socket structure bound to this port, just release the packet. */
343 _nx_packet_release(packet_ptr);
344 return;
345 }
346
347 /* Disable interrupts. */
348 TX_DISABLE
349
350 /* Determine if the socket is still valid. */
351 if (socket_ptr -> nx_udp_socket_id != NX_UDP_ID)
352 {
353
354 #ifndef NX_DISABLE_UDP_INFO
355
356 /* Increment the no port for delivery count. */
357 ip_ptr -> nx_ip_udp_no_port_for_delivery++;
358
359 /* Increment the total UDP receive packets dropped count. */
360 ip_ptr -> nx_ip_udp_receive_packets_dropped++;
361
362 /* Increment the total UDP receive packets dropped count for this socket. */
363 socket_ptr -> nx_udp_socket_packets_dropped++;
364 #endif
365
366 /* Restore interrupts. */
367 TX_RESTORE
368
369 /* Release the packet. */
370 _nx_packet_release(packet_ptr);
371
372 /* Return to caller. */
373 return;
374 }
375
376 /* Pickup the receive notify function. */
377 receive_callback = socket_ptr -> nx_udp_receive_callback;
378
379 /* Determine if we need to update the UDP port head pointer. This should
380 only be done if the found socket pointer is not the head pointer and
381 the mutex for this IP instance is available. */
382 if ((socket_ptr != ip_ptr -> nx_ip_udp_port_table[index]) && (!ip_ptr -> nx_ip_protection.tx_mutex_ownership_count))
383 {
384
385 /* Move the port head pointer to this socket. */
386 ip_ptr -> nx_ip_udp_port_table[index] = socket_ptr;
387 }
388
389 /* Determine if there is thread waiting for a packet from this port. */
390 thread_ptr = socket_ptr -> nx_udp_socket_receive_suspension_list;
391 if (thread_ptr)
392 {
393
394 /* Remove the suspended thread from the list. */
395
396 /* See if this is the only suspended thread on the list. */
397 if (thread_ptr == thread_ptr -> tx_thread_suspended_next)
398 {
399
400 /* Yes, the only suspended thread. */
401
402 /* Update the head pointer. */
403 socket_ptr -> nx_udp_socket_receive_suspension_list = NX_NULL;
404 }
405 else
406 {
407
408 /* At least one more thread is on the same expiration list. */
409
410 /* Update the list head pointer. */
411 socket_ptr -> nx_udp_socket_receive_suspension_list = thread_ptr -> tx_thread_suspended_next;
412
413 /* Update the links of the adjacent threads. */
414 (thread_ptr -> tx_thread_suspended_next) -> tx_thread_suspended_previous =
415 thread_ptr -> tx_thread_suspended_previous;
416 (thread_ptr -> tx_thread_suspended_previous) -> tx_thread_suspended_next =
417 thread_ptr -> tx_thread_suspended_next;
418 }
419
420 /* Decrement the suspension count. */
421 socket_ptr -> nx_udp_socket_receive_suspended_count--;
422
423 /* Prepare for resumption of the first thread. */
424
425 /* Clear cleanup routine to avoid timeout. */
426 thread_ptr -> tx_thread_suspend_cleanup = TX_NULL;
427
428 /* Temporarily disable preemption. */
429 _tx_thread_preempt_disable++;
430
431 /* Return this block pointer to the suspended thread waiting for
432 a block. */
433 *((NX_PACKET **)thread_ptr -> tx_thread_additional_suspend_info) = packet_ptr;
434
435 /* Add debug information. */
436 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
437
438 /* Restore interrupts. */
439 TX_RESTORE
440
441 /* Put return status into the thread control block. */
442 thread_ptr -> tx_thread_suspend_status = NX_SUCCESS;
443
444 /* Resume thread. */
445 _tx_thread_system_resume(thread_ptr);
446 }
447 else
448 {
449
450 /* No, queue the thread in the socket's receive packet queue. */
451
452
453 #ifdef NX_ENABLE_LOW_WATERMARK
454 /* Check low watermark. */
455 if (packet_ptr -> nx_packet_pool_owner -> nx_packet_pool_available <
456 packet_ptr -> nx_packet_pool_owner -> nx_packet_pool_low_watermark)
457 {
458
459 #ifndef NX_DISABLE_UDP_INFO
460 /* Increment the total UDP receive packets dropped count. */
461 ip_ptr -> nx_ip_udp_receive_packets_dropped++;
462
463 /* Increment the total UDP receive packets dropped count for this socket. */
464 socket_ptr -> nx_udp_socket_packets_dropped++;
465 #endif
466
467 /* Restore interrupts. */
468 TX_RESTORE
469
470 /* Release the packet. */
471 _nx_packet_release(packet_ptr);
472
473 /* Just return. */
474 return;
475 }
476 #endif /* NX_ENABLE_LOW_WATERMARK */
477
478 /* Place the packet at the end of the socket's receive queue. */
479 if (socket_ptr -> nx_udp_socket_receive_head)
480 {
481
482 /* Add the new packet to a nonempty list. */
483 (socket_ptr -> nx_udp_socket_receive_tail) -> nx_packet_queue_next = packet_ptr;
484 socket_ptr -> nx_udp_socket_receive_tail = packet_ptr;
485 packet_ptr -> nx_packet_queue_next = NX_NULL;
486
487 /* Increment the number of packets queued. */
488 socket_ptr -> nx_udp_socket_receive_count++;
489
490 /* Determine if the maximum queue depth has been reached. */
491 if (socket_ptr -> nx_udp_socket_receive_count >
492 socket_ptr -> nx_udp_socket_queue_maximum)
493 {
494
495 /* We have exceeded the queue depth, so remove the first item
496 in the queue (which is the oldest). */
497 packet_ptr = socket_ptr -> nx_udp_socket_receive_head;
498 socket_ptr -> nx_udp_socket_receive_head = packet_ptr -> nx_packet_queue_next;
499
500 /* Decrement the number of packets queued. */
501 socket_ptr -> nx_udp_socket_receive_count--;
502
503 #ifndef NX_DISABLE_UDP_INFO
504
505 /* Increment the total UDP receive packets dropped count. */
506 ip_ptr -> nx_ip_udp_receive_packets_dropped++;
507
508 /* Increment the total UDP receive packets dropped count for this socket. */
509 socket_ptr -> nx_udp_socket_packets_dropped++;
510 #endif
511
512 /* Restore interrupts. */
513 TX_RESTORE
514
515 /* Release the packet. */
516 _nx_packet_release(packet_ptr);
517 }
518 else
519 {
520
521 /* Restore interrupts. */
522 TX_RESTORE
523 }
524 }
525 else
526 {
527
528 /* Add the new packet to an empty list. */
529 socket_ptr -> nx_udp_socket_receive_head = packet_ptr;
530 socket_ptr -> nx_udp_socket_receive_tail = packet_ptr;
531 packet_ptr -> nx_packet_queue_next = NX_NULL;
532
533 /* Increment the number of packets queued. */
534 socket_ptr -> nx_udp_socket_receive_count++;
535
536 /* Restore interrupts. */
537 TX_RESTORE
538 }
539
540 /* Add debug information. */
541 NX_PACKET_DEBUG(NX_PACKET_UDP_RECEIVE_QUEUE, __LINE__, packet_ptr);
542 }
543
544 /* Determine if there is a socket receive notification function specified. */
545 if (receive_callback)
546 {
547
548 /* Yes, notification is requested. Call the application's receive notification
549 function for this socket. */
550 (receive_callback)(socket_ptr);
551 }
552 }
553
554