Unless you're working with hardware offload, you probably want to use the relevant BPF helper bpf_l4_csum_replace()
(or alternatively bpf_csum_diff()
).
* int bpf_l4_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 flags)
* Description
* Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the
* packet associated to *skb*. Computation is incremental, so the
* helper must know the former value of the header field that was
* modified (*from*), the new value of this field (*to*), and the
* number of bytes (2 or 4) for this field, stored on the lowest
* four bits of *flags*. Alternatively, it is possible to store
* the difference between the previous and the new values of the
* header field in *to*, by setting *from* and the four lowest
* bits of *flags* to 0. For both methods, *offset* indicates the
* location of the IP checksum within the packet. In addition to
* the size of the field, *flags* can be added (bitwise OR) actual
* flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left
* untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and
* 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.
*
* This helper works in combination with **bpf_csum_diff**\ (),
* which does not update the checksum in-place, but offers more
* flexibility and can handle sizes larger than 2 or 4 for the
* checksum to update.
*
* A call to this helper is susceptible to change the underlying
* packet buffer. Therefore, at load time, all checks on pointers
* previously done by the verifier are invalidated and must be
* performed again, if the helper is used in combination with
* direct packet access.
* Return
* 0 on success, or a negative error in case of failure.
The kernel samples or Cilium display some example usage.
If you cannot use it, there is an eBPF implementation from Netronome available here that might help you.