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

net/ipv6: Fix linklocal to global address with VRF

Example setup:
host: ip -6 addr add dev eth1 2001:db8:104::4
where eth1 is enslaved to a VRF

switch: ip -6 ro add 2001:db8:104::4/128 dev br1
where br1 only has an LLA

ping6 2001:db8:104::4
ssh 2001:db8:104::4

(NOTE: UDP works fine if the PKTINFO has the address set to the global
address and ifindex is set to the index of eth1 with a destination an
LLA).

For ICMP, icmp6_iif needs to be updated to check if skb->dev is an
L3 master. If it is then return the ifindex from rt6i_idev similar
to what is done for loopback.

For TCP, restore the original tcp_v6_iif definition which is needed in
most places and add a new tcp_v6_iif_l3_slave that considers the
l3_slave variability. This latter check is only needed for socket
lookups.

Fixes: 9ff74384600a ("net: vrf: Handle ipv6 multicast and link-local addresses")
Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

David Ahern and committed by
David S. Miller
24b711ed f95de8aa

+12 -4
+5
include/net/tcp.h
··· 841 841 */ 842 842 static inline int tcp_v6_iif(const struct sk_buff *skb) 843 843 { 844 + return TCP_SKB_CB(skb)->header.h6.iif; 845 + } 846 + 847 + static inline int tcp_v6_iif_l3_slave(const struct sk_buff *skb) 848 + { 844 849 bool l3_slave = ipv6_l3mdev_skb(TCP_SKB_CB(skb)->header.h6.flags); 845 850 846 851 return l3_slave ? skb->skb_iif : TCP_SKB_CB(skb)->header.h6.iif;
+3 -2
net/ipv6/icmp.c
··· 402 402 403 403 /* for local traffic to local address, skb dev is the loopback 404 404 * device. Check if there is a dst attached to the skb and if so 405 - * get the real device index. 405 + * get the real device index. Same is needed for replies to a link 406 + * local address on a device enslaved to an L3 master device 406 407 */ 407 - if (unlikely(iif == LOOPBACK_IFINDEX)) { 408 + if (unlikely(iif == LOOPBACK_IFINDEX || netif_is_l3_master(skb->dev))) { 408 409 const struct rt6_info *rt6 = skb_rt6_info(skb); 409 410 410 411 if (rt6)
+4 -2
net/ipv6/tcp_ipv6.c
··· 938 938 &tcp_hashinfo, NULL, 0, 939 939 &ipv6h->saddr, 940 940 th->source, &ipv6h->daddr, 941 - ntohs(th->source), tcp_v6_iif(skb), 941 + ntohs(th->source), 942 + tcp_v6_iif_l3_slave(skb), 942 943 tcp_v6_sdif(skb)); 943 944 if (!sk1) 944 945 goto out; ··· 1610 1609 skb, __tcp_hdrlen(th), 1611 1610 &ipv6_hdr(skb)->saddr, th->source, 1612 1611 &ipv6_hdr(skb)->daddr, 1613 - ntohs(th->dest), tcp_v6_iif(skb), 1612 + ntohs(th->dest), 1613 + tcp_v6_iif_l3_slave(skb), 1614 1614 sdif); 1615 1615 if (sk2) { 1616 1616 struct inet_timewait_sock *tw = inet_twsk(sk);