[BACK]Return to npf_build.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / usr.sbin / npf / npfctl

Annotation of src/usr.sbin/npf/npfctl/npf_build.c, Revision 1.38.2.2

1.38.2.2! snj         1: /*     $NetBSD: npf_build.c,v 1.38.2.1 2015/03/21 17:49:03 snj Exp $   */
1.1       rmind       2:
                      3: /*-
1.33      rmind       4:  * Copyright (c) 2011-2014 The NetBSD Foundation, Inc.
1.1       rmind       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>
1.38.2.2! snj        37: __RCSID("$NetBSD: npf_build.c,v 1.38.2.1 2015/03/21 17:49:03 snj Exp $");
1.1       rmind      38:
                     39: #include <sys/types.h>
1.33      rmind      40: #include <sys/mman.h>
                     41: #include <sys/stat.h>
1.37      rmind      42: #include <netinet/tcp.h>
1.1       rmind      43:
                     44: #include <stdlib.h>
                     45: #include <inttypes.h>
                     46: #include <string.h>
1.29      rmind      47: #include <ctype.h>
1.33      rmind      48: #include <unistd.h>
1.14      rmind      49: #include <errno.h>
1.1       rmind      50: #include <err.h>
                     51:
1.25      rmind      52: #include <pcap/pcap.h>
1.33      rmind      53: #include <cdbw.h>
1.25      rmind      54:
1.1       rmind      55: #include "npfctl.h"
                     56:
1.18      rmind      57: #define        MAX_RULE_NESTING        16
                     58:
1.1       rmind      59: static nl_config_t *           npf_conf = NULL;
                     60: static bool                    npf_debug = false;
1.18      rmind      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;
1.1       rmind      66:
1.27      rmind      67: static void                    npfctl_dump_bpf(struct bpf_program *);
                     68:
1.1       rmind      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;
1.18      rmind      77:        memset(current_group, 0, sizeof(current_group));
1.1       rmind      78: }
                     79:
                     80: int
1.13      rmind      81: npfctl_config_send(int fd, const char *out)
1.1       rmind      82: {
                     83:        int error;
                     84:
1.13      rmind      85:        if (out) {
                     86:                _npf_config_setsubmit(npf_conf, out);
                     87:                printf("\nSaving to %s\n", out);
1.1       rmind      88:        }
1.18      rmind      89:        if (!defgroup) {
1.1       rmind      90:                errx(EXIT_FAILURE, "default group was not defined");
                     91:        }
1.18      rmind      92:        npf_rule_insert(npf_conf, NULL, defgroup);
1.1       rmind      93:        error = npf_config_submit(npf_conf, fd);
1.38.2.1  snj        94:        if (error == EEXIST) { /* XXX */
                     95:                errx(EXIT_FAILURE, "(re)load failed: "
                     96:                    "some table has a duplicate entry?");
                     97:        }
1.3       rmind      98:        if (error) {
                     99:                nl_error_t ne;
                    100:                _npf_config_error(npf_conf, &ne);
                    101:                npfctl_print_error(&ne);
                    102:        }
1.25      rmind     103:        if (fd) {
                    104:                npf_config_destroy(npf_conf);
                    105:        }
1.1       rmind     106:        return error;
                    107: }
                    108:
1.16      rmind     109: nl_config_t *
                    110: npfctl_config_ref(void)
                    111: {
                    112:        return npf_conf;
                    113: }
                    114:
1.18      rmind     115: nl_rule_t *
                    116: npfctl_rule_ref(void)
                    117: {
                    118:        return the_rule;
                    119: }
                    120:
1.28      rmind     121: bool
1.13      rmind     122: npfctl_debug_addif(const char *ifname)
                    123: {
1.28      rmind     124:        const char tname[] = "npftest";
1.13      rmind     125:        const size_t tnamelen = sizeof(tname) - 1;
                    126:
1.28      rmind     127:        if (npf_debug) {
                    128:                _npf_debug_addif(npf_conf, ifname);
                    129:                return strncmp(ifname, tname, tnamelen) == 0;
1.13      rmind     130:        }
1.28      rmind     131:        return 0;
1.13      rmind     132: }
                    133:
