]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
netlink_linearize: incorrect netlink bytecode with binary operation and flags
authorPablo Neira Ayuso <pablo@netfilter.org>
Tue, 27 Jul 2021 15:23:30 +0000 (17:23 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 27 Jul 2021 15:34:35 +0000 (17:34 +0200)
nft generates incorrect bytecode when combining flag datatype and binary
operations:

  # nft --debug=netlink add rule meh tcp_flags 'tcp flags & (fin | syn | rst | ack) syn'
ip
  [ meta load l4proto => reg 1 ]
  [ cmp eq reg 1 0x00000006 ]
  [ payload load 1b @ transport header + 13 => reg 1 ]
  [ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
  [ bitwise reg 1 = ( reg 1 & 0x00000002 ) ^ 0x00000000 ]
  [ cmp neq reg 1 0x00000000 ]

Note the double bitwise expression. The last two expressions are not
correct either since it should match on the syn flag, ie. 0x2.

After this patch, netlink bytecode generation looks correct:

 # nft --debug=netlink add rule meh tcp_flags 'tcp flags & (fin | syn | rst | ack) syn'
ip
  [ meta load l4proto => reg 1 ]
  [ cmp eq reg 1 0x00000006 ]
  [ payload load 1b @ transport header + 13 => reg 1 ]
  [ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
  [ cmp eq reg 1 0x00000002 ]

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
src/netlink_linearize.c
tests/py/inet/tcp.t
tests/py/inet/tcp.t.json
tests/py/inet/tcp.t.payload

index 9ab3ec3ef2ffc7c7908f45edd7f31e832902f4fc..eb53ccec1154cbdc1b7ce3bf0778c161c5dce7a6 100644 (file)
@@ -481,23 +481,31 @@ static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx,
        netlink_gen_raw_data(zero, expr->right->byteorder, len, &nld);
        netlink_gen_data(expr->right, &nld2);
 
-       nle = alloc_nft_expr("bitwise");
-       netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, sreg);
-       netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, sreg);
-       nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len);
-       nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld2.value, nld2.len);
-       nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &nld.value, nld.len);
-       nft_rule_add_expr(ctx, nle, &expr->location);
-
-       nle = alloc_nft_expr("cmp");
-       netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
-       if (expr->op == OP_NEG)
+       if (expr->left->etype == EXPR_BINOP) {
+               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_EQ);
-       else
-               nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_NEQ);
+               nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld2.value, nld2.len);
+               nft_rule_add_expr(ctx, nle, &expr->location);
+       } else {
+               nle = alloc_nft_expr("bitwise");
+               netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, sreg);
+               netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, sreg);
+               nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len);
+               nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld2.value, nld2.len);
+               nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &nld.value, nld.len);
+               nft_rule_add_expr(ctx, nle, &expr->location);
 
-       nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
-       nft_rule_add_expr(ctx, nle, &expr->location);
+               nle = alloc_nft_expr("cmp");
+               netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
+               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);
+       }
 
        mpz_clear(zero);
        release_register(ctx, expr->left);
index 16e15b9f76c1425b9ee6e306c039490f7a0d862c..983564ec5b751d2d3a5c59774e43cc9b1b5475ef 100644 (file)
@@ -69,6 +69,8 @@ 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 | 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 | 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
index 590a3dee5d3fa07610ab9444a46fe3c94b71348a..033a4f22e0fd0353ff787bd4c08e171fdaf440dd 100644 (file)
     }
 ]
 
+# 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"
+        }
+    }
+]
+
index 7f302080f02a6886d3757ff7588ab89d78e6c6d2..eaa7cd099bd6a6f1a56891e2527e9bb9ccbbaf92 100644 (file)
@@ -370,6 +370,22 @@ inet test-inet input
   [ bitwise reg 1 = ( reg 1 & 0x00000003 ) ^ 0x00000000 ]
   [ cmp neq reg 1 0x00000002 ]
 
+# tcp flags & (fin | syn | rst | ack) syn
+inet test-inet input
+  [ meta load l4proto => reg 1 ]
+  [ cmp eq reg 1 0x00000006 ]
+  [ payload load 1b @ transport header + 13 => reg 1 ]
+  [ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
+  [ cmp eq reg 1 0x00000002 ]
+
+# tcp flags & (fin | syn | rst | ack) != syn
+inet test-inet input
+  [ meta load l4proto => reg 1 ]
+  [ cmp eq reg 1 0x00000006 ]
+  [ payload load 1b @ transport header + 13 => reg 1 ]
+  [ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
+  [ cmp neq reg 1 0x00000002 ]
+
 # tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr
 inet test-inet input
   [ meta load l4proto => reg 1 ]