Home | History | Annotate | Line # | Download | only in npfctl
npf_parse.y revision 1.42
      1 /*	$NetBSD: npf_parse.y,v 1.42 2017/01/19 20:18:17 rmind 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>	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 filt_opts
    357 	{
    358 		npfctl_build_natseg($3, $6, $2, &$5, &$7, &$9, &$10, $4);
    359 	}
    360 	| MAP ifref map_sd map_algo opt_proto mapseg map_type mapseg
    361 	{
    362 		npfctl_build_natseg($3, $7, $2, &$6, &$8, &$5, NULL, $4);
    363 	}
    364 	| MAP RULESET group_opts
    365 	{
    366 		npfctl_build_maprset($3.rg_name, $3.rg_attr, $3.rg_ifname);
    367 	}
    368 	;
    369 
    370 /*
    371  * Rule procedure definition and its parameters.
    372  */
    373 
    374 rproc
    375 	: PROCEDURE STRING CURLY_OPEN procs CURLY_CLOSE
    376 	{
    377 		npfctl_build_rproc($2, $4);
    378 	}
    379 	;
    380 
    381 procs
    382 	: procs SEPLINE proc_call
    383 	{
    384 		$$ = npfvar_add_elements($1, $3);
    385 	}
    386 	| proc_call	{ $$ = $1; }
    387 	;
    388 
    389 proc_call
    390 	: IDENTIFIER COLON proc_param_list
    391 	{
    392 		proc_call_t pc;
    393 
    394 		pc.pc_name = estrdup($1);
    395 		pc.pc_opts = $3;
    396 
    397 		$$ = npfvar_create_element(NPFVAR_PROC, &pc, sizeof(pc));
    398 	}
    399 	|		{ $$ = NULL; }
    400 	;
    401 
    402 proc_param_list
    403 	: proc_param_list COMMA proc_param
    404 	{
    405 		$$ = npfvar_add_elements($1, $3);
    406 	}
    407 	| proc_param	{ $$ = $1; }
    408 	|		{ $$ = NULL; }
    409 	;
    410 
    411 proc_param
    412 	: some_name proc_param_val
    413 	{
    414 		proc_param_t pp;
    415 
    416 		pp.pp_param = estrdup($1);
    417 		pp.pp_value = $2 ? estrdup($2) : NULL;
    418 
    419 		$$ = npfvar_create_element(NPFVAR_PROC_PARAM, &pp, sizeof(pp));
    420 	}
    421 	;
    422 
    423 proc_param_val
    424 	: some_name	{ $$ = $1; }
    425 	| number	{ (void)asprintf(&$$, "%ld", $1); }
    426 	| FPNUM		{ (void)asprintf(&$$, "%lf", $1); }
    427 	|		{ $$ = NULL; }
    428 	;
    429 
    430 /*
    431  * Group and dynamic ruleset definition.
    432  */
    433 
    434 group
    435 	: GROUP group_opts
    436 	{
    437 		/* Build a group.  Increase the nesting level. */
    438 		npfctl_build_group($2.rg_name, $2.rg_attr,
    439 		    $2.rg_ifname, $2.rg_default);
    440 	}
    441 	  ruleset_block
    442 	{
    443 		/* Decrease the nesting level. */
    444 		npfctl_build_group_end();
    445 	}
    446 	;
    447 
    448 ruleset
    449 	: RULESET group_opts
    450 	{
    451 		/* Ruleset is a dynamic group. */
    452 		npfctl_build_group($2.rg_name, $2.rg_attr | NPF_RULE_DYNAMIC,
    453 		    $2.rg_ifname, $2.rg_default);
    454 		npfctl_build_group_end();
    455 	}
    456 	;
    457 
    458 group_dir
    459 	: FORW		{ $$ = NPF_RULE_FORW; }
    460 	| rule_dir
    461 	;
    462 
    463 group_opts
    464 	: DEFAULT
    465 	{
    466 		memset(&$$, 0, sizeof(rule_group_t));
    467 		$$.rg_default = true;
    468 	}
    469 	| STRING group_dir on_ifname
    470 	{
    471 		memset(&$$, 0, sizeof(rule_group_t));
    472 		$$.rg_name = $1;
    473 		$$.rg_attr = $2;
    474 		$$.rg_ifname = $3;
    475 	}
    476 	;
    477 
    478 ruleset_block
    479 	: CURLY_OPEN ruleset_def CURLY_CLOSE
    480 	;
    481 
    482 ruleset_def
    483 	: ruleset_def SEPLINE rule_group
    484 	| rule_group
    485 	;
    486 
    487 rule_group
    488 	: rule
    489 	| group
    490 	| ruleset
    491 	|
    492 	;
    493 
    494 /*
    495  * Rule and misc.
    496  */
    497 
    498 rule
    499 	: block_or_pass opt_stateful rule_dir opt_final on_ifname
    500 	  opt_family opt_proto all_or_filt_opts opt_apply
    501 	{
    502 		npfctl_build_rule($1 | $2 | $3 | $4, $5,
    503 		    $6, &$7, &$8, NULL, $9);
    504 	}
    505 	| block_or_pass opt_stateful rule_dir opt_final on_ifname
    506 	  PCAP_FILTER STRING opt_apply
    507 	{
    508 		npfctl_build_rule($1 | $2 | $3 | $4, $5,
    509 		    AF_UNSPEC, NULL, NULL, $7, $8);
    510 	}
    511 	;
    512 
    513 block_or_pass
    514 	: BLOCK block_opts	{ $$ = $2; }
    515 	| PASS			{ $$ = NPF_RULE_PASS; }
    516 	;
    517 
    518 rule_dir
    519 	: IN			{ $$ = NPF_RULE_IN; }
    520 	| OUT			{ $$ = NPF_RULE_OUT; }
    521 	|			{ $$ = NPF_RULE_IN | NPF_RULE_OUT; }
    522 	;
    523 
    524 opt_final
    525 	: FINAL			{ $$ = NPF_RULE_FINAL; }
    526 	|			{ $$ = 0; }
    527 	;
    528 
    529 on_ifname
    530 	: ON ifref		{ $$ = $2; }
    531 	|			{ $$ = NULL; }
    532 	;
    533 
    534 afamily
    535 	: INET4			{ $$ = AF_INET; }
    536 	| INET6			{ $$ = AF_INET6; }
    537 	;
    538 
    539 maybe_not
    540 	: EXCL_MARK		{ $$ = true; }
    541 	|			{ $$ = false; }
    542 	;
    543 
    544 opt_family
    545 	: FAMILY afamily	{ $$ = $2; }
    546 	|			{ $$ = AF_UNSPEC; }
    547 	;
    548 
    549 opt_proto
    550 	: PROTO TCP tcp_flags_and_mask
    551 	{
    552 		$$.op_proto = IPPROTO_TCP;
    553 		$$.op_opts = $3;
    554 	}
    555 	| PROTO ICMP icmp_type_and_code
    556 	{
    557 		$$.op_proto = IPPROTO_ICMP;
    558 		$$.op_opts = $3;
    559 	}
    560 	| PROTO ICMP6 icmp_type_and_code
    561 	{
    562 		$$.op_proto = IPPROTO_ICMPV6;
    563 		$$.op_opts = $3;
    564 	}
    565 	| PROTO some_name
    566 	{
    567 		$$.op_proto = npfctl_protono($2);
    568 		$$.op_opts = NULL;
    569 	}
    570 	| PROTO number
    571 	{
    572 		$$.op_proto = $2;
    573 		$$.op_opts = NULL;
    574 	}
    575 	|
    576 	{
    577 		$$.op_proto = -1;
    578 		$$.op_opts = NULL;
    579 	}
    580 	;
    581 
    582 all_or_filt_opts
    583 	: ALL
    584 	{
    585 		$$.fo_finvert = false;
    586 		$$.fo_from.ap_netaddr = NULL;
    587 		$$.fo_from.ap_portrange = NULL;
    588 		$$.fo_tinvert = false;
    589 		$$.fo_to.ap_netaddr = NULL;
    590 		$$.fo_to.ap_portrange = NULL;
    591 	}
    592 	| filt_opts	{ $$ = $1; }
    593 	;
    594 
    595 opt_stateful
    596 	: STATEFUL	{ $$ = NPF_RULE_STATEFUL; }
    597 	| STATEFUL_ENDS	{ $$ = NPF_RULE_STATEFUL | NPF_RULE_MULTIENDS; }
    598 	|		{ $$ = 0; }
    599 	;
    600 
    601 opt_apply
    602 	: APPLY STRING	{ $$ = $2; }
    603 	|		{ $$ = NULL; }
    604 	;
    605 
    606 block_opts
    607 	: RETURNRST	{ $$ = NPF_RULE_RETRST; }
    608 	| RETURNICMP	{ $$ = NPF_RULE_RETICMP; }
    609 	| RETURN	{ $$ = NPF_RULE_RETRST | NPF_RULE_RETICMP; }
    610 	|		{ $$ = 0; }
    611 	;
    612 
    613 filt_opts
    614 	: FROM maybe_not filt_addr port_range TO maybe_not filt_addr port_range
    615 	{
    616 		$$.fo_finvert = $2;
    617 		$$.fo_from.ap_netaddr = $3;
    618 		$$.fo_from.ap_portrange = $4;
    619 		$$.fo_tinvert = $6;
    620 		$$.fo_to.ap_netaddr = $7;
    621 		$$.fo_to.ap_portrange = $8;
    622 	}
    623 	| FROM maybe_not filt_addr port_range
    624 	{
    625 		$$.fo_finvert = $2;
    626 		$$.fo_from.ap_netaddr = $3;
    627 		$$.fo_from.ap_portrange = $4;
    628 		$$.fo_tinvert = false;
    629 		$$.fo_to.ap_netaddr = NULL;
    630 		$$.fo_to.ap_portrange = NULL;
    631 	}
    632 	| TO maybe_not filt_addr port_range
    633 	{
    634 		$$.fo_finvert = false;
    635 		$$.fo_from.ap_netaddr = NULL;
    636 		$$.fo_from.ap_portrange = NULL;
    637 		$$.fo_tinvert = $2;
    638 		$$.fo_to.ap_netaddr = $3;
    639 		$$.fo_to.ap_portrange = $4;
    640 	}
    641 	;
    642 
    643 filt_addr
    644 	: list			{ $$ = $1; }
    645 	| addr_or_ifaddr	{ $$ = $1; }
    646 	| dynamic_ifaddrs	{ $$ = npfctl_ifnet_table($1); }
    647 	| TABLE_ID		{ $$ = npfctl_parse_table_id($1); }
    648 	| ANY			{ $$ = NULL; }
    649 	;
    650 
    651 addr_and_mask
    652 	: addr SLASH number
    653 	{
    654 		$$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
    655 	}
    656 	| addr SLASH addr
    657 	{
    658 		$$ = npfctl_parse_fam_addr_mask($1, $3, NULL);
    659 	}
    660 	| addr
    661 	{
    662 		$$ = npfctl_parse_fam_addr_mask($1, NULL, NULL);
    663 	}
    664 	;
    665 
    666 addr_or_ifaddr
    667 	: addr_and_mask
    668 	{
    669 		assert($1 != NULL);
    670 		$$ = $1;
    671 	}
    672 	| static_ifaddrs
    673 	{
    674 		if (npfvar_get_count($1) != 1)
    675 			yyerror("multiple interfaces are not supported");
    676 		ifnet_addr_t *ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0);
    677 		$$ = ifna->ifna_addrs;
    678 	}
    679 	| VAR_ID
    680 	{
    681 		npfvar_t *vp = npfvar_lookup($1);
    682 		int type = npfvar_get_type(vp, 0);
    683 		ifnet_addr_t *ifna;
    684 
    685 again:
    686 		switch (type) {
    687 		case NPFVAR_IDENTIFIER:
    688 		case NPFVAR_STRING:
    689 			vp = npfctl_parse_ifnet(npfvar_expand_string(vp),
    690 			    AF_UNSPEC);
    691 			type = npfvar_get_type(vp, 0);
    692 			goto again;
    693 		case NPFVAR_FAM:
    694 		case NPFVAR_TABLE:
    695 			$$ = vp;
    696 			break;
    697 		case NPFVAR_INTERFACE:
    698 			$$ = NULL;
    699 			for (u_int i = 0; i < npfvar_get_count(vp); i++) {
    700 				ifna = npfvar_get_data(vp, type, i);
    701 				$$ = npfvar_add_elements($$, ifna->ifna_addrs);
    702 			}
    703 			break;
    704 		case -1:
    705 			yyerror("undefined variable '%s'", $1);
    706 			break;
    707 		default:
    708 			yyerror("wrong variable '%s' type '%s' for address "
    709 			    "or interface", $1, npfvar_type(type));
    710 			break;
    711 		}
    712 	}
    713 	;
    714 
    715 addr
    716 	: IPV4ADDR	{ $$ = $1; }
    717 	| IPV6ADDR	{ $$ = $1; }
    718 	;
    719 
    720 port_range
    721 	: PORT port		/* just port */
    722 	{
    723 		$$ = npfctl_parse_port_range($2, $2);
    724 	}
    725 	| PORT port MINUS port	/* port from-to */
    726 	{
    727 		$$ = npfctl_parse_port_range($2, $4);
    728 	}
    729 	| PORT VAR_ID
    730 	{
    731 		npfvar_t *vp = npfvar_lookup($2);
    732 		$$ = npfctl_parse_port_range_variable($2, vp);
    733 	}
    734 	| PORT list
    735 	{
    736 		$$ = npfctl_parse_port_range_variable(NULL, $2);
    737 	}
    738 	|			{ $$ = NULL; }
    739 	;
    740 
    741 port
    742 	: number	{ $$ = $1; }
    743 	| IDENTIFIER	{ $$ = npfctl_portno($1); }
    744 	| STRING	{ $$ = npfctl_portno($1); }
    745 	;
    746 
    747 icmp_type_and_code
    748 	: ICMPTYPE icmp_type
    749 	{
    750 		$$ = npfctl_parse_icmp($<num>0, $2, -1);
    751 	}
    752 	| ICMPTYPE icmp_type CODE number
    753 	{
    754 		$$ = npfctl_parse_icmp($<num>0, $2, $4);
    755 	}
    756 	| ICMPTYPE icmp_type CODE IDENTIFIER
    757 	{
    758 		$$ = npfctl_parse_icmp($<num>0, $2,
    759 		    npfctl_icmpcode($<num>0, $2, $4));
    760 	}
    761 	| ICMPTYPE icmp_type CODE VAR_ID
    762 	{
    763 		char *s = npfvar_expand_string(npfvar_lookup($4));
    764 		$$ = npfctl_parse_icmp($<num>0, $2,
    765 		    npfctl_icmpcode($<num>0, $2, s));
    766 	}
    767 	|		{ $$ = NULL; }
    768 	;
    769 
    770 tcp_flags_and_mask
    771 	: FLAGS tcp_flags SLASH tcp_flags
    772 	{
    773 		npfvar_add_elements($2, $4);
    774 		$$ = $2;
    775 	}
    776 	| FLAGS tcp_flags
    777 	{
    778 		if (npfvar_get_count($2) != 1)
    779 			yyerror("multiple tcpflags are not supported");
    780 		char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0);
    781 		npfvar_add_elements($2, npfctl_parse_tcpflag(s));
    782 		$$ = $2;
    783 	}
    784 	|		{ $$ = NULL; }
    785 	;
    786 
    787 tcp_flags
    788 	: IDENTIFIER	{ $$ = npfctl_parse_tcpflag($1); }
    789 	;
    790 
    791 icmp_type
    792 	: number	{ $$ = $1; }
    793 	| IDENTIFIER	{ $$ = npfctl_icmptype($<num>-1, $1); }
    794 	| VAR_ID
    795 	{
    796 		char *s = npfvar_expand_string(npfvar_lookup($1));
    797 		$$ = npfctl_icmptype($<num>-1, s);
    798 	}
    799 	;
    800 
    801 ifname
    802 	: some_name
    803 	{
    804 		npfctl_note_interface($1);
    805 		$$ = $1;
    806 	}
    807 	| VAR_ID
    808 	{
    809 		npfvar_t *vp = npfvar_lookup($1);
    810 		const int type = npfvar_get_type(vp, 0);
    811 		ifnet_addr_t *ifna;
    812 
    813 		switch (type) {
    814 		case NPFVAR_STRING:
    815 		case NPFVAR_IDENTIFIER:
    816 			$$ = npfvar_expand_string(vp);
    817 			break;
    818 		case NPFVAR_INTERFACE:
    819 			if (npfvar_get_count(vp) != 1)
    820 				yyerror(
    821 				    "multiple interfaces are not supported");
    822 			ifna = npfvar_get_data(vp, type, 0);
    823 			$$ = ifna->ifna_name;
    824 			break;
    825 		case -1:
    826 			yyerror("undefined variable '%s' for interface", $1);
    827 			break;
    828 		default:
    829 			yyerror("wrong variable '%s' type '%s' for interface",
    830 			    $1, npfvar_type(type));
    831 			break;
    832 		}
    833 		npfctl_note_interface($$);
    834 	}
    835 	;
    836 
    837 static_ifaddrs
    838 	: afamily PAR_OPEN ifname PAR_CLOSE
    839 	{
    840 		$$ = npfctl_parse_ifnet($3, $1);
    841 	}
    842 	;
    843 
    844 dynamic_ifaddrs
    845 	: IFADDRS PAR_OPEN ifname PAR_CLOSE
    846 	{
    847 		$$ = $3;
    848 	}
    849 	;
    850 
    851 ifref
    852 	: ifname
    853 	| dynamic_ifaddrs
    854 	| static_ifaddrs
    855 	{
    856 		if (npfvar_get_count($1) != 1)
    857 			yyerror("multiple interfaces are not supported");
    858 		ifnet_addr_t *ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0);
    859 		npfctl_note_interface(ifna->ifna_name);
    860 		$$ = ifna->ifna_name;
    861 	}
    862 	;
    863 
    864 number
    865 	: HEX		{ $$ = $1; }
    866 	| NUM		{ $$ = $1; }
    867 	;
    868 
    869 some_name
    870 	: IDENTIFIER	{ $$ = $1; }
    871 	| STRING	{ $$ = $1; }
    872 	;
    873 
    874 %%
    875