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