netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Florian Westphal <fw@strlen.de>
To: <netfilter-devel@vger.kernel.org>
Cc: Florian Westphal <fw@strlen.de>
Subject: [PATCH nft 5/6] scanner: support arbitrary chain names
Date: Wed, 17 Mar 2021 00:40:38 +0100	[thread overview]
Message-ID: <20210316234039.15677-6-fw@strlen.de> (raw)
In-Reply-To: <20210316234039.15677-1-fw@strlen.de>

Previous change removed constraints on table naming, this extends it
to handle chain names as well.

The new exclusive scope also needs to permit '{' to handle bounded
chains, e.g.

 .... jump { ip ...

and "handle", to deal with: 'delete chain test-ip handle 2' -- in both
cases flex needs to leave the chain scope immediately.

The parser_state struct is used to record if we saw a family or table
name:

chain tname cname
chain ip tname cname
chain cname {
chain tname handle 1
chain ip tname handle 1

... are all valid.  We can't exit after the second name, first name
could have been the family name, not the table.

This change allows nft to parse:
  table ip dup {
        chain fwd {
        }
 }
 table inet dup {
        chain fwd {
        }
 }

The point here is not to allow aribitrary names (for instance
'table ip handle' won't work), its to make sure a new/future expression
keyword won't break an existing ruleset where the 'new keyword' happened
to be the name of a chain/table.

Remaining issue:

   |       RULE            rule_position   rule
   {
       $$ = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &$2, &@$, $3);
   }
   |       /* empty */     rule_position   rule

so, implicit 'add rule', e.g.  "nft ip tname cname ip saddr ..."
will not work when either tname or cname would be added as
token/expression in a future release.

This can't be fully resolved, there is no context (from scanner) to know that
'ip tname cname' need to be parsed as 'IP STRING STRING'.

With the long form (nft add rule ip tname cname ...), the RULE token
allows to switch scanner start condition.

Switching state from bison doesn't work reliably as the switch rule
will be evaluated too late, e.g. 'nft add dup ...'.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/parser.h   |  3 ++
 src/parser_bison.y |  5 +--
 src/scanner.l      | 81 ++++++++++++++++++++++++++++++++++++++--------
 3 files changed, 74 insertions(+), 15 deletions(-)

diff --git a/include/parser.h b/include/parser.h
index 0843aa1adb6a..d6cf20729421 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -26,6 +26,8 @@ struct parser_state {
 	unsigned int			flex_state_pop;
 	unsigned int			startcond_type;
 	unsigned int			saw_family:1;
+	unsigned int			saw_table:1;
+	unsigned int			saw_chain:1;
 	struct list_head		*cmds;
 };
 
@@ -52,6 +54,7 @@ enum startcond_type {
 	PARSER_SC_STMT_LOG,
 
 	PARSER_SC_STRING_TABLE,
+	PARSER_SC_STRING_CHAIN,
 };
 
 struct mnl_socket;
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 98fe4431c4f4..bbac85fd35ce 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -864,6 +864,7 @@ opt_newline		:	NEWLINE
 close_scope_arp		: { scanner_pop_start_cond(nft->scanner, PARSER_SC_ARP); };
 close_scope_ct		: { scanner_pop_start_cond(nft->scanner, PARSER_SC_CT); };
 close_scope_counter	: { scanner_pop_start_cond(nft->scanner, PARSER_SC_COUNTER); };
+close_scope_chain	: { scanner_pop_start_cond(nft->scanner, PARSER_SC_STRING_CHAIN); }
 close_scope_eth		: { scanner_pop_start_cond(nft->scanner, PARSER_SC_ETH); };
 close_scope_fib		: { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FIB); };
 close_scope_hash	: { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HASH); };
@@ -2462,7 +2463,7 @@ tableid_spec 		: 	family_spec 	HANDLE NUM
 			}
 			;
 
-chain_spec		:	table_spec	identifier
+chain_spec		:	table_spec	identifier	close_scope_chain
 			{
 				$$		= $1;
 				$$.chain.name	= $2;
@@ -2470,7 +2471,7 @@ chain_spec		:	table_spec	identifier
 			}
 			;
 
-chainid_spec 		: 	table_spec 	HANDLE NUM
+chainid_spec 		: 	table_spec 	HANDLE NUM	close_scope_chain
 			{
 				$$ 			= $1;
 				$$.handle.location 	= @3;
diff --git a/src/scanner.l b/src/scanner.l
index bf6f290db3db..a156accaa944 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -99,6 +99,7 @@ static void reset_pos(struct parser_state *state, struct location *loc)
 }
 
 static int scanner_handle_tablename(void *scanner, const char *token);
+static int scanner_handle_chainname(void *scanner, const char *token);
 
 static void scanner_push_start_cond(void *scanner, enum startcond_type type);
 
@@ -219,6 +220,8 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 %s SCANSTATE_STMT_LOG
 
 %x SCANSTATE_STRING_TABLE
+%x SCANSTATE_STRING_CHAIN
+%x SCANSTATE_STRING_JUMP
 %%
 
 "=="			{ return EQ; }
@@ -277,9 +280,9 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 "devices"		{ return DEVICES; }
 "table"			{ scanner_push_start_cond(yyscanner, SCANSTATE_STRING_TABLE); return TABLE; }
 "tables"		{ return TABLES; }
-"chain"			{ return CHAIN; }
+"chain"			{ scanner_push_start_cond(yyscanner, SCANSTATE_STRING_CHAIN); return CHAIN; }
 "chains"		{ return CHAINS; }
-"rule"			{ return RULE; }
+"rule"			{ scanner_push_start_cond(yyscanner, SCANSTATE_STRING_CHAIN); return RULE; }
 "rules"			{ return RULES; }
 "sets"			{ return SETS; }
 "set"			{ return SET; }
