summary refs log tree commit diff
path: root/src/nm-ip4-config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-ip4-config.c')
-rw-r--r--src/nm-ip4-config.c183
1 files changed, 150 insertions, 33 deletions
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index db8c4b43..5918f3dc 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -35,6 +35,7 @@
 #include "nm-core-internal.h"
 #include "nm-route-manager.h"
 #include "gsystem-local-alloc.h"
+#include "nm-macros-internal.h"
 
 G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, G_TYPE_OBJECT)
 
@@ -45,6 +46,7 @@ typedef struct {
 
 	gboolean never_default;
 	guint32 gateway;
+	gboolean has_gateway;
 	GArray *addresses;
 	GArray *routes;
 	GArray *nameservers;
@@ -56,6 +58,8 @@ typedef struct {
 	GArray *wins;
 	guint32 mtu;
 	NMIPConfigSource mtu_source;
+	gint64 route_metric;
+	gboolean metered;
 } NMIP4ConfigPrivate;
 
 /* internal guint32 are assigned to gobject properties of type uint. Ensure, that uint is large enough */
@@ -185,7 +189,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 	guint i;
 	guint32 lowest_metric = G_MAXUINT32;
 	guint32 old_gateway = 0;
-	gboolean has_gateway = FALSE;
+	gboolean old_has_gateway = FALSE;
 
 	/* Slaves have no IP configuration */
 	if (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex) > 0)
@@ -202,6 +206,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 
 	/* Extract gateway from default route */
 	old_gateway = priv->gateway;
+	old_has_gateway = priv->has_gateway;
 	for (i = 0; i < priv->routes->len; ) {
 		const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i);
 
@@ -210,7 +215,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 				priv->gateway = route->gateway;
 				lowest_metric = route->metric;
 			}
-			has_gateway = TRUE;
+			priv->has_gateway = TRUE;
 			/* Remove the default route from the list */
 			g_array_remove_index_fast (priv->routes, i);
 			continue;
@@ -218,10 +223,14 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 		i++;
 	}
 
+	/* we detect the route metric based on the default route. All non-default
+	 * routes have their route metrics explicitly set. */
+	priv->route_metric = priv->has_gateway ? (gint64) lowest_metric : (gint64) -1;
+
 	/* If there is a host route to the gateway, ignore that route.  It is
 	 * automatically added by NetworkManager when needed.
 	 */
