merge: branch 'bg/bridge-vlan-reapply'

Support reapplying bridge port VLANs

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2002
This commit is contained in:
Íñigo Huguet 2024-08-21 05:30:37 +00:00
commit 926bfab5b5
11 changed files with 702 additions and 70 deletions

1
NEWS
View file

@ -23,6 +23,7 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
* ndisc: Support multiple gateways for a single network
* wifi: Support configuring channel-width in AP mode
* keyfile: Stop writing offensive terms into keyfiles
* Support reapplying the VLANs on bridge ports.
=============================================
NetworkManager-1.48

View file

@ -13,6 +13,7 @@
#include "NetworkManagerUtils.h"
#include "nm-device-private.h"
#include "libnm-platform/nm-platform.h"
#include "libnm-platform/nm-platform-utils.h"
#include "nm-device-factory.h"
#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
#include "libnm-core-intern/nm-core-internal.h"
@ -419,20 +420,18 @@ static const Option controller_options[] = {
0,
}};
static const NMPlatformBridgeVlan **
setting_vlans_to_platform(GPtrArray *array)
static NMPlatformBridgeVlan *
setting_vlans_to_platform(GPtrArray *array, guint *out_len)
{
NMPlatformBridgeVlan **arr;
NMPlatformBridgeVlan *p_data;
guint i;
NMPlatformBridgeVlan *arr;
guint i;
if (!array || !array->len)
if (!array || !array->len) {
*out_len = 0;
return NULL;
}
G_STATIC_ASSERT_EXPR(_nm_alignof(NMPlatformBridgeVlan *) >= _nm_alignof(NMPlatformBridgeVlan));
arr = g_malloc((sizeof(NMPlatformBridgeVlan *) * (array->len + 1))
+ (sizeof(NMPlatformBridgeVlan) * (array->len)));
p_data = (NMPlatformBridgeVlan *) &arr[array->len + 1];
arr = g_new(NMPlatformBridgeVlan, array->len);
for (i = 0; i < array->len; i++) {
NMBridgeVlan *vlan = array->pdata[i];
@ -440,16 +439,16 @@ setting_vlans_to_platform(GPtrArray *array)
nm_bridge_vlan_get_vid_range(vlan, &vid_start, &vid_end);
p_data[i] = (NMPlatformBridgeVlan){
arr[i] = (NMPlatformBridgeVlan){
.vid_start = vid_start,
.vid_end = vid_end,
.pvid = nm_bridge_vlan_is_pvid(vlan),
.untagged = nm_bridge_vlan_is_untagged(vlan),
};
arr[i] = &p_data[i];
}
arr[i] = NULL;
return (const NMPlatformBridgeVlan **) arr;
*out_len = array->len;
return arr;
}
static void
@ -639,15 +638,16 @@ is_bridge_pvid_changed(NMDevice *device, NMSettingBridge *s_bridge)
static gboolean
bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge, gboolean is_reapply)
{
NMDeviceBridge *self = NM_DEVICE_BRIDGE(device);
gconstpointer hwaddr;
size_t length;
gboolean enabled;
guint16 pvid;
NMPlatform *plat;
int ifindex;
gs_unref_ptrarray GPtrArray *vlans = NULL;
gs_free const NMPlatformBridgeVlan **plat_vlans = NULL;
NMDeviceBridge *self = NM_DEVICE_BRIDGE(device);
gconstpointer hwaddr;
size_t length;
gboolean enabled;
guint16 pvid;
NMPlatform *plat;
int ifindex;
gs_unref_ptrarray GPtrArray *vlans = NULL;
gs_free NMPlatformBridgeVlan *plat_vlans = NULL;
guint num_vlans;
if (self->vlan_configured)
return TRUE;
@ -664,7 +664,7 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge, gboolean is
.vlan_filtering_val = FALSE,
.vlan_default_pvid_has = TRUE,
.vlan_default_pvid_val = 1}));
nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, NULL);
nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, NULL, 0);
return TRUE;
}
@ -696,7 +696,7 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge, gboolean is
.vlan_default_pvid_val = 0}));
/* Clear all existing VLANs */
if (!nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, NULL))
if (!nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, NULL, 0))
return FALSE;
/* Now set the default PVID. After this point the kernel creates
@ -714,8 +714,9 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge, gboolean is
/* Create VLANs only after setting the default PVID, so that
* any PVID VLAN overrides the bridge's default PVID. */
g_object_get(s_bridge, NM_SETTING_BRIDGE_VLANS, &vlans, NULL);
plat_vlans = setting_vlans_to_platform(vlans);
if (plat_vlans && !nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, plat_vlans))
plat_vlans = setting_vlans_to_platform(vlans, &num_vlans);
if (plat_vlans
&& !nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, plat_vlans, num_vlans))
return FALSE;
nm_platform_link_set_bridge_info(plat,
@ -728,6 +729,121 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge, gboolean is
return TRUE;
}
static NMPlatformBridgeVlan *
merge_bridge_vlan_default_pvid(NMPlatformBridgeVlan *vlans, guint *num_vlans, guint default_pvid)
{
NMPlatformBridgeVlan *vlan;
gboolean has_pvid = FALSE;
guint i;
for (i = 0; i < *num_vlans; i++) {
if (vlans[i].pvid) {
has_pvid = TRUE;
break;
}
}
/* search if the list of VLANs already contains the default PVID */
vlan = NULL;
for (i = 0; i < *num_vlans; i++) {
if (default_pvid >= vlans[i].vid_start && default_pvid <= vlans[i].vid_end) {
vlan = &vlans[i];
break;
}
}
if (!vlan) {
/* VLAN id not found, append the default PVID at the end.
* Set the PVID flag only if the port didn't have one. */
vlans = g_realloc_n(vlans, *num_vlans + 1, sizeof(NMPlatformBridgeVlan));
(*num_vlans)++;
vlans[*num_vlans - 1] = (NMPlatformBridgeVlan){
.vid_start = default_pvid,
.vid_end = default_pvid,
.untagged = TRUE,
.pvid = !has_pvid,
};
}
return vlans;
}
void
nm_device_reapply_bridge_port_vlans(NMDevice *device)
{
NMDevice *self = device; /* for logging */
NMSettingBridgePort *s_bridge_port;
NMDevice *controller;
NMSettingBridge *s_bridge;
gs_unref_ptrarray GPtrArray *tmp_vlans = NULL;
gs_free NMPlatformBridgeVlan *setting_vlans = NULL;
gs_free NMPlatformBridgeVlan *plat_vlans = NULL;
guint num_setting_vlans = 0;
guint num_plat_vlans = 0;
NMPlatform *plat;
int ifindex;
gboolean do_reapply;
s_bridge_port = nm_device_get_applied_setting(device, NM_TYPE_SETTING_BRIDGE_PORT);
if (!s_bridge_port)
return;
controller = nm_device_get_controller(device);
if (!controller)
return;
s_bridge = nm_device_get_applied_setting(controller, NM_TYPE_SETTING_BRIDGE);
if (!s_bridge)
return;
if (nm_setting_bridge_get_vlan_filtering(s_bridge)) {
g_object_get(s_bridge_port, NM_SETTING_BRIDGE_PORT_VLANS, &tmp_vlans, NULL);
setting_vlans = setting_vlans_to_platform(tmp_vlans, &num_setting_vlans);
/* During a regular activation, we first set the default_pvid on the bridge
* (which creates the PVID VLAN on the port) and then add the VLANs on the port.
* This ensures that the PVID VLAN is inherited from the bridge, but it's
* overridden if the port specifies one.
* During a reapply on the port, we are not going to touch the bridge and
* so we need to merge manually the PVID from the bridge with the port VLANs. */
setting_vlans =
merge_bridge_vlan_default_pvid(setting_vlans,
&num_setting_vlans,
nm_setting_bridge_get_vlan_default_pvid(s_bridge));
}
plat = nm_device_get_platform(device);
ifindex = nm_device_get_ifindex(device);
if (!nm_platform_link_get_bridge_vlans(plat, ifindex, &plat_vlans, &num_plat_vlans)) {
_LOGD(LOGD_DEVICE, "reapply-bridge-port-vlans: can't get current VLANs from platform");
do_reapply = TRUE;
} else {
nmp_utils_bridge_vlan_normalize(setting_vlans, &num_setting_vlans);
nmp_utils_bridge_vlan_normalize(plat_vlans, &num_plat_vlans);
if (!nmp_utils_bridge_normalized_vlans_equal(setting_vlans,
num_setting_vlans,
plat_vlans,
num_plat_vlans)) {
_LOGD(LOGD_DEVICE, "reapply-bridge-port-vlans: VLANs in platform need reapply");
do_reapply = TRUE;
} else {
_LOGD(LOGD_DEVICE, "reapply-bridge-port-vlans: VLANs in platform didn't change");
do_reapply = FALSE;
}
}
if (do_reapply) {
nm_platform_link_set_bridge_vlans(plat, ifindex, TRUE, NULL, 0);
if (num_setting_vlans > 0)
nm_platform_link_set_bridge_vlans(plat,
ifindex,
TRUE,
setting_vlans,
num_setting_vlans);
}
}
static void
_platform_lnk_bridge_init_from_setting(NMSettingBridge *s_bridge, NMPlatformLnkBridge *props)
{
@ -937,13 +1053,14 @@ attach_port(NMDevice *device,
bridge_set_vlan_options(device, s_bridge, FALSE);
if (nm_setting_bridge_get_vlan_filtering(s_bridge)) {
gs_free const NMPlatformBridgeVlan **plat_vlans = NULL;
gs_unref_ptrarray GPtrArray *vlans = NULL;
gs_free NMPlatformBridgeVlan *plat_vlans = NULL;
gs_unref_ptrarray GPtrArray *vlans = NULL;
guint num_vlans;
if (s_port)
g_object_get(s_port, NM_SETTING_BRIDGE_PORT_VLANS, &vlans, NULL);
plat_vlans = setting_vlans_to_platform(vlans);
plat_vlans = setting_vlans_to_platform(vlans, &num_vlans);
/* Since the link was just enportd, there are no existing VLANs
* (except for the default one) and so there's no need to flush. */
@ -952,7 +1069,8 @@ attach_port(NMDevice *device,
&& !nm_platform_link_set_bridge_vlans(nm_device_get_platform(port),
nm_device_get_ifindex(port),
TRUE,
plat_vlans))
plat_vlans,
num_vlans))
return FALSE;
}

