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

Merge git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next

Pablo Neira Ayuso says:

====================
Netfilter updates for net-next

1) Move struct nft_payload_set definition to .c file where it is
only used.

2) Shrink transport and inner header offset fields in the nft_pktinfo
structure to 16-bits, from Florian Westphal.

3) Get rid of nft_objref Kbuild toggle, make it built-in into
nf_tables. This expression is used to instantiate conntrack helpers
in nftables. After removing the conntrack helper auto-assignment
toggle it this feature became more important so move it to the nf_tables
core module. Also from Florian.

4) Extend the existing function to calculate payload inner header offset
to deal with the GRE and IPIP transport protocols.

6) Add inner expression support for nf_tables. This new expression
provides a packet parser for tunneled packets which uses a userspace
description of the expected inner headers. The inner expression
invokes the payload expression (via direct call) to match on the
inner header protocol fields using the inner link, network and
transport header offsets.

An example of the bytecode generated from userspace to match on
IP source encapsulated in a VxLAN packet:

# nft --debug=netlink add rule netdev x y udp dport 4789 vxlan ip saddr 1.2.3.4
netdev x y
[ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x00000011 ]
[ payload load 2b @ transport header + 2 => reg 1 ]
[ cmp eq reg 1 0x0000b512 ]
[ inner type vxlan hdrsize 8 flags f [ meta load protocol => reg 1 ] ]
[ cmp eq reg 1 0x00000008 ]
[ inner type vxlan hdrsize 8 flags f [ payload load 4b @ network header + 12 => reg 1 ] ]
[ cmp eq reg 1 0x04030201 ]

7) Store inner link, network and transport header offsets in percpu
area to parse inner packet header once only. Matching on a different
tunnel type invalidates existing offsets in the percpu area and it
invokes the inner tunnel parser again.

8) Add support for inner meta matching. This support for
NFTA_META_PROTOCOL, which specifies the inner ethertype, and
NFT_META_L4PROTO, which specifies the inner transport protocol.

9) Extend nft_inner to parse GENEVE optional fields to calculate the
link layer offset.

10) Update inner expression so tunnel offset points to GRE header
to normalize tunnel header handling. This also allows to perform
different interpretations of the GRE header from userspace.

* git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next:
netfilter: nft_inner: set tunnel offset to GRE header offset
netfilter: nft_inner: add geneve support
netfilter: nft_meta: add inner match support
netfilter: nft_inner: add percpu inner context
netfilter: nft_inner: support for inner tunnel header matching
netfilter: nft_payload: access ipip payload for inner offset
netfilter: nft_payload: access GRE payload via inner offset
netfilter: nft_objref: make it builtin
netfilter: nf_tables: reduce nft_pktinfo by 8 bytes
netfilter: nft_payload: move struct nft_payload_set definition where it belongs
====================

