diff --git a/sys/include/net/gnrc/ipv6/nib/conf.h b/sys/include/net/gnrc/ipv6/nib/conf.h index fddbc03e8005..4ed7f54f272a 100644 --- a/sys/include/net/gnrc/ipv6/nib/conf.h +++ b/sys/include/net/gnrc/ipv6/nib/conf.h @@ -186,6 +186,13 @@ extern "C" { #define CONFIG_GNRC_IPV6_NIB_SLAAC 1 #endif +/** + * @brief Use stable privacy addresses (rfc7217) + */ +#ifndef CONFIG_GNRC_IPV6_STABLE_PRIVACY +#define CONFIG_GNRC_IPV6_STABLE_PRIVACY 0 +#endif + /** * @brief handle Redirect Messages */ diff --git a/sys/include/net/gnrc/netif/internal.h b/sys/include/net/gnrc/netif/internal.h index 5e0de86c2b60..358cf786655e 100644 --- a/sys/include/net/gnrc/netif/internal.h +++ b/sys/include/net/gnrc/netif/internal.h @@ -130,6 +130,28 @@ void gnrc_netif_ipv6_addr_remove_internal(gnrc_netif_t *netif, int gnrc_netif_ipv6_addr_idx(gnrc_netif_t *netif, const ipv6_addr_t *addr); +/** + * @brief Returns the index of the first pfx + * in gnrc_netif_t::ipv6_addrs of @p netif + * where the first @p pfx_len bits match with @p pfx + * + * @pre `(netif != NULL) && (pfx != NULL)` + * + * Can be used to check if an address is assigned to an interface. + * + * @param[in] netif the network interface + * @param[in] pfx the address to check + * @param[in] pfx_len the amount of bits to compare + * + * @note Only available with @ref net_gnrc_ipv6 "gnrc_ipv6". + * + * @return index of the first matching address + * in gnrc_netif_t::ipv6_addrs of @p netif + * @return -1, if no matching address found for @p netif + */ +int gnrc_netif_ipv6_addr_pfx_idx(gnrc_netif_t *netif, + const ipv6_addr_t *pfx, uint8_t pfx_len); + /** * @brief Gets state from address flags * @@ -160,6 +182,25 @@ static inline uint8_t gnrc_netif_ipv6_addr_dad_trans(const gnrc_netif_t *netif, return netif->ipv6.addrs_flags[idx] & GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_TENTATIVE; } +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) || defined(DOXYGEN) +/** + * @brief Gets number of address generation retries already performed for an address + * + * @param[in] netif the network interface + * @param[in] idx index of the address (and its flags) + * + * @return the number of address generation retries already + * performed + */ +static inline uint8_t gnrc_netif_ipv6_addr_gen_retries(const gnrc_netif_t *netif, + int idx) +{ + return (netif->ipv6.addrs_flags[idx] + & GNRC_NETIF_IPV6_ADDRS_FLAGS_IDGEN_RETRIES) + >> GNRC_NETIF_IPV6_ADDRS_FLAGS_IDGEN_RETRIES_POS; +} +#endif + /** * @brief Returns the index of an address in gnrc_netif_t::ipv6_addrs of @p * netif that matches @p addr best diff --git a/sys/include/net/gnrc/netif/ipv6.h b/sys/include/net/gnrc/netif/ipv6.h index d58c708017d8..fd756ac7c3fa 100644 --- a/sys/include/net/gnrc/netif/ipv6.h +++ b/sys/include/net/gnrc/netif/ipv6.h @@ -68,6 +68,19 @@ extern "C" { * @brief Address is an anycast address */ #define GNRC_NETIF_IPV6_ADDRS_FLAGS_ANYCAST (0x20U) + +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) || defined(DOXYGEN) +/** + * @brief Number of address generation retries + * For addresses generated as per RFC7217, this stores the DAD_Counter value + * and the upper limit is defined by @ref STABLE_PRIVACY_IDGEN_RETRIES + */ +#define GNRC_NETIF_IPV6_ADDRS_FLAGS_IDGEN_RETRIES (0xC0U) +/** + * @brief Shift position of address generation retries + */ +#define GNRC_NETIF_IPV6_ADDRS_FLAGS_IDGEN_RETRIES_POS (6) +#endif /** @} */ /** diff --git a/sys/include/net/netopt.h b/sys/include/net/netopt.h index 213418a7c33f..ffe4122be1a1 100644 --- a/sys/include/net/netopt.h +++ b/sys/include/net/netopt.h @@ -25,8 +25,11 @@ #ifndef NET_NETOPT_H #define NET_NETOPT_H +#include #include #include +#include +#include "eui64.h" #ifdef __cplusplus extern "C" { @@ -911,6 +914,38 @@ typedef enum { /* add other states if needed */ } netopt_state_t; +/** + * @brief Option parameter to be used with @ref NETOPT_IPV6_IID + */ +enum { + NETOPT_IPV6_IID_HWADDR = 0, +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) || defined(DOXYGEN) + NETOPT_IPV6_IID_RFC7217, +#endif +}; + +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) || defined(DOXYGEN) +/** + * @brief Data for @ref NETOPT_IPV6_IID when using RFC7217, + * passed on to ipv6_get_rfc7217_iid, + * from which the descriptions are also copied + */ +typedef struct { + /** + * @param[out] where to store the generated interface identifier + */ + eui64_t *iid; + /** + * @param[in] pfx The prefix for which the IID is to be generated. + */ + const ipv6_addr_t *pfx; + /** + * @param[in,out] dad_ctr ("DAD_Counter" in rfc7217) + */ + uint8_t *dad_ctr; +} netopt_ipv6_rfc7217_iid_data; +#endif + /** * @brief Option parameter to be used with @ref NETOPT_RF_TESTMODE */ diff --git a/sys/net/gnrc/Makefile.dep b/sys/net/gnrc/Makefile.dep index 3dab93eade61..81f96b38c2d6 100644 --- a/sys/net/gnrc/Makefile.dep +++ b/sys/net/gnrc/Makefile.dep @@ -431,6 +431,21 @@ ifneq (,$(filter gnrc_ipv6_nib,$(USEMODULE))) USEMODULE += random endif +#This checks if the option is being set via Kconfig or CFLAGS +ifneq (,$(or $(CONFIG_GNRC_IPV6_STABLE_PRIVACY),$(filter -DCONFIG_GNRC_IPV6_STABLE_PRIVACY=1,$(CFLAGS)))) + # Set another macro that is needed for this option. + + ##prepare value + stable_privacy_secret_key != python3 -c "import secrets; print(','.join([f'0x{byte:02x}' for byte in secrets.token_bytes(16)]))" + + ##set macro + CFLAGS += -DSTABLE_PRIVACY_SECRET_KEY=$(stable_privacy_secret_key) + + # dependencies + USEMODULE += hashes ##include "hashes/sha256.h" + USEMODULE += ztimer_msec +endif + ifneq (,$(filter gnrc_udp,$(USEMODULE))) DEFAULT_MODULE += auto_init_gnrc_udp USEMODULE += gnrc_nettype_udp diff --git a/sys/net/gnrc/netif/gnrc_netif.c b/sys/net/gnrc/netif/gnrc_netif.c index 8ececfad0356..60dd8a963405 100644 --- a/sys/net/gnrc/netif/gnrc_netif.c +++ b/sys/net/gnrc/netif/gnrc_netif.c @@ -49,6 +49,7 @@ #define ENABLE_DEBUG 0 #include "debug.h" +#include "../network_layer/ipv6/nib/_nib-slaac.h" static void _update_l2addr_from_dev(gnrc_netif_t *netif); static void _check_netdev_capabilities(netdev_t *dev); @@ -269,8 +270,23 @@ int gnrc_netif_get_from_netdev(gnrc_netif_t *netif, gnrc_netapi_opt_t *opt) } break; case NETOPT_IPV6_IID: - assert(opt->data_len >= sizeof(eui64_t)); - res = gnrc_netif_ipv6_get_iid(netif, opt->data); + switch ((int16_t)opt->context) { + case NETOPT_IPV6_IID_HWADDR: + assert(opt->data_len >= sizeof(eui64_t)); + res = gnrc_netif_ipv6_get_iid(netif, opt->data); + break; +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) + case NETOPT_IPV6_IID_RFC7217: + assert(opt->data_len == sizeof(netopt_ipv6_rfc7217_iid_data)); + netopt_ipv6_rfc7217_iid_data *data = + (netopt_ipv6_rfc7217_iid_data *) opt->data; + res = ipv6_get_rfc7217_iid_idempotent( + data->iid, netif, data->pfx, data->dad_ctr); + break; +#endif + default: + break; + } break; case NETOPT_MAX_PDU_SIZE: if (opt->context == GNRC_NETTYPE_IPV6) { @@ -502,6 +518,9 @@ void gnrc_netif_release(gnrc_netif_t *netif) #if IS_USED(MODULE_GNRC_NETIF_IPV6) static int _addr_idx(const gnrc_netif_t *netif, const ipv6_addr_t *addr); +static int _addr_pfx_idx(const gnrc_netif_t *netif, + const ipv6_addr_t *pfx, + uint8_t pfx_len); static int _group_idx(const gnrc_netif_t *netif, const ipv6_addr_t *addr); static char addr_str[IPV6_ADDR_MAX_STR_LEN]; @@ -730,6 +749,23 @@ int gnrc_netif_ipv6_addr_idx(gnrc_netif_t *netif, return idx; } +int gnrc_netif_ipv6_addr_pfx_idx(gnrc_netif_t *netif, + const ipv6_addr_t *pfx, uint8_t pfx_len) +{ + int idx; + + assert((netif != NULL) && (pfx != NULL)); + DEBUG("gnrc_netif: get index of first address matching %s/%u" + " from interface %" PRIkernel_pid "\n", + ipv6_addr_to_str(addr_str, pfx, sizeof(addr_str)), + pfx_len, + netif->pid); + gnrc_netif_acquire(netif); + idx = _addr_pfx_idx(netif, pfx, pfx_len); + gnrc_netif_release(netif); + return idx; +} + int gnrc_netif_ipv6_addr_match(gnrc_netif_t *netif, const ipv6_addr_t *addr) { @@ -947,11 +983,35 @@ static int _idx(const gnrc_netif_t *netif, const ipv6_addr_t *addr, bool mcast) return -1; } +static int _pfx_idx(const gnrc_netif_t *netif, const ipv6_addr_t *pfx, uint8_t pfx_len, bool mcast) +{ + /*same as function @ref _idx above, but with generalized condition*/ + if (!ipv6_addr_is_unspecified(pfx)) { + const ipv6_addr_t *iplist = (mcast) ? netif->ipv6.groups : + netif->ipv6.addrs; + unsigned ipmax = (mcast) ? GNRC_NETIF_IPV6_GROUPS_NUMOF : + CONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF; + for (unsigned i = 0; i < ipmax; i++) { + if (ipv6_addr_match_prefix(&iplist[i], pfx) >= pfx_len) { + return i; + } + } + } + return -1; +} + static inline int _addr_idx(const gnrc_netif_t *netif, const ipv6_addr_t *addr) { return _idx(netif, addr, false); } +static inline int _addr_pfx_idx(const gnrc_netif_t *netif, + const ipv6_addr_t *pfx, + uint8_t pfx_len) +{ + return _pfx_idx(netif, pfx, pfx_len, false); +} + static inline int _group_idx(const gnrc_netif_t *netif, const ipv6_addr_t *addr) { return _idx(netif, addr, true); @@ -1284,8 +1344,22 @@ int gnrc_netif_ipv6_add_prefix(gnrc_netif_t *netif, assert(netif != NULL); DEBUG("gnrc_netif: (re-)configure prefix %s/%d\n", ipv6_addr_to_str(addr_str, pfx, sizeof(addr_str)), pfx_len); - if (gnrc_netapi_get(netif->pid, NETOPT_IPV6_IID, 0, &iid, - sizeof(eui64_t)) >= 0) { +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) + uint8_t dad_ctr = 0; + netopt_ipv6_rfc7217_iid_data data; + data.iid = &iid; + data.pfx = pfx; + data.dad_ctr = &dad_ctr; + /* no need to store dad_ctr with address (via flags) + * as it can't fail DAD, since already valid */ +#endif + if (gnrc_netapi_get(netif->pid, NETOPT_IPV6_IID, +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) + NETOPT_IPV6_IID_RFC7217, &data, sizeof(data) +#else + NETOPT_IPV6_IID_HWADDR, &iid, sizeof(eui64_t) +#endif + ) >= 0) { ipv6_addr_set_aiid(&addr, iid.uint8); } else { diff --git a/sys/net/gnrc/network_layer/ipv6/nib/Kconfig b/sys/net/gnrc/network_layer/ipv6/nib/Kconfig index bf87ffdcd224..449765f6c7db 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/Kconfig +++ b/sys/net/gnrc/network_layer/ipv6/nib/Kconfig @@ -38,6 +38,14 @@ config GNRC_IPV6_NIB_SLAAC default n if USEMODULE_GNRC_IPV6_NIB_6LR || USEMODULE_GNRC_IPV6_NIB_6LN default y +config GNRC_IPV6_STABLE_PRIVACY + bool "Use stable privacy addresses (rfc7217)" + depends on GNRC_IPV6_NIB_SLAAC + help + Generate semantically opaque interface identifiers + (i.e. L2ADDR is not embedded) + for SLAAC IPv6 addresses + config GNRC_IPV6_NIB_QUEUE_PKT bool "Use packet queue with address resolution" default n if USEMODULE_GNRC_IPV6_NIB_6LN || GNRC_IPV6_NIB_6LN diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.c index a76cb03778ec..61777bc142e7 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.c @@ -15,6 +15,12 @@ #include #include +#include "_nib-slaac.h" +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) +#include +#include "ztimer.h" +#include "random.h" +#endif #include "log.h" #include "luid.h" @@ -30,8 +36,24 @@ #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_6LN) || IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_SLAAC) static char addr_str[IPV6_ADDR_MAX_STR_LEN]; -void _auto_configure_addr(gnrc_netif_t *netif, const ipv6_addr_t *pfx, - uint8_t pfx_len) +inline void auto_configure_addr(gnrc_netif_t *netif, const ipv6_addr_t *pfx, + uint8_t pfx_len) +{ +#if !IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) + _auto_configure_addr(netif, pfx, pfx_len); +#else + _auto_configure_addr_with_dad_ctr(netif, pfx, pfx_len, 0); +#endif +} + +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) +void _auto_configure_addr_with_dad_ctr(gnrc_netif_t *netif, + const ipv6_addr_t *pfx, uint8_t pfx_len, + uint8_t dad_ctr) +#else +void _auto_configure_addr(gnrc_netif_t *netif, + const ipv6_addr_t *pfx, uint8_t pfx_len) +#endif { ipv6_addr_t addr = IPV6_ADDR_UNSPECIFIED; int idx; @@ -46,20 +68,32 @@ void _auto_configure_addr(gnrc_netif_t *netif, const ipv6_addr_t *pfx, return; } #endif - if (!(netif->flags & GNRC_NETIF_FLAGS_HAS_L2ADDR)) { - DEBUG("nib: interface %i has no link-layer addresses\n", netif->pid); - return; - } DEBUG("nib: add address based on %s/%u automatically to interface %u\n", ipv6_addr_to_str(addr_str, pfx, sizeof(addr_str)), pfx_len, netif->pid); #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_6LN) bool new_address = false; #endif /* CONFIG_GNRC_IPV6_NIB_6LN */ - if (gnrc_netif_ipv6_get_iid(netif, (eui64_t *)&addr.u64[1]) < 0) { - DEBUG("nib: Can't get IID on interface %u\n", netif->pid); - return; +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) + bool is_rfc7217 = !(gnrc_netif_is_6ln(netif) && ipv6_addr_is_link_local(pfx)); + if (is_rfc7217) { + if (ipv6_get_rfc7217_iid((eui64_t *) &addr.u64[1], netif, pfx, &dad_ctr) < 0) { + return; + } + flags |= (dad_ctr << GNRC_NETIF_IPV6_ADDRS_FLAGS_IDGEN_RETRIES_POS); + } else +#endif + { + if (!(netif->flags & GNRC_NETIF_FLAGS_HAS_L2ADDR)) { + DEBUG("nib: interface %i has no link-layer addresses\n", netif->pid); + return; + } + if (gnrc_netif_ipv6_get_iid(netif, (eui64_t *)&addr.u64[1]) < 0) { + DEBUG("nib: Can't get IID on interface %u\n", netif->pid); + return; + } } + ipv6_addr_init_prefix(&addr, pfx, pfx_len); if ((idx = gnrc_netif_ipv6_addr_idx(netif, &addr)) < 0) { if ((idx = gnrc_netif_ipv6_addr_add_internal(netif, &addr, pfx_len, @@ -96,7 +130,120 @@ void _auto_configure_addr(gnrc_netif_t *netif, const ipv6_addr_t *pfx, } #endif /* CONFIG_GNRC_IPV6_NIB_6LN || CONFIG_GNRC_IPV6_NIB_SLAAC */ +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) +bool _iid_is_iana_reserved(const eui64_t *iid) +{ + return (iid->uint64.u64 == htonll(0)) + || (iid->uint32[0].u32 == htonl(0x02005eff) && iid->uint8[4] == 0xfe) + || (iid->uint32[0].u32 == htonl(0xfdffffff) && iid->uint16[2].u16 == htons(0xffff) && iid->uint8[6] == 0xff && (iid->uint8[7] & 0x80)); +} + +inline bool _stable_privacy_should_retry_idgen(uint8_t *dad_ctr, const char *reason) { + if (*dad_ctr < STABLE_PRIVACY_IDGEN_RETRIES) { /*within retry limit*/ + LOG_DEBUG("nib: %s", reason); + *dad_ctr += 1; + LOG_DEBUG(", retrying IDGEN. (%u/%u)\n", *dad_ctr, STABLE_PRIVACY_IDGEN_RETRIES); + return true; + } + /*retried often enough*/ + LOG_WARNING("nib: %s", reason); + LOG_WARNING(", not retrying: IDGEN_RETRIES limit reached\n"); + return false; +} + +int ipv6_get_rfc7217_iid(eui64_t *iid, gnrc_netif_t *netif, const ipv6_addr_t *pfx, + uint8_t *dad_ctr) +{ +#if GNRC_NETIF_L2ADDR_MAXLEN > 0 + if (!(netif->flags & GNRC_NETIF_FLAGS_HAS_L2ADDR)) +#endif /* GNRC_NETIF_L2ADDR_MAXLEN > 0 */ + { + LOG_ERROR("nib: interface %i has no link-layer addresses\n", netif->pid); + return -ENOTSUP; + } + +#ifndef STABLE_PRIVACY_SECRET_KEY +#error "Stable privacy requires a secret_key, this should have been configured by sys/net/gnrc/Makefile.dep" +#endif + const uint8_t secret_key[16] = { STABLE_PRIVACY_SECRET_KEY }; + /*SHOULD be of at least 128 bits - https://datatracker.ietf.org/doc/html/rfc7217*/ + + uint8_t digest[SHA256_DIGEST_LENGTH]; + + { + sha256_context_t c; + sha256_init(&c); + sha256_update(&c, pfx, sizeof(*pfx)); + sha256_update(&c, &netif->l2addr, netif->l2addr_len); + sha256_update(&c, dad_ctr, sizeof(*dad_ctr)); + sha256_update(&c, secret_key, sizeof(secret_key)); + sha256_final(&c, digest); + } + + iid->uint64.u64 = 0; + /* uninitialized if called via gnrc_netapi_get (NETOPT_IPV6_IID_RFC7217) + * needs to be all zeros as precondition for the following copy operation*/ + + assert(sizeof(digest) >= sizeof(*iid)); /*as bits: 256 >= 64, "digest is large enough"*/ + + /* copy digest into IID + * RFC 7217 says "starting from the least significant bit", + * i.e. in reverse order */ + for (uint8_t i = 0; i < sizeof(*iid); i++) { /*for each of the 8 bytes*/ + for (int j = 0; j < 8; j++) { /*for each of the 8 bits _within byte_*/ + if ((digest[(sizeof(digest)-1)-i])&(1<uint8[i] |= 1 << ((8-1)-j); /*set 1, precondition: iid->uint8[i] = 0*/ + } + } + } + + /* "The resulting Interface Identifier SHOULD be compared against + * the reserved IPv6 Interface Identifiers" + * "and against those Interface Identifiers already employed [...]" + * - https://datatracker.ietf.org/doc/html/rfc7217#section-5 */ + ipv6_addr_t addr = IPV6_ADDR_UNSPECIFIED; + ipv6_addr_init_prefix(&addr, pfx, SLAAC_PREFIX_LENGTH); + ipv6_addr_set_aiid(&addr, iid->uint8); + if (_iid_is_iana_reserved(iid) || ((gnrc_netif_ipv6_addr_idx(netif, &addr)) >= 0)) { + if (!_stable_privacy_should_retry_idgen(dad_ctr, + "IANA reserved IID generated" + " or generated address already exists")) { + return -1; + } + iid->uint64.u64 = 0; /* @pre for method call */ + return ipv6_get_rfc7217_iid(iid, netif, pfx, dad_ctr); + } + + return 0; +} + +inline int ipv6_get_rfc7217_iid_idempotent(eui64_t *iid, gnrc_netif_t *netif, const ipv6_addr_t *pfx, + uint8_t *dad_ctr) { + int idx; + if ((idx = gnrc_netif_ipv6_addr_pfx_idx(netif, pfx, SLAAC_PREFIX_LENGTH)) >= 0) { + /* if the prefix is already known, + * do not cause generation of a potentially different + * stable privacy address (keyword DAD_Counter). + * refer to https://datatracker.ietf.org/doc/html/rfc4862#section-5.5.3 d) + * */ + DEBUG("nib: Not calling IDGEN, prefix already known.\n"); + + //write out params + //- dad_ctr + *dad_ctr = gnrc_netif_ipv6_addr_gen_retries(netif, idx); + //- iid + ipv6_addr_t *addr = &netif->ipv6.addrs[idx]; + memcpy(iid, &addr->u64[1], sizeof(*iid)); + + return 1; + } + + return ipv6_get_rfc7217_iid(iid, netif, pfx,dad_ctr); +} +#endif + #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_SLAAC) +#if !IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) static bool _try_l2addr_reconfiguration(gnrc_netif_t *netif) { uint8_t hwaddr[GNRC_NETIF_L2ADDR_MAXLEN]; @@ -152,14 +299,41 @@ static bool _try_addr_reconfiguration(gnrc_netif_t *netif) } return hwaddr_reconf; } +#endif void _remove_tentative_addr(gnrc_netif_t *netif, const ipv6_addr_t *addr) { DEBUG("nib: other node has TENTATIVE address %s assigned " "=> removing that address\n", ipv6_addr_to_str(addr_str, addr, sizeof(addr_str))); +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) + int idx = gnrc_netif_ipv6_addr_idx(netif, addr); + assert(idx >= 0); + uint8_t dad_counter = gnrc_netif_ipv6_addr_gen_retries(netif, idx); +#endif gnrc_netif_ipv6_addr_remove_internal(netif, addr); +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) + if (_stable_privacy_should_retry_idgen(&dad_counter, "Duplicate address detected")) { + + /* > Hosts SHOULD introduce a random delay between 0 and IDGEN_DELAY seconds + * - https://datatracker.ietf.org/doc/html/rfc7217#section-6 */ + uint32_t random_delay_ms = random_uint32_range(0, STABLE_PRIVACY_IDGEN_DELAY_MS); + ztimer_sleep(ZTIMER_MSEC, random_delay_ms); + + _auto_configure_addr_with_dad_ctr(netif, addr, SLAAC_PREFIX_LENGTH, dad_counter); + } else { + /*"hosts MUST NOT automatically fall back to employing other algorithms + for generating Interface Identifiers" + - https://datatracker.ietf.org/doc/html/rfc7217#section-6*/ + + /*> If the address is a link-local address + > [...] + > not formed from an interface identifier based on the hardware address + > IP operation on the interface MAY be continued + - https://datatracker.ietf.org/doc/html/rfc4862#section-5.4.5*/ + } +#else if (!ipv6_addr_is_link_local(addr) || !_try_addr_reconfiguration(netif)) { /* Cannot use target address as personal address and can @@ -175,6 +349,7 @@ void _remove_tentative_addr(gnrc_netif_t *netif, const ipv6_addr_t *addr) "but DHCPv6 is not provided", netif->pid); } } +#endif } static int _get_netif_state(gnrc_netif_t **netif, const ipv6_addr_t *addr) diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.h index 1736f69cc7d2..c2c392607420 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.h @@ -32,6 +32,55 @@ extern "C" { #endif +/** + * > An IPv6 address prefix used for stateless autoconfiguration [ACONF] + * of an Ethernet interface must have a length of 64 bits. + * - https://datatracker.ietf.org/doc/html/rfc2464 + * + * > An IPv6 address prefix used for stateless autoconfiguration [RFC4862] + * of an IEEE 802.15.4 interface MUST have a length of 64 bits. + * - https://datatracker.ietf.org/doc/html/rfc4944 + * + * Also see + * @ref INTERFACE_IDENTIFIER_LENGTH + * in combination with "sum" "does not equal 128 bits" + * from https://datatracker.ietf.org/doc/html/rfc4862#section-5.5.3 d) + */ +#define SLAAC_PREFIX_LENGTH (64U) + +/** + * "the address architecture [RFC4291] also defines the length of the interface identifiers" + * - https://datatracker.ietf.org/doc/html/rfc4862#section-2 + * + * "Interface IDs are required to be 64 bits long" + * - https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.1 + */ +#define INTERFACE_IDENTIFIER_LENGTH (64U) + +/** + * @name Stable privacy host constants + * @see [RFC 7217, section 7](https://tools.ietf.org/html/rfc7217#section-7) + * @{ + */ +/** + * @brief Limit for stable privacy (IDGEN) addr. generation retries for subsequent DAD failures + * + * @note Must not be greater than 3 for @ref net_gnrc since + * @ref GNRC_NETIF_IPV6_ADDRS_FLAGS_IDGEN_RETRIES restricts it to + * that number. + */ +#ifndef STABLE_PRIVACY_IDGEN_RETRIES +#define STABLE_PRIVACY_IDGEN_RETRIES (3U) /*default value*/ +#endif + +/** + * @brief Upper limit for random time to wait between IDGEN retries + */ +#ifndef STABLE_PRIVACY_IDGEN_DELAY_MS +#define STABLE_PRIVACY_IDGEN_DELAY_MS 1000 /*default value*/ +#endif +/** @} */ + #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_6LN) || IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_SLAAC) || defined(DOXYGEN) /** * @brief Auto-configures an address from a given prefix @@ -40,12 +89,75 @@ extern "C" { * @param[in] pfx The prefix for the address. * @param[in] pfx_len Length of @p pfx in bits. */ +void auto_configure_addr(gnrc_netif_t *netif, const ipv6_addr_t *pfx, + uint8_t pfx_len); + +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) +/** + * @brief Overload of @ref _auto_configure_addr + * @param dad_ctr rfc7217 DAD_Counter + */ +void _auto_configure_addr_with_dad_ctr(gnrc_netif_t *netif, const ipv6_addr_t *pfx, + uint8_t pfx_len, uint8_t dad_ctr); +#else +/** + * @brief Auto-configures an address from a given prefix. + * Internal method without a DAD_Counter. + * Use auto_configure_addr() instead + * to automatically use the appropriate function. + */ void _auto_configure_addr(gnrc_netif_t *netif, const ipv6_addr_t *pfx, uint8_t pfx_len); +#endif + #else /* CONFIG_GNRC_IPV6_NIB_6LN || CONFIG_GNRC_IPV6_NIB_SLAAC */ -#define _auto_configure_addr(netif, pfx, pfx_len) \ +#define auto_configure_addr(netif, pfx, pfx_len) \ (void)netif; (void)pfx; (void)pfx_len; #endif /* CONFIG_GNRC_IPV6_NIB_6LN || CONFIG_GNRC_IPV6_NIB_SLAAC */ + +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) || defined(DOXYGEN) +/** + * @brief Check if an interface identifier of an IPv6 address + * is reserved by IANA, i.e. it shouldn't be used + * This is should be checked when the interface identifier is randomly generated. + * @see [RFC5453], https://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xhtml + * @param[in] iid The interface identifier to check for + * @return whether the IID is IANA reserved + */ +bool _iid_is_iana_reserved(const eui64_t *iid); + +/** + * @param[in,out] dad_ctr + * @param[in] reason + */ +bool _stable_privacy_should_retry_idgen(uint8_t *dad_ctr, const char *reason); + +/** + * @brief + * @pre @p iid is all 0 bits (iid->uint64.u64 == 0) + * @param[out] iid where to store the generated interface identifier + * @param[in] netif The network interface to use as source for IID generation. + * ("Net_iface" in rfc7217) + * @param[in] pfx The prefix for which the IID is to be generated. + * ("Prefix" in rfc7217) + * It is assumed to be of length @ref SLAAC_PREFIX_LENGTH + * @param[in,out] dad_ctr ("DAD_Counter" in rfc7217) + * Value may increase and is to be stored associated with the address by caller + * @return 0 on success + * @return -1 if failed, because retry limit reached + * @return `-ENOTSUP`, if interface has no link-layer address. + */ +int ipv6_get_rfc7217_iid(eui64_t *iid, gnrc_netif_t *netif, const ipv6_addr_t *pfx, + uint8_t *dad_ctr); + +/** + * @brief @ref ipv6_get_rfc7217_iid for those callers which assume idempotency. + * @return 1 if ignored for idempotency, else @ref ipv6_get_rfc7217_iid + */ +int ipv6_get_rfc7217_iid_idempotent(eui64_t *iid, gnrc_netif_t *netif, const ipv6_addr_t *pfx, + uint8_t *dad_ctr); +#endif + #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_SLAAC) || defined(DOXYGEN) /** * @brief Removes a tentative address from the interface and tries to diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c index ac7e3fcfa2e6..45d7c900b9ee 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c @@ -171,7 +171,7 @@ void gnrc_ipv6_nib_iface_up(gnrc_netif_t *netif) DEBUG("nib: Can't join link-local all-nodes on interface %u\n", netif->pid); } _add_static_lladdr(netif); - _auto_configure_addr(netif, &ipv6_addr_link_local_prefix, 64U); + auto_configure_addr(netif, &ipv6_addr_link_local_prefix, 64U); if (_should_search_rtr(netif)) { uint32_t next_rs_time = random_uint32_range(0, NDP_MAX_RS_MS_DELAY); @@ -1657,8 +1657,18 @@ static uint32_t _handle_pio(gnrc_netif_t *netif, const icmpv6_hdr_t *icmpv6, DEBUG(" - Preferred lifetime: %" PRIu32 "\n", byteorder_ntohl(pio->pref_ltime)); - if (pio->flags & NDP_OPT_PI_FLAGS_A) { - _auto_configure_addr(netif, &pio->prefix, pio->prefix_len); + if (pio->flags & NDP_OPT_PI_FLAGS_A + && pio->prefix_len == SLAAC_PREFIX_LENGTH +#if IS_ACTIVE(CONFIG_GNRC_IPV6_STABLE_PRIVACY) + && (gnrc_netif_ipv6_addr_pfx_idx(netif, &pio->prefix, pio->prefix_len) < 0) + /* if the prefix is already known, + * do not cause generation of a potentially different + * stable privacy address (keyword DAD_Counter). + * refer to https://datatracker.ietf.org/doc/html/rfc4862#section-5.5.3 d) + * */ +#endif + ) { + auto_configure_addr(netif, &pio->prefix, pio->prefix_len); } if ((pio->flags & (NDP_OPT_PI_FLAGS_A | NDP_OPT_PI_FLAGS_L)) || _multihop_p6c(netif, abr)) { diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c b/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c index 8dff23925d18..4af3129b5fad 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl_control_messages.c @@ -50,6 +50,7 @@ #define ENABLE_DEBUG 0 #include "debug.h" +#include "../../network_layer/ipv6/nib/_nib-slaac.h" static char addr_str[IPV6_ADDR_MAX_STR_LEN]; @@ -585,10 +586,8 @@ static bool _parse_options(int msg_type, gnrc_rpl_instance_t *inst, gnrc_rpl_opt && !(pi->LAR_flags & GNRC_RPL_PREFIX_AUTO_ADDRESS_BIT)) { break; } - ipv6_addr_set_aiid(&pi->prefix, iid.uint8); - /* TODO: find a way to do this with DAD (i.e. state != VALID) */ - gnrc_netif_ipv6_addr_add_internal(netif, &pi->prefix, pi->prefix_len, - GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_VALID); + auto_configure_addr(netif, &pi->prefix, pi->prefix_len); + /* set lifetimes */ gnrc_ipv6_nib_pl_set(netif->pid, &pi->prefix, pi->prefix_len, _sec_to_ms(byteorder_ntohl(pi->valid_lifetime)), diff --git a/tests/net/gnrc_ipv6_nib/main.c b/tests/net/gnrc_ipv6_nib/main.c index 3233aa96c564..d18097ce7973 100644 --- a/tests/net/gnrc_ipv6_nib/main.c +++ b/tests/net/gnrc_ipv6_nib/main.c @@ -39,7 +39,7 @@ #define _RTR_LTIME (6612U) #define _REACH_TIME (1210388825UL) #define _RETRANS_TIMER (3691140UL) -#define _LOC_GB_PFX_LEN (45U) +#define _LOC_GB_PFX_LEN (64U) #define _REM_GB_PFX_LEN (37U) #define _PIO_PFX_LTIME (0x8476fedf) diff --git a/tests/net/gnrc_ipv6_nib_6ln/main.c b/tests/net/gnrc_ipv6_nib_6ln/main.c index 96aa995e055a..339339ac4d2e 100644 --- a/tests/net/gnrc_ipv6_nib_6ln/main.c +++ b/tests/net/gnrc_ipv6_nib_6ln/main.c @@ -43,7 +43,7 @@ #define _RTR_LTIME (6612U) #define _REACH_TIME (1210388825UL) #define _RETRANS_TIMER (3691140UL) -#define _LOC_GB_PFX_LEN (45U) +#define _LOC_GB_PFX_LEN (64U) #define _REM_GB_PFX_LEN (37U) #define _PIO_PFX_LTIME (0x8476fedf) #define _CTX_LTIME (29169U) diff --git a/tests/net/gnrc_netif/main.c b/tests/net/gnrc_netif/main.c index caf86553e173..76fc290548a8 100644 --- a/tests/net/gnrc_netif/main.c +++ b/tests/net/gnrc_netif/main.c @@ -805,12 +805,12 @@ static void test_netapi_get__IPV6_IID(void) TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), gnrc_netapi_get(ethernet_netif.pid, NETOPT_IPV6_IID, - 0, &value, sizeof(value))); + NETOPT_IPV6_IID_HWADDR, &value, sizeof(value))); TEST_ASSERT_EQUAL_INT(0, memcmp(&value, ðernet_ipv6_ll.u64[1], sizeof(value))); TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), gnrc_netapi_get(ieee802154_netif.pid, NETOPT_IPV6_IID, - 0, &value, sizeof(value))); + NETOPT_IPV6_IID_HWADDR, &value, sizeof(value))); TEST_ASSERT_EQUAL_INT(0, memcmp(&value, &ieee802154_ipv6_ll_long.u64[1], sizeof(value))); TEST_ASSERT_EQUAL_INT(sizeof(ieee802154_l2addr_len), @@ -820,7 +820,7 @@ static void test_netapi_get__IPV6_IID(void) sizeof(ieee802154_l2addr_len))); TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), gnrc_netapi_get(ieee802154_netif.pid, NETOPT_IPV6_IID, - 0, &value, + NETOPT_IPV6_IID_HWADDR, &value, sizeof(value))); TEST_ASSERT_EQUAL_INT(0, memcmp(&value, &ieee802154_eui64_short, sizeof(value))); @@ -833,7 +833,7 @@ static void test_netapi_get__IPV6_IID(void) sizeof(ieee802154_l2addr_len))); TEST_ASSERT_EQUAL_INT(-ENOTSUP, gnrc_netapi_get(netifs[0].pid, NETOPT_IPV6_IID, - 0, &value, sizeof(value))); + NETOPT_IPV6_IID_HWADDR, &value, sizeof(value))); } static void test_netapi_get__MAX_PACKET_SIZE(void)