From 37704bb2b696276daa47f6ba3ff29e9633b35fe0 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 11 Jul 2024 09:03:05 +0200 Subject: [PATCH] platform: support reading bridge VLANs Add a function to read the list of bridge VLANs on an interface. (cherry picked from commit 7ae4660a77c686bb9238b9570ee93ebee82a972e) (cherry picked from commit dfaadf3d3e75b172202d20116ca670d9da5076f6) (cherry picked from commit 707a3a34ae29a4554c1bca6f6a350b347bfe6b81) (cherry picked from commit 0f8faa0d34860edb684dc47935256fd39f158928) --- src/libnm-platform/nm-linux-platform.c | 133 +++++++++++++++++++++++++ src/libnm-platform/nm-platform.c | 34 +++++++ src/libnm-platform/nm-platform.h | 8 ++ 3 files changed, 175 insertions(+) diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index e791c36a14..fb9e85b8ed 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -8972,6 +8972,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, @@ -11285,6 +11417,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; diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index fbc9c4a44c..8d50213b07 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -2063,6 +2063,40 @@ nm_platform_link_set_bridge_vlans(NMPlatform *self, return klass->link_set_bridge_vlans(self, ifindex, on_master, 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, diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index a3efd6f276..14062d8176 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -1151,6 +1151,10 @@ typedef struct { gboolean on_master, 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); @@ -1992,6 +1996,10 @@ gboolean nm_platform_link_set_bridge_vlans(NMPlatform *self, gboolean on_master, 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);