summary refs log tree commit diff
path: root/src/nm-ip4-config.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2015-01-22 00:29:39 +0100
committerMichael Biebl <biebl@debian.org>2015-01-22 00:29:39 +0100
commit2c032d8f1c6292c1338a615e6ec40252889ba85c (patch)
tree1f77182220b2b0264288ba4a476ab47e5bc48716 /src/nm-ip4-config.c
parent33491bc4279481db8ae47213e34a6d695a0e8830 (diff)
Imported Upstream version 1.0.0 upstream/1.0.0
Diffstat (limited to 'src/nm-ip4-config.c')
-rw-r--r--src/nm-ip4-config.c440
1 files changed, 309 insertions, 131 deletions
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index e27c0436..fd466780 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -19,18 +19,20 @@
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
+#include "config.h"
+
 #include <string.h>
+#include <arpa/inet.h>
 
 #include "nm-ip4-config.h"
 
-#include "libgsystem.h"
-#include "nm-platform.h"
 #include "nm-utils.h"
+#include "nm-platform.h"
 #include "nm-dbus-manager.h"
 #include "nm-dbus-glib-types.h"
 #include "nm-ip4-config-glue.h"
 #include "NetworkManagerUtils.h"
-#include "nm-utils-private.h"
+#include "nm-core-internal.h"
 
 G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, G_TYPE_OBJECT)
 
@@ -51,6 +53,7 @@ typedef struct {
 	char *nis_domain;
 	GArray *wins;
 	guint32 mtu;
+	NMIPConfigSource mtu_source;
 } NMIP4ConfigPrivate;
 
 /* internal guint32 are assigned to gobject properties of type uint. Ensure, that uint is large enough */
@@ -59,9 +62,11 @@ G_STATIC_ASSERT (G_MAXUINT >= 0xFFFFFFFF);
 
 enum {
 	PROP_0,
-	PROP_GATEWAY,
+	PROP_ADDRESS_DATA,
 	PROP_ADDRESSES,
+	PROP_ROUTE_DATA,
 	PROP_ROUTES,
+	PROP_GATEWAY,
 	PROP_NAMESERVERS,
 	PROP_DOMAINS,
 	PROP_SEARCHES,
@@ -176,7 +181,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 	NMIP4Config *config;
 	NMIP4ConfigPrivate *priv;
 	guint i;
-	guint lowest_metric = G_MAXUINT;
+	guint32 lowest_metric = G_MAXUINT32;
 	guint32 old_gateway = 0;
 	gboolean has_gateway = FALSE;
 
@@ -191,14 +196,14 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 	g_array_unref (priv->routes);
 
 	priv->addresses = nm_platform_ip4_address_get_all (ifindex);
-	priv->routes = nm_platform_ip4_route_get_all (ifindex, TRUE);
+	priv->routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL);
 
 	/* Extract gateway from default route */
 	old_gateway = priv->gateway;
 	for (i = 0; i < priv->routes->len; i++) {
 		const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i);
 
-		if (route->network == 0) {
+		if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) {
 			if (route->metric < lowest_metric) {
 				priv->gateway = route->gateway;
 				lowest_metric = route->metric;
@@ -235,6 +240,8 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 	}
 
 	/* actually, nobody should be connected to the signal, just to be sure, notify */
+	_NOTIFY (config, PROP_ADDRESS_DATA);
+	_NOTIFY (config, PROP_ROUTE_DATA);
 	_NOTIFY (config, PROP_ADDRESSES);
 	_NOTIFY (config, PROP_ROUTES);
 	if (priv->gateway != old_gateway)
@@ -244,42 +251,36 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 }
 
 gboolean
