[PATCH 5/5] lib/route: Support IFLA_BRIDGE_MODE

Jef Oliver jef.oliver at intel.com
Wed Aug 31 17:27:09 PDT 2016


This patch adds support for hardware assisted bridge modes stored
in IFLA_AF_SPEC[IFLA_BRIDGE_MODE].

This patch adds rtnl_link_[g/s]et_hwmode() functions, allowing
for getting and setting the hardware mode for a bridged link.

This patch adds the convenience functions for translating between
integer and string names for hardware modes.

This patch adds rtnl_link_bridge_set_self(), a function that
stores flags in IFLA_AF_SPEC[IFLA_BRIDGE_FLAGS], an attribute
that tells the kernel whether it should apply settings to the
virtual bridge device or the hardware device itself. This requires
hardware that supports the hardware flags.

This patch adds bridge_fill_af(), a function to fill IFLA_AF_SPEC
with AF_BRIDGE specific information.

Signed-off-by: Jef Oliver <jef.oliver at intel.com>
---
 include/netlink/route/link/bridge.h |  13 ++++
 lib/route/link/bridge.c             | 143 +++++++++++++++++++++++++++++++++++-
 libnl-route-3.sym                   |   5 ++
 3 files changed, 160 insertions(+), 1 deletion(-)

diff --git a/include/netlink/route/link/bridge.h b/include/netlink/route/link/bridge.h
index b7b9ae9..f2e16e3 100644
--- a/include/netlink/route/link/bridge.h
+++ b/include/netlink/route/link/bridge.h
@@ -43,6 +43,11 @@ enum rtnl_link_bridge_flags {
 	RTNL_BRIDGE_LEARNING_SYNC	= 0x0040,
 };
 
+#define RTNL_BRIDGE_HWMODE_VEB BRIDGE_MODE_VEB
+#define RTNL_BRIDGE_HWMODE_VEPA BRIDGE_MODE_VEPA
+#define RTNL_BRIDGE_HWMODE_MAX BRIDGE_MODE_VEPA
+#define RTNL_BRIDGE_HWMODE_UNDEF BRIDGE_MODE_UNDEF
+
 extern struct rtnl_link *rtnl_link_bridge_alloc(void);
 
 extern int	rtnl_link_is_bridge(struct rtnl_link *);
@@ -61,12 +66,20 @@ extern int	rtnl_link_bridge_unset_flags(struct rtnl_link *, unsigned int);
 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_get_hwmode(struct rtnl_link *, uint16_t *);
+extern int	rtnl_link_bridge_set_hwmode(struct rtnl_link *, uint16_t);
+
 extern char * rtnl_link_bridge_flags2str(int, char *, size_t);
 extern int	rtnl_link_bridge_str2flags(const char *);
 
 extern char * rtnl_link_bridge_portstate2str(int, char *, size_t);
 extern int  rtnl_link_bridge_str2portstate(const char *);
 
+extern char * rtnl_link_bridge_hwmode2str(uint16_t, char *, size_t);
+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_pvid(struct rtnl_link *link);
diff --git a/lib/route/link/bridge.c b/lib/route/link/bridge.c
index a47b79b..57f1ec8 100644
--- a/lib/route/link/bridge.c
+++ b/lib/route/link/bridge.c
@@ -33,6 +33,8 @@
 #define BRIDGE_ATTR_COST		(1 << 2)
 #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 PRIV_FLAG_NEW_ATTRS		(1 << 0)
 
@@ -40,7 +42,9 @@ struct bridge_data
 {
 	uint8_t			b_port_state;
 	uint8_t			b_priv_flags; /* internal flags */
+	uint16_t		b_hwmode;
 	uint16_t		b_priority;
+	uint16_t		b_self; /* here for comparison reasons */
 	uint32_t		b_cost;
 	uint32_t		b_flags;
 	uint32_t		b_flags_mask;
@@ -188,7 +192,10 @@ static int bridge_parse_af_full(struct rtnl_link *link, struct nlattr *attr_full
 
 	nla_for_each_nested(attr, attr_full, remaining) {
 
-		if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)
+		if (nla_type(attr) == IFLA_BRIDGE_MODE) {
+			bd->b_hwmode = nla_get_u16(attr);
+			bd->ce_mask |= BRIDGE_ATTR_HWMODE;
+		} else if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)
 			continue;
 
 		if (nla_len(attr) != sizeof(struct bridge_vlan_info))
@@ -232,6 +239,23 @@ static int bridge_parse_af_full(struct rtnl_link *link, struct nlattr *attr_full
 	return 0;
 }
 
+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);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
 static int bridge_fill_pi(struct rtnl_link *link, struct nl_msg *msg,
 		   void *data)
 {
@@ -370,6 +394,13 @@ 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_HWMODE) {
+		char hbuf[32];
+
+		rtnl_link_bridge_hwmode2str(bd->b_hwmode, hbuf, sizeof(hbuf));
+		nl_dump(p, "hwmode %s", hbuf);
+	}
+
 	if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN)
 		rtnl_link_bridge_dump_vlans(p, bd);
 
@@ -397,6 +428,8 @@ static int bridge_compare(struct rtnl_link *_a, struct rtnl_link *_b,
 	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)));
+	diff |= BRIDGE_DIFF(HWMODE, a->b_hwmode != b->b_hwmode);
+	diff |= BRIDGE_DIFF(SELF, a->b_self != b->b_self);
 
 	if (flags & LOOSE_COMPARISON)
 		diff |= BRIDGE_DIFF(FLAGS,
@@ -708,6 +741,90 @@ int rtnl_link_bridge_get_flags(struct rtnl_link *link)
 	return bd->b_flags;
 }
 
