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