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) { 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 " 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); 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