NPF checkpoint:

- Add support for session saving/restoring.
- Add packet logging support (can tcpdump a pseudo-interface).
- Support reload without flushing of sessions; rework some locking.
- Revisit session mangement, replace linking with npf_sentry_t entries.
- Add some counters for statistics, using percpu(9).
- Add IP_DF flag cleansing.
- Fix various bugs; misc clean-up.
This commit is contained in:
rmind 2010-12-18 01:07:25 +00:00
parent 3c8b71849f
commit 628e094cdc
22 changed files with 1947 additions and 950 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.3 2010/11/11 06:30:39 rmind Exp $ # $NetBSD: Makefile,v 1.4 2010/12/18 01:07:26 rmind Exp $
.include "../Makefile.inc" .include "../Makefile.inc"
@ -9,5 +9,6 @@ KMOD= npf
SRCS= npf.c npf_ctl.c npf_handler.c npf_instr.c npf_mbuf.c SRCS= npf.c npf_ctl.c npf_handler.c npf_instr.c npf_mbuf.c
SRCS+= npf_processor.c npf_ruleset.c npf_tableset.c npf_inet.c SRCS+= npf_processor.c npf_ruleset.c npf_tableset.c npf_inet.c
SRCS+= npf_session.c npf_state.c npf_nat.c npf_alg.c npf_sendpkt.c SRCS+= npf_session.c npf_state.c npf_nat.c npf_alg.c npf_sendpkt.c
SRCS+= npf_log.c
.include <bsd.kmodule.mk> .include <bsd.kmodule.mk>

View File

