Skip to content

Commit 94177a2

Browse files
jukkarnashif
authored andcommitted
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 <[email protected]>
1 parent c9f3690 commit 94177a2

File tree

9 files changed

+342
-52
lines changed

9 files changed

+342
-52
lines changed

include/zephyr/net/net_if.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,16 @@ struct net_if_ipv6 {
331331
/** Retransmit timer (RFC 4861, page 52) */
332332
uint32_t retrans_timer;
333333

334+
#if defined(CONFIG_NET_IPV6_IID_STABLE)
335+
/** IID (Interface Identifier) pointer used for link local address */
336+
struct net_if_addr *iid;
337+
338+
/** Incremented when network interface goes down so that we can
339+
* generate new stable addresses when interface comes back up.
340+
*/
341+
uint32_t network_counter;
342+
#endif /* CONFIG_NET_IPV6_IID_STABLE */
343+
334344
#if defined(CONFIG_NET_IPV6_PE)
335345
/** Privacy extension DESYNC_FACTOR value from RFC 8981 ch 3.4.
336346
* "DESYNC_FACTOR is a random value within the range 0 - MAX_DESYNC_FACTOR.

include/zephyr/net/net_ip.h

Lines changed: 27 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,51 +1439,40 @@ static inline bool net_ipv6_addr_is_v4_mapped(const struct in6_addr *addr)
14391439
}
14401440

14411441
/**
1442-
* @brief Create IPv6 address interface identifier
1442+
* @brief Generate IPv6 address using a prefix and interface identifier.
1443+
* Interface identifier is either generated from EUI-64 (MAC) defined
1444+
* in RFC 4291 or from randomized value defined in RFC 7217.
1445+
*
1446+
* @param iface Network interface
1447+
* @param prefix IPv6 prefix, can be left out in which case fe80::/64 is used
1448+
* @param network_id Network identifier (for example SSID in WLAN), this is
1449+
* optional can be set to NULL
1450+
* @param network_id_len Network identifier length, if set to 0 then the
1451+
* network id is ignored.
1452+
* @param dad_counter Duplicate Address Detection counter value, can be set to 0
1453+
* if it is not known.
1454+
* @param addr IPv6 address
1455+
* @param lladdr Link local address
1456+
*
1457+
* @return 0 if ok, < 0 if error
1458+
*/
1459+
int net_ipv6_addr_generate_iid(struct net_if *iface,
1460+
const struct in6_addr *prefix,
1461+
uint8_t *network_id, size_t network_id_len,
1462+
uint8_t dad_counter,
1463+
struct in6_addr *addr,
1464+
struct net_linkaddr *lladdr);
1465+
1466+
/**
1467+
* @brief Create IPv6 address interface identifier.
14431468
*
14441469
* @param addr IPv6 address
14451470
* @param lladdr Link local address
14461471
*/
14471472
static inline void net_ipv6_addr_create_iid(struct in6_addr *addr,
14481473
struct net_linkaddr *lladdr)
14491474
{
1450-
UNALIGNED_PUT(htonl(0xfe800000), &addr->s6_addr32[0]);
1451-
UNALIGNED_PUT(0, &addr->s6_addr32[1]);
1452-
1453-
switch (lladdr->len) {
1454-
case 2:
1455-
/* The generated IPv6 shall not toggle the
1456-
* Universal/Local bit. RFC 6282 ch 3.2.2
1457-
*/
1458-
if (lladdr->type == NET_LINK_IEEE802154) {
1459-
UNALIGNED_PUT(0, &addr->s6_addr32[2]);
1460-
addr->s6_addr[11] = 0xff;
1461-
addr->s6_addr[12] = 0xfe;
1462-
addr->s6_addr[13] = 0U;
1463-
addr->s6_addr[14] = lladdr->addr[0];
1464-
addr->s6_addr[15] = lladdr->addr[1];
1465-
}
1466-
1467-
break;
1468-
case 6:
1469-
/* We do not toggle the Universal/Local bit
1470-
* in Bluetooth. See RFC 7668 ch 3.2.2
1471-
*/
1472-
memcpy(&addr->s6_addr[8], lladdr->addr, 3);
1473-
addr->s6_addr[11] = 0xff;
1474-
addr->s6_addr[12] = 0xfe;
1475-
memcpy(&addr->s6_addr[13], lladdr->addr + 3, 3);
1476-
1477-
if (lladdr->type == NET_LINK_ETHERNET) {
1478-
addr->s6_addr[8] ^= 0x02;
1479-
}
1480-
1481-
break;
1482-
case 8:
1483-
memcpy(&addr->s6_addr[8], lladdr->addr, lladdr->len);
1484-
addr->s6_addr[8] ^= 0x02;
1485-
break;
1486-
}
1475+
(void)net_ipv6_addr_generate_iid(NULL, NULL, NULL, 0, 0, addr, lladdr);
14871476
}
14881477

14891478
/**

subsys/net/ip/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ zephyr_library_sources_ifdef(CONFIG_NET_CONNECTION_SOCKETS connection.c)
5454
zephyr_library_sources_ifdef(CONFIG_NET_SOCKETS_PACKET packet_socket.c)
5555
zephyr_library_sources_ifdef(CONFIG_NET_SOCKETS_CAN canbus_socket.c)
5656

57-
if(CONFIG_NET_TCP_ISN_RFC6528 OR CONFIG_NET_IPV6_PE)
57+
if(CONFIG_NET_TCP_ISN_RFC6528 OR CONFIG_NET_IPV6_PE OR CONFIG_NET_IPV6_IID_STABLE)
5858
zephyr_library_link_libraries_ifdef(CONFIG_MBEDTLS mbedTLS)
5959
endif()
6060
endif()

subsys/net/ip/Kconfig.ipv6

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,46 @@ config NET_IPV6_RA_RDNSS
174174
Support Router Advertisement Recursive DNS Server option.
175175
See RFC 6106 for details. The value depends on your network needs.
176176

177+
choice NET_IPV6_IID_GENERATION
178+
prompt "IPv6 Interface Identifier (IID) generation"
179+
default NET_IPV6_IID_EUI_64
180+
help
181+
Determines how the IPv6 Interface Identifier (IID) is generated.
182+
By default the legacy format using EUI-64 (MAC address) specified in
183+
RFC 4291 chapter 2.5.1 is used.
184+
User can also choose to use stable IID specified in RFC 7217 in which
185+
case a randomized IID is generated for each network interface.
186+
The stable IID enhances privacy by having a different IID for each
187+
network interface.
188+
189+
config NET_IPV6_IID_EUI_64
190+
bool "Generate IID using EUI-64"
191+
help
192+
Generate IID from modified EUI-64 a.k.a MAC address. This is the
193+
legacy way described in RFC 4291 chapter 2.5.1
194+
195+
config NET_IPV6_IID_STABLE
196+
bool "Generate stable IID [EXPERIMENTAL]"
197+
select MBEDTLS
198+
select MBEDTLS_MD
199+
select EXPERIMENTAL
200+
depends on !NET_6LO
201+
help
202+
Generate a stable IID described in RFC 7217. This option specifies a
203+
method for generating IPv6 Interface Identifiers to be used with
204+
IPv6 Stateless Address Autoconfiguration (SLAAC), such that an IPv6
205+
address configured using this method is stable within each subnet,
206+
but the corresponding Interface Identifier changes when the host
207+
moves from one network to another. This method is meant to be an
208+
alternative to generating Interface Identifiers based on hardware
209+
addresses (e.g., IEEE LAN Media Access Control (MAC) addresses),
210+
such that the benefits of stable addresses can be achieved without
211+
sacrificing the security and privacy of users.
212+
Currently the stable IID generation is disabled for 6lo networks
213+
because of header compression.
214+
215+
endchoice
216+
177217
config NET_IPV6_PE
178218
bool "Privacy extension (RFC 8981) support [EXPERIMENTAL]"
179219
select MBEDTLS

subsys/net/ip/ipv6.c

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ LOG_MODULE_REGISTER(net_ipv6, CONFIG_NET_IPV6_LOG_LEVEL);
1818

1919
#include <errno.h>
2020
#include <stdlib.h>
21+
22+
#if defined(CONFIG_NET_IPV6_IID_STABLE)
23+
#include <zephyr/random/random.h>
24+
#include <mbedtls/md.h>
25+
#endif /* CONFIG_NET_IPV6_IID_STABLE */
26+
2127
#include <zephyr/net/net_core.h>
2228
#include <zephyr/net/net_pkt.h>
2329
#include <zephyr/net/net_stats.h>
@@ -815,6 +821,194 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback)
815821
return NET_DROP;
816822
}
817823

