Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'vrf-looping'

David Ahern says:

====================
net: Fix looping with vrf, xfrms and qdisc on VRF

Trev reported that use of VRFs with xfrms is looping when a qdisc
is added to the VRF device. The combination of xfrm + qdisc is not
handled by the VRF driver which lost track that it has already
seen the packet.

The XFRM_TRANSFORMED flag is used by the netfilter code for a similar
purpose, so re-use for VRF. Patch 1 drops the #ifdef around setting
the flag in the xfrm output functions. Patch 2 adds a check to
the VRF driver for flag; if set the packet has already passed through
the VRF driver once and does not need to recirculated a second time.

This is a day 1 bug with VRFs; stable wise, I would only take this
back to 4.14. I have a set of test cases which I will submit to
net-next.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+4 -6
+4 -2
drivers/net/vrf.c
··· 474 474 if (rt6_need_strict(&ipv6_hdr(skb)->daddr)) 475 475 return skb; 476 476 477 - if (qdisc_tx_is_default(vrf_dev)) 477 + if (qdisc_tx_is_default(vrf_dev) || 478 + IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) 478 479 return vrf_ip6_out_direct(vrf_dev, sk, skb); 479 480 480 481 return vrf_ip6_out_redirect(vrf_dev, skb); ··· 687 686 ipv4_is_lbcast(ip_hdr(skb)->daddr)) 688 687 return skb; 689 688 690 - if (qdisc_tx_is_default(vrf_dev)) 689 + if (qdisc_tx_is_default(vrf_dev) || 690 + IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) 691 691 return vrf_ip_out_direct(vrf_dev, sk, skb); 692 692 693 693 return vrf_ip_out_redirect(vrf_dev, skb);
-2
net/ipv4/xfrm4_output.c
··· 58 58 { 59 59 memset(IPCB(skb), 0, sizeof(*IPCB(skb))); 60 60 61 - #ifdef CONFIG_NETFILTER 62 61 IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; 63 - #endif 64 62 65 63 return xfrm_output(sk, skb); 66 64 }
-2
net/ipv6/xfrm6_output.c
··· 111 111 { 112 112 memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); 113 113 114 - #ifdef CONFIG_NETFILTER 115 114 IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED; 116 - #endif 117 115 118 116 return xfrm_output(sk, skb); 119 117 }