1 /*- 2 * Copyright (c) 2011-2025 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Martin Husemann, Christos Zoulas and Mindaugas Rasiukevicius. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 %{ 31 32 #include <err.h> 33 #include <netdb.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #ifdef __NetBSD__ 38 #include <vis.h> 39 #endif 40 41 #include "npfctl.h" 42 43 #define YYSTACKSIZE 4096 44 45 int yystarttoken; 46 const char * yyfilename; 47 48 extern int yylineno, yycolumn; 49 extern int yylex(int); 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 #ifdef __NetBSD__ 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 context = dst; 74 #endif 75 fprintf(stderr, " near '%s'", context); 76 } 77 fprintf(stderr, "\n"); 78 exit(EXIT_FAILURE); 79 } 80 81 %} 82 83 /* 84 * No conflicts allowed. Keep it this way. 85 */ 86 %expect 0 87 %expect-rr 0 88 89 /* 90 * Depending on the mode of operation, set a different start symbol. 91 * Workaround yacc limitation by passing the start token. 92 */ 93 %start input 94 %token RULE_ENTRY_TOKEN MAP_ENTRY_TOKEN 95 %lex-param { int yystarttoken } 96 97 /* 98 * General tokens. 99 */ 100 %token ALG 101 %token ALGO 102 %token ALL 103 %token ANY 104 %token APPLY 105 %token ARROWBOTH 106 %token ARROWLEFT 107 %token ARROWRIGHT 108 %token BLOCK 109 %token CDB 110 %token CONST 111 %token CURLY_CLOSE 112 %token CURLY_OPEN 113 %token CODE 114 %token COLON 115 %token COMMA 116 %token DEFAULT 117 %token TDYNAMIC 118 %token TSTATIC 119 %token EQ 120 %token ETHER 121 %token EXCL_MARK 122 %token TFILE 123 %token FLAGS 124 %token FROM 125 %token GROUP 126 %token GT 127 %token HASH 128 %token ICMPTYPE 129 %token ID 130 %token IFADDRS 131 %token IN 132 %token INET4 133 %token INET6 134 %token INTERFACE 135 %token INVALID 136 %token IPHASH 137 %token IPSET 138 %token IRG 139 %token LPM 140 %token L2 141 %token LT 142 %token MAP 143 %token NEWLINE 144 %token NO_PORTS 145 %token MINUS 146 %token NAME 147 %token NETMAP 148 %token NPT66 149 %token ON 150 %token OFF 151 %token OUT 152 %token PAR_CLOSE 153 %token PAR_OPEN 154 %token PASS 155 %token PCAP_FILTER 156 %token PORT 157 %token PROCEDURE 158 %token PROTO 159 %token FAMILY 160 %token FINAL 161 %token FORW 162 %token RETURN 163 %token RETURNICMP 164 %token RETURNRST 165 %token ROUNDROBIN 166 %token RULESET 167 %token SEMICOLON 168 %token SET 169 %token SLASH 170 %token STATEFUL 171 %token STATEFUL_ALL 172 %token TABLE 173 %token TCP 174 %token TO 175 %token TREE 176 %token TYPE 177 %token USER 178 %token XRG 179 %token <num> ICMP 180 %token <num> ICMP6 181 182 %token <num> HEX 183 %token <str> ETHERHEX 184 %token <str> IDENTIFIER 185 %token <str> IPV4ADDR 186 %token <str> IPV6ADDR 187 %token <str> MACADDR 188 %token <num> NUM 189 %token <fpnum> FPNUM 190 %token <str> STRING 191 %token <str> PARAM 192 %token <str> TABLE_ID 193 %token <str> VAR_ID 194 195 %type <str> addr some_name table_store dynamic_ifaddrs 196 %type <str> proc_param_val opt_apply ifname on_ifname ifref 197 %type <num> port opt_final number afamily opt_family 198 %type <num> block_or_pass rule_dir group_dir block_opts 199 %type <num> maybe_not opt_stateful icmp_type table_type 200 %type <num> map_sd map_algo map_flags map_type layer 201 %type <num> param_val op_unary op_binary 202 %type <etype> ether_type 203 %type <rid> uid gid 204 %type <var> static_ifaddrs filt_addr_element 205 %type <var> filt_port filt_port_list port_range icmp_type_and_code 206 %type <var> filt_addr addr_and_mask tcp_flags tcp_flags_and_mask 207 %type <var> procs proc_call proc_param_list proc_param mac_addr 208 %type <var> element list_elems list_trail list value filt_addr_list 209 %type <var> opt_proto proto proto_elems 210 %type <addrport> mapseg 211 %type <uid> uids uid_item user_id 212 %type <gid> gids gid_item group_id 213 %type <filtopts> filt_opts all_or_filt_opts l2_filt_opts l2_all_of_filt_opts 214 %type <optproto> rawproto 215 %type <rulegroup> group_opts 216 217 %union { 218 char * str; 219 uint16_t etype; 220 unsigned long num; 221 uint32_t rid; 222 double fpnum; 223 npfvar_t * var; 224 addr_port_t addrport; 225 filt_opts_t filtopts; 226 opt_proto_t optproto; 227 rule_group_t rulegroup; 228 struct r_id uid; 229 struct r_id gid; 230 } 231 232 %% 233 234 input 235 : lines 236 | RULE_ENTRY_TOKEN rule 237 | MAP_ENTRY_TOKEN map 238 ; 239 240 lines 241 : lines sepline line 242 | line 243 ; 244 245 line 246 : vardef 247 | table 248 | map 249 | group 250 | rproc 251 | alg 252 | set 253 | 254 ; 255 256 alg 257 : ALG STRING 258 { 259 npfctl_build_alg($2); 260 } 261 ; 262 263 sepline 264 : NEWLINE 265 | SEMICOLON 266 ; 267 268 param_val 269 : number { $$ = $1; } 270 | ON { $$ = true; } 271 | OFF { $$ = false; } 272 ; 273 274 set 275 : SET PARAM param_val { 276 npfctl_setparam($2, $3); 277 } 278 ; 279 280 /* 281 * A value - an element or a list of elements. 282 * Can be assigned to a variable or used inline. 283 */ 284 285 vardef 286 : VAR_ID EQ value 287 { 288 npfvar_add($3, $1); 289 } 290 ; 291 292 value 293 : element 294 | list 295 ; 296 297 list 298 : CURLY_OPEN opt_nl list_elems CURLY_CLOSE 299 { 300 $$ = $3; 301 } 302 ; 303 304 list_elems 305 : element list_trail 306 { 307 $$ = npfvar_add_elements($1, $2); 308 } 309 ; 310 311 element 312 : IDENTIFIER 313 { 314 $$ = npfvar_create_from_string(NPFVAR_IDENTIFIER, $1); 315 } 316 | STRING 317 { 318 $$ = npfvar_create_from_string(NPFVAR_STRING, $1); 319 } 320 | number MINUS number 321 { 322 $$ = npfctl_parse_port_range($1, $3); 323 } 324 | number 325 { 326 uint32_t val = $1; 327 $$ = npfvar_create_element(NPFVAR_NUM, &val, sizeof(val)); 328 } 329 | VAR_ID 330 { 331 $$ = npfvar_create_from_string(NPFVAR_VAR_ID, $1); 332 } 333 | TABLE_ID { $$ = npfctl_parse_table_id($1); } 334 | dynamic_ifaddrs { $$ = npfctl_ifnet_table($1); } 335 | static_ifaddrs { $$ = $1; } 336 | addr_and_mask { $$ = $1; } 337 | mac_addr { $$ = $1; } 338 ; 339 340 list_trail 341 : element_sep element list_trail 342 { 343 $$ = npfvar_add_elements($2, $3); 344 } 345 | opt_nl { $$ = NULL; } 346 | element_sep { $$ = NULL; } 347 ; 348 349 element_sep 350 : opt_nl COMMA opt_nl 351 ; 352 353 opt_nl 354 : opt_nl NEWLINE 355 | 356 ; 357 358 /* 359 * Table definition. 360 */ 361 362 table 363 : TABLE TABLE_ID TYPE table_type table_store 364 { 365 npfctl_build_table($2, $4, $5); 366 } 367 ; 368 369 table_type 370 : IPSET { $$ = NPF_TABLE_IPSET; } 371 | HASH 372 { 373 warnx("warning - table type \"hash\" is deprecated and may be " 374 "deleted in\nthe future; please use the \"ipset\" type " 375 "instead."); 376 $$ = NPF_TABLE_IPSET; 377 } 378 | LPM { $$ = NPF_TABLE_LPM; } 379 | TREE 380 { 381 warnx("warning - table type \"tree\" is deprecated and may be " 382 "deleted in\nthe future; please use the \"lpm\" type " 383 "instead."); 384 $$ = NPF_TABLE_LPM; 385 } 386 | CONST { $$ = NPF_TABLE_CONST; } 387 | CDB 388 { 389 warnx("warning -- table type \"cdb\" is deprecated and may be " 390 "deleted in\nthe future; please use the \"const\" type " 391 "instead."); 392 $$ = NPF_TABLE_CONST; 393 } 394 ; 395 396 table_store 397 : TFILE STRING { $$ = $2; } 398 | TDYNAMIC 399 { 400 warnx("warning - the \"dynamic\" keyword for tables is obsolete"); 401 $$ = NULL; 402 } 403 | { $$ = NULL; } 404 ; 405 406 /* 407 * Map definition. 408 */ 409 410 map_sd 411 : TSTATIC { $$ = NPFCTL_NAT_STATIC; } 412 | TDYNAMIC { $$ = NPFCTL_NAT_DYNAMIC; } 413 | { $$ = NPFCTL_NAT_DYNAMIC; } 414 ; 415 416 map_algo 417 : ALGO NETMAP { $$ = NPF_ALGO_NETMAP; } 418 | ALGO IPHASH { $$ = NPF_ALGO_IPHASH; } 419 | ALGO ROUNDROBIN { $$ = NPF_ALGO_RR; } 420 | ALGO NPT66 { $$ = NPF_ALGO_NPT66; } 421 | { $$ = 0; } 422 ; 423 424 map_flags 425 : NO_PORTS { $$ = NPF_NAT_PORTS; } 426 | { $$ = 0; } 427 ; 428 429 map_type 430 : ARROWBOTH { $$ = NPF_NATIN | NPF_NATOUT; } 431 | ARROWLEFT { $$ = NPF_NATIN; } 432 | ARROWRIGHT { $$ = NPF_NATOUT; } 433 ; 434 435 mapseg 436 : filt_addr filt_port 437 { 438 $$.ap_netaddr = $1; 439 $$.ap_portrange = $2; 440 } 441 ; 442 443 map 444 : MAP ifref map_sd map_algo map_flags mapseg map_type mapseg 445 PASS opt_family opt_proto all_or_filt_opts 446 { 447 npfctl_build_natseg($3, $7, $5, $2, &$6, &$8, $11, &$12, $4); 448 } 449 | MAP ifref map_sd map_algo map_flags mapseg map_type mapseg 450 { 451 npfctl_build_natseg($3, $7, $5, $2, &$6, &$8, NULL, NULL, $4); 452 } 453 | MAP ifref map_sd map_algo map_flags proto mapseg map_type mapseg 454 { 455 npfctl_build_natseg($3, $8, $5, $2, &$7, &$9, $6, NULL, $4); 456 } 457 | MAP RULESET group_opts 458 { 459 npfctl_build_maprset($3.rg_name, $3.rg_attr, $3.rg_ifname); 460 } 461 ; 462 463 /* 464 * Rule procedure definition and its parameters. 465 */ 466 467 rproc 468 : PROCEDURE STRING CURLY_OPEN procs CURLY_CLOSE 469 { 470 npfctl_build_rproc($2, $4); 471 } 472 ; 473 474 procs 475 : procs sepline proc_call 476 { 477 $$ = npfvar_add_elements($1, $3); 478 } 479 | proc_call { $$ = $1; } 480 ; 481 482 proc_call 483 : IDENTIFIER COLON proc_param_list 484 { 485 proc_call_t pc; 486 487 pc.pc_name = estrdup($1); 488 pc.pc_opts = $3; 489 490 $$ = npfvar_create_element(NPFVAR_PROC, &pc, sizeof(pc)); 491 } 492 | { $$ = NULL; } 493 ; 494 495 proc_param_list 496 : proc_param_list COMMA proc_param 497 { 498 $$ = npfvar_add_elements($1, $3); 499 } 500 | proc_param { $$ = $1; } 501 | { $$ = NULL; } 502 ; 503 504 proc_param 505 : some_name proc_param_val 506 { 507 proc_param_t pp; 508 509 pp.pp_param = estrdup($1); 510 pp.pp_value = $2 ? estrdup($2) : NULL; 511 512 $$ = npfvar_create_element(NPFVAR_PROC_PARAM, &pp, sizeof(pp)); 513 } 514 ; 515 516 proc_param_val 517 : some_name { $$ = $1; } 518 | number { (void)asprintf(&$$, "%ld", $1); } 519 | FPNUM { (void)asprintf(&$$, "%lf", $1); } 520 | { $$ = NULL; } 521 ; 522 523 /* 524 * Group and dynamic ruleset definition. 525 */ 526 527 group 528 : GROUP group_opts 529 { 530 /* Build a group. Increase the nesting level. */ 531 npfctl_build_group($2.rg_name, $2.rg_attr, 532 $2.rg_ifname, $2.rg_default); 533 } 534 ruleset_block 535 { 536 /* Decrease the nesting level. */ 537 npfctl_build_group_end(); 538 } 539 ; 540 541 ruleset 542 : RULESET group_opts 543 { 544 /* Ruleset is a dynamic group. */ 545 npfctl_build_group($2.rg_name, $2.rg_attr | NPF_RULE_DYNAMIC, 546 $2.rg_ifname, $2.rg_default); 547 npfctl_build_group_end(); 548 } 549 ; 550 551 group_dir 552 : FORW { $$ = NPF_RULE_FORW; } 553 | rule_dir 554 ; 555 556 group_opts 557 : DEFAULT layer 558 { 559 memset(&$$, 0, sizeof(rule_group_t)); 560 $$.rg_default = true; 561 $$.rg_attr |= $2; 562 } 563 | STRING group_dir on_ifname layer 564 { 565 memset(&$$, 0, sizeof(rule_group_t)); 566 $$.rg_name = $1; 567 $$.rg_attr = $2 | $4; 568 $$.rg_ifname = $3; 569 } 570 ; 571 572 layer 573 : L2 { $$ = NPF_RULE_LAYER_2; } 574 | { $$ = NPF_RULE_LAYER_3; } /* ret layer3 by default */ 575 ; 576 577 ruleset_block 578 : CURLY_OPEN ruleset_def CURLY_CLOSE 579 ; 580 581 ruleset_def 582 : ruleset_def sepline rule_group 583 | rule_group 584 ; 585 586 rule_group 587 : rule 588 | group 589 | ruleset 590 | 591 ; 592 593 /* 594 * Rule and misc. 595 */ 596 597 rule 598 : block_or_pass opt_stateful rule_dir opt_final on_ifname 599 opt_family opt_proto all_or_filt_opts opt_apply 600 { 601 npfctl_build_rule($1 | $2 | $3 | $4, $5, 602 $6, $7, &$8, NULL, $9); 603 } 604 | block_or_pass opt_stateful rule_dir opt_final on_ifname 605 PCAP_FILTER STRING opt_apply 606 { 607 npfctl_build_rule($1 | $2 | $3 | $4, $5, 608 AF_UNSPEC, NULL, NULL, $7, $8); 609 } 610 | block_or_pass ETHER rule_dir opt_final on_ifname 611 l2_all_of_filt_opts 612 { 613 npfctl_build_rule($1 | $3 | $4, $5, 0, NULL, &$6, NULL, NULL); 614 } 615 ; 616 617 block_or_pass 618 : BLOCK block_opts { $$ = $2; } 619 | PASS { $$ = NPF_RULE_PASS; } 620 ; 621 622 rule_dir 623 : IN { $$ = NPF_RULE_IN; } 624 | OUT { $$ = NPF_RULE_OUT; } 625 | { $$ = NPF_RULE_IN | NPF_RULE_OUT; } 626 ; 627 628 opt_final 629 : FINAL { $$ = NPF_RULE_FINAL; } 630 | { $$ = 0; } 631 ; 632 633 on_ifname 634 : ON ifref { $$ = $2; } 635 | { $$ = NULL; } 636 ; 637 638 afamily 639 : INET4 { $$ = AF_INET; } 640 | INET6 { $$ = AF_INET6; } 641 ; 642 643 maybe_not 644 : EXCL_MARK { $$ = true; } 645 | { $$ = false; } 646 ; 647 648 opt_family 649 : FAMILY afamily { $$ = $2; } 650 | { $$ = AF_UNSPEC; } 651 ; 652 653 rawproto 654 : TCP tcp_flags_and_mask 655 { 656 $$.op_proto = IPPROTO_TCP; 657 $$.op_opts = $2; 658 } 659 | ICMP icmp_type_and_code 660 { 661 $$.op_proto = IPPROTO_ICMP; 662 $$.op_opts = $2; 663 } 664 | ICMP6 icmp_type_and_code 665 { 666 $$.op_proto = IPPROTO_ICMPV6; 667 $$.op_opts = $2; 668 } 669 | some_name 670 { 671 $$.op_proto = npfctl_protono($1); 672 $$.op_opts = NULL; 673 } 674 | number 675 { 676 $$.op_proto = $1; 677 $$.op_opts = NULL; 678 } 679 ; 680 681 proto_elems 682 : proto_elems COMMA rawproto 683 { 684 npfvar_t *pvar = npfvar_create_element( 685 NPFVAR_PROTO, &$3, sizeof($3)); 686 $$ = npfvar_add_elements($1, pvar); 687 } 688 | rawproto 689 { 690 $$ = npfvar_create_element(NPFVAR_PROTO, &$1, sizeof($1)); 691 } 692 ; 693 694 proto 695 : PROTO rawproto 696 { 697 $$ = npfvar_create_element(NPFVAR_PROTO, &$2, sizeof($2)); 698 } 699 | PROTO CURLY_OPEN proto_elems CURLY_CLOSE 700 { 701 $$ = $3; 702 } 703 ; 704 705 opt_proto 706 : proto { $$ = $1; } 707 | { $$ = NULL; } 708 ; 709 710 all_or_filt_opts 711 : ALL user_id group_id 712 { 713 $$ = npfctl_parse_l3filt_opt(NULL, NULL, false, NULL, NULL, false, $2, $3); 714 } 715 | filt_opts { $$ = $1; } 716 ; 717 718 l2_all_of_filt_opts 719 : ALL 720 { 721 $$ = npfctl_parse_l2filt_opt(NULL, false, NULL, false, 0); 722 } 723 | l2_filt_opts { $$ = $1; } 724 ; 725 726 opt_stateful 727 : STATEFUL { $$ = NPF_RULE_STATEFUL; } 728 | STATEFUL_ALL { $$ = NPF_RULE_STATEFUL | NPF_RULE_GSTATEFUL; } 729 | { $$ = 0; } 730 ; 731 732 opt_apply 733 : APPLY STRING { $$ = $2; } 734 | { $$ = NULL; } 735 ; 736 737 block_opts 738 : RETURNRST { $$ = NPF_RULE_RETRST; } 739 | RETURNICMP { $$ = NPF_RULE_RETICMP; } 740 | RETURN { $$ = NPF_RULE_RETRST | NPF_RULE_RETICMP; } 741 | { $$ = 0; } 742 ; 743 744 filt_opts 745 : FROM maybe_not filt_addr filt_port TO maybe_not filt_addr filt_port 746 user_id group_id 747 { 748 $$ = npfctl_parse_l3filt_opt($3, $4, $2, $7, $8, $6, $9, $10); 749 } 750 | FROM maybe_not filt_addr filt_port user_id group_id 751 { 752 $$ = npfctl_parse_l3filt_opt($3, $4, $2, NULL, NULL, false, $5, $6); 753 } 754 | TO maybe_not filt_addr filt_port user_id group_id 755 { 756 $$ = npfctl_parse_l3filt_opt(NULL, NULL, false, $3, $4, $2, $5, $6); 757 } 758 ; 759 760 l2_filt_opts 761 : FROM maybe_not filt_addr TO maybe_not filt_addr ether_type 762 { 763 $$ = npfctl_parse_l2filt_opt($3, $2, $6, $5, $7); 764 765 } 766 | FROM maybe_not filt_addr ether_type 767 { 768 $$ = npfctl_parse_l2filt_opt($3, $2, NULL, false, $4); 769 } 770 | TO maybe_not filt_addr ether_type 771 { 772 $$ = npfctl_parse_l2filt_opt(NULL, false, $3, $2, $4); 773 } 774 ; 775 776 ether_type 777 : TYPE ETHERHEX { $$ = npfctl_parse_ether_type($2); } 778 | { $$ = 0; } 779 ; 780 781 filt_addr_list 782 : filt_addr_list COMMA filt_addr_element 783 { 784 npfvar_add_elements($1, $3); 785 } 786 | filt_addr_element 787 ; 788 789 filt_addr 790 : CURLY_OPEN filt_addr_list CURLY_CLOSE 791 { 792 $$ = $2; 793 } 794 | filt_addr_element { $$ = $1; } 795 | ANY { $$ = NULL; } 796 ; 797 798 addr_and_mask 799 : addr SLASH number 800 { 801 $$ = npfctl_parse_fam_addr_mask($1, NULL, &$3); 802 } 803 | addr SLASH addr 804 { 805 $$ = npfctl_parse_fam_addr_mask($1, $3, NULL); 806 } 807 | addr 808 { 809 $$ = npfctl_parse_fam_addr_mask($1, NULL, NULL); 810 } 811 ; 812 813 mac_addr 814 : MACADDR 815 { 816 $$ = npfctl_parse_mac_addr($1); 817 } 818 ; 819 820 filt_addr_element 821 : addr_and_mask { assert($1 != NULL); $$ = $1; } 822 | mac_addr { assert($1 != NULL); $$ = $1; } 823 | static_ifaddrs 824 { 825 if (npfvar_get_count($1) != 1) 826 yyerror("multiple interfaces are not supported"); 827 ifnet_addr_t *ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0); 828 $$ = ifna->ifna_addrs; 829 } 830 | dynamic_ifaddrs { $$ = npfctl_ifnet_table($1); } 831 | TABLE_ID { $$ = npfctl_parse_table_id($1); } 832 | VAR_ID 833 { 834 npfvar_t *vp = npfvar_lookup($1); 835 int type = npfvar_get_type(vp, 0); 836 ifnet_addr_t *ifna; 837 again: 838 switch (type) { 839 case NPFVAR_IDENTIFIER: 840 case NPFVAR_STRING: 841 vp = npfctl_parse_ifnet(npfvar_expand_string(vp), 842 AF_UNSPEC); 843 type = npfvar_get_type(vp, 0); 844 goto again; 845 case NPFVAR_FAM: 846 case NPFVAR_MAC: 847 case NPFVAR_TABLE: 848 $$ = vp; 849 break; 850 case NPFVAR_INTERFACE: 851 $$ = NULL; 852 for (u_int i = 0; i < npfvar_get_count(vp); i++) { 853 ifna = npfvar_get_data(vp, type, i); 854 $$ = npfvar_add_elements($$, ifna->ifna_addrs); 855 } 856 break; 857 case -1: 858 yyerror("undefined variable '%s'", $1); 859 break; 860 default: 861 yyerror("wrong variable '%s' type '%s' for address " 862 "or interface", $1, npfvar_type(type)); 863 break; 864 } 865 } 866 ; 867 868 addr 869 : IPV4ADDR { $$ = $1; } 870 | IPV6ADDR { $$ = $1; } 871 ; 872 873 filt_port 874 : PORT CURLY_OPEN filt_port_list CURLY_CLOSE 875 { 876 $$ = npfctl_parse_port_range_variable(NULL, $3); 877 } 878 | PORT port_range { $$ = $2; } 879 | { $$ = NULL; } 880 ; 881 882 filt_port_list 883 : filt_port_list COMMA port_range 884 { 885 npfvar_add_elements($1, $3); 886 } 887 | port_range 888 ; 889 890 port_range 891 : port /* just port */ 892 { 893 $$ = npfctl_parse_port_range($1, $1); 894 } 895 | port MINUS port /* port from-to */ 896 { 897 $$ = npfctl_parse_port_range($1, $3); 898 } 899 | VAR_ID 900 { 901 npfvar_t *vp; 902 if ((vp = npfvar_lookup($1)) == NULL) 903 yyerror("undefined port variable %s", $1); 904 $$ = npfctl_parse_port_range_variable($1, vp); 905 } 906 ; 907 908 port 909 : number { $$ = $1; } 910 | IDENTIFIER { $$ = npfctl_portno($1); } 911 | STRING { $$ = npfctl_portno($1); } 912 ; 913 914 icmp_type_and_code 915 : ICMPTYPE icmp_type 916 { 917 $$ = npfctl_parse_icmp($<num>0, $2, -1); 918 } 919 | ICMPTYPE icmp_type CODE number 920 { 921 $$ = npfctl_parse_icmp($<num>0, $2, $4); 922 } 923 | ICMPTYPE icmp_type CODE IDENTIFIER 924 { 925 $$ = npfctl_parse_icmp($<num>0, $2, 926 npfctl_icmpcode($<num>0, $2, $4)); 927 } 928 | ICMPTYPE icmp_type CODE VAR_ID 929 { 930 char *s = npfvar_expand_string(npfvar_lookup($4)); 931 $$ = npfctl_parse_icmp($<num>0, $2, 932 npfctl_icmpcode($<num>0, $2, s)); 933 } 934 | { $$ = NULL; } 935 ; 936 937 tcp_flags_and_mask 938 : FLAGS tcp_flags SLASH tcp_flags 939 { 940 npfvar_add_elements($2, $4); 941 $$ = $2; 942 } 943 | FLAGS tcp_flags 944 { 945 if (npfvar_get_count($2) != 1) 946 yyerror("multiple tcpflags are not supported"); 947 char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0); 948 npfvar_add_elements($2, npfctl_parse_tcpflag(s)); 949 $$ = $2; 950 } 951 | { $$ = NULL; } 952 ; 953 954 tcp_flags 955 : IDENTIFIER { $$ = npfctl_parse_tcpflag($1); } 956 ; 957 958 icmp_type 959 : number { $$ = $1; } 960 | IDENTIFIER { $$ = npfctl_icmptype($<num>-1, $1); } 961 | VAR_ID 962 { 963 char *s = npfvar_expand_string(npfvar_lookup($1)); 964 $$ = npfctl_icmptype($<num>-1, s); 965 } 966 ; 967 968 ifname 969 : some_name 970 { 971 npfctl_note_interface($1); 972 $$ = $1; 973 } 974 | VAR_ID 975 { 976 npfvar_t *vp = npfvar_lookup($1); 977 const int type = npfvar_get_type(vp, 0); 978 ifnet_addr_t *ifna; 979 const char *name; 980 unsigned *tid; 981 bool ifaddr; 982 983 switch (type) { 984 case NPFVAR_STRING: 985 case NPFVAR_IDENTIFIER: 986 $$ = npfvar_expand_string(vp); 987 break; 988 case NPFVAR_INTERFACE: 989 if (npfvar_get_count(vp) != 1) 990 yyerror( 991 "multiple interfaces are not supported"); 992 ifna = npfvar_get_data(vp, type, 0); 993 $$ = ifna->ifna_name; 994 break; 995 case NPFVAR_TABLE: 996 tid = npfvar_get_data(vp, type, 0); 997 name = npfctl_table_getname(npfctl_config_ref(), 998 *tid, &ifaddr); 999 if (!ifaddr) { 1000 yyerror("variable '%s' references a table " 1001 "%s instead of an interface", $1, name); 1002 } 1003 $$ = estrdup(name); 1004 break; 1005 case -1: 1006 yyerror("undefined variable '%s' for interface", $1); 1007 break; 1008 default: 1009 yyerror("wrong variable '%s' type '%s' for interface", 1010 $1, npfvar_type(type)); 1011 break; 1012 } 1013 npfctl_note_interface($$); 1014 } 1015 ; 1016 1017 static_ifaddrs 1018 : afamily PAR_OPEN ifname PAR_CLOSE 1019 { 1020 $$ = npfctl_parse_ifnet($3, $1); 1021 } 1022 ; 1023 1024 dynamic_ifaddrs 1025 : IFADDRS PAR_OPEN ifname PAR_CLOSE 1026 { 1027 $$ = $3; 1028 } 1029 ; 1030 1031 ifref 1032 : ifname 1033 | dynamic_ifaddrs 1034 | static_ifaddrs 1035 { 1036 ifnet_addr_t *ifna; 1037 1038 if (npfvar_get_count($1) != 1) { 1039 yyerror("multiple interfaces are not supported"); 1040 } 1041 ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0); 1042 npfctl_note_interface(ifna->ifna_name); 1043 $$ = ifna->ifna_name; 1044 } 1045 ; 1046 1047 user_id 1048 : /* empty */ { $$.op = NPF_OP_NONE; } 1049 | USER uids { $$ = $2; } 1050 ; 1051 1052 uids 1053 : uid_item { $$ = $1; } 1054 ; 1055 1056 uid_item 1057 : uid 1058 { 1059 npfctl_init_rid(&$$, $1, $1, NPF_OP_EQ); 1060 } 1061 | op_unary uid 1062 { 1063 npfctl_init_rid(&$$, $2, $2, $1); 1064 } 1065 | uid op_binary uid 1066 { 1067 npfctl_init_rid(&$$, $1, $3, $2); 1068 } 1069 ; 1070 1071 uid 1072 : NUM 1073 { 1074 if ($1 >= UID_MAX) { 1075 yyerror("illegal uid value %lu", $1); 1076 } 1077 $$ = $1; 1078 } 1079 | IDENTIFIER 1080 { 1081 if (npfctl_parse_user($1, &$$) == -1) { 1082 yyerror("unknown user %s", $1); 1083 } 1084 } 1085 | VAR_ID 1086 { 1087 npf_var_rid($1, npfctl_parse_user, &$$, "user"); 1088 } 1089 ; 1090 1091 group_id 1092 : /* empty */ { $$.op = NPF_OP_NONE; } 1093 | GROUP gids { $$ = $2; } 1094 ; 1095 1096 gids 1097 : gid_item { $$ = $1; } 1098 ; 1099 1100 gid_item 1101 : gid 1102 { 1103 npfctl_init_rid(&$$, $1, $1, NPF_OP_EQ); 1104 } 1105 | op_unary gid 1106 { 1107 npfctl_init_rid(&$$, $2, $2, $1); 1108 } 1109 | gid op_binary gid 1110 { 1111 npfctl_init_rid(&$$, $1, $3, $2); 1112 } 1113 ; 1114 1115 gid 1116 : NUM 1117 { 1118 if ($1 >= GID_MAX) { 1119 yyerror("illegal gid value %lu", $1); 1120 } 1121 $$ = $1; 1122 } 1123 | IDENTIFIER 1124 { 1125 if (npfctl_parse_group($1, &$$) == -1) { 1126 yyerror("unknown group %s", $1); 1127 } 1128 } 1129 | VAR_ID 1130 { 1131 npf_var_rid($1, npfctl_parse_group, &$$, "group"); 1132 } 1133 ; 1134 1135 op_unary 1136 : EQ { $$ = NPF_OP_EQ; } 1137 | EXCL_MARK EQ { $$ = NPF_OP_NE; } 1138 | LT EQ { $$ = NPF_OP_LE; } 1139 | LT { $$ = NPF_OP_LT; } 1140 | GT EQ { $$ = NPF_OP_GE; } 1141 | GT { $$ = NPF_OP_GT; } 1142 ; 1143 1144 op_binary 1145 : XRG { $$ = NPF_OP_XRG; } 1146 | IRG { $$ = NPF_OP_IRG; } 1147 ; 1148 1149 number 1150 : HEX { $$ = $1; } 1151 | NUM { $$ = $1; } 1152 ; 1153 1154 some_name 1155 : IDENTIFIER { $$ = $1; } 1156 | STRING { $$ = $1; } 1157 ; 1158 1159 %% 1160