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:
rmind 2019-07-23 00:52:01 +00:00
parent ac03b3fba2
commit dadc88e3b0
51 changed files with 2583 additions and 1201 deletions

View File

@ -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
.\" -----

View File

@ -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.
*/

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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;
/*

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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 *);

View File

@ -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",
&params->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);

274
sys/net/npf/npf_connkey.c Normal file
View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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 *);

View File

@ -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.
*

View File

@ -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",

View File

@ -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;

202
sys/net/npf/npf_params.c Normal file
View File

@ -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 = &paramreg->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;
}

528
sys/net/npf/npf_portmap.c Normal file
View File

@ -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",
&params->min_port,
.default_val = 1024,
.min = 1024, .max = 65535
},
{
"portmap.max_port",
&params->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);
}
}

View File

@ -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>

View File

@ -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);

View File

@ -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",
&params->timeouts[NPF_ANY_CONN_CLOSED],
.default_val = 0,
.min = 0, .max = INT_MAX
},
{
"state.generic.timeout.new",
&params->timeouts[NPF_ANY_CONN_NEW],
.default_val = 30,
.min = 0, .max = INT_MAX
},
{
"state.generic.timeout.established",
&params->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);

View File

@ -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",
&params->timeouts[NPF_TCPT_NEW],
.default_val = 30,
.min = 0, .max = INT_MAX
},
/* Established. */
{
"state.tcp.timeout.established",
&params->timeouts[NPF_TCPT_ESTABLISHED],
.default_val = 60 * 60 * 24,
.min = 0, .max = INT_MAX
},
/* Half-closed cases. */
{
"state.tcp.timeout.half_close",
&params->timeouts[NPF_TCPT_HALFCLOSE],
.default_val = 60 * 60 * 6,
.min = 0, .max = INT_MAX
},
/* Full close cases. */
{
"state.tcp.timeout.close",
&params->timeouts[NPF_TCPT_CLOSE],
.default_val = 10,
.min = 0, .max = INT_MAX
},
/* TCP time-wait (2 * MSL). */
{
"state.tcp.timeout.time_wait",
&params->timeouts[NPF_TCPT_TIMEWAIT],
.default_val = 60 * 2 * 2,
.min = 0, .max = INT_MAX
},
/*
* Enforce strict order RST.
*/
{
"state.tcp.strict_order_rst",
&params->strict_order_rst,
.default_val = 1, // true
.min = 0, .max = 1
},
/*
* TCP state tracking: maximum allowed ACK window.
*/
{
"state.tcp.max_ack_win",
&params->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);
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

130
usr.sbin/npf/npf-params.7 Normal file
View File

@ -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 .

View File

@ -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

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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.
*/

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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"

View File

@ -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);

View File

@ -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();
}

View File

@ -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++) {

View File

@ -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;

View File

@ -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