npf_parse.y revision 1.9 1 /* $NetBSD: npf_parse.y,v 1.9 2012/07/01 23:21: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 ARROWBOTH
77 %token ARROWLEFT
78 %token ARROWRIGHT
79 %token BLOCK
80 %token CURLY_CLOSE
81 %token CURLY_OPEN
82 %token CODE
83 %token COLON
84 %token COMMA
85 %token DEFAULT
86 %token TDYNAMIC
87 %token TSTATIC
88 %token EQ
89 %token TFILE
90 %token FLAGS
91 %token FROM
92 %token GROUP
93 %token HASH
94 %token ICMPTYPE
95 %token ID
96 %token IN
97 %token INET
98 %token INET6
99 %token INTERFACE
100 %token MAP
101 %token MINUS
102 %token NAME
103 %token ON
104 %token OUT
105 %token PAR_CLOSE
106 %token PAR_OPEN
107 %token PASS
108 %token PORT
109 %token PROCEDURE
110 %token PROTO
111 %token FAMILY
112 %token FINAL
113 %token RETURN
114 %token RETURNICMP
115 %token RETURNRST
116 %token SEPLINE
117 %token SLASH
118 %token STATEFUL
119 %token TABLE
120 %token TCP
121 %token TO
122 %token TREE
123 %token TYPE
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, 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 some_name
476 {
477 $$.op_proto = npfctl_protono($2);
478 $$.op_opts = NULL;
479 }
480 | PROTO NUM
481 {
482 $$.op_proto = $2;
483 $$.op_opts = NULL;
484 }
485 |
486 {
487 $$.op_proto = -1;
488 $$.op_opts = NULL;
489 }
490 ;
491
492 all_or_filt_opts
493 : ALL
494 {
495 $$.fo_from.ap_netaddr = NULL;
496 $$.fo_from.ap_portrange = NULL;
497 $$.fo_to.ap_netaddr = NULL;
498 $$.fo_to.ap_portrange = NULL;
499 }
500 | filt_opts { $$ = $1; }
501 ;
502
503 opt_stateful
504 : STATEFUL { $$ = NPF_RULE_STATEFUL; }
505 | { $$ = 0; }
506 ;
507
508 opt_apply
509 : APPLY STRING { $$ = $2; }
510 | { $$ = NULL; }
511 ;
512
513 block_opts
514 : RETURNRST { $$ = NPF_RULE_RETRST; }
515 | RETURNICMP { $$ = NPF_RULE_RETICMP; }
516 | RETURN { $$ = NPF_RULE_RETRST | NPF_RULE_RETICMP; }
517 | { $$ = 0; }
518 ;
519
520 filt_opts
521 : FROM filt_addr port_range TO filt_addr port_range
522 {
523 $$.fo_from.ap_netaddr = $2;
524 $$.fo_from.ap_portrange = $3;
525 $$.fo_to.ap_netaddr = $5;
526 $$.fo_to.ap_portrange = $6;
527 }
528 | FROM filt_addr port_range
529 {
530 $$.fo_from.ap_netaddr = $2;
531 $$.fo_from.ap_portrange = $3;
532 $$.fo_to.ap_netaddr = NULL;
533 $$.fo_to.ap_portrange = NULL;
534 }
535 | TO filt_addr port_range
536 {
537 $$.fo_from.ap_netaddr = NULL;
538 $$.fo_from.ap_portrange = NULL;
539 $$.fo_to.ap_netaddr = $2;
540 $$.fo_to.ap_portrange = $3;
541 }
542 ;
543
544 filt_addr
545 : addr_or_iface { $$ = $1; }
546 | TABLE_ID { $$ = npfctl_parse_table_id($1); }
547 | ANY { $$ = NULL; }
548 ;
549
550 addr_and_mask
551 : addr SLASH NUM
552 {
553 $$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
554 }
555 | addr SLASH HEX
556 {
557 $$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
558 }
559 | addr SLASH addr
560 {
561 $$ = npfctl_parse_fam_addr_mask($1, $3, NULL);
562 }
563 | addr
564 {
565 $$ = npfctl_parse_fam_addr_mask($1, NULL, NULL);
566 }
567 ;
568
569 addr_or_iface
570 : addr_and_mask
571 {
572 assert($1 != NULL);
573 $$ = $1;
574 }
575 | some_name
576 {
577 $$ = npfctl_parse_iface($1);
578 }
579 | VAR_ID
580 {
581 npfvar_t *vp = npfvar_lookup($1);
582 const int type = npfvar_get_type(vp, 0);
583
584 switch (type) {
585 case NPFVAR_VAR_ID:
586 case NPFVAR_STRING:
587 $$ = npfctl_parse_iface(npfvar_expand_string(vp));
588 break;
589 case NPFVAR_FAM:
590 $$ = vp;
591 break;
592 case -1:
593 yyerror("undefined variable '%s' for interface", $1);
594 break;
595 default:
596 yyerror("wrong variable '%s' type '%s' or interface",
597 $1, npfvar_type(type));
598 $$ = NULL;
599 break;
600 }
601 }
602 ;
603
604 addr
605 : IPV4ADDR { $$ = $1; }
606 | IPV6ADDR { $$ = $1; }
607 ;
608
609
610 port_range
611 : PORT port /* just port */
612 {
613 $$ = npfctl_parse_port_range($2, $2);
614 }
615 | PORT port MINUS port /* port from-to */
616 {
617 $$ = npfctl_parse_port_range($2, $4);
618 }
619 | PORT VAR_ID
620 {
621 $$ = npfctl_parse_port_range_variable($2);
622 }
623 |
624 {
625 $$ = NULL;
626 }
627 ;
628
629 port
630 : NUM { $$ = $1; }
631 | IDENTIFIER { $$ = npfctl_portno($1); }
632 ;
633
634 icmp_type_and_code
635 : ICMPTYPE icmp_type
636 {
637 $$ = npfctl_parse_icmp($2, -1);
638 }
639 | ICMPTYPE icmp_type CODE NUM
640 {
641 $$ = npfctl_parse_icmp($2, $4);
642 }
643 | ICMPTYPE icmp_type CODE IDENTIFIER
644 {
645 $$ = npfctl_parse_icmp($2, npfctl_icmpcode($2, $4));
646 }
647 | ICMPTYPE icmp_type CODE VAR_ID
648 {
649 char *s = npfvar_expand_string(npfvar_lookup($4));
650 $$ = npfctl_parse_icmp($2, npfctl_icmpcode($2, s));
651 }
652 |
653 {
654 $$ = npfctl_parse_icmp(-1, -1);
655 }
656 ;
657
658 tcp_flags_and_mask
659 : FLAGS tcp_flags SLASH tcp_flags
660 {
661 npfvar_add_elements($2, $4);
662 $$ = $2;
663 }
664 | FLAGS tcp_flags
665 {
666 char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0);
667 npfvar_add_elements($2, npfctl_parse_tcpflag(s));
668 $$ = $2;
669 }
670 | { $$ = NULL; }
671 ;
672
673 tcp_flags
674 : IDENTIFIER { $$ = npfctl_parse_tcpflag($1); }
675 ;
676
677 icmp_type
678 : NUM { $$ = $1; }
679 | IDENTIFIER { $$ = npfctl_icmptype($1); }
680 | VAR_ID
681 {
682 char *s = npfvar_expand_string(npfvar_lookup($1));
683 $$ = npfctl_icmptype(s);
684 }
685 ;
686
687 ifindex
688 : some_name
689 {
690 $$ = npfctl_find_ifindex($1);
691 }
692 | VAR_ID
693 {
694 npfvar_t *vp = npfvar_lookup($1);
695 const int type = npfvar_get_type(vp, 0);
696
697 switch (type) {
698 case NPFVAR_VAR_ID:
699 case NPFVAR_STRING:
700 $$ = npfctl_find_ifindex(npfvar_expand_string(vp));
701 break;
702 case -1:
703 yyerror("undefined variable '%s' for interface", $1);
704 break;
705 default:
706 yyerror("wrong variable '%s' type '%s' for interface",
707 $1, npfvar_type(type));
708 $$ = -1;
709 break;
710 }
711 }
712 ;
713
714 some_name
715 : IDENTIFIER { $$ = $1; }
716 | STRING { $$ = $1; }
717 ;
718
719 %%
720