-nm_ip4_config_commit (const NMIP4Config *config, int ifindex)
+nm_ip4_config_commit (const NMIP4Config *config, int ifindex, guint32 default_route_metric)
 {
 	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
-	int mtu = nm_ip4_config_get_mtu (config);
+	guint32 mtu = nm_ip4_config_get_mtu (config);
 	int i;
 
 	g_return_val_if_fail (ifindex > 0, FALSE);
 	g_return_val_if_fail (config != NULL, FALSE);
 
 	/* Addresses */
-	nm_platform_ip4_address_sync (ifindex, priv->addresses);
+	nm_platform_ip4_address_sync (ifindex, priv->addresses, default_route_metric);
 
 	/* Routes */
 	{
 		int count = nm_ip4_config_get_num_routes (config);
 		GArray *routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), count);
-		NMPlatformIP4Route route;
+		const NMPlatformIP4Route *route;
 		gboolean success;
 
 		for (i = 0; i < count; i++) {
-			memcpy (&route, nm_ip4_config_get_route (config, i), sizeof (route));
+			route = nm_ip4_config_get_route (config, i);
 
 			/* Don't add the route if it's more specific than one of the subnets
 			 * the device already has an IP address on.
 			 */
-			if (   route.gateway == 0
-			    && nm_ip4_config_destination_is_direct (config, route.network, route.plen))
+			if (   route->gateway == 0
+			    && nm_ip4_config_destination_is_direct (config, route->network, route->plen))
 				continue;
 
-			/* Don't add the default route if the connection
-			 * is never supposed to be the default connection.
-			 */
-			if (nm_ip4_config_get_never_default (config) && route.network == 0)
-				continue;
-
-			g_array_append_val (routes, route);
+			g_array_append_vals (routes, route, 1);
 		}
 
 		success = nm_platform_ip4_route_sync (ifindex, routes);
@@ -296,7 +297,7 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex)
 }
 
 void
-nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIP4Config *setting, int default_route_metric)
+nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric)
 {
 	guint naddresses, nroutes, nnameservers, nsearches;
 	int i;
@@ -304,74 +305,83 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIP4Config *setting, i
 	if (!setting)
 		return;
 
+	g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
 	g_object_freeze_notify (G_OBJECT (config));
 
-	naddresses = nm_setting_ip4_config_get_num_addresses (setting);
-	nroutes = nm_setting_ip4_config_get_num_routes (setting);
-	nnameservers = nm_setting_ip4_config_get_num_dns (setting);
-	nsearches = nm_setting_ip4_config_get_num_dns_searches (setting);
+	naddresses = nm_setting_ip_config_get_num_addresses (setting);
+	nroutes = nm_setting_ip_config_get_num_routes (setting);
+	nnameservers = nm_setting_ip_config_get_num_dns (setting);
+	nsearches = nm_setting_ip_config_get_num_dns_searches (setting);
 
 	/* Gateway */
-	if (nm_setting_ip4_config_get_never_default (setting))
+	if (nm_setting_ip_config_get_never_default (setting))
 		nm_ip4_config_set_never_default (config, TRUE);
-	else if (nm_setting_ip4_config_get_ignore_auto_routes (setting))
+	else if (nm_setting_ip_config_get_ignore_auto_routes (setting))
 		nm_ip4_config_set_never_default (config, FALSE);
-	for (i = 0; i < naddresses; i++) {
-		guint32 gateway = nm_ip4_address_get_gateway (nm_setting_ip4_config_get_address (setting, i));
+	if (nm_setting_ip_config_get_gateway (setting)) {
+		guint32 gateway;
 
-		if (gateway) {
-			nm_ip4_config_set_gateway (config, gateway);
-			break;
-		}
+		inet_pton (AF_INET, nm_setting_ip_config_get_gateway (setting), &gateway);
+		nm_ip4_config_set_gateway (config, gateway);
 	}
 
 	/* Addresses */
 	for (i = 0; i < naddresses; i++) {
-		NMIP4Address *s_addr = nm_setting_ip4_config_get_address (setting, i);
-		const char *label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (setting, i));
+		NMIPAddress *s_addr = nm_setting_ip_config_get_address (setting, i);
+		GVariant *label;
 		NMPlatformIP4Address address;
 
 		memset (&address, 0, sizeof (address));
-		address.address = nm_ip4_address_get_address (s_addr);
-		address.plen = nm_ip4_address_get_prefix (s_addr);
+		nm_ip_address_get_address_binary (s_addr, &address.address);
+		address.plen = nm_ip_address_get_prefix (s_addr);
 		address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
 		address.preferred = NM_PLATFORM_LIFETIME_PERMANENT;
-		address.source = NM_PLATFORM_SOURCE_USER;
+		address.source = NM_IP_CONFIG_SOURCE_USER;
+
+		label = nm_ip_address_get_attribute (s_addr, "label");
 		if (label)
-			g_strlcpy (address.label, label, sizeof (address.label));
+			g_strlcpy (address.label, g_variant_get_string (label, NULL), sizeof (address.label));
 
 		nm_ip4_config_add_address (config, &address);
 	}
 
 	/* Routes */
-	if (nm_setting_ip4_config_get_ignore_auto_routes (setting))
+	if (nm_setting_ip_config_get_ignore_auto_routes (setting))
 		nm_ip4_config_reset_routes (config);
 	for (i = 0; i < nroutes; i++) {
-		NMIP4Route *s_route = nm_setting_ip4_config_get_route (setting, i);
+		NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i);
 		NMPlatformIP4Route route;
 
 		memset (&route, 0, sizeof (route));
-		route.network = nm_ip4_route_get_dest (s_route);
-		route.plen = nm_ip4_route_get_prefix (s_route);
-		route.gateway = nm_ip4_route_get_next_hop (s_route);
-		route.metric = nm_ip4_route_get_metric (s_route);
-		if (!route.metric)
+		nm_ip_route_get_dest_binary (s_route, &route.network);
+		route.plen = nm_ip_route_get_prefix (s_route);
+		nm_ip_route_get_next_hop_binary (s_route, &route.gateway);
+		if (nm_ip_route_get_metric (s_route) == -1)
 			route.metric = default_route_metric;
-		route.source = NM_PLATFORM_SOURCE_USER;
+		else
+			route.metric = nm_ip_route_get_metric (s_route);
+		route.source = NM_IP_CONFIG_SOURCE_USER;
+
+		g_assert (route.plen > 0);
 
 		nm_ip4_config_add_route (config, &route);
 	}
 
 	/* DNS */
