bonding: enslave/release bonding slaves in activation/deactivation

Code is written generic enough to allow easy addition of further master/slave
relationships such as bridging relations.

Signed-off-by: Thomas Graf <tgraf@redhat.com>

(whitespace cleanups and libnl compat by dcbw)
This commit is contained in:
Thomas Graf
2011-10-18 13:48:48 +02:00
committed by Dan Williams
parent a65028a025
commit b496355b2a
5 changed files with 285 additions and 11 deletions

View File

@@ -537,6 +537,16 @@ nm_device_get_act_request (NMDevice *self)
return NM_DEVICE_GET_PRIVATE (self)->act_request;
}
NMConnection *
nm_device_get_connection (NMDevice *self)
{
NMActRequest *req;
req = nm_device_get_act_request (self);
g_assert (req);
return nm_act_request_get_connection (req);
}
gboolean
nm_device_is_available (NMDevice *self)
@@ -759,6 +769,52 @@ 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);
if (!nm_system_iface_enslave (slave, master))
return FALSE;
nm_device_hw_bring_up (slave, TRUE, NULL);
}
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_device_activate_stage1_device_prepare
*
@@ -773,6 +829,7 @@ 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);
@@ -783,6 +840,13 @@ 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;
@@ -2771,6 +2835,7 @@ nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason)
NMDevice *self = NM_DEVICE (device);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
NMDevice *master;
gboolean tried_ipv6 = FALSE;
int ifindex, family;
@@ -2814,6 +2879,9 @@ nm_device_deactivate (NMDeviceInterface *device, 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);

View File

@@ -155,6 +155,7 @@ NMDevice * nm_device_get_master (NMDevice *self);
void nm_device_set_master (NMDevice *self, NMDevice *master);
NMActRequest * nm_device_get_act_request (NMDevice *dev);
NMConnection * nm_device_get_connection (NMDevice *dev);
gboolean nm_device_is_available (NMDevice *dev);

View File

@@ -16,6 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2011 Caixa Magica Software.
* Copyright (C) 2011 Red Hat, Inc.
*/
#ifndef NM_NETLINK_COMPAT_H
@@ -241,6 +242,19 @@ rtnl_link_get_type (struct rtnl_link *rtnl_link)
return NULL;
}
static inline int
rtnl_link_bond_enslave_ifindex (struct nl_sock *h, int master_ifidx, int slave_ifidx)
{
/* Bonding only in libnl3 */
return -NLE_OPNOTSUPP;
}
static inline int
rtnl_link_bond_release_ifindex (struct nl_sock *h, int slave_ifidx)
{
/* Bonding only in libnl3 */
return -NLE_OPNOTSUPP;
}
#endif /* HAVE_LIBNL1 || HAVE_LIBNL2 */
#endif /* NM_NETLINK_COMPAT_H */

View File