Link: https://lore.kernel.org/r/20221026132227.3287-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+695 -45
+8 -2
include/net/netfilter/nf_tables.h
··· 24 24 enum { 25 25 NFT_PKTINFO_L4PROTO = (1 << 0), 26 26 NFT_PKTINFO_INNER = (1 << 1), 27 + NFT_PKTINFO_INNER_FULL = (1 << 2), 27 28 }; 28 29 29 30 struct nft_pktinfo { ··· 33 32 u8 flags; 34 33 u8 tprot; 35 34 u16 fragoff; 36 - unsigned int thoff; 37 - unsigned int inneroff; 35 + u16 thoff; 36 + u16 inneroff; 38 37 }; 39 38 40 39 static inline struct sock *nft_sk(const struct nft_pktinfo *pkt) ··· 376 375 return (void *)expr->data; 377 376 } 378 377 378 + struct nft_expr_info; 379 + 380 + int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla, 381 + struct nft_expr_info *info); 379 382 int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src); 380 383 void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr); 381 384 int nft_expr_dump(struct sk_buff *skb, unsigned int attr, ··· 869 864 const struct nlattr * const tb[]); 870 865 void (*release_ops)(const struct nft_expr_ops *ops); 871 866 const struct nft_expr_ops *ops; 867 + const struct nft_expr_ops *inner_ops; 872 868 struct list_head list; 873 869 const char *name; 874 870 struct module *owner;
+26 -10
include/net/netfilter/nf_tables_core.h
··· 18 18 extern struct nft_expr_type nft_rt_type; 19 19 extern struct nft_expr_type nft_exthdr_type; 20 20 extern struct nft_expr_type nft_last_type; 21 + extern struct nft_expr_type nft_objref_type; 22 + extern struct nft_expr_type nft_inner_type; 21 23 22 24 #ifdef CONFIG_NETWORK_SECMARK 23 25 extern struct nft_object_type nft_secmark_obj_type; ··· 66 64 u8 offset; 67 65 u8 len; 68 66 u8 dreg; 69 - }; 70 - 71 - struct nft_payload_set { 72 - enum nft_payload_bases base:8; 73 - u8 offset; 74 - u8 len; 75 - u8 sreg; 76 - u8 csum_type; 77 - u8 csum_offset; 78 - u8 csum_flags; 79 67 }; 80 68 81 69 extern const struct nft_expr_ops nft_payload_fast_ops; ··· 140 148 struct nft_regs *regs, const struct nft_pktinfo *pkt); 141 149 void nft_counter_eval(const struct nft_expr *expr, struct nft_regs *regs, 142 150 const struct nft_pktinfo *pkt); 151 + 152 + enum { 153 + NFT_PAYLOAD_CTX_INNER_TUN = (1 << 0), 154 + NFT_PAYLOAD_CTX_INNER_LL = (1 << 1), 155 + NFT_PAYLOAD_CTX_INNER_NH = (1 << 2), 156 + NFT_PAYLOAD_CTX_INNER_TH = (1 << 3), 157 + }; 158 + 159 + struct nft_inner_tun_ctx { 160 + u16 type; 161 + u16 inner_tunoff; 162 + u16 inner_lloff; 163 + u16 inner_nhoff; 164 + u16 inner_thoff; 165 + __be16 llproto; 166 + u8 l4proto; 167 + u8 flags; 168 + }; 169 + 170 + int nft_payload_inner_offset(const struct nft_pktinfo *pkt); 171 + void nft_payload_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, 172 + const struct nft_pktinfo *pkt, 173 + struct nft_inner_tun_ctx *ctx); 174 + 143 175 #endif /* _NET_NF_TABLES_CORE_H */
+4
include/net/netfilter/nf_tables_ipv4.h
··· 35 35 return -1; 36 36 else if (len < thoff) 37 37 return -1; 38 + else if (thoff < sizeof(*iph)) 39 + return -1; 38 40 39 41 pkt->flags = NFT_PKTINFO_L4PROTO; 40 42 pkt->tprot = iph->protocol; ··· 71 69 return -1; 72 70 } else if (len < thoff) { 73 71 goto inhdr_error; 72 + } else if (thoff < sizeof(*iph)) { 73 + return -1; 74 74 } 75 75 76 76 pkt->flags = NFT_PKTINFO_L4PROTO;
+3 -3
include/net/netfilter/nf_tables_ipv6.h
··· 13 13 unsigned short frag_off; 14 14 15 15 protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags); 16 - if (protohdr < 0) { 16 + if (protohdr < 0 || thoff > U16_MAX) { 17 17 nft_set_pktinfo_unspec(pkt); 18 18 return; 19 19 } ··· 47 47 return -1; 48 48 49 49 protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags); 50 - if (protohdr < 0) 50 + if (protohdr < 0 || thoff > U16_MAX) 51 51 return -1; 52 52 53 53 pkt->flags = NFT_PKTINFO_L4PROTO; ··· 93 93 } 94 94 95 95 protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags); 96 - if (protohdr < 0) 96 + if (protohdr < 0 || thoff > U16_MAX) 97 97 goto inhdr_error; 98 98 99 99 pkt->flags = NFT_PKTINFO_L4PROTO;
+6
include/net/netfilter/nft_meta.h
··· 46 46 47 47 bool nft_meta_get_reduce(struct nft_regs_track *track, 48 48 const struct nft_expr *expr); 49 + 50 + struct nft_inner_tun_ctx; 51 + void nft_meta_inner_eval(const struct nft_expr *expr, 52 + struct nft_regs *regs, const struct nft_pktinfo *pkt, 53 + struct nft_inner_tun_ctx *tun_ctx); 54 + 49 55 #endif
+27
include/uapi/linux/netfilter/nf_tables.h
··· 760 760 NFT_PAYLOAD_NETWORK_HEADER, 761 761 NFT_PAYLOAD_TRANSPORT_HEADER, 762 762 NFT_PAYLOAD_INNER_HEADER, 763 + NFT_PAYLOAD_TUN_HEADER, 763 764 }; 764 765 765 766 /** ··· 779 778 enum nft_payload_csum_flags { 780 779 NFT_PAYLOAD_L4CSUM_PSEUDOHDR = (1 << 0), 781 780 }; 781 + 782 + enum nft_inner_type { 783 + NFT_INNER_UNSPEC = 0, 784 + NFT_INNER_VXLAN, 785 + NFT_INNER_GENEVE, 786 + }; 787 + 788 + enum nft_inner_flags { 789 + NFT_INNER_HDRSIZE = (1 << 0), 790 + NFT_INNER_LL = (1 << 1), 791 + NFT_INNER_NH = (1 << 2), 792 + NFT_INNER_TH = (1 << 3), 793 + }; 794 + #define NFT_INNER_MASK (NFT_INNER_HDRSIZE | NFT_INNER_LL | \ 795 + NFT_INNER_NH | NFT_INNER_TH) 796 + 797 + enum nft_inner_attributes { 798 + NFTA_INNER_UNSPEC, 799 + NFTA_INNER_NUM, 800 + NFTA_INNER_TYPE, 801 + NFTA_INNER_FLAGS, 802 + NFTA_INNER_HDRSIZE, 803 + NFTA_INNER_EXPR, 804 + __NFTA_INNER_MAX 805 + }; 806 + #define NFTA_INNER_MAX (__NFTA_INNER_MAX - 1) 782 807 783 808 /** 784 809 * enum nft_payload_attributes - nf_tables payload expression netlink attributes
-6
net/netfilter/Kconfig
··· 568 568 This option adds the "tunnel" expression that you can use to set 569 569 tunneling policies. 570 570 571 - config NFT_OBJREF 572 - tristate "Netfilter nf_tables stateful object reference module" 573 - help 574 - This option adds the "objref" expression that allows you to refer to 575 - stateful objects, such as counters and quotas. 576 - 577 571 config NFT_QUEUE 578 572 depends on NETFILTER_NETLINK_QUEUE 579 573 tristate "Netfilter nf_tables queue module"
+2 -2
net/netfilter/Makefile
··· 86 86 nf_tables_trace.o nft_immediate.o nft_cmp.o nft_range.o \ 87 87 nft_bitwise.o nft_byteorder.o nft_payload.o nft_lookup.o \ 88 88 nft_dynset.o nft_meta.o nft_rt.o nft_exthdr.o nft_last.o \ 89 - nft_counter.o nft_chain_route.o nf_tables_offload.o \ 89 + nft_counter.o nft_objref.o nft_inner.o \ 90 + nft_chain_route.o nf_tables_offload.o \ 90 91 nft_set_hash.o nft_set_bitmap.o nft_set_rbtree.o \ 91 92 nft_set_pipapo.o 92 93 ··· 105 104 obj-$(CONFIG_NFT_FLOW_OFFLOAD) += nft_flow_offload.o 106 105 obj-$(CONFIG_NFT_LIMIT) += nft_limit.o 107 106 obj-$(CONFIG_NFT_NAT) += nft_nat.o 108 - obj-$(CONFIG_NFT_OBJREF) += nft_objref.o 109 107 obj-$(CONFIG_NFT_QUEUE) += nft_queue.o 110 108 obj-$(CONFIG_NFT_QUOTA) += nft_quota.o 111 109 obj-$(CONFIG_NFT_REJECT) += nft_reject.o
+37
net/netfilter/nf_tables_api.c
··· 2857 2857 return err; 2858 2858 } 2859 2859 2860 + int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla, 2861 + struct nft_expr_info *info) 2862 + { 2863 + struct nlattr *tb[NFTA_EXPR_MAX + 1]; 2864 + const struct nft_expr_type *type; 2865 + int err; 2866 + 2867 + err = nla_parse_nested_deprecated(tb, NFTA_EXPR_MAX, nla, 2868 + nft_expr_policy, NULL); 2869 + if (err < 0) 2870 + return err; 2871 + 2872 + if (!tb[NFTA_EXPR_DATA]) 2873 + return -EINVAL; 2874 + 2875 + type = __nft_expr_type_get(ctx->family, tb[NFTA_EXPR_NAME]); 2876 + if (IS_ERR(type)) 2877 + return PTR_ERR(type); 2878 + 2879 + if (!type->inner_ops) 2880 + return -EOPNOTSUPP; 2881 + 2882 + err = nla_parse_nested_deprecated(info->tb, type->maxattr, 2883 + tb[NFTA_EXPR_DATA], 2884 + type->policy, NULL); 2885 + if (err < 0) 2886 + goto err_nla_parse; 2887 + 2888 + info->attr = nla; 2889 + info->ops = type->inner_ops; 2890 + 2891 + return 0; 2892 + 2893 + err_nla_parse: 2894 + return err; 2895 + } 2896 + 2860 2897 static int nf_tables_newexpr(const struct nft_ctx *ctx, 2861 2898 const struct nft_expr_info *expr_info, 2862 2899 struct nft_expr *expr)
+2
net/netfilter/nf_tables_core.c
··· 340 340 &nft_exthdr_type, 341 341 &nft_last_type, 342 342 &nft_counter_type, 343 + &nft_objref_type, 344 + &nft_inner_type, 343 345 }; 344 346 345 347 static struct nft_object_type *nft_basic_objects[] = {
+384
net/netfilter/nft_inner.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2022 Pablo Neira Ayuso <pablo@netfilter.org> 4 + */ 5 + 6 + #include <linux/kernel.h> 7 + #include <linux/if_vlan.h> 8 + #include <linux/init.h> 9 + #include <linux/module.h> 10 + #include <linux/netlink.h> 11 + #include <linux/netfilter.h> 12 + #include <linux/netfilter/nf_tables.h> 13 + #include <net/netfilter/nf_tables_core.h> 14 + #include <net/netfilter/nf_tables.h> 15 + #include <net/netfilter/nft_meta.h> 16 + #include <net/netfilter/nf_tables_offload.h> 17 + #include <linux/tcp.h> 18 + #include <linux/udp.h> 19 + #include <net/gre.h> 20 + #include <net/geneve.h> 21 + #include <net/ip.h> 22 + #include <linux/icmpv6.h> 23 + #include <linux/ip.h> 24 + #include <linux/ipv6.h> 25 + 26 + static DEFINE_PER_CPU(struct nft_inner_tun_ctx, nft_pcpu_tun_ctx); 27 + 28 + /* Same layout as nft_expr but it embeds the private expression data area. */ 29 + struct __nft_expr { 30 + const struct nft_expr_ops *ops; 31 + union { 32 + struct nft_payload payload; 33 + struct nft_meta meta; 34 + } __attribute__((aligned(__alignof__(u64)))); 35 + }; 36 + 37 + enum { 38 + NFT_INNER_EXPR_PAYLOAD, 39 + NFT_INNER_EXPR_META, 40 + }; 41 + 42 + struct nft_inner { 43 + u8 flags; 44 + u8 hdrsize; 45 + u8 type; 46 + u8 expr_type; 47 + 48 + struct __nft_expr expr; 49 + }; 50 + 51 + static int nft_inner_parse_l2l3(const struct nft_inner *priv, 52 + const struct nft_pktinfo *pkt, 53 + struct nft_inner_tun_ctx *ctx, u32 off) 54 + { 55 + __be16 llproto, outer_llproto; 56 + u32 nhoff, thoff; 57 + 58 + if (priv->flags & NFT_INNER_LL) { 59 + struct vlan_ethhdr *veth, _veth; 60 + struct ethhdr *eth, _eth; 61 + u32 hdrsize; 62 + 63 + eth = skb_header_pointer(pkt->skb, off, sizeof(_eth), &_eth); 64 + if (!eth) 65 + return -1; 66 + 67 + switch (eth->h_proto) { 68 + case htons(ETH_P_IP): 69 + case htons(ETH_P_IPV6): 70 + llproto = eth->h_proto; 71 + hdrsize = sizeof(_eth); 72 + break; 73 + case htons(ETH_P_8021Q): 74 + veth = skb_header_pointer(pkt->skb, off, sizeof(_veth), &_veth); 75 + if (!eth) 76 + return -1; 77 + 78 + outer_llproto = veth->h_vlan_encapsulated_proto; 79 + llproto = veth->h_vlan_proto; 80 + hdrsize = sizeof(_veth); 81 + break; 82 + default: 83 + return -1; 84 + } 85 + 86 + ctx->inner_lloff = off; 87 + ctx->flags |= NFT_PAYLOAD_CTX_INNER_LL; 88 + off += hdrsize; 89 + } else { 90 + struct iphdr *iph; 91 + u32 _version; 92 + 93 + iph = skb_header_pointer(pkt->skb, off, sizeof(_version), &_version); 94 + if (!iph) 95 + return -1; 96 + 97 + switch (iph->version) { 98 + case 4: 99 + llproto = htons(ETH_P_IP); 100 + break; 101 + case 6: 102 + llproto = htons(ETH_P_IPV6); 103 + break; 104 + default: 105 + return -1; 106 + } 107 + } 108 + 109 + ctx->llproto = llproto; 110 + if (llproto == htons(ETH_P_8021Q)) 111 + llproto = outer_llproto; 112 + 113 + nhoff = off; 114 + 115 + switch (llproto) { 116 + case htons(ETH_P_IP): { 117 + struct iphdr *iph, _iph; 118 + 119 + iph = skb_header_pointer(pkt->skb, nhoff, sizeof(_iph), &_iph); 120 + if (!iph) 121 + return -1; 122 + 123 + if (iph->ihl < 5 || iph->version != 4) 124 + return -1; 125 + 126 + ctx->inner_nhoff = nhoff; 127 + ctx->flags |= NFT_PAYLOAD_CTX_INNER_NH; 128 + 129 + thoff = nhoff + (iph->ihl * 4); 130 + if ((ntohs(iph->frag_off) & IP_OFFSET) == 0) { 131 + ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH; 132 + ctx->inner_thoff = thoff; 133 + ctx->l4proto = iph->protocol; 134 + } 135 + } 136 + break; 137 + case htons(ETH_P_IPV6): { 138 + struct ipv6hdr *ip6h, _ip6h; 139 + int fh_flags = IP6_FH_F_AUTH; 140 + unsigned short fragoff; 141 + int l4proto; 142 + 143 + ip6h = skb_header_pointer(pkt->skb, nhoff, sizeof(_ip6h), &_ip6h); 144 + if (!ip6h) 145 + return -1; 146 + 147 + if (ip6h->version != 6) 148 + return -1; 149 + 150 + ctx->inner_nhoff = nhoff; 151 + ctx->flags |= NFT_PAYLOAD_CTX_INNER_NH; 152 + 153 + thoff = nhoff; 154 + l4proto = ipv6_find_hdr(pkt->skb, &thoff, -1, &fragoff, &fh_flags); 155 + if (l4proto < 0 || thoff > U16_MAX) 156 + return -1; 157 + 158 + if (fragoff == 0) { 159 + thoff = nhoff + sizeof(_ip6h); 160 + ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH; 161 + ctx->inner_thoff = thoff; 162 + ctx->l4proto = l4proto; 163 + } 164 + } 165 + break; 166 + default: 167 + return -1; 168 + } 169 + 170 + return 0; 171 + } 172 + 173 + static int nft_inner_parse_tunhdr(const struct nft_inner *priv, 174 + const struct nft_pktinfo *pkt, 175 + struct nft_inner_tun_ctx *ctx, u32 *off) 176 + { 177 + if (pkt->tprot == IPPROTO_GRE) { 178 + ctx->inner_tunoff = pkt->thoff; 179 + ctx->flags |= NFT_PAYLOAD_CTX_INNER_TUN; 180 + return 0; 181 + } 182 + 183 + if (pkt->tprot != IPPROTO_UDP) 184 + return -1; 185 + 186 + ctx->inner_tunoff = *off; 187 + ctx->flags |= NFT_PAYLOAD_CTX_INNER_TUN; 188 + *off += priv->hdrsize; 189 + 190 + switch (priv->type) { 191 + case NFT_INNER_GENEVE: { 192 + struct genevehdr *gnvh, _gnvh; 193 + 194 + gnvh = skb_header_pointer(pkt->skb, pkt->inneroff, 195 + sizeof(_gnvh), &_gnvh); 196 + if (!gnvh) 197 + return -1; 198 + 199 + *off += gnvh->opt_len * 4; 200 + } 201 + break; 202 + default: 203 + break; 204 + } 205 + 206 + return 0; 207 + } 208 + 209 + static int nft_inner_parse(const struct nft_inner *priv, 210 + struct nft_pktinfo *pkt, 211 + struct nft_inner_tun_ctx *tun_ctx) 212 + { 213 + struct nft_inner_tun_ctx ctx = {}; 214 + u32 off = pkt->inneroff; 215 + 216 + if (priv->flags & NFT_INNER_HDRSIZE && 217 + nft_inner_parse_tunhdr(priv, pkt, &ctx, &off) < 0) 218 + return -1; 219 + 220 + if (priv->flags & (NFT_INNER_LL | NFT_INNER_NH)) { 221 + if (nft_inner_parse_l2l3(priv, pkt, &ctx, off) < 0) 222 + return -1; 223 + } else if (priv->flags & NFT_INNER_TH) { 224 + ctx.inner_thoff = off; 225 + ctx.flags |= NFT_PAYLOAD_CTX_INNER_TH; 226 + } 227 + 228 + *tun_ctx = ctx; 229 + tun_ctx->type = priv->type; 230 + pkt->flags |= NFT_PKTINFO_INNER_FULL; 231 + 232 + return 0; 233 + } 234 + 235 + static bool nft_inner_parse_needed(const struct nft_inner *priv, 236 + const struct nft_pktinfo *pkt, 237 + const struct nft_inner_tun_ctx *tun_ctx) 238 + { 239 + if (!(pkt->flags & NFT_PKTINFO_INNER_FULL)) 240 + return true; 241 + 242 + if (priv->type != tun_ctx->type) 243 + return true; 244 + 245 + return false; 246 + } 247 + 248 + static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, 249 + const struct nft_pktinfo *pkt) 250 + { 251 + struct nft_inner_tun_ctx *tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx); 252 + const struct nft_inner *priv = nft_expr_priv(expr); 253 + 254 + if (nft_payload_inner_offset(pkt) < 0) 255 + goto err; 256 + 257 + if (nft_inner_parse_needed(priv, pkt, tun_ctx) && 258 + nft_inner_parse(priv, (struct nft_pktinfo *)pkt, tun_ctx) < 0) 259 + goto err; 260 + 261 + switch (priv->expr_type) { 262 + case NFT_INNER_EXPR_PAYLOAD: 263 + nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx); 264 + break; 265 + case NFT_INNER_EXPR_META: 266 + nft_meta_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx); 267 + break; 268 + default: 269 + WARN_ON_ONCE(1); 270 + goto err; 271 + } 272 + return; 273 + err: 274 + regs->verdict.code = NFT_BREAK; 275 + } 276 + 277 + static const struct nla_policy nft_inner_policy[NFTA_INNER_MAX + 1] = { 278 + [NFTA_INNER_NUM] = { .type = NLA_U32 }, 279 + [NFTA_INNER_FLAGS] = { .type = NLA_U32 }, 280 + [NFTA_INNER_HDRSIZE] = { .type = NLA_U32 }, 281 + [NFTA_INNER_TYPE] = { .type = NLA_U32 }, 282 + [NFTA_INNER_EXPR] = { .type = NLA_NESTED }, 283 + }; 284 + 285 + struct nft_expr_info { 286 + const struct nft_expr_ops *ops; 287 + const struct nlattr *attr; 288 + struct nlattr *tb[NFT_EXPR_MAXATTR + 1]; 289 + }; 290 + 291 + static int nft_inner_init(const struct nft_ctx *ctx, 292 + const struct nft_expr *expr, 293 + const struct nlattr * const tb[]) 294 + { 295 + struct nft_inner *priv = nft_expr_priv(expr); 296 + u32 flags, hdrsize, type, num; 297 + struct nft_expr_info expr_info; 298 + int err; 299 + 300 + if (!tb[NFTA_INNER_FLAGS] || 301 + !tb[NFTA_INNER_HDRSIZE] || 302 + !tb[NFTA_INNER_TYPE] || 303 + !tb[NFTA_INNER_EXPR]) 304 + return -EINVAL; 305 + 306 + flags = ntohl(nla_get_be32(tb[NFTA_INNER_FLAGS])); 307 + if (flags & ~NFT_INNER_MASK) 308 + return -EOPNOTSUPP; 309 + 310 + num = ntohl(nla_get_be32(tb[NFTA_INNER_NUM])); 311 + if (num != 0) 312 + return -EOPNOTSUPP; 313 + 314 + hdrsize = ntohl(nla_get_be32(tb[NFTA_INNER_HDRSIZE])); 315 + type = ntohl(nla_get_be32(tb[NFTA_INNER_TYPE])); 316 + 317 + if (type > U8_MAX) 318 + return -EINVAL; 319 + 320 + if (flags & NFT_INNER_HDRSIZE) { 321 + if (hdrsize == 0 || hdrsize > 64) 322 + return -EOPNOTSUPP; 323 + } 324 + 325 + priv->flags = flags; 326 + priv->hdrsize = hdrsize; 327 + priv->type = type; 328 + 329 + err = nft_expr_inner_parse(ctx, tb[NFTA_INNER_EXPR], &expr_info); 330 + if (err < 0) 331 + return err; 332 + 333 + priv->expr.ops = expr_info.ops; 334 + 335 + if (!strcmp(expr_info.ops->type->name, "payload")) 336 + priv->expr_type = NFT_INNER_EXPR_PAYLOAD; 337 + else if (!strcmp(expr_info.ops->type->name, "meta")) 338 + priv->expr_type = NFT_INNER_EXPR_META; 339 + else 340 + return -EINVAL; 341 + 342 + err = expr_info.ops->init(ctx, (struct nft_expr *)&priv->expr, 343 + (const struct nlattr * const*)expr_info.tb); 344 + if (err < 0) 345 + return err; 346 + 347 + return 0; 348 + } 349 + 350 + static int nft_inner_dump(struct sk_buff *skb, const struct nft_expr *expr) 351 + { 352 + const struct nft_inner *priv = nft_expr_priv(expr); 353 + 354 + if (nla_put_be32(skb, NFTA_INNER_NUM, htonl(0)) || 355 + nla_put_be32(skb, NFTA_INNER_TYPE, htonl(priv->type)) || 356 + nla_put_be32(skb, NFTA_INNER_FLAGS, htonl(priv->flags)) || 357 + nla_put_be32(skb, NFTA_INNER_HDRSIZE, htonl(priv->hdrsize))) 358 + goto nla_put_failure; 359 + 360 + if (nft_expr_dump(skb, NFTA_INNER_EXPR, 361 + (struct nft_expr *)&priv->expr) < 0) 362 + goto nla_put_failure; 363 + 364 + return 0; 365 + 366 + nla_put_failure: 367 + return -1; 368 + } 369 + 370 + static const struct nft_expr_ops nft_inner_ops = { 371 + .type = &nft_inner_type, 372 + .size = NFT_EXPR_SIZE(sizeof(struct nft_inner)), 373 + .eval = nft_inner_eval, 374 + .init = nft_inner_init, 375 + .dump = nft_inner_dump, 376 + }; 377 + 378 + struct nft_expr_type nft_inner_type __read_mostly = { 379 + .name = "inner", 380 + .ops = &nft_inner_ops, 381 + .policy = nft_inner_policy, 382 + .maxattr = NFTA_INNER_MAX, 383 + .owner = THIS_MODULE, 384 + };
+62
net/netfilter/nft_meta.c
··· 831 831 return ERR_PTR(-EINVAL); 832 832 } 833 833 834 + static int nft_meta_inner_init(const struct nft_ctx *ctx, 835 + const struct nft_expr *expr, 836 + const struct nlattr * const tb[]) 837 + { 838 + struct nft_meta *priv = nft_expr_priv(expr); 839 + unsigned int len; 840 + 841 + priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); 842 + switch (priv->key) { 843 + case NFT_META_PROTOCOL: 844 + len = sizeof(u16); 845 + break; 846 + case NFT_META_L4PROTO: 847 + len = sizeof(u32); 848 + break; 849 + default: 850 + return -EOPNOTSUPP; 851 + } 852 + priv->len = len; 853 + 854 + return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg, 855 + NULL, NFT_DATA_VALUE, len); 856 + } 857 + 858 + void nft_meta_inner_eval(const struct nft_expr *expr, 859 + struct nft_regs *regs, 860 + const struct nft_pktinfo *pkt, 861 + struct nft_inner_tun_ctx *tun_ctx) 862 + { 863 + const struct nft_meta *priv = nft_expr_priv(expr); 864 + u32 *dest = &regs->data[priv->dreg]; 865 + 866 + switch (priv->key) { 867 + case NFT_META_PROTOCOL: 868 + nft_reg_store16(dest, (__force u16)tun_ctx->llproto); 869 + break; 870 + case NFT_META_L4PROTO: 871 + if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TH)) 872 + goto err; 873 + 874 + nft_reg_store8(dest, tun_ctx->l4proto); 875 + break; 876 + default: 877 + WARN_ON_ONCE(1); 878 + goto err; 879 + } 880 + return; 881 + 882 + err: 883 + regs->verdict.code = NFT_BREAK; 884 + } 885 + EXPORT_SYMBOL_GPL(nft_meta_inner_eval); 886 + 887 + static const struct nft_expr_ops nft_meta_inner_ops = { 888 + .type = &nft_meta_type, 889 + .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), 890 + .init = nft_meta_inner_init, 891 + .dump = nft_meta_get_dump, 892 + /* direct call to nft_meta_inner_eval(). */ 893 + }; 894 + 834 895 struct nft_expr_type nft_meta_type __read_mostly = { 835 896 .name = "meta", 836 897 .select_ops = nft_meta_select_ops, 898 + .inner_ops = &nft_meta_inner_ops, 837 899 .policy = nft_meta_policy, 838 900 .maxattr = NFTA_META_MAX, 839 901 .owner = THIS_MODULE,
+1 -21
net/netfilter/nft_objref.c
··· 82 82 obj->use++; 83 83 } 84 84 85 - static struct nft_expr_type nft_objref_type; 86 85 static const struct nft_expr_ops nft_objref_ops = { 87 86 .type = &nft_objref_type, 88 87 .size = NFT_EXPR_SIZE(sizeof(struct nft_object *)), ··· 194 195 nf_tables_destroy_set(ctx, priv->set); 195 196 } 196 197 197 - static struct nft_expr_type nft_objref_type; 198 198 static const struct nft_expr_ops nft_objref_map_ops = { 199 199 .type = &nft_objref_type, 200 200 .size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)), ··· 231 233 [NFTA_OBJREF_SET_ID] = { .type = NLA_U32 }, 232 234 }; 233 235 234 - static struct nft_expr_type nft_objref_type __read_mostly = { 236 + struct nft_expr_type nft_objref_type __read_mostly = { 235 237 .name = "objref", 236 238 .select_ops = nft_objref_select_ops, 237 239 .policy = nft_objref_policy, 238 240 .maxattr = NFTA_OBJREF_MAX, 239 241 .owner = THIS_MODULE, 240 242 }; 241 - 242 - static int __init nft_objref_module_init(void) 243 - { 244 - return nft_register_expr(&nft_objref_type); 245 - } 246 - 247 - static void __exit nft_objref_module_exit(void) 248 - { 249 - nft_unregister_expr(&nft_objref_type); 250 - } 251 - 252 - module_init(nft_objref_module_init); 253 - module_exit(nft_objref_module_exit); 254 - 255 - MODULE_LICENSE("GPL"); 256 - MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); 257 - MODULE_ALIAS_NFT_EXPR("objref"); 258 - MODULE_DESCRIPTION("nftables stateful object reference module");
+133 -1
net/netfilter/nft_payload.c
··· 19 19 /* For layer 4 checksum field offset. */ 20 20 #include <linux/tcp.h> 21 21 #include <linux/udp.h> 22 + #include <net/gre.h> 22 23 #include <linux/icmpv6.h> 23 24 #include <linux/ip.h> 24 25 #include <linux/ipv6.h> ··· 101 100 pkt->inneroff = thoff + __tcp_hdrlen(th); 102 101 } 103 102 break; 103 + case IPPROTO_GRE: { 104 + u32 offset = sizeof(struct gre_base_hdr), version; 105 + struct gre_base_hdr *gre, _gre; 106 + 107 + gre = skb_header_pointer(pkt->skb, thoff, sizeof(_gre), &_gre); 108 + if (!gre) 109 + return -1; 110 + 111 + version = gre->flags & GRE_VERSION; 112 + switch (version) { 113 + case GRE_VERSION_0: 114 + if (gre->flags & GRE_ROUTING) 115 + return -1; 116 + 117 + if (gre->flags & GRE_CSUM) { 118 + offset += sizeof_field(struct gre_full_hdr, csum) + 119 + sizeof_field(struct gre_full_hdr, reserved1); 120 + } 121 + if (gre->flags & GRE_KEY) 122 + offset += sizeof_field(struct gre_full_hdr, key); 123 + 124 + if (gre->flags & GRE_SEQ) 125 + offset += sizeof_field(struct gre_full_hdr, seq); 126 + break; 127 + default: 128 + return -1; 129 + } 130 + 131 + pkt->inneroff = thoff + offset; 132 + } 133 + break; 134 + case IPPROTO_IPIP: 135 + pkt->inneroff = thoff; 136 + break; 104 137 default: 105 138 return -1; 106 139 } ··· 144 109 return 0; 145 110 } 146 111 147 - static int nft_payload_inner_offset(const struct nft_pktinfo *pkt) 112 + int nft_payload_inner_offset(const struct nft_pktinfo *pkt) 148 113 { 149 114 if (!(pkt->flags & NFT_PKTINFO_INNER) && 150 115 __nft_payload_inner_offset((struct nft_pktinfo *)pkt) < 0) ··· 587 552 .offload = nft_payload_offload, 588 553 }; 589 554 555 + void nft_payload_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, 556 + const struct nft_pktinfo *pkt, 557 + struct nft_inner_tun_ctx *tun_ctx) 558 + { 559 + const struct nft_payload *priv = nft_expr_priv(expr); 560 + const struct sk_buff *skb = pkt->skb; 561 + u32 *dest = &regs->data[priv->dreg]; 562 + int offset; 563 + 564 + if (priv->len % NFT_REG32_SIZE) 565 + dest[priv->len / NFT_REG32_SIZE] = 0; 566 + 567 + switch (priv->base) { 568 + case NFT_PAYLOAD_TUN_HEADER: 569 + if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TUN)) 570 + goto err; 571 + 572 + offset = tun_ctx->inner_tunoff; 573 + break; 574 + case NFT_PAYLOAD_LL_HEADER: 575 + if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_LL)) 576 + goto err; 577 + 578 + offset = tun_ctx->inner_lloff; 579 + break; 580 + case NFT_PAYLOAD_NETWORK_HEADER: 581 + if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_NH)) 582 + goto err; 583 + 584 + offset = tun_ctx->inner_nhoff; 585 + break; 586 + case NFT_PAYLOAD_TRANSPORT_HEADER: 587 + if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TH)) 588 + goto err; 589 + 590 + offset = tun_ctx->inner_thoff; 591 + break; 592 + default: 593 + WARN_ON_ONCE(1); 594 + goto err; 595 + } 596 + offset += priv->offset; 597 + 598 + if (skb_copy_bits(skb, offset, dest, priv->len) < 0) 599 + goto err; 600 + 601 + return; 602 + err: 603 + regs->verdict.code = NFT_BREAK; 604 + } 605 + 606 + static int nft_payload_inner_init(const struct nft_ctx *ctx, 607 + const struct nft_expr *expr, 608 + const struct nlattr * const tb[]) 609 + { 610 + struct nft_payload *priv = nft_expr_priv(expr); 611 + u32 base; 612 + 613 + base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); 614 + switch (base) { 615 + case NFT_PAYLOAD_TUN_HEADER: 616 + case NFT_PAYLOAD_LL_HEADER: 617 + case NFT_PAYLOAD_NETWORK_HEADER: 618 + case NFT_PAYLOAD_TRANSPORT_HEADER: 619 + break; 620 + default: 621 + return -EOPNOTSUPP; 622 + } 623 + 624 + priv->base = base; 625 + priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); 626 + priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); 627 + 628 + return nft_parse_register_store(ctx, tb[NFTA_PAYLOAD_DREG], 629 + &priv->dreg, NULL, NFT_DATA_VALUE, 630 + priv->len); 631 + } 632 + 633 + static const struct nft_expr_ops nft_payload_inner_ops = { 634 + .type = &nft_payload_type, 635 + .size = NFT_EXPR_SIZE(sizeof(struct nft_payload)), 636 + .init = nft_payload_inner_init, 637 + .dump = nft_payload_dump, 638 + /* direct call to nft_payload_inner_eval(). */ 639 + }; 640 + 590 641 static inline void nft_csum_replace(__sum16 *sum, __wsum fsum, __wsum tsum) 591 642 { 592 643 *sum = csum_fold(csum_add(csum_sub(~csum_unfold(*sum), fsum), tsum)); ··· 785 664 786 665 return 0; 787 666 } 667 + 668 + struct nft_payload_set { 669 + enum nft_payload_bases base:8; 670 + u8 offset; 671 + u8 len; 672 + u8 sreg; 673 + u8 csum_type; 674 + u8 csum_offset; 675 + u8 csum_flags; 676 + }; 788 677 789 678 static void nft_payload_set_eval(const struct nft_expr *expr, 790 679 struct nft_regs *regs, ··· 1016 885 struct nft_expr_type nft_payload_type __read_mostly = { 1017 886 .name = "payload", 1018 887 .select_ops = nft_payload_select_ops, 888 + .inner_ops = &nft_payload_inner_ops, 1019 889 .policy = nft_payload_policy, 1020 890 .maxattr = NFTA_PAYLOAD_MAX, 1021 891 .owner = THIS_MODULE,