summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2014-11-13 19:52:21 -0600
committerDan Williams <dcbw@redhat.com>2014-11-17 10:20:37 -0600
commit70f227f5528d74957f397f7e431cd15677fd95e4 (patch)
treeb48e6039910c2ca2b722b8f3da47b7cde0f730dd
parentd1295b12e9f802867edef57ee02c87495df1683e (diff)
core: bounce disable_ipv6 when setting userspace IPv6 link-local (bgo #740096)
The kernel does not terminate an ongoing IPv6LL address process when the IPv6LL address generation mode is set to 'none' (indicating that userspace wishes to handle IPv6LL). Next, NetworkManager does not expose IPv6 addresses internally until they have completed DAD. This means that the kernel may still be performing DAD for an IPv6LL address when NetworkManager turns userspace IPv6LL on, and when DAD is complete NetworkManager will finally pay attention to the address. If the device is in the DISCONNECTED state, NetworkManager will then generate and assume an IPv6LL-only connection on the device. Unfortunately, that behavior happens if the following is true: 1) IPv6LL addressing takes a while (eg, dad_transmits is high or the kernel takes a while for some reason) 2) the activated connection fails quickly (dhclient fails or some other fatal error terminates the activation attempt) 3) the activated connection has ipv6.method=ignore In this case, when the device was brought up and ipv6.method=ignore, NetworkManager re-enabled kernel IPv6LL and reset the IPv6 sysctl properties. The kernel then generated an IPv6LL address and began DAD. dhclient failed quickly, and NM deactivated the device. NM then turned off kernel IPv6LL when deactivating the device, but the kernel does not terminate the ongoing DAD. Some time after the device entered the DISCONNECTED state, the kernel finished DAD and that allowed NetworkManager to internally see the address, which caused NetworkManager to emit the 'recheck-assume' signal. This generated a new IPv6LL-only connection which was then assumed. Bouncing 'disable_ipv6' when re-enabling userspace IPv6LL during device deactivation flushes the tentative kernel IPv6LL address, thus preventing the address from being announced after userspace IPv6LL is re-enabled. The other alternative is to expose tentative addresses (eg those still doing DAD) in NMPlatform so they would be flushed when the device deactivates, but that is a larger & riskier set of changes. Reproducer: - ifconfig eth0 down - prepare a DHCPv4 connection with ipv6.method=ignore - set /proc/sys/net/ipv6/conf/all/dad_transmits to "15" - ensure that DHCPv4 will fail (replace dhclient with a script that exits after 2 seconds or something) - run NetworkManager - activate the DHCP connection and watch it immediately fail - wait for the kernel to announce the IPv6LL address after DAD finishes - watch NM "assume" the new IPv6LL connection https://bugzilla.gnome.org/show_bug.cgi?id=740096
-rw-r--r--src/devices/nm-device.c23
1 files changed, 18 insertions, 5 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 59fc4bb03b..4836331a69 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -4170,6 +4170,8 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
int ifindex = nm_device_get_ip_ifindex (self);
+ const char *iface = nm_device_get_ip_iface (self);
+ char *value;
if (!nm_platform_check_support_user_ipv6ll ())
return;
@@ -4182,6 +4184,17 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
if ( !nm_platform_link_set_user_ipv6ll_enabled (ifindex, enable)
&& nm_platform_get_error () != NM_PLATFORM_ERROR_NOT_FOUND)
_LOGW (LOGD_IP6, "failed to %s userspace IPv6LL address handling", detail);
+
+ if (enable) {
+ /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */
+ value = nm_platform_sysctl_get (nm_utils_ip6_property_path (iface, "disable_ipv6"));
+ if (g_strcmp0 (value, "0") == 0) {
+ nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
+ nm_device_ipv6_sysctl_set (self, "disable_ipv6", "0");
+ }
+ g_free (value);
+ }
+
}
}
@@ -7285,14 +7298,14 @@ _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)
+ if (old_state > NM_DEVICE_STATE_DISCONNECTED) {
+ /* Ensure devices that previously assumed a connection now have
+ * userspace IPv6LL enabled.
+ */
set_nm_ipv6ll (self, TRUE);
- if (old_state > NM_DEVICE_STATE_UNAVAILABLE)
nm_device_cleanup (self, reason);
+ }
break;
default:
break;