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