diff options
Diffstat (limited to 'src/parser_yy.y')
-rw-r--r-- | src/parser_yy.y | 2990 |
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, "a->bytes)) { + error("bytes"); + free_quota(quota); + YYERROR; + } + if (!parse_uint64($5, "a->consumed)) { + error("consumed"); + free_quota(quota); + YYERROR; + } + if (!parse_uint32($7, "a->flags)) { + error("flags"); + free_quota(quota); + YYERROR; + } + + if (add_expr(&batch, "a->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, "a->bytes)) { + error("bytes"); + free_obj(obj); + YYERROR; + } + + if (!parse_uint64($11, "a->consumed)) { + error("consumed"); + free_obj(obj); + YYERROR; + } + + if (!parse_uint32($13, "a->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; +} |