NPF improvements:
- Add support for dynamic NETMAP algorithm (stateful net-to-net). - Add most of the support for the dynamic NAT rules; a little bit more userland work is needed to finish this up and enable. - Replace 'stateful-ends' with more permissive 'stateful-all'. - Add various tunable parameters and document them, see npf-params(7). - Reduce the memory usage of the connection state table (conndb). - Portmap rewrite: use memory more efficiently, handle addresses dynamically. - Bug fix: add splsoftnet()/splx() around the thmap writers and comment. - npftest: clean up and simplify; fix some memleaks to make ASAN happy.
This commit is contained in:
parent
ac03b3fba2
commit
dadc88e3b0
|
@ -1,6 +1,6 @@
|
|||
.\" $NetBSD: libnpf.3,v 1.7 2019/01/19 21:19:31 rmind Exp $
|
||||
.\" $NetBSD: libnpf.3,v 1.8 2019/07/23 00:52:01 rmind Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2011-2018 The NetBSD Foundation, Inc.
|
||||
.\" Copyright (c) 2011-2019 The NetBSD Foundation, Inc.
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" This material is based upon work partially supported by The
|
||||
|
@ -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 29, 2018
|
||||
.Dd April 14, 2019
|
||||
.Dt LIBNPF 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -95,10 +95,10 @@
|
|||
.Ft int
|
||||
.Fn npf_nat_setport "nl_nat_t *nt" "in_port_t port"
|
||||
.Ft int
|
||||
.Fn npf_nat_insert "nl_config_t *ncf" "nl_nat_t *nt" "pri_t pri"
|
||||
.Fn npf_nat_insert "nl_config_t *ncf" "nl_nat_t *nt"
|
||||
.\" ---
|
||||
.Ft nl_table_t *
|
||||
.Fn npf_table_create "const char *name" "u_int id" "int type"
|
||||
.Fn npf_table_create "const char *name" "unsigned id" "int type"
|
||||
.Ft int
|
||||
.Fn npf_table_add_entry "nl_table_t *tl" "int af" \
|
||||
"const npf_addr_t *addr" "const npf_netmask_t mask"
|
||||
|
@ -178,8 +178,11 @@ Create a state (session) on match, track the connection and pass the
|
|||
backwards stream (the returning packets) without the ruleset inspection.
|
||||
The state is uniquely identified by a 5-tuple (source and destination
|
||||
IP addresses, port numbers and an interface identifier).
|
||||
.It Dv NPF_RULE_MULTIENDS
|
||||
.It Dv NPF_RULE_GSTATEFUL
|
||||
Exclude the interface identifier from the state key i.e. use a 4-tuple.
|
||||
This makes the state global with the respect network interfaces.
|
||||
The state is also picked on packet travelling different direction that
|
||||
originally.
|
||||
.It Dv NPF_RULE_RETRST
|
||||
Return TCP RST packet in a case of packet block.
|
||||
.It Dv NPF_RULE_RETICMP
|
||||
|
@ -373,8 +376,8 @@ original address (zero bits of the mask).
|
|||
IPv6-to-IPv6 Network Prefix Translation (NPTv6, defined in RFC 6296).
|
||||
.El
|
||||
.\" ---
|
||||
.It Fn npf_nat_insert "ncf" "nt" "pri"
|
||||
Insert NAT policy, its rule, into the specified configuration.
|
||||
.It Fn npf_nat_insert "ncf" "nt"
|
||||
Insert the NAT policy, its rule, into the specified configuration.
|
||||
The NAT rule must not be referenced after insertion.
|
||||
.El
|
||||
.\" -----
|
||||
|
|
120
lib/libnpf/npf.c
120
lib/libnpf/npf.c
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2010-2018 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2010-2019 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
|
@ -28,7 +28,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.45 2019/01/19 21:19:31 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.46 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
|
@ -80,16 +80,11 @@ struct nl_config {
|
|||
unsigned ncf_rule_count;
|
||||
|
||||
/* Iterators. */
|
||||
unsigned ncf_rule_iter;
|
||||
unsigned ncf_reduce[16];
|
||||
unsigned ncf_nlevel;
|
||||
unsigned ncf_counter;
|
||||
|
||||
nl_rule_t ncf_cur_rule;
|
||||
|
||||
unsigned ncf_table_iter;
|
||||
nl_table_t ncf_cur_table;
|
||||
|
||||
unsigned ncf_rproc_iter;
|
||||
nl_rproc_t ncf_cur_rproc;
|
||||
};
|
||||
|
||||
|
@ -242,6 +237,8 @@ npf_config_submit(nl_config_t *ncf, int fd, npf_error_t *errinfo)
|
|||
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 =
|
||||
|
@ -345,6 +342,47 @@ npf_config_destroy(nl_config_t *ncf)
|
|||
free(ncf);
|
||||
}
|
||||
|
||||
/*
|
||||
* PARAMETERS.
|
||||
*/
|
||||
|
||||
int
|
||||
npf_param_get(nl_config_t *ncf, const char *name, int *valp)
|
||||
{
|
||||
const nvlist_t *params;
|
||||
|
||||
params = dnvlist_get_nvlist(ncf->ncf_dict, "params", NULL);
|
||||
if (params == NULL || !nvlist_exists(params, name)) {
|
||||
return ENOENT;
|
||||
}
|
||||
*valp = (int)dnvlist_get_number(params, name, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
npf_param_set(nl_config_t *ncf, const char *name, int val)
|
||||
{
|
||||
nvlist_t *params;
|
||||
|
||||
/* Ensure params dictionary. */
|
||||
if (nvlist_exists(ncf->ncf_dict, "params")) {
|
||||
params = nvlist_take_nvlist(ncf->ncf_dict, "params");
|
||||
} else {
|
||||
params = nvlist_create(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the parameter is already set, then free it first.
|
||||
* Set the parameter. Note: values can be negative.
|
||||
*/
|
||||
if (nvlist_exists(params, name)) {
|
||||
nvlist_free_number(params, name);
|
||||
}
|
||||
nvlist_add_number(params, name, (uint64_t)val);
|
||||
nvlist_add_nvlist(ncf->ncf_dict, "params", params);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* DYNAMIC RULESET INTERFACE.
|
||||
*/
|
||||
|
@ -539,9 +577,10 @@ npf_rule_insert(nl_config_t *ncf, nl_rule_t *parent, nl_rule_t *rl)
|
|||
}
|
||||
|
||||
static nl_rule_t *
|
||||
_npf_rule_iterate1(nl_config_t *ncf, const char *key, unsigned *level)
|
||||
_npf_rule_iterate1(nl_config_t *ncf, const char *key,
|
||||
nl_iter_t *iter, unsigned *level)
|
||||
{
|
||||
unsigned i = ncf->ncf_rule_iter++;
|
||||
unsigned i = *iter;
|
||||
const nvlist_t *rule_dict;
|
||||
uint32_t skipto;
|
||||
|
||||
|
@ -549,16 +588,14 @@ _npf_rule_iterate1(nl_config_t *ncf, const char *key, unsigned *level)
|
|||
/* Initialise the iterator. */
|
||||
ncf->ncf_nlevel = 0;
|
||||
ncf->ncf_reduce[0] = 0;
|
||||
ncf->ncf_counter = 0;
|
||||
}
|
||||
|
||||
rule_dict = _npf_dataset_getelement(ncf->ncf_dict, key, i);
|
||||
if (!rule_dict) {
|
||||
/* Reset the iterator. */
|
||||
ncf->ncf_rule_iter = 0;
|
||||
*iter = NPF_ITER_BEGIN;
|
||||
return NULL;
|
||||
}
|
||||
ncf->ncf_cur_rule.rule_dict = __UNCONST(rule_dict); // XXX
|
||||
*iter = i + 1; // next
|
||||
*level = ncf->ncf_nlevel;
|
||||
|
||||
skipto = dnvlist_get_number(rule_dict, "skip-to", 0);
|
||||
|
@ -566,17 +603,19 @@ _npf_rule_iterate1(nl_config_t *ncf, const char *key, unsigned *level)
|
|||
ncf->ncf_nlevel++;
|
||||
ncf->ncf_reduce[ncf->ncf_nlevel] = skipto;
|
||||
}
|
||||
if (ncf->ncf_reduce[ncf->ncf_nlevel] == ++ncf->ncf_counter) {
|
||||
if (ncf->ncf_reduce[ncf->ncf_nlevel] == (i + 1)) {
|
||||
assert(ncf->ncf_nlevel > 0);
|
||||
ncf->ncf_nlevel--;
|
||||
}
|
||||
|
||||
ncf->ncf_cur_rule.rule_dict = __UNCONST(rule_dict); // XXX
|
||||
return &ncf->ncf_cur_rule;
|
||||
}
|
||||
|
||||
nl_rule_t *
|
||||
npf_rule_iterate(nl_config_t *ncf, unsigned *level)
|
||||
npf_rule_iterate(nl_config_t *ncf, nl_iter_t *iter, unsigned *level)
|
||||
{
|
||||
return _npf_rule_iterate1(ncf, "rules", level);
|
||||
return _npf_rule_iterate1(ncf, "rules", iter, level);
|
||||
}
|
||||
|
||||
const char *
|
||||
|
@ -710,17 +749,17 @@ npf_rproc_insert(nl_config_t *ncf, nl_rproc_t *rp)
|
|||
}
|
||||
|
||||
nl_rproc_t *
|
||||
npf_rproc_iterate(nl_config_t *ncf)
|
||||
npf_rproc_iterate(nl_config_t *ncf, nl_iter_t *iter)
|
||||
{
|
||||
const nvlist_t *rproc_dict;
|
||||
unsigned i = ncf->ncf_rproc_iter++;
|
||||
unsigned i = *iter;
|
||||
|
||||
rproc_dict = _npf_dataset_getelement(ncf->ncf_dict, "rprocs", i);
|
||||
if (!rproc_dict) {
|
||||
/* Reset the iterator. */
|
||||
ncf->ncf_rproc_iter = 0;
|
||||
*iter = NPF_ITER_BEGIN;
|
||||
return NULL;
|
||||
}
|
||||
*iter = i + 1; // next
|
||||
ncf->ncf_cur_rproc.rproc_dict = __UNCONST(rproc_dict); // XXX
|
||||
return &ncf->ncf_cur_rproc;
|
||||
}
|
||||
|
@ -755,13 +794,13 @@ npf_nat_create(int type, unsigned flags, const char *ifname)
|
|||
/* Translation type and flags. */
|
||||
nvlist_add_number(rule_dict, "type", type);
|
||||
nvlist_add_number(rule_dict, "flags", flags);
|
||||
nvlist_add_bool(rule_dict, "nat-rule", true);
|
||||
return (nl_nat_t *)rl;
|
||||
}
|
||||
|
||||
int
|
||||
npf_nat_insert(nl_config_t *ncf, nl_nat_t *nt, int pri __unused)
|
||||
npf_nat_insert(nl_config_t *ncf, nl_nat_t *nt)
|
||||
{
|
||||
nvlist_add_number(nt->rule_dict, "prio", (uint64_t)NPF_PRI_LAST);
|
||||
nvlist_append_nvlist_array(ncf->ncf_dict, "nat", nt->rule_dict);
|
||||
nvlist_destroy(nt->rule_dict);
|
||||
free(nt);
|
||||
|
@ -769,17 +808,17 @@ npf_nat_insert(nl_config_t *ncf, nl_nat_t *nt, int pri __unused)
|
|||
}
|
||||
|
||||
nl_nat_t *
|
||||
npf_nat_iterate(nl_config_t *ncf)
|
||||
npf_nat_iterate(nl_config_t *ncf, nl_iter_t *iter)
|
||||
{
|
||||
unsigned level;
|
||||
return _npf_rule_iterate1(ncf, "nat", &level);
|
||||
return _npf_rule_iterate1(ncf, "nat", iter, &level);
|
||||
}
|
||||
|
||||
int
|
||||
npf_nat_setaddr(nl_nat_t *nt, int af, npf_addr_t *addr, npf_netmask_t mask)
|
||||
{
|
||||
/* Translation IP and mask. */
|
||||
if (!_npf_add_addr(nt->rule_dict, "nat-ip", af, addr)) {
|
||||
if (!_npf_add_addr(nt->rule_dict, "nat-addr", af, addr)) {
|
||||
return nvlist_error(nt->rule_dict);
|
||||
}
|
||||
nvlist_add_number(nt->rule_dict, "nat-mask", (uint32_t)mask);
|
||||
|
@ -797,6 +836,9 @@ npf_nat_setport(nl_nat_t *nt, in_port_t port)
|
|||
int
|
||||
npf_nat_settable(nl_nat_t *nt, unsigned tid)
|
||||
{
|
||||
/*
|
||||
* Translation table ID; the address/mask will then serve as a filter.
|
||||
*/
|
||||
nvlist_add_number(nt->rule_dict, "nat-table-id", tid);
|
||||
return nvlist_error(nt->rule_dict);
|
||||
}
|
||||
|
@ -843,8 +885,8 @@ npf_nat_getaddr(nl_nat_t *nt, size_t *alen, npf_netmask_t *mask)
|
|||
{
|
||||
const void *data;
|
||||
|
||||
if (nvlist_exists(nt->rule_dict, "nat-ip")) {
|
||||
data = nvlist_get_binary(nt->rule_dict, "nat-ip", alen);
|
||||
if (nvlist_exists(nt->rule_dict, "nat-addr")) {
|
||||
data = nvlist_get_binary(nt->rule_dict, "nat-addr", alen);
|
||||
*mask = nvlist_get_number(nt->rule_dict, "nat-mask");
|
||||
} else {
|
||||
data = NULL;
|
||||
|
@ -892,7 +934,6 @@ npf_table_add_entry(nl_table_t *tl, int af, const npf_addr_t *addr,
|
|||
{
|
||||
nvlist_t *entry;
|
||||
|
||||
/* Create the table entry. */
|
||||
entry = nvlist_create(0);
|
||||
if (!entry) {
|
||||
return ENOMEM;
|
||||
|
@ -946,7 +987,7 @@ _npf_table_build(nl_table_t *tl)
|
|||
}
|
||||
|
||||
/*
|
||||
* Produce the constant database into a temporary file.
|
||||
* Write the constant database into a temporary file.
|
||||
*/
|
||||
strncpy(sfn, "/tmp/npfcdb.XXXXXX", sizeof(sfn));
|
||||
sfn[sizeof(sfn) - 1] = '\0';
|
||||
|
@ -1021,17 +1062,17 @@ npf_table_insert(nl_config_t *ncf, nl_table_t *tl)
|
|||
}
|
||||
|
||||
nl_table_t *
|
||||
npf_table_iterate(nl_config_t *ncf)
|
||||
npf_table_iterate(nl_config_t *ncf, nl_iter_t *iter)
|
||||
{
|
||||
const nvlist_t *table_dict;
|
||||
unsigned i = ncf->ncf_table_iter++;
|
||||
unsigned i = *iter;
|
||||
|
||||
table_dict = _npf_dataset_getelement(ncf->ncf_dict, "tables", i);
|
||||
if (!table_dict) {
|
||||
/* Reset the iterator. */
|
||||
ncf->ncf_table_iter = 0;
|
||||
*iter = NPF_ITER_BEGIN;
|
||||
return NULL;
|
||||
}
|
||||
*iter = i + 1; // next
|
||||
ncf->ncf_cur_table.table_dict = __UNCONST(table_dict); // XXX
|
||||
return &ncf->ncf_cur_table;
|
||||
}
|
||||
|
@ -1066,7 +1107,7 @@ npf_table_destroy(nl_table_t *tl)
|
|||
*/
|
||||
|
||||
int
|
||||
_npf_alg_load(nl_config_t *ncf, const char *name)
|
||||
npf_alg_load(nl_config_t *ncf, const char *name)
|
||||
{
|
||||
nvlist_t *alg_dict;
|
||||
|
||||
|
@ -1080,15 +1121,6 @@ _npf_alg_load(nl_config_t *ncf, const char *name)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_npf_alg_unload(nl_config_t *ncf, const char *name)
|
||||
{
|
||||
if (!_npf_dataset_lookup(ncf->ncf_dict, "algs", "name", name)) {
|
||||
return ENOENT;
|
||||
}
|
||||
return ENOTSUP;
|
||||
}
|
||||
|
||||
/*
|
||||
* CONNECTION / NAT ENTRY INTERFACE.
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2011-2018 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2011-2019 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
|
@ -45,21 +45,29 @@ typedef struct nl_config nl_config_t;
|
|||
typedef struct nl_rule nl_rule_t;
|
||||
typedef struct nl_rproc nl_rproc_t;
|
||||
typedef struct nl_table nl_table_t;
|
||||
|
||||
typedef struct nl_rule nl_nat_t;
|
||||
|
||||
typedef struct nl_ext nl_ext_t;
|
||||
|
||||
/*
|
||||
* Iterator.
|
||||
*/
|
||||
#define NPF_ITER_BEGIN 0
|
||||
|
||||
typedef signed long nl_iter_t;
|
||||
|
||||
/*
|
||||
* Extensions API types.
|
||||
*/
|
||||
typedef int (*npfext_initfunc_t)(void);
|
||||
typedef nl_ext_t *(*npfext_consfunc_t)(const char *);
|
||||
typedef int (*npfext_paramfunc_t)(nl_ext_t *, const char *, const char *);
|
||||
|
||||
#ifdef _NPF_PRIVATE
|
||||
typedef int (*npf_conn_func_t)(unsigned, const npf_addr_t *,
|
||||
const in_port_t *, const char *, void *);
|
||||
|
||||
typedef void (*nl_rule_callback_t)(nl_rule_t *, unsigned);
|
||||
typedef void (*nl_table_callback_t)(unsigned, int);
|
||||
|
||||
#endif
|
||||
/*
|
||||
* API functions.
|
||||
*/
|
||||
|
||||
nl_config_t * npf_config_create(void);
|
||||
void npf_config_destroy(nl_config_t *);
|
||||
|
@ -72,6 +80,11 @@ bool npf_config_active_p(nl_config_t *);
|
|||
bool npf_config_loaded_p(nl_config_t *);
|
||||
void * npf_config_build(nl_config_t *);
|
||||
|
||||
int npf_alg_load(nl_config_t *, const char *);
|
||||
|
||||
int npf_param_get(nl_config_t *, const char *, int *);
|
||||
int npf_param_set(nl_config_t *, const char *, int);
|
||||
|
||||
int npf_ruleset_add(int, const char *, nl_rule_t *, uint64_t *);
|
||||
int npf_ruleset_remove(int, const char *, uint64_t);
|
||||
int npf_ruleset_remkey(int, const char *, const void *, size_t);
|
||||
|
@ -88,6 +101,13 @@ int npf_rule_setprio(nl_rule_t *, int);
|
|||
int npf_rule_setproc(nl_rule_t *, const char *);
|
||||
int npf_rule_setkey(nl_rule_t *, const void *, size_t);
|
||||
int npf_rule_setinfo(nl_rule_t *, const void *, size_t);
|
||||
const char * npf_rule_getname(nl_rule_t *);
|
||||
uint32_t npf_rule_getattr(nl_rule_t *);
|
||||
const char * npf_rule_getinterface(nl_rule_t *);
|
||||
const void * npf_rule_getinfo(nl_rule_t *, size_t *);
|
||||
const char * npf_rule_getproc(nl_rule_t *);
|
||||
uint64_t npf_rule_getid(nl_rule_t *);
|
||||
const void * npf_rule_getcode(nl_rule_t *, int *, size_t *);
|
||||
bool npf_rule_exists_p(nl_config_t *, const char *);
|
||||
int npf_rule_insert(nl_config_t *, nl_rule_t *, nl_rule_t *);
|
||||
void * npf_rule_export(nl_rule_t *, size_t *);
|
||||
|
@ -97,17 +117,30 @@ nl_rproc_t * npf_rproc_create(const char *);
|
|||
int npf_rproc_extcall(nl_rproc_t *, nl_ext_t *);
|
||||
bool npf_rproc_exists_p(nl_config_t *, const char *);
|
||||
int npf_rproc_insert(nl_config_t *, nl_rproc_t *);
|
||||
const char * npf_rproc_getname(nl_rproc_t *);
|
||||
|
||||
nl_nat_t * npf_nat_create(int, unsigned, const char *);
|
||||
int npf_nat_insert(nl_config_t *, nl_nat_t *, int);
|
||||
int npf_nat_lookup(int, int, npf_addr_t *[2], in_port_t [2], int, int);
|
||||
int npf_nat_setaddr(nl_nat_t *, int, npf_addr_t *, npf_netmask_t);
|
||||
int npf_nat_setport(nl_nat_t *, in_port_t);
|
||||
int npf_nat_settable(nl_nat_t *, unsigned);
|
||||
int npf_nat_settablefilter(nl_nat_t *, int, npf_addr_t *, npf_netmask_t);
|
||||
int npf_nat_setalgo(nl_nat_t *, unsigned);
|
||||
int npf_nat_setnpt66(nl_nat_t *, uint16_t);
|
||||
int npf_nat_gettype(nl_nat_t *);
|
||||
unsigned npf_nat_getflags(nl_nat_t *);
|
||||
const npf_addr_t *npf_nat_getaddr(nl_nat_t *, size_t *, npf_netmask_t *);
|
||||
in_port_t npf_nat_getport(nl_nat_t *);
|
||||
unsigned npf_nat_gettable(nl_nat_t *);
|
||||
unsigned npf_nat_getalgo(nl_nat_t *);
|
||||
int npf_nat_insert(nl_config_t *, nl_nat_t *);
|
||||
int npf_nat_lookup(int, int, npf_addr_t *[2], in_port_t [2], int, int);
|
||||
|
||||
int npf_conn_list(int, npf_conn_func_t, void *);
|
||||
|
||||
nl_table_t * npf_table_create(const char *, unsigned, int);
|
||||
const char * npf_table_getname(nl_table_t *);
|
||||
unsigned npf_table_getid(nl_table_t *);
|
||||
int npf_table_gettype(nl_table_t *);
|
||||
int npf_table_add_entry(nl_table_t *, int,
|
||||
const npf_addr_t *, const npf_netmask_t);
|
||||
int npf_table_insert(nl_config_t *, nl_table_t *);
|
||||
|
@ -117,43 +150,15 @@ void npf_table_destroy(nl_table_t *);
|
|||
|
||||
#include <ifaddrs.h>
|
||||
|
||||
nl_rule_t * npf_rule_iterate(nl_config_t *, unsigned *);
|
||||
const char * npf_rule_getname(nl_rule_t *);
|
||||
uint32_t npf_rule_getattr(nl_rule_t *);
|
||||
const char * npf_rule_getinterface(nl_rule_t *);
|
||||
const void * npf_rule_getinfo(nl_rule_t *, size_t *);
|
||||
const char * npf_rule_getproc(nl_rule_t *);
|
||||
uint64_t npf_rule_getid(nl_rule_t *);
|
||||
const void * npf_rule_getcode(nl_rule_t *, int *, size_t *);
|
||||
|
||||
nl_table_t * npf_table_iterate(nl_config_t *);
|
||||
const char * npf_table_getname(nl_table_t *);
|
||||
unsigned npf_table_getid(nl_table_t *);
|
||||
int npf_table_gettype(nl_table_t *);
|
||||
|
||||
nl_nat_t * npf_nat_iterate(nl_config_t *);
|
||||
int npf_nat_gettype(nl_nat_t *);
|
||||
unsigned npf_nat_getflags(nl_nat_t *);
|
||||
const npf_addr_t *npf_nat_getaddr(nl_nat_t *, size_t *, npf_netmask_t *);
|
||||
in_port_t npf_nat_getport(nl_nat_t *);
|
||||
unsigned npf_nat_gettable(nl_nat_t *);
|
||||
unsigned npf_nat_getalgo(nl_nat_t *);
|
||||
|
||||
nl_rproc_t * npf_rproc_iterate(nl_config_t *);
|
||||
const char * npf_rproc_getname(nl_rproc_t *);
|
||||
nl_rule_t * npf_rule_iterate(nl_config_t *, nl_iter_t *, unsigned *);
|
||||
nl_nat_t * npf_nat_iterate(nl_config_t *, nl_iter_t *);
|
||||
nl_rproc_t * npf_rproc_iterate(nl_config_t *, nl_iter_t *);
|
||||
nl_table_t * npf_table_iterate(nl_config_t *, nl_iter_t *);
|
||||
|
||||
int _npf_ruleset_list(int, const char *, nl_config_t *);
|
||||
void _npf_debug_addif(nl_config_t *, const char *);
|
||||
void _npf_config_dump(nl_config_t *, int);
|
||||
|
||||
/* The ALG interface is experimental */
|
||||
int _npf_alg_load(nl_config_t *, const char *);
|
||||
int _npf_alg_unload(nl_config_t *, const char *);
|
||||
|
||||
typedef int (*npf_conn_func_t)(unsigned, const npf_addr_t *,
|
||||
const in_port_t *, const char *, void *);
|
||||
int npf_conn_list(int, npf_conn_func_t, void *);
|
||||
|
||||
#endif
|
||||
|
||||
__END_DECLS
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: files.npf,v 1.21 2018/09/29 14:41:36 rmind Exp $
|
||||
# $NetBSD: files.npf,v 1.22 2019/07/23 00:52:01 rmind Exp $
|
||||
#
|
||||
# Public Domain.
|
||||
#
|
||||
|
@ -16,16 +16,19 @@ file net/npf/npf_ctl.c npf
|
|||
file net/npf/npf_handler.c npf
|
||||
file net/npf/npf_mbuf.c npf
|
||||
file net/npf/npf_bpf.c npf
|
||||
file net/npf/npf_params.c npf
|
||||
file net/npf/npf_ruleset.c npf
|
||||
file net/npf/npf_rproc.c npf
|
||||
file net/npf/npf_tableset.c npf
|
||||
file net/npf/npf_if.c npf
|
||||
file net/npf/npf_inet.c npf
|
||||
file net/npf/npf_conn.c npf
|
||||
file net/npf/npf_connkey.c npf
|
||||
file net/npf/npf_conndb.c npf
|
||||
file net/npf/npf_state.c npf
|
||||
file net/npf/npf_state_tcp.c npf
|
||||
file net/npf/npf_nat.c npf
|
||||
file net/npf/npf_portmap.c npf
|
||||
file net/npf/npf_alg.c npf
|
||||
file net/npf/npf_sendpkt.c npf
|
||||
file net/npf/npf_worker.c npf
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.37 2019/01/19 21:19:31 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.38 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -76,8 +76,11 @@ npf_create(int flags, const npf_mbufops_t *mbufops, const npf_ifops_t *ifops)
|
|||
npf->stats_percpu = percpu_alloc(NPF_STATS_SIZE);
|
||||
npf->mbufops = mbufops;
|
||||
|
||||
npf_param_init(npf);
|
||||
npf_state_sysinit(npf);
|
||||
npf_ifmap_init(npf, ifops);
|
||||
npf_conn_init(npf, flags);
|
||||
npf_portmap_init(npf);
|
||||
npf_alg_init(npf);
|
||||
npf_ext_init(npf);
|
||||
|
||||
|
@ -98,8 +101,11 @@ npf_destroy(npf_t *npf)
|
|||
/* Finally, safe to destroy the subsystems. */
|
||||
npf_ext_fini(npf);
|
||||
npf_alg_fini(npf);
|
||||
npf_portmap_fini(npf);
|
||||
npf_conn_fini(npf);
|
||||
npf_ifmap_fini(npf);
|
||||
npf_state_sysfini(npf);
|
||||
npf_param_fini(npf);
|
||||
|
||||
pserialize_destroy(npf->qsbr);
|
||||
percpu_free(npf->stats_percpu, NPF_STATS_SIZE);
|
||||
|
@ -173,12 +179,29 @@ npf_stats_collect(void *mem, void *arg, struct cpu_info *ci)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
npf_stats_clear_cb(void *mem, void *arg, struct cpu_info *ci)
|
||||
{
|
||||
uint64_t *percpu_stats = mem;
|
||||
|
||||
for (unsigned i = 0; i < NPF_STATS_COUNT; i++) {
|
||||
percpu_stats[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_stats: export collected statistics.
|
||||
*/
|
||||
|
||||
__dso_public void
|
||||
npf_stats(npf_t *npf, uint64_t *buf)
|
||||
{
|
||||
memset(buf, 0, NPF_STATS_SIZE);
|
||||
percpu_foreach(npf->stats_percpu, npf_stats_collect, buf);
|
||||
}
|
||||
|
||||
__dso_public void
|
||||
npf_stats_clear(npf_t *npf)
|
||||
{
|
||||
percpu_foreach(npf->stats_percpu, npf_stats_clear_cb, NULL);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define NPF_VERSION 21
|
||||
#define NPF_VERSION 22
|
||||
|
||||
#if defined(_NPF_STANDALONE)
|
||||
#include "npf_stand.h"
|
||||
|
@ -203,7 +203,7 @@ bool npf_autounload_p(void);
|
|||
#define NPF_RULE_RETRST 0x00000010
|
||||
#define NPF_RULE_RETICMP 0x00000020
|
||||
#define NPF_RULE_DYNAMIC 0x00000040
|
||||
#define NPF_RULE_MULTIENDS 0x00000080
|
||||
#define NPF_RULE_GSTATEFUL 0x00000080
|
||||
|
||||
#define NPF_DYNAMIC_GROUP (NPF_RULE_GROUP | NPF_RULE_DYNAMIC)
|
||||
|
||||
|
@ -317,8 +317,9 @@ typedef struct npf_ioctl_table {
|
|||
|
||||
typedef struct {
|
||||
int64_t id;
|
||||
char * error_msg;
|
||||
char * source_file;
|
||||
u_int source_line;
|
||||
unsigned source_line;
|
||||
} npf_error_t;
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2010-2013 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2010-2019 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
|
@ -33,15 +33,13 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_alg.c,v 1.19 2019/01/19 21:19:31 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_alg.c,v 1.20 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/pserialize.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <net/pfil.h>
|
||||
#include <sys/module.h>
|
||||
#endif
|
||||
|
||||
|
@ -55,20 +53,20 @@ __KERNEL_RCSID(0, "$NetBSD: npf_alg.c,v 1.19 2019/01/19 21:19:31 rmind Exp $");
|
|||
|
||||
struct npf_alg {
|
||||
const char * na_name;
|
||||
u_int na_slot;
|
||||
unsigned na_slot;
|
||||
};
|
||||
|
||||
struct npf_algset {
|
||||
/* List of ALGs and the count. */
|
||||
npf_alg_t alg_list[NPF_MAX_ALGS];
|
||||
u_int alg_count;
|
||||
unsigned alg_count;
|
||||
|
||||
/* Matching, inspection and translation functions. */
|
||||
npfa_funcs_t alg_funcs[NPF_MAX_ALGS];
|
||||
};
|
||||
|
||||
static const char alg_prefix[] = "npf_alg_";
|
||||
#define NPF_EXT_PREFLEN (sizeof(alg_prefix) - 1)
|
||||
#define NPF_ALG_PREF "npf_alg_"
|
||||
#define NPF_ALG_PREFLEN (sizeof(NPF_ALG_PREF) - 1)
|
||||
|
||||
void
|
||||
npf_alg_init(npf_t *npf)
|
||||
|
@ -94,7 +92,7 @@ npf_alg_lookup(npf_t *npf, const char *name)
|
|||
|
||||
KASSERT(npf_config_locked_p(npf));
|
||||
|
||||
for (u_int i = 0; i < aset->alg_count; i++) {
|
||||
for (unsigned i = 0; i < aset->alg_count; i++) {
|
||||
npf_alg_t *alg = &aset->alg_list[i];
|
||||
const char *aname = alg->na_name;
|
||||
|
||||
|
@ -111,9 +109,9 @@ npf_alg_construct(npf_t *npf, const char *name)
|
|||
|
||||
npf_config_enter(npf);
|
||||
if ((alg = npf_alg_lookup(npf, name)) == NULL) {
|
||||
char modname[NPF_EXT_PREFLEN + 64];
|
||||
char modname[NPF_ALG_PREFLEN + 64];
|
||||
|
||||
snprintf(modname, sizeof(modname), "%s%s", alg_prefix, name);
|
||||
snprintf(modname, sizeof(modname), "%s%s", NPF_ALG_PREF, name);
|
||||
npf_config_exit(npf);
|
||||
|
||||
if (module_autoload(modname, MODULE_CLASS_MISC) != 0) {
|
||||
|
@ -135,7 +133,7 @@ npf_alg_register(npf_t *npf, const char *name, const npfa_funcs_t *funcs)
|
|||
npf_algset_t *aset = npf->algset;
|
||||
npfa_funcs_t *afuncs;
|
||||
npf_alg_t *alg;
|
||||
u_int i;
|
||||
unsigned i;
|
||||
|
||||
npf_config_enter(npf);
|
||||
if (npf_alg_lookup(npf, name) != NULL) {
|
||||
|
@ -178,7 +176,7 @@ int
|
|||
npf_alg_unregister(npf_t *npf, npf_alg_t *alg)
|
||||
{
|
||||
npf_algset_t *aset = npf->algset;
|
||||
u_int i = alg->na_slot;
|
||||
unsigned i = alg->na_slot;
|
||||
npfa_funcs_t *afuncs;
|
||||
|
||||
/* Deactivate the functions first. */
|
||||
|
@ -198,7 +196,16 @@ npf_alg_unregister(npf_t *npf, npf_alg_t *alg)
|
|||
}
|
||||
|
||||
/*
|
||||
* npf_alg_match: call ALG matching inspectors, determine if any ALG matches.
|
||||
* npf_alg_match: call the ALG matching inspectors.
|
||||
*
|
||||
* The purpose of the "matching" inspector function in the ALG API
|
||||
* is to determine whether this connection matches the ALG criteria
|
||||
* i.e. is concerning the ALG. If yes, ALG can associate itself with
|
||||
* the given NAT state structure and set/save an arbitrary parameter.
|
||||
* This is done using the using the npf_nat_setalg() function.
|
||||
*
|
||||
* => This is called when the packet matches the dynamic NAT policy
|
||||
* and the NAT state entry is being created for it [NAT-ESTABLISH].
|
||||
*/
|
||||
bool
|
||||
npf_alg_match(npf_cache_t *npc, npf_nat_t *nt, int di)
|
||||
|
@ -207,8 +214,10 @@ npf_alg_match(npf_cache_t *npc, npf_nat_t *nt, int di)
|
|||
bool match = false;
|
||||
int s;
|
||||
|
||||
KASSERTMSG(npf_iscached(npc, NPC_IP46), "expecting protocol number");
|
||||
|
||||
s = pserialize_read_enter();
|
||||
for (u_int i = 0; i < aset->alg_count; i++) {
|
||||
for (unsigned i = 0; i < aset->alg_count; i++) {
|
||||
const npfa_funcs_t *f = &aset->alg_funcs[i];
|
||||
|
||||
if (f->match && f->match(npc, nt, di)) {
|
||||
|
@ -221,7 +230,15 @@ npf_alg_match(npf_cache_t *npc, npf_nat_t *nt, int di)
|
|||
}
|
||||
|
||||
/*
|
||||
* npf_alg_exec: execute ALG hooks for translation.
|
||||
* npf_alg_exec: execute the ALG translation processors.
|
||||
*
|
||||
* The ALG function would perform any additional packet translation
|
||||
* or manipulation here. The translate function will be called by
|
||||
* once the ALG has been associated with the NAT state through the
|
||||
* npf_alg_match() inspector.
|
||||
*
|
||||
* => This is called when the packet is being translated according
|
||||
* to the dynamic NAT logic [NAT-TRANSLATE].
|
||||
*/
|
||||
void
|
||||
npf_alg_exec(npf_cache_t *npc, npf_nat_t *nt, bool forw)
|
||||
|
@ -229,8 +246,10 @@ npf_alg_exec(npf_cache_t *npc, npf_nat_t *nt, bool forw)
|
|||
npf_algset_t *aset = npc->npc_ctx->algset;
|
||||
int s;
|
||||
|
||||
KASSERTMSG(npf_iscached(npc, NPC_IP46), "expecting protocol number");
|
||||
|
||||
s = pserialize_read_enter();
|
||||
for (u_int i = 0; i < aset->alg_count; i++) {
|
||||
for (unsigned i = 0; i < aset->alg_count; i++) {
|
||||
const npfa_funcs_t *f = &aset->alg_funcs[i];
|
||||
|
||||
if (f->translate) {
|
||||
|
@ -240,6 +259,23 @@ npf_alg_exec(npf_cache_t *npc, npf_nat_t *nt, bool forw)
|
|||
pserialize_read_exit(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_alg_conn: query ALGs giving which may perform a custom state lookup.
|
||||
*
|
||||
* The purpose of ALG connection inspection function is to provide
|
||||
* ALGs with a mechanism to override the regular connection state
|
||||
* lookup, if they need to. For example, some ALGs may want to
|
||||
* extract and use a different 5-tuple to perform a lookup.
|
||||
*
|
||||
* => This is called at the beginning of the connection state lookup
|
||||
* function [CONN-LOOKUP].
|
||||
*
|
||||
* => Must use the npf_conn_lookup() function to perform the custom
|
||||
* connection state lookup and return the result.
|
||||
*
|
||||
* => Returning NULL will result in NPF performing a regular state
|
||||
* lookup for the packet.
|
||||
*/
|
||||
npf_conn_t *
|
||||
npf_alg_conn(npf_cache_t *npc, int di)
|
||||
{
|
||||
|
@ -248,7 +284,7 @@ npf_alg_conn(npf_cache_t *npc, int di)
|
|||
int s;
|
||||
|
||||
s = pserialize_read_enter();
|
||||
for (u_int i = 0; i < aset->alg_count; i++) {
|
||||
for (unsigned i = 0; i < aset->alg_count; i++) {
|
||||
const npfa_funcs_t *f = &aset->alg_funcs[i];
|
||||
|
||||
if (!f->inspect)
|
||||
|
@ -260,6 +296,9 @@ npf_alg_conn(npf_cache_t *npc, int di)
|
|||
return con;
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_alg_export: serialise the configuration of ALGs.
|
||||
*/
|
||||
int
|
||||
npf_alg_export(npf_t *npf, nvlist_t *npf_dict)
|
||||
{
|
||||
|
@ -267,7 +306,7 @@ npf_alg_export(npf_t *npf, nvlist_t *npf_dict)
|
|||
|
||||
KASSERT(npf_config_locked_p(npf));
|
||||
|
||||
for (u_int i = 0; i < aset->alg_count; i++) {
|
||||
for (unsigned i = 0; i < aset->alg_count; i++) {
|
||||
const npf_alg_t *alg = &aset->alg_list[i];
|
||||
nvlist_t *algdict;
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.31 2018/09/29 14:41:36 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.32 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/module.h>
|
||||
|
@ -453,33 +453,36 @@ err:
|
|||
* and module interface.
|
||||
*/
|
||||
|
||||
static int
|
||||
npf_alg_icmp_init(void)
|
||||
int
|
||||
npf_alg_icmp_init(npf_t *npf)
|
||||
{
|
||||
static const npfa_funcs_t icmp = {
|
||||
.match = npfa_icmp_match,
|
||||
.translate = npfa_icmp_nat,
|
||||
.inspect = npfa_icmp_conn,
|
||||
};
|
||||
alg_icmp = npf_alg_register(npf_getkernctx(), "icmp", &icmp);
|
||||
alg_icmp = npf_alg_register(npf, "icmp", &icmp);
|
||||
return alg_icmp ? 0 : ENOMEM;
|
||||
}
|
||||
|
||||
static int
|
||||
npf_alg_icmp_fini(void)
|
||||
int
|
||||
npf_alg_icmp_fini(npf_t *npf)
|
||||
{
|
||||
KASSERT(alg_icmp != NULL);
|
||||
return npf_alg_unregister(npf_getkernctx(), alg_icmp);
|
||||
return npf_alg_unregister(npf, alg_icmp);
|
||||
}
|
||||
|
||||
#ifdef _KERNEL
|
||||
static int
|
||||
npf_alg_icmp_modcmd(modcmd_t cmd, void *arg)
|
||||
{
|
||||
npf_t *npf = npf_getkernctx();
|
||||
|
||||
switch (cmd) {
|
||||
case MODULE_CMD_INIT:
|
||||
return npf_alg_icmp_init();
|
||||
return npf_alg_icmp_init(npf);
|
||||
case MODULE_CMD_FINI:
|
||||
return npf_alg_icmp_fini();
|
||||
return npf_alg_icmp_fini(npf);
|
||||
case MODULE_CMD_AUTOUNLOAD:
|
||||
return EBUSY;
|
||||
default:
|
||||
|
@ -487,3 +490,4 @@ npf_alg_icmp_modcmd(modcmd_t cmd, void *arg)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_conf.c,v 1.12 2018/09/29 14:41:36 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_conf.c,v 1.13 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -169,6 +169,7 @@ npf_config_load(npf_t *npf, npf_ruleset_t *rset, npf_tableset_t *tset,
|
|||
/* Synchronise: drain all references. */
|
||||
pserialize_perform(npf->qsbr);
|
||||
if (flush) {
|
||||
npf_portmap_flush(npf);
|
||||
npf_ifmap_flush(npf);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,8 +43,13 @@
|
|||
*
|
||||
* All connections have two keys and thus two entries:
|
||||
*
|
||||
* npf_conn_t::c_forw_entry for the forwards stream and
|
||||
* npf_conn_t::c_back_entry for the backwards stream.
|
||||
* - npf_conn_getforwkey(con) -- for the forwards stream;
|
||||
* - npf_conn_getbackkey(con, alen) -- for the backwards stream.
|
||||
*
|
||||
* Note: the keys are stored in npf_conn_t::c_keys[], which is used
|
||||
* to allocate variable-length npf_conn_t structures based on whether
|
||||
* the IPv4 or IPv6 addresses are used. See the npf_connkey.c source
|
||||
* file for the description of the key layouts.
|
||||
*
|
||||
* The keys are formed from the 5-tuple (source/destination address,
|
||||
* source/destination port and the protocol). Additional matching
|
||||
|
@ -102,7 +107,7 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.26 2019/01/19 21:19:31 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.27 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -111,9 +116,7 @@ __KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.26 2019/01/19 21:19:31 rmind Exp $");
|
|||
#include <netinet/tcp.h>
|
||||
|
||||
#include <sys/atomic.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <net/pfil.h>
|
||||
#include <sys/pool.h>
|
||||
|
@ -125,6 +128,9 @@ __KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.26 2019/01/19 21:19:31 rmind Exp $");
|
|||
#include "npf_conn.h"
|
||||
#include "npf_impl.h"
|
||||
|
||||
/* A helper to select the IPv4 or IPv6 connection cache. */
|
||||
#define NPF_CONNCACHE(alen) (((alen) >> 4) & 0x1)
|
||||
|
||||
/*
|
||||
* Connection flags: PFIL_IN and PFIL_OUT values are reserved for direction.
|
||||
*/
|
||||
|
@ -136,7 +142,7 @@ CTASSERT(PFIL_ALL == (0x001 | 0x002));
|
|||
|
||||
enum { CONN_TRACKING_OFF, CONN_TRACKING_ON };
|
||||
|
||||
static nvlist_t *npf_conn_export(npf_t *, const npf_conn_t *);
|
||||
static nvlist_t *npf_conn_export(npf_t *, npf_conn_t *);
|
||||
|
||||
/*
|
||||
* npf_conn_sys{init,fini}: initialise/destroy connection tracking.
|
||||
|
@ -145,8 +151,13 @@ static nvlist_t *npf_conn_export(npf_t *, const npf_conn_t *);
|
|||
void
|
||||
npf_conn_init(npf_t *npf, int flags)
|
||||
{
|
||||
npf->conn_cache = pool_cache_init(sizeof(npf_conn_t), coherency_unit,
|
||||
0, 0, "npfconpl", NULL, IPL_NET, NULL, NULL, NULL);
|
||||
npf->conn_cache[0] = pool_cache_init(
|
||||
offsetof(npf_conn_t, c_keys[NPF_CONNKEY_V4WORDS * 2]),
|
||||
0, 0, 0, "npfcn4pl", NULL, IPL_NET, NULL, NULL, NULL);
|
||||
npf->conn_cache[1] = pool_cache_init(
|
||||
offsetof(npf_conn_t, c_keys[NPF_CONNKEY_V6WORDS * 2]),
|
||||
0, 0, 0, "npfcn6pl", NULL, IPL_NET, NULL, NULL, NULL);
|
||||
|
||||
mutex_init(&npf->conn_lock, MUTEX_DEFAULT, IPL_NONE);
|
||||
npf->conn_tracking = CONN_TRACKING_OFF;
|
||||
npf->conn_db = npf_conndb_create();
|
||||
|
@ -154,17 +165,21 @@ npf_conn_init(npf_t *npf, int flags)
|
|||
if ((flags & NPF_NO_GC) == 0) {
|
||||
npf_worker_register(npf, npf_conn_worker);
|
||||
}
|
||||
npf_conndb_sysinit(npf);
|
||||
}
|
||||
|
||||
void
|
||||
npf_conn_fini(npf_t *npf)
|
||||
{
|
||||
npf_conndb_sysfini(npf);
|
||||
|
||||
/* Note: the caller should have flushed the connections. */
|
||||
KASSERT(npf->conn_tracking == CONN_TRACKING_OFF);
|
||||
npf_worker_unregister(npf, npf_conn_worker);
|
||||
|
||||
npf_conndb_destroy(npf->conn_db);
|
||||
pool_cache_destroy(npf->conn_cache);
|
||||
pool_cache_destroy(npf->conn_cache[0]);
|
||||
pool_cache_destroy(npf->conn_cache[1]);
|
||||
mutex_destroy(&npf->conn_lock);
|
||||
}
|
||||
|
||||
|
@ -206,7 +221,8 @@ npf_conn_load(npf_t *npf, npf_conndb_t *ndb, bool track)
|
|||
*/
|
||||
npf_conndb_gc(npf, odb, true, false);
|
||||
npf_conndb_destroy(odb);
|
||||
pool_cache_invalidate(npf->conn_cache);
|
||||
pool_cache_invalidate(npf->conn_cache[0]);
|
||||
pool_cache_invalidate(npf->conn_cache[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,138 +254,6 @@ npf_conn_trackable_p(const npf_cache_t *npc)
|
|||
return true;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
connkey_setkey(npf_connkey_t *key, uint16_t proto, const void *ipv,
|
||||
const uint16_t *id, unsigned alen, bool forw)
|
||||
{
|
||||
uint32_t isrc, idst, *k = key->ck_key;
|
||||
const npf_addr_t * const *ips = ipv;
|
||||
|
||||
if (__predict_true(forw)) {
|
||||
isrc = NPF_SRC, idst = NPF_DST;
|
||||
} else {
|
||||
isrc = NPF_DST, idst = NPF_SRC;
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a key formed out of 32-bit integers. The key layout:
|
||||
*
|
||||
* Field: | proto | alen | src-id | dst-id | src-addr | dst-addr |
|
||||
* +--------+--------+--------+--------+----------+----------+
|
||||
* Bits: | 16 | 16 | 16 | 16 | 32-128 | 32-128 |
|
||||
*
|
||||
* The source and destination are inverted if they key is for the
|
||||
* backwards stream (forw == false). The address length depends
|
||||
* on the 'alen' field; it is a length in bytes, either 4 or 16.
|
||||
*/
|
||||
|
||||
k[0] = ((uint32_t)proto << 16) | (alen & 0xffff);
|
||||
k[1] = ((uint32_t)id[isrc] << 16) | id[idst];
|
||||
|
||||
if (__predict_true(alen == sizeof(in_addr_t))) {
|
||||
k[2] = ips[isrc]->word32[0];
|
||||
k[3] = ips[idst]->word32[0];
|
||||
return 4 * sizeof(uint32_t);
|
||||
} else {
|
||||
const u_int nwords = alen >> 2;
|
||||
memcpy(&k[2], ips[isrc], alen);
|
||||
memcpy(&k[2 + nwords], ips[idst], alen);
|
||||
return (2 + (nwords * 2)) * sizeof(uint32_t);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
connkey_getkey(const npf_connkey_t *key, uint16_t *proto, npf_addr_t *ips,
|
||||
uint16_t *id, uint16_t *alen)
|
||||
{
|
||||
const uint32_t *k = key->ck_key;
|
||||
|
||||
*proto = k[0] >> 16;
|
||||
*alen = k[0] & 0xffff;
|
||||
id[NPF_SRC] = k[1] >> 16;
|
||||
id[NPF_DST] = k[1] & 0xffff;
|
||||
|
||||
switch (*alen) {
|
||||
case sizeof(struct in6_addr):
|
||||
case sizeof(struct in_addr):
|
||||
memcpy(&ips[NPF_SRC], &k[2], *alen);
|
||||
memcpy(&ips[NPF_DST], &k[2 + ((unsigned)*alen >> 2)], *alen);
|
||||
return;
|
||||
default:
|
||||
KASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_conn_conkey: construct a key for the connection lookup.
|
||||
*
|
||||
* => Returns the key length in bytes or zero on failure.
|
||||
*/
|
||||
unsigned
|
||||
npf_conn_conkey(const npf_cache_t *npc, npf_connkey_t *key, const bool forw)
|
||||
{
|
||||
const u_int proto = npc->npc_proto;
|
||||
const u_int alen = npc->npc_alen;
|
||||
const struct tcphdr *th;
|
||||
const struct udphdr *uh;
|
||||
uint16_t id[2];
|
||||
|
||||
switch (proto) {
|
||||
case IPPROTO_TCP:
|
||||
KASSERT(npf_iscached(npc, NPC_TCP));
|
||||
th = npc->npc_l4.tcp;
|
||||
id[NPF_SRC] = th->th_sport;
|
||||
id[NPF_DST] = th->th_dport;
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
KASSERT(npf_iscached(npc, NPC_UDP));
|
||||
uh = npc->npc_l4.udp;
|
||||
id[NPF_SRC] = uh->uh_sport;
|
||||
id[NPF_DST] = uh->uh_dport;
|
||||
break;
|
||||
case IPPROTO_ICMP:
|
||||
if (npf_iscached(npc, NPC_ICMP_ID)) {
|
||||
const struct icmp *ic = npc->npc_l4.icmp;
|
||||
id[NPF_SRC] = ic->icmp_id;
|
||||
id[NPF_DST] = ic->icmp_id;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
case IPPROTO_ICMPV6:
|
||||
if (npf_iscached(npc, NPC_ICMP_ID)) {
|
||||
const struct icmp6_hdr *ic6 = npc->npc_l4.icmp6;
|
||||
id[NPF_SRC] = ic6->icmp6_id;
|
||||
id[NPF_DST] = ic6->icmp6_id;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
/* Unsupported protocol. */
|
||||
return 0;
|
||||
}
|
||||
return connkey_setkey(key, proto, npc->npc_ips, id, alen, forw);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
connkey_set_addr(npf_connkey_t *key, const npf_addr_t *naddr, const int di)
|
||||
{
|
||||
const u_int alen = key->ck_key[0] & 0xffff;
|
||||
uint32_t *addr = &key->ck_key[2 + ((alen >> 2) * di)];
|
||||
|
||||
KASSERT(alen > 0);
|
||||
memcpy(addr, naddr, alen);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
connkey_set_id(npf_connkey_t *key, const uint16_t id, const int di)
|
||||
{
|
||||
const uint32_t oid = key->ck_key[1];
|
||||
const u_int shift = 16 * !di;
|
||||
const uint32_t mask = 0xffff0000 >> shift;
|
||||
|
||||
key->ck_key[1] = ((uint32_t)id << shift) | (oid & mask);
|
||||
}
|
||||
|
||||
static inline void
|
||||
conn_update_atime(npf_conn_t *con)
|
||||
{
|
||||
|
@ -380,23 +264,36 @@ conn_update_atime(npf_conn_t *con)
|
|||
}
|
||||
|
||||
/*
|
||||
* npf_conn_ok: check if the connection is active and has the right direction.
|
||||
* npf_conn_check: check that:
|
||||
*
|
||||
* - the connection is active;
|
||||
*
|
||||
* - the packet is travelling in the right direction with the respect
|
||||
* to the connection direction (if interface-id is not zero);
|
||||
*
|
||||
* - the packet is travelling on the same interface as the
|
||||
* connection interface (if interface-id is not zero).
|
||||
*/
|
||||
static bool
|
||||
npf_conn_ok(const npf_conn_t *con, const int di, bool forw)
|
||||
npf_conn_check(const npf_conn_t *con, const nbuf_t *nbuf,
|
||||
const unsigned di, const bool forw)
|
||||
{
|
||||
const uint32_t flags = con->c_flags;
|
||||
const unsigned ifid = con->c_ifid;
|
||||
bool active, pforw;
|
||||
|
||||
/* Check if connection is active and not expired. */
|
||||
bool ok = (flags & (CONN_ACTIVE | CONN_EXPIRE)) == CONN_ACTIVE;
|
||||
if (__predict_false(!ok)) {
|
||||
active = (flags & (CONN_ACTIVE | CONN_EXPIRE)) == CONN_ACTIVE;
|
||||
if (__predict_false(!active)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if the direction is consistent */
|
||||
bool pforw = (flags & PFIL_ALL) == (unsigned)di;
|
||||
if (__predict_false(forw != pforw)) {
|
||||
return false;
|
||||
if (ifid && nbuf) {
|
||||
pforw = (flags & PFIL_ALL) == (unsigned)di;
|
||||
if (__predict_false(forw != pforw)) {
|
||||
return false;
|
||||
}
|
||||
if (__predict_false(ifid != nbuf->nb_ifid)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -413,7 +310,6 @@ npf_conn_lookup(const npf_cache_t *npc, const int di, bool *forw)
|
|||
const nbuf_t *nbuf = npc->npc_nbuf;
|
||||
npf_conn_t *con;
|
||||
npf_connkey_t key;
|
||||
u_int cifid;
|
||||
|
||||
/* Construct a key and lookup for a connection in the store. */
|
||||
if (!npf_conn_conkey(npc, &key, true)) {
|
||||
|
@ -425,18 +321,8 @@ npf_conn_lookup(const npf_cache_t *npc, const int di, bool *forw)
|
|||
}
|
||||
KASSERT(npc->npc_proto == con->c_proto);
|
||||
|
||||
/* Check if connection is active and not expired. */
|
||||
if (!npf_conn_ok(con, di, *forw)) {
|
||||
atomic_dec_uint(&con->c_refcnt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Match the interface and the direction of the connection entry
|
||||
* and the packet.
|
||||
*/
|
||||
cifid = con->c_ifid;
|
||||
if (__predict_false(cifid && cifid != nbuf->nb_ifid)) {
|
||||
/* Extra checks for the connection and packet. */
|
||||
if (!npf_conn_check(con, nbuf, di, *forw)) {
|
||||
atomic_dec_uint(&con->c_refcnt);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -510,10 +396,13 @@ npf_conn_inspect(npf_cache_t *npc, const int di, int *error)
|
|||
* => Connection will be activated on the first reference release.
|
||||
*/
|
||||
npf_conn_t *
|
||||
npf_conn_establish(npf_cache_t *npc, int di, bool per_if)
|
||||
npf_conn_establish(npf_cache_t *npc, int di, bool global)
|
||||
{
|
||||
npf_t *npf = npc->npc_ctx;
|
||||
const unsigned alen = npc->npc_alen;
|
||||
const unsigned idx = NPF_CONNCACHE(alen);
|
||||
const nbuf_t *nbuf = npc->npc_nbuf;
|
||||
npf_connkey_t *fw, *bk;
|
||||
npf_conn_t *con;
|
||||
int error = 0;
|
||||
|
||||
|
@ -524,7 +413,7 @@ npf_conn_establish(npf_cache_t *npc, int di, bool per_if)
|
|||
}
|
||||
|
||||
/* Allocate and initialise the new connection. */
|
||||
con = pool_cache_get(npf->conn_cache, PR_NOWAIT);
|
||||
con = pool_cache_get(npf->conn_cache[idx], PR_NOWAIT);
|
||||
if (__predict_false(!con)) {
|
||||
npf_worker_signal(npf);
|
||||
return NULL;
|
||||
|
@ -538,15 +427,18 @@ npf_conn_establish(npf_cache_t *npc, int di, bool per_if)
|
|||
con->c_rproc = NULL;
|
||||
con->c_nat = NULL;
|
||||
|
||||
con->c_proto = npc->npc_proto;
|
||||
CTASSERT(sizeof(con->c_proto) >= sizeof(npc->npc_proto));
|
||||
|
||||
/* Initialize the protocol state. */
|
||||
if (!npf_state_init(npc, &con->c_state)) {
|
||||
npf_conn_destroy(npf, con);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
KASSERT(npf_iscached(npc, NPC_IP46));
|
||||
npf_connkey_t *fw = &con->c_forw_entry;
|
||||
npf_connkey_t *bk = &con->c_back_entry;
|
||||
|
||||
fw = npf_conn_getforwkey(con);
|
||||
bk = npf_conn_getbackkey(con, alen);
|
||||
|
||||
/*
|
||||
* Construct "forwards" and "backwards" keys. Also, set the
|
||||
|
@ -557,9 +449,7 @@ npf_conn_establish(npf_cache_t *npc, int di, bool per_if)
|
|||
npf_conn_destroy(npf, con);
|
||||
return NULL;
|
||||
}
|
||||
fw->ck_backptr = bk->ck_backptr = con;
|
||||
con->c_ifid = per_if ? nbuf->nb_ifid : 0;
|
||||
con->c_proto = npc->npc_proto;
|
||||
con->c_ifid = global ? nbuf->nb_ifid : 0;
|
||||
|
||||
/*
|
||||
* Set last activity time for a new connection and acquire
|
||||
|
@ -574,11 +464,11 @@ npf_conn_establish(npf_cache_t *npc, int di, bool per_if)
|
|||
* the connection later.
|
||||
*/
|
||||
mutex_enter(&con->c_lock);
|
||||
if (!npf_conndb_insert(npf->conn_db, fw)) {
|
||||
if (!npf_conndb_insert(npf->conn_db, fw, con, true)) {
|
||||
error = EISCONN;
|
||||
goto err;
|
||||
}
|
||||
if (!npf_conndb_insert(npf->conn_db, bk)) {
|
||||
if (!npf_conndb_insert(npf->conn_db, bk, con, false)) {
|
||||
npf_conn_t *ret __diagused;
|
||||
ret = npf_conndb_remove(npf->conn_db, fw);
|
||||
KASSERT(ret == con);
|
||||
|
@ -609,6 +499,10 @@ err:
|
|||
void
|
||||
npf_conn_destroy(npf_t *npf, npf_conn_t *con)
|
||||
{
|
||||
const npf_connkey_t *key = npf_conn_getforwkey(con);
|
||||
const unsigned alen = NPF_CONNKEY_ALEN(key);
|
||||
const unsigned idx __unused = NPF_CONNCACHE(alen);
|
||||
|
||||
KASSERT(con->c_refcnt == 0);
|
||||
|
||||
if (con->c_nat) {
|
||||
|
@ -625,7 +519,7 @@ npf_conn_destroy(npf_t *npf, npf_conn_t *con)
|
|||
mutex_destroy(&con->c_lock);
|
||||
|
||||
/* Free the structure, increase the counter. */
|
||||
pool_cache_put(npf->conn_cache, con);
|
||||
pool_cache_put(npf->conn_cache[idx], con);
|
||||
npf_stats_inc(npf, NPF_STAT_CONN_DESTROY);
|
||||
NPF_PRINTF(("NPF: conn %p destroyed\n", con));
|
||||
}
|
||||
|
@ -638,24 +532,22 @@ npf_conn_destroy(npf_t *npf, npf_conn_t *con)
|
|||
*/
|
||||
int
|
||||
npf_conn_setnat(const npf_cache_t *npc, npf_conn_t *con,
|
||||
npf_nat_t *nt, u_int ntype)
|
||||
npf_nat_t *nt, unsigned ntype)
|
||||
{
|
||||
static const u_int nat_type_dimap[] = {
|
||||
[NPF_NATOUT] = NPF_DST,
|
||||
[NPF_NATIN] = NPF_SRC,
|
||||
};
|
||||
npf_t *npf = npc->npc_ctx;
|
||||
npf_connkey_t key, *bk;
|
||||
npf_connkey_t key, *fw, *bk;
|
||||
npf_conn_t *ret __diagused;
|
||||
npf_addr_t *taddr;
|
||||
in_port_t tport;
|
||||
u_int tidx;
|
||||
|
||||
KASSERT(con->c_refcnt > 0);
|
||||
|
||||
npf_nat_gettrans(nt, &taddr, &tport);
|
||||
KASSERT(ntype == NPF_NATOUT || ntype == NPF_NATIN);
|
||||
tidx = nat_type_dimap[ntype];
|
||||
|
||||
/* Construct a "backwards" key. */
|
||||
if (!npf_conn_conkey(npc, &key, false)) {
|
||||
|
@ -678,24 +570,22 @@ npf_conn_setnat(const npf_cache_t *npc, npf_conn_t *con,
|
|||
return EISCONN;
|
||||
}
|
||||
|
||||
/* Remove the "backwards" entry. */
|
||||
ret = npf_conndb_remove(npf->conn_db, &con->c_back_entry);
|
||||
/* Remove the "backwards" key. */
|
||||
fw = npf_conn_getforwkey(con);
|
||||
bk = npf_conn_getbackkey(con, NPF_CONNKEY_ALEN(fw));
|
||||
ret = npf_conndb_remove(npf->conn_db, bk);
|
||||
KASSERT(ret == con);
|
||||
|
||||
/* Set the source/destination IDs to the translation values. */
|
||||
bk = &con->c_back_entry;
|
||||
connkey_set_addr(bk, taddr, tidx);
|
||||
if (tport) {
|
||||
connkey_set_id(bk, tport, tidx);
|
||||
}
|
||||
npf_conn_adjkey(bk, taddr, tport, nat_type_dimap[ntype]);
|
||||
|
||||
/* Finally, re-insert the "backwards" entry. */
|
||||
if (!npf_conndb_insert(npf->conn_db, bk)) {
|
||||
/* Finally, re-insert the "backwards" key. */
|
||||
if (!npf_conndb_insert(npf->conn_db, bk, con, false)) {
|
||||
/*
|
||||
* Race: we have hit the duplicate, remove the "forwards"
|
||||
* entry and expire our connection; it is no longer valid.
|
||||
* key and expire our connection; it is no longer valid.
|
||||
*/
|
||||
ret = npf_conndb_remove(npf->conn_db, &con->c_forw_entry);
|
||||
ret = npf_conndb_remove(npf->conn_db, fw);
|
||||
KASSERT(ret == con);
|
||||
|
||||
atomic_or_uint(&con->c_flags, CONN_REMOVED | CONN_EXPIRE);
|
||||
|
@ -792,9 +682,9 @@ npf_conn_getnat(npf_conn_t *con, const int di, bool *forw)
|
|||
* npf_conn_expired: criterion to check if connection is expired.
|
||||
*/
|
||||
bool
|
||||
npf_conn_expired(const npf_conn_t *con, uint64_t tsnow)
|
||||
npf_conn_expired(npf_t *npf, const npf_conn_t *con, uint64_t tsnow)
|
||||
{
|
||||
const int etime = npf_state_etime(&con->c_state, con->c_proto);
|
||||
const int etime = npf_state_etime(npf, &con->c_state, con->c_proto);
|
||||
int elapsed;
|
||||
|
||||
if (__predict_false(con->c_flags & CONN_EXPIRE)) {
|
||||
|
@ -819,11 +709,15 @@ npf_conn_remove(npf_conndb_t *cd, npf_conn_t *con)
|
|||
/* Remove both entries of the connection. */
|
||||
mutex_enter(&con->c_lock);
|
||||
if ((con->c_flags & CONN_REMOVED) == 0) {
|
||||
npf_connkey_t *fw, *bk;
|
||||
npf_conn_t *ret __diagused;
|
||||
|
||||
ret = npf_conndb_remove(cd, &con->c_forw_entry);
|
||||
fw = npf_conn_getforwkey(con);
|
||||
ret = npf_conndb_remove(cd, fw);
|
||||
KASSERT(ret == con);
|
||||
ret = npf_conndb_remove(cd, &con->c_back_entry);
|
||||
|
||||
bk = npf_conn_getbackkey(con, NPF_CONNKEY_ALEN(fw));
|
||||
ret = npf_conndb_remove(cd, bk);
|
||||
KASSERT(ret == con);
|
||||
}
|
||||
|
||||
|
@ -876,30 +770,15 @@ npf_conndb_export(npf_t *npf, nvlist_t *npf_dict)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static nvlist_t *
|
||||
npf_connkey_export(const npf_connkey_t *key)
|
||||
{
|
||||
uint16_t id[2], alen, proto;
|
||||
npf_addr_t ips[2];
|
||||
nvlist_t *kdict;
|
||||
|
||||
kdict = nvlist_create(0);
|
||||
connkey_getkey(key, &proto, ips, id, &alen);
|
||||
nvlist_add_number(kdict, "proto", proto);
|
||||
nvlist_add_number(kdict, "sport", id[NPF_SRC]);
|
||||
nvlist_add_number(kdict, "dport", id[NPF_DST]);
|
||||
nvlist_add_binary(kdict, "saddr", &ips[NPF_SRC], alen);
|
||||
nvlist_add_binary(kdict, "daddr", &ips[NPF_DST], alen);
|
||||
return kdict;
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_conn_export: serialise a single connection.
|
||||
*/
|
||||
static nvlist_t *
|
||||
npf_conn_export(npf_t *npf, const npf_conn_t *con)
|
||||
npf_conn_export(npf_t *npf, npf_conn_t *con)
|
||||
{
|
||||
nvlist_t *cdict, *kdict;
|
||||
npf_connkey_t *fw, *bk;
|
||||
unsigned alen;
|
||||
|
||||
if ((con->c_flags & (CONN_ACTIVE|CONN_EXPIRE)) != CONN_ACTIVE) {
|
||||
return NULL;
|
||||
|
@ -913,36 +792,25 @@ npf_conn_export(npf_t *npf, const npf_conn_t *con)
|
|||
}
|
||||
nvlist_add_binary(cdict, "state", &con->c_state, sizeof(npf_state_t));
|
||||
|
||||
kdict = npf_connkey_export(&con->c_forw_entry);
|
||||
fw = npf_conn_getforwkey(con);
|
||||
alen = NPF_CONNKEY_ALEN(fw);
|
||||
bk = npf_conn_getbackkey(con, alen);
|
||||
|
||||
kdict = npf_connkey_export(fw);
|
||||
nvlist_move_nvlist(cdict, "forw-key", kdict);
|
||||
|
||||
kdict = npf_connkey_export(&con->c_back_entry);
|
||||
kdict = npf_connkey_export(bk);
|
||||
nvlist_move_nvlist(cdict, "back-key", kdict);
|
||||
|
||||
/* Let the address length be based on on first key. */
|
||||
nvlist_add_number(cdict, "alen", alen);
|
||||
|
||||
if (con->c_nat) {
|
||||
npf_nat_export(cdict, con->c_nat);
|
||||
}
|
||||
return cdict;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
npf_connkey_import(const nvlist_t *kdict, npf_connkey_t *key)
|
||||
{
|
||||
npf_addr_t const * ips[2];
|
||||
uint16_t proto, id[2];
|
||||
size_t alen1, alen2;
|
||||
|
||||
proto = dnvlist_get_number(kdict, "proto", 0);
|
||||
id[NPF_SRC] = dnvlist_get_number(kdict, "sport", 0);
|
||||
id[NPF_DST] = dnvlist_get_number(kdict, "dport", 0);
|
||||
ips[NPF_SRC] = dnvlist_get_binary(kdict, "saddr", &alen1, NULL, 0);
|
||||
ips[NPF_DST] = dnvlist_get_binary(kdict, "daddr", &alen2, NULL, 0);
|
||||
if (__predict_false(alen1 == 0 || alen1 != alen2)) {
|
||||
return 0;
|
||||
}
|
||||
return connkey_setkey(key, proto, ips, id, alen1, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_conn_import: fully reconstruct a single connection from a
|
||||
* nvlist and insert into the given database.
|
||||
|
@ -956,10 +824,18 @@ npf_conn_import(npf_t *npf, npf_conndb_t *cd, const nvlist_t *cdict,
|
|||
const nvlist_t *nat, *conkey;
|
||||
const char *ifname;
|
||||
const void *state;
|
||||
unsigned alen, idx;
|
||||
size_t len;
|
||||
|
||||
/*
|
||||
* To determine the length of the connection, which depends
|
||||
* on the address length in the connection keys.
|
||||
*/
|
||||
alen = dnvlist_get_number(cdict, "alen", 0);
|
||||
idx = NPF_CONNCACHE(alen);
|
||||
|
||||
/* Allocate a connection and initialise it (clear first). */
|
||||
con = pool_cache_get(npf->conn_cache, PR_WAITOK);
|
||||
con = pool_cache_get(npf->conn_cache[idx], PR_WAITOK);
|
||||
memset(con, 0, sizeof(npf_conn_t));
|
||||
mutex_init(&con->c_lock, MUTEX_DEFAULT, IPL_SOFTNET);
|
||||
npf_stats_inc(npf, NPF_STAT_CONN_CREATE);
|
||||
|
@ -989,23 +865,27 @@ npf_conn_import(npf_t *npf, npf_conndb_t *cd, const nvlist_t *cdict,
|
|||
/*
|
||||
* Fetch and copy the keys for each direction.
|
||||
*/
|
||||
fw = npf_conn_getforwkey(con);
|
||||
conkey = dnvlist_get_nvlist(cdict, "forw-key", NULL);
|
||||
fw = &con->c_forw_entry;
|
||||
if (conkey == NULL || !npf_connkey_import(conkey, fw)) {
|
||||
goto err;
|
||||
}
|
||||
bk = npf_conn_getbackkey(con, NPF_CONNKEY_ALEN(fw));
|
||||
conkey = dnvlist_get_nvlist(cdict, "back-key", NULL);
|
||||
bk = &con->c_back_entry;
|
||||
if (conkey == NULL || !npf_connkey_import(conkey, bk)) {
|
||||
goto err;
|
||||
}
|
||||
fw->ck_backptr = bk->ck_backptr = con;
|
||||
|
||||
/* Insert the entries and the connection itself. */
|
||||
if (!npf_conndb_insert(cd, fw)) {
|
||||
/* Guard against the contradicting address lengths. */
|
||||
if (NPF_CONNKEY_ALEN(fw) != alen || NPF_CONNKEY_ALEN(bk) != alen) {
|
||||
goto err;
|
||||
}
|
||||
if (!npf_conndb_insert(cd, bk)) {
|
||||
|
||||
/* Insert the entries and the connection itself. */
|
||||
if (!npf_conndb_insert(cd, fw, con, true)) {
|
||||
goto err;
|
||||
}
|
||||
if (!npf_conndb_insert(cd, bk, con, false)) {
|
||||
npf_conndb_remove(cd, fw);
|
||||
goto err;
|
||||
}
|
||||
|
@ -1031,12 +911,12 @@ npf_conn_find(npf_t *npf, const nvlist_t *idict, nvlist_t **odict)
|
|||
if (!kdict || !npf_connkey_import(kdict, &key)) {
|
||||
return EINVAL;
|
||||
}
|
||||
dir = dnvlist_get_number(idict, "direction", 0);
|
||||
con = npf_conndb_lookup(npf->conn_db, &key, &forw);
|
||||
if (con == NULL) {
|
||||
return ESRCH;
|
||||
}
|
||||
if (!npf_conn_ok(con, dir, true)) {
|
||||
dir = dnvlist_get_number(idict, "direction", 0);
|
||||
if (!npf_conn_check(con, NULL, dir, true)) {
|
||||
atomic_dec_uint(&con->c_refcnt);
|
||||
return ESRCH;
|
||||
}
|
||||
|
@ -1048,30 +928,19 @@ npf_conn_find(npf_t *npf, const nvlist_t *idict, nvlist_t **odict)
|
|||
#if defined(DDB) || defined(_NPF_TESTING)
|
||||
|
||||
void
|
||||
npf_conn_print(const npf_conn_t *con)
|
||||
npf_conn_print(npf_conn_t *con)
|
||||
{
|
||||
const u_int alen = NPF_CONN_GETALEN(&con->c_forw_entry);
|
||||
const uint32_t *fkey = con->c_forw_entry.ck_key;
|
||||
const uint32_t *bkey = con->c_back_entry.ck_key;
|
||||
const u_int proto = con->c_proto;
|
||||
const npf_connkey_t *fw = npf_conn_getforwkey(con);
|
||||
const npf_connkey_t *bk = npf_conn_getbackkey(con, NPF_CONNKEY_ALEN(fw));
|
||||
const unsigned proto = con->c_proto;
|
||||
struct timespec tspnow;
|
||||
const void *src, *dst;
|
||||
int etime;
|
||||
|
||||
getnanouptime(&tspnow);
|
||||
etime = npf_state_etime(&con->c_state, proto);
|
||||
|
||||
printf("%p:\n\tproto %d flags 0x%x tsdiff %ld etime %d\n", con,
|
||||
proto, con->c_flags, (long)(tspnow.tv_sec - con->c_atime), etime);
|
||||
|
||||
src = &fkey[2], dst = &fkey[2 + (alen >> 2)];
|
||||
printf("\tforw %s:%d", npf_addr_dump(src, alen), ntohs(fkey[1] >> 16));
|
||||
printf("-> %s:%d\n", npf_addr_dump(dst, alen), ntohs(fkey[1] & 0xffff));
|
||||
|
||||
src = &bkey[2], dst = &bkey[2 + (alen >> 2)];
|
||||
printf("\tback %s:%d", npf_addr_dump(src, alen), ntohs(bkey[1] >> 16));
|
||||
printf("-> %s:%d\n", npf_addr_dump(dst, alen), ntohs(bkey[1] & 0xffff));
|
||||
|
||||
proto, con->c_flags, (long)(tspnow.tv_sec - con->c_atime),
|
||||
npf_state_etime(npf_getkernctx(), &con->c_state, proto));
|
||||
npf_connkey_print(fw);
|
||||
npf_connkey_print(bk);
|
||||
npf_state_dump(&con->c_state);
|
||||
if (con->c_nat) {
|
||||
npf_nat_dump(con->c_nat);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2009-2019 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
|
@ -42,35 +42,21 @@ typedef struct npf_connkey npf_connkey_t;
|
|||
|
||||
#if defined(__NPF_CONN_PRIVATE)
|
||||
|
||||
/*
|
||||
* See npf_conn_conkey() function for the key layout description.
|
||||
*/
|
||||
#define NPF_CONN_NKEYWORDS (2 + ((sizeof(npf_addr_t) * 2) >> 2))
|
||||
#define NPF_CONN_GETALEN(key) ((key)->ck_key[0] & 0xffff)
|
||||
#define NPF_CONN_KEYLEN(key) (8 + (2 * NPF_CONN_GETALEN(key)))
|
||||
|
||||
struct npf_connkey {
|
||||
/*
|
||||
* Back-pointer to the actual connection and the compacted key.
|
||||
*/
|
||||
npf_conn_t * ck_backptr;
|
||||
uint32_t ck_key[NPF_CONN_NKEYWORDS];
|
||||
};
|
||||
|
||||
/*
|
||||
* The main connection tracking structure.
|
||||
*/
|
||||
|
||||
struct npf_conn {
|
||||
/*
|
||||
* Connection "forwards" and "backwards" entries, plus the
|
||||
* interface ID (if zero, then the state is global) and flags.
|
||||
* Protocol, address length, the interface ID (if zero,
|
||||
* then the state is global) and connection flags.
|
||||
*/
|
||||
npf_connkey_t c_forw_entry;
|
||||
npf_connkey_t c_back_entry;
|
||||
u_int c_proto;
|
||||
u_int c_ifid;
|
||||
u_int c_flags;
|
||||
unsigned c_proto;
|
||||
unsigned c_ifid;
|
||||
unsigned c_flags;
|
||||
|
||||
/* Matching rule flags and ID. */
|
||||
unsigned c_retfl;
|
||||
uint64_t c_rid;
|
||||
|
||||
/*
|
||||
* Entry in the connection database/list. The entry is
|
||||
|
@ -86,21 +72,52 @@ struct npf_conn {
|
|||
npf_nat_t * c_nat;
|
||||
|
||||
/*
|
||||
* The protocol state, reference count and the last activity
|
||||
* time (used to calculate expiration time).
|
||||
* The Reference count and the last activity time (used to
|
||||
* calculate expiration time). Note: *unsigned* 32-bit integer
|
||||
* as a timestamp is sufficient for us.
|
||||
*/
|
||||
unsigned c_refcnt;
|
||||
uint32_t c_atime;
|
||||
|
||||
/* The protocol state and lock. */
|
||||
kmutex_t c_lock;
|
||||
npf_state_t c_state;
|
||||
u_int c_refcnt;
|
||||
uint64_t c_atime;
|
||||
|
||||
/*
|
||||
* Save the matching rule ID and flags.
|
||||
* Connection "forwards" and "backwards" keys. They are accessed
|
||||
* as npf_connkey_t, see below and npf_conn_getkey().
|
||||
*/
|
||||
uint64_t c_rid;
|
||||
u_int c_retfl;
|
||||
uint32_t c_keys[];
|
||||
};
|
||||
|
||||
/*
|
||||
* Connection key interface.
|
||||
*
|
||||
* See the key layout description in the npf_connkey.c source file.
|
||||
*/
|
||||
|
||||
#define NPF_CONNKEY_V4WORDS (2 + ((sizeof(struct in_addr) * 2) >> 2))
|
||||
#define NPF_CONNKEY_V6WORDS (2 + ((sizeof(struct in6_addr) * 2) >> 2))
|
||||
#define NPF_CONNKEY_MAXWORDS (NPF_CONNKEY_V6WORDS)
|
||||
|
||||
#define NPF_CONNKEY_ALEN(key) ((key)->ck_key[0] & 0xffff)
|
||||
#define NPF_CONNKEY_LEN(key) (8 + (NPF_CONNKEY_ALEN(key) * 2))
|
||||
|
||||
struct npf_connkey {
|
||||
/* Warning: ck_key has a variable length -- see above. */
|
||||
uint32_t ck_key[NPF_CONNKEY_MAXWORDS];
|
||||
};
|
||||
|
||||
unsigned npf_conn_conkey(const npf_cache_t *, npf_connkey_t *, bool);
|
||||
npf_connkey_t * npf_conn_getforwkey(npf_conn_t *);
|
||||
npf_connkey_t * npf_conn_getbackkey(npf_conn_t *, unsigned);
|
||||
void npf_conn_adjkey(npf_connkey_t *, const npf_addr_t *,
|
||||
const uint16_t, const int);
|
||||
|
||||
unsigned npf_connkey_import(const nvlist_t *, npf_connkey_t *);
|
||||
nvlist_t * npf_connkey_export(const npf_connkey_t *);
|
||||
void npf_connkey_print(const npf_connkey_t *);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -111,7 +128,6 @@ void npf_conn_fini(npf_t *);
|
|||
void npf_conn_tracking(npf_t *, bool);
|
||||
void npf_conn_load(npf_t *, npf_conndb_t *, bool);
|
||||
|
||||
unsigned npf_conn_conkey(const npf_cache_t *, npf_connkey_t *, bool);
|
||||
npf_conn_t * npf_conn_lookup(const npf_cache_t *, const int, bool *);
|
||||
npf_conn_t * npf_conn_inspect(npf_cache_t *, const int, int *);
|
||||
npf_conn_t * npf_conn_establish(npf_cache_t *, int, bool);
|
||||
|
@ -125,22 +141,26 @@ void npf_conn_setpass(npf_conn_t *, const npf_match_info_t *,
|
|||
int npf_conn_setnat(const npf_cache_t *, npf_conn_t *,
|
||||
npf_nat_t *, u_int);
|
||||
npf_nat_t * npf_conn_getnat(npf_conn_t *, const int, bool *);
|
||||
bool npf_conn_expired(const npf_conn_t *, uint64_t);
|
||||
bool npf_conn_expired(npf_t *, const npf_conn_t *, uint64_t);
|
||||
void npf_conn_remove(npf_conndb_t *, npf_conn_t *);
|
||||
void npf_conn_worker(npf_t *);
|
||||
int npf_conn_import(npf_t *, npf_conndb_t *, const nvlist_t *,
|
||||
npf_ruleset_t *);
|
||||
int npf_conn_find(npf_t *, const nvlist_t *, nvlist_t **);
|
||||
void npf_conn_print(const npf_conn_t *);
|
||||
void npf_conn_print(npf_conn_t *);
|
||||
|
||||
/*
|
||||
* Connection database (aka state table) interface.
|
||||
*/
|
||||
void npf_conndb_sysinit(npf_t *);
|
||||
void npf_conndb_sysfini(npf_t *);
|
||||
|
||||
npf_conndb_t * npf_conndb_create(void);
|
||||
void npf_conndb_destroy(npf_conndb_t *);
|
||||
|
||||
npf_conn_t * npf_conndb_lookup(npf_conndb_t *, const npf_connkey_t *, bool *);
|
||||
bool npf_conndb_insert(npf_conndb_t *, npf_connkey_t *);
|
||||
bool npf_conndb_insert(npf_conndb_t *, const npf_connkey_t *,
|
||||
npf_conn_t *, bool);
|
||||
npf_conn_t * npf_conndb_remove(npf_conndb_t *, npf_connkey_t *);
|
||||
|
||||
void npf_conndb_enqueue(npf_conndb_t *, npf_conn_t *);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2010-2018 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2010-2019 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
|
@ -29,11 +29,24 @@
|
|||
|
||||
/*
|
||||
* NPF connection storage.
|
||||
*
|
||||
* Warning (not applicable for the userspace npfkern):
|
||||
*
|
||||
* thmap is partially lock-free data structure that uses its own
|
||||
* spin-locks on the writer side (insert/delete operations).
|
||||
*
|
||||
* The relevant interrupt priority level (IPL) must be set and the
|
||||
* kernel preemption disabled across the critical paths to prevent
|
||||
* deadlocks and priority inversion problems. These are essentially
|
||||
* the same guarantees as a spinning mutex(9) would provide.
|
||||
*
|
||||
* This is achieved with SPL routines splsoftnet() and splx() around
|
||||
* the thmap_del() and thmap_put() calls.
|
||||
*/
|
||||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_conndb.c,v 1.5 2019/01/19 21:19:31 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_conndb.c,v 1.6 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -47,8 +60,6 @@ __KERNEL_RCSID(0, "$NetBSD: npf_conndb.c,v 1.5 2019/01/19 21:19:31 rmind Exp $")
|
|||
#include "npf_conn.h"
|
||||
#include "npf_impl.h"
|
||||
|
||||
#define NPF_GC_STEP 256
|
||||
|
||||
struct npf_conndb {
|
||||
thmap_t * cd_map;
|
||||
|
||||
|
@ -67,6 +78,41 @@ struct npf_conndb {
|
|||
npf_conn_t * cd_marker;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int gc_step;
|
||||
int _reserved0;
|
||||
} npf_conndb_params_t;
|
||||
|
||||
/*
|
||||
* Pointer tag for connection keys which represent the "forwards" entry.
|
||||
*/
|
||||
#define CONNDB_FORW_BIT ((uintptr_t)0x1)
|
||||
#define CONNDB_ISFORW_P(p) (((uintptr_t)(p) & CONNDB_FORW_BIT) != 0)
|
||||
#define CONNDB_GET_PTR(p) ((void *)((uintptr_t)(p) & ~CONNDB_FORW_BIT))
|
||||
|
||||
void
|
||||
npf_conndb_sysinit(npf_t *npf)
|
||||
{
|
||||
npf_conndb_params_t *params = npf_param_allocgroup(npf,
|
||||
NPF_PARAMS_CONNDB, sizeof(npf_conndb_params_t));
|
||||
npf_param_t param_map[] = {
|
||||
{
|
||||
"gc.step",
|
||||
¶ms->gc_step,
|
||||
.default_val = 256,
|
||||
.min = 1, .max = INT_MAX
|
||||
}
|
||||
};
|
||||
npf_param_register(npf, param_map, __arraycount(param_map));
|
||||
}
|
||||
|
||||
void
|
||||
npf_conndb_sysfini(npf_t *npf)
|
||||
{
|
||||
const size_t len = sizeof(npf_conndb_params_t);
|
||||
npf_param_freegroup(npf, NPF_PARAMS_CONNDB, len);
|
||||
}
|
||||
|
||||
npf_conndb_t *
|
||||
npf_conndb_create(void)
|
||||
{
|
||||
|
@ -99,25 +145,30 @@ npf_conndb_destroy(npf_conndb_t *cd)
|
|||
npf_conn_t *
|
||||
npf_conndb_lookup(npf_conndb_t *cd, const npf_connkey_t *ck, bool *forw)
|
||||
{
|
||||
const unsigned keylen = NPF_CONN_KEYLEN(ck);
|
||||
npf_connkey_t *foundkey;
|
||||
const unsigned keylen = NPF_CONNKEY_LEN(ck);
|
||||
npf_conn_t *con;
|
||||
void *val;
|
||||
|
||||
/*
|
||||
* Lookup the connection key in the key-value map.
|
||||
*/
|
||||
foundkey = thmap_get(cd->cd_map, ck->ck_key, keylen);
|
||||
if (!foundkey) {
|
||||
val = thmap_get(cd->cd_map, ck->ck_key, keylen);
|
||||
if (!val) {
|
||||
return NULL;
|
||||
}
|
||||
con = foundkey->ck_backptr;
|
||||
|
||||
/*
|
||||
* Determine whether this is the "forwards" or "backwards" key
|
||||
* and clear the pointer tag.
|
||||
*/
|
||||
*forw = CONNDB_ISFORW_P(val);
|
||||
con = CONNDB_GET_PTR(val);
|
||||
KASSERT(con != NULL);
|
||||
|
||||
/*
|
||||
* Acquire a reference and return the connection.
|
||||
*/
|
||||
atomic_inc_uint(&con->c_refcnt);
|
||||
*forw = (foundkey == &con->c_forw_entry);
|
||||
return con;
|
||||
}
|
||||
|
||||
|
@ -127,10 +178,25 @@ npf_conndb_lookup(npf_conndb_t *cd, const npf_connkey_t *ck, bool *forw)
|
|||
* => Returns true on success and false on failure.
|
||||
*/
|
||||
bool
|
||||
npf_conndb_insert(npf_conndb_t *cd, npf_connkey_t *ck)
|
||||
npf_conndb_insert(npf_conndb_t *cd, const npf_connkey_t *ck,
|
||||
npf_conn_t *con, bool forw)
|
||||
{
|
||||
const unsigned keylen = NPF_CONN_KEYLEN(ck);
|
||||
return thmap_put(cd->cd_map, ck->ck_key, keylen, ck) == ck;
|
||||
const unsigned keylen = NPF_CONNKEY_LEN(ck);
|
||||
const uintptr_t tag = (CONNDB_FORW_BIT * !!forw);
|
||||
void *val;
|
||||
bool ok;
|
||||
|
||||
/*
|
||||
* Tag the connection pointer if this is the "forwards" key.
|
||||
*/
|
||||
KASSERT(!CONNDB_ISFORW_P(con));
|
||||
val = (void *)((uintptr_t)(void *)con | tag);
|
||||
|
||||
int s = splsoftnet();
|
||||
ok = thmap_put(cd->cd_map, ck->ck_key, keylen, val) == val;
|
||||
splx(s);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -140,17 +206,14 @@ npf_conndb_insert(npf_conndb_t *cd, npf_connkey_t *ck)
|
|||
npf_conn_t *
|
||||
npf_conndb_remove(npf_conndb_t *cd, npf_connkey_t *ck)
|
||||
{
|
||||
const unsigned keylen = NPF_CONN_KEYLEN(ck);
|
||||
npf_connkey_t *foundkey;
|
||||
npf_conn_t *con;
|
||||
const unsigned keylen = NPF_CONNKEY_LEN(ck);
|
||||
void *val;
|
||||
|
||||
foundkey = thmap_del(cd->cd_map, ck->ck_key, keylen);
|
||||
if (!foundkey) {
|
||||
return NULL;
|
||||
}
|
||||
con = foundkey->ck_backptr;
|
||||
KASSERT(con != NULL);
|
||||
return con;
|
||||
int s = splsoftnet();
|
||||
val = thmap_del(cd->cd_map, ck->ck_key, keylen);
|
||||
splx(s);
|
||||
|
||||
return CONNDB_GET_PTR(val);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -214,9 +277,10 @@ npf_conndb_getnext(npf_conndb_t *cd, npf_conn_t *con)
|
|||
* npf_conndb_gc_incr: incremental G/C of the expired connections.
|
||||
*/
|
||||
static void
|
||||
npf_conndb_gc_incr(npf_conndb_t *cd, const time_t now)
|
||||
npf_conndb_gc_incr(npf_t *npf, npf_conndb_t *cd, const time_t now)
|
||||
{
|
||||
unsigned target = NPF_GC_STEP;
|
||||
const npf_conndb_params_t *params = npf->params[NPF_PARAMS_CONNDB];
|
||||
unsigned target = params->gc_step;
|
||||
npf_conn_t *con;
|
||||
|
||||
/*
|
||||
|
@ -240,7 +304,7 @@ npf_conndb_gc_incr(npf_conndb_t *cd, const time_t now)
|
|||
/*
|
||||
* Can we G/C this connection?
|
||||
*/
|
||||
if (npf_conn_expired(con, now)) {
|
||||
if (npf_conn_expired(npf, con, now)) {
|
||||
/* Yes: move to the G/C list. */
|
||||
LIST_REMOVE(con, c_entry);
|
||||
LIST_INSERT_HEAD(&cd->cd_gclist, con, c_entry);
|
||||
|
@ -298,7 +362,7 @@ npf_conndb_gc(npf_t *npf, npf_conndb_t *cd, bool flush, bool sync)
|
|||
cd->cd_marker = NULL;
|
||||
} else {
|
||||
/* Incremental G/C of the expired connections. */
|
||||
npf_conndb_gc_incr(cd, tsnow.tv_sec);
|
||||
npf_conndb_gc_incr(npf, cd, tsnow.tv_sec);
|
||||
}
|
||||
mutex_exit(&npf->conn_lock);
|
||||
|
||||
|
|
|
@ -0,0 +1,274 @@
|
|||
/*-
|
||||
* Copyright (c) 2014-2019 Mindaugas Rasiukevicius <rmind at netbsd org>
|
||||
* Copyright (c) 2010-2014 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
* NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Connection key -- is a structure encoding the 5-tuple (protocol, address
|
||||
* length, source/destination address and source/destination port/ID).
|
||||
*
|
||||
* Key layout
|
||||
*
|
||||
* The single key is formed out of 32-bit integers. The layout is:
|
||||
*
|
||||
* Field: | proto | alen | src-id | dst-id | src-addr | dst-addr |
|
||||
* +--------+--------+--------+--------+----------+----------+
|
||||
* Bits: | 16 | 16 | 16 | 16 | 32-128 | 32-128 |
|
||||
*
|
||||
* The source and destination are inverted if the key is for the
|
||||
* backwards stream (forw == false). The address length depends on
|
||||
* the 'alen' field. The length is in bytes and is either 4 or 16.
|
||||
*
|
||||
* Warning: the keys must be immutable while they are in conndb.
|
||||
*
|
||||
* Embedding in the connection structure (npf_conn_t)
|
||||
*
|
||||
* Two keys are stored in the npf_conn_t::c_keys[] array, which is
|
||||
* variable-length, depending on whether the keys store IPv4 or IPv6
|
||||
* addresses. The length of the first key determines the position
|
||||
* of the second key.
|
||||
*/
|
||||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_connkey.c,v 1.1 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#define __NPF_CONN_PRIVATE
|
||||
#include "npf_conn.h"
|
||||
#include "npf_impl.h"
|
||||
|
||||
static inline unsigned
|
||||
connkey_setkey(npf_connkey_t *key, uint16_t proto, const void *ipv,
|
||||
const uint16_t *id, unsigned alen, bool forw)
|
||||
{
|
||||
const npf_addr_t * const *ips = ipv;
|
||||
uint32_t *k = key->ck_key;
|
||||
unsigned isrc, idst;
|
||||
|
||||
if (__predict_true(forw)) {
|
||||
isrc = NPF_SRC, idst = NPF_DST;
|
||||
} else {
|
||||
isrc = NPF_DST, idst = NPF_SRC;
|
||||
}
|
||||
|
||||
/*
|
||||
* See the key layout explanation above.
|
||||
*/
|
||||
|
||||
k[0] = ((uint32_t)proto << 16) | (alen & 0xffff);
|
||||
k[1] = ((uint32_t)id[isrc] << 16) | id[idst];
|
||||
|
||||
if (__predict_true(alen == sizeof(in_addr_t))) {
|
||||
k[2] = ips[isrc]->word32[0];
|
||||
k[3] = ips[idst]->word32[0];
|
||||
return 4 * sizeof(uint32_t);
|
||||
} else {
|
||||
const unsigned nwords = alen >> 2;
|
||||
memcpy(&k[2], ips[isrc], alen);
|
||||
memcpy(&k[2 + nwords], ips[idst], alen);
|
||||
return (2 + (nwords * 2)) * sizeof(uint32_t);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
connkey_getkey(const npf_connkey_t *key, uint16_t *proto, npf_addr_t *ips,
|
||||
uint16_t *id, uint16_t *alen)
|
||||
{
|
||||
const uint32_t *k = key->ck_key;
|
||||
|
||||
/*
|
||||
* See the key layout explanation above.
|
||||
*/
|
||||
|
||||
*proto = k[0] >> 16;
|
||||
*alen = k[0] & 0xffff;
|
||||
id[NPF_SRC] = k[1] >> 16;
|
||||
id[NPF_DST] = k[1] & 0xffff;
|
||||
|
||||
switch (*alen) {
|
||||
case sizeof(struct in6_addr):
|
||||
case sizeof(struct in_addr):
|
||||
memcpy(&ips[NPF_SRC], &k[2], *alen);
|
||||
memcpy(&ips[NPF_DST], &k[2 + ((unsigned)*alen >> 2)], *alen);
|
||||
return;
|
||||
default:
|
||||
KASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_conn_adjkey: adjust the connection key by resetting the address/port.
|
||||
*/
|
||||
void
|
||||
npf_conn_adjkey(npf_connkey_t *key, const npf_addr_t *naddr,
|
||||
const uint16_t id, const int di)
|
||||
{
|
||||
uint32_t * const k = key->ck_key;
|
||||
const unsigned alen = k[0] & 0xffff;
|
||||
uint32_t *addr = &k[2 + ((alen >> 2) * di)];
|
||||
|
||||
KASSERT(alen > 0);
|
||||
memcpy(addr, naddr, alen);
|
||||
|
||||
if (id) {
|
||||
const uint32_t oid = k[1];
|
||||
const unsigned shift = 16 * !di;
|
||||
const uint32_t mask = 0xffff0000 >> shift;
|
||||
k[1] = ((uint32_t)id << shift) | (oid & mask);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_conn_conkey: construct a key for the connection lookup.
|
||||
*
|
||||
* => Returns the key length in bytes or zero on failure.
|
||||
*/
|
||||
unsigned
|
||||
npf_conn_conkey(const npf_cache_t *npc, npf_connkey_t *key, const bool forw)
|
||||
{
|
||||
const unsigned proto = npc->npc_proto;
|
||||
const unsigned alen = npc->npc_alen;
|
||||
const struct tcphdr *th;
|
||||
const struct udphdr *uh;
|
||||
uint16_t id[2] = { 0, 0 };
|
||||
|
||||
switch (proto) {
|
||||
case IPPROTO_TCP:
|
||||
KASSERT(npf_iscached(npc, NPC_TCP));
|
||||
th = npc->npc_l4.tcp;
|
||||
id[NPF_SRC] = th->th_sport;
|
||||
id[NPF_DST] = th->th_dport;
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
KASSERT(npf_iscached(npc, NPC_UDP));
|
||||
uh = npc->npc_l4.udp;
|
||||
id[NPF_SRC] = uh->uh_sport;
|
||||
id[NPF_DST] = uh->uh_dport;
|
||||
break;
|
||||
case IPPROTO_ICMP:
|
||||
if (npf_iscached(npc, NPC_ICMP_ID)) {
|
||||
const struct icmp *ic = npc->npc_l4.icmp;
|
||||
id[NPF_SRC] = ic->icmp_id;
|
||||
id[NPF_DST] = ic->icmp_id;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
case IPPROTO_ICMPV6:
|
||||
if (npf_iscached(npc, NPC_ICMP_ID)) {
|
||||
const struct icmp6_hdr *ic6 = npc->npc_l4.icmp6;
|
||||
id[NPF_SRC] = ic6->icmp6_id;
|
||||
id[NPF_DST] = ic6->icmp6_id;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
/* Unsupported protocol. */
|
||||
return 0;
|
||||
}
|
||||
return connkey_setkey(key, proto, npc->npc_ips, id, alen, forw);
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_conn_getforwkey: get the address to the "forwards" key.
|
||||
*/
|
||||
npf_connkey_t *
|
||||
npf_conn_getforwkey(npf_conn_t *conn)
|
||||
{
|
||||
return (void *)&conn->c_keys[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_conn_getbackkey: get the address to the "backwards" key.
|
||||
*
|
||||
* => It depends on the address length.
|
||||
*/
|
||||
npf_connkey_t *
|
||||
npf_conn_getbackkey(npf_conn_t *conn, unsigned alen)
|
||||
{
|
||||
const unsigned off = 2 + ((alen * 2) >> 2);
|
||||
KASSERT(off == NPF_CONNKEY_V4WORDS || off == NPF_CONNKEY_V6WORDS);
|
||||
return (void *)&conn->c_keys[off];
|
||||
}
|
||||
|
||||
/*
|
||||
* Connection key exporting/importing.
|
||||
*/
|
||||
|
||||
nvlist_t *
|
||||
npf_connkey_export(const npf_connkey_t *key)
|
||||
{
|
||||
uint16_t ids[2], alen, proto;
|
||||
npf_addr_t ips[2];
|
||||
nvlist_t *kdict;
|
||||
|
||||
kdict = nvlist_create(0);
|
||||
connkey_getkey(key, &proto, ips, ids, &alen);
|
||||
nvlist_add_number(kdict, "proto", proto);
|
||||
nvlist_add_number(kdict, "sport", ids[NPF_SRC]);
|
||||
nvlist_add_number(kdict, "dport", ids[NPF_DST]);
|
||||
nvlist_add_binary(kdict, "saddr", &ips[NPF_SRC], alen);
|
||||
nvlist_add_binary(kdict, "daddr", &ips[NPF_DST], alen);
|
||||
return kdict;
|
||||
}
|
||||
|
||||
unsigned
|
||||
npf_connkey_import(const nvlist_t *kdict, npf_connkey_t *key)
|
||||
{
|
||||
npf_addr_t const * ips[2];
|
||||
uint16_t proto, ids[2];
|
||||
size_t alen1, alen2;
|
||||
|
||||
proto = dnvlist_get_number(kdict, "proto", 0);
|
||||
ids[NPF_SRC] = dnvlist_get_number(kdict, "sport", 0);
|
||||
ids[NPF_DST] = dnvlist_get_number(kdict, "dport", 0);
|
||||
ips[NPF_SRC] = dnvlist_get_binary(kdict, "saddr", &alen1, NULL, 0);
|
||||
ips[NPF_DST] = dnvlist_get_binary(kdict, "daddr", &alen2, NULL, 0);
|
||||
if (alen1 == 0 || alen1 > sizeof(npf_addr_t) || alen1 != alen2) {
|
||||
return 0;
|
||||
}
|
||||
return connkey_setkey(key, proto, ips, ids, alen1, true);
|
||||
}
|
||||
|
||||
#if defined(DDB) || defined(_NPF_TESTING)
|
||||
|
||||
void
|
||||
npf_connkey_print(const npf_connkey_t *key)
|
||||
{
|
||||
uint16_t proto, ids[2], alen;
|
||||
npf_addr_t ips[2];
|
||||
|
||||
connkey_getkey(key, &proto, ips, ids, &alen);
|
||||
printf("\tforw %s:%d", npf_addr_dump(&ips[0], alen), ids[0]);
|
||||
printf("-> %s:%d\n", npf_addr_dump(&ips[1], alen), ids[1]);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2009-2018 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2009-2019 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
|
@ -36,7 +36,7 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.53 2019/01/19 21:19:31 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.54 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/conf.h>
|
||||
|
@ -100,6 +100,50 @@ npf_nvlist_copyout(npf_t *npf, void *data, nvlist_t *nvl)
|
|||
return error;
|
||||
}
|
||||
|
||||
static int __noinline
|
||||
npf_mk_params(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict, bool set)
|
||||
{
|
||||
const nvlist_t *params;
|
||||
int type, error, val;
|
||||
const char *name;
|
||||
void *cookie;
|
||||
|
||||
params = dnvlist_get_nvlist(npf_dict, "params", NULL);
|
||||
if (params == NULL) {
|
||||
return 0;
|
||||
}
|
||||
cookie = NULL;
|
||||
while ((name = nvlist_next(params, &type, &cookie)) != NULL) {
|
||||
if (type != NV_TYPE_NUMBER) {
|
||||
NPF_ERR_DEBUG(errdict);
|
||||
return EINVAL;
|
||||
}
|
||||
val = (int)nvlist_get_number(params, name);
|
||||
if (set) {
|
||||
/* Actually set the parameter. */
|
||||
error = npf_param_set(npf, name, val);
|
||||
KASSERT(error == 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Validate the parameter and its value. */
|
||||
error = npf_param_check(npf, name, val);
|
||||
if (__predict_true(error == 0)) {
|
||||
continue;
|
||||
}
|
||||
if (error == ENOENT) {
|
||||
nvlist_add_stringf(errdict, "error-msg",
|
||||
"invalid parameter `%s`", name);
|
||||
}
|
||||
if (error == EINVAL) {
|
||||
nvlist_add_stringf(errdict, "error-msg",
|
||||
"invalid parameter `%s` value %d", name, val);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __noinline
|
||||
npf_mk_table_entries(npf_table_t *t, const nvlist_t *table, nvlist_t *errdict)
|
||||
{
|
||||
|
@ -409,8 +453,58 @@ npf_mk_rules(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict,
|
|||
}
|
||||
|
||||
static int __noinline
|
||||
npf_mk_natlist(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict,
|
||||
npf_tableset_t *tblset, npf_ruleset_t **ntsetp)
|
||||
npf_mk_singlenat(npf_t *npf, const nvlist_t *nat, npf_ruleset_t *ntset,
|
||||
npf_tableset_t *tblset, nvlist_t *errdict, npf_rule_t **rlp)
|
||||
{
|
||||
npf_rule_t *rl = NULL;
|
||||
npf_natpolicy_t *np;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* NAT rules are standard rules, plus the translation policy.
|
||||
* We first construct the rule structure.
|
||||
*/
|
||||
error = npf_mk_singlerule(npf, nat, NULL, &rl, errdict);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
KASSERT(rl != NULL);
|
||||
*rlp = rl;
|
||||
|
||||
/* If rule is named, it is a group with NAT policies. */
|
||||
if (dnvlist_get_string(nat, "name", NULL)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check the table ID. */
|
||||
if (nvlist_exists_number(nat, "nat-table-id")) {
|
||||
unsigned tid = nvlist_get_number(nat, "nat-table-id");
|
||||
|
||||
if (!npf_tableset_getbyid(tblset, tid)) {
|
||||
NPF_ERR_DEBUG(errdict);
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate a new NAT policy and assign it to the rule. */
|
||||
np = npf_nat_newpolicy(npf, nat, ntset);
|
||||
if (np == NULL) {
|
||||
NPF_ERR_DEBUG(errdict);
|
||||
error = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
npf_rule_setnat(rl, np);
|
||||
out:
|
||||
if (error) {
|
||||
npf_rule_free(rl);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __noinline
|
||||
npf_mk_natlist(npf_t *npf, nvlist_t *npf_dict, npf_tableset_t *tblset,
|
||||
nvlist_t *errdict, npf_ruleset_t **ntsetp)
|
||||
{
|
||||
const nvlist_t * const *nat_rules;
|
||||
npf_ruleset_t *ntset;
|
||||
|
@ -434,42 +528,12 @@ npf_mk_natlist(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict,
|
|||
for (unsigned i = 0; i < nitems; i++) {
|
||||
const nvlist_t *nat = nat_rules[i];
|
||||
npf_rule_t *rl = NULL;
|
||||
npf_natpolicy_t *np;
|
||||
|
||||
/*
|
||||
* NAT policies are standard rules, plus the additional
|
||||
* translation information. First, make a rule.
|
||||
*/
|
||||
error = npf_mk_singlerule(npf, nat, NULL, &rl, errdict);
|
||||
error = npf_mk_singlenat(npf, nat, ntset, tblset, errdict, &rl);
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
npf_ruleset_insert(ntset, rl);
|
||||
|
||||
/* If rule is named, it is a group with NAT policies. */
|
||||
if (dnvlist_get_string(nat, "name", NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check the table ID. */
|
||||
if (nvlist_exists_number(nat, "nat-table-id")) {
|
||||
unsigned tid = nvlist_get_number(nat, "nat-table-id");
|
||||
|
||||
if (!npf_tableset_getbyid(tblset, tid)) {
|
||||
NPF_ERR_DEBUG(errdict);
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate a new NAT policy and assign to the rule. */
|
||||
np = npf_nat_newpolicy(npf, nat, ntset);
|
||||
if (np == NULL) {
|
||||
NPF_ERR_DEBUG(errdict);
|
||||
error = ENOMEM;
|
||||
break;
|
||||
}
|
||||
npf_rule_setnat(rl, np);
|
||||
}
|
||||
*ntsetp = ntset;
|
||||
return error;
|
||||
|
@ -533,6 +597,10 @@ npfctl_load_nvlist(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict)
|
|||
error = EPROGMISMATCH;
|
||||
goto fail;
|
||||
}
|
||||
error = npf_mk_params(npf, npf_dict, errdict, false /* validate */);
|
||||
if (error) {
|
||||
goto fail;
|
||||
}
|
||||
error = npf_mk_algs(npf, npf_dict, errdict);
|
||||
if (error) {
|
||||
goto fail;
|
||||
|
@ -545,7 +613,7 @@ npfctl_load_nvlist(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict)
|
|||
if (error) {
|
||||
goto fail;
|
||||
}
|
||||
error = npf_mk_natlist(npf, npf_dict, errdict, tblset, &ntset);
|
||||
error = npf_mk_natlist(npf, npf_dict, tblset, errdict, &ntset);
|
||||
if (error) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -563,6 +631,7 @@ npfctl_load_nvlist(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict)
|
|||
*/
|
||||
flush = dnvlist_get_bool(npf_dict, "flush", false);
|
||||
npf_config_load(npf, rlset, tblset, ntset, rpset, conndb, flush);
|
||||
npf_mk_params(npf, npf_dict, errdict, true /* set the params */);
|
||||
|
||||
/* Done. Since data is consumed now, we shall not destroy it. */
|
||||
tblset = NULL;
|
||||
|
@ -698,31 +767,42 @@ npfctl_rule(npf_t *npf, u_long cmd, void *data)
|
|||
const char *ruleset_name;
|
||||
uint32_t rcmd;
|
||||
int error = 0;
|
||||
bool natset;
|
||||
|
||||
error = npf_nvlist_copyin(npf, data, &npf_rule);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
rcmd = dnvlist_get_number(npf_rule, "command", 0);
|
||||
natset = dnvlist_get_bool(npf_rule, "nat-rule", false);
|
||||
ruleset_name = dnvlist_get_string(npf_rule, "ruleset-name", NULL);
|
||||
if (!ruleset_name) {
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rcmd == NPF_CMD_RULE_ADD) {
|
||||
retdict = nvlist_create(0);
|
||||
error = npf_mk_singlerule(npf, npf_rule, NULL, &rl, retdict);
|
||||
if (error) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
npf_config_enter(npf);
|
||||
rlset = npf_config_ruleset(npf);
|
||||
|
||||
rlset = natset ? npf_config_natset(npf) : npf_config_ruleset(npf);
|
||||
switch (rcmd) {
|
||||
case NPF_CMD_RULE_ADD: {
|
||||
retdict = nvlist_create(0);
|
||||
if (natset) {
|
||||
/*
|
||||
* Translation rule.
|
||||
*/
|
||||
error = npf_mk_singlenat(npf, npf_rule, rlset,
|
||||
npf_config_tableset(npf), retdict, &rl);
|
||||
} else {
|
||||
/*
|
||||
* Standard rule.
|
||||
*/
|
||||
error = npf_mk_singlerule(npf, npf_rule, NULL,
|
||||
&rl, retdict);
|
||||
}
|
||||
if (error) {
|
||||
npf_config_exit(npf);
|
||||
goto out;
|
||||
}
|
||||
if ((error = npf_ruleset_add(rlset, ruleset_name, rl)) == 0) {
|
||||
/* Success. */
|
||||
uint64_t id = npf_rule_getid(rl);
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.45 2018/09/29 14:41:36 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.46 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
|
@ -129,8 +129,6 @@ npf_packet_handler(npf_t *npf, struct mbuf **mp, ifnet_t *ifp, int di)
|
|||
npf_match_info_t mi;
|
||||
bool mff;
|
||||
|
||||
/* QSBR checkpoint. */
|
||||
pserialize_checkpoint(npf->qsbr);
|
||||
KASSERT(ifp != NULL);
|
||||
|
||||
/*
|
||||
|
@ -234,7 +232,7 @@ npf_packet_handler(npf_t *npf, struct mbuf **mp, ifnet_t *ifp, int di)
|
|||
*/
|
||||
if ((mi.mi_retfl & NPF_RULE_STATEFUL) != 0 && !con) {
|
||||
con = npf_conn_establish(&npc, di,
|
||||
(mi.mi_retfl & NPF_RULE_MULTIENDS) == 0);
|
||||
(mi.mi_retfl & NPF_RULE_GSTATEFUL) == 0);
|
||||
if (con) {
|
||||
/*
|
||||
* Note: the reference on the rule procedure is
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2009-2019 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
|
@ -72,12 +72,14 @@
|
|||
struct npf_ruleset;
|
||||
struct npf_rule;
|
||||
struct npf_rprocset;
|
||||
struct npf_portmap;
|
||||
struct npf_nat;
|
||||
struct npf_conn;
|
||||
struct npf_config;
|
||||
|
||||
typedef struct npf_ruleset npf_ruleset_t;
|
||||
typedef struct npf_rule npf_rule_t;
|
||||
typedef struct npf_portmap npf_portmap_t;
|
||||
typedef struct npf_nat npf_nat_t;
|
||||
typedef struct npf_rprocset npf_rprocset_t;
|
||||
typedef struct npf_alg npf_alg_t;
|
||||
|
@ -134,7 +136,7 @@ typedef struct {
|
|||
} npf_tcpstate_t;
|
||||
|
||||
typedef struct {
|
||||
u_int nst_state;
|
||||
unsigned nst_state;
|
||||
npf_tcpstate_t nst_tcpst[2];
|
||||
} npf_state_t;
|
||||
|
||||
|
@ -162,6 +164,31 @@ struct nbuf {
|
|||
const npf_mbufops_t *nb_mops;
|
||||
};
|
||||
|
||||
/*
|
||||
* PARAMS.
|
||||
*/
|
||||
|
||||
typedef struct npf_paraminfo npf_paraminfo_t;
|
||||
|
||||
typedef struct {
|
||||
const char * name;
|
||||
int * valp;
|
||||
int default_val;
|
||||
/*
|
||||
* Minimum and maximum allowed values (inclusive).
|
||||
*/
|
||||
int min;
|
||||
int max;
|
||||
} npf_param_t;
|
||||
|
||||
typedef enum {
|
||||
NPF_PARAMS_CONNDB = 0,
|
||||
NPF_PARAMS_GENERIC_STATE,
|
||||
NPF_PARAMS_TCP_STATE,
|
||||
NPF_PARAMS_PORTMAP,
|
||||
NPF_PARAMS_COUNT
|
||||
} npf_paramgroup_t;
|
||||
|
||||
/*
|
||||
* NPF INSTANCE (CONTEXT) STRUCTURE AND AUXILIARY OPERATIONS.
|
||||
*/
|
||||
|
@ -176,16 +203,22 @@ struct npf {
|
|||
bpf_ctx_t * bpfctx;
|
||||
const npf_mbufops_t * mbufops;
|
||||
|
||||
/* Parameters. */
|
||||
npf_paraminfo_t * paraminfo;
|
||||
void * params[NPF_PARAMS_COUNT];
|
||||
|
||||
/*
|
||||
* Connection tracking state: disabled (off) or enabled (on).
|
||||
* Connection tracking database, connection cache and the lock.
|
||||
* There are two caches (pools): for IPv4 and IPv6.
|
||||
*/
|
||||
volatile int conn_tracking;
|
||||
kmutex_t conn_lock;
|
||||
npf_conndb_t * conn_db;
|
||||
pool_cache_t conn_cache;
|
||||
pool_cache_t conn_cache[2];
|
||||
|
||||
/* ALGs. */
|
||||
/* NAT and ALGs. */
|
||||
npf_portmap_t * portmap;
|
||||
npf_algset_t * algset;
|
||||
|
||||
/* Interface mapping. */
|
||||
|
@ -206,7 +239,6 @@ struct npf {
|
|||
percpu_t * stats_percpu;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* NPF extensions and rule procedure interface.
|
||||
*/
|
||||
|
@ -266,6 +298,13 @@ int npfctl_table(npf_t *, void *);
|
|||
void npf_stats_inc(npf_t *, npf_stats_t);
|
||||
void npf_stats_dec(npf_t *, npf_stats_t);
|
||||
|
||||
void npf_param_init(npf_t *);
|
||||
void npf_param_fini(npf_t *);
|
||||
void npf_param_register(npf_t *, npf_param_t *, unsigned);
|
||||
void * npf_param_allocgroup(npf_t *, npf_paramgroup_t, size_t);
|
||||
void npf_param_freegroup(npf_t *, npf_paramgroup_t, size_t);
|
||||
int npf_param_check(npf_t *, const char *, int);
|
||||
|
||||
void npf_ifmap_init(npf_t *, const npf_ifops_t *);
|
||||
void npf_ifmap_fini(npf_t *);
|
||||
u_int npf_ifmap_register(npf_t *, const char *);
|
||||
|
@ -307,6 +346,7 @@ void npf_addr_mask(const npf_addr_t *, const npf_netmask_t,
|
|||
const int, npf_addr_t *);
|
||||
void npf_addr_bitor(const npf_addr_t *, const npf_netmask_t,
|
||||
const int, npf_addr_t *);
|
||||
int npf_netmask_check(const int, npf_netmask_t);
|
||||
|
||||
int npf_tcpsaw(const npf_cache_t *, tcp_seq *, tcp_seq *,
|
||||
uint32_t *);
|
||||
|
@ -357,7 +397,6 @@ void npf_ruleset_destroy(npf_ruleset_t *);
|
|||
void npf_ruleset_insert(npf_ruleset_t *, npf_rule_t *);
|
||||
void npf_ruleset_reload(npf_t *, npf_ruleset_t *,
|
||||
npf_ruleset_t *, bool);
|
||||
npf_rule_t * npf_ruleset_sharepm(npf_ruleset_t *, npf_natpolicy_t *);
|
||||
npf_natpolicy_t *npf_ruleset_findnat(npf_ruleset_t *, uint64_t);
|
||||
void npf_ruleset_freealg(npf_ruleset_t *, npf_alg_t *);
|
||||
int npf_ruleset_export(npf_t *, const npf_ruleset_t *,
|
||||
|
@ -405,13 +444,27 @@ bool npf_rproc_run(npf_cache_t *, npf_rproc_t *,
|
|||
const npf_match_info_t *, int *);
|
||||
|
||||
/* State handling. */
|
||||
void npf_state_sysinit(npf_t *);
|
||||
void npf_state_sysfini(npf_t *);
|
||||
|
||||
bool npf_state_init(npf_cache_t *, npf_state_t *);
|
||||
bool npf_state_inspect(npf_cache_t *, npf_state_t *, const bool);
|
||||
int npf_state_etime(const npf_state_t *, const int);
|
||||
int npf_state_etime(npf_t *, const npf_state_t *, const int);
|
||||
void npf_state_destroy(npf_state_t *);
|
||||
|
||||
void npf_state_tcp_sysinit(npf_t *);
|
||||
void npf_state_tcp_sysfini(npf_t *);
|
||||
bool npf_state_tcp(npf_cache_t *, npf_state_t *, int);
|
||||
int npf_state_tcp_timeout(const npf_state_t *);
|
||||
int npf_state_tcp_timeout(npf_t *, const npf_state_t *);
|
||||
|
||||
/* Portmap. */
|
||||
void npf_portmap_init(npf_t *);
|
||||
void npf_portmap_fini(npf_t *);
|
||||
|
||||
in_port_t npf_portmap_get(npf_t *, int, const npf_addr_t *);
|
||||
bool npf_portmap_take(npf_t *, int, const npf_addr_t *, in_port_t);
|
||||
void npf_portmap_put(npf_t *, int, const npf_addr_t *, in_port_t);
|
||||
void npf_portmap_flush(npf_t *);
|
||||
|
||||
/* NAT. */
|
||||
void npf_nat_sysinit(void);
|
||||
|
@ -420,7 +473,6 @@ npf_natpolicy_t *npf_nat_newpolicy(npf_t *, const nvlist_t *, npf_ruleset_t *);
|
|||
int npf_nat_policyexport(const npf_natpolicy_t *, nvlist_t *);
|
||||
void npf_nat_freepolicy(npf_natpolicy_t *);
|
||||
bool npf_nat_cmppolicy(npf_natpolicy_t *, npf_natpolicy_t *);
|
||||
bool npf_nat_sharepm(npf_natpolicy_t *, npf_natpolicy_t *);
|
||||
void npf_nat_setid(npf_natpolicy_t *, uint64_t);
|
||||
uint64_t npf_nat_getid(const npf_natpolicy_t *);
|
||||
void npf_nat_freealg(npf_natpolicy_t *, npf_alg_t *);
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.53 2019/01/19 21:19:32 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.54 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -221,6 +221,26 @@ npf_addr_cmp(const npf_addr_t *addr1, const npf_netmask_t mask1,
|
|||
return memcmp(addr1, addr2, alen);
|
||||
}
|
||||
|
||||
int
|
||||
npf_netmask_check(const int alen, npf_netmask_t mask)
|
||||
{
|
||||
switch (alen) {
|
||||
case sizeof(struct in_addr):
|
||||
if (__predict_false(mask > 32 && mask != NPF_NO_NETMASK)) {
|
||||
return EINVAL;
|
||||
}
|
||||
break;
|
||||
case sizeof(struct in6_addr):
|
||||
if (__predict_false(mask > 128 && mask != NPF_NO_NETMASK)) {
|
||||
return EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_tcpsaw: helper to fetch SEQ, ACK, WIN and return TCP data length.
|
||||
*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2014-2018 Mindaugas Rasiukevicius <rmind at netbsd org>
|
||||
* Copyright (c) 2014-2019 Mindaugas Rasiukevicius <rmind at netbsd org>
|
||||
* Copyright (c) 2010-2013 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -51,12 +51,8 @@
|
|||
*
|
||||
* The NAT (translation) policy is applied when packet matches the
|
||||
* rule. Apart from the filter criteria, the NAT policy always has
|
||||
* a translation IP address or a table and an associated port map.
|
||||
* The port map is a bitmap used to reserve and use unique TCP/UDP
|
||||
* ports for translation. Port maps are unique to the IP addresses,
|
||||
* therefore multiple NAT policies with the same IP will share the
|
||||
* same port map. However, if NAT policy is using the translation
|
||||
* table, then the port map is shared per table.
|
||||
* a translation IP address or a table. If port translation is set,
|
||||
* then NAT mechanism relies on port map mechanism.
|
||||
*
|
||||
* Connections, translation entries and their life-cycle
|
||||
*
|
||||
|
@ -71,45 +67,22 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.45 2019/01/19 21:19:32 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.46 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/atomic.h>
|
||||
#include <sys/bitops.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/pool.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/cprng.h>
|
||||
|
||||
#include <net/pfil.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "npf_impl.h"
|
||||
#include "npf_conn.h"
|
||||
|
||||
/*
|
||||
* NPF portmap structure.
|
||||
*/
|
||||
typedef struct {
|
||||
u_int p_refcnt;
|
||||
uint32_t p_bitmap[0];
|
||||
} npf_portmap_t;
|
||||
|
||||
/* Portmap range: [ 1024 .. 65535 ] */
|
||||
#define PORTMAP_FIRST (1024)
|
||||
#define PORTMAP_SIZE ((65536 - PORTMAP_FIRST) / 32)
|
||||
#define PORTMAP_FILLED ((uint32_t)~0U)
|
||||
#define PORTMAP_MASK (31)
|
||||
#define PORTMAP_SHIFT (5)
|
||||
|
||||
#define PORTMAP_MEM_SIZE \
|
||||
(sizeof(npf_portmap_t) + (PORTMAP_SIZE * sizeof(uint32_t)))
|
||||
|
||||
/*
|
||||
* NAT policy structure.
|
||||
*/
|
||||
|
@ -117,8 +90,7 @@ struct npf_natpolicy {
|
|||
npf_t * n_npfctx;
|
||||
kmutex_t n_lock;
|
||||
LIST_HEAD(, npf_nat) n_nat_list;
|
||||
volatile u_int n_refcnt;
|
||||
npf_portmap_t * n_portmap;
|
||||
volatile unsigned n_refcnt;
|
||||
uint64_t n_id;
|
||||
|
||||
/*
|
||||
|
@ -128,7 +100,7 @@ struct npf_natpolicy {
|
|||
*
|
||||
* NPF_NP_CMP_START mark starts here.
|
||||
*/
|
||||
int n_type;
|
||||
unsigned n_type;
|
||||
unsigned n_flags;
|
||||
unsigned n_alen;
|
||||
|
||||
|
@ -166,6 +138,7 @@ struct npf_nat {
|
|||
npf_addr_t nt_taddr;
|
||||
npf_addr_t nt_oaddr;
|
||||
|
||||
unsigned nt_alen;
|
||||
in_port_t nt_oport;
|
||||
in_port_t nt_tport;
|
||||
|
||||
|
@ -200,14 +173,11 @@ npf_nat_sysfini(void)
|
|||
|
||||
/*
|
||||
* npf_nat_newpolicy: create a new NAT policy.
|
||||
*
|
||||
* => Shares portmap if policy is on existing translation address.
|
||||
*/
|
||||
npf_natpolicy_t *
|
||||
npf_nat_newpolicy(npf_t *npf, const nvlist_t *nat, npf_ruleset_t *rset)
|
||||
{
|
||||
npf_natpolicy_t *np;
|
||||
npf_portmap_t *pm;
|
||||
const void *addr;
|
||||
size_t len;
|
||||
|
||||
|
@ -227,8 +197,9 @@ npf_nat_newpolicy(npf_t *npf, const nvlist_t *nat, npf_ruleset_t *rset)
|
|||
LIST_INIT(&np->n_nat_list);
|
||||
|
||||
/*
|
||||
* Translation IP, mask and port (if applicable).
|
||||
* Alternatively, there may be a translation table ID.
|
||||
* Translation IP, mask and port (if applicable). If using the
|
||||
* the table, specified by the ID, then the nat-addr/nat-mask will
|
||||
* be used as a filter for the addresses selected from table.
|
||||
*/
|
||||
if (nvlist_exists_number(nat, "nat-table-id")) {
|
||||
if (np->n_flags & NPF_NAT_STATIC) {
|
||||
|
@ -238,13 +209,16 @@ npf_nat_newpolicy(npf_t *npf, const nvlist_t *nat, npf_ruleset_t *rset)
|
|||
np->n_tmask = NPF_NO_NETMASK;
|
||||
np->n_flags |= NPF_NAT_USETABLE;
|
||||
} else {
|
||||
addr = dnvlist_get_binary(nat, "nat-ip", &len, NULL, 0);
|
||||
addr = dnvlist_get_binary(nat, "nat-addr", &len, NULL, 0);
|
||||
if (!addr || len == 0 || len > sizeof(npf_addr_t)) {
|
||||
goto err;
|
||||
}
|
||||
memcpy(&np->n_taddr, addr, len);
|
||||
np->n_alen = len;
|
||||
np->n_tmask = dnvlist_get_number(nat, "nat-mask", 0);
|
||||
np->n_tmask = dnvlist_get_number(nat, "nat-mask", NPF_NO_NETMASK);
|
||||
if (npf_netmask_check(np->n_alen, np->n_tmask)) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
np->n_tport = dnvlist_get_number(nat, "nat-port", 0);
|
||||
|
||||
|
@ -261,32 +235,11 @@ npf_nat_newpolicy(npf_t *npf, const nvlist_t *nat, npf_ruleset_t *rset)
|
|||
case NPF_ALGO_IPHASH:
|
||||
case NPF_ALGO_RR:
|
||||
default:
|
||||
if (np->n_tmask != NPF_NO_NETMASK)
|
||||
if (np->n_tmask != NPF_NO_NETMASK) {
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Determine if port map is needed. */
|
||||
np->n_portmap = NULL;
|
||||
if ((np->n_flags & NPF_NAT_PORTMAP) == 0) {
|
||||
/* No port map. */
|
||||
return np;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inspect NAT policies in the ruleset for port map sharing.
|
||||
* Note that npf_ruleset_sharepm() will increase the reference count.
|
||||
*/
|
||||
if (!npf_ruleset_sharepm(rset, np)) {
|
||||
/* Allocate a new port map for the NAT policy. */
|
||||
pm = kmem_zalloc(PORTMAP_MEM_SIZE, KM_SLEEP);
|
||||
pm->p_refcnt = 1;
|
||||
KASSERT((uintptr_t)pm->p_bitmap == (uintptr_t)pm + sizeof(*pm));
|
||||
np->n_portmap = pm;
|
||||
} else {
|
||||
KASSERT(np->n_portmap != NULL);
|
||||
KASSERT(np->n_portmap->p_refcnt > 0);
|
||||
}
|
||||
return np;
|
||||
err:
|
||||
mutex_destroy(&np->n_lock);
|
||||
|
@ -304,7 +257,7 @@ npf_nat_policyexport(const npf_natpolicy_t *np, nvlist_t *nat)
|
|||
if (np->n_flags & NPF_NAT_USETABLE) {
|
||||
nvlist_add_number(nat, "nat-table-id", np->n_tid);
|
||||
} else {
|
||||
nvlist_add_binary(nat, "nat-ip", &np->n_taddr, np->n_alen);
|
||||
nvlist_add_binary(nat, "nat-addr", &np->n_taddr, np->n_alen);
|
||||
nvlist_add_number(nat, "nat-mask", np->n_tmask);
|
||||
}
|
||||
nvlist_add_number(nat, "nat-port", np->n_tport);
|
||||
|
@ -319,14 +272,13 @@ npf_nat_policyexport(const npf_natpolicy_t *np, nvlist_t *nat)
|
|||
}
|
||||
|
||||
/*
|
||||
* npf_nat_freepolicy: free NAT policy and, on last reference, free portmap.
|
||||
* npf_nat_freepolicy: free the NAT policy.
|
||||
*
|
||||
* => Called from npf_rule_free() during the reload via npf_ruleset_destroy().
|
||||
*/
|
||||
void
|
||||
npf_nat_freepolicy(npf_natpolicy_t *np)
|
||||
{
|
||||
npf_portmap_t *pm = np->n_portmap;
|
||||
npf_conn_t *con;
|
||||
npf_nat_t *nt;
|
||||
|
||||
|
@ -348,13 +300,6 @@ npf_nat_freepolicy(npf_natpolicy_t *np)
|
|||
kpause("npfgcnat", false, 1, NULL);
|
||||
}
|
||||
KASSERT(LIST_EMPTY(&np->n_nat_list));
|
||||
KASSERT(pm == NULL || pm->p_refcnt > 0);
|
||||
|
||||
/* Destroy the port map, on last reference. */
|
||||
if (pm && atomic_dec_uint_nv(&pm->p_refcnt) == 0) {
|
||||
KASSERT((np->n_flags & NPF_NAT_PORTMAP) != 0);
|
||||
kmem_free(pm, PORTMAP_MEM_SIZE);
|
||||
}
|
||||
mutex_destroy(&np->n_lock);
|
||||
kmem_free(np, sizeof(npf_natpolicy_t));
|
||||
}
|
||||
|
@ -366,8 +311,9 @@ npf_nat_freealg(npf_natpolicy_t *np, npf_alg_t *alg)
|
|||
|
||||
mutex_enter(&np->n_lock);
|
||||
LIST_FOREACH(nt, &np->n_nat_list, nt_entry) {
|
||||
if (nt->nt_alg == alg)
|
||||
if (nt->nt_alg == alg) {
|
||||
nt->nt_alg = NULL;
|
||||
}
|
||||
}
|
||||
mutex_exit(&np->n_lock);
|
||||
}
|
||||
|
@ -392,46 +338,6 @@ npf_nat_cmppolicy(npf_natpolicy_t *np, npf_natpolicy_t *mnp)
|
|||
return memcmp(np_raw, mnp_raw, NPF_NP_CMP_SIZE) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
npf_nat_sharepm(npf_natpolicy_t *np, npf_natpolicy_t *mnp)
|
||||
{
|
||||
npf_portmap_t *pm, *mpm;
|
||||
|
||||
KASSERT(np && mnp && np != mnp);
|
||||
KASSERT(LIST_EMPTY(&mnp->n_nat_list));
|
||||
KASSERT(mnp->n_refcnt == 0);
|
||||
|
||||
/* Using port map and having equal translation address? */
|
||||
if ((np->n_flags & mnp->n_flags & NPF_NAT_PORTMAP) == 0) {
|
||||
return false;
|
||||
}
|
||||
if (np->n_alen != mnp->n_alen) {
|
||||
return false;
|
||||
}
|
||||
if ((np->n_flags & NPF_NAT_USETABLE) != 0 && np->n_tid != mnp->n_tid) {
|
||||
return false;
|
||||
}
|
||||
if (np->n_alen && memcmp(&np->n_taddr, &mnp->n_taddr, np->n_alen)) {
|
||||
return false;
|
||||
}
|
||||
mpm = mnp->n_portmap;
|
||||
KASSERT(mpm == NULL || mpm->p_refcnt > 0);
|
||||
|
||||
/*
|
||||
* If NAT policy has an old port map - drop the reference
|
||||
* and destroy the port map if it was the last.
|
||||
*/
|
||||
if (mpm && atomic_dec_uint_nv(&mpm->p_refcnt) == 0) {
|
||||
kmem_free(mpm, PORTMAP_MEM_SIZE);
|
||||
}
|
||||
|
||||
/* Share the port map. */
|
||||
pm = np->n_portmap;
|
||||
atomic_inc_uint(&pm->p_refcnt);
|
||||
mnp->n_portmap = pm;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
npf_nat_setid(npf_natpolicy_t *np, uint64_t id)
|
||||
{
|
||||
|
@ -444,101 +350,12 @@ npf_nat_getid(const npf_natpolicy_t *np)
|
|||
return np->n_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_nat_getport: allocate and return a port in the NAT policy portmap.
|
||||
*
|
||||
* => Returns in network byte-order.
|
||||
* => Zero indicates failure.
|
||||
*/
|
||||
static in_port_t
|
||||
npf_nat_getport(npf_natpolicy_t *np)
|
||||
{
|
||||
npf_portmap_t *pm = np->n_portmap;
|
||||
u_int n = PORTMAP_SIZE, idx, bit;
|
||||
uint32_t map, nmap;
|
||||
|
||||
KASSERT((np->n_flags & NPF_NAT_PORTMAP) != 0);
|
||||
KASSERT(pm->p_refcnt > 0);
|
||||
|
||||
idx = cprng_fast32() % PORTMAP_SIZE;
|
||||
for (;;) {
|
||||
KASSERT(idx < PORTMAP_SIZE);
|
||||
map = pm->p_bitmap[idx];
|
||||
if (__predict_false(map == PORTMAP_FILLED)) {
|
||||
if (n-- == 0) {
|
||||
/* No space. */
|
||||
return 0;
|
||||
}
|
||||
/* This bitmap is filled, next. */
|
||||
idx = (idx ? idx : PORTMAP_SIZE) - 1;
|
||||
continue;
|
||||
}
|
||||
bit = ffs32(~map) - 1;
|
||||
nmap = map | (1 << bit);
|
||||
if (atomic_cas_32(&pm->p_bitmap[idx], map, nmap) == map) {
|
||||
/* Success. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return htons(PORTMAP_FIRST + (idx << PORTMAP_SHIFT) + bit);
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_nat_takeport: allocate specific port in the NAT policy portmap.
|
||||
*/
|
||||
static bool
|
||||
npf_nat_takeport(npf_natpolicy_t *np, in_port_t port)
|
||||
{
|
||||
npf_portmap_t *pm = np->n_portmap;
|
||||
uint32_t map, nmap;
|
||||
u_int idx, bit;
|
||||
|
||||
KASSERT((np->n_flags & NPF_NAT_PORTMAP) != 0);
|
||||
KASSERT(pm->p_refcnt > 0);
|
||||
|
||||
port = ntohs(port) - PORTMAP_FIRST;
|
||||
idx = port >> PORTMAP_SHIFT;
|
||||
bit = port & PORTMAP_MASK;
|
||||
map = pm->p_bitmap[idx];
|
||||
nmap = map | (1 << bit);
|
||||
if (map == nmap) {
|
||||
/* Already taken. */
|
||||
return false;
|
||||
}
|
||||
return atomic_cas_32(&pm->p_bitmap[idx], map, nmap) == map;
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_nat_putport: return port as available in the NAT policy portmap.
|
||||
*
|
||||
* => Port should be in network byte-order.
|
||||
*/
|
||||
static void
|
||||
npf_nat_putport(npf_natpolicy_t *np, in_port_t port)
|
||||
{
|
||||
npf_portmap_t *pm = np->n_portmap;
|
||||
uint32_t map, nmap;
|
||||
u_int idx, bit;
|
||||
|
||||
KASSERT((np->n_flags & NPF_NAT_PORTMAP) != 0);
|
||||
KASSERT(pm->p_refcnt > 0);
|
||||
|
||||
port = ntohs(port) - PORTMAP_FIRST;
|
||||
idx = port >> PORTMAP_SHIFT;
|
||||
bit = port & PORTMAP_MASK;
|
||||
do {
|
||||
map = pm->p_bitmap[idx];
|
||||
KASSERT(map | (1 << bit));
|
||||
nmap = map & ~(1 << bit);
|
||||
} while (atomic_cas_32(&pm->p_bitmap[idx], map, nmap) != map);
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_nat_which: tell which address (source or destination) should be
|
||||
* rewritten given the combination of the NAT type and flow direction.
|
||||
*/
|
||||
static inline u_int
|
||||
npf_nat_which(const int type, bool forw)
|
||||
static inline unsigned
|
||||
npf_nat_which(const unsigned type, bool forw)
|
||||
{
|
||||
/*
|
||||
* Outbound NAT rewrites:
|
||||
|
@ -553,7 +370,7 @@ npf_nat_which(const int type, bool forw)
|
|||
}
|
||||
CTASSERT(NPF_SRC == 0 && NPF_DST == 1);
|
||||
KASSERT(forw == NPF_SRC || forw == NPF_DST);
|
||||
return (u_int)forw;
|
||||
return (unsigned)forw;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -580,6 +397,45 @@ npf_nat_inspect(npf_cache_t *npc, const int di)
|
|||
return np;
|
||||
}
|
||||
|
||||
static void
|
||||
npf_nat_algo_netmap(const npf_cache_t *npc, const npf_natpolicy_t *np,
|
||||
const unsigned which, npf_addr_t *addr)
|
||||
{
|
||||
const npf_addr_t *orig_addr = npc->npc_ips[which];
|
||||
|
||||
/*
|
||||
* NETMAP:
|
||||
*
|
||||
* addr = net-addr | (orig-addr & ~mask)
|
||||
*/
|
||||
npf_addr_mask(&np->n_taddr, np->n_tmask, npc->npc_alen, addr);
|
||||
npf_addr_bitor(orig_addr, np->n_tmask, npc->npc_alen, addr);
|
||||
}
|
||||
|
||||
static inline npf_addr_t *
|
||||
npf_nat_getaddr(npf_cache_t *npc, npf_natpolicy_t *np, const unsigned alen)
|
||||
{
|
||||
npf_tableset_t *ts = npf_config_tableset(np->n_npfctx);
|
||||
npf_table_t *t = npf_tableset_getbyid(ts, np->n_tid);
|
||||
unsigned idx;
|
||||
|
||||
/*
|
||||
* Dynamically select the translation IP address.
|
||||
*/
|
||||
switch (np->n_algo) {
|
||||
case NPF_ALGO_RR:
|
||||
idx = atomic_inc_uint_nv(&np->n_rr_idx);
|
||||
break;
|
||||
case NPF_ALGO_IPHASH:
|
||||
default:
|
||||
idx = npf_addr_mix(alen,
|
||||
npc->npc_ips[NPF_SRC],
|
||||
npc->npc_ips[NPF_DST]);
|
||||
break;
|
||||
}
|
||||
return npf_table_getsome(t, alen, idx);
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_nat_create: create a new NAT translation entry.
|
||||
*/
|
||||
|
@ -594,36 +450,9 @@ npf_nat_create(npf_cache_t *npc, npf_natpolicy_t *np, npf_conn_t *con)
|
|||
KASSERT(npf_iscached(npc, NPC_IP46));
|
||||
KASSERT(npf_iscached(npc, NPC_LAYER4));
|
||||
|
||||
if (np->n_flags & NPF_NAT_USETABLE) {
|
||||
npf_tableset_t *ts = npf_config_tableset(np->n_npfctx);
|
||||
npf_table_t *t = npf_tableset_getbyid(ts, np->n_tid);
|
||||
unsigned idx;
|
||||
|
||||
/*
|
||||
* Dynamically select the translation IP address.
|
||||
*/
|
||||
switch (np->n_algo) {
|
||||
case NPF_ALGO_RR:
|
||||
idx = atomic_inc_uint_nv(&np->n_rr_idx);
|
||||
break;
|
||||
case NPF_ALGO_IPHASH:
|
||||
default:
|
||||
idx = npf_addr_mix(alen,
|
||||
npc->npc_ips[NPF_SRC], npc->npc_ips[NPF_DST]);
|
||||
break;
|
||||
}
|
||||
taddr = npf_table_getsome(t, alen, idx);
|
||||
if (taddr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
/* Static IP address. */
|
||||
taddr = &np->n_taddr;
|
||||
}
|
||||
|
||||
/* Construct a new NAT entry and associate it with the connection. */
|
||||
nt = pool_cache_get(nat_cache, PR_NOWAIT);
|
||||
if (nt == NULL){
|
||||
if (__predict_false(!nt)) {
|
||||
return NULL;
|
||||
}
|
||||
npf_stats_inc(npc->npc_ctx, NPF_STAT_NAT_CREATE);
|
||||
|
@ -631,8 +460,26 @@ npf_nat_create(npf_cache_t *npc, npf_natpolicy_t *np, npf_conn_t *con)
|
|||
nt->nt_conn = con;
|
||||
nt->nt_alg = NULL;
|
||||
|
||||
/* Set the translation address. */
|
||||
memcpy(&nt->nt_taddr, taddr, alen);
|
||||
/*
|
||||
* Select the translation address.
|
||||
*/
|
||||
if (np->n_flags & NPF_NAT_USETABLE) {
|
||||
taddr = npf_nat_getaddr(npc, np, alen);
|
||||
if (__predict_false(!taddr)) {
|
||||
pool_cache_put(nat_cache, nt);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&nt->nt_taddr, taddr, alen);
|
||||
} else if (np->n_algo == NPF_ALGO_NETMAP) {
|
||||
const unsigned which = npf_nat_which(np->n_type, true);
|
||||
npf_nat_algo_netmap(npc, np, which, &nt->nt_taddr);
|
||||
taddr = &nt->nt_taddr;
|
||||
} else {
|
||||
/* Static IP address. */
|
||||
taddr = &np->n_taddr;
|
||||
memcpy(&nt->nt_taddr, taddr, alen);
|
||||
}
|
||||
nt->nt_alen = alen;
|
||||
|
||||
/* Save the original address which may be rewritten. */
|
||||
if (np->n_type == NPF_NATOUT) {
|
||||
|
@ -667,7 +514,7 @@ npf_nat_create(npf_cache_t *npc, npf_natpolicy_t *np, npf_conn_t *con)
|
|||
|
||||
/* Get a new port for translation. */
|
||||
if ((np->n_flags & NPF_NAT_PORTMAP) != 0) {
|
||||
nt->nt_tport = npf_nat_getport(np);
|
||||
nt->nt_tport = npf_portmap_get(np->n_npfctx, alen, taddr);
|
||||
} else {
|
||||
nt->nt_tport = np->n_tport;
|
||||
}
|
||||
|
@ -685,7 +532,7 @@ static inline int
|
|||
npf_nat_translate(npf_cache_t *npc, npf_nat_t *nt, bool forw)
|
||||
{
|
||||
const npf_natpolicy_t *np = nt->nt_natpolicy;
|
||||
const u_int which = npf_nat_which(np->n_type, forw);
|
||||
const unsigned which = npf_nat_which(np->n_type, forw);
|
||||
const npf_addr_t *addr;
|
||||
in_port_t port;
|
||||
|
||||
|
@ -721,22 +568,15 @@ npf_nat_translate(npf_cache_t *npc, npf_nat_t *nt, bool forw)
|
|||
static inline int
|
||||
npf_nat_algo(npf_cache_t *npc, const npf_natpolicy_t *np, bool forw)
|
||||
{
|
||||
const u_int which = npf_nat_which(np->n_type, forw);
|
||||
const npf_addr_t *taddr, *orig_addr;
|
||||
const unsigned which = npf_nat_which(np->n_type, forw);
|
||||
const npf_addr_t *taddr;
|
||||
npf_addr_t addr;
|
||||
|
||||
KASSERT(np->n_flags & NPF_NAT_STATIC);
|
||||
|
||||
switch (np->n_algo) {
|
||||
case NPF_ALGO_NETMAP:
|
||||
/*
|
||||
* NETMAP:
|
||||
*
|
||||
* addr = net-addr | (orig-addr & ~mask)
|
||||
*/
|
||||
orig_addr = npc->npc_ips[which];
|
||||
npf_addr_mask(&np->n_taddr, np->n_tmask, npc->npc_alen, &addr);
|
||||
npf_addr_bitor(orig_addr, np->n_tmask, npc->npc_alen, &addr);
|
||||
npf_nat_algo_netmap(npc, np, which, &addr);
|
||||
taddr = &addr;
|
||||
break;
|
||||
case NPF_ALGO_NPT66:
|
||||
|
@ -901,10 +741,11 @@ void
|
|||
npf_nat_destroy(npf_nat_t *nt)
|
||||
{
|
||||
npf_natpolicy_t *np = nt->nt_natpolicy;
|
||||
npf_t *npf = np->n_npfctx;
|
||||
|
||||
/* Return any taken port to the portmap. */
|
||||
/* Return taken port to the portmap. */
|
||||
if ((np->n_flags & NPF_NAT_PORTMAP) != 0 && nt->nt_tport) {
|
||||
npf_nat_putport(np, nt->nt_tport);
|
||||
npf_portmap_put(npf, nt->nt_alen, &nt->nt_taddr, nt->nt_tport);
|
||||
}
|
||||
npf_stats_inc(np->n_npfctx, NPF_STAT_NAT_DESTROY);
|
||||
|
||||
|
@ -964,7 +805,7 @@ npf_nat_import(npf_t *npf, const nvlist_t *nat,
|
|||
|
||||
/* Take a specific port from port-map. */
|
||||
if ((np->n_flags & NPF_NAT_PORTMAP) != 0 && nt->nt_tport &&
|
||||
!npf_nat_takeport(np, nt->nt_tport)) {
|
||||
!npf_portmap_take(npf, nt->nt_alen, &nt->nt_taddr, nt->nt_tport)) {
|
||||
pool_cache_put(nat_cache, nt);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -991,7 +832,7 @@ npf_nat_dump(const npf_nat_t *nt)
|
|||
|
||||
np = nt->nt_natpolicy;
|
||||
memcpy(&ip, &nt->nt_taddr, sizeof(ip));
|
||||
printf("\tNATP(%p): type %d flags 0x%x taddr %s tport %d\n", np,
|
||||
printf("\tNATP(%p): type %u flags 0x%x taddr %s tport %d\n", np,
|
||||
np->n_type, np->n_flags, inet_ntoa(ip), ntohs(np->n_tport));
|
||||
memcpy(&ip, &nt->nt_oaddr, sizeof(ip));
|
||||
printf("\tNAT: original address %s oport %d tport %d\n",
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_os.c,v 1.11 2019/02/27 21:37:24 mrg Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_os.c,v 1.12 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#ifdef _KERNEL_OPT
|
||||
#include "pf.h"
|
||||
|
@ -140,19 +140,7 @@ npf_fini(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if 1
|
||||
/*
|
||||
* When npf_init() is static and inlined into npf_modcmd() directly (either
|
||||
* by the human or GCC 7), then GCC 7 on 32 bit sparc do * something wrong
|
||||
* and CPUs hang up. Making it not static works for some reason.
|
||||
*
|
||||
* Revert this when the real problem is found.
|
||||
*/
|
||||
int npf_init(void);
|
||||
int
|
||||
#else
|
||||
static int
|
||||
#endif
|
||||
npf_init(void)
|
||||
{
|
||||
npf_t *npf;
|
||||
|
@ -391,6 +379,7 @@ npf_ifaddrhook(void *arg, u_long cmd, void *arg2)
|
|||
case SIOCAIFADDR_IN6:
|
||||
case SIOCDIFADDR_IN6:
|
||||
#endif
|
||||
KASSERT(ifa != NULL);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
/*-
|
||||
* Copyright (c) 2019 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_params.c,v 1.1 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/thmap.h>
|
||||
#endif
|
||||
|
||||
#include "npf_impl.h"
|
||||
|
||||
typedef struct npf_paramreg {
|
||||
struct npf_paramreg * next;
|
||||
unsigned count;
|
||||
npf_param_t params[];
|
||||
} npf_paramreg_t;
|
||||
|
||||
struct npf_paraminfo {
|
||||
npf_paramreg_t * list;
|
||||
thmap_t * map;
|
||||
};
|
||||
|
||||
void
|
||||
npf_param_init(npf_t *npf)
|
||||
{
|
||||
npf_paraminfo_t *paraminfo;
|
||||
|
||||
paraminfo = kmem_zalloc(sizeof(npf_paraminfo_t), KM_SLEEP);
|
||||
paraminfo->map = thmap_create(0, NULL, THMAP_NOCOPY);
|
||||
npf->paraminfo = paraminfo;
|
||||
}
|
||||
|
||||
void
|
||||
npf_param_fini(npf_t *npf)
|
||||
{
|
||||
npf_paraminfo_t *pinfo = npf->paraminfo;
|
||||
npf_paramreg_t *paramreg = pinfo->list;
|
||||
|
||||
while (paramreg) {
|
||||
npf_param_t *plist = paramreg->params;
|
||||
npf_paramreg_t *next = paramreg->next;
|
||||
size_t len;
|
||||
|
||||
/* Remove the parameters from the map. */
|
||||
for (unsigned i = 0; i < paramreg->count; i++) {
|
||||
npf_param_t *param = &plist[i];
|
||||
const char *name = param->name;
|
||||
void *ret __diagused;
|
||||
|
||||
ret = thmap_del(pinfo->map, name, strlen(name));
|
||||
KASSERT(ret != NULL);
|
||||
}
|
||||
|
||||
/* Destroy this registry. */
|
||||
len = offsetof(npf_paramreg_t, params[paramreg->count]);
|
||||
kmem_free(paramreg, len);
|
||||
|
||||
/* Next .. */
|
||||
paramreg = next;
|
||||
}
|
||||
thmap_destroy(pinfo->map);
|
||||
kmem_free(pinfo, sizeof(npf_paraminfo_t));
|
||||
}
|
||||
|
||||
void *
|
||||
npf_param_allocgroup(npf_t *npf, npf_paramgroup_t group, size_t len)
|
||||
{
|
||||
void *params = kmem_zalloc(len, KM_SLEEP);
|
||||
npf->params[group] = params;
|
||||
return params;
|
||||
}
|
||||
|
||||
void
|
||||
npf_param_freegroup(npf_t *npf, npf_paramgroup_t group, size_t len)
|
||||
{
|
||||
kmem_free(npf->params[group], len);
|
||||
npf->params[group] = NULL; // diagnostic
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_param_register: register an array of named parameters.
|
||||
*/
|
||||
void
|
||||
npf_param_register(npf_t *npf, npf_param_t *params, unsigned count)
|
||||
{
|
||||
npf_paraminfo_t *pinfo = npf->paraminfo;
|
||||
npf_paramreg_t *paramreg;
|
||||
size_t len;
|
||||
|
||||
/*
|
||||
* Copy over the parameters.
|
||||
*/
|
||||
len = offsetof(npf_paramreg_t, params[count]);
|
||||
paramreg = kmem_zalloc(len, KM_SLEEP);
|
||||
memcpy(paramreg->params, params, sizeof(npf_param_t) * count);
|
||||
paramreg->count = count;
|
||||
params = NULL; // dead
|
||||
|
||||
/*
|
||||
* Map the parameter names to the variables.
|
||||
* Assign the default values.
|
||||
*/
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
npf_param_t *param = ¶mreg->params[i];
|
||||
const char *name = param->name;
|
||||
void *ret __diagused;
|
||||
|
||||
ret = thmap_put(pinfo->map, name, strlen(name), param);
|
||||
KASSERT(ret == param);
|
||||
|
||||
/* Assign the default value. */
|
||||
KASSERT(param->default_val >= param->min);
|
||||
KASSERT(param->default_val <= param->max);
|
||||
*param->valp = param->default_val;
|
||||
}
|
||||
|
||||
/* Insert the registry of params into the list. */
|
||||
paramreg->next = pinfo->list;
|
||||
pinfo->list = paramreg;
|
||||
}
|
||||
|
||||
/*
|
||||
* NPF param API.
|
||||
*/
|
||||
|
||||
static npf_param_t *
|
||||
npf_param_lookup(npf_t *npf, const char *name)
|
||||
{
|
||||
npf_paraminfo_t *pinfo = npf->paraminfo;
|
||||
const size_t namelen = strlen(name);
|
||||
return thmap_get(pinfo->map, name, namelen);
|
||||
}
|
||||
|
||||
int
|
||||
npf_param_check(npf_t *npf, const char *name, int val)
|
||||
{
|
||||
npf_param_t *param;
|
||||
|
||||
if ((param = npf_param_lookup(npf, name)) == NULL) {
|
||||
return ENOENT;
|
||||
}
|
||||
if (val < param->min || val > param->max) {
|
||||
return EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__dso_public int
|
||||
npf_param_get(npf_t *npf, const char *name, int *val)
|
||||
{
|
||||
npf_param_t *param;
|
||||
|
||||
if ((param = npf_param_lookup(npf, name)) == NULL) {
|
||||
return ENOENT;
|
||||
}
|
||||
*val = *param->valp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__dso_public int
|
||||
npf_param_set(npf_t *npf, const char *name, int val)
|
||||
{
|
||||
npf_param_t *param;
|
||||
|
||||
if ((param = npf_param_lookup(npf, name)) == NULL) {
|
||||
return ENOENT;
|
||||
}
|
||||
if (val < param->min || val > param->max) {
|
||||
return EINVAL;
|
||||
}
|
||||
*param->valp = val;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,528 @@
|
|||
/*-
|
||||
* Copyright (c) 2019 Mindaugas Rasiukevicius <rmind at noxt eu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* NPF port map mechanism.
|
||||
*
|
||||
* The port map is a bitmap used to track TCP/UDP ports used for
|
||||
* translation. Port maps are per IP addresses, therefore multiple
|
||||
* NAT policies operating on the same IP address will share the
|
||||
* same port map.
|
||||
*/
|
||||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_portmap.c,v 1.1 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/atomic.h>
|
||||
#include <sys/bitops.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/cprng.h>
|
||||
#include <sys/thmap.h>
|
||||
#endif
|
||||
|
||||
#include "npf_impl.h"
|
||||
|
||||
/*
|
||||
* Port map uses two-level bitmaps with compression to efficiently
|
||||
* represent the maximum of 65536 (2^16) values.
|
||||
*
|
||||
* Level 0: 64 chunks each representing 1048 bits in two modes:
|
||||
*
|
||||
* a) If PORTMAP_L1_TAG, then up to 5 values are packed in the
|
||||
* 64-bit integer using 12 bits for each value, starting from the
|
||||
* most significant bits. The four 4 least significant bits are
|
||||
* unused or reserved for pointer tagging.
|
||||
*
|
||||
* b) If there are more than 5 values, then PORTMAP_L1_TAG is set
|
||||
* and the value serves as a pointer to the second level bitmap.
|
||||
*
|
||||
* Level 1: 16 chunks each representing 64 bits in plain uint64_t.
|
||||
*/
|
||||
|
||||
#define PORTMAP_MAX_BITS (65536U)
|
||||
#define PORTMAP_MASK (PORTMAP_MAX_BITS - 1)
|
||||
|
||||
#define PORTMAP_L0_SHIFT (10) // or 11
|
||||
#define PORTMAP_L0_MASK ((1U << PORTMAP_L0_SHIFT) - 1)
|
||||
#define PORTMAP_L0_WORDS (PORTMAP_MAX_BITS >> PORTMAP_L0_SHIFT)
|
||||
|
||||
#define PORTMAP_L1_SHIFT (6)
|
||||
#define PORTMAP_L1_MASK ((1U << PORTMAP_L1_SHIFT) - 1)
|
||||
#define PORTMAP_L1_WORDS \
|
||||
((PORTMAP_MAX_BITS / PORTMAP_L0_WORDS) >> PORTMAP_L1_SHIFT)
|
||||
|
||||
#define PORTMAP_L1_TAG (UINT64_C(1)) // use level 1
|
||||
#define PORTMAP_L1_GET(p) ((void *)((uintptr_t)(p) & ~(uintptr_t)3))
|
||||
|
||||
CTASSERT(sizeof(uint64_t) >= sizeof(uintptr_t));
|
||||
|
||||
typedef struct {
|
||||
volatile uint64_t bits1[PORTMAP_L1_WORDS];
|
||||
} bitmap_l1_t;
|
||||
|
||||
typedef struct bitmap {
|
||||
npf_addr_t addr;
|
||||
volatile uint64_t bits0[PORTMAP_L0_WORDS];
|
||||
LIST_ENTRY(bitmap) entry;
|
||||
unsigned addr_len;
|
||||
} bitmap_t;
|
||||
|
||||
struct npf_portmap {
|
||||
thmap_t * addr_map;
|
||||
LIST_HEAD(, bitmap) bitmap_list;
|
||||
kmutex_t list_lock;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int min_port;
|
||||
int max_port;
|
||||
} npf_portmap_params_t;
|
||||
|
||||
void
|
||||
npf_portmap_init(npf_t *npf)
|
||||
{
|
||||
npf_portmap_params_t *params = npf_param_allocgroup(npf,
|
||||
NPF_PARAMS_PORTMAP, sizeof(npf_portmap_params_t));
|
||||
npf_param_t param_map[] = {
|
||||
{
|
||||
"portmap.min_port",
|
||||
¶ms->min_port,
|
||||
.default_val = 1024,
|
||||
.min = 1024, .max = 65535
|
||||
},
|
||||
{
|
||||
"portmap.max_port",
|
||||
¶ms->max_port,
|
||||
.default_val = 65535,
|
||||
.min = 1024, .max = 65535
|
||||
}
|
||||
};
|
||||
npf_param_register(npf, param_map, __arraycount(param_map));
|
||||
|
||||
npf->portmap = kmem_zalloc(sizeof(npf_portmap_t), KM_SLEEP);
|
||||
mutex_init(&npf->portmap->list_lock, MUTEX_DEFAULT, IPL_SOFTNET);
|
||||
npf->portmap->addr_map = thmap_create(0, NULL, THMAP_NOCOPY);
|
||||
}
|
||||
|
||||
void
|
||||
npf_portmap_fini(npf_t *npf)
|
||||
{
|
||||
const size_t len = sizeof(npf_portmap_params_t);
|
||||
npf_portmap_t *pm = npf->portmap;
|
||||
|
||||
npf_param_freegroup(npf, NPF_PARAMS_PORTMAP, len);
|
||||
|
||||
npf_portmap_flush(npf);
|
||||
KASSERT(LIST_EMPTY(&pm->bitmap_list));
|
||||
|
||||
thmap_destroy(pm->addr_map);
|
||||
mutex_destroy(&pm->list_lock);
|
||||
kmem_free(pm, sizeof(npf_portmap_t));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* bitmap_word_isset: test whether the bit value is in the packed array.
|
||||
*
|
||||
* => Return true if any value equals the bit number value.
|
||||
*
|
||||
* Packed array: 60 MSB bits, 5 values, 12 bits each.
|
||||
*
|
||||
* Reference: "Bit Twiddling Hacks" by S.E. Anderson, Stanford.
|
||||
* Based on the hasvalue() and haszero() ideas. Since values are
|
||||
* represented by upper 60 bits, we shift right by 4.
|
||||
*/
|
||||
static bool
|
||||
bitmap_word_isset(uint64_t x, unsigned bit)
|
||||
{
|
||||
uint64_t m, r;
|
||||
|
||||
bit++;
|
||||
KASSERT((x & PORTMAP_L1_TAG) == 0);
|
||||
KASSERT(bit <= (PORTMAP_L0_MASK + 1));
|
||||
|
||||
m = (x >> 4) ^ (UINT64_C(0x1001001001001) * bit);
|
||||
r = (m - UINT64_C(0x1001001001001)) & (~m & UINT64_C(0x800800800800800));
|
||||
return r != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* bitmap_word_cax: compare-and-xor on packed array elements.
|
||||
*/
|
||||
static uint64_t
|
||||
bitmap_word_cax(uint64_t x, int exp, int bit)
|
||||
{
|
||||
unsigned e = exp + 1;
|
||||
|
||||
/*
|
||||
* We need to distinguish "no value" from zero. Just add one,
|
||||
* since we use 12 bits to represent 11 bit values.
|
||||
*/
|
||||
bit++;
|
||||
KASSERT((unsigned)bit <= (PORTMAP_L0_MASK + 1));
|
||||
KASSERT((x & PORTMAP_L1_TAG) == 0);
|
||||
|
||||
if (((x >> 52) & 0xfff) == e)
|
||||
return x ^ ((uint64_t)bit << 52);
|
||||
if (((x >> 40) & 0xfff) == e)
|
||||
return x ^ ((uint64_t)bit << 40);
|
||||
if (((x >> 28) & 0xfff) == e)
|
||||
return x ^ ((uint64_t)bit << 28);
|
||||
if (((x >> 16) & 0xfff) == e)
|
||||
return x ^ ((uint64_t)bit << 16);
|
||||
if (((x >> 4) & 0xfff) == e)
|
||||
return x ^ ((uint64_t)bit << 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
bitmap_word_unpack(uint64_t x, unsigned bitvals[static 5])
|
||||
{
|
||||
unsigned n = 0;
|
||||
uint64_t v;
|
||||
|
||||
KASSERT((x & PORTMAP_L1_TAG) == 0);
|
||||
|
||||
if ((v = ((x >> 52)) & 0xfff) != 0)
|
||||
bitvals[n++] = v - 1;
|
||||
if ((v = ((x >> 40)) & 0xfff) != 0)
|
||||
bitvals[n++] = v - 1;
|
||||
if ((v = ((x >> 28)) & 0xfff) != 0)
|
||||
bitvals[n++] = v - 1;
|
||||
if ((v = ((x >> 16)) & 0xfff) != 0)
|
||||
bitvals[n++] = v - 1;
|
||||
if ((v = ((x >> 4)) & 0xfff) != 0)
|
||||
bitvals[n++] = v - 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static bool
|
||||
bitmap_isset(const bitmap_t *bm, unsigned bit)
|
||||
{
|
||||
unsigned i, chunk_bit;
|
||||
uint64_t bval, b;
|
||||
bitmap_l1_t *bm1;
|
||||
|
||||
KASSERT(bit < PORTMAP_MAX_BITS);
|
||||
i = bit >> PORTMAP_L0_SHIFT;
|
||||
bval = bm->bits0[i];
|
||||
|
||||
/*
|
||||
* Empty check. Note: we can test the whole word against zero,
|
||||
* since zero bit values in the packed array result in bits set.
|
||||
*/
|
||||
if (bval == 0)
|
||||
return false;
|
||||
|
||||
/* Level 0 check. */
|
||||
chunk_bit = bit & PORTMAP_L0_MASK;
|
||||
if ((bval & PORTMAP_L1_TAG) == 0)
|
||||
return bitmap_word_isset(bval, chunk_bit);
|
||||
|
||||
/* Level 1 check. */
|
||||
bm1 = PORTMAP_L1_GET(bval);
|
||||
KASSERT(bm1 != NULL);
|
||||
i = chunk_bit >> PORTMAP_L1_SHIFT;
|
||||
b = UINT64_C(1) << (chunk_bit & PORTMAP_L1_MASK);
|
||||
return (bm1->bits1[i] & b) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool
|
||||
bitmap_set(bitmap_t *bm, unsigned bit)
|
||||
{
|
||||
unsigned i, chunk_bit;
|
||||
uint64_t bval, b, oval, nval;
|
||||
bitmap_l1_t *bm1;
|
||||
again:
|
||||
KASSERT(bit < PORTMAP_MAX_BITS);
|
||||
i = bit >> PORTMAP_L0_SHIFT;
|
||||
chunk_bit = bit & PORTMAP_L0_MASK;
|
||||
bval = bm->bits0[i]; // atomic fetch
|
||||
|
||||
if ((bval & PORTMAP_L1_TAG) == 0) {
|
||||
unsigned n = 0, bitvals[5];
|
||||
uint64_t bm1p;
|
||||
|
||||
if (bitmap_word_isset(bval, chunk_bit)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for a zero-slot and put a value there.
|
||||
*/
|
||||
if ((nval = bitmap_word_cax(bval, -1, chunk_bit)) != 0) {
|
||||
KASSERT((nval & PORTMAP_L1_TAG) == 0);
|
||||
if (atomic_cas_64(&bm->bits0[i], bval, nval) != bval) {
|
||||
goto again;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Full: allocate L1 block and copy over the current
|
||||
* values into the level.
|
||||
*/
|
||||
bm1 = kmem_intr_zalloc(sizeof(bitmap_l1_t), KM_NOSLEEP);
|
||||
if (bm1 == NULL) {
|
||||
return false; // error
|
||||
}
|
||||
n = bitmap_word_unpack(bval, bitvals);
|
||||
while (n--) {
|
||||
const unsigned v = bitvals[n];
|
||||
const unsigned off = v >> PORTMAP_L1_SHIFT;
|
||||
|
||||
KASSERT(v <= PORTMAP_L0_MASK);
|
||||
KASSERT(off < (sizeof(uint64_t) * CHAR_BIT));
|
||||
bm1->bits1[off] |= UINT64_C(1) << (v & PORTMAP_L1_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to set the L1 structure. Note: there is no
|
||||
* ABA problem since the we compare the actual values.
|
||||
* Note: CAS serves as a memory barrier.
|
||||
*/
|
||||
bm1p = (uintptr_t)bm1;
|
||||
KASSERT((bm1p & PORTMAP_L1_TAG) == 0);
|
||||
bm1p |= PORTMAP_L1_TAG;
|
||||
if (atomic_cas_64(&bm->bits0[i], bval, bm1p) != bval) {
|
||||
kmem_intr_free(bm1, sizeof(bitmap_l1_t));
|
||||
goto again;
|
||||
}
|
||||
bval = bm1p;
|
||||
}
|
||||
|
||||
bm1 = PORTMAP_L1_GET(bval);
|
||||
KASSERT(bm1 != NULL);
|
||||
i = chunk_bit >> PORTMAP_L1_SHIFT;
|
||||
b = UINT64_C(1) << (chunk_bit & PORTMAP_L1_MASK);
|
||||
|
||||
oval = bm1->bits1[i]; // atomic fetch
|
||||
if (oval & b) {
|
||||
return false;
|
||||
}
|
||||
nval = oval | b;
|
||||
if (atomic_cas_64(&bm1->bits1[i], oval, nval) != oval) {
|
||||
goto again;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
bitmap_clr(bitmap_t *bm, unsigned bit)
|
||||
{
|
||||
unsigned i, chunk_bit;
|
||||
uint64_t bval, b, oval, nval;
|
||||
bitmap_l1_t *bm1;
|
||||
again:
|
||||
KASSERT(bit < PORTMAP_MAX_BITS);
|
||||
i = bit >> PORTMAP_L0_SHIFT;
|
||||
chunk_bit = bit & PORTMAP_L0_MASK;
|
||||
bval = bm->bits0[i];
|
||||
|
||||
if ((bval & PORTMAP_L1_TAG) == 0) {
|
||||
if (!bitmap_word_isset(bval, chunk_bit)) {
|
||||
return false;
|
||||
}
|
||||
nval = bitmap_word_cax(bval, chunk_bit, chunk_bit);
|
||||
KASSERT((nval & PORTMAP_L1_TAG) == 0);
|
||||
if (atomic_cas_64(&bm->bits0[i], bval, nval) != bval) {
|
||||
goto again;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bm1 = PORTMAP_L1_GET(bval);
|
||||
KASSERT(bm1 != NULL);
|
||||
i = chunk_bit >> PORTMAP_L1_SHIFT;
|
||||
b = UINT64_C(1) << (chunk_bit & PORTMAP_L1_MASK);
|
||||
|
||||
oval = bm1->bits1[i]; // atomic fetch
|
||||
if ((oval & b) == 0) {
|
||||
return false;
|
||||
}
|
||||
nval = oval & ~b;
|
||||
if (atomic_cas_64(&bm1->bits1[i], oval, nval) != oval) {
|
||||
goto again;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static bitmap_t *
|
||||
npf_portmap_autoget(npf_t *npf, unsigned alen, const npf_addr_t *addr)
|
||||
{
|
||||
npf_portmap_t *pm = npf->portmap;
|
||||
bitmap_t *bm;
|
||||
|
||||
KASSERT(pm && pm->addr_map);
|
||||
KASSERT(alen && alen <= sizeof(npf_addr_t));
|
||||
|
||||
/* Lookup the port map for this address. */
|
||||
bm = thmap_get(pm->addr_map, addr, alen);
|
||||
if (bm == NULL) {
|
||||
void *ret;
|
||||
|
||||
/*
|
||||
* Allocate a new port map for this address and
|
||||
* attempt to insert it.
|
||||
*/
|
||||
bm = kmem_intr_zalloc(sizeof(bitmap_t), KM_NOSLEEP);
|
||||
if (bm == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&bm->addr, addr, alen);
|
||||
bm->addr_len = alen;
|
||||
|
||||
int s = splsoftnet();
|
||||
ret = thmap_put(pm->addr_map, &bm->addr, alen, bm);
|
||||
splx(s);
|
||||
|
||||
if (ret == bm) {
|
||||
/* Success: insert the bitmap into the list. */
|
||||
mutex_enter(&pm->list_lock);
|
||||
LIST_INSERT_HEAD(&pm->bitmap_list, bm, entry);
|
||||
mutex_exit(&pm->list_lock);
|
||||
} else {
|
||||
/* Race: use an existing bitmap. */
|
||||
kmem_free(bm, sizeof(bitmap_t));
|
||||
bm = ret;
|
||||
}
|
||||
}
|
||||
return bm;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* npf_portmap_flush: free all bitmaps and remove all addresses.
|
||||
*
|
||||
* => Concurrent calls to this routine are not allowed; therefore no
|
||||
* need to acquire locks.
|
||||
*/
|
||||
void
|
||||
npf_portmap_flush(npf_t *npf)
|
||||
{
|
||||
npf_portmap_t *pm = npf->portmap;
|
||||
bitmap_t *bm;
|
||||
|
||||
KASSERT(npf_config_locked_p(npf));
|
||||
|
||||
while ((bm = LIST_FIRST(&pm->bitmap_list)) != NULL) {
|
||||
for (unsigned i = 0; i < PORTMAP_L0_WORDS; i++) {
|
||||
uintptr_t bm1 = bm->bits0[i];
|
||||
|
||||
if (bm1 & PORTMAP_L1_TAG) {
|
||||
bitmap_l1_t *bm1p = PORTMAP_L1_GET(bm1);
|
||||
kmem_intr_free(bm1p, sizeof(bitmap_l1_t));
|
||||
}
|
||||
bm->bits0[i] = UINT64_C(0);
|
||||
}
|
||||
LIST_REMOVE(bm, entry);
|
||||
thmap_del(pm->addr_map, &bm->addr, bm->addr_len);
|
||||
kmem_intr_free(bm, sizeof(bitmap_t));
|
||||
}
|
||||
/* Note: the caller ensures there are no active references. */
|
||||
thmap_gc(pm->addr_map, thmap_stage_gc(pm->addr_map));
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_portmap_get: allocate and return a port from the given portmap.
|
||||
*
|
||||
* => Returns the port value in network byte-order.
|
||||
* => Zero indicates a failure.
|
||||
*/
|
||||
in_port_t
|
||||
npf_portmap_get(npf_t *npf, int alen, const npf_addr_t *addr)
|
||||
{
|
||||
const npf_portmap_params_t *params = npf->params[NPF_PARAMS_PORTMAP];
|
||||
const unsigned port_delta = params->max_port - params->min_port;
|
||||
unsigned bit, target;
|
||||
bitmap_t *bm;
|
||||
|
||||
bm = npf_portmap_autoget(npf, alen, addr);
|
||||
if (bm == NULL) {
|
||||
/* No memory. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Randomly select a port. */
|
||||
target = params->min_port + (cprng_fast32() % port_delta);
|
||||
bit = target;
|
||||
next:
|
||||
if (bitmap_set(bm, bit)) {
|
||||
/* Success. */
|
||||
return htons(bit);
|
||||
}
|
||||
bit = params->min_port + ((bit + 1) % port_delta);
|
||||
if (target != bit) {
|
||||
/* Next.. */
|
||||
goto next;
|
||||
}
|
||||
/* No space. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_portmap_take: allocate a specific port in the portmap.
|
||||
*/
|
||||
bool
|
||||
npf_portmap_take(npf_t *npf, int alen, const npf_addr_t *addr, in_port_t port)
|
||||
{
|
||||
const npf_portmap_params_t *params = npf->params[NPF_PARAMS_PORTMAP];
|
||||
bitmap_t *bm = npf_portmap_autoget(npf, alen, addr);
|
||||
|
||||
port = ntohs(port);
|
||||
if (!bm || port < params->min_port || port > params->max_port) {
|
||||
/* Out of memory / invalid port. */
|
||||
return false;
|
||||
}
|
||||
return bitmap_set(bm, port);
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_portmap_put: release the port, making it available in the portmap.
|
||||
*
|
||||
* => The port value should be in network byte-order.
|
||||
*/
|
||||
void
|
||||
npf_portmap_put(npf_t *npf, int alen, const npf_addr_t *addr, in_port_t port)
|
||||
{
|
||||
bitmap_t *bm;
|
||||
|
||||
bm = npf_portmap_autoget(npf, alen, addr);
|
||||
if (bm) {
|
||||
port = ntohs(port);
|
||||
bitmap_clr(bm, port);
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_rproc.c,v 1.18 2019/04/11 14:38:06 kamil Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_rproc.c,v 1.19 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.47 2018/09/29 14:41:36 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.48 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -523,8 +523,9 @@ npf_ruleset_reload(npf_t *npf, npf_ruleset_t *newset,
|
|||
return;
|
||||
|
||||
/*
|
||||
* Scan all rules in the new ruleset and share NAT policies.
|
||||
* Also, assign a unique ID for each policy here.
|
||||
* Scan all rules in the new ruleset and inherit the active NAT
|
||||
* policies if they are the same. Also, assign a unique ID for
|
||||
* each policy here.
|
||||
*/
|
||||
LIST_FOREACH(rl, &newset->rs_all, r_aentry) {
|
||||
npf_natpolicy_t *np;
|
||||
|
@ -535,13 +536,6 @@ npf_ruleset_reload(npf_t *npf, npf_ruleset_t *newset,
|
|||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* First, try to share the active port map. If this
|
||||
* policy will be unused, npf_nat_freepolicy() will
|
||||
* drop the reference.
|
||||
*/
|
||||
npf_ruleset_sharepm(oldset, np);
|
||||
|
||||
/* Does it match with any policy in the active ruleset? */
|
||||
LIST_FOREACH(actrl, &oldset->rs_all, r_aentry) {
|
||||
if (!actrl->r_natp)
|
||||
|
@ -575,31 +569,8 @@ npf_ruleset_reload(npf_t *npf, npf_ruleset_t *newset,
|
|||
}
|
||||
|
||||
/*
|
||||
* npf_ruleset_sharepm: attempt to share the active NAT portmap.
|
||||
* npf_ruleset_findnat: find a NAT policy in the ruleset by a given ID.
|
||||
*/
|
||||
npf_rule_t *
|
||||
npf_ruleset_sharepm(npf_ruleset_t *rlset, npf_natpolicy_t *mnp)
|
||||
{
|
||||
npf_natpolicy_t *np;
|
||||
npf_rule_t *rl;
|
||||
|
||||
/*
|
||||
* Scan the NAT policies in the ruleset and match with the
|
||||
* given policy based on the translation IP address. If they
|
||||
* match - adjust the given NAT policy to use the active NAT
|
||||
* portmap. In such case the reference on the old portmap is
|
||||
* dropped and acquired on the active one.
|
||||
*/
|
||||
LIST_FOREACH(rl, &rlset->rs_all, r_aentry) {
|
||||
np = rl->r_natp;
|
||||
if (np == NULL || np == mnp)
|
||||
continue;
|
||||
if (npf_nat_sharepm(np, mnp))
|
||||
break;
|
||||
}
|
||||
return rl;
|
||||
}
|
||||
|
||||
npf_natpolicy_t *
|
||||
npf_ruleset_findnat(npf_ruleset_t *rlset, uint64_t id)
|
||||
{
|
||||
|
@ -664,7 +635,7 @@ npf_rule_alloc(npf_t *npf, const nvlist_t *rule)
|
|||
|
||||
if (NPF_DYNAMIC_RULE_P(rl->r_attr)) {
|
||||
/* Priority of the dynamic rule. */
|
||||
rl->r_priority = dnvlist_get_number(rule, "prio", 0);
|
||||
rl->r_priority = (int)dnvlist_get_number(rule, "prio", 0);
|
||||
} else {
|
||||
/* The skip-to index. No need to validate it. */
|
||||
rl->r_skip_to = dnvlist_get_number(rule, "skip-to", 0);
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.21 2018/10/29 15:37:06 christos Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.22 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
|
@ -53,6 +53,16 @@ __KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.21 2018/10/29 15:37:06 christos Exp
|
|||
#define NPF_ANY_CONN_ESTABLISHED 2
|
||||
#define NPF_ANY_CONN_NSTATES 3
|
||||
|
||||
/*
|
||||
* Parameters.
|
||||
*/
|
||||
typedef struct {
|
||||
int timeouts[NPF_ANY_CONN_NSTATES];
|
||||
} npf_state_params_t;
|
||||
|
||||
/*
|
||||
* Generic FSM.
|
||||
*/
|
||||
static const uint8_t npf_generic_fsm[NPF_ANY_CONN_NSTATES][2] = {
|
||||
[NPF_ANY_CONN_CLOSED] = {
|
||||
[NPF_FLOW_FORW] = NPF_ANY_CONN_NEW,
|
||||
|
@ -67,12 +77,6 @@ static const uint8_t npf_generic_fsm[NPF_ANY_CONN_NSTATES][2] = {
|
|||
},
|
||||
};
|
||||
|
||||
static u_int npf_generic_timeout[] __read_mostly = {
|
||||
[NPF_ANY_CONN_CLOSED] = 0,
|
||||
[NPF_ANY_CONN_NEW] = 30,
|
||||
[NPF_ANY_CONN_ESTABLISHED] = 60,
|
||||
};
|
||||
|
||||
/*
|
||||
* State sampler for debugging.
|
||||
*/
|
||||
|
@ -83,6 +87,46 @@ static void (*npf_state_sample)(npf_state_t *, bool) = NULL;
|
|||
#define NPF_STATE_SAMPLE(n, r)
|
||||
#endif
|
||||
|
||||
void
|
||||
npf_state_sysinit(npf_t *npf)
|
||||
{
|
||||
npf_state_params_t *params = npf_param_allocgroup(npf,
|
||||
NPF_PARAMS_GENERIC_STATE, sizeof(npf_state_params_t));
|
||||
npf_param_t param_map[] = {
|
||||
/*
|
||||
* Generic timeout (in seconds).
|
||||
*/
|
||||
{
|
||||
"state.generic.timeout.closed",
|
||||
¶ms->timeouts[NPF_ANY_CONN_CLOSED],
|
||||
.default_val = 0,
|
||||
.min = 0, .max = INT_MAX
|
||||
},
|
||||
{
|
||||
"state.generic.timeout.new",
|
||||
¶ms->timeouts[NPF_ANY_CONN_NEW],
|
||||
.default_val = 30,
|
||||
.min = 0, .max = INT_MAX
|
||||
},
|
||||
{
|
||||
"state.generic.timeout.established",
|
||||
¶ms->timeouts[NPF_ANY_CONN_ESTABLISHED],
|
||||
.default_val = 60,
|
||||
.min = 0, .max = INT_MAX
|
||||
},
|
||||
};
|
||||
npf_param_register(npf, param_map, __arraycount(param_map));
|
||||
npf_state_tcp_sysinit(npf);
|
||||
}
|
||||
|
||||
void
|
||||
npf_state_sysfini(npf_t *npf)
|
||||
{
|
||||
const size_t len = sizeof(npf_state_params_t);
|
||||
npf_param_freegroup(npf, NPF_PARAMS_GENERIC_STATE, len);
|
||||
npf_state_tcp_sysfini(npf);
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_state_init: initialise the state structure.
|
||||
*
|
||||
|
@ -158,23 +202,25 @@ npf_state_inspect(npf_cache_t *npc, npf_state_t *nst, const bool forw)
|
|||
}
|
||||
|
||||
/*
|
||||
* npf_state_etime: return connection expiration time according to the state.
|
||||
* npf_state_etime: return the expiration time depending on the state.
|
||||
*/
|
||||
int
|
||||
npf_state_etime(const npf_state_t *nst, const int proto)
|
||||
npf_state_etime(npf_t *npf, const npf_state_t *nst, const int proto)
|
||||
{
|
||||
const u_int state = nst->nst_state;
|
||||
const npf_state_params_t *params;
|
||||
const unsigned state = nst->nst_state;
|
||||
int timeout = 0;
|
||||
|
||||
switch (proto) {
|
||||
case IPPROTO_TCP:
|
||||
/* Pass to TCP state tracking engine. */
|
||||
timeout = npf_state_tcp_timeout(nst);
|
||||
timeout = npf_state_tcp_timeout(npf, nst);
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
case IPPROTO_ICMP:
|
||||
/* Generic. */
|
||||
timeout = npf_generic_timeout[state];
|
||||
params = npf->params[NPF_PARAMS_GENERIC_STATE];
|
||||
timeout = params->timeouts[state];
|
||||
break;
|
||||
default:
|
||||
KASSERT(false);
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_state_tcp.c,v 1.19 2018/09/29 14:41:36 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_state_tcp.c,v 1.20 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -64,34 +64,26 @@ __KERNEL_RCSID(0, "$NetBSD: npf_state_tcp.c,v 1.19 2018/09/29 14:41:36 rmind Exp
|
|||
|
||||
#define NPF_TCP_NSTATES 12
|
||||
|
||||
/* Timeouts */
|
||||
#define NPF_TCPT_NEW 0
|
||||
#define NPF_TCPT_ESTABLISHED 1
|
||||
#define NPF_TCPT_HALFCLOSE 2
|
||||
#define NPF_TCPT_CLOSE 3
|
||||
#define NPF_TCPT_TIMEWAIT 4
|
||||
#define NPF_TCPT_COUNT 5
|
||||
|
||||
/*
|
||||
* TCP connection timeout table (in seconds).
|
||||
* Parameters.
|
||||
*/
|
||||
static u_int npf_tcp_timeouts[] __read_mostly = {
|
||||
/* Closed, timeout nearly immediately. */
|
||||
[NPF_TCPS_CLOSED] = 10,
|
||||
/* Unsynchronised states. */
|
||||
[NPF_TCPS_SYN_SENT] = 30,
|
||||
[NPF_TCPS_SIMSYN_SENT] = 30,
|
||||
[NPF_TCPS_SYN_RECEIVED] = 60,
|
||||
/* Established: 24 hours. */
|
||||
[NPF_TCPS_ESTABLISHED] = 60 * 60 * 24,
|
||||
/* FIN seen: 4 minutes (2 * MSL). */
|
||||
[NPF_TCPS_FIN_SENT] = 60 * 2 * 2,
|
||||
[NPF_TCPS_FIN_RECEIVED] = 60 * 2 * 2,
|
||||
/* Half-closed cases: 6 hours. */
|
||||
[NPF_TCPS_CLOSE_WAIT] = 60 * 60 * 6,
|
||||
[NPF_TCPS_FIN_WAIT] = 60 * 60 * 6,
|
||||
/* Full close cases: 30 sec and 2 * MSL. */
|
||||
[NPF_TCPS_CLOSING] = 30,
|
||||
[NPF_TCPS_LAST_ACK] = 30,
|
||||
[NPF_TCPS_TIME_WAIT] = 60 * 2 * 2,
|
||||
};
|
||||
|
||||
static bool npf_strict_order_rst __read_mostly = true;
|
||||
|
||||
#define NPF_TCP_MAXACKWIN 66000
|
||||
typedef struct {
|
||||
int max_ack_win;
|
||||
int strict_order_rst;
|
||||
int timeouts[NPF_TCPT_COUNT];
|
||||
} npf_state_tcp_params_t;
|
||||
|
||||
/*
|
||||
* Helpers.
|
||||
*/
|
||||
#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
|
||||
#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
|
||||
#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
|
||||
|
@ -108,10 +100,10 @@ static bool npf_strict_order_rst __read_mostly = true;
|
|||
#define TCPFC_FIN 4
|
||||
#define TCPFC_COUNT 5
|
||||
|
||||
static inline u_int
|
||||
npf_tcpfl2case(const u_int tcpfl)
|
||||
static inline unsigned
|
||||
npf_tcpfl2case(const unsigned tcpfl)
|
||||
{
|
||||
u_int i, c;
|
||||
unsigned i, c;
|
||||
|
||||
CTASSERT(TH_FIN == 0x01);
|
||||
CTASSERT(TH_SYN == 0x02);
|
||||
|
@ -299,6 +291,7 @@ static const uint8_t npf_tcp_fsm[NPF_TCP_NSTATES][2][TCPFC_COUNT] = {
|
|||
static bool
|
||||
npf_tcp_inwindow(npf_cache_t *npc, npf_state_t *nst, const int di)
|
||||
{
|
||||
const npf_state_tcp_params_t *params;
|
||||
const struct tcphdr * const th = npc->npc_l4.tcp;
|
||||
const int tcpfl = th->th_flags;
|
||||
npf_tcpstate_t *fstate, *tstate;
|
||||
|
@ -306,6 +299,7 @@ npf_tcp_inwindow(npf_cache_t *npc, npf_state_t *nst, const int di)
|
|||
tcp_seq seq, ack, end;
|
||||
uint32_t win;
|
||||
|
||||
params = npc->npc_ctx->params[NPF_PARAMS_TCP_STATE];
|
||||
KASSERT(npf_iscached(npc, NPC_TCP));
|
||||
KASSERT(di == NPF_FLOW_FORW || di == NPF_FLOW_BACK);
|
||||
|
||||
|
@ -402,7 +396,7 @@ npf_tcp_inwindow(npf_cache_t *npc, npf_state_t *nst, const int di)
|
|||
}
|
||||
|
||||
/* Strict in-order sequence for RST packets (RFC 5961). */
|
||||
if (npf_strict_order_rst && (fstate->nst_end - seq) > 1) {
|
||||
if (params->strict_order_rst && (fstate->nst_end - seq) > 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -427,8 +421,8 @@ npf_tcp_inwindow(npf_cache_t *npc, npf_state_t *nst, const int di)
|
|||
* window up or down, since packets may be fragmented.
|
||||
*/
|
||||
ackskew = tstate->nst_end - ack;
|
||||
if (ackskew < -NPF_TCP_MAXACKWIN ||
|
||||
ackskew > (NPF_TCP_MAXACKWIN << fstate->nst_wscale)) {
|
||||
if (ackskew < -(int)params->max_ack_win ||
|
||||
ackskew > ((int)params->max_ack_win << fstate->nst_wscale)) {
|
||||
npf_stats_inc(npc->npc_ctx, NPF_STAT_INVALID_STATE_TCP3);
|
||||
return false;
|
||||
}
|
||||
|
@ -465,8 +459,8 @@ bool
|
|||
npf_state_tcp(npf_cache_t *npc, npf_state_t *nst, int di)
|
||||
{
|
||||
const struct tcphdr * const th = npc->npc_l4.tcp;
|
||||
const u_int tcpfl = th->th_flags, state = nst->nst_state;
|
||||
u_int nstate;
|
||||
const unsigned tcpfl = th->th_flags, state = nst->nst_state;
|
||||
unsigned nstate;
|
||||
|
||||
KASSERT(nst->nst_state < NPF_TCP_NSTATES);
|
||||
|
||||
|
@ -494,10 +488,106 @@ npf_state_tcp(npf_cache_t *npc, npf_state_t *nst, int di)
|
|||
}
|
||||
|
||||
int
|
||||
npf_state_tcp_timeout(const npf_state_t *nst)
|
||||
npf_state_tcp_timeout(npf_t *npf, const npf_state_t *nst)
|
||||
{
|
||||
const u_int state = nst->nst_state;
|
||||
static const uint8_t state_timeout_idx[NPF_TCP_NSTATES] = {
|
||||
[NPF_TCPS_CLOSED] = NPF_TCPT_CLOSE,
|
||||
/* Unsynchronised states. */
|
||||
[NPF_TCPS_SYN_SENT] = NPF_TCPT_NEW,
|
||||
[NPF_TCPS_SIMSYN_SENT] = NPF_TCPT_NEW,
|
||||
[NPF_TCPS_SYN_RECEIVED] = NPF_TCPT_NEW,
|
||||
/* Established (synchronised state). */
|
||||
[NPF_TCPS_ESTABLISHED] = NPF_TCPT_ESTABLISHED,
|
||||
/* Half-closed cases. */
|
||||
[NPF_TCPS_FIN_SENT] = NPF_TCPT_HALFCLOSE,
|
||||
[NPF_TCPS_FIN_RECEIVED] = NPF_TCPT_HALFCLOSE,
|
||||
[NPF_TCPS_CLOSE_WAIT] = NPF_TCPT_HALFCLOSE,
|
||||
[NPF_TCPS_FIN_WAIT] = NPF_TCPT_HALFCLOSE,
|
||||
/* Full close cases. */
|
||||
[NPF_TCPS_CLOSING] = NPF_TCPT_CLOSE,
|
||||
[NPF_TCPS_LAST_ACK] = NPF_TCPT_CLOSE,
|
||||
[NPF_TCPS_TIME_WAIT] = NPF_TCPT_TIMEWAIT,
|
||||
};
|
||||
const npf_state_tcp_params_t *params;
|
||||
const unsigned state = nst->nst_state;
|
||||
|
||||
KASSERT(state < NPF_TCP_NSTATES);
|
||||
return npf_tcp_timeouts[state];
|
||||
params = npf->params[NPF_PARAMS_TCP_STATE];
|
||||
return params->timeouts[state_timeout_idx[state]];
|
||||
}
|
||||
|
||||
void
|
||||
npf_state_tcp_sysinit(npf_t *npf)
|
||||
{
|
||||
npf_state_tcp_params_t *params = npf_param_allocgroup(npf,
|
||||
NPF_PARAMS_TCP_STATE, sizeof(npf_state_tcp_params_t));
|
||||
npf_param_t param_map[] = {
|
||||
/*
|
||||
* TCP connection timeout table (in seconds).
|
||||
*/
|
||||
|
||||
/* Unsynchronised states. */
|
||||
{
|
||||
"state.tcp.timeout.new",
|
||||
¶ms->timeouts[NPF_TCPT_NEW],
|
||||
.default_val = 30,
|
||||
.min = 0, .max = INT_MAX
|
||||
},
|
||||
/* Established. */
|
||||
{
|
||||
"state.tcp.timeout.established",
|
||||
¶ms->timeouts[NPF_TCPT_ESTABLISHED],
|
||||
.default_val = 60 * 60 * 24,
|
||||
.min = 0, .max = INT_MAX
|
||||
},
|
||||
/* Half-closed cases. */
|
||||
{
|
||||
"state.tcp.timeout.half_close",
|
||||
¶ms->timeouts[NPF_TCPT_HALFCLOSE],
|
||||
.default_val = 60 * 60 * 6,
|
||||
.min = 0, .max = INT_MAX
|
||||
},
|
||||
/* Full close cases. */
|
||||
{
|
||||
"state.tcp.timeout.close",
|
||||
¶ms->timeouts[NPF_TCPT_CLOSE],
|
||||
.default_val = 10,
|
||||
.min = 0, .max = INT_MAX
|
||||
},
|
||||
/* TCP time-wait (2 * MSL). */
|
||||
{
|
||||
"state.tcp.timeout.time_wait",
|
||||
¶ms->timeouts[NPF_TCPT_TIMEWAIT],
|
||||
.default_val = 60 * 2 * 2,
|
||||
.min = 0, .max = INT_MAX
|
||||
},
|
||||
|
||||
/*
|
||||
* Enforce strict order RST.
|
||||
*/
|
||||
{
|
||||
"state.tcp.strict_order_rst",
|
||||
¶ms->strict_order_rst,
|
||||
.default_val = 1, // true
|
||||
.min = 0, .max = 1
|
||||
},
|
||||
|
||||
/*
|
||||
* TCP state tracking: maximum allowed ACK window.
|
||||
*/
|
||||
{
|
||||
"state.tcp.max_ack_win",
|
||||
¶ms->max_ack_win,
|
||||
.default_val = 66000,
|
||||
.min = 0, .max = INT_MAX
|
||||
},
|
||||
};
|
||||
npf_param_register(npf, param_map, __arraycount(param_map));
|
||||
}
|
||||
|
||||
void
|
||||
npf_state_tcp_sysfini(npf_t *npf)
|
||||
{
|
||||
const size_t len = sizeof(npf_state_tcp_params_t);
|
||||
npf_param_freegroup(npf, NPF_PARAMS_TCP_STATE, len);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2009-2018 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2009-2019 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
|
@ -35,11 +35,18 @@
|
|||
* The tableset is an array of tables. After the creation, the array
|
||||
* is immutable. The caller is responsible to synchronise the access
|
||||
* to the tableset.
|
||||
*
|
||||
* Warning (not applicable for the userspace npfkern):
|
||||
*
|
||||
* The thmap_put()/thmap_del() are not called from the interrupt
|
||||
* context and are protected by a mutex(9), therefore they do not
|
||||
* SPL wrappers -- see the comment at the top of the npf_conndb.c
|
||||
* source file.
|
||||
*/
|
||||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.32 2019/06/20 17:12:37 christos Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.33 2019/07/23 00:52:01 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -475,26 +482,6 @@ npf_table_check(npf_tableset_t *ts, const char *name, uint64_t tid, uint64_t typ
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
table_cidr_check(int alen, const npf_addr_t *addr, npf_netmask_t mask)
|
||||
{
|
||||
switch (alen) {
|
||||
case sizeof(struct in_addr):
|
||||
if (__predict_false(mask > 32 && mask != NPF_NO_NETMASK)) {
|
||||
return EINVAL;
|
||||
}
|
||||
break;
|
||||
case sizeof(struct in6_addr):
|
||||
if (__predict_false(mask > 128 && mask != NPF_NO_NETMASK)) {
|
||||
return EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
table_ifaddr_insert(npf_table_t *t, const int alen, npf_tblent_t *ent)
|
||||
{
|
||||
|
@ -543,7 +530,7 @@ npf_table_insert(npf_table_t *t, const int alen,
|
|||
npf_tblent_t *ent;
|
||||
int error;
|
||||
|
||||
error = table_cidr_check(alen, addr, mask);
|
||||
error = npf_netmask_check(alen, mask);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
@ -621,7 +608,7 @@ npf_table_remove(npf_table_t *t, const int alen,
|
|||
npf_tblent_t *ent = NULL;
|
||||
int error;
|
||||
|
||||
error = table_cidr_check(alen, addr, mask);
|
||||
error = npf_netmask_check(alen, mask);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
@ -678,7 +665,7 @@ npf_table_lookup(npf_table_t *t, const int alen, const npf_addr_t *addr)
|
|||
bool found;
|
||||
int error;
|
||||
|
||||
error = table_cidr_check(alen, addr, NPF_NO_NETMASK);
|
||||
error = npf_netmask_check(alen, NPF_NO_NETMASK);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -74,6 +74,16 @@ void npf_thread_unregister(npf_t *);
|
|||
int npf_packet_handler(npf_t *, struct mbuf **, struct ifnet *, int);
|
||||
void npf_ifmap_attach(npf_t *, struct ifnet *);
|
||||
void npf_ifmap_detach(npf_t *, struct ifnet *);
|
||||
int npf_param_get(npf_t *, const char *, int *);
|
||||
int npf_param_set(npf_t *, const char *, int);
|
||||
void npf_stats(npf_t *, uint64_t *);
|
||||
void npf_stats_clear(npf_t *);
|
||||
|
||||
/*
|
||||
* ALGs.
|
||||
*/
|
||||
|
||||
int npf_alg_icmp_init(npf_t *);
|
||||
int npf_alg_icmp_fini(npf_t *);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: Makefile,v 1.26 2018/10/29 15:37:45 christos Exp $
|
||||
# $NetBSD: Makefile,v 1.27 2019/07/23 00:52:02 rmind Exp $
|
||||
#
|
||||
# Public Domain.
|
||||
#
|
||||
|
@ -15,7 +15,8 @@ COMMENT=NPF packet filter
|
|||
IOCONF= NPF.ioconf
|
||||
SRCS= npf.c npf_alg.c npf_conf.c npf_ctl.c npf_handler.c
|
||||
SRCS+= npf_bpf.c npf_if.c npf_inet.c npf_mbuf.c npf_nat.c
|
||||
SRCS+= npf_ruleset.c npf_conn.c npf_conndb.c npf_rproc.c
|
||||
SRCS+= npf_params.c npf_ruleset.c npf_rproc.c
|
||||
SRCS+= npf_conn.c npf_conndb.c npf_connkey.c npf_portmap.c
|
||||
SRCS+= npf_state.c npf_state_tcp.c npf_tableset.c
|
||||
SRCS+= lpm.c npf_sendpkt.c npf_worker.c npf_os.c npf_ifaddr.c
|
||||
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
.\"
|
||||
.\" Copyright (c) 2019 Mindaugas Rasiukevicius <rmind at netbsd org>
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd February 26, 2019
|
||||
.Dt NPF-PARAMS 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm npf-params
|
||||
.Nd tunable NPF parameters
|
||||
.Sh DESCRIPTION
|
||||
NPF supports a set of dynamically tunable parameters.
|
||||
.Pp
|
||||
All parameter values are integers and should generally be between and INT_MAX,
|
||||
unless specified otherwise.
|
||||
Some parameters values can be negative; such values would typically
|
||||
have a special meaning.
|
||||
Enable/disable switches should be represented as boolean values 0 ("off")
|
||||
or 1 ("on").
|
||||
.Sh PARAMETERS
|
||||
.Bl -tag -width "123456"
|
||||
.\" ---
|
||||
.Bl -tag -width "123456"
|
||||
.It Li bpf.jit
|
||||
BPF just-in time compilation.
|
||||
Default: 1.
|
||||
.El
|
||||
.\" ---
|
||||
.Bl -tag -width "123456"
|
||||
.It Li gc.step
|
||||
Number of connection state items to process in one garbage collection
|
||||
(G/C) cycle.
|
||||
Must be positive number.
|
||||
Default: 256.
|
||||
.El
|
||||
.\" ---
|
||||
.It Li state.generic
|
||||
Generic state tracking parameters for non-TCP flows.
|
||||
All timeouts are in seconds and must be zero or positive.
|
||||
.Bl -tag -width "123456"
|
||||
.It Li timeout.new
|
||||
Timeout for new ("unsynchronized") state.
|
||||
Default: 30.
|
||||
.It Li timeout.established
|
||||
Timeout for established ("synchronized") state.
|
||||
Default: 60.
|
||||
.It Li timeout.closed
|
||||
Timeout for closed state.
|
||||
Default: 0.
|
||||
.El
|
||||
.\" ---
|
||||
.It Li state.tcp
|
||||
State tracking parameters for TCP connections.
|
||||
All timeout values are in seconds.
|
||||
.Bl -tag -width "123456"
|
||||
.It Li max_ack_win
|
||||
Maximum allowed ACK window.
|
||||
Default: 66000.
|
||||
.It Li strict_order_rst
|
||||
Enforce strict order RST.
|
||||
Default: 1.
|
||||
.\" -
|
||||
.It Li timeout.new
|
||||
Timeout for a new connection in "unsynchronized" state.
|
||||
Default: 30.
|
||||
.It Li timeout.established
|
||||
Timeout for an established connection ("synchronized" state).
|
||||
Default: 86400.
|
||||
.It Li timeout.half_close
|
||||
Timeout for the half-close TCP states.
|
||||
Default: 3600.
|
||||
.It Li timeout.close
|
||||
Timeout for the full close TCP states.
|
||||
Default: 10.
|
||||
.It Li timeout.time_wait
|
||||
Timeout for the TCP time-wait state.
|
||||
Default: 240.
|
||||
.El
|
||||
.\" ---
|
||||
.It Li portmap.min_port
|
||||
Lower bound of the port range used when selecting the port for dynamic NAT
|
||||
with port translation enabled.
|
||||
Default: 1024 (also the lowest allowed value).
|
||||
.It Li portmap.max_port
|
||||
Upper bound of the port range as described above.
|
||||
Default: 65535 (also the highest allowed value).
|
||||
.\" ---
|
||||
.El
|
||||
.\" -----
|
||||
.Sh EXAMPLES
|
||||
An example line in the
|
||||
.Xr npf.conf 5
|
||||
configuration file:
|
||||
.Bd -literal -offset indent
|
||||
set state.tcp.strict_order_rst on # "on" can be used instead of 1
|
||||
set state.tcp.timeout.time_wait 0 # destroy the state immediately
|
||||
.Ed
|
||||
.\" -----
|
||||
.Sh SEE ALSO
|
||||
.Xr libnpf 3 ,
|
||||
.Xr npfkern 3 ,
|
||||
.Xr npf.conf 5 ,
|
||||
.Xr pcap-filter 7 ,
|
||||
.Xr npfctl 8
|
||||
.\" -----
|
||||
.Sh AUTHORS
|
||||
NPF
|
||||
was designed and implemented by
|
||||
.An Mindaugas Rasiukevicius .
|
|
@ -1,6 +1,6 @@
|
|||
.\" $NetBSD: npf.conf.5,v 1.86 2019/04/08 07:58:45 wiz Exp $
|
||||
.\" $NetBSD: npf.conf.5,v 1.87 2019/07/23 00:52:02 rmind Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2009-2018 The NetBSD Foundation, Inc.
|
||||
.\" Copyright (c) 2009-2019 The NetBSD Foundation, Inc.
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" This material is based upon work partially supported by The
|
||||
|
@ -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 February 2, 2019
|
||||
.Dd May 19, 2019
|
||||
.Dt NPF.CONF 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -61,7 +61,9 @@ map rules for address translation
|
|||
.It
|
||||
application level gateways
|
||||
.It
|
||||
procedure definitions to call on filtered packets.
|
||||
procedure definitions to call on filtered packets
|
||||
.It
|
||||
parameter settings.
|
||||
.El
|
||||
.Sh SYNTAX
|
||||
.Ss Variables
|
||||
|
@ -297,12 +299,12 @@ before further processing.
|
|||
Stateful packet inspection is enabled using the
|
||||
.Cm stateful
|
||||
or
|
||||
.Cm stateful-ends
|
||||
.Cm stateful-all
|
||||
keywords.
|
||||
The former creates a state which is uniquely identified by a 5-tuple (source
|
||||
and destination IP addresses, port numbers and an interface identifier).
|
||||
The latter excludes the interface identifier and must be used with
|
||||
precaution.
|
||||
The latter excludes the interface identifier, i.e. making the state global,
|
||||
and must be used with precaution.
|
||||
In both cases, a full TCP state tracking is performed for TCP connections
|
||||
and a limited tracking for message-based protocols (UDP and ICMP).
|
||||
.Pp
|
||||
|
@ -354,8 +356,9 @@ redirecting the public port 9022 to the port 22 of an internal host:
|
|||
.Pp
|
||||
.Dl map $ext_if dynamic proto tcp 10.1.1.2 port 22 <- $ext_if port 9022
|
||||
.Pp
|
||||
The translation address can also by dynamic, based on the interface.
|
||||
The following would select the IPv4 address(es) currently assigned to the interface:
|
||||
The translation address can also be dynamic, based on the interface.
|
||||
The following would select the IPv4 address(es) currently assigned to the
|
||||
interface:
|
||||
.Pp
|
||||
.Dl map $ext_if dynamic 10.1.1.0/24 -> ifaddrs($ext_if)
|
||||
.Pp
|
||||
|
@ -363,7 +366,7 @@ If the dynamic NAT is configured with multiple translation addresses,
|
|||
then a custom selection algorithm can be chosen using the
|
||||
.Cm algo
|
||||
keyword.
|
||||
The currently available algorithms are:
|
||||
The currently available algorithms for the dynamic translation are:
|
||||
.Bl -tag -width "Cm round-robin" -offset indent
|
||||
.It Cm ip-hash
|
||||
The translation address for a new connection is selected based on a
|
||||
|
@ -375,6 +378,8 @@ This is the default algorithm.
|
|||
.It Cm round-robin
|
||||
The translation address for each new connection is selected on a
|
||||
round-robin basis.
|
||||
.It Cm netmap
|
||||
See the description below.
|
||||
.El
|
||||
.Pp
|
||||
The static NAT can also have different address translation algorithms,
|
||||
|
@ -476,6 +481,10 @@ procedure "someproc" {
|
|||
.Ed
|
||||
.Pp
|
||||
In this case, the procedure calls the logging and normalization modules.
|
||||
.Ss Parameter settings
|
||||
NPF supports a set of dynamically tunable parameters. See
|
||||
.Xr npf-params 7
|
||||
for specific details.
|
||||
.Ss Misc
|
||||
Text after a hash
|
||||
.Pq Sq #
|
||||
|
@ -547,7 +556,7 @@ rule-list = [ rule new-line ] rule-list
|
|||
|
||||
npf-filter = [ "family" family-opt ] [ proto ] ( "all" | filt-opts )
|
||||
static-rule = ( "block" [ block-opts ] | "pass" )
|
||||
[ "stateful" | "stateful-ends" ]
|
||||
[ "stateful" | "stateful-all" ]
|
||||
[ "in" | "out" ] [ "final" ] [ "on" interface ]
|
||||
( npf-filter | "pcap-filter" pcap-filter-expr )
|
||||
[ "apply" proc-name ]
|
||||
|
@ -639,6 +648,7 @@ group default {
|
|||
.Sh SEE ALSO
|
||||
.Xr bpf 4 ,
|
||||
.Xr npf 7 ,
|
||||
.Xr npf-params 7 ,
|
||||
.Xr pcap-filter 7 ,
|
||||
.Xr npfctl 8 ,
|
||||
.Xr npfd 8
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: npf_bpf_comp.c,v 1.12 2019/04/17 20:41:58 tih Exp $");
|
||||
__RCSID("$NetBSD: npf_bpf_comp.c,v 1.13 2019/07/23 00:52:02 rmind Exp $");
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2011-2018 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2011-2019 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: npf_build.c,v 1.48 2019/04/17 20:41:58 tih Exp $");
|
||||
__RCSID("$NetBSD: npf_build.c,v 1.49 2019/07/23 00:52:02 rmind Exp $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#define __FAVOR_BSD
|
||||
|
@ -155,18 +155,18 @@ unsigned
|
|||
npfctl_table_getid(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;
|
||||
}
|
||||
|
||||
/* XXX: Iterating all as we need to rewind for the next call. */
|
||||
while ((tl = npf_table_iterate(npf_conf)) != NULL) {
|
||||
while ((tl = npf_table_iterate(npf_conf, &i)) != NULL) {
|
||||
const char *tname = npf_table_getname(tl);
|
||||
if (strcmp(tname, name) == 0) {
|
||||
tid = npf_table_getid(tl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return tid;
|
||||
|
@ -176,12 +176,13 @@ const char *
|
|||
npfctl_table_getname(nl_config_t *ncf, unsigned tid, bool *ifaddr)
|
||||
{
|
||||
const char *name = NULL;
|
||||
nl_iter_t i = NPF_ITER_BEGIN;
|
||||
nl_table_t *tl;
|
||||
|
||||
/* XXX: Iterating all as we need to rewind for the next call. */
|
||||
while ((tl = npf_table_iterate(ncf)) != NULL) {
|
||||
while ((tl = npf_table_iterate(ncf, &i)) != NULL) {
|
||||
if (npf_table_getid(tl) == tid) {
|
||||
name = npf_table_getname(tl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!name) {
|
||||
|
@ -444,7 +445,7 @@ npfctl_build_code(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
|
|||
}
|
||||
len = bf->bf_len * sizeof(struct bpf_insn);
|
||||
|
||||
if (npf_rule_setcode(rl, NPF_CODE_BPF, bf->bf_insns, len) == -1) {
|
||||
if (npf_rule_setcode(rl, NPF_CODE_BPF, bf->bf_insns, len) != 0) {
|
||||
errx(EXIT_FAILURE, "npf_rule_setcode failed");
|
||||
}
|
||||
npfctl_dump_bpf(bf);
|
||||
|
@ -466,7 +467,7 @@ npfctl_build_pcap(nl_rule_t *rl, const char *filter)
|
|||
}
|
||||
len = bf.bf_len * sizeof(struct bpf_insn);
|
||||
|
||||
if (npf_rule_setcode(rl, NPF_CODE_BPF, bf.bf_insns, len) == -1) {
|
||||
if (npf_rule_setcode(rl, NPF_CODE_BPF, bf.bf_insns, len) != 0) {
|
||||
errx(EXIT_FAILURE, "npf_rule_setcode failed");
|
||||
}
|
||||
npfctl_dump_bpf(&bf);
|
||||
|
@ -542,7 +543,8 @@ npfctl_build_maprset(const char *name, int attr, const char *ifname)
|
|||
/* Allow only "in/out" attributes. */
|
||||
attr = NPF_RULE_GROUP | NPF_RULE_DYNAMIC | (attr & attr_di);
|
||||
rl = npf_rule_create(name, attr, ifname);
|
||||
npf_nat_insert(npf_conf, rl, NPF_PRI_LAST);
|
||||
npf_rule_setprio(rl, NPF_PRI_LAST);
|
||||
npf_nat_insert(npf_conf, rl);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -637,7 +639,7 @@ npfctl_build_rule(uint32_t attr, const char *ifname, sa_family_t family,
|
|||
*/
|
||||
static nl_nat_t *
|
||||
npfctl_build_nat(int type, const char *ifname, const addr_port_t *ap,
|
||||
const opt_proto_t *op, const filt_opts_t *fopts, u_int flags)
|
||||
const opt_proto_t *op, const filt_opts_t *fopts, unsigned flags)
|
||||
{
|
||||
const opt_proto_t def_op = { .op_proto = -1, .op_opts = NULL };
|
||||
fam_addr_mask_t *am;
|
||||
|
@ -685,6 +687,38 @@ npfctl_build_nat(int type, const char *ifname, const addr_port_t *ap,
|
|||
return nat;
|
||||
}
|
||||
|
||||
static void
|
||||
npfctl_dnat_check(const addr_port_t *ap, const unsigned algo)
|
||||
{
|
||||
int type = npfvar_get_type(ap->ap_netaddr, 0);
|
||||
fam_addr_mask_t *am;
|
||||
|
||||
switch (algo) {
|
||||
case NPF_ALGO_NETMAP:
|
||||
if (type == NPFVAR_FAM) {
|
||||
break;
|
||||
}
|
||||
yyerror("translation address using NETMAP must be "
|
||||
"a network and not a dynamic pool");
|
||||
break;
|
||||
case NPF_ALGO_IPHASH:
|
||||
case NPF_ALGO_RR:
|
||||
case NPF_ALGO_NONE:
|
||||
if (type != NPFVAR_FAM) {
|
||||
break;
|
||||
}
|
||||
am = npfctl_get_singlefam(ap->ap_netaddr);
|
||||
if (am->fam_mask == NPF_NO_NETMASK) {
|
||||
break;
|
||||
}
|
||||
yyerror("translation address, given the specified algorithm, "
|
||||
"must be a pool or a single address");
|
||||
break;
|
||||
default:
|
||||
yyerror("invalid algorithm specified for dynamic NAT");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_build_natseg: validate and create NAT policies.
|
||||
*/
|
||||
|
@ -726,14 +760,11 @@ npfctl_build_natseg(int sd, int type, unsigned mflags, const char *ifname,
|
|||
* the port mapping.
|
||||
*/
|
||||
flags = !binat ? (NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0;
|
||||
|
||||
switch (algo) {
|
||||
case NPF_ALGO_IPHASH:
|
||||
case NPF_ALGO_RR:
|
||||
case NPF_ALGO_NONE:
|
||||
break;
|
||||
default:
|
||||
yyerror("invalid algorithm specified for dynamic NAT");
|
||||
if (type & NPF_NATIN) {
|
||||
npfctl_dnat_check(ap1, algo);
|
||||
}
|
||||
if (type & NPF_NATOUT) {
|
||||
npfctl_dnat_check(ap2, algo);
|
||||
}
|
||||
break;
|
||||
case NPFCTL_NAT_STATIC:
|
||||
|
@ -804,7 +835,10 @@ npfctl_build_natseg(int sd, int type, unsigned mflags, const char *ifname,
|
|||
nt2 = npfctl_build_nat(NPF_NATOUT, ifname, ap2, op, fopts, flags);
|
||||
}
|
||||
|
||||
if (algo == NPF_ALGO_NPT66) {
|
||||
switch (algo) {
|
||||
case NPF_ALGO_NONE:
|
||||
break;
|
||||
case NPF_ALGO_NPT66:
|
||||
/*
|
||||
* NPTv6 is a special case using special adjustment value.
|
||||
* It is always bidirectional NAT.
|
||||
|
@ -812,7 +846,8 @@ npfctl_build_natseg(int sd, int type, unsigned mflags, const char *ifname,
|
|||
assert(nt1 && nt2);
|
||||
npf_nat_setnpt66(nt1, ~adj);
|
||||
npf_nat_setnpt66(nt2, adj);
|
||||
} else if (algo) {
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* Set the algorithm.
|
||||
*/
|
||||
|
@ -825,10 +860,12 @@ npfctl_build_natseg(int sd, int type, unsigned mflags, const char *ifname,
|
|||
}
|
||||
|
||||
if (nt1) {
|
||||
npf_nat_insert(npf_conf, nt1, NPF_PRI_LAST);
|
||||
npf_rule_setprio(nt1, NPF_PRI_LAST);
|
||||
npf_nat_insert(npf_conf, nt1);
|
||||
}
|
||||
if (nt2) {
|
||||
npf_nat_insert(npf_conf, nt2, NPF_PRI_LAST);
|
||||
npf_rule_setprio(nt2, NPF_PRI_LAST);
|
||||
npf_nat_insert(npf_conf, nt2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -922,8 +959,19 @@ npfctl_ifnet_table(const char *ifname)
|
|||
void
|
||||
npfctl_build_alg(const char *al_name)
|
||||
{
|
||||
if (_npf_alg_load(npf_conf, al_name) != 0) {
|
||||
errx(EXIT_FAILURE, "ALG '%s' already loaded", al_name);
|
||||
if (npf_alg_load(npf_conf, al_name) != 0) {
|
||||
yyerror("ALG '%s' is already loaded", al_name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
npfctl_setparam(const char *name, int val)
|
||||
{
|
||||
if (strcmp(name, "bpf.jit") == 0) {
|
||||
npfctl_bpfjit(val != 0);
|
||||
}
|
||||
if (npf_param_set(npf_conf, name, val) != 0) {
|
||||
yyerror("invalid parameter `%s` or its value", name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2011-2017 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2011-2019 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
|
@ -103,7 +103,6 @@ yyerror(const char *fmt, ...)
|
|||
%token ARROWLEFT
|
||||
%token ARROWRIGHT
|
||||
%token BLOCK
|
||||
%token BPFJIT
|
||||
%token CDB
|
||||
%token CONST
|
||||
%token CURLY_CLOSE
|
||||
|
@ -159,7 +158,7 @@ yyerror(const char *fmt, ...)
|
|||
%token SET
|
||||
%token SLASH
|
||||
%token STATEFUL
|
||||
%token STATEFUL_ENDS
|
||||
%token STATEFUL_ALL
|
||||
%token TABLE
|
||||
%token TCP
|
||||
%token TO
|
||||
|
@ -175,6 +174,7 @@ yyerror(const char *fmt, ...)
|
|||
%token <num> NUM
|
||||
%token <fpnum> FPNUM
|
||||
%token <str> STRING
|
||||
%token <str> PARAM
|
||||
%token <str> TABLE_ID
|
||||
%token <str> VAR_ID
|
||||
|
||||
|
@ -184,6 +184,7 @@ yyerror(const char *fmt, ...)
|
|||
%type <num> block_or_pass rule_dir group_dir block_opts
|
||||
%type <num> maybe_not opt_stateful icmp_type table_type
|
||||
%type <num> map_sd map_algo map_flags map_type
|
||||
%type <num> param_val
|
||||
%type <var> static_ifaddrs addr_or_ifaddr
|
||||
%type <var> port_range icmp_type_and_code
|
||||
%type <var> filt_addr addr_and_mask tcp_flags tcp_flags_and_mask
|
||||
|
@ -193,11 +194,9 @@ yyerror(const char *fmt, ...)
|
|||
%type <filtopts> filt_opts all_or_filt_opts
|
||||
%type <optproto> proto opt_proto
|
||||
%type <rulegroup> group_opts
|
||||
%type <tf> onoff
|
||||
|
||||
%union {
|
||||
char * str;
|
||||
bool tf;
|
||||
unsigned long num;
|
||||
double fpnum;
|
||||
npfvar_t * var;
|
||||
|
@ -237,18 +236,15 @@ alg
|
|||
}
|
||||
;
|
||||
|
||||
onoff
|
||||
: ON {
|
||||
$$ = true;
|
||||
}
|
||||
| OFF {
|
||||
$$ = false;
|
||||
}
|
||||
param_val
|
||||
: number { $$ = $1; }
|
||||
| ON { $$ = true; }
|
||||
| OFF { $$ = false; }
|
||||
;
|
||||
|
||||
set
|
||||
: SET BPFJIT onoff {
|
||||
npfctl_bpfjit($3);
|
||||
: SET PARAM param_val {
|
||||
npfctl_setparam($2, $3);
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -647,7 +643,7 @@ all_or_filt_opts
|
|||
|
||||
opt_stateful
|
||||
: STATEFUL { $$ = NPF_RULE_STATEFUL; }
|
||||
| STATEFUL_ENDS { $$ = NPF_RULE_STATEFUL | NPF_RULE_MULTIENDS; }
|
||||
| STATEFUL_ALL { $$ = NPF_RULE_STATEFUL | NPF_RULE_GSTATEFUL; }
|
||||
| { $$ = 0; }
|
||||
;
|
||||
|
||||
|
@ -914,9 +910,12 @@ ifref
|
|||
| dynamic_ifaddrs
|
||||
| static_ifaddrs
|
||||
{
|
||||
if (npfvar_get_count($1) != 1)
|
||||
ifnet_addr_t *ifna;
|
||||
|
||||
if (npfvar_get_count($1) != 1) {
|
||||
yyerror("multiple interfaces are not supported");
|
||||
ifnet_addr_t *ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0);
|
||||
}
|
||||
ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0);
|
||||
npfctl_note_interface(ifna->ifna_name);
|
||||
$$ = ifna->ifna_name;
|
||||
}
|
||||
|
|
|
@ -125,13 +125,12 @@ block return BLOCK;
|
|||
pass return PASS;
|
||||
pcap-filter return PCAP_FILTER;
|
||||
stateful return STATEFUL;
|
||||
stateful-ends return STATEFUL_ENDS;
|
||||
stateful-all return STATEFUL_ALL;
|
||||
apply return APPLY;
|
||||
final return FINAL;
|
||||
quick return FINAL;
|
||||
on return ON;
|
||||
off return OFF;
|
||||
bpf.jit return BPFJIT;
|
||||
inet6 return INET6;
|
||||
inet4 return INET4;
|
||||
ifaddrs return IFADDRS;
|
||||
|
@ -209,6 +208,11 @@ any return ANY;
|
|||
return VAR_ID;
|
||||
}
|
||||
|
||||
[a-z]*"."[a-z.]* {
|
||||
yylval.str = estrndup(yytext, yyleng);
|
||||
return PARAM;
|
||||
}
|
||||
|
||||
{ID} {
|
||||
yylval.str = estrndup(yytext, yyleng);
|
||||
return IDENTIFIER;
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: npf_show.c,v 1.27 2019/01/19 21:19:32 rmind Exp $");
|
||||
__RCSID("$NetBSD: npf_show.c,v 1.28 2019/07/23 00:52:02 rmind Exp $");
|
||||
|
||||
#include <sys/socket.h>
|
||||
#define __FAVOR_BSD
|
||||
|
@ -246,7 +246,7 @@ print_portrange(npf_conf_info_t *ctx, const uint32_t *words)
|
|||
*/
|
||||
|
||||
#define F(name) __CONCAT(NPF_RULE_, name)
|
||||
#define STATEFUL_ENDS (NPF_RULE_STATEFUL | NPF_RULE_MULTIENDS)
|
||||
#define STATEFUL_ALL (NPF_RULE_STATEFUL | NPF_RULE_GSTATEFUL)
|
||||
#define NAME_AT 2
|
||||
|
||||
static const struct attr_keyword_mapent {
|
||||
|
@ -261,8 +261,8 @@ static const struct attr_keyword_mapent {
|
|||
{ F(RETRST)|F(RETICMP), F(RETRST)|F(RETICMP), "return" },
|
||||
{ F(RETRST)|F(RETICMP), F(RETRST), "return-rst" },
|
||||
{ F(RETRST)|F(RETICMP), F(RETICMP), "return-icmp" },
|
||||
{ STATEFUL_ENDS, F(STATEFUL), "stateful" },
|
||||
{ STATEFUL_ENDS, STATEFUL_ENDS, "stateful-ends" },
|
||||
{ STATEFUL_ALL, F(STATEFUL), "stateful" },
|
||||
{ STATEFUL_ALL, STATEFUL_ALL, "stateful-all" },
|
||||
{ F(DIMASK), F(IN), "in" },
|
||||
{ F(DIMASK), F(OUT), "out" },
|
||||
{ F(FINAL), F(FINAL), "final" },
|
||||
|
@ -436,19 +436,27 @@ out:
|
|||
static void
|
||||
npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt)
|
||||
{
|
||||
const unsigned dynamic_natset = NPF_RULE_GROUP | NPF_RULE_DYNAMIC;
|
||||
nl_rule_t *rl = (nl_nat_t *)nt;
|
||||
const char *ifname, *algo, *seg1, *seg2, *arrow;
|
||||
const npf_addr_t *addr;
|
||||
npf_netmask_t mask;
|
||||
in_port_t port;
|
||||
size_t alen;
|
||||
u_int flags;
|
||||
unsigned flags;
|
||||
char *seg;
|
||||
|
||||
/* Get the interface. */
|
||||
/* Get flags and the interface. */
|
||||
flags = npf_nat_getflags(nt);
|
||||
ifname = npf_rule_getinterface(rl);
|
||||
assert(ifname != NULL);
|
||||
|
||||
if ((npf_rule_getattr(rl) & dynamic_natset) == dynamic_natset) {
|
||||
const char *name = npf_rule_getname(rl);
|
||||
fprintf(ctx->fp, "map ruleset \"%s\" on %s\n", name, ifname);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the translation address or table (and port, if used). */
|
||||
addr = npf_nat_getaddr(nt, &alen, &mask);
|
||||
if (addr) {
|
||||
|
@ -482,7 +490,6 @@ npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt)
|
|||
default:
|
||||
abort();
|
||||
}
|
||||
flags = npf_nat_getflags(nt);
|
||||
|
||||
/* NAT algorithm. */
|
||||
switch (npf_nat_getalgo(nt)) {
|
||||
|
@ -564,25 +571,30 @@ npfctl_config_show(int fd)
|
|||
nl_rproc_t *rp;
|
||||
nl_nat_t *nt;
|
||||
nl_table_t *tl;
|
||||
nl_iter_t i;
|
||||
unsigned level;
|
||||
|
||||
while ((tl = npf_table_iterate(ncf)) != NULL) {
|
||||
i = NPF_ITER_BEGIN;
|
||||
while ((tl = npf_table_iterate(ncf, &i)) != NULL) {
|
||||
npfctl_print_table(ctx, tl);
|
||||
}
|
||||
print_linesep(ctx);
|
||||
|
||||
while ((rp = npf_rproc_iterate(ncf)) != NULL) {
|
||||
i = NPF_ITER_BEGIN;
|
||||
while ((rp = npf_rproc_iterate(ncf, &i)) != NULL) {
|
||||
const char *rpname = npf_rproc_getname(rp);
|
||||
fprintf(ctx->fp, "procedure \"%s\"\n", rpname);
|
||||
}
|
||||
print_linesep(ctx);
|
||||
|
||||
while ((nt = npf_nat_iterate(ncf)) != NULL) {
|
||||
i = NPF_ITER_BEGIN;
|
||||
while ((nt = npf_nat_iterate(ncf, &i)) != NULL) {
|
||||
npfctl_print_nat(ctx, nt);
|
||||
}
|
||||
print_linesep(ctx);
|
||||
|
||||
while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
|
||||
i = NPF_ITER_BEGIN;
|
||||
while ((rl = npf_rule_iterate(ncf, &i, &level)) != NULL) {
|
||||
print_indent(ctx, level);
|
||||
npfctl_print_rule(ctx, rl);
|
||||
}
|
||||
|
@ -599,6 +611,7 @@ npfctl_ruleset_show(int fd, const char *ruleset_name)
|
|||
nl_config_t *ncf;
|
||||
nl_rule_t *rl;
|
||||
unsigned level;
|
||||
nl_iter_t i;
|
||||
int error;
|
||||
|
||||
ncf = npf_config_create();
|
||||
|
@ -607,7 +620,8 @@ npfctl_ruleset_show(int fd, const char *ruleset_name)
|
|||
if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) {
|
||||
return error;
|
||||
}
|
||||
while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
|
||||
i = NPF_ITER_BEGIN;
|
||||
while ((rl = npf_rule_iterate(ncf, &i, &level)) != NULL) {
|
||||
npfctl_print_rule(ctx, rl);
|
||||
}
|
||||
npf_config_destroy(ncf);
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: npfctl.c,v 1.58 2019/02/03 03:19:31 mrg Exp $");
|
||||
__RCSID("$NetBSD: npfctl.c,v 1.59 2019/07/23 00:52:02 rmind Exp $");
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -230,6 +230,9 @@ npfctl_print_error(const npf_error_t *ne)
|
|||
{
|
||||
const char *srcfile = ne->source_file;
|
||||
|
||||
if (ne->error_msg) {
|
||||
warnx("%s", ne->error_msg);
|
||||
}
|
||||
if (srcfile) {
|
||||
warnx("source %s line %d", srcfile, ne->source_line);
|
||||
}
|
||||
|
@ -585,13 +588,13 @@ npfctl_load(int fd)
|
|||
}
|
||||
|
||||
struct npf_conn_filter {
|
||||
uint16_t alen;
|
||||
const char *ifname;
|
||||
bool nat;
|
||||
bool wide;
|
||||
bool name;
|
||||
int width;
|
||||
FILE *fp;
|
||||
uint16_t alen;
|
||||
const char * ifname;
|
||||
bool nat;
|
||||
bool wide;
|
||||
bool name;
|
||||
int width;
|
||||
FILE * fp;
|
||||
};
|
||||
|
||||
static int
|
||||
|
@ -614,11 +617,13 @@ npfctl_conn_print(unsigned alen, const npf_addr_t *a, const in_port_t *p,
|
|||
(alen == sizeof(struct in_addr) ? "%a" : "[%a]");
|
||||
src = npfctl_print_addrmask(alen, fmt, &a[0], NPF_NO_NETMASK);
|
||||
dst = npfctl_print_addrmask(alen, fmt, &a[1], NPF_NO_NETMASK);
|
||||
if (fil->wide)
|
||||
|
||||
if (fil->wide) {
|
||||
fprintf(fp, "%s:%d %s:%d", src, p[0], dst, p[1]);
|
||||
else
|
||||
} else {
|
||||
fprintf(fp, "%*.*s:%-5d %*.*s:%-5d", w, w, src, p[0],
|
||||
w, w, dst, p[1]);
|
||||
}
|
||||
free(src);
|
||||
free(dst);
|
||||
if (!p[2]) {
|
||||
|
@ -629,15 +634,13 @@ npfctl_conn_print(unsigned alen, const npf_addr_t *a, const in_port_t *p,
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
npfctl_conn_list(int fd, int argc, char **argv)
|
||||
{
|
||||
struct npf_conn_filter f;
|
||||
int c;
|
||||
int header = true;
|
||||
memset(&f, 0, sizeof(f));
|
||||
int c, w, header = true;
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
|
@ -672,12 +675,13 @@ npfctl_conn_list(int fd, int argc, char **argv)
|
|||
}
|
||||
}
|
||||
f.width = f.alen == sizeof(struct in_addr) ? 25 : 41;
|
||||
int w = f.width + 6;
|
||||
w = f.width + 6;
|
||||
f.fp = stdout;
|
||||
if (header)
|
||||
|
||||
if (header) {
|
||||
fprintf(f.fp, "%*.*s %*.*s\n",
|
||||
w, w, "From address:port ", w, w, "To address:port ");
|
||||
|
||||
}
|
||||
npf_conn_list(fd, npfctl_conn_print, &f);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -211,6 +211,8 @@ void npfctl_build_natseg(int, int, unsigned, const char *,
|
|||
void npfctl_build_maprset(const char *, int, const char *);
|
||||
void npfctl_build_table(const char *, u_int, const char *);
|
||||
|
||||
void npfctl_setparam(const char *, int);
|
||||
|
||||
/*
|
||||
* For the systems which do not define TH_ECE and TW_CRW.
|
||||
*/
|
||||
|
|
|
@ -42,48 +42,29 @@
|
|||
|
||||
static bool lverbose = false;
|
||||
|
||||
static struct mbuf *
|
||||
fill_packet(int proto)
|
||||
{
|
||||
struct mbuf *m;
|
||||
struct ip *ip;
|
||||
struct tcphdr *th;
|
||||
|
||||
m = mbuf_construct(proto);
|
||||
th = mbuf_return_hdrs(m, false, &ip);
|
||||
ip->ip_src.s_addr = inet_addr("192.168.2.100");
|
||||
ip->ip_dst.s_addr = inet_addr("10.0.0.1");
|
||||
th->th_sport = htons(15000);
|
||||
th->th_dport = htons(80);
|
||||
return m;
|
||||
}
|
||||
|
||||
static int
|
||||
test_bpf_code(void *code, size_t size)
|
||||
{
|
||||
ifnet_t *dummy_ifp = npf_test_addif(IFNAME_TEST, false, false);
|
||||
npf_cache_t npc = { .npc_info = 0, .npc_ctx = npf_getkernctx() };
|
||||
uint32_t memstore[BPF_MEMWORDS];
|
||||
bpf_args_t bc_args;
|
||||
npf_cache_t *npc;
|
||||
struct mbuf *m;
|
||||
nbuf_t nbuf;
|
||||
int ret, jret;
|
||||
void *jcode;
|
||||
|
||||
/* Layer 3 (IP + TCP). */
|
||||
m = fill_packet(IPPROTO_TCP);
|
||||
nbuf_init(npf_getkernctx(), &nbuf, m, dummy_ifp);
|
||||
npc.npc_nbuf = &nbuf;
|
||||
npf_cache_all(&npc);
|
||||
m = mbuf_get_pkt(AF_INET, IPPROTO_TCP,
|
||||
"192.168.2.100", "10.0.0.1", 15000, 80);
|
||||
npc = get_cached_pkt(m, NULL);
|
||||
#ifdef _NPF_STANDALONE
|
||||
bc_args.pkt = (const uint8_t *)nbuf_dataptr(&nbuf);
|
||||
bc_args.pkt = (const uint8_t *)nbuf_dataptr(npc->npc_nbuf);
|
||||
#else
|
||||
bc_args.pkt = (const uint8_t *)m;
|
||||
#endif
|
||||
bc_args.buflen = m_length(m);
|
||||
bc_args.wirelen = bc_args.buflen;
|
||||
bc_args.mem = memstore;
|
||||
bc_args.arg = &npc;
|
||||
bc_args.arg = npc;
|
||||
|
||||
ret = npf_bpf_filter(&bc_args, code, NULL);
|
||||
|
||||
|
@ -96,13 +77,12 @@ test_bpf_code(void *code, size_t size)
|
|||
} else if (lverbose) {
|
||||
printf("JIT-compilation failed\n");
|
||||
}
|
||||
m_freem(m);
|
||||
|
||||
put_cached_pkt(npc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
npf_bpfcop_run(u_int reg)
|
||||
npf_bpfcop_run(unsigned reg)
|
||||
{
|
||||
struct bpf_insn insns_npf_bpfcop[] = {
|
||||
BPF_STMT(BPF_MISC+BPF_COP, NPF_COP_L3),
|
||||
|
@ -115,35 +95,28 @@ npf_bpfcop_run(u_int reg)
|
|||
static bool
|
||||
npf_bpfcop_test(void)
|
||||
{
|
||||
bool fail = false;
|
||||
|
||||
/* A <- IP version (4 or 6) */
|
||||
struct bpf_insn insns_ipver[] = {
|
||||
BPF_STMT(BPF_MISC+BPF_COP, NPF_COP_L3),
|
||||
BPF_STMT(BPF_RET+BPF_A, 0),
|
||||
};
|
||||
fail |= (test_bpf_code(&insns_ipver, sizeof(insns_ipver)) != IPVERSION);
|
||||
CHECK_TRUE(test_bpf_code(&insns_ipver, sizeof(insns_ipver)) == IPVERSION);
|
||||
|
||||
/* BPF_MW_IPVERI <- IP version */
|
||||
fail |= (npf_bpfcop_run(BPF_MW_IPVER) != IPVERSION);
|
||||
CHECK_TRUE(npf_bpfcop_run(BPF_MW_IPVER) == IPVERSION);
|
||||
|
||||
/* BPF_MW_L4OFF <- L4 header offset */
|
||||
fail |= (npf_bpfcop_run(BPF_MW_L4OFF) != sizeof(struct ip));
|
||||
CHECK_TRUE(npf_bpfcop_run(BPF_MW_L4OFF) == sizeof(struct ip));
|
||||
|
||||
/* BPF_MW_L4PROTO <- L4 protocol */
|
||||
fail |= (npf_bpfcop_run(BPF_MW_L4PROTO) != IPPROTO_TCP);
|
||||
CHECK_TRUE(npf_bpfcop_run(BPF_MW_L4PROTO) == IPPROTO_TCP);
|
||||
|
||||
return fail;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
npf_bpf_test(bool verbose)
|
||||
{
|
||||
bool fail = false;
|
||||
|
||||
lverbose = verbose;
|
||||
|
||||
fail |= npf_bpfcop_test();
|
||||
|
||||
return !fail;
|
||||
return npf_bpfcop_test();
|
||||
}
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
|
||||
static bool lverbose = false;
|
||||
|
||||
#define CHECK_TRUE(x) \
|
||||
if (!(x)) { printf("FAIL: %s line %d\n", __func__, __LINE__); return 0; }
|
||||
|
||||
static unsigned
|
||||
count_conns(npf_conndb_t *cd)
|
||||
{
|
||||
|
@ -34,40 +31,32 @@ count_conns(npf_conndb_t *cd)
|
|||
}
|
||||
|
||||
static struct mbuf *
|
||||
fill_packet(unsigned i)
|
||||
get_packet(unsigned i)
|
||||
{
|
||||
struct mbuf *m;
|
||||
struct ip *ip;
|
||||
struct udphdr *uh;
|
||||
|
||||
m = mbuf_construct(IPPROTO_UDP);
|
||||
uh = mbuf_return_hdrs(m, false, &ip);
|
||||
ip->ip_src.s_addr = inet_addr("10.0.0.1") + i;
|
||||
ip->ip_dst.s_addr = inet_addr("172.16.0.1");
|
||||
uh->uh_sport = htons(9000);
|
||||
uh->uh_dport = htons(9000);
|
||||
m = mbuf_get_pkt(AF_INET, IPPROTO_UDP,
|
||||
"10.0.0.1", "172.16.0.1", 9000, 9000);
|
||||
(void)mbuf_return_hdrs(m, false, &ip);
|
||||
ip->ip_src.s_addr += i;
|
||||
return m;
|
||||
}
|
||||
|
||||
static bool
|
||||
enqueue_connection(unsigned i, bool expire)
|
||||
{
|
||||
ifnet_t *dummy_ifp = npf_test_addif(IFNAME_TEST, false, false);
|
||||
npf_cache_t npc = { .npc_info = 0, .npc_ctx = npf_getkernctx() };
|
||||
struct mbuf *m = fill_packet(i);
|
||||
struct mbuf *m = get_packet(i);
|
||||
npf_cache_t *npc = get_cached_pkt(m, NULL);
|
||||
npf_conn_t *con;
|
||||
nbuf_t nbuf;
|
||||
|
||||
nbuf_init(npf_getkernctx(), &nbuf, m, dummy_ifp);
|
||||
npc.npc_nbuf = &nbuf;
|
||||
npf_cache_all(&npc);
|
||||
|
||||
con = npf_conn_establish(&npc, PFIL_IN, true);
|
||||
con = npf_conn_establish(npc, PFIL_IN, true);
|
||||
CHECK_TRUE(con != NULL);
|
||||
if (expire) {
|
||||
npf_conn_expire(con);
|
||||
}
|
||||
npf_conn_release(con);
|
||||
put_cached_pkt(npc);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -121,6 +110,11 @@ static bool
|
|||
run_gc_tests(void)
|
||||
{
|
||||
bool ok;
|
||||
int val;
|
||||
|
||||
/* Check the default value. */
|
||||
npf_param_get(npf_getkernctx(), "gc.step", &val);
|
||||
CHECK_TRUE(val == 256);
|
||||
|
||||
/* Empty => GC => 0 in conndb. */
|
||||
ok = run_conn_gc(0, 0, 0);
|
||||
|
@ -150,6 +144,11 @@ run_gc_tests(void)
|
|||
ok = run_conn_gc(0, 512, 256);
|
||||
CHECK_TRUE(ok);
|
||||
|
||||
/* 512 expired => GC => 127 in conndb. */
|
||||
npf_param_set(npf_getkernctx(), "gc.step", 128);
|
||||
ok = run_conn_gc(0, 512, 384);
|
||||
CHECK_TRUE(ok);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -256,6 +256,80 @@ mbuf_icmp_append(struct mbuf *m, struct mbuf *m_orig)
|
|||
m_freem(m_orig);
|
||||
}
|
||||
|
||||
struct mbuf *
|
||||
mbuf_get_pkt(int af, int proto, const char *src, const char *dst,
|
||||
int sport, int dport)
|
||||
{
|
||||
struct mbuf *m;
|
||||
struct ip *ip;
|
||||
struct ip6_hdr *ip6;
|
||||
struct tcphdr *th;
|
||||
struct udphdr *uh;
|
||||
void *p, *ipsrc, *ipdst;
|
||||
|
||||
switch (af) {
|
||||
case AF_INET6:
|
||||
m = mbuf_construct6(proto);
|
||||
p = mbuf_return_hdrs6(m, &ip6);
|
||||
ipsrc = &ip6->ip6_src;
|
||||
ipdst = &ip6->ip6_dst;
|
||||
break;
|
||||
case AF_INET:
|
||||
default:
|
||||
m = mbuf_construct(proto);
|
||||
p = mbuf_return_hdrs(m, false, &ip);
|
||||
ipsrc = &ip->ip_src.s_addr;
|
||||
ipdst = &ip->ip_dst.s_addr;
|
||||
}
|
||||
|
||||
npf_inet_pton(af, src, ipsrc);
|
||||
npf_inet_pton(af, dst, ipdst);
|
||||
|
||||
switch (proto) {
|
||||
case IPPROTO_TCP:
|
||||
th = p;
|
||||
th->th_sport = htons(sport);
|
||||
th->th_dport = htons(dport);
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
uh = p;
|
||||
uh->uh_sport = htons(sport);
|
||||
uh->uh_dport = htons(dport);
|
||||
break;
|
||||
default:
|
||||
KASSERT(false);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
npf_cache_t *
|
||||
get_cached_pkt(struct mbuf *m, const char *ifname)
|
||||
{
|
||||
ifnet_t *ifp = npf_test_getif(ifname ? ifname : IFNAME_DUMMY);
|
||||
npf_cache_t *npc = kmem_zalloc(sizeof(npf_cache_t), KM_SLEEP);
|
||||
nbuf_t *nbuf = kmem_zalloc(sizeof(nbuf_t), KM_SLEEP);
|
||||
int ret;
|
||||
|
||||
npc->npc_info = 0;
|
||||
npc->npc_ctx = npf_getkernctx();
|
||||
|
||||
nbuf_init(npc->npc_ctx, nbuf, m, ifp);
|
||||
npc->npc_nbuf = nbuf;
|
||||
ret = npf_cache_all(npc);
|
||||
assert(ret); (void)ret;
|
||||
|
||||
return npc;
|
||||
}
|
||||
|
||||
void
|
||||
put_cached_pkt(npf_cache_t *npc)
|
||||
{
|
||||
struct mbuf *m = nbuf_head_mbuf(npc->npc_nbuf);
|
||||
kmem_free(npc->npc_nbuf, sizeof(nbuf_t));
|
||||
kmem_free(npc, sizeof(npf_cache_t));
|
||||
m_freem(m);
|
||||
}
|
||||
|
||||
const npf_mbufops_t npftest_mbufops = {
|
||||
.alloc = npfkern_m_get,
|
||||
.free = npfkern_m_freem,
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#define NPF_BINAT (NPF_NATIN | NPF_NATOUT)
|
||||
|
||||
#define RANDOM_PORT 53472
|
||||
#define RANDOM_PORT 46759
|
||||
|
||||
static const struct test_case {
|
||||
const char * src;
|
||||
|
@ -150,14 +150,14 @@ static const struct test_case {
|
|||
};
|
||||
|
||||
static bool
|
||||
nmatch_addr(int af, const char *saddr, const npf_addr_t *addr2)
|
||||
match_addr(int af, const char *saddr, const npf_addr_t *addr2)
|
||||
{
|
||||
npf_addr_t addr1;
|
||||
size_t len;
|
||||
|
||||
npf_inet_pton(af, saddr, &addr1);
|
||||
len = af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr);
|
||||
return memcmp(&addr1, addr2, len) != 0;
|
||||
len = af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr);
|
||||
return memcmp(&addr1, addr2, len) == 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -204,41 +204,12 @@ checkresult(bool verbose, unsigned i, struct mbuf *m, ifnet_t *ifp, int error)
|
|||
in_addr_t sport = forw ? t->tport : t->sport;
|
||||
in_addr_t dport = forw ? t->dport : t->tport;
|
||||
|
||||
bool defect = false;
|
||||
defect |= nmatch_addr(af, saddr, npc.npc_ips[NPF_SRC]);
|
||||
defect |= sport != ntohs(uh->uh_sport);
|
||||
defect |= nmatch_addr(af, daddr, npc.npc_ips[NPF_DST]);
|
||||
defect |= dport != ntohs(uh->uh_dport);
|
||||
CHECK_TRUE(match_addr(af, saddr, npc.npc_ips[NPF_SRC]));
|
||||
CHECK_TRUE(sport == ntohs(uh->uh_sport));
|
||||
CHECK_TRUE(match_addr(af, daddr, npc.npc_ips[NPF_DST]));
|
||||
CHECK_TRUE(dport == ntohs(uh->uh_dport));
|
||||
|
||||
return !defect;
|
||||
}
|
||||
|
||||
static struct mbuf *
|
||||
fill_packet(const struct test_case *t)
|
||||
{
|
||||
struct mbuf *m;
|
||||
void *ipsrc, *ipdst;
|
||||
struct udphdr *uh;
|
||||
|
||||
if (t->af == AF_INET6) {
|
||||
struct ip6_hdr *ip6;
|
||||
|
||||
m = mbuf_construct6(IPPROTO_UDP);
|
||||
uh = mbuf_return_hdrs6(m, &ip6);
|
||||
ipsrc = &ip6->ip6_src, ipdst = &ip6->ip6_dst;
|
||||
} else {
|
||||
struct ip *ip;
|
||||
|
||||
m = mbuf_construct(IPPROTO_UDP);
|
||||
uh = mbuf_return_hdrs(m, false, &ip);
|
||||
ipsrc = &ip->ip_src.s_addr, ipdst = &ip->ip_dst.s_addr;
|
||||
}
|
||||
|
||||
npf_inet_pton(t->af, t->src, ipsrc);
|
||||
npf_inet_pton(t->af, t->dst, ipdst);
|
||||
uh->uh_sport = htons(t->sport);
|
||||
uh->uh_dport = htons(t->dport);
|
||||
return m;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -249,7 +220,7 @@ npf_nat_test(bool verbose)
|
|||
for (unsigned i = 0; i < __arraycount(test_cases); i++) {
|
||||
const struct test_case *t = &test_cases[i];
|
||||
ifnet_t *ifp = npf_test_getif(t->ifname);
|
||||
struct mbuf *m = fill_packet(t);
|
||||
struct mbuf *m;
|
||||
int error;
|
||||
bool ret;
|
||||
|
||||
|
@ -257,14 +228,14 @@ npf_nat_test(bool verbose)
|
|||
printf("Interface %s is not configured.\n", t->ifname);
|
||||
return false;
|
||||
}
|
||||
m = mbuf_get_pkt(t->af, IPPROTO_UDP,
|
||||
t->src, t->dst, t->sport, t->dport);
|
||||
error = npf_packet_handler(npf, &m, ifp, t->di);
|
||||
ret = checkresult(verbose, i, m, ifp, error);
|
||||
if (m) {
|
||||
m_freem(m);
|
||||
}
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
CHECK_TRUE(ret);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ parse_nbuf_chain(struct mbuf *m)
|
|||
n += sizeof(uint32_t);
|
||||
}
|
||||
mbuf_consistency_check(&nbuf);
|
||||
m_freem(nbuf_head_mbuf(&nbuf));
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -74,7 +75,7 @@ static char *
|
|||
mbuf_getstring(struct mbuf *m)
|
||||
{
|
||||
char *s = kmem_zalloc(MBUF_CHAIN_LEN + 1, KM_SLEEP);
|
||||
u_int tlen = 0;
|
||||
unsigned tlen = 0;
|
||||
|
||||
while (m) {
|
||||
int len = m->m_len;
|
||||
|
@ -106,7 +107,7 @@ static struct mbuf *
|
|||
mbuf_bytesize(size_t clen)
|
||||
{
|
||||
struct mbuf *m0 = NULL, *m = NULL;
|
||||
u_int i, n;
|
||||
unsigned i, n;
|
||||
|
||||
/* Chain of clen (e.g. 128) mbufs, each storing 1 byte of data. */
|
||||
for (i = 0, n = 0; i < clen; i++) {
|
||||
|
@ -130,16 +131,16 @@ mbuf_bytesize(size_t clen)
|
|||
}
|
||||
|
||||
/*
|
||||
* Generate random amount of mbufs, with random offsets and lengths.
|
||||
* Generate random number of mbufs, with random offsets and lengths.
|
||||
*/
|
||||
static struct mbuf *
|
||||
mbuf_random_len(size_t chain_len)
|
||||
{
|
||||
struct mbuf *m0 = NULL, *m = NULL;
|
||||
u_int tlen = 0, n = 0;
|
||||
unsigned tlen = 0, n = 0;
|
||||
|
||||
while (tlen < chain_len) {
|
||||
u_int off, len;
|
||||
unsigned off, len;
|
||||
char *d;
|
||||
|
||||
/* Random offset and length range: 1 .. 16. */
|
||||
|
@ -174,16 +175,16 @@ mbuf_random_len(size_t chain_len)
|
|||
}
|
||||
|
||||
static bool
|
||||
validate_mbuf_data(bool verbose, char *bufa, char *bufb)
|
||||
validate_mbuf_data(char *bufa, char *bufb)
|
||||
{
|
||||
bool ret = (strcmp(bufa, bufb) == 0);
|
||||
bool ok = strcmp(bufa, bufb) == 0;
|
||||
|
||||
if (verbose) {
|
||||
if (!ok) {
|
||||
printf("Buffer A: %s\nBuffer B: %s\n", bufa, bufb);
|
||||
}
|
||||
kmem_free(bufa, MBUF_CHAIN_LEN + 1);
|
||||
kmem_free(bufb, MBUF_CHAIN_LEN + 1);
|
||||
return ret;
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -191,17 +192,25 @@ npf_nbuf_test(bool verbose)
|
|||
{
|
||||
struct mbuf *m1, *m2;
|
||||
char *bufa, *bufb;
|
||||
bool fail = false;
|
||||
unsigned n = 10000;
|
||||
bool ok;
|
||||
|
||||
m1 = mbuf_random_len(MBUF_CHAIN_LEN);
|
||||
bufa = mbuf_getstring(m1);
|
||||
bufb = parse_nbuf_chain(m1);
|
||||
fail |= !validate_mbuf_data(verbose, bufa, bufb);
|
||||
while (n--) {
|
||||
m1 = mbuf_random_len(MBUF_CHAIN_LEN);
|
||||
bufa = mbuf_getstring(m1);
|
||||
bufb = parse_nbuf_chain(m1);
|
||||
ok = validate_mbuf_data(bufa, bufb);
|
||||
CHECK_TRUE(ok);
|
||||
m_freem(m1);
|
||||
}
|
||||
|
||||
m2 = mbuf_bytesize(MBUF_CHAIN_LEN);
|
||||
bufa = mbuf_getstring(m2);
|
||||
bufb = parse_nbuf_chain(m2);
|
||||
fail |= !validate_mbuf_data(verbose, bufa, bufb);
|
||||
ok = validate_mbuf_data(bufa, bufb);
|
||||
CHECK_TRUE(ok);
|
||||
m_freem(m2);
|
||||
|
||||
return !fail;
|
||||
(void)verbose;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -24,34 +24,19 @@ static volatile int done;
|
|||
static uint64_t * npackets;
|
||||
static bool stateful;
|
||||
|
||||
static struct mbuf *
|
||||
fill_packet(unsigned i)
|
||||
{
|
||||
struct mbuf *m;
|
||||
struct ip *ip;
|
||||
struct udphdr *uh;
|
||||
char buf[32];
|
||||
|
||||
m = mbuf_construct(IPPROTO_UDP);
|
||||
uh = mbuf_return_hdrs(m, false, &ip);
|
||||
|
||||
snprintf(buf, sizeof(buf), "192.0.2.%u", i + i);
|
||||
ip->ip_src.s_addr = inet_addr(PUB_IP1);
|
||||
ip->ip_dst.s_addr = inet_addr(stateful ? LOCAL_IP2 : LOCAL_IP3);
|
||||
uh->uh_sport = htons(80);
|
||||
uh->uh_dport = htons(15000 + i);
|
||||
return m;
|
||||
}
|
||||
|
||||
__dead static void
|
||||
worker(void *arg)
|
||||
{
|
||||
npf_t *npf = npf_getkernctx();
|
||||
ifnet_t *ifp = npf_test_getif(IFNAME_INT);
|
||||
unsigned int i = (uintptr_t)arg;
|
||||
struct mbuf *m = fill_packet(i);
|
||||
struct mbuf *m;
|
||||
uint64_t n = 0;
|
||||
|
||||
m = mbuf_get_pkt(AF_INET, IPPROTO_UDP,
|
||||
PUB_IP1, stateful ? LOCAL_IP2 : LOCAL_IP3,
|
||||
80, 15000 + i);
|
||||
|
||||
while (!run)
|
||||
/* spin-wait */;
|
||||
while (!done) {
|
||||
|
@ -62,6 +47,7 @@ worker(void *arg)
|
|||
n++;
|
||||
}
|
||||
npackets[i] = n;
|
||||
m_freem(m);
|
||||
kthread_exit(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,9 +11,6 @@
|
|||
#include "npf_impl.h"
|
||||
#include "npf_test.h"
|
||||
|
||||
#define CHECK_TRUE(x) \
|
||||
if (!(x)) { printf("FAIL: %s line %d\n", __func__, __LINE__); return 0; }
|
||||
|
||||
#define RESULT_PASS 0
|
||||
#define RESULT_BLOCK ENETUNREACH
|
||||
|
||||
|
@ -58,58 +55,47 @@ static const struct test_case {
|
|||
|
||||
};
|
||||
|
||||
static struct mbuf *
|
||||
fill_packet(const struct test_case *t)
|
||||
{
|
||||
struct mbuf *m;
|
||||
struct ip *ip;
|
||||
struct udphdr *uh;
|
||||
|
||||
m = mbuf_construct(IPPROTO_UDP);
|
||||
uh = mbuf_return_hdrs(m, false, &ip);
|
||||
ip->ip_src.s_addr = inet_addr(t->src);
|
||||
ip->ip_dst.s_addr = inet_addr(t->dst);
|
||||
uh->uh_sport = htons(9000);
|
||||
uh->uh_dport = htons(9000);
|
||||
return m;
|
||||
}
|
||||
|
||||
static int
|
||||
npf_rule_raw_test(struct mbuf *m, ifnet_t *ifp, int di)
|
||||
run_raw_testcase(unsigned i)
|
||||
{
|
||||
const struct test_case *t = &test_cases[i];
|
||||
npf_t *npf = npf_getkernctx();
|
||||
npf_cache_t npc = { .npc_info = 0, .npc_ctx = npf };
|
||||
nbuf_t nbuf;
|
||||
npf_cache_t *npc;
|
||||
struct mbuf *m;
|
||||
npf_rule_t *rl;
|
||||
npf_match_info_t mi;
|
||||
int error;
|
||||
int slock, error;
|
||||
|
||||
nbuf_init(npf, &nbuf, m, ifp);
|
||||
npc.npc_nbuf = &nbuf;
|
||||
npf_cache_all(&npc);
|
||||
m = mbuf_get_pkt(AF_INET, IPPROTO_UDP, t->src, t->dst, 9000, 9000);
|
||||
npc = get_cached_pkt(m, t->ifname);
|
||||
|
||||
int slock = npf_config_read_enter();
|
||||
rl = npf_ruleset_inspect(&npc, npf_config_ruleset(npf),
|
||||
di, NPF_LAYER_3);
|
||||
slock = npf_config_read_enter();
|
||||
rl = npf_ruleset_inspect(npc, npf_config_ruleset(npf), t->di, NPF_LAYER_3);
|
||||
if (rl) {
|
||||
npf_match_info_t mi;
|
||||
error = npf_rule_conclude(rl, &mi);
|
||||
} else {
|
||||
error = ENOENT;
|
||||
}
|
||||
npf_config_read_exit(slock);
|
||||
|
||||
put_cached_pkt(npc);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
npf_test_case(unsigned i)
|
||||
run_handler_testcase(unsigned i)
|
||||
{
|
||||
const struct test_case *t = &test_cases[i];
|
||||
ifnet_t *ifp = npf_test_getif(t->ifname);
|
||||
npf_t *npf = npf_getkernctx();
|
||||
struct mbuf *m;
|
||||
int error;
|
||||
|
||||
struct mbuf *m = fill_packet(t);
|
||||
error = npf_rule_raw_test(m, ifp, t->di);
|
||||
m_freem(m);
|
||||
m = mbuf_get_pkt(AF_INET, IPPROTO_UDP, t->src, t->dst, 9000, 9000);
|
||||
error = npf_packet_handler(npf, &m, ifp, t->di);
|
||||
if (m) {
|
||||
m_freem(m);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -118,14 +104,43 @@ npf_blockall_rule(void)
|
|||
{
|
||||
npf_t *npf = npf_getkernctx();
|
||||
nvlist_t *rule = nvlist_create(0);
|
||||
npf_rule_t *rl;
|
||||
|
||||
nvlist_add_number(rule, "attr",
|
||||
NPF_RULE_IN | NPF_RULE_OUT | NPF_RULE_DYNAMIC);
|
||||
return npf_rule_alloc(npf, rule);
|
||||
rl = npf_rule_alloc(npf, rule);
|
||||
nvlist_destroy(rule);
|
||||
return rl;
|
||||
}
|
||||
|
||||
bool
|
||||
npf_rule_test(bool verbose)
|
||||
static bool
|
||||
test_static(bool verbose)
|
||||
{
|
||||
for (unsigned i = 0; i < __arraycount(test_cases); i++) {
|
||||
const struct test_case *t = &test_cases[i];
|
||||
int error, serror;
|
||||
|
||||
if (npf_test_getif(t->ifname) == NULL) {
|
||||
printf("Interface %s is not configured.\n", t->ifname);
|
||||
return false;
|
||||
}
|
||||
|
||||
error = run_raw_testcase(i);
|
||||
serror = run_handler_testcase(i);
|
||||
|
||||
if (verbose) {
|
||||
printf("rule test %d:\texpected %d (stateful) and %d\n"
|
||||
"\t\t-> returned %d and %d\n",
|
||||
i + 1, t->stateful_ret, t->ret, serror, error);
|
||||
}
|
||||
CHECK_TRUE(error == t->ret);
|
||||
CHECK_TRUE(serror == t->stateful_ret)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
test_dynamic(void)
|
||||
{
|
||||
npf_t *npf = npf_getkernctx();
|
||||
npf_ruleset_t *rlset;
|
||||
|
@ -133,37 +148,11 @@ npf_rule_test(bool verbose)
|
|||
uint64_t id;
|
||||
int error;
|
||||
|
||||
for (unsigned i = 0; i < __arraycount(test_cases); i++) {
|
||||
const struct test_case *t = &test_cases[i];
|
||||
ifnet_t *ifp = npf_test_getif(t->ifname);
|
||||
int serror;
|
||||
|
||||
if (ifp == NULL) {
|
||||
printf("Interface %s is not configured.\n", t->ifname);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct mbuf *m = fill_packet(t);
|
||||
error = npf_rule_raw_test(m, ifp, t->di);
|
||||
serror = npf_packet_handler(npf, &m, ifp, t->di);
|
||||
|
||||
if (m) {
|
||||
m_freem(m);
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("rule test %d:\texpected %d (stateful) and %d\n"
|
||||
"\t\t-> returned %d and %d\n",
|
||||
i + 1, t->stateful_ret, t->ret, serror, error);
|
||||
}
|
||||
CHECK_TRUE(serror == t->stateful_ret && error == t->ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test dynamic NPF rules.
|
||||
*/
|
||||
|
||||
error = npf_test_case(0);
|
||||
error = run_raw_testcase(0);
|
||||
CHECK_TRUE(error == RESULT_PASS);
|
||||
|
||||
npf_config_enter(npf);
|
||||
|
@ -173,7 +162,7 @@ npf_rule_test(bool verbose)
|
|||
error = npf_ruleset_add(rlset, "test-rules", rl);
|
||||
CHECK_TRUE(error == 0);
|
||||
|
||||
error = npf_test_case(0);
|
||||
error = run_raw_testcase(0);
|
||||
CHECK_TRUE(error == RESULT_BLOCK);
|
||||
|
||||
id = npf_rule_getid(rl);
|
||||
|
@ -182,8 +171,22 @@ npf_rule_test(bool verbose)
|
|||
|
||||
npf_config_exit(npf);
|
||||
|
||||
error = npf_test_case(0);
|
||||
error = run_raw_testcase(0);
|
||||
CHECK_TRUE(error == RESULT_PASS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
npf_rule_test(bool verbose)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
ok = test_static(verbose);
|
||||
CHECK_TRUE(ok);
|
||||
|
||||
ok = test_dynamic();
|
||||
CHECK_TRUE(ok);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -133,10 +133,8 @@ construct_packet(const tcp_meta_t *p)
|
|||
static bool
|
||||
process_packet(const int i, npf_state_t *nst, bool *snew)
|
||||
{
|
||||
ifnet_t *dummy_ifp = npf_test_addif(IFNAME_TEST, false, false);
|
||||
const tcp_meta_t *p = &packet_sequence[i];
|
||||
npf_cache_t npc = { .npc_info = 0, .npc_ctx = npf_getkernctx() };
|
||||
nbuf_t nbuf;
|
||||
npf_cache_t *npc;
|
||||
int ret;
|
||||
|
||||
if (p->flags == 0) {
|
||||
|
@ -145,18 +143,14 @@ process_packet(const int i, npf_state_t *nst, bool *snew)
|
|||
return true;
|
||||
}
|
||||
|
||||
nbuf_init(npf_getkernctx(), &nbuf, construct_packet(p), dummy_ifp);
|
||||
npc.npc_nbuf = &nbuf;
|
||||
ret = npf_cache_all(&npc);
|
||||
KASSERT((ret & NPC_IPFRAG) == 0);
|
||||
|
||||
npc = get_cached_pkt(construct_packet(p), NULL);
|
||||
if (*snew) {
|
||||
ret = npf_state_init(&npc, nst);
|
||||
ret = npf_state_init(npc, nst);
|
||||
KASSERT(ret == true);
|
||||
*snew = false;
|
||||
}
|
||||
ret = npf_state_inspect(&npc, nst, p->flags == OUT);
|
||||
m_freem(nbuf.nb_mbuf);
|
||||
ret = npf_state_inspect(npc, nst, p->flags == OUT);
|
||||
put_cached_pkt(npc);
|
||||
|
||||
return ret ? true : (p->flags & ERR) != 0;
|
||||
}
|
||||
|
@ -168,7 +162,7 @@ npf_state_test(bool verbose)
|
|||
bool snew = true;
|
||||
bool ok = true;
|
||||
|
||||
for (u_int i = 0; i < __arraycount(packet_sequence); i++) {
|
||||
for (unsigned i = 0; i < __arraycount(packet_sequence); i++) {
|
||||
if (process_packet(i, &nst, &snew)) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -48,9 +48,6 @@ static const uint8_t ip6_list[][16] = {
|
|||
}
|
||||
};
|
||||
|
||||
#define CHECK_TRUE(x) \
|
||||
if (!(x)) { printf("FAIL: %s line %d\n", __func__, __LINE__); return 0; }
|
||||
|
||||
#define IPSET_TID 0
|
||||
#define IPSET_NAME "ipset-table"
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include <net/ethertypes.h>
|
||||
#endif
|
||||
|
||||
#define IFNAME_DUMMY "npftest999"
|
||||
|
||||
/* Test interfaces and IP addresses. */
|
||||
#define IFNAME_EXT "npftest0"
|
||||
#define IFNAME_INT "npftest1"
|
||||
|
@ -79,7 +81,11 @@ struct mbuf {
|
|||
|
||||
#endif
|
||||
|
||||
#define CHECK_TRUE(x) \
|
||||
if (!(x)) { printf("FAIL: %s line %d\n", __func__, __LINE__); return 0; }
|
||||
|
||||
const npf_mbufops_t npftest_mbufops;
|
||||
const npf_ifops_t npftest_ifops;
|
||||
|
||||
struct mbuf * npfkern_m_get(int, int);
|
||||
size_t npfkern_m_length(const struct mbuf *);
|
||||
|
@ -105,6 +111,10 @@ void * mbuf_return_hdrs(struct mbuf *, bool, struct ip **);
|
|||
void * mbuf_return_hdrs6(struct mbuf *, struct ip6_hdr **);
|
||||
void mbuf_icmp_append(struct mbuf *, struct mbuf *);
|
||||
|
||||
struct mbuf * mbuf_get_pkt(int, int, const char *, const char *, int, int);
|
||||
npf_cache_t * get_cached_pkt(struct mbuf *, const char *);
|
||||
void put_cached_pkt(npf_cache_t *);
|
||||
|
||||
bool npf_nbuf_test(bool);
|
||||
bool npf_bpf_test(bool);
|
||||
bool npf_table_test(bool, void *, size_t);
|
||||
|
|
|
@ -48,7 +48,7 @@ static void npftest_ifop_flush(void *);
|
|||
static void * npftest_ifop_getmeta(const ifnet_t *);
|
||||
static void npftest_ifop_setmeta(ifnet_t *, void *);
|
||||
|
||||
static const npf_ifops_t npftest_ifops = {
|
||||
const npf_ifops_t npftest_ifops = {
|
||||
.getname = npftest_ifop_getname,
|
||||
.lookup = npf_test_getif,
|
||||
.flush = npftest_ifop_flush,
|
||||
|
@ -72,12 +72,15 @@ npf_test_init(int (*pton_func)(int, const char *, void *),
|
|||
_pton_func = pton_func;
|
||||
_ntop_func = ntop_func;
|
||||
_random_func = rndfunc;
|
||||
|
||||
(void)npf_test_addif(IFNAME_DUMMY, false, false);
|
||||
}
|
||||
|
||||
void
|
||||
npf_test_fini(void)
|
||||
{
|
||||
npf_t *npf = npf_getkernctx();
|
||||
npf_thread_unregister(npf);
|
||||
npf_destroy(npf);
|
||||
npf_sysfini();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: npfstream.c,v 1.8 2019/01/19 21:19:32 rmind Exp $ */
|
||||
/* $NetBSD: npfstream.c,v 1.9 2019/07/23 00:52:02 rmind Exp $ */
|
||||
|
||||
/*
|
||||
* NPF stream processor.
|
||||
|
@ -80,7 +80,7 @@ process_tcpip(const void *data, size_t len, FILE *fp, ifnet_t *ifp)
|
|||
|
||||
fprintf(fp, "%s%2x %5d %3d %11u %11u %11u %11u %12" PRIxPTR,
|
||||
forw ? ">" : "<", (th->th_flags & (TH_SYN | TH_ACK | TH_FIN)),
|
||||
packetno, error, (u_int)seq, (u_int)ntohl(th->th_ack),
|
||||
packetno, error, (unsigned)seq, (unsigned)ntohl(th->th_ack),
|
||||
tcpdlen, ntohs(th->th_win), (uintptr_t)result[0]);
|
||||
|
||||
for (unsigned i = 1; i < __arraycount(result); i++) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: npftest.c,v 1.23 2019/01/19 21:19:32 rmind Exp $ */
|
||||
/* $NetBSD: npftest.c,v 1.24 2019/07/23 00:52:02 rmind Exp $ */
|
||||
|
||||
/*
|
||||
* NPF testing framework.
|
||||
|
@ -277,7 +277,6 @@ main(int argc, char **argv)
|
|||
errx(EXIT_FAILURE, "failed to find the interface");
|
||||
}
|
||||
|
||||
srandom(1);
|
||||
fail = false;
|
||||
|
||||
if (test) {
|
||||
|
@ -311,7 +310,6 @@ main(int argc, char **argv)
|
|||
}
|
||||
|
||||
if (!testname || strcmp("conn", testname) == 0) {
|
||||
srandom(1);
|
||||
ok = rumpns_npf_conn_test(verbose);
|
||||
fail |= result("conn", ok);
|
||||
tname_matched = true;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: npftest.conf,v 1.6 2019/01/19 21:19:32 rmind Exp $
|
||||
# $NetBSD: npftest.conf,v 1.7 2019/07/23 00:52:02 rmind Exp $
|
||||
|
||||
$ext_if = "npftest0"
|
||||
$int_if = "npftest1"
|
||||
|
@ -32,6 +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
|
||||
|
||||
group "ext" on $ext_if {
|
||||
pass out final from $local_ip3
|
||||
|
|
Loading…
Reference in New Issue