switchdev: Execute bridge ndos only for bridge ports
[cascardo/linux.git] / net / switchdev / switchdev.c
index a5fc9dd..3b95fe9 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/workqueue.h>
 #include <linux/if_vlan.h>
 #include <linux/rtnetlink.h>
-#include <net/ip_fib.h>
 #include <net/switchdev.h>
 
 /**
@@ -344,8 +343,6 @@ static size_t switchdev_obj_size(const struct switchdev_obj *obj)
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
                return sizeof(struct switchdev_obj_port_vlan);
-       case SWITCHDEV_OBJ_ID_IPV4_FIB:
-               return sizeof(struct switchdev_obj_ipv4_fib);
        case SWITCHDEV_OBJ_ID_PORT_FDB:
                return sizeof(struct switchdev_obj_port_fdb);
        case SWITCHDEV_OBJ_ID_PORT_MDB:
@@ -774,6 +771,9 @@ int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
        u32 mask = BR_LEARNING | BR_LEARNING_SYNC | BR_FLOOD;
        int err;
 
+       if (!netif_is_bridge_port(dev))
+               return -EOPNOTSUPP;
+
        err = switchdev_port_attr_get(dev, &attr);
        if (err && err != -EOPNOTSUPP)
                return err;
@@ -929,6 +929,9 @@ int switchdev_port_bridge_setlink(struct net_device *dev,
        struct nlattr *afspec;
        int err = 0;
 
+       if (!netif_is_bridge_port(dev))
+               return -EOPNOTSUPP;
+
        protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
                                   IFLA_PROTINFO);
        if (protinfo) {
@@ -962,6 +965,9 @@ int switchdev_port_bridge_dellink(struct net_device *dev,
 {
        struct nlattr *afspec;
 
+       if (!netif_is_bridge_port(dev))
+               return -EOPNOTSUPP;
+
        afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
                                 IFLA_AF_SPEC);
        if (afspec)
@@ -1042,7 +1048,7 @@ static int switchdev_port_fdb_dump_cb(struct switchdev_obj *obj)
        struct nlmsghdr *nlh;
        struct ndmsg *ndm;
 
-       if (dump->idx < dump->cb->args[0])
+       if (dump->idx < dump->cb->args[2])
                goto skip;
 
        nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
@@ -1089,7 +1095,7 @@ nla_put_failure:
  */
 int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
                            struct net_device *dev,
-                           struct net_device *filter_dev, int idx)
+                           struct net_device *filter_dev, int *idx)
 {
        struct switchdev_fdb_dump dump = {
                .fdb.obj.orig_dev = dev,
@@ -1097,207 +1103,27 @@ int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
                .dev = dev,
                .skb = skb,
                .cb = cb,
-               .idx = idx,
+               .idx = *idx,
        };
        int err;
 
        err = switchdev_port_obj_dump(dev, &dump.fdb.obj,
                                      switchdev_port_fdb_dump_cb);
-       cb->args[1] = err;
-       return dump.idx;
+       *idx = dump.idx;
+       return err;
 }
 EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump);
 
