Home | History | Annotate | Line # | Download | only in npfctl
npf_parse.y revision 1.51
      1 /*-
      2  * Copyright (c) 2011-2020 The NetBSD Foundation, Inc.
      3  * All rights reserved.
      4  *
      5  * This code is derived from software contributed to The NetBSD Foundation
      6  * by Martin Husemann, Christos Zoulas and Mindaugas Rasiukevicius.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     27  * POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 %{
     31 
     32 #include <err.h>
     33 #include <netdb.h>
     34 #include <stdio.h>
     35 #include <stdlib.h>
     36 #include <string.h>
     37 #ifdef __NetBSD__
     38 #include <vis.h>
     39 #endif
     40 
     41 #include "npfctl.h"
     42 
     43 #define	YYSTACKSIZE	4096
     44 
     45 int			yystarttoken;
     46 const char *		yyfilename;
     47 
     48 extern int		yylineno, yycolumn;
     49 extern int		yylex(int);
     50 
     51 void
     52 yyerror(const char *fmt, ...)
     53 {
     54 	extern int yyleng;
     55 	extern char *yytext;
     56 
     57 	char *msg, *context = estrndup(yytext, yyleng);
     58 	bool eol = (*context == '\n');
     59 	va_list ap;
     60 
     61 	va_start(ap, fmt);
     62 	vasprintf(&msg, fmt, ap);
     63 	va_end(ap);
     64 
     65 	fprintf(stderr, "%s:%d:%d: %s", yyfilename,
     66 	    yylineno - (int)eol, yycolumn, msg);
     67 	if (!eol) {
     68 #ifdef __NetBSD__
     69 		size_t len = strlen(context);
     70 		char *dst = ecalloc(1, len * 4 + 1);
     71 
     72 		strvisx(dst, context, len, VIS_WHITE|VIS_CSTYLE);
     73 		context = dst;
     74 #endif
     75 		fprintf(stderr, " near '%s'", context);
     76 	}
     77 	fprintf(stderr, "\n");
     78 	exit(EXIT_FAILURE);
     79 }
     80 
     81 %}
     82 
     83 /*
     84  * No conflicts allowed.  Keep it this way.
     85  */
     86 %expect 0
     87 %expect-rr 0
     88 
     89 /*
     90  * Depending on the mode of operation, set a different start symbol.
     91  * Workaround yacc limitation by passing the start token.
     92  */
     93 %start input
     94 %token RULE_ENTRY_TOKEN MAP_ENTRY_TOKEN
     95 %lex-param { int yystarttoken }
     96 
     97 /*
     98  * General tokens.
     99  */
    100 %token			ALG
    101 %token			ALGO
    102 %token			ALL
    103 %token			ANY
    104 %token			APPLY
    105 %token			ARROWBOTH
    106 %token			ARROWLEFT
    107 %token			ARROWRIGHT
    108 %token			BLOCK
    109 %token			CDB
    110 %token			CONST
    111 %token			CURLY_CLOSE
    112 %token			CURLY_OPEN
    113 %token			CODE
    114 %token			COLON
    115 %token			COMMA
    116 %token			DEFAULT
    117 %token			TDYNAMIC
    118 %token			TSTATIC
    119 %token			EQ
    120 %token			EXCL_MARK
    121 %token			TFILE
    122 %token			FLAGS
    123 %token			FROM
    124 %token			GROUP
    125 %token			HASH
    126 %token			ICMPTYPE
    127 %token			ID
    128 %token			IFADDRS
    129 %token			IN
    130 %token			INET4
    131 %token			INET6
    132 %token			INTERFACE
    133 %token			INVALID
    134 %token			IPHASH
    135 %token			IPSET
    136 %token			LPM
    137 %token			MAP
    138 %token			NO_PORTS
    139 %token			MINUS
    140 %token			NAME
    141 %token			NETMAP
    142 %token			NPT66
    143 %token			ON
    144 %token			OFF
    145 %token			OUT
    146 %token			PAR_CLOSE
    147 %token			PAR_OPEN
    148 %token			PASS
    149 %token			PCAP_FILTER
    150 %token			PORT
    151 %token			PROCEDURE
    152 %token			PROTO
    153 %token			FAMILY
    154 %token			FINAL
    155 %token			FORW
    156 %token			RETURN
    157 %token			RETURNICMP
    158 %token			RETURNRST
    159 %token			ROUNDROBIN
    160 %token			RULESET
    161 %token			SEPLINE
    162 %token			SET
    163 %token			SLASH
    164 %token			STATEFUL
    165 %token			STATEFUL_ALL
    166 %token			TABLE
    167 %token			TCP
    168 %token			TO
    169 %token			TREE
    170 %token			TYPE
    171 %token	<num>		ICMP
    172 %token	<num>		ICMP6
    173 
    174 %token	<num>		HEX
    175 %token	<str>		IDENTIFIER
    176 %token	<str>		IPV4ADDR
    177 %token	<str>		IPV6ADDR
    178 %token	<num>		NUM
    179 %token	<fpnum>		FPNUM
    180 %token	<str>		STRING
    181 %token	<str>		PARAM
    182 %token	<str>		TABLE_ID
    183 %token	<str>		VAR_ID
    184 
    185 %type	<str>		addr some_name table_store dynamic_ifaddrs
    186 %type	<str>		proc_param_val opt_apply ifname on_ifname ifref
    187 %type	<num>		port opt_final number afamily opt_family
    188 %type	<num>		block_or_pass rule_dir group_dir block_opts
    189 %type	<num>		maybe_not opt_stateful icmp_type table_type
    190 %type	<num>		map_sd map_algo map_flags map_type
    191 %type	<num>		param_val
    192 %type	<var>		static_ifaddrs filt_addr_element
    193 %type	<var>		filt_port filt_port_list port_range icmp_type_and_code
    194 %type	<var>		filt_addr addr_and_mask tcp_flags tcp_flags_and_mask
    195 %type	<var>		procs proc_call proc_param_list proc_param
    196 %type	<var>		element list_elems list value filt_addr_list
    197 %type	<var>		opt_proto proto proto_elems
    198 %type	<addrport>	mapseg
    199 %type	<filtopts>	filt_opts all_or_filt_opts
    200 %type	<optproto>	rawproto
    201 %type	<rulegroup>	group_opts
    202 
    203 %union {
    204 	char *		str;
    205 	unsigned long	num;
    206 	double		fpnum;
    207 	npfvar_t *	var;
    208 	addr_port_t	addrport;
    209 	filt_opts_t	filtopts;
    210 	opt_proto_t	optproto;
    211 	rule_group_t	rulegroup;
    212 }
    213 
    214 %%
    215 
    216 input
    217 	: lines
    218 	| RULE_ENTRY_TOKEN	rule
    219 	| MAP_ENTRY_TOKEN	map
    220 	;
    221 
    222 lines
    223 	: lines SEPLINE line
    224 	| line
    225 	;
    226 
    227 line
    228 	: vardef
    229 	| table
    230 	| map
    231 	| group
    232 	| rproc
    233 	| alg
    234 	| set
    235 	|
    236 	;
    237 
    238 alg
    239 	: ALG STRING
    240 	{
    241 		npfctl_build_alg($2);
    242 	}
    243 	;
    244 
    245 param_val
    246 	: number	{ $$ = $1; }
    247 	| ON		{ $$ = true; }
    248 	| OFF		{ $$ = false; }
    249 	;
    250 
    251 set
    252 	: SET PARAM param_val {
    253 		npfctl_setparam($2, $3);
    254 	}
    255 	;
    256 
    257 /*
    258  * A value - an element or a list of elements.
    259  * Can be assigned to a variable or used inline.
    260  */
    261 
    262 vardef
    263 	: VAR_ID EQ value
    264 	{
    265 		npfvar_add($3, $1);
    266 	}
    267 	;
    268 
    269 value
    270 	: element
    271 	| list
    272 	;
    273 
    274 list
    275 	: CURLY_OPEN list_elems CURLY_CLOSE
    276 	{
    277 		$$ = $2;
    278 	}
    279 	;
    280 
    281 list_elems
    282 	: list_elems COMMA element
    283 	{
    284 		npfvar_add_elements($1, $3);
    285 	}
    286 	| element
    287 	;
    288 
    289 element
    290 	: IDENTIFIER
    291 	{
    292 		$$ = npfvar_create_from_string(NPFVAR_IDENTIFIER, $1);
    293 	}
    294 	| STRING
    295 	{
    296 		$$ = npfvar_create_from_string(NPFVAR_STRING, $1);
    297 	}
    298 	| number MINUS number
    299 	{
    300 		$$ = npfctl_parse_port_range($1, $3);
    301 	}
    302 	| number
    303 	{
    304 		$$ = npfvar_create_element(NPFVAR_NUM, &$1, sizeof($1));
    305 	}
    306 	| VAR_ID
    307 	{
    308 		$$ = npfvar_create_from_string(NPFVAR_VAR_ID, $1);
    309 	}
    310 	| TABLE_ID		{ $$ = npfctl_parse_table_id($1); }
    311 	| dynamic_ifaddrs	{ $$ = npfctl_ifnet_table($1); }
    312 	| static_ifaddrs	{ $$ = $1; }
    313 	| addr_and_mask		{ $$ = $1; }
    314 	;
    315 
    316 /*
    317  * Table definition.
    318  */
    319 
    320 table
    321 	: TABLE TABLE_ID TYPE table_type table_store
    322 	{
    323 		npfctl_build_table($2, $4, $5);
    324 	}
    325 	;
    326 
    327 table_type
    328 	: IPSET		{ $$ = NPF_TABLE_IPSET; }
    329 	| HASH
    330 	{
    331 		warnx("warning - table type \"hash\" is deprecated and may be "
    332 		    "deleted in\nthe future; please use the \"ipset\" type "
    333 		    "instead.");
    334 		$$ = NPF_TABLE_IPSET;
    335 	}
    336 	| LPM		{ $$ = NPF_TABLE_LPM; }
    337 	| TREE
    338 	{
    339 		warnx("warning - table type \"tree\" is deprecated and may be "
    340 		    "deleted in\nthe future; please use the \"lpm\" type "
    341 		    "instead.");
    342 		$$ = NPF_TABLE_LPM;
    343 	}
    344 	| CONST		{ $$ = NPF_TABLE_CONST; }
    345 	| CDB
    346 	{
    347 		warnx("warning -- table type \"cdb\" is deprecated and may be "
    348 		    "deleted in\nthe future; please use the \"const\" type "
    349 		    "instead.");
    350 		$$ = NPF_TABLE_CONST;
    351 	}
    352 	;
    353 
    354 table_store
    355 	: TFILE STRING	{ $$ = $2; }
    356 	| TDYNAMIC
    357 	{
    358 		warnx("warning - the \"dynamic\" keyword for tables is obsolete");
    359 		$$ = NULL;
    360 	}
    361 	|		{ $$ = NULL; }
    362 	;
    363 
    364 /*
    365  * Map definition.
    366  */
    367 
    368 map_sd
    369 	: TSTATIC	{ $$ = NPFCTL_NAT_STATIC; }
    370 	| TDYNAMIC	{ $$ = NPFCTL_NAT_DYNAMIC; }
    371 	|		{ $$ = NPFCTL_NAT_DYNAMIC; }
    372 	;
    373 
    374 map_algo
    375 	: ALGO NETMAP		{ $$ = NPF_ALGO_NETMAP; }
    376 	| ALGO IPHASH		{ $$ = NPF_ALGO_IPHASH; }
    377 	| ALGO ROUNDROBIN	{ $$ = NPF_ALGO_RR; }
    378 	| ALGO NPT66		{ $$ = NPF_ALGO_NPT66; }
    379 	|			{ $$ = 0; }
    380 	;
    381 
    382 map_flags
    383 	: NO_PORTS	{ $$ = NPF_NAT_PORTS; }
    384 	|		{ $$ = 0; }
    385 	;
    386 
    387 map_type
    388 	: ARROWBOTH	{ $$ = NPF_NATIN | NPF_NATOUT; }
    389 	| ARROWLEFT	{ $$ = NPF_NATIN; }
    390 	| ARROWRIGHT	{ $$ = NPF_NATOUT; }
    391 	;
    392 
    393 mapseg
    394 	: filt_addr filt_port
    395 	{
    396 		$$.ap_netaddr = $1;
    397 		$$.ap_portrange = $2;
    398 	}
    399 	;
    400 
    401 map
    402 	: MAP ifref map_sd map_algo map_flags mapseg map_type mapseg
    403 	  PASS opt_family opt_proto all_or_filt_opts
    404 	{
    405 		npfctl_build_natseg($3, $7, $5, $2, &$6, &$8, $11, &$12, $4);
    406 	}
    407 	| MAP ifref map_sd map_algo map_flags mapseg map_type mapseg
    408 	{
    409 		npfctl_build_natseg($3, $7, $5, $2, &$6, &$8, NULL, NULL, $4);
    410 	}
    411 	| MAP ifref map_sd map_algo map_flags proto mapseg map_type mapseg
    412 	{
    413 		npfctl_build_natseg($3, $8, $5, $2, &$7, &$9, $6, NULL, $4);
    414 	}
    415 	| MAP RULESET group_opts
    416 	{
    417 		npfctl_build_maprset($3.rg_name, $3.rg_attr, $3.rg_ifname);
    418 	}
    419 	;
    420 
    421 /*
    422  * Rule procedure definition and its parameters.
    423  */
    424 
    425 rproc
    426 	: PROCEDURE STRING CURLY_OPEN procs CURLY_CLOSE
    427 	{
    428 		npfctl_build_rproc($2, $4);
    429 	}
    430 	;
    431 
    432 procs
    433 	: procs SEPLINE proc_call
    434 	{
    435 		$$ = npfvar_add_elements($1, $3);
    436 	}
    437 	| proc_call	{ $$ = $1; }
    438 	;
    439 
    440 proc_call
    441 	: IDENTIFIER COLON proc_param_list
    442 	{
    443 		proc_call_t pc;
    444 
    445 		pc.pc_name = estrdup($1);
    446 		pc.pc_opts = $3;
    447 
    448 		$$ = npfvar_create_element(NPFVAR_PROC, &pc, sizeof(pc));
    449 	}
    450 	|		{ $$ = NULL; }
    451 	;
    452 
    453 proc_param_list
    454 	: proc_param_list COMMA proc_param
    455 	{
    456 		$$ = npfvar_add_elements($1, $3);
    457 	}
    458 	| proc_param	{ $$ = $1; }
    459 	|		{ $$ = NULL; }
    460 	;
    461 
    462 proc_param
    463 	: some_name proc_param_val
    464 	{
    465 		proc_param_t pp;
    466 
    467 		pp.pp_param = estrdup($1);
    468 		pp.pp_value = $2 ? estrdup($2) : NULL;
    469 
    470 		$$ = npfvar_create_element(NPFVAR_PROC_PARAM, &pp, sizeof(pp));
    471 	}
    472 	;
    473 
    474 proc_param_val
    475 	: some_name	{ $$ = $1; }
    476 	| number	{ (void)asprintf(&$$, "%ld", $1); }
    477 	| FPNUM		{ (void)asprintf(&$$, "%lf", $1); }
    478 	|		{ $$ = NULL; }
    479 	;
    480 
    481 /*
    482  * Group and dynamic ruleset definition.
    483  */
    484 
    485 group
    486 	: GROUP group_opts
    487 	{
    488 		/* Build a group.  Increase the nesting level. */
    489 		npfctl_build_group($2.rg_name, $2.rg_attr,
    490 		    $2.rg_ifname, $2.rg_default);
    491 	}
    492 	  ruleset_block
    493 	{
    494 		/* Decrease the nesting level. */
    495 		npfctl_build_group_end();
    496 	}
    497 	;
    498 
    499 ruleset
    500 	: RULESET group_opts
    501 	{
    502 		/* Ruleset is a dynamic group. */
    503 		npfctl_build_group($2.rg_name, $2.rg_attr | NPF_RULE_DYNAMIC,
    504 		    $2.rg_ifname, $2.rg_default);
    505 		npfctl_build_group_end();
    506 	}
    507 	;
    508 
    509 group_dir
    510 	: FORW		{ $$ = NPF_RULE_FORW; }
    511 	| rule_dir
    512 	;
    513 
    514 group_opts
    515 	: DEFAULT
    516 	{
    517 		memset(&$$, 0, sizeof(rule_group_t));
    518 		$$.rg_default = true;
    519 	}
    520 	| STRING group_dir on_ifname
    521 	{
    522 		memset(&$$, 0, sizeof(rule_group_t));
    523 		$$.rg_name = $1;
    524 		$$.rg_attr = $2;
    525 		$$.rg_ifname = $3;
    526 	}
    527 	;
    528 
    529 ruleset_block
    530 	: CURLY_OPEN ruleset_def CURLY_CLOSE
    531 	;
    532 
    533 ruleset_def
    534 	: ruleset_def SEPLINE rule_group
    535 	| rule_group
    536 	;
    537 
    538 rule_group
    539 	: rule
    540 	| group
    541 	| ruleset
    542 	|
    543 	;
    544 
    545 /*
    546  * Rule and misc.
    547  */
    548 
    549 rule
    550 	: block_or_pass opt_stateful rule_dir opt_final on_ifname
    551 	  opt_family opt_proto all_or_filt_opts opt_apply
    552 	{
    553 		npfctl_build_rule($1 | $2 | $3 | $4, $5,
    554 		    $6, $7, &$8, NULL, $9);
    555 	}
    556 	| block_or_pass opt_stateful rule_dir opt_final on_ifname
    557 	  PCAP_FILTER STRING opt_apply
    558 	{
    559 		npfctl_build_rule($1 | $2 | $3 | $4, $5,
    560 		    AF_UNSPEC, NULL, NULL, $7, $8);
    561 	}
    562 	;
    563 
    564 block_or_pass
    565 	: BLOCK block_opts	{ $$ = $2; }
    566 	| PASS			{ $$ = NPF_RULE_PASS; }
    567 	;
    568 
    569 rule_dir
    570 	: IN			{ $$ = NPF_RULE_IN; }
    571 	| OUT			{ $$ = NPF_RULE_OUT; }
    572 	|			{ $$ = NPF_RULE_IN | NPF_RULE_OUT; }
    573 	;
    574 
    575 opt_final
    576 	: FINAL			{ $$ = NPF_RULE_FINAL; }
    577 	|			{ $$ = 0; }
    578 	;
    579 
    580 on_ifname
    581 	: ON ifref		{ $$ = $2; }
    582 	|			{ $$ = NULL; }
    583 	;
    584 
    585 afamily
    586 	: INET4			{ $$ = AF_INET; }
    587 	| INET6			{ $$ = AF_INET6; }
    588 	;
    589 
    590 maybe_not
    591 	: EXCL_MARK		{ $$ = true; }
    592 	|			{ $$ = false; }
    593 	;
    594 
    595 opt_family
    596 	: FAMILY afamily	{ $$ = $2; }
    597 	|			{ $$ = AF_UNSPEC; }
    598 	;
    599 
    600 rawproto
    601 	: TCP tcp_flags_and_mask
    602 	{
    603 		$$.op_proto = IPPROTO_TCP;
    604 		$$.op_opts = $2;
    605 	}
    606 	| ICMP icmp_type_and_code
    607 	{
    608 		$$.op_proto = IPPROTO_ICMP;
    609 		$$.op_opts = $2;
    610 	}
    611 	| ICMP6 icmp_type_and_code
    612 	{
    613 		$$.op_proto = IPPROTO_ICMPV6;
    614 		$$.op_opts = $2;
    615 	}
    616 	| some_name
    617 	{
    618 		$$.op_proto = npfctl_protono($1);
    619 		$$.op_opts = NULL;
    620 	}
    621 	| number
    622 	{
    623 		$$.op_proto = $1;
    624 		$$.op_opts = NULL;
    625 	}
    626 	;
    627 
    628 proto_elems
    629 	: proto_elems COMMA rawproto
    630 	{
    631 		npfvar_t *pvar = npfvar_create_element(
    632 		    NPFVAR_PROTO, &$3, sizeof($3));
    633 		$$ = npfvar_add_elements($1, pvar);
    634 	}
    635 	| rawproto
    636 	{
    637 		$$ = npfvar_create_element(NPFVAR_PROTO, &$1, sizeof($1));
    638 	}
    639 	;
    640 
    641 proto
    642 	: PROTO rawproto
    643 	{
    644 		$$ = npfvar_create_element(NPFVAR_PROTO, &$2, sizeof($2));
    645 	}
    646 	| PROTO CURLY_OPEN proto_elems CURLY_CLOSE
    647 	{
    648 		$$ = $3;
    649 	}
    650 	;
    651 
    652 opt_proto
    653 	: proto			{ $$ = $1; }
    654 	|			{ $$ = NULL; }
    655 	;
    656 
    657 all_or_filt_opts
    658 	: ALL
    659 	{
    660 		$$.fo_finvert = false;
    661 		$$.fo_from.ap_netaddr = NULL;
    662 		$$.fo_from.ap_portrange = NULL;
    663 		$$.fo_tinvert = false;
    664 		$$.fo_to.ap_netaddr = NULL;
    665 		$$.fo_to.ap_portrange = NULL;
    666 	}
    667 	| filt_opts	{ $$ = $1; }
    668 	;
    669 
    670 opt_stateful
    671 	: STATEFUL	{ $$ = NPF_RULE_STATEFUL; }
    672 	| STATEFUL_ALL	{ $$ = NPF_RULE_STATEFUL | NPF_RULE_GSTATEFUL; }
    673 	|		{ $$ = 0; }
    674 	;
    675 
    676 opt_apply
    677 	: APPLY STRING	{ $$ = $2; }
    678 	|		{ $$ = NULL; }
    679 	;
    680 
    681 block_opts
    682 	: RETURNRST	{ $$ = NPF_RULE_RETRST; }
    683 	| RETURNICMP	{ $$ = NPF_RULE_RETICMP; }
    684 	| RETURN	{ $$ = NPF_RULE_RETRST | NPF_RULE_RETICMP; }
    685 	|		{ $$ = 0; }
    686 	;
    687 
    688 filt_opts
    689 	: FROM maybe_not filt_addr filt_port TO maybe_not filt_addr filt_port
    690 	{
    691 		$$.fo_finvert = $2;
    692 		$$.fo_from.ap_netaddr = $3;
    693 		$$.fo_from.ap_portrange = $4;
    694 		$$.fo_tinvert = $6;
    695 		$$.fo_to.ap_netaddr = $7;
    696 		$$.fo_to.ap_portrange = $8;
    697 	}
    698 	| FROM maybe_not filt_addr filt_port
    699 	{
    700 		$$.fo_finvert = $2;
    701 		$$.fo_from.ap_netaddr = $3;
    702 		$$.fo_from.ap_portrange = $4;
    703 		$$.fo_tinvert = false;
    704 		$$.fo_to.ap_netaddr = NULL;
    705 		$$.fo_to.ap_portrange = NULL;
    706 	}
    707 	| TO maybe_not filt_addr filt_port
    708 	{
    709 		$$.fo_finvert = false;
    710 		$$.fo_from.ap_netaddr = NULL;
    711 		$$.fo_from.ap_portrange = NULL;
    712 		$$.fo_tinvert = $2;
    713 		$$.fo_to.ap_netaddr = $3;
    714 		$$.fo_to.ap_portrange = $4;
    715 	}
    716 	;
    717 
    718 filt_addr_list
    719 	: filt_addr_list COMMA filt_addr_element
    720 	{
    721 		npfvar_add_elements($1, $3);
    722 	}
    723 	| filt_addr_element
    724 	;
    725 
    726 filt_addr
    727 	: CURLY_OPEN filt_addr_list CURLY_CLOSE
    728 	{
    729 		$$ = $2;
    730 	}
    731 	| filt_addr_element	{ $$ = $1; }
    732 	| ANY			{ $$ = NULL; }
    733 	;
    734 
    735 addr_and_mask
    736 	: addr SLASH number
    737 	{
    738 		$$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
    739 	}
    740 	| addr SLASH addr
    741 	{
    742 		$$ = npfctl_parse_fam_addr_mask($1, $3, NULL);
    743 	}
    744 	| addr
    745 	{
    746 		$$ = npfctl_parse_fam_addr_mask($1, NULL, NULL);
    747 	}
    748 	;
    749 
    750 filt_addr_element
    751 	: addr_and_mask		{ assert($1 != NULL); $$ = $1; }
    752 	| static_ifaddrs
    753 	{
    754 		if (npfvar_get_count($1) != 1)
    755 			yyerror("multiple interfaces are not supported");
    756 		ifnet_addr_t *ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0);
    757 		$$ = ifna->ifna_addrs;
    758 	}
    759 	| dynamic_ifaddrs	{ $$ = npfctl_ifnet_table($1); }
    760 	| TABLE_ID		{ $$ = npfctl_parse_table_id($1); }
    761 	| VAR_ID
    762 	{
    763 		npfvar_t *vp = npfvar_lookup($1);
    764 		int type = npfvar_get_type(vp, 0);
    765 		ifnet_addr_t *ifna;
    766 again:
    767 		switch (type) {
    768 		case NPFVAR_IDENTIFIER:
    769 		case NPFVAR_STRING:
    770 			vp = npfctl_parse_ifnet(npfvar_expand_string(vp),
    771 			    AF_UNSPEC);
    772 			type = npfvar_get_type(vp, 0);
    773 			goto again;
    774 		case NPFVAR_FAM:
    775 		case NPFVAR_TABLE:
    776 			$$ = vp;
    777 			break;
    778 		case NPFVAR_INTERFACE:
    779 			$$ = NULL;
    780 			for (u_int i = 0; i < npfvar_get_count(vp); i++) {
    781 				ifna = npfvar_get_data(vp, type, i);
    782 				$$ = npfvar_add_elements($$, ifna->ifna_addrs);
    783 			}
    784 			break;
    785 		case -1:
    786 			yyerror("undefined variable '%s'", $1);
    787 			break;
    788 		default:
    789 			yyerror("wrong variable '%s' type '%s' for address "
    790 			    "or interface", $1, npfvar_type(type));
    791 			break;
    792 		}
    793 	}
    794 	;
    795 
    796 addr
    797 	: IPV4ADDR	{ $$ = $1; }
    798 	| IPV6ADDR	{ $$ = $1; }
    799 	;
    800 
    801 filt_port
    802 	: PORT CURLY_OPEN filt_port_list CURLY_CLOSE
    803 	{
    804 		$$ = npfctl_parse_port_range_variable(NULL, $3);
    805 	}
    806 	| PORT port_range	{ $$ = $2; }
    807 	|			{ $$ = NULL; }
    808 	;
    809 
    810 filt_port_list
    811 	: filt_port_list COMMA port_range
    812 	{
    813 		npfvar_add_elements($1, $3);
    814 	}
    815 	| port_range
    816 	;
    817 
    818 port_range
    819 	: port		/* just port */
    820 	{
    821 		$$ = npfctl_parse_port_range($1, $1);
    822 	}
    823 	| port MINUS port	/* port from-to */
    824 	{
    825 		$$ = npfctl_parse_port_range($1, $3);
    826 	}
    827 	| VAR_ID
    828 	{
    829 		npfvar_t *vp = npfvar_lookup($1);
    830 		$$ = npfctl_parse_port_range_variable($1, vp);
    831 	}
    832 	;
    833 
    834 port
    835 	: number	{ $$ = $1; }
    836 	| IDENTIFIER	{ $$ = npfctl_portno($1); }
    837 	| STRING	{ $$ = npfctl_portno($1); }
    838 	;
    839 
    840 icmp_type_and_code
    841 	: ICMPTYPE icmp_type
    842 	{
    843 		$$ = npfctl_parse_icmp($<num>0, $2, -1);
    844 	}
    845 	| ICMPTYPE icmp_type CODE number
    846 	{
    847 		$$ = npfctl_parse_icmp($<num>0, $2, $4);
    848 	}
    849 	| ICMPTYPE icmp_type CODE IDENTIFIER
    850 	{
    851 		$$ = npfctl_parse_icmp($<num>0, $2,
    852 		    npfctl_icmpcode($<num>0, $2, $4));
    853 	}
    854 	| ICMPTYPE icmp_type CODE VAR_ID
    855 	{
    856 		char *s = npfvar_expand_string(npfvar_lookup($4));
    857 		$$ = npfctl_parse_icmp($<num>0, $2,
    858 		    npfctl_icmpcode($<num>0, $2, s));
    859 	}
    860 	|		{ $$ = NULL; }
    861 	;
    862 
    863 tcp_flags_and_mask
    864 	: FLAGS tcp_flags SLASH tcp_flags
    865 	{
    866 		npfvar_add_elements($2, $4);
    867 		$$ = $2;
    868 	}
    869 	| FLAGS tcp_flags
    870 	{
    871 		if (npfvar_get_count($2) != 1)
    872 			yyerror("multiple tcpflags are not supported");
    873 		char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0);
    874 		npfvar_add_elements($2, npfctl_parse_tcpflag(s));
    875 		$$ = $2;
    876 	}
    877 	|		{ $$ = NULL; }
    878 	;
    879 
    880 tcp_flags
    881 	: IDENTIFIER	{ $$ = npfctl_parse_tcpflag($1); }
    882 	;
    883 
    884 icmp_type
    885 	: number	{ $$ = $1; }
    886 	| IDENTIFIER	{ $$ = npfctl_icmptype($<num>-1, $1); }
    887 	| VAR_ID
    888 	{
    889 		char *s = npfvar_expand_string(npfvar_lookup($1));
    890 		$$ = npfctl_icmptype($<num>-1, s);
    891 	}
    892 	;
    893 
    894 ifname
    895 	: some_name
    896 	{
    897 		npfctl_note_interface($1);
    898 		$$ = $1;
    899 	}
    900 	| VAR_ID
    901 	{
    902 		npfvar_t *vp = npfvar_lookup($1);
    903 		const int type = npfvar_get_type(vp, 0);
    904 		ifnet_addr_t *ifna;
    905 		const char *name;
    906 		unsigned *tid;
    907 		bool ifaddr;
    908 
    909 		switch (type) {
    910 		case NPFVAR_STRING:
    911 		case NPFVAR_IDENTIFIER:
    912 			$$ = npfvar_expand_string(vp);
    913 			break;
    914 		case NPFVAR_INTERFACE:
    915 			if (npfvar_get_count(vp) != 1)
    916 				yyerror(
    917 				    "multiple interfaces are not supported");
    918 			ifna = npfvar_get_data(vp, type, 0);
    919 			$$ = ifna->ifna_name;
    920 			break;
    921 		case NPFVAR_TABLE:
    922 			tid = npfvar_get_data(vp, type, 0);
    923 			name = npfctl_table_getname(npfctl_config_ref(),
    924 			    *tid, &ifaddr);
    925 			if (!ifaddr) {
    926 				yyerror("variable '%s' references a table "
    927 				    "%s instead of an interface", $1, name);
    928 			}
    929 			$$ = estrdup(name);
    930 			break;
    931 		case -1:
    932 			yyerror("undefined variable '%s' for interface", $1);
    933 			break;
    934 		default:
    935 			yyerror("wrong variable '%s' type '%s' for interface",
    936 			    $1, npfvar_type(type));
    937 			break;
    938 		}
    939 		npfctl_note_interface($$);
    940 	}
    941 	;
    942 
    943 static_ifaddrs
    944 	: afamily PAR_OPEN ifname PAR_CLOSE
    945 	{
    946 		$$ = npfctl_parse_ifnet($3, $1);
    947 	}
    948 	;
    949 
    950 dynamic_ifaddrs
    951 	: IFADDRS PAR_OPEN ifname PAR_CLOSE
    952 	{
    953 		$$ = $3;
    954 	}
    955 	;
    956 
    957 ifref
    958 	: ifname
    959 	| dynamic_ifaddrs
    960 	| static_ifaddrs
    961 	{
    962 		ifnet_addr_t *ifna;
    963 
    964 		if (npfvar_get_count($1) != 1) {
    965 			yyerror("multiple interfaces are not supported");
    966 		}
    967 		ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0);
    968 		npfctl_note_interface(ifna->ifna_name);
    969 		$$ = ifna->ifna_name;
    970 	}
    971 	;
    972 
    973 number
    974 	: HEX		{ $$ = $1; }
    975 	| NUM		{ $$ = $1; }
    976 	;
    977 
    978 some_name
    979 	: IDENTIFIER	{ $$ = $1; }
    980 	| STRING	{ $$ = $1; }
    981 	;
    982 
    983 %%
    984