[PATCH net v5 2/8] psp: don't encapsulate ipv6 packets with extension headers
From: Todd Freed
Date: Tue Nov 10 2026 - 19:20:52 EST
psp_dev_encapsulate() inserts the udp+psp headers at the transport
header, rewrites the protocol of the fixed ip header to udp, and writes
tcp as the psp next header. so tcp must directly follow the fixed
header.
if the packet is an ipv6 packet with extension headers, udp+psp is
placed after them, but the next header indicating the l4 protocol is on
the last extension header in the chain, not in the fixed header.
therefore this produces a malformed packet.
in addition to being malformed, with mlx5 this kind of packet goes out
unencrypted. since the tx association encodes intent to send encrypted
packets, drop such packets instead.
a psp tcp socket emits such packets if certain socket options are set on
it (e.g. IPV6_DSTOPTS). refusing the combination of such socket options
with psp association would be helpful but is not implemented here. since
extension headers can also come from a route the datapath check is
needed anyway.
the check also covers ipv4: the l4 protocol must be tcp, and the
transport header must follow the ip header and its options. packets of
a psp tcp socket always pass it.
Fixes: fc724515741a ("psp: provide encapsulation helper for drivers")
Signed-off-by: Todd Freed <toddfreed@xxxxxxxx>
---
net/psp/psp_main.c | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/net/psp/psp_main.c b/net/psp/psp_main.c
index 273b010d2355..c071f507261b 100644
--- a/net/psp/psp_main.c
+++ b/net/psp/psp_main.c
@@ -245,6 +245,24 @@ static void psp_write_headers(struct net *net, struct sk_buff *skb, __be32 spi,
memset(&psph->iv, 0, sizeof(psph->iv));
}
+/* UDP+PSP goes in at the transport header, the protocol of the fixed IP
+ * header becomes UDP, and TCP the PSP next header: TCP must directly follow
+ * the fixed header. It doesn't with IPv6 extension headers, whose last one
+ * holds the L4 protocol.
+ */
+static bool psp_dev_encap_ok(const struct sk_buff *skb)
+{
+ u32 network_len = skb_network_header_len(skb);
+
+ if (skb->protocol == htons(ETH_P_IP))
+ return ip_hdr(skb)->protocol == IPPROTO_TCP &&
+ network_len == ip_hdrlen(skb);
+ if (skb->protocol == htons(ETH_P_IPV6))
+ return ipv6_hdr(skb)->nexthdr == IPPROTO_TCP &&
+ network_len == sizeof(struct ipv6hdr);
+ return false;
+}
+
/* Encapsulate a TCP packet with PSP by adding the UDP+PSP headers and filling
* them in.
*/
@@ -255,8 +273,7 @@ bool psp_dev_encapsulate(struct net *net, struct sk_buff *skb, __be32 spi,
u32 ethr_len = skb_mac_header_len(skb);
u32 bufflen = ethr_len + network_len;
- if (skb->protocol != htons(ETH_P_IP) &&
- skb->protocol != htons(ETH_P_IPV6))
+ if (!psp_dev_encap_ok(skb))
return false;
if (skb_cow_head(skb, PSP_ENCAP_HLEN))
--
2.53.0-Meta