summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2024-02-29 21:09:29 +0100
committerThomas Haller <thaller@redhat.com>2024-02-29 21:09:36 +0100
commit3646398d57e2da7d3f6ef7979007a7db2e84644f (patch)
treeae105fe2c6d825cf9de1130f2eaac5274db521b4
parentb76c3a5d9a0e421fe6cb0e89cceb70ae15b57695 (diff)
parente21278edd556be41358bc41c9dcca5c534ac2ca3 (diff)
downloadlibnl-3646398d57e2da7d3f6ef7979007a7db2e84644f.tar.gz
route: merge branch 'Cordell-O:main'
https://github.com/thom311/libnl/pull/372
-rw-r--r--include/netlink/route/link/bridge.h5
-rw-r--r--include/netlink/route/link/bridge_info.h10
-rw-r--r--include/nl-priv-static-route/nl-priv-static-route.h4
-rw-r--r--lib/route/link/bridge.c331
-rw-r--r--lib/route/link/bridge_info.c121
-rw-r--r--libnl-route-3.sym12
-rw-r--r--tests/check-direct.c127
7 files changed, 602 insertions, 8 deletions
diff --git a/include/netlink/route/link/bridge.h b/include/netlink/route/link/bridge.h
index e606bd48..0de59c42 100644
--- a/include/netlink/route/link/bridge.h
+++ b/include/netlink/route/link/bridge.h
@@ -61,6 +61,7 @@ extern int rtnl_link_bridge_set_flags(struct rtnl_link *, unsigned int);
extern int rtnl_link_bridge_get_flags(struct rtnl_link *);
extern int rtnl_link_bridge_set_self(struct rtnl_link *);
+extern int rtnl_link_bridge_set_master(struct rtnl_link *);
extern int rtnl_link_bridge_get_hwmode(struct rtnl_link *, uint16_t *);
extern int rtnl_link_bridge_set_hwmode(struct rtnl_link *, uint16_t);
@@ -76,6 +77,10 @@ extern uint16_t rtnl_link_bridge_str2hwmode(const char *);
extern int rtnl_link_bridge_add(struct nl_sock *sk, const char *name);
+extern int rtnl_link_bridge_enable_vlan(struct rtnl_link *link);
+extern int rtnl_link_bridge_set_port_vlan_map_range (struct rtnl_link *link, uint16_t start, uint16_t end, int untagged);
+extern int rtnl_link_bridge_unset_port_vlan_map_range (struct rtnl_link *link, uint16_t start, uint16_t end);
+extern int rtnl_link_bridge_set_port_vlan_pvid (struct rtnl_link *link, uint16_t pvid);
extern int rtnl_link_bridge_pvid(struct rtnl_link *link);
extern int rtnl_link_bridge_has_vlan(struct rtnl_link *link);
diff --git a/include/netlink/route/link/bridge_info.h b/include/netlink/route/link/bridge_info.h
index 09689b32..e8448a46 100644
--- a/include/netlink/route/link/bridge_info.h
+++ b/include/netlink/route/link/bridge_info.h
@@ -13,6 +13,11 @@
extern "C" {
#endif
+extern void rtnl_link_bridge_set_ageing_time(struct rtnl_link *link,
+ uint32_t ageing_time);
+extern int rtnl_link_bridge_get_ageing_time(struct rtnl_link *link,
+ uint32_t *ageing_time);
+
extern void rtnl_link_bridge_set_vlan_filtering(struct rtnl_link *link,
uint8_t vlan_filtering);
extern int rtnl_link_bridge_get_vlan_filtering(struct rtnl_link *link,
@@ -23,6 +28,11 @@ extern void rtnl_link_bridge_set_vlan_protocol(struct rtnl_link *link,
extern int rtnl_link_bridge_get_vlan_protocol(struct rtnl_link *link,
uint16_t *vlan_protocol);
+extern void rtnl_link_bridge_set_vlan_default_pvid(struct rtnl_link *link,
+ uint16_t default_pvid);
+extern int rtnl_link_bridge_get_vlan_default_pvid(struct rtnl_link *link,
+ uint16_t *default_pvid);
+
extern void rtnl_link_bridge_set_vlan_stats_enabled(struct rtnl_link *link,
uint8_t vlan_stats_enabled);
extern int rtnl_link_bridge_get_vlan_stats_enabled(struct rtnl_link *link,
diff --git a/include/nl-priv-static-route/nl-priv-static-route.h b/include/nl-priv-static-route/nl-priv-static-route.h
index 65ff5312..ad9f98f9 100644
--- a/include/nl-priv-static-route/nl-priv-static-route.h
+++ b/include/nl-priv-static-route/nl-priv-static-route.h
@@ -3,6 +3,10 @@
#ifndef NETLINK_ROUTE_UTILS_PRIV_H_
#define NETLINK_ROUTE_UTILS_PRIV_H_
+#include <netlink/route/link/bridge.h>
+
extern const uint8_t *const _nltst_map_stat_id_from_IPSTATS_MIB_v2;
+extern int _nl_bridge_fill_vlan_info(struct nl_msg *msg,
+ struct rtnl_link_bridge_vlan *vlan_info);
#endif
diff --git a/lib/route/link/bridge.c b/lib/route/link/bridge.c
index 5b441648..a9c7eea7 100644
--- a/lib/route/link/bridge.c
+++ b/lib/route/link/bridge.c
@@ -23,6 +23,7 @@
#include "nl-route.h"
#include "link-api.h"
#include "nl-priv-dynamic-core/nl-core.h"
+#include "nl-priv-static-route/nl-priv-static-route.h"
#define VLAN_VID_MASK 0x0fff /* VLAN Identifier */
@@ -33,7 +34,7 @@
#define BRIDGE_ATTR_FLAGS (1 << 3)
#define BRIDGE_ATTR_PORT_VLAN (1 << 4)
#define BRIDGE_ATTR_HWMODE (1 << 5)
-#define BRIDGE_ATTR_SELF (1 << 6)
+#define BRIDGE_ATTR_CONFIG_MODE (1 << 6)
#define PRIV_FLAG_NEW_ATTRS (1 << 0)
@@ -43,7 +44,7 @@ struct bridge_data
uint8_t b_priv_flags; /* internal flags */
uint16_t b_hwmode;
uint16_t b_priority;
- uint16_t b_self; /* here for comparison reasons */
+ uint16_t b_config_mode;
uint32_t b_cost;
uint32_t b_flags;
uint32_t b_flags_mask;
@@ -57,6 +58,24 @@ static void set_bit(unsigned nr, uint32_t *addr)
addr[nr / 32] |= (((uint32_t) 1) << (nr % 32));
}
+static void unset_bit(unsigned nr, uint32_t *addr)
+{
+ if (nr < RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX)
+ addr[nr / 32] &= ~(((uint32_t) 1) << (nr % 32));
+}
+
+static bool vlan_id_untagged(struct rtnl_link_bridge_vlan *vlan_info, uint16_t vid)
+{
+ uint32_t mask, bit;
+
+ _nl_assert(vid / 32u < ARRAY_SIZE(vlan_info->untagged_bitmap));
+
+ mask = vlan_info->untagged_bitmap[vid / 32];
+ bit = (((uint32_t) 1) << vid % 32);
+
+ return mask & bit;
+}
+
static int find_next_bit(int i, uint32_t x)
{
int j;
@@ -239,16 +258,149 @@ static int bridge_parse_af_full(struct rtnl_link *link, struct nlattr *attr_full
return 0;
}
+int _nl_bridge_fill_vlan_info(struct nl_msg *msg, struct rtnl_link_bridge_vlan * vlan_info)
+{
+ struct bridge_vlan_info vinfo;
+ int i = -1, j, k;
+ int start = -1, prev = -1;
+ int done;
+ bool untagged = false;
+
+ for (k = 0; k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN; k++)
+ {
+ int base_bit;
+ uint32_t a = vlan_info->vlan_bitmap[k];
+
+ base_bit = k * 32;
+ i = -1;
+ done = 0;
+ while (!done)
+ {
+ j = find_next_bit(i, a);
+ if (j > 0)
+ {
+ /* Skip if id equal to pvid */
+ if (vlan_info->pvid != 0 && j - 1 + base_bit == vlan_info->pvid)
+ goto nxt;
+ /* first hit of any bit */
+ if (start < 0 && prev < 0)
+ {
+ start = prev = j - 1 + base_bit;
+ /* Start range attribute */
+ untagged = vlan_id_untagged(vlan_info,start);
+ vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN;
+ vinfo.flags |= untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0;
+ vinfo.vid = start;
+ goto nxt;
+ }
+ /* this bit is a continuation of prior bits */
+ if (j - 2 + base_bit == prev)
+ {
+ prev++;
+ /* Hit end of untagged/tagged range */
+ if (untagged != vlan_id_untagged(vlan_info,prev))
+ {
+ /* put vlan into attributes */
+ if (start == prev-1)
+ {
+ /* only 1 vid in range */
+ vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
+ NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo);
+ }
+ else
+ {
+ /* end of untagged/tagged range */
+ NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo);
+
+ vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END;
+ vinfo.flags |= untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0;
+ vinfo.vid = prev-1;
+ NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo);
+ }
+ /* start of new range */
+ untagged = !untagged;
+ vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN;
+ vinfo.flags |= untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0;
+ vinfo.vid = prev;
+ }
+ goto nxt;
+ }
+ }
+ else
+ done = 1;
+
+ if (start >= 0)
+ {
+ if (done && k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN - 1)
+ break;
+
+ if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN && start != prev)
+ {
+ NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo);
+
+ vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END;
+ vinfo.flags |= untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0;
+ vinfo.vid = prev;
+ NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo);
+ }
+ else if (start == prev)
+ {
+ vinfo.flags = untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0;
+ vinfo.vid = start;
+ NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo);
+ }
+
+ if (done)
+ break;
+ }
+ if (j > 0)
+ {
+ start = prev = j - 1 + base_bit;
+ untagged = vlan_id_untagged(vlan_info,start);
+ vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN;
+ vinfo.flags |= untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0;
+ vinfo.vid = start;
+ }
+nxt:
+ i = j;
+ }
+ }
+
+ if (vlan_info->pvid != 0)
+ {
+ untagged = vlan_id_untagged(vlan_info,vlan_info->pvid);
+ vinfo.flags = BRIDGE_VLAN_INFO_PVID;
+ vinfo.flags |= untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0;
+ vinfo.vid = vlan_info->pvid;
+ NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo);
+ }
+
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
static int bridge_fill_af(struct rtnl_link *link, struct nl_msg *msg,
void *data)
{
struct bridge_data *bd = data;
- if ((bd->ce_mask & BRIDGE_ATTR_SELF)||(bd->ce_mask & BRIDGE_ATTR_HWMODE))
- NLA_PUT_U16(msg, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF);
-
if (bd->ce_mask & BRIDGE_ATTR_HWMODE)
+ {
NLA_PUT_U16(msg, IFLA_BRIDGE_MODE, bd->b_hwmode);
+ bd->b_config_mode = BRIDGE_FLAGS_SELF;
+ bd->ce_mask |= BRIDGE_ATTR_CONFIG_MODE;
+ }
+
+ if (bd->ce_mask & BRIDGE_ATTR_CONFIG_MODE)
+ NLA_PUT_U16(msg, IFLA_BRIDGE_FLAGS, bd->b_config_mode);
+
+ if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN) {
+ if (_nl_bridge_fill_vlan_info(msg, &bd->vlan_info)) {
+ goto nla_put_failure;
+ }
+ }
return 0;
@@ -445,7 +597,7 @@ static int bridge_compare(struct rtnl_link *_a, struct rtnl_link *_b,
memcmp(&a->vlan_info, &b->vlan_info,
sizeof(struct rtnl_link_bridge_vlan)));
diff |= _DIFF(BRIDGE_ATTR_HWMODE, a->b_hwmode != b->b_hwmode);
- diff |= _DIFF(BRIDGE_ATTR_SELF, a->b_self != b->b_self);
+ diff |= _DIFF(BRIDGE_ATTR_CONFIG_MODE, a->b_config_mode != b->b_config_mode);
if (flags & LOOSE_COMPARISON)
diff |= _DIFF(BRIDGE_ATTR_FLAGS,
@@ -773,8 +925,31 @@ int rtnl_link_bridge_set_self(struct rtnl_link *link)
IS_BRIDGE_LINK_ASSERT(link);
- bd->b_self |= 1;
- bd->ce_mask |= BRIDGE_ATTR_SELF;
+ bd->b_config_mode = BRIDGE_FLAGS_SELF;
+ bd->ce_mask |= BRIDGE_ATTR_CONFIG_MODE;
+
+ return 0;
+}
+
+/**
+ * Set link change type to master
+ * @arg link Link Object of type bridge
+ *
+ * This will set the bridge change flag to master, meaning that changes to
+ * be applied with this link object will be applied directly to the virtual
+ * device in a bridge instead of the physical device.
+ *
+ * @return 0 on success or negative error code
+ * @return -NLE_OPNOTSUP Link is not a bridge
+ */
+int rtnl_link_bridge_set_master(struct rtnl_link *link)
+{
+ struct bridge_data *bd = bridge_data(link);
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ bd->b_config_mode = BRIDGE_FLAGS_MASTER;
+ bd->ce_mask |= BRIDGE_ATTR_CONFIG_MODE;
return 0;
}
@@ -915,6 +1090,146 @@ uint16_t rtnl_link_bridge_str2hwmode(const char *name)
/** @} */
+/**
+ * Enable the ability to set vlan info
+ * @arg link Link object of type bridge
+ *
+ * @return 0 on success or negative error code
+ * @return -NLE_OPNOTSUP Link is not a bridge
+ */
+int rtnl_link_bridge_enable_vlan(struct rtnl_link *link)
+{
+ struct bridge_data *bd = bridge_data(link);
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ bd->ce_mask |= BRIDGE_ATTR_PORT_VLAN;
+
+ return 0;
+}
+
+/**
+ * @name Quality of Service
+ * @{
+ */
+
+/**
+ * Set port vlan membership range
+ * @arg link Link object of type bridge
+ * @arg start Start of membership range.
+ * @arg end End of membership range.
+ * @arg untagged Set membership range to be untagged.
+ *
+ * This will set the vlan membership range for a bridge port.
+ * This will unset the untagged membership if untagged is false.
+ * Supported range is 1-4094
+ *
+ * @return 0 on success or negative error code
+ * @return -NLE_NOATTR if port vlan attribute not present
+ * @return -NLE_OPNOTSUP Link is not a bridge
+ * @return -NLE_INVAL range is not in supported range.
+ */
+int rtnl_link_bridge_set_port_vlan_map_range (struct rtnl_link *link, uint16_t start, uint16_t end, int untagged)
+{
+ struct rtnl_link_bridge_vlan * vinfo;
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ vinfo = rtnl_link_bridge_get_port_vlan(link);
+
+ if (!vinfo)
+ return -NLE_NOATTR;
+
+ if (start == 0 || start > end || end >= VLAN_VID_MASK)
+ return -NLE_INVAL;
+
+ for (uint16_t i = start; i <= end; i++)
+ {
+ set_bit(i,vinfo->vlan_bitmap);
+ if (untagged) {
+ set_bit(i,vinfo->untagged_bitmap);
+ } else {
+ unset_bit(i,vinfo->untagged_bitmap);
+ }
+ }
+ return 0;
+}
+
+/**
+ * Unset port vlan membership range
+ * @arg link Link object of type bridge
+ * @arg start Start of membership range.
+ * @arg end End of membership range.
+ *
+ * This will unset the vlan membership range for a bridge port
+ * for both tagged and untagged membership.
+ * Supported range is 1-4094
+ *
+ * @return 0 on success or negative error code
+ * @return -NLE_NOATTR if port vlan attribute not present
+ * @return -NLE_OPNOTSUP Link is not a bridge
+ * @return -NLE_INVAL range is not in supported range.
+ */
+int rtnl_link_bridge_unset_port_vlan_map_range (struct rtnl_link *link, uint16_t start, uint16_t end)
+{
+ struct rtnl_link_bridge_vlan * vinfo;
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ vinfo = rtnl_link_bridge_get_port_vlan(link);
+
+ if (!vinfo)
+ return -NLE_NOATTR;
+
+ if (start == 0 || start > end || end >= VLAN_VID_MASK)
+ return -NLE_INVAL;
+
+ for (uint16_t i = start; i <= end; i++)
+ {
+ unset_bit(i,vinfo->vlan_bitmap);
+ unset_bit(i,vinfo->untagged_bitmap);
+ }
+ return 0;
+}
+
+/**
+ * Set port primary vlan id
+ * @arg link Link object of type bridge
+ * @arg pvid PVID to set.
+ * @arg untagged Set vlan id to be untagged.
+ *
+ * This will set the primary vlan id for a bridge port.
+ * Supported range is 0-4094, Setting pvid to 0 will unset it.
+ * You will most likely want to set/unset pvid in the vlan map.
+ * @see rtnl_link_bridge_set_port_vlan_map_range()
+ * @see rtnl_link_bridge_unset_port_vlan_map_range()
+ *
+ * @return 0 on success or negative error code
+ * @return -NLE_NOATTR if port vlan attribute not present
+ * @return -NLE_OPNOTSUP Link is not a bridge
+ * @return -NLE_INVAL PVID is above supported range.
+ */
+int rtnl_link_bridge_set_port_vlan_pvid (struct rtnl_link *link, uint16_t pvid)
+{
+ struct rtnl_link_bridge_vlan * vinfo;
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ vinfo = rtnl_link_bridge_get_port_vlan(link);
+
+ if (!vinfo)
+ return -NLE_NOATTR;
+
+ if (pvid >= VLAN_VID_MASK)
+ return -NLE_INVAL;
+
+ vinfo->pvid = pvid;
+
+ return 0;
+}
+
+/** @} */
+
int rtnl_link_bridge_pvid(struct rtnl_link *link)
{
struct bridge_data *bd;
diff --git a/lib/route/link/bridge_info.c b/lib/route/link/bridge_info.c
index 61b885f0..311e947b 100644
--- a/lib/route/link/bridge_info.c
+++ b/lib/route/link/bridge_info.c
@@ -21,15 +21,21 @@
#define BRIDGE_ATTR_VLAN_FILTERING (1 << 0)
#define BRIDGE_ATTR_VLAN_PROTOCOL (1 << 1)
#define BRIDGE_ATTR_VLAN_STATS_ENABLED (1 << 2)
+#define BRIDGE_ATTR_AGEING_TIME (1 << 3)
+#define BRIDGE_ATTR_VLAN_DEFAULT_PVID (1 << 4)
struct bridge_info {
uint32_t ce_mask; /* to support attr macros */
+ uint32_t b_ageing_time;
uint16_t b_vlan_protocol;
+ uint16_t b_vlan_default_pvid;
uint8_t b_vlan_filtering;
uint8_t b_vlan_stats_enabled;
};
static const struct nla_policy bi_attrs_policy[IFLA_BR_MAX + 1] = {
+ [IFLA_BR_AGEING_TIME] = { .type = NLA_U32 },
+ [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NLA_U16 },
[IFLA_BR_VLAN_FILTERING] = { .type = NLA_U8 },
[IFLA_BR_VLAN_PROTOCOL] = { .type = NLA_U16 },
[IFLA_BR_VLAN_STATS_ENABLED] = { .type = NLA_U8 },
@@ -75,6 +81,17 @@ static int bridge_info_parse(struct rtnl_link *link, struct nlattr *data,
bi = link->l_info;
+ if (tb[IFLA_BR_AGEING_TIME]) {
+ bi->b_ageing_time = nla_get_u32(tb[IFLA_BR_AGEING_TIME]);
+ bi->ce_mask |= BRIDGE_ATTR_AGEING_TIME;
+ }
+
+ if (tb[IFLA_BR_VLAN_DEFAULT_PVID]) {
+ bi->b_vlan_default_pvid =
+ nla_get_u16(tb[IFLA_BR_VLAN_DEFAULT_PVID]);
+ bi->ce_mask |= BRIDGE_ATTR_VLAN_DEFAULT_PVID;
+ }
+
if (tb[IFLA_BR_VLAN_FILTERING]) {
bi->b_vlan_filtering = nla_get_u8(tb[IFLA_BR_VLAN_FILTERING]);
bi->ce_mask |= BRIDGE_ATTR_VLAN_FILTERING;
@@ -104,9 +121,16 @@ static int bridge_info_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
if (!data)
return -NLE_MSGSIZE;
+ if (bi->ce_mask & BRIDGE_ATTR_AGEING_TIME)
+ NLA_PUT_U32(msg, IFLA_BR_AGEING_TIME, bi->b_ageing_time);
+
if (bi->ce_mask & BRIDGE_ATTR_VLAN_FILTERING)
NLA_PUT_U8(msg, IFLA_BR_VLAN_FILTERING, bi->b_vlan_filtering);
+ if (bi->ce_mask & BRIDGE_ATTR_VLAN_DEFAULT_PVID)
+ NLA_PUT_U16(msg, IFLA_BR_VLAN_DEFAULT_PVID,
+ bi->b_vlan_default_pvid);
+
if (bi->ce_mask & BRIDGE_ATTR_VLAN_PROTOCOL)
NLA_PUT_U16(msg, IFLA_BR_VLAN_PROTOCOL,
htons(bi->b_vlan_protocol));
@@ -144,6 +168,53 @@ static struct rtnl_link_info_ops bridge_info_ops = {
} while (0)
/**
+ * Set ageing time for dynamic forwarding entries
+ * @arg link Link object of type bridge
+ * @arg ageing_time Interval to set.
+ *
+ * @return void
+ */
+void rtnl_link_bridge_set_ageing_time(struct rtnl_link *link,
+ uint32_t ageing_time)
+{
+ struct bridge_info *bi = bridge_info(link);
+
+ IS_BRIDGE_INFO_ASSERT(link);
+
+ bi->b_ageing_time = ageing_time;
+
+ bi->ce_mask |= BRIDGE_ATTR_AGEING_TIME;
+}
+
+/**
+ * Get ageing time for dynamic forwarding entries
+ * @arg link Link object of type bridge
+ * @arg ageing_time Output argument.
+ *
+ * @see rtnl_link_bridge_set_ageing_time()
+ * @return Zero on success, otherwise a negative error code.
+ * @retval -NLE_NOATTR
+ * @retval -NLE_INVAL
+ */
+int rtnl_link_bridge_get_ageing_time(struct rtnl_link *link,
+ uint32_t *ageing_time)
+{
+ struct bridge_info *bi = bridge_info(link);
+
+ IS_BRIDGE_INFO_ASSERT(link);
+
+ if (!(bi->ce_mask & BRIDGE_ATTR_AGEING_TIME))
+ return -NLE_NOATTR;
+
+ if (!ageing_time)
+ return -NLE_INVAL;
+
+ *ageing_time = bi->b_ageing_time;
+
+ return 0;
+}
+
+/**
* Set VLAN filtering flag
* @arg link Link object of type bridge
* @arg vlan_filtering VLAN_filtering boolean flag to set.
@@ -244,6 +315,56 @@ int rtnl_link_bridge_get_vlan_protocol(struct rtnl_link *link,
}
/**
+ * Set VLAN default pvid
+ * @arg link Link object of type bridge
+ * @arg default pvid VLAN default pvid to set.
+ *
+ * @see rtnl_link_bridge_get_vlan_default_pvid()
+ *
+ * @return void
+ */
+void rtnl_link_bridge_set_vlan_default_pvid(struct rtnl_link *link,
+ uint16_t default_pvid)
+{
+ struct bridge_info *bi = bridge_info(link);
+
+ IS_BRIDGE_INFO_ASSERT(link);
+
+ bi->b_vlan_default_pvid = default_pvid;
+
+ bi->ce_mask |= BRIDGE_ATTR_VLAN_DEFAULT_PVID;
+}
+
+/**
+ * Get VLAN default pvid
+ * @arg link Link object of type bridge
+ * @arg default_pvid Output argument.
+ *
+ * @see rtnl_link_bridge_set_vlan_default_pvid()
+ *
+ * @return Zero on success, otherwise a negative error code.
+ * @retval -NLE_NOATTR
+ * @retval -NLE_INVAL
+ */
+int rtnl_link_bridge_get_vlan_default_pvid(struct rtnl_link *link,
+ uint16_t *default_pvid)
+{
+ struct bridge_info *bi = bridge_info(link);
+
+ IS_BRIDGE_INFO_ASSERT(link);
+
+ if (!(bi->ce_mask & BRIDGE_ATTR_VLAN_DEFAULT_PVID))
+ return -NLE_NOATTR;
+
+ if (!default_pvid)
+ return -NLE_INVAL;
+
+ *default_pvid = bi->b_vlan_default_pvid;
+
+ return 0;
+}
+
+/**
* Set VLAN stats enabled flag
* @arg link Link object of type bridge
* @arg vlan_stats_enabled VLAN stats enabled flag to set
diff --git a/libnl-route-3.sym b/libnl-route-3.sym
index fa7af455..eb4752ab 100644
--- a/libnl-route-3.sym
+++ b/libnl-route-3.sym
@@ -1314,3 +1314,15 @@ global:
rtnl_link_bond_set_min_links;
rtnl_link_can_get_device_stats;
} libnl_3_8;
+
+libnl_3_10 {
+global:
+ rtnl_link_bridge_enable_vlan;
+ rtnl_link_bridge_get_ageing_time;
+ rtnl_link_bridge_get_vlan_default_pvid;
+ rtnl_link_bridge_set_ageing_time;
+ rtnl_link_bridge_set_master;
+ rtnl_link_bridge_set_port_vlan_map_range;
+ rtnl_link_bridge_set_port_vlan_pvid;
+ rtnl_link_bridge_unset_port_vlan_map_range;
+} libnl_3_9;
diff --git a/tests/check-direct.c b/tests/check-direct.c
index db1f48dd..33b7742d 100644
--- a/tests/check-direct.c
+++ b/tests/check-direct.c
@@ -5,10 +5,16 @@
#include <check.h>
#include <linux/snmp.h>
+#include <linux/if_bridge.h>
#include <netlink/route/link.h>
+#include <netlink/route/link/bridge.h>
#include "nl-priv-static-route/nl-priv-static-route.h"
+#include "nl-aux-core/nl-core.h"
+
+#define CASES 5
+#define MAX_ATTR 7
START_TEST(static_checks)
{
@@ -53,12 +59,133 @@ START_TEST(static_checks)
}
END_TEST
+static void set_bitmap_range(u_int32_t start, u_int32_t end,
+ struct rtnl_link_bridge_vlan *vlan_info,
+ int untagged)
+{
+ for (u_int32_t i = start; i <= end; i++) {
+ vlan_info->vlan_bitmap[i / 32] |= (((uint32_t)1) << (i % 32));
+ if (untagged) {
+ vlan_info->untagged_bitmap[i / 32] |=
+ (((uint32_t)1) << (i % 32));
+ }
+ }
+}
+
+START_TEST(vlan_attribute_check)
+{
+ struct nlmsghdr *nlh;
+ struct nlattr *a;
+ int attr_count, rem;
+ struct bridge_vlan_info *vlan_attr;
+ struct rtnl_link_bridge_vlan vlan_info[CASES];
+ struct bridge_vlan_info expected_attr[CASES][MAX_ATTR];
+
+ for (int i = 0; i < CASES; i++) {
+ memset(&vlan_info[i], 0, sizeof(struct rtnl_link_bridge_vlan));
+ memset(&expected_attr[i], 0,
+ sizeof(struct bridge_vlan_info) * MAX_ATTR);
+ }
+
+ // Case 1 setting pvid untagged.
+ vlan_info[0].pvid = 1;
+ set_bitmap_range(1, 1, &vlan_info[0], 1);
+ expected_attr[0][0].vid = 1;
+ expected_attr[0][0].flags = BRIDGE_VLAN_INFO_PVID |
+ BRIDGE_VLAN_INFO_UNTAGGED;
+
+ // Case 2 setting vid range.
+ vlan_info[1].pvid = 0;
+ set_bitmap_range(1, 4094, &vlan_info[1], 0);
+ expected_attr[1][0].vid = 1;
+ expected_attr[1][0].flags = BRIDGE_VLAN_INFO_RANGE_BEGIN;
+ expected_attr[1][1].vid = 4094;
+ expected_attr[1][1].flags = BRIDGE_VLAN_INFO_RANGE_END;
+
+ // Case 3 interweaving pvid with vid range.
+ vlan_info[2].pvid = 7;
+ set_bitmap_range(1, 27, &vlan_info[2], 0);
+ set_bitmap_range(7, 7, &vlan_info[2], 1);
+ expected_attr[2][0].vid = 1;
+ expected_attr[2][0].flags = BRIDGE_VLAN_INFO_RANGE_BEGIN;
+ expected_attr[2][1].vid = 6;
+ expected_attr[2][1].flags = BRIDGE_VLAN_INFO_RANGE_END;
+ expected_attr[2][2].vid = 8;
+ expected_attr[2][2].flags = BRIDGE_VLAN_INFO_RANGE_BEGIN;
+ expected_attr[2][3].vid = 27;
+ expected_attr[2][3].flags = BRIDGE_VLAN_INFO_RANGE_END;
+ expected_attr[2][4].vid = 7;
+ expected_attr[2][4].flags = BRIDGE_VLAN_INFO_PVID |
+ BRIDGE_VLAN_INFO_UNTAGGED;
+
+ // Case 4 interweaving untagged and tagged vid ranges.
+ vlan_info[3].pvid = 1;
+ set_bitmap_range(1, 1, &vlan_info[3], 1);
+ set_bitmap_range(1, 25, &vlan_info[3], 0);
+ set_bitmap_range(26, 50, &vlan_info[3], 1);
+ set_bitmap_range(51, 75, &vlan_info[3], 0);
+ expected_attr[3][0].vid = 2;
+ expected_attr[3][0].flags = BRIDGE_VLAN_INFO_RANGE_BEGIN;
+ expected_attr[3][1].vid = 25;
+ expected_attr[3][1].flags = BRIDGE_VLAN_INFO_RANGE_END;
+ expected_attr[3][2].vid = 26;
+ expected_attr[3][2].flags = BRIDGE_VLAN_INFO_RANGE_BEGIN |
+ BRIDGE_VLAN_INFO_UNTAGGED;
+ expected_attr[3][3].vid = 50;
+ expected_attr[3][3].flags = BRIDGE_VLAN_INFO_RANGE_END |
+ BRIDGE_VLAN_INFO_UNTAGGED;
+ expected_attr[3][4].vid = 51;
+ expected_attr[3][4].flags = BRIDGE_VLAN_INFO_RANGE_BEGIN;
+ expected_attr[3][5].vid = 75;
+ expected_attr[3][5].flags = BRIDGE_VLAN_INFO_RANGE_END;
+ expected_attr[3][6].vid = 1;
+ expected_attr[3][6].flags = BRIDGE_VLAN_INFO_PVID |
+ BRIDGE_VLAN_INFO_UNTAGGED;
+
+ // Case 5 individual vid.
+ vlan_info[4].pvid = 0;
+ set_bitmap_range(5, 5, &vlan_info[4], 0);
+ set_bitmap_range(3067, 3067, &vlan_info[4], 1);
+ expected_attr[4][0].vid = 5;
+ expected_attr[4][0].flags = 0;
+ expected_attr[4][1].vid = 3067;
+ expected_attr[4][1].flags = BRIDGE_VLAN_INFO_UNTAGGED;
+
+ for (int i = 0; i < CASES; i++) {
+ _nl_auto_nl_msg struct nl_msg *msg = nlmsg_alloc();
+ attr_count = 0;
+ ck_assert_msg(msg, "Unable to allocate netlink message");
+ ck_assert_int_eq(0,
+ _nl_bridge_fill_vlan_info(msg, &vlan_info[i]));
+
+ nlh = nlmsg_hdr(msg);
+
+ nlmsg_for_each_attr(a, nlh, 0, rem) {
+ ck_assert_msg(expected_attr[i][attr_count].vid != 0,
+ "Attribute number %d unexpected",
+ attr_count);
+ ck_assert_msg(
+ nla_type(a) == IFLA_BRIDGE_VLAN_INFO,
+ "Expected attribute IFLA_BRIDGE_VLAN_INFO %d",
+ IFLA_BRIDGE_VLAN_INFO);
+ vlan_attr = (struct bridge_vlan_info *)nla_data(a);
+ ck_assert_int_eq(vlan_attr->vid,
+ expected_attr[i][attr_count].vid);
+ ck_assert_int_eq(vlan_attr->flags,
+ expected_attr[i][attr_count].flags);
+ attr_count++;
+ }
+ }
+}
+END_TEST
+
static Suite *make_suite(void)
{
Suite *suite = suite_create("Direct");
TCase *tc = tcase_create("Core");
tcase_add_test(tc, static_checks);
+ tcase_add_test(tc, vlan_attribute_check);
suite_add_tcase(suite, tc);
return suite;
}