summary refs log tree commit diff
path: root/src/ip6-manager/nm-ip6-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ip6-manager/nm-ip6-manager.c')
-rw-r--r--src/ip6-manager/nm-ip6-manager.c57
1 files changed, 47 insertions, 10 deletions
diff --git a/src/ip6-manager/nm-ip6-manager.c b/src/ip6-manager/nm-ip6-manager.c
index c2871e4a..340867dd 100644
--- a/src/ip6-manager/nm-ip6-manager.c
+++ b/src/ip6-manager/nm-ip6-manager.c
@@ -455,7 +455,7 @@ finish_addrconf (gpointer user_data)
 
 		nm_ip6_manager_cancel_addrconf (manager, ifindex);
 		g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
-		               ifindex, device->dhcp_opts, FALSE);
+		               ifindex, IP6_DHCP_OPT_NONE, FALSE);
 	}
 
 	return FALSE;
@@ -702,7 +702,7 @@ check_addresses (NMIP6Device *device)
 				device_set_state (device, NM_IP6_DEVICE_GOT_LINK_LOCAL);
 			device->has_linklocal = TRUE;
 		} else {
-			if (device->state < NM_IP6_DEVICE_GOT_ADDRESS)
+			if (device->state == NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT)
 				device_set_state (device, NM_IP6_DEVICE_GOT_ADDRESS);
 			device->has_nonlinklocal = TRUE;
 		}
@@ -806,8 +806,8 @@ nm_ip6_device_sync_from_netlink (NMIP6Device *device)
 	            state_to_string (device->state),
 	            state_to_string (device->target_state));
 
-	check_addresses (device);
 	check_ra_flags (device);
+	check_addresses (device);
 	check_addrconf_complete (device);
 }
 
@@ -1407,6 +1407,46 @@ poll_ip6_flags (gpointer user_data)
 	return TRUE;
 }
 
+#define FIRST_ROUTE(m) ((struct rtnl_route *) nl_cache_get_first (m))
+#define NEXT_ROUTE(m) ((struct rtnl_route *) nl_cache_get_next ((struct nl_object *) m))
+
+#define FIRST_ADDR(m) ((struct rtnl_addr *) nl_cache_get_first (m))
+#define NEXT_ADDR(m) ((struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) m))
+
+static void
+wait_for_no_addresses (NMIP6Manager *self, NMIP6Device *device)
+{
+	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (self);
+	guint64 now, end;
+	gboolean has_addrs = TRUE;
+
+	now = end = g_get_real_time ();
+	end += (G_USEC_PER_SEC * 3);
+
+	while (has_addrs && now < end) {
+		struct rtnl_addr *rtnladdr;
+		struct nl_addr *nladdr;
+
+		nl_cache_refill (priv->nlh, priv->addr_cache);
+		for (has_addrs = FALSE, rtnladdr = FIRST_ADDR (priv->addr_cache);
+		     rtnladdr;
+		     rtnladdr = NEXT_ADDR (rtnladdr)) {
+
+			nladdr = rtnl_addr_get_local (rtnladdr);
+			if (   rtnl_addr_get_ifindex (rtnladdr) == device->ifindex
+			    && nladdr
+			    && nl_addr_get_family (nladdr) == AF_INET6) {
+				/* Still IPv6 addresses on the interface */
+				has_addrs = TRUE;
+				nm_log_dbg (LOGD_IP6, "(%s) waiting for cleared IPv6 addresses", device->iface);
+				g_usleep (100);
+				now = g_get_real_time ();
+				break;
+			}
+		}
+	}
+}
+
 void
 nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, int ifindex)
 {
@@ -1440,7 +1480,10 @@ nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, int ifindex)
 	 */
 	if (device->target_state >= NM_IP6_DEVICE_GOT_ADDRESS) {
 		nm_utils_do_sysctl (device->disable_ip6_path, "1");
-		g_usleep (200);
+		/* Wait until all existing IPv6 addresses have been removed from the link,
+		 * to ensure they don't confuse our IPv6 addressing state machine.
+		 */
+		wait_for_no_addresses (manager, device);
 		nm_utils_do_sysctl (device->disable_ip6_path, "0");
 	}
 
@@ -1466,12 +1509,6 @@ nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager, int ifindex)
 	                     GINT_TO_POINTER (ifindex));
 }
 
-#define FIRST_ROUTE(m) ((struct rtnl_route *) nl_cache_get_first (m))
-#define NEXT_ROUTE(m) ((struct rtnl_route *) nl_cache_get_next ((struct nl_object *) m))
-
-#define FIRST_ADDR(m) ((struct rtnl_addr *) nl_cache_get_first (m))
-#define NEXT_ADDR(m) ((struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) m))
-
 NMIP6Config *
 nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, int ifindex)
 {