platform: use netlink to set bond options

sysfs is deprecated and kernel people will not add new bond options to
sysfs. Netlink is a stable API and therefore is the right method to
communicate with kernel in order to set the link options.
This commit is contained in:
Fernando Fernandez Mancera 2022-05-06 10:11:16 +02:00
parent 34c1497ee4
commit 89fb27c53c
12 changed files with 663 additions and 36 deletions

View file

@ -9,6 +9,8 @@
#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"
@ -16,6 +18,7 @@
#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"
#define _NMLOG_DEVICE_TYPE NMDeviceBond
@ -349,51 +352,194 @@ set_bond_arp_ip_targets(NMDevice *device, NMSettingBond *s_bond)
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET));
}
static gboolean
apply_bonding_config(NMDeviceBond *self)
static void
_bond_to_nm_ether_addr_ad_actor_system(const char *ad_actor, NMEtherAddr *out_addr)
{
NMDevice *device = NM_DEVICE(self);
NMSettingBond *s_bond;
NMBondMode mode;
const char *mode_str;
gs_free char *device_bond_mode = NULL;
if (ad_actor == NULL) {
*out_addr = NM_ETHER_ADDR_INIT(0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
return;
}
if (!nm_utils_hwaddr_aton(ad_actor, out_addr, ETH_ALEN))
nm_assert_not_reached();
}
s_bond = nm_device_get_applied_setting(device, NM_TYPE_SETTING_BOND);
g_return_val_if_fail(s_bond, FALSE);
static guint64
_bond_opt_value_to_platform(const char *value, guint8 type)
{
if (value != NULL) {
if (type == 8)
return _nm_utils_ascii_str_to_uint64(value, 10, 0, G_MAXUINT8, 0);
else if (type == 16)
return _nm_utils_ascii_str_to_uint64(value, 10, 0, G_MAXUINT16, 0);
else
return _nm_utils_ascii_str_to_uint64(value, 10, 0, G_MAXUINT32, 0);
}
return 0;
}
mode_str = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_MODE);
mode = _nm_setting_bond_mode_from_string(mode_str);
g_return_val_if_fail(mode != NM_BOND_MODE_UNKNOWN, FALSE);
static void
_bond_arp_ip_target_to_platform(const char *value,
struct in_addr out[NM_BOND_MAX_ARP_TARGETS],
guint8 *ip_count)
{
gs_free const char **ip = NULL;
struct in_addr in_a;
int i, added = 0;
/* Set mode first, as some other options (e.g. arp_interval) are valid
* only for certain modes.
*/
device_bond_mode = nm_platform_sysctl_master_get_option(nm_device_get_platform(device),
nm_device_get_ifindex(device),
NM_SETTING_BOND_OPTION_MODE);
/* Need to release all slaves before we can change bond mode */
if (!nm_streq0(device_bond_mode, mode_str))
nm_device_master_release_slaves_all(device);
ip = nm_strsplit_set(value, " ");
set_bond_attr_or_default(device, s_bond, NM_SETTING_BOND_OPTION_MODE);
if (!ip)
return;
set_bond_arp_ip_targets(device, s_bond);
for (i = 0; ip[i]; i++) {
if (!inet_aton(ip[i], &in_a))
continue;
added++;
set_bond_attrs_or_default(device, s_bond, NM_MAKE_STRV(OPTIONS_APPLY_SUBSET));
return TRUE;
out[i] = in_a;
}
*ip_count = added;
}
static int
_bond_primary_ifname_to_ifindex(const char *value)
{
int ifindex = -1;
NMDevice *device = NULL;
if (value != NULL) {
device = nm_manager_get_device_by_iface(NM_MANAGER_GET, value);
if (!device)
return ifindex;
ifindex = nm_device_get_ifindex(device);
}
return ifindex;
}
static void
_platform_lnk_bond_init_from_setting(NMSettingBond *s_bond, NMPlatformLnkBond *props)
{
*props = (NMPlatformLnkBond){
.mode = _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_MODE),
8),
.primary = _bond_primary_ifname_to_ifindex(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_PRIMARY)),
.miimon = (guint32) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_MIIMON),
32),
.updelay = (guint32) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_UPDELAY),
32),
.downdelay = (guint32) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY),
32),
.arp_interval = (guint32) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL),
32),
.resend_igmp = (guint32) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_RESEND_IGMP),
32),
.min_links = (guint32) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_MIN_LINKS),
32),
.lp_interval = (guint32) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_LP_INTERVAL),
32),
.packets_per_port = (guint32) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE),
32),
.peer_notif_delay = (guint32) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY),
32),
.arp_all_targets = (guint32) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS),
32),
.arp_validate = (guint32) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_VALIDATE),
32),
.ad_actor_sys_prio = (guint16) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO),
16),
.ad_user_port_key = (guint16) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY),
16),
.primary_reselect = (guint8) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT),
8),
.fail_over_mac = (guint8) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC),
8),
.xmit_hash_policy = (guint8) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY),
8),
.num_unsol_na = (guint8) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_NUM_UNSOL_NA),
8),
.num_grat_arp = (guint8) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP),
8),
.all_ports_active = (guint8) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE),
8),
.lacp_rate = (guint8) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_LACP_RATE),
8),
.ad_select = (guint8) _bond_opt_value_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_AD_SELECT),
8),
};
_bond_to_nm_ether_addr_ad_actor_system(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM),
&props->ad_actor_system);
if (nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER) != NULL) {
props->use_carrier = _nm_utils_ascii_str_to_bool(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER),
FALSE);
}
if (nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB)
!= NULL) {
props->tlb_dynamic_lb = _nm_utils_ascii_str_to_bool(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB),
FALSE);
}
if (nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET)
!= NULL) {
_bond_arp_ip_target_to_platform(
nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET),
props->arp_ip_target,
&props->arp_ip_targets_num);
}
}
static NMActStageReturn
act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
{
NMDeviceBond *self = NM_DEVICE_BOND(device);
NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
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);
_platform_lnk_bond_init_from_setting(s_bond, &props);
/* Interface must be down to set bond options */
nm_device_take_down(device, TRUE);
if (!apply_bonding_config(self))
r = nm_platform_link_bond_change(nm_device_get_platform(device), ifindex, &props);
if (r < 0) {
ret = NM_ACT_STAGE_RETURN_FAILURE;
else {
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;
}
@ -535,12 +681,19 @@ create_and_realize(NMDevice *device,
const NMPlatformLink **out_plink,
GError **error)
{
const char *iface = nm_device_get_iface(device);
int r;
const char *iface = nm_device_get_iface(device);
NMSettingBond *s_bond;
NMPlatformLnkBond props;
int r;
g_assert(iface);
r = nm_platform_link_bond_add(nm_device_get_platform(device), iface, out_plink);
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,

View file

@ -1334,6 +1334,22 @@ nm_manager_get_device_by_ifindex(NMManager *self, int ifindex)
return NULL;
}
NMDevice *
nm_manager_get_device_by_iface(NMManager *self, const char *iface)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
NMDevice *device;
if (iface) {
c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
if (nm_streq0(nm_device_get_iface(device), iface))
return device;
}
}
return NULL;
}
static NMDevice *
find_device_by_permanent_hw_addr(NMManager *self, const char *hwaddr)
{

View file

@ -150,6 +150,7 @@ const CList *nm_manager_get_devices(NMManager *manager);
});)
NMDevice *nm_manager_get_device_by_ifindex(NMManager *manager, int ifindex);
NMDevice *nm_manager_get_device_by_iface(NMManager *manager, const char *iface);
NMDevice *nm_manager_get_device_by_path(NMManager *manager, const char *path);
guint32

