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> 153a07327dSPablo Neira Ayuso #include <net/netfilter/nf_tables_offload.h> 163a07327dSPablo Neira Ayuso #include <linux/tcp.h> 173a07327dSPablo Neira Ayuso #include <linux/udp.h> 183a07327dSPablo Neira Ayuso #include <net/gre.h> 193a07327dSPablo Neira Ayuso #include <net/ip.h> 203a07327dSPablo Neira Ayuso #include <linux/icmpv6.h> 213a07327dSPablo Neira Ayuso #include <linux/ip.h> 223a07327dSPablo Neira Ayuso #include <linux/ipv6.h> 233a07327dSPablo Neira Ayuso 24*0e795b37SPablo Neira Ayuso static DEFINE_PER_CPU(struct nft_inner_tun_ctx, nft_pcpu_tun_ctx); 25*0e795b37SPablo Neira Ayuso 263a07327dSPablo Neira Ayuso /* Same layout as nft_expr but it embeds the private expression data area. */ 273a07327dSPablo Neira Ayuso struct __nft_expr { 283a07327dSPablo Neira Ayuso const struct nft_expr_ops *ops; 293a07327dSPablo Neira Ayuso union { 303a07327dSPablo Neira Ayuso struct nft_payload payload; 313a07327dSPablo Neira Ayuso } __attribute__((aligned(__alignof__(u64)))); 323a07327dSPablo Neira Ayuso }; 333a07327dSPablo Neira Ayuso 343a07327dSPablo Neira Ayuso enum { 353a07327dSPablo Neira Ayuso NFT_INNER_EXPR_PAYLOAD, 363a07327dSPablo Neira Ayuso }; 373a07327dSPablo Neira Ayuso 383a07327dSPablo Neira Ayuso struct nft_inner { 393a07327dSPablo Neira Ayuso u8 flags; 403a07327dSPablo Neira Ayuso u8 hdrsize; 413a07327dSPablo Neira Ayuso u8 type; 423a07327dSPablo Neira Ayuso u8 expr_type; 433a07327dSPablo Neira Ayuso 443a07327dSPablo Neira Ayuso struct __nft_expr expr; 453a07327dSPablo Neira Ayuso }; 463a07327dSPablo Neira Ayuso 473a07327dSPablo Neira Ayuso static int nft_inner_parse_l2l3(const struct nft_inner *priv, 483a07327dSPablo Neira Ayuso const struct nft_pktinfo *pkt, 493a07327dSPablo Neira Ayuso struct nft_inner_tun_ctx *ctx, u32 off) 503a07327dSPablo Neira Ayuso { 513a07327dSPablo Neira Ayuso __be16 llproto, outer_llproto; 523a07327dSPablo Neira Ayuso u32 nhoff, thoff; 533a07327dSPablo Neira Ayuso 543a07327dSPablo Neira Ayuso if (priv->flags & NFT_INNER_LL) { 553a07327dSPablo Neira Ayuso struct vlan_ethhdr *veth, _veth; 563a07327dSPablo Neira Ayuso struct ethhdr *eth, _eth; 573a07327dSPablo Neira Ayuso u32 hdrsize; 583a07327dSPablo Neira Ayuso 593a07327dSPablo Neira Ayuso eth = skb_header_pointer(pkt->skb, off, sizeof(_eth), &_eth); 603a07327dSPablo Neira Ayuso if (!eth) 613a07327dSPablo Neira Ayuso return -1; 623a07327dSPablo Neira Ayuso 633a07327dSPablo Neira Ayuso switch (eth->h_proto) { 643a07327dSPablo Neira Ayuso case htons(ETH_P_IP): 653a07327dSPablo Neira Ayuso case htons(ETH_P_IPV6): 663a07327dSPablo Neira Ayuso llproto = eth->h_proto; 673a07327dSPablo Neira Ayuso hdrsize = sizeof(_eth); 683a07327dSPablo Neira Ayuso break; 693a07327dSPablo Neira Ayuso case htons(ETH_P_8021Q): 703a07327dSPablo Neira Ayuso veth = skb_header_pointer(pkt->skb, off, sizeof(_veth), &_veth); 713a07327dSPablo Neira Ayuso if (!eth) 723a07327dSPablo Neira Ayuso return -1; 733a07327dSPablo Neira Ayuso 743a07327dSPablo Neira Ayuso outer_llproto = veth->h_vlan_encapsulated_proto; 753a07327dSPablo Neira Ayuso llproto = veth->h_vlan_proto; 763a07327dSPablo Neira Ayuso hdrsize = sizeof(_veth); 773a07327dSPablo Neira Ayuso break; 783a07327dSPablo Neira Ayuso default: 793a07327dSPablo Neira Ayuso return -1; 803a07327dSPablo Neira Ayuso } 813a07327dSPablo Neira Ayuso 823a07327dSPablo Neira Ayuso ctx->inner_lloff = off; 833a07327dSPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_LL; 843a07327dSPablo Neira Ayuso off += hdrsize; 853a07327dSPablo Neira Ayuso } else { 863a07327dSPablo Neira Ayuso struct iphdr *iph; 873a07327dSPablo Neira Ayuso u32 _version; 883a07327dSPablo Neira Ayuso 893a07327dSPablo Neira Ayuso iph = skb_header_pointer(pkt->skb, off, sizeof(_version), &_version); 903a07327dSPablo Neira Ayuso if (!iph) 913a07327dSPablo Neira Ayuso return -1; 923a07327dSPablo Neira Ayuso 933a07327dSPablo Neira Ayuso switch (iph->version) { 943a07327dSPablo Neira Ayuso case 4: 953a07327dSPablo Neira Ayuso llproto = htons(ETH_P_IP); 963a07327dSPablo Neira Ayuso break; 973a07327dSPablo Neira Ayuso case 6: 983a07327dSPablo Neira Ayuso llproto = htons(ETH_P_IPV6); 993a07327dSPablo Neira Ayuso break; 1003a07327dSPablo Neira Ayuso default: 1013a07327dSPablo Neira Ayuso return -1; 1023a07327dSPablo Neira Ayuso } 1033a07327dSPablo Neira Ayuso } 1043a07327dSPablo Neira Ayuso 1053a07327dSPablo Neira Ayuso ctx->llproto = llproto; 1063a07327dSPablo Neira Ayuso if (llproto == htons(ETH_P_8021Q)) 1073a07327dSPablo Neira Ayuso llproto = outer_llproto; 1083a07327dSPablo Neira Ayuso 1093a07327dSPablo Neira Ayuso nhoff = off; 1103a07327dSPablo Neira Ayuso 1113a07327dSPablo Neira Ayuso switch (llproto) { 1123a07327dSPablo Neira Ayuso case htons(ETH_P_IP): { 1133a07327dSPablo Neira Ayuso struct iphdr *iph, _iph; 1143a07327dSPablo Neira Ayuso 1153a07327dSPablo Neira Ayuso iph = skb_header_pointer(pkt->skb, nhoff, sizeof(_iph), &_iph); 1163a07327dSPablo Neira Ayuso if (!iph) 1173a07327dSPablo Neira Ayuso return -1; 1183a07327dSPablo Neira Ayuso 1193a07327dSPablo Neira Ayuso if (iph->ihl < 5 || iph->version != 4) 1203a07327dSPablo Neira Ayuso return -1; 1213a07327dSPablo Neira Ayuso 1223a07327dSPablo Neira Ayuso ctx->inner_nhoff = nhoff; 1233a07327dSPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_NH; 1243a07327dSPablo Neira Ayuso 1253a07327dSPablo Neira Ayuso thoff = nhoff + (iph->ihl * 4); 1263a07327dSPablo Neira Ayuso if ((ntohs(iph->frag_off) & IP_OFFSET) == 0) { 1273a07327dSPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH; 1283a07327dSPablo Neira Ayuso ctx->inner_thoff = thoff; 1293a07327dSPablo Neira Ayuso ctx->l4proto = iph->protocol; 1303a07327dSPablo Neira Ayuso } 1313a07327dSPablo Neira Ayuso } 1323a07327dSPablo Neira Ayuso break; 1333a07327dSPablo Neira Ayuso case htons(ETH_P_IPV6): { 1343a07327dSPablo Neira Ayuso struct ipv6hdr *ip6h, _ip6h; 1353a07327dSPablo Neira Ayuso int fh_flags = IP6_FH_F_AUTH; 1363a07327dSPablo Neira Ayuso unsigned short fragoff; 1373a07327dSPablo Neira Ayuso int l4proto; 1383a07327dSPablo Neira Ayuso 1393a07327dSPablo Neira Ayuso ip6h = skb_header_pointer(pkt->skb, nhoff, sizeof(_ip6h), &_ip6h); 1403a07327dSPablo Neira Ayuso if (!ip6h) 1413a07327dSPablo Neira Ayuso return -1; 1423a07327dSPablo Neira Ayuso 1433a07327dSPablo Neira Ayuso if (ip6h->version != 6) 1443a07327dSPablo Neira Ayuso return -1; 1453a07327dSPablo Neira Ayuso 1463a07327dSPablo Neira Ayuso ctx->inner_nhoff = nhoff; 1473a07327dSPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_NH; 1483a07327dSPablo Neira Ayuso 1493a07327dSPablo Neira Ayuso thoff = nhoff; 1503a07327dSPablo Neira Ayuso l4proto = ipv6_find_hdr(pkt->skb, &thoff, -1, &fragoff, &fh_flags); 1513a07327dSPablo Neira Ayuso if (l4proto < 0 || thoff > U16_MAX) 1523a07327dSPablo Neira Ayuso return -1; 1533a07327dSPablo Neira Ayuso 1543a07327dSPablo Neira Ayuso if (fragoff == 0) { 1553a07327dSPablo Neira Ayuso thoff = nhoff + sizeof(_ip6h); 1563a07327dSPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH; 1573a07327dSPablo Neira Ayuso ctx->inner_thoff = thoff; 1583a07327dSPablo Neira Ayuso ctx->l4proto = l4proto; 1593a07327dSPablo Neira Ayuso } 1603a07327dSPablo Neira Ayuso } 1613a07327dSPablo Neira Ayuso break; 1623a07327dSPablo Neira Ayuso default: 1633a07327dSPablo Neira Ayuso return -1; 1643a07327dSPablo Neira Ayuso } 1653a07327dSPablo Neira Ayuso 1663a07327dSPablo Neira Ayuso return 0; 1673a07327dSPablo Neira Ayuso } 1683a07327dSPablo Neira Ayuso 1693a07327dSPablo Neira Ayuso static int nft_inner_parse_tunhdr(const struct nft_inner *priv, 1703a07327dSPablo Neira Ayuso const struct nft_pktinfo *pkt, 1713a07327dSPablo Neira Ayuso struct nft_inner_tun_ctx *ctx, u32 *off) 1723a07327dSPablo Neira Ayuso { 1733a07327dSPablo Neira Ayuso if (pkt->tprot != IPPROTO_UDP || 1743a07327dSPablo Neira Ayuso pkt->tprot != IPPROTO_GRE) 1753a07327dSPablo Neira Ayuso return -1; 1763a07327dSPablo Neira Ayuso 1773a07327dSPablo Neira Ayuso ctx->inner_tunoff = *off; 1783a07327dSPablo Neira Ayuso ctx->flags |= NFT_PAYLOAD_CTX_INNER_TUN; 1793a07327dSPablo Neira Ayuso *off += priv->hdrsize; 1803a07327dSPablo Neira Ayuso 1813a07327dSPablo Neira Ayuso return 0; 1823a07327dSPablo Neira Ayuso } 1833a07327dSPablo Neira Ayuso 1843a07327dSPablo Neira Ayuso static int nft_inner_parse(const struct nft_inner *priv, 185*0e795b37SPablo Neira Ayuso struct nft_pktinfo *pkt, 1863a07327dSPablo Neira Ayuso struct nft_inner_tun_ctx *tun_ctx) 1873a07327dSPablo Neira Ayuso { 1883a07327dSPablo Neira Ayuso struct nft_inner_tun_ctx ctx = {}; 1893a07327dSPablo Neira Ayuso u32 off = pkt->inneroff; 1903a07327dSPablo Neira Ayuso 1913a07327dSPablo Neira Ayuso if (priv->flags & NFT_INNER_HDRSIZE && 1923a07327dSPablo Neira Ayuso nft_inner_parse_tunhdr(priv, pkt, &ctx, &off) < 0) 1933a07327dSPablo Neira Ayuso return -1; 1943a07327dSPablo Neira Ayuso 1953a07327dSPablo Neira Ayuso if (priv->flags & (NFT_INNER_LL | NFT_INNER_NH)) { 1963a07327dSPablo Neira Ayuso if (nft_inner_parse_l2l3(priv, pkt, &ctx, off) < 0) 1973a07327dSPablo Neira Ayuso return -1; 1983a07327dSPablo Neira Ayuso } else if (priv->flags & NFT_INNER_TH) { 1993a07327dSPablo Neira Ayuso ctx.inner_thoff = off; 2003a07327dSPablo Neira Ayuso ctx.flags |= NFT_PAYLOAD_CTX_INNER_TH; 2013a07327dSPablo Neira Ayuso } 2023a07327dSPablo Neira Ayuso 2033a07327dSPablo Neira Ayuso *tun_ctx = ctx; 204*0e795b37SPablo Neira Ayuso tun_ctx->type = priv->type; 205*0e795b37SPablo Neira Ayuso pkt->flags |= NFT_PKTINFO_INNER_FULL; 2063a07327dSPablo Neira Ayuso 2073a07327dSPablo Neira Ayuso return 0; 2083a07327dSPablo Neira Ayuso } 2093a07327dSPablo Neira Ayuso 210*0e795b37SPablo Neira Ayuso static bool nft_inner_parse_needed(const struct nft_inner *priv, 211*0e795b37SPablo Neira Ayuso const struct nft_pktinfo *pkt, 212*0e795b37SPablo Neira Ayuso const struct nft_inner_tun_ctx *tun_ctx) 213*0e795b37SPablo Neira Ayuso { 214*0e795b37SPablo Neira Ayuso if (!(pkt->flags & NFT_PKTINFO_INNER_FULL)) 215*0e795b37SPablo Neira Ayuso return true; 216*0e795b37SPablo Neira Ayuso 217*0e795b37SPablo Neira Ayuso if (priv->type != tun_ctx->type) 218*0e795b37SPablo Neira Ayuso return true; 219*0e795b37SPablo Neira Ayuso 220*0e795b37SPablo Neira Ayuso return false; 221*0e795b37SPablo Neira Ayuso } 222*0e795b37SPablo Neira Ayuso 2233a07327dSPablo Neira Ayuso static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, 2243a07327dSPablo Neira Ayuso const struct nft_pktinfo *pkt) 2253a07327dSPablo Neira Ayuso { 226*0e795b37SPablo Neira Ayuso struct nft_inner_tun_ctx *tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx); 2273a07327dSPablo Neira Ayuso const struct nft_inner *priv = nft_expr_priv(expr); 2283a07327dSPablo Neira Ayuso 2293a07327dSPablo Neira Ayuso if (nft_payload_inner_offset(pkt) < 0) 2303a07327dSPablo Neira Ayuso goto err; 2313a07327dSPablo Neira Ayuso 232*0e795b37SPablo Neira Ayuso if (nft_inner_parse_needed(priv, pkt, tun_ctx) && 233*0e795b37SPablo Neira Ayuso nft_inner_parse(priv, (struct nft_pktinfo *)pkt, tun_ctx) < 0) 2343a07327dSPablo Neira Ayuso goto err; 2353a07327dSPablo Neira Ayuso 2363a07327dSPablo Neira Ayuso switch (priv->expr_type) { 2373a07327dSPablo Neira Ayuso case NFT_INNER_EXPR_PAYLOAD: 238*0e795b37SPablo Neira Ayuso nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx); 2393a07327dSPablo Neira Ayuso break; 2403a07327dSPablo Neira Ayuso default: 2413a07327dSPablo Neira Ayuso WARN_ON_ONCE(1); 2423a07327dSPablo Neira Ayuso goto err; 2433a07327dSPablo Neira Ayuso } 2443a07327dSPablo Neira Ayuso return; 2453a07327dSPablo Neira Ayuso err: 2463a07327dSPablo Neira Ayuso regs->verdict.code = NFT_BREAK; 2473a07327dSPablo Neira Ayuso } 2483a07327dSPablo Neira Ayuso 2493a07327dSPablo Neira Ayuso static const struct nla_policy nft_inner_policy[NFTA_INNER_MAX + 1] = { 2503a07327dSPablo Neira Ayuso [NFTA_INNER_NUM] = { .type = NLA_U32 }, 2513a07327dSPablo Neira Ayuso [NFTA_INNER_FLAGS] = { .type = NLA_U32 }, 2523a07327dSPablo Neira Ayuso [NFTA_INNER_HDRSIZE] = { .type = NLA_U32 }, 2533a07327dSPablo Neira Ayuso [NFTA_INNER_TYPE] = { .type = NLA_U32 }, 2543a07327dSPablo Neira Ayuso [NFTA_INNER_EXPR] = { .type = NLA_NESTED }, 2553a07327dSPablo Neira Ayuso }; 2563a07327dSPablo Neira Ayuso 2573a07327dSPablo Neira Ayuso struct nft_expr_info { 2583a07327dSPablo Neira Ayuso const struct nft_expr_ops *ops; 2593a07327dSPablo Neira Ayuso const struct nlattr *attr; 2603a07327dSPablo Neira Ayuso struct nlattr *tb[NFT_EXPR_MAXATTR + 1]; 2613a07327dSPablo Neira Ayuso }; 2623a07327dSPablo Neira Ayuso 2633a07327dSPablo Neira Ayuso static int nft_inner_init(const struct nft_ctx *ctx, 2643a07327dSPablo Neira Ayuso const struct nft_expr *expr, 2653a07327dSPablo Neira Ayuso const struct nlattr * const tb[]) 2663a07327dSPablo Neira Ayuso { 2673a07327dSPablo Neira Ayuso struct nft_inner *priv = nft_expr_priv(expr); 2683a07327dSPablo Neira Ayuso u32 flags, hdrsize, type, num; 2693a07327dSPablo Neira Ayuso struct nft_expr_info expr_info; 2703a07327dSPablo Neira Ayuso int err; 2713a07327dSPablo Neira Ayuso 2723a07327dSPablo Neira Ayuso if (!tb[NFTA_INNER_FLAGS] || 2733a07327dSPablo Neira Ayuso !tb[NFTA_INNER_HDRSIZE] || 2743a07327dSPablo Neira Ayuso !tb[NFTA_INNER_TYPE] || 2753a07327dSPablo Neira Ayuso !tb[NFTA_INNER_EXPR]) 2763a07327dSPablo Neira Ayuso return -EINVAL; 2773a07327dSPablo Neira Ayuso 2783a07327dSPablo Neira Ayuso flags = ntohl(nla_get_be32(tb[NFTA_INNER_FLAGS])); 2793a07327dSPablo Neira Ayuso if (flags & ~NFT_INNER_MASK) 2803a07327dSPablo Neira Ayuso return -EOPNOTSUPP; 2813a07327dSPablo Neira Ayuso 2823a07327dSPablo Neira Ayuso num = ntohl(nla_get_be32(tb[NFTA_INNER_NUM])); 2833a07327dSPablo Neira Ayuso if (num != 0) 2843a07327dSPablo Neira Ayuso return -EOPNOTSUPP; 2853a07327dSPablo Neira Ayuso 2863a07327dSPablo Neira Ayuso hdrsize = ntohl(nla_get_be32(tb[NFTA_INNER_HDRSIZE])); 2873a07327dSPablo Neira Ayuso type = ntohl(nla_get_be32(tb[NFTA_INNER_TYPE])); 2883a07327dSPablo Neira Ayuso 2893a07327dSPablo Neira Ayuso if (type > U8_MAX) 2903a07327dSPablo Neira Ayuso return -EINVAL; 2913a07327dSPablo Neira Ayuso 2923a07327dSPablo Neira Ayuso if (flags & NFT_INNER_HDRSIZE) { 2933a07327dSPablo Neira Ayuso if (hdrsize == 0 || hdrsize > 64) 2943a07327dSPablo Neira Ayuso return -EOPNOTSUPP; 2953a07327dSPablo Neira Ayuso } 2963a07327dSPablo Neira Ayuso 2973a07327dSPablo Neira Ayuso priv->flags = flags; 2983a07327dSPablo Neira Ayuso priv->hdrsize = hdrsize; 2993a07327dSPablo Neira Ayuso priv->type = type; 3003a07327dSPablo Neira Ayuso 3013a07327dSPablo Neira Ayuso err = nft_expr_inner_parse(ctx, tb[NFTA_INNER_EXPR], &expr_info); 3023a07327dSPablo Neira Ayuso if (err < 0) 3033a07327dSPablo Neira Ayuso return err; 3043a07327dSPablo Neira Ayuso 3053a07327dSPablo Neira Ayuso priv->expr.ops = expr_info.ops; 3063a07327dSPablo Neira Ayuso 3073a07327dSPablo Neira Ayuso if (!strcmp(expr_info.ops->type->name, "payload")) 3083a07327dSPablo Neira Ayuso priv->expr_type = NFT_INNER_EXPR_PAYLOAD; 3093a07327dSPablo Neira Ayuso else 3103a07327dSPablo Neira Ayuso return -EINVAL; 3113a07327dSPablo Neira Ayuso 3123a07327dSPablo Neira Ayuso err = expr_info.ops->init(ctx, (struct nft_expr *)&priv->expr, 3133a07327dSPablo Neira Ayuso (const struct nlattr * const*)expr_info.tb); 3143a07327dSPablo Neira Ayuso if (err < 0) 3153a07327dSPablo Neira Ayuso return err; 3163a07327dSPablo Neira Ayuso 3173a07327dSPablo Neira Ayuso return 0; 3183a07327dSPablo Neira Ayuso } 3193a07327dSPablo Neira Ayuso 3203a07327dSPablo Neira Ayuso static int nft_inner_dump(struct sk_buff *skb, const struct nft_expr *expr) 3213a07327dSPablo Neira Ayuso { 3223a07327dSPablo Neira Ayuso const struct nft_inner *priv = nft_expr_priv(expr); 3233a07327dSPablo Neira Ayuso 3243a07327dSPablo Neira Ayuso if (nla_put_be32(skb, NFTA_INNER_NUM, htonl(0)) || 3253a07327dSPablo Neira Ayuso nla_put_be32(skb, NFTA_INNER_TYPE, htonl(priv->type)) || 3263a07327dSPablo Neira Ayuso nla_put_be32(skb, NFTA_INNER_FLAGS, htonl(priv->flags)) || 3273a07327dSPablo Neira Ayuso nla_put_be32(skb, NFTA_INNER_HDRSIZE, htonl(priv->hdrsize))) 3283a07327dSPablo Neira Ayuso goto nla_put_failure; 3293a07327dSPablo Neira Ayuso 3303a07327dSPablo Neira Ayuso if (nft_expr_dump(skb, NFTA_INNER_EXPR, 3313a07327dSPablo Neira Ayuso (struct nft_expr *)&priv->expr) < 0) 3323a07327dSPablo Neira Ayuso goto nla_put_failure; 3333a07327dSPablo Neira Ayuso 3343a07327dSPablo Neira Ayuso return 0; 3353a07327dSPablo Neira Ayuso 3363a07327dSPablo Neira Ayuso nla_put_failure: 3373a07327dSPablo Neira Ayuso return -1; 3383a07327dSPablo Neira Ayuso } 3393a07327dSPablo Neira Ayuso 3403a07327dSPablo Neira Ayuso static const struct nft_expr_ops nft_inner_ops = { 3413a07327dSPablo Neira Ayuso .type = &nft_inner_type, 3423a07327dSPablo Neira Ayuso .size = NFT_EXPR_SIZE(sizeof(struct nft_inner)), 3433a07327dSPablo Neira Ayuso .eval = nft_inner_eval, 3443a07327dSPablo Neira Ayuso .init = nft_inner_init, 3453a07327dSPablo Neira Ayuso .dump = nft_inner_dump, 3463a07327dSPablo Neira Ayuso }; 3473a07327dSPablo Neira Ayuso 3483a07327dSPablo Neira Ayuso struct nft_expr_type nft_inner_type __read_mostly = { 3493a07327dSPablo Neira Ayuso .name = "inner", 3503a07327dSPablo Neira Ayuso .ops = &nft_inner_ops, 3513a07327dSPablo Neira Ayuso .policy = nft_inner_policy, 3523a07327dSPablo Neira Ayuso .maxattr = NFTA_INNER_MAX, 3533a07327dSPablo Neira Ayuso .owner = THIS_MODULE, 3543a07327dSPablo Neira Ayuso }; 355