@@ -21,6 +21,7 @@
* Copyright (C) January, 1998 Sergei Viznyuk <sv@phystech.com>
*/
#include <config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
@@ -41,6 +42,8 @@
#include <glib.h>
#include <ctype.h>
#include <linux/if.h>
#include <linux/sockios.h>
#include <linux/if_bonding.h>
#include "nm-system.h"
#include "nm-device.h"
@@ -57,6 +60,10 @@
#include <netlink/utils.h>
#include <netlink/route/link.h>
#ifdef HAVE_LIBNL3
#include <netlink/route/link/bonding.h>
#endif
static void nm_system_device_set_priority (int ifindex,
NMIP4Config *config,
int priority);
@@ -657,15 +664,8 @@ nm_system_iface_set_up (int ifindex,
return success;
}
/**
* nm_system_iface_is_up:
* @ifindex: interface index
*
* Returns: %TRUE if the interface is up, %FALSE if it was down or the check
* failed.
**/
gboolean
nm_system_iface_is_up (int ifindex)
guint32
nm_system_iface_get_flags (int ifindex)
{
struct rtnl_link *l;
guint32 flags;
@@ -686,7 +686,20 @@ nm_system_iface_is_up (int ifindex)
flags = rtnl_link_get_flags (l);
rtnl_link_put (l);
return flags & IFF_UP;
return flags;
}
/**
* nm_system_iface_is_up:
* @ifindex: interface index
*
* Returns: %TRUE if the interface is up, %FALSE if it was down or the check
* failed.
**/
gboolean
nm_system_iface_is_up (int ifindex)
{
return nm_system_iface_get_flags (ifindex) & IFF_UP;
}
/**
@@ -1234,6 +1247,181 @@ nm_system_add_bonding_master(NMSettingBond *setting)
return TRUE;
}
static gboolean
nm_system_iface_compat_enslave (NMDevice *slave, const char *master_name)
{
struct ifreq ifr;
int fd;
gboolean ret = FALSE;
memset (&ifr, 0, sizeof (ifr));
fd = socket (PF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
nm_log_err (LOGD_DEVICE, "couldn't open control socket.");
return FALSE;
}
strncpy (ifr.ifr_name, master_name, IFNAMSIZ);
strncpy (ifr.ifr_slave, nm_device_get_iface (slave), 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;
errout:
close (fd);
return ret;
}
/**
* nm_system_iface_enslave:
* @slave: Slave device
* @master: Master device
*
* 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.
*
* Returns: %TRUE on success, or %FALSE
*/
gboolean
nm_system_iface_enslave (NMDevice *slave, NMDevice *master)
{
struct nl_sock *sock;
const char *master_name;
int err, master_ifindex, slave_ifindex;
master_name = nm_device_get_iface (master);
if (!master_name)
return 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);
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));
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);
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));
return FALSE;
}
return TRUE;
}
static gboolean
nm_system_iface_compat_release (NMDevice *device, const char *master_name)
{
struct ifreq ifr;
int fd;
gboolean ret = FALSE;
memset (&ifr, 0, sizeof (ifr));
fd = socket (PF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
nm_log_err (LOGD_DEVICE, "couldn't open control socket.");
return FALSE;
}
strncpy (ifr.ifr_name, master_name, IFNAMSIZ);
strncpy (ifr.ifr_slave, nm_device_get_iface (device), 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;
errout:
close (fd);
return ret;
}
/**
* nm_system_iface_release:
* @slave: Slave device
* @maser: Master device
*
* Releases the 'slave' which is attached to 'master. This function targets
* implementing a generic interface to releasing all kinds of slaves. Currently
* only bonding is properly supported due to the backwards compatibility
* function being bonding specific.
*
* Returns: %TRUE on success, or %FALSE
*/
gboolean
nm_system_iface_release (NMDevice *slave, NMDevice *master)
{
struct nl_sock *sock;
const char *master_name;
int err, slave_ifindex;
master_name = nm_device_get_iface (master);
if (!master_name)
return TRUE;
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;
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) {
nm_log_err (LOGD_DEVICE, "(%s): error releasing slave %s: %d (%s)",
master_name, nm_device_get_iface (slave),
err, nl_geterror (err));
return FALSE;
}
out:
return TRUE;
}
/**
* nm_system_get_link_type:
* @name: name of link

View File

@@ -84,6 +84,7 @@ gboolean nm_system_iface_set_up (int ifindex,
gboolean up,
gboolean *no_firmware);
guint32 nm_system_iface_get_flags (int ifindex);
gboolean nm_system_iface_is_up (int ifindex);
gboolean nm_system_iface_set_mtu (int ifindex, guint32 mtu);
@@ -91,6 +92,8 @@ gboolean nm_system_iface_set_mtu (int ifindex, guint32 mtu);
gboolean nm_system_iface_set_mac (int ifindex, const struct ether_addr *mac);
gboolean nm_system_add_bonding_master (NMSettingBond *setting);
gboolean nm_system_iface_enslave (NMDevice *slave, NMDevice *master);
gboolean nm_system_iface_release (NMDevice *slave, NMDevice *master);
char * nm_system_get_link_type (const char *name);