diff --git a/include/net/net_if.h b/include/net/net_if.h index 6be5241ec1c1c..53e096f8b6ba7 100644 --- a/include/net/net_if.h +++ b/include/net/net_if.h @@ -65,10 +65,29 @@ struct net_if_addr { /** What is the current state of the address */ enum net_addr_state addr_state; -#if defined(CONFIG_NET_IPV6_DAD) && defined(CONFIG_NET_NATIVE_IPV6) +#if defined(CONFIG_NET_NATIVE_IPV6) +#if defined(CONFIG_NET_IPV6_PE_ENABLE) + /** Address creation time. This is used to determine if the maximum + * lifetime for this address is reached or not. The value is in seconds. + */ + u32_t addr_create_time; + + /** Preferred lifetime for the address in seconds. + */ + u32_t addr_preferred_lifetime; + + /** Address timeout value. This is only used if DAD needs to be redone + * for this address because of earlier DAD failure. This value is in + * seconds. + */ + s32_t addr_timeout; +#endif + +#if defined(CONFIG_NET_IPV6_DAD) /** How many times we have done DAD */ u8_t dad_count; #endif +#endif /* CONFIG_NET_NATIVE_IPV6 */ /** Is the IP address valid forever */ u8_t is_infinite : 1; @@ -79,7 +98,10 @@ struct net_if_addr { /** Is this IP address usage limited to the subnet (mesh) or not */ u8_t is_mesh_local : 1; - u8_t _unused : 5; + /** Is this IP address temporary and generated for example by + * IPv6 privacy extension (RFC 4941) + */ + u8_t is_temporary : 1; }; /** @@ -464,6 +486,20 @@ struct net_if { /** Network interface instance configuration */ struct net_if_config config; + + /** Network interface specific flags */ + /** Enable IPv6 privacy extension (RFC 4941), this is enabled + * by default if PE support is enabled in configuration. + */ + u8_t pe_enabled : 1; + + /* If PE is enabled, then this tells whether public addresses + * are preferred over temporary ones for this interface. + */ + u8_t pe_prefer_public : 1; + + u8_t _unused : 6; + } __net_if_align; /** @@ -1078,7 +1114,7 @@ static inline void net_if_ipv6_maddr_leave(struct net_if_mcast_addr *addr) * @return Pointer to prefix, NULL if not found. */ struct net_if_ipv6_prefix *net_if_ipv6_prefix_get(struct net_if *iface, - struct in6_addr *addr); + const struct in6_addr *addr); /** * @brief Check if this IPv6 prefix belongs to this interface diff --git a/include/net/net_ip.h b/include/net/net_ip.h index 8fbeb3b12bb15..78f85f5f485f1 100644 --- a/include/net/net_ip.h +++ b/include/net/net_ip.h @@ -1406,6 +1406,47 @@ static inline u8_t net_priority2vlan(enum net_priority priority) */ const char *net_family2str(sa_family_t family); +#if defined(CONFIG_NET_IPV6_PE_ENABLE) +/** + * @brief Add IPv6 prefix as a privacy extension filter. + * + * @details Note that the filters can be either black listing or white listing. + * + * @param addr IPv6 prefix + * @param is_blacklist Tells if this filter for white listing or black listing. + * + * @return 0 if ok, <0 if error + */ +int net_ipv6_pe_add_filter(struct in6_addr *addr, bool is_blacklist); + +/** + * @brief Delete IPv6 prefix from privacy extension filter list. + * + * @param addr IPv6 prefix + * + * @return 0 if ok, <0 if error + */ +int net_ipv6_pe_del_filter(struct in6_addr *addr); + +#else /* CONFIG_NET_IPV6_PE_ENABLE */ + +static inline int net_ipv6_pe_add_filter(struct in6_addr *addr, + bool is_blacklist) +{ + ARG_UNUSED(addr); + ARG_UNUSED(is_blacklist); + + return -ENOTSUP; +} + +static inline int net_ipv6_pe_del_filter(struct in6_addr *addr) +{ + ARG_UNUSED(addr); + + return -ENOTSUP; +} +#endif /* CONFIG_NET_IPV6_PE_ENABLE */ + #ifdef __cplusplus } #endif diff --git a/subsys/net/ip/CMakeLists.txt b/subsys/net/ip/CMakeLists.txt index 72fb6eae95dbb..1f7a5823de493 100644 --- a/subsys/net/ip/CMakeLists.txt +++ b/subsys/net/ip/CMakeLists.txt @@ -30,6 +30,8 @@ zephyr_library_sources_ifdef(CONFIG_NET_IPV6 icmpv6.c nbr.c ipv6.c ipv6_nbr.c) zephyr_library_sources_ifdef(CONFIG_NET_IPV6_MLD ipv6_mld.c) zephyr_library_sources_ifdef(CONFIG_NET_IPV6_FRAGMENT ipv6_fragment.c) +zephyr_library_sources_ifdef(CONFIG_NET_IPV6_PE_ENABLE ipv6_pe.c) +zephyr_library_sources_ifdef(CONFIG_NET_MGMT_EVENT net_mgmt.c) zephyr_library_sources_ifdef(CONFIG_NET_ROUTE route.c) zephyr_library_sources_ifdef(CONFIG_NET_STATISTICS net_stats.c) zephyr_library_sources_ifdef(CONFIG_NET_TCP connection.c tcp.c) diff --git a/subsys/net/ip/Kconfig.ipv6 b/subsys/net/ip/Kconfig.ipv6 index 7730911484029..ba6224b1c2c69 100644 --- a/subsys/net/ip/Kconfig.ipv6 +++ b/subsys/net/ip/Kconfig.ipv6 @@ -119,6 +119,77 @@ config NET_IPV6_RA_RDNSS Support Router Advertisement Recursive DNS Server option. See RFC 6106 for details. The value depends on your network needs. +config NET_IPV6_PE_ENABLE + bool "Enable privacy extension (RFC 4941) support" + select MBEDTLS + help + This enables IPv6 privacy extension (RFC 4941) support. + The interface identifier is randomized and SLAAC addresses + generated from it will expire. This requires that applications are + prepared to use new IPv6 addresses when old ones will expire. + Note that you should make sure that the value of config option + CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT should be large enough so that + two PE generated IPv6 addresses can be added to the network interface + at the same time. + +if NET_IPV6_PE_ENABLE + +config NET_IPV6_PE_FILTER_PREFIX_COUNT + int "Size of the IPv6 prefix filter list" + default 0 + help + Size of the white/black filter list of IPv6 prefixes. User can + set filters at runtime and it is possible to enable or disable + privacy extension support according to this filter list. + By default no filters are enabled. + +config NET_IPV6_PE_PREFER_PUBLIC_ADDRESSES + bool "Prefer public preferred address over temporary one" + help + Prefer public addresses over temporary addresses. + +config NET_IPV6_PE_TEMP_VALID_LIFETIME + int "Max lifetime for temporary address (in minutes)" + default 10080 + help + No temporary address should ever remain valid for longer than this + value. The value is in minutes. Default value is 1 week (7*24*60). + +config NET_IPV6_PE_TEMP_PREFERRED_LIFETIME + int "Max preferred lifetime for temporary address (in minutes)" + default 1440 + help + No temporary address should ever remain preferred for longer than this + value. The value is in minutes. Default value is 1 day (24*60). + +config NET_IPV6_PE_REGEN_ADVANCE + int "Regenerate advance time units" + default 5 + help + A temporary address is created only if the calculated Preferred + Lifetime is greater than REGEN_ADVANCE time units. + The value is in seconds. + +config NET_IPV6_PE_MAX_DESYNC_FACTOR + int "Max desync factor (in minutes)" + default 10 + help + This is the upper bound on DESYNC_FACTOR. The value is in minutes. + The value DESYNC_FACTOR is a random value (different for each client) + that ensures that clients don't synchronize with each other and + generate new addresses at exactly the same time. + +config NET_IPV6_PE_TEMP_IDGEN_RETRIES + int "Max amount of failed DAD attempts" + default 3 + help + The node MUST perform duplicate address detection (DAD) on the + generated temporary address. If after TEMP_IDGEN_RETRIES consecutive + attempts no non-unique address was generated then there will be no + attempt to generate temporary addresses for that interface. + +endif + config NET_6LO bool "Enable 6lowpan IPv6 Compression library" default y if NET_L2_IEEE802154 diff --git a/subsys/net/ip/ipv6.h b/subsys/net/ip/ipv6.h index 9e79dd5195031..e121078c6d77b 100644 --- a/subsys/net/ip/ipv6.h +++ b/subsys/net/ip/ipv6.h @@ -483,4 +483,91 @@ void net_ipv6_mld_init(void); #define net_ipv6_nbr_init(...) #endif +/** + * @brief Start IPv6 privacy extension procedure. + * + * @param iface Interface to use. + * @param prefix IPv6 prefix to use. + * @param vlifetime Lifetime of this IPv6 prefix (in seconds). + * @param preferred_lifetime Preferred lifetime of this IPv6 prefix (in seconds) + */ +#if defined(CONFIG_NET_IPV6_PE_ENABLE) +void net_ipv6_pe_start(struct net_if *iface, const struct in6_addr *prefix, + u32_t vlifetime, u32_t preferred_lifetime); + +#else +static inline void net_ipv6_pe_start(struct net_if *iface, + const struct in6_addr *prefix, + u32_t vlifetime, + u32_t preferred_lifetime) +{ + ARG_UNUSED(iface); + ARG_UNUSED(prefix); + ARG_UNUSED(vlifetime); + ARG_UNUSED(preferred_lifetime); +} +#endif /* CONFIG_NET_IPV6_PE_ENABLE */ + +/** + * @brief Check if maximum number of Duplicate Address Detection (DAD) requests + * have been done. + * + * @param count Number of DAD requests done. + * + * @return Return True if DAD can continue, False if max amount of DAD + * requests have been done. + */ +#if defined(CONFIG_NET_IPV6_PE_ENABLE) +bool net_ipv6_pe_check_dad(int count); +#else +static inline bool net_ipv6_pe_check_dad(int count) +{ + ARG_UNUSED(count); + + return false; +} +#endif /* CONFIG_NET_IPV6_PE_ENABLE */ + +/** + * @brief Initialize IPv6 privacy extension support for a network interface. + * + * @param iface Network interface + * + * @return Return 0 if ok or <0 if there is an error. + */ +#if defined(CONFIG_NET_IPV6_PE_ENABLE) +int net_ipv6_pe_init(struct net_if *iface); +#else +static inline int net_ipv6_pe_init(struct net_if *iface) +{ + iface->pe_enabled = false; + iface->pe_prefer_public = false; + + return 0; +} +#endif /* CONFIG_NET_IPV6_PE_ENABLE */ + +typedef void (*net_ipv6_pe_filter_cb_t)(struct in6_addr *prefix, + bool is_blacklist, + void *user_data); + +/** + * @brief Go through all the IPv6 privacy extension filters and call callback + * for each IPv6 prefix. + * + * @param cb User supplied callback function to call. + * @param user_data User specified data. + * + * @return Total number of filters found. + */ +#if defined(CONFIG_NET_IPV6_PE_ENABLE) +int net_ipv6_pe_filter_foreach(net_ipv6_pe_filter_cb_t cb, void *user_data); +#else +static inline int net_ipv6_pe_filter_foreach(net_ipv6_pe_filter_cb_t cb, + void *user_data) +{ + return 0; +} +#endif + #endif /* __IPV6_H */ diff --git a/subsys/net/ip/ipv6_nbr.c b/subsys/net/ip/ipv6_nbr.c index d662deb42b4d5..17ec8543acd17 100644 --- a/subsys/net/ip/ipv6_nbr.c +++ b/subsys/net/ip/ipv6_nbr.c @@ -2117,6 +2117,7 @@ static inline u32_t remaining_lifetime(struct net_if_addr *ifaddr) static inline void handle_prefix_autonomous(struct net_pkt *pkt, struct net_icmpv6_nd_opt_prefix_info *prefix_info) { + struct net_if *iface = net_pkt_iface(pkt); struct in6_addr addr = { }; struct net_if_addr *ifaddr; @@ -2124,8 +2125,7 @@ static inline void handle_prefix_autonomous(struct net_pkt *pkt, * setup link local address, and then copy prefix over first 8 * bytes of that address. */ - net_ipv6_addr_create_iid(&addr, - net_if_get_link_addr(net_pkt_iface(pkt))); + net_ipv6_addr_create_iid(&addr, net_if_get_link_addr(iface)); memcpy(&addr, &prefix_info->prefix, sizeof(struct in6_addr) / 2); ifaddr = net_if_ipv6_addr_lookup(&addr, NULL); @@ -2160,14 +2160,22 @@ static inline void handle_prefix_autonomous(struct net_pkt *pkt, } else { if (prefix_info->valid_lifetime == NET_IPV6_ND_INFINITE_LIFETIME) { - net_if_ipv6_addr_add(net_pkt_iface(pkt), - &addr, NET_ADDR_AUTOCONF, 0); + net_if_ipv6_addr_add(iface, &addr, + NET_ADDR_AUTOCONF, 0); } else { - net_if_ipv6_addr_add(net_pkt_iface(pkt), - &addr, NET_ADDR_AUTOCONF, + net_if_ipv6_addr_add(iface, &addr, NET_ADDR_AUTOCONF, prefix_info->valid_lifetime); } } + + /* If privacy extensions are enabled, then start the procedure for that + * too. + */ + if (IS_ENABLED(CONFIG_NET_IPV6_PE_ENABLE) && iface->pe_enabled) { + net_ipv6_pe_start(iface, &prefix_info->prefix, + prefix_info->valid_lifetime, + prefix_info->preferred_lifetime); + } } static inline bool handle_ra_prefix(struct net_pkt *pkt) diff --git a/subsys/net/ip/ipv6_pe.c b/subsys/net/ip/ipv6_pe.c new file mode 100644 index 0000000000000..6af64913f58b7 --- /dev/null +++ b/subsys/net/ip/ipv6_pe.c @@ -0,0 +1,525 @@ +/** @file + * @brief IPv6 privacy extension related functions + */ + +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_DECLARE(net_ipv6, CONFIG_NET_IPV6_LOG_LEVEL); + +#include +#include +#include + +#if defined(CONFIG_NET_IPV6_PE_ENABLE) +#include +#endif + +#include +#include +#include + +#include "net_private.h" +#include "ipv6.h" + +/* IPv6 privacy extension (RFC 4941) constants. Note that the code uses + * seconds value internally for applicaple options. These are also values + * that can be changed at runtime if needed as recommended in RFC 4941 + * chapter 3.5. + */ +static u32_t TEMP_VALID_LIFETIME = CONFIG_NET_IPV6_PE_TEMP_VALID_LIFETIME * 60; +static u32_t TEMP_PREFERRED_LIFETIME = + CONFIG_NET_IPV6_PE_TEMP_PREFERRED_LIFETIME * 60; + +#define REGEN_ADVANCE CONFIG_NET_IPV6_PE_REGEN_ADVANCE +#define MAX_DESYNC_FACTOR (CONFIG_NET_IPV6_PE_MAX_DESYNC_FACTOR * 60) + +/* RFC 4941 ch 5. "DESYNC_FACTOR is a random value within the range 0 - + * MAX_DESYNC_FACTOR. It is computed once at system start (rather than + * each time it is used) and must never be greater than + * (TEMP_VALID_LIFETIME - REGEN_ADVANCE). + * + * The RFC is contradicting as if one changes TEMP_VALID_LIFETIME at runtime, + * then DESYNC_FACTOR should be recalculated because it cannot be greater than + * TEMP_VALID_LIFETIME - REGEN_ADVANCE. But the RFC also says that the + * DESYNC_FACTOR value is only computed once at system start. For a sake of + * clarity, lets assume that if TEMP_VALID_LIFETIME is changed at runtime, we + * need to re-calculate DESYNC_FACTOR accordingly. + */ +static u32_t DESYNC_FACTOR; + +#define TEMP_IDGEN_RETRIES CONFIG_NET_IPV6_PE_TEMP_IDGEN_RETRIES + +#if CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT > 0 +/* Is this blacklisting filter or not */ +static bool ipv6_pe_blacklist; +static struct in6_addr ipv6_pe_filter[CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT]; +#endif + +/* We need to periodically update the private address. */ +static struct k_delayed_work temp_lifetime; + +static bool ipv6_pe_use_this_prefix(const struct in6_addr *prefix) +{ +#if CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT > 0 + int i, filters_found = false; + + for (i = 0; i < CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT; i++) { + if (net_ipv6_is_addr_unspecified(&ipv6_pe_filter[i])) { + continue; + } + + filters_found = true; + + if (net_ipv6_addr_cmp(prefix, &ipv6_pe_filter[i])) { + if (ipv6_pe_blacklist) { + return false; + } else { + return true; + } + } + } + + if (filters_found) { + /* There was no match so if we are blacklisting, then this + * address must be acceptable. + */ + if (ipv6_pe_blacklist) { + return true; + } + + return false; + } + + return true; +#else + return true; +#endif +} + +static bool ipv6_pe_prefix_already_exists(struct net_if_ipv6 *ipv6, + const struct in6_addr *prefix) +{ + int i; + + for (i = 0; i < NET_IF_MAX_IPV6_ADDR; i++) { + if (!ipv6->unicast[i].is_used || + ipv6->unicast[i].address.family != AF_INET6 || + !ipv6->unicast[i].is_temporary) { + continue; + } + + if (net_ipv6_is_prefix( + (u8_t *)&ipv6->unicast[i].address.in6_addr, + (u8_t *)prefix, 64)) { + return true; + } + } + + return false; +} + +static int ipv6_pe_prefix_remove(struct net_if *iface, + struct net_if_ipv6 *ipv6, + const struct in6_addr *prefix) +{ + int count = 0; + int i; + + for (i = 0; i < NET_IF_MAX_IPV6_ADDR; i++) { + if (ipv6->unicast[i].is_used && + ipv6->unicast[i].address.family == AF_INET6 && + ipv6->unicast[i].is_temporary && + net_ipv6_is_prefix( + (u8_t *)&ipv6->unicast[i].address.in6_addr, + (u8_t *)prefix, 64)) { + net_if_ipv6_addr_rm(iface, + &ipv6->unicast[i].address.in6_addr); + count++; + } + } + + return count; +} + +static bool ipv6_pe_prefix_update_lifetimes(struct net_if_ipv6 *ipv6, + const struct in6_addr *prefix, + u32_t vlifetime) +{ + s32_t addr_age, new_age; + int i; + + for (i = 0; i < NET_IF_MAX_IPV6_ADDR; i++) { + if (!(ipv6->unicast[i].is_used && + ipv6->unicast[i].address.family == AF_INET6 && + ipv6->unicast[i].is_temporary && + ipv6->unicast[i].addr_state == NET_ADDR_PREFERRED && + net_ipv6_is_prefix( + (u8_t *)&ipv6->unicast[i].address.in6_addr, + (u8_t *)prefix, 64))) { + continue; + } + + addr_age = (u32_t)(k_uptime_get() / 1000) - + ipv6->unicast[i].addr_create_time; + new_age = abs(addr_age) + vlifetime; + + if ((new_age >= TEMP_VALID_LIFETIME) || + (new_age >= (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR))) { + break; + } + + net_if_ipv6_addr_update_lifetime(&ipv6->unicast[i], vlifetime); + + /* RFC 4941 ch 3.4, "... at most one temporary address per + * prefix should be in a non-deprecated state at any given + * time on a given interface." + * Because of this there is no need to continue the loop. + */ + return true; + } + + return false; +} + +void net_ipv6_pe_start(struct net_if *iface, const struct in6_addr *prefix, + u32_t vlifetime, u32_t preferred_lifetime) +{ + u8_t link_addr[NET_LINK_ADDR_MAX_LENGTH]; + struct net_linkaddr temp_link_id; + struct net_if_addr *ifaddr; + struct net_if_ipv6 *ipv6; + struct in6_addr addr; + u8_t md5[128 / 8]; + s32_t remaining; + int i; + + if (net_if_config_ipv6_get(iface, &ipv6) < 0) { + NET_WARN("Cannot do %sDAD IPv6, config is not valid.", "PE "); + return; + } + + if (!ipv6) { + return; + } + + /* Check if user agrees to use this prefix */ + if (!ipv6_pe_use_this_prefix(prefix)) { + NET_DBG("Prefix %s/64 is not to be used", + net_sprint_ipv6_addr(prefix)); + return; + } + + /* If the prefix is already added and it is still valid, then we do + * not try to add it again. + */ + if (ipv6_pe_prefix_already_exists(ipv6, prefix)) { + if (vlifetime == 0) { + i = ipv6_pe_prefix_remove(iface, ipv6, prefix); + + NET_DBG("Removed %d addresses using prefix %s/64", + i, net_sprint_ipv6_addr(prefix)); + return; + } + + ipv6_pe_prefix_update_lifetimes(ipv6, prefix, vlifetime); + return; + } + + preferred_lifetime = MIN(preferred_lifetime, + TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR); + if (preferred_lifetime == 0 || preferred_lifetime <= REGEN_ADVANCE) { + NET_DBG("Too short preferred lifetime, temp address not " + "created for prefix %s/64", + net_sprint_ipv6_addr(prefix)); + return; + } + + NET_DBG("Starting PE process for prefix %s/64", + net_sprint_ipv6_addr(prefix)); + + temp_link_id.len = net_if_get_link_addr(iface)->len; + temp_link_id.type = net_if_get_link_addr(iface)->type; + temp_link_id.addr = link_addr; + + /* TODO: remember the random address and use it to generate a new + * one. RFC 4941 ch 3.2.1 + */ + for (i = 0; i < temp_link_id.len; i++) { + link_addr[i] = sys_rand32_get(); + } + + /* Create IPv6 address using the given prefix and iid. We first + * setup link local address, and then copy prefix over first 8 + * bytes of that address. + */ + net_ipv6_addr_create_iid(&addr, &temp_link_id); + memcpy(&addr, prefix, sizeof(struct in6_addr) / 2); + + mbedtls_md5_ret(addr.s6_addr + 8, sizeof(addr) - 8, md5); + + memcpy(addr.s6_addr + 8, &md5[8], sizeof(addr) - 8); + + /* Turn OFF universal bit, RFC 4941, ch 3.2.1 bullet 3. */ + addr.s6_addr[8] ^= 0x02; + + vlifetime = MIN(TEMP_VALID_LIFETIME, vlifetime); + + ifaddr = net_if_ipv6_addr_add(iface, &addr, NET_ADDR_AUTOCONF, + vlifetime); + if (!ifaddr) { + NET_ERR("Cannot add %s address to interface %p", + net_sprint_ipv6_addr(&addr), iface); + return; + } + + ifaddr->is_temporary = true; + ifaddr->addr_timeout = vlifetime; + ifaddr->addr_create_time = (u32_t)(k_uptime_get() / 1000); + + NET_DBG("Starting DAD for %s iface %p", net_sprint_ipv6_addr(&addr), + iface); + + net_if_ipv6_start_dad(iface, ifaddr); + + remaining = k_delayed_work_remaining_get(&temp_lifetime); + if (remaining == 0 || remaining > K_SECONDS(vlifetime)) { + NET_DBG("Next check for temp addresses in %d seconds", + vlifetime); + k_delayed_work_submit(&temp_lifetime, K_SECONDS(vlifetime)); + } +} + +#if CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT > 0 + +static void iface_cb(struct net_if *iface, void *user_data) +{ + bool is_new_filter_blacklist = !ipv6_pe_blacklist; + struct in6_addr *prefix = user_data; + struct net_if_ipv6 *ipv6; + int i, ret; + + if (net_if_config_ipv6_get(iface, &ipv6) < 0) { + return; + } + + if (!ipv6) { + return; + } + + for (i = 0; i < NET_IF_MAX_IPV6_ADDR; i++) { + if (!ipv6->unicast[i].is_used || + ipv6->unicast[i].address.family != AF_INET6 || + !ipv6->unicast[i].is_temporary) { + continue; + } + + ret = net_ipv6_is_prefix( + (u8_t *)&ipv6->unicast[i].address.in6_addr, + (u8_t *)prefix, 64); + + /* TODO: Do this removal gracefully so that applications + * have time to cope with this change. + */ + if (is_new_filter_blacklist) { + if (ret) { + net_if_ipv6_addr_rm(iface, + &ipv6->unicast[i].address.in6_addr); + } + } else { + if (!ret) { + net_if_ipv6_addr_rm(iface, + &ipv6->unicast[i].address.in6_addr); + } + } + } +} + +/* If we change filter value, then check if existing IPv6 prefixes will + * conflict with the new filter. + */ +static void ipv6_pe_recheck_filters(bool is_blacklist) +{ + int i; + + for (i = 0; i < CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT; i++) { + if (net_ipv6_is_addr_unspecified(&ipv6_pe_filter[i])) { + continue; + } + + net_if_foreach(iface_cb, &ipv6_pe_filter[i]); + } +} +#endif /* CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT > 0 */ + +int net_ipv6_pe_add_filter(struct in6_addr *addr, bool is_blacklist) +{ +#if CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT > 0 + int i; + + for (i = 0; i < CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT; i++) { + if (net_ipv6_is_addr_unspecified(&ipv6_pe_filter[i])) { + net_ipaddr_copy(&ipv6_pe_filter[i], addr); + + if (ipv6_pe_blacklist != is_blacklist) { + ipv6_pe_recheck_filters(is_blacklist); + } + + ipv6_pe_blacklist = is_blacklist ? true : false; + + NET_DBG("Adding %s filter %s", + ipv6_pe_blacklist ? "blacklist" : "whitelist", + net_sprint_ipv6_addr(&ipv6_pe_filter[i])); + + return 0; + } + } + + return -ENOENT; +#else + return -ENOTSUP; +#endif +} + +int net_ipv6_pe_del_filter(struct in6_addr *addr) +{ +#if CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT > 0 + int i; + + for (i = 0; i < CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT; i++) { + if (net_ipv6_addr_cmp(&ipv6_pe_filter[i], addr)) { + NET_DBG("Removing %s filter %s", + ipv6_pe_blacklist ? "blacklist" : "whitelist", + net_sprint_ipv6_addr(&ipv6_pe_filter[i])); + + net_ipaddr_copy(&ipv6_pe_filter[i], + net_ipv6_unspecified_address()); + + return 0; + } + } + + return -ENOENT; +#else + return -ENOTSUP; +#endif +} + +bool net_ipv6_pe_check_dad(int count) +{ + if (count > TEMP_IDGEN_RETRIES) { + return false; + } + + return true; +} + +int net_ipv6_pe_filter_foreach(net_ipv6_pe_filter_cb_t cb, void *user_data) +{ +#if CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT > 0 + int i, count = 0; + + for (i = 0; i < CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT; i++) { + if (net_ipv6_is_addr_unspecified(&ipv6_pe_filter[i])) { + continue; + } + + cb(&ipv6_pe_filter[i], ipv6_pe_blacklist, user_data); + + count++; + } + + return count; +#else + return 0; +#endif +} + +static void renewal_cb(struct net_if *iface, void *user_data) +{ + struct net_if_ipv6 *ipv6; + struct in6_addr prefix; + int i; + + if (net_if_config_ipv6_get(iface, &ipv6) < 0) { + return; + } + + if (!ipv6) { + return; + } + + for (i = 0; i < NET_IF_MAX_IPV6_ADDR; i++) { + s32_t diff; + + if (!ipv6->unicast[i].is_used || + ipv6->unicast[i].address.family != AF_INET6 || + !ipv6->unicast[i].is_temporary) { + continue; + } + + /* If the address is too old, then generate a new one + * and remove the old address. + */ + diff = (s32_t)(ipv6->unicast[i].addr_create_time - + ((u32_t)(k_uptime_get() / 1000))); + diff = abs(diff); + + if (diff < (TEMP_PREFERRED_LIFETIME - + REGEN_ADVANCE - DESYNC_FACTOR)) { + continue; + } + + net_ipaddr_copy(&prefix, &ipv6->unicast[i].address.in6_addr); + + net_if_ipv6_addr_rm(iface, + &ipv6->unicast[i].address.in6_addr); + + memset(prefix.s6_addr + 8, 0, sizeof(prefix) - 8); + + net_ipv6_pe_start(iface, &prefix, + ipv6->unicast[i].addr_timeout, + ipv6->unicast[i].addr_preferred_lifetime); + } +} + +static void ipv6_pe_renew(struct k_work *work) +{ + ARG_UNUSED(work); + + net_if_foreach(renewal_cb, NULL); +} + +int net_ipv6_pe_init(struct net_if *iface) +{ + static bool init_done; + u32_t lifetime; + + if (TEMP_VALID_LIFETIME - REGEN_ADVANCE <= 0) { + iface->pe_enabled = false; + return -EINVAL; + } + + iface->pe_enabled = true; + iface->pe_prefer_public = + IS_ENABLED(CONFIG_NET_IPV6_PE_PREFER_PUBLIC_ADDRESSES) ? + true : false; + + if (!init_done) { + lifetime = TEMP_VALID_LIFETIME - REGEN_ADVANCE; + + /* RFC 4941 ch 5 */ + DESYNC_FACTOR = sys_rand32_get() % MIN(MAX_DESYNC_FACTOR, + lifetime); + init_done = true; + } + + k_delayed_work_init(&temp_lifetime, ipv6_pe_renew); + + return 0; +} + diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index 14d7bcdbd221d..7db5def898221 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -13,6 +13,7 @@ LOG_MODULE_REGISTER(net_if, CONFIG_NET_IF_LOG_LEVEL); #include #include #include +#include #include #include #include @@ -275,6 +276,8 @@ static inline void init_iface(struct net_if *iface) NET_DBG("On iface %p", iface); api->init(iface); + + net_ipv6_pe_init(iface); } enum net_verdict net_if_send_data(struct net_if *iface, struct net_pkt *pkt) @@ -867,8 +870,8 @@ static void dad_timeout(struct k_work *work) } } -static void net_if_ipv6_start_dad(struct net_if *iface, - struct net_if_addr *ifaddr) +void net_if_ipv6_start_dad(struct net_if *iface, + struct net_if_addr *ifaddr) { ifaddr->addr_state = NET_ADDR_TENTATIVE; @@ -942,6 +945,7 @@ void net_if_start_dad(struct net_if *iface) void net_if_ipv6_dad_failed(struct net_if *iface, const struct in6_addr *addr) { struct net_if_addr *ifaddr; + u32_t timeout, preferred_lifetime; ifaddr = net_if_ipv6_addr_lookup(addr, &iface); if (!ifaddr) { @@ -952,11 +956,32 @@ void net_if_ipv6_dad_failed(struct net_if *iface, const struct in6_addr *addr) sys_slist_find_and_remove(&active_dad_timers, &ifaddr->dad_node); + if (IS_ENABLED(CONFIG_NET_IPV6_PE_ENABLE)) { + ifaddr->dad_count++; + + timeout = ifaddr->addr_timeout; + preferred_lifetime = ifaddr->addr_preferred_lifetime; + + if (!net_ipv6_pe_check_dad(ifaddr->dad_count)) { + NET_ERR("Cannot generate PE address for interface %p", + iface); + iface->pe_enabled = false; + } + } + net_mgmt_event_notify_with_info(NET_EVENT_IPV6_DAD_FAILED, iface, &ifaddr->address.in6_addr, sizeof(struct in6_addr)); + /* The old address needs to be removed from the interface before we can + * start new DAD for the new PE address as the amount of address slots + * is limited. + */ net_if_ipv6_addr_rm(iface, addr); + + if (IS_ENABLED(CONFIG_NET_IPV6_PE_ENABLE) && iface->pe_enabled) { + net_ipv6_pe_start(iface, addr, timeout, preferred_lifetime); + } } static inline void iface_ipv6_dad_init(void) @@ -1339,8 +1364,10 @@ static inline void net_if_addr_init(struct net_if_addr *ifaddr, u32_t vlifetime) { ifaddr->is_used = true; + ifaddr->is_temporary = false; ifaddr->address.family = AF_INET6; ifaddr->addr_type = addr_type; + net_ipaddr_copy(&ifaddr->address.in6_addr, addr); /* FIXME - set the mcast addr for this node */ @@ -1979,7 +2006,7 @@ bool net_if_ipv6_prefix_rm(struct net_if *iface, struct in6_addr *addr, } struct net_if_ipv6_prefix *net_if_ipv6_prefix_get(struct net_if *iface, - struct in6_addr *addr) + const struct in6_addr *addr) { struct net_if_ipv6_prefix *prefix = NULL; struct net_if_ipv6 *ipv6; @@ -2242,25 +2269,53 @@ static inline bool is_proper_ipv6_address(struct net_if_addr *addr) return false; } +static bool use_public_address(bool prefer_public, bool is_temporary) +{ + if (IS_ENABLED(CONFIG_NET_IPV6_PE_ENABLE)) { + if (!prefer_public && is_temporary) { + return false; + } + } + + return true; +} + static struct in6_addr *net_if_ipv6_get_best_match(struct net_if *iface, const struct in6_addr *dst, + u8_t prefix_len, u8_t *best_so_far) { struct net_if_ipv6 *ipv6 = iface->config.ip.ipv6; + struct net_if_addr *public_addr = NULL; struct in6_addr *src = NULL; - u8_t len; + u8_t public_addr_len = 0; + struct in6_addr *temp_addr; + u8_t len, temp_addr_len; + bool ret; int i; if (!ipv6) { return NULL; } + if (IS_ENABLED(CONFIG_NET_IPV6_PE_ENABLE)) { + temp_addr = NULL; + temp_addr_len = 0; + } else { + ARG_UNUSED(temp_addr); + ARG_UNUSED(temp_addr_len); + } + for (i = 0; i < NET_IF_MAX_IPV6_ADDR; i++) { if (!is_proper_ipv6_address(&ipv6->unicast[i])) { continue; } len = get_diff_ipv6(dst, &ipv6->unicast[i].address.in6_addr); + if (len >= prefix_len) { + len = prefix_len; + } + if (len >= *best_so_far) { /* Mesh local address can only be selected for the same * subnet. @@ -2269,11 +2324,39 @@ static struct in6_addr *net_if_ipv6_get_best_match(struct net_if *iface, continue; } + ret = use_public_address(iface->pe_prefer_public, + ipv6->unicast[i].is_temporary); + if (!ret) { + temp_addr = &ipv6->unicast[i].address.in6_addr; + temp_addr_len = len; + continue; + } + + if (!ipv6->unicast[i].is_temporary) { + public_addr = &ipv6->unicast[i]; + public_addr_len = len; + } + *best_so_far = len; src = &ipv6->unicast[i].address.in6_addr; } } + if (IS_ENABLED(CONFIG_NET_IPV6_PE_ENABLE) && + !iface->pe_prefer_public && temp_addr) { + if (temp_addr_len >= *best_so_far) { + *best_so_far = temp_addr_len; + src = temp_addr; + } + } else { + /* By default prefer always public address if found */ + if (public_addr && + !net_ipv6_addr_cmp(&public_addr->address.in6_addr, src)) { + src = &public_addr->address.in6_addr; + *best_so_far = public_addr_len; + } + } + return src; } @@ -2285,6 +2368,13 @@ const struct in6_addr *net_if_ipv6_select_src_addr(struct net_if *dst_iface, struct net_if *iface; if (!net_ipv6_is_ll_addr(dst) && !net_ipv6_is_addr_mcast(dst)) { + struct net_if_ipv6_prefix *prefix; + u8_t prefix_len = 128; + + prefix = net_if_ipv6_prefix_get(dst_iface, dst); + if (prefix) { + prefix_len = prefix->len; + } for (iface = __net_if_start; !dst_iface && iface != __net_if_end; @@ -2292,6 +2382,7 @@ const struct in6_addr *net_if_ipv6_select_src_addr(struct net_if *dst_iface, struct in6_addr *addr; addr = net_if_ipv6_get_best_match(iface, dst, + prefix_len, &best_match); if (addr) { src = addr; @@ -2301,6 +2392,7 @@ const struct in6_addr *net_if_ipv6_select_src_addr(struct net_if *dst_iface, /* If caller has supplied interface, then use that */ if (dst_iface) { src = net_if_ipv6_get_best_match(dst_iface, dst, + prefix_len, &best_match); } diff --git a/subsys/net/ip/net_private.h b/subsys/net/ip/net_private.h index fea9d8b107cc6..b76c5d1425a79 100644 --- a/subsys/net/ip/net_private.h +++ b/subsys/net/ip/net_private.h @@ -90,6 +90,12 @@ int net_context_get_timestamp(struct net_context *context, struct net_ptp_time *timestamp); #endif +#if defined(CONFIG_NET_IPV6_PE_ENABLE) +/* This is needed by ipv6_pe.c when privacy extension support is enabled */ +void net_if_ipv6_start_dad(struct net_if *iface, + struct net_if_addr *ifaddr); +#endif + #if defined(CONFIG_NET_GPTP) /** * @brief Initialize Precision Time Protocol Layer. diff --git a/subsys/net/ip/net_shell.c b/subsys/net/ip/net_shell.c index eb7f2196f28a7..2360b5794c7af 100644 --- a/subsys/net/ip/net_shell.c +++ b/subsys/net/ip/net_shell.c @@ -392,12 +392,13 @@ static void iface_cb(struct net_if *iface, void *user_data) continue; } - PR("\t%s %s %s%s%s\n", + PR("\t%s %s %s%s%s%s\n", net_sprint_ipv6_addr(&unicast->address.in6_addr), addrtype2str(unicast->addr_type), addrstate2str(unicast->addr_state), unicast->is_infinite ? " infinite" : "", - unicast->is_mesh_local ? " meshlocal" : ""); + unicast->is_mesh_local ? " meshlocal" : "", + unicast->is_temporary ? " temporary" : ""); count++; } @@ -453,6 +454,12 @@ static void iface_cb(struct net_if *iface, void *user_data) router->is_infinite ? " infinite" : ""); } +#if defined(CONFIG_NET_IPV6_PE_ENABLE) + printk("IPv6 privacy extension : %s (preferring %s addresses)\n", + iface->pe_enabled ? "enabled" : "disabled", + iface->pe_prefer_public ? "public" : "temporary"); +#endif + if (ipv6) { PR("IPv6 hop limit : %d\n", ipv6->hop_limit); @@ -2332,6 +2339,26 @@ static u32_t time_diff(u32_t time1, u32_t time2) return (u32_t)abs((s32_t)time1 - (s32_t)time2); } +#if defined(CONFIG_NET_IPV6_PE_ENABLE) +static void ipv6_pe_filter_cb(struct in6_addr *prefix, bool is_blacklist, + void *user_data) +{ + char ipaddr[INET6_ADDRSTRLEN + 1]; + int *count = user_data; + + net_addr_ntop(AF_INET6, prefix, ipaddr, sizeof(ipaddr) - 1); + + if ((*count) == 0) { + printk("IPv6 privacy extension %s list filters :\n", + is_blacklist ? "black" : "white"); + } + + printk("[%d] %s/64\n", *count, ipaddr); + + (*count)++; +} +#endif /* CONFIG_NET_IPV6_PE_ENABLE */ + static void address_lifetime_cb(struct net_if *iface, void *user_data) { struct net_shell_user_data *data = user_data; @@ -2386,13 +2413,14 @@ static void address_lifetime_cb(struct net_if *iface, void *user_data) "%u", (u32_t)(remaining / 1000U)); } - PR("%s \t%s\t%s \t%s/%d\n", + PR("%s \t%s\t%s \t%s/%d%s\n", addrtype2str(ipv6->unicast[i].addr_type), addrstate2str(ipv6->unicast[i].addr_state), remaining_str, net_sprint_ipv6_addr( &ipv6->unicast[i].address.in6_addr), - prefix_len); + prefix_len, + ipv6->unicast[i].is_temporary ? " (temporary)" : ""); } } #endif /* CONFIG_NET_NATIVE_IPV6 */ @@ -2401,6 +2429,15 @@ static int cmd_net_ipv6(const struct shell *shell, size_t argc, char *argv[]) { #if defined(CONFIG_NET_NATIVE_IPV6) struct net_shell_user_data user_data; + int arg = 0; + +#if defined(CONFIG_NET_IPV6_PE_ENABLE) + int ret; +#endif + + if (argc > 1) { + goto skip_summary; + } #endif PR("IPv6 support : %s\n", @@ -2440,6 +2477,20 @@ static int cmd_net_ipv6(const struct shell *shell, size_t argc, char *argv[]) "disabled"); } + PR("Privacy extension support : %s\n", + IS_ENABLED(CONFIG_NET_IPV6_PE_ENABLE) ? "enabled" : + "disabled"); + +#if defined(CONFIG_NET_IPV6_PE_ENABLE) + ret = 0; + + net_ipv6_pe_filter_foreach(ipv6_pe_filter_cb, &ret); + + PR("Max number of IPv6 privacy extension filters " + " : %d\n", + CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT); +#endif + PR("Max number of IPv6 network interfaces " "in the system : %d\n", CONFIG_NET_IF_MAX_IPV6_COUNT); @@ -2458,7 +2509,96 @@ static int cmd_net_ipv6(const struct shell *shell, size_t argc, char *argv[]) /* Print information about address lifetime */ net_if_foreach(address_lifetime_cb, &user_data); + + if (argc <= 1) { + return 0; + } + +skip_summary: + + if (strcmp(argv[arg], "pe") == 0) { +#if CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT > 0 + bool do_whitelisting = true; + struct in6_addr prefix; + bool do_add; + + arg++; + + if (!argv[arg]) { + PR("No sub-options given. See \"help net ipv6\" " + "command for details.\n"); + return 0; + } + + if (strcmp(argv[arg], "add") == 0) { + arg++; + do_add = true; + } else if (strcmp(argv[arg], "del") == 0) { + arg++; + do_add = false; + } else { + PR("Unknown sub-option \"%s\"\n", argv[arg]); + return 0; + } + + if (!argv[arg]) { + PR("No sub-options given. See \"help net ipv6\" " + "command for details.\n"); + return 0; + } + + if (strcmp(argv[arg], "white") == 0) { + arg++; + } else if (strcmp(argv[arg], "black") == 0) { + arg++; + do_whitelisting = false; + } + + if (!argv[arg]) { + PR("No sub-options given. See \"help net ipv6\" " + "command for details.\n"); + return 0; + } + + ret = net_addr_pton(AF_INET6, argv[arg], &prefix); + if (ret < 0) { + PR("Invalid prefix \"%s\"\n", argv[arg]); + if (strstr(argv[arg], "/")) { + PR("Do not add the prefix length.\n"); + } + + return 0; + } + + if (do_add) { + ret = net_ipv6_pe_add_filter(&prefix, !do_whitelisting); + } else { + ret = net_ipv6_pe_del_filter(&prefix); + } + + if (ret < 0) { + PR("Cannot %s %s %sfilter (%d)\n", + do_add ? "add" : "delete", + argv[arg], + do_add ? + (do_whitelisting ? "whitelist " : + "blacklist ") : "", + ret); + return 0; + } + + PR("%s %sfilter for %s\n", do_add ? "Added" : "Deleted", + do_add ? + (do_whitelisting ? "whitelist " : "blacklist ") : "", + argv[arg]); +#else + PR("IPv6 privacy extension filter support is disabled.\n"); + PR("Set CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT > 0 to " + "enable it.\n"); #endif +#endif /* CONFIG_NET_NATIVE_IPV6 */ + return 0; + } return 0; } @@ -3958,6 +4098,19 @@ SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_gptp, SHELL_SUBCMD_SET_END ); +SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_ipv6, + SHELL_CMD(pe, NULL, + "net ipv6 pe add [black|white] \n" + "Add IPv6 address to filter list. The black/white " + "parameter tells if this is white listed (accepted) or " + "black listed (declined) prefix. Default is to white list " + "the prefix.\n" + "ipv6 pe del \n" + "Delete IPv6 address from filter list.", + cmd_net_ipv6), + SHELL_SUBCMD_SET_END +); + #if !defined(NET_VLAN_MAX_COUNT) #define MAX_IFACE_COUNT NET_IF_MAX_CONFIGS #else @@ -4231,7 +4384,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE(net_commands, SHELL_CMD(iface, &net_cmd_iface, "Print information about network interfaces.", cmd_net_iface), - SHELL_CMD(ipv6, NULL, + SHELL_CMD(ipv6, &net_cmd_ipv6, "Print information about IPv6 specific information and " "configuration.", cmd_net_ipv6), diff --git a/tests/net/all/prj.conf b/tests/net/all/prj.conf index 9760f30233f97..9115c66121ac3 100644 --- a/tests/net/all/prj.conf +++ b/tests/net/all/prj.conf @@ -181,6 +181,11 @@ CONFIG_NET_IPV6_LOG_LEVEL_DBG=y CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL_DBG=y CONFIG_NET_ICMPV6_LOG_LEVEL_DBG=y +# IPv6 privacy extension +CONFIG_NET_IPV6_PE_ENABLE=y +CONFIG_NET_IPV6_PE_PREFER_PUBLIC_ADDRESSES=n +CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT=2 + # 6lo CONFIG_NET_6LO=y CONFIG_NET_6LO_CONTEXT=y diff --git a/tests/net/ipv6/prj.conf b/tests/net/ipv6/prj.conf index 48fe041d84ffd..2747323540898 100644 --- a/tests/net/ipv6/prj.conf +++ b/tests/net/ipv6/prj.conf @@ -24,3 +24,19 @@ CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=9 CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=7 CONFIG_NET_IF_IPV6_PREFIX_COUNT=3 CONFIG_NET_UDP_CHECKSUM=n + +CONFIG_NET_IF_MAX_IPV6_COUNT=2 +CONFIG_SYS_LOG_NET_LEVEL=2 +#CONFIG_NET_DEBUG_IF=y +#CONFIG_NET_DEBUG_CORE=y +#CONFIG_NET_DEBUG_IPV6=y +#CONFIG_NET_DEBUG_ICMPV6=y +#CONFIG_NET_DEBUG_L2_ETHERNET=y +#CONFIG_NET_DEBUG_UTILS=y +#CONFIG_NET_DEBUG_NET_PKT=y +#CONFIG_NET_DEBUG_6LO=y + +#CONFIG_NET_SHELL=y +CONFIG_NET_IPV6_PE_ENABLE=y +CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT=2 +CONFIG_NET_IPV6_PE_PREFER_PUBLIC_ADDRESSES=n diff --git a/tests/net/ipv6/src/main.c b/tests/net/ipv6/src/main.c index 29c9818d2111a..1ceb53f218b30 100644 --- a/tests/net/ipv6/src/main.c +++ b/tests/net/ipv6/src/main.c @@ -93,8 +93,8 @@ static const unsigned char icmpv6_ra[] = { /* MTU */ 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc, /* Prefix info*/ - 0x03, 0x04, 0x40, 0xc0, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfe, 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; @@ -1347,6 +1347,207 @@ static void test_dst_iface_scope_mcast_send(void) net_context_put(ctx); } +#if defined(CONFIG_NET_IPV6_PE_ENABLE) +static bool is_pe_address_found(struct net_if *iface, struct in6_addr *prefix) +{ + struct net_if_ipv6 *ipv6; + int i; + + ipv6 = iface->config.ip.ipv6; + + zassert_not_null(ipv6, "IPv6 configuration is wrong for iface %p", + iface); + + for (i = 0; i < NET_IF_MAX_IPV6_ADDR; i++) { + if (!ipv6->unicast[i].is_used || + ipv6->unicast[i].address.family != AF_INET6 || + !ipv6->unicast[i].is_temporary) { + continue; + } + + if (net_is_ipv6_prefix( + (u8_t *)&ipv6->unicast[i].address.in6_addr, + (u8_t *)prefix, 64)) { + return true; + } + } + + return false; +} + +static void get_pe_addresses(struct net_if *iface, + struct in6_addr **public_addr, + struct in6_addr **temp_addr) +{ + struct in6_addr prefix = { { { 0x3f, 0xfe, 0x05, 0x07, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0 } } }; + struct net_if_ipv6 *ipv6; + int i; + + ipv6 = iface->config.ip.ipv6; + + zassert_not_null(ipv6, "IPv6 configuration is wrong for iface %p", + iface); + + for (i = 0; i < NET_IF_MAX_IPV6_ADDR; i++) { + if (!ipv6->unicast[i].is_used || + ipv6->unicast[i].address.family != AF_INET6) { + continue; + } + + if (net_is_ipv6_prefix( + (u8_t *)&ipv6->unicast[i].address.in6_addr, + (u8_t *)&prefix, 64)) { + if (ipv6->unicast[i].is_temporary) { + *temp_addr = + &ipv6->unicast[i].address.in6_addr; + } else { + *public_addr = + &ipv6->unicast[i].address.in6_addr; + } + } + } +} +#endif + +static void test_privacy_extension(void) +{ +#if defined(CONFIG_NET_IPV6_PE_ENABLE) + struct in6_addr prefix = { { { 0x3f, 0xfe, 0x05, 0x07, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0 } } }; + struct net_if *iface = net_if_get_default(); + bool found; + + zassert_true(iface->pe_enabled, + "Privacy extension not enabled for iface %p", iface); + + if (IS_ENABLED(CONFIG_NET_IPV6_PE_PREFER_PUBLIC_ADDRESSES)) { + zassert_true(iface->pe_prefer_public, + "Prefer public flag not set correctly for " + "iface %p", iface); + } + + /* We received RA message earlier, make sure that temporary address + * is created because of that message. + */ + + found = is_pe_address_found(iface, &prefix); + zassert_true(found, "Temporary address not found for iface %p", iface); +#endif +} + +static void test_privacy_extension_filters(void) +{ +#if defined(CONFIG_NET_IPV6_PE_ENABLE) +#if CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT > 0 + struct in6_addr prefix1 = { { { 0x3f, 0xfe, 0x05, 0x07, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0 } } }; + struct in6_addr prefix2 = { { { 0x3f, 0xfe, 0x04, 0x07, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0 } } }; + struct in6_addr prefix3 = { { { 0x3f, 0xfe, 0x03, 0x07, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0 } } }; + struct net_if *iface = net_if_get_default(); + bool found; + int ret; + + /* First add blacklist filters */ + ret = net_ipv6_pe_add_filter(&prefix1, true); + zassert_equal(ret, 0, "Filter cannot be added (%d)", ret); + + ret = net_ipv6_pe_add_filter(&prefix2, true); + zassert_equal(ret, 0, "Filter cannot be added (%d)", ret); + + ret = net_ipv6_pe_add_filter(&prefix3, true); + zassert_true(ret < 0, "Filter could be added"); + + /* Then delete them */ + ret = net_ipv6_pe_del_filter(&prefix1); + zassert_equal(ret, 0, "Filter cannot be deleted (%d)", ret); + + ret = net_ipv6_pe_del_filter(&prefix2); + zassert_equal(ret, 0, "Filter cannot be deleted (%d)", ret); + + ret = net_ipv6_pe_del_filter(&prefix2); + zassert_true(ret < 0, "Filter found (%d)", ret); + + /* Then add whitelist filter */ + ret = net_ipv6_pe_add_filter(&prefix1, false); + zassert_equal(ret, 0, "Filter cannot be added (%d)", ret); + + /* Send RS again as we have now PE whitelist filter in place */ + test_rs_message(); + + /* IP stack needs to process the packet */ + k_sleep(K_MSEC(150)); + + found = is_pe_address_found(iface, &prefix1); + zassert_true(found, "Temporary address not found for iface %p", iface); + + /* Then try with blacklisted filter */ + ret = net_ipv6_pe_del_filter(&prefix1); + zassert_equal(ret, 0, "Filter cannot be deleted (%d)", ret); + + ret = net_ipv6_pe_add_filter(&prefix1, true); + zassert_equal(ret, 0, "Filter cannot be added (%d)", ret); + + k_sleep(K_MSEC(10)); + + /* Send RS again as we have now PE blacklist filter in place */ + test_rs_message(); + + k_sleep(K_MSEC(150)); + + found = is_pe_address_found(iface, &prefix1); + zassert_false(found, "Temporary address found for iface %p", iface); + + ret = net_ipv6_pe_del_filter(&prefix1); + zassert_equal(ret, 0, "Filter cannot be deleted (%d)", ret); + + /* Add the temp address back for the next tests */ + ret = net_ipv6_pe_add_filter(&prefix1, false); + zassert_equal(ret, 0, "Filter cannot be added (%d)", ret); + + k_sleep(K_MSEC(50)); + + /* Send RS again as we have now PE whitelist filter in place */ + test_rs_message(); + + k_sleep(K_MSEC(150)); + + found = is_pe_address_found(iface, &prefix1); + zassert_true(found, "Temporary address not found for iface %p", iface); +#endif +#endif +} + +static void test_privacy_extension_get_addr(void) +{ +#if defined(CONFIG_NET_IPV6_PE_ENABLE) + struct in6_addr dst_addr = { { { 0x3f, 0xfe, 0x05, 0x07, 0, 0, 0, 1, + 0, 0, 2, 3, 4, 5, 6, 7 } } }; + struct net_if *iface = net_if_get_default(); + struct in6_addr *public_addr = NULL; + struct in6_addr *temp_addr = NULL; + const struct in6_addr *src_addr; + + get_pe_addresses(iface, &public_addr, &temp_addr); + + zassert_not_null(public_addr, "No public address found"); + zassert_not_null(temp_addr, "No temporary address found"); + + src_addr = net_if_ipv6_select_src_addr(iface, &dst_addr); + zassert_not_null(src_addr, "No suitable source address found"); + + if (iface->pe_prefer_public) { + zassert_true(net_ipv6_addr_cmp(src_addr, public_addr), + "Non public address selected"); + } else { + zassert_true(net_ipv6_addr_cmp(src_addr, temp_addr), + "Non temporary address selected"); + } +#endif +} + void test_main(void) { ztest_test_suite(test_ipv6_fn, @@ -1376,7 +1577,10 @@ void test_main(void) ztest_unit_test(test_dst_zero_scope_mcast_recv), ztest_unit_test(test_dst_site_scope_mcast_recv_drop), ztest_unit_test(test_dst_site_scope_mcast_recv_ok), - ztest_unit_test(test_dst_org_scope_mcast_recv) + ztest_unit_test(test_dst_org_scope_mcast_recv), + ztest_unit_test(test_privacy_extension), + ztest_unit_test(test_privacy_extension_filters), + ztest_unit_test(test_privacy_extension_get_addr) ); ztest_run_test_suite(test_ipv6_fn); } diff --git a/tests/net/ipv6/testcase.yaml b/tests/net/ipv6/testcase.yaml index e92430af22927..36ceb4e3a2a7f 100644 --- a/tests/net/ipv6/testcase.yaml +++ b/tests/net/ipv6/testcase.yaml @@ -1,4 +1,21 @@ +common: + tags: net ipv6 ipv6_pe + depends_on: netif tests: net.ipv6: - tags: net ipv6 - depends_on: netif + extra_configs: + - CONFIG_NET_IPV6_PE_ENABLE=n + net.ipv6.privacy_extension.prefer_public: + extra_configs: + - CONFIG_NET_IPV6_PE_ENABLE=y + - CONFIG_NET_IPV6_PE_PREFER_PUBLIC_ADDRESSES=y + - CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT=2 + - CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=9 + - CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=7 + net.ipv6.privacy_extension.prefer_temporary: + extra_configs: + - CONFIG_NET_IPV6_PE_ENABLE=y + - CONFIG_NET_IPV6_PE_PREFER_PUBLIC_ADDRESSES=n + - CONFIG_NET_IPV6_PE_FILTER_PREFIX_COUNT=2 + - CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=9 + - CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=7