Add bpf_xdp_skb_ext_store_rx_csum(). It stores the RX checksum status in the skb core extension area. A later patch sets the stored status on the skb built from the xdp_frame. The program can read the status from the NIC with bpf_xdp_metadata_rx_checksum() from Lorenzo's RX checksum kfunc series [1]. The kfunc accepts XDP_CHECKSUM_NONE with csum_level 0, and XDP_CHECKSUM_UNNECESSARY with csum_level up to SKB_MAX_CSUM_LEVEL: - Storing XDP_CHECKSUM_UNNECESSARY asserts that the checksums are correct, as in [2]. A program must store it only after it has verified the checksums. - Storing XDP_CHECKSUM_NONE invalidates the checksum status, as requested in [3][4]. - After a decap, storing a lower csum_level updates the status, as requested in [5]. - CHECKSUM_COMPLETE is not passed to the stack for now, as suggested in [5]. Link: https://lore.kernel.org/all/20260715-bpf-xdp-meta-rxcksum-v5-1-623d5c0d0ab7@kernel.org/ [1] Link: https://lore.kernel.org/all/20260630191510.81402-2-deliran@verdict.gg/ [2] Link: https://lore.kernel.org/all/20260227153231.78d16b69@kernel.org/ [3] Link: https://lore.kernel.org/all/amEojEGsIGkUjIWn@devvm7509.cco0.facebook.com/ [4] Link: https://lore.kernel.org/all/20260721183256.6f49d6e1@kernel.org/ [5] Signed-off-by: Tianyi Gao --- enum xdp_checksum has the same names and values as in [1], so this patch does not depend on that series. The series merged later drops its copy. Stanislav was not sure that any NIC reports a level of 2 or more [4]. A separate maximum for XDP would give the same skb field two limits, so the kfunc uses SKB_MAX_CSUM_LEVEL. --- include/net/xdp.h | 6 +++++ net/core/xdp.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/include/net/xdp.h b/include/net/xdp.h index 23c976e92898..d8b77d80bfbe 100644 --- a/include/net/xdp.h +++ b/include/net/xdp.h @@ -701,6 +701,12 @@ enum xdp_rss_hash_type { XDP_RSS_TYPE_L4_IPV6_SCTP_EX = XDP_RSS_TYPE_L4_IPV6_SCTP | XDP_RSS_L3_DYNHDR, }; +enum xdp_checksum { + XDP_CHECKSUM_NONE = BIT(CHECKSUM_NONE), + XDP_CHECKSUM_UNNECESSARY = BIT(CHECKSUM_UNNECESSARY), + XDP_CHECKSUM_COMPLETE = BIT(CHECKSUM_COMPLETE), +}; + struct xdp_metadata_ops { int (*xmo_rx_timestamp)(const struct xdp_md *ctx, u64 *timestamp); int (*xmo_rx_hash)(const struct xdp_md *ctx, u32 *hash, diff --git a/net/core/xdp.c b/net/core/xdp.c index 2c1d5ec3311b..eea04e7b7204 100644 --- a/net/core/xdp.c +++ b/net/core/xdp.c @@ -1004,9 +1004,15 @@ struct xdp_skb_ext_vlan { u16 tci; }; +struct xdp_skb_ext_csum { + u8 ip_summed; /* CHECKSUM_* */ + u8 level; +}; + static const u8 xdp_skb_ext_type_len[] = { [XDP_SKB_EXT_HASH] = XDP_SKB_EXT_CHUNKSIZEOF(struct xdp_skb_ext_hash), [XDP_SKB_EXT_VLAN] = XDP_SKB_EXT_CHUNKSIZEOF(struct xdp_skb_ext_vlan), + [XDP_SKB_EXT_CSUM] = XDP_SKB_EXT_CHUNKSIZEOF(struct xdp_skb_ext_csum), }; /* Limit the header and all entries to 64 bytes, so that a new entry @@ -1120,6 +1126,56 @@ __bpf_kfunc int bpf_xdp_skb_ext_store_rx_vlan(struct xdp_md *ctx, return 0; } +/** + * bpf_xdp_skb_ext_store_rx_csum - Store the RX checksum status of this packet. + * @ctx: XDP context pointer. + * @ip_summed: ``XDP_CHECKSUM_NONE`` or ``XDP_CHECKSUM_UNNECESSARY``. + * @csum_level: Number of consecutive verified checksums minus one, up to + * ``SKB_MAX_CSUM_LEVEL``. Must be 0 with ``XDP_CHECKSUM_NONE``. + * + * Store ``XDP_CHECKSUM_UNNECESSARY`` only after the program has verified + * the checksums. + * + * Return: + * * Returns 0 on success or ``-errno`` on error. + * * ``-EINVAL`` : @ip_summed is not supported, or @csum_level is not valid + * for it + * * ``-ENOSPC`` : not enough headroom for the entry + * * ``-EOPNOTSUPP`` : AF_XDP zero-copy buffer + */ +__bpf_kfunc int bpf_xdp_skb_ext_store_rx_csum(struct xdp_md *ctx, + enum xdp_checksum ip_summed, + u8 csum_level) +{ + struct xdp_buff *xdp = (struct xdp_buff *)ctx; + struct xdp_skb_ext_csum *entry; + u8 val; + + switch (ip_summed) { + case XDP_CHECKSUM_NONE: + if (csum_level) + return -EINVAL; + val = CHECKSUM_NONE; + break; + case XDP_CHECKSUM_UNNECESSARY: + if (csum_level > SKB_MAX_CSUM_LEVEL) + return -EINVAL; + val = CHECKSUM_UNNECESSARY; + break; + default: + return -EINVAL; + } + + entry = xdp_skb_ext_add(xdp, XDP_SKB_EXT_CSUM); + if (IS_ERR(entry)) + return PTR_ERR(entry); + + entry->ip_summed = val; + entry->level = csum_level; + + return 0; +} + /** * bpf_xdp_skb_ext_del - Delete a stored entry of this packet. * @ctx: XDP context pointer. @@ -1149,6 +1205,7 @@ __bpf_kfunc_end_defs(); BTF_KFUNCS_START(xdp_skb_ext_kfunc_ids) BTF_ID_FLAGS(func, bpf_xdp_skb_ext_store_rx_hash) BTF_ID_FLAGS(func, bpf_xdp_skb_ext_store_rx_vlan) +BTF_ID_FLAGS(func, bpf_xdp_skb_ext_store_rx_csum) BTF_ID_FLAGS(func, bpf_xdp_skb_ext_del) BTF_KFUNCS_END(xdp_skb_ext_kfunc_ids) -- 2.56.0