1.32      rmind     134: unsigned
                    135: npfctl_table_getid(const char *name)
1.1       rmind     136: {
1.32      rmind     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;
1.1       rmind     153: }
                    154:
1.7       rmind     155: static in_port_t
1.1       rmind     156: npfctl_get_singleport(const npfvar_t *vp)
                    157: {
                    158:        port_range_t *pr;
1.7       rmind     159:        in_port_t *port;
1.1       rmind     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:        }
1.7       rmind     168:        port = &pr->pr_start;
                    169:        return *port;
1.1       rmind     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:
1.10      rmind     181: static bool
1.25      rmind     182: npfctl_build_fam(npf_bpf_t *ctx, sa_family_t family,
1.1       rmind     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) {
1.15      rmind     191:                if (!fam->fam_ifindex) {
1.1       rmind     192:                        yyerror("specified address is not of the required "
                    193:                            "family %d", family);
                    194:                }
1.10      rmind     195:                return false;
1.1       rmind     196:        }
1.30      rmind     197:
1.25      rmind     198:        family = fam->fam_family;
1.30      rmind     199:        if (family != AF_INET && family != AF_INET6) {
                    200:                yyerror("family %d is not supported", family);
                    201:        }
1.1       rmind     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) {
1.30      rmind     208:                static const npf_addr_t zero; /* must be static */
1.10      rmind     209:
1.1       rmind     210:                if (memcmp(&fam->fam_addr, &zero, sizeof(npf_addr_t))) {
                    211:                        yyerror("filter criterion would never match");
                    212:                }
1.10      rmind     213:                return false;
1.1       rmind     214:        }
                    215:
1.25      rmind     216:        npfctl_bpf_cidr(ctx, opts, family, &fam->fam_addr, fam->fam_mask);
1.10      rmind     217:        return true;
1.1       rmind     218: }
                    219:
                    220: static void
1.25      rmind     221: npfctl_build_vars(npf_bpf_t *ctx, sa_family_t family, npfvar_t *vars, int opts)
1.1       rmind     222: {
1.6       christos  223:        const int type = npfvar_get_type(vars, 0);
1.1       rmind     224:        size_t i;
                    225:
1.25      rmind     226:        npfctl_bpf_group(ctx);
1.1       rmind     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;
1.25      rmind     234:                        npfctl_build_fam(ctx, family, fam, opts);
1.1       rmind     235:                        break;
                    236:                }
                    237:                case NPFVAR_PORT_RANGE: {
                    238:                        port_range_t *pr = data;
1.25      rmind     239:                        npfctl_bpf_ports(ctx, opts, pr->pr_start, pr->pr_end);
1.1       rmind     240:                        break;
                    241:                }
                    242:                case NPFVAR_TABLE: {
1.32      rmind     243:                        u_int tid;
                    244:                        memcpy(&tid, data, sizeof(u_int));
1.25      rmind     245:                        npfctl_bpf_table(ctx, opts, tid);
1.1       rmind     246:                        break;
                    247:                }
                    248:                default:
                    249:                        assert(false);
                    250:                }
                    251:        }
1.25      rmind     252:        npfctl_bpf_endgroup(ctx);
1.1       rmind     253: }
                    254:
1.25      rmind     255: static void
                    256: npfctl_build_proto(npf_bpf_t *ctx, sa_family_t family, const opt_proto_t *op)
