diff options
Diffstat (limited to 'src/parser_bison.y')
-rw-r--r-- | src/parser_bison.y | 623 |
1 files changed, 422 insertions, 201 deletions
diff --git a/src/parser_bison.y b/src/parser_bison.y index e4f21ca1..53f45315 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -9,12 +9,14 @@ */ %{ +#include <nft.h> #include <ctype.h> #include <stddef.h> #include <stdio.h> #include <inttypes.h> #include <syslog.h> +#include <net/if.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <netinet/if_ether.h> @@ -80,12 +82,11 @@ static int open_scope(struct parser_state *state, struct scope *scope) static void close_scope(struct parser_state *state) { - if (state->scope_err) { + if (state->scope_err || state->scope == 0) { state->scope_err = false; return; } - assert(state->scope > 0); state->scope--; } @@ -145,6 +146,51 @@ static bool already_set(const void *attr, const struct location *loc, return true; } +static struct expr *ifname_expr_alloc(const struct location *location, + struct list_head *queue, + const char *name) +{ + size_t length = strlen(name); + struct expr *expr; + + if (length == 0) { + free_const(name); + erec_queue(error(location, "empty interface name"), queue); + return NULL; + } + + if (length >= IFNAMSIZ) { + free_const(name); + erec_queue(error(location, "interface name too long"), queue); + return NULL; + } + + expr = constant_expr_alloc(location, &ifname_type, BYTEORDER_HOST_ENDIAN, + length * BITS_PER_BYTE, name); + + free_const(name); + + return expr; +} + +static void timeout_state_free(struct timeout_state *s) +{ + free_const(s->timeout_str); + free(s); +} + +static void timeout_states_free(struct list_head *list) +{ + struct timeout_state *ts, *next; + + list_for_each_entry_safe(ts, next, list, head) { + list_del(&ts->head); + timeout_state_free(ts); + } + + free(list); +} + #define YYLLOC_DEFAULT(Current, Rhs, N) location_update(&Current, Rhs, N) #define symbol_value(loc, str) \ @@ -202,6 +248,7 @@ int nft_lex(void *, void *, void *); uint16_t kind; /* must allow > 255 for SACK1, 2.. hack */ uint8_t field; } tcp_kind_field; + struct timeout_state *timeout_state; } %token TOKEN_EOF 0 "end of file" @@ -330,7 +377,7 @@ int nft_lex(void *, void *, void *); %token <string> STRING "string" %token <string> QUOTED_STRING "quoted string" %token <string> ASTERISK_STRING "string with a trailing asterisk" -%destructor { xfree($$); } STRING QUOTED_STRING ASTERISK_STRING +%destructor { free_const($$); } STRING QUOTED_STRING ASTERISK_STRING %token LL_HDR "ll" %token NETWORK_HDR "nh" @@ -395,6 +442,7 @@ int nft_lex(void *, void *, void *); %token ICMP6 "icmpv6" %token PPTR "param-problem" %token MAXDELAY "max-delay" +%token TADDR "taddr" %token AH "ah" %token RESERVED "reserved" @@ -645,9 +693,9 @@ int nft_lex(void *, void *, void *); %type <limit_rate> limit_rate_bytes %type <string> identifier type_identifier string comment_spec -%destructor { xfree($$); } identifier type_identifier string comment_spec +%destructor { free_const($$); } identifier type_identifier string comment_spec -%type <val> time_spec quota_used +%type <val> time_spec time_spec_or_num_s quota_used %type <expr> data_type_expr data_type_atom_expr %destructor { expr_free($$); } data_type_expr data_type_atom_expr @@ -679,11 +727,13 @@ int nft_lex(void *, void *, void *); %type <val> family_spec family_spec_explicit %type <val32> int_num chain_policy %type <prio_spec> extended_prio_spec prio_spec +%destructor { expr_free($$.expr); } extended_prio_spec prio_spec + %type <string> extended_prio_name quota_unit basehook_device_name -%destructor { xfree($$); } extended_prio_name quota_unit basehook_device_name +%destructor { free_const($$); } extended_prio_name quota_unit basehook_device_name %type <expr> dev_spec -%destructor { xfree($$); } dev_spec +%destructor { free($$); } dev_spec %type <table> table_block_alloc table_block %destructor { close_scope(state); table_free($$); } table_block_alloc @@ -692,6 +742,8 @@ int nft_lex(void *, void *, void *); %type <rule> rule rule_alloc %destructor { rule_free($$); } rule +%type <val> table_flags table_flag + %type <val> set_flag_list set_flag %type <val> set_policy_spec @@ -701,7 +753,7 @@ int nft_lex(void *, void *, void *); %type <set> map_block_alloc map_block %destructor { set_free($$); } map_block_alloc -%type <val> map_block_obj_type +%type <val> map_block_obj_type map_block_obj_typeof map_block_data_interval %type <flowtable> flowtable_block_alloc flowtable_block %destructor { flowtable_free($$); } flowtable_block_alloc @@ -710,11 +762,14 @@ int nft_lex(void *, void *, void *); %destructor { obj_free($$); } obj_block_alloc %type <list> stmt_list stateful_stmt_list set_elem_stmt_list -%destructor { stmt_list_free($$); xfree($$); } stmt_list stateful_stmt_list set_elem_stmt_list +%destructor { stmt_list_free($$); free($$); } stmt_list stateful_stmt_list set_elem_stmt_list %type <stmt> stmt match_stmt verdict_stmt set_elem_stmt %destructor { stmt_free($$); } stmt match_stmt verdict_stmt set_elem_stmt %type <stmt> counter_stmt counter_stmt_alloc stateful_stmt last_stmt %destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_stmt last_stmt +%type <stmt> objref_stmt objref_stmt_counter objref_stmt_limit objref_stmt_quota objref_stmt_ct objref_stmt_synproxy +%destructor { stmt_free($$); } objref_stmt objref_stmt_counter objref_stmt_limit objref_stmt_quota objref_stmt_ct objref_stmt_synproxy + %type <stmt> payload_stmt %destructor { stmt_free($$); } payload_stmt %type <stmt> ct_stmt @@ -759,8 +814,8 @@ int nft_lex(void *, void *, void *); %type <expr> symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr %destructor { expr_free($$); } symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr -%type <expr> primary_expr shift_expr and_expr typeof_expr typeof_data_expr -%destructor { expr_free($$); } primary_expr shift_expr and_expr typeof_expr typeof_data_expr +%type <expr> primary_expr shift_expr and_expr typeof_expr typeof_data_expr typeof_key_expr typeof_verdict_expr +%destructor { expr_free($$); } primary_expr shift_expr and_expr typeof_expr typeof_data_expr typeof_key_expr typeof_verdict_expr %type <expr> exclusive_or_expr inclusive_or_expr %destructor { expr_free($$); } exclusive_or_expr inclusive_or_expr %type <expr> basic_expr @@ -830,6 +885,8 @@ int nft_lex(void *, void *, void *); %type <expr> payload_expr payload_raw_expr %destructor { expr_free($$); } payload_expr payload_raw_expr %type <val> payload_base_spec +%type <val> payload_raw_len + %type <expr> eth_hdr_expr vlan_hdr_expr %destructor { expr_free($$); } eth_hdr_expr vlan_hdr_expr %type <val> eth_hdr_field vlan_hdr_field @@ -899,7 +956,7 @@ int nft_lex(void *, void *, void *); %type <val> markup_format %type <string> monitor_event -%destructor { xfree($$); } monitor_event +%destructor { free_const($$); } monitor_event %type <val> monitor_object monitor_format %type <val> synproxy_ts synproxy_sack @@ -933,10 +990,13 @@ int nft_lex(void *, void *, void *); %destructor { expr_free($$); } exthdr_exists_expr %type <val> exthdr_key -%type <val> ct_l4protoname ct_obj_type ct_cmd_type +%type <val> ct_l4protoname ct_obj_type ct_cmd_type ct_obj_type_map -%type <list> timeout_states timeout_state -%destructor { xfree($$); } timeout_states timeout_state +%type <timeout_state> timeout_state +%destructor { timeout_state_free($$); } timeout_state + +%type <list> timeout_states +%destructor { timeout_states_free($$); } timeout_states %type <val> xfrm_state_key xfrm_state_proto_key xfrm_dir xfrm_spnum %type <expr> xfrm_expr @@ -1024,10 +1084,10 @@ close_scope_xt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_XT); } common_block : INCLUDE QUOTED_STRING stmt_separator { if (scanner_include_file(nft, scanner, $2, &@$) < 0) { - xfree($2); + free_const($2); YYERROR; } - xfree($2); + free_const($2); } | DEFINE identifier '=' initializer_expr stmt_separator { @@ -1037,19 +1097,19 @@ common_block : INCLUDE QUOTED_STRING stmt_separator erec_queue(error(&@2, "redefinition of symbol '%s'", $2), state->msgs); expr_free($4); - xfree($2); + free_const($2); YYERROR; } symbol_bind(scope, $2, $4); - xfree($2); + free_const($2); } | REDEFINE identifier '=' initializer_expr stmt_separator { struct scope *scope = current_scope(state); symbol_bind(scope, $2, $4); - xfree($2); + free_const($2); } | UNDEFINE identifier stmt_separator { @@ -1058,10 +1118,10 @@ common_block : INCLUDE QUOTED_STRING stmt_separator if (symbol_unbind(scope, $2) < 0) { erec_queue(error(&@2, "undefined symbol '%s'", $2), state->msgs); - xfree($2); + free_const($2); YYERROR; } - xfree($2); + free_const($2); } | error stmt_separator { @@ -1358,6 +1418,14 @@ delete_cmd : TABLE table_or_id_spec { $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, NULL); } + | CHAIN chain_spec chain_block_alloc + '{' chain_block '}' + { + $5->location = @5; + handle_merge(&$3->handle, &$2); + close_scope(state); + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, $5); + } | RULE ruleid_spec { $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_RULE, &$2, &@$, NULL); @@ -1709,6 +1777,18 @@ reset_cmd : COUNTERS ruleset_spec { $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULE, &$2, &@$, NULL); } + | ELEMENT set_spec set_block_expr + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_ELEMENTS, &$2, &@$, $3); + } + | SET set_or_id_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_SET, &$2, &@$, NULL); + } + | MAP set_or_id_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_MAP, &$2, &@$, NULL); + } ; flush_cmd : TABLE table_spec @@ -1827,31 +1907,38 @@ table_block_alloc : /* empty */ } ; -table_options : FLAGS STRING +table_options : FLAGS table_flags { - if (strcmp($2, "dormant") == 0) { - $<table>0->flags |= TABLE_F_DORMANT; - xfree($2); - } else if (strcmp($2, "owner") == 0) { - $<table>0->flags |= TABLE_F_OWNER; - xfree($2); - } else { - erec_queue(error(&@2, "unknown table option %s", $2), - state->msgs); - xfree($2); - YYERROR; - } + $<table>0->flags |= $2; } | comment_spec { if (already_set($<table>0->comment, &@$, state)) { - xfree($1); + free_const($1); YYERROR; } $<table>0->comment = $1; } ; +table_flags : table_flag + | table_flags COMMA table_flag + { + $$ = $1 | $3; + } + ; +table_flag : STRING + { + $$ = parse_table_flag($1); + free_const($1); + if ($$ == 0) { + erec_queue(error(&@1, "unknown table option %s", $1), + state->msgs); + YYERROR; + } + } + ; + table_block : /* empty */ { $$ = $<table>-1; } | table_block common_block | table_block stmt_separator @@ -1920,7 +2007,7 @@ table_block : /* empty */ { $$ = $<table>-1; } list_add_tail(&$4->list, &$1->objs); $$ = $1; } - | table_block CT HELPER obj_identifier obj_block_alloc '{' ct_helper_block '}' close_scope_ct stmt_separator + | table_block CT HELPER obj_identifier obj_block_alloc '{' ct_helper_block '}' stmt_separator close_scope_ct { $5->location = @4; $5->type = NFT_OBJECT_CT_HELPER; @@ -1929,7 +2016,7 @@ table_block : /* empty */ { $$ = $<table>-1; } list_add_tail(&$5->list, &$1->objs); $$ = $1; } - | table_block CT TIMEOUT obj_identifier obj_block_alloc '{' ct_timeout_block '}' close_scope_ct stmt_separator + | table_block CT TIMEOUT obj_identifier obj_block_alloc '{' ct_timeout_block '}' stmt_separator close_scope_ct { $5->location = @4; $5->type = NFT_OBJECT_CT_TIMEOUT; @@ -1938,7 +2025,7 @@ table_block : /* empty */ { $$ = $<table>-1; } list_add_tail(&$5->list, &$1->objs); $$ = $1; } - | table_block CT EXPECTATION obj_identifier obj_block_alloc '{' ct_expect_block '}' close_scope_ct stmt_separator + | table_block CT EXPECTATION obj_identifier obj_block_alloc '{' ct_expect_block '}' stmt_separator close_scope_ct { $5->location = @4; $5->type = NFT_OBJECT_CT_EXPECT; @@ -1984,7 +2071,7 @@ table_block : /* empty */ { $$ = $<table>-1; } chain_block_alloc : /* empty */ { - $$ = chain_alloc(NULL); + $$ = chain_alloc(); if (open_scope(state, &$$->scope) < 0) { erec_queue(error(&@$, "too many levels of nesting"), state->msgs); @@ -2004,10 +2091,19 @@ chain_block : /* empty */ { $$ = $<chain>-1; } list_add_tail(&$2->list, &$1->rules); $$ = $1; } + | chain_block DEVICES '=' flowtable_expr stmt_separator + { + if ($$->dev_expr) { + list_splice_init(&$4->expressions, &$$->dev_expr->expressions); + expr_free($4); + break; + } + $$->dev_expr = $4; + } | chain_block comment_spec stmt_separator { if (already_set($1->comment, &@2, state)) { - xfree($2); + free_const($2); YYERROR; } $1->comment = $2; @@ -2023,7 +2119,7 @@ subchain_block : /* empty */ { $$ = $<chain>-1; } } ; -typeof_data_expr : primary_expr +typeof_verdict_expr : primary_expr { struct expr *e = $1; @@ -2055,6 +2151,17 @@ typeof_data_expr : primary_expr } ; +typeof_data_expr : INTERVAL typeof_expr + { + $2->flags |= EXPR_F_INTERVAL; + $$ = $2; + } + | typeof_verdict_expr + { + $$ = $1; + } + ; + typeof_expr : primary_expr { if (expr_ops($1)->build_udata == NULL) { @@ -2080,22 +2187,25 @@ typeof_expr : primary_expr set_block_alloc : /* empty */ { - $$ = set_alloc(NULL); + $$ = set_alloc(&internal_location); } ; +typeof_key_expr : TYPEOF typeof_expr { $$ = $2; } + | TYPE data_type_expr close_scope_type { $$ = $2; } + ; + set_block : /* empty */ { $$ = $<set>-1; } | set_block common_block | set_block stmt_separator - | set_block TYPE data_type_expr stmt_separator close_scope_type - { - $1->key = $3; - $$ = $1; - } - | set_block TYPEOF typeof_expr stmt_separator + | set_block typeof_key_expr stmt_separator { - $1->key = $3; - datatype_set($1->key, $3->dtype); + if (already_set($1->key, &@2, state)) { + expr_free($2); + YYERROR; + } + + $1->key = $2; $$ = $1; } | set_block FLAGS set_flag_list stmt_separator @@ -2121,6 +2231,10 @@ set_block : /* empty */ { $$ = $<set>-1; } } | set_block ELEMENTS '=' set_block_expr { + if (already_set($1->init, &@2, state)) { + expr_free($4); + YYERROR; + } $1->init = $4; $$ = $1; } @@ -2133,7 +2247,7 @@ set_block : /* empty */ { $$ = $<set>-1; } | set_block comment_spec stmt_separator { if (already_set($1->comment, &@2, state)) { - xfree($2); + free_const($2); YYERROR; } $1->comment = $2; @@ -2160,10 +2274,14 @@ set_flag : CONSTANT { $$ = NFT_SET_CONSTANT; } map_block_alloc : /* empty */ { - $$ = set_alloc(NULL); + $$ = set_alloc(&internal_location); } ; +ct_obj_type_map : TIMEOUT { $$ = NFT_OBJECT_CT_TIMEOUT; } + | EXPECTATION { $$ = NFT_OBJECT_CT_EXPECT; } + ; + map_block_obj_type : COUNTER close_scope_counter { $$ = NFT_OBJECT_COUNTER; } | QUOTA close_scope_quota { $$ = NFT_OBJECT_QUOTA; } | LIMIT close_scope_limit { $$ = NFT_OBJECT_LIMIT; } @@ -2171,6 +2289,14 @@ map_block_obj_type : COUNTER close_scope_counter { $$ = NFT_OBJECT_COUNTER; } | SYNPROXY close_scope_synproxy { $$ = NFT_OBJECT_SYNPROXY; } ; +map_block_obj_typeof : map_block_obj_type + | CT ct_obj_type_map close_scope_ct { $$ = $2; } + ; + +map_block_data_interval : INTERVAL { $$ = EXPR_F_INTERVAL; } + | { $$ = 0; } + ; + map_block : /* empty */ { $$ = $<set>-1; } | map_block common_block | map_block stmt_separator @@ -2179,23 +2305,24 @@ map_block : /* empty */ { $$ = $<set>-1; } $1->timeout = $3; $$ = $1; } - | map_block TYPE - data_type_expr COLON data_type_expr - stmt_separator close_scope_type + | map_block GC_INTERVAL time_spec stmt_separator { - $1->key = $3; - $1->data = $5; - - $1->flags |= NFT_SET_MAP; + $1->gc_int = $3; $$ = $1; } | map_block TYPE - data_type_expr COLON INTERVAL data_type_expr + data_type_expr COLON map_block_data_interval data_type_expr stmt_separator close_scope_type { + if (already_set($1->key, &@2, state)) { + expr_free($3); + expr_free($6); + YYERROR; + } + $1->key = $3; $1->data = $6; - $1->data->flags |= EXPR_F_INTERVAL; + $1->data->flags |= $5; $1->flags |= NFT_SET_MAP; $$ = $1; @@ -2204,29 +2331,43 @@ map_block : /* empty */ { $$ = $<set>-1; } typeof_expr COLON typeof_data_expr stmt_separator { - $1->key = $3; - datatype_set($1->key, $3->dtype); - $1->data = $5; + if (already_set($1->key, &@2, state)) { + expr_free($3); + expr_free($5); + YYERROR; + } - $1->flags |= NFT_SET_MAP; - $$ = $1; - } - | map_block TYPEOF - typeof_expr COLON INTERVAL typeof_expr - stmt_separator - { $1->key = $3; - datatype_set($1->key, $3->dtype); - $1->data = $6; - $1->data->flags |= EXPR_F_INTERVAL; - $1->flags |= NFT_SET_MAP; + if ($5->etype == EXPR_CT && $5->ct.key == NFT_CT_HELPER) { + $1->objtype = NFT_OBJECT_CT_HELPER; + $1->flags |= NFT_SET_OBJECT; + expr_free($5); + } else { + $1->data = $5; + $1->flags |= NFT_SET_MAP; + } + $$ = $1; } | map_block TYPE data_type_expr COLON map_block_obj_type stmt_separator close_scope_type { + if (already_set($1->key, &@2, state)) { + expr_free($3); + YYERROR; + } + + $1->key = $3; + $1->objtype = $5; + $1->flags |= NFT_SET_OBJECT; + $$ = $1; + } + | map_block TYPEOF + typeof_expr COLON map_block_obj_typeof + stmt_separator + { $1->key = $3; $1->objtype = $5; $1->flags |= NFT_SET_OBJECT; @@ -2251,7 +2392,7 @@ map_block : /* empty */ { $$ = $<set>-1; } | map_block comment_spec stmt_separator { if (already_set($1->comment, &@2, state)) { - xfree($2); + free_const($2); YYERROR; } $1->comment = $2; @@ -2276,7 +2417,7 @@ set_policy_spec : PERFORMANCE { $$ = NFT_SET_POL_PERFORMANCE; } flowtable_block_alloc : /* empty */ { - $$ = flowtable_alloc(NULL); + $$ = flowtable_alloc(&internal_location); } ; @@ -2290,10 +2431,10 @@ flowtable_block : /* empty */ { $$ = $<flowtable>-1; } if ($$->hook.name == NULL) { erec_queue(error(&@3, "unknown chain hook"), state->msgs); - xfree($3); + free_const($3); YYERROR; } - xfree($3); + free_const($3); $$->priority = $4; } @@ -2338,17 +2479,21 @@ flowtable_list_expr : flowtable_expr_member flowtable_expr_member : QUOTED_STRING { - $$ = constant_expr_alloc(&@$, &string_type, - BYTEORDER_HOST_ENDIAN, - strlen($1) * BITS_PER_BYTE, $1); - xfree($1); + struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1); + + if (!expr) + YYERROR; + + $$ = expr; } | STRING { - $$ = constant_expr_alloc(&@$, &string_type, - BYTEORDER_HOST_ENDIAN, - strlen($1) * BITS_PER_BYTE, $1); - xfree($1); + struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1); + + if (!expr) + YYERROR; + + $$ = expr; } | variable_expr { @@ -2363,12 +2508,12 @@ data_type_atom_expr : type_identifier if (dtype == NULL) { erec_queue(error(&@1, "unknown datatype %s", $1), state->msgs); - xfree($1); + free_const($1); YYERROR; } $$ = constant_expr_alloc(&@1, dtype, dtype->byteorder, dtype->size, NULL); - xfree($1); + free_const($1); } | TIME { @@ -2391,7 +2536,7 @@ data_type_expr : data_type_atom_expr obj_block_alloc : /* empty */ { - $$ = obj_alloc(NULL); + $$ = obj_alloc(&internal_location); } ; @@ -2405,7 +2550,7 @@ counter_block : /* empty */ { $$ = $<obj>-1; } | counter_block comment_spec { if (already_set($<obj>1->comment, &@2, state)) { - xfree($2); + free_const($2); YYERROR; } $<obj>1->comment = $2; @@ -2422,7 +2567,7 @@ quota_block : /* empty */ { $$ = $<obj>-1; } | quota_block comment_spec { if (already_set($<obj>1->comment, &@2, state)) { - xfree($2); + free_const($2); YYERROR; } $<obj>1->comment = $2; @@ -2439,7 +2584,7 @@ ct_helper_block : /* empty */ { $$ = $<obj>-1; } | ct_helper_block comment_spec { if (already_set($<obj>1->comment, &@2, state)) { - xfree($2); + free_const($2); YYERROR; } $<obj>1->comment = $2; @@ -2450,6 +2595,7 @@ ct_timeout_block : /*empty */ { $$ = $<obj>-1; init_list_head(&$$->ct_timeout.timeout_list); + $$->type = NFT_OBJECT_CT_TIMEOUT; } | ct_timeout_block common_block | ct_timeout_block stmt_separator @@ -2460,7 +2606,7 @@ ct_timeout_block : /*empty */ | ct_timeout_block comment_spec { if (already_set($<obj>1->comment, &@2, state)) { - xfree($2); + free_const($2); YYERROR; } $<obj>1->comment = $2; @@ -2477,7 +2623,7 @@ ct_expect_block : /*empty */ { $$ = $<obj>-1; } | ct_expect_block comment_spec { if (already_set($<obj>1->comment, &@2, state)) { - xfree($2); + free_const($2); YYERROR; } $<obj>1->comment = $2; @@ -2494,7 +2640,7 @@ limit_block : /* empty */ { $$ = $<obj>-1; } | limit_block comment_spec { if (already_set($<obj>1->comment, &@2, state)) { - xfree($2); + free_const($2); YYERROR; } $<obj>1->comment = $2; @@ -2511,7 +2657,7 @@ secmark_block : /* empty */ { $$ = $<obj>-1; } | secmark_block comment_spec { if (already_set($<obj>1->comment, &@2, state)) { - xfree($2); + free_const($2); YYERROR; } $<obj>1->comment = $2; @@ -2528,7 +2674,7 @@ synproxy_block : /* empty */ { $$ = $<obj>-1; } | synproxy_block comment_spec { if (already_set($<obj>1->comment, &@2, state)) { - xfree($2); + free_const($2); YYERROR; } $<obj>1->comment = $2; @@ -2549,12 +2695,15 @@ hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec if (chain_type == NULL) { erec_queue(error(&@3, "unknown chain type"), state->msgs); - xfree($3); + free_const($3); + free_const($5); + expr_free($6); + expr_free($7.expr); YYERROR; } $<chain>0->type.loc = @3; $<chain>0->type.str = xstrdup(chain_type); - xfree($3); + free_const($3); $<chain>0->loc = @$; $<chain>0->hook.loc = @5; @@ -2562,10 +2711,12 @@ hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec if ($<chain>0->hook.name == NULL) { erec_queue(error(&@5, "unknown chain hook"), state->msgs); - xfree($5); + free_const($5); + expr_free($6); + expr_free($7.expr); YYERROR; } - xfree($5); + free_const($5); $<chain>0->dev_expr = $6; $<chain>0->priority = $7; @@ -2612,7 +2763,7 @@ extended_prio_spec : int_num BYTEORDER_HOST_ENDIAN, strlen($1) * BITS_PER_BYTE, $1); - xfree($1); + free_const($1); $$ = spec; } | extended_prio_name PLUS NUM @@ -2625,7 +2776,7 @@ extended_prio_spec : int_num BYTEORDER_HOST_ENDIAN, strlen(str) * BITS_PER_BYTE, str); - xfree($1); + free_const($1); $$ = spec; } | extended_prio_name DASH NUM @@ -2638,7 +2789,7 @@ extended_prio_spec : int_num BYTEORDER_HOST_ENDIAN, strlen(str) * BITS_PER_BYTE, str); - xfree($1); + free_const($1); $$ = spec; } ; @@ -2649,12 +2800,11 @@ int_num : NUM { $$ = $1; } dev_spec : DEVICE string { - struct expr *expr; + struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $2); + + if (!expr) + YYERROR; - expr = constant_expr_alloc(&@$, &string_type, - BYTEORDER_HOST_ENDIAN, - strlen($2) * BITS_PER_BYTE, $2); - xfree($2); $$ = compound_expr_alloc(&@$, EXPR_LIST); compound_expr_add($$, expr); @@ -2724,7 +2874,7 @@ time_spec : STRING uint64_t res; erec = time_parse(&@1, $1, &res); - xfree($1); + free_const($1); if (erec != NULL) { erec_queue(erec, state->msgs); YYERROR; @@ -2733,6 +2883,11 @@ time_spec : STRING } ; +/* compatibility kludge to allow either 60, 60s, 1m, ... */ +time_spec_or_num_s : NUM + | time_spec { $$ = $1 / 1000u; } + ; + family_spec : /* empty */ { $$ = NFPROTO_IPV4; } | family_spec_explicit ; @@ -2920,7 +3075,7 @@ comment_spec : COMMENT string erec_queue(error(&@2, "comment too long, %d characters maximum allowed", NFTNL_UDATA_COMMENT_MAXLEN), state->msgs); - xfree($2); + free_const($2); YYERROR; } $$ = $2; @@ -2957,7 +3112,7 @@ rule_alloc : stmt_list list_for_each_entry(i, $1, list) $$->num_stmts++; list_splice_tail($1, &$$->stmts); - xfree($1); + free($1); } ; @@ -2987,6 +3142,60 @@ stateful_stmt_list : stateful_stmt } ; +objref_stmt_counter : COUNTER NAME stmt_expr close_scope_counter + { + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_COUNTER; + $$->objref.expr = $3; + } + ; + +objref_stmt_limit : LIMIT NAME stmt_expr close_scope_limit + { + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_LIMIT; + $$->objref.expr = $3; + } + ; + +objref_stmt_quota : QUOTA NAME stmt_expr close_scope_quota + { + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_QUOTA; + $$->objref.expr = $3; + } + ; + +objref_stmt_synproxy : SYNPROXY NAME stmt_expr close_scope_synproxy + { + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_SYNPROXY; + $$->objref.expr = $3; + } + ; + +objref_stmt_ct : CT TIMEOUT SET stmt_expr close_scope_ct + { + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_CT_TIMEOUT; + $$->objref.expr = $4; + + } + | CT EXPECTATION SET stmt_expr close_scope_ct + { + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_CT_EXPECT; + $$->objref.expr = $4; + } + ; + +objref_stmt : objref_stmt_counter + | objref_stmt_limit + | objref_stmt_quota + | objref_stmt_synproxy + | objref_stmt_ct + ; + stateful_stmt : counter_stmt close_scope_counter | limit_stmt | quota_stmt @@ -3016,13 +3225,14 @@ stmt : verdict_stmt | chain_stmt | optstrip_stmt | xt_stmt close_scope_xt + | objref_stmt ; xt_stmt : XT STRING string { $$ = NULL; - xfree($2); - xfree($3); + free_const($2); + free_const($3); erec_queue(error(&@$, "unsupported xtables compat expression, use iptables-nft with this ruleset"), state->msgs); YYERROR; @@ -3105,12 +3315,6 @@ counter_stmt_alloc : COUNTER { $$ = counter_stmt_alloc(&@$); } - | COUNTER NAME stmt_expr - { - $$ = objref_stmt_alloc(&@$); - $$->objref.type = NFT_OBJECT_COUNTER; - $$->objref.expr = $3; - } ; counter_args : counter_arg @@ -3122,10 +3326,12 @@ counter_args : counter_arg counter_arg : PACKETS NUM { + assert($<stmt>0->ops->type == STMT_COUNTER); $<stmt>0->counter.packets = $2; } | BYTES NUM { + assert($<stmt>0->ops->type == STMT_COUNTER); $<stmt>0->counter.bytes = $2; } ; @@ -3180,7 +3386,7 @@ log_arg : PREFIX string expr = constant_expr_alloc(&@$, &string_type, BYTEORDER_HOST_ENDIAN, (strlen($2) + 1) * BITS_PER_BYTE, $2); - xfree($2); + free_const($2); $<stmt>0->log.prefix = expr; $<stmt>0->log.flags |= STMT_LOG_PREFIX; break; @@ -3254,7 +3460,7 @@ log_arg : PREFIX string state->msgs); } expr_free(expr); - xfree($2); + free_const($2); YYERROR; } item = variable_expr_alloc(&@$, scope, sym); @@ -3284,7 +3490,7 @@ log_arg : PREFIX string } } - xfree($2); + free_const($2); $<stmt>0->log.prefix = expr; $<stmt>0->log.flags |= STMT_LOG_PREFIX; } @@ -3337,10 +3543,10 @@ level_type : string else { erec_queue(error(&@1, "invalid log level"), state->msgs); - xfree($1); + free_const($1); YYERROR; } - xfree($1); + free_const($1); } ; @@ -3406,12 +3612,6 @@ limit_stmt : LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope $$->limit.type = NFT_LIMIT_PKT_BYTES; $$->limit.flags = $3; } - | LIMIT NAME stmt_expr close_scope_limit - { - $$ = objref_stmt_alloc(&@$); - $$->objref.type = NFT_OBJECT_LIMIT; - $$->objref.expr = $3; - } ; quota_mode : OVER { $$ = NFT_QUOTA_F_INV; } @@ -3430,7 +3630,7 @@ quota_used : /* empty */ { $$ = 0; } uint64_t rate; erec = data_unit_parse(&@$, $3, &rate); - xfree($3); + free_const($3); if (erec != NULL) { erec_queue(erec, state->msgs); YYERROR; @@ -3445,7 +3645,7 @@ quota_stmt : QUOTA quota_mode NUM quota_unit quota_used close_scope_quota uint64_t rate; erec = data_unit_parse(&@$, $4, &rate); - xfree($4); + free_const($4); if (erec != NULL) { erec_queue(erec, state->msgs); YYERROR; @@ -3455,12 +3655,6 @@ quota_stmt : QUOTA quota_mode NUM quota_unit quota_used close_scope_quota $$->quota.used = $5; $$->quota.flags = $2; } - | QUOTA NAME stmt_expr close_scope_quota - { - $$ = objref_stmt_alloc(&@$); - $$->objref.type = NFT_OBJECT_QUOTA; - $$->objref.expr = $3; - } ; limit_mode : OVER { $$ = NFT_LIMIT_F_INV; } @@ -3489,7 +3683,7 @@ limit_rate_bytes : NUM STRING uint64_t rate, unit; erec = rate_parse(&@$, $2, &rate, &unit); - xfree($2); + free_const($2); if (erec != NULL) { erec_queue(erec, state->msgs); YYERROR; @@ -3511,7 +3705,7 @@ limit_bytes : NUM BYTES { $$ = $1; } uint64_t rate; erec = data_unit_parse(&@$, $2, &rate); - xfree($2); + free_const($2); if (erec != NULL) { erec_queue(erec, state->msgs); YYERROR; @@ -3540,7 +3734,7 @@ reject_with_expr : STRING { $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE, current_scope(state), $1); - xfree($1); + free_const($1); } | integer_expr { $$ = $1; } ; @@ -3555,40 +3749,40 @@ reject_opts : /* empty */ $<stmt>0->reject.family = NFPROTO_IPV4; $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH; $<stmt>0->reject.expr = $4; - datatype_set($<stmt>0->reject.expr, &icmp_code_type); + datatype_set($<stmt>0->reject.expr, &reject_icmp_code_type); } | WITH ICMP reject_with_expr { $<stmt>0->reject.family = NFPROTO_IPV4; $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH; $<stmt>0->reject.expr = $3; - datatype_set($<stmt>0->reject.expr, &icmp_code_type); + datatype_set($<stmt>0->reject.expr, &reject_icmp_code_type); } | WITH ICMP6 TYPE reject_with_expr close_scope_type close_scope_icmp { $<stmt>0->reject.family = NFPROTO_IPV6; $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH; $<stmt>0->reject.expr = $4; - datatype_set($<stmt>0->reject.expr, &icmpv6_code_type); + datatype_set($<stmt>0->reject.expr, &reject_icmpv6_code_type); } | WITH ICMP6 reject_with_expr { $<stmt>0->reject.family = NFPROTO_IPV6; $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH; $<stmt>0->reject.expr = $3; - datatype_set($<stmt>0->reject.expr, &icmpv6_code_type); + datatype_set($<stmt>0->reject.expr, &reject_icmpv6_code_type); } | WITH ICMPX TYPE reject_with_expr close_scope_type { $<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH; $<stmt>0->reject.expr = $4; - datatype_set($<stmt>0->reject.expr, &icmpx_code_type); + datatype_set($<stmt>0->reject.expr, &reject_icmpx_code_type); } | WITH ICMPX reject_with_expr { $<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH; $<stmt>0->reject.expr = $3; - datatype_set($<stmt>0->reject.expr, &icmpx_code_type); + datatype_set($<stmt>0->reject.expr, &reject_icmpx_code_type); } | WITH TCP close_scope_tcp RESET close_scope_reset { @@ -3599,8 +3793,8 @@ reject_opts : /* empty */ nat_stmt : nat_stmt_alloc nat_stmt_args ; -nat_stmt_alloc : SNAT { $$ = nat_stmt_alloc(&@$, NFT_NAT_SNAT); } - | DNAT { $$ = nat_stmt_alloc(&@$, NFT_NAT_DNAT); } +nat_stmt_alloc : SNAT { $$ = nat_stmt_alloc(&@$, __NFT_NAT_SNAT); } + | DNAT { $$ = nat_stmt_alloc(&@$, __NFT_NAT_DNAT); } ; tproxy_stmt : TPROXY TO stmt_expr @@ -3651,12 +3845,6 @@ synproxy_stmt_alloc : SYNPROXY { $$ = synproxy_stmt_alloc(&@$); } - | SYNPROXY NAME stmt_expr - { - $$ = objref_stmt_alloc(&@$); - $$->objref.type = NFT_OBJECT_SYNPROXY; - $$->objref.expr = $3; - } ; synproxy_args : synproxy_arg @@ -4204,12 +4392,12 @@ variable_expr : '$' identifier erec_queue(error(&@2, "unknown identifier '%s'", $2), state->msgs); } - xfree($2); + free_const($2); YYERROR; } $$ = variable_expr_alloc(&@$, scope, sym); - xfree($2); + free_const($2); } ; @@ -4219,7 +4407,7 @@ symbol_expr : variable_expr $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE, current_scope(state), $1); - xfree($1); + free_const($1); } ; @@ -4232,7 +4420,7 @@ set_ref_symbol_expr : AT identifier close_scope_at $$ = symbol_expr_alloc(&@$, SYMBOL_SET, current_scope(state), $2); - xfree($2); + free_const($2); } ; @@ -4329,10 +4517,10 @@ osf_ttl : /* empty */ else { erec_queue(error(&@2, "invalid ttl option"), state->msgs); - xfree($2); + free_const($2); YYERROR; } - xfree($2); + free_const($2); } ; @@ -4460,6 +4648,12 @@ meter_key_expr_alloc : concat_expr set_elem_expr : set_elem_expr_alloc | set_elem_expr_alloc set_elem_expr_options + | set_elem_expr_alloc set_elem_expr_options set_elem_stmt_list + { + $$ = $1; + list_splice_tail($3, &$$->stmt_list); + free($3); + } ; set_elem_key_expr : set_lhs_expr { $$ = $1; } @@ -4470,7 +4664,7 @@ set_elem_expr_alloc : set_elem_key_expr set_elem_stmt_list { $$ = set_elem_expr_alloc(&@1, $1); list_splice_tail($2, &$$->stmt_list); - xfree($2); + free($2); } | set_elem_key_expr { @@ -4496,7 +4690,7 @@ set_elem_option : TIMEOUT time_spec | comment_spec { if (already_set($<expr>0->comment, &@1, state)) { - xfree($1); + free_const($1); YYERROR; } $<expr>0->comment = $1; @@ -4578,7 +4772,7 @@ set_elem_stmt : COUNTER close_scope_counter uint64_t rate; erec = data_unit_parse(&@$, $4, &rate); - xfree($4); + free_const($4); if (erec != NULL) { erec_queue(erec, state->msgs); YYERROR; @@ -4611,7 +4805,7 @@ set_elem_expr_option : TIMEOUT time_spec | comment_spec { if (already_set($<expr>0->comment, &@1, state)) { - xfree($1); + free_const($1); YYERROR; } $<expr>0->comment = $1; @@ -4663,7 +4857,7 @@ quota_config : quota_mode NUM quota_unit quota_used uint64_t rate; erec = data_unit_parse(&@$, $3, &rate); - xfree($3); + free_const($3); if (erec != NULL) { erec_queue(erec, state->msgs); YYERROR; @@ -4692,10 +4886,10 @@ secmark_config : string ret = snprintf(secmark->ctx, sizeof(secmark->ctx), "%s", $1); if (ret <= 0 || ret >= (int)sizeof(secmark->ctx)) { erec_queue(error(&@1, "invalid context '%s', max length is %u\n", $1, (int)sizeof(secmark->ctx)), state->msgs); - xfree($1); + free_const($1); YYERROR; } - xfree($1); + free_const($1); } ; @@ -4712,27 +4906,34 @@ ct_obj_type : HELPER { $$ = NFT_OBJECT_CT_HELPER; } ; ct_cmd_type : HELPERS { $$ = CMD_OBJ_CT_HELPERS; } - | TIMEOUT { $$ = CMD_OBJ_CT_TIMEOUT; } - | EXPECTATION { $$ = CMD_OBJ_CT_EXPECT; } + | TIMEOUT { $$ = CMD_OBJ_CT_TIMEOUTS; } + | EXPECTATION { $$ = CMD_OBJ_CT_EXPECTATIONS; } ; ct_l4protoname : TCP close_scope_tcp { $$ = IPPROTO_TCP; } | UDP close_scope_udp { $$ = IPPROTO_UDP; } ; -ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator close_scope_type +ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator close_scope_type { struct ct_helper *ct; int ret; ct = &$<obj>0->ct_helper; + if (ct->l4proto) { + erec_queue(error(&@2, "You can only specify this once. This statement is already set for %s.", ct->name), state->msgs); + free_const($2); + YYERROR; + } + ret = snprintf(ct->name, sizeof(ct->name), "%s", $2); if (ret <= 0 || ret >= (int)sizeof(ct->name)) { erec_queue(error(&@2, "invalid name '%s', max length is %u\n", $2, (int)sizeof(ct->name)), state->msgs); + free_const($2); YYERROR; } - xfree($2); + free_const($2); ct->l4proto = $4; } @@ -4746,17 +4947,16 @@ timeout_states : timeout_state { $$ = xmalloc(sizeof(*$$)); init_list_head($$); - list_add_tail($1, $$); + list_add_tail(&$1->head, $$); } | timeout_states COMMA timeout_state { - list_add_tail($3, $1); + list_add_tail(&$3->head, $1); $$ = $1; } ; -timeout_state : STRING COLON NUM - +timeout_state : STRING COLON time_spec_or_num_s { struct timeout_state *ts; @@ -4765,7 +4965,7 @@ timeout_state : STRING COLON NUM ts->timeout_value = $3; ts->location = @1; init_list_head(&ts->head); - $$ = &ts->head; + $$ = ts; } ; @@ -4783,7 +4983,7 @@ ct_timeout_config : PROTOCOL ct_l4protoname stmt_separator ct = &$<obj>0->ct_timeout; list_splice_tail($4, &ct->timeout_list); - xfree($4); + free($4); } | L3PROTOCOL family_spec_explicit stmt_separator { @@ -5128,7 +5328,7 @@ chain_expr : variable_expr BYTEORDER_HOST_ENDIAN, strlen($1) * BITS_PER_BYTE, $1); - xfree($1); + free_const($1); } ; @@ -5146,7 +5346,7 @@ meta_expr : META meta_key close_scope_meta unsigned int key; erec = meta_key_parse(&@$, $2, &key); - xfree($2); + free_const($2); if (erec != NULL) { erec_queue(erec, state->msgs); YYERROR; @@ -5223,9 +5423,10 @@ meta_stmt : META meta_key SET stmt_expr close_scope_meta unsigned int key; erec = meta_key_parse(&@$, $2, &key); - xfree($2); + free_const($2); if (erec != NULL) { erec_queue(erec, state->msgs); + expr_free($4); YYERROR; } @@ -5464,19 +5665,6 @@ ct_stmt : CT ct_key SET stmt_expr close_scope_ct break; } } - | CT TIMEOUT SET stmt_expr close_scope_ct - { - $$ = objref_stmt_alloc(&@$); - $$->objref.type = NFT_OBJECT_CT_TIMEOUT; - $$->objref.expr = $4; - - } - | CT EXPECTATION SET stmt_expr close_scope_ct - { - $$ = objref_stmt_alloc(&@$); - $$->objref.type = NFT_OBJECT_CT_EXPECT; - $$->objref.expr = $4; - } | CT ct_dir ct_key_dir_optional SET stmt_expr close_scope_ct { $$ = ct_stmt_alloc(&@$, $3, $2, $5); @@ -5516,7 +5704,25 @@ payload_expr : payload_raw_expr | gretap_hdr_expr ; -payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM close_scope_at +payload_raw_len : NUM + { + if ($1 > NFT_MAX_EXPR_LEN_BITS) { + erec_queue(error(&@1, "raw payload length %u exceeds upper limit of %u", + $1, NFT_MAX_EXPR_LEN_BITS), + state->msgs); + YYERROR; + } + + if ($1 == 0) { + erec_queue(error(&@1, "raw payload length cannot be 0"), state->msgs); + YYERROR; + } + + $$ = $1; + } + ; + +payload_raw_expr : AT payload_base_spec COMMA NUM COMMA payload_raw_len close_scope_at { $$ = payload_expr_alloc(&@$, NULL, 0); payload_init_raw($$, $2, $4, $6); @@ -5534,10 +5740,10 @@ payload_base_spec : LL_HDR { $$ = PROTO_BASE_LL_HDR; } $$ = PROTO_BASE_INNER_HDR; } else { erec_queue(error(&@1, "unknown raw payload base"), state->msgs); - xfree($1); + free_const($1); YYERROR; } - xfree($1); + free_const($1); } ; @@ -5685,6 +5891,8 @@ icmp6_hdr_field : TYPE close_scope_type { $$ = ICMP6HDR_TYPE; } | ID { $$ = ICMP6HDR_ID; } | SEQUENCE { $$ = ICMP6HDR_SEQ; } | MAXDELAY { $$ = ICMP6HDR_MAXDELAY; } + | TADDR { $$ = ICMP6HDR_TADDR; } + | DADDR { $$ = ICMP6HDR_DADDR; } ; auth_hdr_expr : AH auth_hdr_field close_scope_ah @@ -5757,8 +5965,12 @@ tcp_hdr_expr : TCP tcp_hdr_field | TCP OPTION tcp_hdr_option_kind_and_field { $$ = tcpopt_expr_alloc(&@$, $3.kind, $3.field); + if ($$ == NULL) { + erec_queue(error(&@1, "Could not find a tcp option template"), state->msgs); + YYERROR; + } } - | TCP OPTION AT close_scope_at tcp_hdr_option_type COMMA NUM COMMA NUM + | TCP OPTION AT close_scope_at tcp_hdr_option_type COMMA NUM COMMA payload_raw_len { $$ = tcpopt_expr_alloc(&@$, $5, 0); tcpopt_init_raw($$, $5, $7, $9, 0); @@ -5952,6 +6164,15 @@ dccp_hdr_expr : DCCP dccp_hdr_field close_scope_dccp { $$ = payload_expr_alloc(&@$, &proto_dccp, $2); } + | DCCP OPTION NUM close_scope_dccp + { + if ($3 > DCCPOPT_TYPE_MAX) { + erec_queue(error(&@1, "value too large"), + state->msgs); + YYERROR; + } + $$ = dccpopt_expr_alloc(&@$, $3); + } ; dccp_hdr_field : SPORT { $$ = DCCPHDR_SPORT; } |