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