[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