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

netfilter: nft_payload: support for inner header matching / mangling

Allow to match and mangle on inner headers / payload data after the
transport header. There is a new field in the pktinfo structure that
stores the inner header offset which is calculated only when requested.
Only TCP and UDP supported at this stage.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+58 -2
+2
include/net/netfilter/nf_tables.h
··· 23 23 24 24 enum { 25 25 NFT_PKTINFO_L4PROTO = (1 << 0), 26 + NFT_PKTINFO_INNER = (1 << 1), 26 27 }; 27 28 28 29 struct nft_pktinfo { ··· 33 32 u8 tprot; 34 33 u16 fragoff; 35 34 unsigned int thoff; 35 + unsigned int inneroff; 36 36 }; 37 37 38 38 static inline struct sock *nft_sk(const struct nft_pktinfo *pkt)
+2
include/uapi/linux/netfilter/nf_tables.h
··· 753 753 * @NFT_PAYLOAD_LL_HEADER: link layer header 754 754 * @NFT_PAYLOAD_NETWORK_HEADER: network header 755 755 * @NFT_PAYLOAD_TRANSPORT_HEADER: transport header 756 + * @NFT_PAYLOAD_INNER_HEADER: inner header / payload 756 757 */ 757 758 enum nft_payload_bases { 758 759 NFT_PAYLOAD_LL_HEADER, 759 760 NFT_PAYLOAD_NETWORK_HEADER, 760 761 NFT_PAYLOAD_TRANSPORT_HEADER, 762 + NFT_PAYLOAD_INNER_HEADER, 761 763 }; 762 764 763 765 /**
+54 -2
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 <linux/ip.h> 25 26 #include <net/sctp/checksum.h> 26 27 27 28 static bool nft_payload_rebuild_vlan_hdr(const struct sk_buff *skb, int mac_off, ··· 80 79 return skb_copy_bits(skb, offset + mac_off, dst_u8, len) == 0; 81 80 } 82 81 82 + static int __nft_payload_inner_offset(struct nft_pktinfo *pkt) 83 + { 84 + unsigned int thoff = nft_thoff(pkt); 85 + 86 + if (!(pkt->flags & NFT_PKTINFO_L4PROTO)) 87 + return -1; 88 + 89 + switch (pkt->tprot) { 90 + case IPPROTO_UDP: 91 + pkt->inneroff = thoff + sizeof(struct udphdr); 92 + break; 93 + case IPPROTO_TCP: { 94 + struct tcphdr *th, _tcph; 95 + 96 + th = skb_header_pointer(pkt->skb, thoff, sizeof(_tcph), &_tcph); 97 + if (!th) 98 + return -1; 99 + 100 + pkt->inneroff = thoff + __tcp_hdrlen(th); 101 + } 102 + break; 103 + default: 104 + return -1; 105 + } 106 + 107 + pkt->flags |= NFT_PKTINFO_INNER; 108 + 109 + return 0; 110 + } 111 + 112 + static int nft_payload_inner_offset(const struct nft_pktinfo *pkt) 113 + { 114 + if (!(pkt->flags & NFT_PKTINFO_INNER) && 115 + __nft_payload_inner_offset((struct nft_pktinfo *)pkt) < 0) 116 + return -1; 117 + 118 + return pkt->inneroff; 119 + } 120 + 83 121 void nft_payload_eval(const struct nft_expr *expr, 84 122 struct nft_regs *regs, 85 123 const struct nft_pktinfo *pkt) ··· 151 111 if (!(pkt->flags & NFT_PKTINFO_L4PROTO)) 152 112 goto err; 153 113 offset = nft_thoff(pkt); 114 + break; 115 + case NFT_PAYLOAD_INNER_HEADER: 116 + offset = nft_payload_inner_offset(pkt); 117 + if (offset < 0) 118 + goto err; 154 119 break; 155 120 default: 156 121 BUG(); ··· 659 614 goto err; 660 615 offset = nft_thoff(pkt); 661 616 break; 617 + case NFT_PAYLOAD_INNER_HEADER: 618 + offset = nft_payload_inner_offset(pkt); 619 + if (offset < 0) 620 + goto err; 621 + break; 662 622 default: 663 623 BUG(); 664 624 } ··· 672 622 offset += priv->offset; 673 623 674 624 if ((priv->csum_type == NFT_PAYLOAD_CSUM_INET || priv->csum_flags) && 675 - (priv->base != NFT_PAYLOAD_TRANSPORT_HEADER || 625 + ((priv->base != NFT_PAYLOAD_TRANSPORT_HEADER && 626 + priv->base != NFT_PAYLOAD_INNER_HEADER) || 676 627 skb->ip_summed != CHECKSUM_PARTIAL)) { 677 628 fsum = skb_checksum(skb, offset, priv->len, 0); 678 629 tsum = csum_partial(src, priv->len, 0); ··· 792 741 case NFT_PAYLOAD_LL_HEADER: 793 742 case NFT_PAYLOAD_NETWORK_HEADER: 794 743 case NFT_PAYLOAD_TRANSPORT_HEADER: 744 + case NFT_PAYLOAD_INNER_HEADER: 795 745 break; 796 746 default: 797 747 return ERR_PTR(-EOPNOTSUPP); ··· 811 759 len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); 812 760 813 761 if (len <= 4 && is_power_of_2(len) && IS_ALIGNED(offset, len) && 814 - base != NFT_PAYLOAD_LL_HEADER) 762 + base != NFT_PAYLOAD_LL_HEADER && base != NFT_PAYLOAD_INNER_HEADER) 815 763 return &nft_payload_fast_ops; 816 764 else 817 765 return &nft_payload_ops;