-	if (nm_setting_ip4_config_get_ignore_auto_dns (setting)) {
+	if (nm_setting_ip_config_get_ignore_auto_dns (setting)) {
 		nm_ip4_config_reset_nameservers (config);
 		nm_ip4_config_reset_domains (config);
 		nm_ip4_config_reset_searches (config);
 	}
-	for (i = 0; i < nnameservers; i++)
-		nm_ip4_config_add_nameserver (config, nm_setting_ip4_config_get_dns (setting, i));
+	for (i = 0; i < nnameservers; i++) {
+		guint32 ip;
+
+		if (inet_pton (AF_INET, nm_setting_ip_config_get_dns (setting, i), &ip) == 1)
+			nm_ip4_config_add_nameserver (config, ip);
+	}
 	for (i = 0; i < nsearches; i++)
-		nm_ip4_config_add_search (config, nm_setting_ip4_config_get_dns_search (setting, i));
+		nm_ip4_config_add_search (config, nm_setting_ip_config_get_dns_search (setting, i));
 
 	g_object_thaw_notify (G_OBJECT (config));
 }
@@ -379,17 +389,17 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIP4Config *setting, i
 NMSetting *
 nm_ip4_config_create_setting (const NMIP4Config *config)
 {
-	NMSettingIP4Config *s_ip4;
+	NMSettingIPConfig *s_ip4;
 	guint32 gateway;
 	guint naddresses, nroutes, nnameservers, nsearches;
 	const char *method = NULL;
 	int i;
 
-	s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ());
+	s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ());
 
 	if (!config) {
 		g_object_set (s_ip4,
-		              NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
+		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
 		              NULL);
 		return NM_SETTING (s_ip4);
 	}
@@ -403,7 +413,7 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 	/* Addresses */
 	for (i = 0; i < naddresses; i++) {
 		const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i);
