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 + 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)); @@ -143,6 +167,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 @@ -243,6 +314,56 @@ int rtnl_link_bridge_get_vlan_protocol(struct rtnl_link *link, return 0; } +/** + * 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 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 #include +#include #include +#include #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; }