NetworkManager/src/core/devices/nm-device-bond.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1101 lines
43 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2011 - 2018 Red Hat, Inc.
*/
#include "src/core/nm-default-daemon.h"
#include "nm-device-bond.h"
#include <stdlib.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include <linux/if_link.h>
#include "NetworkManagerUtils.h"
#include "nm-device-private.h"
#include "libnm-platform/nm-platform.h"
2014-09-08 10:15:02 -05:00
#include "nm-device-factory.h"
#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "nm-manager.h"
#include "nm-setting-bond-port.h"
#include "nm-bond-manager.h"
#define _NMLOG_DEVICE_TYPE NMDeviceBond
#include "nm-device-logging.h"
/*****************************************************************************/
#define OPTIONS_APPLY_SUBSET \
NM_SETTING_BOND_OPTION_MIIMON, NM_SETTING_BOND_OPTION_UPDELAY, \
NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_ARP_INTERVAL, \
NM_SETTING_BOND_OPTION_ARP_VALIDATE, NM_SETTING_BOND_OPTION_PRIMARY, \
NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO, \
NM_SETTING_BOND_OPTION_AD_SELECT, NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY, \
NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS, \
NM_SETTING_BOND_OPTION_FAIL_OVER_MAC, NM_SETTING_BOND_OPTION_LACP_RATE, \
NM_SETTING_BOND_OPTION_LP_INTERVAL, NM_SETTING_BOND_OPTION_MIN_LINKS, \
NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, \
NM_SETTING_BOND_OPTION_RESEND_IGMP, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB, \
NM_SETTING_BOND_OPTION_USE_CARRIER, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, \
NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY, \
NM_SETTING_BOND_OPTION_ARP_MISSED_MAX, NM_SETTING_BOND_OPTION_LACP_ACTIVE
#define OPTIONS_REAPPLY_SUBSET \
NM_SETTING_BOND_OPTION_MIIMON, NM_SETTING_BOND_OPTION_UPDELAY, \
NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_ARP_INTERVAL, \
NM_SETTING_BOND_OPTION_ARP_VALIDATE, NM_SETTING_BOND_OPTION_PRIMARY, \
NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO, \
NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS, \
NM_SETTING_BOND_OPTION_LP_INTERVAL, NM_SETTING_BOND_OPTION_MIN_LINKS, \
NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, \
NM_SETTING_BOND_OPTION_RESEND_IGMP, NM_SETTING_BOND_OPTION_USE_CARRIER, \
NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, \
NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY, NM_SETTING_BOND_OPTION_ARP_MISSED_MAX, \
NM_SETTING_BOND_OPTION_LACP_ACTIVE
#define OPTIONS_REAPPLY_FULL \
OPTIONS_REAPPLY_SUBSET, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, \
NM_SETTING_BOND_OPTION_ARP_IP_TARGET, NM_SETTING_BOND_OPTION_NS_IP6_TARGET
/*****************************************************************************/
struct _NMDeviceBond {
NMDevice parent;
NMBondManager *bond_manager;
};
struct _NMDeviceBondClass {
NMDeviceClass parent;
};
G_DEFINE_TYPE(NMDeviceBond, nm_device_bond, NM_TYPE_DEVICE)
/*****************************************************************************/
static NMDeviceCapabilities
get_generic_capabilities(NMDevice *dev)
{
return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
}
static gboolean
complete_connection(NMDevice *device,
NMConnection *connection,
const char *specific_object,
NMConnection *const *existing_connections,
GError **error)
{
nm_utils_complete_generic(nm_device_get_platform(device),
connection,
NM_SETTING_BOND_SETTING_NAME,
existing_connections,
NULL,
_("Bond connection"),
"bond",
NULL);
_nm_connection_ensure_setting(connection, NM_TYPE_SETTING_BOND);
return TRUE;
}
/*****************************************************************************/
static gboolean
_set_bond_attr(NMDevice *device, const char *attr, const char *value)
{
NMDeviceBond *self = NM_DEVICE_BOND(device);
int ifindex = nm_device_get_ifindex(device);
gboolean ret;
nm_assert(attr && attr[0]);
nm_assert(value);
if (nm_streq(value, NM_BOND_AD_ACTOR_SYSTEM_DEFAULT)
&& nm_streq(attr, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM)) {
gs_free char *cur_val = NULL;
/* kernel does not allow setting ad_actor_system to "00:00:00:00:00:00". We would thus
* log an EINVAL error. Avoid that... at least, if the value is already "00:00:00:00:00:00". */
cur_val =
nm_platform_sysctl_controller_get_option(nm_device_get_platform(device), ifindex, attr);
if (nm_streq0(cur_val, NM_BOND_AD_ACTOR_SYSTEM_DEFAULT))
return TRUE;
/* OK, the current value is different, and we will proceed setting "00:00:00:00:00:00".
* That will fail, and we will log a warning. There is nothing else to do. */
}
ret = nm_platform_sysctl_controller_set_option(nm_device_get_platform(device),
ifindex,
attr,
value);
if (!ret)
_LOGW(LOGD_PLATFORM, "failed to set bonding attribute '%s' to '%s'", attr, value);
return ret;
}
#define _set_bond_attr_take(device, attr, value) \
G_STMT_START \
{ \
gs_free char *_tmp = (value); \
\
_set_bond_attr(device, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, _tmp); \
} \
G_STMT_END
#define _set_bond_attr_printf(device, attr, fmt, ...) \
_set_bond_attr_take((device), (attr), g_strdup_printf(fmt, __VA_ARGS__))
static gboolean
ignore_option(NMSettingBond *s_bond, const char *option, const char *value)
{
const char *defvalue;
if (nm_streq0(option, NM_SETTING_BOND_OPTION_MIIMON)) {
/* The default value for miimon, when missing in the setting, is
* 0 if arp_interval is != 0, and 100 otherwise. So, let's ignore
* miimon=0 (which means that miimon is disabled) and accept any
* other value. Adding miimon=100 does not cause any harm.
*/
defvalue = "0";
} else
defvalue = nm_setting_bond_get_option_default(s_bond, option);
return nm_streq0(value, defvalue);
}
static void
update_connection(NMDevice *device, NMConnection *connection)
{
NMDeviceBond *self = NM_DEVICE_BOND(device);
NMSettingBond *s_bond = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_BOND);
int ifindex = nm_device_get_ifindex(device);
NMBondMode mode = NM_BOND_MODE_UNKNOWN;
const char **options;
/* Read bond options from sysfs and update the Bond setting to match */
options = nm_setting_bond_get_valid_options(NULL);
for (; options[0]; options++) {
const char *option = options[0];
gs_free char *value = NULL;
char *p;
if (NM_IN_STRSET(option,
NM_SETTING_BOND_OPTION_ACTIVE_SLAVE,
NM_SETTING_BOND_OPTION_BALANCE_SLB))
continue;
value = nm_platform_sysctl_controller_get_option(nm_device_get_platform(device),
ifindex,
option);
if (value && _nm_setting_bond_get_option_type(s_bond, option) == NM_BOND_OPTION_TYPE_BOTH) {
p = strchr(value, ' ');
if (p)
*p = '\0';
}
if (mode == NM_BOND_MODE_UNKNOWN) {
if (value && nm_streq(option, NM_SETTING_BOND_OPTION_MODE))
mode = _nm_setting_bond_mode_from_string(value);
if (mode == NM_BOND_MODE_UNKNOWN)
continue;
}
if (!_nm_setting_bond_option_supported(option, mode))
continue;
if (value && value[0] && !ignore_option(s_bond, option, value)) {
/* Replace " " with "," for arp_ip_targets from the kernel */
if (nm_streq(option, NM_SETTING_BOND_OPTION_ARP_IP_TARGET)) {
for (p = value; *p; p++) {
if (*p == ' ')
*p = ',';
}
}
libnm/bond: remove validation from nm_setting_bond_add_option() and explicitly validate For historic reasons is NMSettingBond implemented differently from other settings. It uses a strdict, and adds some validation on top of that. The idea was probably to be able to treat bond options more generically. But in practice we cannot treat them as opaque values, but need to know, validate and understand all the options. Thus, this implementation with a strdict is not nice. The user can set the GObject property NM_SETTING_BOND_OPTIONS to any strdict, and the setter performs no validation or normalization. That is probably good, because g_object_set() cannot return an error to signalize invalid settings. As often, we have corresponding C API like nm_setting_bond_add_option() and nm_setting_bond_remove_option(). It should be possible to get the same result both with the C API and with the GObject property setting. Since there is already a way to set certain invalid values, it does not help if the C API tries to prevent that. That implies, that also add-option does not perform additional validation and sets whatever the user asks. Remove all validation from nm_setting_bond_add_option() and nm_setting_bond_remove_option(). This validation was anyway only very basic. It was calling nm_setting_bond_validate_option(), which can check whether the string is (for example) and integer, but it cannot do validation beyond one option. In most cases, the validation needs to take into account the bond mode or other options, so validating one option in isolation is not very useful. Proper validation should instead be done via nm_connection_verify(). However, due to another historic oddity, that verification is very forgiving too and doesn't reject many invalid settings when it should. That is hard to fix, because making validation more strict can break existing (and working) configurations. However, verify() already contains basic validation via nm_setting_bond_validate_option(). So in the previous behavior nm_setting_bond_add_option() would silently do nothing (only returning %FALSE) for invalid options, while now it would add the invalid options to the dictionary -- only to have it later fail validation during nm_connection_verify(). That is a slight change in behavior, however it seems preferable. It seems preferable and acceptable because most users that call nm_setting_bond_add_option() already understand the meaning and valid values. Keyfile and ifcfg-rh readers are the few exceptions, which really just parse a string dictionary, without need to understand them. But nmtui or nmstate already know the option they want to set. They don't expect a failure there, nor do they need the validation. Note that this change in behavior could be dangerous for example for the keyfile/ifcfg-rh readers, which silently ignored errors before. We don't want them to start failing if they read invalid options from a file, so instead let those callers explicitly pre-validate the value and log an warning. https://bugzilla.redhat.com/show_bug.cgi?id=1887523
2020-10-15 10:57:51 +02:00
if (!_nm_setting_bond_validate_option(option, value, NULL))
_LOGT(LOGD_BOND, "cannot set invalid bond option '%s' = '%s'", option, value);
else
nm_setting_bond_add_option(s_bond, option, value);
}
}
}
static gboolean
controller_update_port_connection(NMDevice *self,
NMDevice *port,
NMConnection *connection,
GError **error)
{
NMSettingBondPort *s_port;
int ifindex_port = nm_device_get_ifindex(port);
NMConnection *applied_connection = nm_device_get_applied_connection(self);
const NMPlatformLink *pllink;
g_return_val_if_fail(ifindex_port > 0, FALSE);
s_port = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_BOND_PORT);
pllink = nm_platform_link_get(nm_device_get_platform(port), ifindex_port);
if (pllink && pllink->port_kind == NM_PORT_KIND_BOND)
g_object_set(s_port,
NM_SETTING_BOND_PORT_QUEUE_ID,
pllink->port_data.bond.queue_id,
NM_SETTING_BOND_PORT_PRIO,
pllink->port_data.bond.prio,
NULL);
g_object_set(nm_connection_get_setting_connection(connection),
NM_SETTING_CONNECTION_CONTROLLER,
nm_connection_get_uuid(applied_connection),
NM_SETTING_CONNECTION_PORT_TYPE,
NM_SETTING_BOND_SETTING_NAME,
NULL);
return TRUE;
}
static void
set_arp_targets(NMDevice *device, const char *cur_arp_ip_target, const char *new_arp_ip_target)
{
gs_unref_ptrarray GPtrArray *free_list = NULL;
gs_free const char **cur_strv = NULL;
gs_free const char **new_strv = NULL;
gsize cur_len;
gsize new_len;
gsize i;
gsize j;
cur_strv =
nm_strsplit_set_full(cur_arp_ip_target, NM_ASCII_SPACES, NM_STRSPLIT_SET_FLAGS_STRSTRIP);
new_strv = nm_utils_bond_option_ip_split(new_arp_ip_target);
cur_len = NM_PTRARRAY_LEN(cur_strv);
new_len = NM_PTRARRAY_LEN(new_strv);
if (new_len > 0) {
for (j = 0, i = 0; i < new_len; i++) {
const char *s;
in_addr_t a4;
s = new_strv[i];
glib-aux: rename IP address related helpers from "nm-inet-utils.h" - name things related to `in_addr_t`, `struct in6_addr`, `NMIPAddr` as `nm_ip4_addr_*()`, `nm_ip6_addr_*()`, `nm_ip_addr_*()`, respectively. - we have a wrapper `nm_inet_ntop()` for `inet_ntop()`. This name of our wrapper is chosen to be familiar with the libc underlying function. With this, also name functions that are about string representations of addresses `nm_inet_*()`, `nm_inet4_*()`, `nm_inet6_*()`. For example, `nm_inet_parse_str()`, `nm_inet_is_normalized()`. <<<< R() { git grep -l "$1" | xargs sed -i "s/\<$1\>/$2/g" } R NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX NM_CMP_DIRECT_IP4_ADDR_SAME_PREFIX R NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX R NM_UTILS_INET_ADDRSTRLEN NM_INET_ADDRSTRLEN R _nm_utils_inet4_ntop nm_inet4_ntop R _nm_utils_inet6_ntop nm_inet6_ntop R _nm_utils_ip4_get_default_prefix nm_ip4_addr_get_default_prefix R _nm_utils_ip4_get_default_prefix0 nm_ip4_addr_get_default_prefix0 R _nm_utils_ip4_netmask_to_prefix nm_ip4_addr_netmask_to_prefix R _nm_utils_ip4_prefix_to_netmask nm_ip4_addr_netmask_from_prefix R nm_utils_inet4_ntop_dup nm_inet4_ntop_dup R nm_utils_inet6_ntop_dup nm_inet6_ntop_dup R nm_utils_inet_ntop nm_inet_ntop R nm_utils_inet_ntop_dup nm_inet_ntop_dup R nm_utils_ip4_address_clear_host_address nm_ip4_addr_clear_host_address R nm_utils_ip4_address_is_link_local nm_ip4_addr_is_link_local R nm_utils_ip4_address_is_loopback nm_ip4_addr_is_loopback R nm_utils_ip4_address_is_zeronet nm_ip4_addr_is_zeronet R nm_utils_ip4_address_same_prefix nm_ip4_addr_same_prefix R nm_utils_ip4_address_same_prefix_cmp nm_ip4_addr_same_prefix_cmp R nm_utils_ip6_address_clear_host_address nm_ip6_addr_clear_host_address R nm_utils_ip6_address_same_prefix nm_ip6_addr_same_prefix R nm_utils_ip6_address_same_prefix_cmp nm_ip6_addr_same_prefix_cmp R nm_utils_ip6_is_ula nm_ip6_addr_is_ula R nm_utils_ip_address_same_prefix nm_ip_addr_same_prefix R nm_utils_ip_address_same_prefix_cmp nm_ip_addr_same_prefix_cmp R nm_utils_ip_is_site_local nm_ip_addr_is_site_local R nm_utils_ipaddr_is_normalized nm_inet_is_normalized R nm_utils_ipaddr_is_valid nm_inet_is_valid R nm_utils_ipx_address_clear_host_address nm_ip_addr_clear_host_address R nm_utils_parse_inaddr nm_inet_parse_str R nm_utils_parse_inaddr_bin nm_inet_parse_bin R nm_utils_parse_inaddr_bin_full nm_inet_parse_bin_full R nm_utils_parse_inaddr_prefix nm_inet_parse_with_prefix_str R nm_utils_parse_inaddr_prefix_bin nm_inet_parse_with_prefix_bin R test_nm_utils_ip6_address_same_prefix test_nm_ip_addr_same_prefix ./contrib/scripts/nm-code-format.sh -F
2022-08-19 13:15:20 +02:00
if (nm_inet_parse_bin(AF_INET, s, NULL, &a4)) {
char sbuf[INET_ADDRSTRLEN];
glib-aux: rename IP address related helpers from "nm-inet-utils.h" - name things related to `in_addr_t`, `struct in6_addr`, `NMIPAddr` as `nm_ip4_addr_*()`, `nm_ip6_addr_*()`, `nm_ip_addr_*()`, respectively. - we have a wrapper `nm_inet_ntop()` for `inet_ntop()`. This name of our wrapper is chosen to be familiar with the libc underlying function. With this, also name functions that are about string representations of addresses `nm_inet_*()`, `nm_inet4_*()`, `nm_inet6_*()`. For example, `nm_inet_parse_str()`, `nm_inet_is_normalized()`. <<<< R() { git grep -l "$1" | xargs sed -i "s/\<$1\>/$2/g" } R NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX NM_CMP_DIRECT_IP4_ADDR_SAME_PREFIX R NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX R NM_UTILS_INET_ADDRSTRLEN NM_INET_ADDRSTRLEN R _nm_utils_inet4_ntop nm_inet4_ntop R _nm_utils_inet6_ntop nm_inet6_ntop R _nm_utils_ip4_get_default_prefix nm_ip4_addr_get_default_prefix R _nm_utils_ip4_get_default_prefix0 nm_ip4_addr_get_default_prefix0 R _nm_utils_ip4_netmask_to_prefix nm_ip4_addr_netmask_to_prefix R _nm_utils_ip4_prefix_to_netmask nm_ip4_addr_netmask_from_prefix R nm_utils_inet4_ntop_dup nm_inet4_ntop_dup R nm_utils_inet6_ntop_dup nm_inet6_ntop_dup R nm_utils_inet_ntop nm_inet_ntop R nm_utils_inet_ntop_dup nm_inet_ntop_dup R nm_utils_ip4_address_clear_host_address nm_ip4_addr_clear_host_address R nm_utils_ip4_address_is_link_local nm_ip4_addr_is_link_local R nm_utils_ip4_address_is_loopback nm_ip4_addr_is_loopback R nm_utils_ip4_address_is_zeronet nm_ip4_addr_is_zeronet R nm_utils_ip4_address_same_prefix nm_ip4_addr_same_prefix R nm_utils_ip4_address_same_prefix_cmp nm_ip4_addr_same_prefix_cmp R nm_utils_ip6_address_clear_host_address nm_ip6_addr_clear_host_address R nm_utils_ip6_address_same_prefix nm_ip6_addr_same_prefix R nm_utils_ip6_address_same_prefix_cmp nm_ip6_addr_same_prefix_cmp R nm_utils_ip6_is_ula nm_ip6_addr_is_ula R nm_utils_ip_address_same_prefix nm_ip_addr_same_prefix R nm_utils_ip_address_same_prefix_cmp nm_ip_addr_same_prefix_cmp R nm_utils_ip_is_site_local nm_ip_addr_is_site_local R nm_utils_ipaddr_is_normalized nm_inet_is_normalized R nm_utils_ipaddr_is_valid nm_inet_is_valid R nm_utils_ipx_address_clear_host_address nm_ip_addr_clear_host_address R nm_utils_parse_inaddr nm_inet_parse_str R nm_utils_parse_inaddr_bin nm_inet_parse_bin R nm_utils_parse_inaddr_bin_full nm_inet_parse_bin_full R nm_utils_parse_inaddr_prefix nm_inet_parse_with_prefix_str R nm_utils_parse_inaddr_prefix_bin nm_inet_parse_with_prefix_bin R test_nm_utils_ip6_address_same_prefix test_nm_ip_addr_same_prefix ./contrib/scripts/nm-code-format.sh -F
2022-08-19 13:15:20 +02:00
nm_inet4_ntop(a4, sbuf);
if (!nm_streq(s, sbuf)) {
if (!free_list)
free_list = g_ptr_array_new_with_free_func(g_free);
s = g_strdup(sbuf);
g_ptr_array_add(free_list, (gpointer) s);
}
}
if (!nm_strv_contains(new_strv, i, s))
new_strv[j++] = s;
}
new_strv[j] = NULL;
new_len = j;
}
if (cur_len == 0 && new_len == 0)
return;
all: unify and rename strv helper API Naming is important, because the name of a thing should give you a good idea what it does. Also, to find a thing, it needs a good name in the first place. But naming is also hard. Historically, some strv helper API was named as nm_utils_strv_*(), and some API had a leading underscore (as it is internal API). This was all inconsistent. Do some renaming and try to unify things. We get rid of the leading underscore if this is just a regular (internal) helper. But not for example from _nm_strv_find_first(), because that is the implementation of nm_strv_find_first(). - _nm_utils_strv_cleanup() -> nm_strv_cleanup() - _nm_utils_strv_cleanup_const() -> nm_strv_cleanup_const() - _nm_utils_strv_cmp_n() -> _nm_strv_cmp_n() - _nm_utils_strv_dup() -> _nm_strv_dup() - _nm_utils_strv_dup_packed() -> _nm_strv_dup_packed() - _nm_utils_strv_find_first() -> _nm_strv_find_first() - _nm_utils_strv_sort() -> _nm_strv_sort() - _nm_utils_strv_to_ptrarray() -> nm_strv_to_ptrarray() - _nm_utils_strv_to_slist() -> nm_strv_to_gslist() - nm_utils_strv_cmp_n() -> nm_strv_cmp_n() - nm_utils_strv_dup() -> nm_strv_dup() - nm_utils_strv_dup_packed() -> nm_strv_dup_packed() - nm_utils_strv_dup_shallow_maybe_a() -> nm_strv_dup_shallow_maybe_a() - nm_utils_strv_equal() -> nm_strv_equal() - nm_utils_strv_find_binary_search() -> nm_strv_find_binary_search() - nm_utils_strv_find_first() -> nm_strv_find_first() - nm_utils_strv_make_deep_copied() -> nm_strv_make_deep_copied() - nm_utils_strv_make_deep_copied_n() -> nm_strv_make_deep_copied_n() - nm_utils_strv_make_deep_copied_nonnull() -> nm_strv_make_deep_copied_nonnull() - nm_utils_strv_sort() -> nm_strv_sort() Note that no names are swapped and none of the new names existed previously. That means, all the new names are really new, which simplifies to find errors due to this larger refactoring. E.g. if you backport a patch from after this change to an old branch, you'll get a compiler error and notice that something is missing.
2021-07-29 10:02:11 +02:00
if (nm_strv_equal(cur_strv, new_strv))
return;
for (i = 0; i < cur_len; i++)
_set_bond_attr_printf(device, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, "-%s", cur_strv[i]);
for (i = 0; i < new_len; i++)
_set_bond_attr_printf(device, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, "+%s", new_strv[i]);
}
/*
* Sets bond attribute stored in the option hashtable or
* the default value if no value was set.
*/
static void
set_bond_attr_or_default(NMDevice *device, NMSettingBond *s_bond, const char *opt)
{
NMDeviceBond *self = NM_DEVICE_BOND(device);
const char *value;
value = nm_setting_bond_get_option_normalized(s_bond, opt);
if (!value) {
if (_LOGT_ENABLED(LOGD_BOND) && nm_setting_bond_get_option_by_name(s_bond, opt))
_LOGT(LOGD_BOND, "bond option '%s' not set as it conflicts with other options", opt);
return;
}
_set_bond_attr(device, opt, value);
}
static void
set_bond_attrs_or_default(NMDevice *device, NMSettingBond *s_bond, const char *const *attr_v)
{
nm_assert(NM_IS_DEVICE(device));
nm_assert(s_bond);
nm_assert(attr_v);
for (; *attr_v; ++attr_v)
set_bond_attr_or_default(device, s_bond, *attr_v);
}
static void
set_bond_arp_ip_targets(NMDevice *device, NMSettingBond *s_bond)
{
int ifindex = nm_device_get_ifindex(device);
gs_free char *cur_arp_ip_target = NULL;
/* ARP targets: clear and initialize the list */
cur_arp_ip_target =
nm_platform_sysctl_controller_get_option(nm_device_get_platform(device),
ifindex,
NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
set_arp_targets(
device,
cur_arp_ip_target,
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET));
}
static guint8
_bond_arp_ip_target_to_platform(const char *value, in_addr_t out[static NM_BOND_MAX_ARP_TARGETS])
{
gs_free const char **ip = NULL;
in_addr_t in_a;
int i;
int added = 0;
ip = nm_utils_bond_option_ip_split(value);
if (!ip)
return added;
for (i = 0; ip[i]; i++) {
if (added > NM_BOND_MAX_ARP_TARGETS - 1)
break;
glib-aux: rename IP address related helpers from "nm-inet-utils.h" - name things related to `in_addr_t`, `struct in6_addr`, `NMIPAddr` as `nm_ip4_addr_*()`, `nm_ip6_addr_*()`, `nm_ip_addr_*()`, respectively. - we have a wrapper `nm_inet_ntop()` for `inet_ntop()`. This name of our wrapper is chosen to be familiar with the libc underlying function. With this, also name functions that are about string representations of addresses `nm_inet_*()`, `nm_inet4_*()`, `nm_inet6_*()`. For example, `nm_inet_parse_str()`, `nm_inet_is_normalized()`. <<<< R() { git grep -l "$1" | xargs sed -i "s/\<$1\>/$2/g" } R NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX NM_CMP_DIRECT_IP4_ADDR_SAME_PREFIX R NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX R NM_UTILS_INET_ADDRSTRLEN NM_INET_ADDRSTRLEN R _nm_utils_inet4_ntop nm_inet4_ntop R _nm_utils_inet6_ntop nm_inet6_ntop R _nm_utils_ip4_get_default_prefix nm_ip4_addr_get_default_prefix R _nm_utils_ip4_get_default_prefix0 nm_ip4_addr_get_default_prefix0 R _nm_utils_ip4_netmask_to_prefix nm_ip4_addr_netmask_to_prefix R _nm_utils_ip4_prefix_to_netmask nm_ip4_addr_netmask_from_prefix R nm_utils_inet4_ntop_dup nm_inet4_ntop_dup R nm_utils_inet6_ntop_dup nm_inet6_ntop_dup R nm_utils_inet_ntop nm_inet_ntop R nm_utils_inet_ntop_dup nm_inet_ntop_dup R nm_utils_ip4_address_clear_host_address nm_ip4_addr_clear_host_address R nm_utils_ip4_address_is_link_local nm_ip4_addr_is_link_local R nm_utils_ip4_address_is_loopback nm_ip4_addr_is_loopback R nm_utils_ip4_address_is_zeronet nm_ip4_addr_is_zeronet R nm_utils_ip4_address_same_prefix nm_ip4_addr_same_prefix R nm_utils_ip4_address_same_prefix_cmp nm_ip4_addr_same_prefix_cmp R nm_utils_ip6_address_clear_host_address nm_ip6_addr_clear_host_address R nm_utils_ip6_address_same_prefix nm_ip6_addr_same_prefix R nm_utils_ip6_address_same_prefix_cmp nm_ip6_addr_same_prefix_cmp R nm_utils_ip6_is_ula nm_ip6_addr_is_ula R nm_utils_ip_address_same_prefix nm_ip_addr_same_prefix R nm_utils_ip_address_same_prefix_cmp nm_ip_addr_same_prefix_cmp R nm_utils_ip_is_site_local nm_ip_addr_is_site_local R nm_utils_ipaddr_is_normalized nm_inet_is_normalized R nm_utils_ipaddr_is_valid nm_inet_is_valid R nm_utils_ipx_address_clear_host_address nm_ip_addr_clear_host_address R nm_utils_parse_inaddr nm_inet_parse_str R nm_utils_parse_inaddr_bin nm_inet_parse_bin R nm_utils_parse_inaddr_bin_full nm_inet_parse_bin_full R nm_utils_parse_inaddr_prefix nm_inet_parse_with_prefix_str R nm_utils_parse_inaddr_prefix_bin nm_inet_parse_with_prefix_bin R test_nm_utils_ip6_address_same_prefix test_nm_ip_addr_same_prefix ./contrib/scripts/nm-code-format.sh -F
2022-08-19 13:15:20 +02:00
if (!nm_inet_parse_bin(AF_INET, ip[i], NULL, &in_a))
nm_assert_not_reached(); /* verify() already validated the IP addresses */
out[added++] = in_a;
}
return added;
}
static guint8
_bond_ns_ip6_target_to_platform(const char *value,
struct in6_addr out[static NM_BOND_MAX_ARP_TARGETS])
{
gs_free const char **ip = NULL;
struct in6_addr in6_a;
int i;
int added = 0;
ip = nm_utils_bond_option_ip_split(value);
if (!ip)
return added;
for (i = 0; ip[i]; i++) {
if (added > NM_BOND_MAX_ARP_TARGETS - 1)
break;
if (!nm_inet_parse_bin(AF_INET6, ip[i], NULL, &in6_a))
nm_assert_not_reached(); /* verify() already validated the IP addresses */
out[added++] = in6_a;
}
return added;
}
static int
_setting_bond_primary_opt_as_ifindex(NMSettingBond *s_bond)
{
const char *primary_str;
int ifindex = 0;
primary_str = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_PRIMARY);
if (primary_str != NULL)
ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, primary_str);
return ifindex;
}
static void
_platform_lnk_bond_init_from_setting(NMSettingBond *s_bond, NMPlatformLnkBond *props)
{
const char *opt_value;
#define _v_fcn(fcn, s_bond, opt) (fcn(nm_setting_bond_get_option_normalized((s_bond), (opt))))
#define _v_u8(s_bond, opt) _nm_setting_bond_opt_value_as_u8((s_bond), (opt))
#define _v_u16(s_bond, opt) _nm_setting_bond_opt_value_as_u16((s_bond), (opt))
#define _v_u32(s_bond, opt) _nm_setting_bond_opt_value_as_u32((s_bond), (opt))
#define _v_intbool(s_bond, opt) _nm_setting_bond_opt_value_as_intbool((s_bond), (opt))
*props = (NMPlatformLnkBond) {
.mode = _v_fcn(_nm_setting_bond_mode_from_string, s_bond, NM_SETTING_BOND_OPTION_MODE),
.primary = _setting_bond_primary_opt_as_ifindex(s_bond),
.miimon = _v_u32(s_bond, NM_SETTING_BOND_OPTION_MIIMON),
.updelay = _v_u32(s_bond, NM_SETTING_BOND_OPTION_UPDELAY),
.downdelay = _v_u32(s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY),
.arp_interval = _v_u32(s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL),
.resend_igmp = _v_u32(s_bond, NM_SETTING_BOND_OPTION_RESEND_IGMP),
.min_links = _v_u32(s_bond, NM_SETTING_BOND_OPTION_MIN_LINKS),
.lp_interval = _v_u32(s_bond, NM_SETTING_BOND_OPTION_LP_INTERVAL),
.packets_per_port = _v_u32(s_bond, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE),
.peer_notif_delay = _v_u32(s_bond, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY),
.arp_all_targets = _v_fcn(_nm_setting_bond_arp_all_targets_from_string,
s_bond,
NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS),
.arp_validate = _v_fcn(_nm_setting_bond_arp_validate_from_string,
s_bond,
NM_SETTING_BOND_OPTION_ARP_VALIDATE),
.ad_actor_sys_prio = _v_u16(s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO),
.ad_user_port_key = _v_u16(s_bond, NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY),
.primary_reselect = _v_fcn(_nm_setting_bond_primary_reselect_from_string,
s_bond,
NM_SETTING_BOND_OPTION_PRIMARY_RESELECT),
.fail_over_mac = _v_fcn(_nm_setting_bond_fail_over_mac_from_string,
s_bond,
NM_SETTING_BOND_OPTION_FAIL_OVER_MAC),
.xmit_hash_policy = _v_fcn(_nm_setting_bond_xmit_hash_policy_from_string,
s_bond,
NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY),
.num_grat_arp = _v_u8(s_bond, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP),
.all_ports_active = _v_u8(s_bond, NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE),
.arp_missed_max = _v_u8(s_bond, NM_SETTING_BOND_OPTION_ARP_MISSED_MAX),
.lacp_active = _v_fcn(_nm_setting_bond_lacp_active_from_string,
s_bond,
NM_SETTING_BOND_OPTION_LACP_ACTIVE),
.lacp_rate = _v_fcn(_nm_setting_bond_lacp_rate_from_string,
s_bond,
NM_SETTING_BOND_OPTION_LACP_RATE),
.ad_select = _v_fcn(_nm_setting_bond_ad_select_from_string,
s_bond,
NM_SETTING_BOND_OPTION_AD_SELECT),
.use_carrier = _v_intbool(s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER),
.tlb_dynamic_lb = _v_intbool(s_bond, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB),
};
nm_ether_addr_from_string(
&props->ad_actor_system,
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM));
opt_value = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
if (opt_value != NULL)
props->arp_ip_targets_num =
_bond_arp_ip_target_to_platform(opt_value, props->arp_ip_target);
opt_value = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_NS_IP6_TARGET);
if (opt_value != NULL)
props->ns_ip6_targets_num =
_bond_ns_ip6_target_to_platform(opt_value, props->ns_ip6_target);
props->miimon_has = !props->arp_interval && !props->arp_validate;
props->updelay_has = props->miimon_has && props->miimon;
props->downdelay_has = props->miimon_has && props->miimon;
props->peer_notif_delay_has = (props->miimon || props->arp_interval) && props->peer_notif_delay;
props->resend_igmp_has = props->resend_igmp != 1;
props->lp_interval_has = props->lp_interval != 1;
props->tlb_dynamic_lb_has = NM_IN_SET(props->mode, NM_BOND_MODE_TLB, NM_BOND_MODE_ALB);
props->lacp_active_has = NM_IN_SET(props->mode, NM_BOND_MODE_8023AD);
}
static void
_balance_slb_cb(NMBondManager *bond_manager, NMBondManagerEventType event_type, gpointer user_data)
{
NMDevice *device = user_data;
NMDeviceBond *self = NM_DEVICE_BOND(device);
nm_assert(NM_IS_DEVICE_BOND(self));
nm_assert(self->bond_manager == bond_manager);
switch (event_type) {
case NM_BOND_MANAGER_EVENT_TYPE_STATE:
switch (nm_bond_manager_get_state(bond_manager)) {
case NM_OPTION_BOOL_FALSE:
if (nm_device_get_state(device) <= NM_DEVICE_STATE_ACTIVATED) {
_LOGD(LOGD_BOND, "balance-slb: failed");
nm_device_state_changed(device,
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_CONFIG_FAILED);
}
return;
case NM_OPTION_BOOL_TRUE:
if (nm_device_get_state(device) <= NM_DEVICE_STATE_ACTIVATED
&& nm_device_devip_get_state(device, AF_UNSPEC) <= NM_DEVICE_IP_STATE_PENDING) {
nm_device_devip_set_state(device, AF_UNSPEC, NM_DEVICE_IP_STATE_READY, NULL);
}
return;
case NM_OPTION_BOOL_DEFAULT:
if (nm_device_get_state(device) <= NM_DEVICE_STATE_ACTIVATED
&& nm_device_devip_get_state(device, AF_UNSPEC) == NM_DEVICE_IP_STATE_READY) {
/* We are again busy. We can also go back to "pending" from "ready".
* If ip-config state is not yet complete, this will further delay it.
* Otherwise, it should have no effect. */
nm_device_devip_set_state(device, AF_UNSPEC, NM_DEVICE_IP_STATE_PENDING, NULL);
}
return;
}
nm_assert_not_reached();
return;
}
nm_assert_not_reached();
}
static void
_balance_slb_setup(NMDeviceBond *self, NMConnection *connection)
{
int ifindex = nm_device_get_ifindex(NM_DEVICE(self));
gboolean balance_slb = FALSE;
const char *uuid;
NMSettingBond *s_bond;
if (ifindex > 0 && connection && (s_bond = nm_connection_get_setting_bond(connection)))
balance_slb = _v_intbool(s_bond, NM_SETTING_BOND_OPTION_BALANCE_SLB);
if (!balance_slb) {
if (nm_clear_pointer(&self->bond_manager, nm_bond_manager_destroy)) {
_LOGD(LOGD_BOND, "balance-slb: stopped");
nm_device_devip_set_state(NM_DEVICE(self), AF_UNSPEC, NM_DEVICE_IP_STATE_NONE, NULL);
}
return;
}
uuid = nm_connection_get_uuid(connection);
if (self->bond_manager) {
if (nm_bond_manager_get_ifindex(self->bond_manager) == ifindex
&& nm_streq0(nm_bond_manager_get_connection_uuid(self->bond_manager), uuid)) {
_LOGD(LOGD_BOND, "balance-slb: reapply");
nm_bond_manager_reapply(self->bond_manager);
return;
}
nm_clear_pointer(&self->bond_manager, nm_bond_manager_destroy);
_LOGD(LOGD_BOND, "balance-slb: restart");
}
_LOGD(LOGD_BOND, "balance-slb: start");
if (nm_device_devip_get_state(NM_DEVICE(self), AF_UNSPEC) < NM_DEVICE_IP_STATE_PENDING)
nm_device_devip_set_state(NM_DEVICE(self), AF_UNSPEC, NM_DEVICE_IP_STATE_PENDING, NULL);
self->bond_manager = nm_bond_manager_new(nm_device_get_platform(NM_DEVICE(self)),
ifindex,
uuid,
_balance_slb_cb,
self);
nm_assert(nm_bond_manager_get_state(self->bond_manager) == NM_OPTION_BOOL_DEFAULT);
}
static NMActStageReturn
act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
{
NMDeviceBond *self = NM_DEVICE_BOND(device);
NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
NMConnection *connection;
NMSettingBond *s_bond;
NMPlatformLnkBond props;
int r;
int ifindex = nm_device_get_ifindex(device);
connection = nm_device_get_applied_connection(device);
g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE);
s_bond = nm_connection_get_setting_bond(connection);
g_return_val_if_fail(s_bond, NM_ACT_STAGE_RETURN_FAILURE);
if (nm_device_managed_type_is_external(device))
return NM_ACT_STAGE_RETURN_SUCCESS;
_balance_slb_setup(self, connection);
if (nm_device_managed_type_is_external_or_assume(device))
return NM_ACT_STAGE_RETURN_SUCCESS;
_platform_lnk_bond_init_from_setting(s_bond, &props);
/* Interface must be down to set bond options */
nm_device_take_down(device, TRUE);
r = nm_platform_link_bond_change(nm_device_get_platform(device), ifindex, &props);
if (r < 0) {
ret = NM_ACT_STAGE_RETURN_FAILURE;
NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
} else {
if (!nm_device_hw_addr_set_cloned(device, nm_device_get_applied_connection(device), FALSE))
ret = NM_ACT_STAGE_RETURN_FAILURE;
}
/* This is a workaround because netlink do not support ifname as primary */
set_bond_attr_or_default(device, s_bond, NM_SETTING_BOND_OPTION_PRIMARY);
nm_device_bring_up(device);
return ret;
}
static void
commit_port_options(NMDevice *bond_device, NMDevice *port, NMSettingBondPort *s_port)
{
NMBondMode mode = NM_BOND_MODE_UNKNOWN;
const char *value;
NMSettingBond *s_bond;
gint32 prio;
gboolean prio_has;
s_bond = nm_device_get_applied_setting(bond_device, NM_TYPE_SETTING_BOND);
if (s_bond) {
value = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_MODE);
mode = _nm_setting_bond_mode_from_string(value);
}
prio = s_port ? nm_setting_bond_port_get_prio(s_port) : NM_BOND_PORT_PRIO_DEF;
if (prio != 0) {
/* The profile explicitly sets the priority. No matter what, we try to set it
* in netlink. */
prio_has = TRUE;
} else if (!NM_IN_SET(mode, NM_BOND_MODE_ACTIVEBACKUP, NM_BOND_MODE_TLB, NM_BOND_MODE_ALB)) {
/* The priority only is configurable with certain modes. If we don't have
* one of those modes, don't try to set the priority explicitly to zero. */
prio_has = FALSE;
} else if (nm_platform_kernel_support_get_full(
NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BOND_PORT_PRIO,
FALSE)
== NM_OPTION_BOOL_TRUE) {
/* We can only detect support if we have it. We cannot detect lack of support if
* we don't have it.
*
* But we did explicitly detect support, so explicitly set the prio to zero. */
prio_has = TRUE;
} else {
/* We either have an unsuitable mode or didn't detect kernel support for the
* priority. Don't explicitly set priority to zero. It is already the default,
* so it shouldn't be necessary. */
prio_has = FALSE;
}
nm_platform_link_change(nm_device_get_platform(port),
nm_device_get_ifindex(port),
NULL,
&((NMPlatformLinkBondPort) {
.queue_id = s_port ? nm_setting_bond_port_get_queue_id(s_port)
: NM_BOND_PORT_QUEUE_ID_DEF,
.prio = prio_has ? prio : 0,
.prio_has = prio_has,
}),
NULL,
0);
}
static NMTernary
attach_port(NMDevice *device,
NMDevice *port,
NMConnection *connection,
gboolean configure,
GCancellable *cancellable,
NMDeviceAttachPortCallback callback,
gpointer user_data)
{
NMDeviceBond *self = NM_DEVICE_BOND(device);
NMSettingBondPort *s_port;
nm_device_controller_check_port_physical_port(device, port, LOGD_BOND);
if (configure) {
gboolean success;
nm_device_take_down(port, TRUE);
success = nm_platform_link_attach_port(nm_device_get_platform(device),
nm_device_get_ip_ifindex(device),
nm_device_get_ip_ifindex(port));
nm_device_bring_up(port);
2012-11-14 14:05:30 -06:00
if (!success) {
_LOGI(LOGD_BOND, "attaching bond port %s: failed", nm_device_get_ip_iface(port));
return FALSE;
}
s_port = _nm_connection_get_setting(connection, NM_TYPE_SETTING_BOND_PORT);
commit_port_options(device, port, s_port);
_LOGI(LOGD_BOND, "attached bond port %s", nm_device_get_ip_iface(port));
} else
_LOGI(LOGD_BOND, "bond port %s was attached", nm_device_get_ip_iface(port));
return TRUE;
}
static NMTernary
detach_port(NMDevice *device,
NMDevice *port,
gboolean configure,
GCancellable *cancellable,
NMDeviceAttachPortCallback callback,
gpointer user_data)
{
NMDeviceBond *self = NM_DEVICE_BOND(device);
gboolean success;
gs_free char *address = NULL;
int ifindex_port;
int ifindex;
if (configure) {
ifindex = nm_device_get_ifindex(device);
if (ifindex <= 0 || !nm_platform_link_get(nm_device_get_platform(device), ifindex))
configure = FALSE;
}
ifindex_port = nm_device_get_ip_ifindex(port);
if (ifindex_port <= 0)
_LOGD(LOGD_BOND, "bond port %s is already detached", nm_device_get_ip_iface(port));
if (configure) {
NMConnection *applied;
NMSettingWired *s_wired;
const char *cloned_mac;
address = g_strdup(nm_device_get_hw_address(device));
if (ifindex_port > 0) {
success = nm_platform_link_release_port(nm_device_get_platform(device),
nm_device_get_ip_ifindex(device),
ifindex_port);
device: fix crash releasing destroyed slave I encountered this on a WIP branch, but I think it can happen under regular conditions. I think there is no error condition here, and we should do nothing if we have no ifindex. <debug> [1561653068.2192] platform: signal: link removed: 1699: test1p <DOWN;broadcast,multicast> mtu 1500 master 1698 arp 1 veth* init addrgenmode none addr D6:14:45:97:06:75 brd FF:FF:FF:FF:FF:FF driver veth rx:0,0 tx:38,5606 ... <info> [1561653068.2617] device (test1): state change: activated -> unmanaged (reason 'unmanaged', sys-iface-state: 'removed') ... <trace> [1561653068.2635] device[0x564058c73750] (test1p): sys-iface-state: external -> removed <debug> [1561653068.2635] device[0x564058c73750] (test1p): unrealize (ifindex 1699) <debug> [1561653068.2636] device[0x564058c73750] (test1p): parent: clear <trace> [1561653068.2636] device[0x564058b98eb0] (vethbr): mtu: commit-mtu... <debug> [1561653068.2639] device[0x564058c73750] (test1p): unmanaged: flags set to [platform-init,!sleeping,!by-type,!user-explicit,!user-settings,!user-udev,!is-slave=0x10/0x1479/unmanaged/unrealized], set-unmanaged [platform-init=0x10]) <debug> [1561653068.2639] device[0x564058c73750] (test1p): unmanaged: flags set to [platform-init,!sleeping,!user-settings=0x10/0x51/unmanaged/unrealized], forget [parent,by-type,user-explicit,user-udev,external-down,is-slave=0x1c2c]) <info> [1561653068.2639] device (test1p): state change: activated -> unmanaged (reason 'unmanaged', sys-iface-state: 'removed') <debug> [1561653068.2640] device[0x564058c73750] (test1p): deactivating device (reason 'unmanaged') [3] <trace> [1561653068.2640] device[0x564058c73750] (test1p): ip4-state: set to 0 (none) <trace> [1561653068.2640] device[0x564058c73750] (test1p): ip6-state: set to 0 (none) <trace> [1561653068.2640] device[0x564058c73750] (test1p): remove_pending_action (0): 'dhcp6' not pending (expected) <trace> [1561653068.2640] device[0x564058c73750] (test1p): remove_pending_action (0): 'autoconf6' not pending (expected) <debug> [1561653068.2640] rules-manager: sync <debug> [1561653068.2640] device[0x564058c73750] (test1p): set metered value 0 <debug> [1561653068.2641] device[0x564058c73750] (test1p): ip4-config: update (commit=1, new-config=(nil)) <debug> [1561653068.2641] device[0x564058c73750] (test1p): ip6-config: update (commit=1, new-config=(nil)) <debug> [1561653068.2644] device[0x564058b98eb0] (vethbr): slave test1p state change 100 (activated) -> 10 (unmanaged) <trace> [1561653068.2644] device[0x564058b98eb0] (vethbr): master: release one slave 0x564058c73750/test1p ((src/platform/nm-platform.c:2002)): assertion '<dropped>' failed backtrace: ... #3 0x0000564057fb713e _nm_g_return_if_fail_warning (NetworkManager) #4 0x000056405808b37c release_slave (NetworkManager) #5 0x0000564058079aef nm_device_master_release_one_slave (NetworkManager) #6 0x00005640580844d7 slave_state_changed (NetworkManager) #7 0x00007efc24833fae ffi_call_unix64 (libffi.so.6) #8 0x00007efc2483396f ffi_call (libffi.so.6) #9 0x00007efc29b836e5 g_cclosure_marshal_generic (libgobject-2.0.so.0) #10 0x00007efc29b82c1d g_closure_invoke (libgobject-2.0.so.0) #11 0x00007efc29b96173 signal_emit_unlocked_R (libgobject-2.0.so.0) #12 0x00007efc29b9f29a g_signal_emit_valist (libgobject-2.0.so.0) #13 0x00007efc29b9f893 g_signal_emit (libgobject-2.0.so.0) #14 0x000056405807ab20 _set_state_full (NetworkManager) #15 0x000056405807d803 nm_device_unrealize (NetworkManager) #16 0x0000564057f6072c _platform_link_cb_idle (NetworkManager) #17 0x00007efc296a01db g_idle_dispatch (libglib-2.0.so.0) ...
2019-06-28 17:31:40 +02:00
if (success) {
_LOGI(LOGD_BOND, "detached bond port %s", nm_device_get_ip_iface(port));
device: fix crash releasing destroyed slave I encountered this on a WIP branch, but I think it can happen under regular conditions. I think there is no error condition here, and we should do nothing if we have no ifindex. <debug> [1561653068.2192] platform: signal: link removed: 1699: test1p <DOWN;broadcast,multicast> mtu 1500 master 1698 arp 1 veth* init addrgenmode none addr D6:14:45:97:06:75 brd FF:FF:FF:FF:FF:FF driver veth rx:0,0 tx:38,5606 ... <info> [1561653068.2617] device (test1): state change: activated -> unmanaged (reason 'unmanaged', sys-iface-state: 'removed') ... <trace> [1561653068.2635] device[0x564058c73750] (test1p): sys-iface-state: external -> removed <debug> [1561653068.2635] device[0x564058c73750] (test1p): unrealize (ifindex 1699) <debug> [1561653068.2636] device[0x564058c73750] (test1p): parent: clear <trace> [1561653068.2636] device[0x564058b98eb0] (vethbr): mtu: commit-mtu... <debug> [1561653068.2639] device[0x564058c73750] (test1p): unmanaged: flags set to [platform-init,!sleeping,!by-type,!user-explicit,!user-settings,!user-udev,!is-slave=0x10/0x1479/unmanaged/unrealized], set-unmanaged [platform-init=0x10]) <debug> [1561653068.2639] device[0x564058c73750] (test1p): unmanaged: flags set to [platform-init,!sleeping,!user-settings=0x10/0x51/unmanaged/unrealized], forget [parent,by-type,user-explicit,user-udev,external-down,is-slave=0x1c2c]) <info> [1561653068.2639] device (test1p): state change: activated -> unmanaged (reason 'unmanaged', sys-iface-state: 'removed') <debug> [1561653068.2640] device[0x564058c73750] (test1p): deactivating device (reason 'unmanaged') [3] <trace> [1561653068.2640] device[0x564058c73750] (test1p): ip4-state: set to 0 (none) <trace> [1561653068.2640] device[0x564058c73750] (test1p): ip6-state: set to 0 (none) <trace> [1561653068.2640] device[0x564058c73750] (test1p): remove_pending_action (0): 'dhcp6' not pending (expected) <trace> [1561653068.2640] device[0x564058c73750] (test1p): remove_pending_action (0): 'autoconf6' not pending (expected) <debug> [1561653068.2640] rules-manager: sync <debug> [1561653068.2640] device[0x564058c73750] (test1p): set metered value 0 <debug> [1561653068.2641] device[0x564058c73750] (test1p): ip4-config: update (commit=1, new-config=(nil)) <debug> [1561653068.2641] device[0x564058c73750] (test1p): ip6-config: update (commit=1, new-config=(nil)) <debug> [1561653068.2644] device[0x564058b98eb0] (vethbr): slave test1p state change 100 (activated) -> 10 (unmanaged) <trace> [1561653068.2644] device[0x564058b98eb0] (vethbr): master: release one slave 0x564058c73750/test1p ((src/platform/nm-platform.c:2002)): assertion '<dropped>' failed backtrace: ... #3 0x0000564057fb713e _nm_g_return_if_fail_warning (NetworkManager) #4 0x000056405808b37c release_slave (NetworkManager) #5 0x0000564058079aef nm_device_master_release_one_slave (NetworkManager) #6 0x00005640580844d7 slave_state_changed (NetworkManager) #7 0x00007efc24833fae ffi_call_unix64 (libffi.so.6) #8 0x00007efc2483396f ffi_call (libffi.so.6) #9 0x00007efc29b836e5 g_cclosure_marshal_generic (libgobject-2.0.so.0) #10 0x00007efc29b82c1d g_closure_invoke (libgobject-2.0.so.0) #11 0x00007efc29b96173 signal_emit_unlocked_R (libgobject-2.0.so.0) #12 0x00007efc29b9f29a g_signal_emit_valist (libgobject-2.0.so.0) #13 0x00007efc29b9f893 g_signal_emit (libgobject-2.0.so.0) #14 0x000056405807ab20 _set_state_full (NetworkManager) #15 0x000056405807d803 nm_device_unrealize (NetworkManager) #16 0x0000564057f6072c _platform_link_cb_idle (NetworkManager) #17 0x00007efc296a01db g_idle_dispatch (libglib-2.0.so.0) ...
2019-06-28 17:31:40 +02:00
} else {
_LOGW(LOGD_BOND, "failed to detach bond port %s", nm_device_get_ip_iface(port));
}
device: fix crash releasing destroyed slave I encountered this on a WIP branch, but I think it can happen under regular conditions. I think there is no error condition here, and we should do nothing if we have no ifindex. <debug> [1561653068.2192] platform: signal: link removed: 1699: test1p <DOWN;broadcast,multicast> mtu 1500 master 1698 arp 1 veth* init addrgenmode none addr D6:14:45:97:06:75 brd FF:FF:FF:FF:FF:FF driver veth rx:0,0 tx:38,5606 ... <info> [1561653068.2617] device (test1): state change: activated -> unmanaged (reason 'unmanaged', sys-iface-state: 'removed') ... <trace> [1561653068.2635] device[0x564058c73750] (test1p): sys-iface-state: external -> removed <debug> [1561653068.2635] device[0x564058c73750] (test1p): unrealize (ifindex 1699) <debug> [1561653068.2636] device[0x564058c73750] (test1p): parent: clear <trace> [1561653068.2636] device[0x564058b98eb0] (vethbr): mtu: commit-mtu... <debug> [1561653068.2639] device[0x564058c73750] (test1p): unmanaged: flags set to [platform-init,!sleeping,!by-type,!user-explicit,!user-settings,!user-udev,!is-slave=0x10/0x1479/unmanaged/unrealized], set-unmanaged [platform-init=0x10]) <debug> [1561653068.2639] device[0x564058c73750] (test1p): unmanaged: flags set to [platform-init,!sleeping,!user-settings=0x10/0x51/unmanaged/unrealized], forget [parent,by-type,user-explicit,user-udev,external-down,is-slave=0x1c2c]) <info> [1561653068.2639] device (test1p): state change: activated -> unmanaged (reason 'unmanaged', sys-iface-state: 'removed') <debug> [1561653068.2640] device[0x564058c73750] (test1p): deactivating device (reason 'unmanaged') [3] <trace> [1561653068.2640] device[0x564058c73750] (test1p): ip4-state: set to 0 (none) <trace> [1561653068.2640] device[0x564058c73750] (test1p): ip6-state: set to 0 (none) <trace> [1561653068.2640] device[0x564058c73750] (test1p): remove_pending_action (0): 'dhcp6' not pending (expected) <trace> [1561653068.2640] device[0x564058c73750] (test1p): remove_pending_action (0): 'autoconf6' not pending (expected) <debug> [1561653068.2640] rules-manager: sync <debug> [1561653068.2640] device[0x564058c73750] (test1p): set metered value 0 <debug> [1561653068.2641] device[0x564058c73750] (test1p): ip4-config: update (commit=1, new-config=(nil)) <debug> [1561653068.2641] device[0x564058c73750] (test1p): ip6-config: update (commit=1, new-config=(nil)) <debug> [1561653068.2644] device[0x564058b98eb0] (vethbr): slave test1p state change 100 (activated) -> 10 (unmanaged) <trace> [1561653068.2644] device[0x564058b98eb0] (vethbr): master: release one slave 0x564058c73750/test1p ((src/platform/nm-platform.c:2002)): assertion '<dropped>' failed backtrace: ... #3 0x0000564057fb713e _nm_g_return_if_fail_warning (NetworkManager) #4 0x000056405808b37c release_slave (NetworkManager) #5 0x0000564058079aef nm_device_master_release_one_slave (NetworkManager) #6 0x00005640580844d7 slave_state_changed (NetworkManager) #7 0x00007efc24833fae ffi_call_unix64 (libffi.so.6) #8 0x00007efc2483396f ffi_call (libffi.so.6) #9 0x00007efc29b836e5 g_cclosure_marshal_generic (libgobject-2.0.so.0) #10 0x00007efc29b82c1d g_closure_invoke (libgobject-2.0.so.0) #11 0x00007efc29b96173 signal_emit_unlocked_R (libgobject-2.0.so.0) #12 0x00007efc29b9f29a g_signal_emit_valist (libgobject-2.0.so.0) #13 0x00007efc29b9f893 g_signal_emit (libgobject-2.0.so.0) #14 0x000056405807ab20 _set_state_full (NetworkManager) #15 0x000056405807d803 nm_device_unrealize (NetworkManager) #16 0x0000564057f6072c _platform_link_cb_idle (NetworkManager) #17 0x00007efc296a01db g_idle_dispatch (libglib-2.0.so.0) ...
2019-06-28 17:31:40 +02:00
}
if ((applied = nm_device_get_applied_connection(device))
&& ((s_wired = nm_connection_get_setting_wired(applied)))
&& ((cloned_mac = nm_setting_wired_get_cloned_mac_address(s_wired)))) {
/* When the last port is released the bond MAC will be set to a random
* value by kernel; if we have set a cloned-mac-address, we need to
* restore it to the previous value. */
nm_platform_process_events(nm_device_get_platform(device));
if (nm_device_update_hw_address(device))
nm_device_hw_addr_set(device, address, "restore", FALSE);
}
/* Kernel bonding code "closes" the port when releasing it, (which clears
* IFF_UP), so we must bring it back up here to ensure carrier changes and
* other state is noticed by the now-released port.
*/
if (ifindex_port > 0) {
if (!nm_device_bring_up(port))
_LOGW(LOGD_BOND, "detached bond port could not be brought up.");
device: fix crash releasing destroyed slave I encountered this on a WIP branch, but I think it can happen under regular conditions. I think there is no error condition here, and we should do nothing if we have no ifindex. <debug> [1561653068.2192] platform: signal: link removed: 1699: test1p <DOWN;broadcast,multicast> mtu 1500 master 1698 arp 1 veth* init addrgenmode none addr D6:14:45:97:06:75 brd FF:FF:FF:FF:FF:FF driver veth rx:0,0 tx:38,5606 ... <info> [1561653068.2617] device (test1): state change: activated -> unmanaged (reason 'unmanaged', sys-iface-state: 'removed') ... <trace> [1561653068.2635] device[0x564058c73750] (test1p): sys-iface-state: external -> removed <debug> [1561653068.2635] device[0x564058c73750] (test1p): unrealize (ifindex 1699) <debug> [1561653068.2636] device[0x564058c73750] (test1p): parent: clear <trace> [1561653068.2636] device[0x564058b98eb0] (vethbr): mtu: commit-mtu... <debug> [1561653068.2639] device[0x564058c73750] (test1p): unmanaged: flags set to [platform-init,!sleeping,!by-type,!user-explicit,!user-settings,!user-udev,!is-slave=0x10/0x1479/unmanaged/unrealized], set-unmanaged [platform-init=0x10]) <debug> [1561653068.2639] device[0x564058c73750] (test1p): unmanaged: flags set to [platform-init,!sleeping,!user-settings=0x10/0x51/unmanaged/unrealized], forget [parent,by-type,user-explicit,user-udev,external-down,is-slave=0x1c2c]) <info> [1561653068.2639] device (test1p): state change: activated -> unmanaged (reason 'unmanaged', sys-iface-state: 'removed') <debug> [1561653068.2640] device[0x564058c73750] (test1p): deactivating device (reason 'unmanaged') [3] <trace> [1561653068.2640] device[0x564058c73750] (test1p): ip4-state: set to 0 (none) <trace> [1561653068.2640] device[0x564058c73750] (test1p): ip6-state: set to 0 (none) <trace> [1561653068.2640] device[0x564058c73750] (test1p): remove_pending_action (0): 'dhcp6' not pending (expected) <trace> [1561653068.2640] device[0x564058c73750] (test1p): remove_pending_action (0): 'autoconf6' not pending (expected) <debug> [1561653068.2640] rules-manager: sync <debug> [1561653068.2640] device[0x564058c73750] (test1p): set metered value 0 <debug> [1561653068.2641] device[0x564058c73750] (test1p): ip4-config: update (commit=1, new-config=(nil)) <debug> [1561653068.2641] device[0x564058c73750] (test1p): ip6-config: update (commit=1, new-config=(nil)) <debug> [1561653068.2644] device[0x564058b98eb0] (vethbr): slave test1p state change 100 (activated) -> 10 (unmanaged) <trace> [1561653068.2644] device[0x564058b98eb0] (vethbr): master: release one slave 0x564058c73750/test1p ((src/platform/nm-platform.c:2002)): assertion '<dropped>' failed backtrace: ... #3 0x0000564057fb713e _nm_g_return_if_fail_warning (NetworkManager) #4 0x000056405808b37c release_slave (NetworkManager) #5 0x0000564058079aef nm_device_master_release_one_slave (NetworkManager) #6 0x00005640580844d7 slave_state_changed (NetworkManager) #7 0x00007efc24833fae ffi_call_unix64 (libffi.so.6) #8 0x00007efc2483396f ffi_call (libffi.so.6) #9 0x00007efc29b836e5 g_cclosure_marshal_generic (libgobject-2.0.so.0) #10 0x00007efc29b82c1d g_closure_invoke (libgobject-2.0.so.0) #11 0x00007efc29b96173 signal_emit_unlocked_R (libgobject-2.0.so.0) #12 0x00007efc29b9f29a g_signal_emit_valist (libgobject-2.0.so.0) #13 0x00007efc29b9f893 g_signal_emit (libgobject-2.0.so.0) #14 0x000056405807ab20 _set_state_full (NetworkManager) #15 0x000056405807d803 nm_device_unrealize (NetworkManager) #16 0x0000564057f6072c _platform_link_cb_idle (NetworkManager) #17 0x00007efc296a01db g_idle_dispatch (libglib-2.0.so.0) ...
2019-06-28 17:31:40 +02:00
}
} else {
if (ifindex_port > 0) {
_LOGI(LOGD_BOND, "bond port %s was detached", nm_device_get_ip_iface(port));
device: fix crash releasing destroyed slave I encountered this on a WIP branch, but I think it can happen under regular conditions. I think there is no error condition here, and we should do nothing if we have no ifindex. <debug> [1561653068.2192] platform: signal: link removed: 1699: test1p <DOWN;broadcast,multicast> mtu 1500 master 1698 arp 1 veth* init addrgenmode none addr D6:14:45:97:06:75 brd FF:FF:FF:FF:FF:FF driver veth rx:0,0 tx:38,5606 ... <info> [1561653068.2617] device (test1): state change: activated -> unmanaged (reason 'unmanaged', sys-iface-state: 'removed') ... <trace> [1561653068.2635] device[0x564058c73750] (test1p): sys-iface-state: external -> removed <debug> [1561653068.2635] device[0x564058c73750] (test1p): unrealize (ifindex 1699) <debug> [1561653068.2636] device[0x564058c73750] (test1p): parent: clear <trace> [1561653068.2636] device[0x564058b98eb0] (vethbr): mtu: commit-mtu... <debug> [1561653068.2639] device[0x564058c73750] (test1p): unmanaged: flags set to [platform-init,!sleeping,!by-type,!user-explicit,!user-settings,!user-udev,!is-slave=0x10/0x1479/unmanaged/unrealized], set-unmanaged [platform-init=0x10]) <debug> [1561653068.2639] device[0x564058c73750] (test1p): unmanaged: flags set to [platform-init,!sleeping,!user-settings=0x10/0x51/unmanaged/unrealized], forget [parent,by-type,user-explicit,user-udev,external-down,is-slave=0x1c2c]) <info> [1561653068.2639] device (test1p): state change: activated -> unmanaged (reason 'unmanaged', sys-iface-state: 'removed') <debug> [1561653068.2640] device[0x564058c73750] (test1p): deactivating device (reason 'unmanaged') [3] <trace> [1561653068.2640] device[0x564058c73750] (test1p): ip4-state: set to 0 (none) <trace> [1561653068.2640] device[0x564058c73750] (test1p): ip6-state: set to 0 (none) <trace> [1561653068.2640] device[0x564058c73750] (test1p): remove_pending_action (0): 'dhcp6' not pending (expected) <trace> [1561653068.2640] device[0x564058c73750] (test1p): remove_pending_action (0): 'autoconf6' not pending (expected) <debug> [1561653068.2640] rules-manager: sync <debug> [1561653068.2640] device[0x564058c73750] (test1p): set metered value 0 <debug> [1561653068.2641] device[0x564058c73750] (test1p): ip4-config: update (commit=1, new-config=(nil)) <debug> [1561653068.2641] device[0x564058c73750] (test1p): ip6-config: update (commit=1, new-config=(nil)) <debug> [1561653068.2644] device[0x564058b98eb0] (vethbr): slave test1p state change 100 (activated) -> 10 (unmanaged) <trace> [1561653068.2644] device[0x564058b98eb0] (vethbr): master: release one slave 0x564058c73750/test1p ((src/platform/nm-platform.c:2002)): assertion '<dropped>' failed backtrace: ... #3 0x0000564057fb713e _nm_g_return_if_fail_warning (NetworkManager) #4 0x000056405808b37c release_slave (NetworkManager) #5 0x0000564058079aef nm_device_master_release_one_slave (NetworkManager) #6 0x00005640580844d7 slave_state_changed (NetworkManager) #7 0x00007efc24833fae ffi_call_unix64 (libffi.so.6) #8 0x00007efc2483396f ffi_call (libffi.so.6) #9 0x00007efc29b836e5 g_cclosure_marshal_generic (libgobject-2.0.so.0) #10 0x00007efc29b82c1d g_closure_invoke (libgobject-2.0.so.0) #11 0x00007efc29b96173 signal_emit_unlocked_R (libgobject-2.0.so.0) #12 0x00007efc29b9f29a g_signal_emit_valist (libgobject-2.0.so.0) #13 0x00007efc29b9f893 g_signal_emit (libgobject-2.0.so.0) #14 0x000056405807ab20 _set_state_full (NetworkManager) #15 0x000056405807d803 nm_device_unrealize (NetworkManager) #16 0x0000564057f6072c _platform_link_cb_idle (NetworkManager) #17 0x00007efc296a01db g_idle_dispatch (libglib-2.0.so.0) ...
2019-06-28 17:31:40 +02:00
}
}
return TRUE;
}
static gboolean
create_and_realize(NMDevice *device,
NMConnection *connection,
NMDevice *parent,
const NMPlatformLink **out_plink,
GError **error)
{
const char *iface = nm_device_get_iface(device);
NMSettingBond *s_bond;
NMPlatformLnkBond props;
int r;
g_assert(iface);
s_bond = nm_connection_get_setting_bond(connection);
nm_assert(s_bond);
_platform_lnk_bond_init_from_setting(s_bond, &props);
r = nm_platform_link_bond_add(nm_device_get_platform(device), iface, &props, out_plink);
if (r < 0) {
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_CREATION_FAILED,
"Failed to create bond interface '%s' for '%s': %s",
iface,
nm_connection_get_id(connection),
nm_strerror(r));
return FALSE;
}
return TRUE;
}
static gboolean
can_reapply_change(NMDevice *device,
const char *setting_name,
NMSetting *s_old,
NMSetting *s_new,
GHashTable *diffs,
GError **error)
{
NMDeviceClass *device_class;
/* Only handle bond setting here, delegate other settings to parent class */
if (nm_streq(setting_name, NM_SETTING_BOND_SETTING_NAME)) {
NMSettingBond *s_a = NM_SETTING_BOND(s_old);
NMSettingBond *s_b = NM_SETTING_BOND(s_new);
const char **option_list;
if (!nm_device_hash_check_invalid_keys(diffs,
NM_SETTING_BOND_SETTING_NAME,
error,
NM_SETTING_BOND_OPTIONS))
return FALSE;
option_list = nm_setting_bond_get_valid_options(NULL);
for (; *option_list; ++option_list) {
const char *name = *option_list;
/* We support changes to these */
if (NM_IN_STRSET(name, OPTIONS_REAPPLY_FULL, NM_SETTING_BOND_OPTION_BALANCE_SLB))
continue;
/* Reject any other changes */
if (!nm_streq0(nm_setting_bond_get_option_normalized(s_a, name),
nm_setting_bond_get_option_normalized(s_b, name))) {
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
"Can't reapply '%s' bond option",
name);
return FALSE;
}
}
return TRUE;
}
device_class = NM_DEVICE_CLASS(nm_device_bond_parent_class);
return device_class->can_reapply_change(device, setting_name, s_old, s_new, diffs, error);
}
static void
reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_new)
{
NMDeviceBond *self = NM_DEVICE_BOND(device);
NMSettingBond *s_bond;
const char *value;
NMBondMode mode;
NM_DEVICE_CLASS(nm_device_bond_parent_class)->reapply_connection(device, con_old, con_new);
_LOGD(LOGD_BOND, "reapplying bond settings");
s_bond = nm_connection_get_setting_bond(con_new);
g_return_if_fail(s_bond);
value = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_MODE);
mode = _nm_setting_bond_mode_from_string(value);
g_return_if_fail(mode != NM_BOND_MODE_UNKNOWN);
/* Below we set only the bond options that kernel allows one to modify
* while keeping the bond interface up */
set_bond_arp_ip_targets(device, s_bond);
set_bond_attrs_or_default(device, s_bond, NM_MAKE_STRV(OPTIONS_REAPPLY_SUBSET));
_balance_slb_setup(self, con_new);
}
static void
deactivate(NMDevice *device)
{
NMDeviceBond *self = NM_DEVICE_BOND(device);
_balance_slb_setup(self, NULL);
}
/*****************************************************************************/
bonding: send ARP announcement on bonding-slb link/carrier down When a bond in balance-slb is created, the ports are enabled or disabled based on carrier and link state. If the link/carrier goes down, the port becomes disabled and we must make sure the MAC tables of the switches are updated properly so the traffic is redirected. In order to solve this, we send a GARP or RARP broadcast packet on the bond. This fix cover 3 different balance-slb scenarios. Scenario 1: The bond in balance-slb mode has IPv4 address configured and some ports connected. Here the bond is acting like active-backup as the packets will always have as source MAC the address of the bond interface. When a port goes down, NetworkManager will send a GARP broadcast announcing the address configured on the bond with the MAC address configured on the port. Scenario 2: The bond in balance-slb mode is connected to a bridge and has some ports connected. The bridge has IPv4 configured. When a port goes down, NetworkManager will send a GARP broadcast announcing the address configured on the bridge with the MAC address configured on the port. Scenario 3: The bond in balance-slb mode is connected to a bridge and has some ports connected. The bridge does not have IP configuration and therefore everything is L2. When a port goes down, NetworkManager will query the FDB table and filter the entries by the ones belonging to the bridge and the bond ifindexes. Then, it will send a RARP broadcast announcing every learned MAC address from FDB. Fixes: e9268e392418 ('firewall: add mlag firewall utils for multi chassis link aggregation (MLAG) for bonding-slb')
2024-11-07 09:10:20 +01:00
gboolean
nm_device_bond_is_slb(NMDevice *device)
{
NMConnection *connection;
NMSettingBond *s_bond;
connection = nm_device_get_applied_connection(device);
if (!connection)
return FALSE;
s_bond = nm_connection_get_setting_bond(connection);
if (!s_bond)
return FALSE;
if (!_nm_setting_bond_opt_value_as_intbool(s_bond, NM_SETTING_BOND_OPTION_BALANCE_SLB))
return FALSE;
return TRUE;
}
gboolean
nm_device_bond_announce_ports_on_slb(NMDevice *controller, NMDevice *port)
{
NMDeviceBond *self = NM_DEVICE_BOND(controller);
int port_ifindex = nm_device_get_ifindex(port);
int controller_ifindex = nm_device_get_ifindex(controller);
NML3Cfg *l3cfg = nm_device_get_l3cfg(controller);
NMDevice *bond_controller = nm_device_get_controller(controller);
NML3Cfg *bridge_l3cfg;
gs_free in_addr_t *addrs_array = NULL;
gsize addrs_len;
addrs_array = nm_l3cfg_get_configured_ip4_addresses(l3cfg, &addrs_len);
if (addrs_len > 0) {
/* the bond has IPs configured, it is not attached to a
* bridge then. */
if (!nm_bond_manager_send_arp(controller_ifindex,
-1,
nm_device_get_platform(port),
addrs_array,
addrs_len)) {
_LOGT(LOGD_BOND,
"failed to send gARP on port %s (ifindex %d)",
nm_device_get_iface(port),
port_ifindex);
return FALSE;
}
} else if (bond_controller
&& nm_device_get_device_type(bond_controller) == NM_DEVICE_TYPE_BRIDGE) {
/* the bond is attached to a bridge, firts let's check if the bridge has IP
* configuration. */
bridge_l3cfg = nm_device_get_l3cfg(bond_controller);
addrs_array = nm_l3cfg_get_configured_ip4_addresses(bridge_l3cfg, &addrs_len);
if (addrs_len > 0) {
/* the bridge has IPs configured, announcing them on the bond */
if (!nm_bond_manager_send_arp(controller_ifindex,
-1,
nm_device_get_platform(port),
addrs_array,
addrs_len)) {
_LOGT(LOGD_BOND,
"failed to send gARP on port %s (ifindex %d) on behalf of bridge",
nm_device_get_iface(port),
port_ifindex);
return FALSE;
}
}
/* we are going to ARP probe the content of the FDB table */
if (!nm_bond_manager_send_arp(controller_ifindex,
nm_device_get_ifindex(bond_controller),
nm_device_get_platform(port),
NULL,
0)) {
_LOGT(LOGD_BOND, "failed to send ARP probing with content of FDB table");
return FALSE;
}
}
return TRUE;
}
/*****************************************************************************/
static void
nm_device_bond_init(NMDeviceBond *self)
{
nm_assert(nm_device_is_controller(NM_DEVICE(self)));
}
core/dbus: rework D-Bus implementation to use lower layer GDBusConnection API Previously, we used the generated GDBusInterfaceSkeleton types and glued them via the NMExportedObject base class to our NM types. We also used GDBusObjectManagerServer. Don't do that anymore. The resulting code was more complicated despite (or because?) using generated classes. It was hard to understand, complex, had ordering-issues, and had a runtime and memory overhead. This patch refactors this entirely and uses the lower layer API GDBusConnection directly. It replaces the generated code, GDBusInterfaceSkeleton, and GDBusObjectManagerServer. All this is now done by NMDbusObject and NMDBusManager and static descriptor instances of type GDBusInterfaceInfo. This adds a net plus of more then 1300 lines of hand written code. I claim that this implementation is easier to understand. Note that previously we also required extensive and complex glue code to bind our objects to the generated skeleton objects. Instead, now glue our objects directly to GDBusConnection. The result is more immediate and gets rid of layers of code in between. Now that the D-Bus glue us more under our control, we can address issus and bottlenecks better, instead of adding code to bend the generated skeletons to our needs. Note that the current implementation now only supports one D-Bus connection. That was effectively the case already, although there were places (and still are) where the code pretends it could also support connections from a private socket. We dropped private socket support mainly because it was unused, untested and buggy, but also because GDBusObjectManagerServer could not export the same objects on multiple connections. Now, it would be rather straight forward to fix that and re-introduce ObjectManager on each private connection. But this commit doesn't do that yet, and the new code intentionally supports only one D-Bus connection. Also, the D-Bus startup was simplified. There is no retry, either nm_dbus_manager_start() succeeds, or it detects the initrd case. In the initrd case, bus manager never tries to connect to D-Bus. Since the initrd scenario is not yet used/tested, this is good enough for the moment. It could be easily extended later, for example with polling whether the system bus appears (like was done previously). Also, restart of D-Bus daemon isn't supported either -- just like before. Note how NMDBusManager now implements the ObjectManager D-Bus interface directly. Also, this fixes race issues in the server, by no longer delaying PropertiesChanged signals. NMExportedObject would collect changed properties and send the signal out in idle_emit_properties_changed() on idle. This messes up the ordering of change events w.r.t. other signals and events on the bus. Note that not only NMExportedObject messed up the ordering. Also the generated code would hook into notify() and process change events in and idle handle, exhibiting the same ordering issue too. No longer do that. PropertiesChanged signals will be sent right away by hooking into dispatch_properties_changed(). This means, changing a property in quick succession will no longer be combined and is guaranteed to emit signals for each individual state. Quite possibly we emit now more PropertiesChanged signals then before. However, we are now able to group a set of changes by using standard g_object_freeze_notify()/g_object_thaw_notify(). We probably should make more use of that. Also, now that our signals are all handled in the right order, we might find places where we still emit them in the wrong order. But that is then due to the order in which our GObjects emit signals, not due to an ill behavior of the D-Bus glue. Possibly we need to identify such ordering issues and fix them. Numbers (for contrib/rpm --without debug on x86_64): - the patch changes the code size of NetworkManager by - 2809360 bytes + 2537528 bytes (-9.7%) - Runtime measurements are harder because there is a large variance during testing. In other words, the numbers are not reproducible. Currently, the implementation performs no caching of GVariants at all, but it would be rather simple to add it, if that turns out to be useful. Anyway, without strong claim, it seems that the new form tends to perform slightly better. That would be no surprise. $ time (for i in {1..1000}; do nmcli >/dev/null || break; echo -n .; done) - real 1m39.355s + real 1m37.432s $ time (for i in {1..2000}; do busctl call org.freedesktop.NetworkManager /org/freedesktop org.freedesktop.DBus.ObjectManager GetManagedObjects > /dev/null || break; echo -n .; done) - real 0m26.843s + real 0m25.281s - Regarding RSS size, just looking at the processes in similar conditions, doesn't give a large difference. On my system they consume about 19MB RSS. It seems that the new version has a slightly smaller RSS size. - 19356 RSS + 18660 RSS
2018-02-26 13:51:52 +01:00
static const NMDBusInterfaceInfoExtended interface_info_device_bond = {
.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
NM_DBUS_INTERFACE_DEVICE_BOND,
.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE(
"HwAddress",
"s",
NM_DEVICE_HW_ADDRESS,
.annotations = NM_GDBUS_ANNOTATION_INFO_LIST_DEPRECATED(), ),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE(
"Carrier",
"b",
NM_DEVICE_CARRIER,
.annotations = NM_GDBUS_ANNOTATION_INFO_LIST_DEPRECATED(), ),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE(
"Slaves",
"ao",
NM_DEVICE_SLAVES,
.annotations = NM_GDBUS_ANNOTATION_INFO_LIST_DEPRECATED(), ), ), ),
core/dbus: rework D-Bus implementation to use lower layer GDBusConnection API Previously, we used the generated GDBusInterfaceSkeleton types and glued them via the NMExportedObject base class to our NM types. We also used GDBusObjectManagerServer. Don't do that anymore. The resulting code was more complicated despite (or because?) using generated classes. It was hard to understand, complex, had ordering-issues, and had a runtime and memory overhead. This patch refactors this entirely and uses the lower layer API GDBusConnection directly. It replaces the generated code, GDBusInterfaceSkeleton, and GDBusObjectManagerServer. All this is now done by NMDbusObject and NMDBusManager and static descriptor instances of type GDBusInterfaceInfo. This adds a net plus of more then 1300 lines of hand written code. I claim that this implementation is easier to understand. Note that previously we also required extensive and complex glue code to bind our objects to the generated skeleton objects. Instead, now glue our objects directly to GDBusConnection. The result is more immediate and gets rid of layers of code in between. Now that the D-Bus glue us more under our control, we can address issus and bottlenecks better, instead of adding code to bend the generated skeletons to our needs. Note that the current implementation now only supports one D-Bus connection. That was effectively the case already, although there were places (and still are) where the code pretends it could also support connections from a private socket. We dropped private socket support mainly because it was unused, untested and buggy, but also because GDBusObjectManagerServer could not export the same objects on multiple connections. Now, it would be rather straight forward to fix that and re-introduce ObjectManager on each private connection. But this commit doesn't do that yet, and the new code intentionally supports only one D-Bus connection. Also, the D-Bus startup was simplified. There is no retry, either nm_dbus_manager_start() succeeds, or it detects the initrd case. In the initrd case, bus manager never tries to connect to D-Bus. Since the initrd scenario is not yet used/tested, this is good enough for the moment. It could be easily extended later, for example with polling whether the system bus appears (like was done previously). Also, restart of D-Bus daemon isn't supported either -- just like before. Note how NMDBusManager now implements the ObjectManager D-Bus interface directly. Also, this fixes race issues in the server, by no longer delaying PropertiesChanged signals. NMExportedObject would collect changed properties and send the signal out in idle_emit_properties_changed() on idle. This messes up the ordering of change events w.r.t. other signals and events on the bus. Note that not only NMExportedObject messed up the ordering. Also the generated code would hook into notify() and process change events in and idle handle, exhibiting the same ordering issue too. No longer do that. PropertiesChanged signals will be sent right away by hooking into dispatch_properties_changed(). This means, changing a property in quick succession will no longer be combined and is guaranteed to emit signals for each individual state. Quite possibly we emit now more PropertiesChanged signals then before. However, we are now able to group a set of changes by using standard g_object_freeze_notify()/g_object_thaw_notify(). We probably should make more use of that. Also, now that our signals are all handled in the right order, we might find places where we still emit them in the wrong order. But that is then due to the order in which our GObjects emit signals, not due to an ill behavior of the D-Bus glue. Possibly we need to identify such ordering issues and fix them. Numbers (for contrib/rpm --without debug on x86_64): - the patch changes the code size of NetworkManager by - 2809360 bytes + 2537528 bytes (-9.7%) - Runtime measurements are harder because there is a large variance during testing. In other words, the numbers are not reproducible. Currently, the implementation performs no caching of GVariants at all, but it would be rather simple to add it, if that turns out to be useful. Anyway, without strong claim, it seems that the new form tends to perform slightly better. That would be no surprise. $ time (for i in {1..1000}; do nmcli >/dev/null || break; echo -n .; done) - real 1m39.355s + real 1m37.432s $ time (for i in {1..2000}; do busctl call org.freedesktop.NetworkManager /org/freedesktop org.freedesktop.DBus.ObjectManager GetManagedObjects > /dev/null || break; echo -n .; done) - real 0m26.843s + real 0m25.281s - Regarding RSS size, just looking at the processes in similar conditions, doesn't give a large difference. On my system they consume about 19MB RSS. It seems that the new version has a slightly smaller RSS size. - 19356 RSS + 18660 RSS
2018-02-26 13:51:52 +01:00
};
static void
nm_device_bond_class_init(NMDeviceBondClass *klass)
{
core/dbus: rework D-Bus implementation to use lower layer GDBusConnection API Previously, we used the generated GDBusInterfaceSkeleton types and glued them via the NMExportedObject base class to our NM types. We also used GDBusObjectManagerServer. Don't do that anymore. The resulting code was more complicated despite (or because?) using generated classes. It was hard to understand, complex, had ordering-issues, and had a runtime and memory overhead. This patch refactors this entirely and uses the lower layer API GDBusConnection directly. It replaces the generated code, GDBusInterfaceSkeleton, and GDBusObjectManagerServer. All this is now done by NMDbusObject and NMDBusManager and static descriptor instances of type GDBusInterfaceInfo. This adds a net plus of more then 1300 lines of hand written code. I claim that this implementation is easier to understand. Note that previously we also required extensive and complex glue code to bind our objects to the generated skeleton objects. Instead, now glue our objects directly to GDBusConnection. The result is more immediate and gets rid of layers of code in between. Now that the D-Bus glue us more under our control, we can address issus and bottlenecks better, instead of adding code to bend the generated skeletons to our needs. Note that the current implementation now only supports one D-Bus connection. That was effectively the case already, although there were places (and still are) where the code pretends it could also support connections from a private socket. We dropped private socket support mainly because it was unused, untested and buggy, but also because GDBusObjectManagerServer could not export the same objects on multiple connections. Now, it would be rather straight forward to fix that and re-introduce ObjectManager on each private connection. But this commit doesn't do that yet, and the new code intentionally supports only one D-Bus connection. Also, the D-Bus startup was simplified. There is no retry, either nm_dbus_manager_start() succeeds, or it detects the initrd case. In the initrd case, bus manager never tries to connect to D-Bus. Since the initrd scenario is not yet used/tested, this is good enough for the moment. It could be easily extended later, for example with polling whether the system bus appears (like was done previously). Also, restart of D-Bus daemon isn't supported either -- just like before. Note how NMDBusManager now implements the ObjectManager D-Bus interface directly. Also, this fixes race issues in the server, by no longer delaying PropertiesChanged signals. NMExportedObject would collect changed properties and send the signal out in idle_emit_properties_changed() on idle. This messes up the ordering of change events w.r.t. other signals and events on the bus. Note that not only NMExportedObject messed up the ordering. Also the generated code would hook into notify() and process change events in and idle handle, exhibiting the same ordering issue too. No longer do that. PropertiesChanged signals will be sent right away by hooking into dispatch_properties_changed(). This means, changing a property in quick succession will no longer be combined and is guaranteed to emit signals for each individual state. Quite possibly we emit now more PropertiesChanged signals then before. However, we are now able to group a set of changes by using standard g_object_freeze_notify()/g_object_thaw_notify(). We probably should make more use of that. Also, now that our signals are all handled in the right order, we might find places where we still emit them in the wrong order. But that is then due to the order in which our GObjects emit signals, not due to an ill behavior of the D-Bus glue. Possibly we need to identify such ordering issues and fix them. Numbers (for contrib/rpm --without debug on x86_64): - the patch changes the code size of NetworkManager by - 2809360 bytes + 2537528 bytes (-9.7%) - Runtime measurements are harder because there is a large variance during testing. In other words, the numbers are not reproducible. Currently, the implementation performs no caching of GVariants at all, but it would be rather simple to add it, if that turns out to be useful. Anyway, without strong claim, it seems that the new form tends to perform slightly better. That would be no surprise. $ time (for i in {1..1000}; do nmcli >/dev/null || break; echo -n .; done) - real 1m39.355s + real 1m37.432s $ time (for i in {1..2000}; do busctl call org.freedesktop.NetworkManager /org/freedesktop org.freedesktop.DBus.ObjectManager GetManagedObjects > /dev/null || break; echo -n .; done) - real 0m26.843s + real 0m25.281s - Regarding RSS size, just looking at the processes in similar conditions, doesn't give a large difference. On my system they consume about 19MB RSS. It seems that the new version has a slightly smaller RSS size. - 19356 RSS + 18660 RSS
2018-02-26 13:51:52 +01:00
NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass);
NMDeviceClass *device_class = NM_DEVICE_CLASS(klass);
core/dbus: rework D-Bus implementation to use lower layer GDBusConnection API Previously, we used the generated GDBusInterfaceSkeleton types and glued them via the NMExportedObject base class to our NM types. We also used GDBusObjectManagerServer. Don't do that anymore. The resulting code was more complicated despite (or because?) using generated classes. It was hard to understand, complex, had ordering-issues, and had a runtime and memory overhead. This patch refactors this entirely and uses the lower layer API GDBusConnection directly. It replaces the generated code, GDBusInterfaceSkeleton, and GDBusObjectManagerServer. All this is now done by NMDbusObject and NMDBusManager and static descriptor instances of type GDBusInterfaceInfo. This adds a net plus of more then 1300 lines of hand written code. I claim that this implementation is easier to understand. Note that previously we also required extensive and complex glue code to bind our objects to the generated skeleton objects. Instead, now glue our objects directly to GDBusConnection. The result is more immediate and gets rid of layers of code in between. Now that the D-Bus glue us more under our control, we can address issus and bottlenecks better, instead of adding code to bend the generated skeletons to our needs. Note that the current implementation now only supports one D-Bus connection. That was effectively the case already, although there were places (and still are) where the code pretends it could also support connections from a private socket. We dropped private socket support mainly because it was unused, untested and buggy, but also because GDBusObjectManagerServer could not export the same objects on multiple connections. Now, it would be rather straight forward to fix that and re-introduce ObjectManager on each private connection. But this commit doesn't do that yet, and the new code intentionally supports only one D-Bus connection. Also, the D-Bus startup was simplified. There is no retry, either nm_dbus_manager_start() succeeds, or it detects the initrd case. In the initrd case, bus manager never tries to connect to D-Bus. Since the initrd scenario is not yet used/tested, this is good enough for the moment. It could be easily extended later, for example with polling whether the system bus appears (like was done previously). Also, restart of D-Bus daemon isn't supported either -- just like before. Note how NMDBusManager now implements the ObjectManager D-Bus interface directly. Also, this fixes race issues in the server, by no longer delaying PropertiesChanged signals. NMExportedObject would collect changed properties and send the signal out in idle_emit_properties_changed() on idle. This messes up the ordering of change events w.r.t. other signals and events on the bus. Note that not only NMExportedObject messed up the ordering. Also the generated code would hook into notify() and process change events in and idle handle, exhibiting the same ordering issue too. No longer do that. PropertiesChanged signals will be sent right away by hooking into dispatch_properties_changed(). This means, changing a property in quick succession will no longer be combined and is guaranteed to emit signals for each individual state. Quite possibly we emit now more PropertiesChanged signals then before. However, we are now able to group a set of changes by using standard g_object_freeze_notify()/g_object_thaw_notify(). We probably should make more use of that. Also, now that our signals are all handled in the right order, we might find places where we still emit them in the wrong order. But that is then due to the order in which our GObjects emit signals, not due to an ill behavior of the D-Bus glue. Possibly we need to identify such ordering issues and fix them. Numbers (for contrib/rpm --without debug on x86_64): - the patch changes the code size of NetworkManager by - 2809360 bytes + 2537528 bytes (-9.7%) - Runtime measurements are harder because there is a large variance during testing. In other words, the numbers are not reproducible. Currently, the implementation performs no caching of GVariants at all, but it would be rather simple to add it, if that turns out to be useful. Anyway, without strong claim, it seems that the new form tends to perform slightly better. That would be no surprise. $ time (for i in {1..1000}; do nmcli >/dev/null || break; echo -n .; done) - real 1m39.355s + real 1m37.432s $ time (for i in {1..2000}; do busctl call org.freedesktop.NetworkManager /org/freedesktop org.freedesktop.DBus.ObjectManager GetManagedObjects > /dev/null || break; echo -n .; done) - real 0m26.843s + real 0m25.281s - Regarding RSS size, just looking at the processes in similar conditions, doesn't give a large difference. On my system they consume about 19MB RSS. It seems that the new version has a slightly smaller RSS size. - 19356 RSS + 18660 RSS
2018-02-26 13:51:52 +01:00
dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_device_bond);
device_class->connection_type_supported = NM_SETTING_BOND_SETTING_NAME;
device_class->connection_type_check_compatible = NM_SETTING_BOND_SETTING_NAME;
device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_BOND);
device_class->is_controller = TRUE;
device_class->get_generic_capabilities = get_generic_capabilities;
device_class->complete_connection = complete_connection;
device_class->update_connection = update_connection;
device_class->controller_update_port_connection = controller_update_port_connection;
device_class->create_and_realize = create_and_realize;
device_class->act_stage1_prepare = act_stage1_prepare;
device_class->act_stage1_prepare_also_for_external_or_assume = TRUE;
device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
device_class->attach_port = attach_port;
device_class->detach_port = detach_port;
device_class->can_reapply_change = can_reapply_change;
device_class->reapply_connection = reapply_connection;
device_class->deactivate = deactivate;
}
2014-09-08 10:15:02 -05:00
/*****************************************************************************/
2014-09-08 10:15:02 -05:00
#define NM_TYPE_BOND_DEVICE_FACTORY (nm_bond_device_factory_get_type())
#define NM_BOND_DEVICE_FACTORY(obj) \
(_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_BOND_DEVICE_FACTORY, NMBondDeviceFactory))
2014-09-08 10:15:02 -05:00
static NMDevice *
create_device(NMDeviceFactory *factory,
const char *iface,
const NMPlatformLink *plink,
NMConnection *connection,
gboolean *out_ignore)
2014-09-08 10:15:02 -05:00
{
return g_object_new(NM_TYPE_DEVICE_BOND,
NM_DEVICE_IFACE,
iface,
NM_DEVICE_DRIVER,
"bonding",
NM_DEVICE_TYPE_DESC,
"Bond",
NM_DEVICE_DEVICE_TYPE,
NM_DEVICE_TYPE_BOND,
NM_DEVICE_LINK_TYPE,
NM_LINK_TYPE_BOND,
NULL);
2014-09-08 10:15:02 -05:00
}
NM_DEVICE_FACTORY_DEFINE_INTERNAL(
BOND,
Bond,
bond,
NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(NM_LINK_TYPE_BOND)
NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(NM_SETTING_BOND_SETTING_NAME),
factory_class->create_device = create_device;);