xref: /netbsd-src/usr.sbin/npf/npfctl/npf_parse.y (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: npf_parse.y,v 1.46 2017/12/10 22:04:41 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			NO_PORTS
128 %token			MINUS
129 %token			NAME
130 %token			NPT66
131 %token			ON
132 %token			OFF
133 %token			OUT
134 %token			PAR_CLOSE
135 %token			PAR_OPEN
136 %token			PASS
137 %token			PCAP_FILTER
138 %token			PORT
139 %token			PROCEDURE
140 %token			PROTO
141 %token			FAMILY
142 %token			FINAL
143 %token			FORW
144 %token			RETURN
145 %token			RETURNICMP
146 %token			RETURNRST
147 %token			RULESET
148 %token			SEPLINE
149 %token			SET
150 %token			SLASH
151 %token			STATEFUL
152 %token			STATEFUL_ENDS
153 %token			TABLE
154 %token			TCP
155 %token			TO
156 %token			TREE
157 %token			TYPE
158 %token	<num>		ICMP
159 %token	<num>		ICMP6
160 
161 %token	<num>		HEX
162 %token	<str>		IDENTIFIER
163 %token	<str>		IPV4ADDR
164 %token	<str>		IPV6ADDR
165 %token	<num>		NUM
166 %token	<fpnum>		FPNUM
167 %token	<str>		STRING
168 %token	<str>		TABLE_ID
169 %token	<str>		VAR_ID
170 
171 %type	<str>		addr, some_name, table_store, dynamic_ifaddrs
172 %type	<str>		proc_param_val, opt_apply, ifname, on_ifname, ifref
173 %type	<num>		port, opt_final, number, afamily, opt_family
174 %type	<num>		block_or_pass, rule_dir, group_dir, block_opts
175 %type	<num>		maybe_not, opt_stateful, icmp_type, table_type
176 %type	<num>		map_sd, map_algo, map_flags, map_type
177 %type	<var>		static_ifaddrs, addr_or_ifaddr
178 %type	<var>		port_range, icmp_type_and_code
179 %type	<var>		filt_addr, addr_and_mask, tcp_flags, tcp_flags_and_mask
180 %type	<var>		procs, proc_call, proc_param_list, proc_param
181 %type	<var>		element, list_elems, list, value
182 %type	<addrport>	mapseg
183 %type	<filtopts>	filt_opts, all_or_filt_opts
184 %type	<optproto>	proto opt_proto
185 %type	<rulegroup>	group_opts
186 %type	<tf>		onoff
187 
188 %union {
189 	char *		str;
190 	bool		tf;
191 	unsigned long	num;
192 	double		fpnum;
193 	npfvar_t *	var;
194 	addr_port_t	addrport;
195 	filt_opts_t	filtopts;
196 	opt_proto_t	optproto;
197 	rule_group_t	rulegroup;
198 }
199 
200 %%
201 
202 input
203 	: { CHECK_PARSER_FILE	} lines
204 	| { CHECK_PARSER_STRING	} rule
205 	;
206 
207 lines
208 	: lines SEPLINE line
209 	| line
210 	;
211 
212 line
213 	: vardef
214 	| table
215 	| map
216 	| group
217 	| rproc
218 	| alg
219 	| set
220 	|
221 	;
222 
223 alg
224 	: ALG STRING
225 	{
226 		npfctl_build_alg($2);
227 	}
228 	;
229 
230 onoff
231 	: ON {
232 		$$ = true;
233 	}
234 	| OFF {
235 		$$ = false;
236 	}
237 	;
238 
239 set
240 	: SET BPFJIT onoff {
241 		npfctl_bpfjit($3);
242 	}
243 	;
244 
245 /*
246  * A value - an element or a list of elements.
247  * Can be assigned to a variable or used inline.
248  */
249 
250 vardef
251 	: VAR_ID EQ value
252 	{
253 		npfvar_add($3, $1);
254 	}
255 	;
256 
257 value
258 	: element
259 	| list
260 	;
261 
262 list
263 	: CURLY_OPEN list_elems CURLY_CLOSE
264 	{
265 		$$ = $2;
266 	}
267 	;
268 
269 list_elems
270 	: list_elems COMMA element
271 	{
272 		npfvar_add_elements($1, $3);
273 	}
274 	| element
275 	;
276 
277 element
278 	: IDENTIFIER
279 	{
280 		$$ = npfvar_create_from_string(NPFVAR_IDENTIFIER, $1);
281 	}
282 	| STRING
283 	{
284 		$$ = npfvar_create_from_string(NPFVAR_STRING, $1);
285 	}
286 	| number MINUS number
287 	{
288 		$$ = npfctl_parse_port_range($1, $3);
289 	}
290 	| number
291 	{
292 		$$ = npfvar_create_element(NPFVAR_NUM, &$1, sizeof($1));
293 	}
294 	| VAR_ID
295 	{
296 		$$ = npfvar_create_from_string(NPFVAR_VAR_ID, $1);
297 	}
298 	| TABLE_ID		{ $$ = npfctl_parse_table_id($1); }
299 	| dynamic_ifaddrs	{ $$ = npfctl_ifnet_table($1); }
300 	| static_ifaddrs	{ $$ = $1; }
301 	| addr_and_mask		{ $$ = $1; }
302 	;
303 
304 /*
305  * Table definition.
306  */
307 
308 table
309 	: TABLE TABLE_ID TYPE table_type table_store
310 	{
311 		npfctl_build_table($2, $4, $5);
312 	}
313 	;
314 
315 table_type
316 	: HASH		{ $$ = NPF_TABLE_HASH; }
317 	| TREE		{ $$ = NPF_TABLE_TREE; }
318 	| CDB		{ $$ = NPF_TABLE_CDB; }
319 	;
320 
321 table_store
322 	: TDYNAMIC	{ $$ = NULL; }
323 	| TFILE STRING	{ $$ = $2; }
324 	;
325 
326 /*
327  * Map definition.
328  */
329 
330 map_sd
331 	: TSTATIC	{ $$ = NPFCTL_NAT_STATIC; }
332 	| TDYNAMIC	{ $$ = NPFCTL_NAT_DYNAMIC; }
333 	|		{ $$ = NPFCTL_NAT_DYNAMIC; }
334 	;
335 
336 map_algo
337 	: ALGO NPT66	{ $$ = NPF_ALGO_NPT66; }
338 	|		{ $$ = 0; }
339 	;
340 
341 map_flags
342 	: NO_PORTS	{ $$ = NPF_NAT_PORTS; }
343 	|		{ $$ = 0; }
344 	;
345 
346 map_type
347 	: ARROWBOTH	{ $$ = NPF_NATIN | NPF_NATOUT; }
348 	| ARROWLEFT	{ $$ = NPF_NATIN; }
349 	| ARROWRIGHT	{ $$ = NPF_NATOUT; }
350 	;
351 
352 mapseg
353 	: addr_or_ifaddr port_range
354 	{
355 		$$.ap_netaddr = $1;
356 		$$.ap_portrange = $2;
357 	}
358 	;
359 
360 map
361 	: MAP ifref map_sd map_algo map_flags mapseg map_type mapseg
362 	  PASS opt_proto all_or_filt_opts
363 	{
364 		npfctl_build_natseg($3, $7, $5, $2, &$6, &$8, &$10, &$11, $4);
365 	}
366 	| MAP ifref map_sd map_algo map_flags mapseg map_type mapseg
367 	{
368 		npfctl_build_natseg($3, $7, $5, $2, &$6, &$8, NULL, NULL, $4);
369 	}
370 	| MAP ifref map_sd map_algo map_flags proto mapseg map_type mapseg
371 	{
372 		npfctl_build_natseg($3, $8, $5, $2, &$7, &$9, &$6, NULL, $4);
373 	}
374 	| MAP RULESET group_opts
375 	{
376 		npfctl_build_maprset($3.rg_name, $3.rg_attr, $3.rg_ifname);
377 	}
378 	;
379 
380 /*
381  * Rule procedure definition and its parameters.
382  */
383 
384 rproc
385 	: PROCEDURE STRING CURLY_OPEN procs CURLY_CLOSE
386 	{
387 		npfctl_build_rproc($2, $4);
388 	}
389 	;
390 
391 procs
392 	: procs SEPLINE proc_call
393 	{
394 		$$ = npfvar_add_elements($1, $3);
395 	}
396 	| proc_call	{ $$ = $1; }
397 	;
398 
399 proc_call
400 	: IDENTIFIER COLON proc_param_list
401 	{
402 		proc_call_t pc;
403 
404 		pc.pc_name = estrdup($1);
405 		pc.pc_opts = $3;
406 
407 		$$ = npfvar_create_element(NPFVAR_PROC, &pc, sizeof(pc));
408 	}
409 	|		{ $$ = NULL; }
410 	;
411 
412 proc_param_list
413 	: proc_param_list COMMA proc_param
414 	{
415 		$$ = npfvar_add_elements($1, $3);
416 	}
417 	| proc_param	{ $$ = $1; }
418 	|		{ $$ = NULL; }
419 	;
420 
421 proc_param
422 	: some_name proc_param_val
423 	{
424 		proc_param_t pp;
425 
426 		pp.pp_param = estrdup($1);
427 		pp.pp_value = $2 ? estrdup($2) : NULL;
428 
429 		$$ = npfvar_create_element(NPFVAR_PROC_PARAM, &pp, sizeof(pp));
430 	}
431 	;
432 
433 proc_param_val
434 	: some_name	{ $$ = $1; }
435 	| number	{ (void)asprintf(&$$, "%ld", $1); }
436 	| FPNUM		{ (void)asprintf(&$$, "%lf", $1); }
437 	|		{ $$ = NULL; }
438 	;
439 
440 /*
441  * Group and dynamic ruleset definition.
442  */
443 
444 group
445 	: GROUP group_opts
446 	{
447 		/* Build a group.  Increase the nesting level. */
448 		npfctl_build_group($2.rg_name, $2.rg_attr,
449 		    $2.rg_ifname, $2.rg_default);
450 	}
451 	  ruleset_block
452 	{
453 		/* Decrease the nesting level. */
454 		npfctl_build_group_end();
455 	}
456 	;
457 
458 ruleset
459 	: RULESET group_opts
460 	{
461 		/* Ruleset is a dynamic group. */
462 		npfctl_build_group($2.rg_name, $2.rg_attr | NPF_RULE_DYNAMIC,
463 		    $2.rg_ifname, $2.rg_default);
464 		npfctl_build_group_end();
465 	}
466 	;
467 
468 group_dir
469 	: FORW		{ $$ = NPF_RULE_FORW; }
470 	| rule_dir
471 	;
472 
473 group_opts
474 	: DEFAULT
475 	{
476 		memset(&$$, 0, sizeof(rule_group_t));
477 		$$.rg_default = true;
478 	}
479 	| STRING group_dir on_ifname
480 	{
481 		memset(&$$, 0, sizeof(rule_group_t));
482 		$$.rg_name = $1;
483 		$$.rg_attr = $2;
484 		$$.rg_ifname = $3;
485 	}
486 	;
487 
488 ruleset_block
489 	: CURLY_OPEN ruleset_def CURLY_CLOSE
490 	;
491 
492 ruleset_def
493 	: ruleset_def SEPLINE rule_group
494 	| rule_group
495 	;
496 
497 rule_group
498 	: rule
499 	| group
500 	| ruleset
501 	|
502 	;
503 
504 /*
505  * Rule and misc.
506  */
507 
508 rule
509 	: block_or_pass opt_stateful rule_dir opt_final on_ifname
510 	  opt_family opt_proto all_or_filt_opts opt_apply
511 	{
512 		npfctl_build_rule($1 | $2 | $3 | $4, $5,
513 		    $6, &$7, &$8, NULL, $9);
514 	}
515 	| block_or_pass opt_stateful rule_dir opt_final on_ifname
516 	  PCAP_FILTER STRING opt_apply
517 	{
518 		npfctl_build_rule($1 | $2 | $3 | $4, $5,
519 		    AF_UNSPEC, NULL, NULL, $7, $8);
520 	}
521 	;
522 
523 block_or_pass
524 	: BLOCK block_opts	{ $$ = $2; }
525 	| PASS			{ $$ = NPF_RULE_PASS; }
526 	;
527 
528 rule_dir
529 	: IN			{ $$ = NPF_RULE_IN; }
530 	| OUT			{ $$ = NPF_RULE_OUT; }
531 	|			{ $$ = NPF_RULE_IN | NPF_RULE_OUT; }
532 	;
533 
534 opt_final
535 	: FINAL			{ $$ = NPF_RULE_FINAL; }
536 	|			{ $$ = 0; }
537 	;
538 
539 on_ifname
540 	: ON ifref		{ $$ = $2; }
541 	|			{ $$ = NULL; }
542 	;
543 
544 afamily
545 	: INET4			{ $$ = AF_INET; }
546 	| INET6			{ $$ = AF_INET6; }
547 	;
548 
549 maybe_not
550 	: EXCL_MARK		{ $$ = true; }
551 	|			{ $$ = false; }
552 	;
553 
554 opt_family
555 	: FAMILY afamily	{ $$ = $2; }
556 	|			{ $$ = AF_UNSPEC; }
557 	;
558 
559 proto
560 	: PROTO TCP tcp_flags_and_mask
561 	{
562 		$$.op_proto = IPPROTO_TCP;
563 		$$.op_opts = $3;
564 	}
565 	| PROTO ICMP icmp_type_and_code
566 	{
567 		$$.op_proto = IPPROTO_ICMP;
568 		$$.op_opts = $3;
569 	}
570 	| PROTO ICMP6 icmp_type_and_code
571 	{
572 		$$.op_proto = IPPROTO_ICMPV6;
573 		$$.op_opts = $3;
574 	}
575 	| PROTO some_name
576 	{
577 		$$.op_proto = npfctl_protono($2);
578 		$$.op_opts = NULL;
579 	}
580 	| PROTO number
581 	{
582 		$$.op_proto = $2;
583 		$$.op_opts = NULL;
584 	}
585 	;
586 
587 opt_proto
588 	: proto			{ $$ = $1; }
589 	|
590 	{
591 		$$.op_proto = -1;
592 		$$.op_opts = NULL;
593 	}
594 	;
595 
596 all_or_filt_opts
597 	: ALL
598 	{
599 		$$.fo_finvert = false;
600 		$$.fo_from.ap_netaddr = NULL;
601 		$$.fo_from.ap_portrange = NULL;
602 		$$.fo_tinvert = false;
603 		$$.fo_to.ap_netaddr = NULL;
604 		$$.fo_to.ap_portrange = NULL;
605 	}
606 	| filt_opts	{ $$ = $1; }
607 	;
608 
609 opt_stateful
610 	: STATEFUL	{ $$ = NPF_RULE_STATEFUL; }
611 	| STATEFUL_ENDS	{ $$ = NPF_RULE_STATEFUL | NPF_RULE_MULTIENDS; }
612 	|		{ $$ = 0; }
613 	;
614 
615 opt_apply
616 	: APPLY STRING	{ $$ = $2; }
617 	|		{ $$ = NULL; }
618 	;
619 
620 block_opts
621 	: RETURNRST	{ $$ = NPF_RULE_RETRST; }
622 	| RETURNICMP	{ $$ = NPF_RULE_RETICMP; }
623 	| RETURN	{ $$ = NPF_RULE_RETRST | NPF_RULE_RETICMP; }
624 	|		{ $$ = 0; }
625 	;
626 
627 filt_opts
628 	: FROM maybe_not filt_addr port_range TO maybe_not filt_addr port_range
629 	{
630 		$$.fo_finvert = $2;
631 		$$.fo_from.ap_netaddr = $3;
632 		$$.fo_from.ap_portrange = $4;
633 		$$.fo_tinvert = $6;
634 		$$.fo_to.ap_netaddr = $7;
635 		$$.fo_to.ap_portrange = $8;
636 	}
637 	| FROM maybe_not filt_addr port_range
638 	{
639 		$$.fo_finvert = $2;
640 		$$.fo_from.ap_netaddr = $3;
641 		$$.fo_from.ap_portrange = $4;
642 		$$.fo_tinvert = false;
643 		$$.fo_to.ap_netaddr = NULL;
644 		$$.fo_to.ap_portrange = NULL;
645 	}
646 	| TO maybe_not filt_addr port_range
647 	{
648 		$$.fo_finvert = false;
649 		$$.fo_from.ap_netaddr = NULL;
650 		$$.fo_from.ap_portrange = NULL;
651 		$$.fo_tinvert = $2;
652 		$$.fo_to.ap_netaddr = $3;
653 		$$.fo_to.ap_portrange = $4;
654 	}
655 	;
656 
657 filt_addr
658 	: list			{ $$ = $1; }
659 	| addr_or_ifaddr	{ $$ = $1; }
660 	| dynamic_ifaddrs	{ $$ = npfctl_ifnet_table($1); }
661 	| TABLE_ID		{ $$ = npfctl_parse_table_id($1); }
662 	| ANY			{ $$ = NULL; }
663 	;
664 
665 addr_and_mask
666 	: addr SLASH number
667 	{
668 		$$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
669 	}
670 	| addr SLASH addr
671 	{
672 		$$ = npfctl_parse_fam_addr_mask($1, $3, NULL);
673 	}
674 	| addr
675 	{
676 		$$ = npfctl_parse_fam_addr_mask($1, NULL, NULL);
677 	}
678 	;
679 
680 addr_or_ifaddr
681 	: addr_and_mask
682 	{
683 		assert($1 != NULL);
684 		$$ = $1;
685 	}
686 	| static_ifaddrs
687 	{
688 		if (npfvar_get_count($1) != 1)
689 			yyerror("multiple interfaces are not supported");
690 		ifnet_addr_t *ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0);
691 		$$ = ifna->ifna_addrs;
692 	}
693 	| VAR_ID
694 	{
695 		npfvar_t *vp = npfvar_lookup($1);
696 		int type = npfvar_get_type(vp, 0);
697 		ifnet_addr_t *ifna;
698 
699 again:
700 		switch (type) {
701 		case NPFVAR_IDENTIFIER:
702 		case NPFVAR_STRING:
703 			vp = npfctl_parse_ifnet(npfvar_expand_string(vp),
704 			    AF_UNSPEC);
705 			type = npfvar_get_type(vp, 0);
706 			goto again;
707 		case NPFVAR_FAM:
708 		case NPFVAR_TABLE:
709 			$$ = vp;
710 			break;
711 		case NPFVAR_INTERFACE:
712 			$$ = NULL;
713 			for (u_int i = 0; i < npfvar_get_count(vp); i++) {
714 				ifna = npfvar_get_data(vp, type, i);
715 				$$ = npfvar_add_elements($$, ifna->ifna_addrs);
716 			}
717 			break;
718 		case -1:
719 			yyerror("undefined variable '%s'", $1);
720 			break;
721 		default:
722 			yyerror("wrong variable '%s' type '%s' for address "
723 			    "or interface", $1, npfvar_type(type));
724 			break;
725 		}
726 	}
727 	;
728 
729 addr
730 	: IPV4ADDR	{ $$ = $1; }
731 	| IPV6ADDR	{ $$ = $1; }
732 	;
733 
734 port_range
735 	: PORT port		/* just port */
736 	{
737 		$$ = npfctl_parse_port_range($2, $2);
738 	}
739 	| PORT port MINUS port	/* port from-to */
740 	{
741 		$$ = npfctl_parse_port_range($2, $4);
742 	}
743 	| PORT VAR_ID
744 	{
745 		npfvar_t *vp = npfvar_lookup($2);
746 		$$ = npfctl_parse_port_range_variable($2, vp);
747 	}
748 	| PORT list
749 	{
750 		$$ = npfctl_parse_port_range_variable(NULL, $2);
751 	}
752 	|			{ $$ = NULL; }
753 	;
754 
755 port
756 	: number	{ $$ = $1; }
757 	| IDENTIFIER	{ $$ = npfctl_portno($1); }
758 	| STRING	{ $$ = npfctl_portno($1); }
759 	;
760 
761 icmp_type_and_code
762 	: ICMPTYPE icmp_type
763 	{
764 		$$ = npfctl_parse_icmp($<num>0, $2, -1);
765 	}
766 	| ICMPTYPE icmp_type CODE number
767 	{
768 		$$ = npfctl_parse_icmp($<num>0, $2, $4);
769 	}
770 	| ICMPTYPE icmp_type CODE IDENTIFIER
771 	{
772 		$$ = npfctl_parse_icmp($<num>0, $2,
773 		    npfctl_icmpcode($<num>0, $2, $4));
774 	}
775 	| ICMPTYPE icmp_type CODE VAR_ID
776 	{
777 		char *s = npfvar_expand_string(npfvar_lookup($4));
778 		$$ = npfctl_parse_icmp($<num>0, $2,
779 		    npfctl_icmpcode($<num>0, $2, s));
780 	}
781 	|		{ $$ = NULL; }
782 	;
783 
784 tcp_flags_and_mask
785 	: FLAGS tcp_flags SLASH tcp_flags
786 	{
787 		npfvar_add_elements($2, $4);
788 		$$ = $2;
789 	}
790 	| FLAGS tcp_flags
791 	{
792 		if (npfvar_get_count($2) != 1)
793 			yyerror("multiple tcpflags are not supported");
794 		char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0);
795 		npfvar_add_elements($2, npfctl_parse_tcpflag(s));
796 		$$ = $2;
797 	}
798 	|		{ $$ = NULL; }
799 	;
800 
801 tcp_flags
802 	: IDENTIFIER	{ $$ = npfctl_parse_tcpflag($1); }
803 	;
804 
805 icmp_type
806 	: number	{ $$ = $1; }
807 	| IDENTIFIER	{ $$ = npfctl_icmptype($<num>-1, $1); }
808 	| VAR_ID
809 	{
810 		char *s = npfvar_expand_string(npfvar_lookup($1));
811 		$$ = npfctl_icmptype($<num>-1, s);
812 	}
813 	;
814 
815 ifname
816 	: some_name
817 	{
818 		npfctl_note_interface($1);
819 		$$ = $1;
820 	}
821 	| VAR_ID
822 	{
823 		npfvar_t *vp = npfvar_lookup($1);
824 		const int type = npfvar_get_type(vp, 0);
825 		ifnet_addr_t *ifna;
826 
827 		switch (type) {
828 		case NPFVAR_STRING:
829 		case NPFVAR_IDENTIFIER:
830 			$$ = npfvar_expand_string(vp);
831 			break;
832 		case NPFVAR_INTERFACE:
833 			if (npfvar_get_count(vp) != 1)
834 				yyerror(
835 				    "multiple interfaces are not supported");
836 			ifna = npfvar_get_data(vp, type, 0);
837 			$$ = ifna->ifna_name;
838 			break;
839 		case -1:
840 			yyerror("undefined variable '%s' for interface", $1);
841 			break;
842 		default:
843 			yyerror("wrong variable '%s' type '%s' for interface",
844 			    $1, npfvar_type(type));
845 			break;
846 		}
847 		npfctl_note_interface($$);
848 	}
849 	;
850 
851 static_ifaddrs
852 	: afamily PAR_OPEN ifname PAR_CLOSE
853 	{
854 		$$ = npfctl_parse_ifnet($3, $1);
855 	}
856 	;
857 
858 dynamic_ifaddrs
859 	: IFADDRS PAR_OPEN ifname PAR_CLOSE
860 	{
861 		$$ = $3;
862 	}
863 	;
864 
865 ifref
866 	: ifname
867 	| dynamic_ifaddrs
868 	| static_ifaddrs
869 	{
870 		if (npfvar_get_count($1) != 1)
871 			yyerror("multiple interfaces are not supported");
872 		ifnet_addr_t *ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0);
873 		npfctl_note_interface(ifna->ifna_name);
874 		$$ = ifna->ifna_name;
875 	}
876 	;
877 
878 number
879 	: HEX		{ $$ = $1; }
880 	| NUM		{ $$ = $1; }
881 	;
882 
883 some_name
884 	: IDENTIFIER	{ $$ = $1; }
885 	| STRING	{ $$ = $1; }
886 	;
887 
888 %%
889