|
@@ -15,6 +15,7 @@
|
|
|
#include <linux/mutex.h>
|
|
|
#include <linux/notifier.h>
|
|
|
#include <linux/netdevice.h>
|
|
|
+#include <linux/if_bridge.h>
|
|
|
#include <net/ip_fib.h>
|
|
|
#include <net/switchdev.h>
|
|
|
|
|
@@ -357,28 +358,156 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
|
|
|
|
|
|
+static int switchdev_port_br_setflag(struct net_device *dev,
|
|
|
+ struct nlattr *nlattr,
|
|
|
+ unsigned long brport_flag)
|
|
|
+{
|
|
|
+ struct switchdev_attr attr = {
|
|
|
+ .id = SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS,
|
|
|
+ };
|
|
|
+ u8 flag = nla_get_u8(nlattr);
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = switchdev_port_attr_get(dev, &attr);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (flag)
|
|
|
+ attr.brport_flags |= brport_flag;
|
|
|
+ else
|
|
|
+ attr.brport_flags &= ~brport_flag;
|
|
|
+
|
|
|
+ return switchdev_port_attr_set(dev, &attr);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct nla_policy
|
|
|
+switchdev_port_bridge_policy[IFLA_BRPORT_MAX + 1] = {
|
|
|
+ [IFLA_BRPORT_STATE] = { .type = NLA_U8 },
|
|
|
+ [IFLA_BRPORT_COST] = { .type = NLA_U32 },
|
|
|
+ [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 },
|
|
|
+ [IFLA_BRPORT_MODE] = { .type = NLA_U8 },
|
|
|
+ [IFLA_BRPORT_GUARD] = { .type = NLA_U8 },
|
|
|
+ [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 },
|
|
|
+ [IFLA_BRPORT_FAST_LEAVE] = { .type = NLA_U8 },
|
|
|
+ [IFLA_BRPORT_LEARNING] = { .type = NLA_U8 },
|
|
|
+ [IFLA_BRPORT_LEARNING_SYNC] = { .type = NLA_U8 },
|
|
|
+ [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 },
|
|
|
+};
|
|
|
+
|
|
|
+static int switchdev_port_br_setlink_protinfo(struct net_device *dev,
|
|
|
+ struct nlattr *protinfo)
|
|
|
+{
|
|
|
+ struct nlattr *attr;
|
|
|
+ int rem;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = nla_validate_nested(protinfo, IFLA_BRPORT_MAX,
|
|
|
+ switchdev_port_bridge_policy);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ nla_for_each_nested(attr, protinfo, rem) {
|
|
|
+ switch (nla_type(attr)) {
|
|
|
+ case IFLA_BRPORT_LEARNING:
|
|
|
+ err = switchdev_port_br_setflag(dev, attr,
|
|
|
+ BR_LEARNING);
|
|
|
+ break;
|
|
|
+ case IFLA_BRPORT_LEARNING_SYNC:
|
|
|
+ err = switchdev_port_br_setflag(dev, attr,
|
|
|
+ BR_LEARNING_SYNC);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int switchdev_port_br_afspec(struct net_device *dev,
|
|
|
+ struct nlattr *afspec,
|
|
|
+ int (*f)(struct net_device *dev,
|
|
|
+ struct switchdev_obj *obj))
|
|
|
+{
|
|
|
+ struct nlattr *attr;
|
|
|
+ struct bridge_vlan_info *vinfo;
|
|
|
+ struct switchdev_obj obj = {
|
|
|
+ .id = SWITCHDEV_OBJ_PORT_VLAN,
|
|
|
+ };
|
|
|
+ int rem;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ nla_for_each_nested(attr, afspec, rem) {
|
|
|
+ if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)
|
|
|
+ continue;
|
|
|
+ if (nla_len(attr) != sizeof(struct bridge_vlan_info))
|
|
|
+ return -EINVAL;
|
|
|
+ vinfo = nla_data(attr);
|
|
|
+ obj.vlan.flags = vinfo->flags;
|
|
|
+ if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
|
|
|
+ if (obj.vlan.vid_start)
|
|
|
+ return -EINVAL;
|
|
|
+ obj.vlan.vid_start = vinfo->vid;
|
|
|
+ } else if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) {
|
|
|
+ if (!obj.vlan.vid_start)
|
|
|
+ return -EINVAL;
|
|
|
+ obj.vlan.vid_end = vinfo->vid;
|
|
|
+ if (obj.vlan.vid_end <= obj.vlan.vid_start)
|
|
|
+ return -EINVAL;
|
|
|
+ err = f(dev, &obj);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ memset(&obj.vlan, 0, sizeof(obj.vlan));
|
|
|
+ } else {
|
|
|
+ if (obj.vlan.vid_start)
|
|
|
+ return -EINVAL;
|
|
|
+ obj.vlan.vid_start = vinfo->vid;
|
|
|
+ obj.vlan.vid_end = vinfo->vid;
|
|
|
+ err = f(dev, &obj);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ memset(&obj.vlan, 0, sizeof(obj.vlan));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
- * switchdev_port_bridge_setlink - Notify switch device port of bridge
|
|
|
- * port attributes
|
|
|
+ * switchdev_port_bridge_setlink - Set bridge port attributes
|
|
|
*
|
|
|
* @dev: port device
|
|
|
- * @nlh: netlink msg with bridge port attributes
|
|
|
- * @flags: bridge setlink flags
|
|
|
+ * @nlh: netlink header
|
|
|
+ * @flags: netlink flags
|
|
|
*
|
|
|
- * Notify switch device port of bridge port attributes
|
|
|
+ * Called for SELF on rtnl_bridge_setlink to set bridge port
|
|
|
+ * attributes.
|
|
|
*/
|
|
|
int switchdev_port_bridge_setlink(struct net_device *dev,
|
|
|
struct nlmsghdr *nlh, u16 flags)
|
|
|
{
|
|
|
- const struct net_device_ops *ops = dev->netdev_ops;
|
|
|
+ struct nlattr *protinfo;
|
|
|
+ struct nlattr *afspec;
|
|
|
+ int err = 0;
|
|
|
|
|
|
- if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
|
|
|
- return 0;
|
|
|
+ protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
|
|
|
+ IFLA_PROTINFO);
|
|
|
+ if (protinfo) {
|
|
|
+ err = switchdev_port_br_setlink_protinfo(dev, protinfo);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
|
|
|
- if (!ops->ndo_bridge_setlink)
|
|
|
- return -EOPNOTSUPP;
|
|
|
+ afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
|
|
|
+ IFLA_AF_SPEC);
|
|
|
+ if (afspec)
|
|
|
+ err = switchdev_port_br_afspec(dev, afspec,
|
|
|
+ switchdev_port_obj_add);
|
|
|
|
|
|
- return ops->ndo_bridge_setlink(dev, nlh, flags);
|
|
|
+ return err;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink);
|
|
|
|