-	if (has_gateway) {
+	if (priv->has_gateway) {
 		for (i = 0; i < priv->routes->len; i++) {
 			const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i);
 
@@ -237,7 +246,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 	/* If the interface has the default route, and has IPv4 addresses, capture
 	 * nameservers from /etc/resolv.conf.
 	 */
-	if (priv->addresses->len && has_gateway && capture_resolv_conf) {
+	if (priv->addresses->len && priv->has_gateway && capture_resolv_conf) {
 		if (nm_ip4_config_capture_resolv_conf (priv->nameservers, NULL))
 			_NOTIFY (config, PROP_NAMESERVERS);
 	}
@@ -247,7 +256,8 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 	_NOTIFY (config, PROP_ROUTE_DATA);
 	_NOTIFY (config, PROP_ADDRESSES);
 	_NOTIFY (config, PROP_ROUTES);
-	if (priv->gateway != old_gateway)
+	if (   priv->gateway != old_gateway
+	    || priv->has_gateway != old_has_gateway)
 		_NOTIFY (config, PROP_GATEWAY);
 
 	return config;
@@ -335,6 +345,7 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_fu
 void
 nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric)
 {
+	NMIP4ConfigPrivate *priv;
 	guint naddresses, nroutes, nnameservers, nsearches;
 	int i;
 
@@ -343,6 +354,8 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu
 
 	g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
 
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
 	g_object_freeze_notify (G_OBJECT (config));
 
 	naddresses = nm_setting_ip_config_get_num_addresses (setting);
@@ -362,6 +375,9 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu
 		nm_ip4_config_set_gateway (config, gateway);
 	}
 
+	if (priv->route_metric  == -1)
+		priv->route_metric = nm_setting_ip_config_get_route_metric (setting);
+
 	/* Addresses */
 	for (i = 0; i < naddresses; i++) {
 		NMIPAddress *s_addr = nm_setting_ip_config_get_address (setting, i);
@@ -430,6 +446,7 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 	guint naddresses, nroutes, nnameservers, nsearches;
 	const char *method = NULL;
 	int i;
+	gint64 route_metric;
 
 	s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ());
 
@@ -445,6 +462,7 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 	nroutes = nm_ip4_config_get_num_routes (config);
 	nnameservers = nm_ip4_config_get_num_nameservers (config);
 	nsearches = nm_ip4_config_get_num_searches (config);
+	route_metric = nm_ip4_config_get_route_metric (config);
 
 	/* Addresses */
 	for (i = 0; i < naddresses; i++) {
@@ -470,7 +488,7 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 	}
 
 	/* Gateway */
-	if (   gateway
+	if (   nm_ip4_config_has_gateway (config)
 	    && 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),
@@ -480,7 +498,11 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 	/* 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_IP_CONFIG_METHOD, method, NULL);
+
+	g_object_set (s_ip4,
+	              NM_SETTING_IP_CONFIG_METHOD, method,
+	              NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) route_metric,
+	              NULL);
 
 	/* Routes */
 	for (i = 0; i < nroutes; i++) {
@@ -521,13 +543,17 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 /******************************************************************/
 
 void
-nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src)
+nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFlags merge_flags)
 {
+	NMIP4ConfigPrivate *dst_priv, *src_priv;
 	guint32 i;
 
 	g_return_if_fail (src != NULL);
 	g_return_if_fail (dst != NULL);
 
+	dst_priv = NM_IP4_CONFIG_GET_PRIVATE (dst);
+	src_priv = NM_IP4_CONFIG_GET_PRIVATE (src);
+
 	g_object_freeze_notify (G_OBJECT (dst));
 
 	/* addresses */
@@ -535,24 +561,37 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src)
 		nm_ip4_config_add_address (dst, nm_ip4_config_get_address (src, i));
 
 	/* nameservers */
-	for (i = 0; i < nm_ip4_config_get_num_nameservers (src); i++)
-		nm_ip4_config_add_nameserver (dst, nm_ip4_config_get_nameserver (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip4_config_get_num_nameservers (src); i++)
+			nm_ip4_config_add_nameserver (dst, nm_ip4_config_get_nameserver (src, i));
+	}
 
 	/* default gateway */
-	if (nm_ip4_config_get_gateway (src))
+	if (nm_ip4_config_has_gateway (src))
 		nm_ip4_config_set_gateway (dst, nm_ip4_config_get_gateway (src));
 
 	/* routes */
-	for (i = 0; i < nm_ip4_config_get_num_routes (src); i++)
-		nm_ip4_config_add_route (dst, nm_ip4_config_get_route (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) {
+		for (i = 0; i < nm_ip4_config_get_num_routes (src); i++)
+			nm_ip4_config_add_route (dst, nm_ip4_config_get_route (src, i));
+	}
+
+	if (dst_priv->route_metric == -1)
+		dst_priv->route_metric = src_priv->route_metric;
+	else if (src_priv->route_metric != -1)
+		dst_priv->route_metric = MIN (dst_priv->route_metric, src_priv->route_metric);
 
 	/* domains */
-	for (i = 0; i < nm_ip4_config_get_num_domains (src); i++)
-		nm_ip4_config_add_domain (dst, nm_ip4_config_get_domain (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip4_config_get_num_domains (src); i++)
+			nm_ip4_config_add_domain (dst, nm_ip4_config_get_domain (src, i));
+	}
 
 	/* dns searches */
-	for (i = 0; i < nm_ip4_config_get_num_searches (src); i++)
-		nm_ip4_config_add_search (dst, nm_ip4_config_get_search (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip4_config_get_num_searches (src); i++)
+			nm_ip4_config_add_search (dst, nm_ip4_config_get_search (src, i));
+	}
 
 	/* MSS */
 	if (nm_ip4_config_get_mss (src))
@@ -564,15 +603,23 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src)
 		                       nm_ip4_config_get_mtu_source (src));
 
 	/* NIS */
-	for (i = 0; i < nm_ip4_config_get_num_nis_servers (src); i++)
-		nm_ip4_config_add_nis_server (dst, nm_ip4_config_get_nis_server (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip4_config_get_num_nis_servers (src); i++)
+			nm_ip4_config_add_nis_server (dst, nm_ip4_config_get_nis_server (src, i));
 
-	if (nm_ip4_config_get_nis_domain (src))
-		nm_ip4_config_set_nis_domain (dst, nm_ip4_config_get_nis_domain (src));
+		if (nm_ip4_config_get_nis_domain (src))
+			nm_ip4_config_set_nis_domain (dst, nm_ip4_config_get_nis_domain (src));
+	}
 
 	/* WINS */
-	for (i = 0; i < nm_ip4_config_get_num_wins (src); i++)
-		nm_ip4_config_add_wins (dst, nm_ip4_config_get_wins (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip4_config_get_num_wins (src); i++)
+			nm_ip4_config_add_wins (dst, nm_ip4_config_get_wins (src, i));
+	}
+
+	/* metered flag */
+	nm_ip4_config_set_metered (dst, nm_ip4_config_get_metered (dst) ||
+	                                nm_ip4_config_get_metered (src));
 
 	g_object_thaw_notify (G_OBJECT (dst));
 }
