1 /* $NetBSD: npf_parse.y,v 1.35 2014/03/15 15:22:37 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2011-2014 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, Christos Zoulas and Mindaugas Rasiukevicius. 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 int yyparsetarget; 44 const char * yyfilename; 45 46 extern int yylineno, yycolumn; 47 extern int yylex(void); 48 49 void 50 yyerror(const char *fmt, ...) 51 { 52 extern int yyleng; 53 extern char *yytext; 54 55 char *msg, *context = estrndup(yytext, yyleng); 56 bool eol = (*context == '\n'); 57 va_list ap; 58 59 va_start(ap, fmt); 60 vasprintf(&msg, fmt, ap); 61 va_end(ap); 62 63 fprintf(stderr, "%s:%d:%d: %s", yyfilename, 64 yylineno - (int)eol, yycolumn, msg); 65 if (!eol) { 66 size_t len = strlen(context); 67 char *dst = ecalloc(1, len * 4 + 1); 68 69 strvisx(dst, context, len, VIS_WHITE|VIS_CSTYLE); 70 fprintf(stderr, " near '%s'", dst); 71 } 72 fprintf(stderr, "\n"); 73 exit(EXIT_FAILURE); 74 } 75 76 #define CHECK_PARSER_FILE \ 77 if (yyparsetarget != NPFCTL_PARSE_FILE) \ 78 yyerror("rule must be in the group"); 79 80 #define CHECK_PARSER_STRING \ 81 if (yyparsetarget != NPFCTL_PARSE_STRING) \ 82 yyerror("invalid rule syntax"); 83 84 %} 85 86 %token ALG 87 %token ALGO 88 %token ALL 89 %token ANY 90 %token APPLY 91 %token ARROWBOTH 92 %token ARROWLEFT 93 %token ARROWRIGHT 94 %token BLOCK 95 %token CDB 96 %token CURLY_CLOSE 97 %token CURLY_OPEN 98 %token CODE 99 %token COLON 100 %token COMMA 101 %token DEFAULT 102 %token TDYNAMIC 103 %token TSTATIC 104 %token EQ 105 %token TFILE 106 %token FLAGS 107 %token FROM 108 %token GROUP 109 %token HASH 110 %token ICMPTYPE 111 %token ID 112 %token IN 113 %token INET4 114 %token INET6 115 %token INTERFACE 116 %token MAP 117 %token MINUS 118 %token NAME 119 %token NPT66 120 %token ON 121 %token OUT 122 %token PAR_CLOSE 123 %token PAR_OPEN 124 %token PASS 125 %token PCAP_FILTER 126 %token PORT 127 %token PROCEDURE 128 %token PROTO 129 %token FAMILY 130 %token FINAL 131 %token FORW 132 %token RETURN 133 %token RETURNICMP 134 %token RETURNRST 135 %token RULESET 136 %token SEPLINE 137 %token SLASH 138 %token STATEFUL 139 %token STATEFUL_ENDS 140 %token TABLE 141 %token TCP 142 %token TO 143 %token TREE 144 %token TYPE 145 %token <num> ICMP 146 %token <num> ICMP6 147 148 %token <num> HEX 149 %token <str> IDENTIFIER 150 %token <str> IPV4ADDR 151 %token <str> IPV6ADDR 152 %token <num> NUM 153 %token <fpnum> FPNUM 154 %token <str> STRING 155 %token <str> TABLE_ID 156 %token <str> VAR_ID 157 158 %type <str> addr, some_name, table_store 159 %type <str> proc_param_val, opt_apply, ifname, on_ifname, ifref 160 %type <num> port, opt_final, number, afamily, opt_family 161 %type <num> block_or_pass, rule_dir, group_dir, block_opts 162 %type <num> opt_stateful, icmp_type, table_type 163 %type <num> map_sd, map_algo, map_type 164 %type <var> ifaddrs, addr_or_ifaddr, port_range, icmp_type_and_code 165 %type <var> filt_addr, addr_and_mask, tcp_flags, tcp_flags_and_mask 166 %type <var> procs, proc_call, proc_param_list, proc_param 167 %type <var> element, list_elems, list, value 168 %type <addrport> mapseg 169 %type <filtopts> filt_opts, all_or_filt_opts 170 %type <optproto> opt_proto 171 %type <rulegroup> group_opts 172 173 %union { 174 char * str; 175 unsigned long num; 176 double fpnum; 177 npfvar_t * var; 178 addr_port_t addrport; 179 filt_opts_t filtopts; 180 opt_proto_t optproto; 181 rule_group_t rulegroup; 182 } 183 184 %% 185 186 input 187 : { CHECK_PARSER_FILE } lines 188 | { CHECK_PARSER_STRING } rule 189 ; 190 191 lines 192 : lines SEPLINE line 193 | line 194 ; 195 196 line 197 : vardef 198 | table 199 | map 200 | group 201 | rproc 202 | alg 203 | 204 ; 205 206 alg 207 : ALG STRING 208 { 209 npfctl_build_alg($2); 210 } 211 ; 212 213 /* 214 * A value - an element or a list of elements. 215 * Can be assigned to a variable or used inline. 216 */ 217 218 vardef 219 : VAR_ID EQ value 220 { 221 npfvar_add($3, $1); 222 } 223 ; 224 225 value 226 : element 227 | list 228 ; 229 230 list 231 : CURLY_OPEN list_elems CURLY_CLOSE 232 { 233 $$ = $2; 234 } 235 ; 236 237 list_elems 238 : list_elems COMMA element 239 { 240 npfvar_add_elements($1, $3); 241 } 242 | element 243 ; 244 245 element 246 : IDENTIFIER 247 { 248 $$ = npfvar_create_from_string(NPFVAR_IDENTIFIER, $1); 249 } 250 | STRING 251 { 252 $$ = npfvar_create_from_string(NPFVAR_STRING, $1); 253 } 254 | number MINUS number 255 { 256 $$ = npfctl_parse_port_range($1, $3); 257 } 258 | number 259 { 260 $$ = npfvar_create_element(NPFVAR_NUM, &$1, sizeof($1)); 261 } 262 | VAR_ID 263 { 264 $$ = npfvar_create_from_string(NPFVAR_VAR_ID, $1); 265 } 266 | TABLE_ID { $$ = npfctl_parse_table_id($1); } 267 | ifaddrs { $$ = $1; } 268 | addr_and_mask { $$ = $1; } 269 ; 270 271 /* 272 * Table definition. 273 */ 274 275 table 276 : TABLE TABLE_ID TYPE table_type table_store 277 { 278 npfctl_build_table($2, $4, $5); 279 } 280 ; 281 282 table_type 283 : HASH { $$ = NPF_TABLE_HASH; } 284 | TREE { $$ = NPF_TABLE_TREE; } 285 | CDB { $$ = NPF_TABLE_CDB; } 286 ; 287 288 table_store 289 : TDYNAMIC { $$ = NULL; } 290 | TFILE STRING { $$ = $2; } 291 ; 292 293 /* 294 * Map definition. 295 */ 296 297 map_sd 298 : TSTATIC { $$ = NPFCTL_NAT_STATIC; } 299 | TDYNAMIC { $$ = NPFCTL_NAT_DYNAMIC; } 300 | { $$ = NPFCTL_NAT_DYNAMIC; } 301 ; 302 303 map_algo 304 : ALGO NPT66 { $$ = NPF_ALGO_NPT66; } 305 | { $$ = 0; } 306 ; 307 308 map_type 309 : ARROWBOTH { $$ = NPF_NATIN | NPF_NATOUT; } 310 | ARROWLEFT { $$ = NPF_NATIN; } 311 | ARROWRIGHT { $$ = NPF_NATOUT; } 312 ; 313 314 mapseg 315 : addr_or_ifaddr port_range 316 { 317 $$.ap_netaddr = $1; 318 $$.ap_portrange = $2; 319 } 320 ; 321 322 map 323 : MAP ifref map_sd map_algo mapseg map_type mapseg PASS filt_opts 324 { 325 npfctl_build_natseg($3, $6, $2, &$5, &$7, &$9, $4); 326 } 327 | MAP ifref map_sd map_algo mapseg map_type mapseg 328 { 329 npfctl_build_natseg($3, $6, $2, &$5, &$7, NULL, $4); 330 } 331 | MAP RULESET group_opts 332 { 333 npfctl_build_maprset($3.rg_name, $3.rg_attr, $3.rg_ifname); 334 } 335 ; 336 337 /* 338 * Rule procedure definition and its parameters. 339 */ 340 341 rproc 342 : PROCEDURE STRING CURLY_OPEN procs CURLY_CLOSE 343 { 344 npfctl_build_rproc($2, $4); 345 } 346 ; 347 348 procs 349 : procs SEPLINE proc_call 350 { 351 $$ = npfvar_add_elements($1, $3); 352 } 353 | proc_call { $$ = $1; } 354 ; 355 356 proc_call 357 : IDENTIFIER COLON proc_param_list 358 { 359 proc_call_t pc; 360 361 pc.pc_name = estrdup($1); 362 pc.pc_opts = $3; 363 364 $$ = npfvar_create_element(NPFVAR_PROC, &pc, sizeof(pc)); 365 } 366 | { $$ = NULL; } 367 ; 368 369 proc_param_list 370 : proc_param_list COMMA proc_param 371 { 372 $$ = npfvar_add_elements($1, $3); 373 } 374 | proc_param { $$ = $1; } 375 | { $$ = NULL; } 376 ; 377 378 proc_param 379 : some_name proc_param_val 380 { 381 proc_param_t pp; 382 383 pp.pp_param = estrdup($1); 384 pp.pp_value = $2 ? estrdup($2) : NULL; 385 386 $$ = npfvar_create_element(NPFVAR_PROC_PARAM, &pp, sizeof(pp)); 387 } 388 ; 389 390 proc_param_val 391 : some_name { $$ = $1; } 392 | number { (void)asprintf(&$$, "%ld", $1); } 393 | FPNUM { (void)asprintf(&$$, "%lf", $1); } 394 | { $$ = NULL; } 395 ; 396 397 /* 398 * Group and dynamic ruleset definition. 399 */ 400 401 group 402 : GROUP group_opts 403 { 404 /* Build a group. Increase the nesting level. */ 405 npfctl_build_group($2.rg_name, $2.rg_attr, 406 $2.rg_ifname, $2.rg_default); 407 } 408 ruleset_block 409 { 410 /* Decrease the nesting level. */ 411 npfctl_build_group_end(); 412 } 413 ; 414 415 ruleset 416 : RULESET group_opts 417 { 418 /* Ruleset is a dynamic group. */ 419 npfctl_build_group($2.rg_name, $2.rg_attr | NPF_RULE_DYNAMIC, 420 $2.rg_ifname, $2.rg_default); 421 npfctl_build_group_end(); 422 } 423 ; 424 425 group_dir 426 : FORW { $$ = NPF_RULE_FORW; } 427 | rule_dir 428 ; 429 430 group_opts 431 : DEFAULT 432 { 433 memset(&$$, 0, sizeof(rule_group_t)); 434 $$.rg_default = true; 435 } 436 | STRING group_dir on_ifname 437 { 438 memset(&$$, 0, sizeof(rule_group_t)); 439 $$.rg_name = $1; 440 $$.rg_attr = $2; 441 $$.rg_ifname = $3; 442 } 443 ; 444 445 ruleset_block 446 : CURLY_OPEN ruleset_def CURLY_CLOSE 447 ; 448 449 ruleset_def 450 : ruleset_def SEPLINE rule_group 451 | rule_group 452 ; 453 454 rule_group 455 : rule 456 | group 457 | ruleset 458 | 459 ; 460 461 /* 462 * Rule and misc. 463 */ 464 465 rule 466 : block_or_pass opt_stateful rule_dir opt_final on_ifname 467 opt_family opt_proto all_or_filt_opts opt_apply 468 { 469 npfctl_build_rule($1 | $2 | $3 | $4, $5, 470 $6, &$7, &$8, NULL, $9); 471 } 472 | block_or_pass opt_stateful rule_dir opt_final on_ifname 473 PCAP_FILTER STRING opt_apply 474 { 475 npfctl_build_rule($1 | $2 | $3 | $4, $5, 476 AF_UNSPEC, NULL, NULL, $7, $8); 477 } 478 ; 479 480 block_or_pass 481 : BLOCK block_opts { $$ = $2; } 482 | PASS { $$ = NPF_RULE_PASS; } 483 ; 484 485 rule_dir 486 : IN { $$ = NPF_RULE_IN; } 487 | OUT { $$ = NPF_RULE_OUT; } 488 | { $$ = NPF_RULE_IN | NPF_RULE_OUT; } 489 ; 490 491 opt_final 492 : FINAL { $$ = NPF_RULE_FINAL; } 493 | { $$ = 0; } 494 ; 495 496 on_ifname 497 : ON ifref { $$ = $2; } 498 | { $$ = NULL; } 499 ; 500 501 afamily 502 : INET4 { $$ = AF_INET; } 503 | INET6 { $$ = AF_INET6; } 504 ; 505 506 opt_family 507 : FAMILY afamily { $$ = $2; } 508 | { $$ = AF_UNSPEC; } 509 ; 510 511 opt_proto 512 : PROTO TCP tcp_flags_and_mask 513 { 514 $$.op_proto = IPPROTO_TCP; 515 $$.op_opts = $3; 516 } 517 | PROTO ICMP icmp_type_and_code 518 { 519 $$.op_proto = IPPROTO_ICMP; 520 $$.op_opts = $3; 521 } 522 | PROTO ICMP6 icmp_type_and_code 523 { 524 $$.op_proto = IPPROTO_ICMPV6; 525 $$.op_opts = $3; 526 } 527 | PROTO some_name 528 { 529 $$.op_proto = npfctl_protono($2); 530 $$.op_opts = NULL; 531 } 532 | PROTO number 533 { 534 $$.op_proto = $2; 535 $$.op_opts = NULL; 536 } 537 | 538 { 539 $$.op_proto = -1; 540 $$.op_opts = NULL; 541 } 542 ; 543 544 all_or_filt_opts 545 : ALL 546 { 547 $$.fo_from.ap_netaddr = NULL; 548 $$.fo_from.ap_portrange = NULL; 549 $$.fo_to.ap_netaddr = NULL; 550 $$.fo_to.ap_portrange = NULL; 551 } 552 | filt_opts { $$ = $1; } 553 ; 554 555 opt_stateful 556 : STATEFUL { $$ = NPF_RULE_STATEFUL; } 557 | STATEFUL_ENDS { $$ = NPF_RULE_STATEFUL | NPF_RULE_MULTIENDS; } 558 | { $$ = 0; } 559 ; 560 561 opt_apply 562 : APPLY STRING { $$ = $2; } 563 | { $$ = NULL; } 564 ; 565 566 block_opts 567 : RETURNRST { $$ = NPF_RULE_RETRST; } 568 | RETURNICMP { $$ = NPF_RULE_RETICMP; } 569 | RETURN { $$ = NPF_RULE_RETRST | NPF_RULE_RETICMP; } 570 | { $$ = 0; } 571 ; 572 573 filt_opts 574 : FROM filt_addr port_range TO filt_addr port_range 575 { 576 $$.fo_from.ap_netaddr = $2; 577 $$.fo_from.ap_portrange = $3; 578 $$.fo_to.ap_netaddr = $5; 579 $$.fo_to.ap_portrange = $6; 580 } 581 | FROM filt_addr port_range 582 { 583 $$.fo_from.ap_netaddr = $2; 584 $$.fo_from.ap_portrange = $3; 585 $$.fo_to.ap_netaddr = NULL; 586 $$.fo_to.ap_portrange = NULL; 587 } 588 | TO filt_addr port_range 589 { 590 $$.fo_from.ap_netaddr = NULL; 591 $$.fo_from.ap_portrange = NULL; 592 $$.fo_to.ap_netaddr = $2; 593 $$.fo_to.ap_portrange = $3; 594 } 595 ; 596 597 filt_addr 598 : addr_or_ifaddr { $$ = $1; } 599 | TABLE_ID { $$ = npfctl_parse_table_id($1); } 600 | ANY { $$ = NULL; } 601 ; 602 603 addr_and_mask 604 : addr SLASH number 605 { 606 $$ = npfctl_parse_fam_addr_mask($1, NULL, &$3); 607 } 608 | addr SLASH addr 609 { 610 $$ = npfctl_parse_fam_addr_mask($1, $3, NULL); 611 } 612 | addr 613 { 614 $$ = npfctl_parse_fam_addr_mask($1, NULL, NULL); 615 } 616 ; 617 618 addr_or_ifaddr 619 : addr_and_mask 620 { 621 assert($1 != NULL); 622 $$ = $1; 623 } 624 | ifaddrs 625 { 626 ifnet_addr_t *ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0); 627 $$ = ifna->ifna_addrs; 628 } 629 | VAR_ID 630 { 631 npfvar_t *vp = npfvar_lookup($1); 632 int type = npfvar_get_type(vp, 0); 633 ifnet_addr_t *ifna; 634 635 again: 636 switch (type) { 637 case NPFVAR_IDENTIFIER: 638 case NPFVAR_STRING: 639 vp = npfctl_parse_ifnet(npfvar_expand_string(vp), 640 AF_UNSPEC); 641 type = npfvar_get_type(vp, 0); 642 goto again; 643 case NPFVAR_FAM: 644 $$ = vp; 645 break; 646 case NPFVAR_INTERFACE: 647 $$ = NULL; 648 for (u_int i = 0; i < npfvar_get_count(vp); i++) { 649 ifna = npfvar_get_data(vp, type, i); 650 $$ = npfvar_add_elements($$, ifna->ifna_addrs); 651 } 652 break; 653 case -1: 654 yyerror("undefined variable '%s'", $1); 655 break; 656 default: 657 yyerror("wrong variable '%s' type '%s' for address " 658 "or interface", $1, npfvar_type(type)); 659 break; 660 } 661 } 662 ; 663 664 addr 665 : IPV4ADDR { $$ = $1; } 666 | IPV6ADDR { $$ = $1; } 667 ; 668 669 port_range 670 : PORT port /* just port */ 671 { 672 $$ = npfctl_parse_port_range($2, $2); 673 } 674 | PORT port MINUS port /* port from-to */ 675 { 676 $$ = npfctl_parse_port_range($2, $4); 677 } 678 | PORT VAR_ID 679 { 680 $$ = npfctl_parse_port_range_variable($2); 681 } 682 | 683 { 684 $$ = NULL; 685 } 686 ; 687 688 port 689 : number { $$ = $1; } 690 | IDENTIFIER { $$ = npfctl_portno($1); } 691 | STRING { $$ = npfctl_portno($1); } 692 ; 693 694 icmp_type_and_code 695 : ICMPTYPE icmp_type 696 { 697 $$ = npfctl_parse_icmp($<num>0, $2, -1); 698 } 699 | ICMPTYPE icmp_type CODE number 700 { 701 $$ = npfctl_parse_icmp($<num>0, $2, $4); 702 } 703 | ICMPTYPE icmp_type CODE IDENTIFIER 704 { 705 $$ = npfctl_parse_icmp($<num>0, $2, 706 npfctl_icmpcode($<num>0, $2, $4)); 707 } 708 | ICMPTYPE icmp_type CODE VAR_ID 709 { 710 char *s = npfvar_expand_string(npfvar_lookup($4)); 711 $$ = npfctl_parse_icmp($<num>0, $2, 712 npfctl_icmpcode($<num>0, $2, s)); 713 } 714 | { $$ = NULL; } 715 ; 716 717 tcp_flags_and_mask 718 : FLAGS tcp_flags SLASH tcp_flags 719 { 720 npfvar_add_elements($2, $4); 721 $$ = $2; 722 } 723 | FLAGS tcp_flags 724 { 725 char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0); 726 npfvar_add_elements($2, npfctl_parse_tcpflag(s)); 727 $$ = $2; 728 } 729 | { $$ = NULL; } 730 ; 731 732 tcp_flags 733 : IDENTIFIER { $$ = npfctl_parse_tcpflag($1); } 734 ; 735 736 icmp_type 737 : number { $$ = $1; } 738 | IDENTIFIER { $$ = npfctl_icmptype($<num>-1, $1); } 739 | VAR_ID 740 { 741 char *s = npfvar_expand_string(npfvar_lookup($1)); 742 $$ = npfctl_icmptype($<num>-1, s); 743 } 744 ; 745 746 ifname 747 : some_name 748 { 749 npfctl_note_interface($1); 750 $$ = $1; 751 } 752 | VAR_ID 753 { 754 npfvar_t *vp = npfvar_lookup($1); 755 const int type = npfvar_get_type(vp, 0); 756 ifnet_addr_t *ifna; 757 758 switch (type) { 759 case NPFVAR_STRING: 760 case NPFVAR_IDENTIFIER: 761 $$ = npfvar_expand_string(vp); 762 break; 763 case NPFVAR_INTERFACE: 764 ifna = npfvar_get_data(vp, type, 0); 765 $$ = ifna->ifna_name; 766 break; 767 case -1: 768 yyerror("undefined variable '%s' for interface", $1); 769 break; 770 default: 771 yyerror("wrong variable '%s' type '%s' for interface", 772 $1, npfvar_type(type)); 773 break; 774 } 775 npfctl_note_interface($$); 776 } 777 ; 778 779 ifaddrs 780 : afamily PAR_OPEN ifname PAR_CLOSE 781 { 782 $$ = npfctl_parse_ifnet($3, $1); 783 } 784 ; 785 786 ifref 787 : ifname 788 | ifaddrs 789 { 790 ifnet_addr_t *ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0); 791 npfctl_note_interface(ifna->ifna_name); 792 $$ = ifna->ifna_name; 793 } 794 ; 795 796 number 797 : HEX { $$ = $1; } 798 | NUM { $$ = $1; } 799 ; 800 801 some_name 802 : IDENTIFIER { $$ = $1; } 803 | STRING { $$ = $1; } 804 ; 805 806 %% 807