1 /* $NetBSD: npf_parse.y,v 1.14 2012/10/02 23:38:52 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 = xstrndup(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 = zalloc(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 = xstrdup($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 = xstrdup($1); 342 pp.pp_value = $2 ? xstrdup($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 614 port_range 615 : PORT port /* just port */ 616 { 617 $$ = npfctl_parse_port_range($2, $2); 618 } 619 | PORT port MINUS port /* port from-to */ 620 { 621 $$ = npfctl_parse_port_range($2, $4); 622 } 623 | PORT VAR_ID 624 { 625 $$ = npfctl_parse_port_range_variable($2); 626 } 627 | 628 { 629 $$ = NULL; 630 } 631 ; 632 633 port 634 : NUM { $$ = $1; } 635 | IDENTIFIER { $$ = npfctl_portno($1); } 636 ; 637 638 icmp_type_and_code 639 : ICMPTYPE icmp_type 640 { 641 $$ = npfctl_parse_icmp($<num>0, $2, -1); 642 } 643 | ICMPTYPE icmp_type CODE NUM 644 { 645 $$ = npfctl_parse_icmp($<num>0, $2, $4); 646 } 647 | ICMPTYPE icmp_type CODE IDENTIFIER 648 { 649 $$ = npfctl_parse_icmp($<num>0, $2, npfctl_icmpcode($<num>0, $2, $4)); 650 } 651 | ICMPTYPE icmp_type CODE VAR_ID 652 { 653 char *s = npfvar_expand_string(npfvar_lookup($4)); 654 $$ = npfctl_parse_icmp($<num>0, $2, npfctl_icmpcode($<num>0, $2, s)); 655 } 656 | 657 { 658 $$ = npfctl_parse_icmp($<num>0, -1, -1); 659 } 660 ; 661 662 tcp_flags_and_mask 663 : FLAGS tcp_flags SLASH tcp_flags 664 { 665 npfvar_add_elements($2, $4); 666 $$ = $2; 667 } 668 | FLAGS tcp_flags 669 { 670 char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0); 671 npfvar_add_elements($2, npfctl_parse_tcpflag(s)); 672 $$ = $2; 673 } 674 | { $$ = NULL; } 675 ; 676 677 tcp_flags 678 : IDENTIFIER { $$ = npfctl_parse_tcpflag($1); } 679 ; 680 681 icmp_type 682 : NUM { $$ = $1; } 683 | IDENTIFIER { $$ = npfctl_icmptype($<num>-1, $1); } 684 | VAR_ID 685 { 686 char *s = npfvar_expand_string(npfvar_lookup($1)); 687 $$ = npfctl_icmptype($<num>-1, s); 688 } 689 ; 690 691 ifindex 692 : some_name 693 { 694 $$ = npfctl_find_ifindex($1); 695 } 696 | VAR_ID 697 { 698 npfvar_t *vp = npfvar_lookup($1); 699 const int type = npfvar_get_type(vp, 0); 700 701 switch (type) { 702 case NPFVAR_VAR_ID: 703 case NPFVAR_STRING: 704 $$ = npfctl_find_ifindex(npfvar_expand_string(vp)); 705 break; 706 case -1: 707 yyerror("undefined variable '%s' for interface", $1); 708 break; 709 default: 710 yyerror("wrong variable '%s' type '%s' for interface", 711 $1, npfvar_type(type)); 712 $$ = -1; 713 break; 714 } 715 } 716 ; 717 718 some_name 719 : IDENTIFIER { $$ = $1; } 720 | STRING { $$ = $1; } 721 ; 722 723 %% 724