Example #1
0
static void
nm_ip6_manager_init (NMIP6Manager *manager)
{
	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);

	priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal,
	                                       NULL,
	                                       (GDestroyNotify) nm_ip6_device_destroy);

	priv->monitor = nm_netlink_monitor_get ();
	nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_IFADDR, NULL);
	nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_PREFIX, NULL);
	nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_ROUTE, NULL);
	nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_ND_USEROPT, NULL);
	nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_LINK, NULL);

	priv->netlink_id = g_signal_connect (priv->monitor, "notification",
	                                     G_CALLBACK (netlink_notification), manager);

	priv->nlh = nm_netlink_get_default_handle ();
	rtnl_addr_alloc_cache (priv->nlh, &priv->addr_cache);
	g_warn_if_fail (priv->addr_cache != NULL);
	rtnl_route_alloc_cache (priv->nlh, NETLINK_ROUTE, NL_AUTO_PROVIDE, &priv->route_cache);
	g_warn_if_fail (priv->route_cache != NULL);
}
static NMIP6Device *
process_address_change (NMIP6Manager *manager, struct nl_msg *msg)
{
	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
	NMIP6Device *device;
	struct nlmsghdr *hdr;
	struct rtnl_addr *rtnladdr;
	int old_size;

	hdr = nlmsg_hdr (msg);
	rtnladdr = NULL;
	nl_msg_parse (msg, ref_object, &rtnladdr);
	if (!rtnladdr) {
		nm_log_dbg (LOGD_IP6, "error processing netlink new/del address message");
		return NULL;
	}

	device = nm_ip6_manager_get_device (manager, rtnl_addr_get_ifindex (rtnladdr));

	old_size = nl_cache_nitems (priv->addr_cache);
	nl_cache_include (priv->addr_cache, (struct nl_object *)rtnladdr, NULL, NULL);

	/* The kernel will re-notify us of automatically-added addresses
	 * every time it gets another router advertisement. We only want
	 * to notify higher levels if we actually changed something.
	 */
	nm_log_dbg (LOGD_IP6, "(%s): address cache size: %d -> %d:",
		    device_get_iface (device), old_size, nl_cache_nitems (priv->addr_cache));
	dump_address_change (device, hdr, rtnladdr);
	rtnl_addr_put (rtnladdr);
	if (nl_cache_nitems (priv->addr_cache) == old_size)
		return NULL;

	return device;
}
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;
			}
		}
	}
}
Example #4
0
void
nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager, int ifindex)
{
	g_return_if_fail (NM_IS_IP6_MANAGER (manager));
	g_return_if_fail (ifindex > 0);

	g_hash_table_remove (NM_IP6_MANAGER_GET_PRIVATE (manager)->devices,
	                     GINT_TO_POINTER (ifindex));
}
static void
check_addresses (NMIP6Device *device)
{
	NMIP6Manager *manager = device->manager;
	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
	struct rtnl_addr *rtnladdr;
	struct nl_addr *nladdr;
	struct in6_addr *addr;

	/* Reset address information */
	device->has_linklocal = FALSE;
	device->has_nonlinklocal = FALSE;

	/* 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/%d",
			            device->iface, buf,
			            rtnl_addr_get_prefixlen (rtnladdr));
		}

		if (IN6_IS_ADDR_LINKLOCAL (addr)) {
			if (device->state == NM_IP6_DEVICE_UNCONFIGURED)
				device_set_state (device, NM_IP6_DEVICE_GOT_LINK_LOCAL);
			device->has_linklocal = TRUE;
		} else {
			if (device->state == NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT)
				device_set_state (device, NM_IP6_DEVICE_GOT_ADDRESS);
			device->has_nonlinklocal = 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) && !device->has_linklocal)
		device_set_state (device, NM_IP6_DEVICE_UNCONFIGURED);

	nm_log_dbg (LOGD_IP6, "(%s): addresses checked (state %s)",
		    device->iface, state_to_string (device->state));
}
static NMIP6Device *
nm_ip6_device_new (NMIP6Manager *manager,
                   int ifindex,
                   const guint8 *hwaddr,
                   guint hwaddr_len)
{
	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
	NMIP6Device *device;

	g_return_val_if_fail (ifindex > 0, NULL);
	g_return_val_if_fail (hwaddr != NULL, NULL);
	g_return_val_if_fail (hwaddr_len > 0, NULL);
	g_return_val_if_fail (hwaddr_len <= NM_UTILS_HWADDR_LEN_MAX, NULL);

	device = g_slice_new0 (NMIP6Device);
	if (!device) {
		nm_log_err (LOGD_IP6, "(%d): out of memory creating IP6 addrconf object.",
		            ifindex);
		return NULL;
	}

	device->ifindex = ifindex;
	device->iface = nm_netlink_index_to_iface (ifindex);
	if (!device->iface) {
		nm_log_err (LOGD_IP6, "(%d): could not find interface name from index.",
		            ifindex);
		goto error;
	}

	memcpy (device->hwaddr, hwaddr, hwaddr_len);
	device->hwaddr_len = hwaddr_len;

	device->manager = manager;

	device->rdnss_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));

	device->dnssl_domains = g_array_new (FALSE, FALSE, sizeof (NMIP6DNSSL));

	g_hash_table_replace (priv->devices, GINT_TO_POINTER (device->ifindex), device);

	/* and the original value of IPv6 enable/disable */
	device->disable_ip6_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/disable_ipv6",
	                                            device->iface);
	g_assert (device->disable_ip6_path);
	device->disable_ip6_save_valid = nm_utils_get_proc_sys_net_value_with_bounds (device->disable_ip6_path,
	                                                                              device->iface,
	                                                                              &device->disable_ip6_save,
	                                                                              0, 1);

	return device;

