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