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