-		NMIP4Address *s_addr;
+		NMIPAddress *s_addr;
 
 		/* Detect dynamic address */
 		if (address->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) {
@@ -415,61 +425,58 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 		if (!method)
 			method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
 
-		s_addr = nm_ip4_address_new ();
+		s_addr = nm_ip_address_new_binary (AF_INET, &address->address, address->plen, NULL);
+		if (*address->label)
+			nm_ip_address_set_attribute (s_addr, "label", g_variant_new_string (address->label));
 
-		nm_ip4_address_set_address (s_addr, address->address);
-		nm_ip4_address_set_prefix (s_addr, address->plen);
-		/* For backwards compatibility, attach the gateway to an address if it's
-		 * in the same subnet.
-		 */
-		if (same_prefix (address->address, gateway, address->plen))
-			nm_ip4_address_set_gateway (s_addr, gateway);
+		nm_setting_ip_config_add_address (s_ip4, s_addr);
+		nm_ip_address_unref (s_addr);
+	}
 
-		if (*address->label)
-			NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (s_ip4, s_addr, address->label));
-		else
-			nm_setting_ip4_config_add_address (s_ip4, s_addr);
-		nm_ip4_address_unref (s_addr);
+	/* Gateway */
+	if (   gateway
+	    && nm_setting_ip_config_get_num_addresses (s_ip4) > 0) {
+		g_object_set (s_ip4,
+		              NM_SETTING_IP_CONFIG_GATEWAY, nm_utils_inet4_ntop (gateway, NULL),
+		              NULL);
 	}
 
 	/* Use 'disabled' if the method wasn't previously set */
 	if (!method)
 		method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
-	g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, method, NULL);
+	g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, method, NULL);
 
 	/* Routes */
 	for (i = 0; i < nroutes; i++) {
 		const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i);
-		NMIP4Route *s_route;
+		NMIPRoute *s_route;
 
 		/* Ignore default route. */
 		if (!route->plen)
 			continue;
 
 		/* Ignore routes provided by external sources */
-		if (route->source != NM_PLATFORM_SOURCE_USER)
+		if (route->source != NM_IP_CONFIG_SOURCE_USER)
 			continue;
 
-		s_route = nm_ip4_route_new ();
-		nm_ip4_route_set_dest (s_route, route->network);
-		nm_ip4_route_set_prefix (s_route, route->plen);
-		nm_ip4_route_set_next_hop (s_route, route->gateway);
-		nm_ip4_route_set_metric (s_route, route->metric);
-
-		nm_setting_ip4_config_add_route (s_ip4, s_route);
-		nm_ip4_route_unref (s_route);
+		s_route = nm_ip_route_new_binary (AF_INET,
+		                                  &route->network, route->plen,
+		                                  &route->gateway, route->metric,
+		                                  NULL);
+		nm_setting_ip_config_add_route (s_ip4, s_route);
+		nm_ip_route_unref (s_route);
 	}
 
 	/* DNS */
 	for (i = 0; i < nnameservers; i++) {
 		guint32 nameserver = nm_ip4_config_get_nameserver (config, i);
 
-		nm_setting_ip4_config_add_dns (s_ip4, nameserver);
+		nm_setting_ip_config_add_dns (s_ip4, nm_utils_inet4_ntop (nameserver, NULL));
 	}
 	for (i = 0; i < nsearches; i++) {
 		const char *search = nm_ip4_config_get_search (config, i);
 
-		nm_setting_ip4_config_add_dns_search (s_ip4, search);
+		nm_setting_ip_config_add_dns_search (s_ip4, search);
 	}
 
 	return NM_SETTING (s_ip4);
@@ -517,7 +524,8 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src)
 
 	/* MTU */
 	if (!nm_ip4_config_get_mtu (dst))
-		nm_ip4_config_set_mtu (dst, nm_ip4_config_get_mtu (src));
+		nm_ip4_config_set_mtu (dst, nm_ip4_config_get_mtu (src),
+		                       nm_ip4_config_get_mtu_source (src));
 
 	/* NIS */
 	for (i = 0; i < nm_ip4_config_get_num_nis_servers (src); i++)
