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