error:
	nm_ip6_device_destroy (device);
	return NULL;
}
Example #7
0
static NMIP6Device *
nm_ip6_manager_get_device (NMIP6Manager *manager, int ifindex)
{
	NMIP6ManagerPrivate *priv;

	g_return_val_if_fail (manager != NULL, NULL);
	g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL);

	priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
	return g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex));
}
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);
}
Example #9
0
static void
finalize (GObject *object)
{
	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (object);

	g_signal_handler_disconnect (priv->monitor, priv->netlink_id);

	g_hash_table_destroy (priv->devices);
	g_object_unref (priv->monitor);
	nl_cache_free (priv->addr_cache);
	nl_cache_free (priv->route_cache);

	singleton = NULL;

	G_OBJECT_CLASS (nm_ip6_manager_parent_class)->finalize (object);
}
Example #10
0
static NMIP6Manager *
nm_ip6_manager_new (void)
{
	NMIP6Manager *manager;
	NMIP6ManagerPrivate *priv;

	manager = g_object_new (NM_TYPE_IP6_MANAGER, NULL);
	priv = NM_IP6_MANAGER_GET_PRIVATE (manager);

	if (!priv->devices) {
		nm_log_err (LOGD_IP6, "not enough memory to initialize IP6 manager tables");
		g_object_unref (manager);
		manager = NULL;
	}

	return manager;
}
gboolean
nm_ip6_manager_prepare_interface (NMIP6Manager *manager,
                                  int ifindex,
                                  const guint8 *hwaddr,
                                  guint hwaddr_len,
                                  NMSettingIP6Config *s_ip6,
                                  const char *accept_ra_path)
{
	NMIP6ManagerPrivate *priv;
	NMIP6Device *device;
	const char *method = NULL;

	g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), FALSE);
	g_return_val_if_fail (ifindex > 0, FALSE);
	g_return_val_if_fail (hwaddr != NULL, FALSE);
	g_return_val_if_fail (hwaddr_len > 0, FALSE);
	g_return_val_if_fail (hwaddr_len <= NM_UTILS_HWADDR_LEN_MAX, FALSE);

	priv = NM_IP6_MANAGER_GET_PRIVATE (manager);

	device = nm_ip6_device_new (manager, ifindex, hwaddr, hwaddr_len);
	g_return_val_if_fail (device != NULL, FALSE);
	g_return_val_if_fail (   strchr (device->iface, '/') == NULL
	                      && strcmp (device->iface, "all") != 0
	                      && strcmp (device->iface, "default") != 0,
	                      FALSE);

	if (s_ip6)
		method = nm_setting_ip6_config_get_method (s_ip6);
	if (!method)
		method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;

	/* Establish target state and turn router advertisement acceptance on or off */
	if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
		device->target_state = NM_IP6_DEVICE_GOT_LINK_LOCAL;
		nm_utils_do_sysctl (accept_ra_path, "0");
	} else {
		device->target_state = NM_IP6_DEVICE_GOT_ADDRESS;
		nm_utils_do_sysctl (accept_ra_path, "2");
	}

	return TRUE;
}
static NMIP6Device *
process_route_change (NMIP6Manager *manager, struct nl_msg *msg)
{
	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
	NMIP6Device *device;
	struct nlmsghdr *hdr;
	struct rtnl_route *rtnlroute;
	int old_size;

	hdr = nlmsg_hdr (msg);
	rtnlroute = NULL;
	nl_msg_parse (msg, ref_object, &rtnlroute);
	if (!rtnlroute) {
		nm_log_dbg (LOGD_IP6, "error processing netlink new/del route message");
		return NULL;
	}

	/* Cached/cloned routes are created by the kernel for specific operations
	 * and aren't part of the interface's permanent routing configuration.
	 */
	if (rtnl_route_get_flags (rtnlroute) & RTM_F_CLONED) {
		rtnl_route_put (rtnlroute);
		return NULL;
	}

	device = nm_ip6_manager_get_device (manager, rtnl_route_get_oif (rtnlroute));

	old_size = nl_cache_nitems (priv->route_cache);
	nl_cache_include (priv->route_cache, (struct nl_object *)rtnlroute, NULL, NULL);

	/* As above in process_address_change */
	nm_log_dbg (LOGD_IP6, "(%s): route cache size: %d -> %d:",
		    device_get_iface (device), old_size, nl_cache_nitems (priv->route_cache));
	dump_route_change (device, hdr, rtnlroute);
	rtnl_route_put (rtnlroute);
	if (nl_cache_nitems (priv->route_cache) == old_size)
		return NULL;

	return device;
}
Example #13
0
static NMIP6Device *
process_route (NMIP6Manager *manager, struct nl_msg *msg)
{
	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
	NMIP6Device *device;
	struct rtnl_route *rtnlroute;
	int old_size;

	nm_log_dbg (LOGD_IP6, "processing netlink new/del route message");

	rtnlroute = NULL;
	nl_msg_parse (msg, ref_object, &rtnlroute);
	if (!rtnlroute) {
		nm_log_dbg (LOGD_IP6, "error processing netlink new/del route message");
		return NULL;
	}

	device = nm_ip6_manager_get_device (manager, rtnl_route_get_oif (rtnlroute));
	if (!device) {
		nm_log_dbg (LOGD_IP6, "ignoring message for unknown device");
		rtnl_route_put (rtnlroute);
		return NULL;
	}

	old_size = nl_cache_nitems (priv->route_cache);
	nl_cache_include (priv->route_cache, (struct nl_object *)rtnlroute, NULL, NULL);
	rtnl_route_put (rtnlroute);

	/* As above in process_addr */
	if (nl_cache_nitems (priv->route_cache) == old_size) {
		nm_log_dbg (LOGD_IP6, "(%s): route cache unchanged, ignoring message",
		            device->iface);
		return NULL;
	}

	return device;
}
Example #14
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);
		}
	}
}
Example #15
0
NMIP6Config *
nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, int ifindex)
{
	NMIP6ManagerPrivate *priv;
	NMIP6Device *device;
	NMIP6Config *config;
	struct rtnl_addr *rtnladdr;
	struct nl_addr *nladdr;
	struct in6_addr *addr;
	NMIP6Address *ip6addr;
	struct rtnl_route *rtnlroute;
	struct nl_addr *nldest, *nlgateway;
	struct in6_addr *dest, *gateway;
	gboolean defgw_set = FALSE;
	struct in6_addr defgw;
	uint32_t metric;
	NMIP6Route *ip6route;
	int i;

	g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL);
	g_return_val_if_fail (ifindex > 0, NULL);

	priv = NM_IP6_MANAGER_GET_PRIVATE (manager);

	device = (NMIP6Device *) g_hash_table_lookup (priv->devices,
	                                              GINT_TO_POINTER (ifindex));
	if (!device) {
		nm_log_warn (LOGD_IP6, "(%d): addrconf not started.", ifindex);
		return NULL;
	}

	config = nm_ip6_config_new ();
	if (!config) {
		nm_log_err (LOGD_IP6, "(%s): out of memory creating IP6 config object.",
		            device->iface);
		return NULL;
	}

	/* Make sure we refill the route and address caches, otherwise we won't get
	 * up-to-date information here since the netlink route/addr change messages
	 * may be lagging a bit.
	 */
	nl_cache_refill (priv->nlh, priv->route_cache);
	nl_cache_refill (priv->nlh, priv->addr_cache);

	/* Add routes */
	for (rtnlroute = FIRST_ROUTE (priv->route_cache); rtnlroute; rtnlroute = NEXT_ROUTE (rtnlroute)) {
		/* Make sure it's an IPv6 route for this device */
		if (rtnl_route_get_oif (rtnlroute) != device->ifindex)
			continue;
		if (rtnl_route_get_family (rtnlroute) != AF_INET6)
			continue;

		nldest = rtnl_route_get_dst (rtnlroute);
		if (!nldest || nl_addr_get_family (nldest) != AF_INET6)
			continue;
		dest = nl_addr_get_binary_addr (nldest);

		nlgateway = rtnl_route_get_gateway (rtnlroute);
		if (!nlgateway || nl_addr_get_family (nlgateway) != AF_INET6)
			continue;
		gateway = nl_addr_get_binary_addr (nlgateway);

		if (rtnl_route_get_dst_len (rtnlroute) == 0) {
			/* Default gateway route; don't add to normal routes but to each address */
			if (!defgw_set) {
				memcpy (&defgw, gateway, sizeof (defgw));
				defgw_set = TRUE;
			}
			continue;
		}

		/* Also ignore link-local routes where the destination and gateway are
		 * the same, which apparently get added by the kernel but return -EINVAL
		 * when we try to add them via netlink.
		 */
		if (gateway && IN6_ARE_ADDR_EQUAL (dest, gateway))
			continue;

		ip6route = nm_ip6_route_new ();
		nm_ip6_route_set_dest (ip6route, dest);
		nm_ip6_route_set_prefix (ip6route, rtnl_route_get_dst_len (rtnlroute));
		nm_ip6_route_set_next_hop (ip6route, gateway);
		rtnl_route_get_metric(rtnlroute, 1, &metric);
		if (metric != UINT_MAX)
			nm_ip6_route_set_metric (ip6route, metric);
		nm_ip6_config_take_route (config, ip6route);
	}

	/* Add addresses */
	for (rtnladdr = FIRST_ADDR (priv->addr_cache); rtnladdr; rtnladdr = NEXT_ADDR (rtnladdr)) {
		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);
		ip6addr = nm_ip6_address_new ();
		nm_ip6_address_set_prefix (ip6addr, rtnl_addr_get_prefixlen (rtnladdr));
		nm_ip6_address_set_address (ip6addr, addr);
		nm_ip6_config_take_address (config, ip6addr);
		if (defgw_set)
			nm_ip6_address_set_gateway (ip6addr, &defgw);
	}

	/* Add DNS servers */
	if (device->rdnss_servers) {
		NMIP6RDNSS *rdnss = (NMIP6RDNSS *)(device->rdnss_servers->data);

		for (i = 0; i < device->rdnss_servers->len; i++)
			nm_ip6_config_add_nameserver (config, &rdnss[i].addr);
	}

	/* Add DNS domains */
	if (device->dnssl_domains) {
		NMIP6DNSSL *dnssl = (NMIP6DNSSL *)(device->dnssl_domains->data);

		for (i = 0; i < device->dnssl_domains->len; i++)
			nm_ip6_config_add_domain (config, dnssl[i].domain);
	}

	return config;
}