Module Name: src Committed By: rmind Date: Wed Aug 21 21:45:47 UTC 2019
Modified Files: src/lib/libnpf: libnpf.3 npf.c npf.h src/sys/net/npf: npf.h npf_ctl.c npf_impl.h npf_os.c npf_tableset.c Log Message: npfkern/libnpf: Add support for the table replace/swap operation. Contributed by Timshel Knoll-Miller. To generate a diff of this commit: cvs rdiff -u -r1.9 -r1.10 src/lib/libnpf/libnpf.3 cvs rdiff -u -r1.46 -r1.47 src/lib/libnpf/npf.c cvs rdiff -u -r1.36 -r1.37 src/lib/libnpf/npf.h cvs rdiff -u -r1.60 -r1.61 src/sys/net/npf/npf.h cvs rdiff -u -r1.55 -r1.56 src/sys/net/npf/npf_ctl.c cvs rdiff -u -r1.76 -r1.77 src/sys/net/npf/npf_impl.h cvs rdiff -u -r1.14 -r1.15 src/sys/net/npf/npf_os.c cvs rdiff -u -r1.33 -r1.34 src/sys/net/npf/npf_tableset.c 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 src/lib/libnpf/libnpf.3:1.10 --- src/lib/libnpf/libnpf.3:1.9 Tue Jul 23 14:18:20 2019 +++ src/lib/libnpf/libnpf.3 Wed Aug 21 21:45:47 2019 @@ -1,4 +1,4 @@ -.\" $NetBSD: libnpf.3,v 1.9 2019/07/23 14:18:20 wiz Exp $ +.\" $NetBSD: libnpf.3,v 1.10 2019/08/21 21:45:47 rmind 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 April 14, 2019 +.Dd August 21, 2019 .Dt LIBNPF 3 .Os .Sh NAME @@ -41,7 +41,7 @@ .Ft nl_config_t * .Fn npf_config_create "void" .Ft int -.Fn npf_config_submit "nl_config_t *ncf" "int fd" "nl_error_t *errinfo" +.Fn npf_config_submit "nl_config_t *ncf" "int fd" "npf_error_t *errinfo" .Ft nl_config_t * .Fn npf_config_retrieve "int fd" .Ft int @@ -104,6 +104,8 @@ "const npf_addr_t *addr" "const npf_netmask_t mask" .Ft int .Fn npf_table_insert "nl_config_t *ncf" "nl_table_t *tl" +.Ft int +.Fn npf_table_replace "int fd" "nl_table_t *tl" "npf_error_t *errinfo" .Ft void .Fn npf_table_destroy "nl_table_t *tl" .\" ----- @@ -347,7 +349,9 @@ for IPv4 or for IPv6 address. Additionally, .Fa mask -may be specified to indicate the translation network. +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 .Fn npf_nat_setalgo function. @@ -423,11 +427,25 @@ must be either for IPv4 or .Dv AF_INET6 for IPv6 address. +If there is no mask, then +.Fa mask +should be set to +.Dv NPF_NO_NETMASK . +.\" --- .It Fn npf_table_insert "ncf" "tl" Add the table to the configuration object. This routine performs a check for duplicate table IDs. The table must not be referenced after insertion. .\" --- +.It Fn npf_table_replace "fd" "tl" "errinfo" +Submit the table object, specified by +.Fa tl , +to the kernel, to replace the existing table with the +corresponding table name and ID. +On failure, the error information is written into the structure +specified by +.Fa errinfo . +.\" --- .It Fn npf_table_destroy "tl" Destroy the specified table. .El Index: src/lib/libnpf/npf.c diff -u src/lib/libnpf/npf.c:1.46 src/lib/libnpf/npf.c:1.47 --- src/lib/libnpf/npf.c:1.46 Tue Jul 23 00:52:01 2019 +++ src/lib/libnpf/npf.c Wed Aug 21 21:45:47 2019 @@ -28,7 +28,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.46 2019/07/23 00:52:01 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.47 2019/08/21 21:45:47 rmind Exp $"); #include <sys/types.h> #include <sys/mman.h> @@ -203,6 +203,30 @@ _npf_rules_process(nl_config_t *ncf, nvl } /* + * _npf_extract_error: check the error number field and extract the + * error details into the npf_error_t structure. + */ +static int +_npf_extract_error(nvlist_t *resp, npf_error_t *errinfo) +{ + int error; + + error = dnvlist_get_number(resp, "errno", 0); + if (error && errinfo) { + memset(errinfo, 0, sizeof(npf_error_t)); + + errinfo->id = dnvlist_get_number(resp, "id", 0); + errinfo->error_msg = + dnvlist_take_string(resp, "error-msg", NULL); + errinfo->source_file = + dnvlist_take_string(resp, "source-file", NULL); + errinfo->source_line = + dnvlist_take_number(resp, "source-line", 0); + } + return error; +} + +/* * CONFIGURATION INTERFACE. */ @@ -233,17 +257,7 @@ npf_config_submit(nl_config_t *ncf, int assert(errnv == NULL); return errno; } - error = dnvlist_get_number(errnv, "errno", 0); - if (error && errinfo) { - memset(errinfo, 0, sizeof(npf_error_t)); - errinfo->id = dnvlist_get_number(errnv, "id", 0); - errinfo->error_msg = - dnvlist_take_string(errnv, "error-msg", NULL); - errinfo->source_file = - dnvlist_take_string(errnv, "source-file", NULL); - errinfo->source_line = - dnvlist_take_number(errnv, "source-line", 0); - } + error = _npf_extract_error(errnv, errinfo); nvlist_destroy(errnv); return error; } @@ -949,7 +963,7 @@ npf_table_add_entry(nl_table_t *tl, int } static inline int -_npf_table_build(nl_table_t *tl) +_npf_table_build_const(nl_table_t *tl) { struct cdbw *cdbw; const nvlist_t * const *entries; @@ -959,6 +973,10 @@ _npf_table_build(nl_table_t *tl) struct stat sb; char sfn[32]; + if (dnvlist_get_number(tl->table_dict, "type", 0) != NPF_TABLE_CONST) { + return 0; + } + if (!nvlist_exists_nvlist_array(tl->table_dict, "entries")) { return 0; } @@ -1050,10 +1068,8 @@ npf_table_insert(nl_config_t *ncf, nl_ta if (_npf_dataset_lookup(ncf->ncf_dict, "tables", "name", name)) { return EEXIST; } - if (dnvlist_get_number(tl->table_dict, "type", 0) == NPF_TABLE_CONST) { - if ((error = _npf_table_build(tl)) != 0) { - return error; - } + if ((error = _npf_table_build_const(tl)) != 0) { + return error; } nvlist_append_nvlist_array(ncf->ncf_dict, "tables", tl->table_dict); nvlist_destroy(tl->table_dict); @@ -1061,6 +1077,27 @@ npf_table_insert(nl_config_t *ncf, nl_ta return 0; } +int +npf_table_replace(int fd, nl_table_t *tl, npf_error_t *errinfo) +{ + nvlist_t *errnv = NULL; + int error; + + /* Ensure const tables are built. */ + if ((error = _npf_table_build_const(tl)) != 0) { + return error; + } + + if (nvlist_xfer_ioctl(fd, IOC_NPF_TABLE_REPLACE, + tl->table_dict, &errnv) == -1) { + assert(errnv == NULL); + return errno; + } + error = _npf_extract_error(errnv, errinfo); + nvlist_destroy(errnv); + return error; +} + nl_table_t * npf_table_iterate(nl_config_t *ncf, nl_iter_t *iter) { Index: src/lib/libnpf/npf.h diff -u src/lib/libnpf/npf.h:1.36 src/lib/libnpf/npf.h:1.37 --- src/lib/libnpf/npf.h:1.36 Tue Jul 23 00:52:01 2019 +++ src/lib/libnpf/npf.h Wed Aug 21 21:45:47 2019 @@ -146,6 +146,8 @@ int npf_table_add_entry(nl_table_t *, i int npf_table_insert(nl_config_t *, nl_table_t *); void npf_table_destroy(nl_table_t *); +int npf_table_replace(int, nl_table_t *, npf_error_t *); + #ifdef _NPF_PRIVATE #include <ifaddrs.h> Index: src/sys/net/npf/npf.h diff -u src/sys/net/npf/npf.h:1.60 src/sys/net/npf/npf.h:1.61 --- src/sys/net/npf/npf.h:1.60 Tue Jul 23 00:52:01 2019 +++ src/sys/net/npf/npf.h Wed Aug 21 21:45:47 2019 @@ -310,6 +310,7 @@ typedef struct npf_ioctl_table { #define IOC_NPF_SAVE _IOR('N', 105, nvlist_ref_t) #define IOC_NPF_RULE _IOWR('N', 107, nvlist_ref_t) #define IOC_NPF_CONN_LOOKUP _IOWR('N', 108, nvlist_ref_t) +#define IOC_NPF_TABLE_REPLACE _IOWR('N', 109, nvlist_ref_t) /* * NPF error report. Index: src/sys/net/npf/npf_ctl.c diff -u src/sys/net/npf/npf_ctl.c:1.55 src/sys/net/npf/npf_ctl.c:1.56 --- src/sys/net/npf/npf_ctl.c:1.55 Sun Aug 11 20:26:33 2019 +++ src/sys/net/npf/npf_ctl.c Wed Aug 21 21:45:47 2019 @@ -36,7 +36,7 @@ #ifdef _KERNEL #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.55 2019/08/11 20:26:33 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.56 2019/08/21 21:45:47 rmind Exp $"); #include <sys/param.h> #include <sys/conf.h> @@ -178,6 +178,63 @@ npf_mk_table_entries(npf_table_t *t, con return error; } +/* + * npf_mk_table: create a table from provided nvlist. + */ +static int __noinline +npf_mk_table(npf_t *npf, const nvlist_t *tbl_dict, nvlist_t *errdict, + npf_tableset_t *tblset, npf_table_t **tblp, bool replacing) +{ + npf_table_t *t; + const char *name; + const void *blob; + uint64_t tid; + size_t size; + int type; + int error = 0; + + KASSERT(tblp != NULL); + + /* Table name, ID and type. Validate them. */ + name = dnvlist_get_string(tbl_dict, "name", NULL); + if (!name) { + NPF_ERR_DEBUG(errdict); + error = EINVAL; + goto out; + } + tid = dnvlist_get_number(tbl_dict, "id", UINT64_MAX); + type = dnvlist_get_number(tbl_dict, "type", UINT64_MAX); + error = npf_table_check(tblset, name, tid, type, replacing); + if (error) { + NPF_ERR_DEBUG(errdict); + goto out; + } + + /* Get the entries or binary data. */ + blob = dnvlist_get_binary(tbl_dict, "data", &size, NULL, 0); + if (type == NPF_TABLE_CONST && (blob == NULL || size == 0)) { + NPF_ERR_DEBUG(errdict); + error = EINVAL; + goto out; + } + + t = npf_table_create(name, (u_int)tid, type, blob, size); + if (t == NULL) { + NPF_ERR_DEBUG(errdict); + error = ENOMEM; + goto out; + } + + if ((error = npf_mk_table_entries(t, tbl_dict, errdict)) != 0) { + npf_table_destroy(t); + goto out; + } + + *tblp = t; +out: + return error; +} + static int __noinline npf_mk_tables(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict, npf_tableset_t **tblsetp) @@ -200,49 +257,15 @@ npf_mk_tables(npf_t *npf, nvlist_t *npf_ tblset = npf_tableset_create(nitems); for (unsigned i = 0; i < nitems; i++) { const nvlist_t *table = tables[i]; - const char *name; - const void *blob; npf_table_t *t; - uint64_t tid; - size_t size; - int type; - /* Table name, ID and type. Validate them. */ - name = dnvlist_get_string(table, "name", NULL); - if (!name) { - NPF_ERR_DEBUG(errdict); - error = EINVAL; - break; - } - tid = dnvlist_get_number(table, "id", UINT64_MAX); - type = dnvlist_get_number(table, "type", UINT64_MAX); - error = npf_table_check(tblset, name, tid, type); + error = npf_mk_table(npf, table, errdict, tblset, &t, 0); if (error) { - NPF_ERR_DEBUG(errdict); - break; - } - - /* Get the entries or binary data. */ - blob = dnvlist_get_binary(table, "data", &size, NULL, 0); - if (type == NPF_TABLE_CONST && (blob == NULL || size == 0)) { - NPF_ERR_DEBUG(errdict); - error = EINVAL; break; } - /* Create and insert the table. */ - t = npf_table_create(name, (u_int)tid, type, blob, size); - if (t == NULL) { - NPF_ERR_DEBUG(errdict); - error = ENOMEM; - break; - } error = npf_tableset_insert(tblset, t); KASSERT(error == 0); - - if ((error = npf_mk_table_entries(t, table, errdict)) != 0) { - break; - } } *tblsetp = tblset; return error; @@ -733,6 +756,63 @@ out: } /* + * npfctl_table_replace_nvlist: atomically replace a table's contents + * with the passed table data. + */ +static int __noinline +npfctl_table_replace_nvlist(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict) +{ + npf_table_t *tbl, *gc_tbl = NULL; + npf_tableset_t *tblset; + int error = 0; + + npf_config_enter(npf); + tblset = npf_config_tableset(npf); + + /* Get the entries or binary data. */ + error = npf_mk_table(npf, npf_dict, errdict, tblset, &tbl, true); + if (error) { + goto err; + } + + gc_tbl = npf_tableset_swap(tblset, tbl); + if (gc_tbl == NULL) { + error = EINVAL; + gc_tbl = tbl; + goto err; + } + npf_config_sync(npf); +err: + npf_config_exit(npf); + if (gc_tbl) { + npf_table_destroy(gc_tbl); + } + return error; +} + +int +npfctl_table_replace(npf_t *npf, u_long cmd, void *data) +{ + nvlist_t *request, *response; + int error; + + /* + * Retrieve the configuration and check the version. + * Construct a response with error reporting. + */ + error = npf_nvlist_copyin(npf, data, &request); + if (error) { + return error; + } + response = nvlist_create(0); + error = npfctl_table_replace_nvlist(npf, request, response); + nvlist_add_number(response, "errno", error); + error = npf_nvlist_copyout(npf, data, response); + nvlist_destroy(request); + return error; +} + +/* * npfctl_conn_lookup: lookup a connection in the list of connections */ int Index: src/sys/net/npf/npf_impl.h diff -u src/sys/net/npf/npf_impl.h:1.76 src/sys/net/npf/npf_impl.h:1.77 --- src/sys/net/npf/npf_impl.h:1.76 Sun Aug 11 20:26:34 2019 +++ src/sys/net/npf/npf_impl.h Wed Aug 21 21:45:47 2019 @@ -292,6 +292,7 @@ int npfctl_save(npf_t *, u_long, void * int npfctl_load(npf_t *, u_long, void *); int npfctl_rule(npf_t *, u_long, void *); int npfctl_conn_lookup(npf_t *, u_long, void *); +int npfctl_table_replace(npf_t *, u_long, void *); int npfctl_table(npf_t *, void *); void npf_stats_inc(npf_t *, npf_stats_t); @@ -379,7 +380,7 @@ npf_table_t * npf_table_create(const cha void npf_table_destroy(npf_table_t *); u_int npf_table_getid(npf_table_t *); -int npf_table_check(npf_tableset_t *, const char *, uint64_t, uint64_t); +int npf_table_check(npf_tableset_t *, const char *, uint64_t, uint64_t, bool); int npf_table_insert(npf_table_t *, const int, const npf_addr_t *, const npf_netmask_t); int npf_table_remove(npf_table_t *, const int, Index: src/sys/net/npf/npf_os.c diff -u src/sys/net/npf/npf_os.c:1.14 src/sys/net/npf/npf_os.c:1.15 --- src/sys/net/npf/npf_os.c:1.14 Sun Aug 11 20:26:34 2019 +++ src/sys/net/npf/npf_os.c Wed Aug 21 21:45:47 2019 @@ -33,7 +33,7 @@ #ifdef _KERNEL #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_os.c,v 1.14 2019/08/11 20:26:34 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_os.c,v 1.15 2019/08/21 21:45:47 rmind Exp $"); #ifdef _KERNEL_OPT #include "pf.h" @@ -259,6 +259,9 @@ npf_dev_ioctl(dev_t dev, u_long cmd, voi case IOC_NPF_CONN_LOOKUP: error = npfctl_conn_lookup(npf, cmd, data); break; + case IOC_NPF_TABLE_REPLACE: + error = npfctl_table_replace(npf, cmd, data); + break; case IOC_NPF_VERSION: *(int *)data = NPF_VERSION; error = 0; Index: src/sys/net/npf/npf_tableset.c diff -u src/sys/net/npf/npf_tableset.c:1.33 src/sys/net/npf/npf_tableset.c:1.34 --- src/sys/net/npf/npf_tableset.c:1.33 Tue Jul 23 00:52:01 2019 +++ src/sys/net/npf/npf_tableset.c Wed Aug 21 21:45:47 2019 @@ -46,7 +46,7 @@ #ifdef _KERNEL #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.33 2019/07/23 00:52:01 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.34 2019/08/21 21:45:47 rmind Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -456,12 +456,15 @@ npf_table_getid(npf_table_t *t) * npf_table_check: validate the name, ID and type. */ int -npf_table_check(npf_tableset_t *ts, const char *name, uint64_t tid, uint64_t type) +npf_table_check(npf_tableset_t *ts, const char *name, uint64_t tid, + uint64_t type, bool replacing) { + const npf_table_t *t; + if (tid >= ts->ts_nitems) { return EINVAL; } - if (ts->ts_map[tid] != NULL) { + if (!replacing && ts->ts_map[tid] != NULL) { return EEXIST; } switch (type) { @@ -476,8 +479,10 @@ npf_table_check(npf_tableset_t *ts, cons if (strlen(name) >= NPF_TABLE_MAXNAMELEN) { return ENAMETOOLONG; } - if (npf_tableset_getbyname(ts, name)) { - return EEXIST; + if ((t = npf_tableset_getbyname(ts, name)) != NULL) { + if (!replacing || t->t_id != tid) { + return EEXIST; + } } return 0; }