diff mbox series

[nft,v2] src: add negation match on singleton bitmask value

Message ID 20210204000006.21912-1-pablo@netfilter.org
State Accepted
Delegated to: Pablo Neira
Headers show
Series [nft,v2] src: add negation match on singleton bitmask value | expand

Commit Message

Pablo Neira Ayuso Feb. 4, 2021, midnight UTC
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 packets

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>
---
v2: add a tests/py unit.

 include/expression.h      |  1 +
 src/evaluate.c            |  8 ++++++++
 src/expression.c          |  1 +
 src/netlink_delinearize.c | 14 +++++++++++---
 src/netlink_linearize.c   |  9 +++++++--
 src/parser_bison.y        |  1 +
 tests/py/any/ct.t         |  1 +
 tests/py/any/ct.t.payload |  6 ++++++
 8 files changed, 36 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/include/expression.h b/include/expression.h
index 718dac5a122d..2d07f3d96beb 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -93,6 +93,7 @@  enum ops {
 	OP_GT,
 	OP_LTE,
 	OP_GTE,
+	OP_NEG,
 	__OP_MAX
 };
 #define OP_MAX		(__OP_MAX - 1)
diff --git a/src/evaluate.c b/src/evaluate.c
index 1d5db4dacd82..2f6c9d68f0d9 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1956,6 +1956,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)
diff --git a/src/expression.c b/src/expression.c
index 58d73e9509b0..a90a89ca9f74 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -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)
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 04560b976974..7cd7d403a038 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -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 &&
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index f1b3ff6940ea..21bc492e85f4 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -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);
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 519e8efe5ab7..11e899ff2f20 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -4639,6 +4639,7 @@  relational_op		:	EQ		{ $$ = OP_EQ; }
 			|	GT		{ $$ = OP_GT; }
 			|	GTE		{ $$ = OP_GTE; }
 			|	LTE		{ $$ = OP_LTE; }
+			|	NOT		{ $$ = OP_NEG; }
 			;
 
 verdict_expr		:	ACCEPT
diff --git a/tests/py/any/ct.t b/tests/py/any/ct.t
index cc09aebcbc44..07583fdf33f8 100644
--- a/tests/py/any/ct.t
+++ b/tests/py/any/ct.t
@@ -29,6 +29,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
diff --git a/tests/py/any/ct.t.payload b/tests/py/any/ct.t.payload
index 51a825034901..2c9648f5b825 100644
--- a/tests/py/any/ct.t.payload
+++ b/tests/py/any/ct.t.payload
@@ -511,3 +511,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 ]
+