]> granicus.if.org Git - libnl/commitdiff
lib/route: Support IFLA_BRIDGE_MODE
authorJef Oliver <jef.oliver@intel.com>
Thu, 1 Sep 2016 00:27:09 +0000 (17:27 -0700)
committerThomas Haller <thaller@redhat.com>
Sat, 24 Sep 2016 11:51:29 +0000 (13:51 +0200)
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@intel.com>
Signed-off-by: Thomas Haller <thaller@redhat.com>
include/netlink/route/link/bridge.h
lib/route/link/bridge.c
libnl-route-3.sym

index b7b9ae972945d8c2d47716b467fe0f2f5321e2a1..f2e16e34c35a98fb545653b1fba66c55e4114653 100644 (file)
@@ -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);
index a47b79b7d1daf3b5c85e750878e0a5e4db5633e2..57f1ec8dda6a0db27fa5142a6e379c111df6ce0f 100644 (file)
@@ -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)
index f80714f0bed1b38b0c50f951735d19e7774e2148..5f42fd2a33e88050b37a145fc411b573317fc58e 100644 (file)
@@ -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;