about summary refs log tree commit diff
path: root/src/nm-ip6-config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-ip6-config.c')
-rw-r--r--src/nm-ip6-config.c557
1 files changed, 345 insertions, 212 deletions
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index f68c4339..fd63dcb4 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -24,26 +24,23 @@
 #include <string.h>
 #include <arpa/inet.h>
 
+#include "nm-default.h"
 #include "nm-ip6-config.h"
 
-#include "nm-glib-compat.h"
 #include "nm-utils.h"
 #include "nm-platform.h"
-#include "nm-dbus-manager.h"
-#include "nm-dbus-glib-types.h"
-#include "nm-ip6-config-glue.h"
 #include "nm-route-manager.h"
+#include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 #include "nm-macros-internal.h"
-#include "nm-core-internal.h"
 
-G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, G_TYPE_OBJECT)
+#include "nmdbus-ip6-config.h"
 
-#define NM_IP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP6_CONFIG, NMIP6ConfigPrivate))
+G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_EXPORTED_OBJECT)
 
-typedef struct {
-	char *path;
+#define NM_IP6_CONFIG_GET_PRIVATE(o) ((o)->priv)
 
+typedef struct _NMIP6ConfigPrivate {
 	gboolean never_default;
 	struct in6_addr gateway;
 	GArray *addresses;
@@ -51,13 +48,16 @@ typedef struct {
 	GArray *nameservers;
 	GPtrArray *domains;
 	GPtrArray *searches;
+	GPtrArray *dns_options;
 	guint32 mss;
+	int ifindex;
 	gint64 route_metric;
 } NMIP6ConfigPrivate;
 
 
 enum {
 	PROP_0,
+	PROP_IFINDEX,
 	PROP_ADDRESS_DATA,
 	PROP_ADDRESSES,
 	PROP_ROUTE_DATA,
@@ -66,6 +66,7 @@ enum {
 	PROP_NAMESERVERS,
 	PROP_DOMAINS,
 	PROP_SEARCHES,
+	PROP_DNS_OPTIONS,
 
 	LAST_PROP
 };
@@ -74,29 +75,18 @@ static GParamSpec *obj_properties[LAST_PROP] = { NULL, };
 
 
 NMIP6Config *
-nm_ip6_config_new (void)
-{
-	return (NMIP6Config *) g_object_new (NM_TYPE_IP6_CONFIG, NULL);
-}
-
-void
-nm_ip6_config_export (NMIP6Config *config)
+nm_ip6_config_new (int ifindex)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
-	static guint32 counter = 0;
-
-	if (!priv->path) {
-		priv->path = g_strdup_printf (NM_DBUS_PATH "/IP6Config/%d", counter++);
-		nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, config);
-	}
+	g_return_val_if_fail (ifindex >= -1, NULL);
+	return (NMIP6Config *) g_object_new (NM_TYPE_IP6_CONFIG,
+	                                     NM_IP6_CONFIG_IFINDEX, ifindex,
+	                                     NULL);
 }
 
-const char *
-nm_ip6_config_get_dbus_path (const NMIP6Config *config)
+int
+nm_ip6_config_get_ifindex (const NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
-
-	return priv->path;
+	return NM_IP6_CONFIG_GET_PRIVATE (config)->ifindex;
 }
 
 /******************************************************************/
@@ -130,9 +120,10 @@ same_prefix (const struct in6_addr *address1, const struct in6_addr *address2, i
  */
 gboolean
 nm_ip6_config_capture_resolv_conf (GArray *nameservers,
+                                   GPtrArray *dns_options,
                                    const char *rc_contents)
 {
-	GPtrArray *read_ns;
+	GPtrArray *read_ns, *read_options;
 	guint i, j;
 	gboolean changed = FALSE;
 
@@ -162,15 +153,32 @@ nm_ip6_config_capture_resolv_conf (GArray *nameservers,
 			changed = TRUE;
 		}
 	}
-
 	g_ptr_array_unref (read_ns);
+
+	if (dns_options) {
+		read_options = nm_utils_read_resolv_conf_dns_options (rc_contents);
+		if (!read_options)
+			return changed;
+
+		for (i = 0; i < read_options->len; i++) {
+			const char *s = g_ptr_array_index (read_options, i);
+
+			if (_nm_utils_dns_option_validate (s, NULL, NULL, TRUE, _nm_utils_dns_option_descs) &&
+				_nm_utils_dns_option_find_idx (dns_options, s) < 0) {
+				g_ptr_array_add (dns_options, g_strdup (s));
+				changed = TRUE;
+			}
+		}
+		g_ptr_array_unref (read_options);
+	}
+
 	return changed;
 }
 
 static gboolean
-addresses_are_duplicate (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b, gboolean consider_plen)
+addresses_are_duplicate (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b)
 {
-	return IN6_ARE_ADDR_EQUAL (&a->address, &b->address) && (!consider_plen || a->plen == b->plen);
+	return IN6_ARE_ADDR_EQUAL (&a->address, &b->address);
 }
 
 static gboolean
@@ -305,7 +313,7 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6Co
 	if (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex) > 0)
 		return NULL;
 
