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