]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add negation match on singleton bitmask value
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 1 Feb 2021 21:21:41 +0000 (22:21 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 5 Feb 2021 12:38:20 +0000 (13:38 +0100)
This patch provides a shortcut for:

ct status and dnat == 0

which allows to check for the packet whose dnat bit is unset:

  # nft add rule x y ct status ! dnat counter

This operation is only available for expression with a bitmask basetype, eg.

  # nft describe ct status
  ct expression, datatype ct_status (conntrack status) (basetype bitmask, integer), 32 bits

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/expression.h
src/evaluate.c
src/expression.c
src/netlink_delinearize.c
src/netlink_linearize.c
src/parser_bison.y
tests/py/any/ct.t
tests/py/any/ct.t.payload

index 718dac5a122d2efaed67df95f530a2b14cc16f91..2d07f3d96bebcea75c30bfd49025f7d28d3c3041 100644 (file)
@@ -93,6 +93,7 @@ enum ops {
        OP_GT,
        OP_LTE,
        OP_GTE,
+       OP_NEG,
        __OP_MAX
 };
 #define OP_MAX         (__OP_MAX - 1)
index ccee7e2102e6c7fedbe6e8a9a05ebd676d3181f6..030bbde4ab2c9cab7622e8f89c85b2db4b474222 100644 (file)
@@ -1958,6 +1958,14 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
 
                /* fall through */
        case OP_NEQ:
+       case OP_NEG:
+               if (rel->op == OP_NEG &&
+                   (right->etype != EXPR_VALUE ||
+                    right->dtype->basetype == NULL ||
+                    right->dtype->basetype->type != TYPE_BITMASK))
+                       return expr_binary_error(ctx->msgs, left, right,
+                                                "negation can only be used with singleton bitmask values");
+
                switch (right->etype) {
                case EXPR_RANGE:
                        if (byteorder_conversion(ctx, &rel->left, BYTEORDER_BIG_ENDIAN) < 0)
index 58d73e9509b04b6b1cac9baeb67d3c868877314c..a90a89ca9f740e3d2323782be12b85500aecca1d 100644 (file)
@@ -560,6 +560,7 @@ const char *expr_op_symbols[] = {
        [OP_GT]         = ">",
        [OP_LTE]        = "<=",
        [OP_GTE]        = ">=",
+       [OP_NEG]        = "!",
 };
 
 static void unary_expr_print(const struct expr *expr, struct output_ctx *octx)
index 04560b9769746f567e602e948214cd81945547b4..7cd7d403a038eaccacf26a474662b66ee72a9c0a 100644 (file)
@@ -2167,7 +2167,7 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *e
 {
        struct expr *binop = expr->left, *value = expr->right;
 
-       if (binop->op == OP_AND && expr->op == OP_NEQ &&
+       if (binop->op == OP_AND && (expr->op == OP_NEQ || expr->op == OP_EQ) &&
            value->dtype->basetype &&
            value->dtype->basetype->type == TYPE_BITMASK &&
            !mpz_cmp_ui(value->value, 0)) {
@@ -2180,8 +2180,16 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *e
 
                expr->left  = expr_get(binop->left);
                expr->right = binop_tree_to_list(NULL, binop->right);
-               expr->op    = OP_IMPLICIT;
-
+               switch (expr->op) {
+               case OP_NEQ:
+                       expr->op = OP_IMPLICIT;
+                       break;
+               case OP_EQ:
+                       expr->op = OP_NEG;
+                       break;
+               default:
+                       BUG("unknown operation type %d\n", expr->op);
+               }
                expr_free(binop);
        } else if (binop->left->dtype->flags & DTYPE_F_PREFIX &&
                   binop->op == OP_AND && expr->right->etype == EXPR_VALUE &&
index f1b3ff6940eaaa8c011d0eda990ba2de6a8d0be2..21bc492e85f43b4475fde6c9fbad69cb3556b73b 100644 (file)
@@ -490,7 +490,11 @@ static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx,
 
        nle = alloc_nft_expr("cmp");
        netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
-       nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_NEQ);
+       if (expr->op == OP_NEG)
+               nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_EQ);
+       else
+               nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_NEQ);
+
        nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
        nft_rule_add_expr(ctx, nle, &expr->location);
 
@@ -518,6 +522,7 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx,
        case OP_GT:
        case OP_LTE:
        case OP_GTE:
+       case OP_NEG:
                break;
        default:
                BUG("invalid relational operation %u\n", expr->op);
@@ -547,7 +552,7 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx,
                }
                break;
        default:
-               if (expr->op == OP_IMPLICIT &&
+               if ((expr->op == OP_IMPLICIT || expr->op == OP_NEG) &&
                    expr->right->dtype->basetype != NULL &&
                    expr->right->dtype->basetype->type == TYPE_BITMASK)
                        return netlink_gen_flagcmp(ctx, expr, dreg);
index 519e8efe5ab7e0a8cdb795767cc798d2d108a713..11e899ff2f20e04050e88bb65efd414e7ddadb19 100644 (file)
@@ -4639,6 +4639,7 @@ relational_op             :       EQ              { $$ = OP_EQ; }
                        |       GT              { $$ = OP_GT; }
                        |       GTE             { $$ = OP_GTE; }
                        |       LTE             { $$ = OP_LTE; }
+                       |       NOT             { $$ = OP_NEG; }
                        ;
 
 verdict_expr           :       ACCEPT
index 0ec027f55c2378a6d45b2c0dcca942f830680e76..a44142ac67cdb54c13272c501d38d22439511651 100644 (file)
@@ -30,6 +30,7 @@ ct status != {expected, seen-reply, assured, confirmed, dying};ok
 ct status expected,seen-reply,assured,confirmed,snat,dnat,dying;ok
 ct status snat;ok
 ct status dnat;ok
+ct status ! dnat;ok
 ct status xxx;fail
 
 ct mark 0;ok;ct mark 0x00000000
index 9223201f576d09cb832cf0380607ed71b5b434ac..a80e5a8d48ffb6feafb354f94ab149716abcc6f3 100644 (file)
@@ -502,3 +502,9 @@ ip test-ip4 output
   [ ct load unknown => reg 1 ]
   [ cmp eq reg 1 0x39300000 ]
 
+# ct status ! dnat
+ip6
+  [ ct load status => reg 1 ]
+  [ bitwise reg 1 = ( reg 1 & 0x00000020 ) ^ 0x00000000 ]
+  [ cmp eq reg 1 0x00000000 ]
+