summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2017-04-27 12:38:40 +0200
committerThomas Haller <thaller@redhat.com>2017-04-27 16:32:33 +0200
commitf15c4961ad02a1edf766f140dd94e2d5449f8d82 (patch)
treef42363257f419349ac4cc9a751247b018d9dbc64
parent67da0a28db834192d207fb315a3ba1983fe4a79e (diff)
downloadNetworkManager-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.c39
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);