-	config = nm_ip6_config_new ();
+	config = nm_ip6_config_new (ifindex);
 	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	g_array_unref (priv->addresses);
@@ -356,7 +364,9 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6Co
 	 * nameservers from /etc/resolv.conf.
 	 */
 	if (priv->addresses->len && has_gateway && capture_resolv_conf)
-		notify_nameservers = nm_ip6_config_capture_resolv_conf (priv->nameservers, NULL);
+		notify_nameservers = nm_ip6_config_capture_resolv_conf (priv->nameservers,
+		                                                        priv->dns_options,
+		                                                        NULL);
 
 	g_array_sort_with_data (priv->addresses, _addresses_sort_cmp, GINT_TO_POINTER (use_temporary));
 
@@ -502,6 +512,12 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, gu
 	for (i = 0; i < nsearches; i++)
 		nm_ip6_config_add_search (config, nm_setting_ip_config_get_dns_search (setting, i));
 
+	i = 0;
+	while ((i = nm_setting_ip_config_next_valid_dns_option (setting, i)) >= 0) {
+		nm_ip6_config_add_dns_option (config, nm_setting_ip_config_get_dns_option (setting, i));
+		i++;
+	}
+
 	g_object_thaw_notify (G_OBJECT (config));
 }
 
@@ -510,7 +526,7 @@ nm_ip6_config_create_setting (const NMIP6Config *config)
 {
 	NMSettingIPConfig *s_ip6;
 	const struct in6_addr *gateway;
-	guint naddresses, nroutes, nnameservers, nsearches;
+	guint naddresses, nroutes, nnameservers, nsearches, noptions;
 	const char *method = NULL;
 	int i;
 	gint64 route_metric;
@@ -529,6 +545,7 @@ nm_ip6_config_create_setting (const NMIP6Config *config)
 	nroutes = nm_ip6_config_get_num_routes (config);
 	nnameservers = nm_ip6_config_get_num_nameservers (config);
 	nsearches = nm_ip6_config_get_num_searches (config);
+	noptions = nm_ip6_config_get_num_dns_options (config);
 	route_metric = nm_ip6_config_get_route_metric (config);
 
 	/* Addresses */
@@ -611,6 +628,12 @@ nm_ip6_config_create_setting (const NMIP6Config *config)
 
 		nm_setting_ip_config_add_dns_search (s_ip6, search);
 	}
+	for (i = 0; i < noptions; i++) {
+		const char *option = nm_ip6_config_get_dns_option (config, i);
+
+		nm_setting_ip_config_add_dns_option (s_ip6, option);
+	}
+
 
 	return NM_SETTING (s_ip6);
 }
@@ -668,6 +691,12 @@ nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFl
 			nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i));
 	}
 
+	/* dns options */
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip6_config_get_num_dns_options (src); i++)
+			nm_ip6_config_add_dns_option (dst, nm_ip6_config_get_dns_option (src, i));
+	}
+
 	if (nm_ip6_config_get_mss (src))
 		nm_ip6_config_set_mss (dst, nm_ip6_config_get_mss (src));
 
@@ -702,7 +731,7 @@ _addresses_get_index (const NMIP6Config *self, const NMPlatformIP6Address *addr)
 	for (i = 0; i < priv->addresses->len; i++) {
 		const NMPlatformIP6Address *a = &g_array_index (priv->addresses, NMPlatformIP6Address, i);
 
-		if (IN6_ARE_ADDR_EQUAL (&addr->address, &a->address))
+		if (addresses_are_duplicate (a, addr))
 			return (int) i;
 	}
 	return -1;
@@ -768,6 +797,21 @@ _searches_get_index (const NMIP6Config *self, const char *search)
 	return -1;
 }
 
