slaac_misc.c
Go to the documentation of this file.
1 /**
2  * @file slaac_misc.c
3  * @brief Helper functions for SLAAC
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2023 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.2.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL SLAAC_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "core/ethernet.h"
37 #include "ipv6/ipv6.h"
38 #include "ipv6/ipv6_misc.h"
39 #include "ipv6/slaac.h"
40 #include "ipv6/slaac_misc.h"
41 #include "ipv6/ndp.h"
42 #include "ipv6/ndp_misc.h"
43 #include "debug.h"
44 
45 //Check TCP/IP stack configuration
46 #if (IPV6_SUPPORT == ENABLED && SLAAC_SUPPORT == ENABLED)
47 
48 
49 /**
50  * @brief Callback function for link change event
51  * @param[in] context Pointer to the SLAAC context
52  **/
53 
55 {
56  NetInterface *interface;
57 
58  //Make sure the SLAAC service has been properly instantiated
59  if(context == NULL)
60  return;
61 
62  //Point to the underlying network interface
63  interface = context->settings.interface;
64 
65  //Check whether SLAAC is enabled
66  if(context->running)
67  {
68  //Automatic DNS server configuration?
69  if(!context->settings.manualDnsConfig)
70  {
71  //Clear the list of DNS servers
72  ipv6FlushDnsServerList(interface);
73  }
74 
75  //Link-up event?
76  if(interface->linkState)
77  {
78  //A link-local address is formed by combining the well-known
79  //link-local prefix fe80::/10 with the interface identifier
81  }
82  }
83 
84  //Any registered callback?
85  if(context->settings.linkChangeEvent != NULL)
86  {
87  //Release exclusive access
89  //Invoke user callback function
90  context->settings.linkChangeEvent(context, interface, interface->linkState);
91  //Get exclusive access
93  }
94 }
95 
96 
97 /**
98  * @brief Parse Router Advertisement message
99  * @param[in] context Pointer to the SLAAC context
100  * @param[in] message Pointer to the Router Advertisement message
101  * @param[in] length Length of the message, in bytes
102  **/
103 
106 {
107  uint_t i;
108  uint_t n;
109  NetInterface *interface;
110  NdpPrefixInfoOption *prefixInfoOption;
111  NdpRdnssOption *rdnssOption;
112 
113  //Point to the underlying network interface
114  interface = context->settings.interface;
115 
116  //Check whether SLAAC is enabled
117  if(!context->running)
118  return;
119 
120  //Make sure that a valid link-local address has been assigned to the interface
122  return;
123 
124  //Calculate the length of the Options field
125  length -= sizeof(NdpRouterAdvMessage);
126 
127  //This flag tracks changes in IPv6 configuration
128  context->configUpdated = FALSE;
129 
130  //Point to the beginning of the Options field
131  n = 0;
132 
133  //Parse Options field
134  while(1)
135  {
136  //Search the Options field for any Prefix Information options
137  prefixInfoOption = ndpGetOption(message->options + n,
139 
140  //No more option of the specified type?
141  if(prefixInfoOption == NULL)
142  break;
143 
144  //Parse the Prefix Information Option
145  slaacParsePrefixInfoOption(context, prefixInfoOption);
146 
147  //Retrieve the offset to the current position
148  n = (uint8_t *) prefixInfoOption - message->options;
149  //Jump to the next option
150  n += prefixInfoOption->length * 8;
151  }
152 
153  //Automatic DNS server configuration?
154  if(!context->settings.manualDnsConfig)
155  {
156  //Search for the Recursive DNS Server (RDNSS) option
157  rdnssOption = ndpGetOption(message->options, length,
159 
160  //RDNSS option found?
161  if(rdnssOption != NULL && rdnssOption->length >= 1)
162  {
163  //Retrieve the number of addresses
164  n = (rdnssOption->length - 1) / 2;
165 
166  //Loop through the list of DNS servers
167  for(i = 0; i < n && i < IPV6_DNS_SERVER_LIST_SIZE; i++)
168  {
169  //Record DNS server address
170  interface->ipv6Context.dnsServerList[i] = rdnssOption->address[i];
171  }
172  }
173  }
174 
175  //Any registered callback?
176  if(context->settings.parseRouterAdvCallback != NULL)
177  {
178  //Invoke user callback function
179  context->settings.parseRouterAdvCallback(context, message, length);
180  }
181 
182  //Check whether a new IPv6 address has been assigned to the interface
183  if(context->configUpdated)
184  {
185  //Dump current IPv6 configuration for debugging purpose
186  slaacDumpConfig(context);
187  }
188 }
189 
190 
191 /**
192  * @brief Parse Prefix Information Option
193  * @param[in] context Pointer to the SLAAC context
194  * @param[in] option Pointer to the Prefix Information option
195  **/
196 
198  NdpPrefixInfoOption *option)
199 {
200  uint_t i;
201  bool_t found;
202  systime_t time;
205  systime_t remainingLifetime;
206  NetInterface *interface;
207  NetInterface *logicalInterface;
208  Ipv6AddrEntry *entry;
209  Ipv6Addr addr;
210 
211  //Make sure the Prefix Information option is valid
212  if(option == NULL || option->length != 4)
213  return;
214 
215  //If the Autonomous flag is not set, silently ignore the Prefix
216  //Information option
217  if(!option->a)
218  return;
219 
220  //If the prefix is the link-local prefix, silently ignore the
221  //Prefix Information option
222  if(ipv6CompPrefix(&option->prefix, &IPV6_LINK_LOCAL_ADDR_PREFIX, 10))
223  return;
224 
225  //Check whether the valid lifetime is zero
226  if(ntohl(option->validLifetime) == 0)
227  return;
228 
229  //If the preferred lifetime is greater than the valid lifetime,
230  //silently ignore the Prefix Information option
231  if(ntohl(option->preferredLifetime) > ntohl(option->validLifetime))
232  return;
233 
234  //If the sum of the prefix length and interface identifier length does
235  //not equal 128 bits, the Prefix Information option must be ignored
236  if(option->prefixLength != 64)
237  return;
238 
239  //Get current time
240  time = osGetSystemTime();
241 
242  //Point to the underlying network interface
243  interface = context->settings.interface;
244 
245  //Point to the logical interface
246  logicalInterface = nicGetLogicalInterface(interface);
247 
248  //Form an address by combining the advertised prefix with the interface
249  //identifier
250  addr.w[0] = option->prefix.w[0];
251  addr.w[1] = option->prefix.w[1];
252  addr.w[2] = option->prefix.w[2];
253  addr.w[3] = option->prefix.w[3];
254  addr.w[4] = logicalInterface->eui64.w[0];
255  addr.w[5] = logicalInterface->eui64.w[1];
256  addr.w[6] = logicalInterface->eui64.w[2];
257  addr.w[7] = logicalInterface->eui64.w[3];
258 
259  //Convert Valid Lifetime to host byte order
260  validLifetime = ntohl(option->validLifetime);
261 
262  //Check the valid lifetime
264  {
265  //The length of time in seconds that the prefix is valid for the
266  //purpose of on-link determination
267  if(validLifetime < (MAX_DELAY / 1000))
268  {
269  validLifetime *= 1000;
270  }
271  else
272  {
274  }
275  }
276  else
277  {
278  //A value of all one bits (0xffffffff) represents infinity
280  }
281 
282  //Convert Preferred Lifetime to host byte order
283  preferredLifetime = ntohl(option->preferredLifetime);
284 
285  //Check the preferred lifetime
287  {
288  //The length of time in seconds that addresses generated from the
289  //prefix via stateless address autoconfiguration remain preferred
290  if(preferredLifetime < (MAX_DELAY / 1000))
291  {
292  preferredLifetime *= 1000;
293  }
294  else
295  {
297  }
298  }
299  else
300  {
301  //A value of all one bits (0xffffffff) represents infinity
303  }
304 
305  //This flag will be set if the advertised prefix matches an address
306  //assigned to the interface
307  found = FALSE;
308 
309  //Loop through the list of IPv6 addresses
310  for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
311  {
312  //Point to the current entry
313  entry = &interface->ipv6Context.addrList[i];
314 
315  //Check whether the advertised prefix is equal to the prefix of an
316  //address configured by stateless autoconfiguration in the list
317  if(ipv6CompPrefix(&entry->addr, &option->prefix, option->prefixLength))
318  {
319  //Valid address?
320  if(entry->state == IPV6_ADDR_STATE_PREFERRED ||
322  {
323  //Set flag
324  found = TRUE;
325 
326  //The preferred lifetime of the address is reset to the Preferred
327  //Lifetime in the received advertisement
329 
330  //Compute the remaining time to the valid lifetime expiration
331  //of the previously autoconfigured address
332  if(timeCompare(time, entry->timestamp + entry->validLifetime) < 0)
333  remainingLifetime = entry->timestamp + entry->validLifetime - time;
334  else
335  remainingLifetime = 0;
336 
337  //The specific action to perform for the valid lifetime of the
338  //address depends on the Valid Lifetime in the received Router
339  //Advertisement and the remaining time
341  validLifetime > remainingLifetime)
342  {
343  //If the received Valid Lifetime is greater than 2 hours or
344  //greater than remaining lifetime, set the valid lifetime of
345  //the corresponding address to the advertised Valid Lifetime
346  entry->validLifetime = validLifetime;
347 
348  //Save current time
349  entry->timestamp = time;
350  //Update the state of the IPv6 address
352  }
353  else if(remainingLifetime <= SLAAC_LIFETIME_2_HOURS)
354  {
355  //If remaining lifetime is less than or equal to 2 hours, ignore
356  //the Prefix Information option with regards to the valid lifetime
357  }
358  else
359  {
360  //Otherwise, reset the valid lifetime of the corresponding
361  //address to 2 hours
363 
364  //Save current time
365  entry->timestamp = time;
366  //Update the state of the IPv6 address
368  }
369  }
370  //Tentative address?
371  else if(entry->state == IPV6_ADDR_STATE_TENTATIVE)
372  {
373  //Do not update the preferred and valid lifetimes of the address
374  //when Duplicate Address Detection is being performed
375  found = TRUE;
376  }
377  }
378  }
379 
380  //IPv6 address not found in the list?
381  if(!found)
382  {
383  //Loop through the list of IPv6 addresses
384  for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
385  {
386  //Point to the current entry
387  entry = &interface->ipv6Context.addrList[i];
388 
389  //Check the state of the IPv6 address
390  if(entry->state == IPV6_ADDR_STATE_INVALID)
391  {
392  //If an address is formed successfully and the address is not yet
393  //in the list, the host adds it to the list of addresses assigned
394  //to the interface, initializing its preferred and valid lifetime
395  //values from the Prefix Information option
396  if(interface->ndpContext.dupAddrDetectTransmits > 0)
397  {
398  //Use the IPv6 address as a tentative address
401  }
402  else
403  {
404  //The use of the IPv6 address is now unrestricted
407  }
408 
409  //A new IPv6 address has just been assigned to the interface
410  context->configUpdated = TRUE;
411  //We are done
412  break;
413  }
414  }
415  }
416 }
417 
418 
419 /**
420  * @brief Generate a link-local address
421  * @param[in] context Pointer to the SLAAC context
422  * @return Error code
423  **/
424 
426 {
427  error_t error;
428  NetInterface *interface;
429  NetInterface *logicalInterface;
430  Ipv6Addr addr;
431 
432  //Point to the underlying network interface
433  interface = context->settings.interface;
434 
435  //Point to the logical interface
436  logicalInterface = nicGetLogicalInterface(interface);
437 
438  //Check whether a link-local address has been manually assigned
439  if(interface->ipv6Context.addrList[0].state != IPV6_ADDR_STATE_INVALID &&
440  interface->ipv6Context.addrList[0].permanent)
441  {
442  //Keep using the current link-local address
443  error = NO_ERROR;
444  }
445  else
446  {
447  //A link-local address is formed by combining the well-known link-local
448  //prefix fe80::/10 with the interface identifier
449  ipv6GenerateLinkLocalAddr(&logicalInterface->eui64, &addr);
450 
451  //Check whether Duplicate Address Detection should be performed
452  if(interface->ndpContext.dupAddrDetectTransmits > 0)
453  {
454  //Use the link-local address as a tentative address
455  error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_TENTATIVE,
457  }
458  else
459  {
460  //The use of the link-local address is now unrestricted
461  error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_PREFERRED,
463  }
464  }
465 
466  //Return status code
467  return error;
468 }
469 
470 
471 /**
472  * @brief Dump IPv6 configuration for debugging purpose
473  * @param[in] context Pointer to the SLAAC context
474  **/
475 
477 {
478 #if (SLAAC_TRACE_LEVEL >= TRACE_LEVEL_INFO)
479  uint_t i;
480  NetInterface *interface;
481  Ipv6Context *ipv6Context;
482 
483  //Point to the underlying network interface
484  interface = context->settings.interface;
485  //Point to the IPv6 context
486  ipv6Context = &interface->ipv6Context;
487 
488  //Debug message
489  TRACE_INFO("\r\n");
490  TRACE_INFO("SLAAC configuration:\r\n");
491 
492  //Link-local address
493  TRACE_INFO(" Link-local Address = %s\r\n",
494  ipv6AddrToString(&ipv6Context->addrList[0].addr, NULL));
495 
496  //Global addresses
497  for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
498  {
499  TRACE_INFO(" Global Address %u = %s\r\n", i,
500  ipv6AddrToString(&ipv6Context->addrList[i].addr, NULL));
501  }
502 
503  //IPv6 prefixes
504  for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++)
505  {
506  TRACE_INFO(" Prefix %u = %s/%" PRIu8 "\r\n", i + 1,
507  ipv6AddrToString(&ipv6Context->prefixList[i].prefix, NULL),
508  ipv6Context->prefixList[i].prefixLen);
509  }
510 
511  //Default routers
512  for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++)
513  {
514  TRACE_INFO(" Default Router %u = %s\r\n", i + 1,
515  ipv6AddrToString(&ipv6Context->routerList[i].addr, NULL));
516  }
517 
518  //DNS servers
519  for(i = 0; i < IPV6_DNS_SERVER_LIST_SIZE; i++)
520  {
521  TRACE_INFO(" DNS Server %u = %s\r\n", i + 1,
522  ipv6AddrToString(&ipv6Context->dnsServerList[i], NULL));
523  }
524 
525  //Maximum transmit unit
526  TRACE_INFO(" MTU = %" PRIuSIZE "\r\n", ipv6Context->linkMtu);
527  TRACE_INFO("\r\n");
528 #endif
529 }
530 
531 #endif
@ IPV6_ADDR_STATE_TENTATIVE
An address whose uniqueness on a link is being verified.
Definition: ipv6.h:168
uint8_t length
Definition: coap_common.h:193
__start_packed struct @11 NdpRdnssOption
Recursive DNS Server option (RDNSS)
char_t * ipv6AddrToString(const Ipv6Addr *ipAddr, char_t *str)
Convert a binary IPv6 address to a string representation.
Definition: ipv6.c:2334
IPv6 (Internet Protocol Version 6)
int bool_t
Definition: compiler_port.h:53
#define SLAAC_LIFETIME_2_HOURS
Definition: slaac.h:46
Helper functions for NDP (Neighbor Discovery Protocol)
#define netMutex
Definition: net_legacy.h:195
#define TRUE
Definition: os_port.h:52
uint32_t preferredLifetime
Ipv6Addr addr
IPv6 address.
Definition: ipv6.h:397
systime_t preferredLifetime
Preferred lifetime.
Definition: ipv6.h:401
#define NDP_INFINITE_LIFETIME
Definition: ndp.h:202
#define timeCompare(t1, t2)
Definition: os_port.h:42
IPv6 Stateless Address Autoconfiguration.
IPv6 context.
Definition: ipv6.h:459
void slaacDumpConfig(SlaacContext *context)
Dump IPv6 configuration for debugging purpose.
Definition: slaac_misc.c:476
@ IPV6_ADDR_STATE_INVALID
An address that is not assigned to any interface.
Definition: ipv6.h:167
Ethernet.
@ NDP_OPT_RECURSIVE_DNS_SERVER
Definition: ndp.h:222
void slaacParsePrefixInfoOption(SlaacContext *context, NdpPrefixInfoOption *option)
Parse Prefix Information Option.
Definition: slaac_misc.c:197
#define FALSE
Definition: os_port.h:48
void ipv6FlushDnsServerList(NetInterface *interface)
Flush the list of DNS servers.
Definition: ipv6_misc.c:735
#define SlaacContext
Definition: slaac.h:50
error_t
Error codes.
Definition: error.h:43
void slaacParseRouterAdv(SlaacContext *context, NdpRouterAdvMessage *message, size_t length)
Parse Router Advertisement message.
Definition: slaac_misc.c:104
#define NetInterface
Definition: net.h:36
Helper functions for IPv6.
#define IPV6_DNS_SERVER_LIST_SIZE
Definition: ipv6.h:94
#define TRACE_INFO(...)
Definition: debug.h:95
__start_packed struct @7 NdpPrefixInfoOption
Prefix Information option (PIO)
NDP (Neighbor Discovery Protocol)
uint32_t systime_t
System time.
error_t ipv6SetAddr(NetInterface *interface, uint_t index, const Ipv6Addr *addr, Ipv6AddrState state, systime_t validLifetime, systime_t preferredLifetime, bool_t permanent)
Set IPv6 address and address state.
Definition: ipv6_misc.c:95
error_t slaacGenerateLinkLocalAddr(SlaacContext *context)
Generate a link-local address.
Definition: slaac_misc.c:425
uint32_t time
IPv6 address entry.
Definition: ipv6.h:396
uint8_t n
#define IPV6_ROUTER_LIST_SIZE
Definition: ipv6.h:87
#define IPV6_ADDR_LIST_SIZE
Definition: ipv6.h:66
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
Ipv6AddrState ipv6GetLinkLocalAddrState(NetInterface *interface)
Get the state of the link-local address.
Definition: ipv6.c:298
void * ndpGetOption(uint8_t *options, size_t length, uint8_t type)
Search a NDP message for a given option.
Definition: ndp_misc.c:641
void ipv6GenerateLinkLocalAddr(const Eui64 *interfaceId, Ipv6Addr *ipAddr)
Generate a IPv6 link-local address from an interface identifier.
Definition: ipv6_misc.c:1452
@ NDP_OPT_PREFIX_INFORMATION
Definition: ndp.h:218
uint8_t message[]
Definition: chap.h:152
bool_t ipv6CompPrefix(const Ipv6Addr *ipAddr1, const Ipv6Addr *ipAddr2, size_t length)
Compare IPv6 address prefixes.
Definition: ipv6_misc.c:1205
systime_t validLifetime
Valid lifetime.
Definition: ipv6.h:400
Helper functions for SLAAC.
uint32_t validLifetime
Ipv4Addr addr
Definition: nbns_common.h:121
#define MAX_DELAY
Definition: os_port.h:79
@ IPV6_ADDR_STATE_PREFERRED
An address assigned to an interface whose use is unrestricted.
Definition: ipv6.h:169
Ipv6AddrState state
IPv6 address state.
Definition: ipv6.h:398
#define IPV6_PREFIX_LIST_SIZE
Definition: ipv6.h:80
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:50
TCP/IP stack core.
NetInterface * nicGetLogicalInterface(NetInterface *interface)
Retrieve logical interface.
Definition: nic.c:52
@ IPV6_ADDR_STATE_DEPRECATED
An address assigned to an interface whose use is discouraged.
Definition: ipv6.h:170
void slaacLinkChangeEvent(SlaacContext *context)
Callback function for link change event.
Definition: slaac_misc.c:54
__start_packed struct @1 NdpRouterAdvMessage
Router Advertisement message.
const Ipv6Addr IPV6_LINK_LOCAL_ADDR_PREFIX
Definition: ipv6.c:82
__start_packed struct @0 Ipv6Addr
IPv6 network address.
#define ntohl(value)
Definition: cpu_endian.h:422
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
#define INFINITE_DELAY
Definition: os_port.h:77
systime_t timestamp
Timestamp to manage entry lifetime.
Definition: ipv6.h:403
systime_t osGetSystemTime(void)
Retrieve system time.