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