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