Commit 6a32e4f9dd9219261f8856f817e6655114cfec2f
Committed by
David S. Miller
1 parent
14ef37b6d0
Exists in
master
and in
6 other branches
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>
Showing 3 changed files with 10 additions and 7 deletions Side-by-side Diff
include/linux/if_vlan.h
... | ... | @@ -106,7 +106,7 @@ |
106 | 106 | extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); |
107 | 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 | 110 | extern struct sk_buff *vlan_untag(struct sk_buff *skb); |
111 | 111 | |
112 | 112 | #else |
113 | 113 | |
... | ... | @@ -128,9 +128,9 @@ |
128 | 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 | 134 | (*skb)->pkt_type = PACKET_OTHERHOST; |
135 | 135 | return false; |
136 | 136 | } |
net/8021q/vlan_core.c
... | ... | @@ -4,7 +4,7 @@ |
4 | 4 | #include <linux/netpoll.h> |
5 | 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 | 9 | struct sk_buff *skb = *skbp; |
10 | 10 | u16 vlan_id = skb->vlan_tci & VLAN_VID_MASK; |
... | ... | @@ -13,7 +13,10 @@ |
13 | 13 | |
14 | 14 | vlan_dev = vlan_find_dev(skb->dev, vlan_id); |
15 | 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 | 20 | skb->pkt_type = PACKET_OTHERHOST; |
18 | 21 | return false; |
19 | 22 | } |
net/core/dev.c
... | ... | @@ -3283,18 +3283,18 @@ |
3283 | 3283 | ncls: |
3284 | 3284 | #endif |
3285 | 3285 | |
3286 | + rx_handler = rcu_dereference(skb->dev->rx_handler); | |
3286 | 3287 | if (vlan_tx_tag_present(skb)) { |
3287 | 3288 | if (pt_prev) { |
3288 | 3289 | ret = deliver_skb(skb, pt_prev, orig_dev); |
3289 | 3290 | pt_prev = NULL; |
3290 | 3291 | } |
3291 | - if (vlan_do_receive(&skb)) | |
3292 | + if (vlan_do_receive(&skb, !rx_handler)) | |
3292 | 3293 | goto another_round; |
3293 | 3294 | else if (unlikely(!skb)) |
3294 | 3295 | goto out; |
3295 | 3296 | } |
3296 | 3297 | |
3297 | - rx_handler = rcu_dereference(skb->dev->rx_handler); | |
3298 | 3298 | if (rx_handler) { |
3299 | 3299 | if (pt_prev) { |
3300 | 3300 | ret = deliver_skb(skb, pt_prev, orig_dev); |