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-2024 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.4.0
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, length - 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  {
334  remainingLifetime = entry->timestamp + entry->validLifetime - time;
335  }
336  else
337  {
338  remainingLifetime = 0;
339  }
340 
341  //The specific action to perform for the valid lifetime of the
342  //address depends on the Valid Lifetime in the received Router
343  //Advertisement and the remaining time
345  validLifetime > remainingLifetime)
346  {
347  //If the received Valid Lifetime is greater than 2 hours or
348  //greater than remaining lifetime, set the valid lifetime of
349  //the corresponding address to the advertised Valid Lifetime
350  entry->validLifetime = validLifetime;
351 
352  //Save current time
353  entry->timestamp = time;
354  //Update the state of the IPv6 address
356  }
357  else if(remainingLifetime <= SLAAC_LIFETIME_2_HOURS)
358  {
359  //If remaining lifetime is less than or equal to 2 hours, ignore
360  //the Prefix Information option with regards to the valid lifetime
361  }
362  else
363  {
364  //Otherwise, reset the valid lifetime of the corresponding
365  //address to 2 hours
367 
368  //Save current time
369  entry->timestamp = time;
370  //Update the state of the IPv6 address
372  }
373  }
374  //Tentative address?
375  else if(entry->state == IPV6_ADDR_STATE_TENTATIVE)
376  {
377  //Do not update the preferred and valid lifetimes of the address
378  //when Duplicate Address Detection is being performed
379  found = TRUE;
380  }
381  }
382  }
383 
384  //IPv6 address not found in the list?
385  if(!found)
386  {
387  //Loop through the list of IPv6 addresses
388  for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
389  {
390  //Point to the current entry
391  entry = &interface->ipv6Context.addrList[i];
392 
393  //Check the state of the IPv6 address
394  if(entry->state == IPV6_ADDR_STATE_INVALID)
395  {
396  //If an address is formed successfully and the address is not yet
397  //in the list, the host adds it to the list of addresses assigned
398  //to the interface, initializing its preferred and valid lifetime
399  //values from the Prefix Information option
400  if(interface->ndpContext.dupAddrDetectTransmits > 0)
401  {
402  //Use the IPv6 address as a tentative address
405  }
406  else
407  {
408  //The use of the IPv6 address is now unrestricted
411  }
412 
413  //A new IPv6 address has just been assigned to the interface
414  context->configUpdated = TRUE;
415  //We are done
416  break;
417  }
418  }
419  }
420 }
421 
422 
423 /**
424  * @brief Generate a link-local address
425  * @param[in] context Pointer to the SLAAC context
426  * @return Error code
427  **/
428 
430 {
431  error_t error;
432  NetInterface *interface;
433  NetInterface *logicalInterface;
434  Ipv6Addr addr;
435 
436  //Point to the underlying network interface
437  interface = context->settings.interface;
438 
439  //Point to the logical interface
440  logicalInterface = nicGetLogicalInterface(interface);
441 
442  //Check whether a link-local address has been manually assigned
443  if(interface->ipv6Context.addrList[0].state != IPV6_ADDR_STATE_INVALID &&
444  interface->ipv6Context.addrList[0].permanent)
445  {
446  //Keep using the current link-local address
447  error = NO_ERROR;
448  }
449  else
450  {
451  //A link-local address is formed by combining the well-known link-local
452  //prefix fe80::/10 with the interface identifier
453  ipv6GenerateLinkLocalAddr(&logicalInterface->eui64, &addr);
454 
455  //Check whether Duplicate Address Detection should be performed
456  if(interface->ndpContext.dupAddrDetectTransmits > 0)
457  {
458  //Use the link-local address as a tentative address
459  error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_TENTATIVE,
461  }
462  else
463  {
464  //The use of the link-local address is now unrestricted
465  error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_PREFERRED,
467  }
468  }
469 
470  //Return status code
471  return error;
472 }
473 
474 
475 /**
476  * @brief Dump IPv6 configuration for debugging purpose
477  * @param[in] context Pointer to the SLAAC context
478  **/
479 
481 {
482 #if (SLAAC_TRACE_LEVEL >= TRACE_LEVEL_INFO)
483  uint_t i;
484  NetInterface *interface;
485  Ipv6Context *ipv6Context;
486 
487  //Point to the underlying network interface
488  interface = context->settings.interface;
489  //Point to the IPv6 context
490  ipv6Context = &interface->ipv6Context;
491 
492  //Debug message
493  TRACE_INFO("\r\n");
494  TRACE_INFO("SLAAC configuration:\r\n");
495 
496  //Link-local address
497  TRACE_INFO(" Link-local Address = %s\r\n",
498  ipv6AddrToString(&ipv6Context->addrList[0].addr, NULL));
499 
500  //Global addresses
501  for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
502  {
503  TRACE_INFO(" Global Address %u = %s\r\n", i,
504  ipv6AddrToString(&ipv6Context->addrList[i].addr, NULL));
505  }
506 
507  //IPv6 prefixes
508  for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++)
509  {
510  TRACE_INFO(" Prefix %u = %s/%" PRIu8 "\r\n", i + 1,
511  ipv6AddrToString(&ipv6Context->prefixList[i].prefix, NULL),
512  ipv6Context->prefixList[i].prefixLen);
513  }
514 
515  //Default routers
516  for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++)
517  {
518  TRACE_INFO(" Default Router %u = %s\r\n", i + 1,
519  ipv6AddrToString(&ipv6Context->routerList[i].addr, NULL));
520  }
521 
522  //DNS servers
523  for(i = 0; i < IPV6_DNS_SERVER_LIST_SIZE; i++)
524  {
525  TRACE_INFO(" DNS Server %u = %s\r\n", i + 1,
526  ipv6AddrToString(&ipv6Context->dnsServerList[i], NULL));
527  }
528 
529  //Maximum transmit unit
530  TRACE_INFO(" MTU = %" PRIuSIZE "\r\n", ipv6Context->linkMtu);
531  TRACE_INFO("\r\n");
532 #endif
533 }
534 
535 #endif
uint8_t message[]
Definition: chap.h:154
unsigned int uint_t
Definition: compiler_port.h:50
#define PRIuSIZE
int bool_t
Definition: compiler_port.h:53
#define ntohl(value)
Definition: cpu_endian.h:422
Debugging facilities.
#define TRACE_INFO(...)
Definition: debug.h:95
uint32_t preferredLifetime
uint32_t validLifetime
uint8_t n
uint32_t time
error_t
Error codes.
Definition: error.h:43
@ NO_ERROR
Success.
Definition: error.h:44
Ethernet.
char_t * ipv6AddrToString(const Ipv6Addr *ipAddr, char_t *str)
Convert a binary IPv6 address to a string representation.
Definition: ipv6.c:2376
const Ipv6Addr IPV6_LINK_LOCAL_ADDR_PREFIX
Definition: ipv6.c:81
Ipv6AddrState ipv6GetLinkLocalAddrState(NetInterface *interface)
Get the state of the link-local address.
Definition: ipv6.c:326
IPv6 (Internet Protocol Version 6)
Ipv6Addr
Definition: ipv6.h:251
#define IPV6_DNS_SERVER_LIST_SIZE
Definition: ipv6.h:93
#define IPV6_PREFIX_LIST_SIZE
Definition: ipv6.h:79
#define IPV6_ADDR_LIST_SIZE
Definition: ipv6.h:65
@ IPV6_ADDR_STATE_DEPRECATED
An address assigned to an interface whose use is discouraged.
Definition: ipv6.h:169
@ IPV6_ADDR_STATE_PREFERRED
An address assigned to an interface whose use is unrestricted.
Definition: ipv6.h:168
@ IPV6_ADDR_STATE_INVALID
An address that is not assigned to any interface.
Definition: ipv6.h:166
@ IPV6_ADDR_STATE_TENTATIVE
An address whose uniqueness on a link is being verified.
Definition: ipv6.h:167
#define IPV6_ROUTER_LIST_SIZE
Definition: ipv6.h:86
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
void ipv6FlushDnsServerList(NetInterface *interface)
Flush the list of DNS servers.
Definition: ipv6_misc.c:751
void ipv6GenerateLinkLocalAddr(const Eui64 *interfaceId, Ipv6Addr *ipAddr)
Generate a IPv6 link-local address from an interface identifier.
Definition: ipv6_misc.c:1468
bool_t ipv6CompPrefix(const Ipv6Addr *ipAddr1, const Ipv6Addr *ipAddr2, size_t length)
Compare IPv6 address prefixes.
Definition: ipv6_misc.c:1221
Helper functions for IPv6.
Ipv4Addr addr
Definition: nbns_common.h:123
NDP (Neighbor Discovery Protocol)
NdpRouterAdvMessage
Definition: ndp.h:310
#define NDP_INFINITE_LIFETIME
Definition: ndp.h:202
NdpPrefixInfoOption
Definition: ndp.h:418
NdpRdnssOption
Definition: ndp.h:482
@ NDP_OPT_RECURSIVE_DNS_SERVER
Definition: ndp.h:222
@ NDP_OPT_PREFIX_INFORMATION
Definition: ndp.h:218
void * ndpGetOption(uint8_t *options, size_t length, uint8_t type)
Search a NDP message for a given option.
Definition: ndp_misc.c:641
Helper functions for NDP (Neighbor Discovery Protocol)
TCP/IP stack core.
#define NetInterface
Definition: net.h:36
#define netMutex
Definition: net_legacy.h:195
NetInterface * nicGetLogicalInterface(NetInterface *interface)
Retrieve logical interface.
Definition: nic.c:52
#define MAX_DELAY
Definition: os_port.h:77
#define timeCompare(t1, t2)
Definition: os_port.h:40
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
#define INFINITE_DELAY
Definition: os_port.h:75
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
systime_t osGetSystemTime(void)
Retrieve system time.
uint32_t systime_t
System time.
IPv6 Stateless Address Autoconfiguration.
#define SlaacContext
Definition: slaac.h:50
#define SLAAC_LIFETIME_2_HOURS
Definition: slaac.h:46
void slaacParseRouterAdv(SlaacContext *context, NdpRouterAdvMessage *message, size_t length)
Parse Router Advertisement message.
Definition: slaac_misc.c:104
error_t slaacGenerateLinkLocalAddr(SlaacContext *context)
Generate a link-local address.
Definition: slaac_misc.c:429
void slaacLinkChangeEvent(SlaacContext *context)
Callback function for link change event.
Definition: slaac_misc.c:54
void slaacDumpConfig(SlaacContext *context)
Dump IPv6 configuration for debugging purpose.
Definition: slaac_misc.c:480
void slaacParsePrefixInfoOption(SlaacContext *context, NdpPrefixInfoOption *option)
Parse Prefix Information Option.
Definition: slaac_misc.c:197
Helper functions for SLAAC.
IPv6 address entry.
Definition: ipv6.h:399
systime_t timestamp
Timestamp to manage entry lifetime.
Definition: ipv6.h:406
Ipv6AddrState state
IPv6 address state.
Definition: ipv6.h:401
systime_t preferredLifetime
Preferred lifetime.
Definition: ipv6.h:404
systime_t validLifetime
Valid lifetime.
Definition: ipv6.h:403
Ipv6Addr addr
IPv6 address.
Definition: ipv6.h:400
IPv6 context.
Definition: ipv6.h:462
Ipv6AddrEntry addrList[IPV6_ADDR_LIST_SIZE]
IPv6 unicast address list.
Definition: ipv6.h:469
Ipv6PrefixEntry prefixList[IPV6_PREFIX_LIST_SIZE]
Prefix list.
Definition: ipv6.h:471
Ipv6Addr dnsServerList[IPV6_DNS_SERVER_LIST_SIZE]
DNS servers.
Definition: ipv6.h:473
Ipv6RouterEntry routerList[IPV6_ROUTER_LIST_SIZE]
Default router list.
Definition: ipv6.h:472
size_t linkMtu
Maximum transmission unit.
Definition: ipv6.h:463
Ipv6Addr prefix
IPv6 prefix information.
Definition: ipv6.h:418
uint8_t prefixLen
IPv6 prefix length.
Definition: ipv6.h:419
Ipv6Addr addr
Router address.
Definition: ipv6.h:435
uint8_t length
Definition: tcp.h:368