[PATCH] route/link nested bridge attribute

Tobias Jungel tobias.jungel at bisdn.de
Tue Nov 24 05:50:44 PST 2015


Modern kernels support vlan filtering on bridges. This patch stores
configured vlan ids on bridge interfaces in struct bridge_data.
Furthermore vlan ids are accessible via rtnl_link_bridge_get_vlan_info.

The vlan ids are currently stored in the same way as they are sent from
the kernel (i.e. in struct bridge_vlan_info).

Signed-off-by: Tobias Jungel <tobias.jungel at bisdn.de>
---
 include/linux-private/linux/if_bridge.h | 14 +++++
 include/netlink/route/link/bridge.h     |  4 ++
 lib/route/link.c                        |  7 ++-
 lib/route/link/bridge.c                 | 90 +++++++++++++++++++++++++++++----
 libnl-route-3.sym                       |  1 +
 5 files changed, 106 insertions(+), 10 deletions(-)

diff --git a/include/linux-private/linux/if_bridge.h b/include/linux-private/linux/if_bridge.h
index 5db2975..cfe7d12 100644
--- a/include/linux-private/linux/if_bridge.h
+++ b/include/linux-private/linux/if_bridge.h
@@ -103,20 +103,34 @@ struct __fdb_entry {
 
 #define BRIDGE_MODE_VEB		0	/* Default loopback mode */
 #define BRIDGE_MODE_VEPA	1	/* 802.1Qbg defined VEPA mode */
+#define BRIDGE_MODE_UNDEF	0xFFFF  /* mode undefined */
 
 /* Bridge management nested attributes
  * [IFLA_AF_SPEC] = {
  *     [IFLA_BRIDGE_FLAGS]
  *     [IFLA_BRIDGE_MODE]
+ *     [IFLA_BRIDGE_VLAN_INFO]
  * }
  */
 enum {
 	IFLA_BRIDGE_FLAGS,
 	IFLA_BRIDGE_MODE,
+	IFLA_BRIDGE_VLAN_INFO,
 	__IFLA_BRIDGE_MAX,
 };
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
 
+#define BRIDGE_VLAN_INFO_MASTER	(1<<0)	/* Operate on Bridge device as well */
+#define BRIDGE_VLAN_INFO_PVID	(1<<1)	/* VLAN is PVID, ingress untagged */
+#define BRIDGE_VLAN_INFO_UNTAGGED	(1<<2)	/* VLAN egresses untagged */
+#define BRIDGE_VLAN_INFO_RANGE_BEGIN	(1<<3) /* VLAN is start of vlan range */
+#define BRIDGE_VLAN_INFO_RANGE_END	(1<<4) /* VLAN is end of vlan range */
+
+struct bridge_vlan_info {
+	__u16 flags;
+	__u16 vid;
+};
+
 /* Bridge multicast database attributes
  * [MDBA_MDB] = {
  *     [MDBA_MDB_ENTRY] = {
diff --git a/include/netlink/route/link/bridge.h b/include/netlink/route/link/bridge.h
index 16a4505..c04a3e2 100644
--- a/include/netlink/route/link/bridge.h
+++ b/include/netlink/route/link/bridge.h
@@ -14,6 +14,7 @@
 
 #include <netlink/netlink.h>
 #include <netlink/route/link.h>
+#include <linux/if_bridge.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -52,6 +53,9 @@ 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 struct bridge_vlan_info *rtnl_link_bridge_get_vlan_info(struct rtnl_link *, int *);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/route/link.c b/lib/route/link.c
index cfe3779..01bd9ff 100644
--- a/lib/route/link.c
+++ b/lib/route/link.c
@@ -604,6 +604,12 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
 		struct nlattr *af_attr;
 		int remaining;
 
+		if (AF_BRIDGE == family && af_ops && af_ops->ao_parse_af) {
+			err = af_ops->ao_parse_af(link, tb[IFLA_AF_SPEC], link->l_af_data[family]);
+			if (err < 0)
+				goto errout;
+		}
+
 		nla_for_each_nested(af_attr, tb[IFLA_AF_SPEC], remaining) {
 			af_ops = af_lookup_and_alloc(link, nla_type(af_attr));
 			if (af_ops && af_ops->ao_parse_af) {
@@ -613,7 +619,6 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
 				if (err < 0)
 					goto errout;
 			}
-
 		}
 		link->ce_mask |= LINK_ATTR_AF_SPEC;
 	}
diff --git a/lib/route/link/bridge.c b/lib/route/link/bridge.c
index 544f02c..1acb1c2 100644
--- a/lib/route/link/bridge.c
+++ b/lib/route/link/bridge.c
@@ -23,7 +23,6 @@
 #include <netlink/route/rtnl.h>
 #include <netlink/route/link/bridge.h>
 #include <netlink-private/route/link/api.h>
-#include <linux/if_bridge.h>
 
 /** @cond SKIP */
 #define BRIDGE_ATTR_PORT_STATE		(1 << 0)
@@ -35,13 +34,16 @@
 
 struct bridge_data
 {
-	uint8_t			b_port_state;
-	uint8_t			b_priv_flags; /* internal flags */
-	uint16_t		b_priority;
-	uint32_t		b_cost;
-	uint32_t		b_flags;
-	uint32_t		b_flags_mask;
-	uint32_t                ce_mask; /* HACK to support attr macros */
+	uint8_t				b_port_state;
+	uint8_t				b_priv_flags; /* internal flags */
+	uint16_t			b_priority;
+	uint32_t			b_cost;
+	uint32_t			b_flags;
+	uint32_t			b_flags_mask;
+	uint32_t                	ce_mask; /* HACK to support attr macros */
+	uint16_t			b_mode;
+	uint32_t			b_vlan_size;
+	struct bridge_vlan_info *	b_vlan;
 };
 
 static struct rtnl_link_af_ops bridge_ops;
@@ -69,12 +71,63 @@ static void *bridge_clone(struct rtnl_link *link, void *data)
 	if ((bd = bridge_alloc(link)))
 		memcpy(bd, data, sizeof(*bd));
 
+	bd->b_vlan = calloc(bd->b_vlan_size, sizeof(*bd->b_vlan));
+	memcpy(bd->b_vlan, ((struct bridge_data *)data)->b_vlan, bd->b_vlan_size * sizeof(*bd->b_vlan));
+
 	return bd;
 }
 
 static void bridge_free(struct rtnl_link *link, void *data)
 {
-	free(data);
+	struct bridge_data *bd = data;
+	if (bd->b_vlan)
+		free(bd->b_vlan);
+	free(bd);
+}
+
+static int bridge_parse_af(struct rtnl_link *link, struct nlattr *attr, void *data)
+{
+	struct bridge_data *bd = data;
+
+	struct nlattr *af_attr;
+	int remaining, i = 0;
+
+	nla_for_each_nested(af_attr, attr, remaining) {
+
+		if (IFLA_BRIDGE_MAX < nla_type(af_attr))
+			continue;
+
+#if 0 /* not yet handled */
+		if (IFLA_BRIDGE_FLAGS == nla_type(af_attr) && 2 == nla_len(af_attr)) {
+		}
+
+		if (IFLA_BRIDGE_MODE == nla_type(af_attr) && 2 == nla_len(af_attr)) {
+		}
+#endif
+
+		if (IFLA_BRIDGE_VLAN_INFO == nla_type(af_attr) &&
+		    sizeof(*bd->b_vlan) == nla_len(af_attr)) {
+			i++;
+		}
+	}
+
+	bd->b_vlan_size = i;
+	bd->b_vlan = calloc(bd->b_vlan_size, sizeof(*bd->b_vlan));
+	if (bd->b_vlan == NULL)
+		return -NLE_NOMEM;
+
+	i = 0;
+	nla_for_each_nested(af_attr, attr, remaining) {
+
+		if (IFLA_BRIDGE_VLAN_INFO != nla_type(af_attr) ||
+		    IFLA_BRIDGE_MAX < nla_type(af_attr) ||
+		    sizeof(*bd->b_vlan) != nla_len(af_attr))
+			continue;
+
+		memcpy(&bd->b_vlan[i++], nla_data(af_attr), sizeof(*bd->b_vlan));
+	}
+
+	return 0;
 }
 
 static struct nla_policy br_attrs_policy[IFLA_BRPORT_MAX+1] = {
@@ -479,6 +532,24 @@ int rtnl_link_bridge_get_flags(struct rtnl_link *link)
 	return bd->b_flags;
 }
 
