diff options
author | Thomas Haller <thaller@redhat.com> | 2017-04-27 12:38:40 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2017-04-27 16:32:33 +0200 |
commit | f15c4961ad02a1edf766f140dd94e2d5449f8d82 (patch) | |
tree | f42363257f419349ac4cc9a751247b018d9dbc64 | |
parent | 67da0a28db834192d207fb315a3ba1983fe4a79e (diff) | |
download | NetworkManager-f15c4961ad02a1edf766f140dd94e2d5449f8d82.tar.gz |
core: avoid generating reserved IPv6 interface identifiers
https://tools.ietf.org/html/rfc7217 says:
The resulting Interface Identifier SHOULD be compared against the
reserved IPv6 Interface Identifiers [RFC5453] [IANA-RESERVED-IID]
and against those Interface Identifiers already employed in an
address of the same network interface and the same network
prefix. In the event that an unacceptable identifier has been
generated, this situation SHOULD be handled in the same way as
the case of duplicate addresses (see Section 6).
In case of conflict, this suggests to create a new address incrementing
the DAD counter, etc. Don't do that. If we generate an address of the
reserved region, just rehash it right away. Note that the actual address
anyway appears random, so this re-hashing is just as good as incrementing
the DAD counter and going through the entire process again.
Note that now we no longer generate certain addresses like we did
previously. But realize that we now merely reject (1 + 16777216 + 128)
addresses out of 2^64. So, the likelyhood of of a user accidentally
generating an address that is suddenly rejected is in the order of
10e-13 (1 / 1,099,503,173,697). Which is not astronomically, but still
extreeeemely unlikely.
Also, the whole process is anyway build on the idea that somebody else
might generate conflicting addresses (DAD). It means, there was always
the extremely tiny chance that the address you generated last time is
suddenly taken by somebody else. So, this change appears to a user
like these reserved addresses are now claimed by another (non existing)
host and a different address gets generated -- business as usual, as
far as SLAAC is concerned.
-rw-r--r-- | src/nm-core-utils.c | 39 |
1 files changed, 37 insertions, 2 deletions
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 96228a9dce..01bb9c1d44 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -3549,6 +3549,31 @@ nm_utils_stable_id_parse (const char *stable_id, /*****************************************************************************/ static gboolean +_is_reserved_ipv6_iid (const guint8 *iid) +{ + /* https://tools.ietf.org/html/rfc5453 */ + /* https://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xml */ + + /* 0000:0000:0000:0000 (Subnet-Router Anycast [RFC4291]) */ + if (memcmp (iid, &nm_ip_addr_zero.addr6.s6_addr[8], 8) == 0) + return TRUE; + + /* 0200:5EFF:FE00:0000 - 0200:5EFF:FE00:5212 (Reserved IPv6 Interface Identifiers corresponding to the IANA Ethernet Block [RFC4291]) + * 0200:5EFF:FE00:5213 (Proxy Mobile IPv6 [RFC6543]) + * 0200:5EFF:FE00:5214 - 0200:5EFF:FEFF:FFFF (Reserved IPv6 Interface Identifiers corresponding to the IANA Ethernet Block [RFC4291]) */ + if (memcmp (iid, (const guint8[]) { 0x02, 0x00, 0x5E, 0xFF, 0xFE }, 5) == 0) + return TRUE; + + /* FDFF:FFFF:FFFF:FF80 - FDFF:FFFF:FFFF:FFFF (Reserved Subnet Anycast Addresses [RFC2526]) */ + if (memcmp (iid, (const guint8[]) { 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 7) == 0) { + if (iid[7] & 0x80) + return TRUE; + } + + return FALSE; +} + +static gboolean _set_stable_privacy (NMUtilsStableType stable_type, struct in6_addr *addr, const char *ifname, @@ -3604,9 +3629,19 @@ _set_stable_privacy (NMUtilsStableType stable_type, g_checksum_update (sum, (const guchar *) secret_key, key_len); g_checksum_get_digest (sum, digest, &len); - g_checksum_free (sum); - g_return_val_if_fail (len == 32, FALSE); + nm_assert (len == sizeof (digest)); + + while (_is_reserved_ipv6_iid (digest)) { + g_checksum_reset (sum); + tmp[0] = htonl (++dad_counter); + g_checksum_update (sum, digest, len); + g_checksum_update (sum, (const guchar *) &tmp[0], sizeof (tmp[0])); + g_checksum_get_digest (sum, digest, &len); + nm_assert (len == sizeof (digest)); + } + + g_checksum_free (sum); memcpy (addr->s6_addr + 8, &digest[0], 8); |