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