View file

@ -323,6 +323,15 @@ link_add(NMPlatform *platform,
dev_lnk = nmp_object_new(NMP_OBJECT_TYPE_LNK_BRIDGE, props);
break;
}
case NM_LINK_TYPE_BOND:
{
const NMPlatformLnkBond *props = extra_data;
nm_assert(props);
dev_lnk = nmp_object_new(NMP_OBJECT_TYPE_LNK_BOND, props);
break;
}
case NM_LINK_TYPE_VETH:
veth_peer = extra_data;
g_assert(veth_peer);

View file

@ -93,6 +93,10 @@ test_loopback(void)
g_assert(!nm_platform_link_supports_vlans(NM_PLATFORM_GET, LO_INDEX));
}
const NMPlatformLnkBond nm_platform_lnk_bond_default = {
.mode = 3,
};
static gboolean
software_add(NMLinkType link_type, const char *name)
{
@ -112,12 +116,12 @@ software_add(NMLinkType link_type, const char *name)
gboolean bond0_exists = !!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "bond0");
int r;
r = nm_platform_link_bond_add(NM_PLATFORM_GET, name, NULL);
r = nm_platform_link_bond_add(NM_PLATFORM_GET, name, &nm_platform_lnk_bond_default, NULL);
/* Check that bond0 is *not* automatically created. */
if (!bond0_exists)
g_assert(!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "bond0"));
return r >= 0;
return NMTST_NM_ERR_SUCCESS(r);
}
case NM_LINK_TYPE_TEAM:
return NMTST_NM_ERR_SUCCESS(nm_platform_link_team_add(NM_PLATFORM_GET, name, NULL));

View file

