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 /** NetX Component                                                        */
16 /**                                                                       */
17 /**   Internet Control Message Protocol (ICMP)                            */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define NX_SOURCE_CODE
23 /* Include necessary system files.  */
24 
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 #ifndef NX_DISABLE_ICMPV6_ROUTER_ADVERTISEMENT_PROCESS
34 
35 /**************************************************************************/
36 /*                                                                        */
37 /*  FUNCTION                                               RELEASE        */
38 /*                                                                        */
39 /*    _nx_icmpv6_process_ra                               PORTABLE C      */
40 /*                                                           6.1.11       */
41 /*  AUTHOR                                                                */
42 /*                                                                        */
43 /*    Yuxin Zhou, Microsoft Corporation                                   */
44 /*                                                                        */
45 /*  DESCRIPTION                                                           */
46 /*                                                                        */
47 /*    This function processes incoming ICMPv6 router advertisement        */
48 /*    messages.                                                           */
49 /*                                                                        */
50 /*  INPUT                                                                 */
51 /*                                                                        */
52 /*    ip_ptr                                Pointer to IP control block   */
53 /*    packet_ptr                            ICMP packet pointer           */
54 /*                                                                        */
55 /*  OUTPUT                                                                */
56 /*                                                                        */
57 /*    None                                                                */
58 /*                                                                        */
59 /*  CALLS                                                                 */
60 /*                                                                        */
61 /*    _nxd_ipv6_default_router_add_interanl Sets IPv6 router              */
62 /*    _nx_ipv6_multicast_join               Join IPv6 multicast group     */
63 /*    _nx_ipv6_prefix_list_delete           Remove an entry from the      */
64 /*                                            prefix list                 */
65 /*    _nx_ipv6_prefix_list_add_entry        Add an entry to the prefix    */
66 /*                                            list                        */
67 /*    _nx_nd_cache_add                      Add an ND cache entry         */
68 /*    [ipv6_address_change_notify]          User callback function        */
69 /*                                                                        */
70 /*  CALLED BY                                                             */
71 /*                                                                        */
72 /*    _nx_icmpv6_packet_process             Main ICMP packet pocess       */
73 /*                                                                        */
74 /*  RELEASE HISTORY                                                       */
75 /*                                                                        */
76 /*    DATE              NAME                      DESCRIPTION             */
77 /*                                                                        */
78 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
79 /*  09-30-2020     Yuxin Zhou               Modified comment(s), improved */
80 /*                                            option length verification, */
81 /*                                            resulting in version 6.1    */
82 /*  04-25-2022     Yuxin Zhou               Modified comment(s), and      */
83 /*                                            added internal ip address   */
84 /*                                            change notification,        */
85 /*                                            resulting in version 6.1.11 */
86 /*                                                                        */
87 /**************************************************************************/
_nx_icmpv6_process_ra(NX_IP * ip_ptr,NX_PACKET * packet_ptr)88 VOID _nx_icmpv6_process_ra(NX_IP *ip_ptr, NX_PACKET *packet_ptr)
89 {
90 
91 INT                           router_type;
92 INT                           packet_length;
93 INT                           prefix_length;
94 UINT                          i;
95 UINT                          status;
96 ULONG                         time_val;
97 ND_CACHE_ENTRY               *nd_entry;
98 NX_ICMPV6_HEADER             *header_ptr;
99 NX_ICMPV6_RA                 *ra_ptr;
100 NX_ICMPV6_OPTION             *option_ptr;
101 NX_ICMPV6_OPTION_PREFIX      *prefix_ptr;
102 #ifdef NX_ENABLE_IPV6_PATH_MTU_DISCOVERY
103 NX_ICMPV6_OPTION_MTU         *mtu_ptr = NULL;
104 #endif
105 NX_INTERFACE                 *if_ptr;
106 NX_IPV6_HEADER               *ipv6_header;
107 NX_IPV6_DEFAULT_ROUTER_ENTRY *rt_entry;
108 #ifdef NX_ENABLE_IPV6_ADDRESS_CHANGE_NOTIFY
109 UINT                          interface_index;
110 #endif /* NX_ENABLE_IPV6_ADDRESS_CHANGE_NOTIFY */
111 
112     /* Add debug information. */
113     NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
114 
115     /* Initialize the ND cache table entry to NULL */
116     nd_entry = NX_NULL;
117 
118     /* Get a pointer to the ICMP message header.  */
119     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
120     header_ptr =  (NX_ICMPV6_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
121 
122     /* If the RA message is invalid, we simply return. */
123     if (_nx_icmpv6_validate_ra(packet_ptr) != NX_SUCCESS)
124     {
125 
126 #ifndef NX_DISABLE_ICMP_INFO
127 
128         /* Increment the ICMP invalid packet error. */
129         ip_ptr -> nx_ip_icmp_invalid_packets++;
130 #endif /* NX_DISABLE_ICMP_INFO */
131 
132         _nx_packet_release(packet_ptr);
133         return;
134     }
135 
136     /* Get a pointer to the router advertisement packet structure. */
137     /*lint -e{929} suppress cast of pointer to pointer, since it is necessary  */
138     ra_ptr = (NX_ICMPV6_RA *)header_ptr;
139 
140     /* If router advertisement flag callback is set, invoke the callback function. */
141     if (ip_ptr -> nx_icmpv6_ra_flag_callback)
142     {
143         ip_ptr -> nx_icmpv6_ra_flag_callback(ip_ptr, (UINT)ra_ptr -> nx_icmpv6_ra_flag);
144     }
145 
146     /* Set a pointer to the IPv6 header. */
147     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
148     ipv6_header = (NX_IPV6_HEADER *)packet_ptr -> nx_packet_ip_header;
149 
150     /* Obtain the pointer to the incoming interface. */
151     if_ptr = packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached;
152 
153     /* (1) Determine if this message comes from a periodic refresh
154        or this is in response to a router solicitation.
155        This is a periodic refresh if the destination address is an all-router multicast address.
156      */
157 
158     if (ra_ptr -> nx_icmpv6_ra_router_lifetime == 0)
159     {
160 
161     NXD_ADDRESS router_address;
162 
163         /* This router is no long valid.  */
164 
165         /* Copy the source address from the IPv6 header to a router address data
166            which we will use to delete it from the router table. */
167         router_address.nxd_ip_version = NX_IP_VERSION_V6;
168         router_address.nxd_ip_address.v6[0] = ipv6_header -> nx_ip_header_source_ip[0];
169         router_address.nxd_ip_address.v6[1] = ipv6_header -> nx_ip_header_source_ip[1];
170         router_address.nxd_ip_address.v6[2] = ipv6_header -> nx_ip_header_source_ip[2];
171         router_address.nxd_ip_address.v6[3] = ipv6_header -> nx_ip_header_source_ip[3];
172 
173 
174         /* Delete it from our default router table.*/
175         _nxd_ipv6_default_router_delete(ip_ptr, &router_address);
176     }
177 
178     /* Does the RA packet have a valid retrans_timer? */
179     if (ra_ptr -> nx_icmpv6_ra_retrans_time)
180     {
181         /* Pickup the retrans_time value.*/
182         time_val = ra_ptr -> nx_icmpv6_ra_retrans_time;
183 
184         /* Byte swapping. */
185         NX_CHANGE_ULONG_ENDIAN(time_val);
186 
187         /* Yes; Reset our re-trans timer. */
188         /* Conver timer ticks (in ms) into IP fast timeout value. */
189         ip_ptr -> nx_ipv6_retrans_timer_ticks = time_val * NX_IP_FAST_TIMER_RATE / 1000;
190 
191         /* If the retrans_timer is smaller than tick resolution, set it to 1.  */
192         if (ip_ptr -> nx_ipv6_retrans_timer_ticks == 0)
193         {
194             ip_ptr -> nx_ipv6_retrans_timer_ticks = 1;
195         }
196     }
197 
198     /* Does the router advertisement have a valid reachable time? */
199     if (ra_ptr -> nx_icmpv6_ra_reachable_time)
200     {
201 
202 
203         /* Yes; set a local variable to store it. */
204         time_val = ra_ptr -> nx_icmpv6_ra_reachable_time;
205 
206         /* Byte swapping. */
207         NX_CHANGE_ULONG_ENDIAN(time_val);
208 
209         /* Convert reachable timer to seconds. */
210         ip_ptr -> nx_ipv6_reachable_timer = time_val / 1000;
211 
212         /* In case the reachable timer is less than 1 second, set reachable timer to 1 second. */
213         if (ip_ptr -> nx_ipv6_reachable_timer == 0)
214         {
215             ip_ptr -> nx_ipv6_reachable_timer = 1;
216         }
217     }
218 
219     if (IPv6_Address_Type(ipv6_header -> nx_ip_header_destination_ip) & IPV6_ALL_NODE_MCAST)
220     {
221 
222         /* The destination address points to all-nodes (unsolicited) multicast.  Therefore this
223            is a periodic refresh. */
224         router_type = NX_IPV6_ROUTE_TYPE_UNSOLICITATED;
225     }
226     else
227     {
228         router_type = NX_IPV6_ROUTE_TYPE_SOLICITATED;
229     }
230 
231     /* (2) Process option field */
232     packet_length = (INT)packet_ptr -> nx_packet_length - (INT)sizeof(NX_ICMPV6_RA);
233 
234     /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary  */
235     option_ptr = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(ra_ptr, sizeof(NX_ICMPV6_RA));
236 
237     /* Going through the rest of the packet options. */
238     while (packet_length > 0)
239     {
240 
241         /* If there are prefix info options, we pick the last one. */
242 
243         /* Is the current option a prefix option? */
244         if (option_ptr -> nx_icmpv6_option_type == ICMPV6_OPTION_TYPE_PREFIX_INFO)
245         {
246 
247             /* Validate option length before cast to avoid OOB access. */
248             if ((UINT)(option_ptr -> nx_icmpv6_option_length << 3) < sizeof(NX_ICMPV6_OPTION_PREFIX))
249             {
250 #ifndef NX_DISABLE_ICMP_INFO
251 
252                 /* Increment the ICMP invalid packet error. */
253                 ip_ptr -> nx_ip_icmp_invalid_packets++;
254 #endif /* NX_DISABLE_ICMP_INFO */
255 
256                 _nx_packet_release(packet_ptr);
257                 return;
258             }
259 
260             /* Yes, set a local pointer to the option. */
261             /*lint -e{929} -e{826} -e{740} suppress cast of pointer to pointer, since it is necessary  */
262             prefix_ptr = (NX_ICMPV6_OPTION_PREFIX *)option_ptr;
263 
264             /* Take care of the endian-ness of the prefix address. */
265             NX_IPV6_ADDRESS_CHANGE_ENDIAN(prefix_ptr -> nx_icmpv6_option_prefix);
266 
267             /* Is this a link local address (prefix)?  */
268             if ((prefix_ptr -> nx_icmpv6_option_prefix[0] & (ULONG)0xFFC00000) == (ULONG)0xFE800000)
269             {
270 
271                 /* Yes.  Ignore (skip) this option, as per  RFC 4861 6.3.4
272                    and RFC 4862 5.5.3, and continue. */
273                 packet_length -= (option_ptr -> nx_icmpv6_option_length << 3);
274 
275                 /* Get the next option. */
276                 /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary  */
277                 option_ptr = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(option_ptr, ((option_ptr -> nx_icmpv6_option_length) << 3));
278 
279                 continue;
280             }
281 
282             /* So far the prefix information is valid.
283                So take care of the endian-ness. */
284             NX_CHANGE_ULONG_ENDIAN(prefix_ptr -> nx_icmpv6_option_prefix_valid_lifetime);
285             NX_CHANGE_ULONG_ENDIAN(prefix_ptr -> nx_icmpv6_option_prefix_preferred_lifetime);
286 
287             /* Does the prefix have a valid lifetime? */
288             if (prefix_ptr -> nx_icmpv6_option_prefix_preferred_lifetime > prefix_ptr -> nx_icmpv6_option_prefix_valid_lifetime)
289             {
290 
291                 /* Ignore this option, according to RFC 4862 5.5.3(c) and continue. */
292                 packet_length -= (option_ptr -> nx_icmpv6_option_length << 3);
293 
294                 /* Get a pointer to the next option. Abort processing the current option any further. */
295                 /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary  */
296                 option_ptr = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(option_ptr, ((option_ptr -> nx_icmpv6_option_length) << 3));
297 
298                 continue;
299             }
300 
301             /* Determine whether or not the on-link flag is set. */
302             /* Ignore the prefix information option when it is link-local. Page 55, Section 6.3.4, RFC 4861. */
303             if (prefix_ptr -> nx_icmpv6_option_prefix_flag & 0x80)
304             {
305 
306                 /* This prefix option contains onlink prefix information. */
307                 /* The following process follows RFC 4861 6.3.4 */
308 
309                 prefix_length = prefix_ptr -> nx_icmpv6_option_prefix_length;
310 
311                 if (prefix_ptr -> nx_icmpv6_option_prefix_valid_lifetime == 0)
312                 {
313 
314                     /* Invalidate the prefix list entry, RFC 4861 6.3.4, p55. */
315                     /* Stateless address with this prefix is deleted when prefix is deleted. */
316                     _nx_ipv6_prefix_list_delete(ip_ptr, prefix_ptr -> nx_icmpv6_option_prefix, prefix_length);
317                 }
318                 else
319                 {
320 
321                     /* This prefix is onlink, and valid_lifetime is non-zero.
322                        So add the prefix to our list. RFC 4861 6.3.4 p55.*/
323                     status = _nx_ipv6_prefix_list_add_entry(ip_ptr, prefix_ptr -> nx_icmpv6_option_prefix,
324                                                             (ULONG)prefix_length, prefix_ptr -> nx_icmpv6_option_prefix_valid_lifetime);
325 
326                     /* Check for "A" bit. */
327                     if ((prefix_ptr -> nx_icmpv6_option_prefix_flag & 0x40) &&
328                         (prefix_ptr -> nx_icmpv6_option_prefix_length == (128 - NX_IPV6_HOST_ID_LENGTH)) &&
329                         (status == NX_SUCCESS))
330                     {
331                     /* The autonomous flag is set */
332 
333                     /* Set first_unused to be an invalid entry. */
334                     UINT              first_unused = NX_MAX_IPV6_ADDRESSES;
335                     ULONG             word2, word3;
336                     ULONG             address[4];
337                     NXD_IPV6_ADDRESS *ipv6_address;
338 
339                         /* Find an entry that shares the same prefix. */
340                         /* Note: RFC 4862 5.5.3(d) specifies that the search is limited to IPv6
341                            addresses formed by address autoconfiguration.  Towards the end of 5.5.3(d),
342                            the RFC explains that the search may still lead to address conflict (by not
343                            searching for addresses configured manually or via DHCP.  Therefore this
344                            implemenation chooses to search the entire IPv6 global address, in order to
345                            avoid conflict IP addresses. */
346                         for (i = 0; i < NX_MAX_IPV6_ADDRESSES; i++)
347                         {
348 
349                             if (ip_ptr -> nx_ipv6_address[i].nxd_ipv6_address_valid == NX_FALSE)
350                             {
351 
352                                 /* Found the first unused entry. */
353                                 first_unused = i;
354                                 break;
355                             }
356                         }
357 
358                         if ((first_unused != NX_MAX_IPV6_ADDRESSES)
359 #ifdef NX_IPV6_STATELESS_AUTOCONFIG_CONTROL
360                             && (if_ptr -> nx_ipv6_stateless_address_autoconfig_status == NX_STATELESS_ADDRESS_AUTOCONFIG_ENABLED)
361 #endif /* NX_IPV6_STATELESS_AUTOCONFIG_CONTROL */
362                            )
363                         {
364 
365                             /* If there are no global addresses with such a prefix, and there is an unused entry,
366                                a new global address is formed. RFC 4862 5.5.3(d), p18 */
367 
368                             ipv6_address = &ip_ptr -> nx_ipv6_address[first_unused];
369 
370                             /* Find 64-bit interface ID. See RFC 4291 */
371                             word2 = if_ptr -> nx_interface_physical_address_msw << 16 |
372                                 ((if_ptr -> nx_interface_physical_address_lsw & 0xFF000000) >> 16) | 0xFF;
373 
374                             /* Fix the 2nd lower-order bit of the 1st byte */
375                             word2 = (word2 & 0xFDFFFFFF) | (~(word2 | 0xFDFFFFFF));
376                             word3 = (if_ptr -> nx_interface_physical_address_lsw & 0x00FFFFFF) | 0xFE000000;
377 
378                             ipv6_address -> nxd_ipv6_address_valid = NX_TRUE;
379                             ipv6_address -> nxd_ipv6_address_type = NX_IP_VERSION_V6;
380 
381                             ipv6_address -> nxd_ipv6_address_attached = if_ptr;
382                             ipv6_address -> nxd_ipv6_address[0] = prefix_ptr -> nx_icmpv6_option_prefix[0];
383                             ipv6_address -> nxd_ipv6_address[1] = prefix_ptr -> nx_icmpv6_option_prefix[1];
384                             ipv6_address -> nxd_ipv6_address[2] = word2;
385                             ipv6_address -> nxd_ipv6_address[3] = word3;
386 
387                             ipv6_address -> nxd_ipv6_address_next = if_ptr -> nxd_interface_ipv6_address_list_head;
388                             if_ptr -> nxd_interface_ipv6_address_list_head = ipv6_address;
389 
390 #ifndef NX_DISABLE_IPV6_DAD
391                             /* Set the address to Tentative, so the stack can start DAD process. */
392                             ipv6_address -> nxd_ipv6_address_state = NX_IPV6_ADDR_STATE_TENTATIVE;
393 #else /* !NX_DISABLE_IPV6_DAD */
394                             /* DAD is disabled.  Set the address to VALID. */
395                             ipv6_address -> nxd_ipv6_address_state = NX_IPV6_ADDR_STATE_VALID;
396 #endif /* NX_DISABLE_IPV6_DAD */
397 
398                             /* Join the solicited-node multicast group */
399                             /* FF02::1:FFXX:XXXX */
400                             SET_SOLICITED_NODE_MULTICAST_ADDRESS(address, ipv6_address -> nxd_ipv6_address);
401                             _nx_ipv6_multicast_join(ip_ptr, address, ipv6_address -> nxd_ipv6_address_attached);
402 
403                             ipv6_address -> nxd_ipv6_address_prefix_length = (UCHAR)prefix_length;
404                             ipv6_address -> nxd_ipv6_address_ConfigurationMethod = NX_IPV6_ADDRESS_BASED_ON_INTERFACE;
405 #ifndef NX_DISABLE_IPV6_DAD
406                             ipv6_address -> nxd_ipv6_address_DupAddrDetectTransmit = NX_IPV6_DAD_TRANSMITS - 1;
407 #endif /* NX_DISABLE_IPV6_DAD */
408 
409 #ifdef NX_ENABLE_IPV6_ADDRESS_CHANGE_NOTIFY
410                             if (ip_ptr -> nx_ipv6_address_change_notify)
411                             {
412                                 interface_index = if_ptr -> nx_interface_index;
413                                 (ip_ptr -> nx_ipv6_address_change_notify)(ip_ptr, NX_IPV6_ADDRESS_STATELESS_AUTO_CONFIG, interface_index,
414                                                                           first_unused, &ipv6_address -> nxd_ipv6_address[0]);
415                             }
416                             if ((ip_ptr -> nx_ipv6_address_change_notify_internal) && (ipv6_address -> nxd_ipv6_address_state == NX_IPV6_ADDR_STATE_VALID))
417                             {
418                                 interface_index = if_ptr -> nx_interface_index;
419                                 (ip_ptr -> nx_ipv6_address_change_notify_internal)(ip_ptr, NX_IPV6_ADDRESS_STATELESS_AUTO_CONFIG, interface_index,
420                                                                                    first_unused, &ipv6_address -> nxd_ipv6_address[0]);
421                             }
422 #endif /* NX_ENABLE_IPV6_ADDRESS_CHANGE_NOTIFY */
423                         }
424                     }
425                 }
426             }
427         }
428         else if (option_ptr -> nx_icmpv6_option_type == ICMPV6_OPTION_TYPE_SRC_LINK_ADDR)
429         {
430 
431             status = _nx_nd_cache_find_entry(ip_ptr, ipv6_header -> nx_ip_header_source_ip, &nd_entry);
432 
433             /* Find the IP address from our local nd cache. */
434             if (status != NX_SUCCESS)
435             {
436 
437                 /* If the source IP address is not in our nd cache, add it. */
438                 /*lint -e{929} -e{826} -e{740} suppress cast of pointer to pointer, since it is necessary  */
439                 _nx_nd_cache_add(ip_ptr, ipv6_header -> nx_ip_header_source_ip, if_ptr,
440                                  (CHAR *)&option_ptr -> nx_icmpv6_option_data, 0, ND_CACHE_STATE_STALE,
441                                  packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr, &nd_entry);
442             }
443             else
444             {
445             /* This entry already exists.  If the mac address is the same, do not update the entry.
446                Otherwise, update the entry and set the state to STALE (RFC2461 7.2.3) */
447             ULONG mac_msw, mac_lsw, new_msw, new_lsw;
448 
449             /*lint -e{928} suppress cast from pointer to pointer, since it is necessary  */
450             UCHAR *new_mac = (UCHAR *)&option_ptr -> nx_icmpv6_option_data;
451 
452                 /* build two MAC addresses for comparison. */
453                 /*lint --e{613} -e{644} suppress possible use of null pointer, since "nd_entry" was set to none NULL by _nx_nd_cache_find_entry. */
454                 mac_msw = ((ULONG)(nd_entry -> nx_nd_cache_mac_addr[0]) << 8) | (nd_entry -> nx_nd_cache_mac_addr[1]);
455                 mac_lsw = ((ULONG)(nd_entry -> nx_nd_cache_mac_addr[2]) << 24) | ((ULONG)(nd_entry -> nx_nd_cache_mac_addr[3]) << 16) |
456                     ((ULONG)(nd_entry -> nx_nd_cache_mac_addr[4]) << 8) | nd_entry -> nx_nd_cache_mac_addr[5];
457                 new_msw = ((ULONG)(new_mac[0]) << 8) | (new_mac[1]);
458                 new_lsw = ((ULONG)(new_mac[2]) << 24) | ((ULONG)(new_mac[3]) << 16) | ((ULONG)(new_mac[4]) << 8) | new_mac[5]; /* lgtm[cpp/overflow-buffer] */
459                 if ((mac_msw != new_msw) || (mac_lsw != new_lsw))
460                 {
461 
462                     /* Router updates its MAC address.  We update our entry as well, and place the
463                        entry into STALE state for a quick address check. */
464 
465                     /* Set the mac address. */
466                     for (i = 0; i < 6; i++)
467                     {
468                         nd_entry -> nx_nd_cache_mac_addr[i] = new_mac[i];
469                     }
470 
471                     /* Set the state to STALE.  */
472                     nd_entry -> nx_nd_cache_nd_status = ND_CACHE_STATE_STALE;
473 
474                     /* Set the interface. */
475                     nd_entry -> nx_nd_cache_interface_ptr = if_ptr;
476                 }
477 
478 
479                 /* Since we received source LLA, we shall transmit any packets that are pending
480                    address resolution of this target. */
481                 if (nd_entry -> nx_nd_cache_packet_waiting_head) /* There are packets waiting to be transmitted */
482                 {
483 
484                     _nx_icmpv6_send_queued_packets(ip_ptr, nd_entry);
485                 }
486             }
487         }
488 #ifdef NX_ENABLE_IPV6_PATH_MTU_DISCOVERY
489 
490         /* Check for an MTU update from the router. */
491         else if (option_ptr -> nx_icmpv6_option_type == ICMPV6_OPTION_TYPE_MTU)
492         {
493 
494         NX_IPV6_DESTINATION_ENTRY *dest_entry_ptr;
495         UINT                       mtu_size;
496 
497             /* Get a local pointer to the MTU option data. */
498             /*lint -e{929} -e{826} -e{740} suppress cast of pointer to pointer, since it is necessary  */
499             mtu_ptr = (NX_ICMPV6_OPTION_MTU *)option_ptr;
500 
501             mtu_size = mtu_ptr -> nx_icmpv6_option_mtu_path_mtu;
502 
503             NX_CHANGE_ULONG_ENDIAN(mtu_size);
504 
505 
506             /* Make sure the MTU size does not exceed the link MTU size. */
507             if (mtu_size > if_ptr -> nx_interface_ip_mtu_size)
508             {
509                 mtu_size = if_ptr -> nx_interface_ip_mtu_size;
510             }
511 
512             /* Add destination table entry. */
513             _nx_icmpv6_dest_table_add(ip_ptr, ipv6_header -> nx_ip_header_source_ip, &dest_entry_ptr,
514                                       ipv6_header -> nx_ip_header_source_ip /* Next Hop address */,
515                                       mtu_size, NX_WAIT_FOREVER,
516                                       packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr);
517         }
518 #endif
519 
520         /* Update the amount of packet option data remaining. */
521         packet_length -= (option_ptr -> nx_icmpv6_option_length << 3);
522 
523         /* Get a pointer to the next option. */
524         /*lint -e{923} suppress cast between pointer and ULONG , since it is necessary  */
525         option_ptr  = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(option_ptr, ((option_ptr -> nx_icmpv6_option_length) << 3));
526     }
527 
528     /* All options are processed.  No errors encountered.  */
529 
530     /* (3) Add this entry to the routing table. */
531     if (ra_ptr -> nx_icmpv6_ra_router_lifetime)
532     {
533 
534         NX_CHANGE_USHORT_ENDIAN(ra_ptr -> nx_icmpv6_ra_router_lifetime);
535 
536         /* Add the router into our default router table. */
537         _nxd_ipv6_default_router_add_internal(ip_ptr, ipv6_header -> nx_ip_header_source_ip,     /* Next Hop address */
538                                               ra_ptr -> nx_icmpv6_ra_router_lifetime, if_ptr,
539                                               router_type, &rt_entry);
540 
541         /* Link router entry and its corresponding nd_entry.
542            Note that at this point, nd_entry may not be valid.
543            When a packet is transmitted using this router, and the nd_entry is invalid (NX_NULL)
544            ipv6 send routine shall use neighbor discovery process to find the address. */
545         /*lint -e{644} suppress variable might not be initialized, since "rt_entry" was initialized in _nxd_ipv6_default_router_add_internal. */
546         if (rt_entry && nd_entry)
547         {
548             rt_entry -> nx_ipv6_default_router_entry_neighbor_cache_ptr = (void *)nd_entry;
549             nd_entry -> nx_nd_cache_is_router = rt_entry;
550         }
551 
552         if (ra_ptr -> nx_icmpv6_ra_hop_limit)
553         {
554             ip_ptr -> nx_ipv6_hop_limit = ra_ptr -> nx_icmpv6_ra_hop_limit;
555         }
556 
557 #ifdef NX_ENABLE_IPV6_PATH_MTU_DISCOVERY
558 
559         /* Special case destination table entry, the router itself.  Note that the destination is the.
560            same as the next hop. Adding the router to the destination table  gives us a place to
561            store path MTU updates as RAs (router advertisements) containing MTU option data. */
562 
563         /* Have we already established path MTU for this router? */
564         if (mtu_ptr == 0)
565         {
566 
567         NX_IPV6_DESTINATION_ENTRY *dest_entry_ptr;
568 
569             /* No, create an entry in the destination table for it, and set
570                the path MTU to our default path MTU. */
571             _nx_icmpv6_dest_table_add(ip_ptr, ipv6_header -> nx_ip_header_source_ip, &dest_entry_ptr,
572                                       ipv6_header -> nx_ip_header_source_ip,
573                                       if_ptr -> nx_interface_ip_mtu_size, NX_WAIT_FOREVER,
574                                       packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr);
575         }
576 #endif
577     }
578 
579 #ifndef NX_DISABLE_ICMPV6_ROUTER_SOLICITATION
580 
581     /* Received a valid RS... Stop RA if it is still running. */
582     if_ptr -> nx_ipv6_rtr_solicitation_count = 0;
583 
584 #endif
585 
586     /* release packet and return. */
587     _nx_packet_release(packet_ptr);
588 }
589 
590 #endif /* NX_DISABLE_ICMPV6_ROUTER_ADVERTISEMENT_PROCESS */
591 
592 #endif /* FEATURE_NX_IPV6 */
593 
594