From 9146d4e8c61de6d7a39e288d9173d4d5ca36b8d0 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 28 Feb 2012 18:35:30 -0600 Subject: [PATCH] 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. --- src/nm-device-bond.c | 142 ++++++++++++++++++++++++++++++++++++- src/nm-device-ethernet.c | 19 ++--- src/nm-device-infiniband.c | 2 +- src/nm-device.c | 129 ++++++++++++++++----------------- src/nm-device.h | 9 +++ src/nm-system.c | 106 ++++++++++++--------------- src/nm-system.h | 11 ++- 7 files changed, 278 insertions(+), 140 deletions(-) diff --git a/src/nm-device-bond.c b/src/nm-device-bond.c index 4bc4e7b8ef..2a293ded84 100644 --- a/src/nm-device-bond.c +++ b/src/nm-device-bond.c @@ -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, diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index 0d42c2bfd8..df0ae94fad 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -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); + 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); + 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"); + /* 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; } diff --git a/src/nm-device-infiniband.c b/src/nm-device-infiniband.c index 9eec0a1f41..ddf43c22cd 100644 --- a/src/nm-device-infiniband.c +++ b/src/nm-device-infiniband.c @@ -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 diff --git a/src/nm-device.c b/src/nm-device.c index f3412e7811..4f62b37d17 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -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; -} + NMActRequest *req; + NMActiveConnection *master_ac; + NMDevice *master; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; -static gboolean -handle_slave_activation (NMDevice *slave, NMDevice *master) -{ - NMConnection *connection; - NMSettingConnection *s_con; + req = nm_device_get_act_request (self); + g_assert (req); - connection = nm_device_get_connection (slave); - g_assert (connection); + /* 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 */ - 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); - } 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)); - - /* 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); diff --git a/src/nm-device.h b/src/nm-device.h index d5cbd5418d..d1fbbd8962 100644 --- a/src/nm-device.h +++ b/src/nm-device.h @@ -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); diff --git a/src/nm-system.c b/src/nm-system.c index a2c36226ae..b37ace53c9 100644 --- a/src/nm-system.c +++ b/src/nm-system.c @@ -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; - } + master_iface, slave_iface, errno, strerror (errno)); + } else + ret = TRUE; - 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; - } + master_iface, slave_iface, errno, strerror (errno)); + } else + ret = TRUE; - 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; } diff --git a/src/nm-system.h b/src/nm-system.h index b5e27f0787..66256fbaa6 100644 --- a/src/nm-system.h +++ b/src/nm-system.h @@ -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,