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 (ICMPv6)                          */
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_ip.h"
31 #include "nx_ipv6.h"
32 #include "nx_icmpv6.h"
33 
34 #ifdef NX_IPSEC_ENABLE
35 #include "nx_ipsec.h"
36 #endif /* NX_IPSEC_ENABLE */
37 
38 #ifdef FEATURE_NX_IPV6
39 #ifndef NX_DISABLE_ICMPV6_ERROR_MESSAGE
40 /**************************************************************************/
41 /*                                                                        */
42 /*  FUNCTION                                               RELEASE        */
43 /*                                                                        */
44 /*    _nx_icmpv6_send_error_message                      PORTABLE C       */
45 /*                                                           6.1.10       */
46 /*  AUTHOR                                                                */
47 /*                                                                        */
48 /*    Yuxin Zhou, Microsoft Corporation                                   */
49 /*                                                                        */
50 /*  DESCRIPTION                                                           */
51 /*                                                                        */
52 /*    This function is called by various IPv6 components to send an       */
53 /*    error message when necessary.                                       */
54 /*                                                                        */
55 /*  INPUT                                                                 */
56 /*                                                                        */
57 /*    ip_ptr                                IP stack instance             */
58 /*    offending_packet                      The packet that caused the    */
59 /*                                              error.                    */
60 /*    word1                                 ICMPv6 error message header   */
61 /*                                              field, progarmmed by      */
62 /*                                              the caller.               */
63 /*    error_pointer                         Pointer to the byte that      */
64 /*                                              caused the error          */
65 /*                                                                        */
66 /*  OUTPUT                                                                */
67 /*                                                                        */
68 /*    None                                                                */
69 /*                                                                        */
70 /*  CALLS                                                                 */
71 /*                                                                        */
72 /*    _nx_ip_checksum_compute               Computer ICMP checksum        */
73 /*    _nx_ipv6_packet_send                  Send ICMP packet out          */
74 /*    _nx_packet_allocate                   Packet allocate               */
75 /*    _nx_packet_release                    Release packet back to pool   */
76 /*    _nxd_ipv6_interface_find              Find outgoing interface for   */
77 /*                                             sending packet             */
78 /*                                                                        */
79 /*  CALLED BY                                                             */
80 /*                                                                        */
81 /*    _nx_ip_fragment_assembly                                            */
82 /*    _nx_ipv6_packet_receive                                             */
83 /*    _nx_udp_packet_receive                                              */
84 /*    _nx_ipv6_header_option_process                                      */
85 /*                                                                        */
86 /*  RELEASE HISTORY                                                       */
87 /*                                                                        */
88 /*    DATE              NAME                      DESCRIPTION             */
89 /*                                                                        */
90 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
91 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
92 /*                                            resulting in version 6.1    */
93 /*  01-31-2022     Yuxin Zhou               Modified comment(s),          */
94 /*                                            fixed unsigned integers     */
95 /*                                            comparison,                 */
96 /*                                            resulting in version 6.1.10 */
97 /*                                                                        */
98 /**************************************************************************/
_nx_icmpv6_send_error_message(NX_IP * ip_ptr,NX_PACKET * offending_packet,ULONG word1,ULONG error_pointer)99 VOID _nx_icmpv6_send_error_message(NX_IP *ip_ptr, NX_PACKET *offending_packet,
100                                    ULONG word1, ULONG error_pointer)
101 {
102 
103 NX_PACKET       *pkt_ptr;
104 USHORT           checksum;
105 #if defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
106 UINT             compute_checksum = 1;
107 #endif /* defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
108 NX_ICMPV6_ERROR *icmpv6_error;
109 UINT             bytes_to_copy, i;
110 ULONG           *src_ip, *dest_ip;
111 ULONG           *src_packet, *dest_packet;
112 UINT             payload;
113 #ifdef NX_IPSEC_ENABLE
114 VOID            *sa = NX_NULL;
115 UINT             ret = 0;
116 ULONG            data_offset;
117 NXD_ADDRESS      src_addr;
118 NXD_ADDRESS      dest_addr;
119 #endif /* NX_IPSEC_ENABLE */
120 
121 
122     /* Add debug information. */
123     NX_PACKET_DEBUG(__FILE__, __LINE__, offending_packet);
124 
125     /* Do not send ICMPv6 error message if ICMPv6 is not enabled. */
126     if (ip_ptr -> nx_ip_icmpv6_packet_process == NX_NULL)
127     {
128         return;
129     }
130 
131     /* Find out the source and destination IP addresses of the offending packet. */
132     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
133     src_ip = (((NX_IPV6_HEADER *)(offending_packet -> nx_packet_ip_header)) -> nx_ip_header_source_ip);
134 
135     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
136     dest_ip = (((NX_IPV6_HEADER *)(offending_packet -> nx_packet_ip_header)) -> nx_ip_header_destination_ip);
137 
138     if (CHECK_UNSPECIFIED_ADDRESS(src_ip))
139     {
140         /*
141          * Sender of the offending packet is unspecified.
142          * So we shouldn't send out ICMP error message.
143          * Drop the packet and return.
144          */
145         return;
146     }
147 
148     /* Allocate a packet to build the ICMPv6 error message in.  */
149     if (_nx_packet_allocate(ip_ptr -> nx_ip_default_packet_pool, &pkt_ptr, NX_IPv6_ICMP_PACKET, NX_NO_WAIT))
150     {
151 
152         /* Error getting packet, so just get out!  */
153         return;
154     }
155 
156     /* Check to see if the packet has enough room to fill with the ICMPv6 error header.  */
157     if ((UINT)(pkt_ptr -> nx_packet_data_end - pkt_ptr -> nx_packet_prepend_ptr) < sizeof(NX_ICMPV6_ERROR))
158     {
159 
160         /* Error getting packet, so just get out!  */
161         _nx_packet_release(pkt_ptr);
162         return;
163     }
164 
165     /* Add debug information. */
166     NX_PACKET_DEBUG(__FILE__, __LINE__, pkt_ptr);
167 
168     /* Mark the packet as IPv6. */
169     /*lint -e{644} suppress variable might not be initialized, since "pkt_ptr" was initialized in _nx_packet_allocate. */
170     pkt_ptr -> nx_packet_ip_version = NX_IP_VERSION_V6;
171 
172     /* Setup the size of the ICMPv6 NA message */
173 
174     /* Size of the message is ICMPv6 */
175     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
176     icmpv6_error = (NX_ICMPV6_ERROR *)(pkt_ptr -> nx_packet_prepend_ptr);
177     icmpv6_error -> nx_icmpv6_error_header.nx_icmpv6_header_type = (UCHAR)((word1 >> 24) & 0xFF);
178     icmpv6_error -> nx_icmpv6_error_header.nx_icmpv6_header_code = (UCHAR)((word1 >> 16) & 0xFF);
179     icmpv6_error -> nx_icmpv6_error_header.nx_icmpv6_header_checksum = 0;
180 
181     icmpv6_error -> nx_icmpv6_error_pointer = error_pointer;
182 
183     /* Change to network byte order. */
184     NX_CHANGE_ULONG_ENDIAN(icmpv6_error -> nx_icmpv6_error_pointer);
185 
186     /* Figure out how many bytes we should copy from the offending packet not including ethernet
187        frame header. */
188     /*lint -e{946} -e{947} suppress pointer subtraction, since it is necessary. */
189     bytes_to_copy = (UINT)(offending_packet -> nx_packet_append_ptr - offending_packet -> nx_packet_ip_header);
190 
191     /* Check that the number of bytes to copy does not exceed the minimum size ICMPv6 message
192        as per RFC 2460. */
193     if ((bytes_to_copy + sizeof(NX_ICMPV6_ERROR) + sizeof(NX_IPV6_HEADER)) >= NX_MINIMUM_IPV6_PATH_MTU)
194     {
195 
196         /* Subtract size of IPv6 and ICMPv6 headers from the ICMPv6 error message packet. */
197         bytes_to_copy = (UINT)(NX_MINIMUM_IPV6_PATH_MTU - (sizeof(NX_IPV6_HEADER) + sizeof(NX_ICMPV6_ERROR)));
198     }
199 
200     /* Check how much of the offending packet data will fit in the allocated packet, leaving
201        room for the Physical frame header, IPv6 header and ICMPv6 header of the error message. */
202     payload = pkt_ptr -> nx_packet_pool_owner -> nx_packet_pool_payload_size;
203 
204     if (((INT)((bytes_to_copy + sizeof(NX_IPV6_HEADER) + sizeof(NX_ICMPV6_ERROR) + NX_PHYSICAL_HEADER) - payload)) > 0)
205     {
206 
207         bytes_to_copy = (UINT)(payload - (sizeof(NX_IPV6_HEADER) + sizeof(NX_ICMPV6_ERROR) + NX_PHYSICAL_HEADER));
208     }
209 
210     /* Set the packet length and pointers.  The length will be increased to include
211        the IPv6 header in the IP send function.  The Prepend function will be similarly
212        updated in the IP send function. */
213     pkt_ptr -> nx_packet_length = bytes_to_copy + (ULONG)sizeof(NX_ICMPV6_ERROR);
214     pkt_ptr -> nx_packet_append_ptr = pkt_ptr -> nx_packet_prepend_ptr + pkt_ptr -> nx_packet_length;
215 
216     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
217     src_packet  = (ULONG *)(offending_packet -> nx_packet_ip_header);
218 
219     /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary  */
220     dest_packet = (ULONG *)NX_UCHAR_POINTER_ADD(icmpv6_error, sizeof(NX_ICMPV6_ERROR));
221 
222     /* Endian swap the incoming IPv6 header (10 ULONGs = 40 bytes)
223        to network byte order. */
224     for (i = 0; i < 10; i++)
225     {
226         NX_CHANGE_ULONG_ENDIAN(*src_packet);
227         src_packet++;
228     }
229 
230     /* Reset the packet pointer to the received packet IP header. */
231     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
232     src_packet  = (ULONG *)(offending_packet -> nx_packet_ip_header);
233 
234     /* Copy the data from the received packet to the ICMPv6 error packet. */
235     for (; (INT)bytes_to_copy > 0; bytes_to_copy -= 4)
236     {
237 
238         *dest_packet++ = *src_packet++;
239     }
240 
241     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
242     src_packet  = (ULONG *)(offending_packet -> nx_packet_ip_header);
243 
244     /* Endian swap the IPv6 header back to host byte order. */
245     for (i = 0; i < 10; i++)
246     {
247         NX_CHANGE_ULONG_ENDIAN(*src_packet);
248         src_packet++;
249     }
250 
251     /* If we received the packet through a Multicast address, we pick an outgoing address
252        based on multicast scope (RFC 3484, 3.1) */
253     if (IPv6_Address_Type(dest_ip) & IPV6_ADDRESS_MULTICAST)
254     {
255 
256         if (_nxd_ipv6_interface_find(ip_ptr, dest_ip,
257                                      &pkt_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr,
258                                      NX_NULL))
259         {
260 
261             /* Cannot find usable outgoing interface. */
262             _nx_packet_release(pkt_ptr);
263             return;
264         }
265     }
266     else
267     {
268 
269         /* If this ICMPv6 error message is a response to a packet sent to link local or global address,
270            use the corresponding interface address as sender's address. */
271         pkt_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr = offending_packet -> nx_packet_address.nx_packet_ipv6_address_ptr;
272     }
273 
274     /*
275        Check if a suitable outoing address was found, and the
276        outgoing address is not valid:
277      */
278     if ((pkt_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr == NX_NULL) ||
279         (pkt_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_state != NX_IPV6_ADDR_STATE_VALID))
280     {
281 
282         /* No good. Drop the packet and return. */
283         _nx_packet_release(pkt_ptr);
284         return;
285     }
286 
287 #ifdef NX_IPSEC_ENABLE
288 
289     /* Check for possible SA match. */
290     if (ip_ptr -> nx_ip_packet_egress_sa_lookup != NX_NULL)               /* IPsec is enabled. */
291     {
292 
293         /* Set up IP address. */
294         src_addr.nxd_ip_version = NX_IP_VERSION_V6;
295 
296         COPY_IPV6_ADDRESS(pkt_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address,
297                           src_addr.nxd_ip_address.v6);
298 
299         dest_addr.nxd_ip_version = NX_IP_VERSION_V6;
300 
301         COPY_IPV6_ADDRESS(src_ip, dest_addr.nxd_ip_address.v6);
302 
303         /* If the SA has not been set. */
304         ret = ip_ptr -> nx_ip_packet_egress_sa_lookup(ip_ptr,                 /* IP ptr */
305                                                       &src_addr,              /* src_addr */
306                                                       &dest_addr,             /* dest_addr */
307                                                       NX_PROTOCOL_ICMPV6,     /* protocol */
308                                                       0,                      /* src_port */
309                                                       0,                      /* dest_port */
310                                                       &data_offset, &sa,
311                                                       ((word1 >> 16) & 0xFFFF));
312         if (ret == NX_IPSEC_TRAFFIC_BYPASS)
313         {
314             sa = NX_NULL;
315             data_offset = 0;
316         }
317         else if (ret == NX_IPSEC_TRAFFIC_DROP || ret == NX_IPSEC_TRAFFIC_PENDING_IKEV2)
318         {
319 
320             /* IPSec SA disallows this packet. Drop the packet and return. */
321             _nx_packet_release(pkt_ptr);
322 
323             return;
324         }
325     }
326 
327     pkt_ptr -> nx_packet_ipsec_sa_ptr = sa;
328 
329 #endif /* NX_IPSEC_ENABLE */
330 
331 #ifdef NX_DISABLE_ICMPV6_TX_CHECKSUM
332     compute_checksum = 0;
333 #endif /* NX_DISABLE_ICMPV6_TX_CHECKSUM */
334 
335 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
336     if (pkt_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM)
337     {
338         compute_checksum = 0;
339     }
340 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
341 
342 #ifdef NX_IPSEC_ENABLE
343     if ((sa != NX_NULL) && (((NX_IPSEC_SA *)sa) -> nx_ipsec_sa_encryption_method != NX_CRYPTO_NONE))
344     {
345         compute_checksum = 1;
346     }
347 #endif /* NX_IPSEC_ENABLE */
348 #if defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
349     if (compute_checksum)
350 #endif /* defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
351     {
352         /* Compute the check sum */
353         checksum = _nx_ip_checksum_compute(pkt_ptr, NX_PROTOCOL_ICMPV6,
354                                            (UINT)pkt_ptr -> nx_packet_length,
355                                            pkt_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address,
356                                            src_ip);
357 
358         icmpv6_error -> nx_icmpv6_error_header.nx_icmpv6_header_checksum = (USHORT)(~checksum);
359 
360         /* Swap to network byte order. */
361         NX_CHANGE_USHORT_ENDIAN(icmpv6_error -> nx_icmpv6_error_header.nx_icmpv6_header_checksum);
362     }
363 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
364     else
365     {
366         pkt_ptr -> nx_packet_interface_capability_flag |= NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM;
367     }
368 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
369 
370     /* Transmit the packet.  The hop limit is set to 255. */
371     _nx_ipv6_packet_send(ip_ptr, pkt_ptr, NX_PROTOCOL_ICMPV6, pkt_ptr -> nx_packet_length, 255,
372                          pkt_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address,
373                          src_ip);
374 
375     return;
376 }
377 #endif /* NX_DISABLE_ICMPV6_ERROR_MESSAGE */
378 #endif /* FEATURE_NX_IPV6 */
379 
380