npf_parse.y revision 1.26 1 /* $NetBSD: npf_parse.y,v 1.26 2013/09/20 03:03:52 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, list_elem, table_store, string
159 %type <str> proc_param_val, opt_apply
160 %type <num> ifindex, port, opt_final, on_ifindex, number
161 %type <num> afamily, opt_family
162 %type <num> block_or_pass, rule_dir, group_dir, block_opts
163 %type <num> opt_stateful, icmp_type, table_type, map_sd, map_type
164 %type <var> ifnet, addr_or_ifnet, port_range, icmp_type_and_code
165 %type <var> filt_addr, addr_and_mask, tcp_flags, tcp_flags_and_mask
166 %type <var> procs, proc_call, proc_param_list, proc_param
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 : def
197 | table
198 | map
199 | group
200 | rproc
201 | alg
202 |
203 ;
204
205 def
206 : VAR_ID
207 {
208 cvar = npfvar_create($1);
209 npfvar_add(cvar);
210 }
211 EQ definition
212 {
213 cvar = NULL;
214 }
215 ;
216
217 definition
218 : list_elem
219 | listdef
220 ;
221
222 listdef
223 : CURLY_OPEN list_elems CURLY_CLOSE
224 ;
225
226 list_elems
227 : list_elem COMMA list_elems
228 | list_elem
229 ;
230
231 list_elem
232 : IDENTIFIER
233 {
234 npfvar_t *vp = npfvar_create(".identifier");
235 npfvar_add_element(vp, NPFVAR_IDENTIFIER, $1, strlen($1) + 1);
236 npfvar_add_elements(cvar, vp);
237 }
238 | STRING
239 {
240 npfvar_t *vp = npfvar_create(".string");
241 npfvar_add_element(vp, NPFVAR_STRING, $1, strlen($1) + 1);
242 npfvar_add_elements(cvar, vp);
243 }
244 | number MINUS number
245 {
246 npfvar_t *vp = npfctl_parse_port_range($1, $3);
247 npfvar_add_elements(cvar, vp);
248 }
249 | number
250 {
251 npfvar_t *vp = npfvar_create(".num");
252 npfvar_add_element(vp, NPFVAR_NUM, &$1, sizeof($1));
253 npfvar_add_elements(cvar, vp);
254 }
255 | VAR_ID
256 {
257 npfvar_t *vp = npfvar_create(".var_id");
258 npfvar_add_element(vp, NPFVAR_VAR_ID, $1, strlen($1) + 1);
259 npfvar_add_elements(cvar, vp);
260 }
261 | ifnet
262 {
263 npfvar_add_elements(cvar, $1);
264 }
265 | addr_and_mask
266 {
267 npfvar_add_elements(cvar, $1);
268 }
269 ;
270
271 table
272 : TABLE TABLE_ID TYPE table_type table_store
273 {
274 npfctl_build_table($2, $4, $5);
275 }
276 ;
277
278 table_type
279 : HASH { $$ = NPF_TABLE_HASH; }
280 | TREE { $$ = NPF_TABLE_TREE; }
281 ;
282
283 table_store
284 : TDYNAMIC { $$ = NULL; }
285 | TFILE STRING { $$ = $2; }
286 ;
287
288 map_sd
289 : TSTATIC { $$ = NPFCTL_NAT_STATIC; }
290 | TDYNAMIC { $$ = NPFCTL_NAT_DYNAMIC; }
291 | { $$ = NPFCTL_NAT_DYNAMIC; }
292 ;
293
294 map_type
295 : ARROWBOTH { $$ = NPF_NATIN | NPF_NATOUT; }
296 | ARROWLEFT { $$ = NPF_NATIN; }
297 | ARROWRIGHT { $$ = NPF_NATOUT; }
298 ;
299
300 mapseg
301 : addr_or_ifnet port_range
302 {
303 $$.ap_netaddr = $1;
304 $$.ap_portrange = $2;
305 }
306 ;
307
308 map
309 : MAP ifindex map_sd mapseg map_type mapseg PASS filt_opts
310 {
311 npfctl_build_natseg($3, $5, $2, &$4, &$6, &$8);
312 }
313 | MAP ifindex map_sd mapseg map_type mapseg
314 {
315 npfctl_build_natseg($3, $5, $2, &$4, &$6, NULL);
316 }
317 | MAP RULESET group_opts
318 {
319 npfctl_build_maprset($3.rg_name, $3.rg_attr, $3.rg_ifnum);
320 }
321 ;
322
323 rproc
324 : PROCEDURE STRING CURLY_OPEN procs CURLY_CLOSE
325 {
326 npfctl_build_rproc($2, $4);
327 }
328 ;
329
330 alg
331 : ALG STRING
332 {
333 npfctl_build_alg($2);
334 }
335 ;
336
337 procs
338 : proc_call SEPLINE procs
339 {
340 $$ = npfvar_add_elements($1, $3);
341 }
342 | proc_call { $$ = $1; }
343 ;
344
345 proc_call
346 : IDENTIFIER COLON proc_param_list
347 {
348 proc_call_t pc;
349
350 pc.pc_name = estrdup($1);
351 pc.pc_opts = $3;
352 $$ = npfvar_create(".proc_call");
353 npfvar_add_element($$, NPFVAR_PROC, &pc, sizeof(pc));
354 }
355 | { $$ = NULL; }
356 ;
357
358 proc_param_list
359 : proc_param COMMA proc_param_list
360 {
361 $$ = npfvar_add_elements($1, $3);
362 }
363 | proc_param { $$ = $1; }
364 | { $$ = NULL; }
365 ;
366
367 proc_param
368 /* Key and value pair. */
369 : some_name proc_param_val
370 {
371 proc_param_t pp;
372
373 pp.pp_param = estrdup($1);
374 pp.pp_value = $2 ? estrdup($2) : NULL;
375 $$ = npfvar_create(".proc_param");
376 npfvar_add_element($$, NPFVAR_PROC_PARAM, &pp, sizeof(pp));
377 }
378 ;
379
380 proc_param_val
381 : some_name { $$ = $1; }
382 | number { (void)asprintf(&$$, "%ld", $1); }
383 | FPNUM { (void)asprintf(&$$, "%lf", $1); }
384 | { $$ = NULL; }
385 ;
386
387 group
388 : GROUP group_opts
389 {
390 /* Build a group. Increases the nesting level. */
391 npfctl_build_group($2.rg_name, $2.rg_attr,
392 $2.rg_ifnum, $2.rg_default);
393 }
394 ruleset_block
395 {
396 /* Decrease the nesting level. */
397 npfctl_build_group_end();
398 }
399 ;
400
401 ruleset
402 : RULESET group_opts
403 {
404 /* Ruleset is a dynamic group. */
405 npfctl_build_group($2.rg_name, $2.rg_attr | NPF_RULE_DYNAMIC,
406 $2.rg_ifnum, $2.rg_default);
407 npfctl_build_group_end();
408 }
409 ;
410
411 group_dir
412 : FORW { $$ = NPF_RULE_FORW; }
413 | rule_dir
414 ;
415
416 group_opts
417 : DEFAULT
418 {
419 memset(&$$, 0, sizeof(rule_group_t));
420 $$.rg_default = true;
421 }
422 | STRING group_dir on_ifindex
423 {
424 memset(&$$, 0, sizeof(rule_group_t));
425 $$.rg_name = $1;
426 $$.rg_attr = $2;
427 $$.rg_ifnum = $3;
428 }
429 ;
430
431 ruleset_block
432 : CURLY_OPEN ruleset_def CURLY_CLOSE
433 ;
434
435 ruleset_def
436 : rule_group SEPLINE ruleset_def
437 | rule_group
438 ;
439
440 rule_group
441 : rule
442 | group
443 | ruleset
444 |
445 ;
446
447 rule
448 : block_or_pass opt_stateful rule_dir opt_final on_ifindex
449 opt_family opt_proto all_or_filt_opts opt_apply
450 {
451 npfctl_build_rule($1 | $2 | $3 | $4, $5,
452 $6, &$7, &$8, NULL, $9);
453 }
454 | block_or_pass opt_stateful rule_dir opt_final on_ifindex
455 PCAP_FILTER STRING opt_apply
456 {
457 npfctl_build_rule($1 | $2 | $3 | $4, $5,
458 AF_UNSPEC, NULL, NULL, $7, $8);
459 }
460 ;
461
462 block_or_pass
463 : BLOCK block_opts { $$ = $2; }
464 | PASS { $$ = NPF_RULE_PASS; }
465 ;
466
467 rule_dir
468 : IN { $$ = NPF_RULE_IN; }
469 | OUT { $$ = NPF_RULE_OUT; }
470 | { $$ = NPF_RULE_IN | NPF_RULE_OUT; }
471 ;
472
473 opt_final
474 : FINAL { $$ = NPF_RULE_FINAL; }
475 | { $$ = 0; }
476 ;
477
478 on_ifindex
479 : ON ifindex { $$ = $2; }
480 | { $$ = 0; }
481 ;
482
483 afamily
484 : INET { $$ = AF_INET; }
485 | INET6 { $$ = AF_INET6; }
486 ;
487
488 opt_family
489 : FAMILY afamily { $$ = $2; }
490 | { $$ = AF_UNSPEC; }
491 ;
492
493 opt_proto
494 : PROTO TCP tcp_flags_and_mask
495 {
496 $$.op_proto = IPPROTO_TCP;
497 $$.op_opts = $3;
498 }
499 | PROTO ICMP icmp_type_and_code
500 {
501 $$.op_proto = IPPROTO_ICMP;
502 $$.op_opts = $3;
503 }
504 | PROTO ICMP6 icmp_type_and_code
505 {
506 $$.op_proto = IPPROTO_ICMPV6;
507 $$.op_opts = $3;
508 }
509 | PROTO some_name
510 {
511 $$.op_proto = npfctl_protono($2);
512 $$.op_opts = NULL;
513 }
514 | PROTO number
515 {
516 $$.op_proto = $2;
517 $$.op_opts = NULL;
518 }
519 |
520 {
521 $$.op_proto = -1;
522 $$.op_opts = NULL;
523 }
524 ;
525
526 all_or_filt_opts
527 : ALL
528 {
529 $$.fo_from.ap_netaddr = NULL;
530 $$.fo_from.ap_portrange = NULL;
531 $$.fo_to.ap_netaddr = NULL;
532 $$.fo_to.ap_portrange = NULL;
533 }
534 | filt_opts { $$ = $1; }
535 ;
536
537 opt_stateful
538 : STATEFUL { $$ = NPF_RULE_STATEFUL; }
539 | { $$ = 0; }
540 ;
541
542 opt_apply
543 : APPLY STRING { $$ = $2; }
544 | { $$ = NULL; }
545 ;
546
547 block_opts
548 : RETURNRST { $$ = NPF_RULE_RETRST; }
549 | RETURNICMP { $$ = NPF_RULE_RETICMP; }
550 | RETURN { $$ = NPF_RULE_RETRST | NPF_RULE_RETICMP; }
551 | { $$ = 0; }
552 ;
553
554 filt_opts
555 : FROM filt_addr port_range TO filt_addr port_range
556 {
557 $$.fo_from.ap_netaddr = $2;
558 $$.fo_from.ap_portrange = $3;
559 $$.fo_to.ap_netaddr = $5;
560 $$.fo_to.ap_portrange = $6;
561 }
562 | FROM filt_addr port_range
563 {
564 $$.fo_from.ap_netaddr = $2;
565 $$.fo_from.ap_portrange = $3;
566 $$.fo_to.ap_netaddr = NULL;
567 $$.fo_to.ap_portrange = NULL;
568 }
569 | TO filt_addr port_range
570 {
571 $$.fo_from.ap_netaddr = NULL;
572 $$.fo_from.ap_portrange = NULL;
573 $$.fo_to.ap_netaddr = $2;
574 $$.fo_to.ap_portrange = $3;
575 }
576 ;
577
578 filt_addr
579 : addr_or_ifnet { $$ = $1; }
580 | TABLE_ID { $$ = npfctl_parse_table_id($1); }
581 | ANY { $$ = NULL; }
582 ;
583
584 addr_and_mask
585 : addr SLASH number
586 {
587 $$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
588 }
589 | addr SLASH addr
590 {
591 $$ = npfctl_parse_fam_addr_mask($1, $3, NULL);
592 }
593 | addr
594 {
595 $$ = npfctl_parse_fam_addr_mask($1, NULL, NULL);
596 }
597 ;
598
599 addr_or_ifnet
600 : addr_and_mask
601 {
602 assert($1 != NULL);
603 $$ = $1;
604 }
605 | ifnet
606 {
607 ifnet_addr_t *ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0);
608 $$ = ifna->ifna_addrs;
609 }
610 | VAR_ID
611 {
612 npfvar_t *vp = npfvar_lookup($1);
613 int type = npfvar_get_type(vp, 0);
614 ifnet_addr_t *ifna;
615
616 again:
617 switch (type) {
618 case NPFVAR_IDENTIFIER:
619 case NPFVAR_STRING:
620 vp = npfctl_parse_ifnet(npfvar_expand_string(vp),
621 AF_UNSPEC);
622 type = npfvar_get_type(vp, 0);
623 goto again;
624 case NPFVAR_FAM:
625 $$ = vp;
626 break;
627 case NPFVAR_INTERFACE:
628 ifna = npfvar_get_data(vp, type, 0);
629 $$ = ifna->ifna_addrs;
630 break;
631 case -1:
632 yyerror("undefined variable '%s'", $1);
633 break;
634 default:
635 yyerror("wrong variable '%s' type '%s' for address "
636 "or interface", $1, npfvar_type(type));
637 break;
638 }
639 }
640 ;
641
642 addr
643 : IPV4ADDR { $$ = $1; }
644 | IPV6ADDR { $$ = $1; }
645 ;
646
647 port_range
648 : PORT port /* just port */
649 {
650 $$ = npfctl_parse_port_range($2, $2);
651 }
652 | PORT port MINUS port /* port from-to */
653 {
654 $$ = npfctl_parse_port_range($2, $4);
655 }
656 | PORT VAR_ID
657 {
658 $$ = npfctl_parse_port_range_variable($2);
659 }
660 |
661 {
662 $$ = NULL;
663 }
664 ;
665
666 port
667 : number { $$ = $1; }
668 | IDENTIFIER { $$ = npfctl_portno($1); }
669 | STRING { $$ = npfctl_portno($1); }
670 ;
671
672 icmp_type_and_code
673 : ICMPTYPE icmp_type
674 {
675 $$ = npfctl_parse_icmp($<num>0, $2, -1);
676 }
677 | ICMPTYPE icmp_type CODE number
678 {
679 $$ = npfctl_parse_icmp($<num>0, $2, $4);
680 }
681 | ICMPTYPE icmp_type CODE IDENTIFIER
682 {
683 $$ = npfctl_parse_icmp($<num>0, $2,
684 npfctl_icmpcode($<num>0, $2, $4));
685 }
686 | ICMPTYPE icmp_type CODE VAR_ID
687 {
688 char *s = npfvar_expand_string(npfvar_lookup($4));
689 $$ = npfctl_parse_icmp($<num>0, $2,
690 npfctl_icmpcode($<num>0, $2, s));
691 }
692 | { $$ = NULL; }
693 ;
694
695 tcp_flags_and_mask
696 : FLAGS tcp_flags SLASH tcp_flags
697 {
698 npfvar_add_elements($2, $4);
699 $$ = $2;
700 }
701 | FLAGS tcp_flags
702 {
703 char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0);
704 npfvar_add_elements($2, npfctl_parse_tcpflag(s));
705 $$ = $2;
706 }
707 | { $$ = NULL; }
708 ;
709
710 tcp_flags
711 : IDENTIFIER { $$ = npfctl_parse_tcpflag($1); }
712 ;
713
714 icmp_type
715 : number { $$ = $1; }
716 | IDENTIFIER { $$ = npfctl_icmptype($<num>-1, $1); }
717 | VAR_ID
718 {
719 char *s = npfvar_expand_string(npfvar_lookup($1));
720 $$ = npfctl_icmptype($<num>-1, s);
721 }
722 ;
723
724 string
725 : IDENTIFIER
726 {
727 $$ = $1;
728 }
729 | VAR_ID
730 {
731 npfvar_t *vp = npfvar_lookup($1);
732 const int type = npfvar_get_type(vp, 0);
733
734 switch (type) {
735 case NPFVAR_STRING:
736 case NPFVAR_IDENTIFIER:
737 $$ = npfvar_expand_string(vp);
738 break;
739 case -1:
740 yyerror("undefined variable '%s' for interface", $1);
741 break;
742 default:
743 yyerror("wrong variable '%s' type '%s' for string",
744 $1, npfvar_type(type));
745 break;
746 }
747 }
748 ;
749
750 ifnet
751 : IFNET PAR_OPEN string PAR_CLOSE
752 {
753 $$ = npfctl_parse_ifnet($3, AF_UNSPEC);
754 }
755 | afamily PAR_OPEN string PAR_CLOSE
756 {
757 $$ = npfctl_parse_ifnet($3, $1);
758 }
759 ;
760
761 ifindex
762 : some_name
763 {
764 $$ = npfctl_find_ifindex($1);
765 }
766 | ifnet
767 {
768 ifnet_addr_t *ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0);
769 $$ = ifna->ifna_index;
770 }
771 | VAR_ID
772 {
773 npfvar_t *vp = npfvar_lookup($1);
774 const int type = npfvar_get_type(vp, 0);
775 ifnet_addr_t *ifna;
776
777 switch (type) {
778 case NPFVAR_STRING:
779 case NPFVAR_IDENTIFIER:
780 $$ = npfctl_find_ifindex(npfvar_expand_string(vp));
781 break;
782 case NPFVAR_INTERFACE:
783 ifna = npfvar_get_data(vp, type, 0);
784 $$ = ifna->ifna_index;
785 break;
786 case -1:
787 yyerror("undefined variable '%s' for interface", $1);
788 break;
789 default:
790 yyerror("wrong variable '%s' type '%s' for interface",
791 $1, npfvar_type(type));
792 break;
793 }
794 }
795 ;
796
797 number
798 : HEX { $$ = $1; }
799 | NUM { $$ = $1; }
800 ;
801
802 some_name
803 : IDENTIFIER { $$ = $1; }
804 | STRING { $$ = $1; }
805 ;
806
807 %%
808