From 94591e3cbe3dfc4e17bba5c5b69bf55d185f468f Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Mon, 26 Aug 2024 15:05:01 +0300 Subject: [PATCH 1/4] net: ipv6: Add support for stable IID addresses This implements support for RFC 7217 which describes a method to have stable IPv6 Interface Identifiers to be used with IPv6 Stateless Address Autoconfiguration (SLAAC). The stable IIDs are used to provide enhanced privacy so that an IPv6 address configured using this method is stable within each subnet, but the corresponding Interface Identifier changes when the host moves from one network to another. This method is meant to be an alternative to generating Interface Identifiers based on hardware (MAC) addresses, such that the benefits of stable addresses can be achieved without sacrificing the security and privacy of users. Signed-off-by: Jukka Rissanen --- include/zephyr/net/net_if.h | 10 ++ include/zephyr/net/net_ip.h | 65 ++++----- subsys/net/ip/CMakeLists.txt | 2 +- subsys/net/ip/Kconfig.ipv6 | 40 +++++ subsys/net/ip/ipv6.c | 194 +++++++++++++++++++++++++ subsys/net/ip/ipv6_nbr.c | 21 ++- subsys/net/ip/net_if.c | 44 +++++- subsys/net/l2/ethernet/ethernet_mgmt.c | 3 +- subsys/net/l2/virtual/ipip/ipip.c | 15 +- 9 files changed, 342 insertions(+), 52 deletions(-) diff --git a/include/zephyr/net/net_if.h b/include/zephyr/net/net_if.h index 7795f56597662..c44d73144808a 100644 --- a/include/zephyr/net/net_if.h +++ b/include/zephyr/net/net_if.h @@ -331,6 +331,16 @@ struct net_if_ipv6 { /** Retransmit timer (RFC 4861, page 52) */ uint32_t retrans_timer; +#if defined(CONFIG_NET_IPV6_IID_STABLE) + /** IID (Interface Identifier) pointer used for link local address */ + struct net_if_addr *iid; + + /** Incremented when network interface goes down so that we can + * generate new stable addresses when interface comes back up. + */ + uint32_t network_counter; +#endif /* CONFIG_NET_IPV6_IID_STABLE */ + #if defined(CONFIG_NET_IPV6_PE) /** Privacy extension DESYNC_FACTOR value from RFC 8981 ch 3.4. * "DESYNC_FACTOR is a random value within the range 0 - MAX_DESYNC_FACTOR. diff --git a/include/zephyr/net/net_ip.h b/include/zephyr/net/net_ip.h index 0963bdd69bc31..603639874407f 100644 --- a/include/zephyr/net/net_ip.h +++ b/include/zephyr/net/net_ip.h @@ -1436,7 +1436,32 @@ static inline bool net_ipv6_addr_is_v4_mapped(const struct in6_addr *addr) } /** - * @brief Create IPv6 address interface identifier + * @brief Generate IPv6 address using a prefix and interface identifier. + * Interface identifier is either generated from EUI-64 (MAC) defined + * in RFC 4291 or from randomized value defined in RFC 7217. + * + * @param iface Network interface + * @param prefix IPv6 prefix, can be left out in which case fe80::/64 is used + * @param network_id Network identifier (for example SSID in WLAN), this is + * optional can be set to NULL + * @param network_id_len Network identifier length, if set to 0 then the + * network id is ignored. + * @param dad_counter Duplicate Address Detection counter value, can be set to 0 + * if it is not known. + * @param addr IPv6 address + * @param lladdr Link local address + * + * @return 0 if ok, < 0 if error + */ +int net_ipv6_addr_generate_iid(struct net_if *iface, + const struct in6_addr *prefix, + uint8_t *network_id, size_t network_id_len, + uint8_t dad_counter, + struct in6_addr *addr, + struct net_linkaddr *lladdr); + +/** + * @brief Create IPv6 address interface identifier. * * @param addr IPv6 address * @param lladdr Link local address @@ -1444,43 +1469,7 @@ static inline bool net_ipv6_addr_is_v4_mapped(const struct in6_addr *addr) static inline void net_ipv6_addr_create_iid(struct in6_addr *addr, struct net_linkaddr *lladdr) { - UNALIGNED_PUT(htonl(0xfe800000), &addr->s6_addr32[0]); - UNALIGNED_PUT(0, &addr->s6_addr32[1]); - - switch (lladdr->len) { - case 2: - /* The generated IPv6 shall not toggle the - * Universal/Local bit. RFC 6282 ch 3.2.2 - */ - if (lladdr->type == NET_LINK_IEEE802154) { - UNALIGNED_PUT(0, &addr->s6_addr32[2]); - addr->s6_addr[11] = 0xff; - addr->s6_addr[12] = 0xfe; - addr->s6_addr[13] = 0U; - addr->s6_addr[14] = lladdr->addr[0]; - addr->s6_addr[15] = lladdr->addr[1]; - } - - break; - case 6: - /* We do not toggle the Universal/Local bit - * in Bluetooth. See RFC 7668 ch 3.2.2 - */ - memcpy(&addr->s6_addr[8], lladdr->addr, 3); - addr->s6_addr[11] = 0xff; - addr->s6_addr[12] = 0xfe; - memcpy(&addr->s6_addr[13], lladdr->addr + 3, 3); - - if (lladdr->type == NET_LINK_ETHERNET) { - addr->s6_addr[8] ^= 0x02; - } - - break; - case 8: - memcpy(&addr->s6_addr[8], lladdr->addr, lladdr->len); - addr->s6_addr[8] ^= 0x02; - break; - } + (void)net_ipv6_addr_generate_iid(NULL, NULL, NULL, 0, 0, addr, lladdr); } /** diff --git a/subsys/net/ip/CMakeLists.txt b/subsys/net/ip/CMakeLists.txt index 23d433930b1f7..d9bd7b1c780d9 100644 --- a/subsys/net/ip/CMakeLists.txt +++ b/subsys/net/ip/CMakeLists.txt @@ -54,7 +54,7 @@ zephyr_library_sources_ifdef(CONFIG_NET_CONNECTION_SOCKETS connection.c) zephyr_library_sources_ifdef(CONFIG_NET_SOCKETS_PACKET packet_socket.c) zephyr_library_sources_ifdef(CONFIG_NET_SOCKETS_CAN canbus_socket.c) -if(CONFIG_NET_TCP_ISN_RFC6528 OR CONFIG_NET_IPV6_PE) +if(CONFIG_NET_TCP_ISN_RFC6528 OR CONFIG_NET_IPV6_PE OR CONFIG_NET_IPV6_IID_STABLE) zephyr_library_link_libraries_ifdef(CONFIG_MBEDTLS mbedTLS) endif() endif() diff --git a/subsys/net/ip/Kconfig.ipv6 b/subsys/net/ip/Kconfig.ipv6 index 33e493d2a237d..286fbf4f195e2 100644 --- a/subsys/net/ip/Kconfig.ipv6 +++ b/subsys/net/ip/Kconfig.ipv6 @@ -174,6 +174,46 @@ config NET_IPV6_RA_RDNSS Support Router Advertisement Recursive DNS Server option. See RFC 6106 for details. The value depends on your network needs. +choice NET_IPV6_IID_GENERATION + prompt "IPv6 Interface Identifier (IID) generation" + default NET_IPV6_IID_EUI_64 + help + Determines how the IPv6 Interface Identifier (IID) is generated. + By default the legacy format using EUI-64 (MAC address) specified in + RFC 4291 chapter 2.5.1 is used. + User can also choose to use stable IID specified in RFC 7217 in which + case a randomized IID is generated for each network interface. + The stable IID enhances privacy by having a different IID for each + network interface. + +config NET_IPV6_IID_EUI_64 + bool "Generate IID using EUI-64" + help + Generate IID from modified EUI-64 a.k.a MAC address. This is the + legacy way described in RFC 4291 chapter 2.5.1 + +config NET_IPV6_IID_STABLE + bool "Generate stable IID [EXPERIMENTAL]" + select MBEDTLS + select MBEDTLS_MD + select EXPERIMENTAL + depends on !NET_6LO + help + Generate a stable IID described in RFC 7217. This option specifies a + method for generating IPv6 Interface Identifiers to be used with + IPv6 Stateless Address Autoconfiguration (SLAAC), such that an IPv6 + address configured using this method is stable within each subnet, + but the corresponding Interface Identifier changes when the host + moves from one network to another. This method is meant to be an + alternative to generating Interface Identifiers based on hardware + addresses (e.g., IEEE LAN Media Access Control (MAC) addresses), + such that the benefits of stable addresses can be achieved without + sacrificing the security and privacy of users. + Currently the stable IID generation is disabled for 6lo networks + because of header compression. + +endchoice + config NET_IPV6_PE bool "Privacy extension (RFC 8981) support [EXPERIMENTAL]" select MBEDTLS diff --git a/subsys/net/ip/ipv6.c b/subsys/net/ip/ipv6.c index 032794a76680d..34e4616f79cb7 100644 --- a/subsys/net/ip/ipv6.c +++ b/subsys/net/ip/ipv6.c @@ -18,6 +18,12 @@ LOG_MODULE_REGISTER(net_ipv6, CONFIG_NET_IPV6_LOG_LEVEL); #include #include + +#if defined(CONFIG_NET_IPV6_IID_STABLE) +#include +#include +#endif /* CONFIG_NET_IPV6_IID_STABLE */ + #include #include #include @@ -815,6 +821,194 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) return NET_DROP; } +#if defined(CONFIG_NET_IPV6_IID_STABLE) +static bool check_reserved(const uint8_t *buf, size_t len) +{ + /* Subnet-Router Anycast (RFC 4291) */ + if (memcmp(buf, (uint8_t *)&(struct in6_addr)IN6ADDR_ANY_INIT, len) == 0) { + return true; + } + + /* Reserved Subnet Anycast Addresses (RFC 2526) + * FDFF:FFFF:FFFF:FF80 - FDFF:FFFF:FFFF:FFFF + */ + if (buf[0] == 0xFD && buf[1] == 0xFF && buf[2] == 0xFF && + buf[3] == 0xFF && buf[4] == 0xFF && buf[5] == 0xFF && + buf[6] == 0xFF && buf[7] >= 0x80) { + return true; + } + + return false; +} +#endif /* CONFIG_NET_IPV6_IID_STABLE */ + +static int gen_stable_iid(uint8_t if_index, + const struct in6_addr *prefix, + uint8_t *network_id, size_t network_id_len, + uint8_t dad_counter, + uint8_t *stable_iid, + size_t stable_iid_len) +{ +#if defined(CONFIG_NET_IPV6_IID_STABLE) + const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + mbedtls_md_context_t ctx; + uint8_t digest[32]; + int ret; + static bool once; + static uint8_t secret_key[16]; /* Min 128 bits, RFC 7217 ch 5 */ + struct { + struct in6_addr prefix; + uint8_t if_index; + uint8_t network_id[16]; + uint8_t dad_counter; + } buf = { + .dad_counter = dad_counter, + }; + + if (prefix == NULL) { + NET_ERR("IPv6 prefix must be set for generating a stable IID"); + return -EINVAL; + } + + memcpy(&buf.prefix, prefix, sizeof(struct in6_addr)); + + buf.if_index = if_index; + + if (network_id != NULL && network_id_len > 0) { + memcpy(buf.network_id, network_id, + MIN(network_id_len, sizeof(buf.network_id))); + } + + if (!once) { + sys_rand_get(&secret_key, sizeof(secret_key)); + once = true; + } + + mbedtls_md_init(&ctx); + mbedtls_md_setup(&ctx, md_info, true); + ret = mbedtls_md_hmac_starts(&ctx, secret_key, sizeof(secret_key)); + if (ret != 0) { + NET_DBG("Cannot %s hmac (%d)", "start", ret); + goto err; + } + + ret = mbedtls_md_hmac_update(&ctx, (uint8_t *)&buf, sizeof(buf)); + if (ret != 0) { + NET_DBG("Cannot %s hmac (%d)", "update", ret); + goto err; + } + + ret = mbedtls_md_hmac_finish(&ctx, digest); + if (ret != 0) { + NET_DBG("Cannot %s hmac (%d)", "finish", ret); + goto err; + } + + memcpy(stable_iid, digest, MIN(sizeof(digest), stable_iid_len)); + + /* Check reserved addresses, RFC 5453 ch 3 */ + if (unlikely(check_reserved(stable_iid, stable_iid_len))) { + LOG_HEXDUMP_DBG(stable_iid, stable_iid_len, + "Generated IID is reserved"); + ret = -EINVAL; + goto err; + } + +err: + mbedtls_md_free(&ctx); + + return ret; +#else + return -ENOTSUP; +#endif +} + +int net_ipv6_addr_generate_iid(struct net_if *iface, + const struct in6_addr *prefix, + uint8_t *network_id, + size_t network_id_len, + uint8_t dad_counter, + struct in6_addr *addr, + struct net_linkaddr *lladdr) +{ + struct in6_addr tmp_addr; + uint8_t if_index; + + if_index = (iface == NULL) ? net_if_get_by_iface(net_if_get_default()) + : net_if_get_by_iface(iface); + + if (IS_ENABLED(CONFIG_NET_IPV6_IID_STABLE)) { + struct in6_addr tmp_prefix = { 0 }; + int ret; + + if (prefix == NULL) { + UNALIGNED_PUT(htonl(0xfe800000), &tmp_prefix.s6_addr32[0]); + } else { + UNALIGNED_PUT(prefix->s6_addr32[0], &tmp_prefix.s6_addr32[0]); + UNALIGNED_PUT(prefix->s6_addr32[1], &tmp_prefix.s6_addr32[1]); + } + + ret = gen_stable_iid(if_index, &tmp_prefix, network_id, network_id_len, + dad_counter, (uint8_t *)&tmp_addr + 8, + sizeof(tmp_addr) / 2); + if (ret < 0) { + return ret; + } + } + + if (prefix == NULL) { + UNALIGNED_PUT(htonl(0xfe800000), &tmp_addr.s6_addr32[0]); + UNALIGNED_PUT(0, &tmp_addr.s6_addr32[1]); + } else { + UNALIGNED_PUT(prefix->s6_addr32[0], &tmp_addr.s6_addr32[0]); + UNALIGNED_PUT(prefix->s6_addr32[1], &tmp_addr.s6_addr32[1]); + } + + if (IS_ENABLED(CONFIG_NET_IPV6_IID_EUI_64)) { + switch (lladdr->len) { + case 2: + /* The generated IPv6 shall not toggle the + * Universal/Local bit. RFC 6282 ch 3.2.2 + */ + if (lladdr->type == NET_LINK_IEEE802154) { + UNALIGNED_PUT(0, &tmp_addr.s6_addr32[2]); + tmp_addr.s6_addr[11] = 0xff; + tmp_addr.s6_addr[12] = 0xfe; + tmp_addr.s6_addr[13] = 0U; + tmp_addr.s6_addr[14] = lladdr->addr[0]; + tmp_addr.s6_addr[15] = lladdr->addr[1]; + } + + break; + case 6: + /* We do not toggle the Universal/Local bit + * in Bluetooth. See RFC 7668 ch 3.2.2 + */ + memcpy(&tmp_addr.s6_addr[8], lladdr->addr, 3); + tmp_addr.s6_addr[11] = 0xff; + tmp_addr.s6_addr[12] = 0xfe; + memcpy(&tmp_addr.s6_addr[13], lladdr->addr + 3, 3); + + if (lladdr->type == NET_LINK_ETHERNET) { + tmp_addr.s6_addr[8] ^= 0x02; + } + + break; + case 8: + memcpy(&tmp_addr.s6_addr[8], lladdr->addr, lladdr->len); + tmp_addr.s6_addr[8] ^= 0x02; + break; + } + } + + NET_DBG("%s IID for iface %d %s", + IS_ENABLED(CONFIG_NET_IPV6_IID_STABLE) ? "Stable" : "EUI-64", + if_index, net_sprint_ipv6_addr(&tmp_addr)); + + memcpy(addr, &tmp_addr, sizeof(*addr)); + return 0; +} + void net_ipv6_init(void) { net_ipv6_nbr_init(); diff --git a/subsys/net/ip/ipv6_nbr.c b/subsys/net/ip/ipv6_nbr.c index 6677df73ae859..8530bd3443453 100644 --- a/subsys/net/ip/ipv6_nbr.c +++ b/subsys/net/ip/ipv6_nbr.c @@ -2246,13 +2246,24 @@ static inline void handle_prefix_autonomous(struct net_pkt *pkt, struct net_if *iface = net_pkt_iface(pkt); struct in6_addr addr = { }; struct net_if_addr *ifaddr; + int ret; - /* 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. + /* Create IPv6 address using the given prefix and iid. */ - net_ipv6_addr_create_iid(&addr, net_if_get_link_addr(iface)); - memcpy(&addr, prefix_info->prefix, sizeof(struct in6_addr) / 2); + ret = net_ipv6_addr_generate_iid(iface, + (struct in6_addr *)prefix_info->prefix, + COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE, + ((uint8_t *)&iface->config.ip.ipv6->network_counter), + (NULL)), + COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE, + (sizeof(iface->config.ip.ipv6->network_counter)), + (0U)), + 0U, + &addr, + net_if_get_link_addr(iface)); + if (ret < 0) { + NET_WARN("IPv6 IID generation issue (%d)", ret); + } ifaddr = net_if_ipv6_addr_lookup(&addr, NULL); if (ifaddr && ifaddr->addr_type == NET_ADDR_AUTOCONF) { diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index f9f7f68b41428..684637e7f9154 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -1342,14 +1342,32 @@ void net_if_start_dad(struct net_if *iface) goto out; } - net_ipv6_addr_create_iid(&addr, net_if_get_link_addr(iface)); + ret = net_ipv6_addr_generate_iid(iface, NULL, + COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE, + ((uint8_t *)&ipv6->network_counter), + (NULL)), + COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE, + (sizeof(ipv6->network_counter)), + (0U)), + COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE, + (ipv6->iid ? ipv6->iid->dad_count : 0U), + (0U)), + &addr, + net_if_get_link_addr(iface)); + if (ret < 0) { + NET_WARN("IPv6 IID generation issue (%d)", ret); + goto out; + } ifaddr = net_if_ipv6_addr_add(iface, &addr, NET_ADDR_AUTOCONF, 0); if (!ifaddr) { NET_ERR("Cannot add %s address to interface %p, DAD fails", net_sprint_ipv6_addr(&addr), iface); + goto out; } + IF_ENABLED(CONFIG_NET_IPV6_IID_STABLE, (ipv6->iid = ifaddr)); + /* Start DAD for all the addresses that were added earlier when * the interface was down. */ @@ -1383,10 +1401,11 @@ void net_if_ipv6_dad_failed(struct net_if *iface, const struct in6_addr *addr) goto out; } - - if (IS_ENABLED(CONFIG_NET_IPV6_PE)) { + if (IS_ENABLED(CONFIG_NET_IPV6_IID_STABLE) || IS_ENABLED(CONFIG_NET_IPV6_PE)) { ifaddr->dad_count++; + } + if (IS_ENABLED(CONFIG_NET_IPV6_PE)) { timeout = COND_CODE_1(CONFIG_NET_IPV6_PE, (ifaddr->addr_timeout), (0)); preferred_lifetime = COND_CODE_1(CONFIG_NET_IPV6_PE, @@ -3251,16 +3270,29 @@ static void iface_ipv6_start(struct net_if *iface) static void iface_ipv6_stop(struct net_if *iface) { - struct in6_addr addr = { }; + struct net_if_ipv6 *ipv6 = iface->config.ip.ipv6; if (!net_if_flag_is_set(iface, NET_IF_IPV6) || net_if_flag_is_set(iface, NET_IF_IPV6_NO_ND)) { return; } - net_ipv6_addr_create_iid(&addr, net_if_get_link_addr(iface)); + if (ipv6 == NULL) { + return; + } - (void)net_if_ipv6_addr_rm(iface, &addr); + IF_ENABLED(CONFIG_NET_IPV6_IID_STABLE, (ipv6->network_counter++)); + IF_ENABLED(CONFIG_NET_IPV6_IID_STABLE, (ipv6->iid = NULL)); + + /* Remove all autoconf addresses */ + ARRAY_FOR_EACH(ipv6->unicast, i) { + if (ipv6->unicast[i].is_used && + ipv6->unicast[i].address.family == AF_INET6 && + ipv6->unicast[i].addr_type == NET_ADDR_AUTOCONF) { + (void)net_if_ipv6_addr_rm(iface, + &ipv6->unicast[i].address.in6_addr); + } + } } static void iface_ipv6_init(int if_count) diff --git a/subsys/net/l2/ethernet/ethernet_mgmt.c b/subsys/net/l2/ethernet/ethernet_mgmt.c index 703dbbccb6de9..93183c3b10dfd 100644 --- a/subsys/net/l2/ethernet/ethernet_mgmt.c +++ b/subsys/net/l2/ethernet/ethernet_mgmt.c @@ -98,7 +98,8 @@ static int ethernet_set_config(uint32_t mgmt_request, * generated from old MAC address, from network interface if * needed. */ - if (IS_ENABLED(CONFIG_NET_NATIVE_IPV6)) { + if (IS_ENABLED(CONFIG_NET_NATIVE_IPV6) && + IS_ENABLED(CONFIG_NET_IPV6_IID_EUI_64)) { struct in6_addr iid; net_ipv6_addr_create_iid(&iid, diff --git a/subsys/net/l2/virtual/ipip/ipip.c b/subsys/net/l2/virtual/ipip/ipip.c index 52e595a7358e3..44b7c51c8c478 100644 --- a/subsys/net/l2/virtual/ipip/ipip.c +++ b/subsys/net/l2/virtual/ipip/ipip.c @@ -449,9 +449,22 @@ static int interface_attach(struct net_if *iface, struct net_if *lower_iface) if (IS_ENABLED(CONFIG_NET_IPV6) && ctx->family == AF_INET6) { struct net_if_addr *ifaddr; struct in6_addr iid; + int ret; /* RFC4213 chapter 3.7 */ - net_ipv6_addr_create_iid(&iid, net_if_get_link_addr(iface)); + ret = net_ipv6_addr_generate_iid(iface, NULL, + COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE, + ((uint8_t *)&iface->config.ip.ipv6->network_counter), + (NULL)), + COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE, + (sizeof(iface->config.ip.ipv6->network_counter)), + (0U)), + 0, + &iid, + net_if_get_link_addr(iface)); + if (ret < 0) { + NET_WARN("IPv6 IID generation issue (%d)", ret); + } ifaddr = net_if_ipv6_addr_add(iface, &iid, NET_ADDR_AUTOCONF, 0); if (!ifaddr) { From c6f9cc9f3197196a8af36df3a05be4f47e268dc8 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Mon, 26 Aug 2024 15:09:38 +0300 Subject: [PATCH 2/4] tests: net: ipv6: Add tests for stable IIDs Add tests that verify that stable IIDs generate a proper IPv6 interface identifier described in RFC 7217. Signed-off-by: Jukka Rissanen --- tests/net/iface/src/main.c | 63 +++++++++++++++++++++++++++++++++++ tests/net/iface/testcase.yaml | 15 ++++++--- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/tests/net/iface/src/main.c b/tests/net/iface/src/main.c index b4928d2d7825d..e47318288646b 100644 --- a/tests/net/iface/src/main.c +++ b/tests/net/iface/src/main.c @@ -63,6 +63,7 @@ static struct net_if *iface1; static struct net_if *iface2; static struct net_if *iface3; static struct net_if *iface4; +static struct net_if *eth_iface; static bool test_failed; static bool test_started; @@ -303,6 +304,7 @@ static void iface_cb(struct net_if *iface, void *user_data) if (api->get_capabilities == eth_fake_api_funcs.get_capabilities) { iface4 = iface; + eth_iface = iface; } } else { switch (if_count) { @@ -1332,4 +1334,65 @@ ZTEST(net_iface, test_interface_name) #endif } +static void generate_iid(struct net_if *iface, + struct in6_addr *expected_addr, + struct in6_addr *iid_addr) +{ + const struct in6_addr prefix = { { { 0x20, 0x01, 0x1b, 0x98, 0x24, 0xb8, 0x7e, 0xbb, + 0, 0, 0, 0, 0, 0, 0, 0 } } }; + struct net_linkaddr *lladdr = net_if_get_link_addr(iface); + uint8_t *mac; + int ret; + + (void)net_iface_get_mac(net_if_get_device(iface)); + + lladdr = net_if_get_link_addr(eth_iface); + mac = lladdr->addr; + + memcpy(expected_addr, &prefix, sizeof(struct in6_addr)); + memcpy(&expected_addr->s6_addr[8], &mac[0], 3); + expected_addr->s6_addr[11] = 0xff; + expected_addr->s6_addr[12] = 0xfe; + memcpy(&expected_addr->s6_addr[13], &mac[3], 3); + + expected_addr->s6_addr[8] ^= 0x02; /* Universal bit toggle */ + + ret = net_ipv6_addr_generate_iid(iface, &prefix, NULL, 0, 0, iid_addr, + net_if_get_link_addr(eth_iface)); + zassert_equal(ret, 0, "Unexpected value (%d) returned", ret); +} + +ZTEST(net_iface, test_ipv6_iid_eui64) +{ +#if defined(CONFIG_NET_IPV6_IID_EUI_64) + struct in6_addr iid_addr = { }; + struct in6_addr expected_addr = { }; + + generate_iid(eth_iface, &expected_addr, &iid_addr); + + zassert_mem_equal(&expected_addr, &iid_addr, sizeof(struct in6_addr)); +#else + ztest_test_skip(); +#endif +} + +ZTEST(net_iface, test_ipv6_iid_stable) +{ +#if defined(CONFIG_NET_IPV6_IID_STABLE) + struct in6_addr iid_addr = { }; + struct in6_addr expected_addr = { }; + + generate_iid(eth_iface, &expected_addr, &iid_addr); + + /* Make sure that EUI-64 bytes are not there */ + zassert_not_equal(iid_addr.s6_addr[11], 0xff); + zassert_not_equal(iid_addr.s6_addr[12], 0xfe); + + zassert_true(memcmp(&expected_addr, &iid_addr, sizeof(struct in6_addr)) != 0, + "IID is EUI-64 instead of randomized"); +#else + ztest_test_skip(); +#endif +} + ZTEST_SUITE(net_iface, NULL, iface_setup, NULL, NULL, iface_teardown); diff --git a/tests/net/iface/testcase.yaml b/tests/net/iface/testcase.yaml index 7ef0785212793..a7953ca7e1849 100644 --- a/tests/net/iface/testcase.yaml +++ b/tests/net/iface/testcase.yaml @@ -1,9 +1,14 @@ common: min_ram: 16 depends_on: netif + tags: + - net + - iface + - userspace tests: - net.iface: - tags: - - net - - iface - - userspace + net.iface.iid.eui64: + extra_configs: + - CONFIG_NET_IPV6_IID_EUI_64=y + net.iface.iid.stable: + extra_configs: + - CONFIG_NET_IPV6_IID_STABLE=y From 29eb49109d558038ac9d812959997e5014f9f80e Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 29 Oct 2024 13:41:40 +0200 Subject: [PATCH 3/4] net: shell: ipv6: Print information about SLAAC addresses Print information in "net ipv6" command how the SLAAC addresses are generated. There is the default legacy EUI-64 method (RFC 4862) or the stable method described in RFC 7217. Signed-off-by: Jukka Rissanen --- subsys/net/lib/shell/ipv6.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/subsys/net/lib/shell/ipv6.c b/subsys/net/lib/shell/ipv6.c index eb65185c03ca1..bf0cf904ba5a8 100644 --- a/subsys/net/lib/shell/ipv6.c +++ b/subsys/net/lib/shell/ipv6.c @@ -188,6 +188,9 @@ static int cmd_net_ipv6(const struct shell *sh, size_t argc, char *argv[]) PR("Privacy extension support : %s\n", IS_ENABLED(CONFIG_NET_IPV6_PE) ? "enabled" : "disabled"); + PR("SLAAC IID generation method : %s\n", + IS_ENABLED(CONFIG_NET_IPV6_IID_STABLE) ? + "stable (RFC 7217)" : "EUI-64 (RFC 4862)"); #if defined(CONFIG_NET_IPV6_PE) PR("Max number of IPv6 privacy extension filters " From d6d994e4982f73438ce1f8fa6d260f52d1589b68 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 30 Oct 2024 11:17:14 +0200 Subject: [PATCH 4/4] tests: net: conn_mgr_monitor: Increase the sleep between states The short sleep (10ms) was not enough any more. Increase the sleep to 100ms so that system stabilizes between checks. Signed-off-by: Jukka Rissanen --- tests/net/conn_mgr_monitor/src/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/net/conn_mgr_monitor/src/main.c b/tests/net/conn_mgr_monitor/src/main.c index 522a9d50b9219..cd8242c10d157 100644 --- a/tests/net/conn_mgr_monitor/src/main.c +++ b/tests/net/conn_mgr_monitor/src/main.c @@ -24,8 +24,9 @@ #include /* Time to wait for NET_MGMT events to finish firing */ -#define EVENT_WAIT_TIME_SHORT K_MSEC(10) -#define EVENT_WAIT_TIME K_MSEC(200) +#define EVENT_WAIT_TIME_SHORT K_MSEC(10) +#define EVENT_WAIT_TIME_MEDIUM K_MSEC(100) +#define EVENT_WAIT_TIME K_MSEC(200) /* Time to wait for IPv6 DAD-gated events to finish. @@ -622,7 +623,7 @@ static void cycle_iface_states(struct net_if *iface, enum ip_order ifa_ipm) zassert_equal(net_if_up(iface), 0, "net_if_up should succeed."); /* Verify that no events have been fired yet */ - k_sleep(EVENT_WAIT_TIME_SHORT); + k_sleep(EVENT_WAIT_TIME_MEDIUM); stats = get_reset_stats(); zassert_equal(stats.event_count_gen, 0, "No events should be fired if connectivity availability did not change."); @@ -653,7 +654,6 @@ static void cycle_iface_states(struct net_if *iface, enum ip_order ifa_ipm) zassert_equal(stats.conn_iface_gen, iface, "The test iface should be blamed."); zassert_equal(stats.conn_iface_ipv4, iface, "The test iface should be blamed."); - /* Add IPv6 */ net_if_ipv6_addr_add(iface, &test_ipv6_a, NET_ADDR_MANUAL, 0);