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_na                               PORTABLE C      */
45 /*                                                           6.3.0        */
46 /*  AUTHOR                                                                */
47 /*                                                                        */
48 /*    Yuxin Zhou, Microsoft Corporation                                   */
49 /*                                                                        */
50 /*  DESCRIPTION                                                           */
51 /*                                                                        */
52 /*    This internal function processes incoming neighbor advertisement    */
53 /*    messages. It also updates the ND cache and the default router list. */
54 /*                                                                        */
55 /*  INPUT                                                                 */
56 /*                                                                        */
57 /*    ip_ptr                            IP stack instance                 */
58 /*    packet_ptr                        Received NA packet                */
59 /*                                                                        */
60 /*  OUTPUT                                                                */
61 /*                                                                        */
62 /*    None                                                                */
63 /*                                                                        */
64 /*  CALLS                                                                 */
65 /*                                                                        */
66 /*    _nx_packet_release                  Release packet back to pool     */
67 /*    _nx_nd_cache_add                    Add entry to cache table        */
68 /*    _nx_icmpv6_send_queued_packets      Send out packets queued waiting */
69 /*                                           for physical mapping to IP   */
70 /*                                           address                      */
71 /*    _nx_icmpv6_validate_neighbor_message Validate ICMPv6 packet         */
72 /*    _nx_icmpv6_DAD_failure              Mark IP address status invalid  */
73 /*    _nx_nd_cache_find_entry             Find entry based on dest address*/
74 /*    _nxd_ipv6_find_default_router_from_address                          */
75 /*                                        Find default router by address  */
76 /*    _nxd_ipv6_default_router_delete     Clear the default router bound  */
77 /*                                           to the IP                    */
78 /*                                                                        */
79 /*  CALLED BY                                                             */
80 /*                                                                        */
81 /*    _nx_icmpv6_process                                                  */
82 /*                                                                        */
83 /*  RELEASE HISTORY                                                       */
84 /*                                                                        */
85 /*    DATE              NAME                      DESCRIPTION             */
86 /*                                                                        */
87 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
88 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
89 /*                                            resulting in version 6.1    */
90 /*  10-31-2023     Bo Chen                  Modified comment(s), improved */
91 /*                                            packet length verification, */
92 /*                                            resulting in version 6.3.0  */
93 /*                                                                        */
94 /**************************************************************************/
_nx_icmpv6_process_na(NX_IP * ip_ptr,NX_PACKET * packet_ptr)95 VOID _nx_icmpv6_process_na(NX_IP *ip_ptr, NX_PACKET *packet_ptr)
96 {
97 
98 ND_CACHE_ENTRY   *nd_entry = NX_NULL;
99 NX_ICMPV6_ND     *nd_ptr;
100 NX_ICMPV6_OPTION *option_ptr = NX_NULL;
101 INT               error = 0;
102 INT               lla_same = 0;
103 UINT              option_length;
104 NX_IPV6_HEADER   *ipv6_header_ptr;
105 #ifndef NX_DISABLE_IPV6_DAD
106 UINT              i;
107 #endif /* NX_DISABLE_IPV6_DAD */
108 
109     /* Add debug information. */
110     NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
111 
112 #ifndef NX_DISABLE_RX_SIZE_CHECKING
113     /* Check packet length is at least sizeof(NX_ICMPV6_ND). */
114     if ((packet_ptr -> nx_packet_length < sizeof(NX_ICMPV6_ND))
115 #ifndef NX_DISABLE_PACKET_CHAIN
116         || (packet_ptr -> nx_packet_next) /* Ignore chained packet.  */
117 #endif /* NX_DISABLE_PACKET_CHAIN */
118         )
119     {
120 #ifndef NX_DISABLE_ICMP_INFO
121 
122         /* Increment the ICMP invalid packet error. */
123         ip_ptr -> nx_ip_icmp_invalid_packets++;
124 #endif /* NX_DISABLE_ICMP_INFO */
125 
126         /* Release the packet and we are done. */
127         _nx_packet_release(packet_ptr);
128         return;
129     }
130 #endif /* NX_DISABLE_RX_SIZE_CHECKING */
131 
132     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
133     nd_ptr = (NX_ICMPV6_ND *)(packet_ptr -> nx_packet_prepend_ptr);
134 
135     /* Take care of endian-ness. */
136     NX_IPV6_ADDRESS_CHANGE_ENDIAN(nd_ptr -> nx_icmpv6_nd_targetAddress);
137     NX_CHANGE_ULONG_ENDIAN(nd_ptr -> nx_icmpv6_nd_flag);
138 
139     /* Validate the neighbor advertisement message. */
140     if (_nx_icmpv6_validate_neighbor_message(packet_ptr) != NX_SUCCESS)
141     {
142         error = 1;
143     }
144     else
145     {
146 
147         /* Points to the IPv6 header. */
148         /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
149         ipv6_header_ptr = (NX_IPV6_HEADER *)packet_ptr -> nx_packet_ip_header;
150 
151         /* Find the option field. */
152         /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary  */
153         option_ptr = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(nd_ptr, sizeof(NX_ICMPV6_ND));
154         option_length = (UINT)packet_ptr -> nx_packet_length - (UINT)sizeof(NX_ICMPV6_ND);
155 
156         /* Find the TLLA option */
157         while (option_length > 0)
158         {
159             /* Check if this is a Target LLA option. */
160             if (option_ptr -> nx_icmpv6_option_type == ICMPV6_OPTION_TYPE_TRG_LINK_ADDR)
161             {
162                 break;
163             }
164 
165             /* Get the next option. */
166             option_length -= ((UINT)(option_ptr -> nx_icmpv6_option_length) << 3);
167 
168             /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary  */
169             option_ptr = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(option_ptr, ((option_ptr -> nx_icmpv6_option_length) << 3));
170         }
171 
172         /* Check for no option included. */
173         if (option_length == 0)
174         {
175 
176             option_ptr = NX_NULL;
177         }
178 
179         /* Determine the NA packet destination type. */
180         /* Is the destination a multicast address? */
181         if ((ipv6_header_ptr -> nx_ip_header_destination_ip[0] & (ULONG)0xFF000000) == (ULONG)0xFF000000)
182         {
183 
184             /* Yes; Were there any options in the NA packet? */
185             if (!option_ptr)
186             {
187 
188                 /* No, this is an invalid NA packet (No TLLA). */
189                 error = 1;
190             }
191         }
192 
193 #ifndef NX_DISABLE_IPV6_DAD
194 
195         /* Find the same address as target address in IPv6 address structure.
196            Assume target address is 4-byte aligned.*/
197         for (i = 0; i < NX_MAX_IPV6_ADDRESSES; i++)
198         {
199 
200             if (CHECK_IPV6_ADDRESSES_SAME(ip_ptr -> nx_ipv6_address[i].nxd_ipv6_address,
201                                           nd_ptr -> nx_icmpv6_nd_targetAddress))
202             {
203                 if (ip_ptr -> nx_ipv6_address[i].nxd_ipv6_address_state == NX_IPV6_ADDR_STATE_TENTATIVE)
204                 {
205 
206                     /* Sender sends a NS in response to a DAD request we sent out.
207                        Mark DAD failure. */
208                     _nx_icmpv6_DAD_failure(ip_ptr, &ip_ptr -> nx_ipv6_address[i]);
209 
210 #ifndef NX_DISABLE_ICMP_INFO
211 
212                     /* Increment the ICMP invalid packet error. */
213                     ip_ptr -> nx_ip_icmp_invalid_packets++;
214 #endif /* NX_DISABLE_ICMP_INFO */
215 
216                     /* Release the packet and we are done. */
217                     _nx_packet_release(packet_ptr);
218                     return;
219                 }
220 
221                 break;
222             }
223         }
224 #endif /* NX_DISABLE_IPV6_DAD */
225 
226         /* Find the ND entry */
227         if (_nx_nd_cache_find_entry(ip_ptr, nd_ptr -> nx_icmpv6_nd_targetAddress, &nd_entry) != NX_SUCCESS)
228         {
229             /* The entry does not exist, it indicates that we are not
230                expecting this NA.  So silently ignore this packet,
231                according to RFC 2461 7.2.5 */
232             error = 1;
233         }
234     }
235 
236     /* Do not process the NA any further if any errors detected. */
237     if (error)
238     {
239 
240 #ifndef NX_DISABLE_ICMP_INFO
241 
242         /* Increment the ICMP invalid packet error. */
243         ip_ptr -> nx_ip_icmp_invalid_packets++;
244 #endif /* NX_DISABLE_ICMP_INFO */
245 
246         /* Release the packet and we are done. */
247         _nx_packet_release(packet_ptr);
248         return;
249     }
250 
251     /* Check whether or not supplied LLA is the same as the cached one */
252     if (option_ptr)
253     {
254 
255     /* Compare the link-layer address. */
256     USHORT *new_lla, *lla;
257 
258         /*lint -e{929} suppress cast of pointer to pointer, since it is necessary  */
259         new_lla = (USHORT *)&option_ptr -> nx_icmpv6_option_data;
260 
261         /*lint -e{927} suppress cast of pointer to pointer, since it is necessary  */
262         /*lint -e{644} suppress variable might not be initialized, since "nd_entry" was initialized in _nx_nd_cache_find_entry. */
263         lla = (USHORT *)nd_entry -> nx_nd_cache_mac_addr;
264         if ((new_lla[0] == lla[0]) && (new_lla[1] == lla[1]) && (new_lla[2] == lla[2])) /* lgtm[cpp/overflow-buffer] */
265         {
266 
267             /* No change in LLA. */
268             lla_same = 1;
269         }
270     }
271 
272     /* Determine if the entry is a router. */
273     if (nd_ptr -> nx_icmpv6_nd_flag & 0x80000000)
274     {
275 
276         /* Yes; Does the corresonding ND entry have the IsRouter flag set? */
277         if (nd_entry -> nx_nd_cache_is_router == NX_NULL)
278         {
279 
280         NX_IPV6_DEFAULT_ROUTER_ENTRY *rt_entry;
281 
282             /* No; Find the default router entry. */
283             rt_entry =
284                 _nxd_ipv6_find_default_router_from_address(ip_ptr, nd_ptr -> nx_icmpv6_nd_targetAddress);
285 
286             /* Check the default router.  */
287             if (rt_entry)
288             {
289 
290                 /* Set the IsRouter flag in the ND entry. */
291                 nd_entry -> nx_nd_cache_is_router = rt_entry;
292 
293                 /* Set this link as the corresponding ND entry. */
294                 rt_entry -> nx_ipv6_default_router_entry_neighbor_cache_ptr = (VOID *)nd_entry;
295             }
296         }
297     }
298     else
299     {
300 
301         /*
302          * The neighbor advertisement message indicates that it is not a router.
303          * However if our ND cache still marks it as a router, that means the neighbor is
304          * no longer acting as a router, and we shall clean up our records.
305          */
306         if (nd_entry -> nx_nd_cache_is_router)
307         {
308 
309         NXD_ADDRESS router_address;
310         UINT        clear_router_flag = 1;
311 
312             /* Only if the TLLA option indicates the TLLA is unchanged! */
313             if (option_ptr && !lla_same)
314             {
315 
316                 /* And only if the override bit not is set. */
317                 if ((nd_ptr -> nx_icmpv6_nd_flag & 0x20000000) == 0)
318                 {
319                     clear_router_flag = 0;
320                 }
321             }
322 
323             /* Did we decide to clear the router status of this cache entry? */
324             if (clear_router_flag)
325             {
326 
327                 /* Yes, Ok to clear the cache entry router status! */
328 
329                 /* The IsRouter points to the entry in the default router table.
330                    We first remove the link between the router table entry and the nd cache entry. */
331                 nd_entry -> nx_nd_cache_is_router -> nx_ipv6_default_router_entry_neighbor_cache_ptr = NX_NULL;
332 
333 
334                 /* Remove the entry from the default router list. */
335                 router_address.nxd_ip_version = NX_IP_VERSION_V6;
336                 router_address.nxd_ip_address.v6[0] = nd_ptr -> nx_icmpv6_nd_targetAddress[0];
337                 router_address.nxd_ip_address.v6[1] = nd_ptr -> nx_icmpv6_nd_targetAddress[1];
338                 router_address.nxd_ip_address.v6[2] = nd_ptr -> nx_icmpv6_nd_targetAddress[2];
339                 router_address.nxd_ip_address.v6[3] = nd_ptr -> nx_icmpv6_nd_targetAddress[3];
340 
341                 /* We must remove the entry from the default router list. */
342                 _nxd_ipv6_default_router_delete(ip_ptr, &router_address);
343 
344                 /* Set the router flag to NULL. */
345                 nd_entry -> nx_nd_cache_is_router = NX_NULL;
346             }
347         }
348     }
349 
350     /*
351      * An ND entry exists.  If the cache entry is in incomplete state,
352      * add the NA contains LLA option, if the NA is unsolicitated,
353      * add the LLA to the cache in STALE state.
354      */
355     if ((nd_entry -> nx_nd_cache_nd_status == ND_CACHE_STATE_INCOMPLETE) ||
356         (nd_entry -> nx_nd_cache_nd_status == ND_CACHE_STATE_CREATED))
357     {
358 
359         /* Find the target link layer options. */
360         if (option_ptr)
361         {
362 
363             /* If the solicitation flag is set, set state to REACHABLE; if not,
364                set state to STALE.*/
365             /*lint -e{929} -e{740} -e{826} suppress cast of pointer to pointer, since it is necessary  */
366             _nx_nd_cache_add(ip_ptr, nd_ptr -> nx_icmpv6_nd_targetAddress,
367                              packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached,
368                              (CHAR *)&option_ptr -> nx_icmpv6_option_data, 0,
369                              nd_ptr -> nx_icmpv6_nd_flag & 0x40000000 ? ND_CACHE_STATE_REACHABLE : ND_CACHE_STATE_STALE,
370                              packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr, &nd_entry);
371 
372             /* Any queued packets?. */
373             if (nd_entry -> nx_nd_cache_packet_waiting_head)
374             {
375 
376                 /* Send the queued packets out. */
377                 _nx_icmpv6_send_queued_packets(ip_ptr, nd_entry);
378             }
379         }
380 
381         /* All done. Release the packet and return.  */
382         _nx_packet_release(packet_ptr);
383         return;
384     }
385 
386     /* When we get there, we have an ND entry that has valid (REACHABLE) LLA. */
387 
388     if ((nd_ptr -> nx_icmpv6_nd_flag & 0x20000000) == 0 && option_ptr && (!lla_same))
389     {
390         /*
391          * Override bit is clear.
392          * If the link layer address is different from the one in our cache entry,
393          * and the entry is REACHABLE, update the entry to STALE.
394          */
395         if (nd_entry -> nx_nd_cache_nd_status == ND_CACHE_STATE_REACHABLE)
396         {
397 
398             nd_entry -> nx_nd_cache_nd_status = ND_CACHE_STATE_STALE;
399             nd_entry -> nx_nd_cache_timer_tick = 0;
400         }
401     }
402     else
403     {
404 
405         /* Processing according to RFC2461 7.2.5. */
406         /* If LLA is supplied and is different from cache value, update the cache. */
407         if (option_ptr && !lla_same)
408         {
409         USHORT *new_lla, *lla;
410 
411             /*lint -e{929} -e{927} suppress cast of pointer to pointer, since it is necessary  */
412             new_lla = (USHORT *)&option_ptr -> nx_icmpv6_option_data;
413 
414             /*lint -e{927} suppress cast of pointer to pointer, since it is necessary  */
415             lla = (USHORT *)nd_entry -> nx_nd_cache_mac_addr;
416             lla[0] = new_lla[0];
417             lla[1] = new_lla[1]; /* lgtm[cpp/overflow-buffer] */
418             lla[2] = new_lla[2]; /* lgtm[cpp/overflow-buffer] */
419         }
420         if (nd_ptr -> nx_icmpv6_nd_flag & 0x40000000) /* S bit is set, force cache entry to REACHABLE */
421         {
422 
423             nd_entry -> nx_nd_cache_nd_status = ND_CACHE_STATE_REACHABLE;
424             nd_entry -> nx_nd_cache_timer_tick = ip_ptr -> nx_ipv6_reachable_timer;
425         }
426         else if (option_ptr && (!lla_same)) /* S bit is clear and either TLLA is not supplied or it is different. */
427         {
428             nd_entry -> nx_nd_cache_nd_status = ND_CACHE_STATE_STALE;
429             nd_entry -> nx_nd_cache_timer_tick = 0;
430         }
431     }
432 
433     _nx_packet_release(packet_ptr);
434     return;
435 }
436 
437 #endif /* FEATURE_NX_IPV6 */
438 
439