From 06b30ad690942bd421ddcfc4e55834174ec708f6 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 27 Sep 2014 09:03:17 +0200 Subject: [PATCH 1/6] core: Add NMUtilsIPv6IfaceId to nm-types.h We'd like to use it in nm-platform.h, but it's included by NetworkManagerUtils.h before the declaration occurs. (cherry picked from commit 0d59984ce837581ff78ce747d1fcf6e393b6beb5) --- src/NetworkManagerUtils.h | 4 ++-- src/nm-types.h | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index d687c6a391..2a1101da92 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -192,12 +192,12 @@ gboolean nm_utils_is_specific_hostname (const char *name); * and should not normally be treated as a %guint64, but this is done for * convenience of validity checking and initialization. */ -typedef struct { +struct _NMUtilsIPv6IfaceId { union { guint64 id; guint8 id_u8[8]; }; -} NMUtilsIPv6IfaceId; +}; #define NM_UTILS_IPV6_IFACE_ID_INIT { .id = 0 } diff --git a/src/nm-types.h b/src/nm-types.h index 8d613a22cc..7229eba8b0 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -113,4 +113,7 @@ typedef struct _NMSecretAgent NMSecretAgent; typedef struct _NMSettings NMSettings; typedef struct _NMSettingsConnection NMSettingsConnection; +/* utils */ +typedef struct _NMUtilsIPv6IfaceId NMUtilsIPv6IfaceId; + #endif /* NM_TYPES_H */ From 3cbd7cdc03284712007327476b2e33a4e5c1612c Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 27 Sep 2014 09:03:56 +0200 Subject: [PATCH 2/6] platform: Add support for IPv6 tokenized identifiers (cherry picked from commit db5603e615f502e2fc96040e7f6b83a13fcb0f98) --- src/platform/nm-platform.c | 25 +++++++++++++++++++++++++ src/platform/nm-platform.h | 4 ++++ 2 files changed, 29 insertions(+) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 725fb2f347..60f35398ca 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -779,6 +779,31 @@ nm_platform_link_uses_arp (int ifindex) return klass->link_uses_arp (platform, ifindex); } +/** + * nm_platform_link_get_ipv6_token: + * @ifindex: Interface index + * @iid: Tokenized interface identifier + * + * Returns IPv6 tokenized interface identifier. If the platform or OS doesn't + * support IPv6 tokenized interface identifiers, or the token is not set + * this call will fail and return %FALSE. + * + * Returns: %TRUE a tokenized identifier was available + */ +gboolean +nm_platform_link_get_ipv6_token (int ifindex, NMUtilsIPv6IfaceId *iid) +{ + reset_error (); + + g_return_val_if_fail (ifindex >= 0, FALSE); + g_return_val_if_fail (iid, FALSE); + + if (klass->link_get_ipv6_token) + return klass->link_get_ipv6_token (platform, ifindex, iid); + return FALSE; +} + + /** * nm_platform_link_get_user_ip6vll_enabled: * @ifindex: Interface index diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index a49e0f11ba..76773afc22 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -379,6 +379,8 @@ typedef struct { gboolean (*link_is_connected) (NMPlatform *, int ifindex); gboolean (*link_uses_arp) (NMPlatform *, int ifindex); + gboolean (*link_get_ipv6_token) (NMPlatform *, int ifindex, NMUtilsIPv6IfaceId *iid); + gboolean (*link_get_user_ipv6ll_enabled) (NMPlatform *, int ifindex); gboolean (*link_set_user_ipv6ll_enabled) (NMPlatform *, int ifindex, gboolean enabled); @@ -528,6 +530,8 @@ gboolean nm_platform_link_is_up (int ifindex); gboolean nm_platform_link_is_connected (int ifindex); gboolean nm_platform_link_uses_arp (int ifindex); +gboolean nm_platform_link_get_ipv6_token (int ifindex, NMUtilsIPv6IfaceId *iid); + gboolean nm_platform_link_get_user_ipv6ll_enabled (int ifindex); gboolean nm_platform_link_set_user_ipv6ll_enabled (int ifindex, gboolean enabled); From 4d7e2a1a68f6dbe7c9ab2b9c75f846f34174ddb5 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 27 Sep 2014 09:01:08 +0200 Subject: [PATCH 3/6] build: Check for tokenized identifier support in libnl-route-3 (cherry picked from commit b47d55b5004c4028b27b9153eb4e3bab1b8b82b5) --- configure.ac | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/configure.ac b/configure.ac index 0880134711..8cd0b4207a 100644 --- a/configure.ac +++ b/configure.ac @@ -428,6 +428,13 @@ else AC_DEFINE(HAVE_KERNEL_INET6_ADDR_GEN_MODE, 0, [Define if the kernel has IN6_ADDR_GEN_MODE_*]) fi +# IPv6 tokenized identifiers support in libnl +AC_CHECK_LIB([nl-route-3], [rtnl_link_inet6_get_token], + ac_have_ipv6_token="1", + ac_have_ipv6_token="0") +AC_DEFINE_UNQUOTED(HAVE_LIBNL_INET6_TOKEN, + $ac_have_ipv6_token, [Define if libnl has rtnl_link_inet6_get_token()]) + # uuid library PKG_CHECK_MODULES(UUID, uuid) AC_SUBST(UUID_CFLAGS) From c3c927d1d13d6b2a5bfc9732370dbe6bc4fbebdb Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 29 Sep 2014 17:58:44 +0200 Subject: [PATCH 4/6] platform: refresh link cache when IPv6 tokenized identifier changes (cherry picked from commit 954a4b69b83bf71397cbb27dc742b0244e478bca) --- src/platform/nm-linux-platform.c | 35 ++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 98cd7cc953..22e5102f67 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -44,9 +44,9 @@ #include #include -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE || HAVE_LIBNL_INET6_TOKEN #include -#if HAVE_KERNEL_INET6_ADDR_GEN_MODE +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE && HAVE_KERNEL_INET6_ADDR_GEN_MODE #include #else #define IN6_ADDR_GEN_MODE_EUI64 0 @@ -1905,6 +1905,37 @@ nm_nl_object_diff (ObjectType type, struct nl_object *_a, struct nl_object *_b) return TRUE; } +#if HAVE_LIBNL_INET6_TOKEN + /* libnl ignores PROTINFO changes in object without AF assigned */ + if (type == OBJECT_TYPE_LINK) { + struct rtnl_addr *a = (struct rtnl_addr *) _a; + struct rtnl_addr *b = (struct rtnl_addr *) _b; + auto_nl_addr struct nl_addr *token_a = NULL; + auto_nl_addr struct nl_addr *token_b = NULL; + + if (rtnl_link_inet6_get_token ((struct rtnl_link *) a, &token_a) != 0) + token_a = NULL; + if (rtnl_link_inet6_get_token ((struct rtnl_link *) b, &token_b) != 0) + token_b = NULL; + + if (token_a && token_b) { + if (nl_addr_get_family (token_a) == AF_INET6 && + nl_addr_get_family (token_b) == AF_INET6 && + nl_addr_get_len (token_a) == sizeof (struct in6_addr) && + nl_addr_get_len (token_b) == sizeof (struct in6_addr) && + memcmp (nl_addr_get_binary_addr (token_a), + nl_addr_get_binary_addr (token_b), + sizeof (struct in6_addr))) { + /* Token changed */ + return TRUE; + } + } else if (token_a != token_b) { + /* Token added or removed (?). */ + return TRUE; + } + } +#endif + if (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS) { struct rtnl_addr *a = (struct rtnl_addr *) _a; struct rtnl_addr *b = (struct rtnl_addr *) _b; From 172f5256a75824e0395dd9f6d84e4f9af57bf7b5 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 13 Oct 2014 08:26:52 +0200 Subject: [PATCH 5/6] platform: Add support for IPv6 tokenized identifiers on Linux (cherry picked from commit c2f9940470fc891e2c743e6e33e43b43687414d2) --- src/platform/nm-linux-platform.c | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 22e5102f67..71eae66b22 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -2519,6 +2519,38 @@ link_set_noarp (NMPlatform *platform, int ifindex) return link_change_flags (platform, ifindex, IFF_NOARP, TRUE); } +static gboolean +link_get_ipv6_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId *iid) +{ +#if HAVE_LIBNL_INET6_TOKEN + auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + struct nl_addr *nladdr; + struct in6_addr *addr; + + if (rtnllink && + (rtnl_link_inet6_get_token (rtnllink, &nladdr)) == 0) { + if (nl_addr_get_family (nladdr) != AF_INET6 || + nl_addr_get_len (nladdr) != sizeof (struct in6_addr)) { + nl_addr_put (nladdr); + return FALSE; + } + + addr = nl_addr_get_binary_addr (nladdr); + iid->id_u8[7] = addr->s6_addr[15]; + iid->id_u8[6] = addr->s6_addr[14]; + iid->id_u8[5] = addr->s6_addr[13]; + iid->id_u8[4] = addr->s6_addr[12]; + iid->id_u8[3] = addr->s6_addr[11]; + iid->id_u8[2] = addr->s6_addr[10]; + iid->id_u8[1] = addr->s6_addr[9]; + iid->id_u8[0] = addr->s6_addr[8]; + nl_addr_put (nladdr); + return TRUE; + } +#endif + return FALSE; +} + static gboolean link_get_user_ipv6ll_enabled (NMPlatform *platform, int ifindex) { @@ -4603,6 +4635,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_is_connected = link_is_connected; platform_class->link_uses_arp = link_uses_arp; + platform_class->link_get_ipv6_token = link_get_ipv6_token; + platform_class->link_get_user_ipv6ll_enabled = link_get_user_ipv6ll_enabled; platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; From 77daecf1d1c3decc7a3a721426c00f66cd4a808f Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 27 Sep 2014 09:04:46 +0200 Subject: [PATCH 6/6] core: Use tokenized identifiers when constructing an address We trigger a new solicitation upon seeing the new token. Kernel triggers one too, but that one is of no use to us, since the advertisement might arrive sooner than we learn about the token change. (cherry picked from commit 24e7ea786058597fdb45842377045e73ba8f923d) --- src/devices/nm-device.c | 14 ++++++++++++-- src/rdisc/nm-rdisc.c | 32 +++++++++++++++++++++++++++++--- src/rdisc/nm-rdisc.h | 2 +- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 0de28ed0f4..99ba3d5a6c 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1239,6 +1239,7 @@ device_link_changed (NMDevice *self, NMPlatformLink *info) { NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMUtilsIPv6IfaceId token_iid; gboolean ip_ifname_changed = FALSE; if (info->udi && g_strcmp0 (info->udi, priv->udi)) { @@ -1285,6 +1286,12 @@ device_link_changed (NMDevice *self, NMPlatformLink *info) nm_device_enslave_slave (priv->master, self, NULL); } + if (priv->rdisc && nm_platform_link_get_ipv6_token (priv->ifindex, &token_iid)) { + _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); + if (nm_rdisc_set_iid (priv->rdisc, token_iid)) + nm_rdisc_start (priv->rdisc); + } + if (klass->link_changed) klass->link_changed (self, info); @@ -4282,11 +4289,12 @@ addrconf6_start_with_link_ready (NMDevice *self) g_assert (priv->rdisc); - if (!nm_device_get_ip_iface_identifier (self, &iid)) { + if (nm_platform_link_get_ipv6_token (priv->ifindex, &iid)) { + _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); + } else if (!nm_device_get_ip_iface_identifier (self, &iid)) { _LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 cannot continue"); return FALSE; } - nm_rdisc_set_iid (priv->rdisc, iid); /* Apply any manual configuration before starting RA */ if (!ip6_config_merge_and_apply (self, TRUE, NULL)) @@ -4305,6 +4313,8 @@ addrconf6_start_with_link_ready (NMDevice *self) NM_RDISC_RA_TIMEOUT, G_CALLBACK (rdisc_ra_timeout), self); + + nm_rdisc_set_iid (priv->rdisc, iid); nm_rdisc_start (priv->rdisc); return TRUE; } diff --git a/src/rdisc/nm-rdisc.c b/src/rdisc/nm-rdisc.c index 07271a89e4..eba79cf3fb 100644 --- a/src/rdisc/nm-rdisc.c +++ b/src/rdisc/nm-rdisc.c @@ -241,12 +241,38 @@ clear_rs_timeout (NMRDisc *rdisc) } } -void +/** + * nm_rdisc_set_iid: + * @rdisc: the #NMRDisc + * @iid: the new interface ID + * + * Sets the "Modified EUI-64" interface ID to be used when generating + * IPv6 addresses using received prefixes. Identifiers are either generated + * from the hardware addresses or manually set by the operator with + * "ip token" command. + * + * Upon token change (or initial setting) all addresses generated using + * the old identifier are removed. The caller should ensure the addresses + * will be reset by soliciting router advertisements. + * + * Returns: %TRUE if the token was changed, %FALSE otherwise. + **/ +gboolean nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid) { - g_return_if_fail (NM_IS_RDISC (rdisc)); + g_return_val_if_fail (NM_IS_RDISC (rdisc), FALSE); - rdisc->iid = iid; + if (rdisc->iid.id != iid.id) { + rdisc->iid = iid; + if (rdisc->addresses->len) { + debug ("(%s) IPv6 interface identifier changed, flushing addresses", rdisc->ifname); + g_array_remove_range (rdisc->addresses, 0, rdisc->addresses->len); + g_signal_emit_by_name (rdisc, NM_RDISC_CONFIG_CHANGED, NM_RDISC_CONFIG_ADDRESSES); + } + return TRUE; + } + + return FALSE; } static gboolean diff --git a/src/rdisc/nm-rdisc.h b/src/rdisc/nm-rdisc.h index a79e6c9690..463e19121b 100644 --- a/src/rdisc/nm-rdisc.h +++ b/src/rdisc/nm-rdisc.h @@ -141,7 +141,7 @@ typedef struct { GType nm_rdisc_get_type (void); -void nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid); +gboolean nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid); void nm_rdisc_start (NMRDisc *rdisc); #endif /* __NETWORKMANAGER_RDISC_H__ */