Commit a150d122 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso
Browse files

netfilter: nft_meta: add inner match support



Add support for inner meta matching on:

- NFT_META_PROTOCOL: to match on the ethertype, this can be used
  regardless tunnel protocol provides no link layer header, in that case
  nft_inner sets on the ethertype based on the IP header version field.
- NFT_META_L4PROTO: to match on the layer 4 protocol.

These meta expression are usually autogenerated as dependencies by
userspace nftables.

Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 0e795b37
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -46,4 +46,10 @@ int nft_meta_set_validate(const struct nft_ctx *ctx,

bool nft_meta_get_reduce(struct nft_regs_track *track,
			 const struct nft_expr *expr);

struct nft_inner_tun_ctx;
void nft_meta_inner_eval(const struct nft_expr *expr,
			 struct nft_regs *regs, const struct nft_pktinfo *pkt,
			 struct nft_inner_tun_ctx *tun_ctx);

#endif
+8 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nft_meta.h>
#include <net/netfilter/nf_tables_offload.h>
#include <linux/tcp.h>
#include <linux/udp.h>
@@ -28,11 +29,13 @@ struct __nft_expr {
	const struct nft_expr_ops	*ops;
	union {
		struct nft_payload	payload;
		struct nft_meta		meta;
	} __attribute__((aligned(__alignof__(u64))));
};

enum {
	NFT_INNER_EXPR_PAYLOAD,
	NFT_INNER_EXPR_META,
};

struct nft_inner {
@@ -237,6 +240,9 @@ static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs,
	case NFT_INNER_EXPR_PAYLOAD:
		nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
		break;
	case NFT_INNER_EXPR_META:
		nft_meta_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
		break;
	default:
		WARN_ON_ONCE(1);
		goto err;
@@ -306,6 +312,8 @@ static int nft_inner_init(const struct nft_ctx *ctx,

	if (!strcmp(expr_info.ops->type->name, "payload"))
		priv->expr_type = NFT_INNER_EXPR_PAYLOAD;
	else if (!strcmp(expr_info.ops->type->name, "meta"))
		priv->expr_type = NFT_INNER_EXPR_META;
	else
		return -EINVAL;

+62 −0
Original line number Diff line number Diff line
@@ -831,9 +831,71 @@ nft_meta_select_ops(const struct nft_ctx *ctx,
	return ERR_PTR(-EINVAL);
}

static int nft_meta_inner_init(const struct nft_ctx *ctx,
			       const struct nft_expr *expr,
			       const struct nlattr * const tb[])
{
	struct nft_meta *priv = nft_expr_priv(expr);
	unsigned int len;

	priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
	switch (priv->key) {
	case NFT_META_PROTOCOL:
		len = sizeof(u16);
		break;
	case NFT_META_L4PROTO:
		len = sizeof(u32);
		break;
	default:
		return -EOPNOTSUPP;
	}
	priv->len = len;

	return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg,
					NULL, NFT_DATA_VALUE, len);
}

void nft_meta_inner_eval(const struct nft_expr *expr,
			 struct nft_regs *regs,
			 const struct nft_pktinfo *pkt,
			 struct nft_inner_tun_ctx *tun_ctx)
{
	const struct nft_meta *priv = nft_expr_priv(expr);
	u32 *dest = &regs->data[priv->dreg];

	switch (priv->key) {
	case NFT_META_PROTOCOL:
		nft_reg_store16(dest, (__force u16)tun_ctx->llproto);
		break;
	case NFT_META_L4PROTO:
		if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TH))
			goto err;

		nft_reg_store8(dest, tun_ctx->l4proto);
		break;
	default:
		WARN_ON_ONCE(1);
		goto err;
	}
	return;

err:
	regs->verdict.code = NFT_BREAK;
}
EXPORT_SYMBOL_GPL(nft_meta_inner_eval);

static const struct nft_expr_ops nft_meta_inner_ops = {
	.type		= &nft_meta_type,
	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)),
	.init		= nft_meta_inner_init,
	.dump		= nft_meta_get_dump,
	/* direct call to nft_meta_inner_eval(). */
};

struct nft_expr_type nft_meta_type __read_mostly = {
	.name		= "meta",
	.select_ops	= nft_meta_select_ops,
	.inner_ops	= &nft_meta_inner_ops,
	.policy		= nft_meta_policy,
	.maxattr	= NFTA_META_MAX,
	.owner		= THIS_MODULE,