diff options
Diffstat (limited to 'src/core/dhcp/nm-dhcp-client.c')
| -rw-r--r-- | src/core/dhcp/nm-dhcp-client.c | 80 |
1 files changed, 73 insertions, 7 deletions
diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c index cd6e67e2..18ad4024 100644 --- a/src/core/dhcp/nm-dhcp-client.c +++ b/src/core/dhcp/nm-dhcp-client.c @@ -91,6 +91,11 @@ typedef struct _NMDhcpClientPrivate { union { struct { + /* Timer for restarting DHCP after the IPv6-only timeout */ + GSource *ipv6_only_restart_source; + /* Minimum value accepted for the IPv6-only option. For test/debug only.*/ + guint ipv6_only_min_wait; + struct { NML3CfgCommitTypeHandle *l3cfg_commit_handle; GSource *done_source; @@ -336,7 +341,7 @@ _emit_notify_data(NMDhcpClient *self, const NMDhcpClientNotifyData *notify_data) #define _emit_notify(self, _notify_type, ...) \ _emit_notify_data( \ (self), \ - &((const NMDhcpClientNotifyData){.notify_type = (_notify_type), __VA_ARGS__})) + &((const NMDhcpClientNotifyData) {.notify_type = (_notify_type), __VA_ARGS__})) /*****************************************************************************/ @@ -684,7 +689,7 @@ _acd_check_lease(NMDhcpClient *self, NMOptionBool *out_acd_state) now_msec = nm_utils_get_monotonic_timestamp_msec(); g_array_append_val(priv->v4.acd.reglist, - ((AcdRegListData){ + ((AcdRegListData) { .l3cd = nm_l3_config_data_ref(priv->l3cd_next), .addr = addr, .expiry_msec = now_msec + ACD_REGLIST_GRACE_PERIOD_MSEC, @@ -1375,6 +1380,8 @@ nm_dhcp_client_start(NMDhcpClient *self, GError **error) g_return_val_if_fail(priv->config.uuid, FALSE); nm_assert(!priv->effective_client_id); + priv->is_stopped = FALSE; + IS_IPv4 = NM_IS_IPv4(priv->config.addr_family); if (!IS_IPv4) { @@ -1416,6 +1423,51 @@ nm_dhcp_client_start(NMDhcpClient *self, GError **error) /*****************************************************************************/ +static gboolean +ipv6_only_restart_timeout_cb(gpointer user_data) +{ + NMDhcpClient *self = user_data; + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + gs_free_error GError *error = NULL; + + nm_assert(priv->config.addr_family == AF_INET); + + nm_clear_g_source_inst(&priv->v4.ipv6_only_restart_source); + if (!nm_dhcp_client_start(self, &error)) { + _LOGW("failed to restart the DHCP client after the IPv6-only timeout: %s", error->message); + _emit_notify(self, + NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, + .it_looks_bad.reason = error->message); + } + + return G_SOURCE_CONTINUE; +} + +/** + * nm_dhcp_client_schedule_ipv6_only_restart(): + * @self: the client + * @timeout: the raw value from the DHCP option + * + * Stops the DHCPv4 client and restarts it after the timeout announced + * by the "IPv6-Only preferred" option. + */ +void +nm_dhcp_client_schedule_ipv6_only_restart(NMDhcpClient *self, guint timeout) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + nm_assert(priv->config.addr_family == AF_INET); + nm_assert(!priv->is_stopped); + + timeout = NM_MAX(priv->v4.ipv6_only_min_wait, timeout); + _LOGI("received option \"ipv6-only-preferred\": stopping DHCPv4 for %u seconds", timeout); + + nm_dhcp_client_stop(self, FALSE); + nm_clear_g_source_inst(&priv->no_lease_timeout_source); + priv->v4.ipv6_only_restart_source = + nm_g_timeout_add_seconds_source(timeout, ipv6_only_restart_timeout_cb, self); +} + void nm_dhcp_client_stop_existing(const char *pid_file, const char *binary_name) { @@ -1488,7 +1540,10 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release) if (priv->is_stopped) return; + nm_clear_pointer(&priv->effective_client_id, g_bytes_unref); nm_clear_g_source_inst(&priv->previous_lease_timeout_source); + if (priv->config.addr_family == AF_INET) + nm_clear_g_source_inst(&priv->v4.ipv6_only_restart_source); priv->is_stopped = TRUE; @@ -1934,6 +1989,8 @@ static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(object); + const char *str; + guint min_wait; switch (prop_id) { case PROP_CONFIG: @@ -1943,7 +2000,8 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps /* I know, this is technically not necessary. It just feels nicer to * explicitly initialize the respective union member. */ if (NM_IS_IPv4(priv->config.addr_family)) { - priv->v4 = (typeof(priv->v4)){ + priv->v4 = (typeof(priv->v4)) { + .ipv6_only_min_wait = NM_DHCP_MIN_V6ONLY_WAIT_DEFAULT, .acd = { .addr = INADDR_ANY, @@ -1952,8 +2010,16 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps .done_source = NULL, }, }; + + str = g_getenv("NM_TEST_IPV6_ONLY_MIN_WAIT"); + if (str) { + min_wait = _nm_utils_ascii_str_to_int64(str, 10, 1, G_MAXUINT, 0); + if (min_wait != 0) { + priv->v4.ipv6_only_min_wait = min_wait; + } + } } else { - priv->v6 = (typeof(priv->v6)){ + priv->v6 = (typeof(priv->v6)) { .lladdr_timeout_source = NULL, }; } @@ -1990,13 +2056,13 @@ dispose(GObject *object) nm_clear_g_source_inst(&priv->previous_lease_timeout_source); nm_clear_g_source_inst(&priv->no_lease_timeout_source); - if (!NM_IS_IPv4(priv->config.addr_family)) { + if (priv->config.addr_family == AF_INET) { + nm_clear_g_source_inst(&priv->v4.ipv6_only_restart_source); + } else { nm_clear_g_source_inst(&priv->v6.lladdr_timeout_source); nm_clear_g_source_inst(&priv->v6.dad_timeout_source); } - nm_clear_pointer(&priv->effective_client_id, g_bytes_unref); - nm_assert(!priv->watch_source); nm_assert(!priv->l3cd_next); nm_assert(!priv->l3cd_curr); |