diff options
Diffstat (limited to 'src/devices')
| -rw-r--r-- | src/devices/nm-device.c | 270 | ||||
| -rw-r--r-- | src/devices/tests/test-arping.c | 40 |
2 files changed, 157 insertions, 153 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index b8828d14..e79bc541 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -83,9 +83,8 @@ _LOG_DECLARE_SELF (NMDevice); /*****************************************************************************/ -#define DHCP_RESTART_TIMEOUT 120 -#define DHCP_NUM_TRIES_MAX 3 #define DEFAULT_AUTOCONNECT TRUE +#define DHCP_GRACE_PERIOD_SEC 480 #define CARRIER_WAIT_TIME_MS 6000 #define CARRIER_WAIT_TIME_AFTER_MTU_MS 10000 @@ -387,10 +386,9 @@ typedef struct _NMDevicePrivate { NMDhcpClient * client; gulong state_sigid; NMDhcp4Config * config; - guint restart_id; - guint num_tries_left; char * pac_url; bool was_active; + guint grace_id; } dhcp4; struct { @@ -461,10 +459,9 @@ typedef struct _NMDevicePrivate { NMIP6Config * ip6_config; /* Event ID of the current IP6 config from DHCP */ char * event_id; - guint restart_id; - guint num_tries_left; guint needed_prefixes; bool was_active; + guint grace_id; } dhcp6; gboolean needs_ip6_subnet; @@ -486,7 +483,7 @@ typedef struct _NMDevicePrivate { NMLldpListener *lldp_listener; NMConnectivityState connectivity_state; - guint concheck_periodic_id; + gulong concheck_periodic_id; guint64 concheck_seq; guint check_delete_unrealized_id; @@ -556,7 +553,6 @@ static void realize_start_setup (NMDevice *self, NMUnmanFlagOp unmanaged_user_explicit); static void _set_mtu (NMDevice *self, guint32 mtu); static void _commit_mtu (NMDevice *self, const NMIP4Config *config); -static void dhcp_schedule_restart (NMDevice *self, int addr_family, const char *reason); static void _cancel_activation (NMDevice *self); /*****************************************************************************/ @@ -879,19 +875,36 @@ nm_device_ipv4_sysctl_set (NMDevice *self, const char *property, const char *val } static guint32 -nm_device_ipv4_sysctl_get_uint32 (NMDevice *self, const char *property, guint32 fallback) +nm_device_ipv4_sysctl_get_effective_uint32 (NMDevice *self, const char *property, guint32 fallback) { char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + gint64 v, v_all; if (!nm_device_get_ip_ifindex (self)) return fallback; - return nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, nm_device_get_ip_iface (self), property)), - 10, - 0, - G_MAXUINT32, - fallback); + v = nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, + buf, + nm_device_get_ip_iface (self), + property)), + 10, + 0, + G_MAXUINT32, + -1); + + v_all = nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, + buf, + "all", + property)), + 10, + 0, + G_MAXUINT32, + -1); + + v = NM_MAX (v, v_all); + return v > -1 ? (guint32) v : fallback; } gboolean @@ -2126,8 +2139,7 @@ concheck_periodic_update (NMDevice *self) nm_device_check_connectivity (self, NULL, NULL); } else if (!check_enable && priv->concheck_periodic_id) { /* The default route has gone off, and so has connectivity. */ - g_signal_handler_disconnect (nm_connectivity_get (), priv->concheck_periodic_id); - priv->concheck_periodic_id = 0; + nm_clear_g_signal_handler (nm_connectivity_get (), &priv->concheck_periodic_id); update_connectivity_state (self, NM_CONNECTIVITY_NONE); } #else @@ -2424,12 +2436,12 @@ carrier_changed (NMDevice *self, gboolean carrier) * is restored. */ if (priv->state == NM_DEVICE_STATE_ACTIVATED) nm_device_update_dynamic_ip_setup (self); - else { - if (nm_device_activate_ip4_state_in_wait (self)) - nm_device_activate_stage3_ip4_start (self); - if (nm_device_activate_ip6_state_in_wait (self)) - nm_device_activate_stage3_ip6_start (self); - } + /* If needed, also resume IP configuration that is + * waiting for carrier. */ + if (nm_device_activate_ip4_state_in_wait (self)) + nm_device_activate_stage3_ip4_start (self); + if (nm_device_activate_ip6_state_in_wait (self)) + nm_device_activate_stage3_ip6_start (self); return; } /* fall-through and change state of device */ @@ -2981,7 +2993,7 @@ ip4_rp_filter_update (NMDevice *self) if ( priv->v4_has_shadowed_routes || nm_device_get_best_default_route (self, AF_INET)) { - if (nm_device_ipv4_sysctl_get_uint32 (self, "rp_filter", 0) != 1) { + if (nm_device_ipv4_sysctl_get_effective_uint32 (self, "rp_filter", 0) != 1) { /* Don't touch the rp_filter if it's not strict. */ return; } @@ -5881,7 +5893,7 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - nm_clear_g_source (&priv->dhcp4.restart_id); + nm_clear_g_source (&priv->dhcp4.grace_id); g_clear_pointer (&priv->dhcp4.pac_url, g_free); if (priv->dhcp4.client) { @@ -6026,20 +6038,17 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config) } static gboolean -dhcp4_restart_cb (gpointer user_data) +dhcp4_grace_period_expired (gpointer user_data) { NMDevice *self = user_data; - NMDevicePrivate *priv; - - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - priv = NM_DEVICE_GET_PRIVATE (self); - priv->dhcp4.restart_id = 0; + _LOGI (LOGD_DHCP4, "DHCPv4: grace period expired"); - if (dhcp4_start (self) == NM_ACT_STAGE_RETURN_FAILURE) - dhcp_schedule_restart (self, AF_INET, NULL); + nm_device_ip_method_failed (self, AF_INET, + NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + /* If the device didn't fail, the DHCP client will continue */ - return FALSE; + return G_SOURCE_REMOVE; } static void @@ -6047,44 +6056,48 @@ dhcp4_fail (NMDevice *self, gboolean timeout) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - _LOGD (LOGD_DHCP4, "DHCPv4 failed: timeout %d, num tries left %u", - timeout, priv->dhcp4.num_tries_left); - - dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); + _LOGD (LOGD_DHCP4, "DHCPv4 failed%s", timeout ? " (timeout)" : ""); - /* Don't fail if there are static addresses configured on - * the device, instead retry after some time. + /* Keep client running if there are static addresses configured + * on the interface. */ if ( priv->ip4_state == IP_DONE && priv->con_ip4_config - && nm_ip4_config_get_num_addresses (priv->con_ip4_config) > 0) { - dhcp_schedule_restart (self, AF_INET, "device has IP addresses"); + && nm_ip4_config_get_num_addresses (priv->con_ip4_config) > 0) + goto clear_config; + + /* Fail the method in case of timeout or failure during initial + * configuration. + */ + if ( !priv->dhcp4.was_active + && (timeout || priv->ip4_state == IP_CONF)) { + dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); + nm_device_activate_schedule_ip4_config_timeout (self); return; } - if ( priv->dhcp4.num_tries_left == DHCP_NUM_TRIES_MAX - && (timeout || (priv->ip4_state == IP_CONF)) - && !priv->dhcp4.was_active) - nm_device_activate_schedule_ip4_config_timeout (self); - else if ( priv->dhcp4.num_tries_left < DHCP_NUM_TRIES_MAX - || priv->ip4_state == IP_DONE - || priv->dhcp4.was_active) { - /* Don't fail immediately when the lease expires but try to - * restart DHCP for a predefined number of times. - */ - if (priv->dhcp4.num_tries_left) { - priv->dhcp4.num_tries_left--; - dhcp_schedule_restart (self, AF_INET, "lease expired"); - } else { - nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - /* We failed the ipv4 method but schedule again the retries if the ipv6 method is - * configured, keeping the connection up. - */ - if (nm_device_get_state (self) != NM_DEVICE_STATE_FAILED) - dhcp_schedule_restart (self, AF_INET, "renewal failed"); - } - } else - g_warn_if_reached (); + /* In any other case (expired lease, assumed connection, etc.), + * start a grace period in which we keep the client running, + * hoping that it will regain a lease. + */ + if (!priv->dhcp4.grace_id) { + priv->dhcp4.grace_id = g_timeout_add_seconds (DHCP_GRACE_PERIOD_SEC, + dhcp4_grace_period_expired, + self); + _LOGI (LOGD_DHCP4, + "DHCPv4: %u seconds grace period started", + DHCP_GRACE_PERIOD_SEC); + goto clear_config; + } + return; + +clear_config: + /* The previous configuration is no longer valid */ + if (priv->dhcp4.config) { + nm_exported_object_clear_and_unexport (&priv->dhcp4.config); + priv->dhcp4.config = nm_dhcp4_config_new (); + _notify (self, PROP_DHCP4_CONFIG); + } } static void @@ -6124,6 +6137,8 @@ dhcp4_state_changed (NMDhcpClient *client, break; } + nm_clear_g_source (&priv->dhcp4.grace_id); + /* After some failures, we have been able to renew the lease: * update the ip state */ @@ -6136,7 +6151,6 @@ dhcp4_state_changed (NMDhcpClient *client, nm_dhcp4_config_set_options (priv->dhcp4.config, options); _notify (self, PROP_DHCP4_CONFIG); - priv->dhcp4.num_tries_left = DHCP_NUM_TRIES_MAX; if (priv->ip4_state == IP_CONF) { connection = nm_device_get_applied_connection (self); @@ -6507,7 +6521,6 @@ act_stage3_ip4_config_start (NMDevice *self, } method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - priv->dhcp4.num_tries_left = DHCP_NUM_TRIES_MAX; /* Start IPv4 addressing based on the method requested */ if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) { @@ -6562,7 +6575,7 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; g_clear_object (&priv->dhcp6.ip6_config); g_clear_pointer (&priv->dhcp6.event_id, g_free); - nm_clear_g_source (&priv->dhcp6.restart_id); + nm_clear_g_source (&priv->dhcp6.grace_id); if (priv->dhcp6.client) { nm_clear_g_signal_handler (priv->dhcp6.client, &priv->dhcp6.state_sigid); @@ -6746,53 +6759,17 @@ dhcp6_lease_change (NMDevice *self) } static gboolean -dhcp6_restart_cb (gpointer user_data) +dhcp6_grace_period_expired (gpointer user_data) { NMDevice *self = user_data; - NMDevicePrivate *priv; - - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - priv = NM_DEVICE_GET_PRIVATE (self); - priv->dhcp6.restart_id = 0; - if (!dhcp6_start (self, FALSE)) - dhcp_schedule_restart (self, AF_INET6, NULL); + _LOGI (LOGD_DHCP6, "DHCPv6: grace period expired"); - return FALSE; -} - -static void -dhcp_schedule_restart (NMDevice *self, - int addr_family, - const char *reason) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint tries_left; - char tries_str[255]; - - nm_assert_addr_family (addr_family); - - tries_left = (addr_family == AF_INET) - ? priv->dhcp4.num_tries_left - : priv->dhcp6.num_tries_left; + nm_device_ip_method_failed (self, AF_INET6, + NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + /* If the device didn't fail, the DHCP client will continue */ - _LOGI ((addr_family == AF_INET) ? LOGD_DHCP4 : LOGD_DHCP6, - "scheduling DHCPv%c restart in %u seconds%s%s%s%s", - nm_utils_addr_family_to_char (addr_family), - DHCP_RESTART_TIMEOUT, - (tries_left != DHCP_NUM_TRIES_MAX) - ? nm_sprintf_buf (tries_str, ", %u tries left", tries_left + 1) - : "", - NM_PRINT_FMT_QUOTED (reason, " (reason: ", reason, ")", "")); - - if (addr_family == AF_INET) { - priv->dhcp4.restart_id = g_timeout_add_seconds (DHCP_RESTART_TIMEOUT, - dhcp4_restart_cb, self); - } else { - priv->dhcp6.restart_id = g_timeout_add_seconds (DHCP_RESTART_TIMEOUT, - dhcp6_restart_cb, self); - } + return G_SOURCE_REMOVE; } static void @@ -6801,51 +6778,57 @@ dhcp6_fail (NMDevice *self, gboolean timeout) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean is_dhcp_managed; - _LOGD (LOGD_DHCP6, "DHCPv6 failed: timeout %d, num tries left %u", - timeout, priv->dhcp6.num_tries_left); + _LOGD (LOGD_DHCP6, "DHCPv6 failed%s", timeout ? " (timeout)" : ""); is_dhcp_managed = (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED); - dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); - if (is_dhcp_managed || priv->dhcp6.num_tries_left < DHCP_NUM_TRIES_MAX) { - /* Don't fail if there are static addresses configured on - * the device, instead retry after some time. + if (is_dhcp_managed) { + /* Keep client running if there are static addresses configured + * on the interface. */ if ( priv->ip6_state == IP_DONE && priv->con_ip6_config - && nm_ip6_config_get_num_addresses (priv->con_ip6_config)) { - dhcp_schedule_restart (self, AF_INET6, "device has IP addresses"); + && nm_ip6_config_get_num_addresses (priv->con_ip6_config)) + goto clear_config; + + /* Fail the method in case of timeout or failure during initial + * configuration. + */ + if ( !priv->dhcp6.was_active + && (timeout || priv->ip6_state == IP_CONF)) { + dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); + nm_device_activate_schedule_ip6_config_timeout (self); return; } - if ( priv->dhcp6.num_tries_left == DHCP_NUM_TRIES_MAX - && (timeout || (priv->ip6_state == IP_CONF)) - && !priv->dhcp6.was_active) - nm_device_activate_schedule_ip6_config_timeout (self); - else if ( priv->dhcp6.num_tries_left < DHCP_NUM_TRIES_MAX - || priv->ip6_state == IP_DONE - || priv->dhcp6.was_active) { - /* Don't fail immediately when the lease expires but try to - * restart DHCP for a predefined number of times. - */ - if (priv->dhcp6.num_tries_left) { - priv->dhcp6.num_tries_left--; - dhcp_schedule_restart (self, AF_INET6, "lease expired"); - } else { - nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - /* We failed the ipv6 method but schedule again the retries if the ipv4 method is - * configured, keeping the connection up. - */ - if (nm_device_get_state (self) != NM_DEVICE_STATE_FAILED) - dhcp_schedule_restart (self, AF_INET6, "renewal failed"); - } - } else - g_warn_if_reached (); + /* In any other case (expired lease, assumed connection, etc.), + * start a grace period in which we keep the client running, + * hoping that it will regain a lease. + */ + if (!priv->dhcp6.grace_id) { + priv->dhcp6.grace_id = g_timeout_add_seconds (DHCP_GRACE_PERIOD_SEC, + dhcp6_grace_period_expired, + self); + _LOGI (LOGD_DHCP6, + "DHCPv6: %u seconds grace period started", + DHCP_GRACE_PERIOD_SEC); + goto clear_config; + } } else { /* not a hard failure; just live with the RA info */ + dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); if (priv->ip6_state == IP_CONF) nm_device_activate_schedule_ip6_config_result (self); } + return; + +clear_config: + /* The previous configuration is no longer valid */ + if (priv->dhcp6.config) { + nm_exported_object_clear_and_unexport (&priv->dhcp6.config); + priv->dhcp6.config = nm_dhcp6_config_new (); + _notify (self, PROP_DHCP6_CONFIG); + } } static void @@ -6881,6 +6864,7 @@ dhcp6_state_changed (NMDhcpClient *client, switch (state) { case NM_DHCP_STATE_BOUND: + nm_clear_g_source (&priv->dhcp6.grace_id); /* If the server sends multiple IPv6 addresses, we receive a state * changed event for each of them. Use the event ID to merge IPv6 * addresses from the same transaction into a single configuration. @@ -6911,8 +6895,6 @@ dhcp6_state_changed (NMDhcpClient *client, if (priv->ip6_state == IP_FAIL) _set_ip_state (self, AF_INET6, IP_CONF); - priv->dhcp6.num_tries_left = DHCP_NUM_TRIES_MAX; - if (priv->ip6_state == IP_CONF) { if (priv->dhcp6.ip6_config == NULL) { nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_DHCP_FAILED); @@ -8090,8 +8072,6 @@ act_stage3_ip6_config_start (NMDevice *self, } priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; - priv->dhcp6.num_tries_left = DHCP_NUM_TRIES_MAX; - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) { diff --git a/src/devices/tests/test-arping.c b/src/devices/tests/test-arping.c index bcbc1140..f0537545 100644 --- a/src/devices/tests/test-arping.c +++ b/src/devices/tests/test-arping.c @@ -65,12 +65,22 @@ test_arping_common (test_fixture *fixture, TestInfo *info) gs_unref_object NMArpingManager *manager = NULL; GMainLoop *loop; int i; + const guint WAIT_TIME_OPTIMISTIC = 50; + guint wait_time; + gulong signal_id; if (!nm_utils_find_helper ("arping", NULL, NULL)) { g_test_skip ("arping binary is missing"); return; } + /* first, try with a short waittime. We hope that this is long enough + * to successfully complete the test. Only if that's not the case, we + * assume the computer is currently busy (high load) and we retry with + * a longer timeout. */ + wait_time = WAIT_TIME_OPTIMISTIC; +again: + manager = nm_arping_manager_new (fixture->ifindex0); g_assert (manager != NULL); @@ -83,18 +93,32 @@ test_arping_common (test_fixture *fixture, TestInfo *info) } loop = g_main_loop_new (NULL, FALSE); - g_signal_connect (manager, NM_ARPING_MANAGER_PROBE_TERMINATED, - G_CALLBACK (arping_manager_probe_terminated), loop); - g_assert (nm_arping_manager_start_probe (manager, 250, NULL)); + signal_id = g_signal_connect (manager, NM_ARPING_MANAGER_PROBE_TERMINATED, + G_CALLBACK (arping_manager_probe_terminated), loop); + g_assert (nm_arping_manager_start_probe (manager, wait_time, NULL)); g_assert (nmtst_main_loop_run (loop, 2000)); + g_signal_handler_disconnect (manager, signal_id); + g_main_loop_unref (loop); for (i = 0; info->addresses[i]; i++) { - g_assert_cmpint (nm_arping_manager_check_address (manager, info->addresses[i]), - ==, - info->expected_result[i]); + gboolean val; + + val = nm_arping_manager_check_address (manager, info->addresses[i]); + if (val == info->expected_result[i]) + continue; + + if (wait_time == WAIT_TIME_OPTIMISTIC) { + /* probably we just had a glitch and the system took longer than + * expected. Re-verify with a large timeout this time. */ + wait_time = 1000; + g_clear_object (&manager); + goto again; + } + + g_error ("expected check for address #%d (%s) to %s, but it didn't", + i, nm_utils_inet4_ntop (info->addresses[i], NULL), + info->expected_result[i] ? "detect no duplicated" : "detect a duplicate"); } - - g_main_loop_unref (loop); } static void |