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 /**   Neighbor Discovery Cache                                            */
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_ipv6.h"
29 #include "nx_nd_cache.h"
30 
31 #ifdef FEATURE_NX_IPV6
32 
33 
34 /**************************************************************************/
35 /*                                                                        */
36 /*  FUNCTION                                               RELEASE        */
37 /*                                                                        */
38 /*    _nx_nd_cache_add_entry                              PORTABLE C      */
39 /*                                                           6.1          */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    Yuxin Zhou, Microsoft Corporation                                   */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This internal function finds an entry in the ND cache that is       */
47 /*    mapped to the specified IPv6 address.  If the entry does not exist, */
48 /*    this function allocates an empty entry and add the IP address to it.*/
49 /*                                                                        */
50 /*  Note:                                                                 */
51 /*                                                                        */
52 /*    This routine acquires the nx_nd_cache_protection mutex.             */
53 /*       Application shall not hold this mutex before calling this        */
54 /*       function.                                                        */
55 /*                                                                        */
56 /*    If the table is full and NetX Duo is configured to purge older      */
57 /*    entries to make room for new entries, NetX Duo attempts to find the */
58 /*    the best candidate to remove (STALE or REACHABLE).  NetX Duo        */
59 /*    will not remove any cache entries in the INCOMPLETE, PROBE or DELAY */
60 /*    state since these are probably being processed e.g. neighborhood    */
61 /*    discovery by NetX Duo during this time.                             */
62 /*                                                                        */
63 /*  INPUT                                                                 */
64 /*                                                                        */
65 /*    ip_ptr                   Pointer to IP instance                     */
66 /*    dest_ip                  The IP address to match                    */
67 /*    iface_address            Pointer to the IPv6 address structure      */
68 /*    nd_cache_entry           User specified storage space for pointer to*/
69 /*                               the corresponding ND cache.              */
70 /*                                                                        */
71 /*  OUTPUT                                                                */
72 /*                                                                        */
73 /*    NX_SUCCESS              ND cache entry found, contains valid value  */
74 /*    NX_NOT_SUCCESSFUL       ND cache entry not found or entry is invalid*/
75 /*                                                                        */
76 /*  CALLS                                                                 */
77 /*                                                                        */
78 /*    tx_mutex_get                          Obtain protection mutex       */
79 /*    tx_mutex_put                          Release protection mutex      */
80 /*                                                                        */
81 /*  CALLED BY                                                             */
82 /*                                                                        */
83 /*    _nx_icmpv6_process_redirect                                         */
84 /*    _nx_ipv6_packet_send                                                */
85 /*    _nx_nd_cache_add                                                    */
86 /*                                                                        */
87 /*  RELEASE HISTORY                                                       */
88 /*                                                                        */
89 /*    DATE              NAME                      DESCRIPTION             */
90 /*                                                                        */
91 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
92 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
93 /*                                            resulting in version 6.1    */
94 /*                                                                        */
95 /**************************************************************************/
96 
_nx_nd_cache_add_entry(NX_IP * ip_ptr,ULONG * dest_ip,NXD_IPV6_ADDRESS * iface_address,ND_CACHE_ENTRY ** nd_cache_entry)97 UINT _nx_nd_cache_add_entry(NX_IP *ip_ptr, ULONG *dest_ip,
98                             NXD_IPV6_ADDRESS *iface_address,
99                             ND_CACHE_ENTRY **nd_cache_entry)
100 {
101 
102 UINT i;
103 UINT index;
104 UINT first_available;
105 #ifndef NX_DISABLE_IPV6_PURGE_UNUSED_CACHE_ENTRIES
106 UINT stale_timer_ticks;
107 UINT timer_ticks_left;
108 #endif
109 
110     NX_PARAMETER_NOT_USED(ip_ptr);
111 
112     /* Set the found slot past the end of the table. If a match or available
113        slot found, this will have a lower value. */
114     first_available = NX_IPV6_NEIGHBOR_CACHE_SIZE;
115 
116     /* Initialize the return value. */
117     *nd_cache_entry = NX_NULL;
118 
119     /* Compute a simple hash based on the destination IP address. */
120     index = (UINT)((dest_ip[0] + dest_ip[1] + dest_ip[2] + dest_ip[3]) %
121                    (NX_IPV6_NEIGHBOR_CACHE_SIZE));
122 
123 #ifndef NX_DISABLE_IPV6_PURGE_UNUSED_CACHE_ENTRIES
124 
125     /* Set the lowest possible timer ticks left to compare to. */
126     stale_timer_ticks = 0;
127 
128     /* Start out at a very high number of remaining ticks to compare to. */
129     timer_ticks_left = 0xFFFFFFFF;
130 #endif
131 
132     /* Loop through all the entries. */
133     for (i = 0; i < NX_IPV6_NEIGHBOR_CACHE_SIZE; i++, index++)
134     {
135 
136         /* Check for overflow */
137         if (index == NX_IPV6_NEIGHBOR_CACHE_SIZE)
138         {
139 
140             /* Start back at the first table entry. */
141             index = 0;
142         }
143 
144         /* Is the current entry available? */
145         if (ip_ptr -> nx_ipv6_nd_cache[index].nx_nd_cache_nd_status == ND_CACHE_STATE_INVALID)
146         {
147 
148             /* There is a chance the entry to add does not exist in the table. We create one using the
149                invalid entry. */
150             first_available = index;
151             break;
152         }
153 
154 #ifndef NX_DISABLE_IPV6_PURGE_UNUSED_CACHE_ENTRIES
155         /* Skip over routers and static entries. */
156         if (ip_ptr -> nx_ipv6_nd_cache[index].nx_nd_cache_is_router != NX_NULL || ip_ptr -> nx_ipv6_nd_cache[index].nx_nd_cache_is_static)
157         {
158             continue;
159         }
160 
161         /* Purging is enabled;
162            Attempt to find a STALE entry and if there is more than one,
163            choose the oldest one e.g. the highest timer ticks elapsed. */
164 
165         /* Check for stale entries. These are the best candidates for 'recycling.' */
166         if (ip_ptr -> nx_ipv6_nd_cache[index].nx_nd_cache_nd_status == ND_CACHE_STATE_STALE)
167         {
168 
169             /* Find the 'Stale' cache entry with the highest timer tick since
170                timer tick is incremented in the Stale state.*/
171             if (ip_ptr -> nx_ipv6_nd_cache[index].nx_nd_cache_timer_tick > stale_timer_ticks)
172             {
173                 /* Set this entry as the oldest stale entry. */
174                 stale_timer_ticks = (UINT)ip_ptr -> nx_ipv6_nd_cache[index].nx_nd_cache_timer_tick;
175                 first_available = index;
176             }
177         }
178         /* Next try finding a REACHABLE entry closest to its cache table expiration date. */
179         else if (stale_timer_ticks == 0 &&
180                  ip_ptr -> nx_ipv6_nd_cache[index].nx_nd_cache_nd_status == ND_CACHE_STATE_REACHABLE)
181         {
182 
183             /* Is this entry older that our previous oldest entry? */
184             if (ip_ptr -> nx_ipv6_nd_cache[index].nx_nd_cache_timer_tick < timer_ticks_left)
185             {
186 
187                 /* Set this entry as the oldest entry using timer ticks left. */
188                 timer_ticks_left = (UINT)ip_ptr -> nx_ipv6_nd_cache[index].nx_nd_cache_timer_tick;
189                 first_available = index;
190             }
191         }
192 #endif
193     }
194 
195     /* Did not find a available entry. */
196     if (first_available == NX_IPV6_NEIGHBOR_CACHE_SIZE)
197     {
198 
199         /* Return unsuccessful status. */
200         return(NX_NOT_SUCCESSFUL);
201     }
202 
203     /* Yes; before we invalidate and delete the entry, we need to
204        clean the nd cache. */
205     _nx_nd_cache_delete_internal(ip_ptr, &ip_ptr -> nx_ipv6_nd_cache[first_available]);
206 
207     /* Record the IP address. */
208     COPY_IPV6_ADDRESS(dest_ip, ip_ptr -> nx_ipv6_nd_cache[first_available].nx_nd_cache_dest_ip);
209 
210     /* A new entry starts with CREATED status. */
211     ip_ptr -> nx_ipv6_nd_cache[first_available].nx_nd_cache_nd_status = ND_CACHE_STATE_CREATED;
212 
213     ip_ptr -> nx_ipv6_nd_cache[first_available].nx_nd_cache_outgoing_address = iface_address;
214 
215     ip_ptr -> nx_ipv6_nd_cache[first_available].nx_nd_cache_interface_ptr = iface_address -> nxd_ipv6_address_attached;
216 
217     /* Release the protection. */
218     *nd_cache_entry = &ip_ptr -> nx_ipv6_nd_cache[first_available];
219 
220     return(NX_SUCCESS);
221 }
222 
223 #endif /* FEATURE_NX_IPV6 */
224 
225