1.1       rmind     257: {
                    258:        const npfvar_t *popts = op->op_opts;
1.10      rmind     259:        const int proto = op->op_proto;
1.25      rmind     260:
                    261:        /* IP version and/or L4 protocol matching. */
                    262:        if (family != AF_UNSPEC || proto != -1) {
                    263:                npfctl_bpf_proto(ctx, family, proto);
                    264:        }
1.1       rmind     265:
1.10      rmind     266:        switch (proto) {
1.1       rmind     267:        case IPPROTO_TCP:
1.25      rmind     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);
1.37      rmind     275:                        npfctl_bpf_tcpfl(ctx, *tf, *tf_mask, false);
1.1       rmind     276:                }
                    277:                break;
                    278:        case IPPROTO_ICMP:
1.12      spz       279:        case IPPROTO_ICMPV6:
1.25      rmind     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);
1.12      spz       288:                }
                    289:                break;
1.25      rmind     290:        default:
                    291:                /* No options for other protocols. */
1.1       rmind     292:                break;
1.10      rmind     293:        }
1.1       rmind     294: }
                    295:
                    296: static bool
1.25      rmind     297: npfctl_build_code(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
1.27      rmind     298:     const filt_opts_t *fopts)
1.1       rmind     299: {
1.38      rmind     300:        bool noproto, noaddrs, noports, need_tcpudp = false;
1.7       rmind     301:        const addr_port_t *apfrom = &fopts->fo_from;
                    302:        const addr_port_t *apto = &fopts->fo_to;
1.10      rmind     303:        const int proto = op->op_proto;
1.25      rmind     304:        npf_bpf_t *bc;
1.1       rmind     305:        size_t len;
                    306:
1.25      rmind     307:        /* If none specified, then no byte-code. */
                    308:        noproto = family == AF_UNSPEC && proto == -1 && !op->op_opts;
1.20      rmind     309:        noaddrs = !apfrom->ap_netaddr && !apto->ap_netaddr;
                    310:        noports = !apfrom->ap_portrange && !apto->ap_portrange;
1.25      rmind     311:        if (noproto && noaddrs && noports) {
1.1       rmind     312:                return false;
1.25      rmind     313:        }
1.1       rmind     314:
1.25      rmind     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:
1.38      rmind     324:                        break;
1.25      rmind     325:                case -1:
1.38      rmind     326:                        need_tcpudp = true;
1.25      rmind     327:                        break;
                    328:                default:
                    329:                        yyerror("invalid filter options for protocol %d", proto);
                    330:                }
                    331:        }
1.1       rmind     332:
1.25      rmind     333:        bc = npfctl_bpf_create();
1.1       rmind     334:
1.10      rmind     335:        /* Build layer 4 protocol blocks. */
1.25      rmind     336:        npfctl_build_proto(bc, family, op);
1.10      rmind     337:
1.37      rmind     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:
1.1       rmind     348:        /* Build IP address blocks. */
1.27      rmind     349:        npfctl_build_vars(bc, family, apfrom->ap_netaddr, MATCH_SRC);
                    350:        npfctl_build_vars(bc, family, apto->ap_netaddr, MATCH_DST);
1.1       rmind     351:
                    352:        /* Build port-range blocks. */
1.38      rmind     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:        }
1.27      rmind     360:        npfctl_build_vars(bc, family, apfrom->ap_portrange, MATCH_SRC);
                    361:        npfctl_build_vars(bc, family, apto->ap_portrange, MATCH_DST);
1.25      rmind     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:        }
1.1       rmind     368:
1.25      rmind     369:        /* Complete BPF byte-code and pass to the rule. */
                    370:        struct bpf_program *bf = npfctl_bpf_complete(bc);
1.38.2.2! snj       371:        if (bf == NULL) {
        !           372:                npfctl_bpf_destroy(bc);
        !           373:                return true;
        !           374:        }
1.25      rmind     375:        len = bf->bf_len * sizeof(struct bpf_insn);
1.10      rmind     376:
1.25      rmind     377:        if (npf_rule_setcode(rl, NPF_CODE_BPF, bf->bf_insns, len) == -1) {
1.1       rmind     378:                errx(EXIT_FAILURE, "npf_rule_setcode failed");
                    379:        }
