summaryrefslogtreecommitdiffstats
path: root/src/parser_yy.y
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2025-06-04 23:12:30 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2025-06-05 00:03:59 +0200
commit009cabe369b71cf5452286fd338eae382718789a (patch)
tree9f14eb626cee252ddf79d34cda9b98b57f5a9454 /src/parser_yy.y
initial commitHEADmaster
knft is a tool to improve test coverage for the low-level nftables kernel API by providing a relatively simple way to define a transaction batch with nftables objects without having to mingle with netlink. A set of tests 612 test (.t) files are included. knft also provides a rudimentary deterministic fuzzer (via -f option) along with several fuzzing modes that mangle existing tests in different ways to improve coverage for error unwinding paths: deltable \ delbasechain \ delchain \ delrule | - delete object in this batch delset / delelem / delobj / flushset - flush set dup - duplicate object reverse-commit - turn commit into abort reverse-abort - turn abort into commit table-dormant - inject table dormant flag table-wakeup - inject table wake-up flag swap - swap objects bogus - inject bogus object to make the transaction fail To inspect how the selected fuzzing mode mangles the test, you can use the -d option to enable debugging along with -c to run it in dry-run mode, eg. # src/./knft -c -f deltable -d tests/expr/meta/03-mark_ok.t tests/expr/meta/03-mark_ok.t... [FUZZING] tests/expr/meta/03-mark_ok.t (deltable) >>>> fuzz_loop at index 0 in state=0 add_table(NFPROTO_IPV4, "test", NULL, NULL, NULL); del_table(NFPROTO_IPV4, "test", NULL); add_chain("test", NULL, NULL, NULL, NULL); add_rule("test", "0x1", NULL, NULL, NULL); meta(NULL, "NFT_REG32_15", "3"); cmp("NFT_REG32_15", "0", "ffffffff"); commit(); <<<< fuzz_loop backtrack STACK limit reached ==== still more tries at index 0 in state=0 add_table(NFPROTO_IPV4, "test", NULL, NULL, NULL); add_chain("test", NULL, NULL, NULL, NULL); del_table(NFPROTO_IPV4, "test", NULL); add_rule("test", "0x1", NULL, NULL, NULL); meta(NULL, "NFT_REG32_15", "3"); cmp("NFT_REG32_15", "0", "ffffffff"); commit(); <<<< fuzz_loop backtrack STACK limit reached ... knft provides a few more options: -e to display the error reported by the kernel. -n to perform test runs without flushing the existing ruleset. This tool requires libmnl to build and to parse the netlink messages that are sent and received by the kernel. This tool is released under the GPLv2 (or any later). This project is funded through the NGI0 Entrust established by NLnet (https://nlnet.nl) with support from the European Commission's Next Generation Internet programme.
Diffstat (limited to 'src/parser_yy.y')
-rw-r--r--src/parser_yy.y2990
1 files changed, 2990 insertions, 0 deletions
diff --git a/src/parser_yy.y b/src/parser_yy.y
new file mode 100644
index 0000000..84c0d37
--- /dev/null
+++ b/src/parser_yy.y
@@ -0,0 +1,2990 @@
+%{
+/*
+ * (C) 2024-2025 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/* Funded through the NGI0 Entrust established by NLnet (https://nlnet.nl)
+ * with support from the European Commission's Next Generation Internet
+ * programme.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <libgen.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include "lib.h"
+#include "test.h"
+#include "fuzz.h"
+
+extern char *yytext;
+extern int yylineno;
+
+int yylex(void);
+static int yyerror(const char *msg);
+void yyrestart(FILE *input_file);
+
+static struct test_batch batch;
+static int test_fails;
+
+#define NFT_EXTRA 16
+
+struct keyword_map {
+ const char *name;
+ uint32_t value;
+};
+
+static const struct keyword_map nfproto_map[] = {
+ { "NFPROTO_UNSPEC", NFPROTO_UNSPEC },
+ { "NFPROTO_IPV4", NFPROTO_IPV4 },
+ { "NFPROTO_IPV6", NFPROTO_IPV6 },
+ { "NFPROTO_NETDEV", NFPROTO_NETDEV },
+ { "NFPROTO_BRIDGE", NFPROTO_BRIDGE },
+ { "NFPROTO_INET", NFPROTO_INET },
+ { "NFPROTO_ARP", NFPROTO_ARP },
+ { NULL, 0 },
+};
+
+static const struct keyword_map verdict_map[] = {
+ { "NFT_JUMP", NFT_JUMP },
+ { "NFT_GOTO", NFT_GOTO },
+ { "NFT_RETURN", NFT_RETURN },
+ { "NF_ACCEPT", NF_ACCEPT },
+ { "NF_DROP", NF_DROP },
+ { "NF_QUEUE", NF_QUEUE },
+ { NULL, 0 },
+};
+
+static const struct keyword_map datatype_map[] = {
+ { "NFT_DATA_VERDICT", NFT_DATA_VERDICT },
+ { "NFT_DATA_VALUE", NFT_DATA_VALUE },
+ { NULL, 0 },
+};
+
+static const struct keyword_map base_map[] = {
+ { "NFT_PAYLOAD_LL_HEADER", NFT_PAYLOAD_LL_HEADER },
+ { "NFT_PAYLOAD_NETWORK_HEADER", NFT_PAYLOAD_NETWORK_HEADER },
+ { "NFT_PAYLOAD_TRANSPORT_HEADER", NFT_PAYLOAD_TRANSPORT_HEADER },
+ { "NFT_PAYLOAD_INNER_HEADER", NFT_PAYLOAD_INNER_HEADER },
+ { "NFT_PAYLOAD_TUN_HEADER", NFT_PAYLOAD_TUN_HEADER },
+ { NULL, 0 },
+};
+
+static const struct keyword_map reg_map[] = {
+ { "NFT_REG_VERDICT", NFT_REG_VERDICT },
+ { "NFT_REG_1", NFT_REG_1 },
+ { "NFT_REG_2", NFT_REG_2 },
+ { "NFT_REG_3", NFT_REG_3 },
+ { "NFT_REG_4", NFT_REG_4 },
+ { "NFT_REG32_00", NFT_REG32_00 },
+ { "NFT_REG32_01", NFT_REG32_01 },
+ { "NFT_REG32_02", NFT_REG32_02 },
+ { "NFT_REG32_03", NFT_REG32_03 },
+ { "NFT_REG32_04", NFT_REG32_04 },
+ { "NFT_REG32_05", NFT_REG32_05 },
+ { "NFT_REG32_06", NFT_REG32_06 },
+ { "NFT_REG32_07", NFT_REG32_07 },
+ { "NFT_REG32_08", NFT_REG32_08 },
+ { "NFT_REG32_09", NFT_REG32_09 },
+ { "NFT_REG32_10", NFT_REG32_10 },
+ { "NFT_REG32_11", NFT_REG32_11 },
+ { "NFT_REG32_12", NFT_REG32_12 },
+ { "NFT_REG32_13", NFT_REG32_13 },
+ { "NFT_REG32_14", NFT_REG32_14 },
+ { "NFT_REG32_15", NFT_REG32_15 },
+ { NULL, 0 },
+};
+
+static int keyword_to_value(const char *keyword,
+ const struct keyword_map *map,
+ uint32_t **value)
+{
+ char *endptr = NULL;
+ int i;
+
+ if (!keyword) {
+ free((void *)keyword);
+ *value = NULL;
+ return 0;
+ }
+
+ for (i = 0; map[i].name != NULL; i++) {
+ if (!strcmp(map[i].name, keyword)) {
+ free((void *)keyword);
+ **value = map[i].value;
+ return 1;
+ }
+ }
+
+ **value = strtoul(keyword, &endptr, 0);
+ if (!*endptr) {
+ free((void *)keyword);
+ return 1;
+ }
+
+ return -1;
+}
+
+static bool parse_uint8(const char *str, uint8_t **value)
+{
+ char *endptr = NULL;
+
+ if (!str) {
+ free((void *)str);
+ *value = NULL;
+ return true;
+ }
+
+ **value = strtoul(str, &endptr, 0);
+ if (*endptr)
+ return false;
+
+ if (**value > UINT8_MAX)
+ return false;
+
+ free((void *)str);
+
+ return true;
+}
+
+static bool parse_uint16(const char *str, uint16_t **value)
+{
+ char *endptr = NULL;
+
+ if (!str) {
+ free((void *)str);
+ *value = NULL;
+ return true;
+ }
+
+ **value = strtoul(str, &endptr, 0);
+ if (*endptr)
+ return false;
+
+ if (**value > UINT16_MAX)
+ return false;
+
+ free((void *)str);
+
+ return true;
+}
+
+static bool parse_uint32(const char *str, uint32_t **value)
+{
+ char *endptr = NULL;
+
+ if (!str) {
+ *value = NULL;
+ free((void *)str);
+ return true;
+ }
+
+ **value = strtoul(str, &endptr, 0);
+ if (*endptr)
+ return false;
+
+ if (**value > UINT32_MAX)
+ return false;
+
+ free((void *)str);
+
+ return true;
+}
+
+static bool parse_uint64(const char *str, uint64_t **value)
+{
+ char *endptr = NULL;
+
+ if (!str) {
+ *value = NULL;
+ free((void *)str);
+ return true;
+ }
+
+ **value = strtoull(str, &endptr, 0);
+ if (*endptr)
+ return false;
+
+ free((void *)str);
+
+ return true;
+}
+
+static const uint32_t hexmap[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 01234567 */
+ 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 89:;<=>? */
+ 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, /* @ABCDEFG */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* HIJKLMNO */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* PQRSTUVW */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* XYZ[\]^_ */
+ 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, /* `abcdefg */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* hijklmno */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* pqrstuvw */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* xyz{|}~. */
+};
+
+static int hexmap_nibble(char x)
+{
+ if ((x >= 48 && x < 58) ||
+ (x >= 97 && x < 103))
+ return hexmap[x - 48];
+
+ return -1;
+}
+
+static int hex_parse(uint8_t *x, uint8_t **out, int *len, int maxlen)
+{
+ uint32_t byte_len;
+ int a, b, i, j;
+
+ if (!x) {
+ *out = NULL;
+ return 0;
+ }
+ byte_len = strlen(x);
+ *len = byte_len / 2;
+ if (byte_len % 2 || *len > maxlen)
+ return -1;
+
+ for (i = 0, j = 0; i < strlen(x); i += 2, j++) {
+ a = hexmap_nibble(x[i]);
+ b = hexmap_nibble(x[i + 1]);
+ if (a < 0 || b < 0)
+ return -1;
+
+ /* FIXME: this does not work with big-endian. */
+ (*out)[j] = (a << 4) | b;
+ }
+ free((void *)x);
+
+ return 0;
+}
+
+static const char *_filename;
+
+static void error(const char *token)
+{
+ fprintf(stderr, "ERROR: cannot parse `%s' in %s line %d\n",
+ token, _filename, yylineno);
+}
+
+%}
+
+%union {
+ int val;
+ char *string;
+ struct array *array;
+ struct array_u8 *array_u8;
+ struct array_u32 *array_u32;
+}
+
+%token T_ADD_TABLE
+%token T_SET_TABLE
+%token T_DEL_TABLE
+%token T_ADD_BASECHAIN
+%token T_DEL_BASECHAIN
+%token T_ADD_CHAIN
+%token T_DEL_CHAIN
+%token T_ADD_RULE
+%token T_DEL_RULE
+%token T_ADD_SET
+%token T_SET_SET
+%token T_DEL_SET
+%token T_FLUSH_SET
+%token T_ADD_FLOWTABLE
+%token T_DEL_FLOWTABLE
+%token T_ADD_ELEM
+%token T_DEL_ELEM
+%token T_ADD_BITWISE
+%token T_ADD_BYTEORDER
+%token T_ADD_CMP
+%token T_ADD_CONNLIMIT
+%token T_ADD_COUNTER
+%token T_ADD_CT
+%token T_ADD_DUP
+%token T_ADD_DYNSET
+%token T_ADD_EXTHDR
+%token T_ADD_FIB
+%token T_ADD_FLOW_OFFLOAD
+%token T_ADD_FWD
+%token T_ADD_HASH
+%token T_ADD_IMMEDIATE
+%token T_ADD_INNER
+%token T_ADD_LAST
+%token T_ADD_LIMIT
+%token T_ADD_LOG
+%token T_ADD_LOOKUP
+%token T_ADD_MASQ
+%token T_ADD_MATCH
+%token T_ADD_META
+%token T_ADD_NAT
+%token T_ADD_NUMGEN
+%token T_ADD_OBJREF
+%token T_ADD_OSF
+%token T_ADD_PAYLOAD
+%token T_ADD_QUEUE
+%token T_ADD_QUOTA
+%token T_ADD_RANGE
+%token T_ADD_REDIR
+%token T_ADD_REJECT
+%token T_ADD_RT
+%token T_ADD_SOCKET
+%token T_ADD_SYNPROXY
+%token T_ADD_TARGET
+%token T_ADD_TPROXY
+%token T_ADD_TUNNEL
+%token T_ADD_XFRM
+%token T_ADD_OBJ_COUNTER
+%token T_ADD_OBJ_QUOTA
+%token T_ADD_OBJ_CT_HELPER
+%token T_ADD_OBJ_LIMIT
+%token T_ADD_OBJ_CONNLIMIT
+%token T_ADD_OBJ_TUNNEL
+%token T_ADD_OBJ_CT_TIMEOUT
+%token T_ADD_OBJ_CT_EXPECT
+%token T_ADD_OBJ_SECMARK
+%token T_ADD_OBJ_SYNPROXY
+%token T_ADD_OBJ_UNKNOWN
+%token T_DEL_OBJ_UNKNOWN
+%token T_COMMIT
+%token T_ABORT
+
+%token <string> STRING
+%token <string> QSTRING
+%token <string> NULLSTR
+%destructor { free((void *)$$); } STRING QSTRING
+
+%type <string> qstring
+%destructor { free((void *)$$); } qstring
+%type <array> string_array
+%type <array> string_array_items
+%destructor { array_free($$); } string_array string_array_items
+%type <array_u8> u8_array
+%type <array_u8> u8_array_items
+%destructor { array_u8_free($$); } u8_array u8_array_items
+%type <array_u32> u32_array
+%type <array_u32> u32_array_items
+%destructor { array_u32_free($$); } u32_array u32_array_items
+
+%%
+
+testfile :
+ | lines
+ ;
+
+lines : line
+ | lines line
+ ;
+
+line : add_table
+ | set_table
+ | del_table
+ | add_basechain
+ | del_basechain
+ | add_chain
+ | del_chain
+ | add_set
+ | set_set
+ | del_set
+ | flush_set
+ | add_elem
+ | del_elem
+ | add_flowtable
+ | del_flowtable
+ | add_rule
+ | del_rule
+ | add_bitwise
+ | add_byteorder
+ | add_cmp
+ | add_connlimit
+ | add_counter
+ | add_ct
+ | add_dup
+ | add_dynset
+ | add_exthdr
+ | add_fib
+ | add_fwd
+ | add_hash
+ | add_inner
+ | add_immediate
+ | add_last
+ | add_limit
+ | add_log
+ | add_lookup
+ | add_masq
+ | add_match
+ | add_meta
+ | add_nat
+ | add_numgen
+ | add_objref
+ | add_osf
+ | add_payload
+ | add_queue
+ | add_quota
+ | add_range
+ | add_redir
+ | add_reject
+ | add_rt
+ | add_socket
+ | add_synproxy
+ | add_target
+ | add_tproxy
+ | add_tunnel
+ | add_xfrm
+ | add_obj_counter
+ | add_obj_limit
+ | add_obj_ct_helper
+ | add_obj_ct_timeout
+ | add_obj_quota
+ | add_obj_connlimit
+ | add_obj_secmark
+ | add_obj_synproxy
+ | add_obj_tunnel
+ | add_obj_ct_expect
+ | add_obj_unknown
+ | del_obj_unknown
+ | commit
+ | abort
+ ;
+
+qstring : QSTRING { $$ = $1; }
+ | NULLSTR { $$ = NULL; }
+ ;
+
+add_table : T_ADD_TABLE '(' STRING ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct table *table = table_init();
+
+ if (keyword_to_value($3, nfproto_map, &table->nfproto) <= 0) {
+ error("nfproto");
+ free_table(table);
+ YYERROR;
+ }
+
+ table->name = $5;
+
+ if (!parse_uint32($7, &table->flags)) {
+ error("flags");
+ free_table(table);
+ YYERROR;
+ }
+ if (!parse_uint64($9, &table->handle)) {
+ error("handle");
+ free_table(table);
+ YYERROR;
+ }
+
+ table->userdata = $11;
+
+ batch.lineno = yylineno;
+ add_table(&batch, table);
+ }
+ ;
+
+set_table : T_SET_TABLE '(' STRING ',' qstring ')' ';'
+ {
+ struct table *table = table_init();
+
+ if (keyword_to_value($3, nfproto_map, &table->nfproto) <= 0) {
+ error("nfproto");
+ free_table(table);
+ YYERROR;
+ }
+
+ table->name = $5;
+
+ set_table(&batch, table);
+ }
+ ;
+
+del_table : T_DEL_TABLE '(' STRING ',' qstring ',' qstring ')' ';'
+ {
+ struct table *table = table_init();
+
+ if (keyword_to_value($3, nfproto_map, &table->nfproto) <= 0) {
+ error("nfproto");
+ free_table(table);
+ YYERROR;
+ }
+
+ table->name = $5;
+
+ if (!parse_uint64($7, &table->handle)) {
+ error("handle");
+ free_table(table);
+ yyerror;
+ }
+
+ batch.lineno = yylineno;
+ del_table(&batch, table);
+ }
+ ;
+
+add_chain : T_ADD_CHAIN '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct chain *chain = chain_init();
+
+ chain->name = $3;
+
+ if (!parse_uint32($5, &chain->chain_id)) {
+ error("chain_id");
+ free_chain(chain);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &chain->flags)) {
+ error("flags");
+ free_chain(chain);
+ YYERROR;
+ }
+
+ if (!parse_uint64($9, &chain->handle)) {
+ error("handle");
+ free_chain(chain);
+ yyerror;
+ }
+
+ chain->userdata = $11;
+
+ chain->hooknum = NULL;
+ chain->prio = NULL;
+ chain->policy = NULL;
+ chain->bytes = NULL;
+ chain->pkts = NULL;
+
+ batch.lineno = yylineno;
+ add_chain(&batch, chain);
+ }
+ ;
+
+del_chain : T_DEL_CHAIN '(' qstring ')' ';'
+ {
+ struct chain *chain = chain_init();
+
+ chain->name = $3;
+
+ batch.lineno = yylineno;
+ del_chain(&batch, chain);
+ }
+ ;
+
+string_array_items: QSTRING
+ {
+ $$ = array_alloc();
+ if (!array_add($$, $1)) {
+ error("array");
+ YYERROR;
+ }
+ }
+ | string_array_items ',' QSTRING
+ {
+ if (!array_add($1, $3)) {
+ error("array");
+ YYERROR;
+ }
+ $$ = $1;
+ }
+ ;
+
+string_array : '{' string_array_items '}' { $$ = $2; }
+ | NULLSTR { $$ = NULL; }
+ ;
+
+add_basechain : T_ADD_BASECHAIN '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' string_array ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct chain *chain = chain_init();
+
+ chain->name = $3;
+ chain->type = $5;
+
+ if (!parse_uint32($7, &chain->hooknum)) {
+ error("hooknum");
+ free_chain(chain);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &chain->prio)) {
+ error("priority");
+ free_chain(chain);
+ YYERROR;
+ }
+
+ chain->dev = $11;
+ chain->dev_array = $13;
+
+ if (keyword_to_value($15, verdict_map, &chain->policy) < 0) {
+ error("policy");
+ free_chain(chain);
+ YYERROR;
+ }
+ if (!parse_uint32($17, &chain->chain_id)) {
+ error("chain_id");
+ free_chain(chain);
+ YYERROR;
+ }
+ if (!parse_uint32($19, &chain->flags)) {
+ error("flags");
+ free_chain(chain);
+ YYERROR;
+ }
+ if (!parse_uint64($21, &chain->bytes)) {
+ error("bytes");
+ free_chain(chain);
+ YYERROR;
+ }
+ if (!parse_uint64($23, &chain->pkts)) {
+ error("packets");
+ free_chain(chain);
+ YYERROR;
+ }
+ if (!parse_uint64($25, &chain->handle)) {
+ error("handle");
+ free_chain(chain);
+ YYERROR;
+ }
+
+ chain->userdata = $27;
+
+ batch.lineno = yylineno;
+ add_basechain(&batch, chain);
+ }
+ ;
+
+del_basechain : T_DEL_BASECHAIN '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' string_array ',' qstring ',' qstring ')' ';'
+ {
+ struct chain *chain = chain_init();
+
+ chain->name = $3;
+ chain->type = $5;
+
+ if (!parse_uint32($7, &chain->hooknum)) {
+ error("hooknum");
+ free_chain(chain);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &chain->prio)) {
+ error("priority");
+ free_chain(chain);
+ YYERROR;
+ }
+
+ chain->dev = $11;
+ chain->dev_array = $13;
+
+ if (!parse_uint32($15, &chain->chain_id)) {
+ error("chain_id");
+ free_chain(chain);
+ YYERROR;
+ }
+ if (!parse_uint64($17, &chain->handle)) {
+ error("handle");
+ free_chain(chain);
+ YYERROR;
+ }
+
+ batch.lineno = yylineno;
+ del_basechain(&batch, chain);
+ }
+ ;
+
+add_rule : T_ADD_RULE '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct rule *rule = rule_init();
+
+ rule->chain = $3;
+
+ if (!parse_uint32($5, &rule->rule_id)) {
+ error("rule_id");
+ free_rule(rule);
+ YYERROR;
+ }
+ if (!parse_uint64($7, &rule->pos)) {
+ error("position");
+ free_rule(rule);
+ YYERROR;
+ }
+
+ if (!parse_uint32($9, &rule->pos_id)) {
+ error("position_id");
+ free_rule(rule);
+ YYERROR;
+ }
+
+ rule->userdata = $11;
+
+ batch.lineno = yylineno;
+ add_rule(&batch, rule);
+ }
+ ;
+
+del_rule : T_DEL_RULE '(' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct rule *rule = rule_init();
+
+ rule->chain = $3;
+
+ if (!parse_uint32($5, &rule->rule_id)) {
+ error("rule_id");
+ free_rule(rule);
+ YYERROR;
+ }
+ if (!parse_uint64($7, &rule->handle)) {
+ error("handle");
+ free_rule(rule);
+ YYERROR;
+ }
+
+ batch.lineno = yylineno;
+ del_rule(&batch, rule);
+ }
+ ;
+
+add_bitwise : T_ADD_BITWISE '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct bitwise *bitwise = bitwise_init();
+
+ if (keyword_to_value($3, reg_map, &bitwise->sreg) < 0) {
+ error("sreg");
+ free_bitwise(bitwise);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &bitwise->dreg) < 0) {
+ error("dreg");
+ free_bitwise(bitwise);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &bitwise->op)) {
+ error("op");
+ free_bitwise(bitwise);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &bitwise->len)) {
+ error("len");
+ free_bitwise(bitwise);
+ YYERROR;
+ }
+ if (hex_parse($11, &bitwise->mask, &bitwise->mask_len, 16 + NFT_EXTRA) < 0) {
+ error("mask");
+ free_bitwise(bitwise);
+ YYERROR;
+ }
+ if (hex_parse($13, &bitwise->xor, &bitwise->xor_len, 16 + NFT_EXTRA) < 0) {
+ error("xor");
+ free_bitwise(bitwise);
+ YYERROR;
+ }
+ if (hex_parse($15, &bitwise->data, &bitwise->data_len, 16 + NFT_EXTRA) < 0) {
+ error("bitwise");
+ free_bitwise(bitwise);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &bitwise->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_bitwise(bitwise);
+ YYERROR;
+ }
+ }
+ ;
+
+add_byteorder : T_ADD_BYTEORDER '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct byteorder *byteorder = byteorder_init();
+
+ if (keyword_to_value($3, reg_map, &byteorder->sreg) < 0) {
+ error("sreg");
+ free_byteorder(byteorder);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &byteorder->dreg) < 0) {
+ error("dreg");
+ free_byteorder(byteorder);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &byteorder->op)) {
+ error("op");
+ free_byteorder(byteorder);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &byteorder->len)) {
+ error("len");
+ free_byteorder(byteorder);
+ YYERROR;
+ }
+ if (!parse_uint32($11, &byteorder->size)) {
+ error("size");
+ free_byteorder(byteorder);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &byteorder->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_byteorder(byteorder);
+ YYERROR;
+ }
+ }
+ ;
+
+add_cmp : T_ADD_CMP '(' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct cmp *cmp = cmp_init();
+
+ if (keyword_to_value($3, reg_map, &cmp->sreg) < 0) {
+ error("sreg");
+ free_cmp(cmp);
+ YYERROR;
+ }
+ if (!parse_uint32($5, &cmp->op)) {
+ error("op");
+ free_cmp(cmp);
+ YYERROR;
+ }
+ if (hex_parse($7, &cmp->data, &cmp->data_len, 16 + NFT_EXTRA) < 0) {
+ error("data");
+ free_cmp(cmp);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &cmp->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_cmp(cmp);
+ YYERROR;
+ }
+ }
+ ;
+
+add_connlimit : T_ADD_CONNLIMIT '(' qstring ',' qstring ')' ';'
+ {
+ struct connlimit *connlimit = connlimit_init();
+
+ if (!parse_uint32($3, &connlimit->count)) {
+ error("count");
+ free_connlimit(connlimit);
+ YYERROR;
+ }
+ if (!parse_uint32($5, &connlimit->flags)) {
+ error("flags");
+ free_connlimit(connlimit);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &connlimit->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_connlimit(connlimit);
+ YYERROR;
+ }
+ }
+ ;
+
+add_counter : T_ADD_COUNTER '(' qstring ',' qstring')' ';'
+ {
+ struct counter *counter = counter_init();
+
+ if (!parse_uint64($3, &counter->pkts)) {
+ error("packets");
+ free_counter(counter);
+ YYERROR;
+ }
+ if (!parse_uint64($5, &counter->bytes)) {
+ error("bytes");
+ free_counter(counter);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &counter->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_counter(counter);
+ YYERROR;
+ }
+ }
+ ;
+
+add_ct : T_ADD_CT '(' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct ct *ct = ct_init();
+
+ if (keyword_to_value($3, reg_map, &ct->sreg) < 0) {
+ free_ct(ct);
+ error("sreg");
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &ct->dreg) < 0) {
+ error("dreg");
+ free_ct(ct);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &ct->key)) {
+ error("key");
+ free_ct(ct);
+ YYERROR;
+ }
+ if (!parse_uint8($9, &ct->dir)) {
+ error("dir");
+ free_ct(ct);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &ct->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_ct(ct);
+ YYERROR;
+ }
+ }
+ ;
+
+add_dup : T_ADD_DUP '(' qstring ',' qstring ')' ';'
+ {
+ struct dup *dup = dup_init();
+
+ if (keyword_to_value($3, reg_map, &dup->sreg_addr) < 0) {
+ error("sreg_addr");
+ free_dup(dup);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &dup->sreg_dev) < 0) {
+ error("sreg_dev");
+ free_dup(dup);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &dup->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_dup(dup);
+ YYERROR;
+ }
+ }
+ ;
+
+add_dynset : T_ADD_DYNSET '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct dynset *dynset = dynset_init();
+
+ dynset->set = $3;
+
+ if (!parse_uint32($5, &dynset->set_id)) {
+ error("set_id");
+ free_dynset(dynset);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &dynset->op)) {
+ error("op");
+ free_dynset(dynset);
+ YYERROR;
+ }
+ if (keyword_to_value($9, reg_map, &dynset->sreg_key) < 0) {
+ error("sreg_key");
+ free_dynset(dynset);
+ YYERROR;
+ }
+ if (keyword_to_value($11, reg_map, &dynset->sreg_data) < 0) {
+ error("sreg_data");
+ free_dynset(dynset);
+ YYERROR;
+ }
+ if (!parse_uint64($13, &dynset->timeout)) {
+ error("timeout");
+ free_dynset(dynset);
+ YYERROR;
+ }
+
+ if (!parse_uint32($15, &dynset->flags)) {
+ error("flags");
+ free_dynset(dynset);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &dynset->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_dynset(dynset);
+ YYERROR;
+ }
+ }
+ ;
+
+add_exthdr : T_ADD_EXTHDR '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct exthdr *exthdr = exthdr_init();
+
+ if (keyword_to_value($3, reg_map, &exthdr->sreg) < 0) {
+ error("sreg");
+ free_exthdr(exthdr);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &exthdr->dreg) < 0) {
+ error("dreg");
+ free_exthdr(exthdr);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &exthdr->offset)) {
+ error("offset");
+ free_exthdr(exthdr);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &exthdr->len)) {
+ error("len");
+ free_exthdr(exthdr);
+ YYERROR;
+ }
+ if (!parse_uint32($11, &exthdr->type)) {
+ error("type");
+ free_exthdr(exthdr);
+ YYERROR;
+ }
+ if (!parse_uint32($13, &exthdr->op)) {
+ error("op");
+ free_exthdr(exthdr);
+ YYERROR;
+ }
+ if (!parse_uint32($15, &exthdr->flags)) {
+ error("flags");
+ free_exthdr(exthdr);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &exthdr->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_exthdr(exthdr);
+ YYERROR;
+ }
+ }
+ ;
+
+add_fib : T_ADD_FIB '(' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct fib *fib = fib_init();
+
+ if (!parse_uint32($3, &fib->flags)) {
+ error("flags");
+ free_fib(fib);
+ YYERROR;
+ }
+ if (!parse_uint32($5, &fib->result)) {
+ error("result");
+ free_fib(fib);
+ YYERROR;
+ }
+ if (keyword_to_value($7, reg_map, &fib->dreg) < 0) {
+ error("dreg");
+ free_fib(fib);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &fib->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_fib(fib);
+ YYERROR;
+ }
+ }
+ ;
+
+add_fwd : T_ADD_FWD '(' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct fwd *fwd = fwd_init();
+
+ if (keyword_to_value($3, reg_map, &fwd->sreg_addr) < 0) {
+ error("sreg_addr");
+ free_fwd(fwd);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &fwd->sreg_dev) < 0) {
+ error("sreg_dev");
+ free_fwd(fwd);
+ YYERROR;
+ }
+ if (keyword_to_value($7, nfproto_map, &fwd->nfproto) <= 0) {
+ error("nfproto");
+ free_fwd(fwd);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &fwd->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_fwd(fwd);
+ YYERROR;
+ }
+ }
+ ;
+
+add_hash : T_ADD_HASH '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct hash *hash = hash_init();
+
+ if (!parse_uint32($3, &hash->type)) {
+ error("type");
+ free_hash(hash);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &hash->sreg) < 0) {
+ error("sreg");
+ free_hash(hash);
+ YYERROR;
+ }
+ if (keyword_to_value($7, reg_map, &hash->dreg) < 0) {
+ error("dreg");
+ free_hash(hash);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &hash->len)) {
+ error("len");
+ free_hash(hash);
+ YYERROR;
+ }
+ if (!parse_uint32($11, &hash->modulus)) {
+ error("modulus");
+ free_hash(hash);
+ YYERROR;
+ }
+ if (!parse_uint32($13, &hash->seed)) {
+ error("seed");
+ free_hash(hash);
+ YYERROR;
+ }
+ if (!parse_uint32($15, &hash->offset)) {
+ error("offset");
+ free_hash(hash);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &hash->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_hash(hash);
+ YYERROR;
+ }
+ }
+ ;
+
+add_inner : T_ADD_INNER '(' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct inner *inner = inner_init();
+
+ if (!parse_uint32($3, &inner->num)) {
+ error("num");
+ free_inner(inner);
+ YYERROR;
+ }
+ if (!parse_uint32($5, &inner->type)) {
+ error("type");
+ free_inner(inner);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &inner->flags)) {
+ error("flags");
+ free_inner(inner);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &inner->hdrsize)) {
+ error("hdrsize");
+ free_inner(inner);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &inner->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_inner(inner);
+ YYERROR;
+ }
+
+ batch.ctx.expr_list = &inner->expr_list;
+ }
+ ;
+
+add_immediate : T_ADD_IMMEDIATE '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct immediate *imm = immediate_init();
+
+ if (keyword_to_value($3, reg_map, &imm->dreg) < 0) {
+ error("dreg");
+ free_immediate(imm);
+ YYERROR;
+ }
+ if (keyword_to_value($5, verdict_map, &imm->verdict) < 0) {
+ error("verdict");
+ free_immediate(imm);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &imm->chain_id)) {
+ error("chain_id");
+ free_immediate(imm);
+ YYERROR;
+ }
+
+ imm->chain = $9;
+
+ if (hex_parse($11, &imm->data, &imm->data_len, 16 + NFT_EXTRA) < 0) {
+ error("data");
+ free_immediate(imm);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &imm->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_immediate(imm);
+ YYERROR;
+ }
+ }
+ ;
+
+add_last : T_ADD_LAST '(' qstring ',' qstring')' ';'
+ {
+ struct last *last = last_init();
+
+ if (!parse_uint64($3, &last->msecs)) {
+ error("msecs");
+ free_last(last);
+ YYERROR;
+ }
+ if (!parse_uint32($5, &last->set)) {
+ error("set");
+ free_last(last);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &last->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_last(last);
+ YYERROR;
+ }
+ }
+ ;
+
+add_limit : T_ADD_LIMIT '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct limit *limit = limit_init();
+
+ if (!parse_uint64($3, &limit->rate)) {
+ error("rate");
+ free_limit(limit);
+ YYERROR;
+ }
+ if (!parse_uint64($5, &limit->unit)) {
+ error("unit");
+ free_limit(limit);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &limit->burst)) {
+ error("burst");
+ free_limit(limit);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &limit->type)) {
+ error("type");
+ free_limit(limit);
+ YYERROR;
+ }
+ if (!parse_uint32($11, &limit->flags)) {
+ error("flags");
+ free_limit(limit);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &limit->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_limit(limit);
+ YYERROR;
+ }
+ }
+ ;
+
+add_log : T_ADD_LOG '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct log *log = log_init();
+
+ if (!parse_uint32($3, &log->snaplen)) {
+ error("snaplen");
+ free_log(log);
+ YYERROR;
+ }
+ if (!parse_uint16($5, &log->group)) {
+ error("group");
+ free_log(log);
+ YYERROR;
+ }
+ if (!parse_uint16($7, &log->qthreshold)) {
+ error("qthreshold");
+ free_log(log);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &log->level)) {
+ error("level");
+ free_log(log);
+ YYERROR;
+ }
+ if (!parse_uint32($11, &log->flags)) {
+ error("flags");
+ free_log(log);
+ YYERROR;
+ }
+
+ log->prefix = $13;
+
+ if (add_expr(&batch, &log->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_log(log);
+ YYERROR;
+ }
+ }
+ ;
+
+add_lookup : T_ADD_LOOKUP '(' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct lookup *lookup = lookup_init();
+
+ if (keyword_to_value($3, reg_map, &lookup->sreg) < 0) {
+ error("sreg");
+ free_lookup(lookup);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &lookup->dreg) < 0) {
+ error("dreg");
+ free_lookup(lookup);
+ YYERROR;
+ }
+
+ lookup->set = $7;
+
+ if (!parse_uint32($9, &lookup->set_id)) {
+ error("set_id");
+ free_lookup(lookup);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &lookup->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_lookup(lookup);
+ YYERROR;
+ }
+ }
+ ;
+
+add_masq : T_ADD_MASQ '(' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct masq *masq = masq_init();
+
+ if (!parse_uint32($3, &masq->flags)) {
+ error("flags");
+ free_masq(masq);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &masq->sreg_proto_min) < 0) {
+ error("sreg_proto_min");
+ free_masq(masq);
+ YYERROR;
+ }
+ if (keyword_to_value($7, reg_map, &masq->sreg_proto_max) < 0) {
+ error("sreg_proto_max");
+ free_masq(masq);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &masq->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_masq(masq);
+ YYERROR;
+ }
+ }
+ ;
+
+add_match : T_ADD_MATCH '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct match *match = match_init();
+
+ match->name = $3;
+
+ if (!parse_uint32($5, &match->rev)) {
+ error("rev");
+ free_match(match);
+ YYERROR;
+ }
+ if (hex_parse($7, &match->data, &match->data_len, 65536) < 0) {
+ error("data");
+ free_match(match);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &match->l4proto)) {
+ error("l4proto");
+ free_match(match);
+ YYERROR;
+ }
+ if (!parse_uint32($11, &match->flags)) {
+ error("flags");
+ free_match(match);
+ YYERROR;
+ }
+
+ /* special compat handling. */
+ if (batch.ctx.rule) {
+ batch.ctx.rule->compat.l4proto = match->l4proto;
+ batch.ctx.rule->compat.flags = match->flags;
+ }
+
+ if (add_expr(&batch, &match->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_match(match);
+ YYERROR;
+ }
+ }
+ ;
+
+add_meta : T_ADD_META '(' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct meta *meta = meta_init();
+
+ if (keyword_to_value($3, reg_map, &meta->sreg) < 0) {
+ error("sreg");
+ free_meta(meta);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &meta->dreg) < 0) {
+ error("dreg");
+ free_meta(meta);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &meta->key)) {
+ error("key");
+ free_meta(meta);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &meta->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_meta(meta);
+ YYERROR;
+ }
+ }
+ ;
+
+add_nat : T_ADD_NAT '(' STRING ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct nat *nat = nat_init();
+
+ if (keyword_to_value($3, nfproto_map, &nat->nfproto) <= 0) {
+ error("nfproto");
+ free_nat(nat);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &nat->sreg_addr_min) < 0) {
+ error("sreg_addr_min");
+ free_nat(nat);
+ YYERROR;
+ }
+ if (keyword_to_value($7, reg_map, &nat->sreg_addr_max) < 0) {
+ error("sreg_addr_max");
+ free_nat(nat);
+ YYERROR;
+ }
+ if (keyword_to_value($9, reg_map, &nat->sreg_proto_min) < 0) {
+ error("sreg_proto_min");
+ free_nat(nat);
+ YYERROR;
+ }
+ if (keyword_to_value($11, reg_map, &nat->sreg_proto_max) < 0) {
+ error("sreg_proto_max");
+ free_nat(nat);
+ YYERROR;
+ }
+ if (!parse_uint32($13, &nat->type)) {
+ error("type");
+ free_nat(nat);
+ YYERROR;
+ }
+ if (!parse_uint32($15, &nat->flags)) {
+ error("flags");
+ free_nat(nat);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &nat->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_nat(nat);
+ YYERROR;
+ }
+ }
+ ;
+
+add_numgen : T_ADD_NUMGEN '(' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct numgen *numgen = numgen_init();
+
+ if (keyword_to_value($3, reg_map, &numgen->dreg) < 0) {
+ error("dreg");
+ free_numgen(numgen);
+ YYERROR;
+ }
+ if (!parse_uint32($5, &numgen->modulus)) {
+ error("modulus");
+ free_numgen(numgen);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &numgen->type)) {
+ error("type");
+ free_numgen(numgen);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &numgen->offset)) {
+ error("offset");
+ free_numgen(numgen);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &numgen->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_numgen(numgen);
+ YYERROR;
+ }
+ }
+ ;
+
+add_objref : T_ADD_OBJREF '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct objref *objref = objref_init();
+
+ if (!parse_uint32($3, &objref->type)) {
+ error("type");
+ free_objref(objref);
+ YYERROR;
+ }
+
+ objref->name = $5;
+
+ if (keyword_to_value($7, reg_map, &objref->sreg) < 0) {
+ error("sreg");
+ free_objref(objref);
+ YYERROR;
+ }
+
+ objref->set_name = $9;
+
+ if (!parse_uint32($11, &objref->set_id)) {
+ error("set_id");
+ free_objref(objref);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &objref->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_objref(objref);
+ YYERROR;
+ }
+ }
+ ;
+
+add_osf : T_ADD_OSF '(' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct osf *osf = osf_init();
+
+ if (keyword_to_value($3, reg_map, &osf->dreg) < 0) {
+ error("dreg");
+ free_osf(osf);
+ YYERROR;
+ }
+ if (!parse_uint32($5, &osf->ttl)) {
+ error("ttl");
+ free_osf(osf);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &osf->flags)) {
+ error("flags");
+ free_osf(osf);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &osf->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_osf(osf);
+ YYERROR;
+ }
+ }
+ ;
+
+add_payload : T_ADD_PAYLOAD '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct payload *payload = payload_init();
+
+ if (keyword_to_value($3, reg_map, &payload->sreg) < 0) {
+ error("sreg");
+ free_payload(payload);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &payload->dreg) < 0) {
+ error("dreg");
+ free_payload(payload);
+ YYERROR;
+ }
+ if (keyword_to_value($7, base_map, &payload->base) < 0) {
+ error("base");
+ free_payload(payload);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &payload->offset)) {
+ error("offset");
+ free_payload(payload);
+ YYERROR;
+ }
+ if (!parse_uint32($11, &payload->len)) {
+ error("len");
+ free_payload(payload);
+ YYERROR;
+ }
+ if (!parse_uint32($13, &payload->csum_type)) {
+ error("csum_type");
+ free_payload(payload);
+ YYERROR;
+ }
+ if (!parse_uint32($15, &payload->csum_offset)) {
+ error("csum_offset");
+ free_payload(payload);
+ YYERROR;
+ }
+ if (!parse_uint32($17, &payload->csum_flags)) {
+ error("flags");
+ free_payload(payload);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &payload->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_payload(payload);
+ YYERROR;
+ }
+ }
+ ;
+
+add_queue : T_ADD_QUEUE '(' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct queue *queue = queue_init();
+
+ if (keyword_to_value($3, reg_map, &queue->sreg_qnum) < 0) {
+ error("sreg_qnum");
+ free_queue(queue);
+ YYERROR;
+ }
+ if (!parse_uint16($5, &queue->queue_num)) {
+ error("queue_num");
+ free_queue(queue);
+ YYERROR;
+ }
+ if (!parse_uint16($7, &queue->queue_total)) {
+ error("queue_total");
+ free_queue(queue);
+ YYERROR;
+ }
+ if (!parse_uint16($9, &queue->flags)) {
+ error("flags");
+ free_queue(queue);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &queue->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_queue(queue);
+ YYERROR;
+ }
+ }
+ ;
+
+add_quota : T_ADD_QUOTA '(' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct quota *quota = quota_init();
+
+ if (!parse_uint64($3, &quota->bytes)) {
+ error("bytes");
+ free_quota(quota);
+ YYERROR;
+ }
+ if (!parse_uint64($5, &quota->consumed)) {
+ error("consumed");
+ free_quota(quota);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &quota->flags)) {
+ error("flags");
+ free_quota(quota);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &quota->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_quota(quota);
+ YYERROR;
+ }
+ }
+ ;
+
+add_range : T_ADD_RANGE '(' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct range *range = range_init();
+
+ if (keyword_to_value($3, reg_map, &range->sreg) < 0) {
+ error("sreg");
+ free_range(range);
+ YYERROR;
+ }
+ if (!parse_uint32($5, &range->op)) {
+ error("op");
+ free_range(range);
+ YYERROR;
+ }
+ if (hex_parse($7, &range->data_from, &range->data_from_len, 16 + NFT_EXTRA) < 0) {
+ error("data_from");
+ free_range(range);
+ YYERROR;
+ }
+ if (hex_parse($9, &range->data_to, &range->data_to_len, 16 + NFT_EXTRA) < 0) {
+ error("data_to");
+ free_range(range);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &range->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_range(range);
+ YYERROR;
+ }
+ }
+ ;
+
+add_redir : T_ADD_REDIR '(' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct redir *redir = redir_init();
+
+ if (!parse_uint32($3, &redir->flags)) {
+ error("flags");
+ free_redir(redir);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &redir->sreg_proto_min) < 0) {
+ error("sreg_proto_min");
+ free_redir(redir);
+ YYERROR;
+ }
+ if (keyword_to_value($7, reg_map, &redir->sreg_proto_max) < 0) {
+ error("sreg_proto_max");
+ free_redir(redir);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &redir->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_redir(redir);
+ YYERROR;
+ }
+ }
+ ;
+
+add_reject : T_ADD_REJECT '(' qstring ',' qstring')' ';'
+ {
+ struct reject *reject = reject_init();
+
+ if (!parse_uint32($3, &reject->type)) {
+ error("type");
+ free_reject(reject);
+ YYERROR;
+ }
+ if (!parse_uint8($5, &reject->icmp_code)) {
+ error("icmp_code");
+ free_reject(reject);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &reject->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_reject(reject);
+ YYERROR;
+ }
+ }
+ ;
+
+add_rt : T_ADD_RT '(' qstring ',' qstring ')' ';'
+ {
+ struct rt *rt = rt_init();
+
+ if (keyword_to_value($3, reg_map, &rt->dreg) < 0) {
+ error("dreg");
+ free_rt(rt);
+ YYERROR;
+ }
+ if (!parse_uint32($5, &rt->key)) {
+ error("key");
+ free_rt(rt);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &rt->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_rt(rt);
+ YYERROR;
+ }
+ }
+ ;
+
+add_socket : T_ADD_SOCKET '(' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct socket *socket = socket_init();
+
+ if (keyword_to_value($3, reg_map, &socket->dreg) < 0) {
+ error("dreg");
+ free_socket(socket);
+ YYERROR;
+ }
+ if (!parse_uint32($5, &socket->key)) {
+ error("key");
+ free_socket(socket);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &socket->level)) {
+ error("level");
+ free_socket(socket);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &socket->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_socket(socket);
+ YYERROR;
+ }
+ }
+ ;
+
+add_synproxy : T_ADD_SYNPROXY '(' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct synproxy *synproxy = synproxy_init();
+
+ if (!parse_uint16($3, &synproxy->mss)) {
+ error("mss");
+ free_synproxy(synproxy);
+ YYERROR;
+ }
+ if (!parse_uint8($5, &synproxy->wscale)) {
+ error("wscale");
+ free_synproxy(synproxy);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &synproxy->flags)) {
+ error("flags");
+ free_synproxy(synproxy);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &synproxy->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_synproxy(synproxy);
+ YYERROR;
+ }
+ }
+ ;
+
+add_target : T_ADD_TARGET '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct target *target = target_init();
+
+ target->name = $3;
+
+ if (!parse_uint32($5, &target->rev)) {
+ error("rev");
+ free_target(target);
+ YYERROR;
+ }
+ if (hex_parse($7, &target->data, &target->data_len, 65536) < 0) {
+ error("data");
+ free_target(target);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &target->l4proto)) {
+ error("l4proto");
+ free_target(target);
+ YYERROR;
+ }
+ if (!parse_uint32($11, &target->flags)) {
+ error("flags");
+ free_target(target);
+ YYERROR;
+ }
+
+ /* special compat handling. */
+ if (batch.ctx.rule) {
+ batch.ctx.rule->compat.l4proto = target->l4proto;
+ batch.ctx.rule->compat.flags = target->flags;
+ }
+
+ if (add_expr(&batch, &target->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_target(target);
+ YYERROR;
+ }
+ }
+ ;
+
+add_tproxy : T_ADD_TPROXY '(' STRING ',' qstring ',' qstring ')' ';'
+ {
+ struct tproxy *tproxy = tproxy_init();
+
+ if (keyword_to_value($3, nfproto_map, &tproxy->nfproto) <= 0) {
+ error("nfproto");
+ free_tproxy(tproxy);
+ YYERROR;
+ }
+ if (keyword_to_value($5, reg_map, &tproxy->sreg_addr) < 0) {
+ error("sreg_addr");
+ free_tproxy(tproxy);
+ YYERROR;
+ }
+ if (keyword_to_value($7, reg_map, &tproxy->sreg_port) < 0) {
+ error("sreg_port");
+ free_tproxy(tproxy);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &tproxy->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_tproxy(tproxy);
+ YYERROR;
+ }
+ }
+ ;
+
+add_tunnel : T_ADD_TUNNEL '(' qstring ',' qstring ')' ';'
+ {
+ struct tunnel *tunnel = tunnel_init();
+
+ if (keyword_to_value($3, reg_map, &tunnel->dreg) < 0) {
+ error("dreg");
+ free_tunnel(tunnel);
+ YYERROR;
+ }
+ if (!parse_uint32($5, &tunnel->key)) {
+ error("key");
+ free_tunnel(tunnel);
+ YYERROR;
+ }
+//XXX if (!parse_uint32($7, &tunnel->mode)) {
+// free_tunnel(tunnel);
+// YYERROR;
+// }
+
+ if (add_expr(&batch, &tunnel->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_tunnel(tunnel);
+ YYERROR;
+ }
+ }
+ ;
+
+add_xfrm : T_ADD_XFRM '(' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct xfrm *xfrm = xfrm_init();
+
+ if (keyword_to_value($3, reg_map, &xfrm->dreg) < 0) {
+ error("dreg");
+ free_xfrm(xfrm);
+ YYERROR;
+ }
+ if (!parse_uint32($5, &xfrm->key)) {
+ error("key");
+ free_xfrm(xfrm);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &xfrm->spnum)) {
+ error("spnum");
+ free_xfrm(xfrm);
+ YYERROR;
+ }
+ if (!parse_uint8($9, &xfrm->dir)) {
+ error("dir");
+ free_xfrm(xfrm);
+ YYERROR;
+ }
+
+ if (add_expr(&batch, &xfrm->expr) < 0) {
+ error("missing context rule or set for expression");
+ free_xfrm(xfrm);
+ YYERROR;
+ }
+ }
+ ;
+
+add_obj_counter : T_ADD_OBJ_COUNTER '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct obj *obj = obj_init();
+ struct counter *counter;
+
+ obj->name = $3;
+
+ if (!parse_uint64($5, &obj->handle)) {
+ error("handle");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ obj->userdata = $7;
+
+ *obj->type = NFT_OBJECT_COUNTER;
+
+ counter = counter_init();
+ obj->u.counter = counter;
+
+ if (!parse_uint64($9, &counter->bytes)) {
+ error("bytes");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ if (!parse_uint64($11, &counter->pkts)) {
+ error("pkts");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ add_obj(&batch, obj);
+ }
+ ;
+
+add_obj_quota : T_ADD_OBJ_QUOTA '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct obj *obj = obj_init();
+ struct quota *quota;
+
+ obj->name = $3;
+
+ if (!parse_uint64($5, &obj->handle)) {
+ error("handle");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ obj->userdata = $7;
+
+ *obj->type = NFT_OBJECT_QUOTA;
+
+ quota = quota_init();
+ obj->u.quota = quota;
+
+ if (!parse_uint64($9, &quota->bytes)) {
+ error("bytes");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ if (!parse_uint64($11, &quota->consumed)) {
+ error("consumed");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ if (!parse_uint32($13, &quota->flags)) {
+ error("flags");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ add_obj(&batch, obj);
+ }
+ ;
+
+add_obj_connlimit: T_ADD_OBJ_CONNLIMIT '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct obj *obj = obj_init();
+ struct connlimit *connlimit;
+
+ obj->name = $3;
+
+ if (!parse_uint64($5, &obj->handle)) {
+ error("handle");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ obj->userdata = $7;
+
+ *obj->type = NFT_OBJECT_CONNLIMIT;
+
+ connlimit = connlimit_init();
+ obj->u.connlimit = connlimit;
+
+ if (!parse_uint32($9, &connlimit->count)) {
+ error("count");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ if (!parse_uint32($11, &connlimit->flags)) {
+ error("flags");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ add_obj(&batch, obj);
+ }
+ ;
+
+add_obj_secmark : T_ADD_OBJ_SECMARK '(' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct obj *obj = obj_init();
+ struct secmark *secmark;
+
+ obj->name = $3;
+
+ if (!parse_uint64($5, &obj->handle)) {
+ error("handle");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ obj->userdata = $7;
+
+ *obj->type = NFT_OBJECT_SECMARK;
+
+ secmark = secmark_init();
+ obj->u.secmark = secmark;
+ secmark->ctx = $9;
+
+ add_obj(&batch, obj);
+ }
+ ;
+
+add_obj_synproxy: T_ADD_OBJ_SYNPROXY '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct obj *obj = obj_init();
+ struct synproxy *synproxy;
+
+ obj->name = $3;
+
+ if (!parse_uint64($5, &obj->handle)) {
+ error("handle");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ obj->userdata = $7;
+
+ *obj->type = NFT_OBJECT_SYNPROXY;
+
+ synproxy = synproxy_init();
+ obj->u.synproxy = synproxy;
+
+ if (!parse_uint16($9, &synproxy->mss)) {
+ error("mss");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint8($11, &synproxy->wscale)) {
+ error("wscale");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint32($13, &synproxy->flags)) {
+ error("flags");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ add_obj(&batch, obj);
+ }
+ ;
+
+del_obj_unknown : T_DEL_OBJ_UNKNOWN '(' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct obj *obj = obj_init();
+
+ obj->name = $3;
+
+ if (!parse_uint64($5, &obj->handle)) {
+ error("handle");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ obj->userdata = $7;
+
+ if (!parse_uint32($9, &obj->type)) {
+ error("type");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ del_obj(&batch, obj);
+ }
+ ;
+
+u32_array_items : QSTRING
+ {
+ $$ = array_u32_alloc();
+ if (!array_u32_add($$, $1)) {
+ error("array");
+ YYERROR;
+ }
+ }
+ | u32_array_items ',' QSTRING
+ {
+ if (!array_u32_add($1, $3)) {
+ error("array");
+ YYERROR;
+ }
+ $$ = $1;
+ }
+ ;
+
+u32_array : '{' u32_array_items '}' { $$ = $2; }
+ | NULLSTR { $$ = NULL; }
+ ;
+
+
+add_obj_ct_timeout : T_ADD_OBJ_CT_TIMEOUT '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' u32_array ')' ';'
+ {
+ struct obj *obj = obj_init();
+ struct ct_timeout *ct_timeout;
+
+ obj->name = $3;
+
+ if (!parse_uint64($5, &obj->handle)) {
+ error("handle");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ obj->userdata = $7;
+
+ *obj->type = NFT_OBJECT_CT_TIMEOUT;
+
+ ct_timeout = ct_timeout_init();
+ obj->u.ct_timeout = ct_timeout;
+
+ if (!parse_uint16($9, &ct_timeout->l3proto)) {
+ error("l3proto");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ if (!parse_uint8($11, &ct_timeout->l4proto)) {
+ error("l4proto");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ ct_timeout->timeout_array = $13;
+
+ batch.lineno = yylineno;
+ add_obj(&batch, obj);
+ }
+ ;
+
+add_obj_ct_helper : T_ADD_OBJ_CT_HELPER '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct ct_helper *ct_helper;
+ struct obj *obj = obj_init();
+
+ obj->name = $3;
+
+ if (!parse_uint64($5, &obj->handle)) {
+ error("handle");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ obj->userdata = $7;
+
+ *obj->type = NFT_OBJECT_CT_HELPER;
+
+ ct_helper = ct_helper_init();
+ obj->u.ct_helper = ct_helper;
+
+ ct_helper->name = $9;
+
+ if (!parse_uint16($11, &ct_helper->l3proto)) {
+ error("l3proto");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ if (!parse_uint8($13, &ct_helper->l4proto)) {
+ error("l4proto");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ batch.lineno = yylineno;
+ add_obj(&batch, obj);
+ }
+ ;
+
+add_obj_ct_expect : T_ADD_OBJ_CT_EXPECT '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct obj *obj = obj_init();
+ struct ct_expect *ct_expect;
+
+ obj->name = $3;
+
+ if (!parse_uint64($5, &obj->handle)) {
+ error("handle");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ obj->userdata = $7;
+
+ *obj->type = NFT_OBJECT_CT_EXPECT;
+
+ ct_expect = ct_expect_init();
+ obj->u.ct_expect = ct_expect;
+
+ if (!parse_uint16($9, &ct_expect->l3proto)) {
+ error("l3proto");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ if (!parse_uint8($11, &ct_expect->l4proto)) {
+ error("l4proto");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ if (!parse_uint16($13, &ct_expect->dport)) {
+ error("dport");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ if (!parse_uint32($15, &ct_expect->timeout)) {
+ error("timeout");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ if (!parse_uint8($17, &ct_expect->size)) {
+ error("size");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ batch.lineno = yylineno;
+ add_obj(&batch, obj);
+ }
+ ;
+
+add_obj_limit : T_ADD_OBJ_LIMIT '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct obj *obj = obj_init();
+ struct limit *limit;
+
+ obj->name = $3;
+
+ if (!parse_uint64($5, &obj->handle)) {
+ error("handle");
+ YYERROR;
+ }
+
+ obj->userdata = $7;
+
+ *obj->type = NFT_OBJECT_LIMIT;
+
+ limit = limit_init();
+ obj->u.limit = limit;
+
+ if (!parse_uint64($9, &limit->rate)) {
+ error("rate");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint64($11, &limit->unit)) {
+ error("unit");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint32($13, &limit->burst)) {
+ error("burst");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint32($15, &limit->type)) {
+ error("type");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint32($17, &limit->flags)) {
+ error("flags");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ batch.lineno = yylineno;
+ add_obj(&batch, obj);
+ }
+ ;
+
+add_obj_tunnel : T_ADD_OBJ_TUNNEL '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct obj *obj = obj_init();
+ struct tunnel_tmpl *tunnel;
+ int len;
+
+ obj->name = $3;
+
+ if (!parse_uint64($5, &obj->handle)) {
+ error("handle");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ obj->userdata = $7;
+
+ *obj->type = NFT_OBJECT_TUNNEL;
+
+ tunnel = tunnel_tmpl_init();
+ obj->u.tun = tunnel;
+
+ if (!parse_uint32($9, &tunnel->id)) {
+ error("flags");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint32($11, &tunnel->src_v4)) {
+ error("src_v4");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint32($13, &tunnel->dst_v4)) {
+ error("dst_v4");
+ free_obj(obj);
+ YYERROR;
+ }
+ len = 16;
+ if (hex_parse($15, &tunnel->src_v6, &len, 16) < 0 || len != 16) {
+ error("src_v6");
+ free_obj(obj);
+ YYERROR;
+ }
+ len = 16;
+ if (hex_parse($17, &tunnel->dst_v6, &len, 16) < 0 || len != 16) {
+ error("dst_v6");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint32($19, &tunnel->flowlabel)) {
+ error("flowlabel");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint16($21, &tunnel->sport)) {
+ error("sport");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint16($23, &tunnel->dport)) {
+ error("dport");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint32($25, &tunnel->flags)) {
+ error("flags");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint8($27, &tunnel->tos)) {
+ error("tos");
+ free_obj(obj);
+ YYERROR;
+ }
+ if (!parse_uint8($29, &tunnel->ttl)) {
+ error("ttl");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ batch.lineno = yylineno;
+ add_obj(&batch, obj);
+ }
+ ;
+
+
+add_obj_unknown : T_ADD_OBJ_UNKNOWN '(' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct obj *obj = obj_init();
+
+ obj->name = $3;
+
+ if (!parse_uint64($5, &obj->handle)) {
+ error("handle");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ obj->userdata = $7;
+
+ if (!parse_uint32($9, &obj->type)) {
+ error("type");
+ free_obj(obj);
+ YYERROR;
+ }
+
+ add_obj(&batch, obj);
+ }
+ ;
+
+u8_array_items : QSTRING
+ {
+ $$ = array_u8_alloc();
+ if (!array_u8_add($$, $1)) {
+ error("array");
+ YYERROR;
+ }
+ }
+ | u8_array_items ',' QSTRING
+ {
+ if (!array_u8_add($1, $3)) {
+ error("array");
+ YYERROR;
+ }
+ $$ = $1;
+ }
+ ;
+
+u8_array : '{' u8_array_items '}' { $$ = $2; }
+ | NULLSTR { $$ = NULL; }
+ ;
+
+ /* name, flags, key_len, key_type, data_len, data_type, policy, size, u8_array, set_id, timeout, gc_interval,obj_type, handle, userdata */
+add_set : T_ADD_SET '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' u8_array ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct set *set = set_init();
+
+ set->name = $3;
+ if (!parse_uint32($5, &set->flags)) {
+ error("flags");
+ free_set(set);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &set->key_len)) {
+ error("key_len");
+ free_set(set);
+ YYERROR;
+ }
+ if (!parse_uint32($9, &set->key_type)) {
+ error("key_type");
+ free_set(set);
+ YYERROR;
+ }
+ if (!parse_uint32($11, &set->data_len)) {
+ error("data_len");
+ free_set(set);
+ YYERROR;
+ }
+ if (keyword_to_value($13, datatype_map, &set->data_type) < 0) {
+ error("data_type");
+ free_set(set);
+ YYERROR;
+ }
+ if (!parse_uint32($15, &set->policy)) {
+ error("policy");
+ free_set(set);
+ YYERROR;
+ }
+ if (!parse_uint32($17, &set->size)) {
+ error("size");
+ free_set(set);
+ YYERROR;
+ }
+
+ set->field_array = $19;
+
+ if (!parse_uint32($21, &set->set_id)) {
+ error("set_id");
+ free_set(set);
+ YYERROR;
+ }
+ if (!parse_uint64($23, &set->timeout)) {
+ error("timeout");
+ free_set(set);
+ YYERROR;
+ }
+ if (!parse_uint32($25, &set->gc_interval)) {
+ error("gc_interval");
+ free_set(set);
+ YYERROR;
+ }
+ if (!parse_uint32($27, &set->obj_type)) {
+ error("obj_type");
+ free_set(set);
+ YYERROR;
+ }
+ if (!parse_uint64($29, &set->handle)) {
+ error("handle");
+ free_set(set);
+ YYERROR;
+ }
+
+ set->userdata = $31;
+
+ batch.lineno = yylineno;
+ add_set(&batch, set);
+ }
+ ;
+
+set_set : T_SET_SET '(' qstring ',' qstring ')' ';'
+ {
+ struct set *set = set_init();
+
+ set->name = $3;
+
+ if (!parse_uint32($5, &set->set_id)) {
+ error("set_id");
+ YYERROR;
+ }
+
+ set_set(&batch, set);
+ }
+ ;
+
+del_set : T_DEL_SET '(' qstring ',' qstring ')' ';'
+ {
+ struct set *set = set_init();
+
+ set->name = $3;
+
+ if (!parse_uint64($5, &set->handle)) {
+ error("handle");
+ YYERROR;
+ }
+
+ batch.lineno = yylineno;
+ del_set(&batch, set);
+ }
+ ;
+
+flush_set : T_FLUSH_SET '(' qstring ',' qstring ')' ';'
+ {
+ struct set *set = set_init();
+
+ set->name = $3;
+
+ if (!parse_uint64($5, &set->handle)) {
+ error("handle");
+ YYERROR;
+ }
+
+ batch.lineno = yylineno;
+ flush_set(&batch, set);
+ }
+ ;
+
+ /* key, key_end, flags, verdict, chain, data, timeout, expiration, userdata */
+add_elem : T_ADD_ELEM '(' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct elem *elem = elem_init();
+
+ if (hex_parse($3, &elem->key, &elem->key_len, 64 + NFT_EXTRA) < 0) {
+ error("key");
+ free_elem(elem);
+ YYERROR;
+ }
+ if (hex_parse($5, &elem->key_end, &elem->key_end_len, 64 + NFT_EXTRA) < 0) {
+ error("key_end");
+ free_elem(elem);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &elem->flags)) {
+ error("flags");
+ free_elem(elem);
+ YYERROR;
+ }
+ if (keyword_to_value($9, verdict_map, &elem->verdict) < 0) {
+ error("verdict");
+ free_elem(elem);
+ YYERROR;
+ }
+
+ elem->chain = $11;
+
+ if (hex_parse($13, &elem->data, &elem->data_len, 64 + NFT_EXTRA) < 0) {
+ error("data");
+ free_elem(elem);
+ YYERROR;
+ }
+ if (!parse_uint64($15, &elem->timeout)) {
+ error("timeout");
+ free_elem(elem);
+ YYERROR;
+ }
+ if (!parse_uint64($17, &elem->expiration)) {
+ error("expiration");
+ free_elem(elem);
+ YYERROR;
+ }
+
+ elem->objname = $19;
+
+ elem->userdata = $21;
+
+ batch.lineno = yylineno;
+ add_elem(&batch, elem);
+ }
+ ;
+
+del_elem : T_DEL_ELEM '(' qstring ',' qstring ',' qstring ')' ';'
+ {
+ struct elem *elem = elem_init();
+
+ if (hex_parse($3, &elem->key, &elem->key_len, 64 + NFT_EXTRA) < 0) {
+ error("key_len");
+ YYERROR;
+ }
+ if (hex_parse($5, &elem->key_end, &elem->key_end_len, 64 + NFT_EXTRA) < 0) {
+ error("key_len");
+ YYERROR;
+ }
+ if (!parse_uint32($7, &elem->flags)) {
+ error("flags");
+ YYERROR;
+ }
+
+ batch.lineno = yylineno;
+ del_elem(&batch, elem);
+ }
+ ;
+
+add_flowtable : T_ADD_FLOWTABLE '(' qstring ',' qstring ',' qstring ',' string_array ',' qstring ')' ';'
+ {
+ struct flowtable *flowtable = flowtable_init();
+
+ flowtable->name = $3;
+
+ if (!parse_uint32($5, &flowtable->hooknum)) {
+ error("hooknum");
+ free_flowtable(flowtable);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &flowtable->prio)) {
+ error("priority");
+ free_flowtable(flowtable);
+ YYERROR;
+ }
+
+ flowtable->dev_array = $9;
+
+ if (!parse_uint64($11, &flowtable->handle)) {
+ error("handle");
+ free_flowtable(flowtable);
+ YYERROR;
+ }
+
+ batch.lineno = yylineno;
+ add_flowtable(&batch, flowtable);
+ }
+ ;
+
+del_flowtable : T_DEL_FLOWTABLE '(' qstring ',' qstring ',' qstring ',' string_array ',' qstring ')' ';'
+ {
+ struct flowtable *flowtable = flowtable_init();
+
+ flowtable->name = $3;
+
+ if (!parse_uint32($5, &flowtable->hooknum)) {
+ error("hooknum");
+ free_flowtable(flowtable);
+ YYERROR;
+ }
+ if (!parse_uint32($7, &flowtable->prio)) {
+ error("priority");
+ free_flowtable(flowtable);
+ YYERROR;
+ }
+
+ flowtable->dev_array = $9;
+
+ if (!parse_uint64($11, &flowtable->handle)) {
+ error("handle");
+ free_flowtable(flowtable);
+ YYERROR;
+ }
+
+ batch.lineno = yylineno;
+ del_flowtable(&batch, flowtable);
+ }
+ ;
+
+commit : T_COMMIT '(' ')' ';'
+ {
+ if (batch_commit(&batch) < 0)
+ test_fails = true;
+ }
+ ;
+
+abort : T_ABORT '(' ')' ';'
+ {
+ if (batch_abort(&batch) < 0)
+ test_fails = true;
+ }
+ ;
+
+%%
+
+static int yyerror(const char *msg)
+{
+ fprintf(stderr, "ERROR: parsing test file in line: %d, symbol '%s': %s\n",
+ yylineno, yytext, msg);
+ return -1;
+}
+
+/* -1 fatal, 0 success, 1 failure */
+static int run_one_test(FILE *fp)
+{
+ int ret = 0;
+
+ yylineno = 1;
+ memset(&batch, 0, sizeof(batch));
+ setup_batch(&batch);
+
+ yyrestart(fp);
+ if (yyparse() < 0) {
+ ret = 1;
+ fprintf(stderr, "error parsing test file in line: %d, invalid value\n",
+ yylineno);
+ goto err_close;
+ }
+
+ if (!batch_empty(&batch)) {
+ ret = 1;
+ fprintf(stderr, "no commit/abort in test\n");
+ goto err_close;
+ }
+
+ if (kernel_is_tainted()) {
+ ret = -1;
+ fprintf(stderr, "FATAL: taints kernel\n");
+ goto err_close;
+ }
+
+ /* walk over hooks to check for uaf */
+ list_hooks(batch.nl);
+
+ if (!noflush)
+ flush_ruleset(batch.nl);
+
+ if (kernel_is_tainted()) {
+ ret = -1;
+ fprintf(stderr, "FATAL: taints kernel\n");
+ goto err_close;
+ }
+
+err_close:
+ batch_stop(&batch);
+
+ return ret;
+}
+
+int run_test_interpreter(const char *filename, struct nft_test_ctx *ctx)
+{
+ bool expected_failure = false;
+ int ret = 0;
+ FILE *fp;
+
+ //only useful if flush_ruleset() is commented out
+ //to know previous incremental ruleset
+ //system("nft list ruleset > /tmp/ruleset");
+
+ test_fails = false;
+ _filename = filename;
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "cannot open file %s\n", filename);
+ return -1;
+ }
+
+ if (strstr(filename, "_err.t"))
+ expected_failure = true;
+
+ ret = run_one_test(fp);
+
+ fclose(fp);
+
+ if (ret < 0)
+ goto err_tainted_kernel;
+ else if (ret == 1)
+ test_fails = true;
+
+ if (expected_failure ^ test_fails) {
+ printf("[\x1b[31mFAILED\x1b[37m] %s\n", filename);
+ ret = -1;
+ } else {
+ printf("[\x1b[32mOK\x1b[37m] %s\n", filename);
+ }
+
+ return ret;
+
+err_tainted_kernel:
+ ctx->fatal = true;
+ printf("[\x1b[31mTAINTED\x1b[37m] %s\n", filename);
+
+ return -1;
+}
+
+static int dry_run(FILE *fp)
+{
+ return 0;
+}
+
+int fuzz_test_interpreter(const char *filename, struct nft_test_ctx *ctx)
+{
+ int ret = 0, i;
+ FILE *fp;
+
+ _filename = filename;
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "cannot open file %s\n", filename);
+ return -1;
+ }
+
+ printf("[\x1b[36mFUZZING\x1b[37m] %s (", filename);
+ for (i = 0; i < ctx->fuzz_ctx.num_types; i++) {
+ printf("%s", fuzz_type_to_name(ctx->fuzz_ctx.type[i]));
+ if (i + 1 != ctx->fuzz_ctx.num_types)
+ printf(",");
+ }
+ printf(")\n");
+
+ if (ctx->dryrun)
+ ret = fuzz(&ctx->fuzz_ctx, fp, dry_run);
+ else
+ ret = fuzz(&ctx->fuzz_ctx, fp, run_one_test);
+
+ fclose(fp);
+
+ if (ret < 0)
+ goto err_tainted_kernel;
+ else if (ret == 1)
+ test_fails = true;
+
+ printf("[\x1b[32mOK\x1b[37m] %s (%u in %u.%06us)\n",
+ filename,
+ ctx->fuzz_ctx.fuzz_steps,
+ ctx->fuzz_ctx.tv_delta.tv_sec, ctx->fuzz_ctx.tv_delta.tv_usec);
+
+ return ret;
+
+err_tainted_kernel:
+ ctx->fatal = true;
+ printf("[\x1b[31mTAINTED\x1b[37m] %s\n", filename);
+
+ return -1;
+}
+
+int run_test(const char *filename, struct nft_test_ctx *ctx)
+{
+ int ret;
+
+ if (ctx->fuzz)
+ ret = fuzz_test_interpreter(filename, ctx);
+ else
+ ret = run_test_interpreter(filename, ctx);
+
+ return ret;
+}