+static int
+_dns_options_get_index (const NMIP6Config *self, const char *option)
+{
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
+	guint i;
+
+	for (i = 0; i < priv->dns_options->len; i++) {
+		const char *s = g_ptr_array_index (priv->dns_options, i);
+
+		if (g_strcmp0 (option, s) == 0)
+			return (int) i;
+	}
+	return -1;
+}
+
 /*******************************************************************************/
 
 /**
@@ -835,6 +879,13 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src)
 			nm_ip6_config_del_search (dst, idx);
 	}
 
+	/* dns options */
+	for (i = 0; i < nm_ip6_config_get_num_dns_options (src); i++) {
+		idx = _dns_options_get_index (dst, nm_ip6_config_get_dns_option (src, i));
+		if (idx >= 0)
+			nm_ip6_config_del_dns_option (dst, idx);
+	}
+
 	if (nm_ip6_config_get_mss (src) == nm_ip6_config_get_mss (dst))
 		nm_ip6_config_set_mss (dst, 0);
 
@@ -886,6 +937,7 @@ nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src)
 
 	/* ignore domains */
 	/* ignore dns searches */
+	/* ignome dns options */
 
 	g_object_thaw_notify (G_OBJECT (dst));
 }
@@ -907,7 +959,7 @@ nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src)
 gboolean
 nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes)
 {
-#ifndef G_DISABLE_ASSERT
+#if NM_MORE_ASSERTS
 	gboolean config_equal;
 #endif
 	gboolean has_minor_changes = FALSE, has_relevant_changes = FALSE, are_equal;
@@ -920,7 +972,7 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev
 	g_return_val_if_fail (dst != NULL, FALSE);
 	g_return_val_if_fail (src != dst, FALSE);
 
-#ifndef G_DISABLE_ASSERT
+#if NM_MORE_ASSERTS
 	config_equal = nm_ip6_config_equal (dst, src);
 #endif
 
@@ -929,6 +981,12 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev
 
 	g_object_freeze_notify (G_OBJECT (dst));
 
+	/* ifindex */
+	if (src_priv->ifindex != dst_priv->ifindex) {
+		dst_priv->ifindex = src_priv->ifindex;
+		has_minor_changes = TRUE;
+	}
+
 	/* never_default */
 	if (src_priv->never_default != dst_priv->never_default) {
 		dst_priv->never_default = src_priv->never_default;
@@ -954,7 +1012,10 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev
 			if (nm_platform_ip6_address_cmp (src_addr = nm_ip6_config_get_address (src, i),
 			                                 dst_addr = nm_ip6_config_get_address (dst, i))) {
 				are_equal = FALSE;
-				if (!addresses_are_duplicate (src_addr, dst_addr, TRUE)) {
+				if (   !addresses_are_duplicate (src_addr, dst_addr)
+				    || src_addr->plen != dst_addr->plen
+				    || !IN6_ARE_ADDR_EQUAL (nm_platform_ip6_address_get_peer (src_addr),
+				                            nm_platform_ip6_address_get_peer (dst_addr)))  {
 					has_relevant_changes = TRUE;
 					break;
 				}
@@ -1049,15 +1110,36 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev
 		has_relevant_changes = TRUE;
 	}
 
+	/* dns options */
+	num = nm_ip6_config_get_num_dns_options (src);
+	are_equal = num == nm_ip6_config_get_num_dns_options (dst);
+	if (are_equal) {
+		for (i = 0; i < num; i++ ) {
+			if (g_strcmp0 (nm_ip6_config_get_dns_option (src, i),
+			               nm_ip6_config_get_dns_option (dst, i))) {
+				are_equal = FALSE;
+				break;
+			}
+		}
+	}
+	if (!are_equal) {
+		nm_ip6_config_reset_dns_options (dst);
+		for (i = 0; i < num; i++)
+			nm_ip6_config_add_dns_option (dst, nm_ip6_config_get_dns_option (src, i));
+		has_relevant_changes = TRUE;
+	}
+
 	/* mss */
 	if (src_priv->mss != dst_priv->mss) {
 		nm_ip6_config_set_mss (dst, src_priv->mss);
 		has_minor_changes = TRUE;
 	}
 
+#if NM_MORE_ASSERTS
 	/* config_equal does not compare *all* the fields, therefore, we might have has_minor_changes
 	 * regardless of config_equal. But config_equal must correspond to has_relevant_changes. */
-	g_assert (config_equal == !has_relevant_changes);
+	nm_assert (config_equal == !has_relevant_changes);
+#endif
 
 	g_object_thaw_notify (G_OBJECT (dst));
 
@@ -1078,13 +1160,13 @@ nm_ip6_config_dump (const NMIP6Config *config, const char *detail)
 
 	g_message ("--------- NMIP6Config %p (%s)", config, detail);
 
-	str = nm_ip6_config_get_dbus_path (config);
+	str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (config));
 	if (str)
 		g_message ("   path: %s", str);
 
 	/* addresses */
 	for (i = 0; i < nm_ip6_config_get_num_addresses (config); i++)
-		g_message ("      a: %s", nm_platform_ip6_address_to_string (nm_ip6_config_get_address (config, i)));
+		g_message ("      a: %s", nm_platform_ip6_address_to_string (nm_ip6_config_get_address (config, i), NULL, 0));
 
 	/* default gateway */
 	tmp = nm_ip6_config_get_gateway (config);
@@ -1099,7 +1181,7 @@ nm_ip6_config_dump (const NMIP6Config *config, const char *detail)
 
 	/* routes */
 	for (i = 0; i < nm_ip6_config_get_num_routes (config); i++)
-		g_message ("     rt: %s", nm_platform_ip6_route_to_string (nm_ip6_config_get_route (config, i)));
+		g_message ("     rt: %s", nm_platform_ip6_route_to_string (nm_ip6_config_get_route (config, i), NULL, 0));
 
 	/* domains */
 	for (i = 0; i < nm_ip6_config_get_num_domains (config); i++)
@@ -1109,6 +1191,10 @@ nm_ip6_config_dump (const NMIP6Config *config, const char *detail)
 	for (i = 0; i < nm_ip6_config_get_num_searches (config); i++)
 		g_message (" search: %s", nm_ip6_config_get_search (config, i));
 
+	/* dns options */
+	for (i = 0; i < nm_ip6_config_get_num_dns_options (config); i++)
+		g_message (" dnsopt: %s", nm_ip6_config_get_dns_option (config, i));
+
 	g_message ("    mss: %"G_GUINT32_FORMAT, nm_ip6_config_get_mss (config));
 	g_message (" n-dflt: %d", nm_ip6_config_get_never_default (config));
 }
@@ -1200,7 +1286,7 @@ nm_ip6_config_add_address (NMIP6Config *config, const NMPlatformIP6Address *new)
 	for (i = 0; i < priv->addresses->len; i++ ) {
 		NMPlatformIP6Address *item = &g_array_index (priv->addresses, NMPlatformIP6Address, i);
 
-		if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) {
+		if (addresses_are_duplicate (item, new)) {
 			if (nm_platform_ip6_address_cmp (item, new) == 0)
 				return;
 
@@ -1267,17 +1353,7 @@ gboolean
 nm_ip6_config_address_exists (const NMIP6Config *config,
                               const NMPlatformIP6Address *needle)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
-	guint i;
-
-	for (i = 0; i < priv->addresses->len; i++) {
-		const NMPlatformIP6Address *haystack = &g_array_index (priv->addresses, NMPlatformIP6Address, i);
-
-		if (   IN6_ARE_ADDR_EQUAL (&needle->address, &haystack->address)
-		    && needle->plen == haystack->plen)
-			return TRUE;
-	}
-	return FALSE;
+	return _addresses_get_index (config, needle) >= 0;
 }
 
 /******************************************************************/
@@ -1313,6 +1389,7 @@ nm_ip6_config_add_route (NMIP6Config *config, const NMPlatformIP6Route *new)
 
 	g_return_if_fail (new != NULL);
 	g_return_if_fail (new->plen > 0);
+	g_assert (priv->ifindex);
 
 	for (i = 0; i < priv->routes->len; i++ ) {
 		NMPlatformIP6Route *item = &g_array_index (priv->routes, NMPlatformIP6Route, i);
@@ -1324,11 +1401,13 @@ nm_ip6_config_add_route (NMIP6Config *config, const NMPlatformIP6Route *new)
 			*item = *new;
 			/* Restore highest priority source */
 			item->source = MAX (old_source, new->source);
+			item->ifindex = priv->ifindex;
 			goto NOTIFY;
 		}
 	}
 
 	g_array_append_val (priv->routes, *new);
+	g_array_index (priv->routes, NMPlatformIP6Route, priv->routes->len - 1).ifindex = priv->ifindex;
 NOTIFY:
 	_NOTIFY (config, PROP_ROUTE_DATA);
 	_NOTIFY (config, PROP_ROUTES);
@@ -1612,6 +1691,63 @@ nm_ip6_config_get_search (const NMIP6Config *config, guint i)
 /******************************************************************/
 
 void
+nm_ip6_config_reset_dns_options (NMIP6Config *config)
+{
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+
+	if (priv->dns_options->len != 0) {
+		g_ptr_array_set_size (priv->dns_options, 0);
+		_NOTIFY (config, PROP_DNS_OPTIONS);
+	}
+}
+
+void
+nm_ip6_config_add_dns_option (NMIP6Config *config, const char *new)
+{
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	int i;
+
+	g_return_if_fail (new != NULL);
+	g_return_if_fail (new[0] != '\0');
+
+	for (i = 0; i < priv->dns_options->len; i++)
+		if (!g_strcmp0 (g_ptr_array_index (priv->dns_options, i), new))
+			return;
+
+	g_ptr_array_add (priv->dns_options, g_strdup (new));
+	_NOTIFY (config, PROP_DNS_OPTIONS);
+}
+
+void
+nm_ip6_config_del_dns_option (NMIP6Config *config, guint i)
+{
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+
+	g_return_if_fail (i < priv->dns_options->len);
+
+	g_ptr_array_remove_index (priv->dns_options, i);
+	_NOTIFY (config, PROP_DNS_OPTIONS);
+}
+
+guint32
+nm_ip6_config_get_num_dns_options (const NMIP6Config *config)
+{
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+
+	return priv->dns_options->len;
+}
+
+const char *
+nm_ip6_config_get_dns_option (const NMIP6Config *config, guint i)
+{
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+
+	return g_ptr_array_index (priv->dns_options, i);
+}
+
+/******************************************************************/
+
+void
 nm_ip6_config_set_mss (NMIP6Config *config, guint32 mss)
 {
 	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
@@ -1685,6 +1821,12 @@ nm_ip6_config_hash (const NMIP6Config *config, GChecksum *sum, gboolean dns_only
 		s = nm_ip6_config_get_search (config, i);
 		g_checksum_update (sum, (const guint8 *) s, strlen (s));
 	}
+
+	for (i = 0; i < nm_ip6_config_get_num_dns_options (config); i++) {
+		s = nm_ip6_config_get_dns_option (config, i);
+		g_checksum_update (sum, (const guint8 *) s, strlen (s));
+	}
+
 }
 
 /**
@@ -1732,28 +1874,32 @@ nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b)
 static void
 nm_ip6_config_init (NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	NMIP6ConfigPrivate *priv;
+
+	priv = G_TYPE_INSTANCE_GET_PRIVATE (config, NM_TYPE_IP6_CONFIG, NMIP6ConfigPrivate);
+	config->priv = priv;
 
 	priv->addresses = g_array_new (FALSE, TRUE, sizeof (NMPlatformIP6Address));
 	priv->routes = g_array_new (FALSE, TRUE, sizeof (NMPlatformIP6Route));
 	priv->nameservers = g_array_new (FALSE, TRUE, sizeof (struct in6_addr));
 	priv->domains = g_ptr_array_new_with_free_func (g_free);
 	priv->searches = g_ptr_array_new_with_free_func (g_free);
+	priv->dns_options = g_ptr_array_new_with_free_func (g_free);
 	priv->route_metric = -1;
 }
 
 static void
 finalize (GObject *object)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object);
-
-	g_free (priv->path);
+	NMIP6Config *self = NM_IP6_CONFIG (object);
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
 
 	g_array_unref (priv->addresses);
 	g_array_unref (priv->routes);
 	g_array_unref (priv->nameservers);
 	g_ptr_array_unref (priv->domains);
 	g_ptr_array_unref (priv->searches);
+	g_ptr_array_unref (priv->dns_options);
 
 	G_OBJECT_CLASS (nm_ip6_config_parent_class)->finalize (object);
 }
@@ -1761,31 +1907,21 @@ finalize (GObject *object)
 static void
 nameservers_to_gvalue (GArray *array, GValue *value)
 {
-	GPtrArray *dns;
+	GVariantBuilder builder;
 	guint i = 0;
 
-	dns = g_ptr_array_new ();
+	g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay"));
 
 	while (array && (i < array->len)) {
 		struct in6_addr *addr;
-		GByteArray *bytearray;
-		addr = &g_array_index (array, struct in6_addr, i++);
 
-		bytearray = g_byte_array_sized_new (16);
-		g_byte_array_append (bytearray, (guint8 *) addr->s6_addr, 16);
-		g_ptr_array_add (dns, bytearray);
+		addr = &g_array_index (array, struct in6_addr, i++);
+		g_variant_builder_add (&builder, "@ay",
+		                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+		                                                  addr, 16, 1));
 	}
 
-	g_value_take_boxed (value, dns);
-}
-
-static void
-gvalue_destroy (gpointer data)
-{
-	GValue *value = (GValue *) data;
-
-	g_value_unset (value);
-	g_slice_free (GValue, value);
+	g_value_take_variant (value, g_variant_builder_end (&builder));
 }
 
 static void
@@ -1793,168 +1929,123 @@ get_property (GObject *object, guint prop_id,
 			  GValue *value, GParamSpec *pspec)
 {
 	NMIP6Config *config = NM_IP6_CONFIG (object);
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object);
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	switch (prop_id) {
+	case PROP_IFINDEX:
+		g_value_set_int (value, priv->ifindex);
+		break;
 	case PROP_ADDRESS_DATA:
 		{
-			GPtrArray *addresses = g_ptr_array_new ();
+			GVariantBuilder array_builder, addr_builder;
 			int naddr = nm_ip6_config_get_num_addresses (config);
 			int i;
 
+			g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
 			for (i = 0; i < naddr; i++) {
 				const NMPlatformIP6Address *address = nm_ip6_config_get_address (config, i);
-				GHashTable *addr_hash;
-				GValue *val;
-
-				addr_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gvalue_destroy);
-
-				val = g_slice_new0 (GValue);
-				g_value_init (val, G_TYPE_STRING);
-				g_value_set_string (val, nm_utils_inet6_ntop (&address->address, NULL));
-				g_hash_table_insert (addr_hash, "address", val);
 
-				val = g_slice_new0 (GValue);
-				g_value_init (val, G_TYPE_UINT);
-				g_value_set_uint (val, address->plen);
-				g_hash_table_insert (addr_hash, "prefix", val);
+				g_variant_builder_init (&addr_builder, G_VARIANT_TYPE ("a{sv}"));
+				g_variant_builder_add (&addr_builder, "{sv}",
+				                       "address",
+				                       g_variant_new_string (nm_utils_inet6_ntop (&address->address, NULL)));
+				g_variant_builder_add (&addr_builder, "{sv}",
+				                       "prefix",
+				                       g_variant_new_uint32 (address->plen));
+				if (   !IN6_IS_ADDR_UNSPECIFIED (&address->peer_address)
+				    && !IN6_ARE_ADDR_EQUAL (&address->peer_address, &address->address)) {
+					g_variant_builder_add (&addr_builder, "{sv}",
+					                       "peer",
+					                       g_variant_new_string (nm_utils_inet6_ntop (&address->peer_address, NULL)));
+				}
 
-				g_ptr_array_add (addresses, addr_hash);
+				g_variant_builder_add (&array_builder, "a{sv}", &addr_builder);
 			}
 
-			g_value_take_boxed (value, addresses);
+			g_value_take_variant (value, g_variant_builder_end (&array_builder));
 		}
 		break;
 	case PROP_ADDRESSES:
 		{
-			GPtrArray *addresses = g_ptr_array_new ();
+			GVariantBuilder array_builder;
 			const struct in6_addr *gateway = nm_ip6_config_get_gateway (config);
 			int naddr = nm_ip6_config_get_num_addresses (config);
 			int i;
 
+			g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a(ayuay)"));
 			for (i = 0; i < naddr; i++) {
 				const NMPlatformIP6Address *address = nm_ip6_config_get_address (config, i);
 
-				GValueArray *array = g_value_array_new (3);
-				GValue element = G_VALUE_INIT;
-				GByteArray *ba;
-
-				/* IP address */
-				g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
-				ba = g_byte_array_new ();
-				g_byte_array_append (ba, (guint8 *) &address->address, 16);
-				g_value_take_boxed (&element, ba);
-				g_value_array_append (array, &element);
-				g_value_unset (&element);
-
-				/* Prefix */
-				g_value_init (&element, G_TYPE_UINT);
-				g_value_set_uint (&element, address->plen);
-				g_value_array_append (array, &element);
-				g_value_unset (&element);
-
-				/* Gateway */
-				g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
-				ba = g_byte_array_new ();
-				g_byte_array_append (ba, (guint8 *) (i == 0 && gateway ? gateway : &in6addr_any), sizeof (*gateway));
-				g_value_take_boxed (&element, ba);
-				g_value_array_append (array, &element);
-				g_value_unset (&element);
-
-				g_ptr_array_add (addresses, array);
+				g_variant_builder_add (&array_builder, "(@ayu@ay)",
+				                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+				                                                  &address->address, 16, 1),
+				                       address->plen,
+				                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+				                                                  (i == 0 && gateway ? gateway : &in6addr_any),
+				                                                  16, 1));
 			}
 
