BPF List
 help / color / mirror / Atom feed
From: Ido Schimmel <idosch@nvidia.com>
To: "Íñigo Huguet" <ihuguet@riseup.net>
Cc: David Ahern <dsahern@kernel.org>,
	Andrew Lunn <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Neal Cardwell <ncardwell@google.com>,
	Pablo Neira Ayuso <pablo@netfilter.org>,
	Florian Westphal <fw@strlen.de>,
	Willem de Bruijn <willemdebruijn.kernel@gmail.com>,
	dcaratti@redhat.com, ihuguet@redhat.com,
	Simon Horman <horms@kernel.org>,
	Kuniyuki Iwashima <kuniyu@google.com>, Phil Sutter <phil@nwl.cc>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Junseo Lim <zirajs7@gmail.com>,
	Martin KaFai Lau <martin.lau@kernel.org>,
	Xuanqiang Luo <luoxuanqiang@kylinos.cn>,
	Fernando Fernandez Mancera <fmancera@suse.de>,
	Leon Hwang <leon.hwang@linux.dev>,
	Willem de Bruijn <willemb@google.com>,
	Kees Cook <kees@kernel.org>, Jeff Layton <jlayton@kernel.org>,
	Christian Brauner <brauner@kernel.org>,
	Qi Tang <tpluszz77@gmail.com>, Joe Damato <joe@dama.to>,
	Breno Leitao <leitao@debian.org>,
	Li RongQing <lirongqing@baidu.com>,
	"open list:VRF" <netdev@vger.kernel.org>,
	open list <linux-kernel@vger.kernel.org>,
	"open list:NETFILTER" <netfilter-devel@vger.kernel.org>,
	"open list:NETFILTER" <coreteam@netfilter.org>,
	"open list:BPF [MISC]:Keyword:(?:b|_)bpf(?:b|_)"
	<bpf@vger.kernel.org>
Subject: Re: [PATCH net v2] net/ipv6: don't route packets with unknown source address
Date: Thu, 3 Sep 2026 17:56:31 +0300	[thread overview]
Message-ID: <20260903145631.GA182962@shredder> (raw)
In-Reply-To: <20260903121057.80006-1-ihuguet@riseup.net>

On Thu, Sep 03, 2026 at 02:10:40PM +0200, Íñigo Huguet wrote:
> Don't allow routing packets with a source address that is not configured
> in the host. Allow it only in certain cases like when using a
> transparent socket, by setting the ANYSRC flag in flowi_flags.
> 
> Until now, it was possible to send such a packet if a route can be found
> in the routing table for it. For example:
> 1. Configure an address 1:2::3:4/64 and a static route 1:2::/64
> 2. Establish a TCP connection to 1:2::3:4
> 3. Remove the address from the interface, but keep the route.
> 4. Packets are still sent out by the TCP connection because of
>    the static route. No incoming packets are accepted, though.
> 
> This patch prevents the outgoing packets to be sent in normal
> circumnstances.
> 
> This aligns the behaviour with the IPv4 stack. To determine the places
> where the ANYSRC needs to be set, I set the flag in the same places as
> the IPv4 stack does.
> 
> Apart from consolidating the behaviour of both stacks, there is a more
> important reason why this is needed. RFC 4862 states that "an invalid
> address MUST NOT be used as the source address of outbound packets".
> Therefore, sending packets with a source address considered "invalid",
> like an expired address, is disallowed.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Signed-off-by: Íñigo Huguet <ihuguet@riseup.net>
> 
> ---
> 
> v2:
> - Fix a slab-out-of-bounds bug: in
>   tcp_v6_send_response we must not read the inet_flags because it may
>   not be an inet_sk, but a request socket.
>   Detected by syzbot, Sashiko and other bots.
> - Use the addr_type from saddr instead of daddr in ip6_route_me_harder.
>   Detected by Sashiko.
> - Don't overwrite flowi_flags in tcp_v6_connect when setting the
>   ANY_SPORT flag. Detected by Sashiko.
> - Fixed line length warnings.
> v1: https://lore.kernel.org/netdev/20260901115021.50057-1-ihuguet@riseup.net/
> 
> Testing: tested with a manual reproducer executing the steps described
> above. Tested also with transparent sockets to ensure that the packets
> are sent in that case. Also executed the following selftests to prevent
> potential regressions: fcnal-ipv6, fib_tests, fib-onlink-tests,
> nft_nat, nft_tproxy_tcp, nft_tproxy_udp.
> 
> The change in the netfilter's ip6_route_me_harder function is the one
> that I'm more unsure about. It was not clear to me the reason why it was
> done like this in the IPv4 counterpart. Please review carefully.
> ---
>  drivers/net/vrf.c                |  1 +
>  net/core/lwt_bpf.c               |  1 +
>  net/ipv6/af_inet6.c              |  1 +
>  net/ipv6/datagram.c              |  1 +
>  net/ipv6/inet6_connection_sock.c |  2 ++
>  net/ipv6/ip6_output.c            | 28 ++++++++++++++++++++++++++++
>  net/ipv6/netfilter.c             | 11 +++++++++--
>  net/ipv6/ping.c                  |  1 +
>  net/ipv6/raw.c                   |  1 +
>  net/ipv6/syncookies.c            |  1 +
>  net/ipv6/tcp_ipv6.c              |  4 +++-
>  net/ipv6/udp.c                   |  1 +
>  net/l2tp/l2tp_ip6.c              |  2 ++
>  13 files changed, 52 insertions(+), 3 deletions(-)

