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