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_packet.h"
29 #include "nx_ipv6.h"
30 #include "nx_icmpv6.h"
31 
32 #ifdef NX_IPSEC_ENABLE
33 #include "nx_ipsec.h"
34 #endif /* NX_IPSEC_ENABLE */
35 
36 
37 #ifdef FEATURE_NX_IPV6
38 
39 
40 /**************************************************************************/
41 /*                                                                        */
42 /*  FUNCTION                                               RELEASE        */
43 /*                                                                        */
44 /*    _nx_icmpv6_process_echo_request                     PORTABLE C      */
45 /*                                                           6.1          */
46 /*  AUTHOR                                                                */
47 /*                                                                        */
48 /*    Yuxin Zhou, Microsoft Corporation                                   */
49 /*                                                                        */
50 /*  DESCRIPTION                                                           */
51 /*                                                                        */
52 /*    This internal function processes incoming echo request message.     */
53 /*    It validates the echo request and sends an echo reply back to the   */
54 /*    sender.  Note that when formulating an echo reply, the function     */
55 /*    updates the corresponding IPv6 and ICMPv6 header. The content       */
56 /*    of the ICMP echo request message is untouched.  The ICMPv6          */
57 /*    checksum computation also takes a shortcut by adjusting the         */
58 /*    original checksum values for ICMPv6 header field changes.           */
59 /*                                                                        */
60 /*  INPUT                                                                 */
61 /*                                                                        */
62 /*    ip_ptr                  IP stack instance                           */
63 /*    packet_ptr              Received echo request packet                */
64 /*                                                                        */
65 /*  OUTPUT                                                                */
66 /*                                                                        */
67 /*    None                                                                */
68 /*                                                                        */
69 /*  CALLS                                                                 */
70 /*                                                                        */
71 /*    _nx_packet_release      Release packet back to the packet pool      */
72 /*    _nx_ipv6_packet_send    Transmit IPv6 packet to remote host         */
73 /*                                                                        */
74 /*  CALLED BY                                                             */
75 /*                                                                        */
76 /*    _nx_icmpv6_packet_process            Main ICMPv6 packet handler     */
77 /*                                                                        */
78 /*  RELEASE HISTORY                                                       */
79 /*                                                                        */
80 /*    DATE              NAME                      DESCRIPTION             */
81 /*                                                                        */
82 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
83 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
84 /*                                            resulting in version 6.1    */
85 /*                                                                        */
86 /**************************************************************************/
87 
_nx_icmpv6_process_echo_request(NX_IP * ip_ptr,NX_PACKET * packet_ptr)88 VOID _nx_icmpv6_process_echo_request(NX_IP *ip_ptr, NX_PACKET *packet_ptr)
89 {
90 
91 UINT              status;
92 ULONG             tmp;
93 USHORT            checksum;
94 #if defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
95 UINT              compute_checksum = 1;
96 #endif /* defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
97 NX_ICMPV6_HEADER *header_ptr;
98 NX_IPV6_HEADER   *ipv6_header;
99 ULONG             hop_limit = 255;
100 NXD_ADDRESS       dest_addr;
101 NX_INTERFACE     *interface_ptr;
102 
103 #ifdef NX_IPSEC_ENABLE
104 ULONG             data_offset;
105 VOID             *sa = NX_NULL;
106 NXD_ADDRESS       src_addr;
107 UINT              ret;
108 #endif /* NX_IPSEC_ENABLE */
109 
110 
111     /* Add debug information. */
112     NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
113 
114 #ifndef NX_DISABLE_RX_SIZE_CHECKING
115     /* Check packet length. */
116     if (packet_ptr -> nx_packet_length < sizeof(NX_ICMPV6_ECHO))
117     {
118 #ifndef NX_DISABLE_ICMP_INFO
119 
120         /* Increment the ICMP invalid message count.  */
121         ip_ptr -> nx_ip_icmp_invalid_packets++;
122 #endif
123 
124         /* Invalid ICMP message, just release it.  */
125         _nx_packet_release(packet_ptr);
126         return;
127     }
128 #endif /* NX_DISABLE_RX_SIZE_CHECKING */
129 
130     /* Points to the ICMP message header.  */
131     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
132     header_ptr  = (NX_ICMPV6_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
133 
134     /* Points to the IPv6 header. */
135     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
136     ipv6_header = (NX_IPV6_HEADER *)packet_ptr -> nx_packet_ip_header;
137 
138     /* Check if the destination address is multicast address.  */
139     if (IPv6_Address_Type(ipv6_header -> nx_ip_header_destination_ip) & IPV6_ADDRESS_MULTICAST)
140     {
141 
142         /* Yes, Set the interface.  */
143         interface_ptr = packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached;
144 
145         /* Find a suitable outgoing address. */
146         status = _nxd_ipv6_interface_find(ip_ptr, ipv6_header -> nx_ip_header_source_ip,
147                                           &packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr, interface_ptr);
148 
149         /* Cannot find usable outgoing interface. */
150         if (status != NX_SUCCESS)
151         {
152 
153             /* Release the packet. */
154             _nx_packet_release(packet_ptr);
155 
156             return;
157         }
158     }
159     else
160     {
161 
162         /* Make sure the interface IP address has been validated. */
163         if (packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_state != NX_IPV6_ADDR_STATE_VALID)
164         {
165 
166             /* Not validated, so release the packet and abort.*/
167             _nx_packet_release(packet_ptr);
168 
169             return;
170         }
171     }
172 
173     /* Discard the packet if source address is unspecified (::). */
174     if (CHECK_UNSPECIFIED_ADDRESS(ipv6_header -> nx_ip_header_source_ip))
175     {
176 
177         /* NULL address in the header. Release the packet and abort. */
178         _nx_packet_release(packet_ptr);
179 
180         return;
181     }
182 
183 #ifndef NX_DISABLE_ICMP_INFO
184     /* Increment the ICMP pings received count.  */
185     ip_ptr -> nx_ip_pings_received++;
186 #endif
187 
188     /* Respond to echo request packet.  */
189 
190     /* Set up the destination address. */
191     dest_addr.nxd_ip_version = NX_IP_VERSION_V6;
192     dest_addr.nxd_ip_address.v6[0] = ipv6_header -> nx_ip_header_source_ip[0];
193     dest_addr.nxd_ip_address.v6[1] = ipv6_header -> nx_ip_header_source_ip[1];
194     dest_addr.nxd_ip_address.v6[2] = ipv6_header -> nx_ip_header_source_ip[2];
195     dest_addr.nxd_ip_address.v6[3] = ipv6_header -> nx_ip_header_source_ip[3];
196 
197 #ifdef NX_IPSEC_ENABLE
198 
199     /* Set up the source address for IPSec SA lookup. */
200     src_addr.nxd_ip_version = NX_IP_VERSION_V6;
201 
202     COPY_IPV6_ADDRESS(packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address,
203                       src_addr.nxd_ip_address.v6);
204 
205     /* Check if IPsec is enabled. */
206     if (ip_ptr -> nx_ip_packet_egress_sa_lookup != NX_NULL)
207     {
208 
209         /* Check for possible SA match. */
210         ret = ip_ptr -> nx_ip_packet_egress_sa_lookup(ip_ptr,                                    /* IP ptr */
211                                                       &src_addr,                                 /* src_addr */
212                                                       &dest_addr,                                /* dest_addr */
213                                                       NX_PROTOCOL_ICMPV6,                        /* protocol */
214                                                       0,                                         /* port, not used. */
215                                                       0,                                         /* port, not used. */
216                                                       &data_offset, &sa, (NX_ICMPV6_ECHO_REPLY_TYPE << 8));
217 
218         /* We have a match; apply IPSec processing on this packet. */
219         if (ret == NX_IPSEC_TRAFFIC_PROTECT)
220         {
221 
222             /* Make sure the outgoing packet has enough space for IPsec header info. */
223             if ((ULONG)(packet_ptr -> nx_packet_prepend_ptr - packet_ptr -> nx_packet_data_start) <
224                 (NX_IPv6_PACKET + data_offset))
225             {
226 
227                 /* Not enough space.   Release the packet and return. */
228                 _nx_packet_release(packet_ptr);
229                 return;
230             }
231 
232             /* Save the SA to the packet. */
233             packet_ptr -> nx_packet_ipsec_sa_ptr = sa;
234         }
235         else if (ret == NX_IPSEC_TRAFFIC_DROP || ret == NX_IPSEC_TRAFFIC_PENDING_IKEV2)
236         {
237 
238             /* IPSec SA disallows this packet. Drop the packet and return. */
239             _nx_packet_release(packet_ptr);
240 
241             return;
242         }
243         else
244         {
245             /* IPSec SA indicates the packet requires no IPSec processing.
246                Zero out sa information. */
247             packet_ptr -> nx_packet_ipsec_sa_ptr = NX_NULL;
248         }
249     }
250 #endif /* NX_IPSEC_ENABLE */
251 
252 
253     /* Change the type to Echo Reply and send back the message to the caller.  */
254     header_ptr -> nx_icmpv6_header_type = NX_ICMPV6_ECHO_REPLY_TYPE;
255 
256 #ifdef NX_DISABLE_ICMPV6_TX_CHECKSUM
257     compute_checksum = 0;
258 #endif /* NX_DISABLE_ICMPV6_TX_CHECKSUM */
259 
260 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
261     if (packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM)
262     {
263         compute_checksum = 0;
264     }
265 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
266 
267 #ifdef NX_IPSEC_ENABLE
268     if ((packet_ptr -> nx_packet_ipsec_sa_ptr != NX_NULL) && (((NX_IPSEC_SA *)(packet_ptr -> nx_packet_ipsec_sa_ptr)) -> nx_ipsec_sa_encryption_method != NX_CRYPTO_NONE))
269     {
270         compute_checksum = 1;
271     }
272 #endif
273 #if defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
274     if (compute_checksum)
275 #endif /* defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
276     {
277 
278         /* Take a short cut to fix the checksum. */
279         checksum = header_ptr -> nx_icmpv6_header_checksum;
280 
281         /* Change to host byte order. */
282         NX_CHANGE_USHORT_ENDIAN(checksum);
283 
284         tmp = ((USHORT)(~checksum) & 0xFFFF);
285 
286         /* The original ICMP type is ECHO_REQUEST. */
287         tmp -= (NX_ICMPV6_ECHO_REQUEST_TYPE << 8);
288         if (tmp > (ULONG)0x80000000)
289         {
290             tmp = (tmp & 0xFFFF) - 1;
291         }
292 
293 
294         tmp += (ULONG)(header_ptr -> nx_icmpv6_header_type << 8);
295 
296         /* Compute the checksum differently depending if the echo request sends to
297            a multicast or unicast address.   */
298         if ((IPv6_Address_Type(ipv6_header -> nx_ip_header_destination_ip) &
299              IPV6_ADDRESS_MULTICAST) == IPV6_ADDRESS_MULTICAST)
300         {
301 
302             /* Compute the checksum for a multicast address. */
303             header_ptr -> nx_icmpv6_header_checksum = 0;
304 
305             tmp = _nx_ip_checksum_compute(packet_ptr,
306                                           NX_PROTOCOL_ICMPV6,
307                                           (UINT)packet_ptr -> nx_packet_length,
308                                           packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address,
309                                           ipv6_header -> nx_ip_header_source_ip);
310 
311             tmp = ~tmp;
312 
313             header_ptr -> nx_icmpv6_header_checksum = (USHORT)(tmp);
314 
315             NX_CHANGE_USHORT_ENDIAN(header_ptr -> nx_icmpv6_header_checksum);
316 
317             hop_limit = 255;
318         }
319         else
320         {
321             /* Compute the checksum for a unicast address. */
322             hop_limit = ip_ptr -> nx_ipv6_hop_limit;
323 
324             tmp = (tmp >> 16) + (tmp & 0xFFFF);
325 
326             /* Do it again in case of carrying */
327             tmp = (tmp >> 16) + (tmp & 0xFFFF);
328             header_ptr -> nx_icmpv6_header_checksum = (USHORT)(~tmp);
329             NX_CHANGE_USHORT_ENDIAN(header_ptr -> nx_icmpv6_header_checksum);
330         }
331     }
332 #if defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY)
333     else
334     {
335 
336         /* Fix the bug when NX_DISABLE_ICMP_TX_CHECKSUM has been set, the nx_icmpv6_header_type is not modified.  */
337         /* Change the type to Echo Reply and send back the message to the caller.  */
338         header_ptr -> nx_icmpv6_header_checksum = 0;
339 
340         if ((IPv6_Address_Type(ipv6_header -> nx_ip_header_destination_ip) &
341              IPV6_ADDRESS_MULTICAST) == IPV6_ADDRESS_MULTICAST)
342         {
343             hop_limit = 255;
344         }
345         else
346         {
347 
348             hop_limit = ip_ptr -> nx_ipv6_hop_limit;
349         }
350 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
351         packet_ptr -> nx_packet_interface_capability_flag |= NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM;
352 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
353     }
354 #endif
355 
356 #ifndef NX_DISABLE_ICMP_INFO
357     /* Increment the ICMP pings responded to count.  */
358     ip_ptr -> nx_ip_pings_responded_to++;
359 #endif
360 
361     /* Send the ICMP packet to the IP component.  */
362     _nx_ipv6_packet_send(ip_ptr, packet_ptr, NX_PROTOCOL_ICMPV6,
363                          packet_ptr -> nx_packet_length, hop_limit,
364                          packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address,
365                          dest_addr.nxd_ip_address.v6);
366 }
367 
368 
369 #endif /* FEATURE_NX_IPV6 */
370 
371