From 37f11fbdf8747ffb42cb9944c8a3c3e4bb9c6868 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 24 Jul 2014 15:57:08 -0500 Subject: [PATCH 1/2] platform: add support for kernel IPv6LL address generation modes This patch requires both upstream kernel support for IFLA_INET6_ADDR_GEN_MODE which was merged in this patch: ipv6: addrconf: implement address generation modes bc91b0f07ada5535427373a4e2050877bcc12218 and corresponding libnl support, merged in these patches: veth: add kernel header linux/veth.h for VETH defines 9dc6e6da90016a33929f262bea0187396e1a061b link: update copy of kernel header include/linux/if_link.h b51815a9dbd8e45fd2558bbe337fb360ca2fd861 link/inet6: add link IPv6 address generation mode support 558f966782539f6d975da705fd73cea561c9dc83 --- configure.ac | 19 ++++++ src/platform/nm-linux-platform.c | 104 ++++++++++++++++++++++++++++++- src/platform/nm-platform.c | 61 ++++++++++++++++++ src/platform/nm-platform.h | 8 +++ 4 files changed, 191 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 1bc73f6eb7..23898c3bbb 100644 --- a/configure.ac +++ b/configure.ac @@ -386,6 +386,25 @@ PKG_CHECK_MODULES(LIBNL, libnl-3.0 >= 3.2.8 libnl-route-3.0 libnl-genl-3.0) AC_SUBST(LIBNL_CFLAGS) AC_SUBST(LIBNL_LIBS) +AC_CHECK_LIB([nl-route-3], [rtnl_link_inet6_get_addr_gen_mode], + ac_have_addr_gen_mode="1", + ac_have_addr_gen_mode="0") +AC_DEFINE_UNQUOTED(HAVE_LIBNL_INET6_ADDR_GEN_MODE, + $ac_have_addr_gen_mode, [Define if libnl has rtnl_link_inet6_get_addr_gen_mode()]) + +AC_MSG_CHECKING([Linux kernel IN6_ADDR_GEN_MODE enum]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#ifndef __user + #define __user + #endif + #include ]], + [[int a = IN6_ADDR_GEN_MODE_EUI64; a++;]])], + [ac_have_kernel_gen_mode="1"], + [ac_have_kernel_gen_mode="0"]) +AC_DEFINE_UNQUOTED(HAVE_KERNEL_INET6_ADDR_GEN_MODE, + $ac_have_kernel_gen_mode, [Define if the kernel has IN6_ADDR_GEN_MODE_*]) + # uuid library PKG_CHECK_MODULES(UUID, uuid) AC_SUBST(UUID_CFLAGS) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index feb86010fd..8bec9b2d36 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -44,6 +44,16 @@ #include #include +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE +#include +#if HAVE_KERNEL_INET6_ADDR_GEN_MODE +#include +#else +#define IN6_ADDR_GEN_MODE_EUI64 0 +#define IN6_ADDR_GEN_MODE_NONE 1 +#endif +#endif + #include "NetworkManagerUtils.h" #include "nm-linux-platform.h" #include "NetworkManagerUtils.h" @@ -83,6 +93,7 @@ typedef struct { GHashTable *wifi_data; int support_kernel_extended_ifa_flags; + int support_user_ipv6ll; } NMLinuxPlatformPrivate; #define NM_LINUX_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate)) @@ -669,6 +680,23 @@ check_support_kernel_extended_ifa_flags (NMPlatform *platform) return priv->support_kernel_extended_ifa_flags > 0; } +static gboolean +check_support_user_ipv6ll (NMPlatform *platform) +{ + NMLinuxPlatformPrivate *priv; + + g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + + priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + + if (priv->support_user_ipv6ll == 0) { + nm_log_warn (LOGD_PLATFORM, "Unable to detect kernel support for IFLA_INET6_ADDR_GEN_MODE. Assume no kernel support."); + priv->support_user_ipv6ll = -1; + } + + return priv->support_user_ipv6ll > 0; +} + /* Object type specific utilities */ @@ -1529,8 +1557,20 @@ announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatfor switch (object_type) { case OBJECT_TYPE_LINK: { - NMPlatformLink device; struct rtnl_link *rtnl_link = (struct rtnl_link *) object; + NMPlatformLink device; + +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE + /* If we ever see a link with valid IPv6 link-local address + * generation modes, the kernel supports it. + */ + if (priv->support_user_ipv6ll == 0) { + uint8_t mode; + + if (rtnl_link_inet6_get_addr_gen_mode (rtnl_link, &mode) == 0) + priv->support_user_ipv6ll = 1; + } +#endif if (!init_link (platform, &device, rtnl_link)) return; @@ -2405,6 +2445,46 @@ link_set_noarp (NMPlatform *platform, int ifindex) return link_change_flags (platform, ifindex, IFF_NOARP, TRUE); } +static gboolean +link_get_user_ipv6ll_enabled (NMPlatform *platform, int ifindex) +{ +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + + if (priv->support_user_ipv6ll > 0) { + auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + uint8_t mode = 0; + + if (rtnllink) { + if (rtnl_link_inet6_get_addr_gen_mode (rtnllink, &mode) != 0) { + /* Default to "disabled" on error */ + return FALSE; + } + return mode == IN6_ADDR_GEN_MODE_NONE; + } + } +#endif + return FALSE; +} + +static gboolean +link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled) +{ +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE + if (check_support_user_ipv6ll (platform)) { + auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL); + guint8 mode = enabled ? IN6_ADDR_GEN_MODE_NONE : IN6_ADDR_GEN_MODE_EUI64; + char buf[32]; + + rtnl_link_inet6_set_addr_gen_mode (change, mode); + debug ("link: change %d: set IPv6 address generation mode to %s", + ifindex, rtnl_link_inet6_addrgenmode2str (mode, buf, sizeof (buf))); + return link_change (platform, ifindex, change); + } +#endif + return FALSE; +} + static gboolean supports_ethtool_carrier_detect (const char *ifname) { @@ -4060,6 +4140,24 @@ setup (NMPlatform *platform) for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) _rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object); +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE + /* Initial check for user IPv6LL support once the link cache is allocated + * and filled. If there are no links in the cache yet then we'll check + * when a new link shows up in announce_object(). + */ + object = nl_cache_get_first (priv->link_cache); + if (object) { + uint8_t mode; + + if (rtnl_link_inet6_get_addr_gen_mode ((struct rtnl_link *) object, &mode) == 0) + priv->support_user_ipv6ll = 1; + else + priv->support_user_ipv6ll = -1; + } +#else + priv->support_user_ipv6ll = -1; +#endif + /* Set up udev monitoring */ priv->udev_client = g_udev_client_new (udev_subsys); g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform); @@ -4147,6 +4245,9 @@ 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_user_ipv6ll_enabled = link_get_user_ipv6ll_enabled; + platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; + platform_class->link_get_address = link_get_address; platform_class->link_set_address = link_set_address; platform_class->link_get_mtu = link_get_mtu; @@ -4213,4 +4314,5 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->ip6_route_exists = ip6_route_exists; platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags; + platform_class->check_support_user_ipv6ll = check_support_user_ipv6ll; } diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index b3ffe70a6e..e26e10e158 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -215,6 +215,21 @@ nm_platform_check_support_kernel_extended_ifa_flags () return klass->check_support_kernel_extended_ifa_flags (platform); } +gboolean +nm_platform_check_support_user_ipv6ll (void) +{ + static int supported = -1; + + g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE); + + if (!klass->check_support_user_ipv6ll) + return FALSE; + + if (supported < 0) + supported = klass->check_support_user_ipv6ll (platform) ? 1 : 0; + return !!supported; +} + /******************************************************************/ /** @@ -729,6 +744,52 @@ nm_platform_link_uses_arp (int ifindex) return klass->link_uses_arp (platform, ifindex); } +/** + * nm_platform_link_get_user_ip6vll_enabled: + * @ifindex: Interface index + * + * Check whether NM handles IPv6LL address creation for the link. If the + * platform or OS doesn't support changing the IPv6LL address mode, this call + * will fail and return %FALSE. + * + * Returns: %TRUE if NM handles the IPv6LL address for @ifindex + */ +gboolean +nm_platform_link_get_user_ipv6ll_enabled (int ifindex) +{ + reset_error (); + + g_return_val_if_fail (ifindex >= 0, FALSE); + g_return_val_if_fail (klass->check_support_user_ipv6ll, FALSE); + + if (klass->link_get_user_ipv6ll_enabled) + return klass->link_get_user_ipv6ll_enabled (platform, ifindex); + return FALSE; +} + +/** + * nm_platform_link_set_user_ip6vll_enabled: + * @ifindex: Interface index + * + * Set whether NM handles IPv6LL address creation for the link. If the + * platform or OS doesn't support changing the IPv6LL address mode, this call + * will fail and return %FALSE. + * + * Returns: %TRUE if the operation was successful, %FALSE if it failed. + */ +gboolean +nm_platform_link_set_user_ipv6ll_enabled (int ifindex, gboolean enabled) +{ + reset_error (); + + g_return_val_if_fail (ifindex >= 0, FALSE); + g_return_val_if_fail (klass->check_support_user_ipv6ll, FALSE); + + if (klass->link_set_user_ipv6ll_enabled) + return klass->link_set_user_ipv6ll_enabled (platform, ifindex, enabled); + return FALSE; +} + /** * nm_platform_link_set_address: * @ifindex: Interface index diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index e706d2d74c..7bc80637c5 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -388,6 +388,9 @@ typedef struct { gboolean (*link_is_connected) (NMPlatform *, int ifindex); gboolean (*link_uses_arp) (NMPlatform *, int ifindex); + gboolean (*link_get_user_ipv6ll_enabled) (NMPlatform *, int ifindex); + gboolean (*link_set_user_ipv6ll_enabled) (NMPlatform *, int ifindex, gboolean enabled); + gconstpointer (*link_get_address) (NMPlatform *, int ifindex, size_t *length); gboolean (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length); guint32 (*link_get_mtu) (NMPlatform *, int ifindex); @@ -463,6 +466,7 @@ typedef struct { gboolean (*ip6_route_exists) (NMPlatform *, int ifindex, struct in6_addr network, int plen, int metric); gboolean (*check_support_kernel_extended_ifa_flags) (NMPlatform *); + gboolean (*check_support_user_ipv6ll) (NMPlatform *); } NMPlatformClass; /* NMPlatform signals @@ -528,6 +532,9 @@ 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_user_ipv6ll_enabled (int ifindex); +gboolean nm_platform_link_set_user_ipv6ll_enabled (int ifindex, gboolean enabled); + gconstpointer nm_platform_link_get_address (int ifindex, size_t *length); gboolean nm_platform_link_set_address (int ifindex, const void *address, size_t length); guint32 nm_platform_link_get_mtu (int ifindex); @@ -623,6 +630,7 @@ int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6R gboolean nm_platform_check_support_libnl_extended_ifa_flags (void); gboolean nm_platform_check_support_kernel_extended_ifa_flags (void); +gboolean nm_platform_check_support_user_ipv6ll (void); void nm_platform_addr_flags2str (int flags, char *buf, size_t size); From 5e4761a3a9cb1cba692d41d79aa389f539752e50 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 24 Jul 2014 17:14:30 -0500 Subject: [PATCH 2/2] core: take over IPv6LL address management if kernel supports it (bgo #734149) NM keeps interfaces IFF_UP when possible to receive link layer events like carrier changes. Unfortunately, the kernel also uses IFF_UP as a flag to assign an IPv6LL address to the interface, which results in IPv6 connectivity on the link even if the interface is not supposed to be activated/connected. NM sets disable_ipv6=1 to ensure that the kernel does not set up IPv6LL connectivity on interfaces when they are not supposed to be active and connected. Unfortunately, that prevents users from manually adding IPv6 addresses to the interface, since they expect previous kernel behavior where IPv6 is enabled whenever the interface is IFF_UP. Furthermore, interfaces like PPP and some WWAN devices provide misleading information to the kernel which causes the kernel to create the wrong IPv6LL address for the interface. The IPv6LL address for these devices is obtained through control channels instead (IPV6CP for PPP, proprietary protocols for WWAN devices) and should be used instead of the kernel address. So we'd like to suppress kernel IPv6LL address generation on these interfaces anyway. This patch makes use of the netlink IFLA_INET6_ADDR_GEN_MODE attribute to take over assignment of IPv6LL addresses while keeping the interface IFF_UP, to ensure there is only IPv6 connectivity when the user requests it. To remain compliant with standards, if a user adds IPv6 addresses externally, NetworkManager must also immediately add an IPv6LL address for that interface too. https://bugzilla.gnome.org/show_bug.cgi?id=734149 --- src/devices/nm-device.c | 146 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 11 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index ef10d0c239..18b0ad21c7 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -268,6 +268,7 @@ typedef struct { NMIP6Config * vpn6_config; /* routes added by a VPN which uses this device */ NMIP6Config * wwan_ip6_config; NMIP6Config * ext_ip6_config; /* Stuff added outside NM */ + gboolean nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */ NMRDisc * rdisc; gulong rdisc_changed_id; @@ -563,6 +564,9 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface) if (priv->ip_iface) { priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface); if (priv->ip_ifindex > 0) { + if (nm_platform_check_support_user_ipv6ll ()) + nm_platform_link_set_user_ipv6ll_enabled (priv->ip_ifindex, TRUE); + if (!nm_platform_link_is_up (priv->ip_ifindex)) nm_platform_link_set_up (priv->ip_ifindex); } else { @@ -611,6 +615,12 @@ get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid) return success; } +static gboolean +nm_device_get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *iid) +{ + return NM_DEVICE_GET_CLASS (self)->get_ip_iface_identifier (self, iid); +} + const char * nm_device_get_driver (NMDevice *self) { @@ -1132,7 +1142,12 @@ device_ip_link_changed (NMDevice *self, NMPlatformLink *info) } static void -link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDevice *self) +link_changed_cb (NMPlatform *platform, + int ifindex, + NMPlatformLink *info, + NMPlatformSignalChangeType change_type, + NMPlatformReason reason, + NMDevice *self) { if (change_type != NM_PLATFORM_SIGNAL_CHANGED) return; @@ -3368,6 +3383,52 @@ linklocal6_complete (NMDevice *self) g_return_if_fail (FALSE); } +static void +check_and_add_ipv6ll_addr (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + int ip_ifindex = nm_device_get_ip_ifindex (self); + NMUtilsIPv6IfaceId iid; + struct in6_addr lladdr; + guint i, n; + + if (priv->nm_ipv6ll == FALSE) + return; + + if (priv->ip6_config) { + n = nm_ip6_config_get_num_addresses (priv->ip6_config); + for (i = 0; i < n; i++) { + const NMPlatformIP6Address *addr; + + addr = nm_ip6_config_get_address (priv->ip6_config, i); + if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) { + /* Already have an LL address, nothing to do */ + return; + } + } + } + + if (!nm_device_get_ip_iface_identifier (self, &iid)) { + _LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 may be broken"); + return; + } + + memset (&lladdr, 0, sizeof (lladdr)); + lladdr.s6_addr16[0] = htons (0xfe80); + nm_utils_ipv6_addr_set_interface_identfier (&lladdr, iid); + _LOGD (LOGD_IP6, "adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL)); + if (!nm_platform_ip6_address_add (ip_ifindex, + lladdr, + in6addr_any, + 64, + NM_PLATFORM_LIFETIME_PERMANENT, + NM_PLATFORM_LIFETIME_PERMANENT, + 0)) { + _LOGW (LOGD_IP6, "failed to add IPv6 link-local address %s", + nm_utils_inet6_ntop (&lladdr, NULL)); + } +} + static NMActStageReturn linklocal6_start (NMDevice *self) { @@ -3386,6 +3447,8 @@ linklocal6_start (NMDevice *self) method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); _LOGD (LOGD_DEVICE, "linklocal6: starting IPv6 with method '%s', but the device has no link-local addresses configured. Wait.", method); + check_and_add_ipv6ll_addr (self); + priv->linklocal6_timeout_id = g_timeout_add_seconds (5, linklocal6_timeout_cb, self); return NM_ACT_STAGE_RETURN_POSTPONE; @@ -3630,8 +3693,8 @@ addrconf6_start_with_link_ready (NMDevice *self) g_assert (priv->rdisc); - if (!NM_DEVICE_GET_CLASS (self)->get_ip_iface_identifier (self, &iid)) { - _LOGW (LOGD_IP6, "failed to get interface identifier"); + 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); @@ -3759,8 +3822,39 @@ restore_ip6_properties (NMDevice *self) gpointer key, value; g_hash_table_iter_init (&iter, priv->ip6_saved_properties); - while (g_hash_table_iter_next (&iter, &key, &value)) + while (g_hash_table_iter_next (&iter, &key, &value)) { + /* Don't touch "disable_ipv6" if we're doing userland IPv6LL */ + if (priv->nm_ipv6ll && strcmp (key, "disable_ipv6") == 0) + continue; nm_device_ipv6_sysctl_set (self, key, value); + } +} + +static inline void +set_disable_ipv6 (NMDevice *self, const char *value) +{ + /* We only touch disable_ipv6 when NM is not managing the IPv6LL address */ + if (NM_DEVICE_GET_PRIVATE (self)->nm_ipv6ll == FALSE) + nm_device_ipv6_sysctl_set (self, "disable_ipv6", value); +} + +static inline void +set_nm_ipv6ll (NMDevice *self, gboolean enable) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + int ifindex = nm_device_get_ip_ifindex (self); + + if (!nm_platform_check_support_user_ipv6ll ()) + return; + + priv->nm_ipv6ll = enable; + if (ifindex > 0) { + const char *detail = enable ? "enable" : "disable"; + + _LOGD (LOGD_IP6, "will %s userland IPv6LL", detail); + if (!nm_platform_link_set_user_ipv6ll_enabled (ifindex, enable)) + _LOGW (LOGD_IP6, "failed to %s userspace IPv6LL address handling", detail); + } } static NMSettingIP6ConfigPrivacy @@ -3889,7 +3983,7 @@ act_stage3_ip6_config_start (NMDevice *self, } /* Re-enable IPv6 on the interface */ - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "0"); + set_disable_ipv6 (self, "0"); /* Enable/disable IPv6 Privacy Extensions. * If a global value is configured by sysadmin (e.g. /etc/sysctl.conf), @@ -5908,17 +6002,29 @@ queued_ip_config_change (gpointer user_data) priv->queued_ip_config_id = 0; update_ip_config (self, FALSE); + + /* If no IPv6 link-local address exists but other addresses do then we + * must add the LL address to remain conformant with RFC 3513 chapter 2.1 + * ("Addressing Model"): "All interfaces are required to have at least + * one link-local unicast address". + */ + if (priv->ip6_config && nm_ip6_config_get_num_addresses (priv->ip6_config)) + check_and_add_ipv6ll_addr (self); + return FALSE; } static void -device_ip_changed (NMPlatform *platform, int ifindex, gpointer platform_object, NMPlatformSignalChangeType change_type, NMPlatformReason reason, gpointer user_data) +device_ip_changed (NMPlatform *platform, + int ifindex, + gpointer platform_object, + NMPlatformSignalChangeType change_type, + NMPlatformReason reason, + NMDevice *self) { - NMDevice *self = user_data; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if (nm_device_get_ip_ifindex (self) == ifindex) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (!priv->queued_ip_config_id) priv->queued_ip_config_id = g_idle_add (queued_ip_config_change, self); @@ -6538,7 +6644,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason) _cleanup_generic_pre (self, TRUE); /* Turn off kernel IPv6 */ - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); + set_disable_ipv6 (self, "1"); nm_device_ipv6_sysctl_set (self, "accept_ra", "0"); nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0"); @@ -6653,6 +6759,7 @@ _set_state_full (NMDevice *self, if (nm_device_get_act_request (self)) nm_device_cleanup (self, reason); nm_device_take_down (self, TRUE); + set_nm_ipv6ll (self, FALSE); restore_ip6_properties (self); } break; @@ -6660,7 +6767,8 @@ _set_state_full (NMDevice *self, if (old_state == NM_DEVICE_STATE_UNMANAGED) { save_ip6_properties (self); if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) { - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); + set_nm_ipv6ll (self, TRUE); + set_disable_ipv6 (self, "1"); nm_device_ipv6_sysctl_set (self, "accept_ra_defrtr", "0"); nm_device_ipv6_sysctl_set (self, "accept_ra_pinfo", "0"); nm_device_ipv6_sysctl_set (self, "accept_ra_rtr_pref", "0"); @@ -6685,6 +6793,12 @@ _set_state_full (NMDevice *self, nm_device_cleanup (self, reason); break; case NM_DEVICE_STATE_DISCONNECTED: + /* Ensure devices that previously assumed a connection now have + * userspace IPv6LL enabled. + */ + if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) + set_nm_ipv6ll (self, TRUE); + if (old_state > NM_DEVICE_STATE_UNAVAILABLE) nm_device_cleanup (self, reason); break; @@ -7179,6 +7293,13 @@ constructor (GType type, g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self); + if (nm_platform_check_support_user_ipv6ll ()) { + int ip_ifindex = nm_device_get_ip_ifindex (self); + + if (ip_ifindex > 0) + priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (ip_ifindex); + } + return object; error: @@ -7257,6 +7378,9 @@ dispose (GObject *object) g_warn_if_fail (priv->slaves == NULL); g_assert (priv->master_ready_id == 0); + /* Let the kernel manage IPv6LL again */ + set_nm_ipv6ll (self, FALSE); + _cleanup_generic_post (self, FALSE); g_clear_pointer (&priv->ip6_saved_properties, g_hash_table_unref);