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 | |
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>
-rw-r--r-- | include/linux/if_vlan.h | 6 | ||||
-rw-r--r-- | net/8021q/vlan_core.c | 7 | ||||
-rw-r--r-- | net/core/dev.c | 4 |
3 files changed, 10 insertions, 7 deletions
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 44da4822bcab..12d5543b14f2 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h | |||
@@ -106,7 +106,7 @@ extern struct net_device *__vlan_find_dev_deep(struct net_device *real_dev, | |||
106 | extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); | 106 | extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); |
107 | extern u16 vlan_dev_vlan_id(const struct net_device *dev); | 107 | extern u16 vlan_dev_vlan_id(const struct net_device *dev); |
108 | 108 | ||
109 | extern bool vlan_do_receive(struct sk_buff **skb); | 109 | extern bool vlan_do_receive(struct sk_buff **skb, bool last_handler); |
110 | extern struct sk_buff *vlan_untag(struct sk_buff *skb); | 110 | extern struct sk_buff *vlan_untag(struct sk_buff *skb); |
111 | 111 | ||
112 | #else | 112 | #else |
@@ -128,9 +128,9 @@ static inline u16 vlan_dev_vlan_id(const struct net_device *dev) | |||
128 | return 0; | 128 | return 0; |
129 | } | 129 | } |
130 | 130 | ||
131 | static inline bool vlan_do_receive(struct sk_buff **skb) | 131 | static inline bool vlan_do_receive(struct sk_buff **skb, bool last_handler) |
132 | { | 132 | { |
133 | if ((*skb)->vlan_tci & VLAN_VID_MASK) | 133 | if (((*skb)->vlan_tci & VLAN_VID_MASK) && last_handler) |
134 | (*skb)->pkt_type = PACKET_OTHERHOST; | 134 | (*skb)->pkt_type = PACKET_OTHERHOST; |
135 | return false; | 135 | return false; |
136 | } | 136 | } |
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); |