Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions include/zephyr/net/net_if.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
65 changes: 27 additions & 38 deletions include/zephyr/net/net_ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -1436,51 +1436,40 @@
}

/**
* @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);

Check notice on line 1462 in include/zephyr/net/net_ip.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

include/zephyr/net/net_ip.h:1462 -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); +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
*/
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);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion subsys/net/ip/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
40 changes: 40 additions & 0 deletions subsys/net/ip/Kconfig.ipv6
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
194 changes: 194 additions & 0 deletions subsys/net/ip/ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@

#include <errno.h>
#include <stdlib.h>

#if defined(CONFIG_NET_IPV6_IID_STABLE)
#include <zephyr/random/random.h>
#include <mbedtls/md.h>
#endif /* CONFIG_NET_IPV6_IID_STABLE */

#include <zephyr/net/net_core.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/net_stats.h>
Expand Down Expand Up @@ -815,6 +821,194 @@
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;

Check notice on line 838 in subsys/net/ip/ipv6.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/net/ip/ipv6.c:838 - 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) { + 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 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)

Check notice on line 850 in subsys/net/ip/ipv6.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/net/ip/ipv6.c:850 -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, +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,
{
#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)));
}

Check notice on line 880 in subsys/net/ip/ipv6.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/net/ip/ipv6.c:880 - memcpy(buf.network_id, network_id, - MIN(network_id_len, sizeof(buf.network_id))); + 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;

Check notice on line 913 in subsys/net/ip/ipv6.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/net/ip/ipv6.c:913 - LOG_HEXDUMP_DBG(stable_iid, stable_iid_len, - "Generated IID is reserved"); + LOG_HEXDUMP_DBG(stable_iid, stable_iid_len, "Generated IID is reserved");
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;

Check notice on line 942 in subsys/net/ip/ipv6.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/net/ip/ipv6.c:942 -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) +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); + : net_if_get_by_iface(iface); if (IS_ENABLED(CONFIG_NET_IPV6_IID_STABLE)) { - struct in6_addr tmp_prefix = { 0 }; + struct in6_addr tmp_prefix = {0};

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

Check notice on line 954 in subsys/net/ip/ipv6.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/net/ip/ipv6.c:954 - ret = gen_stable_iid(if_index, &tmp_prefix, network_id, network_id_len, - dad_counter, (uint8_t *)&tmp_addr + 8, - sizeof(tmp_addr) / 2); + ret = gen_stable_iid(if_index, &tmp_prefix, network_id, network_id_len, dad_counter, + (uint8_t *)&tmp_addr + 8, sizeof(tmp_addr) / 2);
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));

Check notice on line 1007 in subsys/net/ip/ipv6.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/net/ip/ipv6.c:1007 - IS_ENABLED(CONFIG_NET_IPV6_IID_STABLE) ? "Stable" : "EUI-64", - if_index, net_sprint_ipv6_addr(&tmp_addr)); + 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();
Expand Down
21 changes: 16 additions & 5 deletions subsys/net/ip/ipv6_nbr.c
Original file line number Diff line number Diff line change
Expand Up @@ -2246,13 +2246,24 @@
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) {

Check notice on line 2264 in subsys/net/ip/ipv6_nbr.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/net/ip/ipv6_nbr.c:2264 - ret = net_ipv6_addr_generate_iid(iface, - (struct in6_addr *)prefix_info->prefix, - COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE, + 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, + COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE, (sizeof(iface->config.ip.ipv6->network_counter)), - (0U)), - 0U, - &addr, - net_if_get_link_addr(iface)); + (0U)), 0U, + &addr, net_if_get_link_addr(iface));
NET_WARN("IPv6 IID generation issue (%d)", ret);
}

ifaddr = net_if_ipv6_addr_lookup(&addr, NULL);
if (ifaddr && ifaddr->addr_type == NET_ADDR_AUTOCONF) {
Expand Down
Loading
Loading