netfilter: nf_tables: Enable fast nft_cmp for inverted matches
authorPhil Sutter <phil@nwl.cc>
Fri, 2 Oct 2020 13:50:56 +0000 (15:50 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Sun, 4 Oct 2020 19:08:32 +0000 (21:08 +0200)
Add a boolean indicating NFT_CMP_NEQ. To include it into the match
decision, it is sufficient to XOR it with the data comparison's result.

While being at it, store the mask that is calculated during expression
init and free the eval routine from having to recalculate it each time.

Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_tables_core.h
net/netfilter/nf_tables_core.c
net/netfilter/nft_cmp.c

index 78516de14d316141c290bf4cbc6662175385508b..df2d91c814cb35798ec64b2e928e417bdf32ad88 100644 (file)
@@ -25,8 +25,10 @@ void nf_tables_core_module_exit(void);
 
 struct nft_cmp_fast_expr {
        u32                     data;
+       u32                     mask;
        enum nft_registers      sreg:8;
        u8                      len;
+       bool                    inv;
 };
 
 struct nft_immediate_expr {
index 587897a2498b87dee5fa0f54be1e66bb0510c48c..e92feacaf55167b75573a2cdc8b53c0f8207c6bd 100644 (file)
@@ -51,9 +51,8 @@ static void nft_cmp_fast_eval(const struct nft_expr *expr,
                              struct nft_regs *regs)
 {
        const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
-       u32 mask = nft_cmp_fast_mask(priv->len);
 
-       if ((regs->data[priv->sreg] & mask) == priv->data)
+       if (((regs->data[priv->sreg] & priv->mask) == priv->data) ^ priv->inv)
                return;
        regs->verdict.code = NFT_BREAK;
 }
index 16f4d84599ac79f9db5703ca6a9a7a18f73f1ffc..bc079d68a5365f5aaeaf276d7533eab33916d14b 100644 (file)
@@ -167,7 +167,6 @@ static int nft_cmp_fast_init(const struct nft_ctx *ctx,
        struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
        struct nft_data_desc desc;
        struct nft_data data;
-       u32 mask;
        int err;
 
        err = nft_data_init(NULL, &data, sizeof(data), &desc,
@@ -181,10 +180,11 @@ static int nft_cmp_fast_init(const struct nft_ctx *ctx,
                return err;
 
        desc.len *= BITS_PER_BYTE;
-       mask = nft_cmp_fast_mask(desc.len);
 
-       priv->data = data.data[0] & mask;
+       priv->mask = nft_cmp_fast_mask(desc.len);
+       priv->data = data.data[0] & priv->mask;
        priv->len  = desc.len;
+       priv->inv  = ntohl(nla_get_be32(tb[NFTA_CMP_OP])) != NFT_CMP_EQ;
        return 0;
 }
 
@@ -201,7 +201,7 @@ static int nft_cmp_fast_offload(struct nft_offload_ctx *ctx,
                },
                .sreg   = priv->sreg,
                .len    = priv->len / BITS_PER_BYTE,
-               .op     = NFT_CMP_EQ,
+               .op     = priv->inv ? NFT_CMP_NEQ : NFT_CMP_EQ,
        };
 
        return __nft_cmp_offload(ctx, flow, &cmp);
@@ -210,11 +210,12 @@ static int nft_cmp_fast_offload(struct nft_offload_ctx *ctx,
 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);
+       enum nft_cmp_ops op = priv->inv ? NFT_CMP_NEQ : NFT_CMP_EQ;
        struct nft_data data;
 
        if (nft_dump_register(skb, NFTA_CMP_SREG, priv->sreg))
                goto nla_put_failure;
-       if (nla_put_be32(skb, NFTA_CMP_OP, htonl(NFT_CMP_EQ)))
+       if (nla_put_be32(skb, NFTA_CMP_OP, htonl(op)))
                goto nla_put_failure;
 
        data.data[0] = priv->data;
@@ -272,7 +273,7 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
                goto err1;
        }
 
-       if (desc.len <= sizeof(u32) && op == NFT_CMP_EQ)
+       if (desc.len <= sizeof(u32) && (op == NFT_CMP_EQ || op == NFT_CMP_NEQ))
                return &nft_cmp_fast_ops;
 
        return &nft_cmp_ops;