+/**
+ *
+ * @param link
+ * @param negress
+ * @return
+ */
+struct bridge_vlan_info *rtnl_link_bridge_get_vlan_info(struct rtnl_link *link,
+							int *negress)
+{
+	struct bridge_data *bd = bridge_data(link);
+
+	if (negress == NULL)
+		return NULL;
+
+	*negress = bd->b_vlan_size;
+	return bd->b_vlan;
+}
+
 static const struct trans_tbl bridge_flags[] = {
 	__ADD(RTNL_BRIDGE_HAIRPIN_MODE, hairpin_mode),
 	__ADD(RTNL_BRIDGE_BPDU_GUARD, 	bpdu_guard),
@@ -509,6 +580,7 @@ static struct rtnl_link_af_ops bridge_ops = {
 	.ao_clone			= &bridge_clone,
 	.ao_free			= &bridge_free,
 	.ao_parse_protinfo		= &bridge_parse_protinfo,
+	.ao_parse_af			= &bridge_parse_af,
 	.ao_dump[NL_DUMP_DETAILS]	= &bridge_dump_details,
 	.ao_compare			= &bridge_compare,
 };
diff --git a/libnl-route-3.sym b/libnl-route-3.sym
index 627cb43..bc50d47 100644
--- a/libnl-route-3.sym
+++ b/libnl-route-3.sym
@@ -249,6 +249,7 @@ global:
 	rtnl_link_bridge_set_priority;
 	rtnl_link_bridge_str2flags;
 	rtnl_link_bridge_unset_flags;
+	rtnl_link_bridge_get_vlan_info;
 	rtnl_link_build_add_request;
 	rtnl_link_build_change_request;
 	rtnl_link_build_delete_request;
-- 
2.5.0




More information about the libnl mailing list