-			g_value_take_boxed (value, addresses);
+			g_value_take_variant (value, g_variant_builder_end (&array_builder));
 		}
 		break;
 	case PROP_ROUTE_DATA:
 		{
-			GPtrArray *routes = g_ptr_array_new ();
+			GVariantBuilder array_builder, route_builder;
 			guint nroutes = nm_ip6_config_get_num_routes (config);
 			int i;
 
+			g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
 			for (i = 0; i < nroutes; i++) {
 				const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i);
-				GHashTable *route_hash;
-				GValue *val;
-
-				route_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gvalue_destroy);
-
-				val = g_slice_new0 (GValue);
-				g_value_init (val, G_TYPE_STRING);
-				g_value_set_string (val, nm_utils_inet6_ntop (&route->network, NULL));
-				g_hash_table_insert (route_hash, "dest", val);
-
-				val = g_slice_new0 (GValue);
-				g_value_init (val, G_TYPE_UINT);
-				g_value_set_uint (val, route->plen);
-				g_hash_table_insert (route_hash, "prefix", val);
 
+				g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}"));
+				g_variant_builder_add (&route_builder, "{sv}",
+				                       "dest",
+				                       g_variant_new_string (nm_utils_inet6_ntop (&route->network, NULL)));
+				g_variant_builder_add (&route_builder, "{sv}",
+				                       "prefix",
+				                       g_variant_new_uint32 (route->plen));
 				if (!IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) {
-					val = g_slice_new0 (GValue);
-					g_value_init (val, G_TYPE_STRING);
-					g_value_set_string (val, nm_utils_inet6_ntop (&route->gateway, NULL));
-					g_hash_table_insert (route_hash, "next-hop", val);
+					g_variant_builder_add (&route_builder, "{sv}",
+					                       "next-hop",
+					                       g_variant_new_string (nm_utils_inet6_ntop (&route->gateway, NULL)));
 				}
 