@ -1,4 +1,4 @@
# $NetBSD: files.npf,v 1.3 2010/11/11 06:30:39 rmind Exp $ # $NetBSD: files.npf,v 1.4 2010/12/18 01:07:25 rmind Exp $
# #
# Public Domain. # Public Domain.
# #
@ -24,6 +24,7 @@ file net/npf/npf_state.c npf
file net/npf/npf_nat.c npf file net/npf/npf_nat.c npf
file net/npf/npf_alg.c npf file net/npf/npf_alg.c npf
file net/npf/npf_sendpkt.c npf file net/npf/npf_sendpkt.c npf
file net/npf/npf_log.c npf
# ALGs # ALGs
file net/npf/npf_alg_icmp.c npf file net/npf/npf_alg_icmp.c npf

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf.c,v 1.1 2010/08/22 18:56:22 rmind Exp $ */ /* $NetBSD: npf.c,v 1.2 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -34,15 +34,19 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.1 2010/08/22 18:56:22 rmind Exp $"); __KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.2 2010/12/18 01:07:25 rmind Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/atomic.h>
#include <sys/conf.h> #include <sys/conf.h>
#include <sys/kauth.h> #include <sys/kauth.h>
#include <sys/kmem.h>
#include <sys/lwp.h> #include <sys/lwp.h>
#include <sys/module.h> #include <sys/module.h>
#include <sys/percpu.h>
#include <sys/rwlock.h>
#include <sys/socketvar.h> #include <sys/socketvar.h>
#include <sys/uio.h> #include <sys/uio.h>
@ -61,6 +65,19 @@ static int npf_dev_ioctl(dev_t, u_long, void *, int, lwp_t *);
static int npf_dev_poll(dev_t, int, lwp_t *); static int npf_dev_poll(dev_t, int, lwp_t *);
static int npf_dev_read(dev_t, struct uio *, int); static int npf_dev_read(dev_t, struct uio *, int);
typedef struct {
npf_ruleset_t * n_rules;
npf_tableset_t * n_tables;
npf_ruleset_t * n_nat_rules;
} npf_core_t;
static void npf_core_destroy(npf_core_t *);
static int npfctl_stats(void *);
static krwlock_t npf_lock __cacheline_aligned;
static npf_core_t * npf_core __cacheline_aligned;
static percpu_t * npf_stats_percpu __read_mostly;
const struct cdevsw npf_cdevsw = { const struct cdevsw npf_cdevsw = {
npf_dev_open, npf_dev_close, npf_dev_read, nowrite, npf_dev_ioctl, npf_dev_open, npf_dev_close, npf_dev_read, nowrite, npf_dev_ioctl,
nostop, notty, npf_dev_poll, nommap, nokqfilter, D_OTHER | D_MPSAFE nostop, notty, npf_dev_poll, nommap, nokqfilter, D_OTHER | D_MPSAFE
@ -72,39 +89,31 @@ npf_init(void)
#ifdef _MODULE #ifdef _MODULE
devmajor_t bmajor = NODEVMAJOR, cmajor = NODEVMAJOR; devmajor_t bmajor = NODEVMAJOR, cmajor = NODEVMAJOR;
#endif #endif
int error; npf_ruleset_t *rset, *nset;
npf_tableset_t *tset;
int error = 0;
/* rw_init(&npf_lock);
* Initialise ruleset, tables and session structures. npf_stats_percpu = percpu_alloc(NPF_STATS_SIZE);
*/ npf_tableset_sysinit();
npf_session_sysinit();
error = npf_ruleset_sysinit();
if (error)
return error;
error = npf_tableset_sysinit();
if (error) {
npf_ruleset_sysfini();
return error;
}
error = npf_session_sysinit();
if (error) {
npf_tableset_sysfini();
npf_ruleset_sysfini();
return error;
}
npf_nat_sysinit(); npf_nat_sysinit();
npf_alg_sysinit(); npf_alg_sysinit();
npflogattach(1);
/* Load empty configuration. */
rset = npf_ruleset_create();
tset = npf_tableset_create();
nset = npf_ruleset_create();
npf_reload(rset, tset, nset);
KASSERT(npf_core != NULL);
#ifdef _MODULE #ifdef _MODULE
/* Attach /dev/npf device. */ /* Attach /dev/npf device. */
error = devsw_attach("npf", NULL, &bmajor, &npf_cdevsw, &cmajor); error = devsw_attach("npf", NULL, &bmajor, &npf_cdevsw, &cmajor);
if (error) { if (error) {
npf_nat_sysfini(); /* It will call devsw_detach(), which is safe. */
npf_session_sysfini(); (void)npf_fini();
npf_tableset_sysfini();
npf_ruleset_sysfini();
} }
#endif #endif
return error; return error;
@ -114,15 +123,24 @@ static int
npf_fini(void) npf_fini(void)
{ {
/*
* At first, detach device, remove pfil hooks and unload existing
* configuration, destroy structures.
*/
#ifdef _MODULE #ifdef _MODULE
/* At first, detach device and remove pfil hooks. */
devsw_detach(NULL, &npf_cdevsw); devsw_detach(NULL, &npf_cdevsw);
#endif #endif
npf_unregister_pfil();
npf_core_destroy(npf_core);
npflogdetach();
/* Note: order is particular. */
npf_nat_sysfini(); npf_nat_sysfini();
npf_alg_sysfini(); npf_alg_sysfini();
npf_session_sysfini(); npf_session_sysfini();
npf_tableset_sysfini(); npf_tableset_sysfini();
npf_ruleset_sysfini(); percpu_free(npf_stats_percpu, NPF_STATS_SIZE);
rw_destroy(&npf_lock);
return 0; return 0;
} }
@ -194,6 +212,15 @@ npf_dev_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
case IOC_NPF_TABLE: case IOC_NPF_TABLE:
error = npfctl_table(data); error = npfctl_table(data);
break; break;
case IOC_NPF_STATS:
error = npfctl_stats(data);
break;
case IOC_NPF_SESSIONS_SAVE:
error = npfctl_sessions_save(cmd, data);
break;
case IOC_NPF_SESSIONS_LOAD:
error = npfctl_sessions_load(cmd, data);
break;
default: default:
error = ENOTTY; error = ENOTTY;
break; break;
@ -214,3 +241,137 @@ npf_dev_read(dev_t dev, struct uio *uio, int flag)
return ENOTSUP; return ENOTSUP;
} }
/*
* NPF core loading/reloading/unloading mechanism.
*/
static void
npf_core_destroy(npf_core_t *nc)
{
npf_tableset_destroy(nc->n_tables);
npf_ruleset_destroy(nc->n_rules);
npf_ruleset_destroy(nc->n_nat_rules);
kmem_free(nc, sizeof(npf_core_t));
}
/*
* npf_reload: atomically load new ruleset, tableset and NAT policies.
* Then destroy old (unloaded) structures.
*/
void
npf_reload(npf_ruleset_t *rset, npf_tableset_t *tset, npf_ruleset_t *nset)
{
npf_core_t *nc, *onc;
/* Setup a new core structure. */
nc = kmem_alloc(sizeof(npf_core_t), KM_SLEEP);
nc->n_rules = rset;
nc->n_tables = tset;
nc->n_nat_rules = nset;
/* Lock and load the core structure. */
rw_enter(&npf_lock, RW_WRITER);
onc = atomic_swap_ptr(&npf_core, nc);
if (onc) {
/* Reload only necessary NAT policies. */
npf_ruleset_natreload(nset, onc->n_nat_rules);
}
/* Unlock. Everything goes "live" now. */
rw_exit(&npf_lock);
/* Turn on/off session tracking accordingly. */
npf_session_tracking(true);
if (onc) {
/* Destroy unloaded structures. */
npf_core_destroy(onc);
}
}
void
npf_core_enter(void)
{
rw_enter(&npf_lock, RW_READER);
}
npf_ruleset_t *
npf_core_ruleset(void)
{
KASSERT(rw_lock_held(&npf_lock));
return npf_core->n_rules;
}
npf_ruleset_t *
npf_core_natset(void)
{
KASSERT(rw_lock_held(&npf_lock));
return npf_core->n_nat_rules;
}
npf_tableset_t *
npf_core_tableset(void)
{
KASSERT(rw_lock_held(&npf_lock));
return npf_core->n_tables;
}
void
npf_core_exit(void)
{
rw_exit(&npf_lock);
}
bool
npf_core_locked(void)
{
return rw_lock_held(&npf_lock);
}
/*
* NPF statistics interface.
*/
void
npf_stats_inc(npf_stats_t st)
{
uint64_t *stats = percpu_getref(npf_stats_percpu);
stats[st]++;
percpu_putref(npf_stats_percpu);
}
void
npf_stats_dec(npf_stats_t st)
{
uint64_t *stats = percpu_getref(npf_stats_percpu);
stats[st]--;
percpu_putref(npf_stats_percpu);
}
static void
npf_stats_collect(void *mem, void *arg, struct cpu_info *ci)
{
uint64_t *percpu_stats = mem, *full_stats = arg;
int i;
for (i = 0; i < NPF_STATS_COUNT; i++) {
full_stats[i] += percpu_stats[i];
}
}
/*
* npfctl_stats: export collected statistics.
*/
static int
npfctl_stats(void *data)
{
uint64_t *fullst, *uptr = *(uint64_t **)data;
int error;
fullst = kmem_zalloc(NPF_STATS_SIZE, KM_SLEEP);
percpu_foreach(npf_stats_percpu, npf_stats_collect, fullst);
error = copyout(fullst, uptr, NPF_STATS_SIZE);
kmem_free(fullst, NPF_STATS_SIZE);
return error;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf.h,v 1.4 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf.h,v 1.5 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -98,9 +98,8 @@ typedef struct in6_addr npf_addr_t;
#define NPC_IP46 (NPC_IP4|NPC_IP6) #define NPC_IP46 (NPC_IP4|NPC_IP6)
typedef struct { typedef struct {
/* Information flags and packet direction. */ /* Information flags. */
uint32_t npc_info; uint32_t npc_info;
int npc_di;
/* Pointers to the IP v4/v6 addresses. */ /* Pointers to the IP v4/v6 addresses. */
npf_addr_t * npc_srcip; npf_addr_t * npc_srcip;
npf_addr_t * npc_dstip; npf_addr_t * npc_dstip;
@ -147,7 +146,7 @@ int nbuf_add_tag(nbuf_t *, uint32_t, uint32_t);
int nbuf_find_tag(nbuf_t *, uint32_t, void **); int nbuf_find_tag(nbuf_t *, uint32_t, void **);
/* Ruleset interface. */ /* Ruleset interface. */
npf_rule_t * npf_rule_alloc(int, pri_t, int, void *, size_t, bool, int, int); npf_rule_t * npf_rule_alloc(prop_dictionary_t, void *, size_t);
void npf_rule_free(npf_rule_t *); void npf_rule_free(npf_rule_t *);
void npf_activate_rule(npf_rule_t *); void npf_activate_rule(npf_rule_t *);
void npf_deactivate_rule(npf_rule_t *); void npf_deactivate_rule(npf_rule_t *);
@ -206,6 +205,33 @@ typedef struct npf_ioctl_table {
int _reserved; int _reserved;
} npf_ioctl_table_t; } npf_ioctl_table_t;
typedef enum {
/* Packets passed. */
NPF_STAT_PASS_DEFAULT,
NPF_STAT_PASS_RULESET,
NPF_STAT_PASS_SESSION,
/* Packets blocked. */
NPF_STAT_BLOCK_DEFAULT,
NPF_STAT_BLOCK_RULESET,
/* Session and NAT entries. */
NPF_STAT_SESSION_CREATE,
NPF_STAT_SESSION_DESTROY,
NPF_STAT_NAT_CREATE,
NPF_STAT_NAT_DESTROY,
/* Invalid state cases. */
NPF_STAT_INVALID_STATE,
NPF_STAT_INVALID_STATE_TCP1,
NPF_STAT_INVALID_STATE_TCP2,
NPF_STAT_INVALID_STATE_TCP3,
/* Raced packets. */
NPF_STAT_RACE_SESSION,
NPF_STAT_RACE_NAT,
/* Count (last). */
NPF_STATS_COUNT
} npf_stats_t;
#define NPF_STATS_SIZE (sizeof(uint64_t) * NPF_STATS_COUNT)
/* /*
* IOCTL operations. * IOCTL operations.
*/ */
@ -214,5 +240,8 @@ typedef struct npf_ioctl_table {
#define IOC_NPF_SWITCH _IOW('N', 101, int) #define IOC_NPF_SWITCH _IOW('N', 101, int)
#define IOC_NPF_RELOAD _IOW('N', 102, struct plistref) #define IOC_NPF_RELOAD _IOW('N', 102, struct plistref)
#define IOC_NPF_TABLE _IOW('N', 103, struct npf_ioctl_table) #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)
#endif /* _NPF_H_ */ #endif /* _NPF_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_alg_icmp.c,v 1.4 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_alg_icmp.c,v 1.5 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2010 The NetBSD Foundation, Inc. * Copyright (c) 2010 The NetBSD Foundation, Inc.
@ -34,7 +34,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.4 2010/11/11 06:30:39 rmind Exp $"); __KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.5 2010/12/18 01:07:25 rmind Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/kernel.h> #include <sys/kernel.h>
@ -151,7 +151,7 @@ npfa_icmp_match(npf_cache_t *npc, nbuf_t *nbuf, void *ntptr)
* npf_icmp_uniqid: retrieve unique identifiers - either ICMP query ID * npf_icmp_uniqid: retrieve unique identifiers - either ICMP query ID
* or TCP/UDP ports of the original packet, which is embedded. * or TCP/UDP ports of the original packet, which is embedded.
*/ */
static inline bool static bool
npf_icmp_uniqid(const int type, npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr) npf_icmp_uniqid(const int type, npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr)
{ {
struct icmp *ic; struct icmp *ic;
@ -252,7 +252,7 @@ npfa_icmp_session(npf_cache_t *npc, nbuf_t *nbuf, void *keyptr)
*/ */
KASSERT(npf_iscached(key, NPC_IP46)); KASSERT(npf_iscached(key, NPC_IP46));
KASSERT(npf_iscached(key, NPC_LAYER4)); KASSERT(npf_iscached(key, NPC_LAYER4));
key->npc_di = (npc->npc_di == PFIL_IN) ? PFIL_OUT : PFIL_IN; key->npc_ipsz = npc->npc_ipsz;
return true; return true;
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_ctl.c,v 1.3 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_ctl.c,v 1.4 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -34,13 +34,10 @@
* *
* Implementation of (re)loading, construction of tables and rules. * Implementation of (re)loading, construction of tables and rules.
* NPF proplib(9) dictionary consumer. * NPF proplib(9) dictionary consumer.
*
* TODO:
* - Consider implementing 'sync' functionality.
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.3 2010/11/11 06:30:39 rmind Exp $"); __KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.4 2010/12/18 01:07:25 rmind Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/conf.h> #include <sys/conf.h>
@ -84,9 +81,6 @@ npf_mk_tables(npf_tableset_t *tblset, prop_array_t tables)
return EINVAL; return EINVAL;
it = prop_array_iterator(tables); it = prop_array_iterator(tables);
if (it == NULL)
return ENOMEM;
while ((tbldict = prop_object_iterator_next(it)) != NULL) { while ((tbldict = prop_object_iterator_next(it)) != NULL) {
prop_dictionary_t ent; prop_dictionary_t ent;
prop_object_iterator_t eit; prop_object_iterator_t eit;
@ -127,10 +121,6 @@ npf_mk_tables(npf_tableset_t *tblset, prop_array_t tables)
break; break;
} }
eit = prop_array_iterator(entries); eit = prop_array_iterator(entries);
if (eit == NULL) {
error = ENOMEM;
break;
}
while ((ent = prop_object_iterator_next(eit)) != NULL) { while ((ent = prop_object_iterator_next(eit)) != NULL) {
in_addr_t addr, mask; /* XXX: IPv6 */ in_addr_t addr, mask; /* XXX: IPv6 */
@ -151,46 +141,17 @@ npf_mk_tables(npf_tableset_t *tblset, prop_array_t tables)
} }
prop_object_iterator_release(it); prop_object_iterator_release(it);
/* /*
* Note: in a case of error, caller will free entire tableset. * Note: in a case of error, caller will free the tableset.
*/ */
return error; return error;
} }
static void *
npf_mk_ncode(const void *ncptr, size_t nc_size)
{
int npf_err, errat;
void *nc;
/*
* Allocate and copy n-code.
*
* XXX: Inefficient; consider extending proplib(9) to provide
* interface for custom allocator and avoid copy.
*/
nc = npf_ncode_alloc(nc_size);
if (nc == NULL) {
return NULL;
}
memcpy(nc, ncptr, nc_size);
npf_err = npf_ncode_validate(nc, nc_size, &errat);
if (npf_err) {
npf_ncode_free(nc, nc_size);
/* TODO: return error details via proplib */
return NULL;
}
return nc;
}
static int static int
npf_mk_singlerule(prop_dictionary_t rldict, npf_mk_singlerule(prop_dictionary_t rldict,
npf_ruleset_t *rlset, npf_rule_t **parent) npf_ruleset_t *rlset, npf_rule_t **parent)
{ {
npf_rule_t *rl; npf_rule_t *rl;
prop_object_t obj; prop_object_t obj;
int attr, ifidx, minttl, maxmss;
pri_t pri;
bool rnd_ipid;
size_t nc_size; size_t nc_size;
void *nc; void *nc;
@ -198,45 +159,31 @@ npf_mk_singlerule(prop_dictionary_t rldict,
if (prop_object_type(rldict) != PROP_TYPE_DICTIONARY) if (prop_object_type(rldict) != PROP_TYPE_DICTIONARY)
return EINVAL; return EINVAL;
/* Attributes (integer). */
obj = prop_dictionary_get(rldict, "attributes");
attr = prop_number_integer_value(obj);
/* Priority (integer). */
obj = prop_dictionary_get(rldict, "priority");
pri = prop_number_integer_value(obj);
/* Interface ID (integer). */
obj = prop_dictionary_get(rldict, "interface");
ifidx = prop_number_integer_value(obj);
/* Randomize IP ID (bool). */
obj = prop_dictionary_get(rldict, "randomize-id");
rnd_ipid = prop_bool_true(obj);
/* Minimum IP TTL (integer). */
obj = prop_dictionary_get(rldict, "min-ttl");
minttl = prop_number_integer_value(obj);
/* Maximum TCP MSS (integer). */
obj = prop_dictionary_get(rldict, "max-mss");
maxmss = prop_number_integer_value(obj);
/* N-code (binary data). */ /* N-code (binary data). */
obj = prop_dictionary_get(rldict, "ncode"); obj = prop_dictionary_get(rldict, "ncode");
if (obj) { if (obj) {
const void *ncptr; const void *ncptr;
int npf_err, errat;
/* Perform n-code validation. */ /*
nc_size = prop_data_size(obj); * Allocate, copy and validate n-code. XXX: Inefficient.
*/
ncptr = prop_data_data_nocopy(obj); ncptr = prop_data_data_nocopy(obj);
nc_size = prop_data_size(obj);
if (ncptr == NULL || nc_size > NPF_NCODE_LIMIT) { if (ncptr == NULL || nc_size > NPF_NCODE_LIMIT) {
return EINVAL; return EINVAL;
} }
nc = npf_mk_ncode(ncptr, nc_size); nc = npf_ncode_alloc(nc_size);
if (nc == NULL) { if (nc == NULL) {
return EINVAL; return EINVAL;
} }
memcpy(nc, ncptr, nc_size);
npf_err = npf_ncode_validate(nc, nc_size, &errat);
if (npf_err) {
npf_ncode_free(nc, nc_size);
/* TODO: return error details via proplib */
return EINVAL;
}
} else { } else {
/* No n-code. */ /* No n-code. */
nc = NULL; nc = NULL;
@ -244,8 +191,7 @@ npf_mk_singlerule(prop_dictionary_t rldict,
} }
/* Allocate and setup NPF rule. */ /* Allocate and setup NPF rule. */
rl = npf_rule_alloc(attr, pri, ifidx, nc, nc_size, rl = npf_rule_alloc(rldict, nc, nc_size);
rnd_ipid, minttl, maxmss);
if (rl == NULL) { if (rl == NULL) {
if (nc) { if (nc) {
npf_ncode_free(nc, nc_size); /* XXX */ npf_ncode_free(nc, nc_size); /* XXX */
@ -270,11 +216,8 @@ npf_mk_rules(npf_ruleset_t *rlset, prop_array_t rules)
if (prop_object_type(rules) != PROP_TYPE_ARRAY) if (prop_object_type(rules) != PROP_TYPE_ARRAY)
return EINVAL; return EINVAL;
it = prop_array_iterator(rules);
if (it == NULL)
return ENOMEM;
error = 0; error = 0;
it = prop_array_iterator(rules);
while ((rldict = prop_object_iterator_next(it)) != NULL) { while ((rldict = prop_object_iterator_next(it)) != NULL) {
prop_object_iterator_t sit; prop_object_iterator_t sit;
prop_array_t subrules; prop_array_t subrules;
@ -298,10 +241,6 @@ npf_mk_rules(npf_ruleset_t *rlset, prop_array_t rules)
break; break;
} }
sit = prop_array_iterator(subrules); sit = prop_array_iterator(subrules);
if (sit == NULL) {
error = ENOMEM;
break;
}
while ((srldict = prop_object_iterator_next(sit)) != NULL) { while ((srldict = prop_object_iterator_next(sit)) != NULL) {
/* For subrule, pass ruleset pointer of parent. */ /* For subrule, pass ruleset pointer of parent. */
error = npf_mk_singlerule(srldict, error = npf_mk_singlerule(srldict,
@ -315,7 +254,7 @@ npf_mk_rules(npf_ruleset_t *rlset, prop_array_t rules)
} }
prop_object_iterator_release(it); prop_object_iterator_release(it);
/* /*
* Note: in a case of error, caller will free entire ruleset. * Note: in a case of error, caller will free the ruleset.
*/ */
return error; return error;
} }
@ -331,19 +270,11 @@ npf_mk_natlist(npf_ruleset_t *nset, prop_array_t natlist)
if (prop_object_type(natlist) != PROP_TYPE_ARRAY) if (prop_object_type(natlist) != PROP_TYPE_ARRAY)
return EINVAL; return EINVAL;
it = prop_array_iterator(natlist);
if (it == NULL)
return ENOMEM;
error = 0; error = 0;
it = prop_array_iterator(natlist);
while ((natdict = prop_object_iterator_next(it)) != NULL) { while ((natdict = prop_object_iterator_next(it)) != NULL) {
prop_object_t obj;
npf_natpolicy_t *np; npf_natpolicy_t *np;
npf_rule_t *rl; npf_rule_t *rl;
const npf_addr_t *taddr;
size_t taddr_sz;
in_port_t tport;
int type, flags;
/* NAT policy - dictionary. */ /* NAT policy - dictionary. */
if (prop_object_type(natdict) != PROP_TYPE_DICTIONARY) { if (prop_object_type(natdict) != PROP_TYPE_DICTIONARY) {
@ -351,23 +282,6 @@ npf_mk_natlist(npf_ruleset_t *nset, prop_array_t natlist)
break; break;
} }
/* Translation type. */
obj = prop_dictionary_get(natdict, "type");
type = prop_number_integer_value(obj);
/* Translation type. */
obj = prop_dictionary_get(natdict, "flags");
flags = prop_number_integer_value(obj);
/* Translation IP. */
obj = prop_dictionary_get(natdict, "translation-ip");
taddr_sz = prop_data_size(obj);
taddr = (const npf_addr_t *)prop_data_data_nocopy(obj);
/* Translation port (for redirect case). */
obj = prop_dictionary_get(natdict, "translation-port");
tport = (in_port_t)prop_number_integer_value(obj);
/* /*
* NAT policies are standard rules, plus additional * NAT policies are standard rules, plus additional
* information for translation. Make a rule. * information for translation. Make a rule.
@ -377,8 +291,9 @@ npf_mk_natlist(npf_ruleset_t *nset, prop_array_t natlist)
break; break;
/* Allocate a new NAT policy and assign to the rule. */ /* Allocate a new NAT policy and assign to the rule. */
np = npf_nat_newpolicy(type, flags, taddr, taddr_sz, tport); np = npf_nat_newpolicy(natdict);
if (np == NULL) { if (np == NULL) {
npf_rule_free(rl);
error = ENOMEM; error = ENOMEM;
break; break;
} }
@ -405,7 +320,6 @@ npfctl_reload(u_long cmd, void *data)
npf_ruleset_t *nset = NULL; npf_ruleset_t *nset = NULL;
prop_dictionary_t dict; prop_dictionary_t dict;
prop_array_t natlist, tables, rules; prop_array_t natlist, tables, rules;
prop_object_t ver;
int error; int error;
/* Retrieve the dictionary. */ /* Retrieve the dictionary. */
@ -418,16 +332,6 @@ npfctl_reload(u_long cmd, void *data)
if (dict == NULL) if (dict == NULL)
return EINVAL; return EINVAL;
#endif #endif
/* Version. */
ver = prop_dictionary_get(dict, "version");
if (ver == NULL || prop_number_integer_value(ver) != NPF_VERSION) {
error = EINVAL;
goto fail;
}
/* XXX: Hard way for now. */
(void)npf_session_tracking(false);
/* NAT policies. */ /* NAT policies. */
nset = npf_ruleset_create(); nset = npf_ruleset_create();
natlist = prop_dictionary_get(dict, "translation"); natlist = prop_dictionary_get(dict, "translation");
@ -449,16 +353,11 @@ npfctl_reload(u_long cmd, void *data)
if (error) if (error)
goto fail; goto fail;
/* Flush and reload NAT policies. */
npf_nat_reload(nset);
/* /*
* Finally, reload the ruleset. It will also reload the tableset. * Finally - reload ruleset, tableset and NAT policies.
* Operation will be performed as a single transaction. * Operation will be performed as a single transaction.
*/ */
npf_ruleset_reload(rlset, tblset); npf_reload(rlset, tblset, nset);
(void)npf_session_tracking(true);
/* Done. Since data is consumed now, we shall not destroy it. */ /* Done. Since data is consumed now, we shall not destroy it. */
tblset = NULL; tblset = NULL;
@ -482,6 +381,110 @@ fail:
return error; return error;
} }
/*
* npfctl_sessions_save: construct a list of sessions and export for saving.
*/
int
npfctl_sessions_save(u_long cmd, void *data)
{
struct plistref *pref = data;
prop_dictionary_t sesdict;
prop_array_t selist, nplist;
int error;
/* Create a dictionary and two lists. */
sesdict = prop_dictionary_create();
selist = prop_array_create();
nplist = prop_array_create();
/* Save the sessions. */
error = npf_session_save(selist, nplist);
if (error) {
goto fail;
}
/* Set the session list, NAT policy list and export the dictionary. */
prop_dictionary_set(sesdict, "session-list", selist);
prop_dictionary_set(sesdict, "nat-policy-list", nplist);
#ifdef _KERNEL
error = prop_dictionary_copyout_ioctl(pref, cmd, sesdict);
#else
error = prop_dictionary_externalize_to_file(sesdict, data) ? 0 : errno;
#endif
fail:
prop_object_release(sesdict);
return error;
}
/*
* npfctl_sessions_load: import a list of sessions, reconstruct them and load.
*/
int
npfctl_sessions_load(u_long cmd, void *data)
{
const struct plistref *pref = data;
npf_sehash_t *sehasht = NULL;
prop_dictionary_t sesdict, sedict;
prop_object_iterator_t it;
prop_array_t selist;
int error;
/* Retrieve the dictionary containing session and NAT policy lists. */
#ifdef _KERNEL
error = prop_dictionary_copyin_ioctl(pref, cmd, &sesdict);
if (error)
return error;
#else
sesdict = prop_dictionary_internalize_from_file(data);
if (sesdict == NULL)
return EINVAL;
#endif
/*
* Note: session objects contain the references to the NAT policy
* entries. Therefore, no need to directly access it.
*/
selist = prop_dictionary_get(sesdict, "session-list");
if (prop_object_type(selist) != PROP_TYPE_ARRAY) {
error = EINVAL;
goto fail;
}
/* Create a session hash table. */
sehasht = sess_htable_create();
if (sehasht == NULL) {
error = ENOMEM;
goto fail;
}
/*
* Iterate through and construct each session.
*/
error = 0;
it = prop_array_iterator(selist);
npf_core_enter();
while ((sedict = prop_object_iterator_next(it)) != NULL) {
/* Session - dictionary. */
if (prop_object_type(sedict) != PROP_TYPE_DICTIONARY) {
error = EINVAL;
goto fail;
}
/* Construct and insert real session structure. */
error = npf_session_restore(sehasht, sedict);
if (error) {
goto fail;
}
}
npf_core_exit();
sess_htable_reload(sehasht);
fail:
prop_object_release(selist);
if (error && sehasht) {
/* Destroy session table. */
sess_htable_destroy(sehasht);
}
return error;
}
/* /*
* npfctl_table: add, remove or query entries in the specified table. * npfctl_table: add, remove or query entries in the specified table.
* *

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_handler.c,v 1.4 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_handler.c,v 1.5 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -34,7 +34,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.4 2010/11/11 06:30:39 rmind Exp $"); __KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.5 2010/12/18 01:07:25 rmind Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -84,7 +84,7 @@ npf_packet_handler(void *arg, struct mbuf **mp, struct ifnet *ifp, int di)
npf_cache_t npc; npf_cache_t npc;
npf_session_t *se; npf_session_t *se;
npf_rule_t *rl; npf_rule_t *rl;
bool keepstate; npf_rproc_t *rp;
int retfl, error; int retfl, error;
/* /*
@ -94,6 +94,7 @@ npf_packet_handler(void *arg, struct mbuf **mp, struct ifnet *ifp, int di)
npc.npc_info = 0; npc.npc_info = 0;
error = 0; error = 0;
retfl = 0; retfl = 0;
rp = NULL;
/* Cache everything. Determine whether it is an IPv4 fragment. */ /* Cache everything. Determine whether it is an IPv4 fragment. */
if (npf_cache_all(&npc, nbuf) && npf_iscached(&npc, NPC_IPFRAG)) { if (npf_cache_all(&npc, nbuf) && npf_iscached(&npc, NPC_IPFRAG)) {
@ -119,7 +120,8 @@ npf_packet_handler(void *arg, struct mbuf **mp, struct ifnet *ifp, int di)
se = npf_session_inspect(&npc, nbuf, di); se = npf_session_inspect(&npc, nbuf, di);
/* If "passing" session found - skip the ruleset inspection. */ /* If "passing" session found - skip the ruleset inspection. */
if (se && npf_session_pass(se)) { if (se && npf_session_pass(se, &rp)) {
npf_stats_inc(NPF_STAT_PASS_SESSION);
goto pass; goto pass;
} }
@ -127,29 +129,44 @@ npf_packet_handler(void *arg, struct mbuf **mp, struct ifnet *ifp, int di)
rl = npf_ruleset_inspect(&npc, nbuf, ifp, di, NPF_LAYER_3); rl = npf_ruleset_inspect(&npc, nbuf, ifp, di, NPF_LAYER_3);
if (rl == NULL) { if (rl == NULL) {
if (default_pass) { if (default_pass) {
npf_stats_inc(NPF_STAT_PASS_DEFAULT);
goto pass; goto pass;
} }
npf_stats_inc(NPF_STAT_BLOCK_DEFAULT);
error = ENETUNREACH; error = ENETUNREACH;
goto out; goto out;
} }
/* Apply the rule. */ /* Apply the rule. */
error = npf_rule_apply(&npc, nbuf, rl, &keepstate, &retfl); error = npf_rule_apply(&npc, nbuf, rl, &retfl);
if (error) { if (error) {
npf_stats_inc(NPF_STAT_BLOCK_RULESET);
goto out; goto out;
} }
npf_stats_inc(NPF_STAT_PASS_RULESET);
/* Establish a "pass" session, if required. */ /* Establish a "pass" session, if required. */
if (keepstate && !se) { if ((retfl & NPF_RULE_KEEPSTATE) != 0 && !se) {
se = npf_session_establish(&npc, nbuf, NULL, di); se = npf_session_establish(&npc, nbuf, di);
if (se == NULL) { if (se == NULL) {
error = ENOMEM; error = ENOMEM;
goto out; goto out;
} }
npf_session_setpass(se); /* Associate rule processing data (XXX locking). */
rp = npf_rproc_return(rl);
npf_session_setpass(se, rp);
} else {
/* XXX: Return rule processing, needs locking. */
} }
pass: pass:
KASSERT(error == 0); KASSERT(error == 0);
/*
* Perform rule processing, if required.
*/
if (rp) {
npf_rproc_run(&npc, nbuf, rp);
}
/* /*
* Perform NAT. * Perform NAT.
*/ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_impl.h,v 1.4 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_impl.h,v 1.5 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -57,16 +57,20 @@
*/ */
struct npf_nat; struct npf_nat;
struct npf_rproc;
struct npf_session; struct npf_session;
typedef struct npf_nat npf_nat_t; typedef struct npf_nat npf_nat_t;
typedef struct npf_rproc npf_rproc_t;
typedef struct npf_alg npf_alg_t; typedef struct npf_alg npf_alg_t;
typedef struct npf_natpolicy npf_natpolicy_t; typedef struct npf_natpolicy npf_natpolicy_t;
typedef struct npf_session npf_session_t; typedef struct npf_session npf_session_t;
struct npf_sehash;
struct npf_tblent; struct npf_tblent;
struct npf_table; struct npf_table;
typedef struct npf_sehash npf_sehash_t;
typedef struct npf_tblent npf_tblent_t; typedef struct npf_tblent npf_tblent_t;
typedef struct npf_table npf_table_t; typedef struct npf_table npf_table_t;
@ -76,11 +80,12 @@ typedef npf_table_t * npf_tableset_t;
* DEFINITIONS. * DEFINITIONS.
*/ */
typedef bool (*npf_algfunc_t)(npf_cache_t *, nbuf_t *, void *); typedef bool (*npf_algfunc_t)(npf_cache_t *, nbuf_t *, void *);
#define NPF_NCODE_LIMIT 1024 #define NPF_NCODE_LIMIT 1024
#define NPF_TABLE_SLOTS 32 #define NPF_TABLE_SLOTS 32
/* /*
* SESSION STATE STRUCTURES * SESSION STATE STRUCTURES
*/ */
@ -88,7 +93,7 @@ typedef bool (*npf_algfunc_t)(npf_cache_t *, nbuf_t *, void *);
#define ST_OPENING 1 /* SYN has been sent. */ #define ST_OPENING 1 /* SYN has been sent. */
#define ST_ACKNOWLEDGE 2 /* SYN-ACK received, wait for ACK. */ #define ST_ACKNOWLEDGE 2 /* SYN-ACK received, wait for ACK. */
#define ST_ESTABLISHED 3 /* ACK seen, connection established. */ #define ST_ESTABLISHED 3 /* ACK seen, connection established. */
#define ST_CLOSING 4 #define ST_CLOSING 4 /* FIN or RST seen. */
typedef struct { typedef struct {
uint32_t nst_seqend; /* SEQ number + length. */ uint32_t nst_seqend; /* SEQ number + length. */
@ -107,14 +112,30 @@ typedef struct {
* INTERFACES. * INTERFACES.
*/ */
/* NPF control. */ /* NPF control, statistics, etc. */
void npf_core_enter(void);
npf_ruleset_t * npf_core_ruleset(void);
npf_ruleset_t * npf_core_natset(void);
npf_tableset_t *npf_core_tableset(void);
void npf_core_exit(void);
bool npf_core_locked(void);
void npf_reload(npf_ruleset_t *, npf_tableset_t *, npf_ruleset_t *);
void npflogattach(int);
void npflogdetach(void);
int npfctl_switch(void *); int npfctl_switch(void *);
int npfctl_reload(u_long, void *); int npfctl_reload(u_long, void *);
int npfctl_sessions_save(u_long, void *);
int npfctl_sessions_load(u_long, void *);
int npfctl_table(void *); int npfctl_table(void *);
void npf_stats_inc(npf_stats_t);
void npf_stats_dec(npf_stats_t);
/* Packet filter hooks. */ /* Packet filter hooks. */
int npf_register_pfil(void); int npf_register_pfil(void);
void npf_unregister_pfil(void); void npf_unregister_pfil(void);
void npf_log_packet(npf_cache_t *, nbuf_t *, int);
/* Protocol helpers. */ /* Protocol helpers. */
bool npf_fetch_ip(npf_cache_t *, nbuf_t *, void *); bool npf_fetch_ip(npf_cache_t *, nbuf_t *, void *);
@ -137,7 +158,7 @@ uint32_t npf_addr_sum(const int, const npf_addr_t *, const npf_addr_t *);
int npf_tcpsaw(npf_cache_t *, tcp_seq *, tcp_seq *, uint32_t *); int npf_tcpsaw(npf_cache_t *, tcp_seq *, tcp_seq *, uint32_t *);
bool npf_fetch_tcpopts(const npf_cache_t *, nbuf_t *, bool npf_fetch_tcpopts(const npf_cache_t *, nbuf_t *,
uint16_t *, int *); uint16_t *, int *);
bool npf_normalize(npf_cache_t *, nbuf_t *, bool, u_int, u_int); bool npf_normalize(npf_cache_t *, nbuf_t *, bool, bool, u_int, u_int);
void npf_return_block(npf_cache_t *, nbuf_t *, const int); void npf_return_block(npf_cache_t *, nbuf_t *, const int);
/* Complex instructions. */ /* Complex instructions. */
@ -154,7 +175,7 @@ int npf_match_icmp4(npf_cache_t *, nbuf_t *, void *, uint32_t);
int npf_match_tcpfl(npf_cache_t *, nbuf_t *, void *, uint32_t); int npf_match_tcpfl(npf_cache_t *, nbuf_t *, void *, uint32_t);
/* Tableset interface. */ /* Tableset interface. */
int npf_tableset_sysinit(void); void npf_tableset_sysinit(void);
void npf_tableset_sysfini(void); void npf_tableset_sysfini(void);
npf_tableset_t *npf_tableset_create(void); npf_tableset_t *npf_tableset_create(void);
@ -177,39 +198,48 @@ int npf_table_rem_v4cidr(npf_tableset_t *, u_int,
int npf_table_match_v4addr(u_int, in_addr_t); int npf_table_match_v4addr(u_int, in_addr_t);
/* Ruleset interface. */ /* Ruleset interface. */
int npf_ruleset_sysinit(void);
void npf_ruleset_sysfini(void);
npf_ruleset_t * npf_ruleset_create(void); npf_ruleset_t * npf_ruleset_create(void);
void npf_ruleset_destroy(npf_ruleset_t *); void npf_ruleset_destroy(npf_ruleset_t *);
void npf_ruleset_insert(npf_ruleset_t *, npf_rule_t *); void npf_ruleset_insert(npf_ruleset_t *, npf_rule_t *);
void npf_ruleset_reload(npf_ruleset_t *, npf_tableset_t *); void npf_ruleset_natreload(npf_ruleset_t *, npf_ruleset_t *);
npf_rule_t * npf_ruleset_matchnat(npf_ruleset_t *, npf_natpolicy_t *);
npf_rule_t * npf_ruleset_match(npf_ruleset_t *, npf_cache_t *, nbuf_t *, npf_rule_t * npf_ruleset_match(npf_ruleset_t *, npf_cache_t *, nbuf_t *,
struct ifnet *, const int, const int); struct ifnet *, const int, const int);
npf_rule_t * npf_ruleset_inspect(npf_cache_t *, nbuf_t *, npf_rule_t * npf_ruleset_inspect(npf_cache_t *, nbuf_t *,
struct ifnet *, const int, const int); struct ifnet *, const int, const int);
int npf_rule_apply(npf_cache_t *, nbuf_t *, npf_rule_t *, int npf_rule_apply(npf_cache_t *, nbuf_t *, npf_rule_t *, int *);
bool *, int *);
npf_ruleset_t * npf_rule_subset(npf_rule_t *);
npf_ruleset_t * npf_rule_subset(npf_rule_t *);
npf_natpolicy_t *npf_rule_getnat(const npf_rule_t *); npf_natpolicy_t *npf_rule_getnat(const npf_rule_t *);
void npf_rule_setnat(npf_rule_t *, npf_natpolicy_t *); void npf_rule_setnat(npf_rule_t *, npf_natpolicy_t *);
npf_rproc_t * npf_rproc_create(prop_dictionary_t);
npf_rproc_t * npf_rproc_return(npf_rule_t *);
void npf_rproc_release(npf_rproc_t *);
void npf_rproc_run(npf_cache_t *, nbuf_t *, npf_rproc_t *);
/* Session handling interface. */ /* Session handling interface. */
int npf_session_sysinit(void); void npf_session_sysinit(void);
void npf_session_sysfini(void); void npf_session_sysfini(void);
int npf_session_tracking(bool); int npf_session_tracking(bool);
npf_sehash_t * sess_htable_create(void);
void sess_htable_destroy(npf_sehash_t *);
void sess_htable_reload(npf_sehash_t *);
npf_session_t * npf_session_inspect(npf_cache_t *, nbuf_t *, const int); npf_session_t * npf_session_inspect(npf_cache_t *, nbuf_t *, const int);
npf_session_t * npf_session_establish(const npf_cache_t *, nbuf_t *, npf_session_t * npf_session_establish(const npf_cache_t *, nbuf_t *, const int);
npf_nat_t *, const int);
void npf_session_release(npf_session_t *); void npf_session_release(npf_session_t *);
bool npf_session_pass(const npf_session_t *); void npf_session_expire(npf_session_t *);
void npf_session_setpass(npf_session_t *); bool npf_session_pass(const npf_session_t *, npf_rproc_t **);
void npf_session_link(npf_session_t *, npf_session_t *); void npf_session_setpass(npf_session_t *, npf_rproc_t *);
int npf_session_setnat(npf_session_t *, npf_nat_t *, const int);
npf_nat_t * npf_session_retnat(npf_session_t *, const int, bool *); npf_nat_t * npf_session_retnat(npf_session_t *, const int, bool *);
int npf_session_save(prop_array_t, prop_array_t);
int npf_session_restore(npf_sehash_t *, prop_dictionary_t);
/* State handling. */ /* State handling. */
bool npf_state_init(const npf_cache_t *, nbuf_t *, npf_state_t *); bool npf_state_init(const npf_cache_t *, nbuf_t *, npf_state_t *);
bool npf_state_inspect(const npf_cache_t *, nbuf_t *, npf_state_t *, bool npf_state_inspect(const npf_cache_t *, nbuf_t *, npf_state_t *,
@ -220,18 +250,20 @@ void npf_state_destroy(npf_state_t *);
/* NAT. */ /* NAT. */
void npf_nat_sysinit(void); void npf_nat_sysinit(void);
void npf_nat_sysfini(void); void npf_nat_sysfini(void);
npf_natpolicy_t *npf_nat_newpolicy(int, int, const npf_addr_t *, size_t, npf_natpolicy_t *npf_nat_newpolicy(prop_dictionary_t);
in_port_t);
void npf_nat_freepolicy(npf_natpolicy_t *); void npf_nat_freepolicy(npf_natpolicy_t *);
void npf_nat_flush(void); bool npf_nat_matchpolicy(npf_natpolicy_t *, npf_natpolicy_t *);
void npf_nat_reload(npf_ruleset_t *);
int npf_do_nat(npf_cache_t *, npf_session_t *, nbuf_t *, int npf_do_nat(npf_cache_t *, npf_session_t *, nbuf_t *,
struct ifnet *, const int); struct ifnet *, const int);
void npf_nat_expire(npf_nat_t *); void npf_nat_expire(npf_nat_t *);
void npf_nat_getorig(npf_nat_t *, npf_addr_t **, in_port_t *); 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); 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_session_t *);
/* ALG interface. */ /* ALG interface. */
void npf_alg_sysinit(void); void npf_alg_sysinit(void);
void npf_alg_sysfini(void); void npf_alg_sysfini(void);

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_inet.c,v 1.4 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_inet.c,v 1.5 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -34,7 +34,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.4 2010/11/11 06:30:39 rmind Exp $"); __KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.5 2010/12/18 01:07:25 rmind Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/kernel.h> #include <sys/kernel.h>
@ -112,6 +112,8 @@ npf_addr_sum(const int sz, const npf_addr_t *a1, const npf_addr_t *a2)
uint32_t mix = 0; uint32_t mix = 0;
int i; int i;
KASSERT(sz > 0 && a1 != NULL && a2 != NULL);
for (i = 0; i < (sz >> 2); i++) { for (i = 0; i < (sz >> 2); i++) {
mix += a1->s6_addr32[i]; mix += a1->s6_addr32[i];
mix += a2->s6_addr32[i]; mix += a2->s6_addr32[i];
@ -524,15 +526,17 @@ npf_rwrcksum(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr, const int di,
} }
static inline bool static inline bool
npf_normalize_ip4(npf_cache_t *npc, nbuf_t *nbuf, bool rnd, int minttl) npf_normalize_ip4(npf_cache_t *npc, nbuf_t *nbuf,
bool rnd, bool no_df, int minttl)
{ {
void *n_ptr = nbuf_dataptr(nbuf); void *n_ptr = nbuf_dataptr(nbuf);
struct ip *ip = &npc->npc_ip.v4; struct ip *ip = &npc->npc_ip.v4;
uint16_t cksum = ip->ip_sum; uint16_t cksum = ip->ip_sum;
uint16_t ip_off = ip->ip_off;
uint8_t ttl = ip->ip_ttl; uint8_t ttl = ip->ip_ttl;
u_int offby = 0; u_int offby = 0;
KASSERT(rnd || minttl); KASSERT(rnd || minttl || no_df);
/* Randomize IPv4 ID. */ /* Randomize IPv4 ID. */
if (rnd) { if (rnd) {
@ -547,6 +551,20 @@ npf_normalize_ip4(npf_cache_t *npc, nbuf_t *nbuf, bool rnd, int minttl)
ip->ip_id = nid; ip->ip_id = nid;
} }
/* IP_DF flag cleansing. */
if (no_df && (ip_off & htons(IP_DF)) != 0) {
uint16_t nip_off = ip_off & ~htons(IP_DF);
if (nbuf_advstore(&nbuf, &n_ptr,
offsetof(struct ip, ip_off) - offby,
sizeof(uint8_t), &nip_off)) {
return false;
}
cksum = npf_fixup16_cksum(cksum, ip_off, nip_off);
ip->ip_off = nip_off;
offby = offsetof(struct ip, ip_off);
}
/* Enforce minimum TTL. */ /* Enforce minimum TTL. */
if (minttl && ttl < minttl) { if (minttl && ttl < minttl) {
if (nbuf_advstore(&nbuf, &n_ptr, if (nbuf_advstore(&nbuf, &n_ptr,
@ -570,7 +588,7 @@ npf_normalize_ip4(npf_cache_t *npc, nbuf_t *nbuf, bool rnd, int minttl)
bool bool
npf_normalize(npf_cache_t *npc, nbuf_t *nbuf, npf_normalize(npf_cache_t *npc, nbuf_t *nbuf,
bool rnd, u_int minttl, u_int maxmss) bool no_df, bool rnd, u_int minttl, u_int maxmss)
{ {
void *n_ptr = nbuf_dataptr(nbuf); void *n_ptr = nbuf_dataptr(nbuf);
struct ip *ip = &npc->npc_ip.v4; struct ip *ip = &npc->npc_ip.v4;
@ -580,7 +598,7 @@ npf_normalize(npf_cache_t *npc, nbuf_t *nbuf,
/* Normalize IPv4. */ /* Normalize IPv4. */
if (npf_iscached(npc, NPC_IP4) && (rnd || minttl)) { if (npf_iscached(npc, NPC_IP4) && (rnd || minttl)) {
if (!npf_normalize_ip4(npc, nbuf, rnd, minttl)) { if (!npf_normalize_ip4(npc, nbuf, rnd, no_df, minttl)) {
return false; return false;
} }
} }

175
sys/net/npf/npf_log.c Normal file
View File

@ -0,0 +1,175 @@
/* $NetBSD: npf_log.c,v 1.1 2010/12/18 01:07:25 rmind Exp $ */
/*-
* Copyright (c) 2010 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.
*/
/*
* NPF logging interface.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_log.c,v 1.1 2010/12/18 01:07:25 rmind Exp $");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/mbuf.h>
#include <sys/mutex.h>
#include <sys/queue.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/bpf.h>
#include "npf_impl.h"
typedef struct npflog_softc {
LIST_ENTRY(npflog_softc) sc_entry;
kmutex_t sc_lock;
struct ifnet sc_if;
int sc_unit;
} npflog_softc_t;
static int npflog_clone_create(struct if_clone *, int );
static int npflog_clone_destroy(struct ifnet *);
static LIST_HEAD(, npflog_softc) npflog_if_list __cacheline_aligned;
static struct if_clone npflog_cloner =
IF_CLONE_INITIALIZER("npflog", npflog_clone_create, npflog_clone_destroy);
void
npflogattach(int nunits)
{
LIST_INIT(&npflog_if_list);
if_clone_attach(&npflog_cloner);
}
void
npflogdetach(void)
{
npflog_softc_t *sc;
while ((sc = LIST_FIRST(&npflog_if_list)) != NULL) {
npflog_clone_destroy(&sc->sc_if);
}
if_clone_detach(&npflog_cloner);
}
static int
npflog_ioctl(struct ifnet *ifp, u_long cmd, void *data)
{
npflog_softc_t *sc = ifp->if_softc;
int error = 0;
mutex_enter(&sc->sc_lock);
switch (cmd) {
case SIOCINITIFADDR:
ifp->if_flags |= (IFF_UP | IFF_RUNNING);
break;
default:
error = ifioctl_common(ifp, cmd, data);
break;
}
mutex_exit(&sc->sc_lock);
return error;
}
static int
npflog_clone_create(struct if_clone *ifc, int unit)
{
npflog_softc_t *sc;
struct ifnet *ifp;
sc = kmem_zalloc(sizeof(npflog_softc_t), KM_SLEEP);
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTNET);
ifp = &sc->sc_if;
ifp->if_softc = sc;
if_initname(ifp, "npflog", unit);
ifp->if_type = IFT_OTHER;
ifp->if_dlt = DLT_NULL;
ifp->if_ioctl = npflog_ioctl;
if_attach(ifp);
if_alloc_sadl(ifp);
bpf_attach(ifp, DLT_NULL, 0);
LIST_INSERT_HEAD(&npflog_if_list, sc, sc_entry);
return 0;
}
static int
npflog_clone_destroy(struct ifnet *ifp)
{
npflog_softc_t *sc = ifp->if_softc;
LIST_REMOVE(sc, sc_entry);
bpf_detach(ifp);
if_detach(ifp);
mutex_destroy(&sc->sc_lock);
kmem_free(sc, sizeof(npflog_softc_t));
return 0;
}
void
npf_log_packet(npf_cache_t *npc, nbuf_t *nbuf, int ifidx)
{
struct mbuf *m = nbuf;
npflog_softc_t *sc;
struct ifnet *ifp;
int family;
KASSERT(m != NULL);
/* Lookup for a pseudo-interface to log. */
LIST_FOREACH(sc, &npflog_if_list, sc_entry) {
ifp = &sc->sc_if;
if (ifp->if_index != ifidx) {
continue;
}
/* Set the address family. */
if (npf_iscached(npc, NPC_IP4)) {
family = AF_INET;
} else if (npf_iscached(npc, NPC_IP6)) {
family = AF_INET6;
} else {
family = AF_UNSPEC;
}
/* Pass through BPF. */
KERNEL_LOCK(1, NULL);
ifp->if_opackets++;
ifp->if_obytes += m->m_pkthdr.len;
bpf_mtap_af(ifp, family, m);
KERNEL_UNLOCK_ONE(NULL);
}
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_nat.c,v 1.3 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_nat.c,v 1.4 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2010 The NetBSD Foundation, Inc. * Copyright (c) 2010 The NetBSD Foundation, Inc.
@ -64,26 +64,28 @@
* the IP addresses, therefore multiple NAT policies with the same IP * the IP addresses, therefore multiple NAT policies with the same IP
* will share the same port map. * will share the same port map.
* *
* NAT sessions and translation entries * Sessions, translation entries and their life-cycle
* *
* NAT module relies on session management module. Each "NAT" session * NAT module relies on session management module. Each translated
* has an associated translation entry (npf_nat_t). It contains saved * session has an associated translation entry (npf_nat_t), which
* i.e. original IP address with port and translation port, allocated * contains information used for backwards stream translation, i.e.
* from the port map. Each NAT translation entry is associated with * original IP address with port and translation port, allocated from
* the policy, which contains translation IP address. Allocated port * the port map. Each NAT entry is associated with the policy, which
* is returned to the port map and translation entry destroyed when * contains translation IP address. Allocated port is returned to the
* "NAT" session expires. * port map and NAT entry is destroyed when session expires.
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.3 2010/11/11 06:30:39 rmind Exp $"); __KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.4 2010/12/18 01:07:25 rmind Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/kernel.h> #include <sys/kernel.h>
#include <sys/atomic.h> #include <sys/atomic.h>
#include <sys/bitops.h> #include <sys/bitops.h>
#include <sys/condvar.h>
#include <sys/kmem.h> #include <sys/kmem.h>
#include <sys/mutex.h>
#include <sys/pool.h> #include <sys/pool.h>
#include <net/pfil.h> #include <net/pfil.h>
#include <netinet/in.h> #include <netinet/in.h>
@ -94,44 +96,53 @@ __KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.3 2010/11/11 06:30:39 rmind Exp $");
* NPF portmap structure. * NPF portmap structure.
*/ */
typedef struct { typedef struct {
u_int p_refcnt; u_int p_refcnt;
uint32_t p_bitmap[0]; uint32_t p_bitmap[0];
} npf_portmap_t; } npf_portmap_t;
/* Portmap range: [ 1024 .. 65535 ] */ /* Portmap range: [ 1024 .. 65535 ] */
#define PORTMAP_FIRST (1024) #define PORTMAP_FIRST (1024)
#define PORTMAP_SIZE ((65536 - PORTMAP_FIRST) / 32) #define PORTMAP_SIZE ((65536 - PORTMAP_FIRST) / 32)
#define PORTMAP_FILLED ((uint32_t)~0) #define PORTMAP_FILLED ((uint32_t)~0)
#define PORTMAP_MASK (31) #define PORTMAP_MASK (31)
#define PORTMAP_SHIFT (5) #define PORTMAP_SHIFT (5)
#define PORTMAP_MEM_SIZE \
(sizeof(npf_portmap_t) + (PORTMAP_SIZE * sizeof(uint32_t)))
/* NAT policy structure. */ /* NAT policy structure. */
struct npf_natpolicy { struct npf_natpolicy {
LIST_ENTRY(npf_natpolicy) n_entry; LIST_HEAD(, npf_nat) n_nat_list;
int n_type; kmutex_t n_lock;
int n_flags; kcondvar_t n_cv;
npf_portmap_t * n_portmap; npf_portmap_t * n_portmap;
size_t n_addr_sz; int n_type;
npf_addr_t n_taddr; int n_flags;
in_port_t n_tport; size_t n_addr_sz;
npf_addr_t n_taddr;
in_port_t n_tport;
}; };
#define NPF_NP_CMP_START offsetof(npf_natpolicy_t, n_type)
#define NPF_NP_CMP_SIZE (sizeof(npf_natpolicy_t) - NPF_NP_CMP_START)
/* NAT translation entry for a session. */ /* NAT translation entry for a session. */
struct npf_nat { struct npf_nat {
npf_natpolicy_t * nt_natpolicy; /* Association (list entry and a link pointer) with NAT policy. */
LIST_ENTRY(npf_nat) nt_entry;
npf_natpolicy_t * nt_natpolicy;
npf_session_t * nt_session;
/* Original address and port (for backwards translation). */ /* Original address and port (for backwards translation). */
npf_addr_t nt_oaddr; npf_addr_t nt_oaddr;
in_port_t nt_oport; in_port_t nt_oport;
/* Translation port (for redirects). */ /* Translation port (for redirects). */
in_port_t nt_tport; in_port_t nt_tport;
/* ALG (if any) associated with this NAT entry. */ /* ALG (if any) associated with this NAT entry. */
npf_alg_t * nt_alg; npf_alg_t * nt_alg;
uintptr_t nt_alg_arg; uintptr_t nt_alg_arg;
}; };
static npf_ruleset_t * nat_ruleset __read_mostly; static pool_cache_t nat_cache __read_mostly;
static LIST_HEAD(, npf_natpolicy) nat_policy_list __read_mostly;
static pool_cache_t nat_cache __read_mostly;
/* /*
* npf_nat_sys{init,fini}: initialise/destroy NAT subsystem structures. * npf_nat_sys{init,fini}: initialise/destroy NAT subsystem structures.
@ -144,17 +155,13 @@ npf_nat_sysinit(void)
nat_cache = pool_cache_init(sizeof(npf_nat_t), coherency_unit, nat_cache = pool_cache_init(sizeof(npf_nat_t), coherency_unit,
0, 0, "npfnatpl", NULL, IPL_NET, NULL, NULL, NULL); 0, 0, "npfnatpl", NULL, IPL_NET, NULL, NULL, NULL);
KASSERT(nat_cache != NULL); KASSERT(nat_cache != NULL);
nat_ruleset = npf_ruleset_create();
LIST_INIT(&nat_policy_list);
} }
void void
npf_nat_sysfini(void) npf_nat_sysfini(void)
{ {
/* Flush NAT policies. */ /* NAT policies should already be destroyed. */
npf_nat_reload(NULL);
KASSERT(LIST_EMPTY(&nat_policy_list));
pool_cache_destroy(nat_cache); pool_cache_destroy(nat_cache);
} }
@ -165,29 +172,46 @@ npf_nat_sysfini(void)
* => XXX: serialise at upper layer. * => XXX: serialise at upper layer.
*/ */
npf_natpolicy_t * npf_natpolicy_t *
npf_nat_newpolicy(int type, int flags, const npf_addr_t *taddr, npf_nat_newpolicy(prop_dictionary_t natdict)
size_t addr_sz, in_port_t tport)
{ {
npf_natpolicy_t *np, *it; npf_natpolicy_t *np/*, *it */;
const npf_addr_t *taddr;
prop_object_t obj;
npf_portmap_t *pm; npf_portmap_t *pm;
np = kmem_zalloc(sizeof(npf_natpolicy_t), KM_SLEEP); np = kmem_zalloc(sizeof(npf_natpolicy_t), KM_SLEEP);
if (np == NULL) { mutex_init(&np->n_lock, MUTEX_DEFAULT, IPL_SOFTNET);
return NULL; cv_init(&np->n_cv, "npfnatcv");
} LIST_INIT(&np->n_nat_list);
KASSERT(type == NPF_NATIN || type == NPF_NATOUT);
np->n_type = type; /* Translation type. */
np->n_flags = flags; obj = prop_dictionary_get(natdict, "type");
np->n_addr_sz = addr_sz; np->n_type = prop_number_integer_value(obj);
memcpy(&np->n_taddr, taddr, sizeof(npf_addr_t));
np->n_tport = tport; /* Translation type. */
obj = prop_dictionary_get(natdict, "flags");
np->n_flags = prop_number_integer_value(obj);
/* Translation IP. */
obj = prop_dictionary_get(natdict, "translation-ip");
np->n_addr_sz = prop_data_size(obj);
KASSERT(np->n_addr_sz > 0 && np->n_addr_sz <= sizeof(npf_addr_t));
taddr = (const npf_addr_t *)prop_data_data_nocopy(obj);
memcpy(&np->n_taddr, taddr, np->n_addr_sz);
/* Translation port (for redirect case). */
obj = prop_dictionary_get(natdict, "translation-port");
np->n_tport = (in_port_t)prop_number_integer_value(obj);
KASSERT(np->n_type == NPF_NATIN || np->n_type == NPF_NATOUT);
pm = NULL; pm = NULL;
if ((flags & NPF_NAT_PORTMAP) == 0) { if ((np->n_flags & NPF_NAT_PORTMAP) == 0) {
goto nopm; goto nopm;
} }
/* Search for a NAT policy using the same translation address. */ /* Search for a NAT policy using the same translation address. */
#if 0
LIST_FOREACH(it, &nat_policy_list, n_entry) { LIST_FOREACH(it, &nat_policy_list, n_entry) {
if (memcmp(&it->n_taddr, &np->n_taddr, sizeof(npf_addr_t))) { if (memcmp(&it->n_taddr, &np->n_taddr, sizeof(npf_addr_t))) {
continue; continue;
@ -195,10 +219,12 @@ npf_nat_newpolicy(int type, int flags, const npf_addr_t *taddr,
pm = it->n_portmap; pm = it->n_portmap;
break; break;
} }
#else
pm = NULL;
#endif
if (pm == NULL) { if (pm == NULL) {
/* Allocate a new port map for the NAT policy. */ /* Allocate a new port map for the NAT policy. */
pm = kmem_zalloc(sizeof(npf_portmap_t) + pm = kmem_zalloc(PORTMAP_MEM_SIZE, KM_SLEEP);
(PORTMAP_SIZE * sizeof(uint32_t)), KM_SLEEP);
if (pm == NULL) { if (pm == NULL) {
kmem_free(np, sizeof(npf_natpolicy_t)); kmem_free(np, sizeof(npf_natpolicy_t));
return NULL; return NULL;
@ -211,46 +237,53 @@ npf_nat_newpolicy(int type, int flags, const npf_addr_t *taddr,
} }
nopm: nopm:
np->n_portmap = pm; np->n_portmap = pm;
/*
* Note: old policies with new might co-exist in the list,
* while reload is in progress, but that is not an issue.
*/
LIST_INSERT_HEAD(&nat_policy_list, np, n_entry);
return np; return np;
} }
/* /*
* npf_nat_freepolicy: free NAT policy and, on last reference, free portmap. * npf_nat_freepolicy: free NAT policy and, on last reference, free portmap.
* *
* => Called from npf_rule_free() during the reload via npf_nat_reload(). * => Called from npf_rule_free() during the reload via npf_ruleset_destroy().
*/ */
void void
npf_nat_freepolicy(npf_natpolicy_t *np) npf_nat_freepolicy(npf_natpolicy_t *np)
{ {
npf_portmap_t *pm = np->n_portmap; npf_portmap_t *pm = np->n_portmap;
npf_nat_t *nt;
LIST_REMOVE(np, n_entry); /* De-associate all entries from the policy. */
mutex_enter(&np->n_lock);
LIST_FOREACH(nt, &np->n_nat_list, nt_entry) {
if (nt->nt_session == NULL) { /* XXXSMP */
npf_session_expire(nt->nt_session);
}
}
while (!LIST_EMPTY(&np->n_nat_list)) {
cv_wait(&np->n_cv, &np->n_lock);
}
mutex_exit(&np->n_lock);
/* Destroy the port map, on last reference. */
if (pm && --pm->p_refcnt == 0) { if (pm && --pm->p_refcnt == 0) {
KASSERT((np->n_flags & NPF_NAT_PORTMAP) != 0); KASSERT((np->n_flags & NPF_NAT_PORTMAP) != 0);
kmem_free(pm, sizeof(npf_portmap_t) + kmem_free(pm, PORTMAP_MEM_SIZE);
(PORTMAP_SIZE * sizeof(uint32_t)));
} }
cv_destroy(&np->n_cv);
mutex_destroy(&np->n_lock);
kmem_free(np, sizeof(npf_natpolicy_t)); kmem_free(np, sizeof(npf_natpolicy_t));
} }
/* bool
* npf_nat_reload: activate new ruleset of NAT policies and destroy old. npf_nat_matchpolicy(npf_natpolicy_t *np, npf_natpolicy_t *mnp)
*
* => Destruction of ruleset will perform npf_nat_freepolicy() for each policy.
*/
void
npf_nat_reload(npf_ruleset_t *nset)
{ {
npf_ruleset_t *oldnset; void *np_raw, *mnp_raw;
/*
oldnset = atomic_swap_ptr(&nat_ruleset, nset); * Compare the relevant NAT policy information (in raw form),
KASSERT(oldnset != NULL); * which is enough for matching criterion.
npf_ruleset_destroy(oldnset); */
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);
} }
/* /*
@ -289,6 +322,28 @@ npf_nat_getport(npf_natpolicy_t *np)
return htons(PORTMAP_FIRST + (idx << PORTMAP_SHIFT) + bit); 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;
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. * npf_nat_putport: return port as available in the NAT policy portmap.
* *
@ -317,10 +372,11 @@ npf_nat_putport(npf_natpolicy_t *np, in_port_t port)
static npf_natpolicy_t * static npf_natpolicy_t *
npf_nat_inspect(npf_cache_t *npc, nbuf_t *nbuf, struct ifnet *ifp, const int di) npf_nat_inspect(npf_cache_t *npc, nbuf_t *nbuf, struct ifnet *ifp, const int di)
{ {
npf_ruleset_t *rlset;
npf_rule_t *rl; npf_rule_t *rl;
rl = npf_ruleset_match(nat_ruleset, npc, nbuf, ifp, di, NPF_LAYER_3); rlset = npf_core_natset();
rl = npf_ruleset_match(rlset, npc, nbuf, ifp, di, NPF_LAYER_3);
return rl ? npf_rule_getnat(rl) : NULL; return rl ? npf_rule_getnat(rl) : NULL;
} }
@ -340,7 +396,12 @@ npf_nat_create(npf_cache_t *npc, npf_natpolicy_t *np)
if (nt == NULL){ if (nt == NULL){
return NULL; return NULL;
} }
npf_stats_inc(NPF_STAT_NAT_CREATE);
mutex_enter(&np->n_lock);
LIST_INSERT_HEAD(&np->n_nat_list, nt, nt_entry);
nt->nt_natpolicy = np; nt->nt_natpolicy = np;
nt->nt_session = NULL;
mutex_exit(&np->n_lock);
nt->nt_alg = NULL; nt->nt_alg = NULL;
/* Save the original address which may be rewritten. */ /* Save the original address which may be rewritten. */
@ -457,11 +518,11 @@ npf_nat_translate(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt,
/* /*
* npf_do_nat: * npf_do_nat:
* - Inspect packet for a NAT policy, unless a session with a NAT * - Inspect packet for a NAT policy, unless a session with a NAT
* association already exists. In such case, determine whether is * association already exists. In such case, determine whether it
* is a "forwards" or "backwards" stream. * is a "forwards" or "backwards" stream.
* - Perform translation: rewrite source address if "forwards" stream * - Perform translation: rewrite source or destination fields,
* and destination address if "backwards". * depending on translation type and direction.
* - Establish sessions or, if already exists, associate a NAT policy. * - Associate a NAT policy with a session (may establish a new).
*/ */
int int
npf_do_nat(npf_cache_t *npc, npf_session_t *se, nbuf_t *nbuf, npf_do_nat(npf_cache_t *npc, npf_session_t *se, nbuf_t *nbuf,
@ -481,6 +542,7 @@ npf_do_nat(npf_cache_t *npc, npf_session_t *se, nbuf_t *nbuf,
/* /*
* Return the NAT entry associated with the session, if any. * Return the NAT entry associated with the session, if any.
* Determines whether the stream is "forwards" or "backwards". * Determines whether the stream is "forwards" or "backwards".
* Note: no need to lock, since reference on session is held.
*/ */
if (se && (nt = npf_session_retnat(se, di, &forw)) != NULL) { if (se && (nt = npf_session_retnat(se, di, &forw)) != NULL) {
np = nt->nt_natpolicy; np = nt->nt_natpolicy;
@ -489,18 +551,27 @@ npf_do_nat(npf_cache_t *npc, npf_session_t *se, nbuf_t *nbuf,
} }
/* Inspect the packet for a NAT policy, if there is no session. */ /* Inspect the packet for a NAT policy, if there is no session. */
npf_core_enter();
np = npf_nat_inspect(npc, nbuf, ifp, di); np = npf_nat_inspect(npc, nbuf, ifp, di);
if (np == NULL) { if (np == NULL) {
/* If packet does not match - done. */ /* If packet does not match - done. */
npf_core_exit();
return 0; return 0;
} }
forw = true; forw = true;
/* Create a new NAT translation entry. */ /*
* Create a new NAT entry. Note: it is safe to unlock, since the
* NAT policy wont be desotroyed while there are list entries, which
* are removed only on session expiration. Currently, NAT entry is
* not yet associated with any session.
*/
nt = npf_nat_create(npc, np); nt = npf_nat_create(npc, np);
if (nt == NULL) { if (nt == NULL) {
npf_core_exit();
return ENOMEM; return ENOMEM;
} }
npf_core_exit();
new = true; new = true;
/* Determine whether any ALG matches. */ /* Determine whether any ALG matches. */
@ -515,7 +586,7 @@ npf_do_nat(npf_cache_t *npc, npf_session_t *se, nbuf_t *nbuf,
* stream depends on other, stateless filtering rules. * stream depends on other, stateless filtering rules.
*/ */
if (se == NULL) { if (se == NULL) {
nse = npf_session_establish(npc, nbuf, NULL, di); nse = npf_session_establish(npc, nbuf, di);
if (nse == NULL) { if (nse == NULL) {
error = ENOMEM; error = ENOMEM;
goto out; goto out;
@ -530,27 +601,17 @@ translate:
} }
if (__predict_false(new)) { if (__predict_false(new)) {
npf_session_t *natse;
/* /*
* Establish a new NAT session using translated address and * Associate NAT translation entry with the session.
* associate NAT translation data with this session.
*
* Note: packet now has a translated address in the cache. * Note: packet now has a translated address in the cache.
*/ */
natse = npf_session_establish(npc, nbuf, nt, di); nt->nt_session = se;
if (natse == NULL) { error = npf_session_setnat(se, nt, di);
error = ENOMEM;
goto out;
}
/*
* Link local session with NAT session, if no link already.
*/
npf_session_link(se, natse);
npf_session_release(natse);
out: out:
if (error) { if (error) {
if (nse != NULL) { /* If session was for NAT only - expire it. */
/* XXX: Expire it?? */ if (nse) {
npf_session_expire(nse);
} }
/* Will free the structure and return the port. */ /* Will free the structure and return the port. */
npf_nat_expire(nt); npf_nat_expire(nt);
@ -562,6 +623,18 @@ out:
return error; return error;
} }
/*
* npf_nat_gettrans: return translation IP address and port.
*/
void
npf_nat_gettrans(npf_nat_t *nt, npf_addr_t **addr, in_port_t *port)
{
npf_natpolicy_t *np = nt->nt_natpolicy;
*addr = &np->n_taddr;
*port = nt->nt_tport;
}
/* /*
* npf_nat_getorig: return original IP address and port from translation entry. * npf_nat_getorig: return original IP address and port from translation entry.
*/ */
@ -592,11 +665,116 @@ npf_nat_expire(npf_nat_t *nt)
{ {
npf_natpolicy_t *np = nt->nt_natpolicy; npf_natpolicy_t *np = nt->nt_natpolicy;
if ((np->n_flags & NPF_NAT_PORTMAP) != 0) { /* Return any taken port to the portmap. */
KASSERT(nt->nt_tport != 0); if ((np->n_flags & NPF_NAT_PORTMAP) != 0 && nt->nt_tport) {
npf_nat_putport(np, nt->nt_tport); npf_nat_putport(np, nt->nt_tport);
} }
/* Remove NAT entry from the list, notify any waiters if last entry. */
mutex_enter(&np->n_lock);
LIST_REMOVE(nt, nt_entry);
if (LIST_EMPTY(&np->n_nat_list)) {
cv_broadcast(&np->n_cv);
}
mutex_exit(&np->n_lock);
/* Free structure, increase the counter. */
pool_cache_put(nat_cache, nt); pool_cache_put(nat_cache, nt);
npf_stats_inc(NPF_STAT_NAT_DESTROY);
}
/*
* npf_nat_save: construct NAT entry and reference to the NAT policy.
*/
int
npf_nat_save(prop_dictionary_t sedict, prop_array_t natlist, 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;
uintptr_t itnp;
/* Set NAT entry data. */
nd = prop_data_create_data(nt, sizeof(npf_nat_t));
prop_dictionary_set(sedict, "nat-data", nd);
/* Find or create a NAT policy. */
it = prop_array_iterator(natlist);
while ((npdict = prop_object_iterator_next(it)) != NULL) {
itnp = (uintptr_t)prop_number_unsigned_integer_value(
prop_dictionary_get(npdict, "id-ptr"));
if (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));
/* Set the data, insert into the array. */
prop_dictionary_set(npdict, "id-ptr",
prop_number_create_unsigned_integer((uintptr_t)np));
prop_dictionary_set(npdict, "nat-policy-data", npd);
prop_array_add(natlist, npdict);
}
prop_dictionary_set(sedict, "nat-policy",
prop_dictionary_copy(npdict));
return 0;
}
/*
* npf_nat_restore: find a matching NAT policy and restore NAT entry.
*
* => Caller should lock the active NAT ruleset.
*/
npf_nat_t *
npf_nat_restore(prop_dictionary_t sedict, npf_session_t *se)
{
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;
/* Get raw NAT entry. */
obj = prop_dictionary_get(sedict, "nat-data");
ntraw = prop_data_data_nocopy(obj);
if (ntraw == NULL || prop_data_size(obj) != sizeof(npf_nat_t)) {
return NULL;
}
/* Find a stored NAT policy information. */
obj = prop_dictionary_get(
prop_dictionary_get(sedict, "nat-policy"), "nat-policy-data");
onp = prop_data_data_nocopy(obj);
if (onp == NULL || prop_data_size(obj) != sizeof(npf_natpolicy_t)) {
return NULL;
}
/* Match if there is an existing NAT policy. */
rl = npf_ruleset_matchnat(npf_core_natset(), __UNCONST(onp));
if (rl == NULL) {
return NULL;
}
np = npf_rule_getnat(rl);
KASSERT(np != NULL);
/* Take a specific port from port-map. */
if (!npf_nat_takeport(np, ntraw->nt_tport)) {
return NULL;
}
/* 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_session = se;
nt->nt_alg = NULL;
return nt;
} }
#if defined(DDB) || defined(_NPF_TESTING) #if defined(DDB) || defined(_NPF_TESTING)
@ -607,26 +785,16 @@ npf_nat_dump(npf_nat_t *nt)
npf_natpolicy_t *np; npf_natpolicy_t *np;
struct in_addr ip; struct in_addr ip;
if (nt) { np = nt->nt_natpolicy;
np = nt->nt_natpolicy; memcpy(&ip, &np->n_taddr, sizeof(ip));
goto skip; printf("\tNATP(%p): type %d flags 0x%x taddr %s tport %d\n",
} np, np->n_type, np->n_flags, inet_ntoa(ip), np->n_tport);
LIST_FOREACH(np, &nat_policy_list, n_entry) { memcpy(&ip, &nt->nt_oaddr, sizeof(ip));
skip: printf("\tNAT: original address %s oport %d tport %d\n",
memcpy(&ip, &np->n_taddr, sizeof(ip)); inet_ntoa(ip), ntohs(nt->nt_oport), ntohs(nt->nt_tport));
printf("\tNAT policy: type %d, flags 0x%x, taddr %s, tport = %d\n", if (nt->nt_alg) {
np->n_type, np->n_flags, inet_ntoa(ip), np->n_tport); printf("\tNAT ALG = %p, ARG = %p\n",
if (nt == NULL) { nt->nt_alg, (void *)nt->nt_alg_arg);
continue;
}
memcpy(&ip, &nt->nt_oaddr, sizeof(ip));
printf("\tNAT: original address %s, oport %d, tport = %d\n",
inet_ntoa(ip), ntohs(nt->nt_oport), ntohs(nt->nt_tport));
if (nt->nt_alg) {
printf("\tNAT ALG = %p, ARG = %p\n",
nt->nt_alg, (void *)nt->nt_alg_arg);
}
return;
} }
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_ncode.h,v 1.3 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_ncode.h,v 1.4 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -82,8 +82,8 @@ int npf_ncode_validate(const void *, size_t, int *);
#define NPF_OPCODE_TAG 0x04 #define NPF_OPCODE_TAG 0x04
/* Set and load instructions. */ /* Set and load instructions. */
#define NPF_OPCODE_MOV 0x10 #define NPF_OPCODE_MOVE 0x10
#define NPF_OPCODE_LOAD 0x11 #define NPF_OPCODE_LW 0x11
/* Compare and jump instructions. */ /* Compare and jump instructions. */
#define NPF_OPCODE_CMP 0x21 #define NPF_OPCODE_CMP 0x21
@ -93,8 +93,19 @@ int npf_ncode_validate(const void *, size_t, int *);
#define NPF_OPCODE_BGT 0x25 #define NPF_OPCODE_BGT 0x25
#define NPF_OPCODE_BLT 0x26 #define NPF_OPCODE_BLT 0x26
/* Arithmetic instructions. */
#define NPF_OPCODE_ADD 0x30
#define NPF_OPCODE_SUB 0x31
#define NPF_OPCODE_MULT 0x32
#define NPF_OPCODE_DIV 0x33
/* Bitwise instructions. */ /* Bitwise instructions. */
#define NPF_OPCODE_AND 0x30 #define NPF_OPCODE_NOT 0x40
#define NPF_OPCODE_AND 0x41
#define NPF_OPCODE_OR 0x42
#define NPF_OPCODE_XOR 0x43
#define NPF_OPCODE_SLL 0x44
#define NPF_OPCODE_SRL 0x45
/* /*
* CISC-like n-code instructions. * CISC-like n-code instructions.

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_processor.c,v 1.3 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_processor.c,v 1.4 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -54,7 +54,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_processor.c,v 1.3 2010/11/11 06:30:39 rmind Exp $"); __KERNEL_RCSID(0, "$NetBSD: npf_processor.c,v 1.4 2010/12/18 01:07:25 rmind Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/kernel.h> #include <sys/kernel.h>
@ -173,9 +173,9 @@ process_next:
/* /*
* RISC-like instructions. * RISC-like instructions.
* *
* - ADVR, LOAD, CMP, CMPR * - ADVR, LW, CMP, CMPR
* - BEQ, BNE, BGT, BLT * - BEQ, BNE, BGT, BLT
* - RET, TAG, MOV * - RET, TAG, MOVE
* - AND, J, INVL * - AND, J, INVL
*/ */
switch (d) { switch (d) {
@ -187,7 +187,7 @@ process_next:
goto fail; goto fail;
} }
break; break;
case NPF_OPCODE_LOAD: case NPF_OPCODE_LW:
i_ptr = nc_fetch_double(i_ptr, &n, &i); /* Size, register */ i_ptr = nc_fetch_double(i_ptr, &n, &i); /* Size, register */
KASSERT(i < NPF_NREGS); KASSERT(i < NPF_NREGS);
KASSERT(n >= sizeof(uint8_t) && n <= sizeof(uint32_t)); KASSERT(n >= sizeof(uint8_t) && n <= sizeof(uint32_t));
@ -242,7 +242,7 @@ process_next:
goto fail; goto fail;
} }
break; break;
case NPF_OPCODE_MOV: case NPF_OPCODE_MOVE:
i_ptr = nc_fetch_double(i_ptr, &n, &i); /* Value, register */ i_ptr = nc_fetch_double(i_ptr, &n, &i); /* Value, register */
KASSERT(i < NPF_NREGS); KASSERT(i < NPF_NREGS);
regs[i] = n; regs[i] = n;
@ -379,7 +379,7 @@ nc_insn_check(const uintptr_t optr, const void *nc, size_t sz,
case NPF_OPCODE_ADVR: case NPF_OPCODE_ADVR:
error = nc_ptr_check(&iptr, nc, sz, 1, &regidx, 1); error = nc_ptr_check(&iptr, nc, sz, 1, &regidx, 1);
break; break;
case NPF_OPCODE_LOAD: case NPF_OPCODE_LW:
error = nc_ptr_check(&iptr, nc, sz, 1, &val, 1); error = nc_ptr_check(&iptr, nc, sz, 1, &val, 1);
if (error || val < sizeof(uint8_t) || val > sizeof(uint32_t)) { if (error || val < sizeof(uint8_t) || val > sizeof(uint32_t)) {
return error ? error : NPF_ERR_INVAL; return error ? error : NPF_ERR_INVAL;
@ -404,7 +404,7 @@ nc_insn_check(const uintptr_t optr, const void *nc, size_t sz,
case NPF_OPCODE_TAG: case NPF_OPCODE_TAG:
error = nc_ptr_check(&iptr, nc, sz, 2, NULL, 0); error = nc_ptr_check(&iptr, nc, sz, 2, NULL, 0);
break; break;
case NPF_OPCODE_MOV: case NPF_OPCODE_MOVE:
error = nc_ptr_check(&iptr, nc, sz, 2, &regidx, 2); error = nc_ptr_check(&iptr, nc, sz, 2, &regidx, 2);
break; break;
case NPF_OPCODE_CMPR: case NPF_OPCODE_CMPR:

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_ruleset.c,v 1.3 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_ruleset.c,v 1.4 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -31,15 +31,11 @@
/* /*
* NPF ruleset module. * NPF ruleset module.
*
* Lock order:
*
* ruleset_lock -> table_lock -> npf_table_t::t_lock
*/ */
#ifdef _KERNEL #ifdef _KERNEL
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.3 2010/11/11 06:30:39 rmind Exp $"); __KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.4 2010/12/18 01:07:25 rmind Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/kernel.h> #include <sys/kernel.h>
@ -48,7 +44,6 @@ __KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.3 2010/11/11 06:30:39 rmind Exp $"
#include <sys/kmem.h> #include <sys/kmem.h>
#include <sys/pool.h> #include <sys/pool.h>
#include <sys/queue.h> #include <sys/queue.h>
#include <sys/rwlock.h>
#include <sys/types.h> #include <sys/types.h>
#include <net/pfil.h> #include <net/pfil.h>
@ -58,75 +53,56 @@ __KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.3 2010/11/11 06:30:39 rmind Exp $"
#include "npf_ncode.h" #include "npf_ncode.h"
#include "npf_impl.h" #include "npf_impl.h"
/* Ruleset structre (queue and default rule). */
struct npf_ruleset {
TAILQ_HEAD(, npf_rule) rs_queue;
npf_rule_t * rs_default;
};
/* Rule hook entry. */
struct npf_hook { struct npf_hook {
void (*hk_fn)(npf_cache_t *, nbuf_t *, void *); void (*hk_fn)(npf_cache_t *, nbuf_t *, void *);
void * hk_arg; void * hk_arg;
LIST_ENTRY(npf_hook) hk_entry; LIST_ENTRY(npf_hook) hk_entry;
}; };
struct npf_ruleset { /* Rule processing structure. */
TAILQ_HEAD(, npf_rule) rs_queue; struct npf_rproc {
npf_rule_t * rs_default; /* Reference count. */
int _reserved; u_int rp_refcnt;
/* Normalization options. */
bool rp_rnd_ipid;
bool rp_no_df;
u_int rp_minttl;
u_int rp_maxmss;
/* Logging interface. */
u_int rp_log_ifid;
}; };
/* Rule structure. */ /* Rule structure. */
struct npf_rule { struct npf_rule {
/* List entry in the ruleset. */ TAILQ_ENTRY(npf_rule) r_entry;
TAILQ_ENTRY(npf_rule) r_entry;
/* Optional: sub-ruleset, NAT policy. */ /* Optional: sub-ruleset, NAT policy. */
npf_ruleset_t r_subset; npf_ruleset_t r_subset;
npf_natpolicy_t * r_nat; npf_natpolicy_t * r_natp;
/* Rule priority: (highest) 0, 1, 2 ... n (lowest). */ /* Rule priority: (highest) 0, 1, 2 ... n (lowest). */
u_int r_priority; u_int r_priority;
/* N-code to process. */ /* N-code to process. */
void * r_ncode; void * r_ncode;
size_t r_nc_size; size_t r_nc_size;
/* Attributes of this rule. */ /* Attributes of this rule. */
uint32_t r_attr; uint32_t r_attr;
/* Interface. */ /* Interface. */
u_int r_ifid; u_int r_ifid;
/* Hit counter. */ /* Hit counter. */
u_long r_hitcount; u_long r_hitcount;
/* Normalization options (XXX - abstract). */ /* Rule processing data. */
bool rl_rnd_ipid; npf_rproc_t * r_rproc;
u_int rl_minttl;
u_int rl_maxmss;
/* List of hooks to process on match. */ /* List of hooks to process on match. */
LIST_HEAD(, npf_hook) r_hooks; kmutex_t r_hooks_lock;
LIST_HEAD(, npf_hook) r_hooks;
}; };
/* Global ruleset, its lock, cache and NAT ruleset. */
static npf_ruleset_t * ruleset;
static krwlock_t ruleset_lock;
static pool_cache_t rule_cache;
/*
* npf_ruleset_sysinit: initialise ruleset structures.
*/
int
npf_ruleset_sysinit(void)
{
rule_cache = pool_cache_init(sizeof(npf_rule_t), coherency_unit,
0, 0, "npfrlpl", NULL, IPL_NONE, NULL, NULL, NULL);
if (rule_cache == NULL) {
return ENOMEM;
}
rw_init(&ruleset_lock);
ruleset = npf_ruleset_create();
return 0;
}
void
npf_ruleset_sysfini(void)
{
npf_ruleset_destroy(ruleset);
rw_destroy(&ruleset_lock);
pool_cache_destroy(rule_cache);
}
npf_ruleset_t * npf_ruleset_t *
npf_ruleset_create(void) npf_ruleset_create(void)
{ {
@ -176,120 +152,194 @@ npf_ruleset_insert(npf_ruleset_t *rlset, npf_rule_t *rl)
} }
/* /*
* npf_ruleset_reload: atomically load new ruleset and tableset, * npf_ruleset_matchnat: find a matching NAT policy in the ruleset.
* and destroy old structures. */
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. */
TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
if (npf_nat_matchpolicy(rl->r_natp, mnp))
break;
}
return rl;
}
/*
* npf_ruleset_natreload: minimum reload of NAT policies by maching
* two (active and new) NAT rulesets.
*
* => Active ruleset should be exclusively locked.
*/ */
void void
npf_ruleset_reload(npf_ruleset_t *nrlset, npf_tableset_t *ntblset) npf_ruleset_natreload(npf_ruleset_t *nrlset, npf_ruleset_t *arlset)
{ {
npf_ruleset_t *oldrlset; npf_natpolicy_t *np, *anp;
npf_tableset_t *oldtblset; npf_rule_t *rl, *arl;
/* KASSERT(npf_core_locked());
* Swap old ruleset with the new.
* XXX: Rework to be fully lock-less; later.
*/
rw_enter(&ruleset_lock, RW_WRITER);
oldrlset = atomic_swap_ptr(&ruleset, nrlset);
KASSERT(oldrlset != NULL);
/* /* Scan a new NAT ruleset against NAT policies in old ruleset. */
* Setup a new tableset. It will lock the global tableset lock, TAILQ_FOREACH(rl, &nrlset->rs_queue, r_entry) {
* therefore ensures atomicity. We shall free the old table-set. np = rl->r_natp;
*/ arl = npf_ruleset_matchnat(arlset, np);
oldtblset = npf_tableset_reload(ntblset); if (arl == NULL) {
KASSERT(oldtblset != NULL); continue;
/* Unlock. Everything goes "live" now. */ }
rw_exit(&ruleset_lock); /* On match - we exchange NAT policies. */
anp = arl->r_natp;
rl->r_natp = anp;
arl->r_natp = np;
}
}
npf_rproc_t *
npf_rproc_create(prop_dictionary_t rpdict)
{
npf_rproc_t *rp;
prop_object_t obj;
rp = kmem_alloc(sizeof(npf_rproc_t), KM_SLEEP);
rp->rp_refcnt = 1;
/* Logging interface ID (integer). */
obj = prop_dictionary_get(rpdict, "log-interface");
rp->rp_log_ifid = prop_number_integer_value(obj);
/* Randomize IP ID (bool). */
obj = prop_dictionary_get(rpdict, "randomize-id");
rp->rp_rnd_ipid = prop_bool_true(obj);
/* IP_DF flag cleansing (bool). */
obj = prop_dictionary_get(rpdict, "no-df");
rp->rp_no_df = prop_bool_true(obj);
/* Minimum IP TTL (integer). */
obj = prop_dictionary_get(rpdict, "min-ttl");
rp->rp_minttl = prop_number_integer_value(obj);
/* Maximum TCP MSS (integer). */
obj = prop_dictionary_get(rpdict, "max-mss");
rp->rp_maxmss = prop_number_integer_value(obj);
return rp;
}
npf_rproc_t *
npf_rproc_return(npf_rule_t *rl)
{
npf_rproc_t *rp = rl->r_rproc;
if (rp) {
atomic_inc_uint(&rp->rp_refcnt);
}
return rp;
}
void
npf_rproc_release(npf_rproc_t *rp)
{
/* Destroy on last reference. */
if (atomic_dec_uint_nv(&rp->rp_refcnt) != 0) {
return;
}
kmem_free(rp, sizeof(npf_rproc_t));
}
void
npf_rproc_run(npf_cache_t *npc, nbuf_t *nbuf, npf_rproc_t *rp)
{
KASSERT(rp->rp_refcnt > 0);
/* Normalize the packet, if required. */
(void)npf_normalize(npc, nbuf,
rp->rp_rnd_ipid, rp->rp_no_df, rp->rp_minttl, rp->rp_maxmss);
/* Log packet, if required. */
if (rp->rp_log_ifid) {
npf_log_packet(npc, nbuf, rp->rp_log_ifid);
}
npf_tableset_destroy(oldtblset);
npf_ruleset_destroy(oldrlset);
} }
/* /*
* npf_rule_alloc: allocate a rule and copy ncode from user-space. * npf_rule_alloc: allocate a rule and copy ncode from user-space.
*
* => N-code should be validated by the caller.
*/ */
npf_rule_t * npf_rule_t *
npf_rule_alloc(int attr, pri_t pri, int ifidx, void *nc, size_t sz, npf_rule_alloc(prop_dictionary_t rldict, void *nc, size_t nc_size)
bool rnd_ipid, int minttl, int maxmss)
{ {
npf_rule_t *rl; npf_rule_t *rl;
prop_object_t obj;
int errat; int errat;
/* Perform validation & building of n-code. */
if (nc && npf_ncode_validate(nc, sz, &errat)) {
return NULL;
}
/* Allocate a rule structure. */ /* Allocate a rule structure. */
rl = pool_cache_get(rule_cache, PR_WAITOK); rl = kmem_alloc(sizeof(npf_rule_t), KM_SLEEP);
if (rl == NULL) {
return NULL;
}
TAILQ_INIT(&rl->r_subset.rs_queue); TAILQ_INIT(&rl->r_subset.rs_queue);
mutex_init(&rl->r_hooks_lock, MUTEX_DEFAULT, IPL_SOFTNET);
LIST_INIT(&rl->r_hooks); LIST_INIT(&rl->r_hooks);
rl->r_priority = pri;
rl->r_attr = attr;
rl->r_ifid = ifidx;
rl->r_ncode = nc;
rl->r_nc_size = sz;
rl->r_hitcount = 0; rl->r_hitcount = 0;
rl->r_nat = NULL; rl->r_natp = NULL;
rl->rl_rnd_ipid = rnd_ipid; /* N-code. */
rl->rl_minttl = minttl; KASSERT(nc == NULL || npf_ncode_validate(nc, nc_size, &errat) == 0);
rl->rl_maxmss = maxmss; rl->r_ncode = nc;
rl->r_nc_size = nc_size;
/* Attributes (integer). */
obj = prop_dictionary_get(rldict, "attributes");
rl->r_attr = prop_number_integer_value(obj);
/* Priority (integer). */
obj = prop_dictionary_get(rldict, "priority");
rl->r_priority = prop_number_integer_value(obj);
/* Interface ID (integer). */
obj = prop_dictionary_get(rldict, "interface");
rl->r_ifid = prop_number_integer_value(obj);
/* Create rule processing structure, if any. */
if (rl->r_attr & (NPF_RULE_LOG | NPF_RULE_NORMALIZE)) {
rl->r_rproc = npf_rproc_create(rldict);
} else {
rl->r_rproc = NULL;
}
return rl; return rl;
} }
#if 0
/*
* npf_activate_rule: activate rule by inserting it into the global ruleset.
*/
void
npf_activate_rule(npf_rule_t *rl)
{
rw_enter(&ruleset_lock, RW_WRITER);
npf_ruleset_insert(ruleset, rl);
rw_exit(&ruleset_lock);
}
/*
* npf_deactivate_rule: deactivate rule by removing it from the ruleset.
*/
void
npf_deactivate_rule(npf_rule_t *)
{
rw_enter(&ruleset_lock, RW_WRITER);
TAILQ_REMOVE(&ruleset->rs_queue, rl, r_entry);
rw_exit(&ruleset_lock);
}
#endif
/* /*
* npf_rule_free: free the specified rule. * npf_rule_free: free the specified rule.
*/ */
void void
npf_rule_free(npf_rule_t *rl) npf_rule_free(npf_rule_t *rl)
{ {
npf_natpolicy_t *np = rl->r_natp;
npf_rproc_t *rp = rl->r_rproc;
if (np) {
/* Free NAT policy. */
npf_nat_freepolicy(np);
}
if (rp) {
/* Release/free rule processing structure. */
npf_rproc_release(rp);
}
if (rl->r_ncode) { if (rl->r_ncode) {
/* Free n-code (if any). */ /* Free n-code. */
npf_ncode_free(rl->r_ncode, rl->r_nc_size); npf_ncode_free(rl->r_ncode, rl->r_nc_size);
} }
if (rl->r_nat) { mutex_destroy(&rl->r_hooks_lock);
/* Free NAT policy (if associated). */ kmem_free(rl, sizeof(npf_rule_t));
npf_nat_freepolicy(rl->r_nat);
}
pool_cache_put(rule_cache, rl);
} }
/* /*
* npf_rule_subset: return sub-ruleset, if any. * npf_rule_subset: return sub-ruleset, if any.
* npf_rule_getnat: get NAT policy assigned to the rule. * npf_rule_getnat: get NAT policy assigned to the rule.
* npf_rule_setnat: assign NAT policy to the rule.
*/ */
npf_ruleset_t * npf_ruleset_t *
@ -301,15 +351,19 @@ npf_rule_subset(npf_rule_t *rl)
npf_natpolicy_t * npf_natpolicy_t *
npf_rule_getnat(const npf_rule_t *rl) npf_rule_getnat(const npf_rule_t *rl)
{ {
return rl->r_nat; return rl->r_natp;
} }
/*
* npf_rule_setnat: assign NAT policy to the rule and insert into the
* NAT policy list in the ruleset.
*/
void void
npf_rule_setnat(npf_rule_t *rl, npf_natpolicy_t *np) npf_rule_setnat(npf_rule_t *rl, npf_natpolicy_t *np)
{ {
KASSERT(rl->r_nat == NULL); KASSERT(rl->r_natp == NULL);
rl->r_nat = np; rl->r_natp = np;
} }
/* /*
@ -325,9 +379,9 @@ npf_hook_register(npf_rule_t *rl,
if (hk != NULL) { if (hk != NULL) {
hk->hk_fn = fn; hk->hk_fn = fn;
hk->hk_arg = arg; hk->hk_arg = arg;
rw_enter(&ruleset_lock, RW_WRITER); mutex_enter(&rl->r_hooks_lock);
LIST_INSERT_HEAD(&rl->r_hooks, hk, hk_entry); LIST_INSERT_HEAD(&rl->r_hooks, hk, hk_entry);
rw_exit(&ruleset_lock); mutex_exit(&rl->r_hooks_lock);
} }
return hk; return hk;
} }
@ -341,9 +395,9 @@ void
npf_hook_unregister(npf_rule_t *rl, npf_hook_t *hk) npf_hook_unregister(npf_rule_t *rl, npf_hook_t *hk)
{ {
rw_enter(&ruleset_lock, RW_WRITER); mutex_enter(&rl->r_hooks_lock);
LIST_REMOVE(hk, hk_entry); LIST_REMOVE(hk, hk_entry);
rw_exit(&ruleset_lock); mutex_exit(&rl->r_hooks_lock);
kmem_free(hk, sizeof(npf_hook_t)); kmem_free(hk, sizeof(npf_hook_t));
} }
@ -401,18 +455,20 @@ npf_rule_t *
npf_ruleset_inspect(npf_cache_t *npc, nbuf_t *nbuf, npf_ruleset_inspect(npf_cache_t *npc, nbuf_t *nbuf,
struct ifnet *ifp, const int di, const int layer) struct ifnet *ifp, const int di, const int layer)
{ {
npf_ruleset_t *rlset = ruleset; npf_ruleset_t *rlset;
npf_rule_t *rl; npf_rule_t *rl;
bool defed; bool defed;
defed = false; defed = false;
rw_enter(&ruleset_lock, RW_READER); npf_core_enter();
rlset = npf_core_ruleset();
reinspect: reinspect:
rl = npf_ruleset_match(rlset, npc, nbuf, ifp, di, layer); rl = npf_ruleset_match(rlset, npc, nbuf, ifp, di, layer);
/* If no final rule, then - default. */ /* If no final rule, then - default. */
if (rl == NULL && !defed) { if (rl == NULL && !defed) {
rl = ruleset->rs_default; npf_ruleset_t *mainrlset = npf_core_ruleset();
rl = mainrlset->rs_default;
defed = true; defed = true;
} }
/* Inspect the sub-ruleset, if any. */ /* Inspect the sub-ruleset, if any. */
@ -421,7 +477,7 @@ reinspect:
goto reinspect; goto reinspect;
} }
if (rl == NULL) { if (rl == NULL) {
rw_exit(&ruleset_lock); npf_core_exit();
} }
return rl; return rl;
} }
@ -433,12 +489,12 @@ reinspect:
* => Releases the ruleset lock. * => Releases the ruleset lock.
*/ */
int int
npf_rule_apply(npf_cache_t *npc, nbuf_t *nbuf, npf_rule_t *rl, npf_rule_apply(npf_cache_t *npc, nbuf_t *nbuf, npf_rule_t *rl, int *retfl)
bool *keepstate, int *retfl)
{ {
npf_hook_t *hk; npf_hook_t *hk;
int error;
KASSERT(rw_lock_held(&ruleset_lock)); KASSERT(npf_core_locked());
/* Update the "hit" counter. */ /* Update the "hit" counter. */
if (rl->r_attr & NPF_RULE_COUNT) { if (rl->r_attr & NPF_RULE_COUNT) {
@ -447,27 +503,20 @@ npf_rule_apply(npf_cache_t *npc, nbuf_t *nbuf, npf_rule_t *rl,
/* If not passing - drop the packet. */ /* If not passing - drop the packet. */
if ((rl->r_attr & NPF_RULE_PASS) == 0) { if ((rl->r_attr & NPF_RULE_PASS) == 0) {
/* Determine whether any return message is needed. */ error = ENETUNREACH;
*retfl = rl->r_attr & (NPF_RULE_RETRST | NPF_RULE_RETICMP); goto done;
rw_exit(&ruleset_lock);
return ENETUNREACH;
} }
error = 0;
/* Passing. Run the hooks. */ /* Passing. Run the hooks. */
LIST_FOREACH(hk, &rl->r_hooks, hk_entry) { LIST_FOREACH(hk, &rl->r_hooks, hk_entry) {
KASSERT(hk->hk_fn != NULL); KASSERT(hk->hk_fn != NULL);
(*hk->hk_fn)(npc, nbuf, hk->hk_arg); (*hk->hk_fn)(npc, nbuf, hk->hk_arg);
} }
done:
/* Normalize the packet, if required. */ *retfl = rl->r_attr;
if (rl->r_attr & NPF_RULE_NORMALIZE) { npf_core_exit();
(void)npf_normalize(npc, nbuf, return error;
rl->rl_rnd_ipid, rl->rl_minttl, rl->rl_maxmss);
}
*keepstate = (rl->r_attr & NPF_RULE_KEEPSTATE) != 0;
rw_exit(&ruleset_lock);
return 0;
} }
#if defined(DDB) || defined(_NPF_TESTING) #if defined(DDB) || defined(_NPF_TESTING)

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_state.c,v 1.1 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_state.c,v 1.2 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2010 The NetBSD Foundation, Inc. * Copyright (c) 2010 The NetBSD Foundation, Inc.
@ -34,7 +34,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.1 2010/11/11 06:30:39 rmind Exp $"); __KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.2 2010/12/18 01:07:25 rmind Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -146,10 +146,12 @@ npf_tcp_inwindow(const npf_cache_t *npc, nbuf_t *nbuf, npf_state_t *nst,
* that is, upper boundary for valid data (I). * that is, upper boundary for valid data (I).
*/ */
if (!SEQ_GEQ(fstate->nst_ackend, end)) { if (!SEQ_GEQ(fstate->nst_ackend, end)) {
npf_stats_inc(NPF_STAT_INVALID_STATE_TCP1);
return false; return false;
} }
/* Lower boundary (II), which is no more than one window back. */ /* Lower boundary (II), which is no more than one window back. */
if (!SEQ_GEQ(seq, fstate->nst_seqend - tstate->nst_maxwin)) { if (!SEQ_GEQ(seq, fstate->nst_seqend - tstate->nst_maxwin)) {
npf_stats_inc(NPF_STAT_INVALID_STATE_TCP2);
return false; return false;
} }
/* /*
@ -158,10 +160,13 @@ npf_tcp_inwindow(const npf_cache_t *npc, nbuf_t *nbuf, npf_state_t *nst,
*/ */
ackskew = tstate->nst_seqend - ack; ackskew = tstate->nst_seqend - ack;
if (ackskew < -MAXACKWINDOW || ackskew > MAXACKWINDOW) { if (ackskew < -MAXACKWINDOW || ackskew > MAXACKWINDOW) {
npf_stats_inc(NPF_STAT_INVALID_STATE_TCP3);
return false; return false;
} }
/* /*
* Packet is passed now.
*
* Negative ackskew might be due to fragmented packets. Since the * Negative ackskew might be due to fragmented packets. Since the
* total length of the packet is unknown - bump the boundary. * total length of the packet is unknown - bump the boundary.
*/ */
@ -188,6 +193,7 @@ npf_state_tcp(const npf_cache_t *npc, nbuf_t *nbuf, npf_state_t *nst,
{ {
const struct tcphdr *th = &npc->npc_l4.tcp; const struct tcphdr *th = &npc->npc_l4.tcp;
const int tcpfl = th->th_flags; const int tcpfl = th->th_flags;
int nstate = 0;
/* /*
* Handle 3-way handshake (SYN -> SYN,ACK -> ACK). * Handle 3-way handshake (SYN -> SYN,ACK -> ACK).
@ -195,19 +201,16 @@ npf_state_tcp(const npf_cache_t *npc, nbuf_t *nbuf, npf_state_t *nst,
switch (nst->nst_state) { switch (nst->nst_state) {
case ST_ESTABLISHED: case ST_ESTABLISHED:
/* Common case - connection established. */ /* Common case - connection established. */
if (tcpfl & TH_ACK) { if (__predict_false(tcpfl & (TH_FIN | TH_RST))) {
/* /* Handle connection closure (FIN or RST). */
* Data transmission. nstate = ST_CLOSING;
*/
} else if (tcpfl & TH_FIN) {
/* XXX TODO */
} }
break; break;
case ST_OPENING: case ST_OPENING:
/* SYN has been sent, expecting SYN-ACK. */ /* SYN has been sent, expecting SYN-ACK. */
if (tcpfl == (TH_SYN | TH_ACK) && !forw) { if (tcpfl == (TH_SYN | TH_ACK) && !forw) {
/* Received backwards SYN-ACK. */ /* Received backwards SYN-ACK. */
nst->nst_state = ST_ACKNOWLEDGE; nstate = ST_ACKNOWLEDGE;
} else if (tcpfl == TH_SYN && forw) { } else if (tcpfl == TH_SYN && forw) {
/* Re-transmission of SYN. */ /* Re-transmission of SYN. */
} else { } else {
@ -217,7 +220,7 @@ npf_state_tcp(const npf_cache_t *npc, nbuf_t *nbuf, npf_state_t *nst,
case ST_ACKNOWLEDGE: case ST_ACKNOWLEDGE:
/* SYN-ACK was seen, expecting ACK. */ /* SYN-ACK was seen, expecting ACK. */
if (tcpfl == TH_ACK && forw) { if (tcpfl == TH_ACK && forw) {
nst->nst_state = ST_ESTABLISHED; nstate = ST_ESTABLISHED;
} else { } else {
return false; return false;
} }
@ -229,7 +232,15 @@ npf_state_tcp(const npf_cache_t *npc, nbuf_t *nbuf, npf_state_t *nst,
npf_state_dump(nst); npf_state_dump(nst);
KASSERT(false); KASSERT(false);
} }
return npf_tcp_inwindow(npc, nbuf, nst, forw); #if 0
if (!npf_tcp_inwindow(npc, nbuf, nst, forw)) {
return false;
}
#endif
if (__predict_false(nstate)) {
nst->nst_state = nstate;
}
return true;
} }
bool bool
@ -238,20 +249,24 @@ npf_state_init(const npf_cache_t *npc, nbuf_t *nbuf, npf_state_t *nst)
const int proto = npf_cache_ipproto(npc); const int proto = npf_cache_ipproto(npc);
KASSERT(npf_iscached(npc, NPC_IP46 | NPC_LAYER4)); KASSERT(npf_iscached(npc, NPC_IP46 | NPC_LAYER4));
mutex_init(&nst->nst_lock, MUTEX_DEFAULT, IPL_SOFTNET);
nst->nst_state = ST_OPENING;
if (proto == IPPROTO_TCP) { if (proto == IPPROTO_TCP) {
const struct tcphdr *th = &npc->npc_l4.tcp; const struct tcphdr *th = &npc->npc_l4.tcp;
/* TCP case: must be SYN. */ /* TCP case: must be SYN. */
KASSERT(npf_iscached(npc, NPC_TCP)); KASSERT(npf_iscached(npc, NPC_TCP));
if (th->th_flags != TH_SYN) { if (th->th_flags != TH_SYN) {
npf_stats_inc(NPF_STAT_INVALID_STATE);
return false; return false;
} }
/* Initial values for TCP window and sequence tracking. */ /* Initial values for TCP window and sequence tracking. */
if (!npf_tcp_inwindow(npc, nbuf, nst, true)) { if (!npf_tcp_inwindow(npc, nbuf, nst, true)) {
npf_stats_inc(NPF_STAT_INVALID_STATE);
return false; return false;
} }
} }
mutex_init(&nst->nst_lock, MUTEX_DEFAULT, IPL_SOFTNET);
nst->nst_state = ST_OPENING;
return true; return true;
} }
@ -284,6 +299,9 @@ npf_state_inspect(const npf_cache_t *npc, nbuf_t *nbuf,
ret = true; ret = true;
} }
mutex_exit(&nst->nst_lock); mutex_exit(&nst->nst_lock);
if (__predict_false(!ret)) {
npf_stats_inc(NPF_STAT_INVALID_STATE);
}
return ret; return ret;
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_tableset.c,v 1.3 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_tableset.c,v 1.4 2010/12/18 01:07:25 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -30,10 +30,7 @@
*/ */
/* /*
* NPF table module. * NPF tableset module.
*
* table_lock ->
* npf_table_t::t_lock
* *
* TODO: * TODO:
* - Currently, code is modeled to handle IPv4 CIDR blocks. * - Currently, code is modeled to handle IPv4 CIDR blocks.
@ -42,7 +39,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.3 2010/11/11 06:30:39 rmind Exp $"); __KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.4 2010/12/18 01:07:25 rmind Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/kernel.h> #include <sys/kernel.h>
@ -63,7 +60,7 @@ struct npf_tblent {
/* Hash/tree entry. */ /* Hash/tree entry. */
union { union {
LIST_ENTRY(npf_tblent) hashq; LIST_ENTRY(npf_tblent) hashq;
struct rb_node rbnode; rb_node_t rbnode;
} te_entry; } te_entry;
/* IPv4 CIDR block. */ /* IPv4 CIDR block. */
in_addr_t te_addr; in_addr_t te_addr;
@ -87,39 +84,24 @@ struct npf_table {
rb_tree_t t_rbtree; rb_tree_t t_rbtree;
}; };
/* Global table array and its lock. */ static pool_cache_t tblent_cache __read_mostly;
static npf_tableset_t * table_array;
static krwlock_t table_lock;
static pool_cache_t tblent_cache;
/* /*
* npf_table_sysinit: initialise tableset structures. * npf_table_sysinit: initialise tableset structures.
*/ */
int void
npf_tableset_sysinit(void) npf_tableset_sysinit(void)
{ {
tblent_cache = pool_cache_init(sizeof(npf_tblent_t), coherency_unit, tblent_cache = pool_cache_init(sizeof(npf_tblent_t), coherency_unit,
0, 0, "npftenpl", NULL, IPL_NONE, NULL, NULL, NULL); 0, 0, "npftenpl", NULL, IPL_NONE, NULL, NULL, NULL);
if (tblent_cache == NULL) {
return ENOMEM;
}
table_array = npf_tableset_create();
if (table_array == NULL) {
pool_cache_destroy(tblent_cache);
return ENOMEM;
}
rw_init(&table_lock);
return 0;
} }
void void
npf_tableset_sysfini(void) npf_tableset_sysfini(void)
{ {
npf_tableset_destroy(table_array);
pool_cache_destroy(tblent_cache); pool_cache_destroy(tblent_cache);
rw_destroy(&table_lock);
} }
npf_tableset_t * npf_tableset_t *
@ -172,25 +154,6 @@ npf_tableset_insert(npf_tableset_t *tblset, npf_table_t *t)
return error; return error;
} }
/*
* npf_tableset_reload: replace old tableset array with a new one.
*
* => Called from npf_ruleset_reload() with a global ruleset lock held.
* => Returns pointer to the old tableset, caller will destroy it.
*/
npf_tableset_t *
npf_tableset_reload(npf_tableset_t *tblset)
{
npf_tableset_t *oldtblset;
rw_enter(&table_lock, RW_WRITER);
oldtblset = table_array;
table_array = tblset;
rw_exit(&table_lock);
return oldtblset;
}
/* /*
* Red-black tree storage. * Red-black tree storage.
*/ */
@ -341,24 +304,25 @@ npf_table_unref(npf_table_t *t)
npf_table_t * npf_table_t *
npf_table_get(npf_tableset_t *tset, u_int tid) npf_table_get(npf_tableset_t *tset, u_int tid)
{ {
npf_tableset_t *rtset;
npf_table_t *t; npf_table_t *t;
if ((u_int)tid >= NPF_TABLE_SLOTS) { if ((u_int)tid >= NPF_TABLE_SLOTS) {
return NULL; return NULL;
} }
if (tset) { if (tset == NULL) {
t = tset[tid]; npf_core_enter();
if (t != NULL) { rtset = npf_core_tableset();
rw_enter(&t->t_lock, RW_READER); } else {
} rtset = tset;
return t;
} }
rw_enter(&table_lock, RW_READER); t = rtset[tid];
t = table_array[tid];
if (t != NULL) { if (t != NULL) {
rw_enter(&t->t_lock, RW_READER); rw_enter(&t->t_lock, RW_READER);
} }
rw_exit(&table_lock); if (tset == NULL) {
npf_core_exit();
}
return t; return t;
} }
@ -406,9 +370,6 @@ npf_table_add_v4cidr(npf_tableset_t *tset, u_int tid,
/* Allocate and setup entry. */ /* Allocate and setup entry. */
e = pool_cache_get(tblent_cache, PR_WAITOK); e = pool_cache_get(tblent_cache, PR_WAITOK);
if (e == NULL) {
return ENOMEM;
}
e->te_addr = addr; e->te_addr = addr;
e->te_mask = mask; e->te_mask = mask;

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_data.c,v 1.4 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_data.c,v 1.5 2010/12/18 01:07:26 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -33,7 +33,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__RCSID("$NetBSD: npf_data.c,v 1.4 2010/11/11 06:30:39 rmind Exp $"); __RCSID("$NetBSD: npf_data.c,v 1.5 2010/12/18 01:07:26 rmind Exp $");
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
@ -67,16 +67,12 @@ static pri_t nat_prio_counter = 1;
void void
npfctl_init_data(void) npfctl_init_data(void)
{ {
prop_number_t ver;
if (getifaddrs(&ifs_list) == -1) if (getifaddrs(&ifs_list) == -1)
err(EXIT_FAILURE, "getifaddrs"); err(EXIT_FAILURE, "getifaddrs");
npf_dict = prop_dictionary_create(); npf_dict = prop_dictionary_create();
ver = prop_number_create_integer(NPF_VERSION);
prop_dictionary_set(npf_dict, "version", ver);
nat_arr = prop_array_create(); nat_arr = prop_array_create();
prop_dictionary_set(npf_dict, "translation", nat_arr); prop_dictionary_set(npf_dict, "translation", nat_arr);
@ -108,6 +104,42 @@ npfctl_ioctl_send(int fd)
return ret; return ret;
} }
int
npfctl_ioctl_sendse(int fd)
{
prop_dictionary_t sesdict;
int error;
sesdict = prop_dictionary_internalize_from_file(NPF_SESSDB_PATH);
if (sesdict == NULL) {
errx(EXIT_FAILURE, "npfctl: no sessions saved "
"('%s' does not exist)", NPF_SESSDB_PATH);
}
error = prop_dictionary_send_ioctl(sesdict, fd, IOC_NPF_SESSIONS_LOAD);
prop_object_release(sesdict);
if (error) {
err(EXIT_FAILURE, "npfctl_ioctl_sendse");
}
return 0;
}
int
npfctl_ioctl_recvse(int fd)
{
prop_dictionary_t sesdict;
int error;
error = prop_dictionary_recv_ioctl(fd, IOC_NPF_SESSIONS_SAVE, &sesdict);
if (error) {
err(EXIT_FAILURE, "prop_array_recv_ioctl");
}
if (!prop_dictionary_externalize_to_file(sesdict, NPF_SESSDB_PATH)) {
errx(EXIT_FAILURE, "could not save to '%s'", NPF_SESSDB_PATH);
}
prop_object_release(sesdict);
return 0;
}
/* /*
* Helper routines: * Helper routines:
* *
@ -393,22 +425,27 @@ npfctl_add_rule(prop_dictionary_t rl, prop_dictionary_t parent)
void void
npfctl_rule_setattr(prop_dictionary_t rl, int attr, char *iface, npfctl_rule_setattr(prop_dictionary_t rl, int attr, char *iface,
bool ipid_rnd, int minttl, int maxmss) char *logiface, bool ipid_rnd, int minttl, int maxmss, bool no_df)
{ {
prop_number_t attrnum; prop_number_t attrnum, ifnum;
unsigned int if_idx;
attrnum = prop_number_create_integer(attr); attrnum = prop_number_create_integer(attr);
prop_dictionary_set(rl, "attributes", attrnum); prop_dictionary_set(rl, "attributes", attrnum);
if (iface) { if (iface) {
prop_number_t ifnum;
unsigned int if_idx;
if (npfctl_getif(iface, &if_idx) == NULL) { if (npfctl_getif(iface, &if_idx) == NULL) {
errx(EXIT_FAILURE, "invalid interface '%s'", iface); errx(EXIT_FAILURE, "invalid interface '%s'", iface);
} }
ifnum = prop_number_create_integer(if_idx); ifnum = prop_number_create_integer(if_idx);
prop_dictionary_set(rl, "interface", ifnum); prop_dictionary_set(rl, "interface", ifnum);
} }
if (logiface) {
if (npfctl_getif(logiface, &if_idx) == NULL) {
errx(EXIT_FAILURE, "invalid interface '%s'", logiface);
}
ifnum = prop_number_create_integer(if_idx);
prop_dictionary_set(rl, "log-interface", ifnum);
}
if (attr & NPF_RULE_NORMALIZE) { if (attr & NPF_RULE_NORMALIZE) {
prop_dictionary_set(rl, "randomize-id", prop_dictionary_set(rl, "randomize-id",
prop_bool_create(ipid_rnd)); prop_bool_create(ipid_rnd));
@ -416,6 +453,8 @@ npfctl_rule_setattr(prop_dictionary_t rl, int attr, char *iface,
prop_number_create_integer(minttl)); prop_number_create_integer(minttl));
prop_dictionary_set(rl, "max-mss", prop_dictionary_set(rl, "max-mss",
prop_number_create_integer(maxmss)); prop_number_create_integer(maxmss));
prop_dictionary_set(rl, "no-df",
prop_bool_create(no_df));
} }
} }
@ -452,7 +491,8 @@ npfctl_rulenc_v4cidr(void **nc, int nblocks[], var_t *dat, bool sd)
} }
static void static void
npfctl_rulenc_ports(void **nc, int nblocks[], var_t *dat, bool tcpudp, bool sd) npfctl_rulenc_ports(void **nc, int nblocks[], var_t *dat, bool tcpudp,
bool both, bool sd)
{ {
element_t *el = dat->v_elements; element_t *el = dat->v_elements;
int foff; int foff;
@ -468,7 +508,7 @@ npfctl_rulenc_ports(void **nc, int nblocks[], var_t *dat, bool tcpudp, bool sd)
errx(EXIT_FAILURE, "invalid service '%s'", el->e_data); errx(EXIT_FAILURE, "invalid service '%s'", el->e_data);
} }
nblocks[0]--; nblocks[0]--;
foff = npfctl_failure_offset(nblocks); foff = both ? 0 : npfctl_failure_offset(nblocks);
npfctl_gennc_ports(nc, foff, fport, tport, tcpudp, sd); npfctl_gennc_ports(nc, foff, fport, tport, tcpudp, sd);
} }
} }
@ -482,11 +522,11 @@ npfctl_rulenc_block(void **nc, int nblocks[], var_t *cidr, var_t *ports,
if (ports == NULL) { if (ports == NULL) {
return; return;
} }
npfctl_rulenc_ports(nc, nblocks, ports, tcpudp, sd); npfctl_rulenc_ports(nc, nblocks, ports, tcpudp, both, sd);
if (!both) { if (!both) {
return; return;
} }
npfctl_rulenc_ports(nc, nblocks, ports, !tcpudp, sd); npfctl_rulenc_ports(nc, nblocks, ports, !tcpudp, false, sd);
} }
void void
@ -505,10 +545,11 @@ npfctl_rule_protodata(prop_dictionary_t rl, char *proto, char *tcp_flags,
*/ */
icmp = false; icmp = false;
tcpudp = true; tcpudp = true;
both = false;
if (proto == NULL) { if (proto == NULL) {
both = true;
goto skip_proto; goto skip_proto;
} }
both = false;
if (strcmp(proto, "icmp") == 0) { if (strcmp(proto, "icmp") == 0) {
/* ICMP case. */ /* ICMP case. */
@ -661,7 +702,7 @@ npfctl_nat_setup(prop_dictionary_t rl, int type, int flags,
{ {
int attr = NPF_RULE_PASS | NPF_RULE_FINAL; int attr = NPF_RULE_PASS | NPF_RULE_FINAL;
in_addr_t addr, mask; in_addr_t addr, mask;
void *addrptr; prop_data_t addrdat;
/* Translation type and flags. */ /* Translation type and flags. */
prop_dictionary_set(rl, "type", prop_dictionary_set(rl, "type",
@ -671,15 +712,15 @@ npfctl_nat_setup(prop_dictionary_t rl, int type, int flags,
/* Interface and attributes. */ /* Interface and attributes. */
attr |= (type == NPF_NATOUT) ? NPF_RULE_OUT : NPF_RULE_IN; attr |= (type == NPF_NATOUT) ? NPF_RULE_OUT : NPF_RULE_IN;
npfctl_rule_setattr(rl, attr, iface, false, 0, 0); npfctl_rule_setattr(rl, attr, iface, NULL, false, 0, 0, false);
/* Translation IP, XXX should be no mask. */ /* Translation IP, XXX should be no mask. */
npfctl_parse_cidr(taddr, &addr, &mask); npfctl_parse_cidr(taddr, &addr, &mask);
addrptr = prop_data_create_data(&addr, sizeof(in_addr_t)); addrdat = prop_data_create_data(&addr, sizeof(in_addr_t));
if (addrptr == NULL) { if (addrdat == NULL) {
err(EXIT_FAILURE, "prop_data_create_data"); err(EXIT_FAILURE, "prop_data_create_data");
} }
prop_dictionary_set(rl, "translation-ip", addrptr); prop_dictionary_set(rl, "translation-ip", addrdat);
/* Translation port (for redirect case). */ /* Translation port (for redirect case). */
if (rport) { if (rport) {

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_ncgen.c,v 1.3 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_ncgen.c,v 1.4 2010/12/18 01:07:26 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -37,7 +37,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__RCSID("$NetBSD: npf_ncgen.c,v 1.3 2010/11/11 06:30:39 rmind Exp $"); __RCSID("$NetBSD: npf_ncgen.c,v 1.4 2010/12/18 01:07:26 rmind Exp $");
#include <sys/types.h> #include <sys/types.h>
@ -152,9 +152,18 @@ npfctl_gennc_ports(void **ncptr, int foff,
*nc++ = (sd ? 0x01 : 0x00); *nc++ = (sd ? 0x01 : 0x00);
*nc++ = ((uint32_t)pfrom << 16) | pto; *nc++ = ((uint32_t)pfrom << 16) | pto;
/* If not equal, jump to failure block, continue otherwise (2 words). */ /*
*nc++ = NPF_OPCODE_BNE; * If not equal, jump to failure block, continue otherwise (2 words).
*nc++ = foff; * Specific case (foff == 0): when matching both TCP and UDP ports,
* skip next port-matching fragment on success (5 + 2 words).
*/
if (foff) {
*nc++ = NPF_OPCODE_BNE;
*nc++ = foff;
} else {
*nc++ = NPF_OPCODE_BEQ;
*nc++ = 5 + 2;
}
/* + 5 words. */ /* + 5 words. */
*ncptr = (void *)nc; *ncptr = (void *)nc;

View File

@ -1,4 +1,4 @@
/* $NetBSD: npf_parser.c,v 1.3 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npf_parser.c,v 1.4 2010/12/18 01:07:26 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -31,7 +31,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__RCSID("$NetBSD: npf_parser.c,v 1.3 2010/11/11 06:30:39 rmind Exp $"); __RCSID("$NetBSD: npf_parser.c,v 1.4 2010/12/18 01:07:26 rmind Exp $");
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -144,7 +144,7 @@ npfctl_parsevalue(char *buf)
} }
static inline int static inline int
npfctl_parsenorm(char *buf, bool *rnd, int *minttl, int *maxmss) npfctl_parsenorm(char *buf, bool *rnd, int *minttl, int *maxmss, bool *no_df)
{ {
char *p = buf, *sptr; char *p = buf, *sptr;
@ -163,6 +163,8 @@ npfctl_parsenorm(char *buf, bool *rnd, int *minttl, int *maxmss)
} else if (strcmp(p, "max-mss") == 0) { } else if (strcmp(p, "max-mss") == 0) {
p = strtok_r(NULL, ", \t", &sptr); p = strtok_r(NULL, ", \t", &sptr);
*maxmss = atoi(p); *maxmss = atoi(p);
} else if (strcmp(p, "no-df") == 0) {
*no_df = true;
} else { } else {
return -1; return -1;
} }
@ -185,9 +187,9 @@ npfctl_parserule(char *buf, prop_dictionary_t rl)
{ {
var_t *from_cidr = NULL, *fports = NULL; var_t *from_cidr = NULL, *fports = NULL;
var_t *to_cidr = NULL, *tports = NULL; var_t *to_cidr = NULL, *tports = NULL;
char *p, *sptr, *iface, *proto = NULL, *tcp_flags = NULL; char *p, *sptr, *iface, *logiface, *proto = NULL, *tcp_flags = NULL;
int icmp_type = -1, icmp_code = -1, minttl = 0, maxmss = 0; int icmp_type = -1, icmp_code = -1, minttl = 0, maxmss = 0;
bool icmp = false, tcp = false, rnd = false; bool icmp = false, tcp = false, rnd = false, no_df = false;
int ret, attr = 0; int ret, attr = 0;
DPRINTF(("rule\t|%s|\n", buf)); DPRINTF(("rule\t|%s|\n", buf));
@ -228,10 +230,24 @@ npfctl_parserule(char *buf, prop_dictionary_t rl)
attr |= (NPF_RULE_IN | NPF_RULE_OUT); attr |= (NPF_RULE_IN | NPF_RULE_OUT);
} }
/* log (XXX: NOP) */ /* log <interface> */
if (strcmp(p, "log") == 0) { if (strcmp(p, "log") == 0) {
var_t *ifvar;
element_t *el;
PARSE_NEXT_TOKEN();
if ((ifvar = npfctl_parsevalue(p)) == NULL)
return PARSE_ERR();
if (ifvar->v_type != VAR_SINGLE) {
errx(EXIT_FAILURE, "invalid interface value '%s'", p);
}
el = ifvar->v_elements;
logiface = el->e_data;
attr |= NPF_RULE_LOG; attr |= NPF_RULE_LOG;
PARSE_NEXT_TOKEN(); PARSE_NEXT_TOKEN();
} else {
logiface = NULL;
} }
/* count */ /* count */
@ -379,7 +395,7 @@ last:
if (p == NULL) { if (p == NULL) {
return PARSE_ERR(); return PARSE_ERR();
} }
if (npfctl_parsenorm(p, &rnd, &minttl, &maxmss)) { if (npfctl_parsenorm(p, &rnd, &minttl, &maxmss, &no_df)) {
return PARSE_ERR(); return PARSE_ERR();
} }
attr |= NPF_RULE_NORMALIZE; attr |= NPF_RULE_NORMALIZE;
@ -392,7 +408,8 @@ last:
} }
/* Set the rule attributes and interface, if any. */ /* Set the rule attributes and interface, if any. */
npfctl_rule_setattr(rl, attr, iface, rnd, minttl, maxmss); npfctl_rule_setattr(rl, attr, iface, logiface,
rnd, minttl, maxmss, no_df);
/* /*
* Generate all protocol data. * Generate all protocol data.
@ -439,7 +456,7 @@ npfctl_parsegroup(char *buf, prop_dictionary_t rl)
attr_dir = NPF_RULE_IN | NPF_RULE_OUT; attr_dir = NPF_RULE_IN | NPF_RULE_OUT;
npfctl_rule_setattr(rl, npfctl_rule_setattr(rl,
GROUP_ATTRS | NPF_RULE_DEFAULT | attr_dir, NULL, GROUP_ATTRS | NPF_RULE_DEFAULT | attr_dir, NULL,
false, 0, 0); NULL, false, 0, 0, false);
return 0; return 0;
} }
@ -486,7 +503,8 @@ npfctl_parsegroup(char *buf, prop_dictionary_t rl)
else else
return -1; return -1;
} }
npfctl_rule_setattr(rl, GROUP_ATTRS | attr_dir, iface, false, 0, 0); npfctl_rule_setattr(rl, GROUP_ATTRS | attr_dir, iface, NULL,
false, 0, 0, false);
return 0; return 0;
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: npfctl.c,v 1.2 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npfctl.c,v 1.3 2010/12/18 01:07:26 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -30,7 +30,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__RCSID("$NetBSD: npfctl.c,v 1.2 2010/11/11 06:30:39 rmind Exp $"); __RCSID("$NetBSD: npfctl.c,v 1.3 2010/12/18 01:07:26 rmind Exp $");
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -50,20 +50,28 @@ __RCSID("$NetBSD: npfctl.c,v 1.2 2010/11/11 06:30:39 rmind Exp $");
#define NPFCTL_RELOAD 3 #define NPFCTL_RELOAD 3
#define NPFCTL_FLUSH 4 #define NPFCTL_FLUSH 4
#define NPFCTL_TABLE 5 #define NPFCTL_TABLE 5
#define NPFCTL_STATS 6
#define NPFCTL_SESSIONS_SAVE 7
#define NPFCTL_SESSIONS_LOAD 8
static struct operations_s { static struct operations_s {
const char * cmd; const char * cmd;
int action; int action;
} operations[] = { } operations[] = {
/* Start, stop, reload */ /* Start, stop, reload */
{ "start", NPFCTL_START }, { "start", NPFCTL_START },
{ "stop", NPFCTL_STOP }, { "stop", NPFCTL_STOP },
{ "reload", NPFCTL_RELOAD }, { "reload", NPFCTL_RELOAD },
{ "flush", NPFCTL_FLUSH }, { "flush", NPFCTL_FLUSH },
/* Table */ /* Table */
{ "table", NPFCTL_TABLE }, { "table", NPFCTL_TABLE },
/* Stats */
{ "stats", NPFCTL_STATS },
/* Sessions */
{ "sess-save", NPFCTL_SESSIONS_SAVE },
{ "sess-load", NPFCTL_SESSIONS_LOAD },
/* --- */ /* --- */
{ NULL, 0 } { NULL, 0 }
}; };
void * void *
@ -99,7 +107,10 @@ usage(void)
const char *progname = getprogname(); const char *progname = getprogname();
fprintf(stderr, fprintf(stderr,
"usage:\t%s [ start | stop | reload ]\n", "usage:\t%s [ start | stop | reload | flush | stats ]\n",
progname);
fprintf(stderr,
"usage:\t%s [ sess-save | sess-load ]\n",
progname); progname);
fprintf(stderr, fprintf(stderr,
"\t%s table <tid> [ flush ]\n", "\t%s table <tid> [ flush ]\n",
@ -141,6 +152,44 @@ npfctl_parsecfg(const char *cfg)
} }
} }
static int
npfctl_print_stats(int fd)
{
uint64_t *st = malloc(NPF_STATS_SIZE);
if (ioctl(fd, IOC_NPF_STATS, &st) != 0) {
err(EXIT_FAILURE, "ioctl(IOC_NPF_STATS)");
}
printf("Packets passed:\n\t%"PRIu64" default pass\n\t"
"%"PRIu64 " ruleset pass\n\t%"PRIu64" session pass\n\n",
st[NPF_STAT_PASS_DEFAULT], st[NPF_STAT_PASS_RULESET],
st[NPF_STAT_PASS_SESSION]);
printf("Packets blocked:\n\t%"PRIu64" default block\n\t"
"%"PRIu64 " ruleset block\n\n", st[NPF_STAT_BLOCK_DEFAULT],
st[NPF_STAT_BLOCK_RULESET]);
printf("Session and NAT entries:\n\t%"PRIu64" session allocations\n\t"
"%"PRIu64" session destructions\n\t%"PRIu64" NAT entry allocations\n\t"
"%"PRIu64" NAT entry destructions\n\n", st[NPF_STAT_SESSION_CREATE],
st[NPF_STAT_SESSION_DESTROY], st[NPF_STAT_NAT_CREATE],
st[NPF_STAT_NAT_DESTROY]);
printf("Invalid packet state cases:\n\t%"PRIu64" cases in total\n\t"
"%"PRIu64" TCP case I\n\t%"PRIu64" TCP case II\n\t%"PRIu64
" TCP case III\n\n", st[NPF_STAT_INVALID_STATE],
st[NPF_STAT_INVALID_STATE_TCP1], st[NPF_STAT_INVALID_STATE_TCP2],
st[NPF_STAT_INVALID_STATE_TCP3]);
printf("Packet race cases:\n\t%"PRIu64" NAT association race\n\t"
"%"PRIu64" duplicate session race\n", st[NPF_STAT_RACE_NAT],
st[NPF_STAT_RACE_SESSION]);
free(st);
return 0;
}
static void static void
npfctl(int action, int argc, char **argv) npfctl(int action, int argc, char **argv)
{ {
@ -148,6 +197,7 @@ npfctl(int action, int argc, char **argv)
npf_ioctl_table_t tbl; npf_ioctl_table_t tbl;
char *arg; char *arg;
#ifndef DEBUG
fd = open(NPF_DEV_PATH, O_RDONLY); fd = open(NPF_DEV_PATH, O_RDONLY);
if (fd == -1) { if (fd == -1) {
err(EXIT_FAILURE, "cannot open " NPF_DEV_PATH); err(EXIT_FAILURE, "cannot open " NPF_DEV_PATH);
@ -157,6 +207,7 @@ npfctl(int action, int argc, char **argv)
errx(EXIT_FAILURE, "incompatible npf interface version " errx(EXIT_FAILURE, "incompatible npf interface version "
"(%d, kernel %d)", NPF_VERSION, ver); "(%d, kernel %d)", NPF_VERSION, ver);
} }
#endif
switch (action) { switch (action) {
case NPFCTL_START: case NPFCTL_START:
boolval = true; boolval = true;
@ -168,6 +219,10 @@ npfctl(int action, int argc, char **argv)
break; break;
case NPFCTL_RELOAD: case NPFCTL_RELOAD:
npfctl_init_data(); npfctl_init_data();
#ifdef DEBUG
npfctl_parsecfg("npf.conf");
return npfctl_ioctl_send(0);
#endif
npfctl_parsecfg(argc < 3 ? NPF_CONF_PATH : argv[2]); npfctl_parsecfg(argc < 3 ? NPF_CONF_PATH : argv[2]);
ret = npfctl_ioctl_send(fd); ret = npfctl_ioctl_send(fd);
break; break;
@ -197,6 +252,15 @@ npfctl(int action, int argc, char **argv)
} }
ret = ioctl(fd, IOC_NPF_TABLE, &tbl); ret = ioctl(fd, IOC_NPF_TABLE, &tbl);
break; break;
case NPFCTL_STATS:
ret = npfctl_print_stats(fd);
break;
case NPFCTL_SESSIONS_SAVE:
ret = npfctl_ioctl_recvse(fd);
break;
case NPFCTL_SESSIONS_LOAD:
ret = npfctl_ioctl_sendse(fd);
break;
} }
if (ret == -1) { if (ret == -1) {
err(EXIT_FAILURE, "ioctl"); err(EXIT_FAILURE, "ioctl");
@ -215,18 +279,13 @@ main(int argc, char **argv)
} }
cmd = argv[1]; cmd = argv[1];
#ifdef DEBUG
npfctl_init_data();
npfctl_parsecfg("npf.conf");
return npfctl_ioctl_send(0);
#endif
/* Find and call the subroutine */ /* Find and call the subroutine */
for (n = 0; operations[n].cmd != NULL; n++) { for (n = 0; operations[n].cmd != NULL; n++) {
if (strcmp(cmd, operations[n].cmd) != 0) if (strcmp(cmd, operations[n].cmd) != 0)
continue; continue;
npfctl(operations[n].action, argc, argv); npfctl(operations[n].action, argc, argv);
break; return 0;
} }
usage();
return 0; return 0;
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: npfctl.h,v 1.3 2010/11/11 06:30:39 rmind Exp $ */ /* $NetBSD: npfctl.h,v 1.4 2010/12/18 01:07:26 rmind Exp $ */
/*- /*-
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc. * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@ -49,6 +49,7 @@
#define NPF_DEV_PATH "/dev/npf" #define NPF_DEV_PATH "/dev/npf"
#define NPF_CONF_PATH "/etc/npf.conf" #define NPF_CONF_PATH "/etc/npf.conf"
#define NPF_SESSDB_PATH "/var/db/npf_sessions.db"
typedef struct { typedef struct {
char * e_data; char * e_data;
@ -72,13 +73,15 @@ char * xstrdup(const char *);
void npfctl_init_data(void); void npfctl_init_data(void);
int npfctl_ioctl_send(int); int npfctl_ioctl_send(int);
int npfctl_ioctl_recvse(int);
int npfctl_ioctl_sendse(int);
bool npfctl_parse_v4mask(char *, in_addr_t *, in_addr_t *); bool npfctl_parse_v4mask(char *, in_addr_t *, in_addr_t *);
prop_dictionary_t npfctl_mk_rule(bool); prop_dictionary_t npfctl_mk_rule(bool);
void npfctl_add_rule(prop_dictionary_t, prop_dictionary_t); void npfctl_add_rule(prop_dictionary_t, prop_dictionary_t);
void npfctl_rule_setattr(prop_dictionary_t, int, char *, void npfctl_rule_setattr(prop_dictionary_t, int, char *,
bool, int, int); char *, bool, int, int, bool);
void npfctl_rule_protodata(prop_dictionary_t, char *, char *, void npfctl_rule_protodata(prop_dictionary_t, char *, char *,
int, int, var_t *, var_t *, var_t *, var_t *); int, int, var_t *, var_t *, var_t *, var_t *);
void npfctl_rule_icmpdata(prop_dictionary_t, var_t *, var_t *); void npfctl_rule_icmpdata(prop_dictionary_t, var_t *, var_t *);