platform: support reading bridge VLANs

Add a function to read the list of bridge VLANs on an interface.
This commit is contained in:
Beniamino Galvani
2024-07-11 09:03:05 +02:00
committed by Íñigo Huguet
parent c5d1e35f99
commit 7ae4660a77
3 changed files with 175 additions and 0 deletions

View File

@@ -9576,6 +9576,138 @@ nla_put_failure:
g_return_val_if_reached(FALSE);
}
typedef struct {
int ifindex;
GArray *vlans;
} BridgeVlanData;
static int
get_bridge_vlans_cb(const struct nl_msg *msg, void *arg)
{
static const struct nla_policy policy[] = {
[IFLA_AF_SPEC] = {.type = NLA_NESTED},
};
struct nlattr *tb[G_N_ELEMENTS(policy)];
gboolean is_range = FALSE;
BridgeVlanData *data = arg;
struct ifinfomsg *ifinfo;
struct nlattr *attr;
int rem;
if (nlmsg_parse_arr(nlmsg_hdr(msg), sizeof(struct ifinfomsg), tb, policy) < 0)
return NL_SKIP;
ifinfo = NLMSG_DATA(nlmsg_hdr(msg));
if (ifinfo->ifi_index != data->ifindex)
return NL_SKIP;
if (!tb[IFLA_AF_SPEC])
return NL_SKIP;
nla_for_each_nested (attr, tb[IFLA_AF_SPEC], rem) {
struct bridge_vlan_info vlan_info;
NMPlatformBridgeVlan vlan = {};
if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)
continue;
if (!data->vlans)
data->vlans = g_array_new(0, FALSE, sizeof(NMPlatformBridgeVlan));
vlan_info = *nla_data_as(struct bridge_vlan_info, attr);
if (is_range) {
nm_g_array_index(data->vlans, NMPlatformBridgeVlan, data->vlans->len - 1).vid_end =
vlan_info.vid;
is_range = FALSE;
continue;
} else {
vlan.vid_start = vlan_info.vid;
vlan.vid_end = vlan_info.vid;
vlan.untagged = vlan_info.flags & BRIDGE_VLAN_INFO_UNTAGGED;
vlan.pvid = vlan_info.flags & BRIDGE_VLAN_INFO_PVID;
if (vlan_info.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
is_range = TRUE;
}
g_array_append_val(data->vlans, vlan);
}
return NL_OK;
}
static gboolean
link_get_bridge_vlans(NMPlatform *platform,
int ifindex,
NMPlatformBridgeVlan **out_vlans,
guint *out_num_vlans)
{
gboolean ret = FALSE;
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
struct nl_sock *sk = NULL;
BridgeVlanData data;
int nle;
nlmsg = _nl_msg_new_link_full(RTM_GETLINK, NLM_F_DUMP, 0, NULL, AF_BRIDGE, 0, 0, 0);
if (!nlmsg)
g_return_val_if_reached(FALSE);
nle = nl_socket_new(&sk, NETLINK_ROUTE, NL_SOCKET_FLAGS_DISABLE_MSG_PEEK, 0, 0);
if (nle < 0) {
_LOGD("get-bridge-vlan: error opening socket: %s (%d)", nm_strerror(nle), nle);
ret = FALSE;
goto err;
}
NLA_PUT_U32(nlmsg, IFLA_EXT_MASK, RTEXT_FILTER_BRVLAN_COMPRESSED);
nle = nl_send_auto(sk, nlmsg);
if (nle < 0) {
_LOGD("get-bridge-vlans: failed sending request: %s (%d)", nm_strerror(nle), nle);
ret = FALSE;
goto err;
}
data = ((BridgeVlanData){
.ifindex = ifindex,
});
do {
nle = nl_recvmsgs(sk,
&((const struct nl_cb){
.valid_cb = get_bridge_vlans_cb,
.valid_arg = &data,
}));
} while (nle == -EAGAIN);
if (nle < 0) {
_LOGD("get-bridge-vlan: recv failed: %s (%d)", nm_strerror(nle), nle);
ret = FALSE;
goto err;
}
if (data.vlans) {
NM_SET_OUT(out_vlans, &nm_g_array_index(data.vlans, NMPlatformBridgeVlan, 0));
NM_SET_OUT(out_num_vlans, data.vlans->len);
} else {
NM_SET_OUT(out_vlans, NULL);
NM_SET_OUT(out_num_vlans, 0);
}
if (data.vlans)
g_array_free(data.vlans, !out_vlans);
ret = TRUE;
err:
if (sk)
nl_socket_free(sk);
return ret;
nla_put_failure:
g_return_val_if_reached(FALSE);
}
static gboolean
link_set_bridge_info(NMPlatform *platform,
int ifindex,
@@ -11915,6 +12047,7 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass)
platform_class->link_set_sriov_params_async = link_set_sriov_params_async;
platform_class->link_set_sriov_vfs = link_set_sriov_vfs;
platform_class->link_set_bridge_vlans = link_set_bridge_vlans;
platform_class->link_get_bridge_vlans = link_get_bridge_vlans;
platform_class->link_set_bridge_info = link_set_bridge_info;
platform_class->link_get_physical_port_id = link_get_physical_port_id;

View File

@@ -2099,6 +2099,40 @@ nm_platform_link_set_bridge_vlans(NMPlatform *self,
return klass->link_set_bridge_vlans(self, ifindex, on_controller, vlans, num_vlans);
}
gboolean
nm_platform_link_get_bridge_vlans(NMPlatform *self,
int ifindex,
NMPlatformBridgeVlan **out_vlans,
guint *out_num_vlans)
{
char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
gboolean ret;
guint i;
_CHECK_SELF(self, klass, FALSE);
g_return_val_if_fail(ifindex > 0, FALSE);
g_return_val_if_fail(out_vlans, FALSE);
g_return_val_if_fail(out_num_vlans, FALSE);
_LOG3D("link: getting bridge VLANs");
ret = klass->link_get_bridge_vlans(self, ifindex, out_vlans, out_num_vlans);
if (_LOGD_ENABLED()) {
if (!ret) {
_LOG3D("link: failure while getting bridge vlans");
} else {
for (i = 0; i < *out_num_vlans; i++) {
_LOG3D("link: bridge VLAN %s",
nm_platform_bridge_vlan_to_string(&(*out_vlans)[i], sbuf, sizeof(sbuf)));
}
}
}
return ret;
}
gboolean
nm_platform_link_set_bridge_info(NMPlatform *self,
int ifindex,

View File

@@ -1190,6 +1190,10 @@ typedef struct {
gboolean on_controller,
const NMPlatformBridgeVlan *vlans,
guint num_vlans);
gboolean (*link_get_bridge_vlans)(NMPlatform *self,
int ifindex,
NMPlatformBridgeVlan **out_vlans,
guint *out_num_vlans);
gboolean (*link_set_bridge_info)(NMPlatform *self,
int ifindex,
const NMPlatformLinkSetBridgeInfoData *bridge_info);
@@ -2055,6 +2059,10 @@ gboolean nm_platform_link_set_bridge_vlans(NMPlatform *self,
gboolean on_controller,
const NMPlatformBridgeVlan *vlans,
guint num_vlans);
gboolean nm_platform_link_get_bridge_vlans(NMPlatform *self,
int ifindex,
NMPlatformBridgeVlan **out_vlans,
guint *out_num_vlans);
gboolean nm_platform_link_set_bridge_info(NMPlatform *self,
int ifindex,
const NMPlatformLinkSetBridgeInfoData *bridge_info);