diff options
Diffstat (limited to 'src/ip6-manager/nm-ip6-manager.c')
| -rw-r--r-- | src/ip6-manager/nm-ip6-manager.c | 57 |
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) { |