Commit 3e403a77 authored by Veaceslav Falico's avatar Veaceslav Falico Committed by David S. Miller

bonding: make it possible to have unlimited nested upper vlans

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: default avatarVeaceslav Falico <vfalico@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 224e923c
...@@ -1042,7 +1042,7 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[], ...@@ -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 bonding *bond = bond_get_bond_by_slave(slave);
struct net_device *upper; struct net_device *upper;
struct list_head *iter; struct list_head *iter;
struct bond_vlan_tag tags[BOND_MAX_VLAN_ENCAP]; struct bond_vlan_tag *tags;
/* send untagged */ /* send untagged */
alb_send_lp_vid(slave, mac_addr, 0, 0); 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[], ...@@ -1070,10 +1070,12 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],
* when strict_match is turned off. * when strict_match is turned off.
*/ */
if (netif_is_macvlan(upper) && !strict_match) { if (netif_is_macvlan(upper) && !strict_match) {
memset(tags, 0, sizeof(tags)); tags = bond_verify_device_path(bond->dev, upper, 0);
bond_verify_device_path(bond->dev, upper, tags); if (IS_ERR_OR_NULL(tags))
BUG();
alb_send_lp_vid(slave, upper->dev_addr, alb_send_lp_vid(slave, upper->dev_addr,
tags[0].vlan_proto, tags[0].vlan_id); tags[0].vlan_proto, tags[0].vlan_id);
kfree(tags);
} }
} }
rcu_read_unlock(); rcu_read_unlock();
......
...@@ -2145,7 +2145,7 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, ...@@ -2145,7 +2145,7 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op,
struct bond_vlan_tag *tags) struct bond_vlan_tag *tags)
{ {
struct sk_buff *skb; 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", netdev_dbg(slave_dev, "arp %d on slave %s: dst %pI4 src %pI4\n",
arp_op, slave_dev->name, &dest_ip, &src_ip); 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, ...@@ -2158,30 +2158,42 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op,
return; return;
} }
if (!tags || tags->vlan_proto == VLAN_N_VID)
goto xmit;
tags++;
/* Go through all the tags backwards and add them to the packet */ /* Go through all the tags backwards and add them to the packet */
for (i = BOND_MAX_VLAN_ENCAP - 1; i > 0; i--) { while (tags->vlan_proto != VLAN_N_VID) {
if (!tags[i].vlan_id) if (!tags->vlan_id) {
tags++;
continue; continue;
}
netdev_dbg(slave_dev, "inner tag: proto %X vid %X\n", netdev_dbg(slave_dev, "inner tag: proto %X vid %X\n",
ntohs(tags[i].vlan_proto), tags[i].vlan_id); ntohs(outer_tag->vlan_proto), tags->vlan_id);
skb = __vlan_put_tag(skb, tags[i].vlan_proto, skb = __vlan_put_tag(skb, tags->vlan_proto,
tags[i].vlan_id); tags->vlan_id);
if (!skb) { if (!skb) {
net_err_ratelimited("failed to insert inner VLAN tag\n"); net_err_ratelimited("failed to insert inner VLAN tag\n");
return; return;
} }
tags++;
} }
/* Set the outer tag */ /* 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", netdev_dbg(slave_dev, "outer tag: proto %X vid %X\n",
ntohs(tags[0].vlan_proto), tags[0].vlan_id); ntohs(outer_tag->vlan_proto), outer_tag->vlan_id);
skb = vlan_put_tag(skb, tags[0].vlan_proto, tags[0].vlan_id); skb = vlan_put_tag(skb, outer_tag->vlan_proto,
outer_tag->vlan_id);
if (!skb) { if (!skb) {
net_err_ratelimited("failed to insert outer VLAN tag\n"); net_err_ratelimited("failed to insert outer VLAN tag\n");
return; return;
} }
} }
xmit:
arp_xmit(skb); arp_xmit(skb);
} }
...@@ -2191,46 +2203,50 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, ...@@ -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 * When the path is validated, collect any vlan information in the
* path. * path.
*/ */
bool bond_verify_device_path(struct net_device *start_dev, struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
struct net_device *end_dev, struct net_device *end_dev,
struct bond_vlan_tag *tags) int level)
{ {
struct bond_vlan_tag *tags;
struct net_device *upper; struct net_device *upper;
struct list_head *iter; struct list_head *iter;
int idx;
if (start_dev == end_dev) if (start_dev == end_dev) {
return true; 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) { netdev_for_each_upper_dev_rcu(start_dev, upper, iter) {
if (bond_verify_device_path(upper, end_dev, tags)) { tags = bond_verify_device_path(upper, end_dev, level + 1);
if (is_vlan_dev(upper)) { if (IS_ERR_OR_NULL(tags)) {
idx = vlan_get_encap_level(upper); if (IS_ERR(tags))
if (idx >= BOND_MAX_VLAN_ENCAP) return tags;
return false; continue;
tags[idx].vlan_proto =
vlan_dev_vlan_proto(upper);
tags[idx].vlan_id = vlan_dev_vlan_id(upper);
}
return true;
} }
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) static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
{ {
struct rtable *rt; struct rtable *rt;
struct bond_vlan_tag tags[BOND_MAX_VLAN_ENCAP]; struct bond_vlan_tag *tags;
__be32 *targets = bond->params.arp_targets, addr; __be32 *targets = bond->params.arp_targets, addr;
int i; int i;
bool ret;
for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i]; i++) { for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i]; i++) {
netdev_dbg(bond->dev, "basa: target %pI4\n", &targets[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 */ /* Find out through which dev should the packet go */
rt = ip_route_output(dev_net(bond->dev), targets[i], 0, 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) ...@@ -2253,10 +2269,10 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
goto found; goto found;
rcu_read_lock(); 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(); rcu_read_unlock();
if (ret) if (!IS_ERR_OR_NULL(tags))
goto found; goto found;
/* Not our device - skip */ /* Not our device - skip */
...@@ -2271,6 +2287,8 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) ...@@ -2271,6 +2287,8 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
ip_rt_put(rt); ip_rt_put(rt);
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
addr, tags); addr, tags);
if (!tags)
kfree(tags);
} }
} }
......
...@@ -36,7 +36,6 @@ ...@@ -36,7 +36,6 @@
#define bond_version DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n" #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_MAX_ARP_TARGETS 16
#define BOND_DEFAULT_MIIMON 100 #define BOND_DEFAULT_MIIMON 100
...@@ -525,9 +524,9 @@ int bond_netlink_init(void); ...@@ -525,9 +524,9 @@ int bond_netlink_init(void);
void bond_netlink_fini(void); void bond_netlink_fini(void);
struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond); struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond);
const char *bond_slave_link_status(s8 link); const char *bond_slave_link_status(s8 link);
bool bond_verify_device_path(struct net_device *start_dev, struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
struct net_device *end_dev, struct net_device *end_dev,
struct bond_vlan_tag *tags); int level);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
void bond_create_proc_entry(struct bonding *bond); void bond_create_proc_entry(struct bonding *bond);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment