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);
}
Esempio n. 3
0
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);
		}
	}
}