summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Ahern <dsa@cumulusnetworks.com>2015-11-25 19:14:16 (GMT)
committerThomas Haller <thaller@redhat.com>2015-12-07 11:20:22 (GMT)
commit05631628a57ce88fe1c884680eac6e00fcca810d (patch)
treee5e8efb3906e2689a3029b4ee291351ba13a4317
parentb2e7199cabe28fdd93f8651866da52d49c04f3ec (diff)
downloadlibnl-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.h15
-rw-r--r--lib/route/link/bridge.c199
-rw-r--r--lib/route/neigh.c9
-rw-r--r--libnl-route-3.sym3
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;