@@ -301,8 +304,8 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 "accept"		{ return ACCEPT; }
 "drop"			{ return DROP; }
 "continue"		{ return CONTINUE; }
-"jump"			{ return JUMP; }
-"goto"			{ return GOTO; }
+"jump"			{ scanner_push_start_cond(yyscanner, SCANSTATE_STRING_JUMP); return JUMP; }
+"goto"			{ scanner_push_start_cond(yyscanner, SCANSTATE_STRING_JUMP); return GOTO; }
 "return"		{ return RETURN; }
 "to"			{ return TO; }
 
@@ -721,11 +724,44 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 				if (token != STRING)
 					return token;
 
+				scanner_pop_start_cond(yyscanner, SCANSTATE_STRING_TABLE);
 				yylval->string = xstrdup(yytext);
 				return STRING;
 			}
 
-<SCANSTATE_STRING_TABLE>{
+<SCANSTATE_STRING_CHAIN>{
+	"handle"	{
+			scanner_pop_start_cond(yyscanner, SCANSTATE_STRING_CHAIN);
+			return HANDLE;
+	}
+	{string}	{
+				int token = scanner_handle_chainname(yyscanner, yytext);
+
+				if (token != STRING)
+					return token;
+
+				yylval->string = xstrdup(yytext);
+				return STRING;
+	}
+	"{"		{
+			scanner_pop_start_cond(yyscanner, SCANSTATE_STRING_CHAIN);
+			return '{';
+	}
+}
+
+<SCANSTATE_STRING_JUMP>{string}	{
+				yylval->string = xstrdup(yytext);
+				scanner_pop_start_cond(yyscanner, SCANSTATE_STRING_JUMP);
+				return STRING;
+			}
+<SCANSTATE_STRING_JUMP>"{"		{
+				/* chain binding, e.g. jump { ... */
+				scanner_pop_start_cond(yyscanner, SCANSTATE_STRING_JUMP);
+				return '{';
+			}
+
+
+<SCANSTATE_STRING_TABLE,SCANSTATE_STRING_CHAIN,SCANSTATE_STRING_JUMP>{
 \\{newline}		{
 				reset_pos(yyget_extra(yyscanner), yylloc);
 			}
@@ -1057,16 +1093,27 @@ static void scanner_push_start_cond(void *scanner, enum startcond_type type)
 
 	state->startcond_type = type;
 	yy_push_state((int)type, scanner);
+
+	if (type == SCANSTATE_STRING_CHAIN)
+		state->saw_chain = 1;
 }
 
 void scanner_pop_start_cond(void *scanner, enum startcond_type t)
 {
 	struct parser_state *state = yyget_extra(scanner);
 
-	if (state->startcond_type != t) {
-		if (state->startcond_type == SCANSTATE_STRING_TABLE)
+	if (t == SCANSTATE_STRING_CHAIN) {
+		if (!state->saw_chain) /* implicit chain scope: never pushed the state */
 			return;
 
+		if (state->startcond_type == t)
+			state->saw_chain = 0;
+	}
+
+	if (state->startcond_type != t) {
+		if (state->startcond_type == SCANSTATE_STRING_TABLE ||
+		    state->startcond_type == SCANSTATE_STRING_CHAIN)
+			return;
 		state->flex_state_pop++;
 		return; /* Can't pop just yet! */
 	}
@@ -1087,12 +1134,8 @@ static int scanner_handle_tablename(void *scanner, const char *token)
 	struct parser_state *state = yyget_extra(scanner);
 	int ret = STRING;
 
-	if (state->startcond_type != SCANSTATE_STRING_TABLE)
-		return STRING;
-
 	if (state->saw_family) {
 		state->saw_family = 0;
-		scanner_pop_start_cond(scanner, SCANSTATE_STRING_TABLE);
 		return STRING;
 	}
 
@@ -1112,8 +1155,20 @@ static int scanner_handle_tablename(void *scanner, const char *token)
 
 	if (ret != STRING)
 		state->saw_family = 1;
-	else
-		scanner_pop_start_cond(scanner, SCANSTATE_STRING_TABLE);
+
+	return ret;
+}
+
+static int scanner_handle_chainname(void *scanner, const char *token)
+{
+	struct parser_state *state = yyget_extra(scanner);
+	bool saw_table = state->saw_table;
+	int ret;
+
+	ret = scanner_handle_tablename(scanner, token);
+
+	if (ret == STRING && saw_table)
+		scanner_pop_start_cond(scanner, SCANSTATE_STRING_CHAIN);
 
 	return ret;
 }
-- 
2.26.2


  parent reply	other threads:[~2021-03-16 23:41 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-16 23:40 [PATCH nft 0/6] arbirary table/chain names Florian Westphal
2021-03-16 23:40 ` [PATCH nft 1/6] scanner: add support for scope nesting Florian Westphal
2021-03-16 23:40 ` [PATCH nft 2/6] scanner: counter: move to own scope Florian Westphal
2021-03-16 23:40 ` [PATCH nft 3/6] scanner: log: " Florian Westphal
2021-03-16 23:40 ` [PATCH nft 4/6] scanner: support arbitary table names Florian Westphal
2021-03-16 23:40 ` Florian Westphal [this message]
2021-03-16 23:40 ` [PATCH nft 6/6] src: allow arbitary chain name in implicit rule add case Florian Westphal
2021-03-18 12:00   ` Phil Sutter
2021-03-18 12:37     ` Florian Westphal
2021-03-18 13:51       ` Phil Sutter
2021-03-18 13:20   ` Florian Westphal
2021-03-24 10:58     ` Florian Westphal

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210316234039.15677-6-fw@strlen.de \
    --to=fw@strlen.de \
    --cc=netfilter-devel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).