Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cb7dbfd0 authored by Patrick McHardy's avatar Patrick McHardy Committed by Pablo Neira Ayuso
Browse files

netfilter: nf_tables: add optimized data comparison for small values



Add an optimized version of nft_data_cmp() that only handles values of to
4 bytes length.

This patch includes original Patrick McHardy's patch entitled (nf_tables:
inline nft_cmp_fast_eval() into main evaluation loop).

Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent ef1f7df9
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -7,6 +7,14 @@ extern void nf_tables_core_module_exit(void);
extern int nft_immediate_module_init(void);
extern void nft_immediate_module_exit(void);

struct nft_cmp_fast_expr {
	u32			data;
	enum nft_registers	sreg:8;
	u8			len;
};

extern const struct nft_expr_ops nft_cmp_fast_ops;

extern int nft_cmp_module_init(void);
extern void nft_cmp_module_exit(void);

+17 −1
Original line number Diff line number Diff line
@@ -20,6 +20,18 @@
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h>

static void nft_cmp_fast_eval(const struct nft_expr *expr,
			      struct nft_data data[NFT_REG_MAX + 1])
{
	const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
	u32 mask;

	mask = ~0U >> (sizeof(priv->data) * BITS_PER_BYTE - priv->len);
	if ((data[priv->sreg].data[0] & mask) == priv->data)
		return;
	data[NFT_REG_VERDICT].verdict = NFT_BREAK;
}

unsigned int nft_do_chain(const struct nf_hook_ops *ops,
			  struct sk_buff *skb,
			  const struct net_device *in,
@@ -48,7 +60,11 @@ unsigned int nft_do_chain(const struct nf_hook_ops *ops,
	data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;
	list_for_each_entry_continue_rcu(rule, &chain->rules, list) {
		nft_rule_for_each_expr(expr, last, rule) {
			if (expr->ops == &nft_cmp_fast_ops)
				nft_cmp_fast_eval(expr, data);
			else
				expr->ops->eval(expr, data, &pkt);

			if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE)
				break;
		}
+93 −23
Original line number Diff line number Diff line
@@ -75,32 +75,11 @@ static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
	struct nft_data_desc desc;
	int err;

	if (tb[NFTA_CMP_SREG] == NULL ||
	    tb[NFTA_CMP_OP] == NULL ||
	    tb[NFTA_CMP_DATA] == NULL)
		return -EINVAL;

	priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
	err = nft_validate_input_register(priv->sreg);
	if (err < 0)
		return err;

	priv->op = ntohl(nla_get_be32(tb[NFTA_CMP_OP]));
	switch (priv->op) {
	case NFT_CMP_EQ:
	case NFT_CMP_NEQ:
	case NFT_CMP_LT:
	case NFT_CMP_LTE:
	case NFT_CMP_GT:
	case NFT_CMP_GTE:
		break;
	default:
		return -EINVAL;
	}

	err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]);
	if (err < 0)
		return err;
	BUG_ON(err < 0);

	priv->len = desc.len;
	return 0;
@@ -133,9 +112,100 @@ static const struct nft_expr_ops nft_cmp_ops = {
	.dump		= nft_cmp_dump,
};

static int nft_cmp_fast_init(const struct nft_ctx *ctx,
			     const struct nft_expr *expr,
			     const struct nlattr * const tb[])
{
	struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
	struct nft_data_desc desc;
	struct nft_data data;
	u32 mask;
	int err;

	priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));

	err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
	BUG_ON(err < 0);
	desc.len *= BITS_PER_BYTE;

	mask = ~0U >> (sizeof(priv->data) * BITS_PER_BYTE - desc.len);
	priv->data = data.data[0] & mask;
	priv->len  = desc.len;
	return 0;
}

static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
	const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
	struct nft_data data;

	if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg)))
		goto nla_put_failure;
	if (nla_put_be32(skb, NFTA_CMP_OP, htonl(NFT_CMP_EQ)))
		goto nla_put_failure;

	data.data[0] = priv->data;
	if (nft_data_dump(skb, NFTA_CMP_DATA, &data,
			  NFT_DATA_VALUE, priv->len / BITS_PER_BYTE) < 0)
		goto nla_put_failure;
	return 0;

nla_put_failure:
	return -1;
}

const struct nft_expr_ops nft_cmp_fast_ops = {
	.type		= &nft_cmp_type,
	.size		= NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)),
	.eval		= NULL,	/* inlined */
	.init		= nft_cmp_fast_init,
	.dump		= nft_cmp_fast_dump,
};

static const struct nft_expr_ops *nft_cmp_select_ops(const struct nlattr * const tb[])
{
	struct nft_data_desc desc;
	struct nft_data data;
	enum nft_registers sreg;
	enum nft_cmp_ops op;
	int err;

	if (tb[NFTA_CMP_SREG] == NULL ||
	    tb[NFTA_CMP_OP] == NULL ||
	    tb[NFTA_CMP_DATA] == NULL)
		return ERR_PTR(-EINVAL);

	sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
	err = nft_validate_input_register(sreg);
	if (err < 0)
		return ERR_PTR(err);

	op = ntohl(nla_get_be32(tb[NFTA_CMP_OP]));
	switch (op) {
	case NFT_CMP_EQ:
	case NFT_CMP_NEQ:
	case NFT_CMP_LT:
	case NFT_CMP_LTE:
	case NFT_CMP_GT:
	case NFT_CMP_GTE:
		break;
	default:
		return ERR_PTR(-EINVAL);
	}

	err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
	if (err < 0)
		return ERR_PTR(err);

	if (desc.len <= sizeof(u32) && op == NFT_CMP_EQ)
		return &nft_cmp_fast_ops;
	else
		return &nft_cmp_ops;
}

static struct nft_expr_type nft_cmp_type __read_mostly = {
	.name		= "cmp",
	.ops		= &nft_cmp_ops,
	.select_ops	= nft_cmp_select_ops,
	.policy		= nft_cmp_policy,
	.maxattr	= NFTA_CMP_MAX,
	.owner		= THIS_MODULE,