DHCPv6: don't kill dhclient when address is depreferred bug748268-DEPREF6
authorTJ <gnome@iam.tj>
Thu, 31 Mar 2016 15:02:57 +0000 (16:02 +0100)
committerTJ <gnome@iam.tj>
Thu, 31 Mar 2016 15:45:30 +0000 (16:45 +0100)
Teach Network Manager how to handle the DEPREFER dhclient state.

Network Manager summarily kills the IPv6 dhclient process when the
DHCPv6 lease contained in the dhclient6-${UUID}.lease file is out-of date, leaving
the IPv6 interface without a stateful IPv6 address, or, if the "require IPv6 address on
this interface" option is enabled, causes NM to cycle continuously deactivating and
reactivating the interface (including the IPv4 addresses).

This is effectively a Denial Of Service. It can be trivially induced if,
for example, the dhclient6-$(UUID}.lease file contains a lease that was issued before the
user went away on vacation or the PC wasn't connected to the same network for a few
days (depending on the lease renew/rebind/expiry times). Calculation on the old lease of

start + preferred_lifetime < NOW

triggers dhclient to 'DEPREFER6' the lease (withdraw the address record)
and ask the DHCPv6 server for a new lease, but Network Manager will kill the
dhclient because it only sees an 'EXPIRE6' state change.

Signed-off-by: TJ <gnome@iam.tj>
src/devices/nm-device.c
src/dhcp-manager/nm-dhcp-client.c
src/dhcp-manager/nm-dhcp-client.h

index 5085833..b2f3c3e 100644 (file)
@@ -5263,12 +5263,17 @@ dhcp6_state_changed (NMDhcpClient *client,
                } else if (priv->ip6_state == IP_DONE)
                        dhcp6_lease_change (self);
                break;
+       case NM_DHCP_STATE_DEPREFER:
+               if (priv->ip6_state == IP_DONE)
+                       priv->ip6_state = IP_WAIT;
+               break;
        case NM_DHCP_STATE_TIMEOUT:
                dhcp6_timeout (self, client);
                break;
        case NM_DHCP_STATE_EXPIRE:
                /* Ignore expiry before we even have a lease (NAK, old lease, etc) */
-               if (priv->ip6_state != IP_CONF)
+               /* Don't fail when a DEPREF6 has caused the EXPIRE6; wait for the new lease */
+               if (priv->ip6_state != IP_CONF && priv->ip6_state != IP_WAIT)
                        dhcp6_fail (self, FALSE);
                break;
        case NM_DHCP_STATE_DONE:
index b8161ab..6c4fe3e 100644 (file)
@@ -191,6 +191,7 @@ nm_dhcp_client_get_fqdn (NMDhcpClient *self)
 static const char *state_table[NM_DHCP_STATE_MAX + 1] = {
        [NM_DHCP_STATE_UNKNOWN]  = "unknown",
        [NM_DHCP_STATE_BOUND]    = "bound",
+       [NM_DHCP_STATE_DEPREFER] = "deprefer",
        [NM_DHCP_STATE_TIMEOUT]  = "timeout",
        [NM_DHCP_STATE_EXPIRE]   = "expire",
        [NM_DHCP_STATE_DONE]     = "done",
@@ -216,6 +217,9 @@ reason_to_state (NMDhcpClient *self, const char *iface, const char *reason)
            g_ascii_strcasecmp (reason, "rebind") == 0 ||
            g_ascii_strcasecmp (reason, "rebind6") == 0)
                return NM_DHCP_STATE_BOUND;
+       else if (g_ascii_strcasecmp (reason, "depref") == 0 ||
+                g_ascii_strcasecmp (reason, "depref6") == 0)
+               return NM_DHCP_STATE_DEPREFER;
        else if (g_ascii_strcasecmp (reason, "timeout") == 0)
                return NM_DHCP_STATE_TIMEOUT;
        else if (g_ascii_strcasecmp (reason, "nak") == 0 ||
index 1c78c5b..9fbc83c 100644 (file)
@@ -46,6 +46,7 @@
 typedef enum {
        NM_DHCP_STATE_UNKNOWN = 0,
        NM_DHCP_STATE_BOUND,        /* new lease or lease changed */
+       NM_DHCP_STATE_DEPREFER,     /* lease out-of-date and will be replaced */
        NM_DHCP_STATE_TIMEOUT,      /* timed out contacting server */
        NM_DHCP_STATE_DONE,         /* client quit or stopped */
        NM_DHCP_STATE_EXPIRE,       /* lease expired or NAKed */