device: track parent device in NMDevice

Multiple subclasses have a parent/link interface (NMDeviceIPTunnel,
NMDeviceVlan). Tracking the parent interface properly is midly
complicated to get right. So, instead of repeating it in each
subclass, track it in the parent device.
This commit is contained in:
Thomas Haller 2016-12-26 11:12:39 +01:00
parent bd09decf16
commit f703f4bb65
3 changed files with 202 additions and 0 deletions

View file

@ -123,6 +123,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice,
PROP_PHYSICAL_PORT_ID,
PROP_IS_MASTER,
PROP_MASTER,
PROP_PARENT,
PROP_HW_ADDRESS,
PROP_PERM_HW_ADDRESS,
PROP_HAS_PENDING_ACTION,
@ -225,10 +226,14 @@ typedef struct _NMDevicePrivate {
GSList *pending_actions;
GSList *dad6_failed_addrs;
NMDevice *parent_device;
char * udi;
char * iface; /* may change, could be renamed by user */
int ifindex;
int parent_ifindex;
union {
const guint8 hw_addr_len; /* read-only */
guint8 hw_addr_len_;
@ -874,6 +879,132 @@ _ip_iface_update (NMDevice *self, const char *ip_iface)
/*****************************************************************************/
int
nm_device_parent_get_ifindex (NMDevice *self)
{
NMDevicePrivate *priv;
g_return_val_if_fail (NM_IS_DEVICE (self), 0);
priv = NM_DEVICE_GET_PRIVATE (self);
return priv->parent_ifindex;
}
NMDevice *
nm_device_parent_get_device (NMDevice *self)
{
NMDevicePrivate *priv;
g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
priv = NM_DEVICE_GET_PRIVATE (self);
return priv->parent_device;
}
static void
parent_changed_notify (NMDevice *self,
int old_ifindex,
NMDevice *old_parent,
int new_ifindex,
NMDevice *new_parent)
{
/* empty handler to allow subclasses to always chain up the virtual function. */
}
static gboolean
_parent_set_ifindex (NMDevice *self,
int parent_ifindex,
gboolean force_check)
{
NMDevicePrivate *priv;
NMDevice *parent_device;
gboolean changed = FALSE;
int old_ifindex;
NMDevice *old_device;
g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
priv = NM_DEVICE_GET_PRIVATE (self);
if (parent_ifindex <= 0)
parent_ifindex = 0;
old_ifindex = priv->parent_ifindex;
old_device = priv->parent_device;
if (priv->parent_ifindex == parent_ifindex) {
if (parent_ifindex > 0) {
if ( !force_check
&& priv->parent_device
&& nm_device_get_ifindex (priv->parent_device) == parent_ifindex)
return FALSE;
} else {
if (!priv->parent_device)
return FALSE;
}
} else {
priv->parent_ifindex = parent_ifindex;
changed = TRUE;
}
if (parent_ifindex > 0) {
parent_device = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
if (parent_device == self)
parent_device = NULL;
} else
parent_device = NULL;
if (parent_device != priv->parent_device) {
priv->parent_device = parent_device;
changed = TRUE;
}
if (changed) {
if (priv->parent_ifindex <= 0)
_LOGD (LOGD_DEVICE, "parent: clear");
else if (!priv->parent_device)
_LOGD (LOGD_DEVICE, "parent: ifindex %d, no device", priv->parent_ifindex);
else {
_LOGD (LOGD_DEVICE, "parent: ifindex %d, device %p, %s", priv->parent_ifindex,
priv->parent_device, nm_device_get_iface (priv->parent_device));
}
NM_DEVICE_GET_CLASS (self)->parent_changed_notify (self, old_ifindex, old_device, priv->parent_ifindex, priv->parent_device);
_notify (self, PROP_PARENT);
}
return changed;
}
void
nm_device_parent_set_ifindex (NMDevice *self,
int parent_ifindex)
{
_parent_set_ifindex (self, parent_ifindex, FALSE);
}
gboolean
nm_device_parent_notify_changed (NMDevice *self,
NMDevice *change_candidate,
gboolean device_removed)
{
NMDevicePrivate *priv;
nm_assert (NM_IS_DEVICE (self));
nm_assert (NM_IS_DEVICE (change_candidate));
priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->parent_ifindex > 0) {
if ( priv->parent_device == change_candidate
|| priv->parent_ifindex == nm_device_get_ifindex (change_candidate))
return _parent_set_ifindex (self, priv->parent_ifindex, device_removed);
}
return FALSE;
}
/*****************************************************************************/
static void
_stats_update_counters (NMDevice *self,
guint64 tx_bytes,
@ -2622,6 +2753,8 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
NM_DEVICE_GET_CLASS (self)->unrealize_notify (self);
_parent_set_ifindex (self, 0, FALSE);
if (priv->ifindex > 0) {
priv->ifindex = 0;
_notify (self, PROP_IFINDEX);
@ -12722,6 +12855,8 @@ dispose (GObject *object)
_LOGD (LOGD_DEVICE, "disposing");
_parent_set_ifindex (self, 0, FALSE);
platform = NM_PLATFORM_GET;
g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self);
g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self);
@ -12758,6 +12893,11 @@ dispose (GObject *object)
link_disconnect_action_cancel (self);
if (priv->ifindex > 0) {
priv->ifindex = 0;
_notify (self, PROP_IFINDEX);
}
if (priv->settings) {
g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_added, self);
g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_updated, self);
@ -13036,6 +13176,9 @@ get_property (GObject *object, guint prop_id,
case PROP_MASTER:
g_value_set_object (value, nm_device_get_master (self));
break;
case PROP_PARENT:
nm_utils_g_value_set_object_path (value, priv->parent_device);
break;
case PROP_HW_ADDRESS:
g_value_set_string (value, priv->hw_addr);
break;
@ -13140,6 +13283,7 @@ nm_device_class_init (NMDeviceClass *klass)
klass->get_ip_iface_identifier = get_ip_iface_identifier;
klass->unmanaged_on_quit = unmanaged_on_quit;
klass->deactivate_reset_hw_addr = deactivate_reset_hw_addr;
klass->parent_changed_notify = parent_changed_notify;
obj_properties[PROP_UDI] =
g_param_spec_string (NM_DEVICE_UDI, "", "",
@ -13294,6 +13438,11 @@ nm_device_class_init (NMDeviceClass *klass)
NM_TYPE_DEVICE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_PARENT] =
g_param_spec_string (NM_DEVICE_PARENT, "", "",
NULL,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_HW_ADDRESS] =
g_param_spec_string (NM_DEVICE_HW_ADDRESS, "", "",
NULL,

View file

@ -66,6 +66,9 @@
#define NM_DEVICE_LLDP_NEIGHBORS "lldp-neighbors"
#define NM_DEVICE_REAL "real"
/* "parent" will later be exposed by some subclasses */
#define NM_DEVICE_PARENT "parent"
/* the "slaves" property is internal in the parent class, but exposed
* by the derived classes NMDeviceBond, NMDeviceBridge and NMDeviceTeam.
* It is thus important that the property name matches. */
@ -316,6 +319,12 @@ typedef struct {
void (* notify_new_device_added) (NMDevice *self, NMDevice *new_device);
void (* parent_changed_notify) (NMDevice *self,
int old_ifindex,
NMDevice *old_parent,
int new_ifindex,
NMDevice *new_parent);
/**
* component_added:
* @self: the #NMDevice
@ -390,6 +399,14 @@ void nm_device_replace_vpn6_config (NMDevice *dev,
void nm_device_capture_initial_config (NMDevice *dev);
int nm_device_parent_get_ifindex (NMDevice *dev);
NMDevice *nm_device_parent_get_device (NMDevice *dev);
void nm_device_parent_set_ifindex (NMDevice *self,
int parent_ifindex);
gboolean nm_device_parent_notify_changed (NMDevice *self,
NMDevice *change_candidate,
gboolean device_removed);
/* Master */
gboolean nm_device_is_master (NMDevice *dev);

View file

@ -949,6 +949,26 @@ settings_startup_complete_changed (NMSettings *settings,
check_if_startup_complete (self);
}
static void
_parent_notify_changed (NMManager *self,
NMDevice *device,
gboolean device_removed)
{
GSList *iter;
nm_assert (NM_IS_DEVICE (device));
nm_assert (NM_IS_MANAGER (self));
for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; ) {
if (nm_device_parent_notify_changed (iter->data, device, device_removed)) {
/* in the unlikely event that this changes anything, we start iterating
* again, to be sure that the device list is up-to-date. */
iter = NM_MANAGER_GET_PRIVATE (self)->devices;
} else
iter = iter->next;
}
}
static void
remove_device (NMManager *self,
NMDevice *device,
@ -985,6 +1005,8 @@ remove_device (NMManager *self,
nm_settings_device_removed (priv->settings, device, quitting);
priv->devices = g_slist_remove (priv->devices, device);
_parent_notify_changed (self, device, TRUE);
if (nm_device_is_real (device)) {
gboolean unconfigure_ip_config = !quitting || unmanage;
@ -1860,6 +1882,14 @@ recheck_assume_connection_cb (NMDevice *device, gpointer user_data)
recheck_assume_connection (user_data, device);
}
static void
device_ifindex_changed (NMDevice *device,
GParamSpec *pspec,
NMManager *self)
{
_parent_notify_changed (self, device, FALSE);
}
static void
device_ip_iface_changed (NMDevice *device,
GParamSpec *pspec,
@ -1998,6 +2028,10 @@ add_device (NMManager *self, NMDevice *device, GError **error)
G_CALLBACK (device_ip_iface_changed),
self);
g_signal_connect (device, "notify::" NM_DEVICE_IFINDEX,
G_CALLBACK (device_ifindex_changed),
self);
g_signal_connect (device, "notify::" NM_DEVICE_IFACE,
G_CALLBACK (device_iface_changed),
self);
@ -2047,6 +2081,8 @@ add_device (NMManager *self, NMDevice *device, GError **error)
nm_device_notify_new_device_added (d, device);
}
_parent_notify_changed (self, device, FALSE);
/* Virtual connections may refer to the new device as
* parent device, retry to activate them.
*/