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 /** Internet Protocol (IP) */
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
30
31 /**************************************************************************/
32 /* */
33 /* FUNCTION RELEASE */
34 /* */
35 /* _nxd_ipv6_address_set PORTABLE C */
36 /* 6.1.11 */
37 /* AUTHOR */
38 /* */
39 /* Yuxin Zhou, Microsoft Corporation */
40 /* */
41 /* DESCRIPTION */
42 /* */
43 /* This function sets the IP address and the prefix length for the */
44 /* supplied IP instance. */
45 /* */
46 /* For the IPv6 address, an application is only allowed to change the */
47 /* global IP address. The link local address is generated through the */
48 /* underlying interface address, therefore cannot be reconfigured by */
49 /* user level applications. */
50 /* */
51 /* INPUT */
52 /* */
53 /* ip_ptr IP control block pointer */
54 /* interface_index The index to the physical */
55 /* interface this address */
56 /* belongs to */
57 /* ip_address Pointer to IP address */
58 /* structure */
59 /* prefix_length Prefix length */
60 /* address_index Index to the IPv6 address */
61 /* table */
62 /* */
63 /* OUTPUT */
64 /* */
65 /* NX_SUCCESS Completion status */
66 /* */
67 /* CALLS */
68 /* */
69 /* _nx_ipv6_multicast_join Multicast group join service */
70 /* [ipv6_address_change_notify] User callback function */
71 /* tx_mutex_get Get protection mutex */
72 /* tx_mutex_put Put protection mutex */
73 /* */
74 /* CALLED BY */
75 /* */
76 /* Application Code */
77 /* */
78 /* RELEASE HISTORY */
79 /* */
80 /* DATE NAME DESCRIPTION */
81 /* */
82 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
83 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
84 /* resulting in version 6.1 */
85 /* 04-25-2022 Yuxin Zhou Modified comment(s), and */
86 /* added internal ip address */
87 /* change notification, */
88 /* resulting in version 6.1.11 */
89 /* */
90 /**************************************************************************/
_nxd_ipv6_address_set(NX_IP * ip_ptr,UINT interface_index,NXD_ADDRESS * ip_address,ULONG prefix_length,UINT * address_index)91 UINT _nxd_ipv6_address_set(NX_IP *ip_ptr, UINT interface_index, NXD_ADDRESS *ip_address, ULONG prefix_length, UINT *address_index)
92 {
93 #ifdef FEATURE_NX_IPV6
94
95 TX_INTERRUPT_SAVE_AREA
96 #ifdef NX_ENABLE_IPV6_ADDRESS_CHANGE_NOTIFY
97 VOID (*address_change_notify)(NX_IP *, UINT, UINT, UINT, ULONG *);
98 VOID (*address_change_notify_internal)(NX_IP *, UINT, UINT, UINT, ULONG *);
99 #endif /* NX_ENABLE_IPV6_ADDRESS_CHANGE_NOTIFY */
100 NXD_IPV6_ADDRESS *ipv6_addr;
101 NXD_IPV6_ADDRESS *interface_ipv6_address;
102 UINT index = (UINT)0xFFFFFFFF;
103 UINT i;
104 ULONG multicast_address[4];
105
106 /* Place protection while the IPv6 address is modified. */
107 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
108
109 if (ip_address)
110 {
111
112 /* Perform duplicate address detection. */
113 for (i = 0; i < NX_MAX_IPV6_ADDRESSES; i++)
114 {
115 if ((ip_ptr -> nx_ipv6_address[i].nxd_ipv6_address_valid) &&
116 (ip_ptr -> nx_ipv6_address[i].nxd_ipv6_address[0] == ip_address -> nxd_ip_address.v6[0]) &&
117 (ip_ptr -> nx_ipv6_address[i].nxd_ipv6_address[1] == ip_address -> nxd_ip_address.v6[1]) &&
118 (ip_ptr -> nx_ipv6_address[i].nxd_ipv6_address[2] == ip_address -> nxd_ip_address.v6[2]) &&
119 (ip_ptr -> nx_ipv6_address[i].nxd_ipv6_address[3] == ip_address -> nxd_ip_address.v6[3]))
120 {
121
122 /* The IPv6 address already exists. */
123 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
124 return(NX_DUPLICATED_ENTRY);
125 }
126 }
127 }
128
129 /* Find an avaiable IPv6 address structure. */
130 for (i = 0; i < NX_MAX_IPV6_ADDRESSES; i++)
131 {
132 /* Look for invalid entries. */
133 if (!ip_ptr -> nx_ipv6_address[i].nxd_ipv6_address_valid)
134 {
135
136 /* An available entry is found. */
137 index = i;
138 break;
139 }
140 }
141
142 if (index == (UINT)0xFFFFFFFF)
143 {
144 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
145 return(NX_NO_MORE_ENTRIES);
146 }
147
148 if (address_index)
149 {
150 *address_index = index;
151 }
152
153 /* Pointer to the IPv6 address that needs to be modified. */
154 ipv6_addr = &(ip_ptr -> nx_ipv6_address[index]);
155
156 /* The address is null. */
157 if ((!ip_address) && (prefix_length == 10))
158 {
159
160 /* No address supplied. Assume it is link local address, constructed from the physical interface. */
161 ULONG word2, word3;
162
163 /* Construct Interface Identifier, following RFC2464, page 3 */
164 /* Assign link local address.
165 LL address is constructed by:
166 0xFE80::{64 bit interface ID}. See RFC 4291 */
167
168 word2 = ip_ptr -> nx_ip_interface[interface_index].nx_interface_physical_address_msw << 16 |
169 ((ip_ptr -> nx_ip_interface[interface_index].nx_interface_physical_address_lsw & 0xFF000000) >> 16) | 0xFF;
170
171 /* Fix the 2nd lower-order bit of the 1st byte, RFC2464, page 3 */
172 word2 = (word2 & 0xFDFFFFFF) | (~(word2 | 0xFDFFFFFF));
173 word3 = (ip_ptr -> nx_ip_interface[interface_index].nx_interface_physical_address_lsw & 0x00FFFFFF) | 0xFE000000;
174
175 /* Point to interface link list head */
176 interface_ipv6_address = ip_ptr -> nx_ip_interface[interface_index].nxd_interface_ipv6_address_list_head;
177
178 /* Perform link local duplicate address detection. */
179 while (interface_ipv6_address)
180 {
181 if ((interface_ipv6_address -> nxd_ipv6_address[0] == 0xFE800000) &&
182 (interface_ipv6_address -> nxd_ipv6_address[1] == 0x00000000) &&
183 (interface_ipv6_address -> nxd_ipv6_address[2] == word2) &&
184 (interface_ipv6_address -> nxd_ipv6_address[3] == word3))
185 {
186
187 /* The IPv6 address already exists. */
188 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
189 return(NX_DUPLICATED_ENTRY);
190 }
191
192 /* Walk down the list. */
193 interface_ipv6_address = interface_ipv6_address -> nxd_ipv6_address_next;
194 }
195
196 /* Set up the link local address. */
197 ipv6_addr -> nxd_ipv6_address[0] = 0xFE800000;
198 ipv6_addr -> nxd_ipv6_address[1] = 0x00000000;
199 ipv6_addr -> nxd_ipv6_address[2] = word2;
200 ipv6_addr -> nxd_ipv6_address[3] = word3;
201 }
202 else if (ip_address != NX_NULL)
203 {
204 ipv6_addr -> nxd_ipv6_address[0] = ip_address -> nxd_ip_address.v6[0];
205 ipv6_addr -> nxd_ipv6_address[1] = ip_address -> nxd_ip_address.v6[1];
206 ipv6_addr -> nxd_ipv6_address[2] = ip_address -> nxd_ip_address.v6[2];
207 ipv6_addr -> nxd_ipv6_address[3] = ip_address -> nxd_ip_address.v6[3];
208 }
209 else
210 {
211 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
212 return(NX_IP_ADDRESS_ERROR);
213 }
214
215 ipv6_addr -> nxd_ipv6_address_valid = NX_TRUE;
216 ipv6_addr -> nxd_ipv6_address_type = NX_IP_VERSION_V6;
217 ipv6_addr -> nxd_ipv6_address_prefix_length = (UCHAR)(prefix_length & 0xFF);
218 ipv6_addr -> nxd_ipv6_address_next = NX_NULL;
219
220 /* Attach to the interface. */
221 ipv6_addr -> nxd_ipv6_address_attached = &ip_ptr -> nx_ip_interface[interface_index];
222
223 /* Point to interface link list head */
224 interface_ipv6_address = ip_ptr -> nx_ip_interface[interface_index].nxd_interface_ipv6_address_list_head;
225
226 /* Walk to the end of the list. */
227 while (interface_ipv6_address)
228 {
229
230 /* If the next entry does not exist, we already reach the end of the list.
231 Add the IPv6 address towards the end. */
232 if (interface_ipv6_address -> nxd_ipv6_address_next)
233 {
234 interface_ipv6_address = interface_ipv6_address -> nxd_ipv6_address_next;
235 }
236 else
237 {
238 interface_ipv6_address -> nxd_ipv6_address_next = ipv6_addr;
239 break;
240 }
241 }
242
243 /* Check whether the head is NULL */
244 if (interface_ipv6_address == NX_NULL)
245 {
246 /* This interface does not have IPv6 addresses yet. */
247 ip_ptr -> nx_ip_interface[interface_index].nxd_interface_ipv6_address_list_head = ipv6_addr;
248 }
249
250 /* If trace is enabled, insert this event into the trace buffer. */
251 NX_TRACE_IN_LINE_INSERT(NXD_TRACE_IPV6_INTERFACE_ADDRESS_SET, ip_ptr, ipv6_addr -> nxd_ipv6_address[3], prefix_length, index, NX_TRACE_IP_EVENTS, 0, 0);
252
253 /* Set the configuration type to manual. */
254 ipv6_addr -> nxd_ipv6_address_ConfigurationMethod = NX_IPV6_ADDRESS_MANUAL_CONFIG;
255
256 /* Release the IP protection. nx_ipv6_multicast_join would need to obtain the mutex. */
257 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
258 /* Join the solicited-node multicast group */
259 /* FF02::1:FFXX:XXXX */
260 SET_SOLICITED_NODE_MULTICAST_ADDRESS(multicast_address, ipv6_addr -> nxd_ipv6_address);
261 _nx_ipv6_multicast_join(ip_ptr, &multicast_address[0], ipv6_addr -> nxd_ipv6_address_attached);
262
263 /* Obtain the IP protection again. */
264 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
265
266 TX_DISABLE
267 #ifndef NX_DISABLE_IPV6_DAD
268
269 /* If ICMPv6 is enabled, mark the address as tentative, per RFC2462.
270 If DAD is not enabled, start the address in VALID state. */
271 if (ip_ptr -> nx_ip_icmpv6_packet_process)
272 {
273
274 /* Start DAD */
275 ipv6_addr -> nxd_ipv6_address_state = NX_IPV6_ADDR_STATE_TENTATIVE;
276 ipv6_addr -> nxd_ipv6_address_DupAddrDetectTransmit = NX_IPV6_DAD_TRANSMITS;
277
278 /* If trace is enabled, log the start of DAD process. */
279 NX_TRACE_IN_LINE_INSERT(NXD_TRACE_IPV6_INITIATE_DAD_PROCESS, ip_ptr, 0, 0, 0, NX_TRACE_INTERNAL_EVENTS, 0, 0);
280 }
281 else
282 {
283
284 /* If ICMPv6 is not enabled on this interface, the DAD process is eliminated,
285 so mark the input IP address directly as valid. */
286 ipv6_addr -> nxd_ipv6_address_state = NX_IPV6_ADDR_STATE_VALID;
287 ipv6_addr -> nxd_ipv6_address_DupAddrDetectTransmit = 0;
288 }
289 #else
290 /* Set the input address as valid. */
291 ipv6_addr -> nxd_ipv6_address_state = NX_IPV6_ADDR_STATE_VALID;
292 #endif
293
294 /* Restore interrupts. */
295 TX_RESTORE
296 #ifdef NX_ENABLE_IPV6_ADDRESS_CHANGE_NOTIFY
297 /* Pickup the current notification callback and additional information pointers. */
298 address_change_notify = ip_ptr -> nx_ipv6_address_change_notify;
299 address_change_notify_internal = ip_ptr -> nx_ipv6_address_change_notify_internal;
300 #endif /* NX_ENABLE_IPV6_ADDRESS_CHANGE_NOTIFY */
301 /* Release the protection while the IPv6 address is modified. */
302 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
303
304 #ifdef NX_ENABLE_IPV6_ADDRESS_CHANGE_NOTIFY
305 /* Is the application configured for notification of address changes and/or
306 prefix_length change? */
307 if (address_change_notify)
308 {
309
310 /* Yes, call the application's address change notify function. */
311 (address_change_notify)(ip_ptr, NX_IPV6_ADDRESS_MANUAL_CONFIG, interface_index, index, &ipv6_addr -> nxd_ipv6_address[0]);
312 }
313
314 /* Is the internal application configured for notification of address changes and/or
315 prefix_length change? */
316 if ((address_change_notify_internal) && (ipv6_addr -> nxd_ipv6_address_state == NX_IPV6_ADDR_STATE_VALID))
317 {
318
319 /* Yes, call the internal application's address change notify function. */
320 (address_change_notify_internal)(ip_ptr, NX_IPV6_ADDRESS_MANUAL_CONFIG, interface_index, index, &ipv6_addr -> nxd_ipv6_address[0]);
321 }
322 #endif /* NX_ENABLE_IPV6_ADDRESS_CHANGE_NOTIFY */
323
324 /* Return completion status. */
325 return(NX_SUCCESS);
326
327 #else /* !FEATURE_NX_IPV6 */
328 NX_PARAMETER_NOT_USED(ip_ptr);
329 NX_PARAMETER_NOT_USED(interface_index);
330 NX_PARAMETER_NOT_USED(ip_address);
331 NX_PARAMETER_NOT_USED(prefix_length);
332 NX_PARAMETER_NOT_USED(address_index);
333
334 return(NX_NOT_SUPPORTED);
335
336 #endif /* FEATURE_NX_IPV6 */
337 }
338
339