]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.15-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 23 Jun 2025 08:47:27 +0000 (10:47 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 23 Jun 2025 08:47:27 +0000 (10:47 +0200)
added patches:
bpf-fix-l4-csum-update-on-ipv6-in-checksum_complete.patch

queue-5.15/bpf-fix-l4-csum-update-on-ipv6-in-checksum_complete.patch [new file with mode: 0644]
queue-5.15/series

diff --git a/queue-5.15/bpf-fix-l4-csum-update-on-ipv6-in-checksum_complete.patch b/queue-5.15/bpf-fix-l4-csum-update-on-ipv6-in-checksum_complete.patch
new file mode 100644 (file)
index 0000000..b965b30
--- /dev/null
@@ -0,0 +1,139 @@
+From ead7f9b8de65632ef8060b84b0c55049a33cfea1 Mon Sep 17 00:00:00 2001
+From: Paul Chaignon <paul.chaignon@gmail.com>
+Date: Thu, 29 May 2025 12:28:35 +0200
+Subject: bpf: Fix L4 csum update on IPv6 in CHECKSUM_COMPLETE
+
+From: Paul Chaignon <paul.chaignon@gmail.com>
+
+commit ead7f9b8de65632ef8060b84b0c55049a33cfea1 upstream.
+
+In Cilium, we use bpf_csum_diff + bpf_l4_csum_replace to, among other
+things, update the L4 checksum after reverse SNATing IPv6 packets. That
+use case is however not currently supported and leads to invalid
+skb->csum values in some cases. This patch adds support for IPv6 address
+changes in bpf_l4_csum_update via a new flag.
+
+When calling bpf_l4_csum_replace in Cilium, it ends up calling
+inet_proto_csum_replace_by_diff:
+
+    1:  void inet_proto_csum_replace_by_diff(__sum16 *sum, struct sk_buff *skb,
+    2:                                       __wsum diff, bool pseudohdr)
+    3:  {
+    4:      if (skb->ip_summed != CHECKSUM_PARTIAL) {
+    5:          csum_replace_by_diff(sum, diff);
+    6:          if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr)
+    7:              skb->csum = ~csum_sub(diff, skb->csum);
+    8:      } else if (pseudohdr) {
+    9:          *sum = ~csum_fold(csum_add(diff, csum_unfold(*sum)));
+    10:     }
+    11: }
+
+The bug happens when we're in the CHECKSUM_COMPLETE state. We've just
+updated one of the IPv6 addresses. The helper now updates the L4 header
+checksum on line 5. Next, it updates skb->csum on line 7. It shouldn't.
+
+For an IPv6 packet, the updates of the IPv6 address and of the L4
+checksum will cancel each other. The checksums are set such that
+computing a checksum over the packet including its checksum will result
+in a sum of 0. So the same is true here when we update the L4 checksum
+on line 5. We'll update it as to cancel the previous IPv6 address
+update. Hence skb->csum should remain untouched in this case.
+
+The same bug doesn't affect IPv4 packets because, in that case, three
+fields are updated: the IPv4 address, the IP checksum, and the L4
+checksum. The change to the IPv4 address and one of the checksums still
+cancel each other in skb->csum, but we're left with one checksum update
+and should therefore update skb->csum accordingly. That's exactly what
+inet_proto_csum_replace_by_diff does.
+
+This special case for IPv6 L4 checksums is also described atop
+inet_proto_csum_replace16, the function we should be using in this case.
+
+This patch introduces a new bpf_l4_csum_replace flag, BPF_F_IPV6,
+to indicate that we're updating the L4 checksum of an IPv6 packet. When
+the flag is set, inet_proto_csum_replace_by_diff will skip the
+skb->csum update.
+
+Fixes: 7d672345ed295 ("bpf: add generic bpf_csum_diff helper")
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Link: https://patch.msgid.link/96a6bc3a443e6f0b21ff7b7834000e17fb549e05.1748509484.git.paul.chaignon@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[ Note: Fixed conflict due to unrelated comment change. ]
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/uapi/linux/bpf.h       | 2 ++
+ net/core/filter.c              | 5 +++--
+ tools/include/uapi/linux/bpf.h | 2 ++
+ 3 files changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
+index 0bdeeabbc5a8..2ac62d5ed466 100644
+--- a/include/uapi/linux/bpf.h
++++ b/include/uapi/linux/bpf.h
+@@ -1695,6 +1695,7 @@ union bpf_attr {
+  *            for updates resulting in a null checksum the value is set to
+  *            **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates
+  *            the checksum is to be computed against a pseudo-header.
++ *            Flag **BPF_F_IPV6** should be set for IPv6 packets.
+  *
+  *            This helper works in combination with **bpf_csum_diff**\ (),
+  *            which does not update the checksum in-place, but offers more
+@@ -5106,6 +5107,7 @@ enum {
+       BPF_F_PSEUDO_HDR                = (1ULL << 4),
+       BPF_F_MARK_MANGLED_0            = (1ULL << 5),
+       BPF_F_MARK_ENFORCE              = (1ULL << 6),
++      BPF_F_IPV6                      = (1ULL << 7),
+ };
+ /* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */
+diff --git a/net/core/filter.c b/net/core/filter.c
+index 65b7fb9c3d29..169d9ba4e7a0 100644
+--- a/net/core/filter.c
++++ b/net/core/filter.c
+@@ -1951,10 +1951,11 @@ BPF_CALL_5(bpf_l4_csum_replace, struct sk_buff *, skb, u32, offset,
+       bool is_pseudo = flags & BPF_F_PSEUDO_HDR;
+       bool is_mmzero = flags & BPF_F_MARK_MANGLED_0;
+       bool do_mforce = flags & BPF_F_MARK_ENFORCE;
++      bool is_ipv6   = flags & BPF_F_IPV6;
+       __sum16 *ptr;
+       if (unlikely(flags & ~(BPF_F_MARK_MANGLED_0 | BPF_F_MARK_ENFORCE |
+-                             BPF_F_PSEUDO_HDR | BPF_F_HDR_FIELD_MASK)))
++                             BPF_F_PSEUDO_HDR | BPF_F_HDR_FIELD_MASK | BPF_F_IPV6)))
+               return -EINVAL;
+       if (unlikely(offset > 0xffff || offset & 1))
+               return -EFAULT;
+@@ -1970,7 +1971,7 @@ BPF_CALL_5(bpf_l4_csum_replace, struct sk_buff *, skb, u32, offset,
+               if (unlikely(from != 0))
+                       return -EINVAL;
+-              inet_proto_csum_replace_by_diff(ptr, skb, to, is_pseudo, false);
++              inet_proto_csum_replace_by_diff(ptr, skb, to, is_pseudo, is_ipv6);
+               break;
+       case 2:
+               inet_proto_csum_replace2(ptr, skb, from, to, is_pseudo);
+diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
+index 54b8c899d21c..fe70f9ce8b00 100644
+--- a/tools/include/uapi/linux/bpf.h
++++ b/tools/include/uapi/linux/bpf.h
+@@ -1695,6 +1695,7 @@ union bpf_attr {
+  *            for updates resulting in a null checksum the value is set to
+  *            **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates
+  *            the checksum is to be computed against a pseudo-header.
++ *            Flag **BPF_F_IPV6** should be set for IPv6 packets.
+  *
+  *            This helper works in combination with **bpf_csum_diff**\ (),
+  *            which does not update the checksum in-place, but offers more
+@@ -5106,6 +5107,7 @@ enum {
+       BPF_F_PSEUDO_HDR                = (1ULL << 4),
+       BPF_F_MARK_MANGLED_0            = (1ULL << 5),
+       BPF_F_MARK_ENFORCE              = (1ULL << 6),
++      BPF_F_IPV6                      = (1ULL << 7),
+ };
+ /* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */
+-- 
+2.43.0
+
index d4978b41b466f82c1612613878192bb5dc50dc2e..ada521d4ba8cb412692ef52c14d437620f38ce81 100644 (file)
@@ -395,3 +395,4 @@ arm64-spectre-increase-parameters-that-can-be-used-to-turn-off-bhb-mitigation-in
 arm64-bpf-add-bhb-mitigation-to-the-epilogue-for-cbpf-programs.patch
 arm64-bpf-only-mitigate-cbpf-programs-loaded-by-unprivileged-users.patch
 arm64-proton-pack-add-new-cpus-k-values-for-branch-mitigation.patch
+bpf-fix-l4-csum-update-on-ipv6-in-checksum_complete.patch