Commit 9146d4e8 authored by Dan Williams's avatar Dan Williams

core: move slave handling to device subclasses

Let the master control how the slave gets enslaved and released
since that's dependent on the virtual interface type of the master.
parent 58227d01
......@@ -33,6 +33,7 @@
#include "nm-device-private.h"
#include "nm-netlink-monitor.h"
#include "nm-enum-types.h"
#include "nm-system.h"
#include "nm-device-bond-glue.h"
......@@ -44,7 +45,8 @@ G_DEFINE_TYPE (NMDeviceBond, nm_device_bond, NM_TYPE_DEVICE_WIRED)
#define NM_BOND_ERROR (nm_bond_error_quark ())
typedef struct {
int dummy;
gboolean disposed;
GSList *slaves;
} NMDeviceBondPrivate;
enum {
......@@ -291,6 +293,122 @@ connection_match_config (NMDevice *self, const GSList *connections)
/******************************************************************/
static void
slave_state_changed (NMDevice *slave,
NMDeviceState new_state,
NMDeviceState old_state,
NMDeviceStateReason reason,
gpointer user_data)
{
NMDeviceBond *self = NM_DEVICE_BOND (user_data);
nm_log_dbg (LOGD_DEVICE, "(%s): slave %s state change %d -> %d",
nm_device_get_iface (NM_DEVICE (self)),
nm_device_get_iface (slave),
old_state,
new_state);
if ( old_state > NM_DEVICE_STATE_DISCONNECTED
&& new_state <= NM_DEVICE_STATE_DISCONNECTED) {
/* Slave is no longer available or managed; can't use it */
nm_device_release_slave (NM_DEVICE (self), slave);
}
}
typedef struct {
NMDevice *slave;
guint state_id;
} SlaveInfo;
static SlaveInfo *
find_slave_info_by_device (NMDeviceBond *self, NMDevice *slave)
{
NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (self);
GSList *iter;
for (iter = priv->slaves; iter; iter = g_slist_next (iter)) {
if (((SlaveInfo *) iter->data)->slave == slave)
return iter->data;
}
return NULL;
}
static void
free_slave_info (SlaveInfo *sinfo)
{
g_return_if_fail (sinfo != NULL);
g_return_if_fail (sinfo->slave != NULL);
g_signal_handler_disconnect (sinfo->slave, sinfo->state_id);
g_object_unref (sinfo->slave);
memset (sinfo, 0, sizeof (*sinfo));
g_free (sinfo);
}
static gboolean
enslave_slave (NMDevice *device, NMDevice *slave)
{
NMDeviceBond *self = NM_DEVICE_BOND (device);
NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (self);
gboolean success, no_firmware = FALSE;
if (find_slave_info_by_device (self, slave))
return TRUE;
nm_device_hw_take_down (slave, TRUE);
success = nm_system_iface_enslave (nm_device_get_ip_ifindex (device),
nm_device_get_ip_iface (device),
nm_device_get_ip_ifindex (slave),
nm_device_get_ip_iface (slave));
if (success) {
SlaveInfo *sinfo;
sinfo = g_malloc0 (sizeof (*slave));
sinfo->slave = g_object_ref (slave);
sinfo->state_id = g_signal_connect (slave,
"state-changed",
(GCallback) slave_state_changed,
self);
priv->slaves = g_slist_append (priv->slaves, sinfo);
nm_log_dbg (LOGD_DEVICE, "(%s): enslaved bond slave %s",
nm_device_get_ip_iface (device),
nm_device_get_ip_iface (slave));
}
nm_device_hw_bring_up (slave, TRUE, &no_firmware);
return success;
}
static gboolean
release_slave (NMDevice *device, NMDevice *slave)
{
NMDeviceBond *self = NM_DEVICE_BOND (device);
NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (self);
gboolean success;
SlaveInfo *sinfo;
sinfo = find_slave_info_by_device (self, slave);
if (!sinfo)
return FALSE;
success = nm_system_iface_release (nm_device_get_ip_ifindex (device),
nm_device_get_ip_iface (device),
nm_device_get_ip_ifindex (slave),
nm_device_get_ip_iface (slave));
nm_log_dbg (LOGD_DEVICE, "(%s): released bond slave %s (success %d)",
nm_device_get_ip_iface (device),
nm_device_get_ip_iface (slave),
success);
priv->slaves = g_slist_remove (priv->slaves, sinfo);
free_slave_info (sinfo);
return success;
}
/******************************************************************/
NMDevice *
nm_device_bond_new (const char *udi, const char *iface)
{
......@@ -353,6 +471,24 @@ set_property (GObject *object, guint prop_id,
}
}
static void
dispose (GObject *object)
{
NMDeviceBond *self = NM_DEVICE_BOND (object);
NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (self);
GSList *iter;
if (priv->disposed) {
G_OBJECT_CLASS (nm_device_bond_parent_class)->dispose (object);
return;
}
priv->disposed = TRUE;
for (iter = priv->slaves; iter; iter = g_slist_next (iter))
release_slave (NM_DEVICE (self), ((SlaveInfo *) iter->data)->slave);
g_slist_free (priv->slaves);
}
static void
nm_device_bond_class_init (NMDeviceBondClass *klass)
{
......@@ -365,6 +501,7 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
object_class->constructed = constructed;
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->dispose = dispose;
parent_class->get_generic_capabilities = real_get_generic_capabilities;
parent_class->update_hw_address = real_update_hw_address;
......@@ -375,6 +512,9 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
parent_class->spec_match_list = spec_match_list;
parent_class->connection_match_config = connection_match_config;
parent_class->enslave_slave = enslave_slave;
parent_class->release_slave = release_slave;
/* properties */
g_object_class_install_property
(object_class, PROP_HW_ADDRESS,
......
......@@ -1055,16 +1055,19 @@ real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
req = nm_device_get_act_request (NM_DEVICE (self));
g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
s_wired = NM_SETTING_WIRED (device_get_setting (dev, NM_TYPE_SETTING_WIRED));
g_assert (s_wired);
/* Set device MAC address if the connection wants to change it */
cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
if (cloned_mac && (cloned_mac->len == ETH_ALEN))
_set_hw_addr (self, (const guint8 *) cloned_mac->data, "set");
ret = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage1_prepare (dev, reason);
if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
req = nm_device_get_act_request (NM_DEVICE (self));
g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
s_wired = NM_SETTING_WIRED (device_get_setting (dev, NM_TYPE_SETTING_WIRED));
g_assert (s_wired);
/* Set device MAC address if the connection wants to change it */
cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
if (cloned_mac && (cloned_mac->len == ETH_ALEN))
_set_hw_addr (self, (const guint8 *) cloned_mac->data, "set");
}
return ret;
}
......
......@@ -225,7 +225,7 @@ real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
return NM_ACT_STAGE_RETURN_FAILURE;
}
return NM_ACT_STAGE_RETURN_SUCCESS;
return NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->act_stage1_prepare (dev, reason);
}
static void
......
......@@ -617,6 +617,51 @@ nm_device_set_master (NMDevice *self, NMDevice *master)
g_object_notify (G_OBJECT (priv->act_request), NM_ACTIVE_CONNECTION_MASTER);
}
/**
* nm_device_enslave_slave:
* @dev: the master device
* @slave: the slave device to enslave
*
* If @dev is capable of enslaving other devices (ie it's a bridge, bond, etc)
* then this function enslaves @slave.
*
* Returns: %TRUE on success, %FALSE on failure or if this device cannot enslave
* other devices.
*/
gboolean
nm_device_enslave_slave (NMDevice *dev, NMDevice *slave)
{
g_return_val_if_fail (dev != NULL, FALSE);
g_return_val_if_fail (slave != NULL, FALSE);
g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
if (NM_DEVICE_GET_CLASS (dev)->enslave_slave)
return NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave);
return FALSE;
}
/**
* nm_device_release_slave:
* @dev: the master device
* @slave: the slave device to release
*
* If @dev is capable of enslaving other devices (ie it's a bridge, bond, etc)
* then this function releases the previously enslaved @slave.
*
* Returns: %TRUE on success, %FALSE on failure, if this device cannot enslave
* other devices, or if @slave was never enslaved.
*/
gboolean
nm_device_release_slave (NMDevice *dev, NMDevice *slave)
{
g_return_val_if_fail (dev != NULL, FALSE);
g_return_val_if_fail (slave != NULL, FALSE);
if (NM_DEVICE_GET_CLASS (dev)->release_slave)
return NM_DEVICE_GET_CLASS (dev)->release_slave (dev, slave);
return FALSE;
}
/*
* nm_device_get_act_request
*
......@@ -914,68 +959,26 @@ ip6_method_matches (NMConnection *connection, const char *match)
static NMActStageReturn
real_act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason)
{
return NM_ACT_STAGE_RETURN_SUCCESS;
}
static gboolean
handle_slave_activation (NMDevice *slave, NMDevice *master)
{
NMConnection *connection;
NMSettingConnection *s_con;
connection = nm_device_get_connection (slave);
g_assert (connection);
s_con = nm_connection_get_setting_connection (connection);
g_assert (s_con);
if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BOND_SETTING_NAME)) {
/*
* Bonding
*
* Kernel expects slaves to be down while the enslaving is
* taking place.
*/
nm_device_hw_take_down (slave, TRUE);
NMActRequest *req;
NMActiveConnection *master_ac;
NMDevice *master;
NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
if (!nm_system_iface_enslave (slave, master))
return FALSE;
req = nm_device_get_act_request (self);
g_assert (req);
nm_device_hw_bring_up (slave, TRUE, NULL);
} else if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_VLAN_SETTING_NAME)) {
/* NOP */
} else {
nm_log_warn (LOGD_DEVICE, "(%s): Unable to enslave. Unknown slave type '%s'",
nm_device_get_iface (slave), nm_setting_connection_get_slave_type (s_con));
/* If the interface is going to be a slave, let the master enslave it here */
master_ac = nm_act_request_get_dependency (req);
if (master_ac && NM_IS_ACT_REQUEST (master_ac)) {
/* FIXME: handle VPNs here too */
/* Abort activation */
return FALSE;
master = NM_DEVICE (nm_act_request_get_device (NM_ACT_REQUEST (master_ac)));
g_assert (master);
if (!nm_device_enslave_slave (master, self))
ret = NM_ACT_STAGE_RETURN_FAILURE;
}
nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) enslaved to %s",
nm_device_get_iface (slave),
nm_device_get_iface (master));
return TRUE;
}
static void
handle_slave_deactivation (NMDevice *slave, NMDevice *master)
{
NMConnection *connection;
NMSettingConnection *s_con;
connection = nm_device_get_connection (slave);
g_assert (connection);
s_con = nm_connection_get_setting_connection (connection);
g_assert (s_con);
if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BOND_SETTING_NAME)) {
nm_system_iface_release (slave, master);
nm_log_info (LOGD_DEVICE, "Device %s released from master %s",
nm_device_get_iface (slave), nm_device_get_iface (master));
}
return ret;
}
/*
......@@ -992,7 +995,6 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
const char *iface;
NMActStageReturn ret;
NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
NMDevice *master;
/* Clear the activation source ID now that this stage has run */
activation_source_clear (self, FALSE, 0);
......@@ -1008,13 +1010,6 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface);
nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
if ((master = nm_device_get_master (self))) {
if (!handle_slave_activation (self, master)) {
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
goto out;
}
}
ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason);
if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
goto out;
......@@ -3071,7 +3066,6 @@ nm_device_deactivate (NMDevice *self, NMDeviceStateReason reason)
{
NMDevicePrivate *priv;
NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
NMDevice *master;
NMConnection *connection = NULL;
NMSettingConnection *s_con = NULL;
gboolean tried_ipv6 = FALSE;
......@@ -3141,9 +3135,6 @@ nm_device_deactivate (NMDevice *self, NMDeviceStateReason reason)
if (NM_DEVICE_GET_CLASS (self)->deactivate)
NM_DEVICE_GET_CLASS (self)->deactivate (self);
if ((master = nm_device_get_master (self)))
handle_slave_deactivation (self, master);
/* Tear down an existing activation request */
clear_act_request (self);
......
......@@ -160,6 +160,12 @@ typedef struct {
const guint8 *other_hwaddr,
guint other_hwaddr_len,
gboolean fail_if_no_hwaddr);
gboolean (* enslave_slave) (NMDevice *self,
NMDevice *slave);
gboolean (* release_slave) (NMDevice *self,
NMDevice *slave);
} NMDeviceClass;
......@@ -189,6 +195,9 @@ NMDevice * nm_device_get_master (NMDevice *self);
const char *nm_device_get_master_path (NMDevice *self);
void nm_device_set_master (NMDevice *self, NMDevice *master);
gboolean nm_device_enslave_slave (NMDevice *dev, NMDevice *slave);
gboolean nm_device_release_slave (NMDevice *dev, NMDevice *slave);
NMActRequest * nm_device_get_act_request (NMDevice *dev);
NMConnection * nm_device_get_connection (NMDevice *dev);
......
......@@ -1309,7 +1309,7 @@ nm_system_add_bonding_master (const char *iface)
}
static gboolean
nm_system_iface_compat_enslave (NMDevice *slave, const char *master_name)
nm_system_iface_compat_enslave (const char *master_iface, const char *slave_iface)
{
struct ifreq ifr;
int fd;
......@@ -1323,20 +1323,16 @@ nm_system_iface_compat_enslave (NMDevice *slave, const char *master_name)
return FALSE;
}
strncpy (ifr.ifr_name, master_name, IFNAMSIZ);
strncpy (ifr.ifr_slave, nm_device_get_iface (slave), IFNAMSIZ);
strncpy (ifr.ifr_name, master_iface, IFNAMSIZ);
strncpy (ifr.ifr_slave, slave_iface, IFNAMSIZ);
if (ioctl (fd, SIOCBONDENSLAVE, &ifr) < 0 &&
ioctl (fd, BOND_ENSLAVE_OLD, &ifr) < 0) {
nm_log_err (LOGD_DEVICE, "(%s): error enslaving %s: %d (%s)",
master_name, nm_device_get_iface (slave),
errno, strerror (errno));
goto errout;
}
ret = TRUE;
master_iface, slave_iface, errno, strerror (errno));
} else
ret = TRUE;
errout:
close (fd);
return ret;
......@@ -1344,58 +1340,56 @@ errout:
/**
* nm_system_iface_enslave:
* @slave: Slave device
* @master: Master device
* @master_ifindex: master device interface index
* @master_iface: master device interface name
* @slave_ifindex: slave device interface index
* @slave_iface: slave device interface name
*
* Enslaves the 'slave' to 'master. This function targets implementing a
* generic interface to attaching all kinds of slaves to masters. Currently
* only bonding is properly supported due to the backwards compatibility
* function being bonding specific.
*
* The slave device needs to be down as a prerequirement.
* The slave device needs to be down as a prerequisite.
*
* Returns: %TRUE on success, or %FALSE
*/
gboolean
nm_system_iface_enslave (NMDevice *slave, NMDevice *master)
nm_system_iface_enslave (gint master_ifindex,
const char *master_iface,
gint slave_ifindex,
const char *slave_iface)
{
struct nl_sock *sock;
const char *master_name;
int err, master_ifindex, slave_ifindex;
int err;
master_name = nm_device_get_iface (master);
if (!master_name)
return FALSE;
g_return_val_if_fail (master_ifindex >= 0, FALSE);
g_return_val_if_fail (master_iface != NULL, FALSE);
g_return_val_if_fail (slave_ifindex >= 0, FALSE);
g_return_val_if_fail (slave_iface != NULL, FALSE);
sock = nm_netlink_get_default_handle ();
master_ifindex = nm_netlink_iface_to_index (master_name);
g_assert (master_ifindex > 0);
if (!(nm_system_iface_get_flags (master_ifindex) & IFF_MASTER)) {
nm_log_err (LOGD_DEVICE, "(%s): interface is not a master", master_name);
nm_log_err (LOGD_DEVICE, "(%s): interface is not a master", master_iface);
return FALSE;
}
slave_ifindex = nm_device_get_ifindex (slave);
g_assert (slave_ifindex > 0);
g_assert (!nm_system_iface_is_up (slave_ifindex));
if (nm_system_iface_get_flags (slave_ifindex) & IFF_SLAVE) {
nm_log_err (LOGD_DEVICE, "(%s): %s is already a slave",
master_name, nm_device_get_iface (slave));
master_iface, slave_iface);
return FALSE;
}
err = rtnl_link_bond_enslave_ifindex (sock, master_ifindex, slave_ifindex);
if (err == -NLE_OPNOTSUPP)
return nm_system_iface_compat_enslave (slave, master_name);
return nm_system_iface_compat_enslave (master_iface, slave_iface);
if (err < 0) {
nm_log_err (LOGD_DEVICE, "(%s): error enslaving %s: %d (%s)",
master_name, nm_device_get_iface (slave),
err, nl_geterror (err));
master_iface, slave_iface, err, nl_geterror (err));
return FALSE;
}
......@@ -1403,7 +1397,7 @@ nm_system_iface_enslave (NMDevice *slave, NMDevice *master)
}
static gboolean
nm_system_iface_compat_release (NMDevice *device, const char *master_name)
nm_system_iface_compat_release (const char *master_iface, const char *slave_iface)
{
struct ifreq ifr;
int fd;
......@@ -1417,29 +1411,26 @@ nm_system_iface_compat_release (NMDevice *device, const char *master_name)
return FALSE;
}
strncpy (ifr.ifr_name, master_name, IFNAMSIZ);
strncpy (ifr.ifr_slave, nm_device_get_iface (device), IFNAMSIZ);
strncpy (ifr.ifr_name, master_iface, IFNAMSIZ);
strncpy (ifr.ifr_slave, slave_iface, IFNAMSIZ);
if (ioctl (fd, SIOCBONDRELEASE, &ifr) < 0 &&
ioctl (fd, BOND_RELEASE_OLD, &ifr) < 0) {
nm_log_err (LOGD_DEVICE, "(%s): error releasing slave %s: %d (%s)",
master_name, nm_device_get_iface (device),
errno, strerror (errno));
goto errout;
}
ret = TRUE;
master_iface, slave_iface, errno, strerror (errno));
} else
ret = TRUE;
errout:
close (fd);
return ret;
}
/**
* nm_system_iface_release:
* @slave: Slave device
* @maser: Master device
* @master_ifindex: master device interface index
* @master_iface: master device interface name
* @slave_ifindex: slave device interface index
* @slave_iface: slave device interface name
*
* Releases the 'slave' which is attached to 'master. This function targets
* implementing a generic interface to releasing all kinds of slaves. Currently
......@@ -1449,37 +1440,34 @@ errout:
* Returns: %TRUE on success, or %FALSE
*/
gboolean
nm_system_iface_release (NMDevice *slave, NMDevice *master)
nm_system_iface_release (gint master_ifindex,
const char *master_iface,
gint slave_ifindex,
const char *slave_iface)
{
struct nl_sock *sock;
const char *master_name;
int err, slave_ifindex;
int err;
master_name = nm_device_get_iface (master);
if (!master_name)
return TRUE;
g_return_val_if_fail (master_ifindex >= 0, FALSE);
g_return_val_if_fail (master_iface != NULL, FALSE);
g_return_val_if_fail (slave_ifindex >= 0, FALSE);
g_return_val_if_fail (slave_iface != NULL, FALSE);
sock = nm_netlink_get_default_handle ();
slave_ifindex = nm_device_get_ifindex (slave);
g_assert (slave_ifindex > 0);
/* Only release if this is actually a slave */
if (!(nm_system_iface_get_flags (slave_ifindex) & IFF_SLAVE))
goto out;
return TRUE;
err = rtnl_link_bond_release_ifindex (sock, slave_ifindex);
if (err == -NLE_OPNOTSUPP)
return nm_system_iface_compat_release (slave, master_name);
if (err < 0) {
return nm_system_iface_compat_release (master_iface, slave_iface);
else if (err < 0) {
nm_log_err (LOGD_DEVICE, "(%s): error releasing slave %s: %d (%s)",
master_name, nm_device_get_iface (slave),
err, nl_geterror (err));
master_iface, slave_iface, err, nl_geterror (err));
return FALSE;
}
out:
return TRUE;
}
......
......@@ -91,8 +91,15 @@ gboolean nm_system_iface_set_mac (int ifindex, const struct eth
gboolean nm_system_apply_bonding_config (NMSettingBond *s_bond);
gboolean nm_system_add_bonding_master (const char *iface);
gboolean nm_system_iface_enslave (NMDevice *slave, NMDevice *master);
gboolean nm_system_iface_release (NMDevice *slave, NMDevice *master);
gboolean nm_system_iface_enslave (gint master_ifindex,
const char *master_iface,
gint slave_ifindex,
const char *slave_iface);
gboolean nm_system_iface_release (gint master_ifindex,
const char *master_iface,
gint slave_ifindex,
const char *slave_iface);
enum {
NM_IFACE_TYPE_UNSPEC = 0,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment