summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2014-01-24 09:46:25 -0600
committerDan Williams <dcbw@redhat.com>2014-01-24 09:46:25 -0600
commitde56f28db62d042c2c293867750228d6ac253892 (patch)
treeb94ea95a534ca6925a9f9400c778cad0089d764b
parentd96d242bc67153673223717e10d84ca1fda11bdf (diff)
parent8959b6dbcba24c922ec77d09424cd441ec1c95f9 (diff)
merge: handle interface-less VPNs like open/libre-swan (bgo #721724) (rh #1030068)
IPSec-based VPNs that use the kernel IPSec functionality don't have tunnel interfaces. Instead, the IP addresses and routes they create are added to the parent interface and the kernel handles securing the traffic based on routing. Modify NM to handle this. Also modify NM to ensure it doesn't touch routes added externally, and to preserve various route properties correctly.
-rw-r--r--src/devices/nm-device.c5
-rw-r--r--src/dhcp-manager/nm-dhcp-client.c5
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient-utils.c1
-rw-r--r--src/dns-manager/nm-dns-manager.c5
-rw-r--r--src/modem-manager/nm-modem-broadband.c1
-rw-r--r--src/modem-manager/nm-modem-old.c1
-rw-r--r--src/nm-dispatcher.c2
-rw-r--r--src/nm-ip4-config.c49
-rw-r--r--src/nm-ip6-config.c49
-rw-r--r--src/nm-policy.c35
-rw-r--r--src/platform/nm-linux-platform.c4
-rw-r--r--src/platform/nm-platform.c169
-rw-r--r--src/platform/nm-platform.h20
-rw-r--r--src/ppp-manager/nm-ppp-manager.c1
-rw-r--r--src/tests/test-ip4-config.c116
-rw-r--r--src/tests/test-ip6-config.c116
-rw-r--r--src/vpn-manager/nm-vpn-connection.c124
17 files changed, 593 insertions, 110 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 9e8a169fd0..35103e54fa 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -2310,12 +2310,14 @@ aipd_get_ip4_config (NMDevice *self, guint32 lla)
memset (&address, 0, sizeof (address));
address.address = lla;
address.plen = 16;
+ address.source = NM_PLATFORM_SOURCE_IP4LL;
nm_ip4_config_add_address (config, &address);
/* Add a multicast route for link-local connections: destination= 224.0.0.0, netmask=240.0.0.0 */
memset (&route, 0, sizeof (route));
route.network = htonl (0xE0000000L);
route.plen = 4;
+ route.source = NM_PLATFORM_SOURCE_IP4LL;
nm_ip4_config_add_route (config, &route);
return config;
@@ -2834,6 +2836,7 @@ shared4_new_config (NMDevice *self, NMConnection *connection, NMDeviceStateReaso
}
config = nm_ip4_config_new ();
+ address.source = NM_PLATFORM_SOURCE_SHARED;
nm_ip4_config_add_address (config, &address);
/* Remove the address lock when the object gets disposed */
@@ -3327,6 +3330,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
address.timestamp = discovered_address->timestamp;
address.lifetime = discovered_address->lifetime;
address.preferred = discovered_address->preferred;
+ address.source = NM_PLATFORM_SOURCE_RDISC;
nm_ip6_config_add_address (priv->ac_ip6_config, &address);
}
@@ -3349,6 +3353,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
route.network = discovered_route->network;
route.plen = discovered_route->plen;
route.gateway = discovered_route->gateway;
+ route.source = NM_PLATFORM_SOURCE_RDISC;
nm_ip6_config_add_route (priv->ac_ip6_config, &route);
}
diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c
index 57298c83ab..b8a9ce30a7 100644
--- a/src/dhcp-manager/nm-dhcp-client.c
+++ b/src/dhcp-manager/nm-dhcp-client.c
@@ -866,6 +866,7 @@ ip4_process_dhcpcd_rfc3442_routes (const char *str,
route.network = rt_addr;
route.plen = rt_cidr;
route.gateway = rt_route;
+ route.source = NM_PLATFORM_SOURCE_DHCP;
nm_ip4_config_add_route (ip4_config, &route);
}
}
@@ -968,6 +969,7 @@ ip4_process_dhclient_rfc3442_routes (const char *str,
char addr[INET_ADDRSTRLEN];
/* normal route */
+ route.source = NM_PLATFORM_SOURCE_DHCP;
nm_ip4_config_add_route (ip4_config, &route);
nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s",
@@ -1087,6 +1089,7 @@ process_classful_routes (GHashTable *options, NMIP4Config *ip4_config)
route.plen = 32;
}
route.gateway = rt_route;
+ route.source = NM_PLATFORM_SOURCE_DHCP;
nm_ip4_config_add_route (ip4_config, &route);
nm_log_info (LOGD_DHCP, " static route %s",
@@ -1252,6 +1255,7 @@ ip4_options_to_config (NMDHCPClient *self)
nm_log_info (LOGD_DHCP4, " lease time %d", address.lifetime);
}
+ address.source = NM_PLATFORM_SOURCE_DHCP;
nm_ip4_config_add_address (ip4_config, &address);
str = g_hash_table_lookup (priv->options, "new_host_name");
@@ -1419,6 +1423,7 @@ ip6_options_to_config (NMDHCPClient *self)
}
address.address = tmp_addr;
+ address.source = NM_PLATFORM_SOURCE_DHCP;
nm_ip6_config_add_address (ip6_config, &address);
nm_log_info (LOGD_DHCP6, " address %s", str);
} else if (priv->info_only == FALSE) {
diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c
index 64e8f72669..fad3a7a8bb 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient-utils.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c
@@ -625,6 +625,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface,
address.plen = nm_utils_ip4_get_default_prefix (address.address);
address.lifetime = address.preferred = expiry;
+ address.source = NM_PLATFORM_SOURCE_DHCP;
ip4 = nm_ip4_config_new ();
nm_ip4_config_add_address (ip4, &address);
diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c
index 44c6f11158..659fbec6a8 100644
--- a/src/dns-manager/nm-dns-manager.c
+++ b/src/dns-manager/nm-dns-manager.c
@@ -185,7 +185,6 @@ merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src)
const char *iface;
iface = g_object_get_data (G_OBJECT (src), IP_CONFIG_IFACE_TAG);
- g_assert (iface);
num = nm_ip6_config_get_num_nameservers (src);
for (i = 0; i < num; i++) {
@@ -201,7 +200,7 @@ merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src)
add_string_item (rc->nameservers, buf);
} else {
if (inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN) > 0) {
- if (IN6_IS_ADDR_LINKLOCAL (addr)) {
+ if (iface && IN6_IS_ADDR_LINKLOCAL (addr)) {
tmp = g_strdup_printf ("%s%%%s", buf, iface);
add_string_item (rc->nameservers, tmp);
g_free (tmp);
@@ -828,7 +827,6 @@ nm_dns_manager_add_ip4_config (NMDnsManager *mgr,
GError *error = NULL;
g_return_val_if_fail (mgr != NULL, FALSE);
- g_return_val_if_fail (iface != NULL, FALSE);
g_return_val_if_fail (config != NULL, FALSE);
priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
@@ -906,7 +904,6 @@ nm_dns_manager_add_ip6_config (NMDnsManager *mgr,
GError *error = NULL;
g_return_val_if_fail (mgr != NULL, FALSE);
- g_return_val_if_fail (iface != NULL, FALSE);
g_return_val_if_fail (config != NULL, FALSE);
priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
diff --git a/src/modem-manager/nm-modem-broadband.c b/src/modem-manager/nm-modem-broadband.c
index 508624d8e4..032557de99 100644
--- a/src/modem-manager/nm-modem-broadband.c
+++ b/src/modem-manager/nm-modem-broadband.c
@@ -656,6 +656,7 @@ static_stage3_done (NMModemBroadband *self)
memset (&address, 0, sizeof (address));
address.address = address_network;
address.plen = mm_bearer_ip_config_get_prefix (self->priv->ipv4_config);
+ address.source = NM_PLATFORM_SOURCE_WWAN;
nm_ip4_config_add_address (config, &address);
nm_log_info (LOGD_MB, " address %s/%d",
diff --git a/src/modem-manager/nm-modem-old.c b/src/modem-manager/nm-modem-old.c
index 60ddd4732d..866a94a122 100644
--- a/src/modem-manager/nm-modem-old.c
+++ b/src/modem-manager/nm-modem-old.c
@@ -620,6 +620,7 @@ static_stage3_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
/* IP address */
address.address = g_value_get_uint (g_value_array_get_nth (ret_array, 0));
address.plen = 32;
+ address.source = NM_PLATFORM_SOURCE_WWAN;
nm_ip4_config_add_address (config, &address);
nm_log_info (LOGD_MB, " address %s/%d",
diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c
index fafc9041a5..7cf93f8411 100644
--- a/src/nm-dispatcher.c
+++ b/src/nm-dispatcher.c
@@ -329,7 +329,7 @@ _dispatcher_call (DispatcherAction action,
device_ip6_props,
device_dhcp4_props,
device_dhcp6_props);
- if (vpn_iface)
+ if (vpn_ip4_config || vpn_ip6_config)
fill_vpn_props (vpn_ip4_config, vpn_ip6_config, vpn_ip4_props, vpn_ip6_props);
}
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index b9ce0cbadb..1a6f3d33cd 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -265,7 +265,6 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, int priority)
for (i = 0; i < count; i++) {
memcpy (&route, nm_ip4_config_get_route (config, i), sizeof (route));
- route.metric = priority;
/* Don't add the route if it's more specific than one of the subnets
* the device already has an IP address on.
@@ -279,6 +278,11 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, int priority)
if (nm_ip4_config_get_never_default (config) && route.network == 0)
continue;
+ /* Use the default metric only if the route was created by NM and
+ * didn't already specify a metric.
+ */
+ if (route.source != NM_PLATFORM_SOURCE_KERNEL && !route.metric)
+ route.metric = priority ? priority : NM_PLATFORM_ROUTE_METRIC_DEFAULT;
g_array_append_val (routes, route);
}
@@ -335,6 +339,7 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIP4Config *setting)
address.plen = nm_ip4_address_get_prefix (s_addr);
address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
address.preferred = NM_PLATFORM_LIFETIME_PERMANENT;
+ address.source = NM_PLATFORM_SOURCE_USER;
nm_ip4_config_add_address (config, &address);
}
@@ -351,6 +356,7 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIP4Config *setting)
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);
+ route.source = NM_PLATFORM_SOURCE_USER;
nm_ip4_config_add_route (config, &route);
}
@@ -981,10 +987,21 @@ nm_ip4_config_reset_addresses (NMIP4Config *config)
}
}
+/**
+ * nm_ip4_config_add_address:
+ * @config: the #NMIP4Config
+ * @new: the new address to add to @config
+ *
+ * Adds the new address to @config. If an address with the same basic properties
+ * (address, prefix) already exists in @config, it is overwritten with the
+ * lifetime and preferred of @new. The source is also overwritten by the source
+ * from @new if that source is higher priority.
+ */
void
nm_ip4_config_add_address (NMIP4Config *config, const NMPlatformIP4Address *new)
{
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+ NMPlatformSource old_source;
int i;
g_return_if_fail (new != NULL);
@@ -995,7 +1012,10 @@ nm_ip4_config_add_address (NMIP4Config *config, const NMPlatformIP4Address *new)
if (addresses_are_duplicate (item, new, FALSE)) {
if (nm_platform_ip4_address_cmp (item, new) == 0)
return;
+ old_source = item->source;
memcpy (item, new, sizeof (*item));
+ /* Restore highest priority source */
+ item->source = MAX (old_source, new->source);
goto NOTIFY;
}
}
@@ -1061,10 +1081,21 @@ nm_ip4_config_reset_routes (NMIP4Config *config)
}
}
+/**
+ * nm_ip4_config_add_route:
+ * @config: the #NMIP4Config
+ * @new: the new route to add to @config
+ *
+ * Adds the new route to @config. If a route with the same basic properties
+ * (network, prefix) already exists in @config, it is overwritten including the
+ * gateway and metric of @new. The source is also overwritten by the source
+ * from @new if that source is higher priority.
+ */
void
nm_ip4_config_add_route (NMIP4Config *config, const NMPlatformIP4Route *new)
{
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+ NMPlatformSource old_source;
int i;
g_return_if_fail (new != NULL);
@@ -1075,7 +1106,10 @@ nm_ip4_config_add_route (NMIP4Config *config, const NMPlatformIP4Route *new)
if (routes_are_duplicate (item, new, FALSE)) {
if (nm_platform_ip4_route_cmp (item, new) == 0)
return;
+ old_source = item->source;
memcpy (item, new, sizeof (*item));
+ /* Restore highest priority source */
+ item->source = MAX (old_source, new->source);
goto NOTIFY;
}
}
@@ -1500,6 +1534,19 @@ nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only
}
}
+/**
+ * nm_ip4_config_equal:
+ * @a: first config to compare
+ * @b: second config to compare
+ *
+ * Compares two #NMIP4Configs for basic equality. This means that all
+ * attributes must exist in the same order in both configs (addresses, routes,
+ * domains, DNS servers, etc) but some attributes (address lifetimes, and address
+ * and route sources) are ignored.
+ *
+ * Returns: %TRUE if the configurations are basically equal to each other,
+ * %FALSE if not
+ */
gboolean
nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b)
{
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index 178f0b51b1..c6f213a7ae 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -266,7 +266,6 @@ nm_ip6_config_commit (const NMIP6Config *config, int ifindex, int priority)
for (i = 0; i < count; i++) {
memcpy (&route, nm_ip6_config_get_route (config, i), sizeof (route));
- route.metric = priority;
/* Don't add the route if it's more specific than one of the subnets
* the device already has an IP address on.
@@ -280,6 +279,11 @@ nm_ip6_config_commit (const NMIP6Config *config, int ifindex, int priority)
if (nm_ip6_config_get_never_default (config) && IN6_IS_ADDR_UNSPECIFIED (&route.network))
continue;
+ /* Use the default metric only if the route was created by NM and
+ * didn't already specify a metric.
+ */
+ if (route.source != NM_PLATFORM_SOURCE_KERNEL && !route.metric)
+ route.metric = priority ? priority : NM_PLATFORM_ROUTE_METRIC_DEFAULT;
g_array_append_val (routes, route);
}
@@ -330,6 +334,7 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIP6Config *setting)
address.plen = nm_ip6_address_get_prefix (s_addr);
address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
address.preferred = NM_PLATFORM_LIFETIME_PERMANENT;
+ address.source = NM_PLATFORM_SOURCE_USER;
nm_ip6_config_add_address (config, &address);
}
@@ -346,6 +351,7 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIP6Config *setting)
route.plen = nm_ip6_route_get_prefix (s_route);
route.gateway = *nm_ip6_route_get_next_hop (s_route);
route.metric = nm_ip6_route_get_metric (s_route);
+ route.source = NM_PLATFORM_SOURCE_USER;
nm_ip6_config_add_route (config, &route);
}
@@ -887,10 +893,21 @@ nm_ip6_config_reset_addresses (NMIP6Config *config)
}
}
+/**
+ * nm_ip6_config_add_address:
+ * @config: the #NMIP6Config
+ * @new: the new address to add to @config
+ *
+ * Adds the new address to @config. If an address with the same basic properties
+ * (address, prefix) already exists in @config, it is overwritten with the
+ * lifetime and preferred of @new. The source is also overwritten by the source
+ * from @new if that source is higher priority.
+ */
void
nm_ip6_config_add_address (NMIP6Config *config, const NMPlatformIP6Address *new)
{
NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+ NMPlatformSource old_source;
int i;
g_return_if_fail (new != NULL);
@@ -901,8 +918,11 @@ nm_ip6_config_add_address (NMIP6Config *config, const NMPlatformIP6Address *new)
if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) {
if (nm_platform_ip6_address_cmp (item, new) == 0)
return;
+ old_source = item->source;
/* Copy over old item to get new lifetime, timestamp, preferred */
*item = *new;
+ /* But restore highest priority source */
+ item->source = MAX (old_source, new->source);
goto NOTIFY;
}
}
@@ -969,10 +989,21 @@ nm_ip6_config_reset_routes (NMIP6Config *config)
}
}
+/**
+ * nm_ip6_config_add_route:
+ * @config: the #NMIP6Config
+ * @new: the new route to add to @config
+ *
+ * Adds the new route to @config. If a route with the same basic properties
+ * (network, prefix) already exists in @config, it is overwritten including the
+ * gateway and metric of @new. The source is also overwritten by the source
+ * from @new if that source is higher priority.
+ */
void
nm_ip6_config_add_route (NMIP6Config *config, const NMPlatformIP6Route *new)
{
NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+ NMPlatformSource old_source;
int i;
g_return_if_fail (new != NULL);
@@ -983,7 +1014,10 @@ nm_ip6_config_add_route (NMIP6Config *config, const NMPlatformIP6Route *new)
if (routes_are_duplicate (item, new, FALSE)) {
if (nm_platform_ip6_route_cmp (item, new) == 0)
return;
+ old_source = item->source;
*item = *new;
+ /* Restore highest priority source */
+ item->source = MAX (old_source, new->source);
goto NOTIFY;
}
}
@@ -1268,6 +1302,19 @@ nm_ip6_config_hash (const NMIP6Config *config, GChecksum *sum, gboolean dns_only
}
}
+/**
+ * nm_ip6_config_equal:
+ * @a: first config to compare
+ * @b: second config to compare
+ *
+ * Compares two #NMIP6Configs for basic equality. This means that all
+ * attributes must exist in the same order in both configs (addresses, routes,
+ * domains, DNS servers, etc) but some attributes (address lifetimes, and address
+ * and route sources) are ignored.
+ *
+ * Returns: %TRUE if the configurations are basically equal to each other,
+ * %FALSE if not
+ */
gboolean
nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b)
{
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 92ec1abe96..15dac7051a 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -655,20 +655,24 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update)
in_addr_t int_gw = nm_vpn_connection_get_ip4_internal_gateway (vpn);
int mss = nm_ip4_config_get_mss (ip4_config);
- if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, int_gw, 0, mss)) {
- (void) nm_platform_ip4_route_add (parent_ifindex, gw_addr, 32, 0, 0, parent_mss);
- if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, int_gw, 0, mss)) {
+ /* If no VPN interface, use the parent interface */
+ if (ip_ifindex <= 0)
+ ip_ifindex = parent_ifindex;
+
+ if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, int_gw, NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) {
+ (void) nm_platform_ip4_route_add (parent_ifindex, gw_addr, 32, 0, NM_PLATFORM_ROUTE_METRIC_DEFAULT, parent_mss);
+ if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, int_gw, NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss))
nm_log_err (LOGD_IP4 | LOGD_VPN, "Failed to set default route.");
- }
}
default_device = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn));
} else {
int mss = nm_ip4_config_get_mss (ip4_config);
- if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, gw_addr, 0, mss)) {
- (void) nm_platform_ip4_route_add (ip_ifindex, gw_addr, 32, 0, 0, mss);
- if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, gw_addr, 0, mss)) {
+ g_assert (ip_iface);
+ if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, gw_addr, NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) {
+ (void) nm_platform_ip4_route_add (ip_ifindex, gw_addr, 32, 0, NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss);
+ if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, gw_addr, NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) {
nm_log_err (LOGD_IP4, "Failed to set default route.");
}
}
@@ -845,9 +849,13 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update)
if (!int_gw)
int_gw = &in6addr_any;
- if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *int_gw, 0, mss)) {
- (void) nm_platform_ip6_route_add (parent_ifindex, *gw_addr, 128, in6addr_any, 0, parent_mss);
- if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *int_gw, 0, mss)) {
+ /* If no VPN interface, use the parent interface */
+ if (ip_ifindex <= 0)
+ ip_ifindex = parent_ifindex;
+
+ if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *int_gw, NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) {
+ (void) nm_platform_ip6_route_add (parent_ifindex, *gw_addr, 128, in6addr_any, NM_PLATFORM_ROUTE_METRIC_DEFAULT, parent_mss);
+ if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *int_gw, NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) {
nm_log_err (LOGD_IP6 | LOGD_VPN, "Failed to set default route.");
}
}
@@ -856,11 +864,10 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update)
} else {
int mss = nm_ip6_config_get_mss (ip6_config);
- if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *gw_addr, 0, mss)) {
- (void) nm_platform_ip6_route_add (ip_ifindex, *gw_addr, 128, in6addr_any, 0, mss);
- if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *gw_addr, 0, mss)) {
+ if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *gw_addr, NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) {
+ (void) nm_platform_ip6_route_add (ip_ifindex, *gw_addr, 128, in6addr_any, NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss);
+ if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *gw_addr, NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss))
nm_log_err (LOGD_IP6, "Failed to set default route.");
- }
}
default_device6 = best;
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 8e85c59262..fd476c4103 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -2196,6 +2196,7 @@ ip4_address_get_all (NMPlatform *platform, int ifindex)
for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) {
if (nl_object_is_marked (object)) {
init_ip4_address (&address, (struct rtnl_addr *) object);
+ address.source = NM_PLATFORM_SOURCE_KERNEL;
g_array_append_val (addresses, address);
nl_object_unmark (object);
}
@@ -2219,6 +2220,7 @@ ip6_address_get_all (NMPlatform *platform, int ifindex)
for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) {
if (nl_object_is_marked (object)) {
init_ip6_address (&address, (struct rtnl_addr *) object);
+ address.source = NM_PLATFORM_SOURCE_KERNEL;
g_array_append_val (addresses, address);
nl_object_unmark (object);
}
@@ -2410,6 +2412,7 @@ ip4_route_get_all (NMPlatform *platform, int ifindex, gboolean include_default)
for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) {
if (nl_object_is_marked (object)) {
if (init_ip4_route (&route, (struct rtnl_route *) object)) {
+ route.source = NM_PLATFORM_SOURCE_KERNEL;
if (route.plen != 0 || include_default)
g_array_append_val (routes, route);
}
@@ -2435,6 +2438,7 @@ ip6_route_get_all (NMPlatform *platform, int ifindex, gboolean include_default)
for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) {
if (nl_object_is_marked (object)) {
if (init_ip6_route (&route, (struct rtnl_route *) object)) {
+ route.source = NM_PLATFORM_SOURCE_KERNEL;
if (route.plen != 0 || include_default)
g_array_append_val (routes, route);
}
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 9a40d6770f..ac24009548 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -1478,9 +1478,6 @@ nm_platform_ip4_route_add (int ifindex,
g_return_val_if_fail (mss >= 0, FALSE);
g_return_val_if_fail (klass->ip4_route_add, FALSE);
- if (!metric)
- metric = 1024;
-
return klass->ip4_route_add (platform, ifindex, network, plen, gateway, metric, mss);
}
@@ -1494,9 +1491,6 @@ nm_platform_ip6_route_add (int ifindex,
g_return_val_if_fail (mss >= 0, FALSE);
g_return_val_if_fail (klass->ip6_route_add, FALSE);
- if (!metric)
- metric = 1024;
-
return klass->ip6_route_add (platform, ifindex, network, plen, gateway, metric, mss);
}
@@ -1563,9 +1557,15 @@ array_contains_ip4_route (const GArray *routes, const NMPlatformIP4Route *route)
guint len = routes ? routes->len : 0;
guint i;
- for (i = 0; i < len; i++)
- if (!memcmp (&g_array_index (routes, NMPlatformIP4Route, i), route, sizeof (*route)))
+ for (i = 0; i < len; i++) {
+ NMPlatformIP4Route *c = &g_array_index (routes, NMPlatformIP4Route, i);
+
+ if (route->network == c->network &&
+ route->plen == c->plen &&
+ route->gateway == c->gateway &&
+ route->metric == c->metric)
return TRUE;
+ }
return FALSE;
}
@@ -1576,9 +1576,15 @@ array_contains_ip6_route (const GArray *routes, const NMPlatformIP6Route *route)
guint len = routes ? routes->len : 0;
guint i;
- for (i = 0; i < len; i++)
- if (!memcmp (&g_array_index (routes, NMPlatformIP6Route, i), route, sizeof (*route)))
+ for (i = 0; i < len; i++) {
+ NMPlatformIP6Route *c = &g_array_index (routes, NMPlatformIP6Route, i);
+
+ if (IN6_ARE_ADDR_EQUAL (&route->network, &c->network) &&
+ route->plen == c->plen &&
+ IN6_ARE_ADDR_EQUAL (&route->gateway, &c->gateway) &&
+ route->metric == c->metric)
return TRUE;
+ }
return FALSE;
}
@@ -1600,33 +1606,53 @@ nm_platform_ip4_route_sync (int ifindex, const GArray *known_routes)
GArray *routes;
NMPlatformIP4Route *route;
const NMPlatformIP4Route *known_route;
- int i;
+ gboolean success;
+ int i, i_type;
/* Delete unknown routes */
routes = nm_platform_ip4_route_get_all (ifindex, FALSE);
for (i = 0; i < routes->len; i++) {
route = &g_array_index (routes, NMPlatformIP4Route, i);
- route->ifindex = 0;
if (!array_contains_ip4_route (known_routes, route))
nm_platform_ip4_route_delete (ifindex, route->network, route->plen, route->metric);
}
- g_array_free (routes, TRUE);
- if (!known_routes)
+ if (!known_routes) {
+ g_array_free (routes, TRUE);
return TRUE;
+ }
/* Add missing routes */
- for (i = 0; i < known_routes->len; i++) {
- known_route = &g_array_index (known_routes, NMPlatformIP4Route, i);
+ for (i_type = 0, success = TRUE; i_type < 2 && success; i_type++) {
+ for (i = 0; i < known_routes->len && success; i++) {
+ known_route = &g_array_index (known_routes, NMPlatformIP4Route, i);
- if (!nm_platform_ip4_route_add (ifindex,
- known_route->network, known_route->plen, known_route->gateway,
- known_route->metric, known_route->mss))
- return FALSE;
+ if ((known_route->gateway == 0) ^ (i_type != 0)) {
+ /* Make two runs over the list of routes. On the first, only add
+ * device routes, on the second the others (gateway routes). */
+ continue;
+ }
+
+ /* Ignore routes that already exist */
+ if (!array_contains_ip4_route (routes, known_route)) {
+ success = nm_platform_ip4_route_add (ifindex,
+ known_route->network,
+ known_route->plen,
+ known_route->gateway,
+ known_route->metric,
+ known_route->mss);
+ if (!success && known_route->source < NM_PLATFORM_SOURCE_USER) {
+ nm_log_dbg (LOGD_PLATFORM, "ignore error adding IPv4 route to kernel: %s",
+ nm_platform_ip4_route_to_string (known_route));
+ success = TRUE;
+ }
+ }
+ }
}
- return TRUE;
+ g_array_free (routes, TRUE);
+ return success;
}
/**
@@ -1646,7 +1672,8 @@ nm_platform_ip6_route_sync (int ifindex, const GArray *known_routes)
GArray *routes;
NMPlatformIP6Route *route;
const NMPlatformIP6Route *known_route;
- int i;
+ gboolean success;
+ int i, i_type;
/* Delete unknown routes */
routes = nm_platform_ip6_route_get_all (ifindex, FALSE);
@@ -1657,22 +1684,42 @@ nm_platform_ip6_route_sync (int ifindex, const GArray *known_routes)
if (!array_contains_ip6_route (known_routes, route))
nm_platform_ip6_route_delete (ifindex, route->network, route->plen, route->metric);
}
- g_array_free (routes, TRUE);
- if (!known_routes)
+ if (!known_routes) {
+ g_array_free (routes, TRUE);
return TRUE;
+ }
/* Add missing routes */
- for (i = 0; i < known_routes->len; i++) {
- known_route = &g_array_index (known_routes, NMPlatformIP6Route, i);
+ for (i_type = 0, success = TRUE; i_type < 2 && success; i_type++) {
+ for (i = 0; i < known_routes->len && success; i++) {
+ known_route = &g_array_index (known_routes, NMPlatformIP6Route, i);
- if (!nm_platform_ip6_route_add (ifindex,
- known_route->network, known_route->plen, known_route->gateway,
- known_route->metric, known_route->mss))
- return FALSE;
+ if (IN6_IS_ADDR_UNSPECIFIED (&known_route->gateway) ^ (i_type != 0)) {
+ /* Make two runs over the list of routes. On the first, only add
+ * device routes, on the second the others (gateway routes). */
+ continue;
+ }
+
+ /* Ignore routes that already exist */
+ if (!array_contains_ip6_route (routes, known_route)) {
+ success = nm_platform_ip6_route_add (ifindex,
+ known_route->network,
+ known_route->plen,
+ known_route->gateway,
+ known_route->metric,
+ known_route->mss);
+ if (!success && known_route->source < NM_PLATFORM_SOURCE_USER) {
+ nm_log_dbg (LOGD_PLATFORM, "ignore error adding IPv6 route to kernel: %s",
+ nm_platform_ip6_route_to_string (known_route));
+ success = TRUE;
+ }
+ }
+ }
}
- return TRUE;
+ g_array_free (routes, TRUE);
+ return success;
}
gboolean
@@ -1684,6 +1731,34 @@ nm_platform_route_flush (int ifindex)
/******************************************************************/
+static const char *
+source_to_string (NMPlatformSource source)
+{
+ switch (source) {
+ case NM_PLATFORM_SOURCE_KERNEL:
+ return "kernel";
+ case NM_PLATFORM_SOURCE_SHARED:
+ return "shared";
+ case NM_PLATFORM_SOURCE_IP4LL:
+ return "ipv4ll";
+ case NM_PLATFORM_SOURCE_PPP:
+ return "ppp";
+ case NM_PLATFORM_SOURCE_WWAN:
+ return "wwan";
+ case NM_PLATFORM_SOURCE_VPN:
+ return "vpn";
+ case NM_PLATFORM_SOURCE_DHCP:
+ return "dhcp";
+ case NM_PLATFORM_SOURCE_RDISC:
+ return "rdisc";
+ case NM_PLATFORM_SOURCE_USER:
+ return "user";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
/**
* nm_platform_ip4_address_to_string:
* @route: pointer to NMPlatformIP4Address address structure
@@ -1718,11 +1793,12 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address)
s_dev = address->ifindex > 0 ? nm_platform_link_get_name (address->ifindex) : NULL;
str_dev = s_dev ? g_strconcat (" dev ", s_dev, NULL) : NULL;
- g_snprintf (buffer, sizeof (buffer), "%s/%d lft %u pref %u time %u%s%s",
+ g_snprintf (buffer, sizeof (buffer), "%s/%d lft %u pref %u time %u%s%s src %s",
s_address, address->plen, (guint)address->lifetime, (guint)address->preferred,
(guint)address->timestamp,
str_peer ? str_peer : "",
- str_dev ? str_dev : "");
+ str_dev ? str_dev : "",
+ source_to_string (address->source));
g_free (str_dev);
g_free (str_peer);
return buffer;
@@ -1767,12 +1843,13 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address)
rtnl_addr_flags2str(address->flags, s_flags, sizeof(s_flags));
str_flags = s_flags[0] ? g_strconcat (" flags ", s_flags, NULL) : NULL;
- g_snprintf (buffer, sizeof (buffer), "%s/%d lft %u pref %u time %u%s%s%s",
+ g_snprintf (buffer, sizeof (buffer), "%s/%d lft %u pref %u time %u%s%s%s src %s",
s_address, address->plen, (guint)address->lifetime, (guint)address->preferred,
(guint)address->timestamp,
str_peer ? str_peer : "",
str_dev ? str_dev : "",
- str_flags ? str_flags : "");
+ str_flags ? str_flags : "",
+ source_to_string (address->source));
g_free (str_flags);
g_free (str_dev);
g_free (str_peer);
@@ -1807,10 +1884,11 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route)
s_dev = route->ifindex > 0 ? nm_platform_link_get_name (route->ifindex) : NULL;
str_dev = s_dev ? g_strconcat (" dev ", s_dev, NULL) : NULL;
- g_snprintf (buffer, sizeof (buffer), "%s/%d via %s%s metric %u mss %u",
+ g_snprintf (buffer, sizeof (buffer), "%s/%d via %s%s metric %u mss %u src %s",
s_network, route->plen, s_gateway,
str_dev ? str_dev : "",
- route->metric, route->mss);
+ route->metric, route->mss,
+ source_to_string (route->source));
g_free (str_dev);
return buffer;
}
@@ -1843,10 +1921,11 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route)
s_dev = route->ifindex > 0 ? nm_platform_link_get_name (route->ifindex) : NULL;
str_dev = s_dev ? g_strconcat (" dev ", s_dev, NULL) : NULL;
- g_snprintf (buffer, sizeof (buffer), "%s/%d via %s%s metric %u mss %u",
+ g_snprintf (buffer, sizeof (buffer), "%s/%d via %s%s metric %u mss %u src %s",
s_network, route->plen, s_gateway,
str_dev ? str_dev : "",
- route->metric, route->mss);
+ route->metric, route->mss,
+ source_to_string (route->source));
g_free (str_dev);
return buffer;
}
@@ -1879,9 +1958,10 @@ int
nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b)
{
_CMP_POINTER (a, b);
- _CMP_FIELD_MEMCMP (a, b, address);
- _CMP_FIELD_MEMCMP (a, b, peer_address);
_CMP_FIELD (a, b, ifindex);
+ _CMP_FIELD (a, b, source);
+ _CMP_FIELD (a, b, address);
+ _CMP_FIELD (a, b, peer_address);
_CMP_FIELD (a, b, plen);
_CMP_FIELD (a, b, timestamp);
_CMP_FIELD (a, b, lifetime);
@@ -1894,6 +1974,7 @@ nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6A
{
_CMP_POINTER (a, b);
_CMP_FIELD (a, b, ifindex);
+ _CMP_FIELD (a, b, source);
_CMP_FIELD_MEMCMP (a, b, address);
_CMP_FIELD_MEMCMP (a, b, peer_address);
_CMP_FIELD (a, b, plen);
@@ -1909,9 +1990,10 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route
{
_CMP_POINTER (a, b);
_CMP_FIELD (a, b, ifindex);
- _CMP_FIELD_MEMCMP (a, b, network);
+ _CMP_FIELD (a, b, source);
+ _CMP_FIELD (a, b, network);
_CMP_FIELD (a, b, plen);
- _CMP_FIELD_MEMCMP (a, b, gateway);
+ _CMP_FIELD (a, b, gateway);
_CMP_FIELD (a, b, metric);
_CMP_FIELD (a, b, mss);
return 0;
@@ -1922,6 +2004,7 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route
{
_CMP_POINTER (a, b);
_CMP_FIELD (a, b, ifindex);
+ _CMP_FIELD (a, b, source);
_CMP_FIELD_MEMCMP (a, b, network);
_CMP_FIELD (a, b, plen);
_CMP_FIELD_MEMCMP (a, b, gateway);
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 0a5a599898..39f36057b4 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -119,8 +119,23 @@ typedef struct {
#define NM_PLATFORM_LIFETIME_PERMANENT G_MAXUINT32
+typedef enum {
+ /* In priority order; higher number == higher priority */
+ NM_PLATFORM_SOURCE_UNKNOWN,
+ NM_PLATFORM_SOURCE_KERNEL,
+ NM_PLATFORM_SOURCE_SHARED,
+ NM_PLATFORM_SOURCE_IP4LL,
+ NM_PLATFORM_SOURCE_PPP,
+ NM_PLATFORM_SOURCE_WWAN,
+ NM_PLATFORM_SOURCE_VPN,
+ NM_PLATFORM_SOURCE_DHCP,
+ NM_PLATFORM_SOURCE_RDISC,
+ NM_PLATFORM_SOURCE_USER,
+} NMPlatformSource;
+
typedef struct {
int ifindex;
+ NMPlatformSource source;
in_addr_t address;
in_addr_t peer_address; /* PTP peer address */
int plen;
@@ -131,6 +146,7 @@ typedef struct {
typedef struct {
int ifindex;
+ NMPlatformSource source;
struct in6_addr address;
struct in6_addr peer_address;
int plen;
@@ -140,8 +156,11 @@ typedef struct {
guint flags; /* ifa_flags from <linux/if_addr.h>, field type "unsigned int" is as used in rtnl_addr_get_flags. */
} NMPlatformIP6Address;
+#define NM_PLATFORM_ROUTE_METRIC_DEFAULT 1024
+
typedef struct {
int ifindex;
+ NMPlatformSource source;
in_addr_t network;
int plen;
in_addr_t gateway;
@@ -151,6 +170,7 @@ typedef struct {
typedef struct {
int ifindex;
+ NMPlatformSource source;
struct in6_addr network;
int plen;
struct in6_addr gateway;
diff --git a/src/ppp-manager/nm-ppp-manager.c b/src/ppp-manager/nm-ppp-manager.c
index 7b8493716e..cf085a8a73 100644
--- a/src/ppp-manager/nm-ppp-manager.c
+++ b/src/ppp-manager/nm-ppp-manager.c
@@ -550,6 +550,7 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager,
address.plen = g_value_get_uint (val);
if (address.address && address.plen) {
+ address.source = NM_PLATFORM_SOURCE_PPP;
nm_ip4_config_add_address (config, &address);
} else {
nm_log_err (LOGD_PPP, "invalid IPv4 address received!");
diff --git a/src/tests/test-ip4-config.c b/src/tests/test-ip4-config.c
index fde4a40cf2..e2d56948b3 100644
--- a/src/tests/test-ip4-config.c
+++ b/src/tests/test-ip4-config.c
@@ -173,6 +173,119 @@ test_subtract (void)
g_object_unref (dst);
}
+static void
+test_compare_with_source (void)
+{
+ NMIP4Config *a, *b;
+ NMPlatformIP4Address addr;
+ NMPlatformIP4Route route;
+
+ a = nm_ip4_config_new ();
+ b = nm_ip4_config_new ();
+
+ /* Address */
+ addr_init (&addr, "1.2.3.4", NULL, 24);
+ addr.source = NM_PLATFORM_SOURCE_USER;
+ nm_ip4_config_add_address (a, &addr);
+
+ addr.source = NM_PLATFORM_SOURCE_VPN;
+ nm_ip4_config_add_address (b, &addr);
+
+ /* Route */
+ route_new (&route, "10.0.0.0", 8, "192.168.1.1");
+ route.source = NM_PLATFORM_SOURCE_USER;
+ nm_ip4_config_add_route (a, &route);
+
+ route.source = NM_PLATFORM_SOURCE_VPN;
+ nm_ip4_config_add_route (b, &route);
+
+ /* Assert that the configs are basically the same, eg that the source is ignored */
+ g_assert (nm_ip4_config_equal (a, b));
+
+ g_object_unref (a);
+ g_object_unref (b);
+}
+
+static void
+test_add_address_with_source (void)
+{
+ NMIP4Config *a;
+ NMPlatformIP4Address addr;
+ const NMPlatformIP4Address *test_addr;
+
+ a = nm_ip4_config_new ();
+
+ /* Test that a higher priority source is not overwritten */
+ addr_init (&addr, "1.2.3.4", NULL, 24);
+ addr.source = NM_PLATFORM_SOURCE_USER;
+ nm_ip4_config_add_address (a, &addr);
+
+ test_addr = nm_ip4_config_get_address (a, 0);
+ g_assert_cmpint (test_addr->source, ==, NM_PLATFORM_SOURCE_USER);
+
+ addr.source = NM_PLATFORM_SOURCE_VPN;
+ nm_ip4_config_add_address (a, &addr);
+
+ test_addr = nm_ip4_config_get_address (a, 0);
+ g_assert_cmpint (test_addr->source, ==, NM_PLATFORM_SOURCE_USER);
+
+ /* Test that a lower priority address source is overwritten */
+ nm_ip4_config_del_address (a, 0);
+ addr.source = NM_PLATFORM_SOURCE_KERNEL;
+ nm_ip4_config_add_address (a, &addr);
+
+ test_addr = nm_ip4_config_get_address (a, 0);
+ g_assert_cmpint (test_addr->source, ==, NM_PLATFORM_SOURCE_KERNEL);
+
+ addr.source = NM_PLATFORM_SOURCE_USER;
+ nm_ip4_config_add_address (a, &addr);
+
+ test_addr = nm_ip4_config_get_address (a, 0);
+ g_assert_cmpint (test_addr->source, ==, NM_PLATFORM_SOURCE_USER);
+
+ g_object_unref (a);
+}
+
+static void
+test_add_route_with_source (void)
+{
+ NMIP4Config *a;
+ NMPlatformIP4Route route;
+ const NMPlatformIP4Route *test_route;
+
+ a = nm_ip4_config_new ();
+
+ /* Test that a higher priority source is not overwritten */
+ route_new (&route, "1.2.3.4", 24, "1.2.3.1");
+ route.source = NM_PLATFORM_SOURCE_USER;
+ nm_ip4_config_add_route (a, &route);
+
+ test_route = nm_ip4_config_get_route (a, 0);
+ g_assert_cmpint (test_route->source, ==, NM_PLATFORM_SOURCE_USER);
+
+ route.source = NM_PLATFORM_SOURCE_VPN;
+ nm_ip4_config_add_route (a, &route);
+
+ test_route = nm_ip4_config_get_route (a, 0);
+ g_assert_cmpint (test_route->source, ==, NM_PLATFORM_SOURCE_USER);
+
+ /* Test that a lower priority address source is overwritten */
+ nm_ip4_config_del_route (a, 0);
+ route.source = NM_PLATFORM_SOURCE_KERNEL;
+ nm_ip4_config_add_route (a, &route);
+
+ test_route = nm_ip4_config_get_route (a, 0);
+ g_assert_cmpint (test_route->source, ==, NM_PLATFORM_SOURCE_KERNEL);
+
+ route.source = NM_PLATFORM_SOURCE_USER;
+ nm_ip4_config_add_route (a, &route);
+
+ test_route = nm_ip4_config_get_route (a, 0);
+ g_assert_cmpint (test_route->source, ==, NM_PLATFORM_SOURCE_USER);
+
+ g_object_unref (a);
+}
+
/*******************************************/
int
@@ -183,6 +296,9 @@ main (int argc, char **argv)
g_type_init ();
g_test_add_func ("/ip4-config/subtract", test_subtract);
+ g_test_add_func ("/ip4-config/compare-with-source", test_compare_with_source);
+ g_test_add_func ("/ip4-config/add-address-with-source", test_add_address_with_source);
+ g_test_add_func ("/ip4-config/add-route-with-source", test_add_route_with_source);
return g_test_run ();
}
diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c
index b8b9c7b9ea..c79ac20a17 100644
--- a/src/tests/test-ip6-config.c
+++ b/src/tests/test-ip6-config.c
@@ -160,6 +160,119 @@ test_subtract (void)
g_object_unref (dst);
}
+static void
+test_compare_with_source (void)
+{
+ NMIP6Config *a, *b;
+ NMPlatformIP6Address addr;
+ NMPlatformIP6Route route;
+
+ a = nm_ip6_config_new ();
+ b = nm_ip6_config_new ();
+
+ /* Address */
+ addr_init (&addr, "1122:3344:5566::7788", NULL, 64);
+ addr.source = NM_PLATFORM_SOURCE_USER;
+ nm_ip6_config_add_address (a, &addr);
+
+ addr.source = NM_PLATFORM_SOURCE_VPN;
+ nm_ip6_config_add_address (b, &addr);
+
+ /* Route */
+ route_new (&route, "abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2");
+ route.source = NM_PLATFORM_SOURCE_USER;
+ nm_ip6_config_add_route (a, &route);
+
+ route.source = NM_PLATFORM_SOURCE_VPN;
+ nm_ip6_config_add_route (b, &route);
+
+ /* Assert that the configs are basically the same, eg that the source is ignored */
+ g_assert (nm_ip6_config_equal (a, b));
+
+ g_object_unref (a);
+ g_object_unref (b);
+}
+
+static void
+test_add_address_with_source (void)
+{
+ NMIP6Config *a;
+ NMPlatformIP6Address addr;
+ const NMPlatformIP6Address *test_addr;
+
+ a = nm_ip6_config_new ();
+
+ /* Test that a higher priority source is not overwritten */
+ addr_init (&addr, "1122:3344:5566::7788", NULL, 64);
+ addr.source = NM_PLATFORM_SOURCE_USER;
+ nm_ip6_config_add_address (a, &addr);
+
+ test_addr = nm_ip6_config_get_address (a, 0);
+ g_assert_cmpint (test_addr->source, ==, NM_PLATFORM_SOURCE_USER);
+
+ addr.source = NM_PLATFORM_SOURCE_VPN;
+ nm_ip6_config_add_address (a, &addr);
+
+ test_addr = nm_ip6_config_get_address (a, 0);
+ g_assert_cmpint (test_addr->source, ==, NM_PLATFORM_SOURCE_USER);
+
+ /* Test that a lower priority address source is overwritten */
+ nm_ip6_config_del_address (a, 0);
+ addr.source = NM_PLATFORM_SOURCE_KERNEL;
+ nm_ip6_config_add_address (a, &addr);
+
+ test_addr = nm_ip6_config_get_address (a, 0);
+ g_assert_cmpint (test_addr->source, ==, NM_PLATFORM_SOURCE_KERNEL);
+
+ addr.source = NM_PLATFORM_SOURCE_USER;
+ nm_ip6_config_add_address (a, &addr);
+
+ test_addr = nm_ip6_config_get_address (a, 0);
+ g_assert_cmpint (test_addr->source, ==, NM_PLATFORM_SOURCE_USER);
+
+ g_object_unref (a);
+}
+
+static void
+test_add_route_with_source (void)
+{
+ NMIP6Config *a;
+ NMPlatformIP6Route route;
+ const NMPlatformIP6Route *test_route;
+
+ a = nm_ip6_config_new ();
+
+ /* Test that a higher priority source is not overwritten */
+ route_new (&route, "abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2");
+ route.source = NM_PLATFORM_SOURCE_USER;
+ nm_ip6_config_add_route (a, &route);
+
+ test_route = nm_ip6_config_get_route (a, 0);
+ g_assert_cmpint (test_route->source, ==, NM_PLATFORM_SOURCE_USER);
+
+ route.source = NM_PLATFORM_SOURCE_VPN;
+ nm_ip6_config_add_route (a, &route);
+
+ test_route = nm_ip6_config_get_route (a, 0);
+ g_assert_cmpint (test_route->source, ==, NM_PLATFORM_SOURCE_USER);
+
+ /* Test that a lower priority address source is overwritten */
+ nm_ip6_config_del_route (a, 0);
+ route.source = NM_PLATFORM_SOURCE_KERNEL;
+ nm_ip6_config_add_route (a, &route);
+
+ test_route = nm_ip6_config_get_route (a, 0);
+ g_assert_cmpint (test_route->source, ==, NM_PLATFORM_SOURCE_KERNEL);
+
+ route.source = NM_PLATFORM_SOURCE_USER;
+ nm_ip6_config_add_route (a, &route);
+
+ test_route = nm_ip6_config_get_route (a, 0);
+ g_assert_cmpint (test_route->source, ==, NM_PLATFORM_SOURCE_USER);
+
+ g_object_unref (a);
+}
+
/*******************************************/
int
@@ -170,6 +283,9 @@ main (int argc, char **argv)
g_type_init ();
g_test_add_func ("/ip6-config/subtract", test_subtract);
+ g_test_add_func ("/ip6-config/compare-with-source", test_compare_with_source);
+ g_test_add_func ("/ip6-config/add-address-with-source", test_add_address_with_source);
+ g_test_add_func ("/ip6-config/add-route-with-source", test_add_route_with_source);
return g_test_run ();
}
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index 8541f10851..b0d71916ef 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -301,13 +301,13 @@ device_state_changed (NMActiveConnection *active,
}
static void
-add_ip4_vpn_gateway_route (NMDevice *parent_device, guint32 vpn_gw)
+add_ip4_vpn_gateway_route (NMIP4Config *config, NMDevice *parent_device, guint32 vpn_gw)
{
NMIP4Config *parent_config;
guint32 parent_gw;
NMPlatformIP4Route route;
- NMIP4Config *vpn4_config;
+ g_return_if_fail (NM_IS_IP4_CONFIG (config));
g_return_if_fail (NM_IS_DEVICE (parent_device));
g_return_if_fail (vpn_gw != 0);
@@ -321,8 +321,6 @@ add_ip4_vpn_gateway_route (NMDevice *parent_device, guint32 vpn_gw)
if (!parent_gw)
return;
- vpn4_config = nm_ip4_config_new ();
-
memset (&route, 0, sizeof (route));
route.network = vpn_gw;
route.plen = 32;
@@ -335,7 +333,8 @@ add_ip4_vpn_gateway_route (NMDevice *parent_device, guint32 vpn_gw)
if (nm_ip4_config_destination_is_direct (parent_config, vpn_gw, 32))
route.gateway = 0;
- nm_ip4_config_add_route (vpn4_config, &route);
+ route.source = NM_PLATFORM_SOURCE_VPN;
+ nm_ip4_config_add_route (config, &route);
/* Ensure there's a route to the parent device's gateway through the
* parent device, since if the VPN claims the default route and the VPN
@@ -345,22 +344,21 @@ add_ip4_vpn_gateway_route (NMDevice *parent_device, guint32 vpn_gw)
memset (&route, 0, sizeof (route));
route.network = parent_gw;
route.plen = 32;
+ route.source = NM_PLATFORM_SOURCE_VPN;
- nm_ip4_config_add_route (vpn4_config, &route);
-
- nm_device_set_vpn4_config (parent_device, vpn4_config);
- g_object_unref (vpn4_config);
+ nm_ip4_config_add_route (config, &route);
}
static void
-add_ip6_vpn_gateway_route (NMDevice *parent_device,
+add_ip6_vpn_gateway_route (NMIP6Config *config,
+ NMDevice *parent_device,
const struct in6_addr *vpn_gw)
{
NMIP6Config *parent_config;
const struct in6_addr *parent_gw;
NMPlatformIP6Route route;
- NMIP6Config *vpn6_config;
+ g_return_if_fail (NM_IS_IP6_CONFIG (config));
g_return_if_fail (NM_IS_DEVICE (parent_device));
g_return_if_fail (vpn_gw != NULL);
@@ -370,8 +368,6 @@ add_ip6_vpn_gateway_route (NMDevice *parent_device,
if (!parent_gw)
return;
- vpn6_config = nm_ip6_config_new ();
-
memset (&route, 0, sizeof (route));
route.network = *vpn_gw;
route.plen = 128;
@@ -384,7 +380,8 @@ add_ip6_vpn_gateway_route (NMDevice *parent_device,
if (nm_ip6_config_destination_is_direct (parent_config, vpn_gw, 128))
route.gateway = in6addr_any;
- nm_ip6_config_add_route (vpn6_config, &route);
+ route.source = NM_PLATFORM_SOURCE_VPN;
+ nm_ip6_config_add_route (config, &route);
/* Ensure there's a route to the parent device's gateway through the
* parent device, since if the VPN claims the default route and the VPN
@@ -394,11 +391,9 @@ add_ip6_vpn_gateway_route (NMDevice *parent_device,
memset (&route, 0, sizeof (route));
route.network = *parent_gw;
route.plen = 128;
+ route.source = NM_PLATFORM_SOURCE_VPN;
- nm_ip6_config_add_route (vpn6_config, &route);
-
- nm_device_set_vpn6_config (parent_device, vpn6_config);
- g_object_unref (vpn6_config);
+ nm_ip6_config_add_route (config, &route);
}
NMVPNConnection *
@@ -601,7 +596,7 @@ print_vpn_config (NMVPNConnection *connection)
nm_utils_inet6_ntop (priv->ip6_external_gw, NULL));
}
- nm_log_info (LOGD_VPN, "Tunnel Device: %s", priv->ip_iface);
+ nm_log_info (LOGD_VPN, "Tunnel Device: %s", priv->ip_iface ? priv->ip_iface : "(none)");
if (priv->ip4_config) {
nm_log_info (LOGD_VPN, "IPv4 configuration:");
@@ -692,25 +687,54 @@ nm_vpn_connection_apply_config (NMVPNConnection *connection)
{
NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (connection));
+ NMIP4Config *vpn4_parent_config = NULL;
+ NMIP6Config *vpn6_parent_config = NULL;
- nm_platform_link_set_up (priv->ip_ifindex);
+ if (priv->ip_ifindex > 0) {
+ nm_platform_link_set_up (priv->ip_ifindex);
- if (priv->ip4_config) {
- if (!nm_ip4_config_commit (priv->ip4_config, priv->ip_ifindex, 0))
- return FALSE;
+ if (priv->ip4_config) {
+ if (!nm_ip4_config_commit (priv->ip4_config, priv->ip_ifindex, 0))
+ return FALSE;
+ }
+
+ if (priv->ip6_config) {
+ if (!nm_ip6_config_commit (priv->ip6_config, priv->ip_ifindex, 0))
+ return FALSE;
+ }
+
+ if (priv->ip4_config)
+ vpn4_parent_config = nm_ip4_config_new ();
+ if (priv->ip6_config)
+ vpn6_parent_config = nm_ip6_config_new ();
+ } else {
+ /* If the VPN didn't return a network interface, it is a route-based
+ * VPN (like kernel IPSec) and all IP addressing and routing should
+ * be done on the parent interface instead.
+ */
+
+ if (priv->ip4_config)
+ vpn4_parent_config = g_object_ref (priv->ip4_config);
+ if (priv->ip6_config)
+ vpn6_parent_config = g_object_ref (priv->ip6_config);
}
- if (priv->ip6_config) {
- if (!nm_ip6_config_commit (priv->ip6_config, priv->ip_ifindex, 0))
- /* FIXME: remove ip4 config */
- return FALSE;
+ if (vpn4_parent_config) {
+ /* Add any explicit route to the VPN gateway through the parent device */
+ if (priv->ip4_external_gw)
+ add_ip4_vpn_gateway_route (vpn4_parent_config, parent_dev, priv->ip4_external_gw);
+
+ nm_device_set_vpn4_config (parent_dev, vpn4_parent_config);
+ g_object_unref (vpn4_parent_config);
}
+ if (vpn6_parent_config) {
+ /* Add any explicit route to the VPN gateway through the parent device */
+ if (priv->ip6_external_gw)
+ add_ip6_vpn_gateway_route (vpn6_parent_config, parent_dev, priv->ip6_external_gw);
- /* Add any explicit route to the VPN gateway through the parent device */
- if (priv->ip4_external_gw)
- add_ip4_vpn_gateway_route (parent_dev, priv->ip4_external_gw);
- if (priv->ip6_external_gw)
- add_ip6_vpn_gateway_route (parent_dev, priv->ip6_external_gw);
+ nm_device_set_vpn6_config (parent_dev, vpn6_parent_config);
+ g_object_unref (vpn6_parent_config);
+ }
nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.",
nm_connection_get_id (priv->connection));
@@ -768,21 +792,25 @@ process_generic_config (NMVPNConnection *connection,
NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
GValue *val;
+ g_clear_pointer (&priv->ip_iface, g_free);
+
val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_TUNDEV);
- if (val)
- priv->ip_iface = g_strdup (g_value_get_string (val));
- else {
- nm_log_err (LOGD_VPN, "invalid or missing tunnel device received!");
- nm_vpn_connection_config_maybe_complete (connection, FALSE);
- return FALSE;
+ if (val) {
+ const char *tmp = g_value_get_string (val);
+
+ /* Backwards compat with NM-openswan */
+ if (g_strcmp0 (tmp, "_none_") != 0)
+ priv->ip_iface = g_strdup (tmp);
}
- /* Grab the interface index for address/routing operations */
- priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface);
- if (!priv->ip_ifindex) {
- nm_log_err (LOGD_VPN, "(%s): failed to look up VPN interface index", priv->ip_iface);
- nm_vpn_connection_config_maybe_complete (connection, FALSE);
- return FALSE;
+ if (priv->ip_iface) {
+ /* Grab the interface index for address/routing operations */
+ priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface);
+ if (!priv->ip_ifindex) {
+ nm_log_err (LOGD_VPN, "(%s): failed to look up VPN interface index", priv->ip_iface);
+ nm_vpn_connection_config_maybe_complete (connection, FALSE);
+ return FALSE;
+ }
}
val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_BANNER);
@@ -920,6 +948,7 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
address.plen = g_value_get_uint (val);
if (address.address && address.plen) {
+ address.source = NM_PLATFORM_SOURCE_VPN;
nm_ip4_config_add_address (config, &address);
} else {
nm_log_err (LOGD_VPN, "invalid IP4 config received!");
@@ -978,6 +1007,7 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
route.network = nm_ip4_route_get_dest (item);
route.plen = nm_ip4_route_get_prefix (item);
route.gateway = nm_ip4_route_get_next_hop (item);
+ route.source = NM_PLATFORM_SOURCE_VPN;
/* Ignore host routes to the VPN gateway since NM adds one itself
* below. Since NM knows more about the routing situation than
@@ -1070,9 +1100,10 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy,
if (val)
address.plen = g_value_get_uint (val);
- if (!IN6_IS_ADDR_UNSPECIFIED (&address.address) && address.plen)
+ if (!IN6_IS_ADDR_UNSPECIFIED (&address.address) && address.plen) {
+ address.source = NM_PLATFORM_SOURCE_VPN;
nm_ip6_config_add_address (config, &address);
- else {
+ } else {
nm_log_err (LOGD_VPN, "invalid IP6 config received!");
g_object_unref (config);
nm_vpn_connection_config_maybe_complete (connection, FALSE);
@@ -1121,6 +1152,7 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy,
route.network = *nm_ip6_route_get_dest (item);
route.plen = nm_ip6_route_get_prefix (item);
route.gateway = *nm_ip6_route_get_next_hop (item);
+ route.source = NM_PLATFORM_SOURCE_VPN;
/* Ignore host routes to the VPN gateway since NM adds one itself
* below. Since NM knows more about the routing situation than