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