summaryrefslogtreecommitdiffstats
path: root/src/parser_bison.y
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:08:37 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:08:37 +0000
commit971e619d8602fa52b1bfcb3ea65b7ab96be85318 (patch)
tree26feb2498c72b796e07b86349d17f544046de279 /src/parser_bison.y
parentInitial commit. (diff)
downloadnftables-971e619d8602fa52b1bfcb3ea65b7ab96be85318.tar.xz
nftables-971e619d8602fa52b1bfcb3ea65b7ab96be85318.zip
Adding upstream version 1.0.9.upstream/1.0.9upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/parser_bison.y')
-rw-r--r--src/parser_bison.y6290
1 files changed, 6290 insertions, 0 deletions
diff --git a/src/parser_bison.y b/src/parser_bison.y
new file mode 100644
index 0000000..c517dc3
--- /dev/null
+++ b/src/parser_bison.y
@@ -0,0 +1,6290 @@
+/*
+ * Copyright (c) 2007-2012 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+%{
+#include <nft.h>
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <syslog.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/if_ether.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_conntrack_tuple_common.h>
+#include <linux/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_log.h>
+#include <linux/netfilter/nfnetlink_osf.h>
+#include <linux/netfilter/nf_synproxy.h>
+#include <linux/xfrm.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+#include <libnftnl/common.h>
+#include <libnftnl/set.h>
+#include <libnftnl/udata.h>
+
+#include <rule.h>
+#include <statement.h>
+#include <expression.h>
+#include <headers.h>
+#include <utils.h>
+#include <parser.h>
+#include <erec.h>
+#include <sctp_chunk.h>
+
+#include "parser_bison.h"
+
+void parser_init(struct nft_ctx *nft, struct parser_state *state,
+ struct list_head *msgs, struct list_head *cmds,
+ struct scope *top_scope)
+{
+ memset(state, 0, sizeof(*state));
+ state->msgs = msgs;
+ state->cmds = cmds;
+ state->scopes[0] = scope_init(top_scope, NULL);
+ init_list_head(&state->indesc_list);
+}
+
+static void yyerror(struct location *loc, struct nft_ctx *nft, void *scanner,
+ struct parser_state *state, const char *s)
+{
+ erec_queue(error(loc, "%s", s), state->msgs);
+}
+
+static struct scope *current_scope(const struct parser_state *state)
+{
+ return state->scopes[state->scope];
+}
+
+static int open_scope(struct parser_state *state, struct scope *scope)
+{
+ if (state->scope >= array_size(state->scopes) - 1) {
+ state->scope_err = true;
+ return -1;
+ }
+
+ scope_init(scope, current_scope(state));
+ state->scopes[++state->scope] = scope;
+
+ return 0;
+}
+
+static void close_scope(struct parser_state *state)
+{
+ if (state->scope_err || state->scope == 0) {
+ state->scope_err = false;
+ return;
+ }
+
+ state->scope--;
+}
+
+static void location_init(void *scanner, struct parser_state *state,
+ struct location *loc)
+{
+ memset(loc, 0, sizeof(*loc));
+ loc->indesc = state->indesc;
+}
+
+static void location_update(struct location *loc, struct location *rhs, int n)
+{
+ if (n) {
+ loc->indesc = rhs[n].indesc;
+ loc->token_offset = rhs[1].token_offset;
+ loc->line_offset = rhs[1].line_offset;
+ loc->first_line = rhs[1].first_line;
+ loc->first_column = rhs[1].first_column;
+ loc->last_line = rhs[n].last_line;
+ loc->last_column = rhs[n].last_column;
+ } else {
+ loc->indesc = rhs[0].indesc;
+ loc->token_offset = rhs[0].token_offset;
+ loc->line_offset = rhs[0].line_offset;
+ loc->first_line = loc->last_line = rhs[0].last_line;
+ loc->first_column = loc->last_column = rhs[0].last_column;
+ }
+}
+
+static struct expr *handle_concat_expr(const struct location *loc,
+ struct expr *expr,
+ struct expr *expr_l, struct expr *expr_r,
+ struct location loc_rhs[3])
+{
+ if (expr->etype != EXPR_CONCAT) {
+ expr = concat_expr_alloc(loc);
+ compound_expr_add(expr, expr_l);
+ } else {
+ location_update(&expr_r->location, loc_rhs, 2);
+
+ expr = expr_l;
+ expr->location = *loc;
+ }
+
+ compound_expr_add(expr, expr_r);
+ return expr;
+}
+
+static bool already_set(const void *attr, const struct location *loc,
+ struct parser_state *state)
+{
+ if (!attr)
+ return false;
+
+ erec_queue(error(loc, "You can only specify this once. This statement is duplicated."),
+ state->msgs);
+ return true;
+}
+
+static struct expr *ifname_expr_alloc(const struct location *location,
+ struct list_head *queue,
+ const char *name)
+{
+ unsigned int length = strlen(name);
+ struct expr *expr;
+
+ if (length == 0) {
+ xfree(name);
+ erec_queue(error(location, "empty interface name"), queue);
+ return NULL;
+ }
+
+ if (length > 16) {
+ xfree(name);
+ erec_queue(error(location, "interface name too long"), queue);
+ return NULL;
+ }
+
+ expr = constant_expr_alloc(location, &ifname_type, BYTEORDER_HOST_ENDIAN,
+ length * BITS_PER_BYTE, name);
+
+ xfree(name);
+
+ return expr;
+}
+
+#define YYLLOC_DEFAULT(Current, Rhs, N) location_update(&Current, Rhs, N)
+
+#define symbol_value(loc, str) \
+ symbol_expr_alloc(loc, SYMBOL_VALUE, current_scope(state), str)
+
+/* Declare those here to avoid compiler warnings */
+void nft_set_debug(int, void *);
+int nft_lex(void *, void *, void *);
+%}
+
+/* Declaration section */
+
+%name-prefix "nft_"
+%debug
+%define api.pure
+%parse-param { struct nft_ctx *nft }
+%parse-param { void *scanner }
+%parse-param { struct parser_state *state }
+%lex-param { scanner }
+%define parse.error verbose
+%locations
+
+%initial-action {
+ location_init(scanner, state, &yylloc);
+ if (nft->debug_mask & NFT_DEBUG_SCANNER)
+ nft_set_debug(1, scanner);
+ if (nft->debug_mask & NFT_DEBUG_PARSER)
+ yydebug = 1;
+}
+
+%union {
+ uint64_t val;
+ uint32_t val32;
+ uint8_t val8;
+ const char * string;
+
+ struct list_head *list;
+ struct cmd *cmd;
+ struct handle handle;
+ struct table *table;
+ struct chain *chain;
+ struct rule *rule;
+ struct stmt *stmt;
+ struct expr *expr;
+ struct set *set;
+ struct obj *obj;
+ struct flowtable *flowtable;
+ struct ct *ct;
+ const struct datatype *datatype;
+ struct handle_spec handle_spec;
+ struct position_spec position_spec;
+ struct prio_spec prio_spec;
+ struct limit_rate limit_rate;
+ struct tcp_kind_field {
+ uint16_t kind; /* must allow > 255 for SACK1, 2.. hack */
+ uint8_t field;
+ } tcp_kind_field;
+}
+
+%token TOKEN_EOF 0 "end of file"
+%token JUNK "junk"
+
+%token NEWLINE "newline"
+%token COLON "colon"
+%token SEMICOLON "semicolon"
+%token COMMA "comma"
+%token DOT "."
+
+%token EQ "=="
+%token NEQ "!="
+%token LT "<"
+%token GT ">"
+%token GTE ">="
+%token LTE "<="
+%token LSHIFT "<<"
+%token RSHIFT ">>"
+%token AMPERSAND "&"
+%token CARET "^"
+%token NOT "!"
+%token SLASH "/"
+%token ASTERISK "*"
+%token DASH "-"
+%token AT "@"
+%token VMAP "vmap"
+
+%token PLUS "+"
+
+%token INCLUDE "include"
+%token DEFINE "define"
+%token REDEFINE "redefine"
+%token UNDEFINE "undefine"
+
+%token FIB "fib"
+
+%token SOCKET "socket"
+%token TRANSPARENT "transparent"
+%token WILDCARD "wildcard"
+%token CGROUPV2 "cgroupv2"
+
+%token TPROXY "tproxy"
+
+%token OSF "osf"
+
+%token SYNPROXY "synproxy"
+%token MSS "mss"
+%token WSCALE "wscale"
+
+%token TYPEOF "typeof"
+
+%token HOOK "hook"
+%token HOOKS "hooks"
+%token DEVICE "device"
+%token DEVICES "devices"
+%token TABLE "table"
+%token TABLES "tables"
+%token CHAIN "chain"
+%token CHAINS "chains"
+%token RULE "rule"
+%token RULES "rules"
+%token SETS "sets"
+%token SET "set"
+%token ELEMENT "element"
+%token MAP "map"
+%token MAPS "maps"
+%token FLOWTABLE "flowtable"
+%token HANDLE "handle"
+%token RULESET "ruleset"
+%token TRACE "trace"
+
+%token INET "inet"
+%token NETDEV "netdev"
+
+%token ADD "add"
+%token UPDATE "update"
+%token REPLACE "replace"
+%token CREATE "create"
+%token INSERT "insert"
+%token DELETE "delete"
+%token GET "get"
+%token LIST "list"
+%token RESET "reset"
+%token FLUSH "flush"
+%token RENAME "rename"
+%token DESCRIBE "describe"
+%token IMPORT "import"
+%token EXPORT "export"
+%token DESTROY "destroy"
+
+%token MONITOR "monitor"
+
+%token ALL "all"
+
+%token ACCEPT "accept"
+%token DROP "drop"
+%token CONTINUE "continue"
+%token JUMP "jump"
+%token GOTO "goto"
+%token RETURN "return"
+%token TO "to"
+
+%token CONSTANT "constant"
+%token INTERVAL "interval"
+%token DYNAMIC "dynamic"
+%token AUTOMERGE "auto-merge"
+%token TIMEOUT "timeout"
+%token GC_INTERVAL "gc-interval"
+%token ELEMENTS "elements"
+%token EXPIRES "expires"
+
+%token POLICY "policy"
+%token MEMORY "memory"
+%token PERFORMANCE "performance"
+%token SIZE "size"
+
+%token FLOW "flow"
+%token OFFLOAD "offload"
+%token METER "meter"
+%token METERS "meters"
+
+%token FLOWTABLES "flowtables"
+
+%token <val> NUM "number"
+%token <string> STRING "string"
+%token <string> QUOTED_STRING "quoted string"
+%token <string> ASTERISK_STRING "string with a trailing asterisk"
+%destructor { xfree($$); } STRING QUOTED_STRING ASTERISK_STRING
+
+%token LL_HDR "ll"
+%token NETWORK_HDR "nh"
+%token TRANSPORT_HDR "th"
+
+%token BRIDGE "bridge"
+
+%token ETHER "ether"
+%token SADDR "saddr"
+%token DADDR "daddr"
+%token TYPE "type"
+
+%token VLAN "vlan"
+%token ID "id"
+%token CFI "cfi"
+%token DEI "dei"
+%token PCP "pcp"
+
+%token ARP "arp"
+%token HTYPE "htype"
+%token PTYPE "ptype"
+%token HLEN "hlen"
+%token PLEN "plen"
+%token OPERATION "operation"
+
+%token IP "ip"
+%token HDRVERSION "version"
+%token HDRLENGTH "hdrlength"
+%token DSCP "dscp"
+%token ECN "ecn"
+%token LENGTH "length"
+%token FRAG_OFF "frag-off"
+%token TTL "ttl"
+%token PROTOCOL "protocol"
+%token CHECKSUM "checksum"
+
+%token PTR "ptr"
+%token VALUE "value"
+
+%token LSRR "lsrr"
+%token RR "rr"
+%token SSRR "ssrr"
+%token RA "ra"
+
+%token ICMP "icmp"
+%token CODE "code"
+%token SEQUENCE "seq"
+%token GATEWAY "gateway"
+%token MTU "mtu"
+
+%token IGMP "igmp"
+%token MRT "mrt"
+
+%token OPTIONS "options"
+
+%token IP6 "ip6"
+%token PRIORITY "priority"
+%token FLOWLABEL "flowlabel"
+%token NEXTHDR "nexthdr"
+%token HOPLIMIT "hoplimit"
+
+%token ICMP6 "icmpv6"
+%token PPTR "param-problem"
+%token MAXDELAY "max-delay"
+%token TADDR "taddr"
+
+%token AH "ah"
+%token RESERVED "reserved"
+%token SPI "spi"
+
+%token ESP "esp"
+
+%token COMP "comp"
+%token FLAGS "flags"
+%token CPI "cpi"
+
+%token PORT "port"
+%token UDP "udp"
+%token SPORT "sport"
+%token DPORT "dport"
+%token UDPLITE "udplite"
+%token CSUMCOV "csumcov"
+
+%token TCP "tcp"
+%token ACKSEQ "ackseq"
+%token DOFF "doff"
+%token WINDOW "window"
+%token URGPTR "urgptr"
+%token OPTION "option"
+%token ECHO "echo"
+%token EOL "eol"
+%token MPTCP "mptcp"
+%token NOP "nop"
+%token SACK "sack"
+%token SACK0 "sack0"
+%token SACK1 "sack1"
+%token SACK2 "sack2"
+%token SACK3 "sack3"
+%token SACK_PERM "sack-permitted"
+%token FASTOPEN "fastopen"
+%token MD5SIG "md5sig"
+%token TIMESTAMP "timestamp"
+%token COUNT "count"
+%token LEFT "left"
+%token RIGHT "right"
+%token TSVAL "tsval"
+%token TSECR "tsecr"
+%token SUBTYPE "subtype"
+
+%token DCCP "dccp"
+
+%token VXLAN "vxlan"
+%token VNI "vni"
+
+%token GRE "gre"
+%token GRETAP "gretap"
+
+%token GENEVE "geneve"
+
+%token SCTP "sctp"
+%token CHUNK "chunk"
+%token DATA "data"
+%token INIT "init"
+%token INIT_ACK "init-ack"
+%token HEARTBEAT "heartbeat"
+%token HEARTBEAT_ACK "heartbeat-ack"
+%token ABORT "abort"
+%token SHUTDOWN "shutdown"
+%token SHUTDOWN_ACK "shutdown-ack"
+%token ERROR "error"
+%token COOKIE_ECHO "cookie-echo"
+%token COOKIE_ACK "cookie-ack"
+%token ECNE "ecne"
+%token CWR "cwr"
+%token SHUTDOWN_COMPLETE "shutdown-complete"
+%token ASCONF_ACK "asconf-ack"
+%token FORWARD_TSN "forward-tsn"
+%token ASCONF "asconf"
+%token TSN "tsn"
+%token STREAM "stream"
+%token SSN "ssn"
+%token PPID "ppid"
+%token INIT_TAG "init-tag"
+%token A_RWND "a-rwnd"
+%token NUM_OSTREAMS "num-outbound-streams"
+%token NUM_ISTREAMS "num-inbound-streams"
+%token INIT_TSN "initial-tsn"
+%token CUM_TSN_ACK "cum-tsn-ack"
+%token NUM_GACK_BLOCKS "num-gap-ack-blocks"
+%token NUM_DUP_TSNS "num-dup-tsns"
+%token LOWEST_TSN "lowest-tsn"
+%token SEQNO "seqno"
+%token NEW_CUM_TSN "new-cum-tsn"
+
+%token VTAG "vtag"
+
+%token RT "rt"
+%token RT0 "rt0"
+%token RT2 "rt2"
+%token RT4 "srh"
+%token SEG_LEFT "seg-left"
+%token ADDR "addr"
+%token LAST_ENT "last-entry"
+%token TAG "tag"
+%token SID "sid"
+
+%token HBH "hbh"
+
+%token FRAG "frag"
+%token RESERVED2 "reserved2"
+%token MORE_FRAGMENTS "more-fragments"
+
+%token DST "dst"
+
+%token MH "mh"
+
+%token META "meta"
+%token MARK "mark"
+%token IIF "iif"
+%token IIFNAME "iifname"
+%token IIFTYPE "iiftype"
+%token OIF "oif"
+%token OIFNAME "oifname"
+%token OIFTYPE "oiftype"
+%token SKUID "skuid"
+%token SKGID "skgid"
+%token NFTRACE "nftrace"
+%token RTCLASSID "rtclassid"
+%token IBRIPORT "ibriport"
+%token OBRIPORT "obriport"
+%token IBRIDGENAME "ibrname"
+%token OBRIDGENAME "obrname"
+%token PKTTYPE "pkttype"
+%token CPU "cpu"
+%token IIFGROUP "iifgroup"
+%token OIFGROUP "oifgroup"
+%token CGROUP "cgroup"
+%token TIME "time"
+
+%token CLASSID "classid"
+%token NEXTHOP "nexthop"
+
+%token CT "ct"
+%token L3PROTOCOL "l3proto"
+%token PROTO_SRC "proto-src"
+%token PROTO_DST "proto-dst"
+%token ZONE "zone"
+%token DIRECTION "direction"
+%token EVENT "event"
+%token EXPECTATION "expectation"
+%token EXPIRATION "expiration"
+%token HELPER "helper"
+%token LABEL "label"
+%token STATE "state"
+%token STATUS "status"
+%token ORIGINAL "original"
+%token REPLY "reply"
+
+%token COUNTER "counter"
+%token NAME "name"
+%token PACKETS "packets"
+%token BYTES "bytes"
+%token AVGPKT "avgpkt"
+
+%token LAST "last"
+%token NEVER "never"
+
+%token COUNTERS "counters"
+%token QUOTAS "quotas"
+%token LIMITS "limits"
+%token SYNPROXYS "synproxys"
+%token HELPERS "helpers"
+
+%token LOG "log"
+%token PREFIX "prefix"
+%token GROUP "group"
+%token SNAPLEN "snaplen"
+%token QUEUE_THRESHOLD "queue-threshold"
+%token LEVEL "level"
+
+%token LIMIT "limit"
+%token RATE "rate"
+%token BURST "burst"
+%token OVER "over"
+%token UNTIL "until"
+
+%token QUOTA "quota"
+%token USED "used"
+
+%token SECMARK "secmark"
+%token SECMARKS "secmarks"
+
+%token SECOND "second"
+%token MINUTE "minute"
+%token HOUR "hour"
+%token DAY "day"
+%token WEEK "week"
+
+%token _REJECT "reject"
+%token WITH "with"
+%token ICMPX "icmpx"
+
+%token SNAT "snat"
+%token DNAT "dnat"
+%token MASQUERADE "masquerade"
+%token REDIRECT "redirect"
+%token RANDOM "random"
+%token FULLY_RANDOM "fully-random"
+%token PERSISTENT "persistent"
+
+%token QUEUE "queue"
+%token QUEUENUM "num"
+%token BYPASS "bypass"
+%token FANOUT "fanout"
+
+%token DUP "dup"
+%token FWD "fwd"
+
+%token NUMGEN "numgen"
+%token INC "inc"
+%token MOD "mod"
+%token OFFSET "offset"
+
+%token JHASH "jhash"
+%token SYMHASH "symhash"
+%token SEED "seed"
+
+%token POSITION "position"
+%token INDEX "index"
+%token COMMENT "comment"
+
+%token XML "xml"
+%token JSON "json"
+%token VM "vm"
+
+%token NOTRACK "notrack"
+
+%token EXISTS "exists"
+%token MISSING "missing"
+
+%token EXTHDR "exthdr"
+
+%token IPSEC "ipsec"
+%token REQID "reqid"
+%token SPNUM "spnum"
+
+%token IN "in"
+%token OUT "out"
+
+%token XT "xt"
+
+%type <limit_rate> limit_rate_pkts
+%type <limit_rate> limit_rate_bytes
+
+%type <string> identifier type_identifier string comment_spec
+%destructor { xfree($$); } identifier type_identifier string comment_spec
+
+%type <val> time_spec time_spec_or_num_s quota_used
+
+%type <expr> data_type_expr data_type_atom_expr
+%destructor { expr_free($$); } data_type_expr data_type_atom_expr
+
+%type <cmd> line
+%destructor { cmd_free($$); } line
+
+%type <cmd> base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd destroy_cmd
+%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd destroy_cmd
+
+%type <handle> table_spec tableid_spec table_or_id_spec
+%destructor { handle_free(&$$); } table_spec tableid_spec table_or_id_spec
+%type <handle> chain_spec chainid_spec chain_or_id_spec
+%destructor { handle_free(&$$); } chain_spec chainid_spec chain_or_id_spec
+
+%type <handle> flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec index_spec
+%destructor { handle_free(&$$); } flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec index_spec
+%type <handle> set_spec setid_spec set_or_id_spec
+%destructor { handle_free(&$$); } set_spec setid_spec set_or_id_spec
+%type <handle> obj_spec objid_spec obj_or_id_spec
+%destructor { handle_free(&$$); } obj_spec objid_spec obj_or_id_spec
+
+%type <handle> set_identifier flowtableid_spec flowtable_identifier obj_identifier
+%destructor { handle_free(&$$); } set_identifier flowtableid_spec obj_identifier
+
+%type <handle> basehook_spec
+%destructor { handle_free(&$$); } basehook_spec
+
+%type <val> family_spec family_spec_explicit
+%type <val32> int_num chain_policy
+%type <prio_spec> extended_prio_spec prio_spec
+%type <string> extended_prio_name quota_unit basehook_device_name
+%destructor { xfree($$); } extended_prio_name quota_unit basehook_device_name
+
+%type <expr> dev_spec
+%destructor { xfree($$); } dev_spec
+
+%type <table> table_block_alloc table_block
+%destructor { close_scope(state); table_free($$); } table_block_alloc
+%type <chain> chain_block_alloc chain_block subchain_block
+%destructor { close_scope(state); chain_free($$); } chain_block_alloc
+%type <rule> rule rule_alloc
+%destructor { rule_free($$); } rule
+
+%type <val> set_flag_list set_flag
+
+%type <val> set_policy_spec
+
+%type <set> set_block_alloc set_block
+%destructor { set_free($$); } set_block_alloc
+
+%type <set> map_block_alloc map_block
+%destructor { set_free($$); } map_block_alloc
+%type <val> map_block_obj_type map_block_data_interval
+
+%type <flowtable> flowtable_block_alloc flowtable_block
+%destructor { flowtable_free($$); } flowtable_block_alloc
+
+%type <obj> obj_block_alloc counter_block quota_block ct_helper_block ct_timeout_block ct_expect_block limit_block secmark_block synproxy_block
+%destructor { obj_free($$); } obj_block_alloc
+
+%type <list> stmt_list stateful_stmt_list set_elem_stmt_list
+%destructor { stmt_list_free($$); xfree($$); } stmt_list stateful_stmt_list set_elem_stmt_list
+%type <stmt> stmt match_stmt verdict_stmt set_elem_stmt
+%destructor { stmt_free($$); } stmt match_stmt verdict_stmt set_elem_stmt
+%type <stmt> counter_stmt counter_stmt_alloc stateful_stmt last_stmt
+%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_stmt last_stmt
+%type <stmt> payload_stmt
+%destructor { stmt_free($$); } payload_stmt
+%type <stmt> ct_stmt
+%destructor { stmt_free($$); } ct_stmt
+%type <stmt> meta_stmt
+%destructor { stmt_free($$); } meta_stmt
+%type <stmt> log_stmt log_stmt_alloc
+%destructor { stmt_free($$); } log_stmt log_stmt_alloc
+%type <val> level_type log_flags log_flags_tcp log_flag_tcp
+%type <stmt> limit_stmt quota_stmt connlimit_stmt
+%destructor { stmt_free($$); } limit_stmt quota_stmt connlimit_stmt
+%type <val> limit_burst_pkts limit_burst_bytes limit_mode limit_bytes time_unit quota_mode
+%type <stmt> reject_stmt reject_stmt_alloc
+%destructor { stmt_free($$); } reject_stmt reject_stmt_alloc
+%type <stmt> nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc
+%destructor { stmt_free($$); } nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc
+%type <val> nf_nat_flags nf_nat_flag offset_opt
+%type <stmt> tproxy_stmt
+%destructor { stmt_free($$); } tproxy_stmt
+%type <stmt> synproxy_stmt synproxy_stmt_alloc
+%destructor { stmt_free($$); } synproxy_stmt synproxy_stmt_alloc
+%type <stmt> chain_stmt
+%destructor { stmt_free($$); } chain_stmt
+%type <val> chain_stmt_type
+
+%type <stmt> queue_stmt queue_stmt_alloc queue_stmt_compat
+%destructor { stmt_free($$); } queue_stmt queue_stmt_alloc queue_stmt_compat
+%type <expr> queue_stmt_expr_simple queue_stmt_expr queue_expr reject_with_expr
+%destructor { expr_free($$); } queue_stmt_expr_simple queue_stmt_expr queue_expr reject_with_expr
+%type <val> queue_stmt_flags queue_stmt_flag
+%type <stmt> dup_stmt
+%destructor { stmt_free($$); } dup_stmt
+%type <stmt> fwd_stmt
+%destructor { stmt_free($$); } fwd_stmt
+%type <stmt> set_stmt
+%destructor { stmt_free($$); } set_stmt
+%type <val> set_stmt_op
+%type <stmt> map_stmt
+%destructor { stmt_free($$); } map_stmt
+%type <stmt> meter_stmt meter_stmt_alloc flow_stmt_legacy_alloc
+%destructor { stmt_free($$); } meter_stmt meter_stmt_alloc flow_stmt_legacy_alloc
+
+%type <expr> symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr
+%destructor { expr_free($$); } symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr
+%type <expr> primary_expr shift_expr and_expr typeof_expr typeof_data_expr
+%destructor { expr_free($$); } primary_expr shift_expr and_expr typeof_expr typeof_data_expr
+%type <expr> exclusive_or_expr inclusive_or_expr
+%destructor { expr_free($$); } exclusive_or_expr inclusive_or_expr
+%type <expr> basic_expr
+%destructor { expr_free($$); } basic_expr
+%type <expr> set_ref_expr set_ref_symbol_expr
+%destructor { expr_free($$); } set_ref_expr set_ref_symbol_expr
+
+%type <expr> multiton_rhs_expr
+%destructor { expr_free($$); } multiton_rhs_expr
+%type <expr> prefix_rhs_expr range_rhs_expr
+%destructor { expr_free($$); } prefix_rhs_expr range_rhs_expr
+
+%type <expr> stmt_expr concat_stmt_expr map_stmt_expr map_stmt_expr_set
+%destructor { expr_free($$); } stmt_expr concat_stmt_expr map_stmt_expr map_stmt_expr_set
+
+%type <expr> multiton_stmt_expr
+%destructor { expr_free($$); } multiton_stmt_expr
+%type <expr> prefix_stmt_expr range_stmt_expr
+%destructor { expr_free($$); } prefix_stmt_expr range_stmt_expr
+
+%type <expr> primary_stmt_expr basic_stmt_expr
+%destructor { expr_free($$); } primary_stmt_expr basic_stmt_expr
+%type <expr> list_stmt_expr shift_stmt_expr
+%destructor { expr_free($$); } list_stmt_expr shift_stmt_expr
+%type <expr> and_stmt_expr exclusive_or_stmt_expr inclusive_or_stmt_expr
+%destructor { expr_free($$); } and_stmt_expr exclusive_or_stmt_expr inclusive_or_stmt_expr
+
+%type <expr> concat_expr
+%destructor { expr_free($$); } concat_expr
+
+%type <expr> map_expr
+%destructor { expr_free($$); } map_expr
+
+%type <expr> verdict_map_stmt
+%destructor { expr_free($$); } verdict_map_stmt
+
+%type <expr> verdict_map_expr verdict_map_list_expr verdict_map_list_member_expr
+%destructor { expr_free($$); } verdict_map_expr verdict_map_list_expr verdict_map_list_member_expr
+
+%type <expr> set_expr set_block_expr set_list_expr set_list_member_expr flowtable_expr flowtable_list_expr flowtable_expr_member
+%destructor { expr_free($$); } set_expr set_block_expr set_list_expr set_list_member_expr flowtable_expr flowtable_list_expr flowtable_expr_member
+%type <expr> set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr
+%destructor { expr_free($$); } set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr
+%type <expr> set_elem_expr_stmt set_elem_expr_stmt_alloc
+%destructor { expr_free($$); } set_elem_expr_stmt set_elem_expr_stmt_alloc
+
+%type <expr> meter_key_expr meter_key_expr_alloc
+%destructor { expr_free($$); } meter_key_expr meter_key_expr_alloc
+
+%type <expr> expr initializer_expr keyword_expr
+%destructor { expr_free($$); } expr initializer_expr keyword_expr
+
+%type <expr> rhs_expr concat_rhs_expr basic_rhs_expr
+%destructor { expr_free($$); } rhs_expr concat_rhs_expr basic_rhs_expr
+%type <expr> primary_rhs_expr list_rhs_expr shift_rhs_expr symbol_stmt_expr
+%destructor { expr_free($$); } primary_rhs_expr list_rhs_expr shift_rhs_expr symbol_stmt_expr
+%type <expr> and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr
+%destructor { expr_free($$); } and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr
+
+%type <obj> counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj synproxy_obj
+%destructor { obj_free($$); } counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj synproxy_obj
+
+%type <expr> relational_expr
+%destructor { expr_free($$); } relational_expr
+%type <val> relational_op
+
+%type <expr> payload_expr payload_raw_expr
+%destructor { expr_free($$); } payload_expr payload_raw_expr
+%type <val> payload_base_spec
+%type <expr> eth_hdr_expr vlan_hdr_expr
+%destructor { expr_free($$); } eth_hdr_expr vlan_hdr_expr
+%type <val> eth_hdr_field vlan_hdr_field
+%type <expr> arp_hdr_expr
+%destructor { expr_free($$); } arp_hdr_expr
+%type <val> arp_hdr_field
+%type <expr> ip_hdr_expr icmp_hdr_expr igmp_hdr_expr numgen_expr hash_expr
+%destructor { expr_free($$); } ip_hdr_expr icmp_hdr_expr igmp_hdr_expr numgen_expr hash_expr
+%type <val> ip_hdr_field icmp_hdr_field igmp_hdr_field
+%type <val> ip_option_type ip_option_field
+%type <expr> ip6_hdr_expr icmp6_hdr_expr
+%destructor { expr_free($$); } ip6_hdr_expr icmp6_hdr_expr
+%type <val> ip6_hdr_field icmp6_hdr_field
+%type <expr> auth_hdr_expr esp_hdr_expr comp_hdr_expr
+%destructor { expr_free($$); } auth_hdr_expr esp_hdr_expr comp_hdr_expr
+%type <val> auth_hdr_field esp_hdr_field comp_hdr_field
+%type <expr> udp_hdr_expr udplite_hdr_expr
+%destructor { expr_free($$); } udp_hdr_expr udplite_hdr_expr
+%type <val> udp_hdr_field udplite_hdr_field
+%type <expr> dccp_hdr_expr sctp_hdr_expr sctp_chunk_alloc
+%destructor { expr_free($$); } dccp_hdr_expr sctp_hdr_expr sctp_chunk_alloc
+%type <val> dccp_hdr_field sctp_hdr_field
+%type <val> sctp_chunk_type sctp_chunk_common_field
+%type <val> sctp_chunk_data_field sctp_chunk_init_field
+%type <val> sctp_chunk_sack_field
+%type <expr> th_hdr_expr
+%destructor { expr_free($$); } th_hdr_expr
+%type <val> th_hdr_field
+
+%type <expr> exthdr_expr
+%destructor { expr_free($$); } exthdr_expr
+%type <expr> hbh_hdr_expr frag_hdr_expr dst_hdr_expr
+%destructor { expr_free($$); } hbh_hdr_expr frag_hdr_expr dst_hdr_expr
+%type <val> hbh_hdr_field frag_hdr_field dst_hdr_field
+%type <expr> rt_hdr_expr rt0_hdr_expr rt2_hdr_expr rt4_hdr_expr
+%destructor { expr_free($$); } rt_hdr_expr rt0_hdr_expr rt2_hdr_expr rt4_hdr_expr
+%type <val> rt_hdr_field rt0_hdr_field rt2_hdr_field rt4_hdr_field
+%type <expr> mh_hdr_expr
+%destructor { expr_free($$); } mh_hdr_expr
+%type <val> mh_hdr_field
+
+%type <expr> meta_expr
+%destructor { expr_free($$); } meta_expr
+%type <val> meta_key meta_key_qualified meta_key_unqualified numgen_type
+
+%type <expr> socket_expr
+%destructor { expr_free($$); } socket_expr
+%type<val> socket_key
+
+%type <val> nf_key_proto
+
+%type <expr> rt_expr
+%destructor { expr_free($$); } rt_expr
+%type <val> rt_key
+
+%type <expr> ct_expr
+%destructor { expr_free($$); } ct_expr
+%type <val> ct_key ct_dir ct_key_dir_optional ct_key_dir ct_key_proto_field
+
+%type <expr> fib_expr
+%destructor { expr_free($$); } fib_expr
+%type <val> fib_tuple fib_result fib_flag
+
+%type <expr> osf_expr
+%type <val> osf_ttl
+%destructor { expr_free($$); } osf_expr
+
+%type <val> markup_format
+%type <string> monitor_event
+%destructor { xfree($$); } monitor_event
+%type <val> monitor_object monitor_format
+
+%type <val> synproxy_ts synproxy_sack
+
+%type <expr> tcp_hdr_expr
+%destructor { expr_free($$); } tcp_hdr_expr
+%type <val> tcp_hdr_field
+%type <val> tcp_hdr_option_type
+%type <val> tcp_hdr_option_sack
+%type <val> tcpopt_field_maxseg tcpopt_field_mptcp tcpopt_field_sack tcpopt_field_tsopt tcpopt_field_window
+%type <tcp_kind_field> tcp_hdr_option_kind_and_field
+
+%type <expr> inner_eth_expr inner_inet_expr inner_expr
+%destructor { expr_free($$); } inner_eth_expr inner_inet_expr inner_expr
+
+%type <expr> vxlan_hdr_expr geneve_hdr_expr gre_hdr_expr gretap_hdr_expr
+%destructor { expr_free($$); } vxlan_hdr_expr geneve_hdr_expr gre_hdr_expr gretap_hdr_expr
+%type <val> vxlan_hdr_field geneve_hdr_field gre_hdr_field
+
+%type <stmt> optstrip_stmt
+%destructor { stmt_free($$); } optstrip_stmt
+
+%type <stmt> xt_stmt
+%destructor { stmt_free($$); } xt_stmt
+
+%type <expr> boolean_expr
+%destructor { expr_free($$); } boolean_expr
+%type <val8> boolean_keys
+
+%type <expr> exthdr_exists_expr
+%destructor { expr_free($$); } exthdr_exists_expr
+%type <val> exthdr_key
+
+%type <val> ct_l4protoname ct_obj_type ct_cmd_type
+
+%type <list> timeout_states timeout_state
+%destructor { xfree($$); } timeout_states timeout_state
+
+%type <val> xfrm_state_key xfrm_state_proto_key xfrm_dir xfrm_spnum
+%type <expr> xfrm_expr
+%destructor { expr_free($$); } xfrm_expr
+
+%type <expr> set_elem_key_expr
+%destructor { expr_free($$); } set_elem_key_expr
+
+%%
+
+input : /* empty */
+ | input line
+ {
+ if ($2 != NULL) {
+ $2->location = @2;
+ list_add_tail(&$2->list, state->cmds);
+ }
+ }
+ ;
+
+stmt_separator : NEWLINE
+ | SEMICOLON
+ ;
+
+opt_newline : NEWLINE
+ | /* empty */
+ ;
+
+close_scope_ah : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_AH); };
+close_scope_arp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ARP); };
+close_scope_at : { scanner_pop_start_cond(nft->scanner, PARSER_SC_AT); };
+close_scope_comp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_COMP); };
+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_last : { scanner_pop_start_cond(nft->scanner, PARSER_SC_LAST); };
+close_scope_dccp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DCCP); };
+close_scope_destroy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_DESTROY); };
+close_scope_dst : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DST); };
+close_scope_dup : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_DUP); };
+close_scope_esp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_ESP); };
+close_scope_eth : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ETH); };
+close_scope_export : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_EXPORT); };
+close_scope_fib : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FIB); };
+close_scope_frag : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FRAG); };
+close_scope_fwd : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_FWD); };
+close_scope_gre : { scanner_pop_start_cond(nft->scanner, PARSER_SC_GRE); };
+close_scope_hash : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HASH); };
+close_scope_hbh : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HBH); };
+close_scope_ip : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP); };
+close_scope_ip6 : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP6); };
+close_scope_vlan : { scanner_pop_start_cond(nft->scanner, PARSER_SC_VLAN); };
+close_scope_icmp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ICMP); };
+close_scope_igmp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IGMP); };
+close_scope_import : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_IMPORT); };
+close_scope_ipsec : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_IPSEC); };
+close_scope_list : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_LIST); };
+close_scope_limit : { scanner_pop_start_cond(nft->scanner, PARSER_SC_LIMIT); };
+close_scope_meta : { scanner_pop_start_cond(nft->scanner, PARSER_SC_META); };
+close_scope_mh : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_MH); };
+close_scope_monitor : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_MONITOR); };
+close_scope_nat : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_NAT); };
+close_scope_numgen : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_NUMGEN); };
+close_scope_osf : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_OSF); };
+close_scope_policy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_POLICY); };
+close_scope_quota : { scanner_pop_start_cond(nft->scanner, PARSER_SC_QUOTA); };
+close_scope_queue : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_QUEUE); };
+close_scope_reject : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_REJECT); };
+close_scope_reset : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_RESET); };
+close_scope_rt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_RT); };
+close_scope_sctp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_SCTP); };
+close_scope_sctp_chunk : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SCTP_CHUNK); };
+close_scope_secmark : { scanner_pop_start_cond(nft->scanner, PARSER_SC_SECMARK); };
+close_scope_socket : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SOCKET); }
+close_scope_tcp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_TCP); };
+close_scope_tproxy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_TPROXY); };
+close_scope_type : { scanner_pop_start_cond(nft->scanner, PARSER_SC_TYPE); };
+close_scope_th : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_TH); };
+close_scope_udp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDP); };
+close_scope_udplite : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDPLITE); };
+
+close_scope_log : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_LOG); }
+close_scope_synproxy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_SYNPROXY); }
+close_scope_xt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_XT); }
+
+common_block : INCLUDE QUOTED_STRING stmt_separator
+ {
+ if (scanner_include_file(nft, scanner, $2, &@$) < 0) {
+ xfree($2);
+ YYERROR;
+ }
+ xfree($2);
+ }
+ | DEFINE identifier '=' initializer_expr stmt_separator
+ {
+ struct scope *scope = current_scope(state);
+
+ if (symbol_lookup(scope, $2) != NULL) {
+ erec_queue(error(&@2, "redefinition of symbol '%s'", $2),
+ state->msgs);
+ expr_free($4);
+ xfree($2);
+ YYERROR;
+ }
+
+ symbol_bind(scope, $2, $4);
+ xfree($2);
+ }
+ | REDEFINE identifier '=' initializer_expr stmt_separator
+ {
+ struct scope *scope = current_scope(state);
+
+ symbol_bind(scope, $2, $4);
+ xfree($2);
+ }
+ | UNDEFINE identifier stmt_separator
+ {
+ struct scope *scope = current_scope(state);
+
+ if (symbol_unbind(scope, $2) < 0) {
+ erec_queue(error(&@2, "undefined symbol '%s'", $2),
+ state->msgs);
+ xfree($2);
+ YYERROR;
+ }
+ xfree($2);
+ }
+ | error stmt_separator
+ {
+ if (++state->nerrs == nft->parser_max_errors)
+ YYABORT;
+ yyerrok;
+ }
+ ;
+
+line : common_block { $$ = NULL; }
+ | stmt_separator { $$ = NULL; }
+ | base_cmd stmt_separator { $$ = $1; }
+ | base_cmd TOKEN_EOF
+ {
+ /*
+ * Very hackish workaround for bison >= 2.4: previous versions
+ * terminated parsing after EOF, 2.4+ tries to get further input
+ * in 'input' and calls the scanner again, causing a crash when
+ * the final input buffer has been popped. Terminate manually to
+ * avoid this. The correct fix should be to adjust the grammar
+ * to accept EOF in input, but for unknown reasons it does not
+ * work.
+ */
+ if ($1 != NULL) {
+ $1->location = @1;
+ list_add_tail(&$1->list, state->cmds);
+ }
+ $$ = NULL;
+ YYACCEPT;
+ }
+ ;
+
+base_cmd : /* empty */ add_cmd { $$ = $1; }
+ | ADD add_cmd { $$ = $2; }
+ | REPLACE replace_cmd { $$ = $2; }
+ | CREATE create_cmd { $$ = $2; }
+ | INSERT insert_cmd { $$ = $2; }
+ | DELETE delete_cmd { $$ = $2; }
+ | GET get_cmd { $$ = $2; }
+ | LIST list_cmd close_scope_list { $$ = $2; }
+ | RESET reset_cmd close_scope_reset { $$ = $2; }
+ | FLUSH flush_cmd { $$ = $2; }
+ | RENAME rename_cmd { $$ = $2; }
+ | IMPORT import_cmd close_scope_import { $$ = $2; }
+ | EXPORT export_cmd close_scope_export { $$ = $2; }
+ | MONITOR monitor_cmd close_scope_monitor { $$ = $2; }
+ | DESCRIBE describe_cmd { $$ = $2; }
+ | DESTROY destroy_cmd close_scope_destroy { $$ = $2; }
+ ;
+
+add_cmd : TABLE table_spec
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_TABLE, &$2, &@$, NULL);
+ }
+ | TABLE table_spec table_block_alloc
+ '{' table_block '}'
+ {
+ handle_merge(&$3->handle, &$2);
+ close_scope(state);
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_TABLE, &$2, &@$, $5);
+ }
+ | CHAIN chain_spec
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ }
+ | CHAIN chain_spec chain_block_alloc
+ '{' chain_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ close_scope(state);
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &$2, &@$, $5);
+ }
+ | RULE rule_position rule
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &$2, &@$, $3);
+ }
+ | /* empty */ rule_position rule
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &$1, &@$, $2);
+ }
+ | SET set_spec set_block_alloc
+ '{' set_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &$2, &@$, $5);
+ }
+ | MAP set_spec map_block_alloc
+ '{' map_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &$2, &@$, $5);
+ }
+ | ELEMENT set_spec set_block_expr
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
+ }
+ | FLOWTABLE flowtable_spec flowtable_block_alloc
+ '{' flowtable_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
+ }
+ | COUNTER obj_spec close_scope_counter
+ {
+ struct obj *obj;
+
+ obj = obj_alloc(&@$);
+ obj->type = NFT_OBJECT_COUNTER;
+ handle_merge(&obj->handle, &$2);
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, obj);
+ }
+ | COUNTER obj_spec counter_obj counter_config close_scope_counter
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, $3);
+ }
+ | COUNTER obj_spec counter_obj '{' counter_block '}' close_scope_counter
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, $3);
+ }
+ | QUOTA obj_spec quota_obj quota_config close_scope_quota
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_QUOTA, &$2, &@$, $3);
+ }
+ | QUOTA obj_spec quota_obj '{' quota_block '}' close_scope_quota
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_QUOTA, &$2, &@$, $3);
+ }
+ | CT HELPER obj_spec ct_obj_alloc '{' ct_helper_block '}' close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_HELPER, &$3, &@$, $4);
+ }
+ | CT TIMEOUT obj_spec ct_obj_alloc '{' ct_timeout_block '}' close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_TIMEOUT, &$3, &@$, $4);
+ }
+ | CT EXPECTATION obj_spec ct_obj_alloc '{' ct_expect_block '}' close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_EXPECT, &$3, &@$, $4);
+ }
+ | LIMIT obj_spec limit_obj limit_config close_scope_limit
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &$2, &@$, $3);
+ }
+ | LIMIT obj_spec limit_obj '{' limit_block '}' close_scope_limit
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &$2, &@$, $3);
+ }
+ | SECMARK obj_spec secmark_obj secmark_config close_scope_secmark
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3);
+ }
+ | SECMARK obj_spec secmark_obj '{' secmark_block '}' close_scope_secmark
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3);
+ }
+ | SYNPROXY obj_spec synproxy_obj synproxy_config close_scope_synproxy
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
+ }
+ | SYNPROXY obj_spec synproxy_obj '{' synproxy_block '}' close_scope_synproxy
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
+ }
+ ;
+
+replace_cmd : RULE ruleid_spec rule
+ {
+ $$ = cmd_alloc(CMD_REPLACE, CMD_OBJ_RULE, &$2, &@$, $3);
+ }
+ ;
+
+create_cmd : TABLE table_spec
+ {
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_TABLE, &$2, &@$, NULL);
+ }
+ | TABLE table_spec table_block_alloc
+ '{' table_block '}'
+ {
+ handle_merge(&$3->handle, &$2);
+ close_scope(state);
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_TABLE, &$2, &@$, $5);
+ }
+ | CHAIN chain_spec
+ {
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ }
+ | CHAIN chain_spec chain_block_alloc
+ '{' chain_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ close_scope(state);
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_CHAIN, &$2, &@$, $5);
+ }
+ | SET set_spec set_block_alloc
+ '{' set_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SET, &$2, &@$, $5);
+ }
+ | MAP set_spec map_block_alloc
+ '{' map_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SET, &$2, &@$, $5);
+ }
+ | ELEMENT set_spec set_block_expr
+ {
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
+ }
+ | FLOWTABLE flowtable_spec flowtable_block_alloc
+ '{' flowtable_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
+ }
+ | COUNTER obj_spec close_scope_counter
+ {
+ struct obj *obj;
+
+ obj = obj_alloc(&@$);
+ obj->type = NFT_OBJECT_COUNTER;
+ handle_merge(&obj->handle, &$2);
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_COUNTER, &$2, &@$, obj);
+ }
+ | COUNTER obj_spec counter_obj counter_config close_scope_counter
+ {
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_COUNTER, &$2, &@$, $3);
+ }
+ | QUOTA obj_spec quota_obj quota_config close_scope_quota
+ {
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_QUOTA, &$2, &@$, $3);
+ }
+ | CT HELPER obj_spec ct_obj_alloc '{' ct_helper_block '}' close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_HELPER, &$3, &@$, $4);
+ }
+ | CT TIMEOUT obj_spec ct_obj_alloc '{' ct_timeout_block '}' close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_TIMEOUT, &$3, &@$, $4);
+ }
+ | CT EXPECTATION obj_spec ct_obj_alloc '{' ct_expect_block '}' close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_EXPECT, &$3, &@$, $4);
+ }
+ | LIMIT obj_spec limit_obj limit_config close_scope_limit
+ {
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_LIMIT, &$2, &@$, $3);
+ }
+ | SECMARK obj_spec secmark_obj secmark_config close_scope_secmark
+ {
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SECMARK, &$2, &@$, $3);
+ }
+ | SYNPROXY obj_spec synproxy_obj synproxy_config close_scope_synproxy
+ {
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
+ }
+ ;
+
+insert_cmd : RULE rule_position rule
+ {
+ $$ = cmd_alloc(CMD_INSERT, CMD_OBJ_RULE, &$2, &@$, $3);
+ }
+ ;
+
+table_or_id_spec : table_spec
+ | tableid_spec
+ ;
+
+chain_or_id_spec : chain_spec
+ | chainid_spec
+ ;
+
+set_or_id_spec : set_spec
+ | setid_spec
+ ;
+
+obj_or_id_spec : obj_spec
+ | objid_spec
+ ;
+
+delete_cmd : TABLE table_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_TABLE, &$2, &@$, NULL);
+ }
+ | CHAIN chain_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ }
+ | CHAIN chain_spec chain_block_alloc
+ '{' chain_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, $5);
+ }
+ | RULE ruleid_spec
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_RULE, &$2, &@$, NULL);
+ }
+ | SET set_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | MAP set_spec
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | ELEMENT set_spec set_block_expr
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
+ }
+ | FLOWTABLE flowtable_spec
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtableid_spec
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtable_spec flowtable_block_alloc
+ '{' flowtable_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
+ }
+ | COUNTER obj_or_id_spec close_scope_counter
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ }
+ | QUOTA obj_or_id_spec close_scope_quota
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_QUOTA, &$2, &@$, NULL);
+ }
+ | CT ct_obj_type obj_spec ct_obj_alloc close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_DELETE, $2, &$3, &@$, $4);
+ if ($2 == NFT_OBJECT_CT_TIMEOUT)
+ init_list_head(&$4->ct_timeout.timeout_list);
+ }
+ | LIMIT obj_or_id_spec close_scope_limit
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_LIMIT, &$2, &@$, NULL);
+ }
+ | SECMARK obj_or_id_spec close_scope_secmark
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SECMARK, &$2, &@$, NULL);
+ }
+ | SYNPROXY obj_or_id_spec close_scope_synproxy
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
+ }
+ ;
+
+destroy_cmd : TABLE table_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_TABLE, &$2, &@$, NULL);
+ }
+ | CHAIN chain_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ }
+ | RULE ruleid_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_RULE, &$2, &@$, NULL);
+ }
+ | SET set_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | MAP set_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | ELEMENT set_spec set_block_expr
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
+ }
+ | FLOWTABLE flowtable_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtableid_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtable_spec flowtable_block_alloc
+ '{' flowtable_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
+ }
+ | COUNTER obj_or_id_spec close_scope_counter
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ }
+ | QUOTA obj_or_id_spec close_scope_quota
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_QUOTA, &$2, &@$, NULL);
+ }
+ | CT ct_obj_type obj_spec ct_obj_alloc close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_DESTROY, $2, &$3, &@$, $4);
+ if ($2 == NFT_OBJECT_CT_TIMEOUT)
+ init_list_head(&$4->ct_timeout.timeout_list);
+ }
+ | LIMIT obj_or_id_spec close_scope_limit
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_LIMIT, &$2, &@$, NULL);
+ }
+ | SECMARK obj_or_id_spec close_scope_secmark
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SECMARK, &$2, &@$, NULL);
+ }
+ | SYNPROXY obj_or_id_spec close_scope_synproxy
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
+ }
+ ;
+
+
+get_cmd : ELEMENT set_spec set_block_expr
+ {
+ $$ = cmd_alloc(CMD_GET, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
+ }
+ ;
+
+list_cmd : TABLE table_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_TABLE, &$2, &@$, NULL);
+ }
+ | TABLES ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_TABLE, &$2, &@$, NULL);
+ }
+ | CHAIN chain_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ }
+ | CHAINS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_CHAINS, &$2, &@$, NULL);
+ }
+ | SETS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SETS, &$2, &@$, NULL);
+ }
+ | SETS TABLE table_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SETS, &$3, &@$, NULL);
+ }
+ | SET set_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | COUNTERS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTERS, &$2, &@$, NULL);
+ }
+ | COUNTERS TABLE table_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTERS, &$3, &@$, NULL);
+ }
+ | COUNTER obj_spec close_scope_counter
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ }
+ | QUOTAS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTAS, &$2, &@$, NULL);
+ }
+ | QUOTAS TABLE table_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTAS, &$3, &@$, NULL);
+ }
+ | QUOTA obj_spec close_scope_quota
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTA, &$2, &@$, NULL);
+ }
+ | LIMITS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &$2, &@$, NULL);
+ }
+ | LIMITS TABLE table_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &$3, &@$, NULL);
+ }
+ | LIMIT obj_spec close_scope_limit
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMIT, &$2, &@$, NULL);
+ }
+ | SECMARKS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARKS, &$2, &@$, NULL);
+ }
+ | SECMARKS TABLE table_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARKS, &$3, &@$, NULL);
+ }
+ | SECMARK obj_spec close_scope_secmark
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARK, &$2, &@$, NULL);
+ }
+ | SYNPROXYS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$2, &@$, NULL);
+ }
+ | SYNPROXYS TABLE table_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$3, &@$, NULL);
+ }
+ | SYNPROXY obj_spec close_scope_synproxy
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
+ }
+ | RULESET ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &$2, &@$, NULL);
+ }
+ | FLOW TABLES ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_METERS, &$3, &@$, NULL);
+ }
+ | FLOW TABLE set_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_METER, &$3, &@$, NULL);
+ }
+ | METERS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_METERS, &$2, &@$, NULL);
+ }
+ | METER set_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_METER, &$2, &@$, NULL);
+ }
+ | FLOWTABLES ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_FLOWTABLES, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtable_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | MAPS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_MAPS, &$2, &@$, NULL);
+ }
+ | MAP set_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_MAP, &$2, &@$, NULL);
+ }
+ | CT ct_obj_type obj_spec close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_LIST, $2, &$3, &@$, NULL);
+ }
+ | CT ct_cmd_type TABLE table_spec close_scope_ct
+ {
+ $$ = cmd_alloc(CMD_LIST, $2, &$4, &@$, NULL);
+ }
+ | HOOKS basehook_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_HOOKS, &$2, &@$, NULL);
+ }
+ ;
+
+basehook_device_name : DEVICE STRING
+ {
+ $$ = $2;
+ }
+ ;
+
+basehook_spec : ruleset_spec
+ {
+ $$ = $1;
+ }
+ | ruleset_spec basehook_device_name
+ {
+ if ($2) {
+ $1.obj.name = $2;
+ $1.obj.location = @2;
+ }
+ $$ = $1;
+ }
+ ;
+
+reset_cmd : COUNTERS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$2, &@$, NULL);
+ }
+ | COUNTERS table_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$2, &@$, NULL);
+ }
+ | COUNTERS TABLE table_spec
+ {
+ /* alias of previous rule. */
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$3, &@$, NULL);
+ }
+ | COUNTER obj_spec close_scope_counter
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTER, &$2,&@$, NULL);
+ }
+ | QUOTAS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$2, &@$, NULL);
+ }
+ | QUOTAS TABLE table_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$3, &@$, NULL);
+ }
+ | QUOTAS table_spec
+ {
+ /* alias of previous rule. */
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$2, &@$, NULL);
+ }
+ | QUOTA obj_spec close_scope_quota
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTA, &$2, &@$, NULL);
+ }
+ | RULES ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL);
+ }
+ | RULES table_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL);
+ }
+ | RULES TABLE table_spec
+ {
+ /* alias of previous rule. */
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$3, &@$, NULL);
+ }
+ | RULES chain_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL);
+ }
+ | RULES CHAIN chain_spec
+ {
+ /* alias of previous rule. */
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$3, &@$, NULL);
+ }
+ | RULE ruleid_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULE, &$2, &@$, NULL);
+ }
+ | ELEMENT set_spec set_block_expr
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
+ }
+ | SET set_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | MAP set_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_MAP, &$2, &@$, NULL);
+ }
+ ;
+
+flush_cmd : TABLE table_spec
+ {
+ $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_TABLE, &$2, &@$, NULL);
+ }
+ | CHAIN chain_spec
+ {
+ $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ }
+ | SET set_spec
+ {
+ $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | MAP set_spec
+ {
+ $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_MAP, &$2, &@$, NULL);
+ }
+ | FLOW TABLE set_spec
+ {
+ $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_METER, &$3, &@$, NULL);
+ }
+ | METER set_spec
+ {
+ $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_METER, &$2, &@$, NULL);
+ }
+ | RULESET ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_RULESET, &$2, &@$, NULL);
+ }
+ ;
+
+rename_cmd : CHAIN chain_spec identifier
+ {
+ $$ = cmd_alloc(CMD_RENAME, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ $$->arg = $3;
+ }
+ ;
+
+import_cmd : RULESET markup_format
+ {
+ struct handle h = { .family = NFPROTO_UNSPEC };
+ struct markup *markup = markup_alloc($2);
+ $$ = cmd_alloc(CMD_IMPORT, CMD_OBJ_MARKUP, &h, &@$, markup);
+ }
+ | markup_format
+ {
+ struct handle h = { .family = NFPROTO_UNSPEC };
+ struct markup *markup = markup_alloc($1);
+ $$ = cmd_alloc(CMD_IMPORT, CMD_OBJ_MARKUP, &h, &@$, markup);
+ }
+ ;
+
+export_cmd : RULESET markup_format
+ {
+ struct handle h = { .family = NFPROTO_UNSPEC };
+ struct markup *markup = markup_alloc($2);
+ $$ = cmd_alloc(CMD_EXPORT, CMD_OBJ_MARKUP, &h, &@$, markup);
+ }
+ | markup_format
+ {
+ struct handle h = { .family = NFPROTO_UNSPEC };
+ struct markup *markup = markup_alloc($1);
+ $$ = cmd_alloc(CMD_EXPORT, CMD_OBJ_MARKUP, &h, &@$, markup);
+ }
+ ;
+
+monitor_cmd : monitor_event monitor_object monitor_format
+ {
+ struct handle h = { .family = NFPROTO_UNSPEC };
+ struct monitor *m = monitor_alloc($3, $2, $1);
+ m->location = @1;
+ $$ = cmd_alloc(CMD_MONITOR, CMD_OBJ_MONITOR, &h, &@$, m);
+ }
+ ;
+
+monitor_event : /* empty */ { $$ = NULL; }
+ | STRING { $$ = $1; }
+ ;
+
+monitor_object : /* empty */ { $$ = CMD_MONITOR_OBJ_ANY; }
+ | TABLES { $$ = CMD_MONITOR_OBJ_TABLES; }
+ | CHAINS { $$ = CMD_MONITOR_OBJ_CHAINS; }
+ | SETS { $$ = CMD_MONITOR_OBJ_SETS; }
+ | RULES { $$ = CMD_MONITOR_OBJ_RULES; }
+ | ELEMENTS { $$ = CMD_MONITOR_OBJ_ELEMS; }
+ | RULESET { $$ = CMD_MONITOR_OBJ_RULESET; }
+ | TRACE { $$ = CMD_MONITOR_OBJ_TRACE; }
+ ;
+
+monitor_format : /* empty */ { $$ = NFTNL_OUTPUT_DEFAULT; }
+ | markup_format
+ ;
+
+markup_format : XML { $$ = __NFT_OUTPUT_NOTSUPP; }
+ | JSON { $$ = NFTNL_OUTPUT_JSON; }
+ | VM JSON { $$ = NFTNL_OUTPUT_JSON; }
+ ;
+
+describe_cmd : primary_expr
+ {
+ struct handle h = { .family = NFPROTO_UNSPEC };
+ $$ = cmd_alloc(CMD_DESCRIBE, CMD_OBJ_EXPR, &h, &@$, NULL);
+ $$->expr = $1;
+ }
+ ;
+
+table_block_alloc : /* empty */
+ {
+ $$ = table_alloc();
+ if (open_scope(state, &$$->scope) < 0) {
+ erec_queue(error(&@$, "too many levels of nesting"),
+ state->msgs);
+ state->nerrs++;
+ }
+ }
+ ;
+
+table_options : FLAGS STRING
+ {
+ if (strcmp($2, "dormant") == 0) {
+ $<table>0->flags |= TABLE_F_DORMANT;
+ xfree($2);
+ } else if (strcmp($2, "owner") == 0) {
+ $<table>0->flags |= TABLE_F_OWNER;
+ xfree($2);
+ } else {
+ erec_queue(error(&@2, "unknown table option %s", $2),
+ state->msgs);
+ xfree($2);
+ YYERROR;
+ }
+ }
+ | comment_spec
+ {
+ if (already_set($<table>0->comment, &@$, state)) {
+ xfree($1);
+ YYERROR;
+ }
+ $<table>0->comment = $1;
+ }
+ ;
+
+table_block : /* empty */ { $$ = $<table>-1; }
+ | table_block common_block
+ | table_block stmt_separator
+ | table_block table_options stmt_separator
+ | table_block CHAIN chain_identifier
+ chain_block_alloc '{' chain_block '}'
+ stmt_separator
+ {
+ $4->location = @3;
+ handle_merge(&$4->handle, &$3);
+ handle_free(&$3);
+ close_scope(state);
+ list_add_tail(&$4->list, &$1->chains);
+ $$ = $1;
+ }
+ | table_block SET set_identifier
+ set_block_alloc '{' set_block '}'
+ stmt_separator
+ {
+ $4->location = @3;
+ handle_merge(&$4->handle, &$3);
+ handle_free(&$3);
+ list_add_tail(&$4->list, &$1->sets);
+ $$ = $1;
+ }
+ | table_block MAP set_identifier
+ map_block_alloc '{' map_block '}'
+ stmt_separator
+ {
+ $4->location = @3;
+ handle_merge(&$4->handle, &$3);
+ handle_free(&$3);
+ list_add_tail(&$4->list, &$1->sets);
+ $$ = $1;
+ }
+
+ | table_block FLOWTABLE flowtable_identifier
+ flowtable_block_alloc '{' flowtable_block '}'
+ stmt_separator
+ {
+ $4->location = @3;
+ handle_merge(&$4->handle, &$3);
+ handle_free(&$3);
+ list_add_tail(&$4->list, &$1->flowtables);
+ $$ = $1;
+ }
+ | table_block COUNTER obj_identifier
+ obj_block_alloc '{' counter_block '}'
+ stmt_separator close_scope_counter
+ {
+ $4->location = @3;
+ $4->type = NFT_OBJECT_COUNTER;
+ handle_merge(&$4->handle, &$3);
+ handle_free(&$3);
+ list_add_tail(&$4->list, &$1->objs);
+ $$ = $1;
+ }
+ | table_block QUOTA obj_identifier
+ obj_block_alloc '{' quota_block '}'
+ stmt_separator close_scope_quota
+ {
+ $4->location = @3;
+ $4->type = NFT_OBJECT_QUOTA;
+ handle_merge(&$4->handle, &$3);
+ handle_free(&$3);
+ list_add_tail(&$4->list, &$1->objs);
+ $$ = $1;
+ }
+ | table_block CT HELPER obj_identifier obj_block_alloc '{' ct_helper_block '}' close_scope_ct stmt_separator
+ {
+ $5->location = @4;
+ $5->type = NFT_OBJECT_CT_HELPER;
+ handle_merge(&$5->handle, &$4);
+ handle_free(&$4);
+ list_add_tail(&$5->list, &$1->objs);
+ $$ = $1;
+ }
+ | table_block CT TIMEOUT obj_identifier obj_block_alloc '{' ct_timeout_block '}' close_scope_ct stmt_separator
+ {
+ $5->location = @4;
+ $5->type = NFT_OBJECT_CT_TIMEOUT;
+ handle_merge(&$5->handle, &$4);
+ handle_free(&$4);
+ list_add_tail(&$5->list, &$1->objs);
+ $$ = $1;
+ }
+ | table_block CT EXPECTATION obj_identifier obj_block_alloc '{' ct_expect_block '}' close_scope_ct stmt_separator
+ {
+ $5->location = @4;
+ $5->type = NFT_OBJECT_CT_EXPECT;
+ handle_merge(&$5->handle, &$4);
+ handle_free(&$4);
+ list_add_tail(&$5->list, &$1->objs);
+ $$ = $1;
+ }
+ | table_block LIMIT obj_identifier
+ obj_block_alloc '{' limit_block '}'
+ stmt_separator close_scope_limit
+ {
+ $4->location = @3;
+ $4->type = NFT_OBJECT_LIMIT;
+ handle_merge(&$4->handle, &$3);
+ handle_free(&$3);
+ list_add_tail(&$4->list, &$1->objs);
+ $$ = $1;
+ }
+ | table_block SECMARK obj_identifier
+ obj_block_alloc '{' secmark_block '}'
+ stmt_separator close_scope_secmark
+ {
+ $4->location = @3;
+ $4->type = NFT_OBJECT_SECMARK;
+ handle_merge(&$4->handle, &$3);
+ handle_free(&$3);
+ list_add_tail(&$4->list, &$1->objs);
+ $$ = $1;
+ }
+ | table_block SYNPROXY obj_identifier
+ obj_block_alloc '{' synproxy_block '}'
+ stmt_separator close_scope_synproxy
+ {
+ $4->location = @3;
+ $4->type = NFT_OBJECT_SYNPROXY;
+ handle_merge(&$4->handle, &$3);
+ handle_free(&$3);
+ list_add_tail(&$4->list, &$1->objs);
+ $$ = $1;
+ }
+ ;
+
+chain_block_alloc : /* empty */
+ {
+ $$ = chain_alloc();
+ if (open_scope(state, &$$->scope) < 0) {
+ erec_queue(error(&@$, "too many levels of nesting"),
+ state->msgs);
+ state->nerrs++;
+ }
+ }
+ ;
+
+chain_block : /* empty */ { $$ = $<chain>-1; }
+ | chain_block common_block
+ | chain_block stmt_separator
+ | chain_block hook_spec stmt_separator
+ | chain_block policy_spec stmt_separator
+ | chain_block flags_spec stmt_separator
+ | chain_block rule stmt_separator
+ {
+ list_add_tail(&$2->list, &$1->rules);
+ $$ = $1;
+ }
+ | chain_block DEVICES '=' flowtable_expr stmt_separator
+ {
+ if ($$->dev_expr) {
+ list_splice_init(&$4->expressions, &$$->dev_expr->expressions);
+ expr_free($4);
+ break;
+ }
+ $$->dev_expr = $4;
+ }
+ | chain_block comment_spec stmt_separator
+ {
+ if (already_set($1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $1->comment = $2;
+ }
+ ;
+
+subchain_block : /* empty */ { $$ = $<chain>-1; }
+ | subchain_block stmt_separator
+ | subchain_block rule stmt_separator
+ {
+ list_add_tail(&$2->list, &$1->rules);
+ $$ = $1;
+ }
+ ;
+
+typeof_data_expr : primary_expr
+ {
+ struct expr *e = $1;
+
+ if (e->etype == EXPR_SYMBOL &&
+ strcmp("verdict", e->identifier) == 0) {
+ struct expr *v = verdict_expr_alloc(&@1, NF_ACCEPT, NULL);
+
+ expr_free(e);
+ v->flags &= ~EXPR_F_CONSTANT;
+ e = v;
+ }
+
+ if (expr_ops(e)->build_udata == NULL) {
+ erec_queue(error(&@1, "map data type '%s' lacks typeof serialization", expr_ops(e)->name),
+ state->msgs);
+ expr_free(e);
+ YYERROR;
+ }
+ $$ = e;
+ }
+ | typeof_expr DOT primary_expr
+ {
+ struct location rhs[] = {
+ [1] = @2,
+ [2] = @3,
+ };
+
+ $$ = handle_concat_expr(&@$, $$, $1, $3, rhs);
+ }
+ ;
+
+typeof_expr : primary_expr
+ {
+ if (expr_ops($1)->build_udata == NULL) {
+ erec_queue(error(&@1, "primary expression type '%s' lacks typeof serialization", expr_ops($1)->name),
+ state->msgs);
+ expr_free($1);
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ | typeof_expr DOT primary_expr
+ {
+ struct location rhs[] = {
+ [1] = @2,
+ [2] = @3,
+ };
+
+ $$ = handle_concat_expr(&@$, $$, $1, $3, rhs);
+ }
+ ;
+
+
+set_block_alloc : /* empty */
+ {
+ $$ = set_alloc(&internal_location);
+ }
+ ;
+
+set_block : /* empty */ { $$ = $<set>-1; }
+ | set_block common_block
+ | set_block stmt_separator
+ | set_block TYPE data_type_expr stmt_separator close_scope_type
+ {
+ $1->key = $3;
+ $$ = $1;
+ }
+ | set_block TYPEOF typeof_expr stmt_separator
+ {
+ $1->key = $3;
+ datatype_set($1->key, $3->dtype);
+ $$ = $1;
+ }
+ | set_block FLAGS set_flag_list stmt_separator
+ {
+ $1->flags = $3;
+ $$ = $1;
+ }
+ | set_block TIMEOUT time_spec stmt_separator
+ {
+ $1->timeout = $3;
+ $$ = $1;
+ }
+ | set_block GC_INTERVAL time_spec stmt_separator
+ {
+ $1->gc_int = $3;
+ $$ = $1;
+ }
+ | set_block stateful_stmt_list stmt_separator
+ {
+ list_splice_tail($2, &$1->stmt_list);
+ $$ = $1;
+ free($2);
+ }
+ | set_block ELEMENTS '=' set_block_expr
+ {
+ $1->init = $4;
+ $$ = $1;
+ }
+ | set_block AUTOMERGE
+ {
+ $1->automerge = true;
+ $$ = $1;
+ }
+ | set_block set_mechanism stmt_separator
+ | set_block comment_spec stmt_separator
+ {
+ if (already_set($1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $1->comment = $2;
+ $$ = $1;
+ }
+ ;
+
+set_block_expr : set_expr
+ | variable_expr
+ ;
+
+set_flag_list : set_flag_list COMMA set_flag
+ {
+ $$ = $1 | $3;
+ }
+ | set_flag
+ ;
+
+set_flag : CONSTANT { $$ = NFT_SET_CONSTANT; }
+ | INTERVAL { $$ = NFT_SET_INTERVAL; }
+ | TIMEOUT { $$ = NFT_SET_TIMEOUT; }
+ | DYNAMIC { $$ = NFT_SET_EVAL; }
+ ;
+
+map_block_alloc : /* empty */
+ {
+ $$ = set_alloc(&internal_location);
+ }
+ ;
+
+map_block_obj_type : COUNTER close_scope_counter { $$ = NFT_OBJECT_COUNTER; }
+ | QUOTA close_scope_quota { $$ = NFT_OBJECT_QUOTA; }
+ | LIMIT close_scope_limit { $$ = NFT_OBJECT_LIMIT; }
+ | SECMARK close_scope_secmark { $$ = NFT_OBJECT_SECMARK; }
+ | SYNPROXY close_scope_synproxy { $$ = NFT_OBJECT_SYNPROXY; }
+ ;
+
+map_block_data_interval : INTERVAL { $$ = EXPR_F_INTERVAL; }
+ | { $$ = 0; }
+ ;
+
+map_block : /* empty */ { $$ = $<set>-1; }
+ | map_block common_block
+ | map_block stmt_separator
+ | map_block TIMEOUT time_spec stmt_separator
+ {
+ $1->timeout = $3;
+ $$ = $1;
+ }
+ | map_block GC_INTERVAL time_spec stmt_separator
+ {
+ $1->gc_int = $3;
+ $$ = $1;
+ }
+ | map_block TYPE
+ data_type_expr COLON map_block_data_interval data_type_expr
+ stmt_separator close_scope_type
+ {
+ $1->key = $3;
+ $1->data = $6;
+ $1->data->flags |= $5;
+
+ $1->flags |= NFT_SET_MAP;
+ $$ = $1;
+ }
+ | map_block TYPEOF
+ typeof_expr COLON typeof_data_expr
+ stmt_separator
+ {
+ $1->key = $3;
+ datatype_set($1->key, $3->dtype);
+ $1->data = $5;
+
+ $1->flags |= NFT_SET_MAP;
+ $$ = $1;
+ }
+ | map_block TYPEOF
+ typeof_expr COLON INTERVAL typeof_expr
+ stmt_separator
+ {
+ $1->key = $3;
+ datatype_set($1->key, $3->dtype);
+ $1->data = $6;
+ $1->data->flags |= EXPR_F_INTERVAL;
+
+ $1->flags |= NFT_SET_MAP;
+ $$ = $1;
+ }
+ | map_block TYPE
+ data_type_expr COLON map_block_obj_type
+ stmt_separator close_scope_type
+ {
+ $1->key = $3;
+ $1->objtype = $5;
+ $1->flags |= NFT_SET_OBJECT;
+ $$ = $1;
+ }
+ | map_block FLAGS set_flag_list stmt_separator
+ {
+ $1->flags |= $3;
+ $$ = $1;
+ }
+ | map_block stateful_stmt_list stmt_separator
+ {
+ list_splice_tail($2, &$1->stmt_list);
+ $$ = $1;
+ free($2);
+ }
+ | map_block ELEMENTS '=' set_block_expr
+ {
+ $1->init = $4;
+ $$ = $1;
+ }
+ | map_block comment_spec stmt_separator
+ {
+ if (already_set($1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $1->comment = $2;
+ $$ = $1;
+ }
+ | map_block set_mechanism stmt_separator
+ ;
+
+set_mechanism : POLICY set_policy_spec close_scope_policy
+ {
+ $<set>0->policy = $2;
+ }
+ | SIZE NUM
+ {
+ $<set>0->desc.size = $2;
+ }
+ ;
+
+set_policy_spec : PERFORMANCE { $$ = NFT_SET_POL_PERFORMANCE; }
+ | MEMORY { $$ = NFT_SET_POL_MEMORY; }
+ ;
+
+flowtable_block_alloc : /* empty */
+ {
+ $$ = flowtable_alloc(&internal_location);
+ }
+ ;
+
+flowtable_block : /* empty */ { $$ = $<flowtable>-1; }
+ | flowtable_block common_block
+ | flowtable_block stmt_separator
+ | flowtable_block HOOK STRING prio_spec stmt_separator
+ {
+ $$->hook.loc = @3;
+ $$->hook.name = chain_hookname_lookup($3);
+ if ($$->hook.name == NULL) {
+ erec_queue(error(&@3, "unknown chain hook"),
+ state->msgs);
+ xfree($3);
+ YYERROR;
+ }
+ xfree($3);
+
+ $$->priority = $4;
+ }
+ | flowtable_block DEVICES '=' flowtable_expr stmt_separator
+ {
+ $$->dev_expr = $4;
+ }
+ | flowtable_block COUNTER close_scope_counter
+ {
+ $$->flags |= NFT_FLOWTABLE_COUNTER;
+ }
+ | flowtable_block FLAGS OFFLOAD stmt_separator
+ {
+ $$->flags |= FLOWTABLE_F_HW_OFFLOAD;
+ }
+ ;
+
+flowtable_expr : '{' flowtable_list_expr '}'
+ {
+ $2->location = @$;
+ $$ = $2;
+ }
+ | variable_expr
+ {
+ $1->location = @$;
+ $$ = $1;
+ }
+ ;
+
+flowtable_list_expr : flowtable_expr_member
+ {
+ $$ = compound_expr_alloc(&@$, EXPR_LIST);
+ compound_expr_add($$, $1);
+ }
+ | flowtable_list_expr COMMA flowtable_expr_member
+ {
+ compound_expr_add($1, $3);
+ $$ = $1;
+ }
+ | flowtable_list_expr COMMA opt_newline
+ ;
+
+flowtable_expr_member : QUOTED_STRING
+ {
+ struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1);
+
+ if (!expr)
+ YYERROR;
+
+ $$ = expr;
+ }
+ | STRING
+ {
+ struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1);
+
+ if (!expr)
+ YYERROR;
+
+ $$ = expr;
+ }
+ | variable_expr
+ {
+ datatype_set($1->sym->expr, &ifname_type);
+ $$ = $1;
+ }
+ ;
+
+data_type_atom_expr : type_identifier
+ {
+ const struct datatype *dtype = datatype_lookup_byname($1);
+ if (dtype == NULL) {
+ erec_queue(error(&@1, "unknown datatype %s", $1),
+ state->msgs);
+ xfree($1);
+ YYERROR;
+ }
+ $$ = constant_expr_alloc(&@1, dtype, dtype->byteorder,
+ dtype->size, NULL);
+ xfree($1);
+ }
+ | TIME
+ {
+ $$ = constant_expr_alloc(&@1, &time_type, time_type.byteorder,
+ time_type.size, NULL);
+ }
+ ;
+
+data_type_expr : data_type_atom_expr
+ | data_type_expr DOT data_type_atom_expr
+ {
+ struct location rhs[] = {
+ [1] = @2,
+ [2] = @3,
+ };
+
+ $$ = handle_concat_expr(&@$, $$, $1, $3, rhs);
+ }
+ ;
+
+obj_block_alloc : /* empty */
+ {
+ $$ = obj_alloc(&internal_location);
+ }
+ ;
+
+counter_block : /* empty */ { $$ = $<obj>-1; }
+ | counter_block common_block
+ | counter_block stmt_separator
+ | counter_block counter_config
+ {
+ $$ = $1;
+ }
+ | counter_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
+ ;
+
+quota_block : /* empty */ { $$ = $<obj>-1; }
+ | quota_block common_block
+ | quota_block stmt_separator
+ | quota_block quota_config
+ {
+ $$ = $1;
+ }
+ | quota_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
+ ;
+
+ct_helper_block : /* empty */ { $$ = $<obj>-1; }
+ | ct_helper_block common_block
+ | ct_helper_block stmt_separator
+ | ct_helper_block ct_helper_config
+ {
+ $$ = $1;
+ }
+ | ct_helper_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
+ ;
+
+ct_timeout_block : /*empty */
+ {
+ $$ = $<obj>-1;
+ init_list_head(&$$->ct_timeout.timeout_list);
+ }
+ | ct_timeout_block common_block
+ | ct_timeout_block stmt_separator
+ | ct_timeout_block ct_timeout_config
+ {
+ $$ = $1;
+ }
+ | ct_timeout_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
+ ;
+
+ct_expect_block : /*empty */ { $$ = $<obj>-1; }
+ | ct_expect_block common_block
+ | ct_expect_block stmt_separator
+ | ct_expect_block ct_expect_config
+ {
+ $$ = $1;
+ }
+ | ct_expect_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
+ ;
+
+limit_block : /* empty */ { $$ = $<obj>-1; }
+ | limit_block common_block
+ | limit_block stmt_separator
+ | limit_block limit_config
+ {
+ $$ = $1;
+ }
+ | limit_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
+ ;
+
+secmark_block : /* empty */ { $$ = $<obj>-1; }
+ | secmark_block common_block
+ | secmark_block stmt_separator
+ | secmark_block secmark_config
+ {
+ $$ = $1;
+ }
+ | secmark_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
+ ;
+
+synproxy_block : /* empty */ { $$ = $<obj>-1; }
+ | synproxy_block common_block
+ | synproxy_block stmt_separator
+ | synproxy_block synproxy_config
+ {
+ $$ = $1;
+ }
+ | synproxy_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
+ ;
+
+type_identifier : STRING { $$ = $1; }
+ | MARK { $$ = xstrdup("mark"); }
+ | DSCP { $$ = xstrdup("dscp"); }
+ | ECN { $$ = xstrdup("ecn"); }
+ | CLASSID { $$ = xstrdup("classid"); }
+ ;
+
+hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec
+ {
+ const char *chain_type = chain_type_name_lookup($3);
+
+ if (chain_type == NULL) {
+ erec_queue(error(&@3, "unknown chain type"),
+ state->msgs);
+ xfree($3);
+ YYERROR;
+ }
+ $<chain>0->type.loc = @3;
+ $<chain>0->type.str = xstrdup(chain_type);
+ xfree($3);
+
+ $<chain>0->loc = @$;
+ $<chain>0->hook.loc = @5;
+ $<chain>0->hook.name = chain_hookname_lookup($5);
+ if ($<chain>0->hook.name == NULL) {
+ erec_queue(error(&@5, "unknown chain hook"),
+ state->msgs);
+ xfree($5);
+ YYERROR;
+ }
+ xfree($5);
+
+ $<chain>0->dev_expr = $6;
+ $<chain>0->priority = $7;
+ $<chain>0->flags |= CHAIN_F_BASECHAIN;
+ }
+ ;
+
+prio_spec : PRIORITY extended_prio_spec
+ {
+ $$ = $2;
+ $$.loc = @$;
+ }
+ ;
+
+extended_prio_name : OUT
+ {
+ $$ = strdup("out");
+ }
+ | STRING
+ ;
+
+extended_prio_spec : int_num
+ {
+ struct prio_spec spec = {0};
+
+ spec.expr = constant_expr_alloc(&@$, &integer_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(int) *
+ BITS_PER_BYTE, &$1);
+ $$ = spec;
+ }
+ | variable_expr
+ {
+ struct prio_spec spec = {0};
+
+ spec.expr = $1;
+ $$ = spec;
+ }
+ | extended_prio_name
+ {
+ struct prio_spec spec = {0};
+
+ spec.expr = constant_expr_alloc(&@$, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen($1) * BITS_PER_BYTE,
+ $1);
+ xfree($1);
+ $$ = spec;
+ }
+ | extended_prio_name PLUS NUM
+ {
+ struct prio_spec spec = {0};
+
+ char str[NFT_NAME_MAXLEN];
+ snprintf(str, sizeof(str), "%s + %" PRIu64, $1, $3);
+ spec.expr = constant_expr_alloc(&@$, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(str) * BITS_PER_BYTE,
+ str);
+ xfree($1);
+ $$ = spec;
+ }
+ | extended_prio_name DASH NUM
+ {
+ struct prio_spec spec = {0};
+ char str[NFT_NAME_MAXLEN];
+
+ snprintf(str, sizeof(str), "%s - %" PRIu64, $1, $3);
+ spec.expr = constant_expr_alloc(&@$, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(str) * BITS_PER_BYTE,
+ str);
+ xfree($1);
+ $$ = spec;
+ }
+ ;
+
+int_num : NUM { $$ = $1; }
+ | DASH NUM { $$ = -$2; }
+ ;
+
+dev_spec : DEVICE string
+ {
+ struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $2);
+
+ if (!expr)
+ YYERROR;
+
+ $$ = compound_expr_alloc(&@$, EXPR_LIST);
+ compound_expr_add($$, expr);
+
+ }
+ | DEVICE variable_expr
+ {
+ datatype_set($2->sym->expr, &ifname_type);
+ $$ = compound_expr_alloc(&@$, EXPR_LIST);
+ compound_expr_add($$, $2);
+ }
+ | DEVICES '=' flowtable_expr
+ {
+ $$ = $3;
+ }
+ | /* empty */ { $$ = NULL; }
+ ;
+
+flags_spec : FLAGS OFFLOAD
+ {
+ $<chain>0->flags |= CHAIN_F_HW_OFFLOAD;
+ }
+ ;
+
+policy_spec : POLICY policy_expr close_scope_policy
+ {
+ if ($<chain>0->policy) {
+ erec_queue(error(&@$, "you cannot set chain policy twice"),
+ state->msgs);
+ expr_free($2);
+ YYERROR;
+ }
+ $<chain>0->policy = $2;
+ $<chain>0->policy->location = @$;
+ }
+ ;
+
+policy_expr : variable_expr
+ {
+ datatype_set($1->sym->expr, &policy_type);
+ $$ = $1;
+ }
+ | chain_policy
+ {
+ $$ = constant_expr_alloc(&@$, &integer_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(int) *
+ BITS_PER_BYTE, &$1);
+ }
+ ;
+
+chain_policy : ACCEPT { $$ = NF_ACCEPT; }
+ | DROP { $$ = NF_DROP; }
+ ;
+
+identifier : STRING
+ | LAST { $$ = xstrdup("last"); }
+ ;
+
+string : STRING
+ | QUOTED_STRING
+ | ASTERISK_STRING
+ ;
+
+time_spec : STRING
+ {
+ struct error_record *erec;
+ uint64_t res;
+
+ erec = time_parse(&@1, $1, &res);
+ xfree($1);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$ = res;
+ }
+ ;
+
+/* compatibility kludge to allow either 60, 60s, 1m, ... */
+time_spec_or_num_s : NUM
+ | time_spec { $$ = $1 / 1000u; }
+ ;
+
+family_spec : /* empty */ { $$ = NFPROTO_IPV4; }
+ | family_spec_explicit
+ ;
+
+family_spec_explicit : IP close_scope_ip { $$ = NFPROTO_IPV4; }
+ | IP6 close_scope_ip6 { $$ = NFPROTO_IPV6; }
+ | INET { $$ = NFPROTO_INET; }
+ | ARP close_scope_arp { $$ = NFPROTO_ARP; }
+ | BRIDGE { $$ = NFPROTO_BRIDGE; }
+ | NETDEV { $$ = NFPROTO_NETDEV; }
+ ;
+
+table_spec : family_spec identifier
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.family = $1;
+ $$.table.location = @2;
+ $$.table.name = $2;
+ }
+ ;
+
+tableid_spec : family_spec HANDLE NUM
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.family = $1;
+ $$.handle.id = $3;
+ $$.handle.location = @3;
+ }
+ ;
+
+chain_spec : table_spec identifier
+ {
+ $$ = $1;
+ $$.chain.name = $2;
+ $$.chain.location = @2;
+ }
+ ;
+
+chainid_spec : table_spec HANDLE NUM
+ {
+ $$ = $1;
+ $$.handle.location = @3;
+ $$.handle.id = $3;
+ }
+ ;
+
+chain_identifier : identifier
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.chain.name = $1;
+ $$.chain.location = @1;
+ }
+ ;
+
+set_spec : table_spec identifier
+ {
+ $$ = $1;
+ $$.set.name = $2;
+ $$.set.location = @2;
+ }
+ ;
+
+setid_spec : table_spec HANDLE NUM
+ {
+ $$ = $1;
+ $$.handle.location = @3;
+ $$.handle.id = $3;
+ }
+ ;
+
+set_identifier : identifier
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.set.name = $1;
+ $$.set.location = @1;
+ }
+ ;
+
+flowtable_spec : table_spec identifier
+ {
+ $$ = $1;
+ $$.flowtable.name = $2;
+ $$.flowtable.location = @2;
+ }
+ ;
+
+flowtableid_spec : table_spec HANDLE NUM
+ {
+ $$ = $1;
+ $$.handle.location = @3;
+ $$.handle.id = $3;
+ }
+ ;
+
+flowtable_identifier : identifier
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.flowtable.name = $1;
+ $$.flowtable.location = @1;
+ }
+ ;
+
+obj_spec : table_spec identifier
+ {
+ $$ = $1;
+ $$.obj.name = $2;
+ $$.obj.location = @2;
+ }
+ ;
+
+objid_spec : table_spec HANDLE NUM
+ {
+ $$ = $1;
+ $$.handle.location = @3;
+ $$.handle.id = $3;
+ }
+ ;
+
+obj_identifier : identifier
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.obj.name = $1;
+ $$.obj.location = @1;
+ }
+ ;
+
+handle_spec : HANDLE NUM
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.handle.location = @2;
+ $$.handle.id = $2;
+ }
+ ;
+
+position_spec : POSITION NUM
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.position.location = @$;
+ $$.position.id = $2;
+ }
+ ;
+
+index_spec : INDEX NUM
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.index.location = @$;
+ $$.index.id = $2 + 1;
+ }
+ ;
+
+rule_position : chain_spec
+ {
+ $$ = $1;
+ }
+ | chain_spec position_spec
+ {
+ handle_merge(&$1, &$2);
+ $$ = $1;
+ }
+ | chain_spec handle_spec
+ {
+ $2.position.location = $2.handle.location;
+ $2.position.id = $2.handle.id;
+ $2.handle.id = 0;
+ handle_merge(&$1, &$2);
+ $$ = $1;
+ }
+ | chain_spec index_spec
+ {
+ handle_merge(&$1, &$2);
+ $$ = $1;
+ }
+ ;
+
+ruleid_spec : chain_spec handle_spec
+ {
+ handle_merge(&$1, &$2);
+ $$ = $1;
+ }
+ ;
+
+comment_spec : COMMENT string
+ {
+ if (strlen($2) > NFTNL_UDATA_COMMENT_MAXLEN) {
+ erec_queue(error(&@2, "comment too long, %d characters maximum allowed",
+ NFTNL_UDATA_COMMENT_MAXLEN),
+ state->msgs);
+ xfree($2);
+ YYERROR;
+ }
+ $$ = $2;
+ }
+ ;
+
+ruleset_spec : /* empty */
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.family = NFPROTO_UNSPEC;
+ }
+ | family_spec_explicit
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.family = $1;
+ }
+ ;
+
+rule : rule_alloc
+ {
+ $$->comment = NULL;
+ }
+ | rule_alloc comment_spec
+ {
+ $$->comment = $2;
+ }
+ ;
+
+rule_alloc : stmt_list
+ {
+ struct stmt *i;
+
+ $$ = rule_alloc(&@$, NULL);
+ list_for_each_entry(i, $1, list)
+ $$->num_stmts++;
+ list_splice_tail($1, &$$->stmts);
+ xfree($1);
+ }
+ ;
+
+stmt_list : stmt
+ {
+ $$ = xmalloc(sizeof(*$$));
+ init_list_head($$);
+ list_add_tail(&$1->list, $$);
+ }
+ | stmt_list stmt
+ {
+ $$ = $1;
+ list_add_tail(&$2->list, $1);
+ }
+ ;
+
+stateful_stmt_list : stateful_stmt
+ {
+ $$ = xmalloc(sizeof(*$$));
+ init_list_head($$);
+ list_add_tail(&$1->list, $$);
+ }
+ | stateful_stmt_list stateful_stmt
+ {
+ $$ = $1;
+ list_add_tail(&$2->list, $1);
+ }
+ ;
+
+stateful_stmt : counter_stmt close_scope_counter
+ | limit_stmt
+ | quota_stmt
+ | connlimit_stmt
+ | last_stmt close_scope_last
+ ;
+
+stmt : verdict_stmt
+ | match_stmt
+ | meter_stmt
+ | payload_stmt
+ | stateful_stmt
+ | meta_stmt
+ | log_stmt close_scope_log
+ | reject_stmt close_scope_reject
+ | nat_stmt close_scope_nat
+ | tproxy_stmt close_scope_tproxy
+ | queue_stmt
+ | ct_stmt
+ | masq_stmt close_scope_nat
+ | redir_stmt close_scope_nat
+ | dup_stmt close_scope_dup
+ | fwd_stmt close_scope_fwd
+ | set_stmt
+ | map_stmt
+ | synproxy_stmt close_scope_synproxy
+ | chain_stmt
+ | optstrip_stmt
+ | xt_stmt close_scope_xt
+ ;
+
+xt_stmt : XT STRING string
+ {
+ $$ = NULL;
+ xfree($2);
+ xfree($3);
+ erec_queue(error(&@$, "unsupported xtables compat expression, use iptables-nft with this ruleset"),
+ state->msgs);
+ YYERROR;
+ }
+ ;
+
+chain_stmt_type : JUMP { $$ = NFT_JUMP; }
+ | GOTO { $$ = NFT_GOTO; }
+ ;
+
+chain_stmt : chain_stmt_type chain_block_alloc '{' subchain_block '}'
+ {
+ $2->location = @2;
+ close_scope(state);
+ $4->location = @4;
+ $$ = chain_stmt_alloc(&@$, $4, $1);
+ }
+ ;
+
+verdict_stmt : verdict_expr
+ {
+ $$ = verdict_stmt_alloc(&@$, $1);
+ }
+ | verdict_map_stmt
+ {
+ $$ = verdict_stmt_alloc(&@$, $1);
+ }
+ ;
+
+verdict_map_stmt : concat_expr VMAP verdict_map_expr
+ {
+ $$ = map_expr_alloc(&@$, $1, $3);
+ }
+ ;
+
+verdict_map_expr : '{' verdict_map_list_expr '}'
+ {
+ $2->location = @$;
+ $$ = $2;
+ }
+ | set_ref_expr
+ ;
+
+verdict_map_list_expr : verdict_map_list_member_expr
+ {
+ $$ = set_expr_alloc(&@$, NULL);
+ compound_expr_add($$, $1);
+ }
+ | verdict_map_list_expr COMMA verdict_map_list_member_expr
+ {
+ compound_expr_add($1, $3);
+ $$ = $1;
+ }
+ | verdict_map_list_expr COMMA opt_newline
+ ;
+
+verdict_map_list_member_expr: opt_newline set_elem_expr COLON verdict_expr opt_newline
+ {
+ $$ = mapping_expr_alloc(&@2, $2, $4);
+ }
+ ;
+
+connlimit_stmt : CT COUNT NUM close_scope_ct
+ {
+ $$ = connlimit_stmt_alloc(&@$);
+ $$->connlimit.count = $3;
+ }
+ | CT COUNT OVER NUM close_scope_ct
+ {
+ $$ = connlimit_stmt_alloc(&@$);
+ $$->connlimit.count = $4;
+ $$->connlimit.flags = NFT_CONNLIMIT_F_INV;
+ }
+ ;
+
+counter_stmt : counter_stmt_alloc
+ | counter_stmt_alloc counter_args
+
+counter_stmt_alloc : COUNTER
+ {
+ $$ = counter_stmt_alloc(&@$);
+ }
+ | COUNTER NAME stmt_expr
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_COUNTER;
+ $$->objref.expr = $3;
+ }
+ ;
+
+counter_args : counter_arg
+ {
+ $<stmt>$ = $<stmt>0;
+ }
+ | counter_args counter_arg
+ ;
+
+counter_arg : PACKETS NUM
+ {
+ $<stmt>0->counter.packets = $2;
+ }
+ | BYTES NUM
+ {
+ $<stmt>0->counter.bytes = $2;
+ }
+ ;
+
+last_stmt : LAST
+ {
+ $$ = last_stmt_alloc(&@$);
+ }
+ | LAST USED NEVER
+ {
+ $$ = last_stmt_alloc(&@$);
+ }
+ | LAST USED time_spec
+ {
+ $$ = last_stmt_alloc(&@$);
+ $$->last.used = $3;
+ $$->last.set = true;
+ }
+ ;
+
+log_stmt : log_stmt_alloc
+ | log_stmt_alloc log_args
+ ;
+
+log_stmt_alloc : LOG
+ {
+ $$ = log_stmt_alloc(&@$);
+ }
+ ;
+
+log_args : log_arg
+ {
+ $<stmt>$ = $<stmt>0;
+ }
+ | log_args log_arg
+ ;
+
+log_arg : PREFIX string
+ {
+ struct scope *scope = current_scope(state);
+ bool done = false, another_var = false;
+ char *start, *end, scratch = '\0';
+ struct expr *expr, *item;
+ struct symbol *sym;
+ enum {
+ PARSE_TEXT,
+ PARSE_VAR,
+ } prefix_state;
+
+ /* No variables in log prefix, skip. */
+ if (!strchr($2, '$')) {
+ expr = constant_expr_alloc(&@$, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ (strlen($2) + 1) * BITS_PER_BYTE, $2);
+ xfree($2);
+ $<stmt>0->log.prefix = expr;
+ $<stmt>0->log.flags |= STMT_LOG_PREFIX;
+ break;
+ }
+
+ /* Parse variables in log prefix string using a
+ * state machine parser with two states. This
+ * parser creates list of expressions composed
+ * of constant and variable expressions.
+ */
+ expr = compound_expr_alloc(&@$, EXPR_LIST);
+
+ start = (char *)$2;
+
+ if (*start != '$') {
+ prefix_state = PARSE_TEXT;
+ } else {
+ prefix_state = PARSE_VAR;
+ start++;
+ }
+ end = start;
+
+ /* Not nice, but works. */
+ while (!done) {
+ switch (prefix_state) {
+ case PARSE_TEXT:
+ while (*end != '\0' && *end != '$')
+ end++;
+
+ if (*end == '\0')
+ done = true;
+
+ *end = '\0';
+ item = constant_expr_alloc(&@$, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ (strlen(start) + 1) * BITS_PER_BYTE,
+ start);
+ compound_expr_add(expr, item);
+
+ if (done)
+ break;
+
+ start = end + 1;
+ end = start;
+
+ /* fall through */
+ case PARSE_VAR:
+ while (isalnum(*end) || *end == '_')
+ end++;
+
+ if (*end == '\0')
+ done = true;
+ else if (*end == '$')
+ another_var = true;
+ else
+ scratch = *end;
+
+ *end = '\0';
+
+ sym = symbol_get(scope, start);
+ if (!sym) {
+ sym = symbol_lookup_fuzzy(scope, start);
+ if (sym) {
+ erec_queue(error(&@2, "unknown identifier '%s'; "
+ "did you mean identifier ‘%s’?",
+ start, sym->identifier),
+ state->msgs);
+ } else {
+ erec_queue(error(&@2, "unknown identifier '%s'",
+ start),
+ state->msgs);
+ }
+ expr_free(expr);
+ xfree($2);
+ YYERROR;
+ }
+ item = variable_expr_alloc(&@$, scope, sym);
+ compound_expr_add(expr, item);
+
+ if (done)
+ break;
+
+ /* Restore original byte after
+ * symbol lookup.
+ */
+ if (scratch) {
+ *end = scratch;
+ scratch = '\0';
+ }
+
+ start = end;
+ if (another_var) {
+ another_var = false;
+ start++;
+ prefix_state = PARSE_VAR;
+ } else {
+ prefix_state = PARSE_TEXT;
+ }
+ end = start;
+ break;
+ }
+ }
+
+ xfree($2);
+ $<stmt>0->log.prefix = expr;
+ $<stmt>0->log.flags |= STMT_LOG_PREFIX;
+ }
+ | GROUP NUM
+ {
+ $<stmt>0->log.group = $2;
+ $<stmt>0->log.flags |= STMT_LOG_GROUP;
+ }
+ | SNAPLEN NUM
+ {
+ $<stmt>0->log.snaplen = $2;
+ $<stmt>0->log.flags |= STMT_LOG_SNAPLEN;
+ }
+ | QUEUE_THRESHOLD NUM
+ {
+ $<stmt>0->log.qthreshold = $2;
+ $<stmt>0->log.flags |= STMT_LOG_QTHRESHOLD;
+ }
+ | LEVEL level_type
+ {
+ $<stmt>0->log.level = $2;
+ $<stmt>0->log.flags |= STMT_LOG_LEVEL;
+ }
+ | FLAGS log_flags
+ {
+ $<stmt>0->log.logflags |= $2;
+ }
+ ;
+
+level_type : string
+ {
+ if (!strcmp("emerg", $1))
+ $$ = NFT_LOGLEVEL_EMERG;
+ else if (!strcmp("alert", $1))
+ $$ = NFT_LOGLEVEL_ALERT;
+ else if (!strcmp("crit", $1))
+ $$ = NFT_LOGLEVEL_CRIT;
+ else if (!strcmp("err", $1))
+ $$ = NFT_LOGLEVEL_ERR;
+ else if (!strcmp("warn", $1))
+ $$ = NFT_LOGLEVEL_WARNING;
+ else if (!strcmp("notice", $1))
+ $$ = NFT_LOGLEVEL_NOTICE;
+ else if (!strcmp("info", $1))
+ $$ = NFT_LOGLEVEL_INFO;
+ else if (!strcmp("debug", $1))
+ $$ = NFT_LOGLEVEL_DEBUG;
+ else if (!strcmp("audit", $1))
+ $$ = NFT_LOGLEVEL_AUDIT;
+ else {
+ erec_queue(error(&@1, "invalid log level"),
+ state->msgs);
+ xfree($1);
+ YYERROR;
+ }
+ xfree($1);
+ }
+ ;
+
+log_flags : TCP log_flags_tcp close_scope_tcp
+ {
+ $$ = $2;
+ }
+ | IP OPTIONS close_scope_ip
+ {
+ $$ = NF_LOG_IPOPT;
+ }
+ | SKUID
+ {
+ $$ = NF_LOG_UID;
+ }
+ | ETHER close_scope_eth
+ {
+ $$ = NF_LOG_MACDECODE;
+ }
+ | ALL
+ {
+ $$ = NF_LOG_MASK;
+ }
+ ;
+
+log_flags_tcp : log_flags_tcp COMMA log_flag_tcp
+ {
+ $$ = $1 | $3;
+ }
+ | log_flag_tcp
+ ;
+
+log_flag_tcp : SEQUENCE
+ {
+ $$ = NF_LOG_TCPSEQ;
+ }
+ | OPTIONS
+ {
+ $$ = NF_LOG_TCPOPT;
+ }
+ ;
+
+limit_stmt : LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit
+ {
+ if ($5 == 0) {
+ erec_queue(error(&@5, "packet limit burst must be > 0"),
+ state->msgs);
+ YYERROR;
+ }
+ $$ = limit_stmt_alloc(&@$);
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
+ $$->limit.type = NFT_LIMIT_PKTS;
+ $$->limit.flags = $3;
+ }
+ | LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit
+ {
+ $$ = limit_stmt_alloc(&@$);
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
+ $$->limit.type = NFT_LIMIT_PKT_BYTES;
+ $$->limit.flags = $3;
+ }
+ | LIMIT NAME stmt_expr close_scope_limit
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_LIMIT;
+ $$->objref.expr = $3;
+ }
+ ;
+
+quota_mode : OVER { $$ = NFT_QUOTA_F_INV; }
+ | UNTIL { $$ = 0; }
+ | /* empty */ { $$ = 0; }
+ ;
+
+quota_unit : BYTES { $$ = xstrdup("bytes"); }
+ | STRING { $$ = $1; }
+ ;
+
+quota_used : /* empty */ { $$ = 0; }
+ | USED NUM quota_unit
+ {
+ struct error_record *erec;
+ uint64_t rate;
+
+ erec = data_unit_parse(&@$, $3, &rate);
+ xfree($3);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$ = $2 * rate;
+ }
+ ;
+
+quota_stmt : QUOTA quota_mode NUM quota_unit quota_used close_scope_quota
+ {
+ struct error_record *erec;
+ uint64_t rate;
+
+ erec = data_unit_parse(&@$, $4, &rate);
+ xfree($4);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$ = quota_stmt_alloc(&@$);
+ $$->quota.bytes = $3 * rate;
+ $$->quota.used = $5;
+ $$->quota.flags = $2;
+ }
+ | QUOTA NAME stmt_expr close_scope_quota
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_QUOTA;
+ $$->objref.expr = $3;
+ }
+ ;
+
+limit_mode : OVER { $$ = NFT_LIMIT_F_INV; }
+ | UNTIL { $$ = 0; }
+ | /* empty */ { $$ = 0; }
+ ;
+
+limit_burst_pkts : /* empty */ { $$ = 5; }
+ | BURST NUM PACKETS { $$ = $2; }
+ ;
+
+limit_rate_pkts : NUM SLASH time_unit
+ {
+ $$.rate = $1;
+ $$.unit = $3;
+ }
+ ;
+
+limit_burst_bytes : /* empty */ { $$ = 0; }
+ | BURST limit_bytes { $$ = $2; }
+ ;
+
+limit_rate_bytes : NUM STRING
+ {
+ struct error_record *erec;
+ uint64_t rate, unit;
+
+ erec = rate_parse(&@$, $2, &rate, &unit);
+ xfree($2);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$.rate = rate * $1;
+ $$.unit = unit;
+ }
+ | limit_bytes SLASH time_unit
+ {
+ $$.rate = $1;
+ $$.unit = $3;
+ }
+ ;
+
+limit_bytes : NUM BYTES { $$ = $1; }
+ | NUM STRING
+ {
+ struct error_record *erec;
+ uint64_t rate;
+
+ erec = data_unit_parse(&@$, $2, &rate);
+ xfree($2);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$ = $1 * rate;
+ }
+ ;
+
+time_unit : SECOND { $$ = 1ULL; }
+ | MINUTE { $$ = 1ULL * 60; }
+ | HOUR { $$ = 1ULL * 60 * 60; }
+ | DAY { $$ = 1ULL * 60 * 60 * 24; }
+ | WEEK { $$ = 1ULL * 60 * 60 * 24 * 7; }
+ ;
+
+reject_stmt : reject_stmt_alloc reject_opts
+ ;
+
+reject_stmt_alloc : _REJECT
+ {
+ $$ = reject_stmt_alloc(&@$);
+ }
+ ;
+
+reject_with_expr : STRING
+ {
+ $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
+ current_scope(state), $1);
+ xfree($1);
+ }
+ | integer_expr { $$ = $1; }
+ ;
+
+reject_opts : /* empty */
+ {
+ $<stmt>0->reject.type = -1;
+ $<stmt>0->reject.icmp_code = -1;
+ }
+ | WITH ICMP TYPE reject_with_expr close_scope_type close_scope_icmp
+ {
+ $<stmt>0->reject.family = NFPROTO_IPV4;
+ $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
+ $<stmt>0->reject.expr = $4;
+ datatype_set($<stmt>0->reject.expr, &icmp_code_type);
+ }
+ | WITH ICMP reject_with_expr
+ {
+ $<stmt>0->reject.family = NFPROTO_IPV4;
+ $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
+ $<stmt>0->reject.expr = $3;
+ datatype_set($<stmt>0->reject.expr, &icmp_code_type);
+ }
+ | WITH ICMP6 TYPE reject_with_expr close_scope_type close_scope_icmp
+ {
+ $<stmt>0->reject.family = NFPROTO_IPV6;
+ $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
+ $<stmt>0->reject.expr = $4;
+ datatype_set($<stmt>0->reject.expr, &icmpv6_code_type);
+ }
+ | WITH ICMP6 reject_with_expr
+ {
+ $<stmt>0->reject.family = NFPROTO_IPV6;
+ $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
+ $<stmt>0->reject.expr = $3;
+ datatype_set($<stmt>0->reject.expr, &icmpv6_code_type);
+ }
+ | WITH ICMPX TYPE reject_with_expr close_scope_type
+ {
+ $<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
+ $<stmt>0->reject.expr = $4;
+ datatype_set($<stmt>0->reject.expr, &icmpx_code_type);
+ }
+ | WITH ICMPX reject_with_expr
+ {
+ $<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
+ $<stmt>0->reject.expr = $3;
+ datatype_set($<stmt>0->reject.expr, &icmpx_code_type);
+ }
+ | WITH TCP close_scope_tcp RESET close_scope_reset
+ {
+ $<stmt>0->reject.type = NFT_REJECT_TCP_RST;
+ }
+ ;
+
+nat_stmt : nat_stmt_alloc nat_stmt_args
+ ;
+
+nat_stmt_alloc : SNAT { $$ = nat_stmt_alloc(&@$, __NFT_NAT_SNAT); }
+ | DNAT { $$ = nat_stmt_alloc(&@$, __NFT_NAT_DNAT); }
+ ;
+
+tproxy_stmt : TPROXY TO stmt_expr
+ {
+ $$ = tproxy_stmt_alloc(&@$);
+ $$->tproxy.family = NFPROTO_UNSPEC;
+ $$->tproxy.addr = $3;
+ }
+ | TPROXY nf_key_proto TO stmt_expr
+ {
+ $$ = tproxy_stmt_alloc(&@$);
+ $$->tproxy.family = $2;
+ $$->tproxy.addr = $4;
+ }
+ | TPROXY TO COLON stmt_expr
+ {
+ $$ = tproxy_stmt_alloc(&@$);
+ $$->tproxy.family = NFPROTO_UNSPEC;
+ $$->tproxy.port = $4;
+ }
+ | TPROXY TO stmt_expr COLON stmt_expr
+ {
+ $$ = tproxy_stmt_alloc(&@$);
+ $$->tproxy.family = NFPROTO_UNSPEC;
+ $$->tproxy.addr = $3;
+ $$->tproxy.port = $5;
+ }
+ | TPROXY nf_key_proto TO stmt_expr COLON stmt_expr
+ {
+ $$ = tproxy_stmt_alloc(&@$);
+ $$->tproxy.family = $2;
+ $$->tproxy.addr = $4;
+ $$->tproxy.port = $6;
+ }
+ | TPROXY nf_key_proto TO COLON stmt_expr
+ {
+ $$ = tproxy_stmt_alloc(&@$);
+ $$->tproxy.family = $2;
+ $$->tproxy.port = $5;
+ }
+ ;
+
+synproxy_stmt : synproxy_stmt_alloc
+ | synproxy_stmt_alloc synproxy_args
+ ;
+
+synproxy_stmt_alloc : SYNPROXY
+ {
+ $$ = synproxy_stmt_alloc(&@$);
+ }
+ | SYNPROXY NAME stmt_expr
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_SYNPROXY;
+ $$->objref.expr = $3;
+ }
+ ;
+
+synproxy_args : synproxy_arg
+ {
+ $<stmt>$ = $<stmt>0;
+ }
+ | synproxy_args synproxy_arg
+ ;
+
+synproxy_arg : MSS NUM
+ {
+ $<stmt>0->synproxy.mss = $2;
+ $<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_MSS;
+ }
+ | WSCALE NUM
+ {
+ $<stmt>0->synproxy.wscale = $2;
+ $<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
+ }
+ | TIMESTAMP
+ {
+ $<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_TIMESTAMP;
+ }
+ | SACK_PERM
+ {
+ $<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_SACK_PERM;
+ }
+ ;
+
+synproxy_config : MSS NUM WSCALE NUM synproxy_ts synproxy_sack
+ {
+ struct synproxy *synproxy;
+ uint32_t flags = 0;
+
+ synproxy = &$<obj>0->synproxy;
+ synproxy->mss = $2;
+ flags |= NF_SYNPROXY_OPT_MSS;
+ synproxy->wscale = $4;
+ flags |= NF_SYNPROXY_OPT_WSCALE;
+ if ($5)
+ flags |= $5;
+ if ($6)
+ flags |= $6;
+ synproxy->flags = flags;
+ }
+ | MSS NUM stmt_separator WSCALE NUM stmt_separator synproxy_ts synproxy_sack
+ {
+ struct synproxy *synproxy;
+ uint32_t flags = 0;
+
+ synproxy = &$<obj>0->synproxy;
+ synproxy->mss = $2;
+ flags |= NF_SYNPROXY_OPT_MSS;
+ synproxy->wscale = $5;
+ flags |= NF_SYNPROXY_OPT_WSCALE;
+ if ($7)
+ flags |= $7;
+ if ($8)
+ flags |= $8;
+ synproxy->flags = flags;
+ }
+ ;
+
+synproxy_obj : /* empty */
+ {
+ $$ = obj_alloc(&@$);
+ $$->type = NFT_OBJECT_SYNPROXY;
+ }
+ ;
+
+synproxy_ts : /* empty */ { $$ = 0; }
+ | TIMESTAMP
+ {
+ $$ = NF_SYNPROXY_OPT_TIMESTAMP;
+ }
+ ;
+
+synproxy_sack : /* empty */ { $$ = 0; }
+ | SACK_PERM
+ {
+ $$ = NF_SYNPROXY_OPT_SACK_PERM;
+ }
+ ;
+
+primary_stmt_expr : symbol_expr { $$ = $1; }
+ | integer_expr { $$ = $1; }
+ | boolean_expr { $$ = $1; }
+ | meta_expr { $$ = $1; }
+ | rt_expr { $$ = $1; }
+ | ct_expr { $$ = $1; }
+ | numgen_expr { $$ = $1; }
+ | hash_expr { $$ = $1; }
+ | payload_expr { $$ = $1; }
+ | keyword_expr { $$ = $1; }
+ | socket_expr { $$ = $1; }
+ | osf_expr { $$ = $1; }
+ | '(' basic_stmt_expr ')' { $$ = $2; }
+ ;
+
+shift_stmt_expr : primary_stmt_expr
+ | shift_stmt_expr LSHIFT primary_stmt_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_LSHIFT, $1, $3);
+ }
+ | shift_stmt_expr RSHIFT primary_stmt_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_RSHIFT, $1, $3);
+ }
+ ;
+
+and_stmt_expr : shift_stmt_expr
+ | and_stmt_expr AMPERSAND shift_stmt_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_AND, $1, $3);
+ }
+ ;
+
+exclusive_or_stmt_expr : and_stmt_expr
+ | exclusive_or_stmt_expr CARET and_stmt_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_XOR, $1, $3);
+ }
+ ;
+
+inclusive_or_stmt_expr : exclusive_or_stmt_expr
+ | inclusive_or_stmt_expr '|' exclusive_or_stmt_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_OR, $1, $3);
+ }
+ ;
+
+basic_stmt_expr : inclusive_or_stmt_expr
+ ;
+
+concat_stmt_expr : basic_stmt_expr
+ | concat_stmt_expr DOT primary_stmt_expr
+ {
+ struct location rhs[] = {
+ [1] = @2,
+ [2] = @3,
+ };
+
+ $$ = handle_concat_expr(&@$, $$, $1, $3, rhs);
+ }
+ ;
+
+map_stmt_expr_set : set_expr
+ | set_ref_expr
+ ;
+
+map_stmt_expr : concat_stmt_expr MAP map_stmt_expr_set
+ {
+ $$ = map_expr_alloc(&@$, $1, $3);
+ }
+ | concat_stmt_expr { $$ = $1; }
+ ;
+
+prefix_stmt_expr : basic_stmt_expr SLASH NUM
+ {
+ $$ = prefix_expr_alloc(&@$, $1, $3);
+ }
+ ;
+
+range_stmt_expr : basic_stmt_expr DASH basic_stmt_expr
+ {
+ $$ = range_expr_alloc(&@$, $1, $3);
+ }
+ ;
+
+multiton_stmt_expr : prefix_stmt_expr
+ | range_stmt_expr
+ ;
+
+stmt_expr : map_stmt_expr
+ | multiton_stmt_expr
+ | list_stmt_expr
+ ;
+
+nat_stmt_args : stmt_expr
+ {
+ $<stmt>0->nat.addr = $1;
+ }
+ | TO stmt_expr
+ {
+ $<stmt>0->nat.addr = $2;
+ }
+ | nf_key_proto TO stmt_expr
+ {
+ $<stmt>0->nat.family = $1;
+ $<stmt>0->nat.addr = $3;
+ }
+ | stmt_expr COLON stmt_expr
+ {
+ $<stmt>0->nat.addr = $1;
+ $<stmt>0->nat.proto = $3;
+ }
+ | TO stmt_expr COLON stmt_expr
+ {
+ $<stmt>0->nat.addr = $2;
+ $<stmt>0->nat.proto = $4;
+ }
+ | nf_key_proto TO stmt_expr COLON stmt_expr
+ {
+ $<stmt>0->nat.family = $1;
+ $<stmt>0->nat.addr = $3;
+ $<stmt>0->nat.proto = $5;
+ }
+ | COLON stmt_expr
+ {
+ $<stmt>0->nat.proto = $2;
+ }
+ | TO COLON stmt_expr
+ {
+ $<stmt>0->nat.proto = $3;
+ }
+ | nat_stmt_args nf_nat_flags
+ {
+ $<stmt>0->nat.flags = $2;
+ }
+ | nf_key_proto ADDR DOT PORT TO stmt_expr
+ {
+ $<stmt>0->nat.family = $1;
+ $<stmt>0->nat.addr = $6;
+ $<stmt>0->nat.type_flags = STMT_NAT_F_CONCAT;
+ }
+ | nf_key_proto INTERVAL TO stmt_expr
+ {
+ $<stmt>0->nat.family = $1;
+ $<stmt>0->nat.addr = $4;
+ }
+ | INTERVAL TO stmt_expr
+ {
+ $<stmt>0->nat.addr = $3;
+ }
+ | nf_key_proto PREFIX TO stmt_expr
+ {
+ $<stmt>0->nat.family = $1;
+ $<stmt>0->nat.addr = $4;
+ $<stmt>0->nat.type_flags =
+ STMT_NAT_F_PREFIX;
+ $<stmt>0->nat.flags |= NF_NAT_RANGE_NETMAP;
+ }
+ | PREFIX TO stmt_expr
+ {
+ $<stmt>0->nat.addr = $3;
+ $<stmt>0->nat.type_flags =
+ STMT_NAT_F_PREFIX;
+ $<stmt>0->nat.flags |= NF_NAT_RANGE_NETMAP;
+ }
+ ;
+
+masq_stmt : masq_stmt_alloc masq_stmt_args
+ | masq_stmt_alloc
+ ;
+
+masq_stmt_alloc : MASQUERADE { $$ = nat_stmt_alloc(&@$, NFT_NAT_MASQ); }
+ ;
+
+masq_stmt_args : TO COLON stmt_expr
+ {
+ $<stmt>0->nat.proto = $3;
+ }
+ | TO COLON stmt_expr nf_nat_flags
+ {
+ $<stmt>0->nat.proto = $3;
+ $<stmt>0->nat.flags = $4;
+ }
+ | nf_nat_flags
+ {
+ $<stmt>0->nat.flags = $1;
+ }
+ ;
+
+redir_stmt : redir_stmt_alloc redir_stmt_arg
+ | redir_stmt_alloc
+ ;
+
+redir_stmt_alloc : REDIRECT { $$ = nat_stmt_alloc(&@$, NFT_NAT_REDIR); }
+ ;
+
+redir_stmt_arg : TO stmt_expr
+ {
+ $<stmt>0->nat.proto = $2;
+ }
+ | TO COLON stmt_expr
+ {
+ $<stmt>0->nat.proto = $3;
+ }
+ | nf_nat_flags
+ {
+ $<stmt>0->nat.flags = $1;
+ }
+ | TO stmt_expr nf_nat_flags
+ {
+ $<stmt>0->nat.proto = $2;
+ $<stmt>0->nat.flags = $3;
+ }
+ | TO COLON stmt_expr nf_nat_flags
+ {
+ $<stmt>0->nat.proto = $3;
+ $<stmt>0->nat.flags = $4;
+ }
+ ;
+
+dup_stmt : DUP TO stmt_expr
+ {
+ $$ = dup_stmt_alloc(&@$);
+ $$->dup.to = $3;
+ }
+ | DUP TO stmt_expr DEVICE stmt_expr
+ {
+ $$ = dup_stmt_alloc(&@$);
+ $$->dup.to = $3;
+ $$->dup.dev = $5;
+ }
+ ;
+
+fwd_stmt : FWD TO stmt_expr
+ {
+ $$ = fwd_stmt_alloc(&@$);
+ $$->fwd.dev = $3;
+ }
+ | FWD nf_key_proto TO stmt_expr DEVICE stmt_expr
+ {
+ $$ = fwd_stmt_alloc(&@$);
+ $$->fwd.family = $2;
+ $$->fwd.addr = $4;
+ $$->fwd.dev = $6;
+ }
+ ;
+
+nf_nat_flags : nf_nat_flag
+ | nf_nat_flags COMMA nf_nat_flag
+ {
+ $$ = $1 | $3;
+ }
+ ;
+
+nf_nat_flag : RANDOM { $$ = NF_NAT_RANGE_PROTO_RANDOM; }
+ | FULLY_RANDOM { $$ = NF_NAT_RANGE_PROTO_RANDOM_FULLY; }
+ | PERSISTENT { $$ = NF_NAT_RANGE_PERSISTENT; }
+ ;
+
+queue_stmt : queue_stmt_compat close_scope_queue
+ | QUEUE TO queue_stmt_expr close_scope_queue
+ {
+ $$ = queue_stmt_alloc(&@$, $3, 0);
+ }
+ | QUEUE FLAGS queue_stmt_flags TO queue_stmt_expr close_scope_queue
+ {
+ $$ = queue_stmt_alloc(&@$, $5, $3);
+ }
+ | QUEUE FLAGS queue_stmt_flags QUEUENUM queue_stmt_expr_simple close_scope_queue
+ {
+ $$ = queue_stmt_alloc(&@$, $5, $3);
+ }
+ ;
+
+queue_stmt_compat : queue_stmt_alloc
+ | queue_stmt_alloc queue_stmt_args
+ ;
+
+queue_stmt_alloc : QUEUE
+ {
+ $$ = queue_stmt_alloc(&@$, NULL, 0);
+ }
+ ;
+
+queue_stmt_args : queue_stmt_arg
+ {
+ $<stmt>$ = $<stmt>0;
+ }
+ | queue_stmt_args queue_stmt_arg
+ ;
+
+queue_stmt_arg : QUEUENUM queue_stmt_expr_simple
+ {
+ $<stmt>0->queue.queue = $2;
+ $<stmt>0->queue.queue->location = @$;
+ }
+ | queue_stmt_flags
+ {
+ $<stmt>0->queue.flags |= $1;
+ }
+ ;
+
+queue_expr : variable_expr
+ | integer_expr
+ ;
+
+queue_stmt_expr_simple : integer_expr
+ | variable_expr
+ | queue_expr DASH queue_expr
+ {
+ $$ = range_expr_alloc(&@$, $1, $3);
+ }
+ ;
+
+queue_stmt_expr : numgen_expr
+ | hash_expr
+ | map_expr
+ | queue_stmt_expr_simple
+ ;
+
+queue_stmt_flags : queue_stmt_flag
+ | queue_stmt_flags COMMA queue_stmt_flag
+ {
+ $$ = $1 | $3;
+ }
+ ;
+
+queue_stmt_flag : BYPASS { $$ = NFT_QUEUE_FLAG_BYPASS; }
+ | FANOUT { $$ = NFT_QUEUE_FLAG_CPU_FANOUT; }
+ ;
+
+set_elem_expr_stmt : set_elem_expr_stmt_alloc
+ | set_elem_expr_stmt_alloc set_elem_options
+ ;
+
+set_elem_expr_stmt_alloc: concat_expr
+ {
+ $$ = set_elem_expr_alloc(&@1, $1);
+ }
+ ;
+
+set_stmt : SET set_stmt_op set_elem_expr_stmt set_ref_expr
+ {
+ $$ = set_stmt_alloc(&@$);
+ $$->set.op = $2;
+ $$->set.key = $3;
+ $$->set.set = $4;
+ }
+ | set_stmt_op set_ref_expr '{' set_elem_expr_stmt '}'
+ {
+ $$ = set_stmt_alloc(&@$);
+ $$->set.op = $1;
+ $$->set.key = $4;
+ $$->set.set = $2;
+ }
+ | set_stmt_op set_ref_expr '{' set_elem_expr_stmt stateful_stmt_list '}'
+ {
+ $$ = set_stmt_alloc(&@$);
+ $$->set.op = $1;
+ $$->set.key = $4;
+ $$->set.set = $2;
+ list_splice_tail($5, &$$->set.stmt_list);
+ free($5);
+ }
+ ;
+
+set_stmt_op : ADD { $$ = NFT_DYNSET_OP_ADD; }
+ | UPDATE { $$ = NFT_DYNSET_OP_UPDATE; }
+ | DELETE { $$ = NFT_DYNSET_OP_DELETE; }
+ ;
+
+map_stmt : set_stmt_op set_ref_expr '{' set_elem_expr_stmt COLON set_elem_expr_stmt '}'
+ {
+ $$ = map_stmt_alloc(&@$);
+ $$->map.op = $1;
+ $$->map.key = $4;
+ $$->map.data = $6;
+ $$->map.set = $2;
+ }
+ | set_stmt_op set_ref_expr '{' set_elem_expr_stmt stateful_stmt_list COLON set_elem_expr_stmt '}'
+ {
+ $$ = map_stmt_alloc(&@$);
+ $$->map.op = $1;
+ $$->map.key = $4;
+ $$->map.data = $7;
+ $$->map.set = $2;
+ list_splice_tail($5, &$$->map.stmt_list);
+ free($5);
+ }
+ ;
+
+meter_stmt : flow_stmt_legacy_alloc flow_stmt_opts '{' meter_key_expr stmt '}'
+ {
+ $1->meter.key = $4;
+ $1->meter.stmt = $5;
+ $$->location = @$;
+ $$ = $1;
+ }
+ | meter_stmt_alloc { $$ = $1; }
+ ;
+
+flow_stmt_legacy_alloc : FLOW
+ {
+ $$ = meter_stmt_alloc(&@$);
+ }
+ ;
+
+flow_stmt_opts : flow_stmt_opt
+ {
+ $<stmt>$ = $<stmt>0;
+ }
+ | flow_stmt_opts flow_stmt_opt
+ ;
+
+flow_stmt_opt : TABLE identifier
+ {
+ $<stmt>0->meter.name = $2;
+ }
+ ;
+
+meter_stmt_alloc : METER identifier '{' meter_key_expr stmt '}'
+ {
+ $$ = meter_stmt_alloc(&@$);
+ $$->meter.name = $2;
+ $$->meter.size = 0;
+ $$->meter.key = $4;
+ $$->meter.stmt = $5;
+ $$->location = @$;
+ }
+ | METER identifier SIZE NUM '{' meter_key_expr stmt '}'
+ {
+ $$ = meter_stmt_alloc(&@$);
+ $$->meter.name = $2;
+ $$->meter.size = $4;
+ $$->meter.key = $6;
+ $$->meter.stmt = $7;
+ $$->location = @$;
+ }
+ ;
+
+match_stmt : relational_expr
+ {
+ $$ = expr_stmt_alloc(&@$, $1);
+ }
+ ;
+
+variable_expr : '$' identifier
+ {
+ struct scope *scope = current_scope(state);
+ struct symbol *sym;
+
+ sym = symbol_get(scope, $2);
+ if (!sym) {
+ sym = symbol_lookup_fuzzy(scope, $2);
+ if (sym) {
+ erec_queue(error(&@2, "unknown identifier '%s'; "
+ "did you mean identifier ‘%s’?",
+ $2, sym->identifier),
+ state->msgs);
+ } else {
+ erec_queue(error(&@2, "unknown identifier '%s'", $2),
+ state->msgs);
+ }
+ xfree($2);
+ YYERROR;
+ }
+
+ $$ = variable_expr_alloc(&@$, scope, sym);
+ xfree($2);
+ }
+ ;
+
+symbol_expr : variable_expr
+ | string
+ {
+ $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
+ current_scope(state),
+ $1);
+ xfree($1);
+ }
+ ;
+
+set_ref_expr : set_ref_symbol_expr
+ | variable_expr
+ ;
+
+set_ref_symbol_expr : AT identifier close_scope_at
+ {
+ $$ = symbol_expr_alloc(&@$, SYMBOL_SET,
+ current_scope(state),
+ $2);
+ xfree($2);
+ }
+ ;
+
+integer_expr : NUM
+ {
+ char str[64];
+
+ snprintf(str, sizeof(str), "%" PRIu64, $1);
+ $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
+ current_scope(state),
+ str);
+ }
+ ;
+
+primary_expr : symbol_expr { $$ = $1; }
+ | integer_expr { $$ = $1; }
+ | payload_expr { $$ = $1; }
+ | exthdr_expr { $$ = $1; }
+ | exthdr_exists_expr { $$ = $1; }
+ | meta_expr { $$ = $1; }
+ | socket_expr { $$ = $1; }
+ | rt_expr { $$ = $1; }
+ | ct_expr { $$ = $1; }
+ | numgen_expr { $$ = $1; }
+ | hash_expr { $$ = $1; }
+ | fib_expr { $$ = $1; }
+ | osf_expr { $$ = $1; }
+ | xfrm_expr { $$ = $1; }
+ | '(' basic_expr ')' { $$ = $2; }
+ ;
+
+fib_expr : FIB fib_tuple fib_result close_scope_fib
+ {
+ if (($2 & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == 0) {
+ erec_queue(error(&@2, "fib: need either saddr or daddr"), state->msgs);
+ YYERROR;
+ }
+
+ if (($2 & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) ==
+ (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) {
+ erec_queue(error(&@2, "fib: saddr and daddr are mutually exclusive"), state->msgs);
+ YYERROR;
+ }
+
+ if (($2 & (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) ==
+ (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) {
+ erec_queue(error(&@2, "fib: iif and oif are mutually exclusive"), state->msgs);
+ YYERROR;
+ }
+
+ $$ = fib_expr_alloc(&@$, $2, $3);
+ }
+ ;
+
+fib_result : OIF { $$ =NFT_FIB_RESULT_OIF; }
+ | OIFNAME { $$ =NFT_FIB_RESULT_OIFNAME; }
+ | TYPE close_scope_type { $$ =NFT_FIB_RESULT_ADDRTYPE; }
+ ;
+
+fib_flag : SADDR { $$ = NFTA_FIB_F_SADDR; }
+ | DADDR { $$ = NFTA_FIB_F_DADDR; }
+ | MARK { $$ = NFTA_FIB_F_MARK; }
+ | IIF { $$ = NFTA_FIB_F_IIF; }
+ | OIF { $$ = NFTA_FIB_F_OIF; }
+ ;
+
+fib_tuple : fib_flag DOT fib_tuple
+ {
+ $$ = $1 | $3;
+ }
+ | fib_flag
+ ;
+
+osf_expr : OSF osf_ttl HDRVERSION close_scope_osf
+ {
+ $$ = osf_expr_alloc(&@$, $2, NFT_OSF_F_VERSION);
+ }
+ | OSF osf_ttl NAME close_scope_osf
+ {
+ $$ = osf_expr_alloc(&@$, $2, 0);
+ }
+ ;
+
+osf_ttl : /* empty */
+ {
+ $$ = NF_OSF_TTL_TRUE;
+ }
+ | TTL STRING
+ {
+ if (!strcmp($2, "loose"))
+ $$ = NF_OSF_TTL_LESS;
+ else if (!strcmp($2, "skip"))
+ $$ = NF_OSF_TTL_NOCHECK;
+ else {
+ erec_queue(error(&@2, "invalid ttl option"),
+ state->msgs);
+ xfree($2);
+ YYERROR;
+ }
+ xfree($2);
+ }
+ ;
+
+shift_expr : primary_expr
+ | shift_expr LSHIFT primary_rhs_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_LSHIFT, $1, $3);
+ }
+ | shift_expr RSHIFT primary_rhs_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_RSHIFT, $1, $3);
+ }
+ ;
+
+and_expr : 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_rhs_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_XOR, $1, $3);
+ }
+ ;
+
+inclusive_or_expr : exclusive_or_expr
+ | inclusive_or_expr '|' exclusive_or_rhs_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_OR, $1, $3);
+ }
+ ;
+
+basic_expr : inclusive_or_expr
+ ;
+
+concat_expr : basic_expr
+ | concat_expr DOT basic_expr
+ {
+ struct location rhs[] = {
+ [1] = @2,
+ [2] = @3,
+ };
+
+ $$ = handle_concat_expr(&@$, $$, $1, $3, rhs);
+ }
+ ;
+
+prefix_rhs_expr : basic_rhs_expr SLASH NUM
+ {
+ $$ = prefix_expr_alloc(&@$, $1, $3);
+ }
+ ;
+
+range_rhs_expr : basic_rhs_expr DASH basic_rhs_expr
+ {
+ $$ = range_expr_alloc(&@$, $1, $3);
+ }
+ ;
+
+multiton_rhs_expr : prefix_rhs_expr
+ | range_rhs_expr
+ ;
+
+map_expr : concat_expr MAP rhs_expr
+ {
+ $$ = map_expr_alloc(&@$, $1, $3);
+ }
+ ;
+
+expr : concat_expr
+ | set_expr
+ | map_expr
+ ;
+
+set_expr : '{' set_list_expr '}'
+ {
+ $2->location = @$;
+ $$ = $2;
+ }
+ ;
+
+set_list_expr : set_list_member_expr
+ {
+ $$ = set_expr_alloc(&@$, NULL);
+ compound_expr_add($$, $1);
+ }
+ | set_list_expr COMMA set_list_member_expr
+ {
+ compound_expr_add($1, $3);
+ $$ = $1;
+ }
+ | set_list_expr COMMA opt_newline
+ ;
+
+set_list_member_expr : opt_newline set_expr opt_newline
+ {
+ $$ = $2;
+ }
+ | opt_newline set_elem_expr opt_newline
+ {
+ $$ = $2;
+ }
+ | opt_newline set_elem_expr COLON set_rhs_expr opt_newline
+ {
+ $$ = mapping_expr_alloc(&@2, $2, $4);
+ }
+ ;
+
+meter_key_expr : meter_key_expr_alloc
+ | meter_key_expr_alloc set_elem_options
+ {
+ $$->location = @$;
+ $$ = $1;
+ }
+ ;
+
+meter_key_expr_alloc : concat_expr
+ {
+ $$ = set_elem_expr_alloc(&@1, $1);
+ }
+ ;
+
+set_elem_expr : set_elem_expr_alloc
+ | set_elem_expr_alloc set_elem_expr_options
+ ;
+
+set_elem_key_expr : set_lhs_expr { $$ = $1; }
+ | ASTERISK { $$ = set_elem_catchall_expr_alloc(&@1); }
+ ;
+
+set_elem_expr_alloc : set_elem_key_expr set_elem_stmt_list
+ {
+ $$ = set_elem_expr_alloc(&@1, $1);
+ list_splice_tail($2, &$$->stmt_list);
+ xfree($2);
+ }
+ | set_elem_key_expr
+ {
+ $$ = set_elem_expr_alloc(&@1, $1);
+ }
+ ;
+
+set_elem_options : set_elem_option
+ {
+ $<expr>$ = $<expr>0;
+ }
+ | set_elem_options set_elem_option
+ ;
+
+set_elem_option : TIMEOUT time_spec
+ {
+ $<expr>0->timeout = $2;
+ }
+ | EXPIRES time_spec
+ {
+ $<expr>0->expiration = $2;
+ }
+ | comment_spec
+ {
+ if (already_set($<expr>0->comment, &@1, state)) {
+ xfree($1);
+ YYERROR;
+ }
+ $<expr>0->comment = $1;
+ }
+ ;
+
+set_elem_expr_options : set_elem_expr_option
+ {
+ $<expr>$ = $<expr>0;
+ }
+ | set_elem_expr_options set_elem_expr_option
+ ;
+
+set_elem_stmt_list : set_elem_stmt
+ {
+ $$ = xmalloc(sizeof(*$$));
+ init_list_head($$);
+ list_add_tail(&$1->list, $$);
+ }
+ | set_elem_stmt_list set_elem_stmt
+ {
+ $$ = $1;
+ list_add_tail(&$2->list, $1);
+ }
+ ;
+
+set_elem_stmt : COUNTER close_scope_counter
+ {
+ $$ = counter_stmt_alloc(&@$);
+ }
+ | COUNTER PACKETS NUM BYTES NUM close_scope_counter
+ {
+ $$ = counter_stmt_alloc(&@$);
+ $$->counter.packets = $3;
+ $$->counter.bytes = $5;
+ }
+ | LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit
+ {
+ if ($5 == 0) {
+ erec_queue(error(&@5, "limit burst must be > 0"),
+ state->msgs);
+ YYERROR;
+ }
+ $$ = limit_stmt_alloc(&@$);
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
+ $$->limit.type = NFT_LIMIT_PKTS;
+ $$->limit.flags = $3;
+ }
+ | LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit
+ {
+ if ($5 == 0) {
+ erec_queue(error(&@6, "limit burst must be > 0"),
+ state->msgs);
+ YYERROR;
+ }
+ $$ = limit_stmt_alloc(&@$);
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
+ $$->limit.type = NFT_LIMIT_PKT_BYTES;
+ $$->limit.flags = $3;
+ }
+ | CT COUNT NUM close_scope_ct
+ {
+ $$ = connlimit_stmt_alloc(&@$);
+ $$->connlimit.count = $3;
+ }
+ | CT COUNT OVER NUM close_scope_ct
+ {
+ $$ = connlimit_stmt_alloc(&@$);
+ $$->connlimit.count = $4;
+ $$->connlimit.flags = NFT_CONNLIMIT_F_INV;
+ }
+ | QUOTA quota_mode NUM quota_unit quota_used close_scope_quota
+ {
+ struct error_record *erec;
+ uint64_t rate;
+
+ erec = data_unit_parse(&@$, $4, &rate);
+ xfree($4);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$ = quota_stmt_alloc(&@$);
+ $$->quota.bytes = $3 * rate;
+ $$->quota.used = $5;
+ $$->quota.flags = $2;
+ }
+ | LAST USED NEVER close_scope_last
+ {
+ $$ = last_stmt_alloc(&@$);
+ }
+ | LAST USED time_spec close_scope_last
+ {
+ $$ = last_stmt_alloc(&@$);
+ $$->last.used = $3;
+ $$->last.set = true;
+ }
+ ;
+
+set_elem_expr_option : TIMEOUT time_spec
+ {
+ $<expr>0->timeout = $2;
+ }
+ | EXPIRES time_spec
+ {
+ $<expr>0->expiration = $2;
+ }
+ | comment_spec
+ {
+ if (already_set($<expr>0->comment, &@1, state)) {
+ xfree($1);
+ YYERROR;
+ }
+ $<expr>0->comment = $1;
+ }
+ ;
+
+set_lhs_expr : concat_rhs_expr
+ ;
+
+set_rhs_expr : concat_rhs_expr
+ | verdict_expr
+ ;
+
+initializer_expr : rhs_expr
+ | list_rhs_expr
+ | '{' '}' { $$ = compound_expr_alloc(&@$, EXPR_SET); }
+ | DASH NUM
+ {
+ int32_t num = -$2;
+
+ $$ = constant_expr_alloc(&@$, &integer_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(num) * BITS_PER_BYTE,
+ &num);
+ }
+ ;
+
+counter_config : PACKETS NUM BYTES NUM
+ {
+ struct counter *counter;
+
+ counter = &$<obj>0->counter;
+ counter->packets = $2;
+ counter->bytes = $4;
+ }
+ ;
+
+counter_obj : /* empty */
+ {
+ $$ = obj_alloc(&@$);
+ $$->type = NFT_OBJECT_COUNTER;
+ }
+ ;
+
+quota_config : quota_mode NUM quota_unit quota_used
+ {
+ struct error_record *erec;
+ struct quota *quota;
+ uint64_t rate;
+
+ erec = data_unit_parse(&@$, $3, &rate);
+ xfree($3);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+
+ quota = &$<obj>0->quota;
+ quota->bytes = $2 * rate;
+ quota->used = $4;
+ quota->flags = $1;
+ }
+ ;
+
+quota_obj : /* empty */
+ {
+ $$ = obj_alloc(&@$);
+ $$->type = NFT_OBJECT_QUOTA;
+ }
+ ;
+
+secmark_config : string
+ {
+ int ret;
+ struct secmark *secmark;
+
+ secmark = &$<obj>0->secmark;
+ ret = snprintf(secmark->ctx, sizeof(secmark->ctx), "%s", $1);
+ if (ret <= 0 || ret >= (int)sizeof(secmark->ctx)) {
+ erec_queue(error(&@1, "invalid context '%s', max length is %u\n", $1, (int)sizeof(secmark->ctx)), state->msgs);
+ xfree($1);
+ YYERROR;
+ }
+ xfree($1);
+ }
+ ;
+
+secmark_obj : /* empty */
+ {
+ $$ = obj_alloc(&@$);
+ $$->type = NFT_OBJECT_SECMARK;
+ }
+ ;
+
+ct_obj_type : HELPER { $$ = NFT_OBJECT_CT_HELPER; }
+ | TIMEOUT { $$ = NFT_OBJECT_CT_TIMEOUT; }
+ | EXPECTATION { $$ = NFT_OBJECT_CT_EXPECT; }
+ ;
+
+ct_cmd_type : HELPERS { $$ = CMD_OBJ_CT_HELPERS; }
+ | TIMEOUT { $$ = CMD_OBJ_CT_TIMEOUTS; }
+ | EXPECTATION { $$ = CMD_OBJ_CT_EXPECTATIONS; }
+ ;
+
+ct_l4protoname : TCP close_scope_tcp { $$ = IPPROTO_TCP; }
+ | UDP close_scope_udp { $$ = IPPROTO_UDP; }
+ ;
+
+ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator close_scope_type
+ {
+ struct ct_helper *ct;
+ int ret;
+
+ ct = &$<obj>0->ct_helper;
+
+ ret = snprintf(ct->name, sizeof(ct->name), "%s", $2);
+ if (ret <= 0 || ret >= (int)sizeof(ct->name)) {
+ erec_queue(error(&@2, "invalid name '%s', max length is %u\n", $2, (int)sizeof(ct->name)), state->msgs);
+ YYERROR;
+ }
+ xfree($2);
+
+ ct->l4proto = $4;
+ }
+ | L3PROTOCOL family_spec_explicit stmt_separator
+ {
+ $<obj>0->ct_helper.l3proto = $2;
+ }
+ ;
+
+timeout_states : timeout_state
+ {
+ $$ = xmalloc(sizeof(*$$));
+ init_list_head($$);
+ list_add_tail($1, $$);
+ }
+ | timeout_states COMMA timeout_state
+ {
+ list_add_tail($3, $1);
+ $$ = $1;
+ }
+ ;
+
+timeout_state : STRING COLON time_spec_or_num_s
+ {
+ struct timeout_state *ts;
+
+ ts = xzalloc(sizeof(*ts));
+ ts->timeout_str = $1;
+ ts->timeout_value = $3;
+ ts->location = @1;
+ init_list_head(&ts->head);
+ $$ = &ts->head;
+ }
+ ;
+
+ct_timeout_config : PROTOCOL ct_l4protoname stmt_separator
+ {
+ struct ct_timeout *ct;
+ int l4proto = $2;
+
+ ct = &$<obj>0->ct_timeout;
+ ct->l4proto = l4proto;
+ }
+ | POLICY '=' '{' timeout_states '}' stmt_separator close_scope_policy
+ {
+ struct ct_timeout *ct;
+
+ ct = &$<obj>0->ct_timeout;
+ list_splice_tail($4, &ct->timeout_list);
+ xfree($4);
+ }
+ | L3PROTOCOL family_spec_explicit stmt_separator
+ {
+ $<obj>0->ct_timeout.l3proto = $2;
+ }
+ ;
+
+ct_expect_config : PROTOCOL ct_l4protoname stmt_separator
+ {
+ $<obj>0->ct_expect.l4proto = $2;
+ }
+ | DPORT NUM stmt_separator
+ {
+ $<obj>0->ct_expect.dport = $2;
+ }
+ | TIMEOUT time_spec stmt_separator
+ {
+ $<obj>0->ct_expect.timeout = $2;
+ }
+ | SIZE NUM stmt_separator
+ {
+ $<obj>0->ct_expect.size = $2;
+ }
+ | L3PROTOCOL family_spec_explicit stmt_separator
+ {
+ $<obj>0->ct_expect.l3proto = $2;
+ }
+ ;
+
+ct_obj_alloc : /* empty */
+ {
+ $$ = obj_alloc(&@$);
+ }
+ ;
+
+limit_config : RATE limit_mode limit_rate_pkts limit_burst_pkts
+ {
+ struct limit *limit;
+
+ limit = &$<obj>0->limit;
+ limit->rate = $3.rate;
+ limit->unit = $3.unit;
+ limit->burst = $4;
+ limit->type = NFT_LIMIT_PKTS;
+ limit->flags = $2;
+ }
+ | RATE limit_mode limit_rate_bytes limit_burst_bytes
+ {
+ struct limit *limit;
+
+ limit = &$<obj>0->limit;
+ limit->rate = $3.rate;
+ limit->unit = $3.unit;
+ limit->burst = $4;
+ limit->type = NFT_LIMIT_PKT_BYTES;
+ limit->flags = $2;
+ }
+ ;
+
+limit_obj : /* empty */
+ {
+ $$ = obj_alloc(&@$);
+ $$->type = NFT_OBJECT_LIMIT;
+ }
+ ;
+
+relational_expr : expr /* implicit */ rhs_expr
+ {
+ $$ = relational_expr_alloc(&@$, OP_IMPLICIT, $1, $2);
+ }
+ | expr /* implicit */ list_rhs_expr
+ {
+ $$ = relational_expr_alloc(&@$, OP_IMPLICIT, $1, $2);
+ }
+ | expr /* implicit */ basic_rhs_expr SLASH list_rhs_expr
+ {
+ $$ = flagcmp_expr_alloc(&@$, OP_EQ, $1, $4, $2);
+ }
+ | expr /* implicit */ list_rhs_expr SLASH list_rhs_expr
+ {
+ $$ = flagcmp_expr_alloc(&@$, OP_EQ, $1, $4, $2);
+ }
+ | expr relational_op basic_rhs_expr SLASH list_rhs_expr
+ {
+ $$ = flagcmp_expr_alloc(&@$, $2, $1, $5, $3);
+ }
+ | expr relational_op list_rhs_expr SLASH list_rhs_expr
+ {
+ $$ = flagcmp_expr_alloc(&@$, $2, $1, $5, $3);
+ }
+ | expr relational_op rhs_expr
+ {
+ $$ = relational_expr_alloc(&@2, $2, $1, $3);
+ }
+ | expr relational_op list_rhs_expr
+ {
+ $$ = relational_expr_alloc(&@2, $2, $1, $3);
+ }
+ ;
+
+list_rhs_expr : basic_rhs_expr COMMA basic_rhs_expr
+ {
+ $$ = list_expr_alloc(&@$);
+ compound_expr_add($$, $1);
+ compound_expr_add($$, $3);
+ }
+ | list_rhs_expr COMMA basic_rhs_expr
+ {
+ $1->location = @$;
+ compound_expr_add($1, $3);
+ $$ = $1;
+ }
+ ;
+
+rhs_expr : concat_rhs_expr { $$ = $1; }
+ | set_expr { $$ = $1; }
+ | set_ref_symbol_expr { $$ = $1; }
+ ;
+
+shift_rhs_expr : primary_rhs_expr
+ | shift_rhs_expr LSHIFT primary_rhs_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_LSHIFT, $1, $3);
+ }
+ | shift_rhs_expr RSHIFT primary_rhs_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_RSHIFT, $1, $3);
+ }
+ ;
+
+and_rhs_expr : shift_rhs_expr
+ | and_rhs_expr AMPERSAND shift_rhs_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_AND, $1, $3);
+ }
+ ;
+
+exclusive_or_rhs_expr : and_rhs_expr
+ | exclusive_or_rhs_expr CARET and_rhs_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_XOR, $1, $3);
+ }
+ ;
+
+inclusive_or_rhs_expr : exclusive_or_rhs_expr
+ | inclusive_or_rhs_expr '|' exclusive_or_rhs_expr
+ {
+ $$ = binop_expr_alloc(&@$, OP_OR, $1, $3);
+ }
+ ;
+
+basic_rhs_expr : inclusive_or_rhs_expr
+ ;
+
+concat_rhs_expr : basic_rhs_expr
+ | multiton_rhs_expr
+ | concat_rhs_expr DOT multiton_rhs_expr
+ {
+ struct location rhs[] = {
+ [1] = @2,
+ [2] = @3,
+ };
+
+ $$ = handle_concat_expr(&@$, $$, $1, $3, rhs);
+ }
+ | concat_rhs_expr DOT basic_rhs_expr
+ {
+ struct location rhs[] = {
+ [1] = @2,
+ [2] = @3,
+ };
+
+ $$ = handle_concat_expr(&@$, $$, $1, $3, rhs);
+ }
+ ;
+
+boolean_keys : EXISTS { $$ = true; }
+ | MISSING { $$ = false; }
+ ;
+
+boolean_expr : boolean_keys
+ {
+ $$ = constant_expr_alloc(&@$, &boolean_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof($1) * BITS_PER_BYTE, &$1);
+ }
+ ;
+
+keyword_expr : ETHER close_scope_eth { $$ = symbol_value(&@$, "ether"); }
+ | IP close_scope_ip { $$ = symbol_value(&@$, "ip"); }
+ | IP6 close_scope_ip6 { $$ = symbol_value(&@$, "ip6"); }
+ | VLAN close_scope_vlan { $$ = symbol_value(&@$, "vlan"); }
+ | ARP close_scope_arp { $$ = symbol_value(&@$, "arp"); }
+ | DNAT close_scope_nat { $$ = symbol_value(&@$, "dnat"); }
+ | SNAT close_scope_nat { $$ = symbol_value(&@$, "snat"); }
+ | ECN { $$ = symbol_value(&@$, "ecn"); }
+ | RESET close_scope_reset { $$ = symbol_value(&@$, "reset"); }
+ | DESTROY close_scope_destroy { $$ = symbol_value(&@$, "destroy"); }
+ | ORIGINAL { $$ = symbol_value(&@$, "original"); }
+ | REPLY { $$ = symbol_value(&@$, "reply"); }
+ | LABEL { $$ = symbol_value(&@$, "label"); }
+ | LAST close_scope_last { $$ = symbol_value(&@$, "last"); }
+ ;
+
+primary_rhs_expr : symbol_expr { $$ = $1; }
+ | integer_expr { $$ = $1; }
+ | boolean_expr { $$ = $1; }
+ | keyword_expr { $$ = $1; }
+ | TCP close_scope_tcp
+ {
+ uint8_t data = IPPROTO_TCP;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | UDP close_scope_udp
+ {
+ uint8_t data = IPPROTO_UDP;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | UDPLITE close_scope_udplite
+ {
+ uint8_t data = IPPROTO_UDPLITE;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | ESP close_scope_esp
+ {
+ uint8_t data = IPPROTO_ESP;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | AH close_scope_ah
+ {
+ uint8_t data = IPPROTO_AH;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | ICMP close_scope_icmp
+ {
+ uint8_t data = IPPROTO_ICMP;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | IGMP
+ {
+ uint8_t data = IPPROTO_IGMP;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | ICMP6 close_scope_icmp
+ {
+ uint8_t data = IPPROTO_ICMPV6;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | GRE close_scope_gre
+ {
+ uint8_t data = IPPROTO_GRE;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | COMP close_scope_comp
+ {
+ uint8_t data = IPPROTO_COMP;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | DCCP close_scope_dccp
+ {
+ uint8_t data = IPPROTO_DCCP;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | SCTP close_scope_sctp
+ {
+ uint8_t data = IPPROTO_SCTP;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | REDIRECT close_scope_nat
+ {
+ uint8_t data = ICMP_REDIRECT;
+ $$ = constant_expr_alloc(&@$, &icmp_type_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | '(' basic_rhs_expr ')' { $$ = $2; }
+ ;
+
+relational_op : EQ { $$ = OP_EQ; }
+ | NEQ { $$ = OP_NEQ; }
+ | LT { $$ = OP_LT; }
+ | GT { $$ = OP_GT; }
+ | GTE { $$ = OP_GTE; }
+ | LTE { $$ = OP_LTE; }
+ | NOT { $$ = OP_NEG; }
+ ;
+
+verdict_expr : ACCEPT
+ {
+ $$ = verdict_expr_alloc(&@$, NF_ACCEPT, NULL);
+ }
+ | DROP
+ {
+ $$ = verdict_expr_alloc(&@$, NF_DROP, NULL);
+ }
+ | CONTINUE
+ {
+ $$ = verdict_expr_alloc(&@$, NFT_CONTINUE, NULL);
+ }
+ | JUMP chain_expr
+ {
+ $$ = verdict_expr_alloc(&@$, NFT_JUMP, $2);
+ }
+ | GOTO chain_expr
+ {
+ $$ = verdict_expr_alloc(&@$, NFT_GOTO, $2);
+ }
+ | RETURN
+ {
+ $$ = verdict_expr_alloc(&@$, NFT_RETURN, NULL);
+ }
+ ;
+
+chain_expr : variable_expr
+ | identifier
+ {
+ $$ = constant_expr_alloc(&@$, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen($1) * BITS_PER_BYTE,
+ $1);
+ xfree($1);
+ }
+ ;
+
+meta_expr : META meta_key close_scope_meta
+ {
+ $$ = meta_expr_alloc(&@$, $2);
+ }
+ | meta_key_unqualified
+ {
+ $$ = meta_expr_alloc(&@$, $1);
+ }
+ | META STRING close_scope_meta
+ {
+ struct error_record *erec;
+ unsigned int key;
+
+ erec = meta_key_parse(&@$, $2, &key);
+ xfree($2);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+
+ $$ = meta_expr_alloc(&@$, key);
+ }
+ ;
+
+meta_key : meta_key_qualified
+ | meta_key_unqualified
+ ;
+
+meta_key_qualified : LENGTH { $$ = NFT_META_LEN; }
+ | PROTOCOL { $$ = NFT_META_PROTOCOL; }
+ | PRIORITY { $$ = NFT_META_PRIORITY; }
+ | RANDOM { $$ = NFT_META_PRANDOM; }
+ | SECMARK close_scope_secmark { $$ = NFT_META_SECMARK; }
+ ;
+
+meta_key_unqualified : MARK { $$ = NFT_META_MARK; }
+ | IIF { $$ = NFT_META_IIF; }
+ | IIFNAME { $$ = NFT_META_IIFNAME; }
+ | IIFTYPE { $$ = NFT_META_IIFTYPE; }
+ | OIF { $$ = NFT_META_OIF; }
+ | OIFNAME { $$ = NFT_META_OIFNAME; }
+ | OIFTYPE { $$ = NFT_META_OIFTYPE; }
+ | SKUID { $$ = NFT_META_SKUID; }
+ | SKGID { $$ = NFT_META_SKGID; }
+ | NFTRACE { $$ = NFT_META_NFTRACE; }
+ | RTCLASSID { $$ = NFT_META_RTCLASSID; }
+ | IBRIPORT { $$ = NFT_META_BRI_IIFNAME; }
+ | OBRIPORT { $$ = NFT_META_BRI_OIFNAME; }
+ | IBRIDGENAME { $$ = NFT_META_BRI_IIFNAME; }
+ | OBRIDGENAME { $$ = NFT_META_BRI_OIFNAME; }
+ | PKTTYPE { $$ = NFT_META_PKTTYPE; }
+ | CPU { $$ = NFT_META_CPU; }
+ | IIFGROUP { $$ = NFT_META_IIFGROUP; }
+ | OIFGROUP { $$ = NFT_META_OIFGROUP; }
+ | CGROUP { $$ = NFT_META_CGROUP; }
+ | IPSEC close_scope_ipsec { $$ = NFT_META_SECPATH; }
+ | TIME { $$ = NFT_META_TIME_NS; }
+ | DAY { $$ = NFT_META_TIME_DAY; }
+ | HOUR { $$ = NFT_META_TIME_HOUR; }
+ ;
+
+meta_stmt : META meta_key SET stmt_expr close_scope_meta
+ {
+ switch ($2) {
+ case NFT_META_SECMARK:
+ switch ($4->etype) {
+ case EXPR_CT:
+ $$ = meta_stmt_alloc(&@$, $2, $4);
+ break;
+ default:
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_SECMARK;
+ $$->objref.expr = $4;
+ break;
+ }
+ break;
+ default:
+ $$ = meta_stmt_alloc(&@$, $2, $4);
+ break;
+ }
+ }
+ | meta_key_unqualified SET stmt_expr
+ {
+ $$ = meta_stmt_alloc(&@$, $1, $3);
+ }
+ | META STRING SET stmt_expr close_scope_meta
+ {
+ struct error_record *erec;
+ unsigned int key;
+
+ erec = meta_key_parse(&@$, $2, &key);
+ xfree($2);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+
+ $$ = meta_stmt_alloc(&@$, key, $4);
+ }
+ | NOTRACK
+ {
+ $$ = notrack_stmt_alloc(&@$);
+ }
+ | FLOW OFFLOAD AT string close_scope_at
+ {
+ $$ = flow_offload_stmt_alloc(&@$, $4);
+ }
+ | FLOW ADD AT string close_scope_at
+ {
+ $$ = flow_offload_stmt_alloc(&@$, $4);
+ }
+ ;
+
+socket_expr : SOCKET socket_key close_scope_socket
+ {
+ $$ = socket_expr_alloc(&@$, $2, 0);
+ }
+ | SOCKET CGROUPV2 LEVEL NUM close_scope_socket
+ {
+ $$ = socket_expr_alloc(&@$, NFT_SOCKET_CGROUPV2, $4);
+ }
+ ;
+
+socket_key : TRANSPARENT { $$ = NFT_SOCKET_TRANSPARENT; }
+ | MARK { $$ = NFT_SOCKET_MARK; }
+ | WILDCARD { $$ = NFT_SOCKET_WILDCARD; }
+ ;
+
+offset_opt : /* empty */ { $$ = 0; }
+ | OFFSET NUM { $$ = $2; }
+ ;
+
+numgen_type : INC { $$ = NFT_NG_INCREMENTAL; }
+ | RANDOM { $$ = NFT_NG_RANDOM; }
+ ;
+
+numgen_expr : NUMGEN numgen_type MOD NUM offset_opt close_scope_numgen
+ {
+ $$ = numgen_expr_alloc(&@$, $2, $4, $5);
+ }
+ ;
+
+xfrm_spnum : SPNUM NUM { $$ = $2; }
+ | { $$ = 0; }
+ ;
+
+xfrm_dir : IN { $$ = XFRM_POLICY_IN; }
+ | OUT { $$ = XFRM_POLICY_OUT; }
+ ;
+
+xfrm_state_key : SPI { $$ = NFT_XFRM_KEY_SPI; }
+ | REQID { $$ = NFT_XFRM_KEY_REQID; }
+ ;
+
+xfrm_state_proto_key : DADDR { $$ = NFT_XFRM_KEY_DADDR_IP4; }
+ | SADDR { $$ = NFT_XFRM_KEY_SADDR_IP4; }
+ ;
+
+xfrm_expr : IPSEC xfrm_dir xfrm_spnum xfrm_state_key close_scope_ipsec
+ {
+ if ($3 > 255) {
+ erec_queue(error(&@3, "value too large"), state->msgs);
+ YYERROR;
+ }
+ $$ = xfrm_expr_alloc(&@$, $2, $3, $4);
+ }
+ | IPSEC xfrm_dir xfrm_spnum nf_key_proto xfrm_state_proto_key close_scope_ipsec
+ {
+ enum nft_xfrm_keys xfrmk = $5;
+
+ switch ($4) {
+ case NFPROTO_IPV4:
+ break;
+ case NFPROTO_IPV6:
+ if ($5 == NFT_XFRM_KEY_SADDR_IP4)
+ xfrmk = NFT_XFRM_KEY_SADDR_IP6;
+ else if ($5 == NFT_XFRM_KEY_DADDR_IP4)
+ xfrmk = NFT_XFRM_KEY_DADDR_IP6;
+ break;
+ default:
+ YYERROR;
+ break;
+ }
+
+ if ($3 > 255) {
+ erec_queue(error(&@3, "value too large"), state->msgs);
+ YYERROR;
+ }
+
+ $$ = xfrm_expr_alloc(&@$, $2, $3, xfrmk);
+ }
+ ;
+
+hash_expr : JHASH expr MOD NUM SEED NUM offset_opt close_scope_hash
+ {
+ $$ = hash_expr_alloc(&@$, $4, true, $6, $7, NFT_HASH_JENKINS);
+ $$->hash.expr = $2;
+ }
+ | JHASH expr MOD NUM offset_opt close_scope_hash
+ {
+ $$ = hash_expr_alloc(&@$, $4, false, 0, $5, NFT_HASH_JENKINS);
+ $$->hash.expr = $2;
+ }
+ | SYMHASH MOD NUM offset_opt close_scope_hash
+ {
+ $$ = hash_expr_alloc(&@$, $3, false, 0, $4, NFT_HASH_SYM);
+ }
+ ;
+
+nf_key_proto : IP close_scope_ip { $$ = NFPROTO_IPV4; }
+ | IP6 close_scope_ip6 { $$ = NFPROTO_IPV6; }
+ ;
+
+rt_expr : RT rt_key close_scope_rt
+ {
+ $$ = rt_expr_alloc(&@$, $2, true);
+ }
+ | RT nf_key_proto rt_key close_scope_rt
+ {
+ enum nft_rt_keys rtk = $3;
+
+ switch ($2) {
+ case NFPROTO_IPV4:
+ break;
+ case NFPROTO_IPV6:
+ if ($3 == NFT_RT_NEXTHOP4)
+ rtk = NFT_RT_NEXTHOP6;
+ break;
+ default:
+ YYERROR;
+ break;
+ }
+
+ $$ = rt_expr_alloc(&@$, rtk, false);
+ }
+ ;
+
+rt_key : CLASSID { $$ = NFT_RT_CLASSID; }
+ | NEXTHOP { $$ = NFT_RT_NEXTHOP4; }
+ | MTU { $$ = NFT_RT_TCPMSS; }
+ | IPSEC close_scope_ipsec { $$ = NFT_RT_XFRM; }
+ ;
+
+ct_expr : CT ct_key close_scope_ct
+ {
+ $$ = ct_expr_alloc(&@$, $2, -1);
+ }
+ | CT ct_dir ct_key_dir close_scope_ct
+ {
+ $$ = ct_expr_alloc(&@$, $3, $2);
+ }
+ | CT ct_dir ct_key_proto_field close_scope_ct
+ {
+ $$ = ct_expr_alloc(&@$, $3, $2);
+ }
+ ;
+
+ct_dir : ORIGINAL { $$ = IP_CT_DIR_ORIGINAL; }
+ | REPLY { $$ = IP_CT_DIR_REPLY; }
+ ;
+
+ct_key : L3PROTOCOL { $$ = NFT_CT_L3PROTOCOL; }
+ | PROTOCOL { $$ = NFT_CT_PROTOCOL; }
+ | MARK { $$ = NFT_CT_MARK; }
+ | STATE { $$ = NFT_CT_STATE; }
+ | DIRECTION { $$ = NFT_CT_DIRECTION; }
+ | STATUS { $$ = NFT_CT_STATUS; }
+ | EXPIRATION { $$ = NFT_CT_EXPIRATION; }
+ | HELPER { $$ = NFT_CT_HELPER; }
+ | SADDR { $$ = NFT_CT_SRC; }
+ | DADDR { $$ = NFT_CT_DST; }
+ | PROTO_SRC { $$ = NFT_CT_PROTO_SRC; }
+ | PROTO_DST { $$ = NFT_CT_PROTO_DST; }
+ | LABEL { $$ = NFT_CT_LABELS; }
+ | EVENT { $$ = NFT_CT_EVENTMASK; }
+ | SECMARK close_scope_secmark { $$ = NFT_CT_SECMARK; }
+ | ID { $$ = NFT_CT_ID; }
+ | ct_key_dir_optional
+ ;
+
+ct_key_dir : SADDR { $$ = NFT_CT_SRC; }
+ | DADDR { $$ = NFT_CT_DST; }
+ | L3PROTOCOL { $$ = NFT_CT_L3PROTOCOL; }
+ | PROTOCOL { $$ = NFT_CT_PROTOCOL; }
+ | PROTO_SRC { $$ = NFT_CT_PROTO_SRC; }
+ | PROTO_DST { $$ = NFT_CT_PROTO_DST; }
+ | ct_key_dir_optional
+ ;
+
+ct_key_proto_field : IP SADDR close_scope_ip { $$ = NFT_CT_SRC_IP; }
+ | IP DADDR close_scope_ip { $$ = NFT_CT_DST_IP; }
+ | IP6 SADDR close_scope_ip6 { $$ = NFT_CT_SRC_IP6; }
+ | IP6 DADDR close_scope_ip6 { $$ = NFT_CT_DST_IP6; }
+ ;
+
+ct_key_dir_optional : BYTES { $$ = NFT_CT_BYTES; }
+ | PACKETS { $$ = NFT_CT_PKTS; }
+ | AVGPKT { $$ = NFT_CT_AVGPKT; }
+ | ZONE { $$ = NFT_CT_ZONE; }
+ ;
+
+symbol_stmt_expr : symbol_expr
+ | keyword_expr
+ ;
+
+list_stmt_expr : symbol_stmt_expr COMMA symbol_stmt_expr
+ {
+ $$ = list_expr_alloc(&@$);
+ compound_expr_add($$, $1);
+ compound_expr_add($$, $3);
+ }
+ | list_stmt_expr COMMA symbol_stmt_expr
+ {
+ $1->location = @$;
+ compound_expr_add($1, $3);
+ $$ = $1;
+ }
+ ;
+
+ct_stmt : CT ct_key SET stmt_expr close_scope_ct
+ {
+ switch ($2) {
+ case NFT_CT_HELPER:
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_CT_HELPER;
+ $$->objref.expr = $4;
+ break;
+ default:
+ $$ = ct_stmt_alloc(&@$, $2, -1, $4);
+ break;
+ }
+ }
+ | CT TIMEOUT SET stmt_expr close_scope_ct
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_CT_TIMEOUT;
+ $$->objref.expr = $4;
+
+ }
+ | CT EXPECTATION SET stmt_expr close_scope_ct
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_CT_EXPECT;
+ $$->objref.expr = $4;
+ }
+ | CT ct_dir ct_key_dir_optional SET stmt_expr close_scope_ct
+ {
+ $$ = ct_stmt_alloc(&@$, $3, $2, $5);
+ }
+ ;
+
+payload_stmt : payload_expr SET stmt_expr
+ {
+ if ($1->etype == EXPR_EXTHDR)
+ $$ = exthdr_stmt_alloc(&@$, $1, $3);
+ else
+ $$ = payload_stmt_alloc(&@$, $1, $3);
+ }
+ ;
+
+payload_expr : payload_raw_expr
+ | eth_hdr_expr
+ | vlan_hdr_expr
+ | arp_hdr_expr
+ | ip_hdr_expr
+ | icmp_hdr_expr
+ | igmp_hdr_expr
+ | ip6_hdr_expr
+ | icmp6_hdr_expr
+ | auth_hdr_expr
+ | esp_hdr_expr
+ | comp_hdr_expr
+ | udp_hdr_expr
+ | udplite_hdr_expr
+ | tcp_hdr_expr close_scope_tcp
+ | dccp_hdr_expr
+ | sctp_hdr_expr
+ | th_hdr_expr
+ | vxlan_hdr_expr
+ | geneve_hdr_expr
+ | gre_hdr_expr
+ | gretap_hdr_expr
+ ;
+
+payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM close_scope_at
+ {
+ $$ = payload_expr_alloc(&@$, NULL, 0);
+ payload_init_raw($$, $2, $4, $6);
+ $$->byteorder = BYTEORDER_BIG_ENDIAN;
+ $$->payload.is_raw = true;
+ }
+ ;
+
+payload_base_spec : LL_HDR { $$ = PROTO_BASE_LL_HDR; }
+ | NETWORK_HDR { $$ = PROTO_BASE_NETWORK_HDR; }
+ | TRANSPORT_HDR close_scope_th { $$ = PROTO_BASE_TRANSPORT_HDR; }
+ | STRING
+ {
+ if (!strcmp($1, "ih")) {
+ $$ = PROTO_BASE_INNER_HDR;
+ } else {
+ erec_queue(error(&@1, "unknown raw payload base"), state->msgs);
+ xfree($1);
+ YYERROR;
+ }
+ xfree($1);
+ }
+ ;
+
+eth_hdr_expr : ETHER eth_hdr_field close_scope_eth
+ {
+ $$ = payload_expr_alloc(&@$, &proto_eth, $2);
+ }
+ ;
+
+eth_hdr_field : SADDR { $$ = ETHHDR_SADDR; }
+ | DADDR { $$ = ETHHDR_DADDR; }
+ | TYPE close_scope_type { $$ = ETHHDR_TYPE; }
+ ;
+
+vlan_hdr_expr : VLAN vlan_hdr_field close_scope_vlan
+ {
+ $$ = payload_expr_alloc(&@$, &proto_vlan, $2);
+ }
+ ;
+
+vlan_hdr_field : ID { $$ = VLANHDR_VID; }
+ | CFI { $$ = VLANHDR_CFI; }
+ | DEI { $$ = VLANHDR_DEI; }
+ | PCP { $$ = VLANHDR_PCP; }
+ | TYPE close_scope_type { $$ = VLANHDR_TYPE; }
+ ;
+
+arp_hdr_expr : ARP arp_hdr_field close_scope_arp
+ {
+ $$ = payload_expr_alloc(&@$, &proto_arp, $2);
+ }
+ ;
+
+arp_hdr_field : HTYPE { $$ = ARPHDR_HRD; }
+ | PTYPE { $$ = ARPHDR_PRO; }
+ | HLEN { $$ = ARPHDR_HLN; }
+ | PLEN { $$ = ARPHDR_PLN; }
+ | OPERATION { $$ = ARPHDR_OP; }
+ | SADDR ETHER close_scope_eth { $$ = ARPHDR_SADDR_ETHER; }
+ | DADDR ETHER close_scope_eth { $$ = ARPHDR_DADDR_ETHER; }
+ | SADDR IP close_scope_ip { $$ = ARPHDR_SADDR_IP; }
+ | DADDR IP close_scope_ip { $$ = ARPHDR_DADDR_IP; }
+ ;
+
+ip_hdr_expr : IP ip_hdr_field close_scope_ip
+ {
+ $$ = payload_expr_alloc(&@$, &proto_ip, $2);
+ }
+ | IP OPTION ip_option_type ip_option_field close_scope_ip
+ {
+ $$ = ipopt_expr_alloc(&@$, $3, $4);
+ if (!$$) {
+ erec_queue(error(&@1, "unknown ip option type/field"), state->msgs);
+ YYERROR;
+ }
+ }
+ | IP OPTION ip_option_type close_scope_ip
+ {
+ $$ = ipopt_expr_alloc(&@$, $3, IPOPT_FIELD_TYPE);
+ $$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ }
+ ;
+
+ip_hdr_field : HDRVERSION { $$ = IPHDR_VERSION; }
+ | HDRLENGTH { $$ = IPHDR_HDRLENGTH; }
+ | DSCP { $$ = IPHDR_DSCP; }
+ | ECN { $$ = IPHDR_ECN; }
+ | LENGTH { $$ = IPHDR_LENGTH; }
+ | ID { $$ = IPHDR_ID; }
+ | FRAG_OFF { $$ = IPHDR_FRAG_OFF; }
+ | TTL { $$ = IPHDR_TTL; }
+ | PROTOCOL { $$ = IPHDR_PROTOCOL; }
+ | CHECKSUM { $$ = IPHDR_CHECKSUM; }
+ | SADDR { $$ = IPHDR_SADDR; }
+ | DADDR { $$ = IPHDR_DADDR; }
+ ;
+
+ip_option_type : LSRR { $$ = IPOPT_LSRR; }
+ | RR { $$ = IPOPT_RR; }
+ | SSRR { $$ = IPOPT_SSRR; }
+ | RA { $$ = IPOPT_RA; }
+ ;
+
+ip_option_field : TYPE close_scope_type { $$ = IPOPT_FIELD_TYPE; }
+ | LENGTH { $$ = IPOPT_FIELD_LENGTH; }
+ | VALUE { $$ = IPOPT_FIELD_VALUE; }
+ | PTR { $$ = IPOPT_FIELD_PTR; }
+ | ADDR { $$ = IPOPT_FIELD_ADDR_0; }
+ ;
+
+icmp_hdr_expr : ICMP icmp_hdr_field close_scope_icmp
+ {
+ $$ = payload_expr_alloc(&@$, &proto_icmp, $2);
+ }
+ ;
+
+icmp_hdr_field : TYPE close_scope_type { $$ = ICMPHDR_TYPE; }
+ | CODE { $$ = ICMPHDR_CODE; }
+ | CHECKSUM { $$ = ICMPHDR_CHECKSUM; }
+ | ID { $$ = ICMPHDR_ID; }
+ | SEQUENCE { $$ = ICMPHDR_SEQ; }
+ | GATEWAY { $$ = ICMPHDR_GATEWAY; }
+ | MTU { $$ = ICMPHDR_MTU; }
+ ;
+
+igmp_hdr_expr : IGMP igmp_hdr_field close_scope_igmp
+ {
+ $$ = payload_expr_alloc(&@$, &proto_igmp, $2);
+ }
+ ;
+
+igmp_hdr_field : TYPE close_scope_type { $$ = IGMPHDR_TYPE; }
+ | CHECKSUM { $$ = IGMPHDR_CHECKSUM; }
+ | MRT { $$ = IGMPHDR_MRT; }
+ | GROUP { $$ = IGMPHDR_GROUP; }
+ ;
+
+ip6_hdr_expr : IP6 ip6_hdr_field close_scope_ip6
+ {
+ $$ = payload_expr_alloc(&@$, &proto_ip6, $2);
+ }
+ ;
+
+ip6_hdr_field : HDRVERSION { $$ = IP6HDR_VERSION; }
+ | DSCP { $$ = IP6HDR_DSCP; }
+ | ECN { $$ = IP6HDR_ECN; }
+ | FLOWLABEL { $$ = IP6HDR_FLOWLABEL; }
+ | LENGTH { $$ = IP6HDR_LENGTH; }
+ | NEXTHDR { $$ = IP6HDR_NEXTHDR; }
+ | HOPLIMIT { $$ = IP6HDR_HOPLIMIT; }
+ | SADDR { $$ = IP6HDR_SADDR; }
+ | DADDR { $$ = IP6HDR_DADDR; }
+ ;
+icmp6_hdr_expr : ICMP6 icmp6_hdr_field close_scope_icmp
+ {
+ $$ = payload_expr_alloc(&@$, &proto_icmp6, $2);
+ }
+ ;
+
+icmp6_hdr_field : TYPE close_scope_type { $$ = ICMP6HDR_TYPE; }
+ | CODE { $$ = ICMP6HDR_CODE; }
+ | CHECKSUM { $$ = ICMP6HDR_CHECKSUM; }
+ | PPTR { $$ = ICMP6HDR_PPTR; }
+ | MTU { $$ = ICMP6HDR_MTU; }
+ | ID { $$ = ICMP6HDR_ID; }
+ | SEQUENCE { $$ = ICMP6HDR_SEQ; }
+ | MAXDELAY { $$ = ICMP6HDR_MAXDELAY; }
+ | TADDR { $$ = ICMP6HDR_TADDR; }
+ | DADDR { $$ = ICMP6HDR_DADDR; }
+ ;
+
+auth_hdr_expr : AH auth_hdr_field close_scope_ah
+ {
+ $$ = payload_expr_alloc(&@$, &proto_ah, $2);
+ }
+ ;
+
+auth_hdr_field : NEXTHDR { $$ = AHHDR_NEXTHDR; }
+ | HDRLENGTH { $$ = AHHDR_HDRLENGTH; }
+ | RESERVED { $$ = AHHDR_RESERVED; }
+ | SPI { $$ = AHHDR_SPI; }
+ | SEQUENCE { $$ = AHHDR_SEQUENCE; }
+ ;
+
+esp_hdr_expr : ESP esp_hdr_field close_scope_esp
+ {
+ $$ = payload_expr_alloc(&@$, &proto_esp, $2);
+ }
+ ;
+
+esp_hdr_field : SPI { $$ = ESPHDR_SPI; }
+ | SEQUENCE { $$ = ESPHDR_SEQUENCE; }
+ ;
+
+comp_hdr_expr : COMP comp_hdr_field close_scope_comp
+ {
+ $$ = payload_expr_alloc(&@$, &proto_comp, $2);
+ }
+ ;
+
+comp_hdr_field : NEXTHDR { $$ = COMPHDR_NEXTHDR; }
+ | FLAGS { $$ = COMPHDR_FLAGS; }
+ | CPI { $$ = COMPHDR_CPI; }
+ ;
+
+udp_hdr_expr : UDP udp_hdr_field close_scope_udp
+ {
+ $$ = payload_expr_alloc(&@$, &proto_udp, $2);
+ }
+ ;
+
+udp_hdr_field : SPORT { $$ = UDPHDR_SPORT; }
+ | DPORT { $$ = UDPHDR_DPORT; }
+ | LENGTH { $$ = UDPHDR_LENGTH; }
+ | CHECKSUM { $$ = UDPHDR_CHECKSUM; }
+ ;
+
+udplite_hdr_expr : UDPLITE udplite_hdr_field close_scope_udplite
+ {
+ $$ = payload_expr_alloc(&@$, &proto_udplite, $2);
+ }
+ ;
+
+udplite_hdr_field : SPORT { $$ = UDPHDR_SPORT; }
+ | DPORT { $$ = UDPHDR_DPORT; }
+ | CSUMCOV { $$ = UDPHDR_LENGTH; }
+ | CHECKSUM { $$ = UDPHDR_CHECKSUM; }
+ ;
+
+tcp_hdr_expr : TCP tcp_hdr_field
+ {
+ $$ = payload_expr_alloc(&@$, &proto_tcp, $2);
+ }
+ | TCP OPTION tcp_hdr_option_type
+ {
+ $$ = tcpopt_expr_alloc(&@$, $3, TCPOPT_COMMON_KIND);
+ $$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ }
+ | TCP OPTION tcp_hdr_option_kind_and_field
+ {
+ $$ = tcpopt_expr_alloc(&@$, $3.kind, $3.field);
+ }
+ | TCP OPTION AT close_scope_at tcp_hdr_option_type COMMA NUM COMMA NUM
+ {
+ $$ = tcpopt_expr_alloc(&@$, $5, 0);
+ tcpopt_init_raw($$, $5, $7, $9, 0);
+ }
+ ;
+
+inner_inet_expr : ip_hdr_expr
+ | icmp_hdr_expr
+ | igmp_hdr_expr
+ | ip6_hdr_expr
+ | icmp6_hdr_expr
+ | auth_hdr_expr
+ | esp_hdr_expr
+ | comp_hdr_expr
+ | udp_hdr_expr
+ | udplite_hdr_expr
+ | tcp_hdr_expr close_scope_tcp
+ | dccp_hdr_expr
+ | sctp_hdr_expr
+ | th_hdr_expr
+ ;
+
+inner_eth_expr : eth_hdr_expr
+ | vlan_hdr_expr
+ | arp_hdr_expr
+ ;
+
+inner_expr : inner_eth_expr
+ | inner_inet_expr
+ ;
+
+vxlan_hdr_expr : VXLAN vxlan_hdr_field
+ {
+ struct expr *expr;
+
+ expr = payload_expr_alloc(&@$, &proto_vxlan, $2);
+ expr->payload.inner_desc = &proto_vxlan;
+ $$ = expr;
+ }
+ | VXLAN inner_expr
+ {
+ $$ = $2;
+ $$->location = @$;
+ $$->payload.inner_desc = &proto_vxlan;
+ }
+ ;
+
+vxlan_hdr_field : VNI { $$ = VXLANHDR_VNI; }
+ | FLAGS { $$ = VXLANHDR_FLAGS; }
+ ;
+
+geneve_hdr_expr : GENEVE geneve_hdr_field
+ {
+ struct expr *expr;
+
+ expr = payload_expr_alloc(&@$, &proto_geneve, $2);
+ expr->payload.inner_desc = &proto_geneve;
+ $$ = expr;
+ }
+ | GENEVE inner_expr
+ {
+ $$ = $2;
+ $$->location = @$;
+ $$->payload.inner_desc = &proto_geneve;
+ }
+ ;
+
+geneve_hdr_field : VNI { $$ = GNVHDR_VNI; }
+ | TYPE { $$ = GNVHDR_TYPE; }
+ ;
+
+gre_hdr_expr : GRE gre_hdr_field close_scope_gre
+ {
+ $$ = payload_expr_alloc(&@$, &proto_gre, $2);
+ }
+ | GRE close_scope_gre inner_inet_expr
+ {
+ $$ = $3;
+ $$->payload.inner_desc = &proto_gre;
+ }
+ ;
+
+gre_hdr_field : HDRVERSION { $$ = GREHDR_VERSION; }
+ | FLAGS { $$ = GREHDR_FLAGS; }
+ | PROTOCOL { $$ = GREHDR_PROTOCOL; }
+ ;
+
+gretap_hdr_expr : GRETAP close_scope_gre inner_expr
+ {
+ $$ = $3;
+ $$->payload.inner_desc = &proto_gretap;
+ }
+ ;
+
+optstrip_stmt : RESET TCP OPTION tcp_hdr_option_type close_scope_tcp
+ {
+ $$ = optstrip_stmt_alloc(&@$, tcpopt_expr_alloc(&@$,
+ $4, TCPOPT_COMMON_KIND));
+ }
+ ;
+
+tcp_hdr_field : SPORT { $$ = TCPHDR_SPORT; }
+ | DPORT { $$ = TCPHDR_DPORT; }
+ | SEQUENCE { $$ = TCPHDR_SEQ; }
+ | ACKSEQ { $$ = TCPHDR_ACKSEQ; }
+ | DOFF { $$ = TCPHDR_DOFF; }
+ | RESERVED { $$ = TCPHDR_RESERVED; }
+ | FLAGS { $$ = TCPHDR_FLAGS; }
+ | WINDOW { $$ = TCPHDR_WINDOW; }
+ | CHECKSUM { $$ = TCPHDR_CHECKSUM; }
+ | URGPTR { $$ = TCPHDR_URGPTR; }
+ ;
+
+tcp_hdr_option_kind_and_field : MSS tcpopt_field_maxseg
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_MAXSEG, .field = $2 };
+ $$ = kind_field;
+ }
+ | tcp_hdr_option_sack tcpopt_field_sack
+ {
+ struct tcp_kind_field kind_field = { .kind = $1, .field = $2 };
+ $$ = kind_field;
+ }
+ | WINDOW tcpopt_field_window
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_WINDOW, .field = $2 };
+ $$ = kind_field;
+ }
+ | TIMESTAMP tcpopt_field_tsopt
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_TIMESTAMP, .field = $2 };
+ $$ = kind_field;
+ }
+ | tcp_hdr_option_type LENGTH
+ {
+ struct tcp_kind_field kind_field = { .kind = $1, .field = TCPOPT_COMMON_LENGTH };
+ $$ = kind_field;
+ }
+ | MPTCP tcpopt_field_mptcp
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_MPTCP, .field = $2 };
+ $$ = kind_field;
+ }
+ ;
+
+tcp_hdr_option_sack : SACK { $$ = TCPOPT_KIND_SACK; }
+ | SACK0 { $$ = TCPOPT_KIND_SACK; }
+ | SACK1 { $$ = TCPOPT_KIND_SACK1; }
+ | SACK2 { $$ = TCPOPT_KIND_SACK2; }
+ | SACK3 { $$ = TCPOPT_KIND_SACK3; }
+ ;
+
+tcp_hdr_option_type : ECHO { $$ = TCPOPT_KIND_ECHO; }
+ | EOL { $$ = TCPOPT_KIND_EOL; }
+ | FASTOPEN { $$ = TCPOPT_KIND_FASTOPEN; }
+ | MD5SIG { $$ = TCPOPT_KIND_MD5SIG; }
+ | MPTCP { $$ = TCPOPT_KIND_MPTCP; }
+ | MSS { $$ = TCPOPT_KIND_MAXSEG; }
+ | NOP { $$ = TCPOPT_KIND_NOP; }
+ | SACK_PERM { $$ = TCPOPT_KIND_SACK_PERMITTED; }
+ | TIMESTAMP { $$ = TCPOPT_KIND_TIMESTAMP; }
+ | WINDOW { $$ = TCPOPT_KIND_WINDOW; }
+ | tcp_hdr_option_sack { $$ = $1; }
+ | NUM {
+ if ($1 > 255) {
+ erec_queue(error(&@1, "value too large"), state->msgs);
+ YYERROR;
+ }
+ $$ = $1;
+ }
+ ;
+
+tcpopt_field_sack : LEFT { $$ = TCPOPT_SACK_LEFT; }
+ | RIGHT { $$ = TCPOPT_SACK_RIGHT; }
+ ;
+
+tcpopt_field_window : COUNT { $$ = TCPOPT_WINDOW_COUNT; }
+ ;
+
+tcpopt_field_tsopt : TSVAL { $$ = TCPOPT_TS_TSVAL; }
+ | TSECR { $$ = TCPOPT_TS_TSECR; }
+ ;
+
+tcpopt_field_maxseg : SIZE { $$ = TCPOPT_MAXSEG_SIZE; }
+ ;
+
+tcpopt_field_mptcp : SUBTYPE { $$ = TCPOPT_MPTCP_SUBTYPE; }
+ ;
+
+dccp_hdr_expr : DCCP dccp_hdr_field close_scope_dccp
+ {
+ $$ = payload_expr_alloc(&@$, &proto_dccp, $2);
+ }
+ | DCCP OPTION NUM close_scope_dccp
+ {
+ if ($3 > DCCPOPT_TYPE_MAX) {
+ erec_queue(error(&@1, "value too large"),
+ state->msgs);
+ YYERROR;
+ }
+ $$ = dccpopt_expr_alloc(&@$, $3);
+ }
+ ;
+
+dccp_hdr_field : SPORT { $$ = DCCPHDR_SPORT; }
+ | DPORT { $$ = DCCPHDR_DPORT; }
+ | TYPE close_scope_type { $$ = DCCPHDR_TYPE; }
+ ;
+
+sctp_chunk_type : DATA { $$ = SCTP_CHUNK_TYPE_DATA; }
+ | INIT { $$ = SCTP_CHUNK_TYPE_INIT; }
+ | INIT_ACK { $$ = SCTP_CHUNK_TYPE_INIT_ACK; }
+ | SACK { $$ = SCTP_CHUNK_TYPE_SACK; }
+ | HEARTBEAT { $$ = SCTP_CHUNK_TYPE_HEARTBEAT; }
+ | HEARTBEAT_ACK { $$ = SCTP_CHUNK_TYPE_HEARTBEAT_ACK; }
+ | ABORT { $$ = SCTP_CHUNK_TYPE_ABORT; }
+ | SHUTDOWN { $$ = SCTP_CHUNK_TYPE_SHUTDOWN; }
+ | SHUTDOWN_ACK { $$ = SCTP_CHUNK_TYPE_SHUTDOWN_ACK; }
+ | ERROR { $$ = SCTP_CHUNK_TYPE_ERROR; }
+ | COOKIE_ECHO { $$ = SCTP_CHUNK_TYPE_COOKIE_ECHO; }
+ | COOKIE_ACK { $$ = SCTP_CHUNK_TYPE_COOKIE_ACK; }
+ | ECNE { $$ = SCTP_CHUNK_TYPE_ECNE; }
+ | CWR { $$ = SCTP_CHUNK_TYPE_CWR; }
+ | SHUTDOWN_COMPLETE { $$ = SCTP_CHUNK_TYPE_SHUTDOWN_COMPLETE; }
+ | ASCONF_ACK { $$ = SCTP_CHUNK_TYPE_ASCONF_ACK; }
+ | FORWARD_TSN { $$ = SCTP_CHUNK_TYPE_FORWARD_TSN; }
+ | ASCONF { $$ = SCTP_CHUNK_TYPE_ASCONF; }
+ ;
+
+sctp_chunk_common_field : TYPE close_scope_type { $$ = SCTP_CHUNK_COMMON_TYPE; }
+ | FLAGS { $$ = SCTP_CHUNK_COMMON_FLAGS; }
+ | LENGTH { $$ = SCTP_CHUNK_COMMON_LENGTH; }
+ ;
+
+sctp_chunk_data_field : TSN { $$ = SCTP_CHUNK_DATA_TSN; }
+ | STREAM { $$ = SCTP_CHUNK_DATA_STREAM; }
+ | SSN { $$ = SCTP_CHUNK_DATA_SSN; }
+ | PPID { $$ = SCTP_CHUNK_DATA_PPID; }
+ ;
+
+sctp_chunk_init_field : INIT_TAG { $$ = SCTP_CHUNK_INIT_TAG; }
+ | A_RWND { $$ = SCTP_CHUNK_INIT_RWND; }
+ | NUM_OSTREAMS { $$ = SCTP_CHUNK_INIT_OSTREAMS; }
+ | NUM_ISTREAMS { $$ = SCTP_CHUNK_INIT_ISTREAMS; }
+ | INIT_TSN { $$ = SCTP_CHUNK_INIT_TSN; }
+ ;
+
+sctp_chunk_sack_field : CUM_TSN_ACK { $$ = SCTP_CHUNK_SACK_CTSN_ACK; }
+ | A_RWND { $$ = SCTP_CHUNK_SACK_RWND; }
+ | NUM_GACK_BLOCKS { $$ = SCTP_CHUNK_SACK_GACK_BLOCKS; }
+ | NUM_DUP_TSNS { $$ = SCTP_CHUNK_SACK_DUP_TSNS; }
+ ;
+
+sctp_chunk_alloc : sctp_chunk_type
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, $1, SCTP_CHUNK_COMMON_TYPE);
+ $$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ }
+ | sctp_chunk_type sctp_chunk_common_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, $1, $2);
+ }
+ | DATA sctp_chunk_data_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_DATA, $2);
+ }
+ | INIT sctp_chunk_init_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_INIT, $2);
+ }
+ | INIT_ACK sctp_chunk_init_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_INIT_ACK, $2);
+ }
+ | SACK sctp_chunk_sack_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_SACK, $2);
+ }
+ | SHUTDOWN CUM_TSN_ACK
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_SHUTDOWN,
+ SCTP_CHUNK_SHUTDOWN_CTSN_ACK);
+ }
+ | ECNE LOWEST_TSN
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_ECNE,
+ SCTP_CHUNK_ECNE_CWR_MIN_TSN);
+ }
+ | CWR LOWEST_TSN
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_CWR,
+ SCTP_CHUNK_ECNE_CWR_MIN_TSN);
+ }
+ | ASCONF_ACK SEQNO
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_ASCONF_ACK,
+ SCTP_CHUNK_ASCONF_SEQNO);
+ }
+ | FORWARD_TSN NEW_CUM_TSN
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_FORWARD_TSN,
+ SCTP_CHUNK_FORWARD_TSN_NCTSN);
+ }
+ | ASCONF SEQNO
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_ASCONF,
+ SCTP_CHUNK_ASCONF_SEQNO);
+ }
+ ;
+
+sctp_hdr_expr : SCTP sctp_hdr_field close_scope_sctp
+ {
+ $$ = payload_expr_alloc(&@$, &proto_sctp, $2);
+ }
+ | SCTP CHUNK sctp_chunk_alloc close_scope_sctp_chunk close_scope_sctp
+ {
+ $$ = $3;
+ }
+ ;
+
+sctp_hdr_field : SPORT { $$ = SCTPHDR_SPORT; }
+ | DPORT { $$ = SCTPHDR_DPORT; }
+ | VTAG { $$ = SCTPHDR_VTAG; }
+ | CHECKSUM { $$ = SCTPHDR_CHECKSUM; }
+ ;
+
+th_hdr_expr : TRANSPORT_HDR th_hdr_field close_scope_th
+ {
+ $$ = payload_expr_alloc(&@$, &proto_th, $2);
+ if ($$)
+ $$->payload.is_raw = true;
+ }
+ ;
+
+th_hdr_field : SPORT { $$ = THDR_SPORT; }
+ | DPORT { $$ = THDR_DPORT; }
+ ;
+
+exthdr_expr : hbh_hdr_expr
+ | rt_hdr_expr
+ | rt0_hdr_expr
+ | rt2_hdr_expr
+ | rt4_hdr_expr
+ | frag_hdr_expr
+ | dst_hdr_expr
+ | mh_hdr_expr
+ ;
+
+hbh_hdr_expr : HBH hbh_hdr_field close_scope_hbh
+ {
+ $$ = exthdr_expr_alloc(&@$, &exthdr_hbh, $2);
+ }
+ ;
+
+hbh_hdr_field : NEXTHDR { $$ = HBHHDR_NEXTHDR; }
+ | HDRLENGTH { $$ = HBHHDR_HDRLENGTH; }
+ ;
+
+rt_hdr_expr : RT rt_hdr_field close_scope_rt
+ {
+ $$ = exthdr_expr_alloc(&@$, &exthdr_rt, $2);
+ }
+ ;
+
+rt_hdr_field : NEXTHDR { $$ = RTHDR_NEXTHDR; }
+ | HDRLENGTH { $$ = RTHDR_HDRLENGTH; }
+ | TYPE close_scope_type { $$ = RTHDR_TYPE; }
+ | SEG_LEFT { $$ = RTHDR_SEG_LEFT; }
+ ;
+
+rt0_hdr_expr : RT0 rt0_hdr_field close_scope_rt
+ {
+ $$ = exthdr_expr_alloc(&@$, &exthdr_rt0, $2);
+ }
+ ;
+
+rt0_hdr_field : ADDR '[' NUM ']'
+ {
+ $$ = RT0HDR_ADDR_1 + $3 - 1;
+ }
+ ;
+
+rt2_hdr_expr : RT2 rt2_hdr_field close_scope_rt
+ {
+ $$ = exthdr_expr_alloc(&@$, &exthdr_rt2, $2);
+ }
+ ;
+
+rt2_hdr_field : ADDR { $$ = RT2HDR_ADDR; }
+ ;
+
+rt4_hdr_expr : RT4 rt4_hdr_field close_scope_rt
+ {
+ $$ = exthdr_expr_alloc(&@$, &exthdr_rt4, $2);
+ }
+ ;
+
+rt4_hdr_field : LAST_ENT { $$ = RT4HDR_LASTENT; }
+ | FLAGS { $$ = RT4HDR_FLAGS; }
+ | TAG { $$ = RT4HDR_TAG; }
+ | SID '[' NUM ']'
+ {
+ $$ = RT4HDR_SID_1 + $3 - 1;
+ }
+ ;
+
+frag_hdr_expr : FRAG frag_hdr_field close_scope_frag
+ {
+ $$ = exthdr_expr_alloc(&@$, &exthdr_frag, $2);
+ }
+ ;
+
+frag_hdr_field : NEXTHDR { $$ = FRAGHDR_NEXTHDR; }
+ | RESERVED { $$ = FRAGHDR_RESERVED; }
+ | FRAG_OFF { $$ = FRAGHDR_FRAG_OFF; }
+ | RESERVED2 { $$ = FRAGHDR_RESERVED2; }
+ | MORE_FRAGMENTS { $$ = FRAGHDR_MFRAGS; }
+ | ID { $$ = FRAGHDR_ID; }
+ ;
+
+dst_hdr_expr : DST dst_hdr_field close_scope_dst
+ {
+ $$ = exthdr_expr_alloc(&@$, &exthdr_dst, $2);
+ }
+ ;
+
+dst_hdr_field : NEXTHDR { $$ = DSTHDR_NEXTHDR; }
+ | HDRLENGTH { $$ = DSTHDR_HDRLENGTH; }
+ ;
+
+mh_hdr_expr : MH mh_hdr_field close_scope_mh
+ {
+ $$ = exthdr_expr_alloc(&@$, &exthdr_mh, $2);
+ }
+ ;
+
+mh_hdr_field : NEXTHDR { $$ = MHHDR_NEXTHDR; }
+ | HDRLENGTH { $$ = MHHDR_HDRLENGTH; }
+ | TYPE close_scope_type { $$ = MHHDR_TYPE; }
+ | RESERVED { $$ = MHHDR_RESERVED; }
+ | CHECKSUM { $$ = MHHDR_CHECKSUM; }
+ ;
+
+exthdr_exists_expr : EXTHDR exthdr_key
+ {
+ const struct exthdr_desc *desc;
+
+ desc = exthdr_find_proto($2);
+
+ /* Assume that NEXTHDR template is always
+ * the first one in list of templates.
+ */
+ $$ = exthdr_expr_alloc(&@$, desc, 1);
+ $$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ }
+ ;
+
+exthdr_key : HBH close_scope_hbh { $$ = IPPROTO_HOPOPTS; }
+ | RT close_scope_rt { $$ = IPPROTO_ROUTING; }
+ | FRAG close_scope_frag { $$ = IPPROTO_FRAGMENT; }
+ | DST close_scope_dst { $$ = IPPROTO_DSTOPTS; }
+ | MH close_scope_mh { $$ = IPPROTO_MH; }
+ ;
+
+%%