diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2015-11-19 11:44:46 +0100 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2015-11-20 17:36:20 +0100 |
commit | 049a006ad9b4d9ef378a05c01887f526ccaa140e (patch) | |
tree | b745d806f3a91bea5aeca12ba529efc8ac35b808 | |
parent | 21a9e92aac09cb6fc2bea92a67ee5d719efaf6e0 (diff) | |
download | NetworkManager-bg/ipv4-dad-wip-rh1259063.tar.gz |
device: add support for IPv4 duplicate address detectionbg/ipv4-dad-wip-rh1259063
-rw-r--r-- | src/devices/nm-device.c | 311 |
1 files changed, 158 insertions, 153 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 0f7f25793e..75c081f148 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -64,6 +64,7 @@ #include "nm-lldp-listener.h" #include "sd-ipv4ll.h" #include "nm-audit-manager.h" +#include "nm-arping-manager.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF (NMDevice); @@ -139,6 +140,7 @@ enum { #define PENDING_ACTION_AUTOCONF6 "autoconf6" typedef void (*ActivationHandleFunc) (NMDevice *self); +typedef void (*ConfigCommitResultFunc) (NMDevice *self, gboolean result, NMDeviceStateReason reason); typedef struct { ActivationHandleFunc func; @@ -274,6 +276,8 @@ typedef struct { NMIP4Config * dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */ NMIP4Config * ext_ip4_config; /* Stuff added outside NM */ NMIP4Config * wwan_ip4_config; /* WWAN configuration */ + NMIP4Config * composite_ip4_config; + GSList * vpn4_configs; /* VPNs which use this device */ struct { gboolean v4_has; @@ -287,14 +291,16 @@ typedef struct { gboolean v4_commit_first_time; gboolean v6_commit_first_time; + ConfigCommitResultFunc v4_commit_cb; + /* DHCPv4 tracking */ NMDhcpClient * dhcp4_client; gulong dhcp4_state_sigid; NMDhcp4Config * dhcp4_config; guint dhcp4_restart_id; - guint arp_round2_id; - PingInfo gw_ping; + NMArpingManager * arping_manager; + PingInfo gw_ping; /* dnsmasq stuff for shared connections */ NMDnsMasqManager *dnsmasq_manager; @@ -368,7 +374,7 @@ static gboolean nm_device_set_ip4_config (NMDevice *self, static gboolean ip4_config_merge_and_apply (NMDevice *self, NMIP4Config *config, gboolean commit, - NMDeviceStateReason *out_reason); + ConfigCommitResultFunc commit_cb); static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *config, @@ -3305,6 +3311,18 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla) return config; } +static void +ipv4ll_merge_cb (NMDevice *self, gboolean result, NMDeviceStateReason reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (!result) { + _LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change."); + priv->ip4_state = IP_FAIL; + nm_device_check_ip_failed (self, FALSE); + } +} + #define IPV4LL_NETWORK (htonl (0xA9FE0000L)) #define IPV4LL_NETMASK (htonl (0xFFFF0000L)) @@ -3359,11 +3377,7 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) nm_clear_g_source (&priv->ipv4ll_timeout); nm_device_activate_schedule_ip4_config_result (self, config); } else if (priv->ip4_state == IP_DONE) { - if (!ip4_config_merge_and_apply (self, config, TRUE, NULL)) { - _LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change."); - priv->ip4_state = IP_FAIL; - nm_device_check_ip_failed (self, FALSE); - } + ip4_config_merge_and_apply (self, config, TRUE, ipv4ll_merge_cb); } else g_assert_not_reached (); @@ -3599,11 +3613,72 @@ _ip4_config_merge_default (gpointer value, gpointer user_data) nm_ip4_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT); } +static void +arping_manager_dad_terminated (NMArpingManager *arping_manager, NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean routes_full_sync, success; + const guint32 default_route_metric = nm_device_get_ip4_route_metric (self); + NMDeviceStateReason reason; + const NMPlatformIP4Address *address; + guint num_static = 0, num_static_failed = 0; + int i; + + g_assert (priv->composite_ip4_config); + + for (i = 0; i < nm_ip4_config_get_num_addresses (priv->composite_ip4_config); ) { + address = nm_ip4_config_get_address (priv->composite_ip4_config, i); + if (address->source == NM_IP_CONFIG_SOURCE_USER) { + num_static++; + success = nm_arping_manager_check_address (arping_manager, address->address); + if (!success) { + nm_ip4_config_del_address (priv->composite_ip4_config, i); + num_static_failed++; + continue; + } + } else if (address->source == NM_IP_CONFIG_SOURCE_DHCP) { + success = nm_arping_manager_check_address (arping_manager, address->address); + if (!success) { + _LOGI (LOGD_IP, "DHCP-provided address already in use"); + goto out; + } + } + i++; + } + + if (num_static && num_static_failed == num_static) { + _LOGI (LOGD_IP4, "all static addresses duplicated"); + success = FALSE; + goto out; + } + + if (NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit) + NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, priv->composite_ip4_config); + + routes_full_sync = priv->v4_commit_first_time + && !nm_device_uses_assumed_connection (self); + + success = nm_device_set_ip4_config (self, priv->composite_ip4_config , + default_route_metric, TRUE, + routes_full_sync, &reason); + + priv->v4_commit_first_time = FALSE; + nm_arping_manager_announce_addresses (arping_manager); + +out: + g_clear_object (&priv->composite_ip4_config); + + if (priv->v4_commit_cb) + priv->v4_commit_cb (self, success, reason); + + priv->v4_commit_cb = NULL; +} + static gboolean ip4_config_merge_and_apply (NMDevice *self, NMIP4Config *config, gboolean commit, - NMDeviceStateReason *out_reason) + ConfigCommitResultFunc commit_cb) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; @@ -3613,9 +3688,9 @@ ip4_config_merge_and_apply (NMDevice *self, const guint32 default_route_metric = nm_device_get_ip4_route_metric (self); guint32 gateway; gboolean connection_has_default_route, connection_is_never_default; - gboolean routes_full_sync; gboolean ignore_auto_routes = FALSE; gboolean ignore_auto_dns = FALSE; + NMSettingIPConfig *s_ip4; /* Merge all the configs into the composite config */ if (config) { @@ -3626,7 +3701,7 @@ ip4_config_merge_and_apply (NMDevice *self, /* Apply ignore-auto-routes and ignore-auto-dns settings */ connection = nm_device_get_applied_connection (self); if (connection) { - NMSettingIPConfig *s_ip4 = nm_connection_get_setting_ip4_config (connection); + s_ip4 = nm_connection_get_setting_ip4_config (connection); if (s_ip4) { ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip4); @@ -3757,35 +3832,60 @@ END_ADD_DEFAULT_ROUTE: priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) &priv->default_route.v4); } - /* Allow setting MTU etc */ if (commit) { - if (NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit) - NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite); - } + const NMPlatformIP4Address *address; + int timeout, i; + + priv->composite_ip4_config = composite; + priv->v4_commit_cb = commit_cb; + priv->arping_manager = nm_arping_manager_new (nm_device_get_iface (self)); + + for (i = 0; i < nm_ip4_config_get_num_addresses (composite); i++) { + address = nm_ip4_config_get_address (composite, i); + if (address->source == NM_IP_CONFIG_SOURCE_USER) + nm_arping_manager_add_address (priv->arping_manager, address->address, TRUE); + else if (address->source == NM_IP_CONFIG_SOURCE_DHCP) + nm_arping_manager_add_address (priv->arping_manager, address->address, FALSE); + } - routes_full_sync = commit - && priv->v4_commit_first_time - && !nm_device_uses_assumed_connection (self); + timeout = nm_setting_ip_config_get_dad_timeout (s_ip4); - success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, routes_full_sync, out_reason); - g_object_unref (composite); + if (timeout == 0) { + gs_free char *value = NULL; - if (commit) - priv->v4_commit_first_time = FALSE; - return success; + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "ipv4.dad-timeout", self); + /* Use 3 seconds timeout by default */ + timeout = _nm_utils_ascii_str_to_int64 (value, 10, -1, NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX, 3); + timeout = timeout == 0 ? 3 : timeout; + } + + if (timeout > 0) { + g_signal_connect (priv->arping_manager, NM_ARPING_MANAGER_DAD_TERMINATED, + G_CALLBACK (arping_manager_dad_terminated), + self); + + nm_arping_manager_start (priv->arping_manager, timeout, NULL); + } else + arping_manager_dad_terminated (priv->arping_manager, self); + } else { + success = nm_device_set_ip4_config (self, composite, default_route_metric, FALSE, FALSE, NULL); + g_object_unref (composite); + + return success; + } + + return TRUE; } static void -dhcp4_lease_change (NMDevice *self, NMIP4Config *config) +dhcp4_lease_change_cb (NMDevice *self, gboolean result, NMDeviceStateReason reason) { - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - - g_return_if_fail (config != NULL); - - if (!ip4_config_merge_and_apply (self, config, TRUE, &reason)) { + if (!result) { _LOGW (LOGD_DHCP4, "failed to update IPv4 config for DHCP change."); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); } else { + nm_device_update_metered (self); /* Notify dispatcher scripts of new DHCP4 config */ nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE, nm_device_get_settings_connection (self), @@ -3884,10 +3984,8 @@ dhcp4_state_changed (NMDhcpClient *client, if (priv->ip4_state == IP_CONF) nm_device_activate_schedule_ip4_config_result (self, ip4_config); - else if (priv->ip4_state == IP_DONE) { - dhcp4_lease_change (self, ip4_config); - nm_device_update_metered (self); - } + else if (priv->ip4_state == IP_DONE) + ip4_config_merge_and_apply (self, ip4_config, TRUE, dhcp4_lease_change_cb); break; case NM_DHCP_STATE_TIMEOUT: dhcp4_fail (self, TRUE); @@ -6031,121 +6129,13 @@ start_sharing (NMDevice *self, NMIP4Config *config) return TRUE; } -static void -send_arps (NMDevice *self, const char *mode_arg) -{ - const char *argv[] = { NULL, mode_arg, "-q", "-I", nm_device_get_ip_iface (self), "-c", "1", NULL, NULL }; - int ip_arg = G_N_ELEMENTS (argv) - 2; - NMConnection *connection; - NMSettingIPConfig *s_ip4; - int i, num; - NMIPAddress *addr; - GError *error = NULL; - - connection = nm_device_get_applied_connection (self); - if (!connection) - return; - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (!s_ip4) - return; - num = nm_setting_ip_config_get_num_addresses (s_ip4); - if (num == 0) - return; - - argv[0] = nm_utils_find_helper ("arping", NULL, NULL); - if (!argv[0]) { - _LOGW (LOGD_DEVICE | LOGD_IP4, "arping could not be found; no ARPs will be sent"); - return; - } - - for (i = 0; i < num; i++) { - gs_free char *tmp_str = NULL; - gboolean success; - - addr = nm_setting_ip_config_get_address (s_ip4, i); - argv[ip_arg] = nm_ip_address_get_address (addr); - - _LOGD (LOGD_DEVICE | LOGD_IP4, - "arping: run %s", (tmp_str = g_strjoinv (" ", (char **) argv))); - success = g_spawn_async (NULL, (char **) argv, NULL, - G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, - NULL, NULL, NULL, &error); - if (!success) { - _LOGW (LOGD_DEVICE | LOGD_IP4, - "arping: could not send ARP for local address %s: %s", - argv[ip_arg], error->message); - g_clear_error (&error); - } - } -} - -static gboolean -arp_announce_round2 (gpointer self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - priv->arp_round2_id = 0; - - if ( priv->state >= NM_DEVICE_STATE_IP_CONFIG - && priv->state <= NM_DEVICE_STATE_ACTIVATED) - send_arps (self, "-U"); - - return G_SOURCE_REMOVE; -} - -static void -arp_cleanup (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->arp_round2_id) { - g_source_remove (priv->arp_round2_id); - priv->arp_round2_id = 0; - } -} - -static void -arp_announce (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; - NMSettingIPConfig *s_ip4; - int num; - - arp_cleanup (self); - - /* We only care about manually-configured addresses; DHCP- and autoip-configured - * ones should already have been seen on the network at this point. - */ - connection = nm_device_get_applied_connection (self); - if (!connection) - return; - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (!s_ip4) - return; - num = nm_setting_ip_config_get_num_addresses (s_ip4); - if (num == 0) - return; - - send_arps (self, "-A"); - priv->arp_round2_id = g_timeout_add_seconds (2, arp_announce_round2, self); -} +static void stage5_ip4_config_commit_cb (NMDevice *self, gboolean result, NMDeviceStateReason reason); static void activate_stage5_ip4_config_commit (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActRequest *req; - const char *method; - NMConnection *connection; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; int ip_ifindex; - req = nm_device_get_act_request (self); - g_assert (req); - connection = nm_act_request_get_applied_connection (req); - g_assert (connection); - /* Interface must be IFF_UP before IP config can be applied */ ip_ifindex = nm_device_get_ip_ifindex (self); if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex) && !nm_device_uses_assumed_connection (self)) { @@ -6154,13 +6144,28 @@ activate_stage5_ip4_config_commit (NMDevice *self) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); } - /* NULL to use the existing priv->dev_ip4_config */ - if (!ip4_config_merge_and_apply (self, NULL, TRUE, &reason)) { + ip4_config_merge_and_apply (self, NULL, TRUE, stage5_ip4_config_commit_cb); +} + +static void +stage5_ip4_config_commit_cb (NMDevice *self, gboolean result, NMDeviceStateReason reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActRequest *req; + NMConnection *connection; + const char *method; + + if (!result) { _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Commit) failed"); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); return; } + req = nm_device_get_act_request (self); + g_assert (req); + connection = nm_act_request_get_applied_connection (req); + g_assert (connection); + /* Start IPv4 sharing if we need it */ method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); @@ -6176,8 +6181,8 @@ activate_stage5_ip4_config_commit (NMDevice *self) * dispatcher scripts get the DHCP lease information. */ if ( priv->dhcp4_client - && nm_device_activate_ip4_state_in_conf (self) - && (nm_device_get_state (self) > NM_DEVICE_STATE_IP_CONFIG)) { + && nm_device_activate_ip4_state_in_conf (self) + && (nm_device_get_state (self) > NM_DEVICE_STATE_IP_CONFIG)) { /* Notify dispatcher scripts of new DHCP4 config */ nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE, nm_device_get_settings_connection (self), @@ -6188,8 +6193,6 @@ activate_stage5_ip4_config_commit (NMDevice *self) NULL); } - arp_announce (self); - /* Enter the IP_CHECK state if this is the first method to complete */ priv->ip4_state = IP_DONE; @@ -6945,8 +6948,7 @@ nm_device_replace_vpn4_config (NMDevice *self, NMIP4Config *old, NMIP4Config *co return; /* NULL to use existing configs */ - if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL)) - _LOGW (LOGD_IP4, "failed to set VPN routes for device"); + ip4_config_merge_and_apply (self, NULL, TRUE, NULL); } void @@ -6962,8 +6964,7 @@ nm_device_set_wwan_ip4_config (NMDevice *self, NMIP4Config *config) priv->wwan_ip4_config = g_object_ref (config); /* NULL to use existing configs */ - if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL)) - _LOGW (LOGD_IP4, "failed to set WWAN IPv4 configuration"); + ip4_config_merge_and_apply (self, NULL, TRUE, NULL); } static gboolean @@ -8617,12 +8618,16 @@ _cleanup_ip_pre (NMDevice *self, CleanupType cleanup_type) nm_device_queued_ip_config_change_clear (self); dhcp4_cleanup (self, cleanup_type, FALSE); - arp_cleanup (self); dhcp6_cleanup (self, cleanup_type, FALSE); linklocal6_cleanup (self); addrconf6_cleanup (self); dnsmasq_cleanup (self); ipv4ll_cleanup (self); + + if (priv->arping_manager) { + nm_arping_manager_reset (priv->arping_manager); + g_clear_object (&priv->arping_manager); + } } static void |