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