1.27      rmind     380:        npfctl_dump_bpf(bf);
1.25      rmind     381:        npfctl_bpf_destroy(bc);
                    382:
1.1       rmind     383:        return true;
                    384: }
                    385:
1.4       rmind     386: static void
1.27      rmind     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
1.4       rmind     407: npfctl_build_rpcall(nl_rproc_t *rp, const char *name, npfvar_t *args)
                    408: {
1.14      rmind     409:        npf_extmod_t *extmod;
                    410:        nl_ext_t *extcall;
                    411:        int error;
1.4       rmind     412:
1.14      rmind     413:        extmod = npf_extmod_get(name, &extcall);
                    414:        if (extmod == NULL) {
1.4       rmind     415:                yyerror("unknown rule procedure '%s'", name);
                    416:        }
                    417:
                    418:        for (size_t i = 0; i < npfvar_get_count(args); i++) {
1.14      rmind     419:                const char *param, *value;
                    420:                proc_param_t *p;
1.4       rmind     421:
1.14      rmind     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;
1.4       rmind     432:                }
                    433:        }
1.14      rmind     434:        error = npf_rproc_extcall(rp, extcall);
                    435:        if (error) {
                    436:                yyerror(error == EEXIST ?
                    437:                    "duplicate procedure call" : "unexpected error");
                    438:        }
1.4       rmind     439: }
                    440:
1.1       rmind     441: /*
                    442:  * npfctl_build_rproc: create and insert a rule procedure.
                    443:  */
                    444: void
1.4       rmind     445: npfctl_build_rproc(const char *name, npfvar_t *procs)
1.1       rmind     446: {
                    447:        nl_rproc_t *rp;
1.4       rmind     448:        size_t i;
1.1       rmind     449:
                    450:        rp = npf_rproc_create(name);
                    451:        if (rp == NULL) {
1.23      christos  452:                errx(EXIT_FAILURE, "%s failed", __func__);
1.1       rmind     453:        }
                    454:        npf_rproc_insert(npf_conf, rp);
1.4       rmind     455:
                    456:        for (i = 0; i < npfvar_get_count(procs); i++) {
1.14      rmind     457:                proc_call_t *pc = npfvar_get_data(procs, NPFVAR_PROC, i);
                    458:                npfctl_build_rpcall(rp, pc->pc_name, pc->pc_opts);
1.4       rmind     459:        }
1.1       rmind     460: }
                    461:
1.22      rmind     462: void
1.28      rmind     463: npfctl_build_maprset(const char *name, int attr, const char *ifname)
1.22      rmind     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);
1.28      rmind     474:        rl = npf_rule_create(name, attr, ifname);
1.22      rmind     475:        npf_nat_insert(npf_conf, rl, NPF_PRI_LAST);
                    476: }
                    477:
1.1       rmind     478: /*
1.18      rmind     479:  * npfctl_build_group: create a group, insert into the global ruleset,
                    480:  * update the current group pointer and increase the nesting level.
1.1       rmind     481:  */
                    482: void
1.28      rmind     483: npfctl_build_group(const char *name, int attr, const char *ifname, bool def)
1.1       rmind     484: {
                    485:        const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT);
                    486:        nl_rule_t *rl;
                    487:
1.18      rmind     488:        if (def || (attr & attr_di) == 0) {
                    489:                attr |= attr_di;
                    490:        }
                    491:
1.28      rmind     492:        rl = npf_rule_create(name, attr | NPF_RULE_GROUP, ifname);
1.18      rmind     493:        npf_rule_setprio(rl, NPF_PRI_LAST);
                    494:        if (def) {
                    495:                if (defgroup) {
1.1       rmind     496:                        yyerror("multiple default groups are not valid");
                    497:                }
1.18      rmind     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:        }
1.1       rmind     506:
1.18      rmind     507:        /* Set the current group and increase the nesting level. */
                    508:        if (rule_nesting_level >= MAX_RULE_NESTING) {
                    509:                yyerror("rule nesting limit reached");
1.1       rmind     510:        }
1.18      rmind     511:        current_group[++rule_nesting_level] = rl;
                    512: }
