1 /* $NetBSD: npf_build.c,v 1.31 2013/11/22 00:25:51 rmind Exp $ */ 2 3 /*- 4 * Copyright (c) 2011-2013 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This material is based upon work partially supported by The 8 * NetBSD Foundation under a contract with 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 * npfctl(8) building of the configuration. 34 */ 35 36 #include <sys/cdefs.h> 37 __RCSID("$NetBSD: npf_build.c,v 1.31 2013/11/22 00:25:51 rmind Exp $"); 38 39 #include <sys/types.h> 40 #include <sys/ioctl.h> 41 42 #include <stdlib.h> 43 #include <inttypes.h> 44 #include <string.h> 45 #include <ctype.h> 46 #include <errno.h> 47 #include <err.h> 48 49 #include <pcap/pcap.h> 50 51 #include "npfctl.h" 52 53 #define MAX_RULE_NESTING 16 54 55 static nl_config_t * npf_conf = NULL; 56 static bool npf_debug = false; 57 static nl_rule_t * the_rule = NULL; 58 59 static nl_rule_t * current_group[MAX_RULE_NESTING]; 60 static unsigned rule_nesting_level = 0; 61 static nl_rule_t * defgroup = NULL; 62 63 static void npfctl_dump_bpf(struct bpf_program *); 64 65 void 66 npfctl_config_init(bool debug) 67 { 68 npf_conf = npf_config_create(); 69 if (npf_conf == NULL) { 70 errx(EXIT_FAILURE, "npf_config_create failed"); 71 } 72 npf_debug = debug; 73 memset(current_group, 0, sizeof(current_group)); 74 } 75 76 int 77 npfctl_config_send(int fd, const char *out) 78 { 79 int error; 80 81 if (out) { 82 _npf_config_setsubmit(npf_conf, out); 83 printf("\nSaving to %s\n", out); 84 } 85 if (!defgroup) { 86 errx(EXIT_FAILURE, "default group was not defined"); 87 } 88 npf_rule_insert(npf_conf, NULL, defgroup); 89 error = npf_config_submit(npf_conf, fd); 90 if (error) { 91 nl_error_t ne; 92 _npf_config_error(npf_conf, &ne); 93 npfctl_print_error(&ne); 94 } 95 if (fd) { 96 npf_config_destroy(npf_conf); 97 } 98 return error; 99 } 100 101 nl_config_t * 102 npfctl_config_ref(void) 103 { 104 return npf_conf; 105 } 106 107 nl_rule_t * 108 npfctl_rule_ref(void) 109 { 110 return the_rule; 111 } 112 113 bool 114 npfctl_debug_addif(const char *ifname) 115 { 116 const char tname[] = "npftest"; 117 const size_t tnamelen = sizeof(tname) - 1; 118 119 if (npf_debug) { 120 _npf_debug_addif(npf_conf, ifname); 121 return strncmp(ifname, tname, tnamelen) == 0; 122 } 123 return 0; 124 } 125 126 bool 127 npfctl_table_exists_p(const char *name) 128 { 129 return npf_conf ? npf_table_exists_p(npf_conf, name) : false; 130 } 131 132 static in_port_t 133 npfctl_get_singleport(const npfvar_t *vp) 134 { 135 port_range_t *pr; 136 in_port_t *port; 137 138 if (npfvar_get_count(vp) > 1) { 139 yyerror("multiple ports are not valid"); 140 } 141 pr = npfvar_get_data(vp, NPFVAR_PORT_RANGE, 0); 142 if (pr->pr_start != pr->pr_end) { 143 yyerror("port range is not valid"); 144 } 145 port = &pr->pr_start; 146 return *port; 147 } 148 149 static fam_addr_mask_t * 150 npfctl_get_singlefam(const npfvar_t *vp) 151 { 152 if (npfvar_get_count(vp) > 1) { 153 yyerror("multiple addresses are not valid"); 154 } 155 return npfvar_get_data(vp, NPFVAR_FAM, 0); 156 } 157 158 static bool 159 npfctl_build_fam(npf_bpf_t *ctx, sa_family_t family, 160 fam_addr_mask_t *fam, int opts) 161 { 162 /* 163 * If family is specified, address does not match it and the 164 * address is extracted from the interface, then simply ignore. 165 * Otherwise, address of invalid family was passed manually. 166 */ 167 if (family != AF_UNSPEC && family != fam->fam_family) { 168 if (!fam->fam_ifindex) { 169 yyerror("specified address is not of the required " 170 "family %d", family); 171 } 172 return false; 173 } 174 175 family = fam->fam_family; 176 if (family != AF_INET && family != AF_INET6) { 177 yyerror("family %d is not supported", family); 178 } 179 180 /* 181 * Optimise 0.0.0.0/0 case to be NOP. Otherwise, address with 182 * zero mask would never match and therefore is not valid. 183 */ 184 if (fam->fam_mask == 0) { 185 static const npf_addr_t zero; /* must be static */ 186 187 if (memcmp(&fam->fam_addr, &zero, sizeof(npf_addr_t))) { 188 yyerror("filter criterion would never match"); 189 } 190 return false; 191 } 192 193 npfctl_bpf_cidr(ctx, opts, family, &fam->fam_addr, fam->fam_mask); 194 return true; 195 } 196 197 static void 198 npfctl_build_vars(npf_bpf_t *ctx, sa_family_t family, npfvar_t *vars, int opts) 199 { 200 const int type = npfvar_get_type(vars, 0); 201 size_t i; 202 203 npfctl_bpf_group(ctx); 204 for (i = 0; i < npfvar_get_count(vars); i++) { 205 void *data = npfvar_get_data(vars, type, i); 206 assert(data != NULL); 207 208 switch (type) { 209 case NPFVAR_FAM: { 210 fam_addr_mask_t *fam = data; 211 npfctl_build_fam(ctx, family, fam, opts); 212 break; 213 } 214 case NPFVAR_PORT_RANGE: { 215 port_range_t *pr = data; 216 npfctl_bpf_ports(ctx, opts, pr->pr_start, pr->pr_end); 217 break; 218 } 219 case NPFVAR_TABLE: { 220 u_int tid = atoi(data); 221 npfctl_bpf_table(ctx, opts, tid); 222 break; 223 } 224 default: 225 assert(false); 226 } 227 } 228 npfctl_bpf_endgroup(ctx); 229 } 230 231 static void 232 npfctl_build_proto(npf_bpf_t *ctx, sa_family_t family, const opt_proto_t *op) 233 { 234 const npfvar_t *popts = op->op_opts; 235 const int proto = op->op_proto; 236 237 /* IP version and/or L4 protocol matching. */ 238 if (family != AF_UNSPEC || proto != -1) { 239 npfctl_bpf_proto(ctx, family, proto); 240 } 241 242 switch (proto) { 243 case IPPROTO_TCP: 244 /* Build TCP flags matching (optional). */ 245 if (popts) { 246 uint8_t *tf, *tf_mask; 247 248 assert(npfvar_get_count(popts) == 2); 249 tf = npfvar_get_data(popts, NPFVAR_TCPFLAG, 0); 250 tf_mask = npfvar_get_data(popts, NPFVAR_TCPFLAG, 1); 251 npfctl_bpf_tcpfl(ctx, *tf, *tf_mask); 252 } 253 break; 254 case IPPROTO_ICMP: 255 case IPPROTO_ICMPV6: 256 /* Build ICMP/ICMPv6 type and/or code matching. */ 257 if (popts) { 258 int *icmp_type, *icmp_code; 259 260 assert(npfvar_get_count(popts) == 2); 261 icmp_type = npfvar_get_data(popts, NPFVAR_ICMP, 0); 262 icmp_code = npfvar_get_data(popts, NPFVAR_ICMP, 1); 263 npfctl_bpf_icmp(ctx, *icmp_type, *icmp_code); 264 } 265 break; 266 default: 267 /* No options for other protocols. */ 268 break; 269 } 270 } 271 272 static bool 273 npfctl_build_code(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op, 274 const filt_opts_t *fopts) 275 { 276 const addr_port_t *apfrom = &fopts->fo_from; 277 const addr_port_t *apto = &fopts->fo_to; 278 const int proto = op->op_proto; 279 bool noproto, noaddrs, noports; 280 npf_bpf_t *bc; 281 size_t len; 282 283 /* If none specified, then no byte-code. */ 284 noproto = family == AF_UNSPEC && proto == -1 && !op->op_opts; 285 noaddrs = !apfrom->ap_netaddr && !apto->ap_netaddr; 286 noports = !apfrom->ap_portrange && !apto->ap_portrange; 287 if (noproto && noaddrs && noports) { 288 return false; 289 } 290 291 /* 292 * Sanity check: ports can only be used with TCP or UDP protocol. 293 * No filter options are supported for other protocols, only the 294 * IP addresses are allowed. 295 */ 296 if (!noports) { 297 switch (proto) { 298 case IPPROTO_TCP: 299 case IPPROTO_UDP: 300 case -1: 301 break; 302 default: 303 yyerror("invalid filter options for protocol %d", proto); 304 } 305 } 306 307 bc = npfctl_bpf_create(); 308 309 /* Build layer 4 protocol blocks. */ 310 npfctl_build_proto(bc, family, op); 311 312 /* Build IP address blocks. */ 313 npfctl_build_vars(bc, family, apfrom->ap_netaddr, MATCH_SRC); 314 npfctl_build_vars(bc, family, apto->ap_netaddr, MATCH_DST); 315 316 /* Build port-range blocks. */ 317 npfctl_build_vars(bc, family, apfrom->ap_portrange, MATCH_SRC); 318 npfctl_build_vars(bc, family, apto->ap_portrange, MATCH_DST); 319 320 /* Set the byte-code marks, if any. */ 321 const void *bmarks = npfctl_bpf_bmarks(bc, &len); 322 if (npf_rule_setinfo(rl, bmarks, len) == -1) { 323 errx(EXIT_FAILURE, "npf_rule_setinfo failed"); 324 } 325 326 /* Complete BPF byte-code and pass to the rule. */ 327 struct bpf_program *bf = npfctl_bpf_complete(bc); 328 len = bf->bf_len * sizeof(struct bpf_insn); 329 330 if (npf_rule_setcode(rl, NPF_CODE_BPF, bf->bf_insns, len) == -1) { 331 errx(EXIT_FAILURE, "npf_rule_setcode failed"); 332 } 333 npfctl_dump_bpf(bf); 334 npfctl_bpf_destroy(bc); 335 336 return true; 337 } 338 339 static void 340 npfctl_build_pcap(nl_rule_t *rl, const char *filter) 341 { 342 const size_t maxsnaplen = 64 * 1024; 343 struct bpf_program bf; 344 size_t len; 345 346 if (pcap_compile_nopcap(maxsnaplen, DLT_RAW, &bf, 347 filter, 1, PCAP_NETMASK_UNKNOWN) == -1) { 348 yyerror("invalid pcap-filter(7) syntax"); 349 } 350 len = bf.bf_len * sizeof(struct bpf_insn); 351 352 if (npf_rule_setcode(rl, NPF_CODE_BPF, bf.bf_insns, len) == -1) { 353 errx(EXIT_FAILURE, "npf_rule_setcode failed"); 354 } 355 npfctl_dump_bpf(&bf); 356 pcap_freecode(&bf); 357 } 358 359 static void 360 npfctl_build_rpcall(nl_rproc_t *rp, const char *name, npfvar_t *args) 361 { 362 npf_extmod_t *extmod; 363 nl_ext_t *extcall; 364 int error; 365 366 extmod = npf_extmod_get(name, &extcall); 367 if (extmod == NULL) { 368 yyerror("unknown rule procedure '%s'", name); 369 } 370 371 for (size_t i = 0; i < npfvar_get_count(args); i++) { 372 const char *param, *value; 373 proc_param_t *p; 374 375 p = npfvar_get_data(args, NPFVAR_PROC_PARAM, i); 376 param = p->pp_param; 377 value = p->pp_value; 378 379 error = npf_extmod_param(extmod, extcall, param, value); 380 switch (error) { 381 case EINVAL: 382 yyerror("invalid parameter '%s'", param); 383 default: 384 break; 385 } 386 } 387 error = npf_rproc_extcall(rp, extcall); 388 if (error) { 389 yyerror(error == EEXIST ? 390 "duplicate procedure call" : "unexpected error"); 391 } 392 } 393 394 /* 395 * npfctl_build_rproc: create and insert a rule procedure. 396 */ 397 void 398 npfctl_build_rproc(const char *name, npfvar_t *procs) 399 { 400 nl_rproc_t *rp; 401 size_t i; 402 403 rp = npf_rproc_create(name); 404 if (rp == NULL) { 405 errx(EXIT_FAILURE, "%s failed", __func__); 406 } 407 npf_rproc_insert(npf_conf, rp); 408 409 for (i = 0; i < npfvar_get_count(procs); i++) { 410 proc_call_t *pc = npfvar_get_data(procs, NPFVAR_PROC, i); 411 npfctl_build_rpcall(rp, pc->pc_name, pc->pc_opts); 412 } 413 } 414 415 void 416 npfctl_build_maprset(const char *name, int attr, const char *ifname) 417 { 418 const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT); 419 nl_rule_t *rl; 420 421 /* If no direction is not specified, then both. */ 422 if ((attr & attr_di) == 0) { 423 attr |= attr_di; 424 } 425 /* Allow only "in/out" attributes. */ 426 attr = NPF_RULE_GROUP | NPF_RULE_GROUP | (attr & attr_di); 427 rl = npf_rule_create(name, attr, ifname); 428 npf_nat_insert(npf_conf, rl, NPF_PRI_LAST); 429 } 430 431 /* 432 * npfctl_build_group: create a group, insert into the global ruleset, 433 * update the current group pointer and increase the nesting level. 434 */ 435 void 436 npfctl_build_group(const char *name, int attr, const char *ifname, bool def) 437 { 438 const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT); 439 nl_rule_t *rl; 440 441 if (def || (attr & attr_di) == 0) { 442 attr |= attr_di; 443 } 444 445 rl = npf_rule_create(name, attr | NPF_RULE_GROUP, ifname); 446 npf_rule_setprio(rl, NPF_PRI_LAST); 447 if (def) { 448 if (defgroup) { 449 yyerror("multiple default groups are not valid"); 450 } 451 if (rule_nesting_level) { 452 yyerror("default group can only be at the top level"); 453 } 454 defgroup = rl; 455 } else { 456 nl_rule_t *cg = current_group[rule_nesting_level]; 457 npf_rule_insert(npf_conf, cg, rl); 458 } 459 460 /* Set the current group and increase the nesting level. */ 461 if (rule_nesting_level >= MAX_RULE_NESTING) { 462 yyerror("rule nesting limit reached"); 463 } 464 current_group[++rule_nesting_level] = rl; 465 } 466 467 void 468 npfctl_build_group_end(void) 469 { 470 assert(rule_nesting_level > 0); 471 current_group[rule_nesting_level--] = NULL; 472 } 473 474 /* 475 * npfctl_build_rule: create a rule, build byte-code from filter options, 476 * if any, and insert into the ruleset of current group, or set the rule. 477 */ 478 void 479 npfctl_build_rule(uint32_t attr, const char *ifname, sa_family_t family, 480 const opt_proto_t *op, const filt_opts_t *fopts, 481 const char *pcap_filter, const char *rproc) 482 { 483 nl_rule_t *rl; 484 485 attr |= (npf_conf ? 0 : NPF_RULE_DYNAMIC); 486 487 rl = npf_rule_create(NULL, attr, ifname); 488 if (pcap_filter) { 489 npfctl_build_pcap(rl, pcap_filter); 490 } else { 491 npfctl_build_code(rl, family, op, fopts); 492 } 493 494 if (rproc) { 495 npf_rule_setproc(rl, rproc); 496 } 497 498 if (npf_conf) { 499 nl_rule_t *cg = current_group[rule_nesting_level]; 500 501 if (rproc && !npf_rproc_exists_p(npf_conf, rproc)) { 502 yyerror("rule procedure '%s' is not defined", rproc); 503 } 504 assert(cg != NULL); 505 npf_rule_setprio(rl, NPF_PRI_LAST); 506 npf_rule_insert(npf_conf, cg, rl); 507 } else { 508 /* We have parsed a single rule - set it. */ 509 the_rule = rl; 510 } 511 } 512 513 /* 514 * npfctl_build_nat: create a single NAT policy of a specified 515 * type with a given filter options. 516 */ 517 static void 518 npfctl_build_nat(int type, const char *ifname, sa_family_t family, 519 const addr_port_t *ap, const filt_opts_t *fopts, bool binat) 520 { 521 const opt_proto_t op = { .op_proto = -1, .op_opts = NULL }; 522 fam_addr_mask_t *am; 523 in_port_t port; 524 nl_nat_t *nat; 525 526 if (!ap->ap_netaddr) { 527 yyerror("%s network segment is not specified", 528 type == NPF_NATIN ? "inbound" : "outbound"); 529 } 530 am = npfctl_get_singlefam(ap->ap_netaddr); 531 if (am->fam_family != family) { 532 yyerror("IPv6 NAT is not supported"); 533 } 534 535 switch (type) { 536 case NPF_NATOUT: 537 /* 538 * Outbound NAT (or source NAT) policy, usually used for the 539 * traditional NAPT. If it is a half for bi-directional NAT, 540 * then no port translation with mapping. 541 */ 542 nat = npf_nat_create(NPF_NATOUT, !binat ? 543 (NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0, 544 ifname, &am->fam_addr, am->fam_family, 0); 545 break; 546 case NPF_NATIN: 547 /* 548 * Inbound NAT (or destination NAT). Unless bi-NAT, a port 549 * must be specified, since it has to be redirection. 550 */ 551 port = 0; 552 if (!binat) { 553 if (!ap->ap_portrange) { 554 yyerror("inbound port is not specified"); 555 } 556 port = npfctl_get_singleport(ap->ap_portrange); 557 } 558 nat = npf_nat_create(NPF_NATIN, !binat ? NPF_NAT_PORTS : 0, 559 ifname, &am->fam_addr, am->fam_family, port); 560 break; 561 default: 562 assert(false); 563 } 564 565 npfctl_build_code(nat, family, &op, fopts); 566 npf_nat_insert(npf_conf, nat, NPF_PRI_LAST); 567 } 568 569 /* 570 * npfctl_build_natseg: validate and create NAT policies. 571 */ 572 void 573 npfctl_build_natseg(int sd, int type, const char *ifname, 574 const addr_port_t *ap1, const addr_port_t *ap2, 575 const filt_opts_t *fopts) 576 { 577 sa_family_t af = AF_INET; 578 filt_opts_t imfopts; 579 bool binat; 580 581 if (sd == NPFCTL_NAT_STATIC) { 582 yyerror("static NAT is not yet supported"); 583 } 584 assert(sd == NPFCTL_NAT_DYNAMIC); 585 assert(ifname != NULL); 586 587 /* 588 * Bi-directional NAT is a combination of inbound NAT and outbound 589 * NAT policies. Note that the translation address is local IP and 590 * the filter criteria is inverted accordingly. 591 */ 592 binat = (NPF_NATIN | NPF_NATOUT) == type; 593 594 /* 595 * If the filter criteria is not specified explicitly, apply implicit 596 * filtering according to the given network segments. 597 * 598 * Note: filled below, depending on the type. 599 */ 600 if (__predict_true(!fopts)) { 601 fopts = &imfopts; 602 } 603 604 if (type & NPF_NATIN) { 605 memset(&imfopts, 0, sizeof(filt_opts_t)); 606 memcpy(&imfopts.fo_to, ap2, sizeof(addr_port_t)); 607 npfctl_build_nat(NPF_NATIN, ifname, af, ap1, fopts, binat); 608 } 609 if (type & NPF_NATOUT) { 610 memset(&imfopts, 0, sizeof(filt_opts_t)); 611 memcpy(&imfopts.fo_from, ap1, sizeof(addr_port_t)); 612 npfctl_build_nat(NPF_NATOUT, ifname, af, ap2, fopts, binat); 613 } 614 } 615 616 /* 617 * npfctl_fill_table: fill NPF table with entries from a specified file. 618 */ 619 static void 620 npfctl_fill_table(nl_table_t *tl, u_int type, const char *fname) 621 { 622 char *buf = NULL; 623 int l = 0; 624 FILE *fp; 625 size_t n; 626 627 fp = fopen(fname, "r"); 628 if (fp == NULL) { 629 err(EXIT_FAILURE, "open '%s'", fname); 630 } 631 while (l++, getline(&buf, &n, fp) != -1) { 632 fam_addr_mask_t fam; 633 int alen; 634 635 if (*buf == '\n' || *buf == '#') { 636 continue; 637 } 638 639 if (!npfctl_parse_cidr(buf, &fam, &alen)) { 640 errx(EXIT_FAILURE, 641 "%s:%d: invalid table entry", fname, l); 642 } 643 if (type == NPF_TABLE_HASH && fam.fam_mask != NPF_NO_NETMASK) { 644 errx(EXIT_FAILURE, 645 "%s:%d: mask used with the hash table", fname, l); 646 } 647 648 /* Create and add a table entry. */ 649 npf_table_add_entry(tl, fam.fam_family, 650 &fam.fam_addr, fam.fam_mask); 651 } 652 if (buf != NULL) { 653 free(buf); 654 } 655 } 656 657 /* 658 * npfctl_build_table: create an NPF table, add to the configuration and, 659 * if required, fill with contents from a file. 660 */ 661 void 662 npfctl_build_table(const char *tname, u_int type, const char *fname) 663 { 664 static unsigned tid = 0; 665 nl_table_t *tl; 666 667 tl = npf_table_create(tname, tid++, type); 668 assert(tl != NULL); 669 670 if (npf_table_insert(npf_conf, tl)) { 671 yyerror("table '%s' is already defined", tname); 672 } 673 674 if (fname) { 675 npfctl_fill_table(tl, type, fname); 676 } 677 } 678 679 /* 680 * npfctl_build_alg: create an NPF application level gateway and add it 681 * to the configuration. 682 */ 683 void 684 npfctl_build_alg(const char *al_name) 685 { 686 if (_npf_alg_load(npf_conf, al_name) != 0) { 687 errx(EXIT_FAILURE, "ALG '%s' already loaded", al_name); 688 } 689 } 690 691 static void 692 npfctl_dump_bpf(struct bpf_program *bf) 693 { 694 if (npf_debug) { 695 extern char *yytext; 696 extern int yylineno; 697 698 int rule_line = yylineno - (int)(*yytext == '\n'); 699 printf("\nRULE AT LINE %d\n", rule_line); 700 bpf_dump(bf, 0); 701 } 702 } 703