@@ -721,11 +768,14 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src)
 	}
 
 	/* default gateway */
-	if (nm_ip4_config_get_gateway (src) == nm_ip4_config_get_gateway (dst))
-		nm_ip4_config_set_gateway (dst, 0);
+	if (   (nm_ip4_config_has_gateway (src) == nm_ip4_config_has_gateway (dst))
+	    && (nm_ip4_config_get_gateway (src) == nm_ip4_config_get_gateway (dst)))
+		nm_ip4_config_unset_gateway (dst);
 
 	if (!nm_ip4_config_get_num_addresses (dst))
-		nm_ip4_config_set_gateway (dst, 0);
+		nm_ip4_config_unset_gateway (dst);
+
+	/* ignore route_metric */
 
 	/* routes */
 	for (i = 0; i < nm_ip4_config_get_num_routes (src); i++) {
@@ -796,12 +846,15 @@ nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src)
 			i++;
 	}
 
+	/* ignore route_metric */
 	/* ignore nameservers */
 
 	/* default gateway */
 	if (   !nm_ip4_config_get_num_addresses (dst)
-	    || (nm_ip4_config_get_gateway (src) != nm_ip4_config_get_gateway (dst)))
-		nm_ip4_config_set_gateway (dst, 0);
+	    || (nm_ip4_config_has_gateway (src) != nm_ip4_config_has_gateway (dst))
+	    || (nm_ip4_config_get_gateway (src) != nm_ip4_config_get_gateway (dst))) {
+		nm_ip4_config_unset_gateway (dst);
+	}
 
 	/* routes */
 	for (i = 0; i < nm_ip4_config_get_num_routes (dst); ) {
@@ -867,11 +920,17 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev
 	}
 
 	/* default gateway */
-	if (src_priv->gateway != dst_priv->gateway) {
+	if (   src_priv->gateway != dst_priv->gateway
+	    || src_priv->has_gateway != dst_priv->has_gateway) {
 		nm_ip4_config_set_gateway (dst, src_priv->gateway);
 		has_relevant_changes = TRUE;
 	}
 
+	if (src_priv->route_metric != dst_priv->route_metric) {
+		dst_priv->route_metric = src_priv->route_metric;
+		has_minor_changes = TRUE;
+	}
+
 	/* addresses */
 	num = nm_ip4_config_get_num_addresses (src);
 	are_equal = num == nm_ip4_config_get_num_addresses (dst);
@@ -1028,6 +1087,12 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev
 		has_minor_changes = TRUE;
 	}
 
