android_kernel_oneplus_msm8998/net/ipv6
Jakub Sitnicki 4d82f395bb ipv6: Skip XFRM lookup if dst_entry in socket cache is valid
[ Upstream commit 00bc0ef5880dc7b82f9c320dead4afaad48e47be ]

At present we perform an xfrm_lookup() for each UDPv6 message we
send. The lookup involves querying the flow cache (flow_cache_lookup)
and, in case of a cache miss, creating an XFRM bundle.

If we miss the flow cache, we can end up creating a new bundle and
deriving the path MTU (xfrm_init_pmtu) from on an already transformed
dst_entry, which we pass from the socket cache (sk->sk_dst_cache) down
to xfrm_lookup(). This can happen only if we're caching the dst_entry
in the socket, that is when we're using a connected UDP socket.

To put it another way, the path MTU shrinks each time we miss the flow
cache, which later on leads to incorrectly fragmented payload. It can
be observed with ESPv6 in transport mode:

  1) Set up a transformation and lower the MTU to trigger fragmentation
    # ip xfrm policy add dir out src ::1 dst ::1 \
      tmpl src ::1 dst ::1 proto esp spi 1
    # ip xfrm state add src ::1 dst ::1 \
      proto esp spi 1 enc 'aes' 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
    # ip link set dev lo mtu 1500

  2) Monitor the packet flow and set up an UDP sink
    # tcpdump -ni lo -ttt &
    # socat udp6-listen:12345,fork /dev/null &

  3) Send a datagram that needs fragmentation with a connected socket
    # perl -e 'print "@" x 1470 | socat - udp6:[::1]:12345
    2016/06/07 18:52:52 socat[724] E read(3, 0x555bb3d5ba00, 8192): Protocol error
    00:00:00.000000 IP6 ::1 > ::1: frag (0|1448) ESP(spi=0x00000001,seq=0x2), length 1448
    00:00:00.000014 IP6 ::1 > ::1: frag (1448|32)
    00:00:00.000050 IP6 ::1 > ::1: ESP(spi=0x00000001,seq=0x3), length 1272
    (^ ICMPv6 Parameter Problem)
    00:00:00.000022 IP6 ::1 > ::1: ESP(spi=0x00000001,seq=0x5), length 136

  4) Compare it to a non-connected socket
    # perl -e 'print "@" x 1500' | socat - udp6-sendto:[::1]:12345
    00:00:40.535488 IP6 ::1 > ::1: frag (0|1448) ESP(spi=0x00000001,seq=0x6), length 1448
    00:00:00.000010 IP6 ::1 > ::1: frag (1448|64)

What happens in step (3) is:

  1) when connecting the socket in __ip6_datagram_connect(), we
     perform an XFRM lookup, miss the flow cache, create an XFRM
     bundle, and cache the destination,

  2) afterwards, when sending the datagram, we perform an XFRM lookup,
     again, miss the flow cache (due to mismatch of flowi6_iif and
     flowi6_oif, which is an issue of its own), and recreate an XFRM
     bundle based on the cached (and already transformed) destination.

To prevent the recreation of an XFRM bundle, avoid an XFRM lookup
altogether whenever we already have a destination entry cached in the
socket. This prevents the path MTU shrinkage and brings us on par with
UDPv4.

The fix also benefits connected PINGv6 sockets, another user of
ip6_sk_dst_lookup_flow(), who also suffer messages being transformed
twice.

Joint work with Hannes Frederic Sowa.

