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