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