1.1       rmind     513:
1.18      rmind     514: void
                    515: npfctl_build_group_end(void)
                    516: {
                    517:        assert(rule_nesting_level > 0);
                    518:        current_group[rule_nesting_level--] = NULL;
1.1       rmind     519: }
                    520:
                    521: /*
1.26      rmind     522:  * npfctl_build_rule: create a rule, build byte-code from filter options,
1.18      rmind     523:  * if any, and insert into the ruleset of current group, or set the rule.
1.1       rmind     524:  */
                    525: void
1.28      rmind     526: npfctl_build_rule(uint32_t attr, const char *ifname, sa_family_t family,
1.27      rmind     527:     const opt_proto_t *op, const filt_opts_t *fopts,
                    528:     const char *pcap_filter, const char *rproc)
1.1       rmind     529: {
                    530:        nl_rule_t *rl;
                    531:
1.19      rmind     532:        attr |= (npf_conf ? 0 : NPF_RULE_DYNAMIC);
1.21      rmind     533:
1.28      rmind     534:        rl = npf_rule_create(NULL, attr, ifname);
1.27      rmind     535:        if (pcap_filter) {
                    536:                npfctl_build_pcap(rl, pcap_filter);
                    537:        } else {
                    538:                npfctl_build_code(rl, family, op, fopts);
                    539:        }
                    540:
1.18      rmind     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;
1.1       rmind     557:        }
                    558: }
                    559:
                    560: /*
1.14      rmind     561:  * npfctl_build_nat: create a single NAT policy of a specified
1.13      rmind     562:  * type with a given filter options.
                    563:  */
1.36      rmind     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)
1.13      rmind     567: {
                    568:        const opt_proto_t op = { .op_proto = -1, .op_opts = NULL };
1.36      rmind     569:        fam_addr_mask_t *am = npfctl_get_singlefam(ap->ap_netaddr);
1.13      rmind     570:        in_port_t port;
                    571:        nl_nat_t *nat;
                    572:
1.35      rmind     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 {
1.13      rmind     578:                port = 0;
                    579:        }
                    580:
1.36      rmind     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);
1.18      rmind     584:        npf_nat_insert(npf_conf, nat, NPF_PRI_LAST);
1.36      rmind     585:        return nat;
1.13      rmind     586: }
                    587:
                    588: /*
1.14      rmind     589:  * npfctl_build_natseg: validate and create NAT policies.
1.1       rmind     590:  */
                    591: void
