npf_build.c revision 1.48
1/*- 2 * Copyright (c) 2011-2018 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This material is based upon work partially supported by The 6 * NetBSD Foundation under a contract with 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 * npfctl(8) building of the configuration. 32 */ 33 34#include <sys/cdefs.h> 35__RCSID("$NetBSD: npf_build.c,v 1.48 2019/04/17 20:41:58 tih Exp $"); 36 37#include <sys/types.h> 38#define __FAVOR_BSD 39#include <netinet/tcp.h> 40 41#include <stdlib.h> 42#include <inttypes.h> 43#include <string.h> 44#include <ctype.h> 45#include <unistd.h> 46#include <fcntl.h> 47#include <errno.h> 48#include <err.h> 49 50#include <pcap/pcap.h> 51 52#include "npfctl.h" 53 54#define MAX_RULE_NESTING 16 55 56static nl_config_t * npf_conf = NULL; 57static bool npf_debug = false; 58static nl_rule_t * the_rule = NULL; 59 60static bool defgroup = false; 61static nl_rule_t * current_group[MAX_RULE_NESTING]; 62static unsigned rule_nesting_level = 0; 63static unsigned npfctl_tid_counter = 0; 64 65static void npfctl_dump_bpf(struct bpf_program *); 66 67void 68npfctl_config_init(bool debug) 69{ 70 npf_conf = npf_config_create(); 71 if (npf_conf == NULL) { 72 errx(EXIT_FAILURE, "npf_config_create failed"); 73 } 74 npf_debug = debug; 75 memset(current_group, 0, sizeof(current_group)); 76} 77 78int 79npfctl_config_send(int fd) 80{ 81 npf_error_t errinfo; 82 int error = 0; 83 84 if (!defgroup) { 85 errx(EXIT_FAILURE, "default group was not defined"); 86 } 87 error = npf_config_submit(npf_conf, fd, &errinfo); 88 if (error == EEXIST) { /* XXX */ 89 errx(EXIT_FAILURE, "(re)load failed: " 90 "some table has a duplicate entry?"); 91 } 92 if (error) { 93 npfctl_print_error(&errinfo); 94 } 95 npf_config_destroy(npf_conf); 96 return error; 97} 98 99void 100npfctl_config_save(nl_config_t *ncf, const char *outfile) 101{ 102 void *blob; 103 size_t len; 104 int fd; 105 106 blob = npf_config_export(ncf, &len); 107 if (!blob) 108 err(EXIT_FAILURE, "npf_config_export"); 109 if ((fd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) 110 err(EXIT_FAILURE, "could not open %s", outfile); 111 if (write(fd, blob, len) != (ssize_t)len) { 112 err(EXIT_FAILURE, "write to %s failed", outfile); 113 } 114 free(blob); 115 close(fd); 116} 117 118void 119npfctl_config_debug(const char *outfile) 120{ 121 printf("\nConfiguration:\n\n"); 122 _npf_config_dump(npf_conf, STDOUT_FILENO); 123 124 printf("\nSaving binary to %s\n", outfile); 125 npfctl_config_save(npf_conf, outfile); 126 npf_config_destroy(npf_conf); 127} 128 129nl_config_t * 130npfctl_config_ref(void) 131{ 132 return npf_conf; 133} 134 135nl_rule_t * 136npfctl_rule_ref(void) 137{ 138 return the_rule; 139} 140 141bool 142npfctl_debug_addif(const char *ifname) 143{ 144 const char tname[] = "npftest"; 145 const size_t tnamelen = sizeof(tname) - 1; 146 147 if (npf_debug) { 148 _npf_debug_addif(npf_conf, ifname); 149 return strncmp(ifname, tname, tnamelen) == 0; 150 } 151 return 0; 152} 153 154unsigned 155npfctl_table_getid(const char *name) 156{ 157 unsigned tid = (unsigned)-1; 158 nl_table_t *tl; 159 160 /* XXX dynamic ruleset */ 161 if (!npf_conf) { 162 return (unsigned)-1; 163 } 164 165 /* XXX: Iterating all as we need to rewind for the next call. */ 166 while ((tl = npf_table_iterate(npf_conf)) != NULL) { 167 const char *tname = npf_table_getname(tl); 168 if (strcmp(tname, name) == 0) { 169 tid = npf_table_getid(tl); 170 } 171 } 172 return tid; 173} 174 175const char * 176npfctl_table_getname(nl_config_t *ncf, unsigned tid, bool *ifaddr) 177{ 178 const char *name = NULL; 179 nl_table_t *tl; 180 181 /* XXX: Iterating all as we need to rewind for the next call. */ 182 while ((tl = npf_table_iterate(ncf)) != NULL) { 183 if (npf_table_getid(tl) == tid) { 184 name = npf_table_getname(tl); 185 } 186 } 187 if (!name) { 188 return NULL; 189 } 190 if (!strncmp(name, NPF_IFNET_TABLE_PREF, NPF_IFNET_TABLE_PREFLEN)) { 191 name += NPF_IFNET_TABLE_PREFLEN; 192 *ifaddr = true; 193 } else { 194 *ifaddr = false; 195 } 196 return name; 197} 198 199static in_port_t 200npfctl_get_singleport(const npfvar_t *vp) 201{ 202 port_range_t *pr; 203 in_port_t *port; 204 205 if (npfvar_get_count(vp) > 1) { 206 yyerror("multiple ports are not valid"); 207 } 208 pr = npfvar_get_data(vp, NPFVAR_PORT_RANGE, 0); 209 if (pr->pr_start != pr->pr_end) { 210 yyerror("port range is not valid"); 211 } 212 port = &pr->pr_start; 213 return *port; 214} 215 216static fam_addr_mask_t * 217npfctl_get_singlefam(const npfvar_t *vp) 218{ 219 fam_addr_mask_t *am; 220 221 if (npfvar_get_type(vp, 0) != NPFVAR_FAM) { 222 yyerror("map segment must be an address or network"); 223 } 224 if (npfvar_get_count(vp) > 1) { 225 yyerror("map segment cannot have multiple static addresses"); 226 } 227 am = npfvar_get_data(vp, NPFVAR_FAM, 0); 228 if (am == NULL) { 229 yyerror("invalid map segment"); 230 } 231 return am; 232} 233 234static unsigned 235npfctl_get_singletable(const npfvar_t *vp) 236{ 237 unsigned *tid; 238 239 if (npfvar_get_count(vp) > 1) { 240 yyerror("multiple tables are not valid"); 241 } 242 tid = npfvar_get_data(vp, NPFVAR_TABLE, 0); 243 assert(tid != NULL); 244 return *tid; 245} 246 247static bool 248npfctl_build_fam(npf_bpf_t *ctx, sa_family_t family, 249 fam_addr_mask_t *fam, int opts) 250{ 251 /* 252 * If family is specified, address does not match it and the 253 * address is extracted from the interface, then simply ignore. 254 * Otherwise, address of invalid family was passed manually. 255 */ 256 if (family != AF_UNSPEC && family != fam->fam_family) { 257 if (!fam->fam_ifindex) { 258 yyerror("specified address is not of the required " 259 "family %d", family); 260 } 261 return false; 262 } 263 264 family = fam->fam_family; 265 if (family != AF_INET && family != AF_INET6) { 266 yyerror("family %d is not supported", family); 267 } 268 269 /* 270 * Optimise 0.0.0.0/0 case to be NOP. Otherwise, address with 271 * zero mask would never match and therefore is not valid. 272 */ 273 if (fam->fam_mask == 0) { 274 static const npf_addr_t zero; /* must be static */ 275 276 if (memcmp(&fam->fam_addr, &zero, sizeof(npf_addr_t))) { 277 yyerror("filter criterion would never match"); 278 } 279 return false; 280 } 281 282 npfctl_bpf_cidr(ctx, opts, family, &fam->fam_addr, fam->fam_mask); 283 return true; 284} 285 286static void 287npfctl_build_vars(npf_bpf_t *ctx, sa_family_t family, npfvar_t *vars, int opts) 288{ 289 const int type = npfvar_get_type(vars, 0); 290 size_t i; 291 292 npfctl_bpf_group(ctx); 293 for (i = 0; i < npfvar_get_count(vars); i++) { 294 void *data = npfvar_get_data(vars, type, i); 295 assert(data != NULL); 296 297 switch (type) { 298 case NPFVAR_FAM: { 299 fam_addr_mask_t *fam = data; 300 npfctl_build_fam(ctx, family, fam, opts); 301 break; 302 } 303 case NPFVAR_PORT_RANGE: { 304 port_range_t *pr = data; 305 npfctl_bpf_ports(ctx, opts, pr->pr_start, pr->pr_end); 306 break; 307 } 308 case NPFVAR_TABLE: { 309 u_int tid; 310 memcpy(&tid, data, sizeof(u_int)); 311 npfctl_bpf_table(ctx, opts, tid); 312 break; 313 } 314 default: 315 assert(false); 316 } 317 } 318 npfctl_bpf_endgroup(ctx, (opts & MATCH_INVERT) != 0); 319} 320 321static void 322npfctl_build_proto(npf_bpf_t *ctx, sa_family_t family, const opt_proto_t *op) 323{ 324 const npfvar_t *popts = op->op_opts; 325 const int proto = op->op_proto; 326 327 /* IP version and/or L4 protocol matching. */ 328 if (family != AF_UNSPEC || proto != -1) { 329 npfctl_bpf_proto(ctx, family, proto); 330 } 331 332 switch (proto) { 333 case IPPROTO_TCP: 334 /* Build TCP flags matching (optional). */ 335 if (popts) { 336 uint8_t *tf, *tf_mask; 337 338 assert(npfvar_get_count(popts) == 2); 339 tf = npfvar_get_data(popts, NPFVAR_TCPFLAG, 0); 340 tf_mask = npfvar_get_data(popts, NPFVAR_TCPFLAG, 1); 341 npfctl_bpf_tcpfl(ctx, *tf, *tf_mask, false); 342 } 343 break; 344 case IPPROTO_ICMP: 345 case IPPROTO_ICMPV6: 346 /* Build ICMP/ICMPv6 type and/or code matching. */ 347 if (popts) { 348 int *icmp_type, *icmp_code; 349 350 assert(npfvar_get_count(popts) == 2); 351 icmp_type = npfvar_get_data(popts, NPFVAR_ICMP, 0); 352 icmp_code = npfvar_get_data(popts, NPFVAR_ICMP, 1); 353 npfctl_bpf_icmp(ctx, *icmp_type, *icmp_code); 354 } 355 break; 356 default: 357 /* No options for other protocols. */ 358 break; 359 } 360} 361 362static bool 363npfctl_build_code(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op, 364 const filt_opts_t *fopts) 365{ 366 bool noproto, noaddrs, noports, nostate, need_tcpudp = false; 367 const addr_port_t *apfrom = &fopts->fo_from; 368 const addr_port_t *apto = &fopts->fo_to; 369 const int proto = op->op_proto; 370 npf_bpf_t *bc; 371 unsigned opts; 372 size_t len; 373 374 /* If none specified, then no byte-code. */ 375 noproto = family == AF_UNSPEC && proto == -1 && !op->op_opts; 376 noaddrs = !apfrom->ap_netaddr && !apto->ap_netaddr; 377 noports = !apfrom->ap_portrange && !apto->ap_portrange; 378 nostate = !(npf_rule_getattr(rl) & NPF_RULE_STATEFUL); 379 if (noproto && noaddrs && noports && nostate) { 380 return false; 381 } 382 383 /* 384 * Sanity check: ports can only be used with TCP or UDP protocol. 385 * No filter options are supported for other protocols, only the 386 * IP addresses are allowed. 387 */ 388 if (!noports) { 389 switch (proto) { 390 case IPPROTO_TCP: 391 case IPPROTO_UDP: 392 break; 393 case -1: 394 need_tcpudp = true; 395 break; 396 default: 397 yyerror("invalid filter options for protocol %d", proto); 398 } 399 } 400 401 bc = npfctl_bpf_create(); 402 403 /* Build layer 4 protocol blocks. */ 404 npfctl_build_proto(bc, family, op); 405 406 /* 407 * If this is a stateful rule and TCP flags are not specified, 408 * then add "flags S/SAFR" filter for TCP protocol case. 409 */ 410 if ((npf_rule_getattr(rl) & NPF_RULE_STATEFUL) != 0 && 411 (proto == -1 || (proto == IPPROTO_TCP && !op->op_opts))) { 412 npfctl_bpf_tcpfl(bc, TH_SYN, 413 TH_SYN | TH_ACK | TH_FIN | TH_RST, proto == -1); 414 } 415 416 /* Build IP address blocks. */ 417 opts = MATCH_SRC | (fopts->fo_finvert ? MATCH_INVERT : 0); 418 npfctl_build_vars(bc, family, apfrom->ap_netaddr, opts); 419 opts = MATCH_DST | (fopts->fo_tinvert ? MATCH_INVERT : 0); 420 npfctl_build_vars(bc, family, apto->ap_netaddr, opts); 421 422 /* Build port-range blocks. */ 423 if (need_tcpudp) { 424 /* TCP/UDP check for the ports. */ 425 npfctl_bpf_group(bc); 426 npfctl_bpf_proto(bc, AF_UNSPEC, IPPROTO_TCP); 427 npfctl_bpf_proto(bc, AF_UNSPEC, IPPROTO_UDP); 428 npfctl_bpf_endgroup(bc, false); 429 } 430 npfctl_build_vars(bc, family, apfrom->ap_portrange, MATCH_SRC); 431 npfctl_build_vars(bc, family, apto->ap_portrange, MATCH_DST); 432 433 /* Set the byte-code marks, if any. */ 434 const void *bmarks = npfctl_bpf_bmarks(bc, &len); 435 if (npf_rule_setinfo(rl, bmarks, len) == -1) { 436 errx(EXIT_FAILURE, "npf_rule_setinfo failed"); 437 } 438 439 /* Complete BPF byte-code and pass to the rule. */ 440 struct bpf_program *bf = npfctl_bpf_complete(bc); 441 if (bf == NULL) { 442 npfctl_bpf_destroy(bc); 443 return true; 444 } 445 len = bf->bf_len * sizeof(struct bpf_insn); 446 447 if (npf_rule_setcode(rl, NPF_CODE_BPF, bf->bf_insns, len) == -1) { 448 errx(EXIT_FAILURE, "npf_rule_setcode failed"); 449 } 450 npfctl_dump_bpf(bf); 451 npfctl_bpf_destroy(bc); 452 453 return true; 454} 455 456static void 457npfctl_build_pcap(nl_rule_t *rl, const char *filter) 458{ 459 const size_t maxsnaplen = 64 * 1024; 460 struct bpf_program bf; 461 size_t len; 462 463 if (pcap_compile_nopcap(maxsnaplen, DLT_RAW, &bf, 464 filter, 1, PCAP_NETMASK_UNKNOWN) == -1) { 465 yyerror("invalid pcap-filter(7) syntax"); 466 } 467 len = bf.bf_len * sizeof(struct bpf_insn); 468 469 if (npf_rule_setcode(rl, NPF_CODE_BPF, bf.bf_insns, len) == -1) { 470 errx(EXIT_FAILURE, "npf_rule_setcode failed"); 471 } 472 npfctl_dump_bpf(&bf); 473 pcap_freecode(&bf); 474} 475 476static void 477npfctl_build_rpcall(nl_rproc_t *rp, const char *name, npfvar_t *args) 478{ 479 npf_extmod_t *extmod; 480 nl_ext_t *extcall; 481 int error; 482 483 extmod = npf_extmod_get(name, &extcall); 484 if (extmod == NULL) { 485 yyerror("unknown rule procedure '%s'", name); 486 } 487 488 for (size_t i = 0; i < npfvar_get_count(args); i++) { 489 const char *param, *value; 490 proc_param_t *p; 491 492 p = npfvar_get_data(args, NPFVAR_PROC_PARAM, i); 493 param = p->pp_param; 494 value = p->pp_value; 495 496 error = npf_extmod_param(extmod, extcall, param, value); 497 switch (error) { 498 case EINVAL: 499 yyerror("invalid parameter '%s'", param); 500 default: 501 break; 502 } 503 } 504 error = npf_rproc_extcall(rp, extcall); 505 if (error) { 506 yyerror(error == EEXIST ? 507 "duplicate procedure call" : "unexpected error"); 508 } 509} 510 511/* 512 * npfctl_build_rproc: create and insert a rule procedure. 513 */ 514void 515npfctl_build_rproc(const char *name, npfvar_t *procs) 516{ 517 nl_rproc_t *rp; 518 size_t i; 519 520 rp = npf_rproc_create(name); 521 if (rp == NULL) { 522 errx(EXIT_FAILURE, "%s failed", __func__); 523 } 524 525 for (i = 0; i < npfvar_get_count(procs); i++) { 526 proc_call_t *pc = npfvar_get_data(procs, NPFVAR_PROC, i); 527 npfctl_build_rpcall(rp, pc->pc_name, pc->pc_opts); 528 } 529 npf_rproc_insert(npf_conf, rp); 530} 531 532void 533npfctl_build_maprset(const char *name, int attr, const char *ifname) 534{ 535 const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT); 536 nl_rule_t *rl; 537 538 /* If no direction is not specified, then both. */ 539 if ((attr & attr_di) == 0) { 540 attr |= attr_di; 541 } 542 /* Allow only "in/out" attributes. */ 543 attr = NPF_RULE_GROUP | NPF_RULE_DYNAMIC | (attr & attr_di); 544 rl = npf_rule_create(name, attr, ifname); 545 npf_nat_insert(npf_conf, rl, NPF_PRI_LAST); 546} 547 548/* 549 * npfctl_build_group: create a group, update the current group pointer 550 * and increase the nesting level. 551 */ 552void 553npfctl_build_group(const char *name, int attr, const char *ifname, bool def) 554{ 555 const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT); 556 nl_rule_t *rl; 557 558 if (def || (attr & attr_di) == 0) { 559 attr |= attr_di; 560 } 561 562 rl = npf_rule_create(name, attr | NPF_RULE_GROUP, ifname); 563 npf_rule_setprio(rl, NPF_PRI_LAST); 564 if (def) { 565 if (defgroup) { 566 yyerror("multiple default groups are not valid"); 567 } 568 if (rule_nesting_level) { 569 yyerror("default group can only be at the top level"); 570 } 571 defgroup = true; 572 } 573 574 /* Set the current group and increase the nesting level. */ 575 if (rule_nesting_level >= MAX_RULE_NESTING) { 576 yyerror("rule nesting limit reached"); 577 } 578 current_group[++rule_nesting_level] = rl; 579} 580 581void 582npfctl_build_group_end(void) 583{ 584 nl_rule_t *parent, *group; 585 586 assert(rule_nesting_level > 0); 587 parent = current_group[rule_nesting_level - 1]; 588 group = current_group[rule_nesting_level]; 589 current_group[rule_nesting_level--] = NULL; 590 591 /* Note: if the parent is NULL, then it is a global rule. */ 592 npf_rule_insert(npf_conf, parent, group); 593} 594 595/* 596 * npfctl_build_rule: create a rule, build byte-code from filter options, 597 * if any, and insert into the ruleset of current group, or set the rule. 598 */ 599void 600npfctl_build_rule(uint32_t attr, const char *ifname, sa_family_t family, 601 const opt_proto_t *op, const filt_opts_t *fopts, 602 const char *pcap_filter, const char *rproc) 603{ 604 nl_rule_t *rl; 605 606 attr |= (npf_conf ? 0 : NPF_RULE_DYNAMIC); 607 608 rl = npf_rule_create(NULL, attr, ifname); 609 if (pcap_filter) { 610 npfctl_build_pcap(rl, pcap_filter); 611 } else { 612 npfctl_build_code(rl, family, op, fopts); 613 } 614 615 if (rproc) { 616 npf_rule_setproc(rl, rproc); 617 } 618 619 if (npf_conf) { 620 nl_rule_t *cg = current_group[rule_nesting_level]; 621 622 if (rproc && !npf_rproc_exists_p(npf_conf, rproc)) { 623 yyerror("rule procedure '%s' is not defined", rproc); 624 } 625 assert(cg != NULL); 626 npf_rule_setprio(rl, NPF_PRI_LAST); 627 npf_rule_insert(npf_conf, cg, rl); 628 } else { 629 /* We have parsed a single rule - set it. */ 630 the_rule = rl; 631 } 632} 633 634/* 635 * npfctl_build_nat: create a single NAT policy of a specified 636 * type with a given filter options. 637 */ 638static nl_nat_t * 639npfctl_build_nat(int type, const char *ifname, const addr_port_t *ap, 640 const opt_proto_t *op, const filt_opts_t *fopts, u_int flags) 641{ 642 const opt_proto_t def_op = { .op_proto = -1, .op_opts = NULL }; 643 fam_addr_mask_t *am; 644 sa_family_t family; 645 in_port_t port; 646 nl_nat_t *nat; 647 unsigned tid; 648 649 if (ap->ap_portrange) { 650 /* 651 * The port forwarding case. In such case, there has to 652 * be a single port used for translation; we keep the port 653 * translation on, but disable the port map. 654 */ 655 port = npfctl_get_singleport(ap->ap_portrange); 656 flags = (flags & ~NPF_NAT_PORTMAP) | NPF_NAT_PORTS; 657 } else { 658 port = 0; 659 } 660 if (!op) { 661 op = &def_op; 662 } 663 664 nat = npf_nat_create(type, flags, ifname); 665 666 switch (npfvar_get_type(ap->ap_netaddr, 0)) { 667 case NPFVAR_FAM: 668 /* Translation address. */ 669 am = npfctl_get_singlefam(ap->ap_netaddr); 670 family = am->fam_family; 671 npf_nat_setaddr(nat, family, &am->fam_addr, am->fam_mask); 672 break; 673 case NPFVAR_TABLE: 674 /* Translation table. */ 675 family = AF_UNSPEC; 676 tid = npfctl_get_singletable(ap->ap_netaddr); 677 npf_nat_settable(nat, tid); 678 break; 679 default: 680 yyerror("map must have a valid translation address"); 681 abort(); 682 } 683 npf_nat_setport(nat, port); 684 npfctl_build_code(nat, family, op, fopts); 685 return nat; 686} 687 688/* 689 * npfctl_build_natseg: validate and create NAT policies. 690 */ 691void 692npfctl_build_natseg(int sd, int type, unsigned mflags, const char *ifname, 693 const addr_port_t *ap1, const addr_port_t *ap2, const opt_proto_t *op, 694 const filt_opts_t *fopts, unsigned algo) 695{ 696 fam_addr_mask_t *am1 = NULL, *am2 = NULL; 697 nl_nat_t *nt1 = NULL, *nt2 = NULL; 698 filt_opts_t imfopts; 699 uint16_t adj = 0; 700 unsigned flags; 701 bool binat; 702 703 assert(ifname != NULL); 704 705 /* 706 * Validate that mapping has the translation address(es) set. 707 */ 708 if ((type & NPF_NATIN) != 0 && ap1->ap_netaddr == NULL) { 709 yyerror("inbound network segment is not specified"); 710 } 711 if ((type & NPF_NATOUT) != 0 && ap2->ap_netaddr == NULL) { 712 yyerror("outbound network segment is not specified"); 713 } 714 715 /* 716 * Bi-directional NAT is a combination of inbound NAT and outbound 717 * NAT policies with the translation segments inverted respectively. 718 */ 719 binat = (NPF_NATIN | NPF_NATOUT) == type; 720 721 switch (sd) { 722 case NPFCTL_NAT_DYNAMIC: 723 /* 724 * Dynamic NAT: stateful translation -- traditional NAPT 725 * is expected. Unless it is bi-directional NAT, perform 726 * the port mapping. 727 */ 728 flags = !binat ? (NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0; 729 730 switch (algo) { 731 case NPF_ALGO_IPHASH: 732 case NPF_ALGO_RR: 733 case NPF_ALGO_NONE: 734 break; 735 default: 736 yyerror("invalid algorithm specified for dynamic NAT"); 737 } 738 break; 739 case NPFCTL_NAT_STATIC: 740 /* 741 * Static NAT: stateless translation. 742 */ 743 flags = NPF_NAT_STATIC; 744 745 /* Note: translation address/network cannot be a table. */ 746 am1 = npfctl_get_singlefam(ap1->ap_netaddr); 747 am2 = npfctl_get_singlefam(ap2->ap_netaddr); 748 749 /* Validate the algorithm. */ 750 switch (algo) { 751 case NPF_ALGO_NPT66: 752 if (am1->fam_mask != am2->fam_mask) { 753 yyerror("asymmetric NPTv6 is not supported"); 754 } 755 adj = npfctl_npt66_calcadj(am1->fam_mask, 756 &am1->fam_addr, &am2->fam_addr); 757 break; 758 case NPF_ALGO_NETMAP: 759 if (am1->fam_mask != am2->fam_mask) { 760 yyerror("net-to-net mapping using the " 761 "NETMAP algorithm must be 1:1"); 762 } 763 break; 764 case NPF_ALGO_NONE: 765 if (am1->fam_mask != NPF_NO_NETMASK || 766 am2->fam_mask != NPF_NO_NETMASK) { 767 yyerror("static net-to-net translation " 768 "must have an algorithm specified"); 769 } 770 break; 771 default: 772 yyerror("invalid algorithm specified for static NAT"); 773 } 774 break; 775 default: 776 abort(); 777 } 778 779 /* 780 * Apply the flag modifications. 781 */ 782 if (mflags & NPF_NAT_PORTS) { 783 flags &= ~(NPF_NAT_PORTS | NPF_NAT_PORTMAP); 784 } 785 786 /* 787 * If the filter criteria is not specified explicitly, apply implicit 788 * filtering according to the given network segments. 789 * 790 * Note: filled below, depending on the type. 791 */ 792 if (__predict_true(!fopts)) { 793 fopts = &imfopts; 794 } 795 796 if (type & NPF_NATIN) { 797 memset(&imfopts, 0, sizeof(filt_opts_t)); 798 memcpy(&imfopts.fo_to, ap2, sizeof(addr_port_t)); 799 nt1 = npfctl_build_nat(NPF_NATIN, ifname, ap1, op, fopts, flags); 800 } 801 if (type & NPF_NATOUT) { 802 memset(&imfopts, 0, sizeof(filt_opts_t)); 803 memcpy(&imfopts.fo_from, ap1, sizeof(addr_port_t)); 804 nt2 = npfctl_build_nat(NPF_NATOUT, ifname, ap2, op, fopts, flags); 805 } 806 807 if (algo == NPF_ALGO_NPT66) { 808 /* 809 * NPTv6 is a special case using special adjustment value. 810 * It is always bidirectional NAT. 811 */ 812 assert(nt1 && nt2); 813 npf_nat_setnpt66(nt1, ~adj); 814 npf_nat_setnpt66(nt2, adj); 815 } else if (algo) { 816 /* 817 * Set the algorithm. 818 */ 819 if (nt1) { 820 npf_nat_setalgo(nt1, algo); 821 } 822 if (nt2) { 823 npf_nat_setalgo(nt2, algo); 824 } 825 } 826 827 if (nt1) { 828 npf_nat_insert(npf_conf, nt1, NPF_PRI_LAST); 829 } 830 if (nt2) { 831 npf_nat_insert(npf_conf, nt2, NPF_PRI_LAST); 832 } 833} 834 835/* 836 * npfctl_fill_table: fill NPF table with entries from a specified file. 837 */ 838static void 839npfctl_fill_table(nl_table_t *tl, u_int type, const char *fname) 840{ 841 char *buf = NULL; 842 int l = 0; 843 FILE *fp; 844 size_t n; 845 846 fp = fopen(fname, "r"); 847 if (fp == NULL) { 848 err(EXIT_FAILURE, "open '%s'", fname); 849 } 850 while (l++, getline(&buf, &n, fp) != -1) { 851 fam_addr_mask_t fam; 852 int alen; 853 854 if (*buf == '\n' || *buf == '#') { 855 continue; 856 } 857 858 if (!npfctl_parse_cidr(buf, &fam, &alen)) { 859 errx(EXIT_FAILURE, 860 "%s:%d: invalid table entry", fname, l); 861 } 862 if (type != NPF_TABLE_LPM && fam.fam_mask != NPF_NO_NETMASK) { 863 errx(EXIT_FAILURE, "%s:%d: mask used with the " 864 "table type other than \"lpm\"", fname, l); 865 } 866 867 npf_table_add_entry(tl, fam.fam_family, 868 &fam.fam_addr, fam.fam_mask); 869 } 870 free(buf); 871} 872 873/* 874 * npfctl_build_table: create an NPF table, add to the configuration and, 875 * if required, fill with contents from a file. 876 */ 877void 878npfctl_build_table(const char *tname, u_int type, const char *fname) 879{ 880 nl_table_t *tl; 881 882 tl = npf_table_create(tname, npfctl_tid_counter++, type); 883 assert(tl != NULL); 884 885 if (fname) { 886 npfctl_fill_table(tl, type, fname); 887 } else if (type == NPF_TABLE_CONST) { 888 yyerror("table type 'const' must be loaded from a file"); 889 } 890 891 if (npf_table_insert(npf_conf, tl)) { 892 yyerror("table '%s' is already defined", tname); 893 } 894} 895 896/* 897 * npfctl_ifnet_table: get a variable with ifaddr-table; auto-create 898 * the table on first reference. 899 */ 900npfvar_t * 901npfctl_ifnet_table(const char *ifname) 902{ 903 char tname[NPF_TABLE_MAXNAMELEN]; 904 nl_table_t *tl; 905 u_int tid; 906 907 snprintf(tname, sizeof(tname), NPF_IFNET_TABLE_PREF "%s", ifname); 908 909 tid = npfctl_table_getid(tname); 910 if (tid == (unsigned)-1) { 911 tid = npfctl_tid_counter++; 912 tl = npf_table_create(tname, tid, NPF_TABLE_IFADDR); 913 (void)npf_table_insert(npf_conf, tl); 914 } 915 return npfvar_create_element(NPFVAR_TABLE, &tid, sizeof(u_int)); 916} 917 918/* 919 * npfctl_build_alg: create an NPF application level gateway and add it 920 * to the configuration. 921 */ 922void 923npfctl_build_alg(const char *al_name) 924{ 925 if (_npf_alg_load(npf_conf, al_name) != 0) { 926 errx(EXIT_FAILURE, "ALG '%s' already loaded", al_name); 927 } 928} 929 930static void 931npfctl_dump_bpf(struct bpf_program *bf) 932{ 933 if (npf_debug) { 934 extern char *yytext; 935 extern int yylineno; 936 937 int rule_line = yylineno - (int)(*yytext == '\n'); 938 printf("\nRULE AT LINE %d\n", rule_line); 939 bpf_dump(bf, 0); 940 } 941} 942