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