1.28      rmind     592: npfctl_build_natseg(int sd, int type, const char *ifname,
                    593:     const addr_port_t *ap1, const addr_port_t *ap2,
1.36      rmind     594:     const filt_opts_t *fopts, u_int algo)
1.1       rmind     595: {
1.36      rmind     596:        fam_addr_mask_t *am1 = NULL, *am2 = NULL;
                    597:        nl_nat_t *nt1 = NULL, *nt2 = NULL;
1.7       rmind     598:        filt_opts_t imfopts;
1.36      rmind     599:        uint16_t adj = 0;
1.35      rmind     600:        u_int flags;
1.13      rmind     601:        bool binat;
1.1       rmind     602:
1.28      rmind     603:        assert(ifname != NULL);
1.7       rmind     604:
1.13      rmind     605:        /*
                    606:         * Bi-directional NAT is a combination of inbound NAT and outbound
1.35      rmind     607:         * NAT policies with the translation segments inverted respectively.
1.13      rmind     608:         */
                    609:        binat = (NPF_NATIN | NPF_NATOUT) == type;
1.7       rmind     610:
1.35      rmind     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:
1.7       rmind     627:        /*
1.36      rmind     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:        /*
1.13      rmind     660:         * If the filter criteria is not specified explicitly, apply implicit
1.14      rmind     661:         * filtering according to the given network segments.
1.13      rmind     662:         *
                    663:         * Note: filled below, depending on the type.
1.7       rmind     664:         */
1.14      rmind     665:        if (__predict_true(!fopts)) {
1.7       rmind     666:                fopts = &imfopts;
1.1       rmind     667:        }
                    668:
1.13      rmind     669:        if (type & NPF_NATIN) {
                    670:                memset(&imfopts, 0, sizeof(filt_opts_t));
                    671:                memcpy(&imfopts.fo_to, ap2, sizeof(addr_port_t));
1.36      rmind     672:                nt1 = npfctl_build_nat(NPF_NATIN, ifname, ap1, fopts, flags);
1.13      rmind     673:        }
                    674:        if (type & NPF_NATOUT) {
                    675:                memset(&imfopts, 0, sizeof(filt_opts_t));
                    676:                memcpy(&imfopts.fo_from, ap1, sizeof(addr_port_t));
1.36      rmind     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);
1.1       rmind     683:        }
                    684: }
                    685:
                    686: /*
                    687:  * npfctl_fill_table: fill NPF table with entries from a specified file.
                    688:  */
                    689: static void
1.11      rmind     690: npfctl_fill_table(nl_table_t *tl, u_int type, const char *fname)
1.1       rmind     691: {
1.34      christos  692:        struct cdbw *cdbw = NULL;       /* XXX: gcc */
1.1       rmind     693:        char *buf = NULL;
                    694:        int l = 0;
                    695:        FILE *fp;
                    696:        size_t n;
                    697:
1.33      rmind     698:        if (type == NPF_TABLE_CDB && (cdbw = cdbw_open()) == NULL) {
                    699:                err(EXIT_FAILURE, "cdbw_open");
                    700:        }
1.1       rmind     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) {
1.11      rmind     706:                fam_addr_mask_t fam;
                    707:                int alen;
1.1       rmind     708:
                    709:                if (*buf == '\n' || *buf == '#') {
                    710:                        continue;
                    711:                }
1.11      rmind     712:
                    713:                if (!npfctl_parse_cidr(buf, &fam, &alen)) {
                    714:                        errx(EXIT_FAILURE,
                    715:                            "%s:%d: invalid table entry", fname, l);
                    716:                }
1.33      rmind     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);
1.1       rmind     720:                }
                    721:
1.33      rmind     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:                }
1.1       rmind     734:        }
                    735:        if (buf != NULL) {
                    736:                free(buf);
                    737:        }
1.33      rmind     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:        }
1.1       rmind     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
1.29      rmind     774: npfctl_build_table(const char *tname, u_int type, const char *fname)
1.1       rmind     775: {
1.29      rmind     776:        static unsigned tid = 0;
1.1       rmind     777:        nl_table_t *tl;
                    778:
1.29      rmind     779:        tl = npf_table_create(tname, tid++, type);
1.1       rmind     780:        assert(tl != NULL);
                    781:
                    782:        if (npf_table_insert(npf_conf, tl)) {
1.29      rmind     783:                yyerror("table '%s' is already defined", tname);
1.1       rmind     784:        }
                    785:
                    786:        if (fname) {
1.11      rmind     787:                npfctl_fill_table(tl, type, fname);
1.33      rmind     788:        } else if (type == NPF_TABLE_CDB) {
                    789:                errx(EXIT_FAILURE, "tables of cdb type must be static");
1.1       rmind     790:        }
                    791: }
1.23      christos  792:
                    793: /*
1.25      rmind     794:  * npfctl_build_alg: create an NPF application level gateway and add it
1.23      christos  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: }
1.27      rmind     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: }

CVSweb <webmaster@jp.NetBSD.org>