Thread (8 messages) 8 messages, 1 author, 18h ago

[PATCH net v4 3/7] ipv4: icmp: don't quote decrypted payload in icmp errors

flat view
HOTtoday

From: Todd Freed <hidden>
Date: 2026-11-03 00:12:12
Also in: linux-kselftest
Subsystem: networking [general], networking [ipv4/ipv6], psp security protocol, the rest · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Daniel Zahka, Willem de Bruijn, Linus Torvalds

an icmp error quotes as much of the invoking packet as fits in 576
bytes. if the device had decrypted the packet (as indicated by
skb->decrypted) then the icmp error is sending part of it back out onto
the network in cleartext.

the two offloads encrypt different parts of the packet:

- ktls rx device offload decrypts the tls records in the tcp payload.
  the tcp header was in the clear on the wire, so quote the ip header
  and the tcp header, options included, and leave out the rfc 4884
  extension, which would carry the payload too.

- psp encryption also covers the tcp header. the quote could include just
  the ip header, but that's not really useful. linux receivers want to
  look at the l4 layer on the quoted packet, so such a truncated icmp
  error packet is just dropped (icmp_socket_deliver()). don't send the
  error at all for psp.

a decrypted psp skb has SKB_EXT_PSP, which is used to distinguish the
two cases. note though that if an inner tunnel is decapsulated,
skb_scrub_packet() drops the extension but keeps skb->decrypted, so an
error about the inner packet takes the ktls path in this case (quote
includes the l4 header), which is less than before.

on tx, skb->decrypted is also set. an error about a locally originated
skb (skb_iif is 0) is sent in full as before, if it goes to a local
address, as determined by the packet's source address.

Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261023234640.1459291-1-toddfreed%40meta.com
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Suggested-by: Björn Töpel <bjorn@kernel.org>
Fixes: 784abe24c903 ("net: Add decrypted field to skb")
Signed-off-by: Todd Freed <redacted>
---
 include/net/psp/functions.h | 11 ++++++++
 net/ipv4/icmp.c             | 54 +++++++++++++++++++++++++++++++++----
 2 files changed, 60 insertions(+), 5 deletions(-)
diff --git a/include/net/psp/functions.h b/include/net/psp/functions.h
index c5c23a54774e..b263b573ae4c 100644
--- a/include/net/psp/functions.h
+++ b/include/net/psp/functions.h
@@ -51,6 +51,12 @@ psp_enqueue_set_decrypted(struct sock *sk, struct sk_buff *skb)
 		skb->decrypted = 1;
 }
 
+/* The device decrypted @skb with psp, on receive */
+static inline bool psp_skb_is_decrypted(const struct sk_buff *skb)
+{
+	return skb_ext_find(skb, SKB_EXT_PSP);
+}
+
 static inline unsigned long
 __psp_skb_coalesce_diff(const struct sk_buff *one, const struct sk_buff *two,
 			unsigned long diffs)
@@ -170,6 +176,11 @@ static inline struct psp_assoc *psp_sk_assoc(const struct sock *sk)
 static inline void
 psp_enqueue_set_decrypted(struct sock *sk, struct sk_buff *skb) { }
 
+static inline bool psp_skb_is_decrypted(const struct sk_buff *skb)
+{
+	return false;
+}
+
 static inline unsigned long
 __psp_skb_coalesce_diff(const struct sk_buff *one, const struct sk_buff *two,
 			unsigned long diffs)
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 90c0e22c29be..d522488475b6 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -78,6 +78,7 @@
 #include <net/ip.h>
 #include <net/route.h>
 #include <net/protocol.h>
+#include <net/psp/functions.h>
 #include <net/icmp.h>
 #include <net/tcp.h>
 #include <net/udp.h>
@@ -823,7 +824,8 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
 	int room;
 	struct rtable *rt = skb_rtable(skb_in);
 	bool apply_ratelimit = false;
-	struct sk_buff *ext_skb;
+	struct sk_buff *ext_skb = NULL;
+	bool decrypted = false;
 	struct ipcm_cookie ipc;
 	struct flowi4 fl4;
 	__be32 saddr;
@@ -979,6 +981,32 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
 	if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
 		goto ende;
 
+	/* Don't send the decrypted part of an skb onto the network */
+	if (unlikely(skb_is_decrypted(skb_in))) {
+		if (!skb_in->skb_iif) {
+			/* A locally originated skb to be encrypted. Permit
+			 * the packet to be quoted, if its source address is
+			 * local. otherwise skip.
+			 */
+			if (!(rt->rt_flags & RTCF_LOCAL))
+				goto ende;
+		} else if (psp_skb_is_decrypted(skb_in)) {
+			/* PSP encrypts the transport header too, so it should
+			 * not be quoted. But an error whose quote is truncated
+			 * to fewer than 8 bytes of the transport header won't
+			 * be processed, so just skip it.
+			 */
+			goto ende;
+		} else {
+			/* TLS offload leaves the tcp header in the clear and
+			 * so can be quoted up to its end. A psp skb whose inner
+			 * tunnel was decapsulated also ends up here, since
+			 * skb_scrub_packet() drops SKB_EXT_PSP.
+			 */
+			decrypted = true;
+		}
+	}
+
 	/* peer icmp_ratelimit */
 	if (!icmpv4_xrlim_allow(net, rt, &fl4, type, code, apply_ratelimit))
 		goto ende;
@@ -996,10 +1024,26 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
 	if (room <= (int)sizeof(struct iphdr))
 		goto ende;
 
-	ext_skb = icmp_ext_append(net, skb_in, &icmp_param->data.icmph, room,
-				  parm->iif);
-	if (ext_skb)
-		icmp_param->skb = ext_skb;
+	if (unlikely(decrypted)) {
+		/* Quote up to the end of the tcp header. The RFC 4884
+		 * extension would carry the payload too.
+		 */
+		int len = iph->ihl * 4 + 8;
+		const struct tcphdr *th;
+		struct tcphdr _th;
+
+		th = skb_header_pointer(skb_in, skb_network_offset(skb_in) +
+					iph->ihl * 4, sizeof(_th), &_th);
+		if (th && iph->protocol == IPPROTO_TCP)
+			len = iph->ihl * 4 + max_t(int, th->doff * 4, 8);
+		room = min_t(int, room, len);
+	} else {
+		ext_skb = icmp_ext_append(net, skb_in,
+					  &icmp_param->data.icmph, room,
+					  parm->iif);
+		if (ext_skb)
+			icmp_param->skb = ext_skb;
+	}
 
 	icmp_param->data_len = icmp_param->skb->len - icmp_param->offset;
 	if (icmp_param->data_len > room)
-- 
2.53.0-Meta
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help