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