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