diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index ce6ad5d984..bcc92eabd5 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -334,6 +334,7 @@ typedef struct _NMDevicePrivate { GSList * vpn6_configs; /* VPNs which use this device */ bool nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */ guint32 ip6_mtu; + NMIP6Config * dad6_ip6_config; NMRDisc * rdisc; gulong rdisc_changed_id; @@ -7091,6 +7092,57 @@ nm_device_activate_ip4_state_done (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_DONE; } +/* + * Returns a NMIP6Config containing NM-configured addresses which + * have the tentative flag, or NULL if none is present. + */ +static NMIP6Config * +dad6_get_pending_addresses (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMIP6Config *confs[] = { priv->ac_ip6_config, + priv->dhcp6.ip6_config, + priv->con_ip6_config, + priv->wwan_ip6_config }; + const NMPlatformIP6Address *addr, *pl_addr; + NMIP6Config *dad6_config = NULL; + guint i, j, num; + int ifindex; + + ifindex = nm_device_get_ip_ifindex (self); + g_return_val_if_fail (ifindex > 0, NULL); + + /* We are interested only in addresses that we have explicitly configured, + * not in externally added ones. + */ + for (i = 0; i < G_N_ELEMENTS (confs); i++) { + if (confs[i]) { + num = nm_ip6_config_get_num_addresses (confs[i]); + for (j = 0; j < num; j++) { + addr = nm_ip6_config_get_address (confs[i], j); + pl_addr = nm_platform_ip6_address_get (NM_PLATFORM_GET, + ifindex, + addr->address, + addr->plen); + if ( pl_addr + && NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_TENTATIVE) + && !NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_DADFAILED) + && !NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_OPTIMISTIC)) { + _LOGt (LOGD_DEVICE, "IPv6 DAD: pending address %s", + nm_platform_ip6_address_to_string (pl_addr, NULL, 0)); + + if (!dad6_config) + dad6_config = nm_ip6_config_new (ifindex); + + nm_ip6_config_add_address (dad6_config, pl_addr); + } + } + } + } + + return dad6_config; +} + static void activate_stage5_ip6_config_commit (NMDevice *self) { @@ -7132,13 +7184,20 @@ activate_stage5_ip6_config_commit (NMDevice *self) return; } } - nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE); nm_device_remove_pending_action (self, PENDING_ACTION_AUTOCONF6, FALSE); - /* Enter the IP_CHECK state if this is the first method to complete */ - priv->ip6_state = IP_DONE; - check_ip_done (self); + /* Check if we have to wait for DAD */ + if (priv->ip6_state == IP_CONF && !priv->dad6_ip6_config) { + priv->dad6_ip6_config = dad6_get_pending_addresses (self); + if (priv->dad6_ip6_config) { + _LOGD (LOGD_DEVICE | LOGD_IP6, "IPv6 DAD: waiting termination"); + } else { + /* No tentative addresses, proceed right away */ + priv->ip6_state = IP_DONE; + check_ip_done (self); + } + } } else { _LOGW (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) failed"); nm_device_ip_method_failed (self, AF_INET6, reason); @@ -7346,6 +7405,7 @@ _cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type) if (nm_clear_g_source (&priv->queued_ip6_config_id)) _LOGD (LOGD_DEVICE, "clearing queued IP6 config change"); + g_clear_object (&priv->dad6_ip6_config); dhcp6_cleanup (self, cleanup_type, FALSE); linklocal6_cleanup (self); addrconf6_cleanup (self); @@ -9218,7 +9278,7 @@ queued_ip6_config_change (gpointer user_data) if ( nm_platform_link_get (NM_PLATFORM_GET, priv->ifindex) && priv->state < NM_DEVICE_STATE_DEACTIVATING) { - /* Handle DAD falures */ + /* Handle DAD failures */ for (iter = priv->dad6_failed_addrs; iter; iter = g_slist_next (iter)) { NMPlatformIP6Address *addr = iter->data; @@ -9249,6 +9309,19 @@ queued_ip6_config_change (gpointer user_data) g_slist_free_full (priv->dad6_failed_addrs, g_free); priv->dad6_failed_addrs = NULL; + /* Check if DAD is still pending */ + if ( priv->ip6_state == IP_CONF + && priv->dad6_ip6_config + && priv->ext_ip6_config_captured) { + if (!nm_ip6_config_has_any_dad_pending (priv->ext_ip6_config_captured, + priv->dad6_ip6_config)) { + _LOGD (LOGD_DEVICE | LOGD_IP6, "IPv6 DAD terminated"); + g_clear_object (&priv->dad6_ip6_config); + priv->ip6_state = IP_DONE; + check_ip_done (self); + } + } + set_unmanaged_external_down (self, TRUE); return FALSE; @@ -10331,6 +10404,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) g_clear_object (&priv->ext_ip6_config_captured); g_clear_object (&priv->wwan_ip6_config); g_clear_object (&priv->ip6_config); + g_clear_object (&priv->dad6_ip6_config); g_slist_free_full (priv->vpn4_configs, g_object_unref); priv->vpn4_configs = NULL; diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 062b8c6616..706770c164 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -1421,6 +1421,47 @@ nm_ip6_config_get_address_first_nontentative (const NMIP6Config *config, gboolea return NULL; } +/** + * nm_ip6_config_has_dad_pending_addresses + * @self: configuration containing the addresses to check + * @candidates: configuration with the list of addresses we are + * interested in + * + * Check whether there are addresses with DAD pending in @self, that + * are also contained in @candidates. + * + * Returns: %TRUE if at least one matching address was found, %FALSE + * otherwise + */ +gboolean +nm_ip6_config_has_any_dad_pending (const NMIP6Config *self, + const NMIP6Config *candidates) +{ + const NMPlatformIP6Address *addr, *addr_c; + guint i, j, num, num_c; + + num = nm_ip6_config_get_num_addresses (self); + + for (i = 0; i < num; i++) { + addr = nm_ip6_config_get_address (self, i); + if ( NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_TENTATIVE) + && !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_DADFAILED) + && !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_OPTIMISTIC)) { + + num_c = nm_ip6_config_get_num_addresses (candidates); + + for (j = 0; j < num_c; j++) { + addr_c = nm_ip6_config_get_address (candidates, j); + if ( addresses_are_duplicate (addr, addr_c) + && addr->plen == addr_c->plen) + return TRUE; + } + } + } + + return FALSE; +} + /******************************************************************/ void diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index 0aba5e9ff2..c3f8d9f879 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -90,6 +90,8 @@ const NMPlatformIP6Address *nm_ip6_config_get_address (const NMIP6Config *config const NMPlatformIP6Address *nm_ip6_config_get_address_first_nontentative (const NMIP6Config *config, gboolean linklocal); gboolean nm_ip6_config_address_exists (const NMIP6Config *config, const NMPlatformIP6Address *address); gboolean nm_ip6_config_addresses_sort (NMIP6Config *config, NMSettingIP6ConfigPrivacy use_temporary); +gboolean nm_ip6_config_has_any_dad_pending (const NMIP6Config *self, + const NMIP6Config *candidates); /* Routes */ void nm_ip6_config_reset_routes (NMIP6Config *config);