-				val = g_slice_new0 (GValue);
-				g_value_init (val, G_TYPE_UINT);
-				g_value_set_uint (val, route->metric);
-				g_hash_table_insert (route_hash, "metric", val);
+				g_variant_builder_add (&route_builder, "{sv}",
+				                       "metric",
+				                       g_variant_new_uint32 (route->metric));
 
-				g_ptr_array_add (routes, route_hash);
+				g_variant_builder_add (&array_builder, "a{sv}", &route_builder);
 			}
 
-			g_value_take_boxed (value, routes);
+			g_value_take_variant (value, g_variant_builder_end (&array_builder));
 		}
 		break;
 	case PROP_ROUTES:
 		{
-			GPtrArray *routes = g_ptr_array_new ();
+			GVariantBuilder array_builder;
 			int nroutes = nm_ip6_config_get_num_routes (config);
 			int i;
 
+			g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a(ayuayu)"));
 			for (i = 0; i < nroutes; i++) {
-				GValueArray *array;
 				const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i);
-				GByteArray *ba;
-				GValue element = G_VALUE_INIT;
 
 				/* legacy versions of nm_ip6_route_set_prefix() in libnm-util assert that the
 				 * plen is positive. Skip the default routes not to break older clients. */
 				if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route))
 					continue;
 