1. This is a behavior change, not a bug fix, and should be targeted at
net-next without a Fixes tag.

2. What is the motivation for this drastic change beyond RFC conformance
and parity with IPv4? IMO, these two are not a good enough reason to
make such a change with a huge blast radius.

Here's a recent example of a one line change that argued for IPv4 parity
and was eventually reverted due to regression reports:

https://lore.kernel.org/all/20260104032357.38555-1-yuhuang@redhat.com/
https://lore.kernel.org/netdev/20260521135310.GC977@cmadams.net/

3. See [1] for a list of regressions that AI flagged. Even if v3 fixes
all of them (which means a much bigger diff), I don't think such a
change will be merged without a proper real-world motivation beyond RFC
conformance and IPv4 parity.

Thanks

[1]
1. Any-IP (local prefix routes) stops working

   Setup: ip -6 route add local 2001:db8::/64 dev lo (or the rule + table
   form from commit ab79ad14a2d5), TCP listener on [::].

   Path: tcp_v6_send_synack() -> inet6_csk_route_req() sets
   fl6->saddr = ireq->ir_v6_loc_addr with flags 0 -> ip6_dst_lookup_tail()
   -> ipv6_chk_addr_and_flags() misses because the address is only in the
   FIB, not in inet6_addr_lst -> -ENETUNREACH.

   Same for inet6_csk_route_socket() on the accepted socket,
   tcp_v6_send_response() for RSTs, and icmpv6_echo_reply(), which keeps
   the incoming daddr as saddr when ipv6_unicast_destination() (RTF_LOCAL)
   is true.

   Effect: no SYN-ACK, no echo reply, no RST for any Any-IP address.
   IPv4 avoids this via the local-table fallback in __ip_dev_find().

2. Anycast source addresses rejected

   Anycast addresses live in idev->ac_list, not inet6_addr_lst. Three
   paths pick one deliberately:

   - icmp6_send() uses ipv6_chk_acast_addr_src() to source ICMPv6 errors
     from the anycast daddr of the offending packet.
   - icmpv6_echo_reply() with anycast_src_echo_reply=1.
   - ip6_datagram_send_ctl() accepts an anycast IPV6_PKTINFO source
     (commit 7c90cc2d40ca), then udpv6_sendmsg() ->
     ip6_sk_dst_lookup_flow() fails it.

   Effect: ICMPv6 errors and echo replies for anycast destinations are
   dropped with OUTNOROUTES incremented. UDP sendmsg() with an anycast
   pktinfo passes the ancillary-data check and then fails with
   -ENETUNREACH.

