npfkern/libnpf: Add support for the table replace/swap operation.
Contributed by Timshel Knoll-Miller.
This commit is contained in:
parent
f916b9b1ff
commit
0e1944da36
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
@ -202,6 +202,30 @@ _npf_rules_process(nl_config_t *ncf, nvlist_t *dict, const char *key)
|
|||
free(items);
|
||||
}
|
||||
|
||||
/*
|
||||
* _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 fd, npf_error_t *errinfo)
|
|||
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 af, const npf_addr_t *addr,
|
|||
}
|
||||
|
||||
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_table_t *tl)
|
|||
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_table_t *tl)
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -146,6 +146,8 @@ int npf_table_add_entry(nl_table_t *, int,
|
|||
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>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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, const nvlist_t *table, nvlist_t *errdict)
|
|||
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_dict, nvlist_t *errdict,
|
|||
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;
|
||||
|
@ -732,6 +755,63 @@ out:
|
|||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
|
|
@ -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 char *, u_int, int, const void *, size_t);
|
|||
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,
|
||||
|
|
|
@ -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, void *data, int flag, lwp_t *l)
|
|||
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;
|
||||
|
|
|
@ -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, const char *name, uint64_t tid, uint64_t typ
|
|||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue