[PATCH net v3 1/2] ipv6: fix cached route reuse for control messages

From: Xia Yan

Date: Sun Nov 08 2026 - 20:46:55 EST


A connected IPv6 UDP or ping socket can reuse its cached route when an
SO_MARK cmsg requests another mark. A packet can then bypass a prohibit
rule instead of failing with EACCES.

ip6_sk_dst_lookup_flow() reads the cache even when connected is false.
UDP clears connected after parsing IP or socket control messages. Ping
always passes false.

Only read the cache when connected is true. Ordinary connected UDP
sends keep using the cache. Ping sends do a fresh lookup.

Found during code review of IPv6 UDP route caching. On UML, the same
connected-socket selftest fails four IPv6 cases before the fix and
passes all 54 with it.

Fixes: c6af0c227a22 ("ip: support SO_MARK cmsg")
Suggested-by: Willem de Bruijn <willemdebruijn.kernel@xxxxxxxxx>
Link: https://lore.kernel.org/netdev/willemdebruijn.kernel.1e0038bcc3ef8@xxxxxxxxx/
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Xia Yan <yanxia@xxxxxxxxxxxxxxxxxxxx>
---
v3:
- Read the cache only when connected is true, as Willem suggested.
- Drop the SO_MARK-specific cookie flag and UDP/ping branches.
- Restore the discovery method in the commit message.

v2: https://lore.kernel.org/netdev/20261104092240.15649-1-yanxia@xxxxxxxxxxxxxxxxxxxx/T/

Tested on net cb284914e749 with GCC 12.3.1:
- UML route checks: 50/50.
- x86_64 W=1: vmlinux, bzImage and modules built with allmodconfig-
and allyesconfig-derived configs. WERROR, KASAN and KCSAN disabled;
allyes also has KUNIT_ALL_TESTS disabled. No added diagnostics.

net/ipv6/ip6_output.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 25fe0ba..cb648c8 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1305,7 +1305,7 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow);
* @sk: socket which provides the dst cache and route info
* @fl6: flow to lookup
* @final_dst: final destination address for ipsec lookup
- * @connected: whether @sk is connected or not
+ * @connected: whether the flow can use and update the socket dst cache
*
* This function performs a route lookup on the given flow with the
* possibility of using the cached route in the socket if it is valid.
@@ -1322,11 +1322,14 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
const struct in6_addr *final_dst,
bool connected)
{
- struct dst_entry *dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie);
+ struct dst_entry *dst;

- dst = ip6_sk_dst_check(sk, dst, fl6);
- if (dst)
- return dst;
+ if (connected) {
+ dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie);
+ dst = ip6_sk_dst_check(sk, dst, fl6);
+ if (dst)
+ return dst;
+ }

dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_dst);
if (connected && !IS_ERR(dst))