Blob Blame History Raw
From 64b101f4a124f39b494dc5906159a8890568d1f9 Mon Sep 17 00:00:00 2001
From: Phil Sutter <phil@nwl.cc>
Date: Fri, 24 Aug 2018 13:26:57 +0200
Subject: [PATCH] parser_bison: Fix for ECN keyword in LHS of relational

Of all possible TCP flags, 'ecn' is special since it is recognized by
lex as a keyword (there is a a field in IPv4 and IPv6 headers with the
same name). Therefore it is listed in keyword_expr, but that was
sufficient for RHS only. The following statement reproduces the issue:

| tcp flags & (syn | ecn) == (syn | ecn)

The solution is to limit binop expressions to accept an RHS expression
on RHS ("real" LHS expressions don't make much sense there anyway),
which then allows keyword_expr to occur there. In order to maintain the
recursive behaviour if braces are present, allow primary_rhs_expr to
consist of a basic_rhs_expr enclosed in braces. This in turn requires
for braced RHS part in relational_expr to be dropped, otherwise bison
complains about shift/reduce conflict.

Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
(cherry picked from commit 14a9968a56f8b35138bab172aa7ce796f5d98e03)
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
 src/parser_bison.y              | 15 ++++++---------
 tests/py/inet/tcp.t             |  1 +
 tests/py/inet/tcp.t.json        | 23 +++++++++++++++++++++++
 tests/py/inet/tcp.t.json.output | 16 ++++++++++++++++
 tests/py/inet/tcp.t.payload     |  8 ++++++++
 5 files changed, 54 insertions(+), 9 deletions(-)

diff --git a/src/parser_bison.y b/src/parser_bison.y
index d75cd50fa29b9..a6b6fc1745a72 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -2948,32 +2948,32 @@ fib_tuple		:  	fib_flag	DOT	fib_tuple
 			;
 
 shift_expr		:	primary_expr
-			|	shift_expr		LSHIFT		primary_expr
+			|	shift_expr		LSHIFT		primary_rhs_expr
 			{
 				$$ = binop_expr_alloc(&@$, OP_LSHIFT, $1, $3);
 			}
-			|	shift_expr		RSHIFT		primary_expr
+			|	shift_expr		RSHIFT		primary_rhs_expr
 			{
 				$$ = binop_expr_alloc(&@$, OP_RSHIFT, $1, $3);
 			}
 			;
 
 and_expr		:	shift_expr
-			|	and_expr		AMPERSAND	shift_expr
+			|	and_expr		AMPERSAND	shift_rhs_expr
 			{
 				$$ = binop_expr_alloc(&@$, OP_AND, $1, $3);
 			}
 			;
 
 exclusive_or_expr	:	and_expr
-			|	exclusive_or_expr	CARET		and_expr
+			|	exclusive_or_expr	CARET		and_rhs_expr
 			{
 				$$ = binop_expr_alloc(&@$, OP_XOR, $1, $3);
 			}
 			;
 
 inclusive_or_expr	:	exclusive_or_expr
-			|	inclusive_or_expr	'|'		exclusive_or_expr
+			|	inclusive_or_expr	'|'		exclusive_or_rhs_expr
 			{
 				$$ = binop_expr_alloc(&@$, OP_OR, $1, $3);
 			}
@@ -3252,10 +3252,6 @@ relational_expr		:	expr	/* implicit */	rhs_expr
 			{
 				$$ = relational_expr_alloc(&@2, $2, $1, $3);
 			}
-			|	expr	relational_op	'(' rhs_expr ')'
-			{
-				$$ = relational_expr_alloc(&@2, $2, $1, $4);
-			}
 			;
 
 list_rhs_expr		:	basic_rhs_expr		COMMA		basic_rhs_expr
@@ -3439,6 +3435,7 @@ primary_rhs_expr	:	symbol_expr		{ $$ = $1; }
 							 BYTEORDER_HOST_ENDIAN,
 							 sizeof(data) * BITS_PER_BYTE, &data);
 			}
+			|	'('	basic_rhs_expr	')'	{ $$ = $2; }
 			;
 
 relational_op		:	EQ		{ $$ = OP_EQ; }
diff --git a/tests/py/inet/tcp.t b/tests/py/inet/tcp.t
index d66ba8438a32f..f96e3634f41ed 100644
--- a/tests/py/inet/tcp.t
+++ b/tests/py/inet/tcp.t
@@ -78,6 +78,7 @@ tcp flags cwr;ok
 tcp flags != cwr;ok
 tcp flags == syn;ok
 tcp flags & (syn|fin) == (syn|fin);ok;tcp flags & (fin | syn) == fin | syn
+tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr;ok;tcp flags == 0xff
 
 tcp window 22222;ok
 tcp window 22;ok
diff --git a/tests/py/inet/tcp.t.json b/tests/py/inet/tcp.t.json
index f5df72aa252a6..9ae51018e93ee 100644
--- a/tests/py/inet/tcp.t.json
+++ b/tests/py/inet/tcp.t.json
@@ -1068,6 +1068,29 @@
     }
 ]
 
+# tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr
+[
+    {
+        "match": {
+            "left": {
+                "&": [
+                    {
+                        "payload": {
+                            "field": "flags",
+                            "protocol": "tcp"
+                        }
+                    },
+                    {
+                        "|": [ "fin", { "|": [ "syn", { "|": [ "rst", { "|": [ "psh", { "|": [ "ack", { "|": [ "urg", { "|": [ "ecn", "cwr" ] } ] } ] } ] } ] } ] } ]
+                    }
+                ]
+            },
+            "op": "==",
+            "right": { "|": [ "fin", { "|": [ "syn", { "|": [ "rst", { "|": [ "psh", { "|": [ "ack", { "|": [ "urg", { "|": [ "ecn", "cwr" ] } ] } ] } ] } ] } ] } ] }
+        }
+    }
+]
+
 # tcp window 22222
 [
     {
diff --git a/tests/py/inet/tcp.t.json.output b/tests/py/inet/tcp.t.json.output
index d099d6febb122..ff58756cf596d 100644
--- a/tests/py/inet/tcp.t.json.output
+++ b/tests/py/inet/tcp.t.json.output
@@ -132,3 +132,19 @@
     }
 ]
 
+# tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr
+[
+    {
+        "match": {
+            "left": {
+                "payload": {
+                    "field": "flags",
+                    "protocol": "tcp"
+                }
+            },
+            "op": "==",
+            "right": 255
+        }
+    }
+]
+
diff --git a/tests/py/inet/tcp.t.payload b/tests/py/inet/tcp.t.payload
index 09538aed746c9..2390a24ead15c 100644
--- a/tests/py/inet/tcp.t.payload
+++ b/tests/py/inet/tcp.t.payload
@@ -436,6 +436,14 @@ inet test-inet input
   [ bitwise reg 1 = (reg=1 & 0x00000003 ) ^ 0x00000000 ]
   [ cmp eq reg 1 0x00000003 ]
 
+# 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 ]
+  [ cmp eq reg 1 0x00000006 ]
+  [ payload load 1b @ transport header + 13 => reg 1 ]
+  [ bitwise reg 1 = (reg=1 & 0x000000ff ) ^ 0x00000000 ]
+  [ cmp eq reg 1 0x000000ff ]
+
 # tcp window 22222
 inet test-inet input
   [ meta load l4proto => reg 1 ]
-- 
2.19.0