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