13a07327dSPablo Neira Ayuso // SPDX-License-Identifier: GPL-2.0-only
23a07327dSPablo Neira Ayuso /*
33a07327dSPablo Neira Ayuso * Copyright (c) 2022 Pablo Neira Ayuso <pablo@netfilter.org>
43a07327dSPablo Neira Ayuso */
53a07327dSPablo Neira Ayuso
63a07327dSPablo Neira Ayuso #include <linux/kernel.h>
73a07327dSPablo Neira Ayuso #include <linux/if_vlan.h>
83a07327dSPablo Neira Ayuso #include <linux/init.h>
93a07327dSPablo Neira Ayuso #include <linux/module.h>
103a07327dSPablo Neira Ayuso #include <linux/netlink.h>
113a07327dSPablo Neira Ayuso #include <linux/netfilter.h>
123a07327dSPablo Neira Ayuso #include <linux/netfilter/nf_tables.h>
133a07327dSPablo Neira Ayuso #include <net/netfilter/nf_tables_core.h>
143a07327dSPablo Neira Ayuso #include <net/netfilter/nf_tables.h>
15a150d122SPablo Neira Ayuso #include <net/netfilter/nft_meta.h>
163a07327dSPablo Neira Ayuso #include <net/netfilter/nf_tables_offload.h>
173a07327dSPablo Neira Ayuso #include <linux/tcp.h>
183a07327dSPablo Neira Ayuso #include <linux/udp.h>
193a07327dSPablo Neira Ayuso #include <net/gre.h>
200db14b95SPablo Neira Ayuso #include <net/geneve.h>
213a07327dSPablo Neira Ayuso #include <net/ip.h>
223a07327dSPablo Neira Ayuso #include <linux/icmpv6.h>
233a07327dSPablo Neira Ayuso #include <linux/ip.h>
243a07327dSPablo Neira Ayuso #include <linux/ipv6.h>
253a07327dSPablo Neira Ayuso
260e795b37SPablo Neira Ayuso static DEFINE_PER_CPU(struct nft_inner_tun_ctx, nft_pcpu_tun_ctx);
270e795b37SPablo Neira Ayuso
283a07327dSPablo Neira Ayuso /* Same layout as nft_expr but it embeds the private expression data area. */
293a07327dSPablo Neira Ayuso struct __nft_expr {
303a07327dSPablo Neira Ayuso const struct nft_expr_ops *ops;
313a07327dSPablo Neira Ayuso union {
323a07327dSPablo Neira Ayuso struct nft_payload payload;
33a150d122SPablo Neira Ayuso struct nft_meta meta;
343a07327dSPablo Neira Ayuso } __attribute__((aligned(__alignof__(u64))));
353a07327dSPablo Neira Ayuso };
363a07327dSPablo Neira Ayuso
373a07327dSPablo Neira Ayuso enum {
383a07327dSPablo Neira Ayuso NFT_INNER_EXPR_PAYLOAD,
39a150d122SPablo Neira Ayuso NFT_INNER_EXPR_META,
403a07327dSPablo Neira Ayuso };
413a07327dSPablo Neira Ayuso
423a07327dSPablo Neira Ayuso struct nft_inner {
433a07327dSPablo Neira Ayuso u8 flags;
443a07327dSPablo Neira Ayuso u8 hdrsize;
453a07327dSPablo Neira Ayuso u8 type;
463a07327dSPablo Neira Ayuso u8 expr_type;
473a07327dSPablo Neira Ayuso
483a07327dSPablo Neira Ayuso struct __nft_expr expr;
493a07327dSPablo Neira Ayuso };
503a07327dSPablo Neira Ayuso
nft_inner_parse_l2l3(const struct nft_inner * priv,const struct nft_pktinfo * pkt,struct nft_inner_tun_ctx * ctx,u32 off)513a07327dSPablo Neira Ayuso static int nft_inner_parse_l2l3(const struct nft_inner *priv,
523a07327dSPablo Neira Ayuso const struct nft_pktinfo *pkt,
533a07327dSPablo Neira Ayuso struct nft_inner_tun_ctx *ctx, u32 off)
543a07327dSPablo Neira Ayuso {
553a07327dSPablo Neira Ayuso __be16 llproto, outer_llproto;
563a07327dSPablo Neira Ayuso u32 nhoff, thoff;
573a07327dSPablo Neira Ayuso
583a07327dSPablo Neira Ayuso if (priv->flags & NFT_INNER_LL) {
593a07327dSPablo Neira Ayuso struct vlan_ethhdr *veth, _veth;
603a07327dSPablo Neira Ayuso struct ethhdr *eth, _eth;
613a07327dSPablo Neira Ayuso u32 hdrsize;
623a07327dSPablo Neira Ayuso
633a07327dSPablo Neira Ayuso eth = skb_header_pointer(pkt->skb, off, sizeof(_eth), &_eth);
643a07327dSPablo Neira Ayuso if (!eth)
653a07327dSPablo Neira Ayuso return -1;
663a07327dSPablo Neira Ayuso
673a07327dSPablo Neira Ayuso switch (eth->h_proto) {
683a07327dSPablo Neira Ayuso case htons(ETH_P_IP):
693a07327dSPablo Neira Ayuso case htons(ETH_P_IPV6):
703a07327dSPablo Neira Ayuso llproto = eth->h_proto;
713a07327dSPablo Neira Ayuso hdrsize = sizeof(_eth);
723a07327dSPablo Neira Ayuso break;
733a07327dSPablo Neira Ayuso case htons(ETH_P_8021Q):
743a07327dSPablo Neira Ayuso veth = skb_header_pointer(pkt->skb, off, sizeof(_veth), &_veth);
757394c2ddSPeng Wu if (!veth)
763a07327dSPablo Neira Ayuso return -1;
773a07327dSPablo Neira Ayuso
783a07327dSPablo Neira Ayuso outer_llproto = veth->h_vlan_encapsulated_proto;
793a07327dSPablo Neira Ayuso llproto = veth->h_vlan_proto;
803a07327dSPablo Neira Ayuso hdrsize = sizeof(_veth);
813a07327dSPablo Neira Ayuso break;
823a07327dSPablo Neira Ayuso default:
833a07327dSPablo Neira Ayuso return -1;
843a07327dSPablo Neira Ayuso }
853a07327dSPablo Neira Ayuso
863a07327dSPablo Neira Ayuso ctx->inner_lloff = off;
873a07327dSPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_LL;
883a07327dSPablo Neira Ayuso off += hdrsize;
893a07327dSPablo Neira Ayuso } else {
903a07327dSPablo Neira Ayuso struct iphdr *iph;
913a07327dSPablo Neira Ayuso u32 _version;
923a07327dSPablo Neira Ayuso
933a07327dSPablo Neira Ayuso iph = skb_header_pointer(pkt->skb, off, sizeof(_version), &_version);
943a07327dSPablo Neira Ayuso if (!iph)
953a07327dSPablo Neira Ayuso return -1;
963a07327dSPablo Neira Ayuso
973a07327dSPablo Neira Ayuso switch (iph->version) {
983a07327dSPablo Neira Ayuso case 4:
993a07327dSPablo Neira Ayuso llproto = htons(ETH_P_IP);
1003a07327dSPablo Neira Ayuso break;
1013a07327dSPablo Neira Ayuso case 6:
1023a07327dSPablo Neira Ayuso llproto = htons(ETH_P_IPV6);
1033a07327dSPablo Neira Ayuso break;
1043a07327dSPablo Neira Ayuso default:
1053a07327dSPablo Neira Ayuso return -1;
1063a07327dSPablo Neira Ayuso }
1073a07327dSPablo Neira Ayuso }
1083a07327dSPablo Neira Ayuso
1093a07327dSPablo Neira Ayuso ctx->llproto = llproto;
1103a07327dSPablo Neira Ayuso if (llproto == htons(ETH_P_8021Q))
1113a07327dSPablo Neira Ayuso llproto = outer_llproto;
1123a07327dSPablo Neira Ayuso
1133a07327dSPablo Neira Ayuso nhoff = off;
1143a07327dSPablo Neira Ayuso
1153a07327dSPablo Neira Ayuso switch (llproto) {
1163a07327dSPablo Neira Ayuso case htons(ETH_P_IP): {
1173a07327dSPablo Neira Ayuso struct iphdr *iph, _iph;
1183a07327dSPablo Neira Ayuso
1193a07327dSPablo Neira Ayuso iph = skb_header_pointer(pkt->skb, nhoff, sizeof(_iph), &_iph);
1203a07327dSPablo Neira Ayuso if (!iph)
1213a07327dSPablo Neira Ayuso return -1;
1223a07327dSPablo Neira Ayuso
1233a07327dSPablo Neira Ayuso if (iph->ihl < 5 || iph->version != 4)
1243a07327dSPablo Neira Ayuso return -1;
1253a07327dSPablo Neira Ayuso
1263a07327dSPablo Neira Ayuso ctx->inner_nhoff = nhoff;
1273a07327dSPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_NH;
1283a07327dSPablo Neira Ayuso
1293a07327dSPablo Neira Ayuso thoff = nhoff + (iph->ihl * 4);
1303a07327dSPablo Neira Ayuso if ((ntohs(iph->frag_off) & IP_OFFSET) == 0) {
1313a07327dSPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH;
1323a07327dSPablo Neira Ayuso ctx->inner_thoff = thoff;
1333a07327dSPablo Neira Ayuso ctx->l4proto = iph->protocol;
1343a07327dSPablo Neira Ayuso }
1353a07327dSPablo Neira Ayuso }
1363a07327dSPablo Neira Ayuso break;
1373a07327dSPablo Neira Ayuso case htons(ETH_P_IPV6): {
1383a07327dSPablo Neira Ayuso struct ipv6hdr *ip6h, _ip6h;
1393a07327dSPablo Neira Ayuso int fh_flags = IP6_FH_F_AUTH;
1403a07327dSPablo Neira Ayuso unsigned short fragoff;
1413a07327dSPablo Neira Ayuso int l4proto;
1423a07327dSPablo Neira Ayuso
1433a07327dSPablo Neira Ayuso ip6h = skb_header_pointer(pkt->skb, nhoff, sizeof(_ip6h), &_ip6h);
1443a07327dSPablo Neira Ayuso if (!ip6h)
1453a07327dSPablo Neira Ayuso return -1;
1463a07327dSPablo Neira Ayuso
1473a07327dSPablo Neira Ayuso if (ip6h->version != 6)
1483a07327dSPablo Neira Ayuso return -1;
1493a07327dSPablo Neira Ayuso
1503a07327dSPablo Neira Ayuso ctx->inner_nhoff = nhoff;
1513a07327dSPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_NH;
1523a07327dSPablo Neira Ayuso
1533a07327dSPablo Neira Ayuso thoff = nhoff;
1543a07327dSPablo Neira Ayuso l4proto = ipv6_find_hdr(pkt->skb, &thoff, -1, &fragoff, &fh_flags);
1553a07327dSPablo Neira Ayuso if (l4proto < 0 || thoff > U16_MAX)
1563a07327dSPablo Neira Ayuso return -1;
1573a07327dSPablo Neira Ayuso
1583a07327dSPablo Neira Ayuso if (fragoff == 0) {
1593a07327dSPablo Neira Ayuso thoff = nhoff + sizeof(_ip6h);
1603a07327dSPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH;
1613a07327dSPablo Neira Ayuso ctx->inner_thoff = thoff;
1623a07327dSPablo Neira Ayuso ctx->l4proto = l4proto;
1633a07327dSPablo Neira Ayuso }
1643a07327dSPablo Neira Ayuso }
1653a07327dSPablo Neira Ayuso break;
1663a07327dSPablo Neira Ayuso default:
1673a07327dSPablo Neira Ayuso return -1;
1683a07327dSPablo Neira Ayuso }
1693a07327dSPablo Neira Ayuso
1703a07327dSPablo Neira Ayuso return 0;
1713a07327dSPablo Neira Ayuso }
1723a07327dSPablo Neira Ayuso
nft_inner_parse_tunhdr(const struct nft_inner * priv,const struct nft_pktinfo * pkt,struct nft_inner_tun_ctx * ctx,u32 * off)1733a07327dSPablo Neira Ayuso static int nft_inner_parse_tunhdr(const struct nft_inner *priv,
1743a07327dSPablo Neira Ayuso const struct nft_pktinfo *pkt,
1753a07327dSPablo Neira Ayuso struct nft_inner_tun_ctx *ctx, u32 *off)
1763a07327dSPablo Neira Ayuso {
17791619eb6SPablo Neira Ayuso if (pkt->tprot == IPPROTO_GRE) {
17891619eb6SPablo Neira Ayuso ctx->inner_tunoff = pkt->thoff;
17991619eb6SPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_TUN;
18091619eb6SPablo Neira Ayuso return 0;
18191619eb6SPablo Neira Ayuso }
18291619eb6SPablo Neira Ayuso
18391619eb6SPablo Neira Ayuso if (pkt->tprot != IPPROTO_UDP)
1843a07327dSPablo Neira Ayuso return -1;
1853a07327dSPablo Neira Ayuso
1863a07327dSPablo Neira Ayuso ctx->inner_tunoff = *off;
1873a07327dSPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_TUN;
1883a07327dSPablo Neira Ayuso *off += priv->hdrsize;
1893a07327dSPablo Neira Ayuso
1900db14b95SPablo Neira Ayuso switch (priv->type) {
1910db14b95SPablo Neira Ayuso case NFT_INNER_GENEVE: {
1920db14b95SPablo Neira Ayuso struct genevehdr *gnvh, _gnvh;
1930db14b95SPablo Neira Ayuso
1940db14b95SPablo Neira Ayuso gnvh = skb_header_pointer(pkt->skb, pkt->inneroff,
1950db14b95SPablo Neira Ayuso sizeof(_gnvh), &_gnvh);
1960db14b95SPablo Neira Ayuso if (!gnvh)
1970db14b95SPablo Neira Ayuso return -1;
1980db14b95SPablo Neira Ayuso
1990db14b95SPablo Neira Ayuso *off += gnvh->opt_len * 4;
2000db14b95SPablo Neira Ayuso }
2010db14b95SPablo Neira Ayuso break;
2020db14b95SPablo Neira Ayuso default:
2030db14b95SPablo Neira Ayuso break;
2040db14b95SPablo Neira Ayuso }
2050db14b95SPablo Neira Ayuso
2063a07327dSPablo Neira Ayuso return 0;
2073a07327dSPablo Neira Ayuso }
2083a07327dSPablo Neira Ayuso
nft_inner_parse(const struct nft_inner * priv,struct nft_pktinfo * pkt,struct nft_inner_tun_ctx * tun_ctx)2093a07327dSPablo Neira Ayuso static int nft_inner_parse(const struct nft_inner *priv,
2100e795b37SPablo Neira Ayuso struct nft_pktinfo *pkt,
2113a07327dSPablo Neira Ayuso struct nft_inner_tun_ctx *tun_ctx)
2123a07327dSPablo Neira Ayuso {
2133a07327dSPablo Neira Ayuso struct nft_inner_tun_ctx ctx = {};
2143a07327dSPablo Neira Ayuso u32 off = pkt->inneroff;
2153a07327dSPablo Neira Ayuso
2163a07327dSPablo Neira Ayuso if (priv->flags & NFT_INNER_HDRSIZE &&
2173a07327dSPablo Neira Ayuso nft_inner_parse_tunhdr(priv, pkt, &ctx, &off) < 0)
2183a07327dSPablo Neira Ayuso return -1;
2193a07327dSPablo Neira Ayuso
2203a07327dSPablo Neira Ayuso if (priv->flags & (NFT_INNER_LL | NFT_INNER_NH)) {
2213a07327dSPablo Neira Ayuso if (nft_inner_parse_l2l3(priv, pkt, &ctx, off) < 0)
2223a07327dSPablo Neira Ayuso return -1;
2233a07327dSPablo Neira Ayuso } else if (priv->flags & NFT_INNER_TH) {
2243a07327dSPablo Neira Ayuso ctx.inner_thoff = off;
2253a07327dSPablo Neira Ayuso ctx.flags |= NFT_PAYLOAD_CTX_INNER_TH;
2263a07327dSPablo Neira Ayuso }
2273a07327dSPablo Neira Ayuso
2283a07327dSPablo Neira Ayuso *tun_ctx = ctx;
2290e795b37SPablo Neira Ayuso tun_ctx->type = priv->type;
2300e795b37SPablo Neira Ayuso pkt->flags |= NFT_PKTINFO_INNER_FULL;
2313a07327dSPablo Neira Ayuso
2323a07327dSPablo Neira Ayuso return 0;
2333a07327dSPablo Neira Ayuso }
2343a07327dSPablo Neira Ayuso
nft_inner_parse_needed(const struct nft_inner * priv,const struct nft_pktinfo * pkt,const struct nft_inner_tun_ctx * tun_ctx)2350e795b37SPablo Neira Ayuso static bool nft_inner_parse_needed(const struct nft_inner *priv,
2360e795b37SPablo Neira Ayuso const struct nft_pktinfo *pkt,
2370e795b37SPablo Neira Ayuso const struct nft_inner_tun_ctx *tun_ctx)
2380e795b37SPablo Neira Ayuso {
2390e795b37SPablo Neira Ayuso if (!(pkt->flags & NFT_PKTINFO_INNER_FULL))
2400e795b37SPablo Neira Ayuso return true;
2410e795b37SPablo Neira Ayuso
2420e795b37SPablo Neira Ayuso if (priv->type != tun_ctx->type)
2430e795b37SPablo Neira Ayuso return true;
2440e795b37SPablo Neira Ayuso
2450e795b37SPablo Neira Ayuso return false;
2460e795b37SPablo Neira Ayuso }
2470e795b37SPablo Neira Ayuso
nft_inner_eval(const struct nft_expr * expr,struct nft_regs * regs,const struct nft_pktinfo * pkt)2483a07327dSPablo Neira Ayuso static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs,
2493a07327dSPablo Neira Ayuso const struct nft_pktinfo *pkt)
2503a07327dSPablo Neira Ayuso {
2510e795b37SPablo Neira Ayuso struct nft_inner_tun_ctx *tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx);
2523a07327dSPablo Neira Ayuso const struct nft_inner *priv = nft_expr_priv(expr);
2533a07327dSPablo Neira Ayuso
2543a07327dSPablo Neira Ayuso if (nft_payload_inner_offset(pkt) < 0)
2553a07327dSPablo Neira Ayuso goto err;
2563a07327dSPablo Neira Ayuso
2570e795b37SPablo Neira Ayuso if (nft_inner_parse_needed(priv, pkt, tun_ctx) &&
2580e795b37SPablo Neira Ayuso nft_inner_parse(priv, (struct nft_pktinfo *)pkt, tun_ctx) < 0)
2593a07327dSPablo Neira Ayuso goto err;
2603a07327dSPablo Neira Ayuso
2613a07327dSPablo Neira Ayuso switch (priv->expr_type) {
2623a07327dSPablo Neira Ayuso case NFT_INNER_EXPR_PAYLOAD:
2630e795b37SPablo Neira Ayuso nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
2643a07327dSPablo Neira Ayuso break;
265a150d122SPablo Neira Ayuso case NFT_INNER_EXPR_META:
266a150d122SPablo Neira Ayuso nft_meta_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
267a150d122SPablo Neira Ayuso break;
2683a07327dSPablo Neira Ayuso default:
2693a07327dSPablo Neira Ayuso WARN_ON_ONCE(1);
2703a07327dSPablo Neira Ayuso goto err;
2713a07327dSPablo Neira Ayuso }
2723a07327dSPablo Neira Ayuso return;
2733a07327dSPablo Neira Ayuso err:
2743a07327dSPablo Neira Ayuso regs->verdict.code = NFT_BREAK;
2753a07327dSPablo Neira Ayuso }
2763a07327dSPablo Neira Ayuso
2773a07327dSPablo Neira Ayuso static const struct nla_policy nft_inner_policy[NFTA_INNER_MAX + 1] = {
2783a07327dSPablo Neira Ayuso [NFTA_INNER_NUM] = { .type = NLA_U32 },
2793a07327dSPablo Neira Ayuso [NFTA_INNER_FLAGS] = { .type = NLA_U32 },
2803a07327dSPablo Neira Ayuso [NFTA_INNER_HDRSIZE] = { .type = NLA_U32 },
2813a07327dSPablo Neira Ayuso [NFTA_INNER_TYPE] = { .type = NLA_U32 },
2823a07327dSPablo Neira Ayuso [NFTA_INNER_EXPR] = { .type = NLA_NESTED },
2833a07327dSPablo Neira Ayuso };
2843a07327dSPablo Neira Ayuso
2853a07327dSPablo Neira Ayuso struct nft_expr_info {
2863a07327dSPablo Neira Ayuso const struct nft_expr_ops *ops;
2873a07327dSPablo Neira Ayuso const struct nlattr *attr;
2883a07327dSPablo Neira Ayuso struct nlattr *tb[NFT_EXPR_MAXATTR + 1];
2893a07327dSPablo Neira Ayuso };
2903a07327dSPablo Neira Ayuso
nft_inner_init(const struct nft_ctx * ctx,const struct nft_expr * expr,const struct nlattr * const tb[])2913a07327dSPablo Neira Ayuso static int nft_inner_init(const struct nft_ctx *ctx,
2923a07327dSPablo Neira Ayuso const struct nft_expr *expr,
2933a07327dSPablo Neira Ayuso const struct nlattr * const tb[])
2943a07327dSPablo Neira Ayuso {
2953a07327dSPablo Neira Ayuso struct nft_inner *priv = nft_expr_priv(expr);
2963a07327dSPablo Neira Ayuso u32 flags, hdrsize, type, num;
2973a07327dSPablo Neira Ayuso struct nft_expr_info expr_info;
2983a07327dSPablo Neira Ayuso int err;
2993a07327dSPablo Neira Ayuso
3003a07327dSPablo Neira Ayuso if (!tb[NFTA_INNER_FLAGS] ||
301*52177bbfSXingyuan Mo !tb[NFTA_INNER_NUM] ||
3023a07327dSPablo Neira Ayuso !tb[NFTA_INNER_HDRSIZE] ||
3033a07327dSPablo Neira Ayuso !tb[NFTA_INNER_TYPE] ||
3043a07327dSPablo Neira Ayuso !tb[NFTA_INNER_EXPR])
3053a07327dSPablo Neira Ayuso return -EINVAL;
3063a07327dSPablo Neira Ayuso
3073a07327dSPablo Neira Ayuso flags = ntohl(nla_get_be32(tb[NFTA_INNER_FLAGS]));
3083a07327dSPablo Neira Ayuso if (flags & ~NFT_INNER_MASK)
3093a07327dSPablo Neira Ayuso return -EOPNOTSUPP;
3103a07327dSPablo Neira Ayuso
3113a07327dSPablo Neira Ayuso num = ntohl(nla_get_be32(tb[NFTA_INNER_NUM]));
3123a07327dSPablo Neira Ayuso if (num != 0)
3133a07327dSPablo Neira Ayuso return -EOPNOTSUPP;
3143a07327dSPablo Neira Ayuso
3153a07327dSPablo Neira Ayuso hdrsize = ntohl(nla_get_be32(tb[NFTA_INNER_HDRSIZE]));
3163a07327dSPablo Neira Ayuso type = ntohl(nla_get_be32(tb[NFTA_INNER_TYPE]));
3173a07327dSPablo Neira Ayuso
3183a07327dSPablo Neira Ayuso if (type > U8_MAX)
3193a07327dSPablo Neira Ayuso return -EINVAL;
3203a07327dSPablo Neira Ayuso
3213a07327dSPablo Neira Ayuso if (flags & NFT_INNER_HDRSIZE) {
3223a07327dSPablo Neira Ayuso if (hdrsize == 0 || hdrsize > 64)
3233a07327dSPablo Neira Ayuso return -EOPNOTSUPP;
3243a07327dSPablo Neira Ayuso }
3253a07327dSPablo Neira Ayuso
3263a07327dSPablo Neira Ayuso priv->flags = flags;
3273a07327dSPablo Neira Ayuso priv->hdrsize = hdrsize;
3283a07327dSPablo Neira Ayuso priv->type = type;
3293a07327dSPablo Neira Ayuso
3303a07327dSPablo Neira Ayuso err = nft_expr_inner_parse(ctx, tb[NFTA_INNER_EXPR], &expr_info);
3313a07327dSPablo Neira Ayuso if (err < 0)
3323a07327dSPablo Neira Ayuso return err;
3333a07327dSPablo Neira Ayuso
3343a07327dSPablo Neira Ayuso priv->expr.ops = expr_info.ops;
3353a07327dSPablo Neira Ayuso
3363a07327dSPablo Neira Ayuso if (!strcmp(expr_info.ops->type->name, "payload"))
3373a07327dSPablo Neira Ayuso priv->expr_type = NFT_INNER_EXPR_PAYLOAD;
338a150d122SPablo Neira Ayuso else if (!strcmp(expr_info.ops->type->name, "meta"))
339a150d122SPablo Neira Ayuso priv->expr_type = NFT_INNER_EXPR_META;
3403a07327dSPablo Neira Ayuso else
3413a07327dSPablo Neira Ayuso return -EINVAL;
3423a07327dSPablo Neira Ayuso
3433a07327dSPablo Neira Ayuso err = expr_info.ops->init(ctx, (struct nft_expr *)&priv->expr,
3443a07327dSPablo Neira Ayuso (const struct nlattr * const*)expr_info.tb);
3453a07327dSPablo Neira Ayuso if (err < 0)
3463a07327dSPablo Neira Ayuso return err;
3473a07327dSPablo Neira Ayuso
3483a07327dSPablo Neira Ayuso return 0;
3493a07327dSPablo Neira Ayuso }
3503a07327dSPablo Neira Ayuso
nft_inner_dump(struct sk_buff * skb,const struct nft_expr * expr,bool reset)3517d34aa3eSPhil Sutter static int nft_inner_dump(struct sk_buff *skb,
3527d34aa3eSPhil Sutter const struct nft_expr *expr, bool reset)
3533a07327dSPablo Neira Ayuso {
3543a07327dSPablo Neira Ayuso const struct nft_inner *priv = nft_expr_priv(expr);
3553a07327dSPablo Neira Ayuso
3563a07327dSPablo Neira Ayuso if (nla_put_be32(skb, NFTA_INNER_NUM, htonl(0)) ||
3573a07327dSPablo Neira Ayuso nla_put_be32(skb, NFTA_INNER_TYPE, htonl(priv->type)) ||
3583a07327dSPablo Neira Ayuso nla_put_be32(skb, NFTA_INNER_FLAGS, htonl(priv->flags)) ||
3593a07327dSPablo Neira Ayuso nla_put_be32(skb, NFTA_INNER_HDRSIZE, htonl(priv->hdrsize)))
3603a07327dSPablo Neira Ayuso goto nla_put_failure;
3613a07327dSPablo Neira Ayuso
3623a07327dSPablo Neira Ayuso if (nft_expr_dump(skb, NFTA_INNER_EXPR,
3638daa8fdeSPhil Sutter (struct nft_expr *)&priv->expr, reset) < 0)
3643a07327dSPablo Neira Ayuso goto nla_put_failure;
3653a07327dSPablo Neira Ayuso
3663a07327dSPablo Neira Ayuso return 0;
3673a07327dSPablo Neira Ayuso
3683a07327dSPablo Neira Ayuso nla_put_failure:
3693a07327dSPablo Neira Ayuso return -1;
3703a07327dSPablo Neira Ayuso }
3713a07327dSPablo Neira Ayuso
3723a07327dSPablo Neira Ayuso static const struct nft_expr_ops nft_inner_ops = {
3733a07327dSPablo Neira Ayuso .type = &nft_inner_type,
3743a07327dSPablo Neira Ayuso .size = NFT_EXPR_SIZE(sizeof(struct nft_inner)),
3753a07327dSPablo Neira Ayuso .eval = nft_inner_eval,
3763a07327dSPablo Neira Ayuso .init = nft_inner_init,
3773a07327dSPablo Neira Ayuso .dump = nft_inner_dump,
3783a07327dSPablo Neira Ayuso };
3793a07327dSPablo Neira Ayuso
3803a07327dSPablo Neira Ayuso struct nft_expr_type nft_inner_type __read_mostly = {
3813a07327dSPablo Neira Ayuso .name = "inner",
3823a07327dSPablo Neira Ayuso .ops = &nft_inner_ops,
3833a07327dSPablo Neira Ayuso .policy = nft_inner_policy,
3843a07327dSPablo Neira Ayuso .maxattr = NFTA_INNER_MAX,
3853a07327dSPablo Neira Ayuso .owner = THIS_MODULE,
3863a07327dSPablo Neira Ayuso };
387