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>
} 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:
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",
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 ||
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 */