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