summaryrefslogtreecommitdiffstats
path: root/src/parser_yy.y
diff options
context:
space:
mode:
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;
+}