diff options
| author | Dan Williams <dcbw@redhat.com> | 2014-11-13 19:52:21 -0600 |
|---|---|---|
| committer | Dan Williams <dcbw@redhat.com> | 2014-11-17 10:20:37 -0600 |
| commit | 70f227f5528d74957f397f7e431cd15677fd95e4 (patch) | |
| tree | b48e6039910c2ca2b722b8f3da47b7cde0f730dd | |
| parent | d1295b12e9f802867edef57ee02c87495df1683e (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.c | 23 |
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; |
