diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-10-29 02:13:39 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-10-30 04:43:30 -0400 |
commit | 6a32e4f9dd9219261f8856f817e6655114cfec2f (patch) | |
tree | 964061ded76a2e5c9e2aa9122a3a88956db109c1 /net | |
parent | 14ef37b6d00eb5d06704e45989ba4c21e7be7673 (diff) |
vlan: allow nested vlan_do_receive()
commit 2425717b27eb (net: allow vlan traffic to be received under bond)
broke ARP processing on vlan on top of bonding.
+-------+
eth0 --| bond0 |---bond0.103
eth1 --| |
+-------+
52870.115435: skb_gro_reset_offset <-napi_gro_receive
52870.115435: dev_gro_receive <-napi_gro_receive
52870.115435: napi_skb_finish <-napi_gro_receive
52870.115435: netif_receive_skb <-napi_skb_finish
52870.115435: get_rps_cpu <-netif_receive_skb
52870.115435: __netif_receive_skb <-netif_receive_skb
52870.115436: vlan_do_receive <-__netif_receive_skb
52870.115436: bond_handle_frame <-__netif_receive_skb
52870.115436: vlan_do_receive <-__netif_receive_skb
52870.115436: arp_rcv <-__netif_receive_skb
52870.115436: kfree_skb <-arp_rcv
Packet is dropped in arp_rcv() because its pkt_type was set to
PACKET_OTHERHOST in the first vlan_do_receive() call, since no eth0.103
exists.
We really need to change pkt_type only if no more rx_handler is about to
be called for the packet.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Reviewed-by: Jiri Pirko <jpirko@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/8021q/vlan_core.c | 7 | ||||
-rw-r--r-- | net/core/dev.c | 4 |
2 files changed, 7 insertions, 4 deletions
diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index f1f2f7bb6661..163397f1fd5a 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c | |||
@@ -4,7 +4,7 @@ | |||
4 | #include <linux/netpoll.h> | 4 | #include <linux/netpoll.h> |
5 | #include "vlan.h" | 5 | #include "vlan.h" |
6 | 6 | ||
7 | bool vlan_do_receive(struct sk_buff **skbp) | 7 | bool vlan_do_receive(struct sk_buff **skbp, bool last_handler) |
8 | { | 8 | { |
9 | struct sk_buff *skb = *skbp; | 9 | struct sk_buff *skb = *skbp; |
10 | u16 vlan_id = skb->vlan_tci & VLAN_VID_MASK; | 10 | u16 vlan_id = skb->vlan_tci & VLAN_VID_MASK; |
@@ -13,7 +13,10 @@ bool vlan_do_receive(struct sk_buff **skbp) | |||
13 | 13 | ||
14 | vlan_dev = vlan_find_dev(skb->dev, vlan_id); | 14 | vlan_dev = vlan_find_dev(skb->dev, vlan_id); |
15 | if (!vlan_dev) { | 15 | if (!vlan_dev) { |
16 | if (vlan_id) | 16 | /* Only the last call to vlan_do_receive() should change |
17 | * pkt_type to PACKET_OTHERHOST | ||
18 | */ | ||
19 | if (vlan_id && last_handler) | ||
17 | skb->pkt_type = PACKET_OTHERHOST; | 20 | skb->pkt_type = PACKET_OTHERHOST; |
18 | return false; | 21 | return false; |
19 | } | 22 | } |
diff --git a/net/core/dev.c b/net/core/dev.c index edcf019c056d..6ba50a1e404c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -3283,18 +3283,18 @@ another_round: | |||
3283 | ncls: | 3283 | ncls: |
3284 | #endif | 3284 | #endif |
3285 | 3285 | ||
3286 | rx_handler = rcu_dereference(skb->dev->rx_handler); | ||
3286 | if (vlan_tx_tag_present(skb)) { | 3287 | if (vlan_tx_tag_present(skb)) { |
3287 | if (pt_prev) { | 3288 | if (pt_prev) { |
3288 | ret = deliver_skb(skb, pt_prev, orig_dev); | 3289 | ret = deliver_skb(skb, pt_prev, orig_dev); |
3289 | pt_prev = NULL; | 3290 | pt_prev = NULL; |
3290 | } | 3291 | } |
3291 | if (vlan_do_receive(&skb)) | 3292 | if (vlan_do_receive(&skb, !rx_handler)) |
3292 | goto another_round; | 3293 | goto another_round; |
3293 | else if (unlikely(!skb)) | 3294 | else if (unlikely(!skb)) |
3294 | goto out; | 3295 | goto out; |
3295 | } | 3296 | } |
3296 | 3297 | ||
3297 | rx_handler = rcu_dereference(skb->dev->rx_handler); | ||
3298 | if (rx_handler) { | 3298 | if (rx_handler) { |
3299 | if (pt_prev) { | 3299 | if (pt_prev) { |
3300 | ret = deliver_skb(skb, pt_prev, orig_dev); | 3300 | ret = deliver_skb(skb, pt_prev, orig_dev); |