* [nft PATCH] scanner: Accept all statements' first words in all scopes
@ 2026-05-07 20:38 Phil Sutter
2026-05-07 22:04 ` Florian Westphal
0 siblings, 1 reply; 4+ messages in thread
From: Phil Sutter @ 2026-05-07 20:38 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal
To fix for token lookahead with exclusive start conditions, we must
accept all keywords which may immediately follow the exclusive scope in
that scope as well. This affects basically the first word of every
statement which may follow a limit statement.
Add a test case to make sure things stay that way. A few quirks exist
though:
- xt statement would need special testing since having it in a rule is
supposed to fail the command
- The parser formally accepts nonsensical things like strings, numbers
and variable references on LHS, but these seem to be needed for the
data part in map elements only
Suggested-by: Florian Westphal <fw@strlen.de>
Fixes: 9d105581b5f1b ("scanner: Introduce SCANSTATE_RATE")
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
src/scanner.l | 188 +++++++++++++++++++++++++-------------------------
1 file changed, 94 insertions(+), 94 deletions(-)
diff --git a/src/scanner.l b/src/scanner.l
index 1b4eb1cf13a47..353e2ca3b3f89 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -301,7 +301,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<*>"/" { return SLASH; }
"-" { return DASH; }
"*" { return ASTERISK; }
-"@" { scanner_push_start_cond(yyscanner, SCANSTATE_AT); return AT; }
+<*>"@" { scanner_push_start_cond(yyscanner, SCANSTATE_AT); return AT; }
"$" { return '$'; }
"=" { return '='; }
"vmap" { return VMAP; }
@@ -332,42 +332,42 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"table" { return TABLE; }
"chain" { return CHAIN; }
"rule" { return RULE; }
-"set" { return SET; }
+<*>"set" { return SET; }
"element" { return ELEMENT; }
"map" { return MAP; }
"flowtable" { return FLOWTABLE; }
"handle" { return HANDLE; }
"ruleset" { return RULESET; }
-"socket" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SOCKET); return SOCKET; }
+<*>"socket" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SOCKET); return SOCKET; }
<SCANSTATE_EXPR_SOCKET>{
"transparent" { return TRANSPARENT; }
"wildcard" { return WILDCARD; }
"cgroupv2" { return CGROUPV2; }
"level" { return LEVEL; }
}
-"tproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_TPROXY); return TPROXY; }
+<*>"tproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_TPROXY); return TPROXY; }
-"accept" { return ACCEPT; }
-"drop" { return DROP; }
-"continue" { return CONTINUE; }
-"jump" { return JUMP; }
-"goto" { return GOTO; }
-"return" { return RETURN; }
-<SCANSTATE_EXPR_QUEUE,SCANSTATE_STMT_DUP,SCANSTATE_STMT_FWD,SCANSTATE_STMT_NAT,SCANSTATE_STMT_TPROXY,SCANSTATE_IP,SCANSTATE_IP6>"to" { return TO; } /* XXX: SCANSTATE_IP is a workaround */
+<*>"accept" { return ACCEPT; }
+<*>"drop" { return DROP; }
+<*>"continue" { return CONTINUE; }
+<*>"jump" { return JUMP; }
+<*>"goto" { return GOTO; }
+<*>"return" { return RETURN; }
+<SCANSTATE_EXPR_QUEUE,SCANSTATE_STMT_DUP,SCANSTATE_STMT_FWD,SCANSTATE_STMT_NAT,SCANSTATE_STMT_TPROXY,SCANSTATE_IP,SCANSTATE_IP6>"to" { return TO; } /* XXX: SCANSTATE_IP(6) is a workaround (lookahead after nf_key_proto) */
"inet" { return INET; }
"netdev" { return NETDEV; }
-"add" { return ADD; }
+<*>"add" { return ADD; }
"replace" { return REPLACE; }
-"update" { return UPDATE; }
+<*>"update" { return UPDATE; }
"create" { return CREATE; }
"insert" { return INSERT; }
-"delete" { return DELETE; }
+<*>"delete" { return DELETE; }
"get" { return GET; }
"list" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_LIST); return LIST; }
-"reset" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_RESET); return RESET; }
+<*>"reset" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_RESET); return RESET; }
"flush" { return FLUSH; }
"rename" { return RENAME; }
"import" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_IMPORT); return IMPORT; }
@@ -396,9 +396,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"memory" { return MEMORY; }
}
-"flow" { return FLOW; }
+<*>"flow" { return FLOW; }
"offload" { return OFFLOAD; }
-"meter" { return METER; }
+<*>"meter" { return METER; }
<SCANSTATE_CMD_LIST>{
"meters" { return METERS; }
@@ -418,7 +418,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<SCANSTATE_LIMIT,SCANSTATE_QUOTA,SCANSTATE_RATE>"kbytes" { return KBYTES; }
<SCANSTATE_LIMIT,SCANSTATE_QUOTA,SCANSTATE_RATE>"mbytes" { return MBYTES; }
-"last" { scanner_push_start_cond(yyscanner, SCANSTATE_LAST); return LAST; }
+<*>"last" { scanner_push_start_cond(yyscanner, SCANSTATE_LAST); return LAST; }
<SCANSTATE_LAST>{
"never" { return NEVER; }
}
@@ -429,7 +429,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"rules" { return RULES; }
}
-<*>"log" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_LOG); return LOG; }
+<*>"log" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_LOG); return LOG; }
<SCANSTATE_STMT_LOG,SCANSTATE_STMT_NAT,SCANSTATE_IP,SCANSTATE_IP6>"prefix" { return PREFIX; }
<SCANSTATE_STMT_LOG>{
"snaplen" { return SNAPLEN; }
@@ -447,13 +447,13 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"audit" { return AUDIT; }
}
-"queue" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_QUEUE); return QUEUE;}
+<*>"queue" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_QUEUE); return QUEUE;}
<SCANSTATE_EXPR_QUEUE>{
"num" { return QUEUENUM;}
"bypass" { return BYPASS;}
"fanout" { return FANOUT;}
}
-"limit" { scanner_push_start_cond(yyscanner, SCANSTATE_LIMIT); return LIMIT; }
+<*>"limit" { scanner_push_start_cond(yyscanner, SCANSTATE_LIMIT); return LIMIT; }
<SCANSTATE_LIMIT,SCANSTATE_RATE>{
"rate" { scanner_push_start_cond(yyscanner, SCANSTATE_RATE); return RATE; }
"burst" { return BURST; }
@@ -465,7 +465,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
}
<SCANSTATE_CT,SCANSTATE_LIMIT,SCANSTATE_QUOTA,SCANSTATE_RATE>"over" { return OVER; }
-"quota" { scanner_push_start_cond(yyscanner, SCANSTATE_QUOTA); return QUOTA; }
+<*>"quota" { scanner_push_start_cond(yyscanner, SCANSTATE_QUOTA); return QUOTA; }
<SCANSTATE_QUOTA,SCANSTATE_RATE>{
"until" { return UNTIL; }
}
@@ -475,16 +475,16 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<*>"hour" { return HOUR; }
<*>"day" { return DAY; }
-"reject" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_REJECT); return _REJECT; }
+<*>"reject" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_REJECT); return _REJECT; }
<SCANSTATE_STMT_REJECT>{
"with" { return WITH; }
"icmpx" { return ICMPX; }
}
-"snat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return SNAT; }
-"dnat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return DNAT; }
-"masquerade" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return MASQUERADE; }
-"redirect" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return REDIRECT; }
+<*>"snat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return SNAT; }
+<*>"dnat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return DNAT; }
+<*>"masquerade" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return MASQUERADE; }
+<*>"redirect" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return REDIRECT; }
"random" { return RANDOM; }
<SCANSTATE_STMT_NAT>{
"fully-random" { return FULLY_RANDOM; }
@@ -496,11 +496,11 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"ll" { return LL_HDR; }
"nh" { return NETWORK_HDR; }
}
-"th" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_TH); return TRANSPORT_HDR; }
+<*>"th" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_TH); return TRANSPORT_HDR; }
"bridge" { return BRIDGE; }
-"ether" { scanner_push_start_cond(yyscanner, SCANSTATE_ETH); return ETHER; }
+<*>"ether" { scanner_push_start_cond(yyscanner, SCANSTATE_ETH); return ETHER; }
<SCANSTATE_ARP,SCANSTATE_CT,SCANSTATE_ETH,SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_FIB,SCANSTATE_EXPR_IPSEC>{
"saddr" { return SADDR; }
"daddr" { return DADDR; }
@@ -508,7 +508,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"type" { scanner_push_start_cond(yyscanner, SCANSTATE_TYPE); return TYPE; }
"typeof" { return TYPEOF; }
-"vlan" { scanner_push_start_cond(yyscanner, SCANSTATE_VLAN); return VLAN; }
+<*>"vlan" { scanner_push_start_cond(yyscanner, SCANSTATE_VLAN); return VLAN; }
<SCANSTATE_CT,SCANSTATE_EXPR_FRAG,SCANSTATE_VLAN,SCANSTATE_IP,SCANSTATE_ICMP>"id" { return ID; }
<SCANSTATE_VLAN>{
"cfi" { return CFI; }
@@ -518,7 +518,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"8021ad" { yylval->string = xstrdup(yytext); return STRING; }
"8021q" { yylval->string = xstrdup(yytext); return STRING; }
-"arp" { scanner_push_start_cond(yyscanner, SCANSTATE_ARP); return ARP; }
+<*>"arp" { scanner_push_start_cond(yyscanner, SCANSTATE_ARP); return ARP; }
<SCANSTATE_ARP>{
"htype" { return HTYPE; }
"ptype" { return PTYPE; }
@@ -527,7 +527,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"operation" { return OPERATION; }
}
-"ip" { scanner_push_start_cond(yyscanner, SCANSTATE_IP); return IP; }
+<*>"ip" { scanner_push_start_cond(yyscanner, SCANSTATE_IP); return IP; }
<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_OSF,SCANSTATE_GRE>{
"version" { return HDRVERSION; }
}
@@ -606,10 +606,10 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"options" { return OPTIONS; }
"option" { return OPTION; }
}
-"time" { return TIME; }
+<*>"time" { return TIME; }
-"icmp" { scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP; }
-"icmpv6" { scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP6; }
+<*>"icmp" { scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP; }
+<*>"icmpv6" { scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP6; }
<SCANSTATE_ICMP>{
"gateway" { return GATEWAY; }
"code" { return CODE; }
@@ -623,13 +623,13 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"sequence" { return SEQUENCE; }
}
-"igmp" { scanner_push_start_cond(yyscanner, SCANSTATE_IGMP); return IGMP; }
+<*>"igmp" { scanner_push_start_cond(yyscanner, SCANSTATE_IGMP); return IGMP; }
<SCANSTATE_IGMP>{
"mrt" { return MRT; }
"group" { return GROUP; }
}
-"ip6" { scanner_push_start_cond(yyscanner, SCANSTATE_IP6); return IP6; }
+<*>"ip6" { scanner_push_start_cond(yyscanner, SCANSTATE_IP6); return IP6; }
"priority" { return PRIORITY; }
<SCANSTATE_IP6>{
"flowlabel" { return FLOWLABEL; }
@@ -639,22 +639,22 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"nexthdr" { return NEXTHDR; }
}
-"ah" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_AH); return AH; }
+<*>"ah" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_AH); return AH; }
<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_FRAG,SCANSTATE_EXPR_MH,SCANSTATE_TCP>{
"reserved" { return RESERVED; }
}
<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_ESP,SCANSTATE_EXPR_IPSEC>"spi" { return SPI; }
-"esp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_ESP); return ESP; }
+<*>"esp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_ESP); return ESP; }
-"comp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_COMP); return COMP; }
+<*>"comp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_COMP); return COMP; }
<SCANSTATE_EXPR_COMP>{
"cpi" { return CPI; }
}
"flags" { return FLAGS; }
-"udp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDP); return UDP; }
-"udplite" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDPLITE); return UDPLITE; }
+<*>"udp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDP); return UDP; }
+<*>"udplite" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDPLITE); return UDPLITE; }
<SCANSTATE_EXPR_UDPLITE>{
"csumcov" { return CSUMCOV; }
}
@@ -668,19 +668,19 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"option" { return OPTION; }
}
-"vxlan" { return VXLAN; }
+<*>"vxlan" { return VXLAN; }
"vni" { return VNI; }
-"geneve" { return GENEVE; }
+<*>"geneve" { return GENEVE; }
-"gre" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRE; }
-"gretap" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRETAP; }
+<*>"gre" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRE; }
+<*>"gretap" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRETAP; }
-"tcp" { scanner_push_start_cond(yyscanner, SCANSTATE_TCP); return TCP; }
+<*>"tcp" { scanner_push_start_cond(yyscanner, SCANSTATE_TCP); return TCP; }
-"dccp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DCCP); return DCCP; }
+<*>"dccp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DCCP); return DCCP; }
-"sctp" { scanner_push_start_cond(yyscanner, SCANSTATE_SCTP); return SCTP; }
+<*>"sctp" { scanner_push_start_cond(yyscanner, SCANSTATE_SCTP); return SCTP; }
<SCANSTATE_SCTP>{
"chunk" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SCTP_CHUNK); return CHUNK; }
@@ -724,45 +724,45 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"new-cum-tsn" { return NEW_CUM_TSN; }
}
-"rt" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT; }
-"rt0" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT0; }
-"rt2" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT2; }
-"srh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT4; }
+<*>"rt" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT; }
+<*>"rt0" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT0; }
+<*>"rt2" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT2; }
+<*>"srh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT4; }
<SCANSTATE_EXPR_RT,SCANSTATE_STMT_NAT,SCANSTATE_IP,SCANSTATE_IP6>"addr" { return ADDR; }
-"hbh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HBH); return HBH; }
+<*>"hbh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HBH); return HBH; }
-"frag" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FRAG); return FRAG; }
+<*>"frag" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FRAG); return FRAG; }
<SCANSTATE_EXPR_FRAG>{
"reserved2" { return RESERVED2; }
"more-fragments" { return MORE_FRAGMENTS; }
}
-"dst" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DST); return DST; }
-
-"mh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_MH); return MH; }
-
-"meta" { scanner_push_start_cond(yyscanner, SCANSTATE_META); return META; }
-"mark" { return MARK; }
-"iif" { return IIF; }
-"iifname" { return IIFNAME; }
-"iiftype" { return IIFTYPE; }
-"oif" { return OIF; }
-"oifname" { return OIFNAME; }
-"oiftype" { return OIFTYPE; }
-"skuid" { return SKUID; }
-"skgid" { return SKGID; }
-"nftrace" { return NFTRACE; }
-"rtclassid" { return RTCLASSID; }
-"ibriport" { return IBRIDGENAME; } /* backwards compat */
-"ibrname" { return IBRIDGENAME; }
-"obriport" { return OBRIDGENAME; } /* backwards compat */
-"obrname" { return OBRIDGENAME; }
-"pkttype" { return PKTTYPE; }
-"cpu" { return CPU; }
-"iifgroup" { return IIFGROUP; }
-"oifgroup" { return OIFGROUP; }
-"cgroup" { return CGROUP; }
+<*>"dst" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DST); return DST; }
+
+<*>"mh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_MH); return MH; }
+
+<*>"meta" { scanner_push_start_cond(yyscanner, SCANSTATE_META); return META; }
+<*>"mark" { return MARK; }
+<*>"iif" { return IIF; }
+<*>"iifname" { return IIFNAME; }
+<*>"iiftype" { return IIFTYPE; }
+<*>"oif" { return OIF; }
+<*>"oifname" { return OIFNAME; }
+<*>"oiftype" { return OIFTYPE; }
+<*>"skuid" { return SKUID; }
+<*>"skgid" { return SKGID; }
+<*>"nftrace" { return NFTRACE; }
+<*>"rtclassid" { return RTCLASSID; }
+<*>"ibriport" { return IBRIDGENAME; } /* backwards compat */
+<*>"ibrname" { return IBRIDGENAME; }
+<*>"obriport" { return OBRIDGENAME; } /* backwards compat */
+<*>"obrname" { return OBRIDGENAME; }
+<*>"pkttype" { return PKTTYPE; }
+<*>"cpu" { return CPU; }
+<*>"iifgroup" { return IIFGROUP; }
+<*>"oifgroup" { return OIFGROUP; }
+<*>"cgroup" { return CGROUP; }
<SCANSTATE_META>{
"nfproto" { return NFPROTO; }
"l4proto" { return L4PROTO; }
@@ -791,7 +791,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"classid" { return CLASSID; }
}
-"ct" { scanner_push_start_cond(yyscanner, SCANSTATE_CT); return CT; }
+<*>"ct" { scanner_push_start_cond(yyscanner, SCANSTATE_CT); return CT; }
<SCANSTATE_CT>{
"avgpkt" { return AVGPKT; }
"l3proto" { return L3PROTOCOL; }
@@ -812,13 +812,13 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"count" { return COUNT; }
}
-"numgen" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_NUMGEN); return NUMGEN; }
+<*>"numgen" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_NUMGEN); return NUMGEN; }
<SCANSTATE_EXPR_NUMGEN>{
"inc" { return INC; }
}
-"jhash" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HASH); return JHASH; }
-"symhash" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HASH); return SYMHASH; }
+<*>"jhash" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HASH); return JHASH; }
+<*>"symhash" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HASH); return SYMHASH; }
<SCANSTATE_EXPR_HASH>{
"seed" { return SEED; }
@@ -827,18 +827,18 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"mod" { return MOD; }
"offset" { return OFFSET; }
}
-"dup" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_DUP); return DUP; }
-"fwd" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_FWD); return FWD; }
+<*>"dup" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_DUP); return DUP; }
+<*>"fwd" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_FWD); return FWD; }
-"fib" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FIB); return FIB; }
+<*>"fib" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FIB); return FIB; }
<SCANSTATE_EXPR_FIB>{
"check" { return CHECK; }
}
-"osf" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_OSF); return OSF; }
+<*>"osf" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_OSF); return OSF; }
-"synproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_SYNPROXY); return SYNPROXY; }
+<*>"synproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_SYNPROXY); return SYNPROXY; }
<SCANSTATE_STMT_SYNPROXY>{
"wscale" { return WSCALE; }
"maxseg" { return MSS; }
@@ -848,7 +848,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"sack-perm" { return SACK_PERM; }
}
-"tunnel" { scanner_push_start_cond(yyscanner, SCANSTATE_TUNNEL); return TUNNEL; }
+<*>"tunnel" { scanner_push_start_cond(yyscanner, SCANSTATE_TUNNEL); return TUNNEL; }
<SCANSTATE_TUNNEL>{
"id" { return ID; }
"sport" { return SPORT; }
@@ -867,7 +867,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"data" { return DATA; }
}
-"notrack" { return NOTRACK; }
+<*>"notrack" { return NOTRACK; }
"all" { return ALL; }
@@ -880,9 +880,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"exists" { return EXISTS; }
"missing" { return MISSING; }
-"exthdr" { return EXTHDR; }
+<*>"exthdr" { return EXTHDR; }
-"ipsec" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_IPSEC); return IPSEC; }
+<*>"ipsec" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_IPSEC); return IPSEC; }
<SCANSTATE_EXPR_IPSEC>{
"reqid" { return REQID; }
"spnum" { return SPNUM; }
@@ -899,7 +899,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"secmark" { scanner_push_start_cond(yyscanner, SCANSTATE_SECMARK); return SECMARK; }
-"xt" { scanner_push_start_cond(yyscanner, SCANSTATE_XT); return XT; }
+<*>"xt" { scanner_push_start_cond(yyscanner, SCANSTATE_XT); return XT; }
{addrstring} {
yylval->string = xstrdup(yytext);
--
2.54.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [nft PATCH] scanner: Accept all statements' first words in all scopes
2026-05-07 20:38 [nft PATCH] scanner: Accept all statements' first words in all scopes Phil Sutter
@ 2026-05-07 22:04 ` Florian Westphal
2026-05-08 10:21 ` Phil Sutter
0 siblings, 1 reply; 4+ messages in thread
From: Florian Westphal @ 2026-05-07 22:04 UTC (permalink / raw)
To: Phil Sutter; +Cc: Pablo Neira Ayuso, netfilter-devel
Phil Sutter <phil@nwl.cc> wrote:
> To fix for token lookahead with exclusive start conditions, we must
> accept all keywords which may immediately follow the exclusive scope in
> that scope as well. This affects basically the first word of every
> statement which may follow a limit statement.
Hmm. Can you give examples for some of these?
> -"@" { scanner_push_start_cond(yyscanner, SCANSTATE_AT); return AT; }
> +<*>"@" { scanner_push_start_cond(yyscanner, SCANSTATE_AT); return AT; }
> +<*>"set" { return SET; }
I have a hard time figureing these two out.
> +<*>"socket" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SOCKET); return SOCKET; }
> +<*>"tproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_TPROXY); return TPROXY; }
Yes, I can see those at least theoretically.
> +<*>"delete" { return DELETE; }
> +<*>"update" { return UPDATE; }
> +<*>"add" { return ADD; }
Hmm. Care to enlighten us? Is this for a theoretical thing only?
(limit + flowtable...?)
> +<*>"reset" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_RESET); return RESET; }
?
> -"last" { scanner_push_start_cond(yyscanner, SCANSTATE_LAST); return LAST; }
> +<*>"last" { scanner_push_start_cond(yyscanner, SCANSTATE_LAST); return LAST; }
This one is also strange. Normally, after limit, one would expect a
meaningful action (verdict, log, etc. -- something that has side
effects).
> +<*>"log" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_LOG); return LOG; }
> +<*>"queue" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_QUEUE); return QUEUE;}
Makes sense.
> -"limit" { scanner_push_start_cond(yyscanner, SCANSTATE_LIMIT); return LIMIT; }
> +<*>"limit" { scanner_push_start_cond(yyscanner, SCANSTATE_LIMIT); return LIMIT; }
limit limit?
> -"quota" { scanner_push_start_cond(yyscanner, SCANSTATE_QUOTA); return QUOTA; }
> +<*>"quota" { scanner_push_start_cond(yyscanner, SCANSTATE_QUOTA); return QUOTA; }
limit + quota? Strange combination, but ok.
> +<*>"reject" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_REJECT); return _REJECT; }
Makes sense.
> -"snat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return SNAT; }
> -"dnat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return DNAT; }
> -"masquerade" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return MASQUERADE; }
> -"redirect" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return REDIRECT; }
> +<*>"snat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return SNAT; }
> +<*>"dnat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return DNAT; }
> +<*>"masquerade" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return MASQUERADE; }
> +<*>"redirect" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return REDIRECT; }
Make no sense IMO, combining limit with nat table?
Is there a use case for this or are you just being conservative to not
break some random stuff?
> +<*>"th" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_TH); return TRANSPORT_HDR; }
Yes, however, I'm not sure its worth it. Because its a strange flow.
th ... limit ... -> makes sense to me.
limit ... th ... -> not so much.
'meta mark' or 'mark', or 'ct' , yes those make sense because it would
be natual to e.g. 'mark set x' for traffic shaping for example.
[ This is not a nack, I am just curious ]
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [nft PATCH] scanner: Accept all statements' first words in all scopes
2026-05-07 22:04 ` Florian Westphal
@ 2026-05-08 10:21 ` Phil Sutter
2026-05-08 10:28 ` Phil Sutter
0 siblings, 1 reply; 4+ messages in thread
From: Phil Sutter @ 2026-05-08 10:21 UTC (permalink / raw)
To: Florian Westphal; +Cc: Pablo Neira Ayuso, netfilter-devel
On Fri, May 08, 2026 at 12:04:19AM +0200, Florian Westphal wrote:
> Phil Sutter <phil@nwl.cc> wrote:
> > To fix for token lookahead with exclusive start conditions, we must
> > accept all keywords which may immediately follow the exclusive scope in
> > that scope as well. This affects basically the first word of every
> > statement which may follow a limit statement.
>
> Hmm. Can you give examples for some of these?
Ah, perfect. I managed to forget to "git add" the test case, will send a
v2. Sorry!
>
> > -"@" { scanner_push_start_cond(yyscanner, SCANSTATE_AT); return AT; }
> > +<*>"@" { scanner_push_start_cond(yyscanner, SCANSTATE_AT); return AT; }
> > +<*>"set" { return SET; }
>
> I have a hard time figureing these two out.
The first one is for payload_raw_expr which starts with "@" token. With
set_stmt, there is an old syntax it seems ("set add ip saddr @foo").
> > +<*>"socket" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SOCKET); return SOCKET; }
> > +<*>"tproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_TPROXY); return TPROXY; }
>
> Yes, I can see those at least theoretically.
They are for socket_expr and tproxy_stmt.
> > +<*>"delete" { return DELETE; }
> > +<*>"update" { return UPDATE; }
> > +<*>"add" { return ADD; }
>
> Hmm. Care to enlighten us? Is this for a theoretical thing only?
> (limit + flowtable...?)
A set_stmt or map_stmt may follow limit_stmt, e.g.:
| limit rate 1/second update @myset { ip saddr }
Note that I did not care about semantics. I don't want to fix only those
uses of nftables *I* can imagine but all possible ones. Therefore I
parsed through parser_bison.y, identifying all tokens which may
immediately follow a limit statement. The only exceptions were those
which seemed outright impossible, like e.g. a number (accepted via
primary_expr -> integer_expr -> NUM).
> > +<*>"reset" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_RESET); return RESET; }
>
> ?
This is for optstrip_stmt.
> > -"last" { scanner_push_start_cond(yyscanner, SCANSTATE_LAST); return LAST; }
> > +<*>"last" { scanner_push_start_cond(yyscanner, SCANSTATE_LAST); return LAST; }
>
> This one is also strange. Normally, after limit, one would expect a
> meaningful action (verdict, log, etc. -- something that has side
> effects).
>
> > +<*>"log" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_LOG); return LOG; }
> > +<*>"queue" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_QUEUE); return QUEUE;}
>
> Makes sense.
>
> > -"limit" { scanner_push_start_cond(yyscanner, SCANSTATE_LIMIT); return LIMIT; }
> > +<*>"limit" { scanner_push_start_cond(yyscanner, SCANSTATE_LIMIT); return LIMIT; }
>
> limit limit?
Heh, that is indeed a bug. Aside from the questionable semantics, LIMIT
token is obviously accepted in SCANSTATE_LIMIT already.
> > -"quota" { scanner_push_start_cond(yyscanner, SCANSTATE_QUOTA); return QUOTA; }
> > +<*>"quota" { scanner_push_start_cond(yyscanner, SCANSTATE_QUOTA); return QUOTA; }
>
> limit + quota? Strange combination, but ok.
>
> > +<*>"reject" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_REJECT); return _REJECT; }
>
> Makes sense.
>
> > -"snat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return SNAT; }
> > -"dnat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return DNAT; }
> > -"masquerade" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return MASQUERADE; }
> > -"redirect" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return REDIRECT; }
> > +<*>"snat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return SNAT; }
> > +<*>"dnat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return DNAT; }
> > +<*>"masquerade" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return MASQUERADE; }
> > +<*>"redirect" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return REDIRECT; }
>
> Make no sense IMO, combining limit with nat table?
> Is there a use case for this or are you just being conservative to not
> break some random stuff?
Yes, the latter. As the famous proverb goes: For any braindead use-case,
there's at least one user who absolutely depends on it.
> > +<*>"th" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_TH); return TRANSPORT_HDR; }
>
> Yes, however, I'm not sure its worth it. Because its a strange flow.
> th ... limit ... -> makes sense to me.
>
> limit ... th ... -> not so much.
>
> 'meta mark' or 'mark', or 'ct' , yes those make sense because it would
> be natual to e.g. 'mark set x' for traffic shaping for example.
>
> [ This is not a nack, I am just curious ]
Looking at it from the opposite perspective: Are there any downsides if
we allow "too much" in SCANSTATE_LIMIT? We may not want to, but if there
will be another statement with an exclusive start condition in future,
we don't have to have this discussion about the sensibility of combining
some statements in a specific order again.
In general, there are various ways of writing nonsensical rules, e.g.
"tcp dport 22 tcp dport 23". If at all, I would not try to catch them in
the parser but at a later stage (eval phase maybe).
Cheers, Phil
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [nft PATCH] scanner: Accept all statements' first words in all scopes
2026-05-08 10:21 ` Phil Sutter
@ 2026-05-08 10:28 ` Phil Sutter
0 siblings, 0 replies; 4+ messages in thread
From: Phil Sutter @ 2026-05-08 10:28 UTC (permalink / raw)
To: Florian Westphal; +Cc: Pablo Neira Ayuso, netfilter-devel
On Fri, May 08, 2026 at 12:21:14PM +0200, Phil Sutter wrote:
> On Fri, May 08, 2026 at 12:04:19AM +0200, Florian Westphal wrote:
[...]
> > > -"limit" { scanner_push_start_cond(yyscanner, SCANSTATE_LIMIT); return LIMIT; }
> > > +<*>"limit" { scanner_push_start_cond(yyscanner, SCANSTATE_LIMIT); return LIMIT; }
> >
> > limit limit?
>
> Heh, that is indeed a bug. Aside from the questionable semantics, LIMIT
> token is obviously accepted in SCANSTATE_LIMIT already.
That is not true: The exclusive start condition is SCANSTATE_RATE, not
_LIMIT. So "limit <foo> limit <bar>" is not accepted without this, and
if my approach at ignoring semantics (and keeping the possibility for
more exclusive start conditions in mind) is OK, I'd rather not drop this
change.
Cheers, Phil
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-05-08 10:28 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-07 20:38 [nft PATCH] scanner: Accept all statements' first words in all scopes Phil Sutter
2026-05-07 22:04 ` Florian Westphal
2026-05-08 10:21 ` Phil Sutter
2026-05-08 10:28 ` Phil Sutter
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.