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