bonding: make it possible to have unlimited nested upper vlans
authorVeaceslav Falico <vfalico@gmail.com>
Thu, 17 Jul 2014 15:02:23 +0000 (17:02 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 21 Jul 2014 03:35:00 +0000 (20:35 -0700)
Currently we're limited by a constant level of vlan nestings, and fail to
find anything beyound that level (currently 2).

To fix this - remove the limit of nestings when going through device tree,
and when the end device is found - allocate the needed amount of vlan tags
and return them, instead of found/not found.

CC: Jay Vosburgh <j.vosburgh@gmail.com>
CC: Andy Gospodarek <andy@greyhouse.net>
Signed-off-by: Veaceslav Falico <vfalico@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/bonding/bond_alb.c
drivers/net/bonding/bond_main.c
drivers/net/bonding/bonding.h

index d3c6801f101e5e89d093d88d390f406209494dcc..95dd1f58c260d941c51ac4a369b2b97ba3f4ba34 100644 (file)
@@ -1042,7 +1042,7 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],
        struct bonding *bond = bond_get_bond_by_slave(slave);
        struct net_device *upper;
        struct list_head *iter;
-       struct bond_vlan_tag tags[BOND_MAX_VLAN_ENCAP];
+       struct bond_vlan_tag *tags;
 
        /* send untagged */
        alb_send_lp_vid(slave, mac_addr, 0, 0);
@@ -1070,10 +1070,12 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],
                 * when strict_match is turned off.
                 */
                if (netif_is_macvlan(upper) && !strict_match) {
-                       memset(tags, 0, sizeof(tags));
-                       bond_verify_device_path(bond->dev, upper, tags);
+                       tags = bond_verify_device_path(bond->dev, upper, 0);
+                       if (IS_ERR_OR_NULL(tags))
+                               BUG();
                        alb_send_lp_vid(slave, upper->dev_addr,
                                        tags[0].vlan_proto, tags[0].vlan_id);
+                       kfree(tags);
                }
        }
        rcu_read_unlock();
index 14f78955161669dbe161b8ef86b809dcc4e1f2f4..023ec365209c06543d043791194270a95c2c7c35 100644 (file)
@@ -2145,7 +2145,7 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op,
                          struct bond_vlan_tag *tags)
 {
        struct sk_buff *skb;
-       int i;
+       struct bond_vlan_tag *outer_tag = tags;
 
        netdev_dbg(slave_dev, "arp %d on slave %s: dst %pI4 src %pI4\n",
                   arp_op, slave_dev->name, &dest_ip, &src_ip);
@@ -2158,30 +2158,42 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op,
                return;
        }
 
+       if (!tags || tags->vlan_proto == VLAN_N_VID)
+               goto xmit;
+
+       tags++;
+
        /* Go through all the tags backwards and add them to the packet */
-       for (i = BOND_MAX_VLAN_ENCAP - 1; i > 0; i--) {
-               if (!tags[i].vlan_id)
+       while (tags->vlan_proto != VLAN_N_VID) {
+               if (!tags->vlan_id) {
+                       tags++;
                        continue;
+               }
 
                netdev_dbg(slave_dev, "inner tag: proto %X vid %X\n",
-                          ntohs(tags[i].vlan_proto), tags[i].vlan_id);
-               skb = __vlan_put_tag(skb, tags[i].vlan_proto,
-                                    tags[i].vlan_id);
+                          ntohs(outer_tag->vlan_proto), tags->vlan_id);
+               skb = __vlan_put_tag(skb, tags->vlan_proto,
+                                    tags->vlan_id);
                if (!skb) {
                        net_err_ratelimited("failed to insert inner VLAN tag\n");
                        return;
                }
+
+               tags++;
        }
        /* Set the outer tag */
-       if (tags[0].vlan_id) {
+       if (outer_tag->vlan_id) {
                netdev_dbg(slave_dev, "outer tag: proto %X vid %X\n",
-                          ntohs(tags[0].vlan_proto), tags[0].vlan_id);
-               skb = vlan_put_tag(skb, tags[0].vlan_proto, tags[0].vlan_id);
+                          ntohs(outer_tag->vlan_proto), outer_tag->vlan_id);
+               skb = vlan_put_tag(skb, outer_tag->vlan_proto,
+                                  outer_tag->vlan_id);
                if (!skb) {
                        net_err_ratelimited("failed to insert outer VLAN tag\n");
                        return;
                }
        }
