core: fix matching of default metric value for static routes

When NM tries to match a generated connection to a persistent one, it
considers also the metric of static routes. However, if the field is
set to -1 (use default value for the device) on the persistent
connection, the comparison will always fail because the generated
connection contains the actual value read from kernel.

To fix the issue, modify check_possible_match() to deal correctly with
-1 and translate it to the expected value for the current device when
performing the comparison.

This allows connections with static routes and default metric to
properly be re-assumed when NM is restarted.

https://bugzilla.redhat.com/show_bug.cgi?id=1302532
This commit is contained in:
Beniamino Galvani
2016-02-01 14:16:30 +01:00
parent 8682bb7f5f
commit 08c3378f64
4 changed files with 239 additions and 20 deletions

View File

@@ -2305,6 +2305,95 @@ check_ip6_method (NMConnection *orig,
return allow;
}
static int
route_compare (NMIPRoute *route1, NMIPRoute *route2, gint64 default_metric)
{
gint64 r, metric1, metric2;
r = g_strcmp0 (nm_ip_route_get_dest (route1), nm_ip_route_get_dest (route2));
if (r)
return r;
r = nm_ip_route_get_prefix (route1) - nm_ip_route_get_prefix (route2);
if (r)
return r > 0 ? 1 : -1;
r = g_strcmp0 (nm_ip_route_get_next_hop (route1), nm_ip_route_get_next_hop (route2));
if (r)
return r;
metric1 = nm_ip_route_get_metric (route1) == -1 ? default_metric : nm_ip_route_get_metric (route1);
metric2 = nm_ip_route_get_metric (route2) == -1 ? default_metric : nm_ip_route_get_metric (route2);
r = metric1 - metric2;
if (r)
return r > 0 ? 1 : -1;
r = nm_ip_route_get_family (route1) - nm_ip_route_get_family (route2);
if (r)
return r > 0 ? 1 : -1;
return 0;
}
static int
route_ptr_compare (const void *a, const void *b)
{
return route_compare (*(NMIPRoute **) a, *(NMIPRoute **) b, -1);
}
static gboolean
check_ip_routes (NMConnection *orig,
NMConnection *candidate,
GHashTable *settings,
gint64 default_metric,
gboolean v4)
{
gs_free NMIPRoute **routes1 = NULL, **routes2 = NULL;
NMSettingIPConfig *s_ip1, *s_ip2;
const char *s_name;
GHashTable *props;
guint i, num;
s_name = v4 ? NM_SETTING_IP4_CONFIG_SETTING_NAME :
NM_SETTING_IP6_CONFIG_SETTING_NAME;
props = check_property_in_hash (settings,
s_name,
NM_SETTING_IP_CONFIG_ROUTES);
if (!props)
return TRUE;
s_ip1 = (NMSettingIPConfig *) nm_connection_get_setting_by_name (orig, s_name);
s_ip2 = (NMSettingIPConfig *) nm_connection_get_setting_by_name (candidate, s_name);
if (!s_ip1 || !s_ip2)
return FALSE;
num = nm_setting_ip_config_get_num_routes (s_ip1);
if (num != nm_setting_ip_config_get_num_routes (s_ip2))
return FALSE;
routes1 = g_new (NMIPRoute *, num);
routes2 = g_new (NMIPRoute *, num);
for (i = 0; i < num; i++) {
routes1[i] = nm_setting_ip_config_get_route (s_ip1, i);
routes2[i] = nm_setting_ip_config_get_route (s_ip2, i);
}
qsort (routes1, num, sizeof (NMIPRoute *), route_ptr_compare);
qsort (routes2, num, sizeof (NMIPRoute *), route_ptr_compare);
for (i = 0; i < num; i++) {
if (route_compare (routes1[i], routes2[i], default_metric))
return FALSE;
}
remove_from_hash (settings, props, s_name, NM_SETTING_IP_CONFIG_ROUTES);
return TRUE;
}
static gboolean
check_ip4_method (NMConnection *orig,
NMConnection *candidate,
@@ -2488,7 +2577,9 @@ static NMConnection *
check_possible_match (NMConnection *orig,
NMConnection *candidate,
GHashTable *settings,
gboolean device_has_carrier)
gboolean device_has_carrier,
gint64 default_v4_metric,
gint64 default_v6_metric)
{
g_return_val_if_fail (settings != NULL, NULL);
@@ -2498,6 +2589,12 @@ check_possible_match (NMConnection *orig,
if (!check_ip4_method (orig, candidate, settings, device_has_carrier))
return NULL;
if (!check_ip_routes (orig, candidate, settings, default_v4_metric, TRUE))
return NULL;
if (!check_ip_routes (orig, candidate, settings, default_v6_metric, FALSE))
return NULL;
if (!check_connection_interface_name (orig, candidate, settings))
return NULL;
@@ -2541,6 +2638,8 @@ NMConnection *
nm_utils_match_connection (GSList *connections,
NMConnection *original,
gboolean device_has_carrier,
gint64 default_v4_metric,
gint64 default_v6_metric,
NMUtilsMatchFilterFunc match_filter_func,
gpointer match_filter_data)
{
@@ -2557,8 +2656,10 @@ nm_utils_match_connection (GSList *connections,
}
if (!nm_connection_diff (original, candidate, NM_SETTING_COMPARE_FLAG_INFERRABLE, &diffs)) {
if (!best_match)
best_match = check_possible_match (original, candidate, diffs, device_has_carrier);
if (!best_match) {
best_match = check_possible_match (original, candidate, diffs, device_has_carrier,
default_v4_metric, default_v6_metric);
}
if (!best_match && nm_logging_enabled (LOGL_DEBUG, LOGD_CORE)) {
GString *diff_string;