View file

@ -27,4 +27,6 @@ extern const NMBtVTableNetworkServer *nm_bt_vtable_network_server;
void _nm_device_bridge_notify_unregister_bt_nap(NMDevice *device, const char *reason);
void nm_device_reapply_bridge_port_vlans(NMDevice *device);
#endif /* __NETWORKMANAGER_DEVICE_BRIDGE_H__ */

View file

@ -13881,6 +13881,8 @@ check_and_reapply_connection(NMDevice *self,
if (priv->state >= NM_DEVICE_STATE_ACTIVATED)
nm_device_update_metered(self);
nm_device_reapply_bridge_port_vlans(self);
sett_conn = nm_device_get_settings_connection(self);
if (sett_conn) {
nm_settings_connection_autoconnect_blocked_reason_set(

View file

@ -9505,17 +9505,20 @@ nla_put_failure:
}
static gboolean
link_set_bridge_vlans(NMPlatform *platform,
int ifindex,
gboolean on_controller,
const NMPlatformBridgeVlan *const *vlans)
link_set_bridge_vlans(NMPlatform *platform,
int ifindex,
gboolean on_controller,
const NMPlatformBridgeVlan *vlans,
guint num_vlans)
{
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
struct nlattr *list;
struct bridge_vlan_info vinfo = {};
guint i;
nlmsg = _nl_msg_new_link_full(vlans ? RTM_SETLINK : RTM_DELLINK,
nm_assert(num_vlans == 0 || vlans);
nlmsg = _nl_msg_new_link_full(num_vlans > 0 ? RTM_SETLINK : RTM_DELLINK,
0,
ifindex,
NULL,
@ -9533,10 +9536,10 @@ link_set_bridge_vlans(NMPlatform *platform,
IFLA_BRIDGE_FLAGS,
on_controller ? BRIDGE_FLAGS_CONTROLLER : BRIDGE_FLAGS_SELF);
if (vlans) {
if (num_vlans > 0) {
/* Add VLANs */
for (i = 0; vlans[i]; i++) {
const NMPlatformBridgeVlan *vlan = vlans[i];
for (i = 0; i < num_vlans; i++) {
const NMPlatformBridgeVlan *vlan = &vlans[i];
gboolean is_range = vlan->vid_start != vlan->vid_end;
vinfo.vid = vlan->vid_start;
@ -9573,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,
@ -11912,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

@ -2275,6 +2275,89 @@ nmp_utils_lifetime_get(guint32 timestamp,
/*****************************************************************************/
static int
bridge_vlan_compare(gconstpointer a, gconstpointer b, gpointer user_data)
{
const NMPlatformBridgeVlan *vlan_a = a;
const NMPlatformBridgeVlan *vlan_b = b;
return (int) vlan_a->vid_start - (int) vlan_b->vid_start;
}
/**
* nmp_utils_bridge_vlan_normalize:
* @vlans: the array of VLAN ranges
* @num_vlans: the number of VLAN ranges in the array. On return, it contains
* the new number.
*
* Sort the VLAN ranges and merge those that are contiguous or overlapping. It
* must not contain invalid data such as 2 overlapping ranges with different
* flags.
*/
void
nmp_utils_bridge_vlan_normalize(NMPlatformBridgeVlan *vlans, guint *num_vlans)
{
guint i;
if (*num_vlans <= 1)
return;
g_qsort_with_data(vlans, *num_vlans, sizeof(NMPlatformBridgeVlan), bridge_vlan_compare, NULL);
/* Merge VLAN ranges that are contiguous or overlap */
i = 0;
while (i < *num_vlans - 1) {
guint j = i + 1;
gboolean can_merge = vlans[j].vid_start <= vlans[i].vid_end + 1
&& vlans[j].pvid == vlans[i].pvid
&& vlans[j].untagged == vlans[i].untagged;
if (can_merge) {
vlans[i].vid_end = NM_MAX(vlans[i].vid_end, vlans[j].vid_end);
for (; j < *num_vlans - 1; j++)
vlans[j] = vlans[j + 1];
*num_vlans -= 1;
} else {
i++;
}
}
}
/**
* nmp_utils_bridge_normalized_vlans_equal:
* @vlans_a: the first array of bridge VLANs
* @num_vlans_a: the number of elements of first array
* @vlans_b: the second array of bridge VLANs
* @num_vlans_b: the number of elements of second array
*
* Given two arrays of bridge VLAN ranges, compare if they are equal,
* i.e. if they represent the same set of VLANs with the same attributes.
* The input arrays must be normalized (sorted and without overlapping or
* duplicated ranges). Normalize with nmp_utils_bridge_vlan_normalize().
*/
gboolean
nmp_utils_bridge_normalized_vlans_equal(const NMPlatformBridgeVlan *vlans_a,
guint num_vlans_a,
const NMPlatformBridgeVlan *vlans_b,
guint num_vlans_b)
{
guint i;
if (num_vlans_a != num_vlans_b)
return FALSE;
for (i = 0; i < num_vlans_a; i++) {
if (vlans_a[i].vid_start != vlans_b[i].vid_start || vlans_a[i].vid_end != vlans_b[i].vid_end
|| vlans_a[i].pvid != vlans_b[i].pvid || vlans_a[i].untagged != vlans_b[i].untagged) {
return FALSE;
}
}
return TRUE;
}
/*****************************************************************************/
static const char *
_trunk_first_line(char *str)
{

View file

@ -99,4 +99,11 @@ guint32 nmp_utils_lifetime_get(guint32 timestamp,
int nmp_utils_modprobe(GError **error, gboolean suppress_error_logging, const char *arg1, ...)
G_GNUC_NULL_TERMINATED;
void nmp_utils_bridge_vlan_normalize(NMPlatformBridgeVlan *vlans, guint *num_vlans);
gboolean nmp_utils_bridge_normalized_vlans_equal(const NMPlatformBridgeVlan *vlans_a,
guint num_vlans_a,
const NMPlatformBridgeVlan *vlans_b,
guint num_vlans_b);
#endif /* __NM_PLATFORM_UTILS_H__ */

View file

@ -2070,10 +2070,11 @@ nm_platform_link_set_sriov_vfs(NMPlatform *self, int ifindex, const NMPlatformVF
}
gboolean
nm_platform_link_set_bridge_vlans(NMPlatform *self,
int ifindex,
gboolean on_controller,
const NMPlatformBridgeVlan *const *vlans)
nm_platform_link_set_bridge_vlans(NMPlatform *self,
int ifindex,
gboolean on_controller,
const NMPlatformBridgeVlan *vlans,
guint num_vlans)
{
guint i;
_CHECK_SELF(self, klass, FALSE);
@ -2085,9 +2086,9 @@ nm_platform_link_set_bridge_vlans(NMPlatform *self,
vlans ? "setting" : "clearing",
on_controller ? "controller" : "self");
if (vlans) {
for (i = 0; vlans[i]; i++) {
for (i = 0; i < num_vlans; i++) {
char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
const NMPlatformBridgeVlan *vlan = vlans[i];
const NMPlatformBridgeVlan *vlan = &vlans[i];
_LOG3D("link: bridge VLAN %s",
nm_platform_bridge_vlan_to_string(vlan, sbuf, sizeof(sbuf)));
@ -2095,7 +2096,41 @@ nm_platform_link_set_bridge_vlans(NMPlatform *self,
}
}
return klass->link_set_bridge_vlans(self, ifindex, on_controller, vlans);
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
@ -6148,9 +6183,9 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
link->initialized ? " init" : " not-init",
link->inet6_addr_gen_mode_inv ? " addrgenmode " : "",
link->inet6_addr_gen_mode_inv ? nm_platform_link_inet6_addrgenmode2str(
_nm_platform_uint8_inv(link->inet6_addr_gen_mode_inv),
str_addrmode,
sizeof(str_addrmode))
_nm_platform_uint8_inv(link->inet6_addr_gen_mode_inv),
str_addrmode,
sizeof(str_addrmode))
: "",
str_address[0] ? " addr " : "",
str_address[0] ? str_address : "",
@ -7386,12 +7421,11 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz
route->lock_mtu ? "lock " : "",
route->mtu)
: "",
route->rt_pref
? nm_sprintf_buf(
str_pref,
" pref %s",
nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2)))
: "");
route->rt_pref ? nm_sprintf_buf(
str_pref,
" pref %s",
nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2)))
: "");
return buf;
}

View file

@ -741,13 +741,6 @@ typedef struct {
gint8 trust;
} NMPlatformVF;
typedef struct {
guint16 vid_start;
guint16 vid_end;
bool untagged : 1;
bool pvid : 1;
} NMPlatformBridgeVlan;
typedef struct {
guint16 vlan_default_pvid_val;
bool vlan_filtering_val : 1;
@ -1185,10 +1178,15 @@ typedef struct {
gpointer callback_data,
GCancellable *cancellable);
gboolean (*link_set_sriov_vfs)(NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs);
gboolean (*link_set_bridge_vlans)(NMPlatform *self,
int ifindex,
gboolean on_controller,
const NMPlatformBridgeVlan *const *vlans);
gboolean (*link_set_bridge_vlans)(NMPlatform *self,
int ifindex,
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);
@ -2049,10 +2047,15 @@ void nm_platform_link_set_sriov_params_async(NMPlatform *self,
gboolean
nm_platform_link_set_sriov_vfs(NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs);
gboolean nm_platform_link_set_bridge_vlans(NMPlatform *self,
int ifindex,
gboolean on_controller,
const NMPlatformBridgeVlan *const *vlans);
gboolean nm_platform_link_set_bridge_vlans(NMPlatform *self,
int ifindex,
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);

View file

@ -38,6 +38,15 @@ typedef enum {
/*****************************************************************************/
typedef struct {
guint16 vid_start;
guint16 vid_end;
bool untagged : 1;
bool pvid : 1;
} NMPlatformBridgeVlan;
/*****************************************************************************/
typedef struct {
/* We don't want to include <linux/ethtool.h> in header files,
* thus create a ABI compatible version of struct ethtool_drvinfo.*/

View file

@ -190,6 +190,239 @@ test_nmp_link_mode_all_advertised_modes_bits(void)
/*****************************************************************************/
static void
test_nmp_utils_bridge_vlans_normalize(void)
{
NMPlatformBridgeVlan vlans[10];
NMPlatformBridgeVlan expect[10];
guint vlans_len;
/* Single one is unmodified */
vlans[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
expect[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
vlans_len = 1;
nmp_utils_bridge_vlan_normalize(vlans, &vlans_len);
g_assert(vlans_len == 1);
g_assert(nmp_utils_bridge_normalized_vlans_equal(vlans, vlans_len, expect, vlans_len));
/* Not merged if flags are different */
vlans[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
vlans[1] = (NMPlatformBridgeVlan){
.vid_start = 11,
.vid_end = 11,
.pvid = TRUE,
};
vlans[2] = (NMPlatformBridgeVlan){
.vid_start = 20,
.vid_end = 25,
};
vlans[3] = (NMPlatformBridgeVlan){
.vid_start = 26,
.vid_end = 30,
.untagged = TRUE,
};
vlans[4] = (NMPlatformBridgeVlan){
.vid_start = 40,
.vid_end = 40,
.untagged = TRUE,
};
vlans[5] = (NMPlatformBridgeVlan){
.vid_start = 40,
.vid_end = 40,
.untagged = TRUE,
.pvid = TRUE,
};
expect[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
expect[1] = (NMPlatformBridgeVlan){
.vid_start = 11,
.vid_end = 11,
.pvid = TRUE,
};
expect[2] = (NMPlatformBridgeVlan){
.vid_start = 20,
.vid_end = 25,
};
expect[3] = (NMPlatformBridgeVlan){
.vid_start = 26,
.vid_end = 30,
.untagged = TRUE,
};
expect[4] = (NMPlatformBridgeVlan){
.vid_start = 40,
.vid_end = 40,
.untagged = TRUE,
};
expect[5] = (NMPlatformBridgeVlan){
.vid_start = 40,
.vid_end = 40,
.untagged = TRUE,
.pvid = TRUE,
};
vlans_len = 6;
nmp_utils_bridge_vlan_normalize(vlans, &vlans_len);
g_assert(vlans_len == 6);
g_assert(nmp_utils_bridge_normalized_vlans_equal(vlans, vlans_len, expect, vlans_len));
/* Overlapping and contiguous ranges are merged */
vlans[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
vlans[1] = (NMPlatformBridgeVlan){
.vid_start = 11,
.vid_end = 20,
.untagged = TRUE,
};
vlans[2] = (NMPlatformBridgeVlan){
.vid_start = 19,
.vid_end = 30,
.untagged = TRUE,
};
expect[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 30,
.untagged = TRUE,
};
vlans_len = 3;
nmp_utils_bridge_vlan_normalize(vlans, &vlans_len);
g_assert(vlans_len == 1);
g_assert(nmp_utils_bridge_normalized_vlans_equal(vlans, vlans_len, expect, vlans_len));
vlans[0] = (NMPlatformBridgeVlan){
.vid_start = 20,
.vid_end = 20,
};
vlans[1] = (NMPlatformBridgeVlan){
.vid_start = 4,
.vid_end = 4,
.pvid = TRUE,
};
vlans[2] = (NMPlatformBridgeVlan){
.vid_start = 33,
.vid_end = 33,
};
vlans[3] = (NMPlatformBridgeVlan){
.vid_start = 100,
.vid_end = 100,
.untagged = TRUE,
};
vlans[4] = (NMPlatformBridgeVlan){
.vid_start = 34,
.vid_end = 40,
};
vlans[5] = (NMPlatformBridgeVlan){
.vid_start = 21,
.vid_end = 32,
};
expect[0] = (NMPlatformBridgeVlan){
.vid_start = 4,
.vid_end = 4,
.pvid = TRUE,
};
expect[1] = (NMPlatformBridgeVlan){
.vid_start = 20,
.vid_end = 40,
};
expect[2] = (NMPlatformBridgeVlan){
.vid_start = 100,
.vid_end = 100,
.untagged = TRUE,
};
vlans_len = 6;
nmp_utils_bridge_vlan_normalize(vlans, &vlans_len);
g_assert(vlans_len == 3);
g_assert(nmp_utils_bridge_normalized_vlans_equal(vlans, vlans_len, expect, vlans_len));
}
static void
test_nmp_utils_bridge_normalized_vlans_equal(void)
{
NMPlatformBridgeVlan a[10];
NMPlatformBridgeVlan b[10];
/* Both empty */
g_assert(nmp_utils_bridge_normalized_vlans_equal(NULL, 0, NULL, 0));
g_assert(nmp_utils_bridge_normalized_vlans_equal(a, 0, b, 0));
g_assert(nmp_utils_bridge_normalized_vlans_equal(a, 0, NULL, 0));
g_assert(nmp_utils_bridge_normalized_vlans_equal(NULL, 0, b, 0));
/* One empty, other not */
a[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
g_assert(!nmp_utils_bridge_normalized_vlans_equal(a, 1, NULL, 0));
g_assert(!nmp_utils_bridge_normalized_vlans_equal(NULL, 0, a, 1));
/* Equal range + VLAN */
a[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
a[1] = (NMPlatformBridgeVlan){
.vid_start = 11,
.vid_end = 11,
.pvid = TRUE,
};
b[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
b[1] = (NMPlatformBridgeVlan){
.vid_start = 11,
.vid_end = 11,
.pvid = TRUE,
};
g_assert(nmp_utils_bridge_normalized_vlans_equal(a, 2, b, 2));
g_assert(nmp_utils_bridge_normalized_vlans_equal(b, 2, a, 2));
/* Different flag */
b[1].pvid = FALSE;
g_assert(!nmp_utils_bridge_normalized_vlans_equal(a, 2, b, 2));
g_assert(!nmp_utils_bridge_normalized_vlans_equal(b, 2, a, 2));
/* Different ranges */
a[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 30,
.untagged = TRUE,
};
b[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 29,
.untagged = TRUE,
};
g_assert(!nmp_utils_bridge_normalized_vlans_equal(a, 1, b, 1));
g_assert(!nmp_utils_bridge_normalized_vlans_equal(b, 1, a, 1));
b[0].vid_start = 2;
b[0].vid_end = 30;
g_assert(!nmp_utils_bridge_normalized_vlans_equal(a, 1, b, 1));
g_assert(!nmp_utils_bridge_normalized_vlans_equal(b, 1, a, 1));
}
/*****************************************************************************/
static void
test_nmpclass_consistency(void)
{
@ -252,6 +485,10 @@ main(int argc, char **argv)
g_test_add_func("/nm-platform/test_nmp_link_mode_all_advertised_modes_bits",
test_nmp_link_mode_all_advertised_modes_bits);
g_test_add_func("/nm-platform/test_nmpclass_consistency", test_nmpclass_consistency);
g_test_add_func("/nm-platform/test_nmp_utils_bridge_vlans_normalize",
test_nmp_utils_bridge_vlans_normalize);
g_test_add_func("/nm-platform/nmp-utils-bridge-vlans-equal",
test_nmp_utils_bridge_normalized_vlans_equal);
return g_test_run();
}