@ -1432,6 +1432,131 @@ _parse_lnk_bridge(const char *kind, struct nlattr *info_data)
/***********************************************************************************/
static NMPObject *
_parse_lnk_bond(const char *kind, struct nlattr *info_data)
{
static const struct nla_policy policy[] = {
[IFLA_BOND_MODE] = {.type = NLA_U8},
[IFLA_BOND_ACTIVE_SLAVE] = {.type = NLA_U32},
[IFLA_BOND_MIIMON] = {.type = NLA_U32},
[IFLA_BOND_UPDELAY] = {.type = NLA_U32},
[IFLA_BOND_DOWNDELAY] = {.type = NLA_U32},
[IFLA_BOND_USE_CARRIER] = {.type = NLA_U8},
[IFLA_BOND_ARP_INTERVAL] = {.type = NLA_U32},
[IFLA_BOND_ARP_IP_TARGET] = {.type = NLA_NESTED},
[IFLA_BOND_ARP_VALIDATE] = {.type = NLA_U32},
[IFLA_BOND_ARP_ALL_TARGETS] = {.type = NLA_U32},
[IFLA_BOND_PRIMARY] = {.type = NLA_U32},
[IFLA_BOND_PRIMARY_RESELECT] = {.type = NLA_U8},
[IFLA_BOND_FAIL_OVER_MAC] = {.type = NLA_U8},
[IFLA_BOND_XMIT_HASH_POLICY] = {.type = NLA_U8},
[IFLA_BOND_RESEND_IGMP] = {.type = NLA_U32},
[IFLA_BOND_NUM_PEER_NOTIF] = {.type = NLA_U8},
[IFLA_BOND_ALL_SLAVES_ACTIVE] = {.type = NLA_U8},
[IFLA_BOND_MIN_LINKS] = {.type = NLA_U32},
[IFLA_BOND_LP_INTERVAL] = {.type = NLA_U32},
[IFLA_BOND_PACKETS_PER_SLAVE] = {.type = NLA_U32},
[IFLA_BOND_AD_LACP_RATE] = {.type = NLA_U8},
[IFLA_BOND_AD_SELECT] = {.type = NLA_U8},
[IFLA_BOND_AD_ACTOR_SYS_PRIO] = {.type = NLA_U16},
[IFLA_BOND_AD_USER_PORT_KEY] = {.type = NLA_U16},
[IFLA_BOND_AD_ACTOR_SYSTEM] = {.minlen = sizeof(NMEtherAddr)},
[IFLA_BOND_TLB_DYNAMIC_LB] = {.type = NLA_U8},
[IFLA_BOND_PEER_NOTIF_DELAY] = {.type = NLA_U32},
};
NMPlatformLnkBond *props;
struct nlattr *tb[G_N_ELEMENTS(policy)];
NMPObject *obj = NULL;
if (!info_data || !nm_streq0(kind, "bond"))
return NULL;
if (nla_parse_nested_arr(tb, info_data, policy) < 0)
return NULL;
obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_BOND, NULL);
props = &obj->lnk_bond;
if (tb[IFLA_BOND_MODE])
props->mode = nla_get_u8(tb[IFLA_BOND_MODE]);
if (tb[IFLA_BOND_PRIMARY]) {
props->primary = nla_get_u32(tb[IFLA_BOND_PRIMARY]);
} else if (tb[IFLA_BOND_ACTIVE_SLAVE]) {
props->primary = nla_get_u32(tb[IFLA_BOND_ACTIVE_SLAVE]);
}
if (tb[IFLA_BOND_MIIMON])
props->miimon = nla_get_u32(tb[IFLA_BOND_MIIMON]);
if (tb[IFLA_BOND_UPDELAY])
props->updelay = nla_get_u32(tb[IFLA_BOND_UPDELAY]);
if (tb[IFLA_BOND_DOWNDELAY])
props->downdelay = nla_get_u32(tb[IFLA_BOND_DOWNDELAY]);
if (tb[IFLA_BOND_USE_CARRIER])
props->use_carrier = nla_get_u8(tb[IFLA_BOND_USE_CARRIER]);
if (tb[IFLA_BOND_ARP_INTERVAL])
props->arp_interval = nla_get_u32(tb[IFLA_BOND_ARP_INTERVAL]);
if (tb[IFLA_BOND_ARP_IP_TARGET]) {
struct nlattr *attr;
int i = 0, rem;
nla_for_each_nested (attr, tb[IFLA_BOND_ARP_IP_TARGET], rem) {
uint32_t ip;
if (i > 15)
break;
if (nla_len(attr) < sizeof(ip))
break;
ip = nla_get_be32(attr);
props->arp_ip_target[i] = *(struct in_addr *) &ip;
props->arp_ip_targets_num++;
i++;
}
}
if (tb[IFLA_BOND_ARP_VALIDATE])
props->arp_validate = nla_get_u32(tb[IFLA_BOND_ARP_VALIDATE]);
if (tb[IFLA_BOND_ARP_ALL_TARGETS])
props->arp_all_targets = nla_get_u32(tb[IFLA_BOND_ARP_ALL_TARGETS]);
if (tb[IFLA_BOND_PRIMARY_RESELECT])
props->primary_reselect = nla_get_u8(tb[IFLA_BOND_PRIMARY_RESELECT]);
if (tb[IFLA_BOND_FAIL_OVER_MAC])
props->fail_over_mac = nla_get_u8(tb[IFLA_BOND_FAIL_OVER_MAC]);
if (tb[IFLA_BOND_XMIT_HASH_POLICY])
props->xmit_hash_policy = nla_get_u8(tb[IFLA_BOND_XMIT_HASH_POLICY]);
if (tb[IFLA_BOND_RESEND_IGMP])
props->resend_igmp = nla_get_u32(tb[IFLA_BOND_RESEND_IGMP]);
if (tb[IFLA_BOND_NUM_PEER_NOTIF]) {
props->num_grat_arp = nla_get_u8(tb[IFLA_BOND_NUM_PEER_NOTIF]);
props->num_unsol_na = nla_get_u8(tb[IFLA_BOND_NUM_PEER_NOTIF]);
}
if (tb[IFLA_BOND_ALL_SLAVES_ACTIVE])
props->all_ports_active = nla_get_u8(tb[IFLA_BOND_ALL_SLAVES_ACTIVE]);
if (tb[IFLA_BOND_MIN_LINKS])
props->min_links = nla_get_u32(tb[IFLA_BOND_MIN_LINKS]);
if (tb[IFLA_BOND_LP_INTERVAL])
props->lp_interval = nla_get_u32(tb[IFLA_BOND_LP_INTERVAL]);
if (tb[IFLA_BOND_PACKETS_PER_SLAVE])
props->packets_per_port = nla_get_u32(tb[IFLA_BOND_PACKETS_PER_SLAVE]);
if (tb[IFLA_BOND_AD_LACP_RATE])
props->lacp_rate = nla_get_u8(tb[IFLA_BOND_AD_LACP_RATE]);
if (tb[IFLA_BOND_AD_SELECT])
props->ad_select = nla_get_u8(tb[IFLA_BOND_AD_SELECT]);
if (tb[IFLA_BOND_AD_ACTOR_SYS_PRIO])
props->ad_actor_sys_prio = nla_get_u16(tb[IFLA_BOND_AD_ACTOR_SYS_PRIO]);
if (tb[IFLA_BOND_AD_USER_PORT_KEY])
props->ad_user_port_key = nla_get_u16(tb[IFLA_BOND_AD_USER_PORT_KEY]);
if (tb[IFLA_BOND_AD_ACTOR_SYSTEM])
props->ad_actor_system = *nla_data_as(NMEtherAddr, tb[IFLA_BOND_AD_ACTOR_SYSTEM]);
if (tb[IFLA_BOND_TLB_DYNAMIC_LB])
props->tlb_dynamic_lb = nla_get_u8(tb[IFLA_BOND_TLB_DYNAMIC_LB]);
if (tb[IFLA_BOND_PEER_NOTIF_DELAY])
props->peer_notif_delay = nla_get_u32(tb[IFLA_BOND_PEER_NOTIF_DELAY]);
return obj;
}
/***********************************************************************************/
static NMPObject *
_parse_lnk_gre(const char *kind, struct nlattr *info_data)
{
@ -3116,6 +3241,9 @@ _new_from_nl_link(NMPlatform *platform,
case NM_LINK_TYPE_BRIDGE:
lnk_data = _parse_lnk_bridge(nl_info_kind, nl_info_data);
break;
case NM_LINK_TYPE_BOND:
lnk_data = _parse_lnk_bond(nl_info_kind, nl_info_data);
break;
case NM_LINK_TYPE_GRE:
case NM_LINK_TYPE_GRETAP:
lnk_data = _parse_lnk_gre(nl_info_kind, nl_info_data);
@ -4277,6 +4405,95 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo
NLA_PUT_U64(msg, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, props->mcast_startup_query_interval);
break;
}
case NM_LINK_TYPE_BOND:
{
const NMPlatformLnkBond *props = extra_data;
struct nlattr *targets;
int targets_added = 0, i = 0;
nm_assert(extra_data);
if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
goto nla_put_failure;
NLA_PUT_U8(msg, IFLA_BOND_MODE, props->mode);
targets = nla_nest_start(msg, IFLA_BOND_ARP_IP_TARGET);
if (!targets)
goto nla_put_failure;
for (i = 0; i < props->arp_ip_targets_num; i++) {
if (props->arp_ip_target[i].s_addr) {
NLA_PUT_U32(msg, i, props->arp_ip_target[i].s_addr);
targets_added++;
}
}
if (targets_added)
nla_nest_end(msg, targets);
else
nla_nest_cancel(msg, targets);
NLA_PUT_U8(msg, IFLA_BOND_ALL_SLAVES_ACTIVE, props->all_ports_active);
NLA_PUT_U8(msg, IFLA_BOND_USE_CARRIER, !!props->use_carrier);
NLA_PUT_U32(msg, IFLA_BOND_ARP_INTERVAL, props->arp_interval);
NLA_PUT_U32(msg, IFLA_BOND_ARP_VALIDATE, props->arp_validate);
NLA_PUT_U32(msg, IFLA_BOND_MIIMON, props->miimon);
if (props->miimon) {
NLA_PUT_U32(msg, IFLA_BOND_UPDELAY, props->updelay);
NLA_PUT_U32(msg, IFLA_BOND_DOWNDELAY, props->downdelay);
}
if (props->miimon || props->arp_interval)
NLA_PUT_U32(msg, IFLA_BOND_PEER_NOTIF_DELAY, props->peer_notif_delay);
if (NM_IN_SET(props->mode, 0)) {
NLA_PUT_U32(msg, IFLA_BOND_PACKETS_PER_SLAVE, props->packets_per_port);
NLA_PUT_U32(msg, IFLA_BOND_RESEND_IGMP, props->resend_igmp);
}
if (NM_IN_SET(props->mode, 1)) {
NLA_PUT_U8(msg, IFLA_BOND_FAIL_OVER_MAC, props->fail_over_mac);
/* num_unsol_na and num_grat_arp cannot be different. */
NLA_PUT_U8(msg, IFLA_BOND_NUM_PEER_NOTIF, props->num_unsol_na);
NLA_PUT_U8(msg, IFLA_BOND_PRIMARY_RESELECT, props->primary_reselect);
NLA_PUT_U32(msg, IFLA_BOND_ARP_ALL_TARGETS, props->arp_all_targets);
NLA_PUT_U32(msg, IFLA_BOND_PRIMARY, props->primary);
NLA_PUT_U32(msg, IFLA_BOND_RESEND_IGMP, props->resend_igmp);
}
if (NM_IN_SET(props->mode, 2))
NLA_PUT_U8(msg, IFLA_BOND_XMIT_HASH_POLICY, props->xmit_hash_policy);
if (NM_IN_SET(props->mode, 4)) {
NLA_PUT_U8(msg, IFLA_BOND_AD_SELECT, props->ad_select);
NLA_PUT_U8(msg, IFLA_BOND_AD_LACP_RATE, props->lacp_rate);
NLA_PUT_U8(msg, IFLA_BOND_XMIT_HASH_POLICY, props->xmit_hash_policy);
NLA_PUT_U16(msg, IFLA_BOND_AD_ACTOR_SYS_PRIO, props->ad_actor_sys_prio);
NLA_PUT_U16(msg, IFLA_BOND_AD_USER_PORT_KEY, props->ad_user_port_key);
NLA_PUT_U32(msg, IFLA_BOND_MIN_LINKS, props->min_links);
NLA_PUT(msg,
IFLA_BOND_AD_ACTOR_SYSTEM,
sizeof(props->ad_actor_system),
&props->ad_actor_system);
}
if (NM_IN_SET(props->mode, 5)) {
NLA_PUT_U8(msg, IFLA_BOND_TLB_DYNAMIC_LB, !!props->tlb_dynamic_lb);
NLA_PUT_U8(msg, IFLA_BOND_XMIT_HASH_POLICY, props->xmit_hash_policy);
NLA_PUT_U8(msg, IFLA_BOND_PRIMARY_RESELECT, props->primary_reselect);
NLA_PUT_U32(msg, IFLA_BOND_PRIMARY, props->primary);
NLA_PUT_U32(msg, IFLA_BOND_RESEND_IGMP, props->resend_igmp);
NLA_PUT_U32(msg, IFLA_BOND_LP_INTERVAL, props->lp_interval);
}
if (NM_IN_SET(props->mode, 6)) {
NLA_PUT_U8(msg, IFLA_BOND_PRIMARY_RESELECT, props->primary_reselect);
NLA_PUT_U32(msg, IFLA_BOND_PRIMARY, props->primary);
NLA_PUT_U32(msg, IFLA_BOND_RESEND_IGMP, props->resend_igmp);
NLA_PUT_U32(msg, IFLA_BOND_LP_INTERVAL, props->lp_interval);
}
break;
}
case NM_LINK_TYPE_VLAN:
{
const NMPlatformLnkVlan *props = extra_data;

View file

@ -234,6 +234,14 @@ nla_get_be64(const struct nlattr *nla)
return unaligned_read_be64(nla_data(nla));
}
static inline uint32_t
nla_get_be32(const struct nlattr *nla)
{
nm_assert(nla_len(nla) >= sizeof(uint32_t));
return unaligned_read_be32(nla_data(nla));
}
static inline char *
nla_get_string(const struct nlattr *nla)
{

View file

@ -1292,6 +1292,12 @@ nm_platform_link_add(NMPlatform *self,
buf_p,
buf_len);
break;
case NM_LINK_TYPE_BOND:
nm_strbuf_append_str(&buf_p, &buf_len, ", ");
nm_platform_lnk_bond_to_string((const NMPlatformLnkBond *) extra_data,
buf_p,
buf_len);
break;
default:
nm_assert(!extra_data);
break;
@ -1332,6 +1338,12 @@ nm_platform_link_change(NMPlatform *self, NMLinkType type, int ifindex, gconstpo
buf_p,
buf_len);
break;
case NM_LINK_TYPE_BOND:
nm_strbuf_append_str(&buf_p, &buf_len, ", ");
nm_platform_lnk_bond_to_string((const NMPlatformLnkBond *) extra_data,
buf_p,
buf_len);
break;
default:
nm_assert(!extra_data);
break;
@ -2232,6 +2244,12 @@ _link_get_lnk(NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatf
return lnk ? &lnk->object : NULL;
}
const NMPlatformLnkBond *
nm_platform_link_get_lnk_bond(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
{
return _link_get_lnk(self, ifindex, NM_LINK_TYPE_BOND, out_link);
}
const NMPlatformLnkBridge *
nm_platform_link_get_lnk_bridge(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
{
@ -5975,6 +5993,69 @@ nm_platform_lnk_bridge_to_string(const NMPlatformLnkBridge *lnk, char *buf, gsiz
return buf;
}
const char *
nm_platform_lnk_bond_to_string(const NMPlatformLnkBond *lnk, char *buf, gsize len)
{
if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
return buf;
g_snprintf(buf,
len,
"bond"
" mode %u"
" primary %u"
" miimon %u"
" updelay %u"
" downdelay %u"
" arp_interval %u"
" resend_igmp %u"
" min_links %u"
" lp_interval %u"
" packets_per_port %u"
" peer_notif_delay %u"
" arp_all_targets %u"
" arp_validate %u"
" ad_actor_sys_prio %u"
" ad_user_port_key %u"
" ad_actor_system " NM_ETHER_ADDR_FORMAT_STR " primary_reselect %u"
" fail_over_mac %u"
" xmit_hash_policy %u"
" num_unsol_na %u"
" num_gray_arp %u"
" all_ports_active %u"
" lacp_rate %u"
" ad_select %u"
" use_carrier %d"
" tlb_dynamic_lb %d",
lnk->mode,
lnk->primary,
lnk->miimon,
lnk->updelay,
lnk->downdelay,
lnk->arp_interval,
lnk->resend_igmp,
lnk->min_links,
lnk->lp_interval,
lnk->packets_per_port,
lnk->peer_notif_delay,
lnk->arp_all_targets,
lnk->arp_validate,
lnk->ad_actor_sys_prio,
lnk->ad_user_port_key,
NM_ETHER_ADDR_FORMAT_VAL(&lnk->ad_actor_system),
lnk->primary_reselect,
lnk->fail_over_mac,
lnk->xmit_hash_policy,
lnk->num_unsol_na,
lnk->num_grat_arp,
lnk->all_ports_active,
lnk->lacp_rate,
lnk->ad_select,
(int) lnk->use_carrier,
(int) lnk->tlb_dynamic_lb);
return buf;
}
const char *
nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len)
{
@ -7692,6 +7773,72 @@ nm_platform_lnk_bridge_hash_update(const NMPlatformLnkBridge *obj, NMHashState *
obj->vlan_stats_enabled));
}
void
nm_platform_lnk_bond_hash_update(const NMPlatformLnkBond *obj, NMHashState *h)
{
nm_hash_update_vals(h,
obj->mode,
obj->primary,
obj->miimon,
obj->updelay,
obj->downdelay,
obj->arp_interval,
obj->resend_igmp,
obj->min_links,
obj->lp_interval,
obj->packets_per_port,
obj->peer_notif_delay,
obj->arp_all_targets,
obj->arp_validate,
obj->ad_actor_sys_prio,
obj->ad_user_port_key,
obj->ad_actor_system,
obj->primary_reselect,
obj->fail_over_mac,
obj->xmit_hash_policy,
obj->num_unsol_na,
obj->num_grat_arp,
obj->all_ports_active,
obj->lacp_rate,
obj->ad_select,
NM_HASH_COMBINE_BOOLS(guint8, obj->use_carrier, obj->tlb_dynamic_lb));
}
int
nm_platform_lnk_bond_cmp(const NMPlatformLnkBond *a, const NMPlatformLnkBond *b)
{
NM_CMP_SELF(a, b);
NM_CMP_FIELD(a, b, mode);
NM_CMP_FIELD(a, b, primary);
NM_CMP_FIELD_MEMCMP(a, b, arp_ip_target);
NM_CMP_FIELD(a, b, miimon);
NM_CMP_FIELD(a, b, updelay);
NM_CMP_FIELD(a, b, downdelay);
NM_CMP_FIELD(a, b, arp_interval);
NM_CMP_FIELD(a, b, resend_igmp);
NM_CMP_FIELD(a, b, min_links);
NM_CMP_FIELD(a, b, lp_interval);
NM_CMP_FIELD(a, b, packets_per_port);
NM_CMP_FIELD(a, b, peer_notif_delay);
NM_CMP_FIELD(a, b, arp_all_targets);
NM_CMP_FIELD(a, b, arp_validate);
NM_CMP_FIELD(a, b, ad_actor_sys_prio);
NM_CMP_FIELD(a, b, ad_user_port_key);
NM_CMP_FIELD_MEMCMP(a, b, ad_actor_system);
NM_CMP_FIELD(a, b, primary_reselect);
NM_CMP_FIELD(a, b, fail_over_mac);
NM_CMP_FIELD(a, b, xmit_hash_policy);
NM_CMP_FIELD(a, b, num_unsol_na);
NM_CMP_FIELD(a, b, num_grat_arp);
NM_CMP_FIELD(a, b, all_ports_active);
NM_CMP_FIELD(a, b, lacp_rate);
NM_CMP_FIELD(a, b, ad_select);
NM_CMP_FIELD_BOOL(a, b, use_carrier);
NM_CMP_FIELD_BOOL(a, b, tlb_dynamic_lb);
return 0;
}
int
nm_platform_lnk_bridge_cmp(const NMPlatformLnkBridge *a, const NMPlatformLnkBridge *b)
{

View file

@ -881,6 +881,42 @@ typedef struct {
extern const NMPlatformLnkBridge nm_platform_lnk_bridge_default;
/* Defined in net/bonding.h. */
#define NM_BOND_MAX_ARP_TARGETS 16
G_STATIC_ASSERT(NM_BOND_MAX_ARP_TARGETS == 16);
typedef struct {
struct in_addr arp_ip_target[NM_BOND_MAX_ARP_TARGETS];
guint32 primary;
guint32 miimon;
guint32 updelay;
guint32 downdelay;
guint32 arp_interval;
guint32 resend_igmp;
guint32 min_links;
guint32 lp_interval;
guint32 packets_per_port;
guint32 peer_notif_delay;
guint32 arp_all_targets;
guint32 arp_validate;
guint16 ad_actor_sys_prio;
guint16 ad_user_port_key;
NMEtherAddr ad_actor_system;
guint8 mode;
guint8 primary_reselect;
guint8 fail_over_mac;
guint8 xmit_hash_policy;
guint8 num_unsol_na;
guint8 num_grat_arp;
guint8 all_ports_active;
guint8 lacp_rate;
guint8 ad_select;
guint8 arp_ip_targets_num;
bool use_carrier : 1;
bool tlb_dynamic_lb : 1;
} NMPlatformLnkBond;
typedef struct {
int parent_ifindex;
in_addr_t local;
@ -1212,6 +1248,7 @@ typedef struct {
gboolean egress_reset_all,
const NMVlanQosMapping *egress_map,
gsize n_egress_map);
gboolean (*link_tun_add)(NMPlatform *self,
const char *name,
const NMPlatformLnkTun *props,
@ -1664,9 +1701,18 @@ nm_platform_link_bridge_change(NMPlatform *self, int ifindex, const NMPlatformLn
}
static inline int
nm_platform_link_bond_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link)
nm_platform_link_bond_change(NMPlatform *self, int ifindex, const NMPlatformLnkBond *props)
{
return nm_platform_link_add(self, NM_LINK_TYPE_BOND, name, 0, NULL, 0, 0, NULL, out_link);
return nm_platform_link_change(self, NM_LINK_TYPE_BOND, ifindex, props);
}
static inline int
nm_platform_link_bond_add(NMPlatform *self,
const char *name,
const NMPlatformLnkBond *props,
const NMPlatformLink **out_link)
{
return nm_platform_link_add(self, NM_LINK_TYPE_BOND, name, 0, NULL, 0, 0, props, out_link);
}
static inline int
@ -2000,6 +2046,8 @@ const NMPObject *nm_platform_link_get_lnk(NMPlatform *self,
int ifindex,
NMLinkType link_type,
const NMPlatformLink **out_link);
const NMPlatformLnkBond *
nm_platform_link_get_lnk_bond(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkBridge *
nm_platform_link_get_lnk_bridge(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkGre *
@ -2294,6 +2342,7 @@ gboolean nm_platform_tc_sync(NMPlatform *self,
GPtrArray *known_tfilters);
const char *nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len);
const char *nm_platform_lnk_bond_to_string(const NMPlatformLnkBond *lnk, char *buf, gsize len);
const char *nm_platform_lnk_bridge_to_string(const NMPlatformLnkBridge *lnk, char *buf, gsize len);
const char *nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len);
const char *
@ -2334,6 +2383,7 @@ const char *
nm_platform_wireguard_peer_to_string(const struct _NMPWireGuardPeer *peer, char *buf, gsize len);
int nm_platform_link_cmp(const NMPlatformLink *a, const NMPlatformLink *b);
int nm_platform_lnk_bond_cmp(const NMPlatformLnkBond *a, const NMPlatformLnkBond *b);
int nm_platform_lnk_bridge_cmp(const NMPlatformLnkBridge *a, const NMPlatformLnkBridge *b);
int nm_platform_lnk_gre_cmp(const NMPlatformLnkGre *a, const NMPlatformLnkGre *b);
int nm_platform_lnk_infiniband_cmp(const NMPlatformLnkInfiniband *a,
@ -2423,6 +2473,7 @@ void nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj,
void nm_platform_routing_rule_hash_update(const NMPlatformRoutingRule *obj,
NMPlatformRoutingRuleCmpType cmp_type,
NMHashState *h);
void nm_platform_lnk_bond_hash_update(const NMPlatformLnkBond *obj, NMHashState *h);
void nm_platform_lnk_bridge_hash_update(const NMPlatformLnkBridge *obj, NMHashState *h);
void nm_platform_lnk_gre_hash_update(const NMPlatformLnkGre *obj, NMHashState *h);
void nm_platform_lnk_infiniband_hash_update(const NMPlatformLnkInfiniband *obj, NMHashState *h);

View file

@ -147,6 +147,7 @@ typedef enum _nm_packed {
NMP_OBJECT_TYPE_LNK_VRF,
NMP_OBJECT_TYPE_LNK_VXLAN,
NMP_OBJECT_TYPE_LNK_WIREGUARD,
NMP_OBJECT_TYPE_LNK_BOND,
__NMP_OBJECT_TYPE_LAST,
NMP_OBJECT_TYPE_MAX = __NMP_OBJECT_TYPE_LAST - 1,

View file

@ -3472,4 +3472,16 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_wireguard_hash_update,
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_wireguard_cmp,
},
[NMP_OBJECT_TYPE_LNK_BOND - 1] =
{
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
.obj_type = NMP_OBJECT_TYPE_LNK_BOND,
.sizeof_data = sizeof(NMPObjectLnkBond),
.sizeof_public = sizeof(NMPlatformLnkBond),
.obj_type_name = "bond",
.lnk_link_type = NM_LINK_TYPE_BOND,
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_bond_to_string,
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_bond_hash_update,
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_bond_cmp,
},
};

View file

@ -249,6 +249,10 @@ typedef struct {
NMPlatformLnkBridge _public;
} NMPObjectLnkBridge;
typedef struct {
NMPlatformLnkBond _public;
} NMPObjectLnkBond;
typedef struct {
NMPlatformLnkGre _public;
} NMPObjectLnkGre;
@ -352,6 +356,9 @@ struct _NMPObject {
NMPlatformLnkBridge lnk_bridge;
NMPObjectLnkBridge _lnk_bridge;
NMPlatformLnkBond lnk_bond;
NMPObjectLnkBond _lnk_bond;
NMPlatformLnkGre lnk_gre;
NMPObjectLnkGre _lnk_gre;
@ -493,6 +500,7 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type)
case NMP_OBJECT_TYPE_TFILTER:
case NMP_OBJECT_TYPE_LNK_BRIDGE:
case NMP_OBJECT_TYPE_LNK_BOND:
case NMP_OBJECT_TYPE_LNK_GRE:
case NMP_OBJECT_TYPE_LNK_GRETAP:
case NMP_OBJECT_TYPE_LNK_INFINIBAND: