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