xref: /netbsd-src/usr.sbin/npf/npfctl/npf_parse.y (revision 82ad575716605df31379cf04a2f3efbc97b8a6f5)
1 /*	$NetBSD: npf_parse.y,v 1.14 2012/10/02 23:38:52 rmind Exp $	*/
2 
3 /*-
4  * Copyright (c) 2011-2012 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 and Christos Zoulas.
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 const char *		yyfilename;
44 
45 extern int		yylineno, yycolumn;
46 extern int		yylex(void);
47 
48 /* Variable under construction (bottom up). */
49 static npfvar_t *	cvar;
50 
51 void
52 yyerror(const char *fmt, ...)
53 {
54 	extern int yyleng;
55 	extern char *yytext;
56 
57 	char *msg, *context = xstrndup(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 		size_t len = strlen(context);
69 		char *dst = zalloc(len * 4 + 1);
70 
71 		strvisx(dst, context, len, VIS_WHITE|VIS_CSTYLE);
72 		fprintf(stderr, " near '%s'", dst);
73 	}
74 	fprintf(stderr, "\n");
75 	exit(EXIT_FAILURE);
76 }
77 
78 %}
79 
80 %token			ALL
81 %token			ANY
82 %token			APPLY
83 %token			ARROWBOTH
84 %token			ARROWLEFT
85 %token			ARROWRIGHT
86 %token			BLOCK
87 %token			CURLY_CLOSE
88 %token			CURLY_OPEN
89 %token			CODE
90 %token			COLON
91 %token			COMMA
92 %token			DEFAULT
93 %token			TDYNAMIC
94 %token			TSTATIC
95 %token			EQ
96 %token			TFILE
97 %token			FLAGS
98 %token			FROM
99 %token			GROUP
100 %token			HASH
101 %token			ICMPTYPE
102 %token			ID
103 %token			IN
104 %token			INET
105 %token			INET6
106 %token			INTERFACE
107 %token			MAP
108 %token			MINUS
109 %token			NAME
110 %token			ON
111 %token			OUT
112 %token			PAR_CLOSE
113 %token			PAR_OPEN
114 %token			PASS
115 %token			PORT
116 %token			PROCEDURE
117 %token			PROTO
118 %token			FAMILY
119 %token			FINAL
120 %token			RETURN
121 %token			RETURNICMP
122 %token			RETURNRST
123 %token			SEPLINE
124 %token			SLASH
125 %token			STATEFUL
126 %token			TABLE
127 %token			TCP
128 %token			TO
129 %token			TREE
130 %token			TYPE
131 %token	<num>		ICMP
132 %token	<num>		ICMP6
133 
134 %token	<num>		HEX
135 %token	<str>		IDENTIFIER
136 %token	<str>		IPV4ADDR
137 %token	<str>		IPV6ADDR
138 %token	<num>		NUM
139 %token	<fpnum>		FPNUM
140 %token	<str>		STRING
141 %token	<str>		TABLE_ID
142 %token	<str>		VAR_ID
143 
144 %type	<str>		addr, some_name, list_elem, table_store
145 %type	<str>		proc_param_val, opt_apply
146 %type	<num>		ifindex, port, opt_final, on_iface
147 %type	<num>		block_or_pass, rule_dir, block_opts, opt_family
148 %type	<num>		opt_stateful, icmp_type, table_type, map_sd, map_type
149 %type	<var>		addr_or_iface, port_range, icmp_type_and_code
150 %type	<var>		filt_addr, addr_and_mask, tcp_flags, tcp_flags_and_mask
151 %type	<var>		procs, proc_call, proc_param_list, proc_param
152 %type	<addrport>	mapseg
153 %type	<filtopts>	filt_opts, all_or_filt_opts
154 %type	<optproto>	opt_proto
155 %type	<rulegroup>	group_attr, group_opt
156 
157 %union {
158 	char *		str;
159 	unsigned long	num;
160 	double		fpnum;
161 	addr_port_t	addrport;
162 	filt_opts_t	filtopts;
163 	npfvar_t *	var;
164 	opt_proto_t	optproto;
165 	rule_group_t	rulegroup;
166 }
167 
168 %%
169 
170 input
171 	: lines
172 	;
173 
174 lines
175 	: line SEPLINE lines
176 	| line
177 	;
178 
179 line
180 	: def
181 	| table
182 	| map
183 	| group
184 	| rproc
185 	|
186 	;
187 
188 def
189 	: VAR_ID
190 	{
191 		cvar = npfvar_create($1);
192 		npfvar_add(cvar);
193 	}
194 	  EQ definition
195 	{
196 		cvar = NULL;
197 	}
198 	;
199 
200 definition
201 	: list_elem
202 	| listdef
203 	;
204 
205 listdef
206 	: CURLY_OPEN list_elems CURLY_CLOSE
207 	;
208 
209 list_elems
210 	: list_elem COMMA list_elems
211 	| list_elem
212 	;
213 
214 list_elem
215 	: IDENTIFIER
216 	{
217 		npfvar_t *vp = npfvar_create(".identifier");
218 		npfvar_add_element(vp, NPFVAR_IDENTIFIER, $1, strlen($1) + 1);
219 		npfvar_add_elements(cvar, vp);
220 	}
221 	| STRING
222 	{
223 		npfvar_t *vp = npfvar_create(".string");
224 		npfvar_add_element(vp, NPFVAR_STRING, $1, strlen($1) + 1);
225 		npfvar_add_elements(cvar, vp);
226 	}
227 	| NUM MINUS NUM
228 	{
229 		npfvar_t *vp = npfctl_parse_port_range($1, $3);
230 		npfvar_add_elements(cvar, vp);
231 	}
232 	| NUM
233 	{
234 		npfvar_t *vp = npfvar_create(".num");
235 		npfvar_add_element(vp, NPFVAR_NUM, &$1, sizeof($1));
236 		npfvar_add_elements(cvar, vp);
237 	}
238 	| VAR_ID
239 	{
240 		npfvar_t *vp = npfvar_create(".var_id");
241 		npfvar_add_element(vp, NPFVAR_VAR_ID, $1, strlen($1) + 1);
242 		npfvar_add_elements(cvar, vp);
243 	}
244 	| addr_and_mask
245 	{
246 		npfvar_add_elements(cvar, $1);
247 	}
248 	;
249 
250 table
251 	: TABLE TABLE_ID TYPE table_type table_store
252 	{
253 		npfctl_build_table($2, $4, $5);
254 	}
255 	;
256 
257 table_type
258 	: HASH		{ $$ = NPF_TABLE_HASH; }
259 	| TREE		{ $$ = NPF_TABLE_TREE; }
260 	;
261 
262 table_store
263 	: TDYNAMIC	{ $$ = NULL; }
264 	| TFILE STRING	{ $$ = $2; }
265 	;
266 
267 map_sd
268 	: TSTATIC	{ $$ = NPFCTL_NAT_STATIC; }
269 	| TDYNAMIC	{ $$ = NPFCTL_NAT_DYNAMIC; }
270 	|		{ $$ = NPFCTL_NAT_DYNAMIC; }
271 	;
272 
273 map_type
274 	: ARROWBOTH	{ $$ = NPF_NATIN | NPF_NATOUT; }
275 	| ARROWLEFT	{ $$ = NPF_NATIN; }
276 	| ARROWRIGHT	{ $$ = NPF_NATOUT; }
277 	;
278 
279 mapseg
280 	: addr_or_iface port_range
281 	{
282 		$$.ap_netaddr = $1;
283 		$$.ap_portrange = $2;
284 	}
285 	;
286 
287 map
288 	: MAP ifindex map_sd mapseg map_type mapseg PASS filt_opts
289 	{
290 		npfctl_build_natseg($3, $5, $2, &$4, &$6, &$8);
291 	}
292 	| MAP ifindex map_sd mapseg map_type mapseg
293 	{
294 		npfctl_build_natseg($3, $5, $2, &$4, &$6, NULL);
295 	}
296 	;
297 
298 rproc
299 	: PROCEDURE STRING CURLY_OPEN procs CURLY_CLOSE
300 	{
301 		npfctl_build_rproc($2, $4);
302 	}
303 	;
304 
305 procs
306 	: proc_call SEPLINE procs
307 	{
308 		$$ = npfvar_add_elements($1, $3);
309 	}
310 	| proc_call	{ $$ = $1; }
311 	;
312 
313 proc_call
314 	: IDENTIFIER COLON proc_param_list
315 	{
316 		proc_call_t pc;
317 
318 		pc.pc_name = xstrdup($1);
319 		pc.pc_opts = $3;
320 		$$ = npfvar_create(".proc_call");
321 		npfvar_add_element($$, NPFVAR_PROC, &pc, sizeof(pc));
322 	}
323 	|	{ $$ = NULL; }
324 	;
325 
326 proc_param_list
327 	: proc_param COMMA proc_param_list
328 	{
329 		$$ = npfvar_add_elements($1, $3);
330 	}
331 	| proc_param	{ $$ = $1; }
332 	|		{ $$ = NULL; }
333 	;
334 
335 proc_param
336 	/* Key and value pair. */
337 	: some_name proc_param_val
338 	{
339 		proc_param_t pp;
340 
341 		pp.pp_param = xstrdup($1);
342 		pp.pp_value = $2 ? xstrdup($2) : NULL;
343 		$$ = npfvar_create(".proc_param");
344 		npfvar_add_element($$, NPFVAR_PROC_PARAM, &pp, sizeof(pp));
345 	}
346 	;
347 
348 proc_param_val
349 	: some_name	{ $$ = $1; }
350 	| NUM		{ (void)asprintf(&$$, "%ld", $1); }
351 	| FPNUM		{ (void)asprintf(&$$, "%lf", $1); }
352 	|		{ $$ = NULL; }
353 	;
354 
355 group
356 	: GROUP PAR_OPEN group_attr PAR_CLOSE
357 	{
358 		npfctl_build_group($3.rg_name, $3.rg_attr, $3.rg_ifnum);
359 	}
360 	  ruleset
361 	;
362 
363 group_attr
364 	: group_opt COMMA group_attr
365 	{
366 		$$ = $3;
367 
368 		if (($1.rg_name && $$.rg_name) ||
369 		    ($1.rg_ifnum && $$.rg_ifnum) ||
370 		    ($1.rg_attr & $$.rg_attr) != 0)
371 			yyerror("duplicate group option");
372 
373 		if ($1.rg_name) {
374 			$$.rg_name = $1.rg_name;
375 		}
376 		if ($1.rg_attr) {
377 			$$.rg_attr |= $1.rg_attr;
378 		}
379 		if ($1.rg_ifnum) {
380 			$$.rg_ifnum = $1.rg_ifnum;
381 		}
382 	}
383 	| group_opt		{ $$ = $1; }
384 	;
385 
386 group_opt
387 	: DEFAULT
388 	{
389 		$$.rg_name = NULL;
390 		$$.rg_ifnum = 0;
391 		$$.rg_attr = NPF_RULE_DEFAULT;
392 	}
393 	| NAME STRING
394 	{
395 		$$.rg_name = $2;
396 		$$.rg_ifnum = 0;
397 		$$.rg_attr = 0;
398 	}
399 	| INTERFACE ifindex
400 	{
401 		$$.rg_name = NULL;
402 		$$.rg_ifnum = $2;
403 		$$.rg_attr = 0;
404 	}
405 	| rule_dir
406 	{
407 		$$.rg_name = NULL;
408 		$$.rg_ifnum = 0;
409 		$$.rg_attr = $1;
410 	}
411 	;
412 
413 ruleset
414 	: CURLY_OPEN rules CURLY_CLOSE
415 	;
416 
417 rules
418 	: rule SEPLINE rules
419 	| rule
420 	;
421 
422 rule
423 	: block_or_pass opt_stateful rule_dir opt_final on_iface opt_family
424 	  opt_proto all_or_filt_opts opt_apply
425 	{
426 		/*
427 		 * Arguments: attributes, interface index, address
428 		 * family, protocol options, filter options.
429 		 */
430 		npfctl_build_rule($1 | $2 | $3 | $4, $5,
431 		    $6, &$7, &$8, $9);
432 	}
433 	|
434 	;
435 
436 block_or_pass
437 	: BLOCK block_opts	{ $$ = $2; }
438 	| PASS			{ $$ = NPF_RULE_PASS; }
439 	;
440 
441 rule_dir
442 	: IN			{ $$ = NPF_RULE_IN; }
443 	| OUT			{ $$ = NPF_RULE_OUT; }
444 	|			{ $$ = NPF_RULE_IN | NPF_RULE_OUT; }
445 	;
446 
447 opt_final
448 	: FINAL			{ $$ = NPF_RULE_FINAL; }
449 	|			{ $$ = 0; }
450 	;
451 
452 on_iface
453 	: ON ifindex		{ $$ = $2; }
454 	|			{ $$ = 0; }
455 	;
456 
457 opt_family
458 	: FAMILY INET		{ $$ = AF_INET; }
459 	| FAMILY INET6		{ $$ = AF_INET6; }
460 	|			{ $$ = AF_UNSPEC; }
461 	;
462 
463 opt_proto
464 	: PROTO TCP tcp_flags_and_mask
465 	{
466 		$$.op_proto = IPPROTO_TCP;
467 		$$.op_opts = $3;
468 	}
469 	| PROTO ICMP icmp_type_and_code
470 	{
471 		$$.op_proto = IPPROTO_ICMP;
472 		$$.op_opts = $3;
473 	}
474 	| PROTO ICMP6 icmp_type_and_code
475 	{
476 		$$.op_proto = IPPROTO_ICMPV6;
477 		$$.op_opts = $3;
478 	}
479 	| PROTO some_name
480 	{
481 		$$.op_proto = npfctl_protono($2);
482 		$$.op_opts = NULL;
483 	}
484 	| PROTO NUM
485 	{
486 		$$.op_proto = $2;
487 		$$.op_opts = NULL;
488 	}
489 	|
490 	{
491 		$$.op_proto = -1;
492 		$$.op_opts = NULL;
493 	}
494 	;
495 
496 all_or_filt_opts
497 	: ALL
498 	{
499 		$$.fo_from.ap_netaddr = NULL;
500 		$$.fo_from.ap_portrange = NULL;
501 		$$.fo_to.ap_netaddr = NULL;
502 		$$.fo_to.ap_portrange = NULL;
503 	}
504 	| filt_opts	{ $$ = $1; }
505 	;
506 
507 opt_stateful
508 	: STATEFUL	{ $$ = NPF_RULE_STATEFUL; }
509 	|		{ $$ = 0; }
510 	;
511 
512 opt_apply
513 	: APPLY STRING	{ $$ = $2; }
514 	|		{ $$ = NULL; }
515 	;
516 
517 block_opts
518 	: RETURNRST	{ $$ = NPF_RULE_RETRST; }
519 	| RETURNICMP	{ $$ = NPF_RULE_RETICMP; }
520 	| RETURN	{ $$ = NPF_RULE_RETRST | NPF_RULE_RETICMP; }
521 	|		{ $$ = 0; }
522 	;
523 
524 filt_opts
525 	: FROM filt_addr port_range TO filt_addr port_range
526 	{
527 		$$.fo_from.ap_netaddr = $2;
528 		$$.fo_from.ap_portrange = $3;
529 		$$.fo_to.ap_netaddr = $5;
530 		$$.fo_to.ap_portrange = $6;
531 	}
532 	| FROM filt_addr port_range
533 	{
534 		$$.fo_from.ap_netaddr = $2;
535 		$$.fo_from.ap_portrange = $3;
536 		$$.fo_to.ap_netaddr = NULL;
537 		$$.fo_to.ap_portrange = NULL;
538 	}
539 	| TO filt_addr port_range
540 	{
541 		$$.fo_from.ap_netaddr = NULL;
542 		$$.fo_from.ap_portrange = NULL;
543 		$$.fo_to.ap_netaddr = $2;
544 		$$.fo_to.ap_portrange = $3;
545 	}
546 	;
547 
548 filt_addr
549 	: addr_or_iface		{ $$ = $1; }
550 	| TABLE_ID		{ $$ = npfctl_parse_table_id($1); }
551 	| ANY			{ $$ = NULL; }
552 	;
553 
554 addr_and_mask
555 	: addr SLASH NUM
556 	{
557 		$$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
558 	}
559 	| addr SLASH HEX
560 	{
561 		$$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
562 	}
563 	| addr SLASH addr
564 	{
565 		$$ = npfctl_parse_fam_addr_mask($1, $3, NULL);
566 	}
567 	| addr
568 	{
569 		$$ = npfctl_parse_fam_addr_mask($1, NULL, NULL);
570 	}
571 	;
572 
573 addr_or_iface
574 	: addr_and_mask
575 	{
576 		assert($1 != NULL);
577 		$$ = $1;
578 	}
579 	| some_name
580 	{
581 		$$ = npfctl_parse_iface($1);
582 	}
583 	| VAR_ID
584 	{
585 		npfvar_t *vp = npfvar_lookup($1);
586 		const int type = npfvar_get_type(vp, 0);
587 
588 		switch (type) {
589 		case NPFVAR_VAR_ID:
590 		case NPFVAR_STRING:
591 			$$ = npfctl_parse_iface(npfvar_expand_string(vp));
592 			break;
593 		case NPFVAR_FAM:
594 			$$ = vp;
595 			break;
596 		case -1:
597 			yyerror("undefined variable '%s' for interface", $1);
598 			break;
599 		default:
600 			yyerror("wrong variable '%s' type '%s' or interface",
601 			    $1, npfvar_type(type));
602 			$$ = NULL;
603 			break;
604 		}
605 	}
606 	;
607 
608 addr
609 	: IPV4ADDR	{ $$ = $1; }
610 	| IPV6ADDR	{ $$ = $1; }
611 	;
612 
613 
614 port_range
615 	: PORT port		/* just port */
616 	{
617 		$$ = npfctl_parse_port_range($2, $2);
618 	}
619 	| PORT port MINUS port	/* port from-to */
620 	{
621 		$$ = npfctl_parse_port_range($2, $4);
622 	}
623 	| PORT VAR_ID
624 	{
625 		$$ = npfctl_parse_port_range_variable($2);
626 	}
627 	|
628 	{
629 		$$ = NULL;
630 	}
631 	;
632 
633 port
634 	: NUM		{ $$ = $1; }
635 	| IDENTIFIER	{ $$ = npfctl_portno($1); }
636 	;
637 
638 icmp_type_and_code
639 	: ICMPTYPE icmp_type
640 	{
641 		$$ = npfctl_parse_icmp($<num>0, $2, -1);
642 	}
643 	| ICMPTYPE icmp_type CODE NUM
644 	{
645 		$$ = npfctl_parse_icmp($<num>0, $2, $4);
646 	}
647 	| ICMPTYPE icmp_type CODE IDENTIFIER
648 	{
649 		$$ = npfctl_parse_icmp($<num>0, $2, npfctl_icmpcode($<num>0, $2, $4));
650 	}
651 	| ICMPTYPE icmp_type CODE VAR_ID
652 	{
653 		char *s = npfvar_expand_string(npfvar_lookup($4));
654 		$$ = npfctl_parse_icmp($<num>0, $2, npfctl_icmpcode($<num>0, $2, s));
655 	}
656 	|
657 	{
658 		$$ = npfctl_parse_icmp($<num>0, -1, -1);
659 	}
660 	;
661 
662 tcp_flags_and_mask
663 	: FLAGS tcp_flags SLASH tcp_flags
664 	{
665 		npfvar_add_elements($2, $4);
666 		$$ = $2;
667 	}
668 	| FLAGS tcp_flags
669 	{
670 		char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0);
671 		npfvar_add_elements($2, npfctl_parse_tcpflag(s));
672 		$$ = $2;
673 	}
674 	|		{ $$ = NULL; }
675 	;
676 
677 tcp_flags
678 	: IDENTIFIER	{ $$ = npfctl_parse_tcpflag($1); }
679 	;
680 
681 icmp_type
682 	: NUM		{ $$ = $1; }
683 	| IDENTIFIER	{ $$ = npfctl_icmptype($<num>-1, $1); }
684 	| VAR_ID
685 	{
686 		char *s = npfvar_expand_string(npfvar_lookup($1));
687 		$$ = npfctl_icmptype($<num>-1, s);
688 	}
689 	;
690 
691 ifindex
692 	: some_name
693 	{
694 		$$ = npfctl_find_ifindex($1);
695 	}
696 	| VAR_ID
697 	{
698 		npfvar_t *vp = npfvar_lookup($1);
699 		const int type = npfvar_get_type(vp, 0);
700 
701 		switch (type) {
702 		case NPFVAR_VAR_ID:
703 		case NPFVAR_STRING:
704 			$$ = npfctl_find_ifindex(npfvar_expand_string(vp));
705 			break;
706 		case -1:
707 			yyerror("undefined variable '%s' for interface", $1);
708 			break;
709 		default:
710 			yyerror("wrong variable '%s' type '%s' for interface",
711 			    $1, npfvar_type(type));
712 			$$ = -1;
713 			break;
714 		}
715 	}
716 	;
717 
718 some_name
719 	: IDENTIFIER	{ $$ = $1; }
720 	| STRING	{ $$ = $1; }
721 	;
722 
723 %%
724