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