Commit 6a32e4f9dd9219261f8856f817e6655114cfec2f

Authored by Eric Dumazet
Committed by David S. Miller
1 parent 14ef37b6d0

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 }
... ... @@ -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);