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