+/**
+ * Set link change type to self
+ * @arg link		Link Object of type bridge
+ *
+ * This will set the bridge change flag to self, meaning that changes to
+ * be applied with this link object will be applied directly to the physical
+ * device in a bridge instead of the virtual device.
+ *
+ * @return 0 on success or negative error code
+ * @return -NLE_OPNOTSUP Link is not a bridge
+ */
+int rtnl_link_bridge_set_self(struct rtnl_link *link) {
+	struct bridge_data *bd = bridge_data(link);
+
+	IS_BRIDGE_LINK_ASSERT(link);
+
+	bd->b_self |= 1;
+	bd->ce_mask |= BRIDGE_ATTR_SELF;
+
+	return 0;
+}
+
+/**
+ * Get hardware mode
+ * @arg link		Link object of type bridge
+ *
+ * @see rtnl_link_bridge_set_hwmode()
+ *
+ * @return 1 and set hwmode if hardware mode is present
+ * @return 0 if hardware mode is not present
+ * @return -NLE_OPNOTSUP Link is not a bridge
+ */
+int rtnl_link_bridge_get_hwmode(struct rtnl_link *link, uint16_t *hwmode)
+{
+	struct bridge_data *bd = bridge_data(link);
+
+	IS_BRIDGE_LINK_ASSERT(link);
+
+	if (bd->ce_mask & BRIDGE_ATTR_HWMODE) {
+		*hwmode = bd->b_hwmode;
+		return 1;
+	}
+	else
+		return 0;
+}
+
+/**
+ * Set hardware mode
+ * @arg link		Link object of type bridge
+ * @arg hwmode		Hardware mode to set on link
+ *
+ * This will set the hardware mode of a link when it supports hardware
+ * offloads for bridging.
+ * @see rtnl_link_bridge_get_hwmode()
+ *
+ * Valid modes are:
+ *   - RTNL_BRIDGE_HWMODE_VEB
+ *   - RTNL_BRIDGE_HWMODE_VEPA
+ *
+ * When setting hardware mode, the change type will be set to self.
+ * @see rtnl_link_bridge_set_self()
+ *
+ * @return 0 on success or negative error code
+ * @return -NLE_OPNOTSUP Link is not a bridge
+ * @return -NLE_INVAL when specified hwmode is unsupported.
+ */
+int rtnl_link_bridge_set_hwmode(struct rtnl_link *link, uint16_t hwmode)
+{
+	int err;
+	struct bridge_data *bd = bridge_data(link);
+
+	if (hwmode > RTNL_BRIDGE_HWMODE_MAX)
+		return -NLE_INVAL;
+
+    if ((err = rtnl_link_bridge_set_self(link)) < 0)
+		return err;
+
+	bd->b_hwmode = hwmode;
+	bd->ce_mask |= BRIDGE_ATTR_HWMODE;
+
+	return 0;
+}
+
+
 static const struct trans_tbl bridge_flags[] = {
 	__ADD(RTNL_BRIDGE_HAIRPIN_MODE, hairpin_mode),
 	__ADD(RTNL_BRIDGE_BPDU_GUARD, 	bpdu_guard),
@@ -760,6 +877,28 @@ int rtnl_link_bridge_str2portstate(const char *name)
 
 /** @} */
 
+static const struct trans_tbl hw_modes[] = {
+	__ADD(RTNL_BRIDGE_HWMODE_VEB, veb),
+	__ADD(RTNL_BRIDGE_HWMODE_VEPA, vepa),
+	__ADD(RTNL_BRIDGE_HWMODE_UNDEF, undef),
+};
+
+/**
+ * @name Hardware Mode Translation
+ * @{
+ */
+
+char *rtnl_link_bridge_hwmode2str(uint16_t st, char *buf, size_t len) {
+	return __type2str(st, buf, len, hw_modes, ARRAY_SIZE(hw_modes));
+}
+
+uint16_t rtnl_link_bridge_str2hwmode(const char *name)
+{
+	return __str2type(name, hw_modes, ARRAY_SIZE(hw_modes));
+}
+
+/** @} */
+
 int rtnl_link_bridge_pvid(struct rtnl_link *link)
 {
 	struct bridge_data *bd;
@@ -818,9 +957,11 @@ static struct rtnl_link_af_ops bridge_ops = {
 	.ao_compare			= &bridge_compare,
 	.ao_parse_af_full		= &bridge_parse_af_full,
 	.ao_get_af			= &bridge_get_af,
+	.ao_fill_af			= &bridge_fill_af,
 	.ao_fill_pi			= &bridge_fill_pi,
 	.ao_fill_pi_flags	= NLA_F_NESTED,
 	.ao_override_rtm	= 1,
+	.ao_fill_af_no_nest	= 1,
 };
 
 static void __init bridge_init(void)
diff --git a/libnl-route-3.sym b/libnl-route-3.sym
index f80714f..5f42fd2 100644
--- a/libnl-route-3.sym
+++ b/libnl-route-3.sym
@@ -951,5 +951,10 @@ libnl_3_2_29 {
 global:
 	rtnl_link_bridge_portstate2str;
 	rtnl_link_bridge_str2portstate;
+	rtnl_link_bridge_set_self;
+	rtnl_link_bridge_get_hwmode;
+	rtnl_link_bridge_set_hwmode;
+	rtnl_link_bridge_hwmode2str;
+	rtnl_link_bridge_str2hwmode;
 } libnl_3_2_28;
 
-- 
2.9.3




More information about the libnl mailing list