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