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 /** Internet Control Message Protocol (ICMP) */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define NX_SOURCE_CODE
23
24 /* Include necessary system files. */
25
26 #include "nx_api.h"
27 #include "nx_icmp.h"
28 #include "nx_packet.h"
29 #include "nx_ip.h"
30 #include "tx_thread.h"
31 #include "nx_icmpv6.h"
32 #include "nx_ipv6.h"
33
34
35 #ifdef FEATURE_NX_IPV6
36
37
38 #ifdef NX_IPSEC_ENABLE
39 #include "nx_ipsec.h"
40 #endif /* NX_IPSEC_ENABLE */
41
42 /**************************************************************************/
43 /* */
44 /* FUNCTION RELEASE */
45 /* */
46 /* _nx_icmp_interface_ping6 PORTABLE C */
47 /* 6.1 */
48 /* AUTHOR */
49 /* */
50 /* Yuxin Zhou, Microsoft Corporation */
51 /* */
52 /* DESCRIPTION */
53 /* */
54 /* This function handles ICMPv6 ping requests and calls the associated */
55 /* network driver to send it out on the network. The function will */
56 /* then suspend for the specified time waiting for the ICMP ping */
57 /* response. */
58 /* */
59 /* INPUT */
60 /* */
61 /* ip_ptr Pointer to IP instance */
62 /* dest_ip_address IPv6 address to ping */
63 /* data_ptr Pointer to user data to */
64 /* include in ping packet */
65 /* data_size Size of user data */
66 /* address_index Source IPv6 address to use */
67 /* response_ptr Pointer to response packet */
68 /* wait_option Time out on packet allocate */
69 /* and sending packet */
70 /* */
71 /* OUTPUT */
72 /* */
73 /* status Actual completion status */
74 /* NX_OVERFLOW Data exceeds packet payload */
75 /* NX_IPSEC_REJECTED IPSec check failed */
76 /* NX_NO_RESPONSE No response to ping message */
77 /* */
78 /* CALLS */
79 /* */
80 /* _nx_icmp_checksum_compute Computes ICMP checksum */
81 /* _nx_ipv6_packet_send Send IPv6 packet function */
82 /* _nx_packet_allocate Allocate a packet for the */
83 /* ICMP ping request */
84 /* _nx_packet_release Release packet on error */
85 /* tx_mutex_get Obtain protection mutex */
86 /* tx_mutex_put Release protection mutex */
87 /* _tx_thread_system_suspend Suspend thread */
88 /* _tx_thread_system_preempt_check Check for preemption */
89 /* */
90 /* CALLED BY */
91 /* */
92 /* _nx_icmp_ping6 */
93 /* Application Send ping on the specified */
94 /* interface */
95 /* */
96 /* RELEASE HISTORY */
97 /* */
98 /* DATE NAME DESCRIPTION */
99 /* */
100 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
101 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
102 /* resulting in version 6.1 */
103 /* */
104 /**************************************************************************/
_nx_icmp_interface_ping6(NX_IP * ip_ptr,NXD_ADDRESS * ip_address,CHAR * data_ptr,ULONG data_size,NXD_IPV6_ADDRESS * ipv6_address,NX_PACKET ** response_ptr,ULONG wait_option)105 UINT _nx_icmp_interface_ping6(NX_IP *ip_ptr, NXD_ADDRESS *ip_address, CHAR *data_ptr, ULONG data_size,
106 NXD_IPV6_ADDRESS *ipv6_address, NX_PACKET **response_ptr, ULONG wait_option)
107 {
108
109 TX_INTERRUPT_SAVE_AREA
110
111 UINT status;
112 NX_PACKET *request_ptr;
113 NX_ICMPV6_ECHO *echo_header_ptr;
114 ULONG checksum;
115 ULONG sequence;
116 TX_THREAD *thread_ptr;
117
118 #ifdef TX_ENABLE_EVENT_TRACE
119 ULONG ip_address_lsw;
120 #endif /* TX_ENABLE_EVENT_TRACE */
121 #ifdef NX_IPSEC_ENABLE
122 VOID *sa = NX_NULL;
123 NXD_ADDRESS src_addr;
124 UINT ret = 0;
125 #endif /* NX_IPSEC_ENABLE */
126 #if defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
127 UINT compute_checksum = 1;
128 #endif /* defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
129
130
131 #ifdef TX_ENABLE_EVENT_TRACE
132 ip_address_lsw = ip_address -> nxd_ip_address.v6[3];
133
134 /* If trace is enabled, insert this event into the trace buffer. */
135 NX_TRACE_IN_LINE_INSERT(NX_TRACE_ICMP_PING6, ip_ptr, ip_address_lsw, data_ptr, data_size, NX_TRACE_ICMP_EVENTS, 0, 0);
136 #endif /* TX_ENABLE_EVENT_TRACE */
137
138 /* Clear the destination pointer. */
139 *response_ptr = NX_NULL;
140
141 #ifdef NX_IPSEC_ENABLE
142
143 src_addr.nxd_ip_version = NX_IP_VERSION_V6;
144 COPY_IPV6_ADDRESS(ipv6_address -> nxd_ipv6_address, src_addr.nxd_ip_address.v6);
145
146 /* Check if the IPsec is enabled. */
147 if (ip_ptr -> nx_ip_packet_egress_sa_lookup != NX_NULL)
148 {
149
150 /* Yes it is. Check for possible SA match. */
151 ret = ip_ptr -> nx_ip_packet_egress_sa_lookup(ip_ptr, /* IP ptr */
152 &src_addr, /* src_addr */
153 ip_address, /* dest_addr */
154 NX_PROTOCOL_ICMPV6, /* protocol */
155 0, /* src_port */
156 0, /* dest_port */
157 NX_NULL, &sa, (NX_ICMPV6_ECHO_REQUEST_TYPE << 8));
158
159 /* Check if the SA rules allow us to send this packet. */
160 if (ret == NX_IPSEC_TRAFFIC_BYPASS)
161 {
162
163 /* They do. Ok to send the ping6 packet. */
164 sa = NX_NULL;
165 }
166 else if (ret == NX_IPSEC_TRAFFIC_DROP || ret == NX_IPSEC_TRAFFIC_PENDING_IKEV2)
167 {
168
169 /* No we cannot. Set the error status and return.*/
170 return(NX_IPSEC_REJECTED);
171 }
172 }
173
174 #endif /* NX_IPSEC_ENABLE */
175
176 /* Determine if the size of the data and the ICMP header is larger than
177 the packet payload area. */
178
179 /* Allocate a packet to place the ICMP echo request message in. */
180 status = _nx_packet_allocate(ip_ptr -> nx_ip_default_packet_pool, &request_ptr,
181 NX_ICMP_PACKET + sizeof(NX_ICMPV6_ECHO), wait_option);
182 if (status)
183 {
184
185 /* Error getting packet, so just get out! */
186 return(status);
187 }
188
189 /* Add debug information. */
190 NX_PACKET_DEBUG(__FILE__, __LINE__, request_ptr);
191
192 /* Mark the packet as IPv6 */
193 /*lint -e{644} suppress variable might not be initialized, since "request_ptr" was initialized in _nx_packet_allocate. */
194 request_ptr -> nx_packet_ip_version = NX_IP_VERSION_V6;
195
196 #ifdef NX_IPSEC_ENABLE
197
198 request_ptr -> nx_packet_ipsec_sa_ptr = sa;
199
200 #endif /* NX_IPSEC_ENABLE */
201
202 /* Copy the data into the packet payload area. */
203 status = _nx_packet_data_append(request_ptr, (VOID *)data_ptr, data_size, ip_ptr -> nx_ip_default_packet_pool, wait_option);
204
205 /* Check return status. */
206 if (status)
207 {
208
209 /* Release the packet. */
210 _nx_packet_release(request_ptr);
211
212 /* Error, the data area is too big for the default packet payload. */
213 return(NX_OVERFLOW);
214 }
215
216 /* Set the outgoing address. */
217 request_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr = ipv6_address;
218
219 #ifndef NX_DISABLE_ICMP_INFO
220 /* Increment the ICMP ping count. */
221 ip_ptr -> nx_ip_pings_sent++;
222 #endif
223
224 #ifdef TX_ENABLE_EVENT_TRACE
225 /* If trace is enabled, insert this event into the trace buffer. */
226 /* NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_ICMP_SEND, ip_ptr, dest_ip_byte3, request_ptr, (((ULONG) NX_ICMP_ECHO_REQUEST_TYPE) << 24), NX_TRACE_INTERNAL_EVENTS, 0, 0) */
227 #endif /* TX_ENABLE_EVENT_TRACE */
228
229 /* Calculate the ICMP echo request message size and store it in the
230 packet header. */
231 request_ptr -> nx_packet_length += (ULONG)sizeof(NX_ICMPV6_ECHO);
232
233 /* Adjust the nx_packet_prepend_ptr for ICMP header. */
234 request_ptr -> nx_packet_prepend_ptr -= sizeof(NX_ICMPV6_ECHO);
235
236 /* Build the ICMP request packet. */
237
238 /* Setup the pointer to the message area. */
239 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
240 echo_header_ptr = (NX_ICMPV6_ECHO *)request_ptr -> nx_packet_prepend_ptr;
241
242 /* Write the ICMP type into the message. Use the lower 16-bits of the IP address for
243 the ICMP identifier. */
244 echo_header_ptr -> nx_icmpv6_echo_header.nx_icmpv6_header_type = NX_ICMPV6_ECHO_REQUEST_TYPE;
245 echo_header_ptr -> nx_icmpv6_echo_header.nx_icmpv6_header_code = 0;
246 echo_header_ptr -> nx_icmpv6_echo_header.nx_icmpv6_header_checksum = 0;
247
248 echo_header_ptr -> nx_icmpv6_echo_identifier = (USHORT)(ipv6_address -> nxd_ipv6_address[3] & NX_LOWER_16_MASK);
249
250 NX_CHANGE_USHORT_ENDIAN(echo_header_ptr -> nx_icmpv6_echo_identifier);
251
252 sequence = (ip_ptr -> nx_ip_icmp_sequence++ & NX_LOWER_16_MASK);
253 echo_header_ptr -> nx_icmpv6_echo_sequence_num = (USHORT)(sequence);
254
255 NX_CHANGE_USHORT_ENDIAN(echo_header_ptr -> nx_icmpv6_echo_sequence_num);
256
257 #ifdef NX_DISABLE_ICMPV6_TX_CHECKSUM
258 compute_checksum = 0;
259 #endif /* NX_DISABLE_ICMPV6_TX_CHECKSUM */
260
261 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
262 if (ipv6_address -> nxd_ipv6_address_attached -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM)
263 {
264 compute_checksum = 0;
265 }
266 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
267 #ifdef NX_IPSEC_ENABLE
268 if (sa != NX_NULL)
269 {
270 if (((NX_IPSEC_SA *)sa) -> nx_ipsec_sa_encryption_method != NX_CRYPTO_NONE)
271 {
272 compute_checksum = 1;
273 }
274 }
275
276 #endif /* NX_IPSEC_ENABLE */
277
278 #if defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
279 if (compute_checksum)
280 #endif /* defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
281 {
282 /* Compute the checksum of the ICMP packet. */
283 checksum = _nx_ip_checksum_compute(request_ptr, NX_PROTOCOL_ICMPV6,
284 (UINT)request_ptr -> nx_packet_length,
285 ipv6_address -> nxd_ipv6_address,
286 ip_address -> nxd_ip_address.v6);
287
288 echo_header_ptr -> nx_icmpv6_echo_header.nx_icmpv6_header_checksum = (USHORT) ~checksum;
289
290 /* Endian swap */
291 NX_CHANGE_USHORT_ENDIAN(echo_header_ptr -> nx_icmpv6_echo_header.nx_icmpv6_header_checksum);
292 }
293 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
294 else
295 {
296 request_ptr -> nx_packet_interface_capability_flag |= NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM;
297 }
298 #endif
299 /* Obtain the IP internal mutex to prevent against possible suspension later in the
300 call to IP packet send. */
301 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
302
303 /* Disable interrupts. */
304 TX_DISABLE
305
306 /* Temporarily disable preemption. */
307 _tx_thread_preempt_disable++;
308
309 /* Pickup thread pointer. */
310 thread_ptr = _tx_thread_current_ptr;
311
312 /* Determine if the request specifies suspension. */
313 if (wait_option)
314 {
315
316 /* Prepare for suspension of this thread. */
317
318 /* Setup cleanup routine pointer. */
319 thread_ptr -> tx_thread_suspend_cleanup = _nx_icmp_cleanup;
320
321 thread_ptr -> tx_thread_suspend_status = NX_NO_RESPONSE;
322
323 /* Setup cleanup information, i.e. this pool control
324 block. */
325 thread_ptr -> tx_thread_suspend_control_block = (void *)ip_ptr;
326
327 /* Save the return packet pointer address as well. */
328 thread_ptr -> tx_thread_additional_suspend_info = (void *)response_ptr;
329
330 /* Save the sequence number so this can be matched up with an ICMP
331 response later. */
332 thread_ptr -> tx_thread_suspend_info = sequence;
333
334 /* Setup suspension list. */
335 if (ip_ptr -> nx_ip_icmp_ping_suspension_list)
336 {
337
338 /* This list is not NULL, add current thread to the end. */
339 thread_ptr -> tx_thread_suspended_next =
340 ip_ptr -> nx_ip_icmp_ping_suspension_list;
341
342 thread_ptr -> tx_thread_suspended_previous =
343 (ip_ptr -> nx_ip_icmp_ping_suspension_list) -> tx_thread_suspended_previous;
344
345 ((ip_ptr -> nx_ip_icmp_ping_suspension_list) -> tx_thread_suspended_previous) -> tx_thread_suspended_next =
346 thread_ptr;
347
348 (ip_ptr -> nx_ip_icmp_ping_suspension_list) -> tx_thread_suspended_previous = thread_ptr;
349 }
350 else
351 {
352
353 /* No other threads are suspended. Setup the head pointer and
354 just setup this threads pointers to itself. */
355 ip_ptr -> nx_ip_icmp_ping_suspension_list = thread_ptr;
356 thread_ptr -> tx_thread_suspended_next = thread_ptr;
357 thread_ptr -> tx_thread_suspended_previous = thread_ptr;
358 }
359
360 /* Increment the suspended thread count. */
361 ip_ptr -> nx_ip_icmp_ping_suspended_count++;
362
363 /* Set the state to suspended. */
364 thread_ptr -> tx_thread_state = TX_TCP_IP;
365
366 /* Set the suspending flag. */
367 thread_ptr -> tx_thread_suspending = TX_TRUE;
368
369 /* Save the timeout value. */
370 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks = wait_option;
371 }
372
373 /* Restore interrupts. */
374 TX_RESTORE
375
376 /* Send the ICMP packet to the IP component. */
377 _nx_ipv6_packet_send(ip_ptr, request_ptr, NX_PROTOCOL_ICMPV6,
378 request_ptr -> nx_packet_length, ip_ptr -> nx_ipv6_hop_limit,
379 ipv6_address -> nxd_ipv6_address,
380 ip_address -> nxd_ip_address.v6);
381
382 /* If wait option is requested, suspend the thread. */
383 if (wait_option)
384 {
385
386 /* Release the protection on the ARP list. */
387 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
388
389 /* Call actual thread suspension routine. */
390 _tx_thread_system_suspend(thread_ptr);
391
392 if (thread_ptr -> tx_thread_suspend_status == NX_SUCCESS)
393 {
394
395 /* Add debug information. */
396 NX_PACKET_DEBUG(__FILE__, __LINE__, *response_ptr);
397 }
398
399 /* Return the status from the thread control block. */
400 return(thread_ptr -> tx_thread_suspend_status);
401 }
402 else
403 {
404
405 /* Disable interrupts. */
406 TX_DISABLE
407
408 /* Release preemption disable. */
409 _tx_thread_preempt_disable--;
410
411 /* Restore interrupts. */
412 TX_RESTORE
413
414 /* Release the protection mutex. */
415 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
416
417 /* Check for preemption. */
418 _tx_thread_system_preempt_check();
419
420 /* Immediate return, return error completion. */
421 return(NX_NO_RESPONSE);
422 }
423 }
424
425 #endif /* FEATURE_NX_IPV6 */
426
427