core: fuzzier matching of generated connections to persistent ones
When generating a connection, if the device has no non-link-local IPv6 address, then it's unclear whether (a) the connection was link-local originally, or (b) the connection was 'auto' but IPv6 failed or timed out. In this case, if there is a persistent connection that is 'auto' but the generated connection is 'link-local', the persistent connection should be used. Add a more-testable framework for doing the connection matching to handle this.
This commit is contained in:
@@ -659,3 +659,93 @@ nm_utils_read_resolv_conf_nameservers (const char *rc_contents)
|
|||||||
return nameservers;
|
return nameservers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NMConnection *
|
||||||
|
check_possible_match (NMConnection *orig,
|
||||||
|
NMConnection *candidate,
|
||||||
|
GHashTable *settings)
|
||||||
|
{
|
||||||
|
GHashTable *props;
|
||||||
|
const char *orig_ip6_method, *candidate_ip6_method;
|
||||||
|
NMSettingIP6Config *candidate_ip6;
|
||||||
|
|
||||||
|
g_return_val_if_fail (settings != NULL, NULL);
|
||||||
|
|
||||||
|
props = g_hash_table_lookup (settings, NM_SETTING_IP6_CONFIG_SETTING_NAME);
|
||||||
|
if ( !props
|
||||||
|
|| (g_hash_table_size (props) != 1)
|
||||||
|
|| !g_hash_table_lookup (props, NM_SETTING_IP6_CONFIG_METHOD)) {
|
||||||
|
/* For now 'method' is the only difference we handle here */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the original connection is 'link-local' and the candidate is both 'auto'
|
||||||
|
* and may-fail=TRUE, then the candidate is OK to use. may-fail is included
|
||||||
|
* in the decision because if the candidate is 'auto' but may-fail=FALSE, then
|
||||||
|
* the connection could not possibly have been previously activated on the
|
||||||
|
* device if the device has no non-link-local IPv6 address.
|
||||||
|
*/
|
||||||
|
orig_ip6_method = nm_utils_get_ip_config_method (orig, NM_TYPE_SETTING_IP6_CONFIG);
|
||||||
|
candidate_ip6_method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP6_CONFIG);
|
||||||
|
candidate_ip6 = nm_connection_get_setting_ip6_config (candidate);
|
||||||
|
|
||||||
|
if ( strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0
|
||||||
|
&& strcmp (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0
|
||||||
|
&& (!candidate_ip6 || nm_setting_ip6_config_get_may_fail (candidate_ip6))) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nm_utils_match_connection:
|
||||||
|
* @connections: a (optionally pre-sorted) list of connections from which to
|
||||||
|
* find a matching connection to @original based on "inferrable" properties
|
||||||
|
* @original: the #NMConnection to find a match for from @connections
|
||||||
|
* @match_filter_func: a function to check whether each connection from @connections
|
||||||
|
* should be considered for matching. This function should return %TRUE if the
|
||||||
|
* connection should be considered, %FALSE if the connection should be ignored
|
||||||
|
* @match_compat_data: data pointer passed to @match_filter_func
|
||||||
|
*
|
||||||
|
* Checks each connection from @connections until a matching connection is found
|
||||||
|
* considering only setting properties marked with %NM_SETTING_PARAM_INFERRABLE
|
||||||
|
* and checking a few other characteristics like IPv6 method. If the caller
|
||||||
|
* desires some priority order of the connections, @connections should be
|
||||||
|
* sorted before calling this function.
|
||||||
|
*
|
||||||
|
* Returns: the best #NMConnection matching @original, or %NULL if no connection
|
||||||
|
* matches well enough.
|
||||||
|
*/
|
||||||
|
NMConnection *
|
||||||
|
nm_utils_match_connection (GSList *connections,
|
||||||
|
NMConnection *original,
|
||||||
|
NMUtilsMatchFilterFunc match_filter_func,
|
||||||
|
gpointer match_filter_data)
|
||||||
|
{
|
||||||
|
NMConnection *best_match = NULL;
|
||||||
|
GSList *iter;
|
||||||
|
|
||||||
|
for (iter = connections; iter; iter = iter->next) {
|
||||||
|
NMConnection *candidate = NM_CONNECTION (iter->data);
|
||||||
|
GHashTable *diffs = NULL;
|
||||||
|
|
||||||
|
if (match_filter_func) {
|
||||||
|
if (!match_filter_func (candidate, match_filter_data))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nm_connection_diff (original, candidate, NM_SETTING_COMPARE_FLAG_INFERRABLE, &diffs)) {
|
||||||
|
if (!best_match)
|
||||||
|
best_match = check_possible_match (original, candidate, diffs);
|
||||||
|
g_hash_table_unref (diffs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exact match */
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Best match (if any) */
|
||||||
|
return best_match;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
#include "nm-ip6-config.h"
|
#include "nm-ip6-config.h"
|
||||||
#include "nm-setting-ip6-config.h"
|
#include "nm-setting-ip6-config.h"
|
||||||
#include "nm-connection.h"
|
#include "nm-connection.h"
|
||||||
|
#include "nm-setting-private.h"
|
||||||
|
|
||||||
gboolean nm_ethernet_address_is_valid (const struct ether_addr *test_addr);
|
gboolean nm_ethernet_address_is_valid (const struct ether_addr *test_addr);
|
||||||
|
|
||||||
@@ -86,4 +87,11 @@ char *nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id);
|
|||||||
|
|
||||||
GPtrArray *nm_utils_read_resolv_conf_nameservers (const char *rc_contents);
|
GPtrArray *nm_utils_read_resolv_conf_nameservers (const char *rc_contents);
|
||||||
|
|
||||||
|
typedef gboolean (NMUtilsMatchFilterFunc) (NMConnection *connection, gpointer user_data);
|
||||||
|
|
||||||
|
NMConnection *nm_utils_match_connection (GSList *connections,
|
||||||
|
NMConnection *original,
|
||||||
|
NMUtilsMatchFilterFunc match_filter_func,
|
||||||
|
gpointer match_filter_data);
|
||||||
|
|
||||||
#endif /* NETWORK_MANAGER_UTILS_H */
|
#endif /* NETWORK_MANAGER_UTILS_H */
|
||||||
|
@@ -57,7 +57,6 @@
|
|||||||
#include "nm-device-tun.h"
|
#include "nm-device-tun.h"
|
||||||
#include "nm-device-macvlan.h"
|
#include "nm-device-macvlan.h"
|
||||||
#include "nm-device-gre.h"
|
#include "nm-device-gre.h"
|
||||||
#include "nm-setting-private.h"
|
|
||||||
#include "nm-setting-bluetooth.h"
|
#include "nm-setting-bluetooth.h"
|
||||||
#include "nm-setting-connection.h"
|
#include "nm-setting-connection.h"
|
||||||
#include "nm-setting-wireless.h"
|
#include "nm-setting-wireless.h"
|
||||||
@@ -1713,6 +1712,12 @@ local_slist_free (void *loc)
|
|||||||
g_slist_free (*location);
|
g_slist_free (*location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
match_connection_filter (NMConnection *connection, gpointer user_data)
|
||||||
|
{
|
||||||
|
return nm_device_check_connection_compatible (NM_DEVICE (user_data), connection, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get_existing_connection:
|
* get_existing_connection:
|
||||||
* @manager: #NMManager instance
|
* @manager: #NMManager instance
|
||||||
@@ -1726,9 +1731,8 @@ get_existing_connection (NMManager *manager, NMDevice *device)
|
|||||||
{
|
{
|
||||||
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
|
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
|
||||||
free_slist GSList *connections = nm_manager_get_activatable_connections (manager);
|
free_slist GSList *connections = nm_manager_get_activatable_connections (manager);
|
||||||
NMConnection *connection = NULL;
|
NMConnection *connection = NULL, *matched;
|
||||||
NMSettingsConnection *added = NULL;
|
NMSettingsConnection *added = NULL;
|
||||||
GSList *iter;
|
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
|
||||||
nm_device_capture_initial_config (device);
|
nm_device_capture_initial_config (device);
|
||||||
@@ -1752,20 +1756,17 @@ get_existing_connection (NMManager *manager, NMDevice *device)
|
|||||||
* When no configured connection matches the generated connection, we keep
|
* When no configured connection matches the generated connection, we keep
|
||||||
* the generated connection instead.
|
* the generated connection instead.
|
||||||
*/
|
*/
|
||||||
for (iter = connections; iter; iter = iter->next) {
|
connections = g_slist_sort (connections, nm_settings_sort_connections);
|
||||||
NMConnection *candidate = NM_CONNECTION (iter->data);
|
matched = nm_utils_match_connection (connections,
|
||||||
|
connection,
|
||||||
if (!nm_device_check_connection_compatible (device, candidate, NULL))
|
match_connection_filter,
|
||||||
continue;
|
device);
|
||||||
|
if (matched) {
|
||||||
if (!nm_connection_compare (connection, candidate, NM_SETTING_COMPARE_FLAG_INFERRABLE))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
nm_log_info (LOGD_DEVICE, "(%s): found matching connection '%s'",
|
nm_log_info (LOGD_DEVICE, "(%s): found matching connection '%s'",
|
||||||
nm_device_get_iface (device),
|
nm_device_get_iface (device),
|
||||||
nm_connection_get_id (candidate));
|
nm_connection_get_id (matched));
|
||||||
g_object_unref (connection);
|
g_object_unref (connection);
|
||||||
return candidate;
|
return matched;
|
||||||
}
|
}
|
||||||
|
|
||||||
nm_log_dbg (LOGD_DEVICE, "(%s): generated connection '%s'",
|
nm_log_dbg (LOGD_DEVICE, "(%s): generated connection '%s'",
|
||||||
|
@@ -1660,21 +1660,20 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin
|
|||||||
|
|
||||||
/***************************************************************/
|
/***************************************************************/
|
||||||
|
|
||||||
static gint
|
/* GCompareFunc helper for sorting "best" connections */
|
||||||
best_connection_sort (gconstpointer a, gconstpointer b, gpointer user_data)
|
gint
|
||||||
|
nm_settings_sort_connections (gconstpointer a, gconstpointer b)
|
||||||
{
|
{
|
||||||
NMSettingsConnection *ac = (NMSettingsConnection *) a;
|
NMSettingsConnection *ac = (NMSettingsConnection *) a;
|
||||||
NMSettingsConnection *bc = (NMSettingsConnection *) b;
|
NMSettingsConnection *bc = (NMSettingsConnection *) b;
|
||||||
guint64 ats = 0, bts = 0;
|
guint64 ats = 0, bts = 0;
|
||||||
|
|
||||||
if (!ac && bc)
|
if (ac == bc)
|
||||||
return -1;
|
|
||||||
else if (ac && !bc)
|
|
||||||
return 1;
|
|
||||||
else if (!ac && !bc)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
if (!ac)
|
||||||
g_assert (ac && bc);
|
return -1;
|
||||||
|
if (!bc)
|
||||||
|
return 1;
|
||||||
|
|
||||||
/* In the future we may use connection priorities in addition to timestamps */
|
/* In the future we may use connection priorities in addition to timestamps */
|
||||||
nm_settings_connection_get_timestamp (ac, &ats);
|
nm_settings_connection_get_timestamp (ac, &ats);
|
||||||
@@ -1722,7 +1721,7 @@ get_best_connections (NMConnectionProvider *provider,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* List is sorted with oldest first */
|
/* List is sorted with oldest first */
|
||||||
sorted = g_slist_insert_sorted_with_data (sorted, connection, best_connection_sort, NULL);
|
sorted = g_slist_insert_sorted (sorted, connection, nm_settings_sort_connections);
|
||||||
added++;
|
added++;
|
||||||
|
|
||||||
if (max_requested && added > max_requested) {
|
if (max_requested && added > max_requested) {
|
||||||
|
@@ -122,4 +122,6 @@ void nm_settings_device_added (NMSettings *self, NMDevice *device);
|
|||||||
|
|
||||||
void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting);
|
void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting);
|
||||||
|
|
||||||
|
gint nm_settings_sort_connections (gconstpointer a, gconstpointer b);
|
||||||
|
|
||||||
#endif /* __NM_SETTINGS_H__ */
|
#endif /* __NM_SETTINGS_H__ */
|
||||||
|
Reference in New Issue
Block a user