Blame src/devices/nm-device-bond.c

Packit Service b23acc
// SPDX-License-Identifier: GPL-2.0+
Packit Service b23acc
/*
Packit Service b23acc
 * Copyright (C) 2011 - 2018 Red Hat, Inc.
Packit Service b23acc
 */
Packit Service b23acc
Packit Service b23acc
#include "nm-default.h"
Packit Service b23acc
Packit Service b23acc
#include "nm-device-bond.h"
Packit Service b23acc
Packit Service b23acc
#include <stdlib.h>
Packit Service 7ded26
#include <net/if.h>
Packit Service b23acc
Packit Service b23acc
#include "NetworkManagerUtils.h"
Packit Service b23acc
#include "nm-device-private.h"
Packit Service b23acc
#include "platform/nm-platform.h"
Packit Service b23acc
#include "nm-device-factory.h"
Packit Service b23acc
#include "nm-core-internal.h"
Packit Service b23acc
#include "nm-ip4-config.h"
Packit Service b23acc
Packit Service b23acc
#include "nm-device-logging.h"
Packit Service b23acc
_LOG_DECLARE_SELF(NMDeviceBond);
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
struct _NMDeviceBond {
Packit Service b23acc
	NMDevice parent;
Packit Service b23acc
};
Packit Service b23acc
Packit Service b23acc
struct _NMDeviceBondClass {
Packit Service b23acc
	NMDeviceClass parent;
Packit Service b23acc
};
Packit Service b23acc
Packit Service b23acc
G_DEFINE_TYPE (NMDeviceBond, nm_device_bond, NM_TYPE_DEVICE)
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
static NMDeviceCapabilities
Packit Service b23acc
get_generic_capabilities (NMDevice *dev)
Packit Service b23acc
{
Packit Service b23acc
	return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static gboolean
Packit Service b23acc
complete_connection (NMDevice *device,
Packit Service b23acc
                     NMConnection *connection,
Packit Service b23acc
                     const char *specific_object,
Packit Service b23acc
                     NMConnection *const*existing_connections,
Packit Service b23acc
                     GError **error)
Packit Service b23acc
{
Packit Service b23acc
	NMSettingBond *s_bond;
Packit Service b23acc
Packit Service b23acc
	nm_utils_complete_generic (nm_device_get_platform (device),
Packit Service b23acc
	                           connection,
Packit Service b23acc
	                           NM_SETTING_BOND_SETTING_NAME,
Packit Service b23acc
	                           existing_connections,
Packit Service b23acc
	                           NULL,
Packit Service b23acc
	                           _("Bond connection"),
Packit Service b23acc
	                           "bond",
Packit Service b23acc
	                           NULL,
Packit Service b23acc
	                           TRUE);
Packit Service b23acc
Packit Service b23acc
	s_bond = nm_connection_get_setting_bond (connection);
Packit Service b23acc
	if (!s_bond) {
Packit Service b23acc
		s_bond = (NMSettingBond *) nm_setting_bond_new ();
Packit Service b23acc
		nm_connection_add_setting (connection, NM_SETTING (s_bond));
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	return TRUE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
static gboolean
Packit Service b23acc
_set_bond_attr (NMDevice *device, const char *attr, const char *value)
Packit Service b23acc
{
Packit Service b23acc
	NMDeviceBond *self = NM_DEVICE_BOND (device);
Packit Service b23acc
	int ifindex = nm_device_get_ifindex (device);
Packit Service b23acc
	gboolean ret;
Packit Service b23acc
Packit Service b23acc
	ret = nm_platform_sysctl_master_set_option (nm_device_get_platform (device),
Packit Service b23acc
	                                            ifindex,
Packit Service b23acc
	                                            attr,
Packit Service b23acc
	                                            value);
Packit Service b23acc
	if (!ret)
Packit Service b23acc
		_LOGW (LOGD_PLATFORM, "failed to set bonding attribute '%s' to '%s'", attr, value);
Packit Service b23acc
	return ret;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
#define _set_bond_attr_take(device, attr, value) \
Packit Service b23acc
	G_STMT_START { \
Packit Service b23acc
		gs_free char *_tmp = (value); \
Packit Service b23acc
		\
Packit Service b23acc
		_set_bond_attr (device, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, _tmp); \
Packit Service b23acc
	} G_STMT_END
Packit Service b23acc
Packit Service b23acc
#define _set_bond_attr_printf(device, attr, fmt, ...) \
Packit Service b23acc
	_set_bond_attr_take ((device), (attr), g_strdup_printf (fmt, __VA_ARGS__))
Packit Service b23acc
Packit Service b23acc
static gboolean
Packit Service b23acc
ignore_option (NMSettingBond *s_bond, const char *option, const char *value)
Packit Service b23acc
{
Packit Service b23acc
	const char *defvalue;
Packit Service b23acc
Packit Service b23acc
	if (nm_streq0 (option, NM_SETTING_BOND_OPTION_MIIMON)) {
Packit Service b23acc
		/* The default value for miimon, when missing in the setting, is
Packit Service b23acc
		 * 0 if arp_interval is != 0, and 100 otherwise. So, let's ignore
Packit Service b23acc
		 * miimon=0 (which means that miimon is disabled) and accept any
Packit Service b23acc
		 * other value. Adding miimon=100 does not cause any harm.
Packit Service b23acc
		 */
Packit Service b23acc
		defvalue = "0";
Packit Service b23acc
	} else
Packit Service b23acc
		defvalue = nm_setting_bond_get_option_default (s_bond, option);
Packit Service b23acc
Packit Service b23acc
	return nm_streq0 (value, defvalue);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
update_connection (NMDevice *device, NMConnection *connection)
Packit Service b23acc
{
Packit Service b23acc
	NMSettingBond *s_bond = nm_connection_get_setting_bond (connection);
Packit Service b23acc
	int ifindex = nm_device_get_ifindex (device);
Packit Service b23acc
	NMBondMode mode = NM_BOND_MODE_UNKNOWN;
Packit Service b23acc
	const char **options;
Packit Service b23acc
Packit Service b23acc
	if (!s_bond) {
Packit Service b23acc
		s_bond = (NMSettingBond *) nm_setting_bond_new ();
Packit Service b23acc
		nm_connection_add_setting (connection, (NMSetting *) s_bond);
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	/* Read bond options from sysfs and update the Bond setting to match */
Packit Service b23acc
	options = nm_setting_bond_get_valid_options (s_bond);
Packit Service b23acc
	for (; *options; options++) {
Packit Service b23acc
		char *p;
Packit Service b23acc
		gs_free char *value = nm_platform_sysctl_master_get_option (nm_device_get_platform (device),
Packit Service b23acc
		                                                            ifindex,
Packit Service b23acc
		                                                            *options);
Packit Service b23acc
Packit Service b23acc
		if (   value
Packit Service b23acc
		    && _nm_setting_bond_get_option_type (s_bond, *options) == NM_BOND_OPTION_TYPE_BOTH) {
Packit Service b23acc
			p = strchr (value, ' ');
Packit Service b23acc
			if (p)
Packit Service b23acc
				*p = '\0';
Packit Service b23acc
		}
Packit Service b23acc
Packit Service b23acc
		if (mode == NM_BOND_MODE_UNKNOWN) {
Packit Service b23acc
			if (value && nm_streq (*options, NM_SETTING_BOND_OPTION_MODE))
Packit Service b23acc
				mode = _nm_setting_bond_mode_from_string (value);
Packit Service b23acc
			if (mode == NM_BOND_MODE_UNKNOWN)
Packit Service b23acc
				continue;
Packit Service b23acc
		}
Packit Service b23acc
Packit Service b23acc
		if (!_nm_setting_bond_option_supported (*options, mode))
Packit Service b23acc
			continue;
Packit Service b23acc
Packit Service b23acc
		if (   value
Packit Service b23acc
		    && value[0]
Packit Service b23acc
		    && !ignore_option (s_bond, *options, value)) {
Packit Service b23acc
			/* Replace " " with "," for arp_ip_targets from the kernel */
Packit Service b23acc
			if (strcmp (*options, NM_SETTING_BOND_OPTION_ARP_IP_TARGET) == 0) {
Packit Service b23acc
				for (p = value; *p; p++) {
Packit Service b23acc
					if (*p == ' ')
Packit Service b23acc
						*p = ',';
Packit Service b23acc
				}
Packit Service b23acc
			}
Packit Service b23acc
Packit Service b23acc
			nm_setting_bond_add_option (s_bond, *options, value);
Packit Service b23acc
		}
Packit Service b23acc
	}
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static gboolean
Packit Service b23acc
master_update_slave_connection (NMDevice *self,
Packit Service b23acc
                                NMDevice *slave,
Packit Service b23acc
                                NMConnection *connection,
Packit Service b23acc
                                GError **error)
Packit Service b23acc
{
Packit Service b23acc
	g_object_set (nm_connection_get_setting_connection (connection),
Packit Service b23acc
	              NM_SETTING_CONNECTION_MASTER, nm_device_get_iface (self),
Packit Service b23acc
	              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME,
Packit Service b23acc
	              NULL);
Packit Service b23acc
	return TRUE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
set_arp_targets (NMDevice *device,
Packit Service f52bd1
                 NMBondMode mode,
Packit Service b23acc
                 const char *cur_arp_ip_target,
Packit Service b23acc
                 const char *new_arp_ip_target)
Packit Service b23acc
{
Packit Service b23acc
	gs_unref_ptrarray GPtrArray *free_list = NULL;
Packit Service b23acc
	gs_free const char **cur_strv = NULL;
Packit Service b23acc
	gs_free const char **new_strv = NULL;
Packit Service b23acc
	gsize cur_len;
Packit Service b23acc
	gsize new_len;
Packit Service b23acc
	gsize i;
Packit Service b23acc
	gsize j;
Packit Service b23acc
Packit Service b23acc
	cur_strv = nm_utils_strsplit_set_full (cur_arp_ip_target, NM_ASCII_SPACES, NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP);
Packit Service b23acc
	new_strv = nm_utils_bond_option_arp_ip_targets_split (new_arp_ip_target);
Packit Service b23acc
Packit Service b23acc
	cur_len = NM_PTRARRAY_LEN (cur_strv);
Packit Service b23acc
	new_len = NM_PTRARRAY_LEN (new_strv);
Packit Service b23acc
Packit Service b23acc
	if (new_len > 0) {
Packit Service b23acc
		for (j = 0, i = 0; i < new_len; i++) {
Packit Service b23acc
			const char *s;
Packit Service b23acc
			in_addr_t a4;
Packit Service b23acc
Packit Service b23acc
			s = new_strv[i];
Packit Service b23acc
			if (nm_utils_parse_inaddr_bin (AF_INET, s, NULL, &a4)) {
Packit Service b23acc
				char sbuf[INET_ADDRSTRLEN];
Packit Service b23acc
Packit Service b23acc
				_nm_utils_inet4_ntop (a4, sbuf);
Packit Service b23acc
				if (!nm_streq (s, sbuf)) {
Packit Service b23acc
					if (!free_list)
Packit Service b23acc
						free_list = g_ptr_array_new_with_free_func (g_free);
Packit Service b23acc
					s = g_strdup (sbuf);
Packit Service b23acc
					g_ptr_array_add (free_list, (gpointer) s);
Packit Service b23acc
				}
Packit Service b23acc
			}
Packit Service b23acc
Packit Service b23acc
			if (nm_utils_strv_find_first ((char **) new_strv, i, s) < 0)
Packit Service b23acc
				new_strv[j++] = s;
Packit Service b23acc
		}
Packit Service b23acc
		new_strv[j] = NULL;
Packit Service b23acc
		new_len = j;
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	if (   cur_len == 0
Packit Service b23acc
	    && new_len == 0)
Packit Service b23acc
		return;
Packit Service b23acc
Packit Service b23acc
	if (_nm_utils_strv_equal ((char **) cur_strv, (char **) new_strv))
Packit Service b23acc
		return;
Packit Service b23acc
Packit Service b23acc
	for (i = 0; i < cur_len; i++)
Packit Service b23acc
		_set_bond_attr_printf (device, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, "-%s", cur_strv[i]);
Packit Service b23acc
	for (i = 0; i < new_len; i++)
Packit Service b23acc
		_set_bond_attr_printf (device, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, "+%s", new_strv[i]);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*
Packit Service b23acc
 * Sets bond attribute stored in the option hashtable or
Packit Service b23acc
 * the default value if no value was set.
Packit Service b23acc
 */
Packit Service b23acc
static void
Packit Service b23acc
set_bond_attr_or_default (NMDevice *device,
Packit Service b23acc
                          NMSettingBond *s_bond,
Packit Service b23acc
                          const char *opt)
Packit Service b23acc
{
Packit Service b23acc
	NMDeviceBond *self = NM_DEVICE_BOND (device);
Packit Service b23acc
	const char *value;
Packit Service b23acc
Packit Service b23acc
	value = nm_setting_bond_get_option_or_default (s_bond, opt);
Packit Service b23acc
	if (!value) {
Packit Service b23acc
		if (   _LOGT_ENABLED (LOGD_BOND)
Packit Service b23acc
		    && nm_setting_bond_get_option_by_name (s_bond, opt))
Packit Service b23acc
			_LOGT (LOGD_BOND, "bond option '%s' not set as it conflicts with other options", opt);
Packit Service b23acc
		return;
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	_set_bond_attr (device, opt, value);
Packit Service b23acc
}
Packit Service b23acc
Packit Service 7ded26
static void
Packit Service 7ded26
set_bond_attr_active_slave (NMDevice *device, NMSettingBond *s_bond)
Packit Service 7ded26
{
Packit Service 7ded26
	NMDeviceBond *self = NM_DEVICE_BOND (device);
Packit Service 7ded26
	const NMPlatformLink *plink;
Packit Service 7ded26
	const char *value;
Packit Service 7ded26
	const char *error_reason;
Packit Service 7ded26
	int ifindex;
Packit Service 7ded26
Packit Service 7ded26
	value = nm_setting_bond_get_option_or_default (s_bond, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE);
Packit Service 7ded26
	if (!value)
Packit Service 7ded26
		return;
Packit Service 7ded26
Packit Service 7ded26
	if (!nm_str_is_empty (value)) {
Packit Service 7ded26
		ifindex = nm_device_get_ifindex (device);
Packit Service 7ded26
		plink = nm_platform_link_get_by_ifname (nm_device_get_platform (device), value);
Packit Service 7ded26
		if (!plink)
Packit Service 7ded26
			error_reason = "does not exist";
Packit Service 7ded26
		else if (plink->master != ifindex)
Packit Service 7ded26
			error_reason = "is not yet enslaved";
Packit Service 7ded26
		else if (!NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP))
Packit Service 7ded26
			error_reason = "is not up";
Packit Service 7ded26
		else
Packit Service 7ded26
			error_reason = NULL;
Packit Service 7ded26
Packit Service 7ded26
		if (error_reason) {
Packit Service 7ded26
			_LOGT (LOGD_BOND, "bond option 'active_slave' not set as device \"%s\" %s", value, error_reason);
Packit Service 7ded26
			return;
Packit Service 7ded26
		}
Packit Service 7ded26
	}
Packit Service 7ded26
Packit Service 7ded26
	_set_bond_attr (device, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, value);
Packit Service 7ded26
}
Packit Service 7ded26
Packit Service b23acc
static gboolean
Packit Service b23acc
apply_bonding_config (NMDeviceBond *self)
Packit Service b23acc
{
Packit Service b23acc
	NMDevice *device = NM_DEVICE (self);
Packit Service f52bd1
	int ifindex = nm_device_get_ifindex (device);
Packit Service b23acc
	NMSettingBond *s_bond;
Packit Service b23acc
	NMBondMode mode;
Packit Service b23acc
	const char *mode_str;
Packit Service f52bd1
	gs_free char *cur_arp_ip_target = NULL;
Packit Service b23acc
Packit Service b23acc
	s_bond = nm_device_get_applied_setting (device, NM_TYPE_SETTING_BOND);
Packit Service b23acc
	g_return_val_if_fail (s_bond, FALSE);
Packit Service b23acc
Packit Service b23acc
	mode_str = nm_setting_bond_get_option_or_default (s_bond, NM_SETTING_BOND_OPTION_MODE);
Packit Service b23acc
	mode = _nm_setting_bond_mode_from_string (mode_str);
Packit Service b23acc
	g_return_val_if_fail (mode != NM_BOND_MODE_UNKNOWN, FALSE);
Packit Service b23acc
Packit Service b23acc
	/* Set mode first, as some other options (e.g. arp_interval) are valid
Packit Service b23acc
	 * only for certain modes.
Packit Service b23acc
	 */
Packit Service b23acc
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_MODE);
Packit Service b23acc
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_MIIMON);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_UPDELAY);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_ARP_VALIDATE);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_PRIMARY);
Packit Service f52bd1
Packit Service f52bd1
	/* ARP targets: clear and initialize the list */
Packit Service f52bd1
	cur_arp_ip_target = nm_platform_sysctl_master_get_option (nm_device_get_platform (device),
Packit Service f52bd1
	                                                          ifindex,
Packit Service f52bd1
	                                                          NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
Packit Service f52bd1
	set_arp_targets (device,
Packit Service f52bd1
	                 mode,
Packit Service f52bd1
	                 cur_arp_ip_target,
Packit Service f52bd1
	                 nm_setting_bond_get_option_or_default (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET));
Packit Service f52bd1
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM);
Packit Service 7ded26
	set_bond_attr_active_slave (device, s_bond);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_AD_SELECT);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_LACP_RATE);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_LP_INTERVAL);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_MIN_LINKS);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_RESEND_IGMP);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY);
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP);
Packit Service b23acc
	return TRUE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static NMActStageReturn
