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