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