Reported-by: Jan Tluka <jtluka@redhat.com>
Signed-off-by: Jakub Sitnicki <jkbs@redhat.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-06-24 10:18:17 -07:00
..
netfilter Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf 2015-12-14 11:09:01 -05:00
addrconf.c rtnl: RTM_GETNETCONF: fix wrong return value 2016-03-03 15:07:07 -08:00
addrconf_core.c ipv6: change ipv6_stub_impl.ipv6_dst_lookup to take net argument 2015-07-31 15:21:30 -07:00
addrlabel.c ipv6/addrlabel: fix ip6addrlbl_get() 2015-12-22 15:57:54 -05:00
af_inet6.c net: add validation for the socket syscall protocol argument 2015-12-14 16:09:30 -05:00
ah6.c ah6: fix error return code 2015-08-25 13:37:31 -07:00
anycast.c
datagram.c ipv6/udp: use sticky pktinfo egress ifindex on connect() 2016-03-03 15:07:05 -08:00
esp6.c
exthdrs.c ipv6: add complete rcu protection around np->opt 2015-12-02 23:37:16 -05:00
exthdrs_core.c ipv6: re-enable fragment header matching in ipv6_find_hdr 2016-04-20 15:41:59 +09:00
exthdrs_offload.c ipv6: fix exthdrs offload registration in out_rt path 2015-09-02 15:31:00 -07:00
fib6_rules.c ipv6: fix the incorrect return value of throw route 2015-10-23 02:38:18 -07:00
icmp.c ipv6: kill sk_dst_lock 2015-12-03 11:32:06 -05:00
ila.c dst: Pass net into dst->output 2015-10-08 04:27:03 -07:00
inet6_connection_sock.c ipv6: kill sk_dst_lock 2015-12-03 11:32:06 -05:00
inet6_hashtables.c net: SO_INCOMING_CPU setsockopt() support 2015-10-12 19:28:20 -07:00
ip6_checksum.c
ip6_fib.c Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net 2015-10-24 06:54:12 -07:00
ip6_flowlabel.c ipv6: fix a lockdep splat 2016-03-03 15:07:05 -08:00
ip6_gre.c tunnel: Clear IPCB(skb)->opt before dst_link_failure called 2016-04-20 15:41:56 +09:00
ip6_icmp.c
ip6_input.c netfilter: Pass net into okfn 2015-09-17 17:18:37 -07:00
ip6_offload.c ipv6: gro: support sit protocol 2015-10-21 19:36:11 -07:00
ip6_offload.h
ip6_output.c ipv6: Skip XFRM lookup if dst_entry in socket cache is valid 2016-06-24 10:18:17 -07:00
ip6_tunnel.c ip6_tunnel: set rtnl_link_ops before calling register_netdevice 2016-04-20 15:42:06 +09:00
ip6_udp_tunnel.c vxlan: do not receive IPv4 packets on IPv6 socket 2015-08-29 13:07:54 -07:00
ip6_vti.c net: Pass net into dst_output and remove dst_output_okfn 2015-10-08 04:26:54 -07:00
ip6mr.c net: ipmr, ip6mr: fix vif/tunnel failure race condition 2015-11-24 17:15:56 -05:00
ipcomp6.c
ipv6_sockglue.c ipv6: add complete rcu protection around np->opt 2015-12-02 23:37:16 -05:00
Kconfig net: Identifier Locator Addressing module 2015-08-17 21:33:06 -07:00
Makefile net: Identifier Locator Addressing module 2015-08-17 21:33:06 -07:00
mcast.c mld, igmp: Fix reserved tailroom calculation 2016-04-20 15:41:58 +09:00
mcast_snoop.c net: fix wrong skb_get() usage / crash in IGMP/MLD parsing code 2015-08-13 17:08:39 -07:00
mip6.c ipv6: use ktime_t for internal timestamps 2015-10-05 03:16:47 -07:00
ndisc.c ipv6: honor ifindex in case we receive ll addresses in router advertisements 2015-12-23 22:03:54 -05:00
netfilter.c ipv6: Pass struct net into ip6_route_me_harder 2015-09-29 20:21:32 +02:00
output_core.c ipv4, ipv6: Pass net into ip_local_out and ip6_local_out 2015-10-08 04:27:02 -07:00
ping.c
proc.c
protocol.c
raw.c ipv6: add complete rcu protection around np->opt 2015-12-02 23:37:16 -05:00
reassembly.c net: use skb_postpush_rcsum instead of own implementations 2016-05-18 17:06:36 -07:00
route.c net/route: enforce hoplimit max value 2016-05-18 17:06:43 -07:00
sit.c sit: fix sit0 percpu double allocations 2015-11-02 22:54:45 -05:00
syncookies.c ipv6: add complete rcu protection around np->opt 2015-12-02 23:37:16 -05:00
sysctl_net_ipv6.c ipv6: Implement different admin modes for automatic flow labels 2015-07-31 17:07:11 -07:00
tcp_ipv6.c tcp: record TLP and ER timer stats in v6 stats 2016-06-24 10:18:17 -07:00
tcpv6_offload.c
tunnel6.c ipv6: fix tunnel error handling 2015-11-03 10:52:13 -05:00
udp.c udp: prevent skbs lingering in tunnel socket queues 2016-06-24 10:18:16 -07:00
udp_impl.h
udp_offload.c
udplite.c
xfrm6_input.c netfilter: Pass struct net into the netfilter hooks 2015-09-17 17:18:37 -07:00
xfrm6_mode_beet.c
xfrm6_mode_ro.c
xfrm6_mode_transport.c
xfrm6_mode_tunnel.c ipv6: update skb->csum when CE mark is propagated 2016-01-31 11:29:01 -08:00
xfrm6_output.c Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net 2015-10-24 06:54:12 -07:00
xfrm6_policy.c Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec 2015-12-22 16:26:31 -05:00
xfrm6_protocol.c
xfrm6_state.c
xfrm6_tunnel.c