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