Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nftables: allow re-computing sctp CRC-32C in 'payload' statements

nftables payload statements are used to mangle SCTP headers, but they can
only replace the Internet Checksum. As a consequence, nftables rules that
mangle sport/dport/vtag in SCTP headers potentially generate packets that
are discarded by the receiver, unless the CRC-32C is "offloaded" (e.g the
rule mangles a skb having 'ip_summed' equal to 'CHECKSUM_PARTIAL'.

Fix this extending uAPI definitions and L4 checksum update function, in a
way that userspace programs (e.g. nft) can instruct the kernel to compute
CRC-32C in SCTP headers. Also ensure that LIBCRC32C is built if NF_TABLES
is 'y' or 'm' in the kernel build configuration.

Signed-off-by: Davide Caratti <dcaratti@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Davide Caratti and committed by
Jakub Kicinski
346e320c 54086c5a

+31
+2
include/uapi/linux/netfilter/nf_tables.h
··· 749 749 * 750 750 * @NFT_PAYLOAD_CSUM_NONE: no checksumming 751 751 * @NFT_PAYLOAD_CSUM_INET: internet checksum (RFC 791) 752 + * @NFT_PAYLOAD_CSUM_SCTP: CRC-32c, for use in SCTP header (RFC 3309) 752 753 */ 753 754 enum nft_payload_csum_types { 754 755 NFT_PAYLOAD_CSUM_NONE, 755 756 NFT_PAYLOAD_CSUM_INET, 757 + NFT_PAYLOAD_CSUM_SCTP, 756 758 }; 757 759 758 760 enum nft_payload_csum_flags {
+1
net/netfilter/Kconfig
··· 441 441 442 442 config NF_TABLES 443 443 select NETFILTER_NETLINK 444 + select LIBCRC32C 444 445 tristate "Netfilter nf_tables support" 445 446 help 446 447 nftables is the new packet classification framework that intends to
+28
net/netfilter/nft_payload.c
··· 22 22 #include <linux/icmpv6.h> 23 23 #include <linux/ip.h> 24 24 #include <linux/ipv6.h> 25 + #include <net/sctp/checksum.h> 25 26 26 27 static bool nft_payload_rebuild_vlan_hdr(const struct sk_buff *skb, int mac_off, 27 28 struct vlan_ethhdr *veth) ··· 485 484 return 0; 486 485 } 487 486 487 + static int nft_payload_csum_sctp(struct sk_buff *skb, int offset) 488 + { 489 + struct sctphdr *sh; 490 + 491 + if (skb_ensure_writable(skb, offset + sizeof(*sh))) 492 + return -1; 493 + 494 + sh = (struct sctphdr *)(skb->data + offset); 495 + sh->checksum = sctp_compute_cksum(skb, offset); 496 + skb->ip_summed = CHECKSUM_UNNECESSARY; 497 + return 0; 498 + } 499 + 488 500 static int nft_payload_l4csum_update(const struct nft_pktinfo *pkt, 489 501 struct sk_buff *skb, 490 502 __wsum fsum, __wsum tsum) ··· 601 587 skb_store_bits(skb, offset, src, priv->len) < 0) 602 588 goto err; 603 589 590 + if (priv->csum_type == NFT_PAYLOAD_CSUM_SCTP && 591 + pkt->tprot == IPPROTO_SCTP && 592 + skb->ip_summed != CHECKSUM_PARTIAL) { 593 + if (nft_payload_csum_sctp(skb, pkt->xt.thoff)) 594 + goto err; 595 + } 596 + 604 597 return; 605 598 err: 606 599 regs->verdict.code = NFT_BREAK; ··· 643 622 switch (priv->csum_type) { 644 623 case NFT_PAYLOAD_CSUM_NONE: 645 624 case NFT_PAYLOAD_CSUM_INET: 625 + break; 626 + case NFT_PAYLOAD_CSUM_SCTP: 627 + if (priv->base != NFT_PAYLOAD_TRANSPORT_HEADER) 628 + return -EINVAL; 629 + 630 + if (priv->csum_offset != offsetof(struct sctphdr, checksum)) 631 + return -EINVAL; 646 632 break; 647 633 default: 648 634 return -EOPNOTSUPP;