-static struct net_device *switchdev_get_lowest_dev(struct net_device *dev)
-{
-       const struct switchdev_ops *ops = dev->switchdev_ops;
-       struct net_device *lower_dev;
-       struct net_device *port_dev;
-       struct list_head *iter;
-
-       /* Recusively search down until we find a sw port dev.
-        * (A sw port dev supports switchdev_port_attr_get).
-        */
-
-       if (ops && ops->switchdev_port_attr_get)
-               return dev;
-
-       netdev_for_each_lower_dev(dev, lower_dev, iter) {
-               port_dev = switchdev_get_lowest_dev(lower_dev);
-               if (port_dev)
-                       return port_dev;
-       }
-
-       return NULL;
-}
-
-static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi)
-{
-       struct switchdev_attr attr = {
-               .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
-       };
-       struct switchdev_attr prev_attr;
-       struct net_device *dev = NULL;
-       int nhsel;
-
-       ASSERT_RTNL();
-
-       /* For this route, all nexthop devs must be on the same switch. */
-
-       for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
-               const struct fib_nh *nh = &fi->fib_nh[nhsel];
-
-               if (!nh->nh_dev)
-                       return NULL;
-
-               dev = switchdev_get_lowest_dev(nh->nh_dev);
-               if (!dev)
-                       return NULL;
-
-               attr.orig_dev = dev;
-               if (switchdev_port_attr_get(dev, &attr))
-                       return NULL;
-
-               if (nhsel > 0 &&
-                   !netdev_phys_item_id_same(&prev_attr.u.ppid, &attr.u.ppid))
-                               return NULL;
-
-               prev_attr = attr;
-       }
-
-       return dev;
-}
-
-/**
- *     switchdev_fib_ipv4_add - Add/modify switch IPv4 route entry
- *
- *     @dst: route's IPv4 destination address
- *     @dst_len: destination address length (prefix length)
- *     @fi: route FIB info structure
- *     @tos: route TOS
- *     @type: route type
- *     @nlflags: netlink flags passed in (NLM_F_*)
- *     @tb_id: route table ID
- *
- *     Add/modify switch IPv4 route entry.
- */
-int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
-                          u8 tos, u8 type, u32 nlflags, u32 tb_id)
-{
-       struct switchdev_obj_ipv4_fib ipv4_fib = {
-               .obj.id = SWITCHDEV_OBJ_ID_IPV4_FIB,
-               .dst = dst,
-               .dst_len = dst_len,
-               .fi = fi,
-               .tos = tos,
-               .type = type,
-               .nlflags = nlflags,
-               .tb_id = tb_id,
-       };
-       struct net_device *dev;
-       int err = 0;
-
-       /* Don't offload route if using custom ip rules or if
-        * IPv4 FIB offloading has been disabled completely.
-        */
-
-#ifdef CONFIG_IP_MULTIPLE_TABLES
-       if (fi->fib_net->ipv4.fib_has_custom_rules)
-               return 0;
-#endif
-
-       if (fi->fib_net->ipv4.fib_offload_disabled)
-               return 0;
-
-       dev = switchdev_get_dev_by_nhs(fi);
-       if (!dev)
-               return 0;
-
-       ipv4_fib.obj.orig_dev = dev;
-       err = switchdev_port_obj_add(dev, &ipv4_fib.obj);
-       if (!err)
-               fi->fib_flags |= RTNH_F_OFFLOAD;
-
-       return err == -EOPNOTSUPP ? 0 : err;
-}
-EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_add);
-
-/**
- *     switchdev_fib_ipv4_del - Delete IPv4 route entry from switch
- *
- *     @dst: route's IPv4 destination address
- *     @dst_len: destination address length (prefix length)
- *     @fi: route FIB info structure
- *     @tos: route TOS
- *     @type: route type
- *     @tb_id: route table ID
- *
- *     Delete IPv4 route entry from switch device.
- */
-int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
-                          u8 tos, u8 type, u32 tb_id)
-{
-       struct switchdev_obj_ipv4_fib ipv4_fib = {
-               .obj.id = SWITCHDEV_OBJ_ID_IPV4_FIB,
-               .dst = dst,
-               .dst_len = dst_len,
-               .fi = fi,
-               .tos = tos,
-               .type = type,
-               .nlflags = 0,
-               .tb_id = tb_id,
-       };
-       struct net_device *dev;
-       int err = 0;
-
-       if (!(fi->fib_flags & RTNH_F_OFFLOAD))
-               return 0;
-
-       dev = switchdev_get_dev_by_nhs(fi);
-       if (!dev)
-               return 0;
-
-       ipv4_fib.obj.orig_dev = dev;
-       err = switchdev_port_obj_del(dev, &ipv4_fib.obj);
-       if (!err)
-               fi->fib_flags &= ~RTNH_F_OFFLOAD;
-
-       return err == -EOPNOTSUPP ? 0 : err;
-}
-EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_del);
-
-/**
- *     switchdev_fib_ipv4_abort - Abort an IPv4 FIB operation
- *
- *     @fi: route FIB info structure
- */
-void switchdev_fib_ipv4_abort(struct fib_info *fi)
-{
-       /* There was a problem installing this route to the offload
-        * device.  For now, until we come up with more refined
-        * policy handling, abruptly end IPv4 fib offloading for
-        * for entire net by flushing offload device(s) of all
-        * IPv4 routes, and mark IPv4 fib offloading broken from
-        * this point forward.
-        */
-
-       fib_flush_external(fi->fib_net);
-       fi->fib_net->ipv4.fib_offload_disabled = true;
-}
-EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_abort);
-
 bool switchdev_port_same_parent_id(struct net_device *a,
                                   struct net_device *b)
 {
        struct switchdev_attr a_attr = {
                .orig_dev = a,
                .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
-               .flags = SWITCHDEV_F_NO_RECURSE,
        };
        struct switchdev_attr b_attr = {
                .orig_dev = b,
                .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
-               .flags = SWITCHDEV_F_NO_RECURSE,
        };
 
        if (switchdev_port_attr_get(a, &a_attr) ||
@@ -1306,89 +1132,4 @@ bool switchdev_port_same_parent_id(struct net_device *a,
 
        return netdev_phys_item_id_same(&a_attr.u.ppid, &b_attr.u.ppid);
 }
-
-static u32 switchdev_port_fwd_mark_get(struct net_device *dev,
-                                      struct net_device *group_dev)
-{
-       struct net_device *lower_dev;
-       struct list_head *iter;
-
-       netdev_for_each_lower_dev(group_dev, lower_dev, iter) {
-               if (lower_dev == dev)
-                       continue;
-               if (switchdev_port_same_parent_id(dev, lower_dev))
-                       return lower_dev->offload_fwd_mark;
-               return switchdev_port_fwd_mark_get(dev, lower_dev);
-       }
-
-       return dev->ifindex;
-}
 EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id);
-
-static void switchdev_port_fwd_mark_reset(struct net_device *group_dev,
-                                         u32 old_mark, u32 *reset_mark)
-{
-       struct net_device *lower_dev;
-       struct list_head *iter;
-
-       netdev_for_each_lower_dev(group_dev, lower_dev, iter) {
-               if (lower_dev->offload_fwd_mark == old_mark) {
-                       if (!*reset_mark)
-                               *reset_mark = lower_dev->ifindex;
-                       lower_dev->offload_fwd_mark = *reset_mark;
-               }
-               switchdev_port_fwd_mark_reset(lower_dev, old_mark, reset_mark);
-       }
-}
-
-/**
- *     switchdev_port_fwd_mark_set - Set port offload forwarding mark
- *
- *     @dev: port device
- *     @group_dev: containing device
- *     @joining: true if dev is joining group; false if leaving group
- *
- *     An ungrouped port's offload mark is just its ifindex.  A grouped
- *     port's (member of a bridge, for example) offload mark is the ifindex
- *     of one of the ports in the group with the same parent (switch) ID.
- *     Ports on the same device in the same group will have the same mark.
- *
- *     Example:
- *
- *             br0             ifindex=9
- *               sw1p1         ifindex=2       mark=2
- *               sw1p2         ifindex=3       mark=2
- *               sw2p1         ifindex=4       mark=5
- *               sw2p2         ifindex=5       mark=5
- *
- *     If sw2p2 leaves the bridge, we'll have:
- *
- *             br0             ifindex=9
- *               sw1p1         ifindex=2       mark=2
- *               sw1p2         ifindex=3       mark=2
- *               sw2p1         ifindex=4       mark=4
- *             sw2p2           ifindex=5       mark=5
- */
-void switchdev_port_fwd_mark_set(struct net_device *dev,
-                                struct net_device *group_dev,
-                                bool joining)
-{
-       u32 mark = dev->ifindex;
-       u32 reset_mark = 0;
-
-       if (group_dev) {
-               ASSERT_RTNL();
-               if (joining)
-                       mark = switchdev_port_fwd_mark_get(dev, group_dev);
-               else if (dev->offload_fwd_mark == mark)
-                       /* Ohoh, this port was the mark reference port,
-                        * but it's leaving the group, so reset the
-                        * mark for the remaining ports in the group.
-                        */
-                       switchdev_port_fwd_mark_reset(group_dev, mark,
-                                                     &reset_mark);
-       }
-
-       dev->offload_fwd_mark = mark;
-}
-EXPORT_SYMBOL_GPL(switchdev_port_fwd_mark_set);