summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2024-03-18 13:10:55 +0100
committerPablo Neira Ayuso <pablo@netfilter.org>2024-03-20 18:50:03 +0100
commitb11b6c68e61ea294eb4c313705ccfe3e7b0eda87 (patch)
treed84b4a22c6648a2bf7d2774801db85bd56c3b345
parentea011231c06cbe828cf6056bc9c3d116e1f528d5 (diff)
netlink_delinearize: restore binop syntax when listing ruleset for flags
c3d57114f119 ("parser_bison: add shortcut syntax for matching flags without binary operations") provides a similar syntax to iptables using a prefix representation for flag matching. Restore original representation using binop when listing the ruleset. The parser still accepts the prefix notation for backward compatibility. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--src/netlink_delinearize.c65
-rw-r--r--tests/py/inet/tcp.t16
-rw-r--r--tests/py/inet/tcp.t.json42
-rw-r--r--tests/py/inet/tcp.t.json.output279
-rw-r--r--tests/py/inet/tcp.t.payload6
-rw-r--r--tests/shell/testcases/nft-f/dumps/sample-ruleset.nft4
-rwxr-xr-xtests/shell/testcases/packetpath/tcp_options16
7 files changed, 346 insertions, 82 deletions
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 1d30a78c..405a065b 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -2517,56 +2517,29 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
if (binop->op == OP_AND && (expr->op == OP_NEQ || expr->op == OP_EQ) &&
right->dtype->basetype &&
- right->dtype->basetype->type == TYPE_BITMASK) {
- switch (right->etype) {
- case EXPR_VALUE:
- if (!mpz_cmp_ui(right->value, 0)) {
- /* Flag comparison: data & flags != 0
- *
- * Split the flags into a list of flag values and convert the
- * op to OP_EQ.
- */
- expr_free(right);
-
- expr->left = expr_get(binop->left);
- expr->right = binop_tree_to_list(NULL, binop->right);
- 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->right->etype == EXPR_VALUE &&
- right->etype == EXPR_VALUE &&
- !mpz_cmp(right->value, binop->right->value)) {
- /* Skip flag / flag representation for:
- * data & flag == flag
- * data & flag != flag
- */
- ;
- } else {
- *exprp = flagcmp_expr_alloc(&expr->location, expr->op,
- expr_get(binop->left),
- binop_tree_to_list(NULL, binop->right),
- expr_get(right));
- expr_free(expr);
- }
+ right->dtype->basetype->type == TYPE_BITMASK &&
+ right->etype == EXPR_VALUE &&
+ !mpz_cmp_ui(right->value, 0)) {
+ /* Flag comparison: data & flags != 0
+ *
+ * Split the flags into a list of flag values and convert the
+ * op to OP_EQ.
+ */
+ expr_free(right);
+
+ expr->left = expr_get(binop->left);
+ expr->right = binop_tree_to_list(NULL, binop->right);
+ switch (expr->op) {
+ case OP_NEQ:
+ expr->op = OP_IMPLICIT;
break;
- case EXPR_BINOP:
- *exprp = flagcmp_expr_alloc(&expr->location, expr->op,
- expr_get(binop->left),
- binop_tree_to_list(NULL, binop->right),
- binop_tree_to_list(NULL, right));
- expr_free(expr);
+ case OP_EQ:
+ expr->op = OP_NEG;
break;
default:
- break;
+ 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 &&
expr_mask_is_prefix(binop->right)) {
diff --git a/tests/py/inet/tcp.t b/tests/py/inet/tcp.t
index f51ebd36..f4bdac17 100644
--- a/tests/py/inet/tcp.t
+++ b/tests/py/inet/tcp.t
@@ -68,8 +68,8 @@ tcp flags != { fin, urg, ecn, cwr} drop;ok
tcp flags cwr;ok
tcp flags != cwr;ok
tcp flags == syn;ok
-tcp flags fin,syn / fin,syn;ok
-tcp flags != syn / fin,syn;ok
+tcp flags fin,syn / fin,syn;ok;tcp flags & (fin | syn) == fin | syn
+tcp flags != syn / fin,syn;ok;tcp flags & (fin | syn) != syn
tcp flags & syn != 0;ok;tcp flags syn
tcp flags & syn == 0;ok;tcp flags ! syn
tcp flags & (syn | ack) != 0;ok;tcp flags syn,ack
@@ -77,12 +77,12 @@ tcp flags & (syn | ack) == 0;ok;tcp flags ! syn,ack
# it should be possible to transform this to: tcp flags syn
tcp flags & syn == syn;ok
tcp flags & syn != syn;ok
-tcp flags & (fin | syn | rst | ack) syn;ok;tcp flags syn / fin,syn,rst,ack
-tcp flags & (fin | syn | rst | ack) == syn;ok;tcp flags syn / fin,syn,rst,ack
-tcp flags & (fin | syn | rst | ack) != syn;ok;tcp flags != syn / fin,syn,rst,ack
-tcp flags & (fin | syn | rst | ack) == (syn | ack);ok;tcp flags syn,ack / fin,syn,rst,ack
-tcp flags & (fin | syn | rst | ack) != (syn | ack);ok;tcp flags != syn,ack / fin,syn,rst,ack
-tcp flags & (syn | ack) == (syn | ack);ok;tcp flags syn,ack / syn,ack
+tcp flags & (fin | syn | rst | ack) syn;ok;tcp flags & (fin | syn | rst | ack) == syn
+tcp flags & (fin | syn | rst | ack) == syn;ok
+tcp flags & (fin | syn | rst | ack) != syn;ok
+tcp flags & (fin | syn | rst | ack) == syn | ack;ok
+tcp flags & (fin | syn | rst | ack) != syn | ack;ok
+tcp flags & (syn | ack) == syn | ack;ok
tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr;ok;tcp flags == 0xff
tcp flags { syn, syn | ack };ok
tcp flags & (fin | syn | rst | psh | ack | urg) == { fin, ack, psh | ack, fin | psh | ack };ok
diff --git a/tests/py/inet/tcp.t.json b/tests/py/inet/tcp.t.json
index 8439c2b5..d3a846cf 100644
--- a/tests/py/inet/tcp.t.json
+++ b/tests/py/inet/tcp.t.json
@@ -1712,7 +1712,7 @@
}
]
-# tcp flags & (fin | syn | rst | ack) == (syn | ack)
+# tcp flags & (fin | syn | rst | ack) == syn | ack
[
{
"match": {
@@ -1741,7 +1741,7 @@
}
]
-# tcp flags & (fin | syn | rst | ack) != (syn | ack)
+# tcp flags & (syn | ack) == syn | ack
[
{
"match": {
@@ -1754,14 +1754,12 @@
}
},
[
- "fin",
"syn",
- "rst",
"ack"
]
]
},
- "op": "!=",
+ "op": "==",
"right": [
"syn",
"ack"
@@ -1770,7 +1768,7 @@
}
]
-# tcp flags & (syn | ack) == (syn | ack)
+# tcp flags & (fin | syn | rst | ack) != syn | ack
[
{
"match": {
@@ -1782,17 +1780,31 @@
"protocol": "tcp"
}
},
- [
- "syn",
- "ack"
- ]
+ {
+ "|": [
+ {
+ "|": [
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ },
+ "rst"
+ ]
+ },
+ "ack"
+ ]
+ }
]
},
- "op": "==",
- "right": [
- "syn",
- "ack"
- ]
+ "op": "!=",
+ "right": {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
}
}
]
diff --git a/tests/py/inet/tcp.t.json.output b/tests/py/inet/tcp.t.json.output
index c471e8d8..e186e127 100644
--- a/tests/py/inet/tcp.t.json.output
+++ b/tests/py/inet/tcp.t.json.output
@@ -208,3 +208,282 @@
}
}
]
+
+# tcp flags fin,syn / fin,syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ }
+ }
+ }
+]
+
+# tcp flags != syn / fin,syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ }
+ ]
+ },
+ "op": "!=",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ {
+ "|": [
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ },
+ "rst"
+ ]
+ },
+ "ack"
+ ]
+ }
+ ]
+ },
+ "op": "==",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) == syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ {
+ "|": [
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ },
+ "rst"
+ ]
+ },
+ "ack"
+ ]
+ }
+ ]
+ },
+ "op": "==",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) != syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ {
+ "|": [
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ },
+ "rst"
+ ]
+ },
+ "ack"
+ ]
+ }
+ ]
+ },
+ "op": "!=",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) == syn | ack
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ {
+ "|": [
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ },
+ "rst"
+ ]
+ },
+ "ack"
+ ]
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) != syn | ack
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ {
+ "|": [
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ },
+ "rst"
+ ]
+ },
+ "ack"
+ ]
+ }
+ ]
+ },
+ "op": "!=",
+ "right": {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
+ }
+ }
+]
+
+# tcp flags & (syn | ack) == syn | ack
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
+ }
+ }
+]
+
diff --git a/tests/py/inet/tcp.t.payload b/tests/py/inet/tcp.t.payload
index 1cfe500b..bc6bb989 100644
--- a/tests/py/inet/tcp.t.payload
+++ b/tests/py/inet/tcp.t.payload
@@ -442,7 +442,7 @@ inet test-inet input
[ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
[ cmp neq reg 1 0x00000002 ]
-# tcp flags & (fin | syn | rst | ack) == (syn | ack)
+# tcp flags & (fin | syn | rst | ack) == syn | ack
inet test-inet input
[ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x00000006 ]
@@ -450,7 +450,7 @@ inet test-inet input
[ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
[ cmp eq reg 1 0x00000012 ]
-# tcp flags & (fin | syn | rst | ack) != (syn | ack)
+# tcp flags & (fin | syn | rst | ack) != syn | ack
inet test-inet input
[ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x00000006 ]
@@ -458,7 +458,7 @@ inet test-inet input
[ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
[ cmp neq reg 1 0x00000012 ]
-# tcp flags & (syn | ack) == (syn | ack)
+# tcp flags & (syn | ack) == syn | ack
inet test-inet input
[ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x00000006 ]
diff --git a/tests/shell/testcases/nft-f/dumps/sample-ruleset.nft b/tests/shell/testcases/nft-f/dumps/sample-ruleset.nft
index 480b694a..1a9f4e7a 100644
--- a/tests/shell/testcases/nft-f/dumps/sample-ruleset.nft
+++ b/tests/shell/testcases/nft-f/dumps/sample-ruleset.nft
@@ -73,7 +73,7 @@ table inet filter {
chain ct_new_pre {
jump rpfilter
- tcp flags != syn / fin,syn,rst,ack counter packets 0 bytes 0 drop
+ tcp flags & (fin | syn | rst | ack) != syn counter packets 0 bytes 0 drop
iifname "eth0" meta nfproto vmap { ipv4 : jump blacklist_input_ipv4, ipv6 : jump blacklist_input_ipv6 }
}
@@ -131,7 +131,7 @@ table inet filter {
type filter hook forward priority mangle; policy accept;
oifname "eth0" jump {
ct state new meta nfproto vmap { ipv4 : jump blacklist_output_ipv4, ipv6 : jump blacklist_output_ipv6 }
- tcp flags syn / syn,rst tcp option maxseg size set rt mtu
+ tcp flags & (syn | rst) == syn tcp option maxseg size set rt mtu
}
}
diff --git a/tests/shell/testcases/packetpath/tcp_options b/tests/shell/testcases/packetpath/tcp_options
index 1c9ee532..88552226 100755
--- a/tests/shell/testcases/packetpath/tcp_options
+++ b/tests/shell/testcases/packetpath/tcp_options
@@ -15,14 +15,14 @@ table inet t {
chain c {
type filter hook output priority 0;
tcp dport != 22345 accept
- tcp flags syn / fin,syn,rst,ack tcp option 254 length ge 4 counter name nomatchc drop
- tcp flags syn / fin,syn,rst,ack tcp option fastopen length ge 2 reset tcp option fastopen counter name nomatchc
- tcp flags syn / fin,syn,rst,ack tcp option sack-perm missing counter name nomatchc
- tcp flags syn / fin,syn,rst,ack tcp option sack-perm exists counter name sackpermc
- tcp flags syn / fin,syn,rst,ack tcp option maxseg size gt 1400 counter name maxsegc
- tcp flags syn / fin,syn,rst,ack tcp option nop missing counter name nomatchc
- tcp flags syn / fin,syn,rst,ack tcp option nop exists counter name nopc
- tcp flags syn / fin,syn,rst,ack drop
+ tcp flags & (fin | syn | rst | ack ) == syn tcp option 254 length ge 4 counter name nomatchc drop
+ tcp flags & (fin | syn | rst | ack ) == syn tcp option fastopen length ge 2 reset tcp option fastopen counter name nomatchc
+ tcp flags & (fin | syn | rst | ack ) == syn tcp option sack-perm missing counter name nomatchc
+ tcp flags & (fin | syn | rst | ack) == syn tcp option sack-perm exists counter name sackpermc
+ tcp flags & (fin | syn | rst | ack) == syn tcp option maxseg size gt 1400 counter name maxsegc
+ tcp flags & (fin | syn | rst | ack) == syn tcp option nop missing counter name nomatchc
+ tcp flags & (fin | syn | rst | ack) == syn tcp option nop exists counter name nopc
+ tcp flags & (fin | syn | rst | ack) == syn drop
}
}
EOF