xref: /openbmc/linux/net/netfilter/nft_inner.c (revision 0e795b37)
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