core: match connections to connections instead of devices
This backwards compatible patch adds the possibility to use new nm_device_generate_connection() API via update_connection() virtual method implementations in NMDevice subclasses. Compatibility is achieved by first trying to use the older API and match_l2_config() virtual method and only then moving on to update_connection(). The nm_device_generate_connection() calls update_connection() to create type-specific NMSetting instances and verifies the connection before returning it. To avoid tinkering with NMSettingConnection in update_connection() we use a class attribute called connection_type which is used by nm_device_generate_connection() itself. Known issues: * nm_device_generate_connection() method doesn't implement DHCP lease configuration matching. We shouldn't actually need it but if a use case for that will come out, we can fix it later. * nm_device_generate_connection() doesn't fill in the slave-specific options. * update_connection() is not implemented and connection_type is not set in the subclasses. This will be fixed in individual patches. * NMSetting's compare_property() implementations in combination with NM_SETTING_COMPARE_FLAG_CANDIDATE are not yet fully ready thus rendering false negatives in some cases. Same as above. Acked-by: Dan Winship <danw@gnome.org> Acked-by: Thomas Haller <thaller@redhat.com>
This commit is contained in:
@@ -37,6 +37,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/if.h>
|
#include <linux/if.h>
|
||||||
|
|
||||||
|
#include "gsystem-local-alloc.h"
|
||||||
#include "nm-glib-compat.h"
|
#include "nm-glib-compat.h"
|
||||||
#include "nm-device.h"
|
#include "nm-device.h"
|
||||||
#include "nm-device-private.h"
|
#include "nm-device-private.h"
|
||||||
@@ -1618,6 +1619,77 @@ can_auto_connect (NMDevice *device,
|
|||||||
return nm_device_can_activate (device, connection);
|
return nm_device_can_activate (device, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
device_has_config (NMDevice *device)
|
||||||
|
{
|
||||||
|
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
|
||||||
|
|
||||||
|
/* Check for IP configuration. */
|
||||||
|
if (priv->ip4_config && nm_ip4_config_get_num_addresses (priv->ip4_config))
|
||||||
|
return TRUE;
|
||||||
|
if (priv->ip6_config && nm_ip6_config_get_num_addresses (priv->ip6_config))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* The existence of a software device is good enough. */
|
||||||
|
if (nm_device_is_software (device))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
NMConnection *
|
||||||
|
nm_device_generate_connection (NMDevice *device)
|
||||||
|
{
|
||||||
|
NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device);
|
||||||
|
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
|
||||||
|
const char *ifname = nm_device_get_iface (device);
|
||||||
|
NMConnection *connection;
|
||||||
|
NMSetting *s_con;
|
||||||
|
NMSetting *s_ip4;
|
||||||
|
NMSetting *s_ip6;
|
||||||
|
gs_free char *uuid = NULL;
|
||||||
|
gs_free char *name = NULL;
|
||||||
|
|
||||||
|
/* If update_connection() is not implemented, just fail. */
|
||||||
|
if (!klass->update_connection)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Return NULL if device is unconfigured. */
|
||||||
|
if (!device_has_config (device))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
nm_log_info (LOGD_DEVICE, "(%s): Generating connection from current device status.", ifname);
|
||||||
|
|
||||||
|
connection = nm_connection_new ();
|
||||||
|
s_con = nm_setting_connection_new ();
|
||||||
|
s_ip4 = nm_setting_ip4_config_new ();
|
||||||
|
s_ip6 = nm_setting_ip6_config_new ();
|
||||||
|
uuid = nm_utils_uuid_generate ();
|
||||||
|
name = g_strdup_printf ("%s", ifname);
|
||||||
|
|
||||||
|
g_object_set (s_con,
|
||||||
|
NM_SETTING_CONNECTION_UUID, uuid,
|
||||||
|
NM_SETTING_CONNECTION_ID, name,
|
||||||
|
NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
|
||||||
|
NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL),
|
||||||
|
NULL);
|
||||||
|
if (klass->connection_type)
|
||||||
|
g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, klass->connection_type, NULL);
|
||||||
|
nm_ip4_config_update_setting (priv->ip4_config, (NMSettingIP4Config *) s_ip4);
|
||||||
|
nm_ip6_config_update_setting (priv->ip6_config, (NMSettingIP6Config *) s_ip6);
|
||||||
|
|
||||||
|
nm_connection_add_setting (connection, s_con);
|
||||||
|
nm_connection_add_setting (connection, s_ip4);
|
||||||
|
nm_connection_add_setting (connection, s_ip6);
|
||||||
|
|
||||||
|
klass->update_connection (device, connection);
|
||||||
|
|
||||||
|
/* Check the connection in case of update_connection() bug. */
|
||||||
|
g_return_val_if_fail (nm_connection_verify (connection, NULL), NULL);
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nm_device_get_best_auto_connection:
|
* nm_device_get_best_auto_connection:
|
||||||
* @dev: an #NMDevice
|
* @dev: an #NMDevice
|
||||||
@@ -1738,12 +1810,29 @@ nm_device_check_connection_compatible (NMDevice *device,
|
|||||||
return NM_DEVICE_GET_CLASS (device)->check_connection_compatible (device, connection, error);
|
return NM_DEVICE_GET_CLASS (device)->check_connection_compatible (device, connection, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nm_device_can_assume_connections:
|
||||||
|
* @device: #NMDevice instance
|
||||||
|
*
|
||||||
|
* This is a convenience function to determine whether connection assumption
|
||||||
|
* via old match_l2_config() or new update_connection() virtual functions
|
||||||
|
* is available for this device.
|
||||||
|
*
|
||||||
|
* Use this function when you need to determine whether full cleanup should
|
||||||
|
* be performed for this device or whether the device should be kept running
|
||||||
|
* between NetworkManager runs.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE for assumable connections and %FALS for full-cleanup connections.
|
||||||
|
*
|
||||||
|
* FIXME: Consider turning this method into (a) a device capability or (b) a class
|
||||||
|
* method.
|
||||||
|
*/
|
||||||
gboolean
|
gboolean
|
||||||
nm_device_can_assume_connections (NMDevice *device)
|
nm_device_can_assume_connections (NMDevice *device)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
|
NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device);
|
||||||
|
|
||||||
return !!NM_DEVICE_GET_CLASS (device)->match_l2_config;
|
return klass->match_l2_config || klass->update_connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@@ -97,6 +97,8 @@ struct _NMDevice {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
GObjectClass parent;
|
GObjectClass parent;
|
||||||
|
|
||||||
|
const char *connection_type;
|
||||||
|
|
||||||
void (*state_changed) (NMDevice *device,
|
void (*state_changed) (NMDevice *device,
|
||||||
NMDeviceState new_state,
|
NMDeviceState new_state,
|
||||||
NMDeviceState old_state,
|
NMDeviceState old_state,
|
||||||
@@ -175,7 +177,24 @@ typedef struct {
|
|||||||
|
|
||||||
gboolean (* spec_match_list) (NMDevice *self, const GSList *specs);
|
gboolean (* spec_match_list) (NMDevice *self, const GSList *specs);
|
||||||
|
|
||||||
|
/* FIXME: We currently support match_l2_config() virtual function for
|
||||||
|
* compatibility. When match_l2_config() is not present, we use the
|
||||||
|
* new update_connection() virtual function which should first call
|
||||||
|
* NMDevice's implementation and then perform type-specific adjustments.
|
||||||
|
*
|
||||||
|
* Therefore subclasses that implement the new API *must* leave
|
||||||
|
* match_l2_config set to NULL and implement update_connection, while
|
||||||
|
* subclasses that implement the old API *must* set match_l2_config
|
||||||
|
* (update_connection is ignored).
|
||||||
|
*
|
||||||
|
* Subclasses which don't implement any of the APIs for connection assumption
|
||||||
|
* *should* leave generate_connection NULL.
|
||||||
|
*
|
||||||
|
* The update_connection() virtual function is also used for live
|
||||||
|
* reconfiguration of the connection according to link level changes.
|
||||||
|
*/
|
||||||
gboolean (* match_l2_config) (NMDevice *self, NMConnection *connection);
|
gboolean (* match_l2_config) (NMDevice *self, NMConnection *connection);
|
||||||
|
void (* update_connection) (NMDevice *device, NMConnection *connection);
|
||||||
|
|
||||||
const GByteArray * (* get_connection_hw_address) (NMDevice *self,
|
const GByteArray * (* get_connection_hw_address) (NMDevice *self,
|
||||||
NMConnection *connection);
|
NMConnection *connection);
|
||||||
@@ -247,6 +266,8 @@ gboolean nm_device_can_activate (NMDevice *dev,
|
|||||||
gboolean nm_device_has_carrier (NMDevice *dev);
|
gboolean nm_device_has_carrier (NMDevice *dev);
|
||||||
gboolean nm_device_ignore_carrier (NMDevice *dev);
|
gboolean nm_device_ignore_carrier (NMDevice *dev);
|
||||||
|
|
||||||
|
NMConnection * nm_device_generate_connection (NMDevice *device);
|
||||||
|
|
||||||
NMConnection * nm_device_get_best_auto_connection (NMDevice *dev,
|
NMConnection * nm_device_get_best_auto_connection (NMDevice *dev,
|
||||||
GSList *connections,
|
GSList *connections,
|
||||||
char **specific_object);
|
char **specific_object);
|
||||||
|
122
src/nm-manager.c
122
src/nm-manager.c
@@ -57,6 +57,7 @@
|
|||||||
#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"
|
||||||
@@ -1940,6 +1941,107 @@ device_auth_request_cb (NMDevice *device,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This should really be moved to gsystem. */
|
||||||
|
#define free_slist __attribute__ ((cleanup(local_slist_free)))
|
||||||
|
static void
|
||||||
|
local_slist_free (void *loc)
|
||||||
|
{
|
||||||
|
GSList **location = loc;
|
||||||
|
|
||||||
|
if (location)
|
||||||
|
g_slist_free (*location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_connection:
|
||||||
|
* @manager: #NMManager instance
|
||||||
|
* @device: #NMDevice instance
|
||||||
|
* @existing: is set to %TRUE when an existing connection was returned
|
||||||
|
*
|
||||||
|
* Returns one of the following:
|
||||||
|
*
|
||||||
|
* 1) An existing connection to be assumed.
|
||||||
|
*
|
||||||
|
* 2) A generated connection to be assumed.
|
||||||
|
*
|
||||||
|
* 3) %NULL when none of the above is available.
|
||||||
|
*
|
||||||
|
* Supports both nm-device's match_l2_config() and update_connection().
|
||||||
|
*/
|
||||||
|
static NMConnection *
|
||||||
|
get_connection (NMManager *manager, NMDevice *device, gboolean *existing)
|
||||||
|
{
|
||||||
|
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
|
||||||
|
free_slist GSList *connections = nm_settings_get_connections (priv->settings);
|
||||||
|
NMConnection *connection = NULL;
|
||||||
|
GSList *iter;
|
||||||
|
|
||||||
|
if (existing)
|
||||||
|
*existing = FALSE;
|
||||||
|
|
||||||
|
/* We still support the older API to match a NMDevice object to an
|
||||||
|
* existing connection using nm_device_find_assumable_connection().
|
||||||
|
*
|
||||||
|
* When the older API is still available for a particular device
|
||||||
|
* type, we use it. To opt for the newer interface, the NMDevice
|
||||||
|
* subclass must omit the match_l2_config virtual function
|
||||||
|
* implementation.
|
||||||
|
*/
|
||||||
|
if (NM_DEVICE_GET_CLASS (device)->match_l2_config) {
|
||||||
|
NMConnection *candidate = nm_device_find_assumable_connection (device, connections);
|
||||||
|
|
||||||
|
if (candidate) {
|
||||||
|
nm_log_info (LOGD_DEVICE, "(%s): Found matching connection '%s' (legacy API)",
|
||||||
|
nm_device_get_iface (device),
|
||||||
|
nm_connection_get_id (candidate));
|
||||||
|
if (existing)
|
||||||
|
*existing = TRUE;
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The core of the API is nm_device_generate_connection() function and
|
||||||
|
* update_connection() virtual method and the convenient connection_type
|
||||||
|
* class attribute. Subclasses supporting the new API must have
|
||||||
|
* update_connection() implemented, otherwise nm_device_generate_connection()
|
||||||
|
* returns NULL.
|
||||||
|
*/
|
||||||
|
connection = nm_device_generate_connection (device);
|
||||||
|
if (!connection) {
|
||||||
|
nm_log_info (LOGD_DEVICE, "(%s): No existing connection detected.",
|
||||||
|
nm_device_get_iface (device));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now we need to compare the generated connection to each configured
|
||||||
|
* connection. The comparison function is the heart of the connection
|
||||||
|
* assumption implementation and it must compare the connections very
|
||||||
|
* carefully to sort out various corner cases. Also, the comparison is
|
||||||
|
* not entirely symmetric.
|
||||||
|
*
|
||||||
|
* When no configured connection matches the generated connection, we keep
|
||||||
|
* the generated connection instead.
|
||||||
|
*/
|
||||||
|
for (iter = connections; iter; iter = iter->next) {
|
||||||
|
NMConnection *candidate = NM_CONNECTION (iter->data);
|
||||||
|
|
||||||
|
if (nm_connection_compare (connection, candidate, NM_SETTING_COMPARE_FLAG_CANDIDATE)) {
|
||||||
|
nm_log_info (LOGD_DEVICE, "(%s): Found matching connection: '%s'",
|
||||||
|
nm_device_get_iface (device),
|
||||||
|
nm_connection_get_id (candidate));
|
||||||
|
g_object_unref (connection);
|
||||||
|
if (existing)
|
||||||
|
*existing = TRUE;
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nm_log_info (LOGD_DEVICE, "(%s): Using generated connection: '%s'",
|
||||||
|
nm_device_get_iface (device),
|
||||||
|
nm_connection_get_id (connection));
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
add_device (NMManager *self, NMDevice *device)
|
add_device (NMManager *self, NMDevice *device)
|
||||||
{
|
{
|
||||||
@@ -1948,7 +2050,7 @@ add_device (NMManager *self, NMDevice *device)
|
|||||||
char *path;
|
char *path;
|
||||||
static guint32 devcount = 0;
|
static guint32 devcount = 0;
|
||||||
const GSList *unmanaged_specs;
|
const GSList *unmanaged_specs;
|
||||||
NMConnection *connection = NULL;
|
NMConnection *connection;
|
||||||
gboolean enabled = FALSE;
|
gboolean enabled = FALSE;
|
||||||
RfKillType rtype;
|
RfKillType rtype;
|
||||||
NMDeviceType devtype;
|
NMDeviceType devtype;
|
||||||
@@ -2034,21 +2136,7 @@ add_device (NMManager *self, NMDevice *device)
|
|||||||
nm_log_info (LOGD_CORE, "(%s): exported as %s", iface, path);
|
nm_log_info (LOGD_CORE, "(%s): exported as %s", iface, path);
|
||||||
g_free (path);
|
g_free (path);
|
||||||
|
|
||||||
/* Check if we should assume the device's active connection by matching its
|
connection = get_connection (self, device, NULL);
|
||||||
* config with an existing system connection.
|
|
||||||
*/
|
|
||||||
if (nm_device_can_assume_connections (device)) {
|
|
||||||
GSList *connections = NULL;
|
|
||||||
|
|
||||||
connections = nm_settings_get_connections (priv->settings);
|
|
||||||
connection = nm_device_find_assumable_connection (device, connections);
|
|
||||||
g_slist_free (connections);
|
|
||||||
|
|
||||||
if (connection)
|
|
||||||
nm_log_dbg (LOGD_DEVICE, "(%s): found existing device connection '%s'",
|
|
||||||
nm_device_get_iface (device),
|
|
||||||
nm_connection_get_id (connection));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start the device if it's supposed to be managed */
|
/* Start the device if it's supposed to be managed */
|
||||||
unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings);
|
unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings);
|
||||||
@@ -2073,7 +2161,7 @@ add_device (NMManager *self, NMDevice *device)
|
|||||||
NMActiveConnection *ac;
|
NMActiveConnection *ac;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
|
||||||
nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume existing connection",
|
nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume connection",
|
||||||
nm_device_get_iface (device));
|
nm_device_get_iface (device));
|
||||||
|
|
||||||
ac = internal_activate_device (self, device, connection, NULL, FALSE, 0, NULL, TRUE, NULL, &error);
|
ac = internal_activate_device (self, device, connection, NULL, FALSE, 0, NULL, TRUE, NULL, &error);
|
||||||
|
Reference in New Issue
Block a user