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