+	/* metered */
+	if (src_priv->metered != dst_priv->metered) {
+		dst_priv->metered = src_priv->metered;
+		has_minor_changes = TRUE;
+	}
+
 	/* 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);
@@ -1059,8 +1124,10 @@ nm_ip4_config_dump (const NMIP4Config *config, const char *detail)
 		g_message ("      a: %s", nm_platform_ip4_address_to_string (nm_ip4_config_get_address (config, i)));
 
 	/* default gateway */
-	tmp = nm_ip4_config_get_gateway (config);
-	g_message ("     gw: %s", nm_utils_inet4_ntop (tmp, NULL));
+	if (nm_ip4_config_has_gateway (config)) {
+		tmp = nm_ip4_config_get_gateway (config);
+		g_message ("     gw: %s", nm_utils_inet4_ntop (tmp, NULL));
+	}
 
 	/* nameservers */
 	for (i = 0; i < nm_ip4_config_get_num_nameservers (config); i++) {
@@ -1098,6 +1165,7 @@ nm_ip4_config_dump (const NMIP4Config *config, const char *detail)
 	}
 
 	g_message (" n-dflt: %d", nm_ip4_config_get_never_default (config));
+	g_message (" mtrd:   %d", (int) nm_ip4_config_get_metered (config));
 }
 
 gboolean
@@ -1139,12 +1207,33 @@ nm_ip4_config_set_gateway (NMIP4Config *config, guint32 gateway)
 {
 	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
-	if (priv->gateway != gateway) {
+	if (priv->gateway != gateway || !priv->has_gateway) {
 		priv->gateway = gateway;
+		priv->has_gateway = TRUE;
 		_NOTIFY (config, PROP_GATEWAY);
 	}
 }
 
+void
+nm_ip4_config_unset_gateway (NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	if (priv->has_gateway) {
+		priv->gateway = 0;
+		priv->has_gateway = FALSE;
+		_NOTIFY (config, PROP_GATEWAY);
+	}
+}
+
+gboolean
+nm_ip4_config_has_gateway (const NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	return priv->has_gateway;
+}
+
 guint32
 nm_ip4_config_get_gateway (const NMIP4Config *config)
 {
@@ -1153,6 +1242,14 @@ nm_ip4_config_get_gateway (const NMIP4Config *config)
 	return priv->gateway;
 }
 
+gint64
+nm_ip4_config_get_route_metric (const NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	return priv->route_metric;
+}
+
 /******************************************************************/
 
 void
@@ -1744,6 +1841,24 @@ nm_ip4_config_get_mtu_source (const NMIP4Config *config)
 
 /******************************************************************/
 
+void
+nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	priv->metered = !!metered;
+}
+
+gboolean
+nm_ip4_config_get_metered (const NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	return priv->metered;
+}
+
+/******************************************************************/
+
 static inline void
 hash_u32 (GChecksum *sum, guint32 n)
 {
@@ -1760,6 +1875,7 @@ nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only
 	g_return_if_fail (sum);
 
 	if (!dns_only) {
+		hash_u32 (sum, nm_ip4_config_has_gateway (config));
 		hash_u32 (sum, nm_ip4_config_get_gateway (config));
 
 		for (i = 0; i < nm_ip4_config_get_num_addresses (config); i++) {
@@ -1856,6 +1972,7 @@ nm_ip4_config_init (NMIP4Config *config)
 	priv->searches = g_ptr_array_new_with_free_func (g_free);
 	priv->nis = g_array_new (FALSE, TRUE, sizeof (guint32));
 	priv->wins = g_array_new (FALSE, TRUE, sizeof (guint32));
+	priv->route_metric = -1;
 }
 
 static void
@@ -2020,7 +2137,7 @@ get_property (GObject *object, guint prop_id,
 		}
 		break;
 	case PROP_GATEWAY:
-		if (priv->gateway)
+		if (priv->has_gateway)
 			g_value_set_string (value, nm_utils_inet4_ntop (priv->gateway, NULL));
 		else
 			g_value_set_string (value, NULL);