+
+xmit:
        arp_xmit(skb);
 }
 
@@ -2191,46 +2203,50 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op,
  * When the path is validated, collect any vlan information in the
  * path.
  */
-bool bond_verify_device_path(struct net_device *start_dev,
-                            struct net_device *end_dev,
-                            struct bond_vlan_tag *tags)
+struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
+                                             struct net_device *end_dev,
+                                             int level)
 {
+       struct bond_vlan_tag *tags;
        struct net_device *upper;
        struct list_head  *iter;
-       int  idx;
 
-       if (start_dev == end_dev)
-               return true;
+       if (start_dev == end_dev) {
+               tags = kzalloc(sizeof(*tags) * (level + 1), GFP_ATOMIC);
+               if (!tags)
+                       return ERR_PTR(-ENOMEM);
+               tags[level].vlan_proto = VLAN_N_VID;
+               return tags;
+       }
 
        netdev_for_each_upper_dev_rcu(start_dev, upper, iter) {
-               if (bond_verify_device_path(upper, end_dev, tags)) {
-                       if (is_vlan_dev(upper)) {
-                               idx = vlan_get_encap_level(upper);
-                               if (idx >= BOND_MAX_VLAN_ENCAP)
-                                       return false;
-
-                               tags[idx].vlan_proto =
-                                                   vlan_dev_vlan_proto(upper);
-                               tags[idx].vlan_id = vlan_dev_vlan_id(upper);
-                       }
-                       return true;
+               tags = bond_verify_device_path(upper, end_dev, level + 1);
+               if (IS_ERR_OR_NULL(tags)) {
+                       if (IS_ERR(tags))
+                               return tags;
+                       continue;
                }
+               if (is_vlan_dev(upper)) {
+                       tags[level].vlan_proto = vlan_dev_vlan_proto(upper);
+                       tags[level].vlan_id = vlan_dev_vlan_id(upper);
+               }
+
+               return tags;
        }
 
-       return false;
+       return NULL;
 }
 
 static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
 {
        struct rtable *rt;
-       struct bond_vlan_tag tags[BOND_MAX_VLAN_ENCAP];
+       struct bond_vlan_tag *tags;
        __be32 *targets = bond->params.arp_targets, addr;
        int i;
-       bool ret;
 
        for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i]; i++) {
                netdev_dbg(bond->dev, "basa: target %pI4\n", &targets[i]);
-               memset(tags, 0, sizeof(tags));
+               tags = NULL;
 
                /* Find out through which dev should the packet go */
                rt = ip_route_output(dev_net(bond->dev), targets[i], 0,
@@ -2253,10 +2269,10 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
                        goto found;
 
                rcu_read_lock();
-               ret = bond_verify_device_path(bond->dev, rt->dst.dev, tags);
+               tags = bond_verify_device_path(bond->dev, rt->dst.dev, 0);
                rcu_read_unlock();
 
-               if (ret)
+               if (!IS_ERR_OR_NULL(tags))
                        goto found;
 
                /* Not our device - skip */
@@ -2271,6 +2287,8 @@ found:
                ip_rt_put(rt);
                bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
                              addr, tags);
+               if (!tags)
+                       kfree(tags);
        }
 }
 
index a85ca51eabf5236dcd6a999f42f3bc620a3d2de7..aace510d08d1dae8b300528ab703452bbe5c99e5 100644 (file)
@@ -36,7 +36,6 @@
 
 #define bond_version DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n"
 
-#define BOND_MAX_VLAN_ENCAP    2
 #define BOND_MAX_ARP_TARGETS   16
 
 #define BOND_DEFAULT_MIIMON    100
@@ -525,9 +524,9 @@ int bond_netlink_init(void);
 void bond_netlink_fini(void);
 struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond);
 const char *bond_slave_link_status(s8 link);
-bool bond_verify_device_path(struct net_device *start_dev,
-                            struct net_device *end_dev,
-                            struct bond_vlan_tag *tags);
+struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
+                                             struct net_device *end_dev,
+                                             int level);
 
 #ifdef CONFIG_PROC_FS
 void bond_create_proc_entry(struct bonding *bond);