linux/include/uapi
Paul Chaignon ead7f9b8de bpf: Fix L4 csum update on IPv6 in CHECKSUM_COMPLETE
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: 7d672345ed ("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>
History 2025-05-30 19:53:51 -07:00
..
asm-generic af_unix: Introduce SO_PASSRIGHTS. 2025-05-23 10:24:18 +01:00
cxl fwctl/cxl: Fix uuid_t usage in uapi 2025-04-11 20:45:43 -03:00
drm Nova changes for v6.16 2025-05-21 05:49:31 +10:00
fwctl pds_fwctl: add rpc and query support 2025-03-21 20:57:55 -03:00
linux bpf: Fix L4 csum update on IPv6 in CHECKSUM_COMPLETE 2025-05-30 19:53:51 -07:00
misc cxl: Remove driver 2025-03-16 22:04:27 +11:00
mtd ubi: Expose interface for detailed erase counters 2025-01-18 15:32:32 +01:00
rdma RDMA/core: Add support to optional-counters binding configuration 2025-03-18 06:18:42 -04:00
regulator uapi: regulator: Fix typo 2024-01-04 13:22:24 +00:00
scsi scsi: cxlflash: Remove driver 2025-02-03 18:04:55 -05:00
sound ASoC: Intel: avs: Make PEAKVOL configurable from topology 2025-02-24 16:01:53 +00:00
video License cleanup: add SPDX license identifier to uapi header files with a license 2017-11-02 11:20:11 +01:00
xen xen/privcmd: Add new syscall to get gsi from dev 2024-09-25 09:54:55 +02:00
Kbuild kbuild: re-implement Makefile.headersinst without recursion 2019-06-15 19:57:02 +09:00