static void check_addrconf_complete (NMIP6Device *device) { CallbackInfo *info; if (!device->addrconf_complete) { /* Managed mode (ie DHCP only) short-circuits automatic addrconf, so * we don't bother waiting for the device's target state to be reached * when the RA requests managed mode. */ if ( (device->state >= device->target_state) || (device->dhcp_opts == IP6_DHCP_OPT_MANAGED)) { /* device->finish_addrconf_id may currently be a timeout * rather than an idle, so we remove the existing source. */ if (device->finish_addrconf_id) g_source_remove (device->finish_addrconf_id); nm_log_dbg (LOGD_IP6, "(%s): reached target state or Managed-mode requested (state '%s') (dhcp opts 0x%X)", device->iface, state_to_string (device->state), device->dhcp_opts); info = callback_info_new (device, TRUE); device->finish_addrconf_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, finish_addrconf, info, (GDestroyNotify) g_free); } } else { if (!device->config_changed_id) { gboolean success = TRUE; /* If for some reason an RA-provided address disappeared, we need * to make sure we fail the connection as it's no longer valid. */ if ( (device->state == NM_IP6_DEVICE_GOT_ADDRESS) && (device->target_state == NM_IP6_DEVICE_GOT_ADDRESS) && !device->has_nonlinklocal) { nm_log_dbg (LOGD_IP6, "(%s): RA-provided address no longer found", device->iface); success = FALSE; } info = callback_info_new (device, success); device->config_changed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, emit_config_changed, info, (GDestroyNotify) g_free); } } nm_log_dbg (LOGD_IP6, "(%s): dhcp_opts checked (state %s)", device->iface, state_to_string (device->state)); }
void nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, int ifindex) { NMIP6ManagerPrivate *priv; NMIP6Device *device; CallbackInfo *info; g_return_if_fail (NM_IS_IP6_MANAGER (manager)); g_return_if_fail (ifindex > 0); priv = NM_IP6_MANAGER_GET_PRIVATE (manager); device = (NMIP6Device *) g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex)); g_return_if_fail (device != NULL); nm_log_info (LOGD_IP6, "Activation (%s) Beginning IP6 addrconf.", device->iface); device->addrconf_complete = FALSE; device->ra_flags = 0; /* Set up a timeout on the transaction to kill it after the timeout */ info = callback_info_new (device, FALSE); device->finish_addrconf_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, NM_IP6_TIMEOUT, finish_addrconf, info, (GDestroyNotify) g_free); /* Bounce IPv6 on the interface to ensure the kernel will start looking for * new RAs; there doesn't seem to be a better way to do this right now. */ if (device->target_state >= NM_IP6_DEVICE_GOT_ADDRESS) { nm_utils_do_sysctl (device->disable_ip6_path, "1"); /* 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"); } device->ip6flags_poll_id = g_timeout_add_seconds (1, poll_ip6_flags, priv->monitor); /* Kick off the initial IPv6 flags request */ nm_netlink_monitor_request_ip6_info (priv->monitor, NULL); /* Sync flags, etc, from netlink; this will also notice if the * device is already fully configured and schedule the * ADDRCONF_COMPLETE signal in that case. */ nm_ip6_device_sync_from_netlink (device); }
static void nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed) { NMIP6Manager *manager = device->manager; NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager); struct rtnl_addr *rtnladdr; struct nl_addr *nladdr; struct in6_addr *addr; CallbackInfo *info; guint dhcp_opts = IP6_DHCP_OPT_NONE; gboolean found_linklocal = FALSE, found_other = FALSE; nm_log_dbg (LOGD_IP6, "(%s): syncing with netlink (ra_flags 0x%X) (state/target '%s'/'%s')", device->iface, device->ra_flags, state_to_string (device->state), state_to_string (device->target_state)); /* Look for any IPv6 addresses the kernel may have set for the device */ for (rtnladdr = (struct rtnl_addr *) nl_cache_get_first (priv->addr_cache); rtnladdr; rtnladdr = (struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) rtnladdr)) { char buf[INET6_ADDRSTRLEN]; if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex) continue; nladdr = rtnl_addr_get_local (rtnladdr); if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6) continue; addr = nl_addr_get_binary_addr (nladdr); if (inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN) > 0) { nm_log_dbg (LOGD_IP6, "(%s): netlink address: %s", device->iface, buf); } if (IN6_IS_ADDR_LINKLOCAL (addr)) { if (device->state == NM_IP6_DEVICE_UNCONFIGURED) device->state = NM_IP6_DEVICE_GOT_LINK_LOCAL; found_linklocal = TRUE; } else { if (device->state < NM_IP6_DEVICE_GOT_ADDRESS) device->state = NM_IP6_DEVICE_GOT_ADDRESS; found_other = TRUE; } } /* There might be a LL address hanging around on the interface from * before in the initial run, but if it goes away later, make sure we * regress from GOT_LINK_LOCAL back to UNCONFIGURED. */ if ((device->state == NM_IP6_DEVICE_GOT_LINK_LOCAL) && !found_linklocal) device->state = NM_IP6_DEVICE_UNCONFIGURED; nm_log_dbg (LOGD_IP6, "(%s): addresses synced (state %s)", device->iface, state_to_string (device->state)); /* We only care about router advertisements if we want a real IPv6 address */ if ( (device->target_state == NM_IP6_DEVICE_GOT_ADDRESS) && (device->ra_flags & IF_RA_RCVD)) { if (device->state < NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT) device->state = NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT; if (device->ra_flags & IF_RA_MANAGED) { dhcp_opts = IP6_DHCP_OPT_MANAGED; nm_log_dbg (LOGD_IP6, "router advertisement deferred to DHCPv6"); } else if (device->ra_flags & IF_RA_OTHERCONF) { dhcp_opts = IP6_DHCP_OPT_OTHERCONF; nm_log_dbg (LOGD_IP6, "router advertisement requests parallel DHCPv6"); } } if (!device->addrconf_complete) { /* Managed mode (ie DHCP only) short-circuits automatic addrconf, so * we don't bother waiting for the device's target state to be reached * when the RA requests managed mode. */ if ( (device->state >= device->target_state) || (dhcp_opts == IP6_DHCP_OPT_MANAGED)) { /* device->finish_addrconf_id may currently be a timeout * rather than an idle, so we remove the existing source. */ if (device->finish_addrconf_id) g_source_remove (device->finish_addrconf_id); nm_log_dbg (LOGD_IP6, "(%s): reached target state or Managed-mode requested (state '%s') (dhcp opts 0x%X)", device->iface, state_to_string (device->state), dhcp_opts); info = callback_info_new (device, dhcp_opts, TRUE); device->finish_addrconf_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, finish_addrconf, info, (GDestroyNotify) g_free); } } else if (config_changed) { if (!device->config_changed_id) { gboolean success = TRUE; /* If for some reason an RA-provided address disappeared, we need * to make sure we fail the connection as it's no longer valid. */ if ( (device->state == NM_IP6_DEVICE_GOT_ADDRESS) && (device->target_state == NM_IP6_DEVICE_GOT_ADDRESS) && !found_other) { nm_log_dbg (LOGD_IP6, "(%s): RA-provided address no longer valid", device->iface); success = FALSE; } info = callback_info_new (device, dhcp_opts, success); device->config_changed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, emit_config_changed, info, (GDestroyNotify) g_free); } } }