Module Name: src Committed By: martin Date: Fri Oct 4 08:06:35 UTC 2019
Modified Files: src/lib/libnpf [netbsd-9]: libnpf.3 npf.c npf.h src/sys/net/npf [netbsd-9]: npf_conn.c npf_ctl.c npf_if.c npf_impl.h npf_ruleset.c src/usr.sbin/npf/npfctl [netbsd-9]: npf.conf.5 npf_build.c npf_parse.y npf_scan.l npfctl.8 npfctl.c npfctl.h src/usr.sbin/npf/npftest [netbsd-9]: npftest.conf Log Message: Pull up following revision(s) (requested by rmind in ticket #282): usr.sbin/npf/npfctl/npf_build.c: revision 1.53 lib/libnpf/npf.c: revision 1.48 usr.sbin/npf/npfctl/npfctl.h: revision 1.50 sys/net/npf/npf_impl.h: revision 1.80 usr.sbin/npf/npfctl/npfctl.h: revision 1.51 sys/net/npf/npf_ruleset.c: revision 1.49 usr.sbin/npf/npfctl/npf.conf.5: revision 1.90 sys/net/npf/npf_ctl.c: revision 1.59 lib/libnpf/libnpf.3: revision 1.11 usr.sbin/npf/npfctl/npf_parse.y: revision 1.50 usr.sbin/npf/npftest/npftest.conf: revision 1.8 usr.sbin/npf/npfctl/npfctl.c: revision 1.62 usr.sbin/npf/npfctl/npfctl.c: revision 1.63 usr.sbin/npf/npfctl/npf_scan.l: revision 1.30 usr.sbin/npf/npfctl/npfctl.8: revision 1.22 lib/libnpf/npf.h: revision 1.38 usr.sbin/npf/npfctl/npfctl.8: revision 1.23 usr.sbin/npf/npfctl/npfctl.8: revision 1.24 sys/net/npf/npf_if.c: revision 1.11 sys/net/npf/npf_if.c: revision 1.12 usr.sbin/npf/npfctl/npf.conf.5: revision 1.89 sys/net/npf/npf_conn.c: revision 1.30 usr.sbin/npf/npfctl/npf_build.c: revision 1.52 npfctl: implement table replace subcommand. Contributed by Timshel Knoll-Miller. NPF ifmap: rework and fix a few small bugs. npfctl: implement table replace subcommand. Contributed by Timshel Knoll-Miller. (missed a file in previous commit; cvs is so helpful..) libnpf/npfctl: support dynamic NAT rulesets using a name prefix. Use -width Pa for FILES. Fix pasto in table replace -t type Use -width Pa for FILES. npf_ifmap_copylogname: be more defensive. To generate a diff of this commit: cvs rdiff -u -r1.9.2.1 -r1.9.2.2 src/lib/libnpf/libnpf.3 cvs rdiff -u -r1.46.2.1 -r1.46.2.2 src/lib/libnpf/npf.c cvs rdiff -u -r1.36.2.1 -r1.36.2.2 src/lib/libnpf/npf.h cvs rdiff -u -r1.27.2.1 -r1.27.2.2 src/sys/net/npf/npf_conn.c cvs rdiff -u -r1.54.2.3 -r1.54.2.4 src/sys/net/npf/npf_ctl.c cvs rdiff -u -r1.9.4.1 -r1.9.4.2 src/sys/net/npf/npf_if.c cvs rdiff -u -r1.75.2.3 -r1.75.2.4 src/sys/net/npf/npf_impl.h cvs rdiff -u -r1.48 -r1.48.2.1 src/sys/net/npf/npf_ruleset.c cvs rdiff -u -r1.88 -r1.88.2.1 src/usr.sbin/npf/npfctl/npf.conf.5 cvs rdiff -u -r1.50.2.1 -r1.50.2.2 src/usr.sbin/npf/npfctl/npf_build.c cvs rdiff -u -r1.49 -r1.49.2.1 src/usr.sbin/npf/npfctl/npf_parse.y cvs rdiff -u -r1.29 -r1.29.2.1 src/usr.sbin/npf/npfctl/npf_scan.l cvs rdiff -u -r1.21 -r1.21.2.1 src/usr.sbin/npf/npfctl/npfctl.8 cvs rdiff -u -r1.60.2.1 -r1.60.2.2 src/usr.sbin/npf/npfctl/npfctl.c cvs rdiff -u -r1.48.2.1 -r1.48.2.2 src/usr.sbin/npf/npfctl/npfctl.h cvs rdiff -u -r1.7 -r1.7.2.1 src/usr.sbin/npf/npftest/npftest.conf Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/libnpf/libnpf.3 diff -u src/lib/libnpf/libnpf.3:1.9.2.1 src/lib/libnpf/libnpf.3:1.9.2.2 --- src/lib/libnpf/libnpf.3:1.9.2.1 Sun Sep 1 13:13:13 2019 +++ src/lib/libnpf/libnpf.3 Fri Oct 4 08:06:35 2019 @@ -1,4 +1,4 @@ -.\" $NetBSD: libnpf.3,v 1.9.2.1 2019/09/01 13:13:13 martin Exp $ +.\" $NetBSD: libnpf.3,v 1.9.2.2 2019/10/04 08:06:35 martin Exp $ .\" .\" Copyright (c) 2011-2019 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -27,7 +27,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd August 21, 2019 +.Dd August 25, 2019 .Dt LIBNPF 3 .Os .Sh NAME @@ -108,6 +108,15 @@ .Fn npf_table_replace "int fd" "nl_table_t *tl" "npf_error_t *errinfo" .Ft void .Fn npf_table_destroy "nl_table_t *tl" +.\" --- +.Ft int +.Fn npf_ruleset_add "int fd" "const char *name" "nl_rule_t *rl" "uint64_t *id" +.Ft int +.Fn npf_ruleset_remove "int fd" "const char *name" "uint64_t id" +.Ft int +.Fn npf_ruleset_remkey "int fd" "const char *name" "const void *key" "size_t len" +.Ft int +.Fn npf_ruleset_flush "int fd" "const char *name" .\" ----- .Sh DESCRIPTION The @@ -352,7 +361,9 @@ Additionally, may be specified to indicate the translation network; otherwise, it should be set to .Dv NPF_NO_NETMASK . -In such case, a custom algorithm may need to be specified using the +.Pp +In order to use the translation network, a custom algorithm may need to +be specified using the .Fn npf_nat_setalgo function. .\" --- @@ -368,6 +379,9 @@ Currently, the following algorithms are Hash of the source and destination addresses. .It Dv NPF_ALGO_RR Round-robin for the translation addresses. +.It Dv NPF_ALGO_NETMAP +Network-to-network map as described below, but with state tracking. +It is used when it is necessary to translate the ports. .El .Pp The following are support with static NAT: @@ -450,6 +464,39 @@ specified by Destroy the specified table. .El .\" ----- +.Ss Ruleset interface +.Bl -tag -width 4n +.It Fn npf_ruleset_add "fd" "name" "rl" "id" +Add a given rule, specified by +.Fa rl , +into the dynamic ruleset named +.Fa name . +On success, return 0 and a unique rule ID in the +.Fa id +parameter. +.It Fn npf_ruleset_remove "fd" "name" "id" +Remove a rule from the dynamic ruleset, specified by +.Fa name . +The rule is specified by its unique ID in the +.Fa id +parameter. +.It Fn npf_ruleset_remkey "fd" "name" "key" "len" +Remove a rule from the dynamic ruleset, specified by +.Fa name . +The rule is specified by its key, in the +.Fa key +and +.Fa len +parameters. +The key for the rule must have been set during its construction, using the +.Fn npf_rule_setkey +routine. +.It Fn npf_ruleset_flush "fd" "name" +Clear the dynamic ruleset, specified by +.Fa name , +by removing all its rules. +.El +.\" ----- .Sh SEE ALSO .Xr bpf 4 , .Xr npf 7 , Index: src/lib/libnpf/npf.c diff -u src/lib/libnpf/npf.c:1.46.2.1 src/lib/libnpf/npf.c:1.46.2.2 --- src/lib/libnpf/npf.c:1.46.2.1 Sun Sep 1 13:13:13 2019 +++ src/lib/libnpf/npf.c Fri Oct 4 08:06:35 2019 @@ -28,7 +28,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.46.2.1 2019/09/01 13:13:13 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.46.2.2 2019/10/04 08:06:35 martin Exp $"); #include <sys/types.h> #include <sys/mman.h> @@ -401,14 +401,31 @@ npf_param_set(nl_config_t *ncf, const ch * DYNAMIC RULESET INTERFACE. */ +static inline bool +_npf_nat_ruleset_p(const char *name) +{ + return strncmp(name, NPF_RULESET_MAP_PREF, + sizeof(NPF_RULESET_MAP_PREF) - 1) == 0; +} + int npf_ruleset_add(int fd, const char *rname, nl_rule_t *rl, uint64_t *id) { + const bool natset = _npf_nat_ruleset_p(rname); nvlist_t *rule_dict = rl->rule_dict; nvlist_t *ret_dict; + nvlist_add_number(rule_dict, "attr", + NPF_RULE_DYNAMIC | nvlist_take_number(rule_dict, "attr")); + + if (natset && !dnvlist_get_bool(rule_dict, "nat-rule", false)) { + errno = EINVAL; + return errno; + } nvlist_add_string(rule_dict, "ruleset-name", rname); + nvlist_add_bool(rule_dict, "nat-ruleset", natset); nvlist_add_number(rule_dict, "command", NPF_CMD_RULE_ADD); + if (nvlist_xfer_ioctl(fd, IOC_NPF_RULE, rule_dict, &ret_dict) == -1) { return errno; } @@ -419,11 +436,14 @@ npf_ruleset_add(int fd, const char *rnam int npf_ruleset_remove(int fd, const char *rname, uint64_t id) { + const bool natset = _npf_nat_ruleset_p(rname); nvlist_t *rule_dict = nvlist_create(0); nvlist_add_string(rule_dict, "ruleset-name", rname); + nvlist_add_bool(rule_dict, "nat-ruleset", natset); nvlist_add_number(rule_dict, "command", NPF_CMD_RULE_REMOVE); nvlist_add_number(rule_dict, "id", id); + if (nvlist_send_ioctl(fd, IOC_NPF_RULE, rule_dict) == -1) { return errno; } @@ -433,11 +453,14 @@ npf_ruleset_remove(int fd, const char *r int npf_ruleset_remkey(int fd, const char *rname, const void *key, size_t len) { + const bool natset = _npf_nat_ruleset_p(rname); nvlist_t *rule_dict = nvlist_create(0); nvlist_add_string(rule_dict, "ruleset-name", rname); + nvlist_add_bool(rule_dict, "nat-ruleset", natset); nvlist_add_number(rule_dict, "command", NPF_CMD_RULE_REMKEY); nvlist_add_binary(rule_dict, "key", key, len); + if (nvlist_send_ioctl(fd, IOC_NPF_RULE, rule_dict) == -1) { return errno; } @@ -447,10 +470,13 @@ npf_ruleset_remkey(int fd, const char *r int npf_ruleset_flush(int fd, const char *rname) { + const bool natset = _npf_nat_ruleset_p(rname); nvlist_t *rule_dict = nvlist_create(0); nvlist_add_string(rule_dict, "ruleset-name", rname); + nvlist_add_bool(rule_dict, "nat-ruleset", natset); nvlist_add_number(rule_dict, "command", NPF_CMD_RULE_FLUSH); + if (nvlist_send_ioctl(fd, IOC_NPF_RULE, rule_dict) == -1) { return errno; } @@ -678,10 +704,12 @@ npf_rule_getcode(nl_rule_t *rl, int *typ int _npf_ruleset_list(int fd, const char *rname, nl_config_t *ncf) { + const bool natset = _npf_nat_ruleset_p(rname); nvlist_t *req, *ret; req = nvlist_create(0); nvlist_add_string(req, "ruleset-name", rname); + nvlist_add_bool(req, "nat-ruleset", natset); nvlist_add_number(req, "command", NPF_CMD_RULE_LIST); if (nvlist_xfer_ioctl(fd, IOC_NPF_RULE, req, &ret) == -1) { Index: src/lib/libnpf/npf.h diff -u src/lib/libnpf/npf.h:1.36.2.1 src/lib/libnpf/npf.h:1.36.2.2 --- src/lib/libnpf/npf.h:1.36.2.1 Sun Sep 1 13:13:13 2019 +++ src/lib/libnpf/npf.h Fri Oct 4 08:06:35 2019 @@ -56,6 +56,12 @@ typedef struct nl_ext nl_ext_t; typedef signed long nl_iter_t; /* + * Ruleset prefix(es). + */ + +#define NPF_RULESET_MAP_PREF "map:" + +/* * Extensions API types. */ typedef int (*npfext_initfunc_t)(void); Index: src/sys/net/npf/npf_conn.c diff -u src/sys/net/npf/npf_conn.c:1.27.2.1 src/sys/net/npf/npf_conn.c:1.27.2.2 --- src/sys/net/npf/npf_conn.c:1.27.2.1 Wed Aug 7 08:28:37 2019 +++ src/sys/net/npf/npf_conn.c Fri Oct 4 08:06:35 2019 @@ -107,7 +107,7 @@ #ifdef _KERNEL #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.27.2.1 2019/08/07 08:28:37 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.27.2.2 2019/10/04 08:06:35 martin Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -782,7 +782,8 @@ npf_conn_export(npf_t *npf, npf_conn_t * nvlist_add_number(cdict, "flags", con->c_flags); nvlist_add_number(cdict, "proto", con->c_proto); if (con->c_ifid) { - const char *ifname = npf_ifmap_getname(npf, con->c_ifid); + char ifname[IFNAMSIZ]; + npf_ifmap_copyname(npf, con->c_ifid, ifname, sizeof(ifname)); nvlist_add_string(cdict, "ifname", ifname); } nvlist_add_binary(cdict, "state", &con->c_state, sizeof(npf_state_t)); Index: src/sys/net/npf/npf_ctl.c diff -u src/sys/net/npf/npf_ctl.c:1.54.2.3 src/sys/net/npf/npf_ctl.c:1.54.2.4 --- src/sys/net/npf/npf_ctl.c:1.54.2.3 Sun Sep 1 13:21:39 2019 +++ src/sys/net/npf/npf_ctl.c Fri Oct 4 08:06:35 2019 @@ -36,7 +36,7 @@ #ifdef _KERNEL #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.54.2.3 2019/09/01 13:21:39 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.54.2.4 2019/10/04 08:06:35 martin Exp $"); #include <sys/param.h> #include <sys/conf.h> @@ -196,7 +196,7 @@ npf_mk_table(npf_t *npf, const nvlist_t goto out; } - t = npf_table_create(name, (u_int)tid, type, blob, size); + t = npf_table_create(name, (unsigned)tid, type, blob, size); if (t == NULL) { NPF_ERR_DEBUG(errdict); error = ENOMEM; @@ -473,7 +473,7 @@ npf_mk_singlenat(npf_t *npf, const nvlis KASSERT(rl != NULL); *rlp = rl; - /* If rule is named, it is a group with NAT policies. */ + /* If this rule is named, then it is a group with NAT policies. */ if (dnvlist_get_string(nat, "name", NULL)) { return 0; } @@ -816,7 +816,7 @@ npfctl_rule(npf_t *npf, u_long cmd, void return error; } rcmd = dnvlist_get_number(npf_rule, "command", 0); - natset = dnvlist_get_bool(npf_rule, "nat-rule", false); + natset = dnvlist_get_bool(npf_rule, "nat-ruleset", false); ruleset_name = dnvlist_get_string(npf_rule, "ruleset-name", NULL); if (!ruleset_name) { error = EINVAL; Index: src/sys/net/npf/npf_if.c diff -u src/sys/net/npf/npf_if.c:1.9.4.1 src/sys/net/npf/npf_if.c:1.9.4.2 --- src/sys/net/npf/npf_if.c:1.9.4.1 Tue Aug 13 14:35:55 2019 +++ src/sys/net/npf/npf_if.c Fri Oct 4 08:06:35 2019 @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2019 Mindaugas Rasiukevicius <rmind at noxt eu> * Copyright (c) 2013 The NetBSD Foundation, Inc. * All rights reserved. * @@ -28,23 +29,34 @@ */ /* - * NPF network interface handling module. + * NPF network interface handling. * - * NPF uses its own interface IDs (npf-if-id). When NPF configuration is - * (re)loaded, each required interface name is registered and a matching - * network interface gets an ID assigned. If an interface is not present, - * it gets an ID on attach. + * NPF uses its own interface IDs (npf-if-id). These IDs start from 1. + * Zero is reserved to indicate "no interface" case or an interface of + * no interest (i.e. not registered). * - * IDs start from 1. Zero is reserved to indicate "no interface" case or - * an interface of no interest (i.e. not registered). + * This module provides an interface to primarily handle the following: * - * The IDs are mapped synchronously based on interface events which are - * monitored using pfil(9) hooks. + * - Bind a symbolic interface name to NPF interface ID. + * - Associate NPF interface ID when the network interface is attached. + * + * When NPF configuration is (re)loaded, each referenced network interface + * name is registered with a unique ID. If the network interface is already + * attached, then the ID is associated with it immediately; otherwise, IDs + * are associated/disassociated on interface events which are monitored + * using pfil(9) hooks. + * + * To avoid race conditions when an active NPF configuration is updated or + * interfaces are detached/attached, the interface names are never removed + * and therefore IDs are never re-assigned. The only point when interface + * names and IDs are cleared is when the configuration is flushed. + * + * A linear counter is used for IDs. */ #ifdef _KERNEL #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_if.c,v 1.9.4.1 2019/08/13 14:35:55 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_if.c,v 1.9.4.2 2019/10/04 08:06:35 martin Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -55,9 +67,13 @@ __KERNEL_RCSID(0, "$NetBSD: npf_if.c,v 1 #include "npf_impl.h" typedef struct npf_ifmap { - char n_ifname[IFNAMSIZ]; + char ifname[IFNAMSIZ + 1]; } npf_ifmap_t; +#define NPF_IFMAP_NOID (0U) +#define NPF_IFMAP_SLOT2ID(npf, slot) ((npf)->ifmap_off + (slot) + 1) +#define NPF_IFMAP_ID2SLOT(npf, id) ((id) - (npf)->ifmap_off - 1) + void npf_ifmap_init(npf_t *npf, const npf_ifops_t *ifops) { @@ -66,8 +82,10 @@ npf_ifmap_init(npf_t *npf, const npf_ifo KASSERT(ifops != NULL); ifops->flush((void *)(uintptr_t)0); + mutex_init(&npf->ifmap_lock, MUTEX_DEFAULT, IPL_SOFTNET); npf->ifmap = kmem_zalloc(nbytes, KM_SLEEP); npf->ifmap_cnt = 0; + npf->ifmap_off = 0; npf->ifops = ifops; } @@ -75,128 +93,151 @@ void npf_ifmap_fini(npf_t *npf) { const size_t nbytes = sizeof(npf_ifmap_t) * NPF_MAX_IFMAP; + mutex_destroy(&npf->ifmap_lock); kmem_free(npf->ifmap, nbytes); } -static u_int -npf_ifmap_new(npf_t *npf) -{ - KASSERT(npf_config_locked_p(npf)); - - for (u_int i = 0; i < npf->ifmap_cnt; i++) - if (npf->ifmap[i].n_ifname[0] == '\0') - return i + 1; - - if (npf->ifmap_cnt == NPF_MAX_IFMAP) { - printf("npf_ifmap_new: out of slots; bump NPF_MAX_IFMAP\n"); - return 0; - } - return ++npf->ifmap_cnt; -} - -static u_int +static unsigned npf_ifmap_lookup(npf_t *npf, const char *ifname) { - KASSERT(npf_config_locked_p(npf)); + KASSERT(mutex_owned(&npf->ifmap_lock)); - for (u_int i = 0; i < npf->ifmap_cnt; i++) { - npf_ifmap_t *nim = &npf->ifmap[i]; + for (unsigned i = 0; i < npf->ifmap_cnt; i++) { + npf_ifmap_t *ifmap = &npf->ifmap[i]; - if (nim->n_ifname[0] && strcmp(nim->n_ifname, ifname) == 0) - return i + 1; + if (strcmp(ifmap->ifname, ifname) == 0) { + return NPF_IFMAP_SLOT2ID(npf, i); + } } - return 0; + return NPF_IFMAP_NOID; } -u_int +/* + * npf_ifmap_register: register an interface name; return an assigned + * NPF network ID on success (non-zero). + * + * This routine is mostly called on NPF configuration (re)load for the + * interfaces names referenced by the rules. + */ +unsigned npf_ifmap_register(npf_t *npf, const char *ifname) { - npf_ifmap_t *nim; + npf_ifmap_t *ifmap; + unsigned id, i; ifnet_t *ifp; - u_int i; - npf_config_enter(npf); - if ((i = npf_ifmap_lookup(npf, ifname)) != 0) { + mutex_enter(&npf->ifmap_lock); + if ((id = npf_ifmap_lookup(npf, ifname)) != NPF_IFMAP_NOID) { goto out; } - if ((i = npf_ifmap_new(npf)) == 0) { + if (npf->ifmap_cnt == NPF_MAX_IFMAP) { + printf("npf_ifmap_new: out of slots; bump NPF_MAX_IFMAP\n"); + id = NPF_IFMAP_NOID; goto out; } - nim = &npf->ifmap[i - 1]; - strlcpy(nim->n_ifname, ifname, IFNAMSIZ); + KASSERT(npf->ifmap_cnt < NPF_MAX_IFMAP); + + /* Allocate a new slot and convert and assign an ID. */ + i = npf->ifmap_cnt++; + ifmap = &npf->ifmap[i]; + strlcpy(ifmap->ifname, ifname, IFNAMSIZ); + id = NPF_IFMAP_SLOT2ID(npf, i); if ((ifp = npf->ifops->lookup(ifname)) != NULL) { - npf->ifops->setmeta(ifp, (void *)(uintptr_t)i); + npf->ifops->setmeta(ifp, (void *)(uintptr_t)id); } out: - npf_config_exit(npf); - return i; + mutex_exit(&npf->ifmap_lock); + return id; } void npf_ifmap_flush(npf_t *npf) { - KASSERT(npf_config_locked_p(npf)); - - for (u_int i = 0; i < npf->ifmap_cnt; i++) { - npf->ifmap[i].n_ifname[0] = '\0'; + mutex_enter(&npf->ifmap_lock); + npf->ifops->flush((void *)(uintptr_t)NPF_IFMAP_NOID); + for (unsigned i = 0; i < npf->ifmap_cnt; i++) { + npf->ifmap[i].ifname[0] = '\0'; } npf->ifmap_cnt = 0; - npf->ifops->flush((void *)(uintptr_t)0); + + /* + * Reset the ID counter if reaching the overflow; this is not + * realistic, but we maintain correctness. + */ + if (npf->ifmap_off < (UINT_MAX - NPF_MAX_IFMAP)) { + npf->ifmap_off += NPF_MAX_IFMAP; + } else { + npf->ifmap_off = 0; + } + mutex_exit(&npf->ifmap_lock); } -u_int +/* + * npf_ifmap_getid: get the ID for the given network interface. + * + * => This routine is typically called from the packet handler when + * matching whether the packet is on particular network interface. + * + * => This routine is lock-free; if the NPF configuration is flushed + * while the packet is in-flight, the ID will not match because we + * keep the IDs linear. + */ +unsigned npf_ifmap_getid(npf_t *npf, const ifnet_t *ifp) { - const u_int i = (uintptr_t)npf->ifops->getmeta(ifp); - KASSERT(i <= npf->ifmap_cnt); - return i; + const unsigned id = (uintptr_t)npf->ifops->getmeta(ifp); + return id; } /* - * This function is toxic; it can return garbage since we don't - * lock, but it is only used temporarily and only for logging. + * npf_ifmap_copylogname: this function is toxic; it can return garbage + * as we don't lock, but it is only used temporarily and only for logging. */ void -npf_ifmap_copyname(npf_t *npf, u_int id, char *buf, size_t len) +npf_ifmap_copylogname(npf_t *npf, unsigned id, char *buf, size_t len) { - if (id > 0 && id < npf->ifmap_cnt) - strlcpy(buf, npf->ifmap[id - 1].n_ifname, - MIN(len, sizeof(npf->ifmap[id - 1].n_ifname))); - else + const unsigned i = NPF_IFMAP_ID2SLOT(npf, id); + + membar_consumer(); + + if (id != NPF_IFMAP_NOID && i < NPF_MAX_IFMAP) { + /* + * Lock-free access is safe as there is an extra byte + * with a permanent NUL terminator at the end. + */ + const npf_ifmap_t *ifmap = &npf->ifmap[i]; + strlcpy(buf, ifmap->ifname, MIN(len, IFNAMSIZ)); + } else { strlcpy(buf, "???", len); + } } -const char * -npf_ifmap_getname(npf_t *npf, const u_int id) +void +npf_ifmap_copyname(npf_t *npf, unsigned id, char *buf, size_t len) { - const char *ifname; - - KASSERT(npf_config_locked_p(npf)); - KASSERT(id > 0 && id <= npf->ifmap_cnt); - - ifname = npf->ifmap[id - 1].n_ifname; - KASSERT(ifname[0] != '\0'); - return ifname; + mutex_enter(&npf->ifmap_lock); + npf_ifmap_copylogname(npf, id, buf, len); + mutex_exit(&npf->ifmap_lock); } __dso_public void npfk_ifmap_attach(npf_t *npf, ifnet_t *ifp) { const npf_ifops_t *ifops = npf->ifops; - u_int i; + unsigned id; - npf_config_enter(npf); - i = npf_ifmap_lookup(npf, ifops->getname(ifp)); - ifops->setmeta(ifp, (void *)(uintptr_t)i); - npf_config_exit(npf); + mutex_enter(&npf->ifmap_lock); + id = npf_ifmap_lookup(npf, ifops->getname(ifp)); + ifops->setmeta(ifp, (void *)(uintptr_t)id); + mutex_exit(&npf->ifmap_lock); } __dso_public void npfk_ifmap_detach(npf_t *npf, ifnet_t *ifp) { /* Diagnostic. */ - npf_config_enter(npf); - npf->ifops->setmeta(ifp, (void *)(uintptr_t)0); - npf_config_exit(npf); + mutex_enter(&npf->ifmap_lock); + npf->ifops->setmeta(ifp, (void *)(uintptr_t)NPF_IFMAP_NOID); + mutex_exit(&npf->ifmap_lock); } Index: src/sys/net/npf/npf_impl.h diff -u src/sys/net/npf/npf_impl.h:1.75.2.3 src/sys/net/npf/npf_impl.h:1.75.2.4 --- src/sys/net/npf/npf_impl.h:1.75.2.3 Sun Sep 1 13:21:39 2019 +++ src/sys/net/npf/npf_impl.h Fri Oct 4 08:06:35 2019 @@ -234,6 +234,8 @@ struct npf { const npf_ifops_t * ifops; struct npf_ifmap * ifmap; unsigned ifmap_cnt; + unsigned ifmap_off; + kmutex_t ifmap_lock; /* Associated worker thread. */ unsigned worker_id; @@ -319,8 +321,8 @@ void npf_ifmap_fini(npf_t *); u_int npf_ifmap_register(npf_t *, const char *); void npf_ifmap_flush(npf_t *); u_int npf_ifmap_getid(npf_t *, const ifnet_t *); -const char * npf_ifmap_getname(npf_t *, const u_int); -void npf_ifmap_copyname(npf_t *, u_int, char *, size_t); +void npf_ifmap_copylogname(npf_t *, unsigned, char *, size_t); +void npf_ifmap_copyname(npf_t *, unsigned, char *, size_t); void npf_ifaddr_sync(npf_t *, ifnet_t *); void npf_ifaddr_flush(npf_t *, ifnet_t *); Index: src/sys/net/npf/npf_ruleset.c diff -u src/sys/net/npf/npf_ruleset.c:1.48 src/sys/net/npf/npf_ruleset.c:1.48.2.1 --- src/sys/net/npf/npf_ruleset.c:1.48 Tue Jul 23 00:52:01 2019 +++ src/sys/net/npf/npf_ruleset.c Fri Oct 4 08:06:35 2019 @@ -33,7 +33,7 @@ #ifdef _KERNEL #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.48 2019/07/23 00:52:01 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.48.2.1 2019/10/04 08:06:35 martin Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -680,7 +680,8 @@ npf_rule_export(npf_t *npf, const npf_ru nvlist_add_binary(rule, "code", rl->r_code, rl->r_clen); } if (rl->r_ifid) { - const char *ifname = npf_ifmap_getname(npf, rl->r_ifid); + char ifname[IFNAMSIZ]; + npf_ifmap_copyname(npf, rl->r_ifid, ifname, sizeof(ifname)); nvlist_add_string(rule, "ifname", ifname); } nvlist_add_number(rule, "id", rl->r_id); Index: src/usr.sbin/npf/npfctl/npf.conf.5 diff -u src/usr.sbin/npf/npfctl/npf.conf.5:1.88 src/usr.sbin/npf/npfctl/npf.conf.5:1.88.2.1 --- src/usr.sbin/npf/npfctl/npf.conf.5:1.88 Tue Jul 23 14:20:22 2019 +++ src/usr.sbin/npf/npfctl/npf.conf.5 Fri Oct 4 08:06:34 2019 @@ -1,4 +1,4 @@ -.\" $NetBSD: npf.conf.5,v 1.88 2019/07/23 14:20:22 wiz Exp $ +.\" $NetBSD: npf.conf.5,v 1.88.2.1 2019/10/04 08:06:34 martin Exp $ .\" .\" Copyright (c) 2009-2019 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -27,7 +27,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd May 19, 2019 +.Dd August 25, 2019 .Dt NPF.CONF 5 .Os .Sh NAME @@ -356,6 +356,11 @@ redirecting the public port 9022 to the .Pp .Dl map $ext_if dynamic proto tcp 10.1.1.2 port 22 <- $ext_if port 9022 .Pp +In the regular dynamic NAT case, it is also possible to disable port +translation using the +.Cm no-ports +flag. +.Pp The translation address can also be dynamic, based on the interface. The following would select the IPv4 address(es) currently assigned to the interface: @@ -528,13 +533,15 @@ table-def = "table" table-id "type" ( "i # Mapping for address translation. -map = "map" interface +map = map-common | map-ruleset +map-common = "map" interface ( "static" [ "algo" map-algo ] | "dynamic" ) [ map-flags ] [ proto ] map-seg ( "->" | "<-" | "<->" ) map-seg [ "pass" [ proto ] filt-opts ] +map-ruleset = "map" "ruleset" group-opts -map-algo = "npt66" +map-algo = "ip-hash" | "round-robin" | "netmap" | "npt66" map-flags = "no-ports" map-seg = ( addr-mask | interface ) [ port-opts ] @@ -582,7 +589,7 @@ addr-mask = addr [ "/" mask ] .Ed .\" ----- .Sh FILES -.Bl -tag -width /usr/share/examples/npf -compact +.Bl -tag -width Pa -compact .It Pa /dev/npf control device .It Pa /etc/npf.conf Index: src/usr.sbin/npf/npfctl/npf_build.c diff -u src/usr.sbin/npf/npfctl/npf_build.c:1.50.2.1 src/usr.sbin/npf/npfctl/npf_build.c:1.50.2.2 --- src/usr.sbin/npf/npfctl/npf_build.c:1.50.2.1 Sun Aug 11 10:10:23 2019 +++ src/usr.sbin/npf/npfctl/npf_build.c Fri Oct 4 08:06:34 2019 @@ -32,7 +32,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: npf_build.c,v 1.50.2.1 2019/08/11 10:10:23 martin Exp $"); +__RCSID("$NetBSD: npf_build.c,v 1.50.2.2 2019/10/04 08:06:34 martin Exp $"); #include <sys/types.h> #define __FAVOR_BSD @@ -151,25 +151,32 @@ npfctl_debug_addif(const char *ifname) return 0; } -unsigned -npfctl_table_getid(const char *name) +nl_table_t * +npfctl_table_getbyname(nl_config_t *ncf, const char *name) { - unsigned tid = (unsigned)-1; nl_iter_t i = NPF_ITER_BEGIN; nl_table_t *tl; /* XXX dynamic ruleset */ - if (!npf_conf) { - return (unsigned)-1; + if (!ncf) { + return NULL; } - while ((tl = npf_table_iterate(npf_conf, &i)) != NULL) { + while ((tl = npf_table_iterate(ncf, &i)) != NULL) { const char *tname = npf_table_getname(tl); if (strcmp(tname, name) == 0) { - tid = npf_table_getid(tl); break; } } - return tid; + return tl; +} + +unsigned +npfctl_table_getid(const char *name) +{ + nl_table_t *tl; + + tl = npfctl_table_getbyname(npf_conf, name); + return tl ? npf_table_getid(tl) : (unsigned)-1; } const char * @@ -530,16 +537,32 @@ npfctl_build_rproc(const char *name, npf npf_rproc_insert(npf_conf, rp); } +/* + * npfctl_build_maprset: create and insert a NAT ruleset. + */ void npfctl_build_maprset(const char *name, int attr, const char *ifname) { const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT); nl_rule_t *rl; + bool natset; + int err; + + /* Validate the prefix. */ + err = npfctl_nat_ruleset_p(name, &natset); + if (!natset) { + yyerror("NAT ruleset names must be prefixed with `" + NPF_RULESET_MAP_PREF "`"); + } + if (err) { + yyerror("NAT ruleset is missing a name (only prefix found)"); + } /* If no direction is not specified, then both. */ if ((attr & attr_di) == 0) { attr |= attr_di; } + /* Allow only "in/out" attributes. */ attr = NPF_RULE_GROUP | NPF_RULE_DYNAMIC | (attr & attr_di); rl = npf_rule_create(name, attr, ifname); @@ -859,13 +882,22 @@ npfctl_build_natseg(int sd, int type, un } } - if (nt1) { - npf_rule_setprio(nt1, NPF_PRI_LAST); - npf_nat_insert(npf_conf, nt1); - } - if (nt2) { - npf_rule_setprio(nt2, NPF_PRI_LAST); - npf_nat_insert(npf_conf, nt2); + if (npf_conf) { + if (nt1) { + npf_rule_setprio(nt1, NPF_PRI_LAST); + npf_nat_insert(npf_conf, nt1); + } + if (nt2) { + npf_rule_setprio(nt2, NPF_PRI_LAST); + npf_nat_insert(npf_conf, nt2); + } + } else { + // XXX/TODO: need to refactor a bit to enable this.. + if (nt1 && nt2) { + errx(EXIT_FAILURE, "bidirectional NAT is currently " + "not yet supported in the dynamic rules"); + } + the_rule = nt1 ? nt1 : nt2; } } @@ -873,15 +905,13 @@ npfctl_build_natseg(int sd, int type, un * npfctl_fill_table: fill NPF table with entries from a specified file. */ static void -npfctl_fill_table(nl_table_t *tl, u_int type, const char *fname) +npfctl_fill_table(nl_table_t *tl, u_int type, const char *fname, FILE *fp) { char *buf = NULL; int l = 0; - FILE *fp; size_t n; - fp = fopen(fname, "r"); - if (fp == NULL) { + if (fp == NULL && (fp = fopen(fname, "r")) == NULL) { err(EXIT_FAILURE, "open '%s'", fname); } while (l++, getline(&buf, &n, fp) != -1) { @@ -908,6 +938,23 @@ npfctl_fill_table(nl_table_t *tl, u_int } /* + * npfctl_load_table: create an NPF table and fill with contents from a file. + */ +nl_table_t * +npfctl_load_table(const char *tname, int tid, u_int type, + const char *fname, FILE *fp) +{ + nl_table_t *tl; + + tl = npf_table_create(tname, tid, type); + if (tl && fname) { + npfctl_fill_table(tl, type, fname, fp); + } + + return tl; +} + +/* * npfctl_build_table: create an NPF table, add to the configuration and, * if required, fill with contents from a file. */ @@ -916,15 +963,13 @@ npfctl_build_table(const char *tname, u_ { nl_table_t *tl; - tl = npf_table_create(tname, npfctl_tid_counter++, type); - assert(tl != NULL); - - if (fname) { - npfctl_fill_table(tl, type, fname); - } else if (type == NPF_TABLE_CONST) { + if (type == NPF_TABLE_CONST && !fname) { yyerror("table type 'const' must be loaded from a file"); } + tl = npfctl_load_table(tname, npfctl_tid_counter++, type, fname, NULL); + assert(tl != NULL); + if (npf_table_insert(npf_conf, tl)) { yyerror("table '%s' is already defined", tname); } @@ -939,9 +984,13 @@ npfctl_ifnet_table(const char *ifname) { char tname[NPF_TABLE_MAXNAMELEN]; nl_table_t *tl; - u_int tid; + unsigned tid; snprintf(tname, sizeof(tname), NPF_IFNET_TABLE_PREF "%s", ifname); + if (!npf_conf) { + errx(EXIT_FAILURE, "expression `ifaddrs(%s)` is currently " + "not yet supported in dynamic rules", ifname); + } tid = npfctl_table_getid(tname); if (tid == (unsigned)-1) { @@ -949,7 +998,7 @@ npfctl_ifnet_table(const char *ifname) tl = npf_table_create(tname, tid, NPF_TABLE_IFADDR); (void)npf_table_insert(npf_conf, tl); } - return npfvar_create_element(NPFVAR_TABLE, &tid, sizeof(u_int)); + return npfvar_create_element(NPFVAR_TABLE, &tid, sizeof(unsigned)); } /* Index: src/usr.sbin/npf/npfctl/npf_parse.y diff -u src/usr.sbin/npf/npfctl/npf_parse.y:1.49 src/usr.sbin/npf/npfctl/npf_parse.y:1.49.2.1 --- src/usr.sbin/npf/npfctl/npf_parse.y:1.49 Tue Jul 23 00:52:02 2019 +++ src/usr.sbin/npf/npfctl/npf_parse.y Fri Oct 4 08:06:34 2019 @@ -42,11 +42,11 @@ #define YYSTACKSIZE 4096 -int yyparsetarget; +int yystarttoken; const char * yyfilename; extern int yylineno, yycolumn; -extern int yylex(void); +extern int yylex(int); void yyerror(const char *fmt, ...) @@ -78,14 +78,6 @@ yyerror(const char *fmt, ...) exit(EXIT_FAILURE); } -#define CHECK_PARSER_FILE \ - if (yyparsetarget != NPFCTL_PARSE_FILE) \ - yyerror("rule must be in the group"); - -#define CHECK_PARSER_STRING \ - if (yyparsetarget != NPFCTL_PARSE_STRING) \ - yyerror("invalid rule syntax"); - %} /* @@ -94,6 +86,17 @@ yyerror(const char *fmt, ...) %expect 0 %expect-rr 0 +/* + * Depending on the mode of operation, set a different start symbol. + * Workaround yacc limitation by passing the start token. + */ +%start input +%token RULE_ENTRY_TOKEN MAP_ENTRY_TOKEN +%lex-param { int yystarttoken } + +/* + * General tokens. + */ %token ALG %token ALGO %token ALL @@ -209,8 +212,9 @@ yyerror(const char *fmt, ...) %% input - : { CHECK_PARSER_FILE } lines - | { CHECK_PARSER_STRING } rule + : lines + | RULE_ENTRY_TOKEN rule + | MAP_ENTRY_TOKEN map ; lines Index: src/usr.sbin/npf/npfctl/npf_scan.l diff -u src/usr.sbin/npf/npfctl/npf_scan.l:1.29 src/usr.sbin/npf/npfctl/npf_scan.l:1.29.2.1 --- src/usr.sbin/npf/npfctl/npf_scan.l:1.29 Tue Jul 23 00:52:02 2019 +++ src/usr.sbin/npf/npfctl/npf_scan.l Fri Oct 4 08:06:34 2019 @@ -39,7 +39,7 @@ int yycolumn; #define YY_USER_ACTION yycolumn += yyleng; -extern int yyparsetarget; +extern int yystarttoken; extern int yylineno; extern const char * yyfilename; extern int yyparse(void); @@ -54,7 +54,7 @@ npfctl_parse_file(const char *name) if (fp == NULL) { err(EXIT_FAILURE, "open '%s'", name); } - yyparsetarget = NPFCTL_PARSE_FILE; + yystarttoken = 0; yyrestart(fp); yylineno = 1; yycolumn = 0; @@ -64,11 +64,21 @@ npfctl_parse_file(const char *name) } void -npfctl_parse_string(const char *str) +npfctl_parse_string(const char *str, parse_entry_t entry) { YY_BUFFER_STATE bs; - yyparsetarget = NPFCTL_PARSE_STRING; + switch (entry) { + case NPFCTL_PARSE_RULE: + yystarttoken = RULE_ENTRY_TOKEN; + break; + case NPFCTL_PARSE_MAP: + yystarttoken = MAP_ENTRY_TOKEN; + break; + default: + abort(); + } + bs = yy_scan_string(str); yyfilename = "stdin"; yyparse(); @@ -85,6 +95,15 @@ NUMBER [0-9]+ HEXDIG [0-9a-fA-F]+ %% +%{ + /* This is prepended to yylex(). */ + if (yystarttoken) { + int token = yystarttoken; + yystarttoken = 0; + return token; + } +%} + alg return ALG; table return TABLE; type return TYPE; Index: src/usr.sbin/npf/npfctl/npfctl.8 diff -u src/usr.sbin/npf/npfctl/npfctl.8:1.21 src/usr.sbin/npf/npfctl/npfctl.8:1.21.2.1 --- src/usr.sbin/npf/npfctl/npfctl.8:1.21 Sat Jan 19 21:19:32 2019 +++ src/usr.sbin/npf/npfctl/npfctl.8 Fri Oct 4 08:06:34 2019 @@ -1,4 +1,4 @@ -.\" $NetBSD: npfctl.8,v 1.21 2019/01/19 21:19:32 rmind Exp $ +.\" $NetBSD: npfctl.8,v 1.21.2.1 2019/10/04 08:06:34 martin Exp $ .\" .\" Copyright (c) 2009-2014 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -27,7 +27,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd December 10, 2017 +.Dd August 26, 2019 .Dt NPFCTL 8 .Os .Sh NAME @@ -114,28 +114,50 @@ List all rules in the dynamic ruleset sp Remove all rules from the dynamic ruleset specified by .Ar name . .\" --- -.It Ic table Ar tid Ic add Aq Ar addr/mask +.It Ic table Ar name Ic add Aq Ar addr/mask In table -.Ar tid , +.Ar name , add the IP address and optionally netmask, specified by .Aq Ar addr/mask . Only the tables of type "lpm" support masks. -.It Ic table Ar tid Ic rem Aq Ar addr/mask +.It Ic table Ar name Ic rem Aq Ar addr/mask In table -.Ar tid , +.Ar name , remove the IP address and optionally netmask, specified by .Aq Ar addr/mask . Only the tables of type "lpm" support masks. -.It Ic table Ar tid Ic test Aq Ar addr +.It Ic table Ar name Ic test Aq Ar addr Query the table -.Ar tid +.Ar name for a specific IP address, specified by .Ar addr . If no mask is specified, a single host is assumed. -.It Ic table Ar tid Ic list +.It Ic table Ar name Ic list List all entries in the currently loaded table specified by -.Ar tid . +.Ar name . This operation is expensive and should be used with caution. +.It Ic table Ar name Ic replace Oo Fl n Ar newname Oc Oo Fl t Ar type Oc Aq Ar path +Replace the existing table specified by +.Ar name +with a new table built from the file specified by +.Ar path . +Optionally, the new table will: +.Bl -tag -width xxxxxxxxxx -compact -offset 3n +.It Fl n Ar newname +be named +.Ar newname , +effectively renaming the table. +If not specified, the name of the table being replaced will be used. +.It Fl t Ar type +be of type +.Ar type ; +currently supported types are +.Cm ipset , +.Cm lpm , +or +.Cm const . +If not specified, the type of the table being replaced will be used. +.El .\" --- .It Ic save Save the active configuration and a snapshot of the current connections. @@ -181,7 +203,7 @@ See for details. .\" ----- .Sh FILES -.Bl -tag -width /etc/npf.conf -compact +.Bl -tag -width Pa -compact .It Pa /dev/npf control device .It Pa /etc/npf.conf @@ -201,6 +223,13 @@ Addition and removal of entries in the t # npfctl table "vip" add 10.0.0.1 # npfctl table "vip" rem 182.168.0.0/24 .Ed +.Pp +Replacing the existing table which has ID "svr" +with a new const table populated from file "/tmp/npf_vps_new", +and renamed to "vps": +.Bd -literal -offset indent +# npfctl table "svr" replace -n "vps" -t const "/tmp/npf_vps_new" +.Ed .\" ----- .Sh SEE ALSO .Xr bpf 4 , Index: src/usr.sbin/npf/npfctl/npfctl.c diff -u src/usr.sbin/npf/npfctl/npfctl.c:1.60.2.1 src/usr.sbin/npf/npfctl/npfctl.c:1.60.2.2 --- src/usr.sbin/npf/npfctl/npfctl.c:1.60.2.1 Sun Sep 1 13:13:14 2019 +++ src/usr.sbin/npf/npfctl/npfctl.c Fri Oct 4 08:06:34 2019 @@ -28,7 +28,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: npfctl.c,v 1.60.2.1 2019/09/01 13:13:14 martin Exp $"); +__RCSID("$NetBSD: npfctl.c,v 1.60.2.2 2019/10/04 08:06:34 martin Exp $"); #include <sys/stat.h> #include <sys/types.h> @@ -54,8 +54,6 @@ __RCSID("$NetBSD: npfctl.c,v 1.60.2.1 20 #include "npfctl.h" -extern void npf_yyparse_string(const char *); - enum { NPFCTL_START, NPFCTL_STOP, @@ -142,10 +140,14 @@ usage(void) "\t%s rule \"rule-name\" { list | flush }\n", progname); fprintf(stderr, - "\t%s table <tid> { add | rem | test } <address/mask>\n", + "\t%s table \"table-name\" { add | rem | test } <address/mask>\n", + progname); + fprintf(stderr, + "\t%s table \"table-name\" { list | flush }\n", progname); fprintf(stderr, - "\t%s table <tid> { list | flush }\n", + "\t%s table \"table-name\" replace [-n \"name\"]" + " [-t <type>] <table-file>\n", progname); fprintf(stderr, "\t%s save | load\n", @@ -275,7 +277,98 @@ npfctl_print_addrmask(int alen, const ch return buf; } -__dead static void +static int +npfctl_table_type(const char *typename) +{ + static const struct tbltype_s { + const char * name; + unsigned type; + } tbltypes[] = { + { "ipset", NPF_TABLE_IPSET }, + { "lpm", NPF_TABLE_LPM }, + { "const", NPF_TABLE_CONST }, + { NULL, 0 } + }; + + for (unsigned i = 0; tbltypes[i].name != NULL; i++) { + if (strcmp(typename, tbltypes[i].name) == 0) { + return tbltypes[i].type; + } + } + return 0; +} + +static void +npfctl_table_replace(int fd, int argc, char **argv) +{ + const char *name, *newname, *path, *typename = NULL; + int c, tid = -1; + FILE *fp; + nl_config_t *ncf; + nl_table_t *t; + u_int type = 0; + + name = newname = argv[0]; + optind = 2; + while ((c = getopt(argc, argv, "n:t:")) != -1) { + switch (c) { + case 't': + typename = optarg; + break; + case 'n': + newname = optarg; + break; + default: + fprintf(stderr, + "Usage: %s table \"table-name\" replace " + "[-n \"name\"] [-t <type>] <table-file>\n", + getprogname()); + exit(EXIT_FAILURE); + } + } + argc -= optind; + argv += optind; + + if (typename && (type = npfctl_table_type(typename)) == 0) { + errx(EXIT_FAILURE, "unsupported table type '%s'", typename); + } + + if (argc != 1) { + usage(); + } + + path = argv[0]; + if (strcmp(path, "-") == 0) { + path = "stdin"; + fp = stdin; + } else if ((fp = fopen(path, "r")) == NULL) { + err(EXIT_FAILURE, "open '%s'", path); + } + + /* Get existing config to lookup ID of existing table */ + if ((ncf = npf_config_retrieve(fd)) == NULL) { + err(EXIT_FAILURE, "npf_config_retrieve()"); + } + if ((t = npfctl_table_getbyname(ncf, name)) == NULL) { + errx(EXIT_FAILURE, + "table '%s' not found in the active configuration", name); + } + tid = npf_table_getid(t); + if (!type) { + type = npf_table_gettype(t); + } + npf_config_destroy(ncf); + + if ((t = npfctl_load_table(newname, tid, type, path, fp)) == NULL) { + err(EXIT_FAILURE, "table load failed"); + } + + if (npf_table_replace(fd, t, NULL)) { + err(EXIT_FAILURE, "npf_table_replace(<%s>)", name); + } +} + +static void npfctl_table(int fd, int argc, char **argv) { static const struct tblops_s { @@ -383,11 +476,10 @@ again: nct.nct_cmd == NPF_CMD_TABLE_LOOKUP ? "match" : "success"); } - exit(EXIT_SUCCESS); } static nl_rule_t * -npfctl_parse_rule(int argc, char **argv) +npfctl_parse_rule(int argc, char **argv, parse_entry_t entry) { char rule_string[1024]; nl_rule_t *rl; @@ -396,7 +488,7 @@ npfctl_parse_rule(int argc, char **argv) if (!join(rule_string, sizeof(rule_string), argc, argv, " ")) { errx(EXIT_FAILURE, "command too long"); } - npfctl_parse_string(rule_string); + npfctl_parse_string(rule_string, entry); if ((rl = npfctl_rule_ref()) == NULL) { errx(EXIT_FAILURE, "could not parse the rule"); } @@ -431,7 +523,15 @@ npfctl_generate_key(nl_rule_t *rl, void free(meta); } -__dead static void +int +npfctl_nat_ruleset_p(const char *name, bool *natset) +{ + const size_t preflen = sizeof(NPF_RULESET_MAP_PREF) - 1; + *natset = strncmp(name, NPF_RULESET_MAP_PREF, preflen) == 0; + return (*natset && strlen(name) <= preflen) ? -1 : 0; +} + +static void npfctl_rule(int fd, int argc, char **argv) { static const struct ruleops_s { @@ -451,11 +551,12 @@ npfctl_rule(int fd, int argc, char **arg const char *ruleset_name = argv[0]; const char *cmd = argv[1]; int error, action = 0; + bool extra_arg, natset; + parse_entry_t entry; uint64_t rule_id; - bool extra_arg; nl_rule_t *rl; - for (int n = 0; ruleops[n].cmd != NULL; n++) { + for (unsigned n = 0; ruleops[n].cmd != NULL; n++) { if (strcmp(cmd, ruleops[n].cmd) == 0) { action = ruleops[n].action; extra_arg = ruleops[n].extra_arg; @@ -469,15 +570,22 @@ npfctl_rule(int fd, int argc, char **arg usage(); } + if (npfctl_nat_ruleset_p(ruleset_name, &natset) != 0) { + errx(EXIT_FAILURE, + "invalid NAT ruleset name (note: the name must be " + "prefixed with `" NPF_RULESET_MAP_PREF "`)"); + } + entry = natset ? NPFCTL_PARSE_MAP : NPFCTL_PARSE_RULE; + switch (action) { case NPF_CMD_RULE_ADD: - rl = npfctl_parse_rule(argc, argv); + rl = npfctl_parse_rule(argc, argv, entry); npfctl_generate_key(rl, key); npf_rule_setkey(rl, key, sizeof(key)); error = npf_ruleset_add(fd, ruleset_name, rl, &rule_id); break; case NPF_CMD_RULE_REMKEY: - rl = npfctl_parse_rule(argc, argv); + rl = npfctl_parse_rule(argc, argv, entry); npfctl_generate_key(rl, key); error = npf_ruleset_remkey(fd, ruleset_name, key, sizeof(key)); break; @@ -509,7 +617,6 @@ npfctl_rule(int fd, int argc, char **arg if (action == NPF_CMD_RULE_ADD) { printf("OK %" PRIx64 "\n", rule_id); } - exit(EXIT_SUCCESS); } static bool bpfjit = true; @@ -754,7 +861,11 @@ npfctl(int action, int argc, char **argv usage(); } argv += 2; - npfctl_table(fd, argc, argv); + if (strcmp(argv[1], "replace") == 0) { + npfctl_table_replace(fd, argc, argv); + } else { + npfctl_table(fd, argc, argv); + } break; case NPFCTL_RULE: if ((argc -= 2) < 2) { Index: src/usr.sbin/npf/npfctl/npfctl.h diff -u src/usr.sbin/npf/npfctl/npfctl.h:1.48.2.1 src/usr.sbin/npf/npfctl/npfctl.h:1.48.2.2 --- src/usr.sbin/npf/npfctl/npfctl.h:1.48.2.1 Sun Aug 11 10:10:23 2019 +++ src/usr.sbin/npf/npfctl/npfctl.h Fri Oct 4 08:06:34 2019 @@ -102,7 +102,11 @@ typedef struct proc_param { const char * pp_value; } proc_param_t; -enum { NPFCTL_PARSE_FILE, NPFCTL_PARSE_STRING }; +typedef enum { + NPFCTL_PARSE_DEFAULT, + NPFCTL_PARSE_RULE, + NPFCTL_PARSE_MAP +} parse_entry_t; #define NPF_IFNET_TABLE_PREF ".ifnet-" #define NPF_IFNET_TABLE_PREFLEN (sizeof(NPF_IFNET_TABLE_PREF) - 1) @@ -111,12 +115,13 @@ bool join(char *, size_t, int, char **, void yyerror(const char *, ...) __printflike(1, 2) __dead; void npfctl_bpfjit(bool); void npfctl_parse_file(const char *); -void npfctl_parse_string(const char *); +void npfctl_parse_string(const char *, parse_entry_t); void npfctl_print_error(const npf_error_t *); char * npfctl_print_addrmask(int, const char *, const npf_addr_t *, npf_netmask_t); void npfctl_note_interface(const char *); +nl_table_t * npfctl_table_getbyname(nl_config_t *, const char *); unsigned npfctl_table_getid(const char *); const char * npfctl_table_getname(nl_config_t *, unsigned, bool *); int npfctl_protono(const char *); @@ -135,6 +140,7 @@ npfvar_t * npfctl_parse_fam_addr_mask(co bool npfctl_parse_cidr(char *, fam_addr_mask_t *, int *); uint16_t npfctl_npt66_calcadj(npf_netmask_t, const npf_addr_t *, const npf_addr_t *); +int npfctl_nat_ruleset_p(const char *, bool *); /* * NPF extension loading. @@ -196,8 +202,11 @@ void npfctl_show_init(void); int npfctl_ruleset_show(int, const char *); nl_rule_t * npfctl_rule_ref(void); +nl_table_t * npfctl_table_ref(void); bool npfctl_debug_addif(const char *); +nl_table_t * npfctl_load_table(const char *, int, u_int, const char *, FILE *); + void npfctl_build_alg(const char *); void npfctl_build_rproc(const char *, npfvar_t *); void npfctl_build_group(const char *, int, const char *, bool); Index: src/usr.sbin/npf/npftest/npftest.conf diff -u src/usr.sbin/npf/npftest/npftest.conf:1.7 src/usr.sbin/npf/npftest/npftest.conf:1.7.2.1 --- src/usr.sbin/npf/npftest/npftest.conf:1.7 Tue Jul 23 00:52:02 2019 +++ src/usr.sbin/npf/npftest/npftest.conf Fri Oct 4 08:06:35 2019 @@ -1,4 +1,4 @@ -# $NetBSD: npftest.conf,v 1.7 2019/07/23 00:52:02 rmind Exp $ +# $NetBSD: npftest.conf,v 1.7.2.1 2019/10/04 08:06:35 martin Exp $ $ext_if = "npftest0" $int_if = "npftest1" @@ -32,7 +32,7 @@ $net_b = 10.255.0.0/16 map $ext_if static algo npt66 $net6_inner <-> $net6_outer map $ext_if static algo netmap $net_a <-> $net_b -map ruleset "dynamic-nat" on $ext_if +map ruleset "map:some-daemon" on $ext_if group "ext" on $ext_if { pass out final from $local_ip3