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