diff options
author | David Ahern <dsa@cumulusnetworks.com> | 2015-11-25 19:14:16 (GMT) |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2015-12-07 11:20:22 (GMT) |
commit | 05631628a57ce88fe1c884680eac6e00fcca810d (patch) | |
tree | e5e8efb3906e2689a3029b4ee291351ba13a4317 | |
parent | b2e7199cabe28fdd93f8651866da52d49c04f3ec (diff) | |
download | libnl-05631628a57ce88fe1c884680eac6e00fcca810d.zip libnl-05631628a57ce88fe1c884680eac6e00fcca810d.tar.gz libnl-05631628a57ce88fe1c884680eac6e00fcca810d.tar.bz2 |
bridge: add support for VLANs
Add operation for requesting VLAN data for AF_BRIDGE and parsing of
IFLA_AF_SPEC for AF_BRIDGE. VLANs are saved in a bitmap.
Also add dumping of vlan info to link list and neigh list.
For example:
$ nl-link-list --details --family=bridge
br1 ether 8e:6e:0e:86:e5:86 master br1 <broadcast,multicast,up,running,lowerup>
mtu 1500 txqlen 0 weight 0 index 18
mode default carrier down
bridge: pvid 1 all vlans: 1 301-400 601-610 untagged vlans: 1
bond1 ether 46:ef:e1:c9:46:fe <broadcast,multicast,master>
mtu 1500 txqlen 0 weight 0 index 20
state down mode default carrier down
bridge:
Signed-off-by: Wilson Kok <wkok@cumulusnetworks.com>
Signed-off-by: David Ahern <dsa@cumulusnetworks.com>
[thaller@redhat.com: modified original patch to use ao_parse_af_full().
Also renaming new API and drop some #defines]
Signed-off-by: Thomas Haller <thaller@redhat.com>
-rw-r--r-- | include/netlink/route/link/bridge.h | 15 | ||||
-rw-r--r-- | lib/route/link/bridge.c | 199 | ||||
-rw-r--r-- | lib/route/neigh.c | 9 | ||||
-rw-r--r-- | libnl-route-3.sym | 3 |
4 files changed, 224 insertions, 2 deletions
diff --git a/include/netlink/route/link/bridge.h b/include/netlink/route/link/bridge.h index 16a4505..a314d77 100644 --- a/include/netlink/route/link/bridge.h +++ b/include/netlink/route/link/bridge.h @@ -19,6 +19,16 @@ extern "C" { #endif +#define RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX 4096 +#define RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN (RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX / 32) + +struct rtnl_link_bridge_vlan +{ + uint16_t pvid; + uint32_t vlan_bitmap[RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN]; + uint32_t untagged_bitmap[RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN]; +}; + /** * Bridge flags * @ingroup bridge @@ -52,6 +62,11 @@ extern char * rtnl_link_bridge_flags2str(int, char *, size_t); extern int rtnl_link_bridge_str2flags(const char *); extern int rtnl_link_bridge_add(struct nl_sock *sk, const char *name); + +extern int rtnl_link_bridge_pvid(struct rtnl_link *link); +extern int rtnl_link_bridge_has_vlan(struct rtnl_link *link); + +extern struct bridge_vlan *rtnl_bridge_get_port_vlan(struct rtnl_link *link); #ifdef __cplusplus } #endif diff --git a/lib/route/link/bridge.c b/lib/route/link/bridge.c index 544f02c..189f9e3 100644 --- a/lib/route/link/bridge.c +++ b/lib/route/link/bridge.c @@ -25,11 +25,14 @@ #include <netlink-private/route/link/api.h> #include <linux/if_bridge.h> +#define VLAN_VID_MASK 0x0fff /* VLAN Identifier */ + /** @cond SKIP */ #define BRIDGE_ATTR_PORT_STATE (1 << 0) #define BRIDGE_ATTR_PRIORITY (1 << 1) #define BRIDGE_ATTR_COST (1 << 2) #define BRIDGE_ATTR_FLAGS (1 << 3) +#define BRIDGE_ATTR_PORT_VLAN (1 << 4) #define PRIV_FLAG_NEW_ATTRS (1 << 0) @@ -42,8 +45,31 @@ struct bridge_data uint32_t b_flags; uint32_t b_flags_mask; uint32_t ce_mask; /* HACK to support attr macros */ + struct rtnl_link_bridge_vlan vlan_info; }; +static void set_bit(unsigned nr, uint32_t *addr) +{ + if (nr < RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX) + addr[nr / 32] |= (((uint32_t) 1) << (nr % 32)); +} + +static int find_next_bit(int i, uint32_t x) +{ + int j; + + if (i >= 32) + return -1; + + /* find first bit */ + if (i < 0) + return __builtin_ffs(x); + + /* mask off prior finds to get next */ + j = __builtin_ffs(x >> i); + return j ? j + i : 0; +} + static struct rtnl_link_af_ops bridge_ops; #define IS_BRIDGE_LINK_ASSERT(link) \ @@ -141,6 +167,116 @@ static int bridge_parse_protinfo(struct rtnl_link *link, struct nlattr *attr, return 0; } +static int bridge_parse_af_full(struct rtnl_link *link, struct nlattr *attr_full, + void *data) +{ + struct bridge_data *bd = data; + struct bridge_vlan_info *vinfo = NULL; + struct nlattr *attr; + int remaining; + + nla_for_each_nested(attr, attr_full, remaining) { + if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO) + return 0; + + if (nla_len(attr) != sizeof(struct bridge_vlan_info)) + return -EINVAL; + + vinfo = nla_data(attr); + if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK) + return -EINVAL; + + if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { + NL_DBG(1, "Unexpected BRIDGE_VLAN_INFO_RANGE_BEGIN flag; can not handle it.\n"); + return -EINVAL; + } + + if (vinfo->flags & BRIDGE_VLAN_INFO_PVID) + bd->vlan_info.pvid = vinfo->vid; + + if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED) + set_bit(vinfo->vid, bd->vlan_info.untagged_bitmap); + + set_bit(vinfo->vid, bd->vlan_info.vlan_bitmap); + + bd->ce_mask |= BRIDGE_ATTR_PORT_VLAN; + } + + return 0; +} + +static int bridge_get_af(struct nl_msg *msg) +{ + __u32 ext_filter_mask = RTEXT_FILTER_BRVLAN; + + return nla_put(msg, IFLA_EXT_MASK, sizeof(ext_filter_mask), &ext_filter_mask); +} + +static void dump_bitmap(struct nl_dump_params *p, const uint32_t *b) +{ + int i = -1, j, k; + int start = -1, prev = -1; + int done, found = 0; + + for (k = 0; k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN; k++) { + int base_bit; + uint32_t a = b[k]; + + base_bit = k * 32; + i = -1; + done = 0; + while (!done) { + j = find_next_bit(i, a); + if (j > 0) { + /* first hit of any bit */ + if (start < 0 && prev < 0) { + start = prev = j - 1 + base_bit; + goto next; + } + /* this bit is a continuation of prior bits */ + if (j - 2 + base_bit == prev) { + prev++; + goto next; + } + } else + done = 1; + + if (start >= 0) { + found++; + if (done && k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN - 1) + break; + + nl_dump(p, " %d", start); + if (start != prev) + nl_dump(p, "-%d", prev); + + if (done) + break; + } + if (j > 0) + start = prev = j - 1 + base_bit; +next: + i = j; + } + } + if (!found) + nl_dump(p, " <none>"); + + return; +} + +static void rtnl_link_bridge_dump_vlans(struct nl_dump_params *p, + struct bridge_data *bd) +{ + nl_dump(p, "pvid %u", bd->vlan_info.pvid); + + nl_dump(p, " all vlans:"); + dump_bitmap(p, bd->vlan_info.vlan_bitmap); + + nl_dump(p, " untagged vlans:"); + dump_bitmap(p, bd->vlan_info.untagged_bitmap); +} + static void bridge_dump_details(struct rtnl_link *link, struct nl_dump_params *p, void *data) { @@ -157,6 +293,17 @@ static void bridge_dump_details(struct rtnl_link *link, if (bd->ce_mask & BRIDGE_ATTR_COST) nl_dump(p, "cost %u ", bd->b_cost); + if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN) + rtnl_link_bridge_dump_vlans(p, bd); + + if (bd->ce_mask & BRIDGE_ATTR_FLAGS) { + char buf[256]; + + rtnl_link_bridge_flags2str(bd->b_flags & bd->b_flags_mask, + buf, sizeof(buf)); + nl_dump(p, "%s", buf); + } + nl_dump(p, "\n"); } @@ -171,6 +318,8 @@ static int bridge_compare(struct rtnl_link *_a, struct rtnl_link *_b, diff |= BRIDGE_DIFF(PORT_STATE, a->b_port_state != b->b_port_state); diff |= BRIDGE_DIFF(PRIORITY, a->b_priority != b->b_priority); diff |= BRIDGE_DIFF(COST, a->b_cost != b->b_cost); + diff |= BRIDGE_DIFF(PORT_VLAN, memcmp(&a->vlan_info, &b->vlan_info, + sizeof(struct rtnl_link_bridge_vlan))); if (flags & LOOSE_COMPARISON) diff |= BRIDGE_DIFF(FLAGS, @@ -503,6 +652,54 @@ int rtnl_link_bridge_str2flags(const char *name) /** @} */ +int rtnl_link_bridge_pvid(struct rtnl_link *link) +{ + struct bridge_data *bd; + + IS_BRIDGE_LINK_ASSERT(link); + + bd = link->l_af_data[AF_BRIDGE]; + if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN) + return (int) bd->vlan_info.pvid; + + return -EINVAL; +} + +int rtnl_link_bridge_has_vlan(struct rtnl_link *link) +{ + struct bridge_data *bd; + int i; + + IS_BRIDGE_LINK_ASSERT(link); + + bd = link->l_af_data[AF_BRIDGE]; + if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN) { + if (bd->vlan_info.pvid) + return 1; + + for (i = 0; i < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN; ++i) { + if (bd->vlan_info.vlan_bitmap[i] || + bd->vlan_info.untagged_bitmap[i]) + return 1; + } + } + return 0; +} + +struct rtnl_link_bridge_vlan *rtnl_link_bridge_get_port_vlan(struct rtnl_link *link) +{ + struct bridge_data *data; + + if (!rtnl_link_is_bridge(link)) + return NULL; + + data = link->l_af_data[AF_BRIDGE]; + if (data && (data->ce_mask & BRIDGE_ATTR_PORT_VLAN)) + return &data->vlan_info; + + return NULL; +} + static struct rtnl_link_af_ops bridge_ops = { .ao_family = AF_BRIDGE, .ao_alloc = &bridge_alloc, @@ -511,6 +708,8 @@ static struct rtnl_link_af_ops bridge_ops = { .ao_parse_protinfo = &bridge_parse_protinfo, .ao_dump[NL_DUMP_DETAILS] = &bridge_dump_details, .ao_compare = &bridge_compare, + .ao_parse_af_full = &bridge_parse_af_full, + .ao_get_af = &bridge_get_af, }; static void __init bridge_init(void) diff --git a/lib/route/neigh.c b/lib/route/neigh.c index 6059e7f..436d766 100644 --- a/lib/route/neigh.c +++ b/lib/route/neigh.c @@ -210,6 +210,7 @@ static void neigh_keygen(struct nl_object *obj, uint32_t *hashkey, struct neigh_hash_key { uint32_t n_family; uint32_t n_ifindex; + uint16_t n_vlan; char n_addr[0]; } __attribute__((packed)) *nkey; #ifdef NL_DEBUG @@ -234,6 +235,7 @@ static void neigh_keygen(struct nl_object *obj, uint32_t *hashkey, } nkey->n_family = neigh->n_family; if (neigh->n_family == AF_BRIDGE) { + nkey->n_vlan = neigh->n_vlan; if (neigh->n_flags & NTF_SELF) nkey->n_ifindex = neigh->n_ifindex; else @@ -316,9 +318,9 @@ static uint32_t neigh_id_attrs_get(struct nl_object *obj) if (neigh->n_family == AF_BRIDGE) { if (neigh->n_flags & NTF_SELF) - return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_IFINDEX); + return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_IFINDEX | NEIGH_ATTR_VLAN); else - return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_MASTER); + return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_MASTER | NEIGH_ATTR_VLAN); } else return (NEIGH_ATTR_IFINDEX | NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY); } @@ -472,6 +474,9 @@ static void neigh_dump_line(struct nl_object *a, struct nl_dump_params *p) nl_dump(p, "lladdr %s ", nl_addr2str(n->n_lladdr, lladdr, sizeof(lladdr))); + if (n->ce_mask & NEIGH_ATTR_VLAN) + nl_dump(p, "vlan %d ", n->n_vlan); + rtnl_neigh_state2str(n->n_state, state, sizeof(state)); rtnl_neigh_flags2str(n->n_flags, flags, sizeof(flags)); diff --git a/libnl-route-3.sym b/libnl-route-3.sym index 627cb43..2400746 100644 --- a/libnl-route-3.sym +++ b/libnl-route-3.sym @@ -904,5 +904,8 @@ global: rtnl_link_vrf_get_tableid; rtnl_link_vrf_set_tableid; rtnl_neigh_alloc_cache_flags; + rtnl_link_bridge_pvid; + rtnl_link_bridge_has_vlan; + rtnl_link_bridge_get_port_vlan; } libnl_3_2_27; |