-				array = g_value_array_new (4);
-
-				g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
-				ba = g_byte_array_new ();
-				g_byte_array_append (ba, (guint8 *) &route->network, sizeof (route->network));
-				g_value_take_boxed (&element, ba);
-				g_value_array_append (array, &element);
-				g_value_unset (&element);
-
-				g_value_init (&element, G_TYPE_UINT);
-				g_value_set_uint (&element, route->plen);
-				g_value_array_append (array, &element);
-				g_value_unset (&element);
-
-				g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
-				ba = g_byte_array_new ();
-				g_byte_array_append (ba, (guint8 *) &route->gateway, sizeof (route->gateway));
-				g_value_take_boxed (&element, ba);
-				g_value_array_append (array, &element);
-				g_value_unset (&element);
-
-				g_value_init (&element, G_TYPE_UINT);
-				g_value_set_uint (&element, route->metric);
-				g_value_array_append (array, &element);
-				g_value_unset (&element);
-
-				g_ptr_array_add (routes, array);
+				g_variant_builder_add (&array_builder, "(@ayu@ayu)",
+				                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+				                                                  &route->network, 16, 1),
+				                       (guint32) route->plen,
+				                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+				                                                  &route->gateway, 16, 1),
+				                       (guint32) route->metric);
 			}
 
