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 /** NetX Component */
15 /** */
16 /** Internet Control Message Protocol (ICMP) */
17 /** */
18 /**************************************************************************/
19 /**************************************************************************/
20
21 #define NX_SOURCE_CODE
22
23
24 /* Include necessary system files. */
25 #include "nx_api.h"
26 #include "nx_packet.h"
27 #include "nx_ip.h"
28 #include "nx_ipv6.h"
29 #include "nx_icmpv6.h"
30
31 #ifdef FEATURE_NX_IPV6
32
33
34
35 /**************************************************************************/
36 /* */
37 /* FUNCTION RELEASE */
38 /* */
39 /* _nx_icmpv6_process_redirect PORTABLE C */
40 /* 6.3.0 */
41 /* AUTHOR */
42 /* */
43 /* Yuxin Zhou, Microsoft Corporation */
44 /* */
45 /* DESCRIPTION */
46 /* */
47 /* This function processes an incoming ICMPv6 redirect message. */
48 /* */
49 /* INPUT */
50 /* */
51 /* ip_ptr Pointer to IP control block */
52 /* packet_ptr ICMP packet pointer */
53 /* */
54 /* OUTPUT */
55 /* */
56 /* NONE */
57 /* */
58 /* CALLS */
59 /* */
60 /* IPv6_Address_Type Find IP address type. */
61 /* _nxd_ipv6_destination_table_find_next_hop */
62 /* Finds next hop address. */
63 /* _nxd_ipv6_router_lookup Routine that finds default */
64 /* router. */
65 /* _nx_icmpv6_validate_options Check for valid option headers */
66 /* _nx_nd_cache_find_entry Find entry in ND cache by IP */
67 /* address */
68 /* _nx_nd_cache_add Add entry to ND cache */
69 /* _nxd_ipv6_default_router_add_internal Update router list with */
70 /* redirect next hop */
71 /* _nx_icmpv6_send_queued_packets Transmit queued packets waiting */
72 /* for physical mapping */
73 /* */
74 /* CALLED BY */
75 /* */
76 /* _nx_icmpv6_packet_process Main ICMPv6 packet pocess */
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 /* 10-31-2023 Bo Chen Modified comment(s), improved */
86 /* packet length verification, */
87 /* resulting in version 6.3.0 */
88 /* */
89 /**************************************************************************/
90
91 #ifndef NX_DISABLE_ICMPV6_REDIRECT_PROCESS
92
_nx_icmpv6_process_redirect(NX_IP * ip_ptr,NX_PACKET * packet_ptr)93 VOID _nx_icmpv6_process_redirect(NX_IP *ip_ptr, NX_PACKET *packet_ptr)
94 {
95
96 NX_ICMPV6_REDIRECT_MESSAGE *redirect_ptr;
97 NX_IPV6_HEADER *ip_header;
98 UINT status;
99 ULONG source_address_type;
100 ULONG router_address[4];
101 ULONG error = 0;
102 ND_CACHE_ENTRY *nd_entry = NX_NULL;
103 ND_CACHE_ENTRY *NDCacheEntry = NX_NULL;
104 UINT i;
105 NX_ICMPV6_OPTION *option_ptr;
106 ULONG packet_length, option_length;
107 NX_IPV6_DEFAULT_ROUTER_ENTRY *rt_entry;
108 NX_INTERFACE *interface_ptr;
109 #ifdef NX_ENABLE_IPV6_PATH_MTU_DISCOVERY
110 NX_ICMPV6_OPTION_MTU *mtu_ptr;
111 #endif
112 ULONG mtu;
113 ULONG mtu_timeout;
114 NX_IPV6_DESTINATION_ENTRY *dest_entry_ptr;
115
116 /* Add debug information. */
117 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
118
119 #ifndef NX_DISABLE_RX_SIZE_CHECKING
120 if ((packet_ptr -> nx_packet_length < sizeof(NX_ICMPV6_REDIRECT_MESSAGE))
121 #ifndef NX_DISABLE_PACKET_CHAIN
122 || (packet_ptr -> nx_packet_next) /* Ignore chained packet. */
123 #endif /* NX_DISABLE_PACKET_CHAIN */
124 )
125 {
126 #ifndef NX_DISABLE_ICMP_INFO
127
128 /* Increment the ICMP invalid message count. */
129 ip_ptr -> nx_ip_icmp_invalid_packets++;
130 #endif
131
132 /* Invalid ICMP message, just release it. */
133 _nx_packet_release(packet_ptr);
134 return;
135 }
136 #endif /* NX_DISABLE_RX_SIZE_CHECKING */
137
138 /* Get interface pointer. */
139 interface_ptr = packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached;
140
141 /* Initialize the MTU. */
142 mtu = interface_ptr -> nx_interface_ip_mtu_size;
143 mtu_timeout = NX_WAIT_FOREVER;
144
145 /* Locate the IPv6 header. */
146 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
147 ip_header = (NX_IPV6_HEADER *)(packet_ptr -> nx_packet_ip_header);
148
149 /* Locate the redirect message. */
150 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
151 redirect_ptr = (NX_ICMPV6_REDIRECT_MESSAGE *)(packet_ptr -> nx_packet_prepend_ptr);
152
153 /* RFC 2461 4.5 the IPv6 and ICMPv6 header fields. */
154
155 /* Convert the destination and target address to host byte order. */
156 NX_IPV6_ADDRESS_CHANGE_ENDIAN(redirect_ptr -> nx_icmpv6_redirect_target_address);
157 NX_IPV6_ADDRESS_CHANGE_ENDIAN(redirect_ptr -> nx_icmpv6_redirect_destination_address);
158
159 /* Verify that sender address is link-local address. */
160 source_address_type = IPv6_Address_Type(ip_header -> nx_ip_header_source_ip);
161 if ((source_address_type & 0xFF) != IPV6_ADDRESS_LINKLOCAL)
162 {
163 error = 1;
164 }
165 /* Verify that hop limit is 255 */
166 else if ((ip_header -> nx_ip_header_word_1 & 0xFF) != 0xFF)
167 {
168 error = 1;
169 }
170 /* Verifty that ICMP code is zero. */
171 else if (redirect_ptr -> nx_icmpv6_redirect_icmpv6_header.nx_icmpv6_header_code != 0)
172 {
173 error = 1;
174 }
175
176 /* The IP source address of the redirect is the same as the current first-hop
177 router for the specified ICMP destination address. */
178
179 /* First look for redirect in the destination table. */
180 if (_nxd_ipv6_destination_table_find_next_hop(ip_ptr, redirect_ptr -> nx_icmpv6_redirect_destination_address,
181 router_address) == NX_SUCCESS)
182 {
183
184 /* Make sure the source is the current 1st hop router. */
185 if (!CHECK_IPV6_ADDRESSES_SAME(router_address, ip_header -> nx_ip_header_source_ip))
186 {
187 error = 1;
188 }
189 }
190 /* Next check if the redirect is the default router. */
191 /* Suppress cast of pointer to pointer, since it is necessary */
192 else if (_nxd_ipv6_router_lookup(ip_ptr, interface_ptr, router_address, /*lint -e{929}*/ (void **)&NDCacheEntry))
193 {
194 /* No default router. */
195 error = 1;
196 }
197
198 /* Make sure the source is the current 1st hop router. */
199 if (!CHECK_IPV6_ADDRESSES_SAME(ip_header -> nx_ip_header_source_ip, router_address))
200 {
201 error = 1;
202 }
203 /* The destination address field may not be multicast. */
204 else if (IPv6_Address_Type(redirect_ptr -> nx_icmpv6_redirect_destination_address) & IPV6_ADDRESS_MULTICAST)
205 {
206 error = 1;
207 }
208 /* Is target address is different from destination address? */
209 else if (!(CHECK_IPV6_ADDRESSES_SAME(redirect_ptr -> nx_icmpv6_redirect_destination_address,
210 redirect_ptr -> nx_icmpv6_redirect_target_address)))
211 {
212
213 /* Yes, so the target is a router, therefore the target address must be link-local.*/
214 if ((IPv6_Address_Type(redirect_ptr -> nx_icmpv6_redirect_target_address) & IPV6_ADDRESS_LINKLOCAL) == 0)
215 {
216 /* It isn't. */
217 error = 1;
218 }
219 }
220
221 /* Release the packet if it fails these validation checks. */
222 if (error)
223 {
224
225 #ifndef NX_DISABLE_ICMP_INFO
226
227 /* Increment the ICMP invalid packet error. */
228 ip_ptr -> nx_ip_icmp_invalid_packets++;
229 #endif /* NX_DISABLE_ICMP_INFO */
230
231 _nx_packet_release(packet_ptr);
232 return;
233 }
234
235 /* If there are additional options, it could be TLLA or the redirected header. */
236 if (packet_ptr -> nx_packet_length - sizeof(NX_ICMPV6_REDIRECT_MESSAGE))
237 {
238
239 /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary */
240 option_ptr = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(redirect_ptr, sizeof(NX_ICMPV6_REDIRECT_MESSAGE));
241 packet_length = packet_ptr -> nx_packet_length - (ULONG)sizeof(NX_ICMPV6_REDIRECT_MESSAGE);
242
243 /* Validate option fields. */
244 if (_nx_icmpv6_validate_options(option_ptr, (INT)packet_length, 0) == NX_NOT_SUCCESSFUL)
245 {
246
247 #ifndef NX_DISABLE_ICMP_INFO
248
249 /* Increment the ICMP invalid packet error. */
250 ip_ptr -> nx_ip_icmp_invalid_packets++;
251 #endif /* NX_DISABLE_ICMP_INFO */
252
253 _nx_packet_release(packet_ptr);
254 return;
255 }
256
257 while (packet_length)
258 {
259
260 /* The packet contains target link layer address. */
261 if (option_ptr -> nx_icmpv6_option_type == ICMPV6_OPTION_TYPE_TRG_LINK_ADDR)
262 {
263 status = _nx_nd_cache_find_entry(ip_ptr, redirect_ptr -> nx_icmpv6_redirect_target_address, &nd_entry);
264
265
266 if (status != NX_SUCCESS)
267 {
268
269 /* The entry is not found in the ND cache table. Simply add it, and we are done. */
270 /*lint -e{929} -e{740} -e{826} suppress cast of pointer to pointer, since it is necessary */
271 if (_nx_nd_cache_add(ip_ptr, redirect_ptr -> nx_icmpv6_redirect_target_address,
272 interface_ptr,
273 (CHAR *)&option_ptr -> nx_icmpv6_option_data, 0, ND_CACHE_STATE_STALE,
274 packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr, &nd_entry) == NX_SUCCESS)
275 {
276
277 /* If the destination and target addresses are different, the redirect is to a router. */
278 if (!CHECK_IPV6_ADDRESSES_SAME(redirect_ptr -> nx_icmpv6_redirect_target_address,
279 redirect_ptr -> nx_icmpv6_redirect_destination_address))
280 {
281
282 /* We need to fill in the IsRouter field in the cache entry
283 with the default router. */
284 _nxd_ipv6_default_router_add_internal(ip_ptr,
285 redirect_ptr -> nx_icmpv6_redirect_target_address, /* TLLA in Redirect ICMPv6 options */
286 0,
287 interface_ptr,
288 NX_IPV6_ROUTE_TYPE_UNSOLICITATED, &rt_entry);
289
290 /* And cross link this entry with the entry in the router list. */
291 /*lint -e{644} suppress variable might not be initialized, since "rt_entry" was initialized in _nxd_ipv6_default_router_add_internal. */
292 /*lint -e{613} suppress possible use of null pointer, since "nd_entry" was set in _nx_nd_cache_add. */
293 nd_entry -> nx_nd_cache_is_router = rt_entry;
294 }
295 }
296 }
297 else
298 {
299
300 /* This entry already exists. If the mac address is the same, do not update the entry. Otherwise,
301 update the entry and set the state to STALE (RFC2461 7.2.3) */
302 ULONG mac_msw, mac_lsw, new_msw, new_lsw;
303
304 /*lint -e{928} suppress cast of pointer to pointer, since it is necessary */
305 UCHAR *new_mac = (UCHAR *)&option_ptr -> nx_icmpv6_option_data;
306
307
308 mac_msw = ((ULONG)(nd_entry -> nx_nd_cache_mac_addr[0]) << 8) | (nd_entry -> nx_nd_cache_mac_addr[1]);
309 new_msw = ((ULONG)(new_mac[0]) << 8) | (new_mac[1]);
310 mac_lsw = ((ULONG)(nd_entry -> nx_nd_cache_mac_addr[2]) << 24) | ((ULONG)(nd_entry -> nx_nd_cache_mac_addr[3]) << 16) |
311 ((ULONG)(nd_entry -> nx_nd_cache_mac_addr[4]) << 8) | nd_entry -> nx_nd_cache_mac_addr[5];
312 new_lsw = ((ULONG)(new_mac[2]) << 24) | ((ULONG)(new_mac[3]) << 16) | ((ULONG)(new_mac[4]) << 8) | new_mac[5]; /* lgtm[cpp/overflow-buffer] */
313
314 if ((mac_msw != new_msw) || (mac_lsw != new_lsw)) /* If the new MAC is different. */
315 {
316
317 /* Update the MAC address. */
318 for (i = 0; i < 6; i++)
319 {
320 nd_entry -> nx_nd_cache_mac_addr[i] = new_mac[i];
321 }
322
323 /* Set the state to STALE. */
324 nd_entry -> nx_nd_cache_nd_status = ND_CACHE_STATE_STALE;
325
326 /* Set the interface. */
327 nd_entry -> nx_nd_cache_interface_ptr = interface_ptr;
328 }
329
330 /* If there are packets chained on the entry waiting to be transmitted: */
331 if (nd_entry -> nx_nd_cache_packet_waiting_head) /* There are packets waiting to be transmitted */
332 {
333 _nx_icmpv6_send_queued_packets(ip_ptr, nd_entry);
334 }
335 }
336 }
337
338 #ifdef NX_ENABLE_IPV6_PATH_MTU_DISCOVERY
339 else if (option_ptr -> nx_icmpv6_option_type == ICMPV6_OPTION_TYPE_MTU)
340 {
341
342 /* Get a local pointer to the MTU option. */
343 /*lint -e{929} -e{740} -e{826} suppress cast of pointer to pointer, since it is necessary */
344 mtu_ptr = (NX_ICMPV6_OPTION_MTU *)option_ptr;
345
346 /* Apply the router's next hop (on link) path MTU. */
347 mtu = mtu_ptr -> nx_icmpv6_option_mtu_path_mtu;
348 NX_CHANGE_ULONG_ENDIAN(mtu);
349
350 if (mtu < NX_MINIMUM_IPV6_PATH_MTU)
351 {
352
353 /* Update the timeout of MTU. */
354 mtu_timeout = NX_PATH_MTU_INCREASE_WAIT_INTERVAL_TICKS;
355 }
356 }
357 #endif /* NX_ENABLE_IPV6_PATH_MTU_DISCOVERY */
358
359 option_length = (UINT)(option_ptr -> nx_icmpv6_option_length << 3);
360 packet_length -= option_length;
361
362 /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary */
363 option_ptr = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(option_ptr, option_length);
364 }
365 }
366
367 /*
368 * If the packet contains target link layer address, we should have added the link layer address
369 * to the ND cache, and the next block is not executed.
370 * However if the packet does not contain target link layer address (thus nd_cache is NULL at
371 * this point, we create an entry.
372 */
373 if (nd_entry == NX_NULL)
374 {
375 status = _nx_nd_cache_find_entry(ip_ptr, redirect_ptr -> nx_icmpv6_redirect_target_address, &nd_entry);
376
377 if (status != NX_SUCCESS)
378 {
379 if (_nx_nd_cache_add_entry(ip_ptr, redirect_ptr -> nx_icmpv6_redirect_target_address,
380 packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr, &nd_entry) != NX_SUCCESS)
381 {
382
383 #ifndef NX_DISABLE_ICMP_INFO
384
385 /* Increment the ICMP invalid packet error. */
386 ip_ptr -> nx_ip_icmp_invalid_packets++;
387 #endif /* NX_DISABLE_ICMP_INFO */
388
389 /* Release the packet. */
390 _nx_packet_release(packet_ptr);
391
392 return;
393 }
394 }
395 }
396
397 /* Find/update existing entry or add a new one for this router in the destination table.
398 A new entry will be assigned default path MTU and MTU timeout values. */
399 status = _nx_icmpv6_dest_table_add(ip_ptr, redirect_ptr -> nx_icmpv6_redirect_destination_address,
400 &dest_entry_ptr, redirect_ptr -> nx_icmpv6_redirect_target_address,
401 mtu, mtu_timeout,
402 packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr);
403 if (status)
404 {
405
406 #ifndef NX_DISABLE_ICMP_INFO
407 /* Increment the ICMP invalid packet error. */
408 ip_ptr -> nx_ip_icmp_invalid_packets++;
409 #endif /* NX_DISABLE_ICMP_INFO */
410
411 /* Release the packet. */
412 _nx_packet_release(packet_ptr);
413
414 return;
415 }
416
417 /* Cross link the nd_entry. */
418 /*lint -e{644} suppress variable might not be initialized, since "dest_entry_ptr" was initialized _nx_icmpv6_dest_table_find or _nx_icmpv6_dest_table_add. */
419 dest_entry_ptr -> nx_ipv6_destination_entry_nd_entry = nd_entry;
420
421 /* Release the packet. */
422 _nx_packet_release(packet_ptr);
423 }
424 #endif /* NX_DISABLE_ICMPV6_REDIRECT_PROCESS */
425
426
427
428 #endif /* FEATURE_NX_IPV6 */
429
430