Packit Service b23acc
act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
Packit Service b23acc
{
Packit Service b23acc
	NMDeviceBond *self = NM_DEVICE_BOND (device);
Packit Service b23acc
	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
Packit Service b23acc
Packit Service b23acc
	/* Interface must be down to set bond options */
Packit Service b23acc
	nm_device_take_down (device, TRUE);
Packit Service b23acc
	if (!apply_bonding_config (self))
Packit Service b23acc
		ret = NM_ACT_STAGE_RETURN_FAILURE;
Packit Service b23acc
	else {
Packit Service b23acc
		if (!nm_device_hw_addr_set_cloned (device,
Packit Service b23acc
		                                   nm_device_get_applied_connection (device),
Packit Service b23acc
		                                   FALSE))
Packit Service b23acc
			ret = NM_ACT_STAGE_RETURN_FAILURE;
Packit Service b23acc
	}
Packit Service b23acc
	nm_device_bring_up (device, TRUE, NULL);
Packit Service b23acc
Packit Service b23acc
	return ret;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static gboolean
Packit Service b23acc
enslave_slave (NMDevice *device,
Packit Service b23acc
               NMDevice *slave,
Packit Service b23acc
               NMConnection *connection,
Packit Service b23acc
               gboolean configure)
Packit Service b23acc
{
Packit Service b23acc
	NMDeviceBond *self = NM_DEVICE_BOND (device);
Packit Service b23acc
	gboolean success = TRUE;
Packit Service b23acc
	const char *slave_iface = nm_device_get_ip_iface (slave);
Packit Service b23acc
	NMConnection *master_con;
Packit Service b23acc
Packit Service b23acc
	nm_device_master_check_slave_physical_port (device, slave, LOGD_BOND);
Packit Service b23acc
Packit Service b23acc
	if (configure) {
Packit Service b23acc
		nm_device_take_down (slave, TRUE);
Packit Service b23acc
		success = nm_platform_link_enslave (nm_device_get_platform (device),
Packit Service b23acc
		                                    nm_device_get_ip_ifindex (device),
Packit Service b23acc
		                                    nm_device_get_ip_ifindex (slave));
Packit Service b23acc
		nm_device_bring_up (slave, TRUE, NULL);
Packit Service b23acc
Packit Service b23acc
		if (!success)
Packit Service b23acc
			return FALSE;
Packit Service b23acc
Packit Service b23acc
		_LOGI (LOGD_BOND, "enslaved bond slave %s", slave_iface);
Packit Service b23acc
Packit Service b23acc
		/* The active_slave option can be set only after the interface is enslaved */
Packit Service b23acc
		master_con = nm_device_get_applied_connection (device);
Packit Service b23acc
		if (master_con) {
Packit Service b23acc
			NMSettingBond *s_bond = nm_connection_get_setting_bond (master_con);
Packit Service b23acc
			const char *active;
Packit Service b23acc
Packit Service b23acc
			if (s_bond) {
Packit Service b23acc
				active = nm_setting_bond_get_option_or_default (s_bond,
Packit Service b23acc
				                                                NM_SETTING_BOND_OPTION_ACTIVE_SLAVE);
Packit Service b23acc
				if (nm_streq0 (active, nm_device_get_iface (slave))) {
Packit Service b23acc
					nm_platform_sysctl_master_set_option (nm_device_get_platform (device),
Packit Service b23acc
					                                      nm_device_get_ifindex (device),
Packit Service 7ded26
					                                      NM_SETTING_BOND_OPTION_ACTIVE_SLAVE,
Packit Service b23acc
					                                      active);
Packit Service b23acc
					_LOGD (LOGD_BOND, "setting slave %s as active one for master %s",
Packit Service b23acc
					       active, nm_device_get_iface (device));
Packit Service b23acc
				}
Packit Service b23acc
			}
Packit Service b23acc
		}
Packit Service b23acc
	} else
Packit Service b23acc
		_LOGI (LOGD_BOND, "bond slave %s was enslaved", slave_iface);
Packit Service b23acc
Packit Service b23acc
	return TRUE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
release_slave (NMDevice *device,
Packit Service b23acc
               NMDevice *slave,
Packit Service b23acc
               gboolean configure)
Packit Service b23acc
{
Packit Service b23acc
	NMDeviceBond *self = NM_DEVICE_BOND (device);
Packit Service b23acc
	gboolean success;
Packit Service b23acc
	gs_free char *address = NULL;
Packit Service b23acc
	int ifindex_slave;
Packit Service b23acc
	int ifindex;
Packit Service b23acc
Packit Service b23acc
	if (configure) {
Packit Service b23acc
		ifindex = nm_device_get_ifindex (device);
Packit Service b23acc
		if (   ifindex <= 0
Packit Service b23acc
		    || !nm_platform_link_get (nm_device_get_platform (device), ifindex))
Packit Service b23acc
			configure = FALSE;
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	ifindex_slave = nm_device_get_ip_ifindex (slave);
Packit Service b23acc
Packit Service b23acc
	if (ifindex_slave <= 0)
Packit Service b23acc
		_LOGD (LOGD_BOND, "bond slave %s is already released", nm_device_get_ip_iface (slave));
Packit Service b23acc
Packit Service b23acc
	if (configure) {
Packit Service b23acc
		/* When the last slave is released the bond MAC will be set to a random
Packit Service b23acc
		 * value by kernel; remember the current one and restore it afterwards.
Packit Service b23acc
		 */
Packit Service b23acc
		address = g_strdup (nm_device_get_hw_address (device));
Packit Service b23acc
Packit Service b23acc
		if (ifindex_slave > 0) {
Packit Service b23acc
			success = nm_platform_link_release (nm_device_get_platform (device),
Packit Service b23acc
			                                    nm_device_get_ip_ifindex (device),
Packit Service b23acc
			                                    ifindex_slave);
Packit Service b23acc
Packit Service b23acc
			if (success) {
Packit Service b23acc
				_LOGI (LOGD_BOND, "released bond slave %s",
Packit Service b23acc
				       nm_device_get_ip_iface (slave));
Packit Service b23acc
			} else {
Packit Service b23acc
				_LOGW (LOGD_BOND, "failed to release bond slave %s",
Packit Service b23acc
				       nm_device_get_ip_iface (slave));
Packit Service b23acc
			}
Packit Service b23acc
		}
Packit Service b23acc
Packit Service b23acc
		nm_platform_process_events (nm_device_get_platform (device));
Packit Service b23acc
		if (nm_device_update_hw_address (device))
Packit Service b23acc
			nm_device_hw_addr_set (device, address, "restore", FALSE);
Packit Service b23acc
Packit Service b23acc
		/* Kernel bonding code "closes" the slave when releasing it, (which clears
Packit Service b23acc
		 * IFF_UP), so we must bring it back up here to ensure carrier changes and
Packit Service b23acc
		 * other state is noticed by the now-released slave.
Packit Service b23acc
		 */
Packit Service b23acc
		if (ifindex_slave > 0) {
Packit Service b23acc
			if (!nm_device_bring_up (slave, TRUE, NULL))
Packit Service b23acc
				_LOGW (LOGD_BOND, "released bond slave could not be brought up.");
Packit Service b23acc
		}
Packit Service b23acc
	} else {
Packit Service b23acc
		if (ifindex_slave > 0) {
Packit Service b23acc
			_LOGI (LOGD_BOND, "bond slave %s was released",
Packit Service b23acc
			       nm_device_get_ip_iface (slave));
Packit Service b23acc
		}
Packit Service b23acc
	}
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static gboolean
Packit Service b23acc
create_and_realize (NMDevice *device,
Packit Service b23acc
                    NMConnection *connection,
Packit Service b23acc
                    NMDevice *parent,
Packit Service b23acc
                    const NMPlatformLink **out_plink,
Packit Service b23acc
                    GError **error)
Packit Service b23acc
{
Packit Service b23acc
	const char *iface = nm_device_get_iface (device);
Packit Service b23acc
	int r;
Packit Service b23acc
Packit Service b23acc
	g_assert (iface);
Packit Service b23acc
Packit Service b23acc
	r = nm_platform_link_bond_add (nm_device_get_platform (device), iface, out_plink);
Packit Service b23acc
	if (r < 0) {
Packit Service b23acc
		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
Packit Service b23acc
		             "Failed to create bond interface '%s' for '%s': %s",
Packit Service b23acc
		             iface,
Packit Service b23acc
		             nm_connection_get_id (connection),
Packit Service b23acc
		             nm_strerror (r));
Packit Service b23acc
		return FALSE;
Packit Service b23acc
	}
Packit Service b23acc
	return TRUE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static gboolean
Packit Service b23acc
check_changed_options (NMSettingBond *s_a, NMSettingBond *s_b, GError **error)
Packit Service b23acc
{
Packit Service f52bd1
	guint i, num;
Packit Service f52bd1
	const char *name = NULL, *value_a = NULL, *value_b = NULL;
Packit Service b23acc
Packit Service f52bd1
	/* Check that options in @s_a have compatible changes in @s_b */
Packit Service b23acc
Packit Service f52bd1
	num = nm_setting_bond_get_num_options (s_a);
Packit Service f52bd1
	for (i = 0; i < num; i++) {
Packit Service f52bd1
		nm_setting_bond_get_option (s_a, i, &name, &value_a);
Packit Service b23acc
Packit Service b23acc
		/* We support changes to these */
Packit Service b23acc
		if (NM_IN_STRSET (name,
Packit Service f52bd1
		                  NM_SETTING_BOND_OPTION_ACTIVE_SLAVE,
Packit Service f52bd1
		                  NM_SETTING_BOND_OPTION_PRIMARY)) {
Packit Service f52bd1
			continue;
Packit Service f52bd1
		}
Packit Service f52bd1
Packit Service f52bd1
		/* Missing in @s_b, but has a default value in @s_a */
Packit Service f52bd1
		value_b = nm_setting_bond_get_option_by_name (s_b, name);
Packit Service f52bd1
		if (   !value_b
Packit Service f52bd1
		    && nm_streq0 (value_a, nm_setting_bond_get_option_default (s_a, name))) {
Packit Service b4886a
			continue;
Packit Service b4886a
		}
Packit Service b4886a
Packit Service b23acc
		/* Reject any other changes */
Packit Service f52bd1
		if (!nm_streq0 (value_a, value_b)) {
Packit Service b23acc
			g_set_error (error,
Packit Service b23acc
			             NM_DEVICE_ERROR,
Packit Service b23acc
			             NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
Packit Service b23acc
			             "Can't reapply '%s' bond option",
Packit Service b23acc
			             name);
Packit Service b23acc
			return FALSE;
Packit Service b23acc
		}
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	return TRUE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static gboolean
Packit Service b23acc
can_reapply_change (NMDevice *device,
Packit Service b23acc
                    const char *setting_name,
Packit Service b23acc
                    NMSetting *s_old,
Packit Service b23acc
                    NMSetting *s_new,
Packit Service b23acc
                    GHashTable *diffs,
Packit Service b23acc
                    GError **error)
Packit Service b23acc
{
Packit Service b23acc
	NMDeviceClass *device_class;
Packit Service f52bd1
	NMSettingBond *s_bond_old, *s_bond_new;
Packit Service b23acc
Packit Service b23acc
	/* Only handle bond setting here, delegate other settings to parent class */
Packit Service b23acc
	if (nm_streq (setting_name, NM_SETTING_BOND_SETTING_NAME)) {
Packit Service b23acc
		if (!nm_device_hash_check_invalid_keys (diffs,
Packit Service b23acc
		                                        NM_SETTING_BOND_SETTING_NAME,
Packit Service b23acc
		                                        error,
Packit Service b23acc
		                                        NM_SETTING_BOND_OPTIONS))
Packit Service b23acc
			return FALSE;
Packit Service b23acc
Packit Service f52bd1
		s_bond_old = NM_SETTING_BOND (s_old);
Packit Service f52bd1
		s_bond_new = NM_SETTING_BOND (s_new);
Packit Service f52bd1
Packit Service f52bd1
		if (   !check_changed_options (s_bond_old, s_bond_new, error)
Packit Service f52bd1
		    || !check_changed_options (s_bond_new, s_bond_old, error)) {
Packit Service f52bd1
			return FALSE;
Packit Service f52bd1
		}
Packit Service f52bd1
Packit Service f52bd1
		return TRUE;
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	device_class = NM_DEVICE_CLASS (nm_device_bond_parent_class);
Packit Service b23acc
	return device_class->can_reapply_change (device,
Packit Service b23acc
	                                         setting_name,
Packit Service b23acc
	                                         s_old,
Packit Service b23acc
	                                         s_new,
Packit Service b23acc
	                                         diffs,
Packit Service b23acc
	                                         error);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_new)
Packit Service b23acc
{
Packit Service b23acc
	NMDeviceBond *self = NM_DEVICE_BOND (device);
Packit Service c0d552
	const char *value;
Packit Service f52bd1
	NMSettingBond *s_bond;
Packit Service b23acc
	NMBondMode mode;
Packit Service b23acc
Packit Service b23acc
	NM_DEVICE_CLASS (nm_device_bond_parent_class)->reapply_connection (device,
Packit Service b23acc
	                                                                   con_old,
Packit Service b23acc
	                                                                   con_new);
Packit Service b23acc
Packit Service b23acc
	_LOGD (LOGD_BOND, "reapplying bond settings");
Packit Service b23acc
	s_bond = nm_connection_get_setting_bond (con_new);
Packit Service b23acc
	g_return_if_fail (s_bond);
Packit Service b23acc
Packit Service b23acc
	value = nm_setting_bond_get_option_or_default (s_bond, NM_SETTING_BOND_OPTION_MODE);
Packit Service b23acc
	mode = _nm_setting_bond_mode_from_string (value);
Packit Service b23acc
	g_return_if_fail (mode != NM_BOND_MODE_UNKNOWN);
Packit Service b23acc
Packit Service f52bd1
	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_PRIMARY);
Packit Service 7ded26
	set_bond_attr_active_slave (device, s_bond);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
nm_device_bond_init (NMDeviceBond * self)
Packit Service b23acc
{
Packit Service b23acc
	nm_assert (nm_device_is_master (NM_DEVICE (self)));
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static const NMDBusInterfaceInfoExtended interface_info_device_bond = {
Packit Service b23acc
	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
Packit Service b23acc
		NM_DBUS_INTERFACE_DEVICE_BOND,
Packit Service b23acc
		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
Packit Service b23acc
			&nm_signal_info_property_changed_legacy,
Packit Service b23acc
		),
Packit Service b23acc
		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
Packit Service b23acc
			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s",  NM_DEVICE_HW_ADDRESS),
Packit Service b23acc
			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier",   "b",  NM_DEVICE_CARRIER),
Packit Service b23acc
			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Slaves",    "ao", NM_DEVICE_SLAVES),
Packit Service b23acc
		),
Packit Service b23acc
	),
Packit Service b23acc
	.legacy_property_changed = TRUE,
Packit Service b23acc
};
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
nm_device_bond_class_init (NMDeviceBondClass *klass)
Packit Service b23acc
{
Packit Service b23acc
	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
Packit Service b23acc
	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
Packit Service b23acc
Packit Service b23acc
	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_bond);
Packit Service b23acc
Packit Service b23acc
	device_class->connection_type_supported = NM_SETTING_BOND_SETTING_NAME;
Packit Service b23acc
	device_class->connection_type_check_compatible = NM_SETTING_BOND_SETTING_NAME;
Packit Service b23acc
	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_BOND);
Packit Service b23acc
Packit Service b23acc
	device_class->is_master = TRUE;
Packit Service b23acc
	device_class->get_generic_capabilities = get_generic_capabilities;
Packit Service b23acc
	device_class->complete_connection = complete_connection;
Packit Service b23acc
Packit Service b23acc
	device_class->update_connection = update_connection;
Packit Service b23acc
	device_class->master_update_slave_connection = master_update_slave_connection;
Packit Service b23acc
Packit Service b23acc
	device_class->create_and_realize = create_and_realize;
Packit Service b23acc
	device_class->act_stage1_prepare = act_stage1_prepare;
Packit Service b23acc
	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
Packit Service b23acc
	device_class->enslave_slave = enslave_slave;
Packit Service b23acc
	device_class->release_slave = release_slave;
Packit Service b23acc
	device_class->can_reapply_change = can_reapply_change;
Packit Service b23acc
	device_class->reapply_connection = reapply_connection;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
#define NM_TYPE_BOND_DEVICE_FACTORY (nm_bond_device_factory_get_type ())
Packit Service b23acc
#define NM_BOND_DEVICE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BOND_DEVICE_FACTORY, NMBondDeviceFactory))
Packit Service b23acc
Packit Service b23acc
static NMDevice *
Packit Service b23acc
create_device (NMDeviceFactory *factory,
Packit Service b23acc
               const char *iface,
Packit Service b23acc
               const NMPlatformLink *plink,
Packit Service b23acc
               NMConnection *connection,
Packit Service b23acc
               gboolean *out_ignore)
Packit Service b23acc
{
Packit Service b23acc
	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BOND,
Packit Service b23acc
	                                  NM_DEVICE_IFACE, iface,
Packit Service b23acc
	                                  NM_DEVICE_DRIVER, "bonding",
Packit Service b23acc
	                                  NM_DEVICE_TYPE_DESC, "Bond",
Packit Service b23acc
	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BOND,
Packit Service b23acc
	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_BOND,
Packit Service b23acc
	                                  NULL);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
NM_DEVICE_FACTORY_DEFINE_INTERNAL (BOND, Bond, bond,
Packit Service b23acc
	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_BOND)
Packit Service b23acc
	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BOND_SETTING_NAME),
Packit Service b23acc
	factory_class->create_device = create_device;
Packit Service b23acc
);