@@ -583,6 +591,9 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src)
 	if (nm_ip4_config_get_gateway (src) == nm_ip4_config_get_gateway (dst))
 		nm_ip4_config_set_gateway (dst, 0);
 
+	if (!nm_ip4_config_get_num_addresses (dst))
+		nm_ip4_config_set_gateway (dst, 0);
+
 	/* routes */
 	for (i = 0; i < nm_ip4_config_get_num_routes (src); i++) {
 		const NMPlatformIP4Route *src_route = nm_ip4_config_get_route (src, i);
@@ -631,7 +642,7 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src)
 
 	/* MTU */
 	if (nm_ip4_config_get_mtu (src) == nm_ip4_config_get_mtu (dst))
-		nm_ip4_config_set_mtu (dst, 0);
+		nm_ip4_config_set_mtu (dst, 0, NM_IP_CONFIG_SOURCE_UNKNOWN);
 
 	/* NIS */
 	for (i = 0; i < nm_ip4_config_get_num_nis_servers (src); i++) {
@@ -871,7 +882,7 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev
 
 	/* mtu */
 	if (src_priv->mtu != dst_priv->mtu) {
-		nm_ip4_config_set_mtu (dst, src_priv->mtu);
+		nm_ip4_config_set_mtu (dst, src_priv->mtu, src_priv->mtu_source);
 		has_minor_changes = TRUE;
 	}
 
@@ -927,8 +938,8 @@ nm_ip4_config_dump (const NMIP4Config *config, const char *detail)
 	for (i = 0; i < nm_ip4_config_get_num_searches (config); i++)
 		g_message (" search: %s", nm_ip4_config_get_search (config, i));
 
-	g_message ("    mss: %u", nm_ip4_config_get_mss (config));
-	g_message ("    mtu: %u", nm_ip4_config_get_mtu (config));
+	g_message ("    mss: %"G_GUINT32_FORMAT, nm_ip4_config_get_mss (config));
+	g_message ("    mtu: %"G_GUINT32_FORMAT, nm_ip4_config_get_mtu (config));
 
 	/* NIS */
 	for (i = 0; i < nm_ip4_config_get_num_nis_servers (config); i++) {
@@ -936,7 +947,7 @@ nm_ip4_config_dump (const NMIP4Config *config, const char *detail)
 		g_message ("    nis: %s", nm_utils_inet4_ntop (tmp, NULL));
 	}
 
-	g_message (" nisdmn: %s", nm_ip4_config_get_nis_domain (config));
+	g_message (" nisdmn: %s", str_if_set (nm_ip4_config_get_nis_domain (config), "(none)"));
 
 	/* WINS */
 	for (i = 0; i < nm_ip4_config_get_num_wins (config); i++) {
@@ -1009,6 +1020,7 @@ nm_ip4_config_reset_addresses (NMIP4Config *config)
 
 	if (priv->addresses->len != 0) {
 		g_array_set_size (priv->addresses, 0);
+		_NOTIFY (config, PROP_ADDRESS_DATA);
 		_NOTIFY (config, PROP_ADDRESSES);
 	}
 }
@@ -1052,7 +1064,7 @@ nm_ip4_config_add_address (NMIP4Config *config, const NMPlatformIP4Address *new)
 			 * with "what should be" and the kernel values are "what turned out after configuring it".
 			 *
 			 * For other sources, the longer lifetime wins. */
-			if (   (new->source == NM_PLATFORM_SOURCE_KERNEL && new->source != item_old.source)
+			if (   (new->source == NM_IP_CONFIG_SOURCE_KERNEL && new->source != item_old.source)
 			    || nm_platform_ip_address_cmp_expiry ((const NMPlatformIPAddress *) &item_old, (const NMPlatformIPAddress *) new) > 0) {
 				item->timestamp = item_old.timestamp;
 				item->lifetime = item_old.lifetime;
@@ -1066,6 +1078,7 @@ nm_ip4_config_add_address (NMIP4Config *config, const NMPlatformIP4Address *new)
 
 	g_array_append_val (priv->addresses, *new);
 NOTIFY:
+	_NOTIFY (config, PROP_ADDRESS_DATA);
 	_NOTIFY (config, PROP_ADDRESSES);
 }
 
@@ -1077,6 +1090,7 @@ nm_ip4_config_del_address (NMIP4Config *config, guint i)
 	g_return_if_fail (i < priv->addresses->len);
 
 	g_array_remove_index (priv->addresses, i);
+	_NOTIFY (config, PROP_ADDRESS_DATA);
 	_NOTIFY (config, PROP_ADDRESSES);
 }
 
@@ -1121,6 +1135,7 @@ nm_ip4_config_reset_routes (NMIP4Config *config)
 
 	if (priv->routes->len != 0) {
 		g_array_set_size (priv->routes, 0);
+		_NOTIFY (config, PROP_ROUTE_DATA);
 		_NOTIFY (config, PROP_ROUTES);
 	}
 }
