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_ip.h"
29 #ifdef FEATURE_NX_IPV6
30 #include "nx_ipv6.h"
31 #endif /* FEATURE_NX_IPV6 */
32 #include "tx_thread.h"
33 #include "nx_packet.h"
34 #include "nx_udp.h"
35 #ifdef NX_IPSEC_ENABLE
36 #include "nx_ipsec.h"
37 #endif /* NX_IPSEC_ENABLE */
38 
39 
40 /**************************************************************************/
41 /*                                                                        */
42 /*  FUNCTION                                               RELEASE        */
43 /*                                                                        */
44 /*    _nx_udp_socket_receive                              PORTABLE C      */
45 /*                                                           6.1          */
46 /*  AUTHOR                                                                */
47 /*                                                                        */
48 /*    Yuxin Zhou, Microsoft Corporation                                   */
49 /*                                                                        */
50 /*  DESCRIPTION                                                           */
51 /*                                                                        */
52 /*    This function checks for a received UDP packet on the specified     */
53 /*    socket and if no packets are on the receive queue, suspends for the */
54 /*    wait option duration.                                               */
55 /*                                                                        */
56 /*  INPUT                                                                 */
57 /*                                                                        */
58 /*    socket_ptr                            Pointer to UDP socket         */
59 /*    packet_ptr                            Pointer to UDP packet pointer */
60 /*    wait_option                           Suspension option             */
61 /*                                                                        */
62 /*  OUTPUT                                                                */
63 /*                                                                        */
64 /*    status                                Completion status             */
65 /*                                                                        */
66 /*  CALLS                                                                 */
67 /*                                                                        */
68 /*    _nx_packet_release                    Release data packet           */
69 /*    _tx_thread_system_suspend             Suspend thread                */
70 /*                                                                        */
71 /*  CALLED BY                                                             */
72 /*                                                                        */
73 /*    Application Code                                                    */
74 /*                                                                        */
75 /*  RELEASE HISTORY                                                       */
76 /*                                                                        */
77 /*    DATE              NAME                      DESCRIPTION             */
78 /*                                                                        */
79 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
80 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
81 /*                                            resulting in version 6.1    */
82 /*                                                                        */
83 /**************************************************************************/
_nx_udp_socket_receive(NX_UDP_SOCKET * socket_ptr,NX_PACKET ** packet_ptr,ULONG wait_option)84 UINT  _nx_udp_socket_receive(NX_UDP_SOCKET *socket_ptr, NX_PACKET **packet_ptr, ULONG wait_option)
85 {
86 TX_INTERRUPT_SAVE_AREA
87 
88 ULONG                 *temp_ptr;
89 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
90 NX_INTERFACE          *interface_ptr = NX_NULL;
91 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
92 #if defined(NX_DISABLE_UDP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
93 UINT                   compute_checksum = 1;
94 #endif /* defined(NX_DISABLE_UDP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
95 TX_THREAD             *thread_ptr;
96 #ifdef TX_ENABLE_EVENT_TRACE
97 TX_TRACE_BUFFER_ENTRY *trace_event;
98 ULONG                  trace_timestamp;
99 #endif
100 
101 
102 #ifdef NX_DISABLE_UDP_RX_CHECKSUM
103     compute_checksum = 0;
104 #endif /* NX_DISABLE_UDP_RX_CHECKSUM */
105 
106     /* If trace is enabled, insert this event into the trace buffer.  */
107     NX_TRACE_IN_LINE_INSERT(NX_TRACE_UDP_SOCKET_RECEIVE, socket_ptr -> nx_udp_socket_ip_ptr, socket_ptr, 0, 0, NX_TRACE_UDP_EVENTS, &trace_event, &trace_timestamp);
108 
109     /* Set the return pointer to NULL initially.  */
110     *packet_ptr =   NX_NULL;
111 
112     /* Loop to retrieve a packet from the interface.  */
113     for (;;)
114     {
115 
116         /* Lockout interrupts.  */
117         TX_DISABLE
118 
119         /* Determine if the socket is currently bound.  */
120         if (!socket_ptr ->  nx_udp_socket_bound_next)
121         {
122 
123             /* Restore interrupts.  */
124             TX_RESTORE
125 
126             /* Socket is not bound, return an error message.  */
127             return(NX_NOT_BOUND);
128         }
129 
130         /* Determine if there is a packet already queued up for this socket.  */
131         if (socket_ptr -> nx_udp_socket_receive_head)
132         {
133 
134             /* Yes, there is a packet waiting.  */
135 
136             /* Remove it and place it in the thread's destination.  */
137             *packet_ptr =  socket_ptr -> nx_udp_socket_receive_head;
138             socket_ptr -> nx_udp_socket_receive_head =  (*packet_ptr) -> nx_packet_queue_next;
139 
140             /* If this was the last packet, set the tail pointer to NULL.  */
141             if (socket_ptr -> nx_udp_socket_receive_head == NX_NULL)
142             {
143                 socket_ptr -> nx_udp_socket_receive_tail =  NX_NULL;
144             }
145 
146             /* Decrease the queued packet count.  */
147             socket_ptr -> nx_udp_socket_receive_count--;
148 
149             /* Restore interrupts.  */
150             TX_RESTORE
151         }
152         else
153         {
154 
155             /* Determine if the request specifies suspension.  */
156             if (wait_option)
157             {
158 
159                 /* Prepare for suspension of this thread.  */
160 
161                 /* Pickup thread pointer.  */
162                 thread_ptr =  _tx_thread_current_ptr;
163 
164                 /* Setup cleanup routine pointer.  */
165                 thread_ptr -> tx_thread_suspend_cleanup =  _nx_udp_receive_cleanup;
166 
167                 /* Setup cleanup information, i.e. this pool control
168                    block.  */
169                 thread_ptr -> tx_thread_suspend_control_block =  (void *)socket_ptr;
170 
171                 /* Save the return packet pointer address as well.  */
172                 thread_ptr -> tx_thread_additional_suspend_info =  (void *)packet_ptr;
173 
174                 /* Setup suspension list.  */
175                 if (socket_ptr -> nx_udp_socket_receive_suspension_list)
176                 {
177 
178                     /* This list is not NULL, add current thread to the end. */
179                     thread_ptr -> tx_thread_suspended_next =
180                         socket_ptr -> nx_udp_socket_receive_suspension_list;
181                     thread_ptr -> tx_thread_suspended_previous =
182                         (socket_ptr -> nx_udp_socket_receive_suspension_list) -> tx_thread_suspended_previous;
183                     ((socket_ptr -> nx_udp_socket_receive_suspension_list) -> tx_thread_suspended_previous) -> tx_thread_suspended_next =
184                         thread_ptr;
185                     (socket_ptr -> nx_udp_socket_receive_suspension_list) -> tx_thread_suspended_previous =   thread_ptr;
186                 }
187                 else
188                 {
189 
190                     /* No other threads are suspended.  Setup the head pointer and
191                        just setup this threads pointers to itself.  */
192                     socket_ptr -> nx_udp_socket_receive_suspension_list =   thread_ptr;
193                     thread_ptr -> tx_thread_suspended_next              =   thread_ptr;
194                     thread_ptr -> tx_thread_suspended_previous          =   thread_ptr;
195                 }
196 
197                 /* Increment the suspended thread count.  */
198                 socket_ptr -> nx_udp_socket_receive_suspended_count++;
199 
200                 /* Set the state to suspended.  */
201                 thread_ptr -> tx_thread_state =  TX_TCP_IP;
202 
203                 /* Set the suspending flag.  */
204                 thread_ptr -> tx_thread_suspending =  TX_TRUE;
205 
206                 /* Temporarily disable preemption.  */
207                 _tx_thread_preempt_disable++;
208 
209                 /* Save the timeout value.  */
210                 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
211 
212                 /* Restore interrupts.  */
213                 TX_RESTORE
214 
215                 /* Call actual thread suspension routine.  */
216                 _tx_thread_system_suspend(thread_ptr);
217 
218                 /* Determine if a packet was received successfully.  */
219                 if (thread_ptr -> tx_thread_suspend_status != NX_SUCCESS)
220                 {
221 
222                     /* If not, just return the error code.  */
223                     return(thread_ptr -> tx_thread_suspend_status);
224                 }
225 
226                 /* Otherwise, just fall through to the checksum logic for the UDP
227                    packet.  */
228             }
229             else
230             {
231 
232                 /* Restore interrupts.  */
233                 TX_RESTORE
234 
235                 /* Set the return pointer to NULL in case it was set but released due to checksum error.  */
236                 *packet_ptr =   NX_NULL;
237 
238                 /* Immediate return, return error completion.  */
239                 return(NX_NO_PACKET);
240             }
241         }
242 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
243         /* Get the packet interface. */
244 #ifndef NX_DISABLE_IPV4
245         if ((*packet_ptr) -> nx_packet_ip_version == NX_IP_VERSION_V4)
246         {
247             interface_ptr = (*packet_ptr) -> nx_packet_address.nx_packet_interface_ptr;
248         }
249 #endif /* !NX_DISABLE_IPV4  */
250 
251 #ifdef FEATURE_NX_IPV6
252         if ((*packet_ptr) -> nx_packet_ip_version == NX_IP_VERSION_V6)
253         {
254             interface_ptr = (*packet_ptr) -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached;
255         }
256 #endif /* FEATURE_NX_IPV6 */
257 
258         if (interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_UDP_RX_CHECKSUM)
259         {
260             compute_checksum = 0;
261         }
262 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
263 
264 #ifdef NX_IPSEC_ENABLE
265         if (((*packet_ptr) -> nx_packet_ipsec_sa_ptr != NX_NULL) && (((NX_IPSEC_SA *)((*packet_ptr) -> nx_packet_ipsec_sa_ptr)) -> nx_ipsec_sa_encryption_method != NX_CRYPTO_NONE))
266         {
267             compute_checksum = 1;
268         }
269 #endif /* NX_IPSEC_ENABLE */
270 
271 #if defined(NX_DISABLE_UDP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
272         if (compute_checksum)
273 #endif /* defined(NX_DISABLE_UDP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
274         {
275 
276             /* Determine if we need to compute the UDP checksum.  If it is disabled for this socket
277                or if the UDP packet has a zero in the checksum field (indicating it was not computed
278                by the sender, skip the checksum processing.  */
279             /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
280             temp_ptr =  (ULONG *)(*packet_ptr) -> nx_packet_prepend_ptr;
281             if ((!socket_ptr -> nx_udp_socket_disable_checksum && (*(temp_ptr + 1) & NX_LOWER_16_MASK)) || /* per-socket checksum is not disabled, and the checksum field is not zero*/
282                 ((*packet_ptr) -> nx_packet_ip_version == NX_IP_VERSION_V6))                               /* It is IPv6 packet */
283             {
284             ULONG         *ip_src_addr = NX_NULL, *ip_dest_addr = NX_NULL;
285             ULONG          checksum;
286             NX_PACKET     *current_ptr = *packet_ptr;
287 #ifdef NX_LITTLE_ENDIAN
288             NX_UDP_HEADER *udp_header_ptr;
289 
290                 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
291                 udp_header_ptr = (NX_UDP_HEADER *)(current_ptr -> nx_packet_prepend_ptr);
292 #endif /* NX_LITTLE_ENDIAN */
293 
294 #ifndef NX_DISABLE_IPV4
295                 if (current_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4)
296                 {
297                 NX_IPV4_HEADER *ipv4_header;
298 
299                     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
300                     ipv4_header = (NX_IPV4_HEADER *)(current_ptr -> nx_packet_ip_header);
301                     ip_src_addr = &(ipv4_header -> nx_ip_header_source_ip);
302                     ip_dest_addr = &(ipv4_header -> nx_ip_header_destination_ip);
303                 }
304 #endif /* !NX_DISABLE_IPV4  */
305 
306 #ifdef FEATURE_NX_IPV6
307                 if (current_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6) /*  IPv6 */
308                 {
309                 NX_IPV6_HEADER *ipv6_header;
310 
311                     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
312                     ipv6_header = (NX_IPV6_HEADER *)(current_ptr -> nx_packet_ip_header);
313                     ip_src_addr = (&ipv6_header -> nx_ip_header_source_ip[0]);
314                     ip_dest_addr = (&ipv6_header -> nx_ip_header_destination_ip[0]);
315                 }
316 
317 #endif /* FEATURE_NX_IPV6 */
318 
319 #ifdef NX_LITTLE_ENDIAN
320                 /* Restore UDP header to network byte order */
321                 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_0);
322                 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_1);
323 #endif /* NX_LITTLE_ENDIAN */
324 
325                 /* nx_ip_checksum_compute takes care of both even number length and odd number length */
326                 /* Compute the checksum of the first packet */
327                 checksum = _nx_ip_checksum_compute(current_ptr, NX_PROTOCOL_UDP,
328                                                    (UINT)current_ptr -> nx_packet_length,
329                                                    /* IPv6 src/dest address */
330                                                    ip_src_addr,
331                                                    ip_dest_addr);
332 
333 #ifdef NX_LITTLE_ENDIAN
334                 /* Convert UDP header to host byte order */
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 #endif /* NX_LITTLE_ENDIAN */
338 
339                 /* Perform the one's complement processing on the checksum.  */
340                 checksum =  NX_LOWER_16_MASK & ~checksum;
341 
342                 /* Determine if it is valid.  */
343                 if (checksum == 0)
344                 {
345 
346                     /* The checksum is okay, so get out of the loop. */
347                     break;
348                 }
349                 else
350                 {
351 
352 #ifndef NX_DISABLE_UDP_INFO
353 
354                     /* Disable interrupts.  */
355                     TX_DISABLE
356 
357                     /* Increment the UDP checksum error count.  */
358                     (socket_ptr -> nx_udp_socket_ip_ptr) -> nx_ip_udp_checksum_errors++;
359 
360                     /* Increment the UDP invalid packets error count.  */
361                     (socket_ptr -> nx_udp_socket_ip_ptr) -> nx_ip_udp_invalid_packets++;
362 
363                     /* Increment the UDP checksum error count for this socket.  */
364                     socket_ptr -> nx_udp_socket_checksum_errors++;
365 
366                     /* Decrement the total UDP receive packets count.  */
367                     (socket_ptr -> nx_udp_socket_ip_ptr) -> nx_ip_udp_packets_received--;
368 
369                     /* Decrement the total UDP receive bytes.  */
370                     (socket_ptr -> nx_udp_socket_ip_ptr) -> nx_ip_udp_bytes_received -=  (*packet_ptr) -> nx_packet_length - (ULONG)sizeof(NX_UDP_HEADER);
371 
372                     /* Decrement the total UDP receive packets count.  */
373                     socket_ptr -> nx_udp_socket_packets_received--;
374 
375                     /* Decrement the total UDP receive bytes.  */
376                     socket_ptr -> nx_udp_socket_bytes_received -=  (*packet_ptr) -> nx_packet_length - (ULONG)sizeof(NX_UDP_HEADER);
377 
378                     /* Restore interrupts.  */
379                     TX_RESTORE
380 #endif
381 
382                     /* Bad UDP checksum.  Release the packet. */
383                     _nx_packet_release(*packet_ptr);
384                 }
385             }
386             else
387             {
388 
389                 /* Checksum logic is either disabled for this socket or the received
390                    UDP packet checksum was not calculated - get out of the loop.  */
391                 break;
392             }
393         }
394 #if defined(NX_DISABLE_UDP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
395         else
396         {
397 
398             /* Simply break - checksum logic is conditionally disabled.  */
399             break;
400         }
401 #endif /* defined(NX_DISABLE_UDP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
402     }
403 
404     /* At this point, we have a valid UDP packet for the caller.  */
405 
406     /* Remove the UDP header.  */
407 
408     /* Decrease the packet length.  */
409     (*packet_ptr) -> nx_packet_length =  (*packet_ptr) -> nx_packet_length - (ULONG)sizeof(NX_UDP_HEADER);
410 
411     /* Position past the UDP header pointer.  */
412     (*packet_ptr) -> nx_packet_prepend_ptr =   (*packet_ptr) -> nx_packet_prepend_ptr + sizeof(NX_UDP_HEADER);
413 
414     /* Update the trace event with the status.  */
415     NX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, NX_TRACE_UDP_SOCKET_RECEIVE, 0, 0, *packet_ptr, (*packet_ptr) -> nx_packet_length);
416 
417     /* Return a successful status to the caller.  */
418     return(NX_SUCCESS);
419 }
420 
421