3. TIME_WAIT replies of IP_TRANSPARENT connections dropped

   tcp_v6_send_response() uses "sk && sk_fullsock(sk)" to decide the
   flags. tcp_v6_rcv() reaches it with a timewait socket for both
   TCP_TW_ACK (tcp_v6_timewait_ack() -> tcp_v6_send_ack()) and TCP_TW_RST
   (tcp_v6_send_reset()). sk_fullsock() is false there, flags are 0, and
   fl6.saddr is the proxied non-local address.

   Effect: tproxy'd IPv6 connections send no ACK or RST from TIME_WAIT.
   IPv4 uses inet_sk_transparent(), which reads tw->tw_transparent and
   ireq->no_srccheck.

4. BPF-set non-local IPv6 tunnel sources stop working

   bpf_skb_set_tunnel_key() sets key.flow_flags = FLOWI_FLAG_ANYSRC only
   in the IPv4 branch (commit b8fff748521c, added so a program can use
   e.g. a container address as the outer source). udp_tunnel6_dst_lookup()
   (vxlan, geneve, bareudp) copies key->u.ipv6.src into fl6.saddr and
   never copies key->flow_flags. Before the patch this did not matter
   because IPv6 never checked the source.

   Effect: IPv6 collect_md tunnels with a BPF-chosen non-local source get
   -ENETUNREACH while the IPv4 equivalent keeps working.

5. ICMPv6 errors under IPsec lost in the relookup path

   icmpv6_route_lookup() does a second ip6_dst_lookup() with fl2 from
   xfrm_decode_session_reverse() when the first xfrm_lookup() returned
   -EPERM. fl2.saddr is the daddr of the packet in error, which is a
   remote host when the packet was being forwarded through a gateway. The
   new check fails it, relookup_failed has dst == NULL in the -EPERM case,
   and the function returns ERR_PTR(-ENETUNREACH).

   Effect: RFC 4301 ICMP handling on IPsec gateways with a block policy no
   longer sends the error into the tunnel. icmp_route_lookup() sets
   fl4_2.flowi4_flags |= FLOWI_FLAG_ANYSRC for exactly this relookup.

  reply	other threads:[~2026-09-03 14:56 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-03 12:10 [PATCH net v2] net/ipv6: don't route packets with unknown source address Íñigo Huguet
2026-09-03 14:56 ` Ido Schimmel [this message]
2026-09-03 22:38 ` Jakub Kicinski
2026-09-04 10:21   ` Matthieu Baerts
2026-09-04 12:11 ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260903145631.GA182962@shredder \
    --to=idosch@nvidia.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=bpf@vger.kernel.org \
    --cc=brauner@kernel.org \
    --cc=coreteam@netfilter.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=dcaratti@redhat.com \
    --cc=dsahern@kernel.org \
    --cc=edumazet@google.com \
    --cc=fmancera@suse.de \
    --cc=fw@strlen.de \
    --cc=horms@kernel.org \
    --cc=ihuguet@redhat.com \
    --cc=ihuguet@riseup.net \
    --cc=jlayton@kernel.org \
    --cc=joe@dama.to \
    --cc=kees@kernel.org \
    --cc=kuba@kernel.org \
    --cc=kuniyu@google.com \
    --cc=leitao@debian.org \
    --cc=leon.hwang@linux.dev \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lirongqing@baidu.com \
    --cc=luoxuanqiang@kylinos.cn \
    --cc=martin.lau@kernel.org \
    --cc=ncardwell@google.com \
    --cc=netdev@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=pablo@netfilter.org \
    --cc=phil@nwl.cc \
    --cc=tpluszz77@gmail.com \
    --cc=willemb@google.com \
    --cc=willemdebruijn.kernel@gmail.com \
    --cc=zirajs7@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox