diff options
-rw-r--r-- | queue-5.15/bpf-fix-l4-csum-update-on-ipv6-in-checksum_complete.patch | 139 | ||||
-rw-r--r-- | queue-5.15/series | 1 |
2 files changed, 140 insertions, 0 deletions
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 index 0000000000..b965b30ad0 --- /dev/null +++ b/queue-5.15/bpf-fix-l4-csum-update-on-ipv6-in-checksum_complete.patch @@ -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 + diff --git a/queue-5.15/series b/queue-5.15/series index d4978b41b4..ada521d4ba 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -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 |