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