824+
#if defined(CONFIG_NET_IPV6_IID_STABLE)
825+
static bool check_reserved(const uint8_t *buf, size_t len)
826+
{
827+
/* Subnet-Router Anycast (RFC 4291) */
828+
if (memcmp(buf, (uint8_t *)&(struct in6_addr)IN6ADDR_ANY_INIT, len) == 0) {
829+
return true;
830+
}
831+
832+
/* Reserved Subnet Anycast Addresses (RFC 2526)
833+
* FDFF:FFFF:FFFF:FF80 - FDFF:FFFF:FFFF:FFFF
834+
*/
835+
if (buf[0] == 0xFD && buf[1] == 0xFF && buf[2] == 0xFF &&
836+
buf[3] == 0xFF && buf[4] == 0xFF && buf[5] == 0xFF &&
837+
buf[6] == 0xFF && buf[7] >= 0x80) {
838+
return true;
839+
}
840+
841+
return false;
842+
}
843+
#endif /* CONFIG_NET_IPV6_IID_STABLE */
844+
845+
static int gen_stable_iid(uint8_t if_index,
846+
const struct in6_addr *prefix,
847+
uint8_t *network_id, size_t network_id_len,
848+
uint8_t dad_counter,
849+
uint8_t *stable_iid,
850+
size_t stable_iid_len)
851+
{
852+
#if defined(CONFIG_NET_IPV6_IID_STABLE)
853+
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
854+
mbedtls_md_context_t ctx;
855+
uint8_t digest[32];
856+
int ret;
857+
static bool once;
858+
static uint8_t secret_key[16]; /* Min 128 bits, RFC 7217 ch 5 */
859+
struct {
860+
struct in6_addr prefix;
861+
uint8_t if_index;
862+
uint8_t network_id[16];
863+
uint8_t dad_counter;
864+
} buf = {
865+
.dad_counter = dad_counter,
866+
};
867+
868+
if (prefix == NULL) {
869+
NET_ERR("IPv6 prefix must be set for generating a stable IID");
870+
return -EINVAL;
871+
}
872+
873+
memcpy(&buf.prefix, prefix, sizeof(struct in6_addr));
874+
875+
buf.if_index = if_index;
876+
877+
if (network_id != NULL && network_id_len > 0) {
878+
memcpy(buf.network_id, network_id,
879+
MIN(network_id_len, sizeof(buf.network_id)));
880+
}
881+
882+
if (!once) {
883+
sys_rand_get(&secret_key, sizeof(secret_key));
884+
once = true;
885+
}
886+
887+
mbedtls_md_init(&ctx);
888+
mbedtls_md_setup(&ctx, md_info, true);
889+
ret = mbedtls_md_hmac_starts(&ctx, secret_key, sizeof(secret_key));
890+
if (ret != 0) {
891+
NET_DBG("Cannot %s hmac (%d)", "start", ret);
892+
goto err;
893+
}
894+
895+
ret = mbedtls_md_hmac_update(&ctx, (uint8_t *)&buf, sizeof(buf));
896+
if (ret != 0) {
897+
NET_DBG("Cannot %s hmac (%d)", "update", ret);
898+
goto err;
899+
}
900+
901+
ret = mbedtls_md_hmac_finish(&ctx, digest);
902+
if (ret != 0) {
903+
NET_DBG("Cannot %s hmac (%d)", "finish", ret);
904+
goto err;
905+
}
906+
907+
memcpy(stable_iid, digest, MIN(sizeof(digest), stable_iid_len));
908+
909+
/* Check reserved addresses, RFC 5453 ch 3 */
910+
if (unlikely(check_reserved(stable_iid, stable_iid_len))) {
911+
LOG_HEXDUMP_DBG(stable_iid, stable_iid_len,
912+
"Generated IID is reserved");
913+
ret = -EINVAL;
914+
goto err;
915+
}
916+
917+
err:
918+
mbedtls_md_free(&ctx);
919+
920+
return ret;
921+
#else
922+
return -ENOTSUP;
923+
#endif
924+
}
925+
926+
int net_ipv6_addr_generate_iid(struct net_if *iface,
927+
const struct in6_addr *prefix,
928+
uint8_t *network_id,
929+
size_t network_id_len,
930+
uint8_t dad_counter,
931+
struct in6_addr *addr,
932+
struct net_linkaddr *lladdr)
933+
{
934+
struct in6_addr tmp_addr;
935+
uint8_t if_index;
936+
937+
if_index = (iface == NULL) ? net_if_get_by_iface(net_if_get_default())
938+
: net_if_get_by_iface(iface);
939+
940+
if (IS_ENABLED(CONFIG_NET_IPV6_IID_STABLE)) {
941+
struct in6_addr tmp_prefix = { 0 };
942+
int ret;
943+
944+
if (prefix == NULL) {
945+
UNALIGNED_PUT(htonl(0xfe800000), &tmp_prefix.s6_addr32[0]);
946+
} else {
947+
UNALIGNED_PUT(prefix->s6_addr32[0], &tmp_prefix.s6_addr32[0]);
948+
UNALIGNED_PUT(prefix->s6_addr32[1], &tmp_prefix.s6_addr32[1]);
949+
}
950+
951+
ret = gen_stable_iid(if_index, &tmp_prefix, network_id, network_id_len,
952+
dad_counter, (uint8_t *)&tmp_addr + 8,
953+
sizeof(tmp_addr) / 2);
954+
if (ret < 0) {
955+
return ret;
956+
}
957+
}
958+
959+
if (prefix == NULL) {
960+
UNALIGNED_PUT(htonl(0xfe800000), &tmp_addr.s6_addr32[0]);
961+
UNALIGNED_PUT(0, &tmp_addr.s6_addr32[1]);
962+
} else {
963+
UNALIGNED_PUT(prefix->s6_addr32[0], &tmp_addr.s6_addr32[0]);
964+
UNALIGNED_PUT(prefix->s6_addr32[1], &tmp_addr.s6_addr32[1]);
965+
}
966+
967+
if (IS_ENABLED(CONFIG_NET_IPV6_IID_EUI_64)) {
968+
switch (lladdr->len) {
969+
case 2:
970+
/* The generated IPv6 shall not toggle the
971+
* Universal/Local bit. RFC 6282 ch 3.2.2
972+
*/
973+
if (lladdr->type == NET_LINK_IEEE802154) {
974+
UNALIGNED_PUT(0, &tmp_addr.s6_addr32[2]);
975+
tmp_addr.s6_addr[11] = 0xff;
976+
tmp_addr.s6_addr[12] = 0xfe;
977+
tmp_addr.s6_addr[13] = 0U;
978+
tmp_addr.s6_addr[14] = lladdr->addr[0];
979+
tmp_addr.s6_addr[15] = lladdr->addr[1];
980+
}
981+
982+
break;
983+
case 6:
984+
/* We do not toggle the Universal/Local bit
985+
* in Bluetooth. See RFC 7668 ch 3.2.2
986+
*/
987+
memcpy(&tmp_addr.s6_addr[8], lladdr->addr, 3);
988+
tmp_addr.s6_addr[11] = 0xff;
989+
tmp_addr.s6_addr[12] = 0xfe;
990+
memcpy(&tmp_addr.s6_addr[13], lladdr->addr + 3, 3);
991+
992+
if (lladdr->type == NET_LINK_ETHERNET) {
993+
tmp_addr.s6_addr[8] ^= 0x02;
994+
}
995+
996+
break;
997+
case 8:
998+
memcpy(&tmp_addr.s6_addr[8], lladdr->addr, lladdr->len);
999+
tmp_addr.s6_addr[8] ^= 0x02;
1000+
break;
1001+
}
1002+
}
1003+
1004+
NET_DBG("%s IID for iface %d %s",
1005+
IS_ENABLED(CONFIG_NET_IPV6_IID_STABLE) ? "Stable" : "EUI-64",
1006+
if_index, net_sprint_ipv6_addr(&tmp_addr));
1007+
1008+
memcpy(addr, &tmp_addr, sizeof(*addr));
1009+
return 0;
1010+
}
1011+
8181012
void net_ipv6_init(void)
8191013
{
8201014
net_ipv6_nbr_init();

subsys/net/ip/ipv6_nbr.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2237,13 +2237,24 @@ static inline void handle_prefix_autonomous(struct net_pkt *pkt,
22372237
struct net_if *iface = net_pkt_iface(pkt);
22382238
struct in6_addr addr = { };
22392239
struct net_if_addr *ifaddr;
2240+
int ret;
22402241

2241-
/* Create IPv6 address using the given prefix and iid. We first
2242-
* setup link local address, and then copy prefix over first 8
2243-
* bytes of that address.
2242+
/* Create IPv6 address using the given prefix and iid.
22442243
*/
2245-
net_ipv6_addr_create_iid(&addr, net_if_get_link_addr(iface));
2246-
memcpy(&addr, prefix_info->prefix, sizeof(struct in6_addr) / 2);
2244+
ret = net_ipv6_addr_generate_iid(iface,
2245+
(struct in6_addr *)prefix_info->prefix,
2246+
COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE,
2247+
((uint8_t *)&iface->config.ip.ipv6->network_counter),
2248+
(NULL)),
2249+
COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE,
2250+
(sizeof(iface->config.ip.ipv6->network_counter)),
2251+
(0U)),
2252+
0U,
2253+
&addr,
2254+
net_if_get_link_addr(iface));
2255+
if (ret < 0) {
2256+
NET_WARN("IPv6 IID generation issue (%d)", ret);
2257+
}
22472258

22482259
ifaddr = net_if_ipv6_addr_lookup(&addr, NULL);
22492260
if (ifaddr && ifaddr->addr_type == NET_ADDR_AUTOCONF) {

0 commit comments

Comments
 (0)