npf_parse.y revision 1.7 1 /* $NetBSD: npf_parse.y,v 1.7 2012/05/30 21:30:07 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 #include <assert.h>
39
40 #include "npfctl.h"
41
42 const char * yyfilename;
43
44 extern int yylineno, yycolumn;
45 extern int yylex(void);
46
47 /* Variable under construction (bottom up). */
48 static npfvar_t * cvar;
49
50 void
51 yyerror(const char *fmt, ...)
52 {
53 extern int yyleng;
54 extern char *yytext;
55
56 char *msg, *context = xstrndup(yytext, yyleng);
57 size_t len = strlen(context);
58 char *dst = zalloc(len * 4 + 1);
59 va_list ap;
60
61 va_start(ap, fmt);
62 vasprintf(&msg, fmt, ap);
63 va_end(ap);
64
65 strvisx(dst, context, len, VIS_WHITE|VIS_CSTYLE);
66 fprintf(stderr, "%s:%d:%d: %s near '%s'\n", yyfilename, yylineno,
67 yycolumn, msg, dst);
68 exit(EXIT_FAILURE);
69 }
70
71 %}
72
73 %token ALL
74 %token ANY
75 %token APPLY
76 %token ARROW
77 %token BINAT
78 %token BLOCK
79 %token CURLY_CLOSE
80 %token CURLY_OPEN
81 %token CODE
82 %token COLON
83 %token COMMA
84 %token DEFAULT
85 %token TDYNAMIC
86 %token EQ
87 %token TFILE
88 %token FLAGS
89 %token FROM
90 %token GROUP
91 %token HASH
92 %token ICMPTYPE
93 %token ID
94 %token IN
95 %token INET
96 %token INET6
97 %token INTERFACE
98 %token MINUS
99 %token NAT
100 %token NAME
101 %token ON
102 %token OUT
103 %token PAR_CLOSE
104 %token PAR_OPEN
105 %token PASS
106 %token PORT
107 %token PROCEDURE
108 %token PROTO
109 %token FAMILY
110 %token FINAL
111 %token RDR
112 %token RETURN
113 %token RETURNICMP
114 %token RETURNRST
115 %token SEPLINE
116 %token SLASH
117 %token STATEFUL
118 %token TABLE
119 %token TCP
120 %token TO
121 %token TREE
122 %token TYPE
123 %token UDP
124 %token ICMP
125
126 %token <num> HEX
127 %token <str> IDENTIFIER
128 %token <str> IPV4ADDR
129 %token <str> IPV6ADDR
130 %token <num> NUM
131 %token <str> STRING
132 %token <str> TABLE_ID
133 %token <str> VAR_ID
134
135 %type <str> addr, iface_name, moduleargname, list_elem, table_store
136 %type <str> opt_apply
137 %type <num> ifindex, port, opt_final, on_iface
138 %type <num> block_or_pass, rule_dir, block_opts, family, opt_family
139 %type <num> opt_stateful, icmp_type, table_type
140 %type <var> addr_or_iface, port_range, icmp_type_and_code
141 %type <var> filt_addr, addr_and_mask, tcp_flags, tcp_flags_and_mask
142 %type <var> modulearg_opts, procs, proc_op, modulearg, moduleargs
143 %type <filtopts> filt_opts, all_or_filt_opts
144 %type <optproto> opt_proto
145 %type <rulegroup> group_attr, group_opt
146
147 %union {
148 char * str;
149 unsigned long num;
150 filt_opts_t filtopts;
151 npfvar_t * var;
152 opt_proto_t optproto;
153 rule_group_t rulegroup;
154 }
155
156 %%
157
158 input
159 : lines
160 ;
161
162 lines
163 : line SEPLINE lines
164 | line
165 ;
166
167 line
168 : def
169 | table
170 | nat
171 | group
172 | rproc
173 |
174 ;
175
176 def
177 : VAR_ID
178 {
179 cvar = npfvar_create($1);
180 npfvar_add(cvar);
181 }
182 EQ definition
183 {
184 cvar = NULL;
185 }
186 ;
187
188 definition
189 : list_elem
190 | listdef
191 ;
192
193 listdef
194 : CURLY_OPEN list_elems CURLY_CLOSE
195 ;
196
197 list_elems
198 : list_elem COMMA list_elems
199 | list_elem
200 ;
201
202 list_elem
203 : IDENTIFIER
204 {
205 npfvar_t *vp = npfvar_create(".identifier");
206 npfvar_add_element(vp, NPFVAR_IDENTIFIER, $1, strlen($1) + 1);
207 npfvar_add_elements(cvar, vp);
208 }
209 | STRING
210 {
211 npfvar_t *vp = npfvar_create(".string");
212 npfvar_add_element(vp, NPFVAR_STRING, $1, strlen($1) + 1);
213 npfvar_add_elements(cvar, vp);
214 }
215 | NUM MINUS NUM
216 {
217 npfvar_t *vp = npfctl_parse_port_range($1, $3);
218 npfvar_add_elements(cvar, vp);
219 }
220 | NUM
221 {
222 npfvar_t *vp = npfvar_create(".num");
223 npfvar_add_element(vp, NPFVAR_NUM, &$1, sizeof($1));
224 npfvar_add_elements(cvar, vp);
225 }
226 | VAR_ID
227 {
228 npfvar_t *vp = npfvar_create(".var_id");
229 npfvar_add_element(vp, NPFVAR_VAR_ID, $1, strlen($1) + 1);
230 npfvar_add_elements(cvar, vp);
231 }
232 | addr_and_mask
233 {
234 npfvar_add_elements(cvar, $1);
235 }
236 ;
237
238 table
239 : TABLE TABLE_ID TYPE table_type table_store
240 {
241 npfctl_build_table($2, $4, $5);
242 }
243 ;
244
245 table_type
246 : HASH { $$ = NPF_TABLE_HASH; }
247 | TREE { $$ = NPF_TABLE_TREE; }
248 ;
249
250 table_store
251 : TDYNAMIC { $$ = NULL; }
252 | TFILE STRING { $$ = $2; }
253 ;
254
255 nat
256 : natdef
257 | binatdef
258 | rdrdef
259 ;
260
261 natdef
262 : NAT ifindex filt_opts ARROW addr_or_iface
263 {
264 npfctl_build_nat(NPFCTL_NAT, $2, &$3, $5, NULL);
265 }
266 ;
267
268 binatdef
269 : BINAT ifindex filt_opts ARROW addr_or_iface
270 {
271 npfctl_build_nat(NPFCTL_BINAT, $2, &$3, $5, $3.fo_from);
272 }
273 ;
274
275 rdrdef
276 : RDR ifindex filt_opts ARROW addr_or_iface port_range
277 {
278 npfctl_build_nat(NPFCTL_RDR, $2, &$3, $5, $6);
279 }
280 ;
281
282 rproc
283 : PROCEDURE STRING CURLY_OPEN procs CURLY_CLOSE
284 {
285 npfctl_build_rproc($2, $4);
286 }
287 ;
288
289 procs
290 : proc_op SEPLINE procs { $$ = npfvar_add_elements($1, $3); }
291 | proc_op { $$ = $1; }
292 ;
293
294 proc_op
295 : IDENTIFIER COLON moduleargs
296 {
297 proc_op_t po;
298
299 po.po_name = xstrdup($1);
300 po.po_opts = $3;
301 $$ = npfvar_create(".proc_ops");
302 npfvar_add_element($$, NPFVAR_PROC_OP, &po, sizeof(po));
303 }
304 | { $$ = NULL; }
305 ;
306
307 moduleargs
308 : modulearg COMMA moduleargs
309 {
310 $$ = npfvar_add_elements($1, $3);
311 }
312 | modulearg { $$ = $1; }
313 | { $$ = NULL; }
314 ;
315
316 modulearg
317 : moduleargname modulearg_opts
318 {
319 module_arg_t ma;
320
321 ma.ma_name = xstrdup($1);
322 ma.ma_opts = $2;
323 $$ = npfvar_create(".module_arg");
324 npfvar_add_element($$, NPFVAR_MODULE_ARG, &ma, sizeof(ma));
325 }
326 ;
327
328 moduleargname
329 : STRING { $$ = $1; }
330 | IDENTIFIER { $$ = $1; }
331 ;
332
333 modulearg_opts
334 : STRING modulearg_opts
335 {
336 npfvar_t *vp = npfvar_create(".modstring");
337 npfvar_add_element(vp, NPFVAR_STRING, $1, strlen($1) + 1);
338 $$ = $2 ? npfvar_add_elements($2, vp) : vp;
339 }
340 | IDENTIFIER modulearg_opts
341 {
342 npfvar_t *vp = npfvar_create(".modident");
343 npfvar_add_element(vp, NPFVAR_IDENTIFIER, $1, strlen($1) + 1);
344 $$ = $2 ? npfvar_add_elements($2, vp) : vp;
345 }
346 | NUM modulearg_opts
347 {
348 npfvar_t *vp = npfvar_create(".modnum");
349 npfvar_add_element(vp, NPFVAR_NUM, &$1, sizeof($1));
350 $$ = $2 ? npfvar_add_elements($2, vp) : vp;
351 }
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 family
458 : INET { $$ = AF_INET; }
459 | INET6 { $$ = AF_INET6; }
460 ;
461
462 opt_proto
463 : PROTO TCP tcp_flags_and_mask
464 {
465 $$.op_proto = IPPROTO_TCP;
466 $$.op_opts = $3;
467 }
468 | PROTO ICMP icmp_type_and_code
469 {
470 $$.op_proto = IPPROTO_ICMP;
471 $$.op_opts = $3;
472 }
473 | PROTO UDP
474 {
475 $$.op_proto = IPPROTO_UDP;
476 $$.op_opts = NULL;
477 }
478 |
479 {
480 $$.op_proto = -1;
481 $$.op_opts = NULL;
482 }
483 ;
484
485 opt_family
486 : FAMILY family { $$ = $2; }
487 | { $$ = AF_UNSPEC; }
488 ;
489
490 all_or_filt_opts
491 : ALL
492 {
493 $$.fo_from = NULL;
494 $$.fo_from_port_range = NULL;
495 $$.fo_to = NULL;
496 $$.fo_to_port_range = NULL;
497 }
498 | filt_opts { $$ = $1; }
499 ;
500
501 opt_stateful
502 : STATEFUL { $$ = NPF_RULE_KEEPSTATE; }
503 | { $$ = 0; }
504 ;
505
506 opt_apply
507 : APPLY STRING { $$ = $2; }
508 | { $$ = NULL; }
509 ;
510
511 block_opts
512 : RETURNRST { $$ = NPF_RULE_RETRST; }
513 | RETURNICMP { $$ = NPF_RULE_RETICMP; }
514 | RETURN { $$ = NPF_RULE_RETRST | NPF_RULE_RETICMP; }
515 | { $$ = 0; }
516 ;
517
518 filt_opts
519 : FROM filt_addr port_range TO filt_addr port_range
520 {
521 $$.fo_from = $2;
522 $$.fo_from_port_range = $3;
523 $$.fo_to = $5;
524 $$.fo_to_port_range = $6;
525 }
526 | FROM filt_addr port_range
527 {
528 $$.fo_from = $2;
529 $$.fo_from_port_range = $3;
530 $$.fo_to = NULL;
531 $$.fo_to_port_range = NULL;
532 }
533 | TO filt_addr port_range
534 {
535 $$.fo_from = NULL;
536 $$.fo_from_port_range = NULL;
537 $$.fo_to = $2;
538 $$.fo_to_port_range = $3;
539 }
540 ;
541
542 filt_addr
543 : addr_or_iface { $$ = $1; }
544 | TABLE_ID { $$ = npfctl_parse_table_id($1); }
545 | ANY { $$ = NULL; }
546 ;
547
548 addr_and_mask
549 : addr SLASH NUM
550 {
551 $$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
552 }
553 | addr SLASH HEX
554 {
555 $$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
556 }
557 | addr SLASH addr
558 {
559 $$ = npfctl_parse_fam_addr_mask($1, $3, NULL);
560 }
561 | addr
562 {
563 $$ = npfctl_parse_fam_addr_mask($1, NULL, NULL);
564 }
565 ;
566
567 addr_or_iface
568 : addr_and_mask { assert($1 != NULL); $$ = $1; }
569 | iface_name
570 {
571 $$ = npfctl_parse_iface($1);
572 }
573 | VAR_ID
574 {
575 npfvar_t *vp = npfvar_lookup($1);
576 const int type = npfvar_get_type(vp, 0);
577
578 switch (type) {
579 case NPFVAR_VAR_ID:
580 case NPFVAR_STRING:
581 $$ = npfctl_parse_iface(npfvar_expand_string(vp));
582 break;
583 case NPFVAR_FAM:
584 $$ = vp;
585 break;
586 case -1:
587 yyerror("undefined variable '%s' for interface", $1);
588 break;
589 default:
590 yyerror("wrong variable '%s' type '%s' or interface",
591 $1, npfvar_type(type));
592 $$ = NULL;
593 break;
594 }
595 }
596 ;
597
598 addr
599 : IPV4ADDR { $$ = $1; }
600 | IPV6ADDR { $$ = $1; }
601 ;
602
603
604 port_range
605 : PORT port /* just port */
606 {
607 $$ = npfctl_parse_port_range($2, $2);
608 }
609 | PORT port MINUS port /* port from:to */
610 {
611 $$ = npfctl_parse_port_range($2, $4);
612 }
613 | PORT VAR_ID {
614 $$ = npfctl_parse_port_range_variable($2);
615 }
616 |
617 {
618 $$ = NULL;
619 }
620 ;
621
622 port
623 : NUM { $$ = $1; }
624 | IDENTIFIER { $$ = npfctl_portno($1); }
625 ;
626
627 icmp_type_and_code
628 : ICMPTYPE icmp_type
629 {
630 $$ = npfctl_parse_icmp($2, -1);
631 }
632 | ICMPTYPE icmp_type CODE NUM
633 {
634 $$ = npfctl_parse_icmp($2, $4);
635 }
636 | ICMPTYPE icmp_type CODE IDENTIFIER
637 {
638 $$ = npfctl_parse_icmp($2, npfctl_icmpcode($2, $4));
639 }
640 | ICMPTYPE icmp_type CODE VAR_ID
641 {
642 char *s = npfvar_expand_string(npfvar_lookup($4));
643 $$ = npfctl_parse_icmp($2, npfctl_icmpcode($2, s));
644 }
645 |
646 {
647 $$ = npfctl_parse_icmp(-1, -1);
648 }
649 ;
650
651 tcp_flags_and_mask
652 : FLAGS tcp_flags SLASH tcp_flags
653 {
654 npfvar_add_elements($2, $4);
655 $$ = $2;
656 }
657 | FLAGS tcp_flags
658 {
659 char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0);
660 npfvar_add_elements($2, npfctl_parse_tcpflag(s));
661 $$ = $2;
662 }
663 | { $$ = NULL; }
664 ;
665
666 tcp_flags
667 : IDENTIFIER { $$ = npfctl_parse_tcpflag($1); }
668 ;
669
670 icmp_type
671 : NUM { $$ = $1; }
672 | IDENTIFIER { $$ = npfctl_icmptype($1); }
673 | VAR_ID
674 {
675 char *s = npfvar_expand_string(npfvar_lookup($1));
676 $$ = npfctl_icmptype(s);
677 }
678 ;
679
680 ifindex
681 : iface_name
682 {
683 $$ = npfctl_find_ifindex($1);
684 }
685 | VAR_ID
686 {
687 npfvar_t *vp = npfvar_lookup($1);
688 const int type = npfvar_get_type(vp, 0);
689
690 switch (type) {
691 case NPFVAR_VAR_ID:
692 case NPFVAR_STRING:
693 $$ = npfctl_find_ifindex(npfvar_expand_string(vp));
694 break;
695 case -1:
696 yyerror("undefined variable '%s' for interface", $1);
697 break;
698 default:
699 yyerror("wrong variable '%s' type '%s' for interface",
700 $1, npfvar_type(type));
701 $$ = -1;
702 break;
703 }
704 }
705 ;
706
707 iface_name
708 : IDENTIFIER { $$ = $1; }
709 | STRING { $$ = $1; }
710 ;
711
712 %%
713