platform: add netlink support for bond port options

sysfs is deprecated and kernel will not add new bond port 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.

(cherry picked from commit bb435674b5)
(cherry picked from commit 1bce7f0dec)
This commit is contained in:
Fernando Fernandez Mancera 2023-03-03 16:36:23 +01:00
parent 836d7511e8
commit ee592c02dd
7 changed files with 250 additions and 33 deletions

View file

@ -223,24 +223,18 @@ controller_update_port_connection(NMDevice *self,
NMConnection *connection,
GError **error)
{
NMSettingBondPort *s_port;
int ifindex_port = nm_device_get_ifindex(port);
NMConnection *applied_connection = nm_device_get_applied_connection(self);
uint queue_id = NM_BOND_PORT_QUEUE_ID_DEF;
gs_free char *queue_id_str = NULL;
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);
queue_id_str =
nm_platform_sysctl_slave_get_option(nm_device_get_platform(self), ifindex_port, "queue_id");
if (queue_id_str) {
queue_id =
_nm_utils_ascii_str_to_int64(queue_id_str, 10, 0, 65535, NM_BOND_PORT_QUEUE_ID_DEF);
g_object_set(s_port, NM_SETTING_BOND_PORT_QUEUE_ID, queue_id, NULL);
} else
_LOGW(LOGD_BOND, "failed to read bond port setting '%s'", NM_SETTING_BOND_PORT_QUEUE_ID);
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, NULL);
g_object_set(nm_connection_get_setting_connection(connection),
NM_SETTING_CONNECTION_MASTER,
@ -501,23 +495,11 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
static void
commit_port_options(NMDevice *bond_device, NMDevice *port, NMSettingBondPort *s_port)
{
char queue_id_str[IFNAMSIZ + NM_STRLEN(":") + 5 + 100];
/*
* The queue-id of bond port is read only, we should modify bond interface using:
* echo "eth1:2" > /sys/class/net/bond0/bonding/queue_id
* Kernel allows parital editing, so no need to care about other bond ports.
*/
g_snprintf(queue_id_str,
sizeof(queue_id_str),
"%s:%" G_GUINT32_FORMAT,
nm_device_get_iface(port),
s_port ? nm_setting_bond_port_get_queue_id(s_port) : NM_BOND_PORT_QUEUE_ID_DEF);
nm_platform_sysctl_master_set_option(nm_device_get_platform(bond_device),
nm_device_get_ifindex(bond_device),
"queue_id",
queue_id_str);
nm_platform_link_change(
nm_device_get_platform(port),
nm_device_get_ifindex(port),
&((NMPlatformLinkBondPort){.queue_id = s_port ? nm_setting_bond_port_get_queue_id(s_port)
: NM_BOND_PORT_QUEUE_ID_DEF}));
}
static NMTernary

View file

@ -667,6 +667,29 @@ link_supports_sriov(NMPlatform *platform, int ifindex)
}
}
static gboolean
link_change(NMPlatform *platform,
int ifindex,
NMPortKind port_kind,
const NMPlatformLinkPortData *port_data)
{
NMFakePlatformLink *device = link_get(platform, ifindex);
nm_auto_nmpobj NMPObject *obj_tmp = NULL;
switch (port_kind) {
case NM_PORT_KIND_BOND:
obj_tmp = nmp_object_clone(device->obj, FALSE);
obj_tmp->link.port_kind = NM_PORT_KIND_BOND;
obj_tmp->link.port_data.bond.queue_id = port_data->bond.queue_id;
link_set_obj(platform, device, obj_tmp);
return TRUE;
case NM_PORT_KIND_NONE:
return TRUE;
}
return nm_assert_unreachable_val(TRUE);
}
static gboolean
link_enslave(NMPlatform *platform, int master, int slave)
{
@ -1322,6 +1345,7 @@ nm_fake_platform_class_init(NMFakePlatformClass *klass)
platform_class->link_set_address = link_set_address;
platform_class->link_set_mtu = link_set_mtu;
platform_class->link_change = link_change;
platform_class->link_change_flags = link_change_flags;
platform_class->link_get_driver_info = link_get_driver_info;

View file

@ -257,6 +257,21 @@ test_slave(int master, int type, SignalData *master_changed)
else
g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex));
if (NM_IN_SET(link_type, NM_LINK_TYPE_BOND)) {
const NMPlatformLink *link;
NMPlatformLinkBondPort bond_port;
bond_port = (NMPlatformLinkBondPort){
.queue_id = 5,
};
g_assert(nm_platform_link_change(NM_PLATFORM_GET, ifindex, &bond_port));
accept_signals(link_changed, 1, 3);
link = nmtstp_link_get(NM_PLATFORM_GET, ifindex, SLAVE_NAME);
g_assert(link);
g_assert_cmpint(link->port_data.bond.queue_id, ==, 5);
}
test_link_changed_signal_arg1 = FALSE;
test_link_changed_signal_arg2 = FALSE;
g_signal_connect(NM_PLATFORM_GET,

View file

@ -93,6 +93,14 @@ G_STATIC_ASSERT(sizeof(int) == sizeof(gint32));
/*****************************************************************************/
typedef enum _nm_packed {
/* No type, empty value */
NM_PORT_KIND_NONE,
NM_PORT_KIND_BOND,
} NMPortKind;
/*****************************************************************************/
typedef enum {
/* No type, used as error value */

View file

@ -3241,9 +3241,11 @@ _new_from_nl_link(NMPlatform *platform,
if (tb[IFLA_LINKINFO]) {
static const struct nla_policy policy_link_info[] = {
[IFLA_INFO_KIND] = {.type = NLA_STRING},
[IFLA_INFO_DATA] = {.type = NLA_NESTED},
[IFLA_INFO_XSTATS] = {.type = NLA_NESTED},
[IFLA_INFO_KIND] = {.type = NLA_STRING},
[IFLA_INFO_DATA] = {.type = NLA_NESTED},
[IFLA_INFO_XSTATS] = {.type = NLA_NESTED},
[IFLA_INFO_SLAVE_KIND] = {.type = NLA_STRING},
[IFLA_INFO_SLAVE_DATA] = {.type = NLA_NESTED},
};
struct nlattr *li[G_N_ELEMENTS(policy_link_info)];
@ -3254,6 +3256,33 @@ _new_from_nl_link(NMPlatform *platform,
nl_info_kind = nla_get_string(li[IFLA_INFO_KIND]);
nl_info_data = li[IFLA_INFO_DATA];
if (li[IFLA_INFO_SLAVE_KIND]) {
const char *s = nla_get_string(li[IFLA_INFO_SLAVE_KIND]);
if (nm_streq(s, "bond"))
obj->link.port_kind = NM_PORT_KIND_BOND;
}
if (li[IFLA_INFO_SLAVE_DATA]) {
static const struct nla_policy policy_bond_port[] = {
[IFLA_BOND_SLAVE_QUEUE_ID] = {.type = NLA_U16},
};
struct nlattr *bp[G_N_ELEMENTS(policy_bond_port)];
switch (obj->link.port_kind) {
case NM_PORT_KIND_BOND:
if (nla_parse_nested_arr(bp, li[IFLA_INFO_SLAVE_DATA], policy_bond_port) < 0)
return NULL;
if (bp[IFLA_BOND_SLAVE_QUEUE_ID])
obj->link.port_data.bond.queue_id = nla_get_u16(bp[IFLA_BOND_SLAVE_QUEUE_ID]);
break;
case NM_PORT_KIND_NONE:
break;
}
}
}
if (tb[IFLA_STATS64]) {
@ -8061,6 +8090,48 @@ link_delete(NMPlatform *platform, int ifindex)
return do_delete_object(platform, &obj_id, nlmsg);
}
static gboolean
link_change(NMPlatform *platform,
int ifindex,
NMPortKind port_kind,
const NMPlatformLinkPortData *port_data)
{
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
struct nlattr *nl_info;
struct nlattr *nl_port_data;
nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL);
if (!nlmsg)
return FALSE;
switch (port_kind) {
case NM_PORT_KIND_BOND:
nm_assert(port_data);
if (!(nl_info = nla_nest_start(nlmsg, IFLA_LINKINFO)))
goto nla_put_failure;
nm_assert(nm_streq0("bond", nm_link_type_to_rtnl_type_string(NM_LINK_TYPE_BOND)));
NLA_PUT_STRING(nlmsg, IFLA_INFO_SLAVE_KIND, "bond");
if (!(nl_port_data = nla_nest_start(nlmsg, IFLA_INFO_SLAVE_DATA)))
goto nla_put_failure;
NLA_PUT_U16(nlmsg, IFLA_BOND_SLAVE_QUEUE_ID, port_data->bond.queue_id);
nla_nest_end(nlmsg, nl_port_data);
nla_nest_end(nlmsg, nl_info);
break;
case NM_PORT_KIND_NONE:
break;
}
return do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == 0;
nla_put_failure:
g_return_val_if_reached(FALSE);
}
static gboolean
link_refresh(NMPlatform *platform, int ifindex)
{
@ -10837,6 +10908,8 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass)
platform_class->link_change_extra = link_change_extra;
platform_class->link_delete = link_delete;
platform_class->link_change = link_change;
platform_class->link_refresh = link_refresh;
platform_class->link_set_netns = link_set_netns;

View file

@ -61,6 +61,31 @@ G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_address.data) == _NM_UTILS_H
G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_perm_address.data) == _NM_UTILS_HWADDR_LEN_MAX);
G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_broadcast.data) == _NM_UTILS_HWADDR_LEN_MAX);
static const char *
_nmp_link_port_data_to_string(NMPortKind port_kind,
const NMPlatformLinkPortData *port_data,
char *sbuf,
gsize sbuf_len)
{
const char *sbuf0 = sbuf;
nm_assert(port_data);
switch (port_kind) {
case NM_PORT_KIND_NONE:
nm_strbuf_append_c(&sbuf, &sbuf_len, '\0');
goto out;
case NM_PORT_KIND_BOND:
nm_strbuf_append(&sbuf, &sbuf_len, "port bond queue-id %u", port_data->bond.queue_id);
goto out;
}
nm_strbuf_append(&sbuf, &sbuf_len, "invalid-port-type %d", (int) port_kind);
out:
return sbuf0;
}
static const char *
_nmp_link_address_to_string(const NMPLinkAddress *addr,
char buf[static(_NM_UTILS_HWADDR_LEN_MAX * 3)])
@ -2092,6 +2117,31 @@ nm_platform_link_set_name(NMPlatform *self, int ifindex, const char *name)
return klass->link_set_name(self, ifindex, name);
}
gboolean
nm_platform_link_change(NMPlatform *self, int ifindex, NMPlatformLinkBondPort *bond_port)
{
_CHECK_SELF(self, klass, FALSE);
g_return_val_if_fail(ifindex >= 0, FALSE);
if (_LOGD_ENABLED()) {
nm_auto_free_gstring GString *str = g_string_new("");
if (bond_port)
g_string_append_printf(str, "bond-port queue-id %d", bond_port->queue_id);
if (str->len > 0 && str->str[str->len - 1] == ' ')
g_string_truncate(str, str->len - 1);
_LOG3D("link: change: %s", str->str);
}
return klass->link_change(self,
ifindex,
bond_port ? NM_PORT_KIND_BOND : NM_PORT_KIND_NONE,
(const NMPlatformLinkPortData *) bond_port);
}
/**
* nm_platform_link_get_physical_port_id:
* @self: platform instance
@ -5893,6 +5943,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
char *s;
gsize l;
char str_addrmode[30];
char str_port_data[200];
char str_address[_NM_UTILS_HWADDR_LEN_MAX * 3];
char str_perm_address[_NM_UTILS_HWADDR_LEN_MAX * 3];
char str_broadcast[_NM_UTILS_HWADDR_LEN_MAX * 3];
@ -5936,6 +5987,11 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
_nmp_link_address_to_string(&link->l_perm_address, str_perm_address);
_nmp_link_address_to_string(&link->l_broadcast, str_broadcast);
_nmp_link_port_data_to_string(link->port_kind,
&link->port_data,
str_port_data,
sizeof(str_port_data));
str_link_type = nm_link_type_to_string(link->type);
g_snprintf(
@ -5957,6 +6013,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
"%s%s" /* l_broadcast */
"%s%s" /* inet6_token */
"%s%s" /* driver */
"%s%s" /* port_data */
" rx:%" G_GUINT64_FORMAT ",%" G_GUINT64_FORMAT " tx:%" G_GUINT64_FORMAT
",%" G_GUINT64_FORMAT,
link->ifindex,
@ -5989,6 +6046,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
: "",
link->driver ? " driver " : "",
link->driver ?: "",
NM_PRINT_FMT_QUOTED2(str_port_data[0] != '\0', " ", str_port_data, ""),
link->rx_packets,
link->rx_bytes,
link->tx_packets,
@ -7927,6 +7985,7 @@ nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h)
obj->arptype,
obj->inet6_addr_gen_mode_inv,
obj->inet6_token,
obj->port_kind,
obj->rx_packets,
obj->rx_bytes,
obj->tx_packets,
@ -7945,6 +8004,20 @@ nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h)
nm_hash_update_mem(h,
obj->l_broadcast.data,
NM_MIN(obj->l_broadcast.len, sizeof(obj->l_broadcast.data)));
switch (obj->port_kind) {
case NM_PORT_KIND_NONE:
break;
case NM_PORT_KIND_BOND:
nm_platform_link_bond_port_hash_update(&obj->port_data.bond, h);
break;
}
}
void
nm_platform_link_bond_port_hash_update(const NMPlatformLinkBondPort *obj, NMHashState *h)
{
nm_hash_update_vals(h, obj->queue_id);
}
int
@ -7974,6 +8047,14 @@ nm_platform_link_cmp(const NMPlatformLink *a, const NMPlatformLink *b)
if (a->l_broadcast.len)
NM_CMP_FIELD_MEMCMP_LEN(a, b, l_broadcast.data, a->l_broadcast.len);
NM_CMP_FIELD_MEMCMP(a, b, inet6_token);
NM_CMP_FIELD(a, b, port_kind);
switch (a->port_kind) {
case NM_PORT_KIND_NONE:
break;
case NM_PORT_KIND_BOND:
NM_CMP_RETURN(nm_platform_link_bond_port_cmp(&a->port_data.bond, &b->port_data.bond));
break;
}
NM_CMP_FIELD(a, b, rx_packets);
NM_CMP_FIELD(a, b, rx_bytes);
NM_CMP_FIELD(a, b, tx_packets);
@ -8053,6 +8134,15 @@ nm_platform_lnk_bond_hash_update(const NMPlatformLnkBond *obj, NMHashState *h)
nm_hash_update(h, obj->arp_ip_target, obj->arp_ip_targets_num * sizeof(obj->arp_ip_target[0]));
}
int
nm_platform_link_bond_port_cmp(const NMPlatformLinkBondPort *a, const NMPlatformLinkBondPort *b)
{
NM_CMP_SELF(a, b);
NM_CMP_FIELD(a, b, queue_id);
return 0;
}
int
nm_platform_lnk_bond_cmp(const NMPlatformLnkBond *a, const NMPlatformLnkBond *b)
{

View file

@ -216,6 +216,14 @@ struct _NMPlatformObjWithIfindex {
__NMPlatformObjWithIfindex_COMMON;
};
typedef struct {
guint16 queue_id;
} NMPlatformLinkBondPort;
typedef union {
NMPlatformLinkBondPort bond;
} NMPlatformLinkPortData;
struct _NMPlatformLink {
__NMPlatformObjWithIfindex_COMMON;
char name[NMP_IFNAMSIZ];
@ -266,6 +274,12 @@ struct _NMPlatformLink {
guint64 tx_packets;
guint64 tx_bytes;
/* IFLA_INFO_SLAVE_KIND */
NMPortKind port_kind;
/* an interface can only hold IFLA_INFO_SLAVE_DATA for one link type */
NMPlatformLinkPortData port_data;
/* @connected is mostly identical to (@n_ifi_flags & IFF_UP). Except for bridge/bond masters,
* where we coerce the link as disconnect if it has no slaves. */
bool connected : 1;
@ -1226,6 +1240,10 @@ typedef struct {
NMLinkType type,
int ifindex,
gconstpointer extra_data);
gboolean (*link_change)(NMPlatform *self,
int ifindex,
NMPortKind port_kind,
const NMPlatformLinkPortData *port_data);
gboolean (*link_delete)(NMPlatform *self, int ifindex);
gboolean (*link_refresh)(NMPlatform *self, int ifindex);
gboolean (*link_set_netns)(NMPlatform *self, int ifindex, int netns_fd);
@ -2073,6 +2091,8 @@ nm_platform_link_change_flags(NMPlatform *self, int ifindex, unsigned value, gbo
return nm_platform_link_change_flags_full(self, ifindex, value, set ? value : 0u);
}
gboolean nm_platform_link_change(NMPlatform *self, int ifindex, NMPlatformLinkBondPort *bond_port);
gboolean nm_platform_link_get_udev_property(NMPlatform *self,
int ifindex,
const char *name,
@ -2563,6 +2583,11 @@ int nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter
int nm_platform_mptcp_addr_cmp(const NMPlatformMptcpAddr *a, const NMPlatformMptcpAddr *b);
void nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h);
void nm_platform_link_bond_port_hash_update(const NMPlatformLinkBondPort *obj, NMHashState *h);
int nm_platform_link_bond_port_cmp(const NMPlatformLinkBondPort *a,
const NMPlatformLinkBondPort *b);
void nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h);
void nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h);
void nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj,