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