@@ -1139,10 +1154,11 @@ void
 nm_ip4_config_add_route (NMIP4Config *config, const NMPlatformIP4Route *new)
 {
 	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
-	NMPlatformSource old_source;
+	NMIPConfigSource old_source;
 	int i;
 
 	g_return_if_fail (new != NULL);
+	g_return_if_fail (new->plen > 0);
 
 	for (i = 0; i < priv->routes->len; i++ ) {
 		NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i);
@@ -1160,6 +1176,7 @@ nm_ip4_config_add_route (NMIP4Config *config, const NMPlatformIP4Route *new)
 
 	g_array_append_val (priv->routes, *new);
 NOTIFY:
+	_NOTIFY (config, PROP_ROUTE_DATA);
 	_NOTIFY (config, PROP_ROUTES);
 }
 
@@ -1171,6 +1188,7 @@ nm_ip4_config_del_route (NMIP4Config *config, guint i)
 	g_return_if_fail (i < priv->routes->len);
 
 	g_array_remove_index (priv->routes, i);
+	_NOTIFY (config, PROP_ROUTE_DATA);
 	_NOTIFY (config, PROP_ROUTES);
 }
 
@@ -1190,6 +1208,58 @@ nm_ip4_config_get_route (const NMIP4Config *config, guint i)
 	return &g_array_index (priv->routes, NMPlatformIP4Route, i);
 }
 
+const NMPlatformIP4Route *
+nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	guint i;
+	NMPlatformIP4Route *best_route = NULL;
+
+	g_return_val_if_fail (host, NULL);
+
+	for (i = 0; i < priv->routes->len; i++) {
+		NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i);
+
+		if (item->gateway != 0)
+			continue;
+
+		if (best_route && best_route->plen > item->plen)
+			continue;
+
+		if (nm_utils_ip4_address_clear_host_address (host, item->plen) != nm_utils_ip4_address_clear_host_address (item->network, item->plen))
+			continue;
+
+		if (best_route && best_route->metric <= item->metric)
+			continue;
+
+		best_route = item;
+	}
+
+	return best_route;
+}
+
+const NMPlatformIP4Address *
+nm_ip4_config_get_subnet_for_host (const NMIP4Config *config, guint32 host)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	guint i;
+	NMPlatformIP4Address *subnet = NULL;
+
+	g_return_val_if_fail (host, NULL);
+
+	for (i = 0; i < priv->addresses->len; i++) {
+		NMPlatformIP4Address *item = &g_array_index (priv->addresses, NMPlatformIP4Address, i);
+
+		if (subnet && subnet->plen >= item->plen)
+			continue;
+		if (nm_utils_ip4_address_clear_host_address (host, item->plen) != nm_utils_ip4_address_clear_host_address (item->address, item->plen))
+			continue;
+		subnet = item;
+	}
+
+	return subnet;
+}
+
 /******************************************************************/
 
 void
@@ -1503,11 +1573,15 @@ nm_ip4_config_get_wins (const NMIP4Config *config, guint i)
 /******************************************************************/
 
 void
-nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu)
+nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu, NMIPConfigSource source)
 {
 	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
-	priv->mtu = mtu;
+	if (source > priv->mtu_source) {
+		priv->mtu = mtu;
+		priv->mtu_source = source;
+	} else if (source == priv->mtu_source && (!priv->mtu || priv->mtu > mtu))
+		priv->mtu = mtu;
 }
 
 guint32
@@ -1518,6 +1592,14 @@ nm_ip4_config_get_mtu (const NMIP4Config *config)
 	return priv->mtu;
 }
 
+NMIPConfigSource
+nm_ip4_config_get_mtu_source (const NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	return priv->mtu_source;
+}
+
 /******************************************************************/
 
 static inline void
@@ -1652,6 +1734,15 @@ finalize (GObject *object)
 }
 
 static void
+gvalue_destroy (gpointer data)
+{
+	GValue *value = (GValue *) data;
+
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+static void
 get_property (GObject *object, guint prop_id,
 			  GValue *value, GParamSpec *pspec)
 {
@@ -1659,11 +1750,41 @@ get_property (GObject *object, guint prop_id,
 	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object);
 
 	switch (prop_id) {
-	case PROP_GATEWAY:
-		if (priv->gateway)
-			g_value_set_string (value, nm_utils_inet4_ntop (priv->gateway, NULL));
-		else
-			g_value_set_string (value, NULL);
+	case PROP_ADDRESS_DATA:
+		{
+			GPtrArray *addresses = g_ptr_array_new ();
+			int naddr = nm_ip4_config_get_num_addresses (config);
+			int i;
+
+			for (i = 0; i < naddr; i++) {
+				const NMPlatformIP4Address *address = nm_ip4_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_inet4_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);
+
+				if (*address->label) {
+					val = g_slice_new0 (GValue);
+					g_value_init (val, G_TYPE_STRING);
+					g_value_set_string (val, address->label);
+					g_hash_table_insert (addr_hash, "label", val);
+				}
+
+				g_ptr_array_add (addresses, addr_hash);
+			}
+
+			g_value_take_boxed (value, addresses);
+		}
 		break;
 	case PROP_ADDRESSES:
 		{
@@ -1674,10 +1795,11 @@ get_property (GObject *object, guint prop_id,
 			for (i = 0; i < naddr; i++) {
 				const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i);
 				GArray *array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
+				guint32 gateway = i == 0 ? priv->gateway : 0;
 
 				g_array_append_val (array, address->address);
 				g_array_append_val (array, address->plen);
-				g_array_append_val (array, priv->gateway);
+				g_array_append_val (array, gateway);
 
 				g_ptr_array_add (addresses, array);
 			}
@@ -1685,6 +1807,47 @@ get_property (GObject *object, guint prop_id,
 			g_value_take_boxed (value, addresses);
 		}
 		break;
+	case PROP_ROUTE_DATA:
+		{
+			GPtrArray *routes = g_ptr_array_new ();
+			guint nroutes = nm_ip4_config_get_num_routes (config);
+			int i;
+
+			for (i = 0; i < nroutes; i++) {
+				const NMPlatformIP4Route *route = nm_ip4_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_inet4_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);
+
+				if (route->gateway) {
+					val = g_slice_new0 (GValue);
+					g_value_init (val, G_TYPE_STRING);
+					g_value_set_string (val, nm_utils_inet4_ntop (route->gateway, NULL));
+					g_hash_table_insert (route_hash, "next-hop", val);
+				}
+
+				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_ptr_array_add (routes, route_hash);
+			}
+
+			g_value_take_boxed (value, routes);
+		}
+		break;
 	case PROP_ROUTES:
 		{
 			GPtrArray *routes = g_ptr_array_new ();
@@ -1693,8 +1856,14 @@ get_property (GObject *object, guint prop_id,
 
 			for (i = 0; i < nroutes; i++) {
 				const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i);
-				GArray *array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 4);
+				GArray *array;
+
+				/* legacy versions of nm_ip4_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_array_sized_new (FALSE, TRUE, sizeof (guint32), 4);
 				g_array_append_val (array, route->network);
 				g_array_append_val (array, route->plen);
 				g_array_append_val (array, route->gateway);
@@ -1706,6 +1875,12 @@ get_property (GObject *object, guint prop_id,
 			g_value_take_boxed (value, routes);
 		}
 		break;
+	case PROP_GATEWAY:
+		if (priv->gateway)
+			g_value_set_string (value, nm_utils_inet4_ntop (priv->gateway, NULL));
+		else
+			g_value_set_string (value, NULL);
+		break;
 	case PROP_NAMESERVERS:
 		g_value_set_boxed (value, priv->nameservers);
 		break;
@@ -1734,48 +1909,51 @@ nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
 	object_class->get_property = get_property;
 	object_class->finalize = finalize;
 
-	obj_properties[PROP_GATEWAY] =
-		 g_param_spec_string (NM_IP4_CONFIG_GATEWAY,
-		                      "Gateway",
-		                      "IP4 gateway",
-		                       NULL,
-		                       G_PARAM_READABLE);
+	obj_properties[PROP_ADDRESS_DATA] =
+		 g_param_spec_boxed (NM_IP4_CONFIG_ADDRESS_DATA, "", "",
+		                     DBUS_TYPE_NM_IP_ADDRESSES,
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ADDRESSES] =
-		 g_param_spec_boxed (NM_IP4_CONFIG_ADDRESSES,
-		                     "Addresses",
-		                     "IP4 addresses",
-		                     DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
-		                     G_PARAM_READABLE);
+		g_param_spec_boxed (NM_IP4_CONFIG_ADDRESSES, "", "",
+		                    DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_ROUTE_DATA] =
+		 g_param_spec_boxed (NM_IP4_CONFIG_ROUTE_DATA, "", "",
+		                     DBUS_TYPE_NM_IP_ROUTES,
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ROUTES] =
-		 g_param_spec_boxed (NM_IP4_CONFIG_ROUTES,
-		                     "Routes",
-		                     "Routes",
-		                     DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
-		                     G_PARAM_READABLE);
+		g_param_spec_boxed (NM_IP4_CONFIG_ROUTES, "", "",
+		                    DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_GATEWAY] =
+		g_param_spec_string (NM_IP4_CONFIG_GATEWAY, "", "",
+		                     NULL,
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_NAMESERVERS] =
-		 g_param_spec_boxed (NM_IP4_CONFIG_NAMESERVERS,
-		                     "Nameservers",
-		                     "DNS list",
+		 g_param_spec_boxed (NM_IP4_CONFIG_NAMESERVERS, "", "",
 		                     DBUS_TYPE_G_UINT_ARRAY,
-		                     G_PARAM_READABLE);
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_DOMAINS] =
-		 g_param_spec_boxed (NM_IP4_CONFIG_DOMAINS,
-		                     "Domains",
-		                     "Domains",
+		 g_param_spec_boxed (NM_IP4_CONFIG_DOMAINS, "", "",
 		                     DBUS_TYPE_G_ARRAY_OF_STRING,
-		                     G_PARAM_READABLE);
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_SEARCHES] =
-		 g_param_spec_boxed (NM_IP4_CONFIG_SEARCHES,
-		                     "Searches",
-		                     "Searches",
+		 g_param_spec_boxed (NM_IP4_CONFIG_SEARCHES, "", "",
 		                     DBUS_TYPE_G_ARRAY_OF_STRING,
-		                     G_PARAM_READABLE);
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_WINS_SERVERS] =
-		 g_param_spec_boxed (NM_IP4_CONFIG_WINS_SERVERS,
-		                     "WinsServers",
-		                     "WINS server list",
+		 g_param_spec_boxed (NM_IP4_CONFIG_WINS_SERVERS, "", "",
 		                     DBUS_TYPE_G_UINT_ARRAY,
-		                     G_PARAM_READABLE);
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, LAST_PROP, obj_properties);