-			g_value_take_boxed (value, routes);
+			g_value_take_variant (value, g_variant_builder_end (&array_builder));
 		}
 		break;
 	case PROP_GATEWAY:
@@ -1967,10 +2058,32 @@ get_property (GObject *object, guint prop_id,
 		nameservers_to_gvalue (priv->nameservers, value);
 		break;
 	case PROP_DOMAINS:
-		g_value_set_boxed (value, priv->domains);
+		nm_utils_g_value_set_strv (value, priv->domains);
 		break;
 	case PROP_SEARCHES:
-		g_value_set_boxed (value, priv->searches);
+		nm_utils_g_value_set_strv (value, priv->searches);
+		break;
+	case PROP_DNS_OPTIONS:
+		nm_utils_g_value_set_strv (value, priv->dns_options);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object,
+              guint prop_id,
+              const GValue *value,
+              GParamSpec *pspec)
+{
+	NMIP6Config *config = NM_IP6_CONFIG (object);
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+
+	switch (prop_id) {
+	case PROP_IFINDEX:
+		priv->ifindex = g_value_get_int (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1982,58 +2095,78 @@ static void
 nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class);
 
 	g_type_class_add_private (config_class, sizeof (NMIP6ConfigPrivate));
 
+	exported_object_class->export_path = NM_DBUS_PATH "/IP6Config/%u";
+
 	/* virtual methods */
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize = finalize;
 
 	/* properties */
+	obj_properties[PROP_IFINDEX] =
+		g_param_spec_int (NM_IP6_CONFIG_IFINDEX, "", "",
+		                  -1, G_MAXINT, -1,
+		                  G_PARAM_READWRITE |
+		                  G_PARAM_CONSTRUCT_ONLY |
+		                  G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ADDRESS_DATA] =
-	    g_param_spec_boxed (NM_IP6_CONFIG_ADDRESS_DATA, "", "",
-	                        DBUS_TYPE_NM_IP_ADDRESSES,
-	                        G_PARAM_READABLE |
-	                        G_PARAM_STATIC_STRINGS);
+		g_param_spec_variant (NM_IP6_CONFIG_ADDRESS_DATA, "", "",
+		                      G_VARIANT_TYPE ("aa{sv}"),
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ADDRESSES] =
-		g_param_spec_boxed (NM_IP6_CONFIG_ADDRESSES, "", "",
-		                    DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS);
+		g_param_spec_variant (NM_IP6_CONFIG_ADDRESSES, "", "",
+		                      G_VARIANT_TYPE ("a(ayuay)"),
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ROUTE_DATA] =
-		g_param_spec_boxed (NM_IP6_CONFIG_ROUTE_DATA, "", "",
-		                    DBUS_TYPE_NM_IP_ROUTES,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS);
+		g_param_spec_variant (NM_IP6_CONFIG_ROUTE_DATA, "", "",
+		                      G_VARIANT_TYPE ("aa{sv}"),
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ROUTES] =
-	    g_param_spec_boxed (NM_IP6_CONFIG_ROUTES, "", "",
-	                        DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE,
-	                        G_PARAM_READABLE |
-	                        G_PARAM_STATIC_STRINGS);
+		g_param_spec_variant (NM_IP6_CONFIG_ROUTES, "", "",
+		                      G_VARIANT_TYPE ("a(ayuayu)"),
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_GATEWAY] =
 		g_param_spec_string (NM_IP6_CONFIG_GATEWAY, "", "",
 		                     NULL,
 		                     G_PARAM_READABLE |
 		                     G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_NAMESERVERS] =
-	    g_param_spec_boxed (NM_IP6_CONFIG_NAMESERVERS, "", "",
-	                        DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR,
-	                        G_PARAM_READABLE |
-	                        G_PARAM_STATIC_STRINGS);
+		g_param_spec_variant (NM_IP6_CONFIG_NAMESERVERS, "", "",
+		                      G_VARIANT_TYPE ("aay"),
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_DOMAINS] =
-	    g_param_spec_boxed (NM_IP6_CONFIG_DOMAINS, "", "",
-	                        DBUS_TYPE_G_ARRAY_OF_STRING,
-	                        G_PARAM_READABLE |
-	                        G_PARAM_STATIC_STRINGS);
+		g_param_spec_boxed (NM_IP6_CONFIG_DOMAINS, "", "",
+		                    G_TYPE_STRV,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_SEARCHES] =
-	    g_param_spec_boxed (NM_IP6_CONFIG_SEARCHES, "", "",
-	                        DBUS_TYPE_G_ARRAY_OF_STRING,
-	                        G_PARAM_READABLE |
-	                        G_PARAM_STATIC_STRINGS);
+		g_param_spec_boxed (NM_IP6_CONFIG_SEARCHES, "", "",
+		                    G_TYPE_STRV,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_DNS_OPTIONS] =
+		g_param_spec_boxed (NM_IP6_CONFIG_DNS_OPTIONS, "", "",
+		                    G_TYPE_STRV,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
 
-	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
-	                                        G_TYPE_FROM_CLASS (config_class),
-	                                        &dbus_glib_nm_ip6_config_object_info);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class),
+	                                        NMDBUS_TYPE_IP6_CONFIG_SKELETON,
+	                                        NULL);
 }