NPF: rework of the connection saving and restoring:

- Add support for saving a snapshot of the current connections together
  with a full configuration.  Support a reverse load operation.  Eliminate
  the old 'sess-save' and 'sess-load' in favour of the new mechanism.
- Share code between load and reload operations: the latter performs
  load from npf.conf without affecting the connections.
- Simplify and fix races with connection loading.
- Bump NPF_VERSION.
This commit is contained in:
rmind 2014-07-23 01:25:34 +00:00
parent 4af1928aa9
commit a02b7176fb
17 changed files with 504 additions and 515 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf.c,v 1.29 2014/05/19 18:47:19 jakllsch Exp $ */
/* $NetBSD: npf.c,v 1.30 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2010-2014 The NetBSD Foundation, Inc.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.29 2014/05/19 18:47:19 jakllsch Exp $");
__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.30 2014/07/23 01:25:34 rmind Exp $");
#include <sys/types.h>
#include <netinet/in_systm.h>
@ -167,7 +167,7 @@ npf_config_submit(nl_config_t *ncf, int fd)
}
if (fd) {
error = prop_dictionary_sendrecv_ioctl(npf_dict, fd,
IOC_NPF_RELOAD, &ncf->ncf_err);
IOC_NPF_LOAD, &ncf->ncf_err);
if (error) {
prop_object_release(npf_dict);
assert(ncf->ncf_err == NULL);
@ -179,20 +179,13 @@ npf_config_submit(nl_config_t *ncf, int fd)
return error;
}
nl_config_t *
npf_config_retrieve(int fd, bool *active, bool *loaded)
static nl_config_t *
_npf_config_consdict(prop_dictionary_t npf_dict)
{
prop_dictionary_t npf_dict;
nl_config_t *ncf;
int error;
error = prop_dictionary_recv_ioctl(fd, IOC_NPF_GETCONF, &npf_dict);
if (error) {
return NULL;
}
ncf = calloc(1, sizeof(*ncf));
if (ncf == NULL) {
prop_object_release(npf_dict);
return NULL;
}
ncf->ncf_dict = npf_dict;
@ -201,12 +194,60 @@ npf_config_retrieve(int fd, bool *active, bool *loaded)
ncf->ncf_rproc_list = prop_dictionary_get(npf_dict, "rprocs");
ncf->ncf_table_list = prop_dictionary_get(npf_dict, "tables");
ncf->ncf_nat_list = prop_dictionary_get(npf_dict, "translation");
return ncf;
}
nl_config_t *
npf_config_retrieve(int fd, bool *active, bool *loaded)
{
prop_dictionary_t npf_dict;
nl_config_t *ncf;
int error;
error = prop_dictionary_recv_ioctl(fd, IOC_NPF_SAVE, &npf_dict);
if (error) {
return NULL;
}
ncf = _npf_config_consdict(npf_dict);
if (ncf == NULL) {
prop_object_release(npf_dict);
return NULL;
}
prop_dictionary_get_bool(npf_dict, "active", active);
*loaded = (ncf->ncf_rules_list != NULL);
return ncf;
}
int
npf_config_export(const nl_config_t *ncf, const char *path)
{
prop_dictionary_t npf_dict = ncf->ncf_dict;
int error = 0;
if (!prop_dictionary_externalize_to_file(npf_dict, path)) {
error = errno;
}
return 0;
}
nl_config_t *
npf_config_import(const char *path)
{
prop_dictionary_t npf_dict;
nl_config_t *ncf;
npf_dict = prop_dictionary_internalize_from_file(path);
if (npf_dict) {
return NULL;
}
ncf = _npf_config_consdict(npf_dict);
if (ncf == NULL) {
prop_object_release(npf_dict);
return NULL;
}
return ncf;
}
int
npf_config_flush(int fd)
{
@ -1136,46 +1177,6 @@ _npf_alg_unload(nl_config_t *ncf, const char *name)
* MISC.
*/
int
npf_sessions_recv(int fd, const char *fpath)
{
prop_dictionary_t sdict;
int error;
error = prop_dictionary_recv_ioctl(fd, IOC_NPF_SESSIONS_SAVE, &sdict);
if (error) {
return error;
}
if (!prop_dictionary_externalize_to_file(sdict, fpath)) {
error = errno;
}
prop_object_release(sdict);
return error;
}
int
npf_sessions_send(int fd, const char *fpath)
{
prop_dictionary_t sdict;
int error;
if (fpath) {
sdict = prop_dictionary_internalize_from_file(fpath);
if (sdict == NULL) {
return errno;
}
} else {
/* Empty: will flush the sessions. */
prop_array_t selist = prop_array_create();
sdict = prop_dictionary_create();
prop_dictionary_set(sdict, "session-list", selist);
prop_object_release(selist);
}
error = prop_dictionary_send_ioctl(sdict, fd, IOC_NPF_SESSIONS_LOAD);
prop_object_release(sdict);
return error;
}
static prop_dictionary_t
_npf_debug_initonce(nl_config_t *ncf)
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf.h,v 1.26 2014/05/19 18:47:19 jakllsch Exp $ */
/* $NetBSD: npf.h,v 1.27 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2011-2014 The NetBSD Foundation, Inc.
@ -74,9 +74,12 @@ typedef void (*nl_table_callback_t)(unsigned, int);
#define NPF_MAX_TABLE_ID (16)
nl_config_t * npf_config_create(void);
int npf_config_submit(nl_config_t *, int);
void npf_config_destroy(nl_config_t *);
int npf_config_submit(nl_config_t *, int);
nl_config_t * npf_config_retrieve(int, bool *, bool *);
nl_config_t * npf_config_import(const char *);
int npf_config_export(const nl_config_t *, const char *);
int npf_config_flush(int);
int npf_ruleset_add(int, const char *, nl_rule_t *, uint64_t *);
@ -120,9 +123,6 @@ void npf_table_destroy(nl_table_t *);
#include <ifaddrs.h>
int npf_sessions_send(int, const char *);
int npf_sessions_recv(int, const char *);
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 *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf.c,v 1.20 2014/07/19 18:24:16 rmind Exp $ */
/* $NetBSD: npf.c,v 1.21 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.20 2014/07/19 18:24:16 rmind Exp $");
__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.21 2014/07/23 01:25:34 rmind Exp $");
#include <sys/param.h>
#include <sys/types.h>
@ -129,9 +129,6 @@ npf_fini(void)
devsw_detach(NULL, &npf_cdevsw);
#endif
npf_pfil_unregister(true);
/* Flush all connections, destroy configuration (ruleset, etc). */
npf_conn_tracking(false);
npf_config_fini();
/* Finally, safe to destroy the subsystems. */
@ -220,23 +217,17 @@ npf_dev_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
case IOC_NPF_RULE:
error = npfctl_rule(cmd, data);
break;
case IOC_NPF_GETCONF:
error = npfctl_getconf(cmd, data);
break;
case IOC_NPF_STATS:
error = npfctl_stats(data);
break;
case IOC_NPF_SESSIONS_SAVE:
error = npfctl_conn_save(cmd, data);
break;
case IOC_NPF_SESSIONS_LOAD:
error = npfctl_conn_load(cmd, data);
case IOC_NPF_SAVE:
error = npfctl_save(cmd, data);
break;
case IOC_NPF_SWITCH:
error = npfctl_switch(data);
break;
case IOC_NPF_RELOAD:
error = npfctl_reload(cmd, data);
case IOC_NPF_LOAD:
error = npfctl_load(cmd, data);
break;
case IOC_NPF_VERSION:
*(int *)data = NPF_VERSION;

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf.h,v 1.44 2014/07/20 00:37:41 rmind Exp $ */
/* $NetBSD: npf.h,v 1.45 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
@ -45,7 +45,7 @@
#include <netinet/in_systm.h>
#include <netinet/in.h>
#define NPF_VERSION 15
#define NPF_VERSION 16
/*
* Public declarations and definitions.
@ -308,13 +308,11 @@ typedef struct npf_ioctl_table {
#define IOC_NPF_VERSION _IOR('N', 100, int)
#define IOC_NPF_SWITCH _IOW('N', 101, int)
#define IOC_NPF_RELOAD _IOWR('N', 102, struct plistref)
#define IOC_NPF_LOAD _IOWR('N', 102, struct plistref)
#define IOC_NPF_TABLE _IOW('N', 103, struct npf_ioctl_table)
#define IOC_NPF_STATS _IOW('N', 104, void *)
#define IOC_NPF_SESSIONS_SAVE _IOR('N', 105, struct plistref)
#define IOC_NPF_SESSIONS_LOAD _IOW('N', 106, struct plistref)
#define IOC_NPF_SAVE _IOR('N', 105, struct plistref)
#define IOC_NPF_RULE _IOWR('N', 107, struct plistref)
#define IOC_NPF_GETCONF _IOR('N', 108, struct plistref)
/*
* Statistics counters.
@ -324,13 +322,13 @@ typedef enum {
/* Packets passed. */
NPF_STAT_PASS_DEFAULT,
NPF_STAT_PASS_RULESET,
NPF_STAT_PASS_SESSION,
NPF_STAT_PASS_CONN,
/* Packets blocked. */
NPF_STAT_BLOCK_DEFAULT,
NPF_STAT_BLOCK_RULESET,
/* Connection and NAT entries. */
NPF_STAT_SESSION_CREATE,
NPF_STAT_SESSION_DESTROY,
NPF_STAT_CONN_CREATE,
NPF_STAT_CONN_DESTROY,
NPF_STAT_NAT_CREATE,
NPF_STAT_NAT_DESTROY,
/* Invalid state cases. */
@ -339,7 +337,7 @@ typedef enum {
NPF_STAT_INVALID_STATE_TCP2,
NPF_STAT_INVALID_STATE_TCP3,
/* Raced packets. */
NPF_STAT_RACE_SESSION,
NPF_STAT_RACE_CONN,
NPF_STAT_RACE_NAT,
/* Fragments. */
NPF_STAT_FRAGMENTS,

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_conf.c,v 1.6 2014/05/30 23:26:06 rmind Exp $ */
/* $NetBSD: npf_conf.c,v 1.7 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2013 The NetBSD Foundation, Inc.
@ -48,7 +48,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_conf.c,v 1.6 2014/05/30 23:26:06 rmind Exp $");
__KERNEL_RCSID(0, "$NetBSD: npf_conf.c,v 1.7 2014/07/23 01:25:34 rmind Exp $");
#include <sys/param.h>
#include <sys/types.h>
@ -59,6 +59,7 @@ __KERNEL_RCSID(0, "$NetBSD: npf_conf.c,v 1.6 2014/05/30 23:26:06 rmind Exp $");
#include <sys/mutex.h>
#include "npf_impl.h"
#include "npf_conn.h"
typedef struct {
npf_ruleset_t * n_rules;
@ -90,7 +91,7 @@ npf_config_init(void)
rpset = npf_rprocset_create();
rlset = npf_ruleset_create(0);
nset = npf_ruleset_create(0);
npf_config_reload(dict, rlset, tset, nset, rpset, true);
npf_config_load(dict, rlset, tset, nset, rpset, NULL, true);
KASSERT(npf_config != NULL);
}
@ -108,8 +109,11 @@ npf_config_destroy(npf_config_t *nc)
void
npf_config_fini(void)
{
/* Flush the connections. */
mutex_enter(&npf_config_lock);
npf_conn_tracking(false);
pserialize_perform(npf_config_psz);
npf_conn_load(NULL, false);
npf_ifmap_flush();
mutex_exit(&npf_config_lock);
@ -119,13 +123,13 @@ npf_config_fini(void)
}
/*
* npf_config_reload: the main routine performing configuration reload.
* npf_config_load: the main routine performing configuration load.
* Performs the necessary synchronisation and destroys the old config.
*/
void
npf_config_reload(prop_dictionary_t dict, npf_ruleset_t *rset,
npf_config_load(prop_dictionary_t dict, npf_ruleset_t *rset,
npf_tableset_t *tset, npf_ruleset_t *nset, npf_rprocset_t *rpset,
bool flush)
npf_conndb_t *conns, bool flush)
{
npf_config_t *nc, *onc;
@ -157,16 +161,31 @@ npf_config_reload(prop_dictionary_t dict, npf_ruleset_t *rset,
if (onc == NULL) {
/* Initial load, done. */
npf_ifmap_flush();
npf_conn_load(conns, !flush);
mutex_exit(&npf_config_lock);
return;
}
/*
* If we are going to flush the connections or load the new ones,
* then disable the connection tracking for the grace period.
*/
if (flush || conns) {
npf_conn_tracking(false);
}
/* Synchronise: drain all references. */
pserialize_perform(npf_config_psz);
if (flush) {
npf_ifmap_flush();
}
/*
* G/C the existing connections and, if passed, load the new ones.
* If not flushing - enable the connection tracking.
*/
npf_conn_load(conns, !flush);
/* Sync the config proplib data. */
npf_tableset_syncdict(tset, dict);
mutex_exit(&npf_config_lock);

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_conn.c,v 1.5 2014/07/20 14:16:00 joerg Exp $ */
/* $NetBSD: npf_conn.c,v 1.6 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2014 Mindaugas Rasiukevicius <rmind at netbsd org>
@ -93,14 +93,13 @@
*
* Lock order
*
* conn_lock ->
* [ npf_config_lock -> ]
* npf_hashbucket_t::cd_lock ->
* npf_conn_t::c_lock
* npf_config_lock ->
* conn_lock ->
* npf_conn_t::c_lock
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.5 2014/07/20 14:16:00 joerg Exp $");
__KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.6 2014/07/23 01:25:34 rmind Exp $");
#include <sys/param.h>
#include <sys/types.h>
@ -132,25 +131,22 @@ CTASSERT(PFIL_ALL == (0x001 | 0x002));
#define CONN_REMOVED 0x020 /* "forw/back" entries removed */
/*
* Connection tracking state: disabled (off), enabled (on) or flush request.
* Connection tracking state: disabled (off) or enabled (on).
*/
enum { CONN_TRACKING_OFF, CONN_TRACKING_ON, CONN_TRACKING_FLUSH };
enum { CONN_TRACKING_OFF, CONN_TRACKING_ON };
static volatile int conn_tracking __cacheline_aligned;
/* Connection tracking database, connection cache and the lock. */
static npf_conndb_t * conn_db __read_mostly;
static pool_cache_t conn_cache __read_mostly;
static kmutex_t conn_lock __cacheline_aligned;
static kcondvar_t conn_cv __cacheline_aligned;
static void npf_conn_gc(npf_conndb_t *, bool, bool);
static void npf_conn_worker(void);
static void npf_conn_destroy(npf_conn_t *);
/*
* npf_conn_sys{init,fini}: initialise/destroy connection tracking.
*
* Connection database is initialised when connection tracking gets
* enabled via npf_conn_tracking() interface.
*/
void
@ -159,9 +155,8 @@ npf_conn_sysinit(void)
conn_cache = pool_cache_init(sizeof(npf_conn_t), coherency_unit,
0, 0, "npfconpl", NULL, IPL_NET, NULL, NULL, NULL);
mutex_init(&conn_lock, MUTEX_DEFAULT, IPL_NONE);
cv_init(&conn_cv, "npfconcv");
conn_tracking = CONN_TRACKING_OFF;
conn_db = NULL;
conn_db = npf_conndb_create();
npf_worker_register(npf_conn_worker);
}
@ -169,58 +164,54 @@ npf_conn_sysinit(void)
void
npf_conn_sysfini(void)
{
/* Disable tracking, flush all connections. */
npf_conn_tracking(false);
/* Note: the caller should have flushed the connections. */
KASSERT(conn_tracking == CONN_TRACKING_OFF);
npf_worker_unregister(npf_conn_worker);
KASSERT(conn_tracking == CONN_TRACKING_OFF);
KASSERT(conn_db == NULL);
npf_conndb_destroy(conn_db);
pool_cache_destroy(conn_cache);
mutex_destroy(&conn_lock);
cv_destroy(&conn_cv);
}
/*
* npf_conn_reload: perform the reload by flushing the current connection
* database and replacing with the new one or just destroying.
* npf_conn_load: perform the load by flushing the current connection
* database and replacing it with the new one or just destroying.
*
* Key routine synchronising with all other readers and writers.
* => The caller must disable the connection tracking and ensure that
* there are no connection database lookups or references in-flight.
*/
static void
npf_conn_reload(npf_conndb_t *ndb, int tracking)
void
npf_conn_load(npf_conndb_t *ndb, bool track)
{
npf_conndb_t *odb;
npf_conndb_t *odb = NULL;
/* Must synchronise with G/C thread and connection saving/restoring. */
mutex_enter(&conn_lock);
while (conn_tracking == CONN_TRACKING_FLUSH) {
cv_wait(&conn_cv, &conn_lock);
}
KASSERT(npf_config_locked_p());
/*
* Set the flush status. It disables connection inspection as well
* as creation. There may be some operations in-flight, drain them.
* The connection database is in the quiescent state.
* Prevent G/C thread from running and install a new database.
*/
npf_config_enter();
conn_tracking = CONN_TRACKING_FLUSH;
npf_config_sync();
npf_config_exit();
/* Notify the worker to G/C all connections. */
npf_worker_signal();
while (conn_tracking == CONN_TRACKING_FLUSH) {
cv_wait(&conn_cv, &conn_lock);
mutex_enter(&conn_lock);
if (ndb) {
KASSERT(conn_tracking == CONN_TRACKING_OFF);
odb = conn_db;
conn_db = ndb;
membar_sync();
}
if (track) {
/* After this point lookups start flying in. */
conn_tracking = CONN_TRACKING_ON;
}
/* Install the new database, make it visible. */
odb = atomic_swap_ptr(&conn_db, ndb);
membar_sync();
conn_tracking = tracking;
/* Done. Destroy the old database, if any. */
mutex_exit(&conn_lock);
if (odb) {
/*
* Flush all, no sync since the caller did it for us.
* Also, release the pool cache memory.
*/
npf_conn_gc(odb, true, false);
npf_conndb_destroy(odb);
pool_cache_invalidate(conn_cache);
}
}
@ -230,21 +221,11 @@ npf_conn_reload(npf_conndb_t *ndb, int tracking)
void
npf_conn_tracking(bool track)
{
if (conn_tracking == CONN_TRACKING_OFF && track) {
/* Disabled -> Enable. */
npf_conndb_t *cd = npf_conndb_create();
npf_conn_reload(cd, CONN_TRACKING_ON);
return;
}
if (conn_tracking == CONN_TRACKING_ON && !track) {
/* Enabled -> Disable. */
npf_conn_reload(NULL, CONN_TRACKING_OFF);
pool_cache_invalidate(conn_cache);
return;
}
KASSERT(npf_config_locked_p());
conn_tracking = track ? CONN_TRACKING_ON : CONN_TRACKING_OFF;
}
static bool
static inline bool
npf_conn_trackable_p(const npf_cache_t *npc)
{
/*
@ -476,7 +457,7 @@ npf_conn_establish(npf_cache_t *npc, int di, bool per_if)
return NULL;
}
NPF_PRINTF(("NPF: create conn %p\n", con));
npf_stats_inc(NPF_STAT_SESSION_CREATE);
npf_stats_inc(NPF_STAT_CONN_CREATE);
/* Reference count and flags (indicate direction). */
mutex_init(&con->c_lock, MUTEX_DEFAULT, IPL_SOFTNET);
@ -521,7 +502,7 @@ npf_conn_establish(npf_cache_t *npc, int di, bool per_if)
if (!npf_conndb_insert(conn_db, bk, con)) {
/* We have hit the duplicate. */
npf_conndb_remove(conn_db, fw);
npf_stats_inc(NPF_STAT_RACE_SESSION);
npf_stats_inc(NPF_STAT_RACE_CONN);
goto err;
}
@ -552,7 +533,7 @@ npf_conn_destroy(npf_conn_t *con)
/* Free the structure, increase the counter. */
pool_cache_put(conn_cache, con);
npf_stats_inc(NPF_STAT_SESSION_DESTROY);
npf_stats_inc(NPF_STAT_CONN_DESTROY);
NPF_PRINTF(("NPF: conn %p destroyed\n", con));
}
@ -719,21 +700,18 @@ npf_conn_expired(const npf_conn_t *con, const struct timespec *tsnow)
}
/*
* npf_conn_worker: G/C to run from a worker thread.
* npf_conn_gc: garbage collect the expired connections.
*
* => Must run in a single-threaded manner.
* => If it is a flush request, then destroy all connections.
* => If 'sync' is true, then perform passive serialisation.
*/
static void
npf_conn_worker(void)
npf_conn_gc(npf_conndb_t *cd, bool flush, bool sync)
{
npf_conn_t *con, *prev, *gclist = NULL;
npf_conndb_t *cd;
struct timespec tsnow;
bool flushall;
mutex_enter(&conn_lock);
if ((cd = conn_db) == NULL) {
goto done;
}
flushall = (conn_tracking != CONN_TRACKING_ON);
getnanouptime(&tsnow);
/*
@ -745,7 +723,7 @@ npf_conn_worker(void)
npf_conn_t *next = con->c_next;
/* Expired? Flushing all? */
if (!npf_conn_expired(con, &tsnow) && !flushall) {
if (!npf_conn_expired(con, &tsnow) && !flush) {
prev = con;
con = next;
continue;
@ -775,12 +753,18 @@ npf_conn_worker(void)
con = next;
}
npf_conndb_settail(cd, prev);
done:
/* Ensure we it is safe to destroy the connections. */
if (gclist) {
npf_config_enter();
npf_config_sync();
npf_config_exit();
/*
* Ensure it is safe to destroy the connections.
* Note: drop the conn_lock (see the lock order).
*/
if (sync) {
mutex_exit(&conn_lock);
if (gclist) {
npf_config_enter();
npf_config_sync();
npf_config_exit();
}
}
/*
@ -802,28 +786,25 @@ done:
npf_conn_destroy(con);
con = next;
}
if (conn_tracking == CONN_TRACKING_FLUSH) {
/* Flush was requested - indicate we are done. */
conn_tracking = CONN_TRACKING_OFF;
cv_broadcast(&conn_cv);
}
mutex_exit(&conn_lock);
}
void
npf_conn_load(npf_conndb_t *cd)
{
KASSERT(cd != NULL);
npf_conn_reload(cd, CONN_TRACKING_ON);
}
/*
* npf_conn_save: construct a list of connections prepared for saving.
* npf_conn_worker: G/C to run from a worker thread.
*/
static void
npf_conn_worker(void)
{
mutex_enter(&conn_lock);
/* Note: the conn_lock will be released (sync == true). */
npf_conn_gc(conn_db, false, true);
}
/*
* npf_conn_export: construct a list of connections prepared for saving.
* Note: this is expected to be an expensive operation.
*/
int
npf_conn_save(prop_array_t conlist, prop_array_t nplist)
npf_conn_export(prop_array_t conlist)
{
npf_conn_t *con, *prev;
@ -832,7 +813,7 @@ npf_conn_save(prop_array_t conlist, prop_array_t nplist)
* destruction and G/C thread.
*/
mutex_enter(&conn_lock);
if (!conn_db || conn_tracking != CONN_TRACKING_ON) {
if (conn_tracking != CONN_TRACKING_ON) {
mutex_exit(&conn_lock);
return 0;
}
@ -861,11 +842,8 @@ npf_conn_save(prop_array_t conlist, prop_array_t nplist)
d = prop_data_create_data(bkey, NPF_CONN_MAXKEYLEN);
prop_dictionary_set_and_rel(cdict, "back-key", d);
CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));
prop_dictionary_set_uint64(cdict, "id-ptr", (uintptr_t)con);
if (con->c_nat) {
npf_nat_save(cdict, nplist, con->c_nat);
npf_nat_export(cdict, con->c_nat);
}
prop_array_add(conlist, cdict);
prop_object_release(cdict);
@ -875,16 +853,16 @@ skip:
}
npf_conndb_settail(conn_db, prev);
mutex_exit(&conn_lock);
return 0;
}
/*
* npf_conn_restore: fully reconstruct a single connection from a directory
* and insert into the given database.
* npf_conn_import: fully reconstruct a single connection from a
* directory and insert into the given database.
*/
int
npf_conn_restore(npf_conndb_t *cd, prop_dictionary_t cdict)
npf_conn_import(npf_conndb_t *cd, prop_dictionary_t cdict,
npf_ruleset_t *natlist)
{
npf_conn_t *con;
npf_connkey_t *fw, *bk;
@ -909,7 +887,7 @@ npf_conn_restore(npf_conndb_t *cd, prop_dictionary_t cdict)
memcpy(&con->c_state, d, sizeof(npf_state_t));
/* Reconstruct NAT association, if any, or return NULL. */
con->c_nat = npf_nat_restore(cdict, con);
con->c_nat = npf_nat_import(cdict, natlist, con);
/*
* Fetch and copy the keys for each direction.

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_conn.h,v 1.2 2014/07/20 00:37:41 rmind Exp $ */
/* $NetBSD: npf_conn.h,v 1.3 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
@ -46,6 +46,9 @@ typedef struct npf_connkey npf_connkey_t;
#include <sys/rbtree.h>
/*
* See npf_conn_key() function for the description key layout.
*/
#define NPF_CONN_NKEYWORDS (2 + ((sizeof(npf_addr_t) * 2) >> 2))
#define NPF_CONN_MAXKEYLEN (NPF_CONN_NKEYWORDS * sizeof(uint32_t))
#define NPF_CONN_GETALEN(key) ((key)->ck_key[0] & 0xffff)
@ -98,6 +101,7 @@ struct npf_conn {
void npf_conn_sysinit(void);
void npf_conn_sysfini(void);
void npf_conn_tracking(bool);
void npf_conn_load(npf_conndb_t *, bool);
bool npf_conn_conkey(const npf_cache_t *, npf_connkey_t *, bool);
npf_conn_t * npf_conn_lookup(const npf_cache_t *, const int, bool *);
@ -110,11 +114,9 @@ void npf_conn_setpass(npf_conn_t *, npf_rproc_t *);
int npf_conn_setnat(const npf_cache_t *, npf_conn_t *,
npf_nat_t *, u_int);
npf_nat_t * npf_conn_retnat(npf_conn_t *, const int, bool *);
void npf_conn_load(npf_conndb_t *);
int npf_conn_save(prop_array_t, prop_array_t);
int npf_conn_restore(npf_conndb_t *, prop_dictionary_t);
int npf_conn_export(prop_array_t);
int npf_conn_import(npf_conndb_t *, prop_dictionary_t,
npf_ruleset_t *);
void npf_conn_print(const npf_conn_t *);
/*

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_conndb.c,v 1.1 2014/07/19 18:24:16 rmind Exp $ */
/* $NetBSD: npf_conndb.c,v 1.2 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2010-2014 The NetBSD Foundation, Inc.
@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_conndb.c,v 1.1 2014/07/19 18:24:16 rmind Exp $");
__KERNEL_RCSID(0, "$NetBSD: npf_conndb.c,v 1.2 2014/07/23 01:25:34 rmind Exp $");
#include <sys/param.h>
#include <sys/types.h>
@ -127,6 +127,10 @@ npf_conndb_destroy(npf_conndb_t *cd)
{
size_t len = offsetof(npf_conndb_t, cd_hashtbl[CONNDB_HASH_BUCKETS]);
KASSERT(cd->cd_recent == NULL);
KASSERT(cd->cd_list == NULL);
KASSERT(cd->cd_tail == NULL);
for (u_int i = 0; i < CONNDB_HASH_BUCKETS; i++) {
npf_hashbucket_t *hb = &cd->cd_hashtbl[i];
@ -252,6 +256,7 @@ npf_conndb_getlist(npf_conndb_t *cd)
KASSERT(cd->cd_list == NULL);
cd->cd_list = con;
} else {
KASSERT(prev->c_next == NULL);
prev->c_next = con;
}
return cd->cd_list;
@ -264,5 +269,6 @@ void
npf_conndb_settail(npf_conndb_t *cd, npf_conn_t *con)
{
KASSERT(con || cd->cd_list == NULL);
KASSERT(!con || con->c_next == NULL);
cd->cd_tail = con;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_ctl.c,v 1.34 2014/07/19 18:24:16 rmind Exp $ */
/* $NetBSD: npf_ctl.c,v 1.35 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.34 2014/07/19 18:24:16 rmind Exp $");
__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.35 2014/07/23 01:25:34 rmind Exp $");
#include <sys/param.h>
#include <sys/conf.h>
@ -181,6 +181,7 @@ npf_mk_tables(npf_tableset_t *tblset, prop_array_t tables,
NPF_ERR_DEBUG(errdict);
break;
}
prop_dictionary_remove(tbldict, "entries");
}
prop_object_iterator_release(it);
/*
@ -453,19 +454,67 @@ npf_mk_natlist(npf_ruleset_t *nset, prop_array_t natlist,
}
/*
* npfctl_reload: store passed data i.e. update settings, create passed
* npf_mk_connlist: import a list of connections and load them.
*/
static int __noinline
npf_mk_connlist(prop_array_t conlist, npf_ruleset_t *natlist,
npf_conndb_t **conndb, prop_dictionary_t errdict)
{
prop_dictionary_t condict;
prop_object_iterator_t it;
npf_conndb_t *cd;
int error;
/* Connection list - array */
if (prop_object_type(conlist) != PROP_TYPE_ARRAY) {
NPF_ERR_DEBUG(errdict);
return EINVAL;
}
/* Create a connection database. */
cd = npf_conndb_create();
error = 0;
it = prop_array_iterator(conlist);
while ((condict = prop_object_iterator_next(it)) != NULL) {
/* Connection - dictionary. */
if (prop_object_type(condict) != PROP_TYPE_DICTIONARY) {
NPF_ERR_DEBUG(errdict);
error = EINVAL;
break;
}
/* Construct and insert real connection structure. */
error = npf_conn_import(cd, condict, natlist);
if (error) {
NPF_ERR_DEBUG(errdict);
break;
}
}
prop_object_iterator_release(it);
if (error) {
/* FIXME: npf_conn_gc(cd, true, false); */
npf_conndb_destroy(cd);
} else {
*conndb = cd;
}
return error;
}
/*
* npfctl_load: store passed data i.e. update settings, create passed
* tables, rules and atomically activate all them.
*/
int
npfctl_reload(u_long cmd, void *data)
npfctl_load(u_long cmd, void *data)
{
struct plistref *pref = data;
prop_dictionary_t npf_dict, errdict;
prop_array_t alglist, natlist, tables, rprocs, rules;
prop_array_t alglist, natlist, tables, rprocs, rules, conlist;
npf_tableset_t *tblset = NULL;
npf_rprocset_t *rpset = NULL;
npf_ruleset_t *rlset = NULL;
npf_ruleset_t *nset = NULL;
npf_conndb_t *conndb = NULL;
uint32_t ver = 0;
size_t nitems;
bool flush;
@ -541,16 +590,22 @@ npfctl_reload(u_long cmd, void *data)
goto fail;
}
/* Connections (if loading any). */
if ((conlist = prop_dictionary_get(npf_dict, "conn-list")) != NULL) {
error = npf_mk_connlist(conlist, nset, &conndb, errdict);
if (error) {
goto fail;
}
prop_dictionary_remove(npf_dict, "conn-list");
}
flush = false;
prop_dictionary_get_bool(npf_dict, "flush", &flush);
/*
* Finally - perform the reload.
* Finally - perform the load.
*/
npf_config_reload(npf_dict, rlset, tblset, nset, rpset, flush);
/* Turn on/off connection tracking accordingly. */
npf_conn_tracking(!flush);
npf_config_load(npf_dict, rlset, tblset, nset, rpset, conndb, flush);
/* Done. Since data is consumed now, we shall not destroy it. */
tblset = NULL;
@ -588,6 +643,39 @@ fail:
return error;
}
/*
* npfctl_save: export the config dictionary as it was submitted,
* including the current snapshot of the connections. Additionally,
* indicate whether the ruleset is currently active.
*/
int
npfctl_save(u_long cmd, void *data)
{
struct plistref *pref = data;
prop_dictionary_t npf_dict;
prop_array_t conlist;
int error;
npf_config_enter();
conlist = prop_array_create();
/* Serialise the connections. */
error = npf_conn_export(conlist);
if (error) {
prop_object_release(conlist);
goto out;
}
npf_dict = npf_config_dict();
prop_dictionary_set_bool(npf_dict, "active", npf_pfil_registered_p());
prop_dictionary_set_and_rel(npf_dict, "conn-list", conlist);
error = prop_dictionary_copyout_ioctl(pref, cmd, npf_dict);
out:
npf_config_exit();
return error;
}
/*
* npfctl_rule: add or remove dynamic rules in the specified ruleset.
*/
@ -688,123 +776,6 @@ out:
return error;
}
/*
* npfctl_getconf: return the config dictionary as it was submitted.
* Additionally, indicate whether the ruleset is currently active.
*/
int
npfctl_getconf(u_long cmd, void *data)
{
struct plistref *pref = data;
prop_dictionary_t npf_dict;
int error;
npf_config_enter();
npf_dict = npf_config_dict();
prop_dictionary_set_bool(npf_dict, "active", npf_pfil_registered_p());
error = prop_dictionary_copyout_ioctl(pref, cmd, npf_dict);
npf_config_exit();
return error;
}
/*
* npfctl_conn_save: construct a list of connections and export.
*/
int
npfctl_conn_save(u_long cmd, void *data)
{
struct plistref *pref = data;
prop_array_t conlist, nplist;
prop_dictionary_t dict;
int error;
/* Create a dictionary and two lists. */
dict = prop_dictionary_create();
conlist = prop_array_create();
nplist = prop_array_create();
/* Save the connections. */
error = npf_conn_save(conlist, nplist);
if (error) {
goto fail;
}
/* Set the connection list, NAT policy list and export. */
prop_dictionary_set(dict, "session-list", conlist);
prop_dictionary_set(dict, "nat-policy-list", nplist);
error = prop_dictionary_copyout_ioctl(pref, cmd, dict);
fail:
prop_object_release(dict);
return error;
}
/*
* npfctl_conn_load: import a list of connections and load them.
*/
int
npfctl_conn_load(u_long cmd, void *data)
{
const struct plistref *pref = data;
npf_conndb_t *conndb = NULL;
prop_dictionary_t dict, condict;
prop_object_iterator_t it;
prop_array_t conlist;
int error;
/* Get the dictionary containing connections and NAT policies. */
error = prop_dictionary_copyin_ioctl(pref, cmd, &dict);
if (error)
return error;
/*
* Note: connection objects contain the references to the NAT
* policy entries. Therefore, no need to directly access it.
*/
conlist = prop_dictionary_get(dict, "session-list");
if (prop_object_type(conlist) != PROP_TYPE_ARRAY) {
prop_object_release(conlist);
return EINVAL;
}
/* Create a connection database. */
conndb = npf_conndb_create();
/*
* Iterate through and construct each connection. Note: acquire
* the config lock as we access NAT policies during the restore.
*/
error = 0;
it = prop_array_iterator(conlist);
npf_config_enter();
while ((condict = prop_object_iterator_next(it)) != NULL) {
/* Connection - dictionary. */
if (prop_object_type(condict) != PROP_TYPE_DICTIONARY) {
error = EINVAL;
break;
}
/* Construct and insert real connection structure. */
error = npf_conn_restore(conndb, condict);
if (error) {
break;
}
}
npf_config_exit();
prop_object_iterator_release(it);
prop_object_release(conlist);
if (!error) {
/* Finally, load the new table. */
npf_conn_load(conndb);
} else {
/* Destroy the connection database. */
npf_conndb_destroy(conndb);
}
return error;
}
/*
* npfctl_table: add, remove or query entries in the specified table.
*

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_handler.c,v 1.32 2014/07/20 00:37:41 rmind Exp $ */
/* $NetBSD: npf_handler.c,v 1.33 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@ -36,7 +36,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.32 2014/07/20 00:37:41 rmind Exp $");
__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.33 2014/07/23 01:25:34 rmind Exp $");
#include <sys/types.h>
#include <sys/param.h>
@ -184,7 +184,7 @@ npf_packet_handler(void *arg, struct mbuf **mp, ifnet_t *ifp, int di)
/* If "passing" connection found - skip the ruleset inspection. */
if (con && npf_conn_pass(con, &rp)) {
npf_stats_inc(NPF_STAT_PASS_SESSION);
npf_stats_inc(NPF_STAT_PASS_CONN);
KASSERT(error == 0);
goto pass;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_impl.h,v 1.55 2014/07/20 00:37:41 rmind Exp $ */
/* $NetBSD: npf_impl.h,v 1.56 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
@ -149,8 +149,9 @@ bool npf_config_locked_p(void);
int npf_config_read_enter(void);
void npf_config_read_exit(int);
void npf_config_reload(prop_dictionary_t, npf_ruleset_t *,
npf_tableset_t *, npf_ruleset_t *, npf_rprocset_t *, bool);
void npf_config_load(prop_dictionary_t, npf_ruleset_t *,
npf_tableset_t *, npf_ruleset_t *, npf_rprocset_t *,
npf_conndb_t *, bool);
npf_ruleset_t * npf_config_ruleset(void);
npf_ruleset_t * npf_config_natset(void);
npf_tableset_t *npf_config_tableset(void);
@ -167,9 +168,8 @@ void npflogattach(int);
void npflogdetach(void);
int npfctl_switch(void *);
int npfctl_reload(u_long, void *);
int npfctl_getconf(u_long, void *);
int npfctl_conn_save(u_long, void *);
int npfctl_conn_load(u_long, void *);
int npfctl_save(u_long, void *);
int npfctl_load(u_long, void *);
int npfctl_rule(u_long, void *);
int npfctl_table(void *);
@ -255,8 +255,8 @@ npf_ruleset_t * npf_ruleset_create(size_t);
void npf_ruleset_destroy(npf_ruleset_t *);
void npf_ruleset_insert(npf_ruleset_t *, npf_rule_t *);
void npf_ruleset_reload(npf_ruleset_t *, npf_ruleset_t *);
npf_rule_t * npf_ruleset_matchnat(npf_ruleset_t *, npf_natpolicy_t *);
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_add(npf_ruleset_t *, const char *, npf_rule_t *);
@ -310,8 +310,10 @@ void npf_nat_sysinit(void);
void npf_nat_sysfini(void);
npf_natpolicy_t *npf_nat_newpolicy(prop_dictionary_t, npf_ruleset_t *);
void npf_nat_freepolicy(npf_natpolicy_t *);
bool npf_nat_matchpolicy(npf_natpolicy_t *, 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 *);
int npf_do_nat(npf_cache_t *, npf_conn_t *, const int);
@ -320,8 +322,9 @@ void npf_nat_getorig(npf_nat_t *, npf_addr_t **, in_port_t *);
void npf_nat_gettrans(npf_nat_t *, npf_addr_t **, in_port_t *);
void npf_nat_setalg(npf_nat_t *, npf_alg_t *, uintptr_t);
int npf_nat_save(prop_dictionary_t, prop_array_t, npf_nat_t *);
npf_nat_t * npf_nat_restore(prop_dictionary_t, npf_conn_t *);
void npf_nat_export(prop_dictionary_t, npf_nat_t *);
npf_nat_t * npf_nat_import(prop_dictionary_t, npf_ruleset_t *,
npf_conn_t *);
/* ALG interface. */
void npf_alg_sysinit(void);

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_nat.c,v 1.30 2014/07/20 00:37:41 rmind Exp $ */
/* $NetBSD: npf_nat.c,v 1.31 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2014 Mindaugas Rasiukevicius <rmind at netbsd org>
@ -71,7 +71,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.30 2014/07/20 00:37:41 rmind Exp $");
__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.31 2014/07/23 01:25:34 rmind Exp $");
#include <sys/param.h>
#include <sys/types.h>
@ -113,14 +113,22 @@ typedef struct {
* NAT policy structure.
*/
struct npf_natpolicy {
kmutex_t n_lock;
LIST_HEAD(, npf_nat) n_nat_list;
volatile u_int n_refcnt;
kmutex_t n_lock;
npf_portmap_t * n_portmap;
/* NPF_NP_CMP_START */
uint64_t n_id;
/*
* Translation type, flags and address. Optionally, prefix
* for the NPTv6 and translation port. Translation algorithm
* and related data (for NPTv6, the adjustment value).
*
* NPF_NP_CMP_START mark starts here.
*/
int n_type;
u_int n_flags;
size_t n_addr_sz;
u_int n_alen;
npf_addr_t n_taddr;
npf_netmask_t n_tmask;
in_port_t n_tport;
@ -183,7 +191,7 @@ npf_nat_sysfini(void)
* => Shares portmap if policy is on existing translation address.
*/
npf_natpolicy_t *
npf_nat_newpolicy(prop_dictionary_t natdict, npf_ruleset_t *nrlset)
npf_nat_newpolicy(prop_dictionary_t natdict, npf_ruleset_t *rset)
{
npf_natpolicy_t *np;
prop_object_t obj;
@ -204,11 +212,11 @@ npf_nat_newpolicy(prop_dictionary_t natdict, npf_ruleset_t *nrlset)
/* Translation IP, mask and port (if applicable). */
obj = prop_dictionary_get(natdict, "translation-ip");
np->n_addr_sz = prop_data_size(obj);
if (np->n_addr_sz == 0 || np->n_addr_sz > sizeof(npf_addr_t)) {
np->n_alen = prop_data_size(obj);
if (np->n_alen == 0 || np->n_alen > sizeof(npf_addr_t)) {
goto err;
}
memcpy(&np->n_taddr, prop_data_data_nocopy(obj), np->n_addr_sz);
memcpy(&np->n_taddr, prop_data_data_nocopy(obj), np->n_alen);
prop_dictionary_get_uint8(natdict, "translation-mask", &np->n_tmask);
prop_dictionary_get_uint16(natdict, "translation-port", &np->n_tport);
@ -235,7 +243,7 @@ npf_nat_newpolicy(prop_dictionary_t natdict, npf_ruleset_t *nrlset)
* Inspect NAT policies in the ruleset for port map sharing.
* Note that npf_ruleset_sharepm() will increase the reference count.
*/
if (!npf_ruleset_sharepm(nrlset, np)) {
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;
@ -297,31 +305,30 @@ 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) {
continue;
}
nt->nt_alg = NULL;
if (nt->nt_alg == alg)
nt->nt_alg = NULL;
}
mutex_exit(&np->n_lock);
}
/*
* npf_nat_matchpolicy: compare two NAT policies.
* npf_nat_cmppolicy: compare two NAT policies.
*
* => Return 0 on match, and non-zero otherwise.
*/
bool
npf_nat_matchpolicy(npf_natpolicy_t *np, npf_natpolicy_t *mnp)
npf_nat_cmppolicy(npf_natpolicy_t *np, npf_natpolicy_t *mnp)
{
void *np_raw, *mnp_raw;
const void *np_raw, *mnp_raw;
/*
* Compare the relevant NAT policy information (in raw form),
* which is enough for matching criterion.
*/
KASSERT(np && mnp && np != mnp);
np_raw = (uint8_t *)np + NPF_NP_CMP_START;
mnp_raw = (uint8_t *)mnp + NPF_NP_CMP_START;
return (memcmp(np_raw, mnp_raw, NPF_NP_CMP_SIZE) == 0);
np_raw = (const uint8_t *)np + NPF_NP_CMP_START;
mnp_raw = (const uint8_t *)mnp + NPF_NP_CMP_START;
return memcmp(np_raw, mnp_raw, NPF_NP_CMP_SIZE) == 0;
}
bool
@ -335,10 +342,10 @@ npf_nat_sharepm(npf_natpolicy_t *np, npf_natpolicy_t *mnp)
if ((np->n_flags & mnp->n_flags & NPF_NAT_PORTMAP) == 0) {
return false;
}
if (np->n_addr_sz != mnp->n_addr_sz) {
if (np->n_alen != mnp->n_alen) {
return false;
}
if (memcmp(&np->n_taddr, &mnp->n_taddr, np->n_addr_sz) != 0) {
if (memcmp(&np->n_taddr, &mnp->n_taddr, np->n_alen) != 0) {
return false;
}
/* If NAT policy has an old port map - drop the reference. */
@ -355,6 +362,18 @@ npf_nat_sharepm(npf_natpolicy_t *np, npf_natpolicy_t *mnp)
return true;
}
void
npf_nat_setid(npf_natpolicy_t *np, uint64_t id)
{
np->n_id = id;
}
uint64_t
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.
*
@ -605,7 +624,7 @@ npf_nat_algo(npf_cache_t *npc, const npf_natpolicy_t *np, bool forw)
}
return error;
}
}
/*
* npf_do_nat:
@ -776,103 +795,62 @@ npf_nat_destroy(npf_nat_t *nt)
}
/*
* npf_nat_save: construct NAT entry and reference to the NAT policy.
* npf_nat_export: serialise the NAT entry with a NAT policy ID.
*/
int
npf_nat_save(prop_dictionary_t condict, prop_array_t natlist, npf_nat_t *nt)
void
npf_nat_export(prop_dictionary_t condict, npf_nat_t *nt)
{
npf_natpolicy_t *np = nt->nt_natpolicy;
prop_object_iterator_t it;
prop_dictionary_t npdict;
prop_data_t nd, npd;
uint64_t itnp;
prop_dictionary_t natdict;
prop_data_t d;
/* Set NAT entry data. */
nd = prop_data_create_data(nt, sizeof(npf_nat_t));
prop_dictionary_set(condict, "nat-data", nd);
prop_object_release(nd);
/* Find or create a NAT policy. */
it = prop_array_iterator(natlist);
while ((npdict = prop_object_iterator_next(it)) != NULL) {
CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));
prop_dictionary_get_uint64(npdict, "id-ptr", &itnp);
if ((uintptr_t)itnp == (uintptr_t)np) {
break;
}
}
if (npdict == NULL) {
/* Create NAT policy dictionary and copy the data. */
npdict = prop_dictionary_create();
npd = prop_data_create_data(np, sizeof(npf_natpolicy_t));
prop_dictionary_set(npdict, "nat-policy-data", npd);
prop_object_release(npd);
CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));
prop_dictionary_set_uint64(npdict, "id-ptr", (uintptr_t)np);
prop_array_add(natlist, npdict);
prop_object_release(npdict);
}
prop_dictionary_set(condict, "nat-policy", npdict);
prop_object_release(npdict);
return 0;
natdict = prop_dictionary_create();
d = prop_data_create_data(&nt->nt_oaddr, sizeof(npf_addr_t));
prop_dictionary_set_and_rel(natdict, "oaddr", d);
prop_dictionary_set_uint16(natdict, "oport", nt->nt_oport);
prop_dictionary_set_uint16(natdict, "tport", nt->nt_tport);
prop_dictionary_set_uint64(natdict, "nat-policy", np->n_id);
prop_dictionary_set_and_rel(condict, "nat", natdict);
}
/*
* npf_nat_restore: find a matching NAT policy and restore NAT entry.
*
* => Caller should lock the active NAT ruleset.
* npf_nat_import: find the NAT policy and unserialise the NAT entry.
*/
npf_nat_t *
npf_nat_restore(prop_dictionary_t condict, npf_conn_t *con)
npf_nat_import(prop_dictionary_t natdict, npf_ruleset_t *natlist,
npf_conn_t *con)
{
const npf_natpolicy_t *onp;
const npf_nat_t *ntraw;
prop_object_t obj;
npf_natpolicy_t *np;
npf_rule_t *rl;
npf_nat_t *nt;
uint64_t np_id;
const void *d;
/* Get raw NAT entry. */
obj = prop_dictionary_get(condict, "nat-data");
ntraw = prop_data_data_nocopy(obj);
if (ntraw == NULL || prop_data_size(obj) != sizeof(npf_nat_t)) {
prop_dictionary_get_uint64(natdict, "nat-policy", &np_id);
if ((np = npf_ruleset_findnat(natlist, np_id)) == NULL) {
return NULL;
}
nt = pool_cache_get(nat_cache, PR_WAITOK);
memset(nt, 0, sizeof(npf_nat_t));
/* Find a stored NAT policy information. */
obj = prop_dictionary_get(
prop_dictionary_get(condict, "nat-policy"), "nat-policy-data");
onp = prop_data_data_nocopy(obj);
if (onp == NULL || prop_data_size(obj) != sizeof(npf_natpolicy_t)) {
prop_object_t obj = prop_dictionary_get(natdict, "oaddr");
if ((d = prop_data_data_nocopy(obj)) == NULL ||
prop_data_size(obj) != sizeof(npf_addr_t)) {
pool_cache_put(nat_cache, nt);
return NULL;
}
/*
* Match if there is an existing NAT policy. Will acquire the
* reference on it if further operations are successful.
*/
KASSERT(npf_config_locked_p());
rl = npf_ruleset_matchnat(npf_config_natset(), __UNCONST(onp));
if (rl == NULL) {
return NULL;
}
np = npf_rule_getnat(rl);
KASSERT(np != NULL);
memcpy(&nt->nt_oaddr, d, sizeof(npf_addr_t));
prop_dictionary_get_uint16(natdict, "oport", &nt->nt_oport);
prop_dictionary_get_uint16(natdict, "tport", &nt->nt_tport);
/* Take a specific port from port-map. */
if (!npf_nat_takeport(np, ntraw->nt_tport)) {
if (!npf_nat_takeport(np, nt->nt_tport)) {
pool_cache_put(nat_cache, nt);
return NULL;
}
atomic_inc_uint(&np->n_refcnt);
/* Create and return NAT entry for association. */
nt = pool_cache_get(nat_cache, PR_WAITOK);
memcpy(nt, ntraw, sizeof(npf_nat_t));
LIST_INSERT_HEAD(&np->n_nat_list, nt, nt_entry);
nt->nt_natpolicy = np;
nt->nt_conn = con;
nt->nt_alg = NULL;
return nt;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_ruleset.c,v 1.34 2014/07/20 00:37:41 rmind Exp $ */
/* $NetBSD: npf_ruleset.c,v 1.35 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.34 2014/07/20 00:37:41 rmind Exp $");
__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.35 2014/07/23 01:25:34 rmind Exp $");
#include <sys/param.h>
#include <sys/types.h>
@ -378,6 +378,22 @@ npf_ruleset_gc(npf_ruleset_t *rlset)
}
}
/*
* npf_ruleset_cmpnat: find a matching NAT policy in the ruleset.
*/
static inline npf_rule_t *
npf_ruleset_cmpnat(npf_ruleset_t *rlset, npf_natpolicy_t *mnp)
{
npf_rule_t *rl;
/* Find a matching NAT policy in the old ruleset. */
LIST_FOREACH(rl, &rlset->rs_all, r_aentry) {
if (rl->r_natp && npf_nat_cmppolicy(rl->r_natp, mnp))
break;
}
return rl;
}
/*
* npf_ruleset_reload: prepare the new ruleset by scanning the active
* ruleset and 1) sharing the dynamic rules 2) sharing NAT policies.
@ -388,6 +404,7 @@ void
npf_ruleset_reload(npf_ruleset_t *newset, npf_ruleset_t *oldset)
{
npf_rule_t *rg, *rl;
uint64_t nid = 0;
KASSERT(npf_config_locked_p());
@ -422,6 +439,7 @@ npf_ruleset_reload(npf_ruleset_t *newset, npf_ruleset_t *oldset)
/*
* Scan all rules in the new ruleset and share NAT policies.
* Also, assign a unique ID for each policy here.
*/
LIST_FOREACH(rl, &newset->rs_all, r_aentry) {
npf_natpolicy_t *np;
@ -431,8 +449,10 @@ npf_ruleset_reload(npf_ruleset_t *newset, npf_ruleset_t *oldset)
if ((np = rl->r_natp) == NULL) {
continue;
}
/* Does it match with any policy in the active ruleset? */
if ((actrl = npf_ruleset_matchnat(oldset, np)) == NULL) {
if ((actrl = npf_ruleset_cmpnat(oldset, np)) == NULL) {
npf_nat_setid(np, ++nid);
continue;
}
@ -442,6 +462,7 @@ npf_ruleset_reload(npf_ruleset_t *newset, npf_ruleset_t *oldset)
*/
rl->r_natp = actrl->r_natp;
npf_ruleset_sharepm(newset, rl->r_natp);
npf_nat_setid(rl->r_natp, ++nid);
/*
* Finally, mark the active rule to not destroy its NAT
@ -456,22 +477,6 @@ npf_ruleset_reload(npf_ruleset_t *newset, npf_ruleset_t *oldset)
newset->rs_idcnt = oldset->rs_idcnt;
}
/*
* npf_ruleset_matchnat: find a matching NAT policy in the ruleset.
*/
npf_rule_t *
npf_ruleset_matchnat(npf_ruleset_t *rlset, npf_natpolicy_t *mnp)
{
npf_rule_t *rl;
/* Find a matching NAT policy in the old ruleset. */
LIST_FOREACH(rl, &rlset->rs_all, r_aentry) {
if (rl->r_natp && npf_nat_matchpolicy(rl->r_natp, mnp))
break;
}
return rl;
}
npf_rule_t *
npf_ruleset_sharepm(npf_ruleset_t *rlset, npf_natpolicy_t *mnp)
{
@ -494,6 +499,20 @@ npf_ruleset_sharepm(npf_ruleset_t *rlset, npf_natpolicy_t *mnp)
return rl;
}
npf_natpolicy_t *
npf_ruleset_findnat(npf_ruleset_t *rlset, uint64_t id)
{
npf_rule_t *rl;
LIST_FOREACH(rl, &rlset->rs_all, r_aentry) {
npf_natpolicy_t *np = rl->r_natp;
if (np && npf_nat_getid(np) == id) {
return np;
}
}
return NULL;
}
/*
* npf_ruleset_freealg: inspect the ruleset and disassociate specified
* ALG from all NAT entries using it.

View File

@ -1,6 +1,6 @@
.\" $NetBSD: npfctl.8,v 1.15 2013/09/20 21:30:49 wiz Exp $
.\" $NetBSD: npfctl.8,v 1.16 2014/07/23 01:25:34 rmind Exp $
.\"
.\" Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
.\" Copyright (c) 2009-2014 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 September 19, 2013
.Dd July 23, 2014
.Dt NPFCTL 8
.Os
.Sh NAME
@ -53,26 +53,26 @@ Valid commands are:
.It Ic start
Enable packet inspection using the currently loaded configuration, if any.
Note that this command does not load or reload the configuration,
or affect existing sessions.
or affect existing connections.
.It Ic stop
Disable packet inspection.
This command does not change the currently loaded configuration,
or affect existing sessions.
or affect existing connections.
.It Ic reload Op Ar path
Load or reload configuration from file.
The configuration file at
.Pa /etc/npf.conf
will be used unless a file is specified by
.Ar path .
All sessions will be preserved during the reload, except those which
All connections will be preserved during the reload, except those which
will lose NAT policy due to removal.
NAT policy is determined by the translation type and address.
Note that change of filter criteria will not expire associated sessions.
Note that change of filter criteria will not expire associated connections.
The reload operation (i.e., replacing the ruleset, NAT policies and tables)
is atomic.
.It Ic flush
Flush configuration.
That is, remove all rules, tables and expire all sessions.
That is, remove all rules, tables and expire all connections.
This command does not disable packet inspection.
.It Ic show
Show the current state and configuration.
@ -137,20 +137,16 @@ List all entries in the currently loaded table specified by
.Ar tid .
This operation is expensive and should be used with caution.
.\" ---
.It Ic sess-save
Save all active sessions.
.It Ic save
Save the active configuration and a spanshot of the current connections.
The data will be stored in the
.Pa /var/db/npf_sessions.db
.Pa /var/db/npf.db
file.
Administrator may want to stop the packet inspection before the
session saving.
.It Ic sess-load
Load saved sessions from the file.
Note that original configuration should be loaded before the session loading.
In a case of NAT policy changes, sessions which lose an associated policy
will not be loaded.
Any existing sessions during the load operation will be expired.
Administrator may want to start packet inspection after the session loading.
Administrator may want to stop the packet inspection before saving.
.It Ic load
Load the saved configuration file and the connections from the file.
Note that any existing connections will be destroyed.
Administrator may want to start packet inspection after the load.
.It Ic stats
Print various statistics.
.It Ic debug

View File

@ -1,7 +1,7 @@
/* $NetBSD: npfctl.c,v 1.40 2013/11/12 00:46:34 rmind Exp $ */
/* $NetBSD: npfctl.c,v 1.41 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
* Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This material is based upon work partially supported by The
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: npfctl.c,v 1.40 2013/11/12 00:46:34 rmind Exp $");
__RCSID("$NetBSD: npfctl.c,v 1.41 2014/07/23 01:25:34 rmind Exp $");
#include <sys/ioctl.h>
#include <sys/stat.h>
@ -60,8 +60,8 @@ enum {
NPFCTL_TABLE,
NPFCTL_RULE,
NPFCTL_STATS,
NPFCTL_SESSIONS_SAVE,
NPFCTL_SESSIONS_LOAD,
NPFCTL_SAVE,
NPFCTL_LOAD,
};
static const struct operations_s {
@ -69,23 +69,23 @@ static const struct operations_s {
int action;
} operations[] = {
/* Start, stop, reload */
{ "start", NPFCTL_START },
{ "stop", NPFCTL_STOP },
{ "reload", NPFCTL_RELOAD },
{ "show", NPFCTL_SHOWCONF, },
{ "flush", NPFCTL_FLUSH },
{ "valid", NPFCTL_VALIDATE },
{ "start", NPFCTL_START },
{ "stop", NPFCTL_STOP },
{ "reload", NPFCTL_RELOAD },
{ "show", NPFCTL_SHOWCONF, },
{ "flush", NPFCTL_FLUSH },
{ "valid", NPFCTL_VALIDATE },
/* Table */
{ "table", NPFCTL_TABLE },
{ "table", NPFCTL_TABLE },
/* Rule */
{ "rule", NPFCTL_RULE },
{ "rule", NPFCTL_RULE },
/* Stats */
{ "stats", NPFCTL_STATS },
/* Sessions */
{ "sess-save", NPFCTL_SESSIONS_SAVE },
{ "sess-load", NPFCTL_SESSIONS_LOAD },
{ "stats", NPFCTL_STATS },
/* Full state save/load */
{ "save", NPFCTL_SAVE },
{ "load", NPFCTL_LOAD },
/* --- */
{ NULL, 0 }
{ NULL, 0 }
};
bool
@ -137,7 +137,7 @@ usage(void)
"\t%s table <tid> { list | flush }\n",
progname);
fprintf(stderr,
"\t%s sess-load | sess-save\n",
"\t%s save | load\n",
progname);
exit(EXIT_FAILURE);
}
@ -153,15 +153,15 @@ npfctl_print_stats(int fd)
{ -1, "Packets passed" },
{ NPF_STAT_PASS_DEFAULT, "default pass" },
{ NPF_STAT_PASS_RULESET, "ruleset pass" },
{ NPF_STAT_PASS_SESSION, "session pass" },
{ NPF_STAT_PASS_CONN, "state pass" },
{ -1, "Packets blocked" },
{ NPF_STAT_BLOCK_DEFAULT, "default block" },
{ NPF_STAT_BLOCK_RULESET, "ruleset block" },
{ -1, "Session and NAT entries" },
{ NPF_STAT_SESSION_CREATE, "session allocations" },
{ NPF_STAT_SESSION_DESTROY, "session destructions" },
{ -1, "State and NAT entries" },
{ NPF_STAT_CONN_CREATE, "state allocations"},
{ NPF_STAT_CONN_DESTROY, "state destructions"},
{ NPF_STAT_NAT_CREATE, "NAT entry allocations" },
{ NPF_STAT_NAT_DESTROY, "NAT entry destructions"},
@ -177,7 +177,7 @@ npfctl_print_stats(int fd)
{ -1, "Packet race cases" },
{ NPF_STAT_RACE_NAT, "NAT association race" },
{ NPF_STAT_RACE_SESSION, "duplicate session race"},
{ NPF_STAT_RACE_CONN, "duplicate state race" },
{ -1, "Fragmentation" },
{ NPF_STAT_FRAGMENTS, "fragments" },
@ -480,6 +480,37 @@ npfctl_rule(int fd, int argc, char **argv)
exit(EXIT_SUCCESS);
}
static int
npfctl_save(int fd)
{
nl_config_t *ncf;
bool active, loaded;
int error;
ncf = npf_config_retrieve(fd, &active, &loaded);
if (ncf == NULL) {
return errno;
}
error = npf_config_export(ncf, NPF_DB_PATH);
npf_config_destroy(ncf);
return 0;
}
static int
npfctl_load(int fd)
{
nl_config_t *ncf;
int error;
ncf = npf_config_import(NPF_DB_PATH);
if (ncf == NULL) {
return errno;
}
error = npf_config_submit(ncf, fd);
npf_config_destroy(ncf);
return error;
}
static void
npfctl(int action, int argc, char **argv)
{
@ -544,22 +575,18 @@ npfctl(int action, int argc, char **argv)
argv += 2;
npfctl_rule(fd, argc, argv);
break;
case NPFCTL_LOAD:
ret = npfctl_load(fd);
fun = "npfctl_config_load";
break;
case NPFCTL_SAVE:
fd = npfctl_save(fd);
fun = "npfctl_config_save";
break;
case NPFCTL_STATS:
ret = npfctl_print_stats(fd);
fun = "npfctl_print_stats";
break;
case NPFCTL_SESSIONS_SAVE:
if (npf_sessions_recv(fd, NPF_SESSDB_PATH) != 0) {
errx(EXIT_FAILURE, "could not save sessions to '%s'",
NPF_SESSDB_PATH);
}
break;
case NPFCTL_SESSIONS_LOAD:
if (npf_sessions_send(fd, NPF_SESSDB_PATH) != 0) {
errx(EXIT_FAILURE, "no sessions loaded from '%s'",
NPF_SESSDB_PATH);
}
break;
}
if (ret) {
err(EXIT_FAILURE, "%s", fun);

View File

@ -1,4 +1,4 @@
/* $NetBSD: npfctl.h,v 1.37 2014/05/15 02:34:29 rmind Exp $ */
/* $NetBSD: npfctl.h,v 1.38 2014/07/23 01:25:34 rmind Exp $ */
/*-
* Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@ -48,7 +48,7 @@
#define NPF_DEV_PATH "/dev/npf"
#define NPF_CONF_PATH "/etc/npf.conf"
#define NPF_SESSDB_PATH "/var/db/npf_sessions.db"
#define NPF_DB_PATH "/var/db/npf.db"
typedef struct fam_addr_mask {
sa_family_t fam_family;

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_test_subr.c,v 1.9 2014/02/13 03:34:40 rmind Exp $ */
/* $NetBSD: npf_test_subr.c,v 1.10 2014/07/23 01:25:34 rmind Exp $ */
/*
* NPF initialisation and handler routines.
@ -40,7 +40,7 @@ int
npf_test_load(const void *xml)
{
prop_dictionary_t npf_dict = prop_dictionary_internalize(xml);
return npfctl_reload(0, npf_dict);
return npfctl_load(0, npf_dict);
}
ifnet_t *