version 1.21, 2016/12/10 22:09:49 |
version 1.22, 2016/12/26 23:05:06 |
|
|
* npf_conn_t::c_lock |
* npf_conn_t::c_lock |
*/ |
*/ |
|
|
|
#ifdef _KERNEL |
#include <sys/cdefs.h> |
#include <sys/cdefs.h> |
__KERNEL_RCSID(0, "$NetBSD$"); |
__KERNEL_RCSID(0, "$NetBSD$"); |
|
|
Line 116 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 117 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/pool.h> |
#include <sys/pool.h> |
#include <sys/queue.h> |
#include <sys/queue.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
|
#endif |
|
|
#define __NPF_CONN_PRIVATE |
#define __NPF_CONN_PRIVATE |
#include "npf_conn.h" |
#include "npf_conn.h" |
Line 130 CTASSERT(PFIL_ALL == (0x001 | 0x002)); |
|
Line 132 CTASSERT(PFIL_ALL == (0x001 | 0x002)); |
|
#define CONN_EXPIRE 0x010 /* explicitly expire */ |
#define CONN_EXPIRE 0x010 /* explicitly expire */ |
#define CONN_REMOVED 0x020 /* "forw/back" entries removed */ |
#define CONN_REMOVED 0x020 /* "forw/back" entries removed */ |
|
|
/* |
|
* Connection tracking state: disabled (off) or enabled (on). |
|
*/ |
|
enum { CONN_TRACKING_OFF, CONN_TRACKING_ON }; |
enum { CONN_TRACKING_OFF, CONN_TRACKING_ON }; |
static volatile int conn_tracking __cacheline_aligned; |
|
|
|
/* Connection tracking database, connection cache and the lock. */ |
static void npf_conn_destroy(npf_t *, npf_conn_t *); |
static npf_conndb_t * conn_db __read_mostly; |
|
static pool_cache_t conn_cache __read_mostly; |
|
static kmutex_t conn_lock __cacheline_aligned; |
|
|
|
static void npf_conn_worker(void); |
|
static void npf_conn_destroy(npf_conn_t *); |
|
|
|
/* |
/* |
* npf_conn_sys{init,fini}: initialise/destroy connection tracking. |
* npf_conn_sys{init,fini}: initialise/destroy connection tracking. |
*/ |
*/ |
|
|
void |
void |
npf_conn_sysinit(void) |
npf_conn_init(npf_t *npf, int flags) |
{ |
{ |
conn_cache = pool_cache_init(sizeof(npf_conn_t), coherency_unit, |
npf->conn_cache = pool_cache_init(sizeof(npf_conn_t), coherency_unit, |
0, 0, "npfconpl", NULL, IPL_NET, NULL, NULL, NULL); |
0, 0, "npfconpl", NULL, IPL_NET, NULL, NULL, NULL); |
mutex_init(&conn_lock, MUTEX_DEFAULT, IPL_NONE); |
mutex_init(&npf->conn_lock, MUTEX_DEFAULT, IPL_NONE); |
conn_tracking = CONN_TRACKING_OFF; |
npf->conn_tracking = CONN_TRACKING_OFF; |
conn_db = npf_conndb_create(); |
npf->conn_db = npf_conndb_create(); |
|
|
npf_worker_register(npf_conn_worker); |
if ((flags & NPF_NO_GC) == 0) { |
|
npf_worker_register(npf, npf_conn_worker); |
|
} |
} |
} |
|
|
void |
void |
npf_conn_sysfini(void) |
npf_conn_fini(npf_t *npf) |
{ |
{ |
/* Note: the caller should have flushed the connections. */ |
/* Note: the caller should have flushed the connections. */ |
KASSERT(conn_tracking == CONN_TRACKING_OFF); |
KASSERT(npf->conn_tracking == CONN_TRACKING_OFF); |
npf_worker_unregister(npf_conn_worker); |
npf_worker_unregister(npf, npf_conn_worker); |
|
|
npf_conndb_destroy(conn_db); |
npf_conndb_destroy(npf->conn_db); |
pool_cache_destroy(conn_cache); |
pool_cache_destroy(npf->conn_cache); |
mutex_destroy(&conn_lock); |
mutex_destroy(&npf->conn_lock); |
} |
} |
|
|
/* |
/* |
Line 180 npf_conn_sysfini(void) |
|
Line 174 npf_conn_sysfini(void) |
|
* there are no connection database lookups or references in-flight. |
* there are no connection database lookups or references in-flight. |
*/ |
*/ |
void |
void |
npf_conn_load(npf_conndb_t *ndb, bool track) |
npf_conn_load(npf_t *npf, npf_conndb_t *ndb, bool track) |
{ |
{ |
npf_conndb_t *odb = NULL; |
npf_conndb_t *odb = NULL; |
|
|
KASSERT(npf_config_locked_p()); |
KASSERT(npf_config_locked_p(npf)); |
|
|
/* |
/* |
* The connection database is in the quiescent state. |
* The connection database is in the quiescent state. |
* Prevent G/C thread from running and install a new database. |
* Prevent G/C thread from running and install a new database. |
*/ |
*/ |
mutex_enter(&conn_lock); |
mutex_enter(&npf->conn_lock); |
if (ndb) { |
if (ndb) { |
KASSERT(conn_tracking == CONN_TRACKING_OFF); |
KASSERT(npf->conn_tracking == CONN_TRACKING_OFF); |
odb = conn_db; |
odb = npf->conn_db; |
conn_db = ndb; |
npf->conn_db = ndb; |
membar_sync(); |
membar_sync(); |
} |
} |
if (track) { |
if (track) { |
/* After this point lookups start flying in. */ |
/* After this point lookups start flying in. */ |
conn_tracking = CONN_TRACKING_ON; |
npf->conn_tracking = CONN_TRACKING_ON; |
} |
} |
mutex_exit(&conn_lock); |
mutex_exit(&npf->conn_lock); |
|
|
if (odb) { |
if (odb) { |
/* |
/* |
* Flush all, no sync since the caller did it for us. |
* Flush all, no sync since the caller did it for us. |
* Also, release the pool cache memory. |
* Also, release the pool cache memory. |
*/ |
*/ |
npf_conn_gc(odb, true, false); |
npf_conn_gc(npf, odb, true, false); |
npf_conndb_destroy(odb); |
npf_conndb_destroy(odb); |
pool_cache_invalidate(conn_cache); |
pool_cache_invalidate(npf->conn_cache); |
} |
} |
} |
} |
|
|
Line 218 npf_conn_load(npf_conndb_t *ndb, bool tr |
|
Line 212 npf_conn_load(npf_conndb_t *ndb, bool tr |
|
* npf_conn_tracking: enable/disable connection tracking. |
* npf_conn_tracking: enable/disable connection tracking. |
*/ |
*/ |
void |
void |
npf_conn_tracking(bool track) |
npf_conn_tracking(npf_t *npf, bool track) |
{ |
{ |
KASSERT(npf_config_locked_p()); |
KASSERT(npf_config_locked_p(npf)); |
conn_tracking = track ? CONN_TRACKING_ON : CONN_TRACKING_OFF; |
npf->conn_tracking = track ? CONN_TRACKING_ON : CONN_TRACKING_OFF; |
} |
} |
|
|
static inline bool |
static inline bool |
npf_conn_trackable_p(const npf_cache_t *npc) |
npf_conn_trackable_p(const npf_cache_t *npc) |
{ |
{ |
|
const npf_t *npf = npc->npc_ctx; |
|
|
/* |
/* |
* Check if connection tracking is on. Also, if layer 3 and 4 are |
* Check if connection tracking is on. Also, if layer 3 and 4 are |
* not cached - protocol is not supported or packet is invalid. |
* not cached - protocol is not supported or packet is invalid. |
*/ |
*/ |
if (conn_tracking != CONN_TRACKING_ON) { |
if (npf->conn_tracking != CONN_TRACKING_ON) { |
return false; |
return false; |
} |
} |
if (!npf_iscached(npc, NPC_IP46) || !npf_iscached(npc, NPC_LAYER4)) { |
if (!npf_iscached(npc, NPC_IP46) || !npf_iscached(npc, NPC_LAYER4)) { |
Line 242 npf_conn_trackable_p(const npf_cache_t * |
|
Line 238 npf_conn_trackable_p(const npf_cache_t * |
|
|
|
static uint32_t |
static uint32_t |
connkey_setkey(npf_connkey_t *key, uint16_t proto, const void *ipv, |
connkey_setkey(npf_connkey_t *key, uint16_t proto, const void *ipv, |
const uint16_t *id, uint16_t alen, bool forw) |
const uint16_t *id, unsigned alen, bool forw) |
{ |
{ |
uint32_t isrc, idst, *k = key->ck_key; |
uint32_t isrc, idst, *k = key->ck_key; |
const npf_addr_t * const *ips = ipv; |
const npf_addr_t * const *ips = ipv; |
|
|
if (__predict_true(forw)) { |
if (__predict_true(forw)) { |
isrc = NPF_SRC, idst = NPF_DST; |
isrc = NPF_SRC, idst = NPF_DST; |
} else { |
} else { |
Line 268 connkey_setkey(npf_connkey_t *key, uint1 |
|
Line 265 connkey_setkey(npf_connkey_t *key, uint1 |
|
k[1] = ((uint32_t)id[isrc] << 16) | id[idst]; |
k[1] = ((uint32_t)id[isrc] << 16) | id[idst]; |
|
|
if (__predict_true(alen == sizeof(in_addr_t))) { |
if (__predict_true(alen == sizeof(in_addr_t))) { |
k[2] = ips[isrc]->s6_addr32[0]; |
k[2] = ips[isrc]->word32[0]; |
k[3] = ips[idst]->s6_addr32[0]; |
k[3] = ips[idst]->word32[0]; |
return 4 * sizeof(uint32_t); |
return 4 * sizeof(uint32_t); |
} else { |
} else { |
const u_int nwords = alen >> 2; |
const u_int nwords = alen >> 2; |
Line 309 connkey_getkey(const npf_connkey_t *key, |
|
Line 306 connkey_getkey(const npf_connkey_t *key, |
|
unsigned |
unsigned |
npf_conn_conkey(const npf_cache_t *npc, npf_connkey_t *key, const bool forw) |
npf_conn_conkey(const npf_cache_t *npc, npf_connkey_t *key, const bool forw) |
{ |
{ |
const uint16_t alen = npc->npc_alen; |
const u_int proto = npc->npc_proto; |
|
const u_int alen = npc->npc_alen; |
const struct tcphdr *th; |
const struct tcphdr *th; |
const struct udphdr *uh; |
const struct udphdr *uh; |
uint16_t id[2]; |
uint16_t id[2]; |
|
|
switch (npc->npc_proto) { |
switch (proto) { |
case IPPROTO_TCP: |
case IPPROTO_TCP: |
KASSERT(npf_iscached(npc, NPC_TCP)); |
KASSERT(npf_iscached(npc, NPC_TCP)); |
th = npc->npc_l4.tcp; |
th = npc->npc_l4.tcp; |
Line 347 npf_conn_conkey(const npf_cache_t *npc, |
|
Line 345 npf_conn_conkey(const npf_cache_t *npc, |
|
/* Unsupported protocol. */ |
/* Unsupported protocol. */ |
return 0; |
return 0; |
} |
} |
|
return connkey_setkey(key, proto, npc->npc_ips, id, alen, forw); |
return connkey_setkey(key, npc->npc_proto, npc->npc_ips, id, alen, |
|
forw); |
|
} |
} |
|
|
static __inline void |
static __inline void |
Line 372 connkey_set_id(npf_connkey_t *key, const |
|
Line 368 connkey_set_id(npf_connkey_t *key, const |
|
key->ck_key[1] = ((uint32_t)id << shift) | (oid & mask); |
key->ck_key[1] = ((uint32_t)id << shift) | (oid & mask); |
} |
} |
|
|
|
static inline void |
|
conn_update_atime(npf_conn_t *con) |
|
{ |
|
struct timespec tsnow; |
|
|
|
getnanouptime(&tsnow); |
|
con->c_atime = tsnow.tv_sec; |
|
} |
|
|
/* |
/* |
* npf_conn_ok: check if the connection is active, and has the right direction. |
* npf_conn_ok: check if the connection is active, and has the right direction. |
*/ |
*/ |
static bool |
static bool |
npf_conn_ok(npf_conn_t *con, const int di, bool forw) |
npf_conn_ok(const npf_conn_t *con, const int di, bool forw) |
{ |
{ |
uint32_t flags = con->c_flags; |
const uint32_t flags = con->c_flags; |
|
|
/* Check if connection is active and not expired. */ |
/* Check if connection is active and not expired. */ |
bool ok = (flags & (CONN_ACTIVE | CONN_EXPIRE)) == CONN_ACTIVE; |
bool ok = (flags & (CONN_ACTIVE | CONN_EXPIRE)) == CONN_ACTIVE; |
Line 387 npf_conn_ok(npf_conn_t *con, const int d |
|
Line 392 npf_conn_ok(npf_conn_t *con, const int d |
|
} |
} |
|
|
/* Check if the direction is consistent */ |
/* Check if the direction is consistent */ |
bool pforw = (flags & PFIL_ALL) == di; |
bool pforw = (flags & PFIL_ALL) == (unsigned)di; |
if (__predict_false(forw != pforw)) { |
if (__predict_false(forw != pforw)) { |
return false; |
return false; |
} |
} |
Line 402 npf_conn_ok(npf_conn_t *con, const int d |
|
Line 407 npf_conn_ok(npf_conn_t *con, const int d |
|
npf_conn_t * |
npf_conn_t * |
npf_conn_lookup(const npf_cache_t *npc, const int di, bool *forw) |
npf_conn_lookup(const npf_cache_t *npc, const int di, bool *forw) |
{ |
{ |
|
npf_t *npf = npc->npc_ctx; |
const nbuf_t *nbuf = npc->npc_nbuf; |
const nbuf_t *nbuf = npc->npc_nbuf; |
npf_conn_t *con; |
npf_conn_t *con; |
npf_connkey_t key; |
npf_connkey_t key; |
Line 411 npf_conn_lookup(const npf_cache_t *npc, |
|
Line 417 npf_conn_lookup(const npf_cache_t *npc, |
|
if (!npf_conn_conkey(npc, &key, true)) { |
if (!npf_conn_conkey(npc, &key, true)) { |
return NULL; |
return NULL; |
} |
} |
con = npf_conndb_lookup(conn_db, &key, forw); |
con = npf_conndb_lookup(npf->conn_db, &key, forw); |
if (con == NULL) { |
if (con == NULL) { |
return NULL; |
return NULL; |
} |
} |
Line 434 npf_conn_lookup(const npf_cache_t *npc, |
|
Line 440 npf_conn_lookup(const npf_cache_t *npc, |
|
} |
} |
|
|
/* Update the last activity time. */ |
/* Update the last activity time. */ |
getnanouptime(&con->c_atime); |
conn_update_atime(con); |
return con; |
return con; |
} |
} |
|
|
Line 479 npf_conn_inspect(npf_cache_t *npc, const |
|
Line 485 npf_conn_inspect(npf_cache_t *npc, const |
|
/* If invalid state: let the rules deal with it. */ |
/* If invalid state: let the rules deal with it. */ |
if (__predict_false(!ok)) { |
if (__predict_false(!ok)) { |
npf_conn_release(con); |
npf_conn_release(con); |
npf_stats_inc(NPF_STAT_INVALID_STATE); |
npf_stats_inc(npc->npc_ctx, NPF_STAT_INVALID_STATE); |
return NULL; |
return NULL; |
} |
} |
|
|
Line 504 npf_conn_inspect(npf_cache_t *npc, const |
|
Line 510 npf_conn_inspect(npf_cache_t *npc, const |
|
npf_conn_t * |
npf_conn_t * |
npf_conn_establish(npf_cache_t *npc, int di, bool per_if) |
npf_conn_establish(npf_cache_t *npc, int di, bool per_if) |
{ |
{ |
|
npf_t *npf = npc->npc_ctx; |
const nbuf_t *nbuf = npc->npc_nbuf; |
const nbuf_t *nbuf = npc->npc_nbuf; |
npf_conn_t *con; |
npf_conn_t *con; |
int error = 0; |
int error = 0; |
Line 515 npf_conn_establish(npf_cache_t *npc, int |
|
Line 522 npf_conn_establish(npf_cache_t *npc, int |
|
} |
} |
|
|
/* Allocate and initialise the new connection. */ |
/* Allocate and initialise the new connection. */ |
con = pool_cache_get(conn_cache, PR_NOWAIT); |
con = pool_cache_get(npf->conn_cache, PR_NOWAIT); |
if (__predict_false(!con)) { |
if (__predict_false(!con)) { |
|
npf_worker_signal(npf); |
return NULL; |
return NULL; |
} |
} |
NPF_PRINTF(("NPF: create conn %p\n", con)); |
NPF_PRINTF(("NPF: create conn %p\n", con)); |
npf_stats_inc(NPF_STAT_CONN_CREATE); |
npf_stats_inc(npf, NPF_STAT_CONN_CREATE); |
|
|
mutex_init(&con->c_lock, MUTEX_DEFAULT, IPL_SOFTNET); |
mutex_init(&con->c_lock, MUTEX_DEFAULT, IPL_SOFTNET); |
con->c_flags = (di & PFIL_ALL); |
con->c_flags = (di & PFIL_ALL); |
Line 530 npf_conn_establish(npf_cache_t *npc, int |
|
Line 538 npf_conn_establish(npf_cache_t *npc, int |
|
|
|
/* Initialize the protocol state. */ |
/* Initialize the protocol state. */ |
if (!npf_state_init(npc, &con->c_state)) { |
if (!npf_state_init(npc, &con->c_state)) { |
npf_conn_destroy(con); |
npf_conn_destroy(npf, con); |
return NULL; |
return NULL; |
} |
} |
|
|
Line 544 npf_conn_establish(npf_cache_t *npc, int |
|
Line 552 npf_conn_establish(npf_cache_t *npc, int |
|
*/ |
*/ |
if (!npf_conn_conkey(npc, fw, true) || |
if (!npf_conn_conkey(npc, fw, true) || |
!npf_conn_conkey(npc, bk, false)) { |
!npf_conn_conkey(npc, bk, false)) { |
npf_conn_destroy(con); |
npf_conn_destroy(npf, con); |
return NULL; |
return NULL; |
} |
} |
fw->ck_backptr = bk->ck_backptr = con; |
fw->ck_backptr = bk->ck_backptr = con; |
Line 555 npf_conn_establish(npf_cache_t *npc, int |
|
Line 563 npf_conn_establish(npf_cache_t *npc, int |
|
* Set last activity time for a new connection and acquire |
* Set last activity time for a new connection and acquire |
* a reference for the caller before we make it visible. |
* a reference for the caller before we make it visible. |
*/ |
*/ |
getnanouptime(&con->c_atime); |
conn_update_atime(con); |
con->c_refcnt = 1; |
con->c_refcnt = 1; |
|
|
/* |
/* |
Line 564 npf_conn_establish(npf_cache_t *npc, int |
|
Line 572 npf_conn_establish(npf_cache_t *npc, int |
|
* the connection later. |
* the connection later. |
*/ |
*/ |
mutex_enter(&con->c_lock); |
mutex_enter(&con->c_lock); |
if (!npf_conndb_insert(conn_db, fw, con)) { |
if (!npf_conndb_insert(npf->conn_db, fw, con)) { |
error = EISCONN; |
error = EISCONN; |
goto err; |
goto err; |
} |
} |
if (!npf_conndb_insert(conn_db, bk, con)) { |
if (!npf_conndb_insert(npf->conn_db, bk, con)) { |
npf_conn_t *ret __diagused; |
npf_conn_t *ret __diagused; |
ret = npf_conndb_remove(conn_db, fw); |
ret = npf_conndb_remove(npf->conn_db, fw); |
KASSERT(ret == con); |
KASSERT(ret == con); |
error = EISCONN; |
error = EISCONN; |
goto err; |
goto err; |
|
|
if (error) { |
if (error) { |
atomic_or_uint(&con->c_flags, CONN_REMOVED | CONN_EXPIRE); |
atomic_or_uint(&con->c_flags, CONN_REMOVED | CONN_EXPIRE); |
atomic_dec_uint(&con->c_refcnt); |
atomic_dec_uint(&con->c_refcnt); |
npf_stats_inc(NPF_STAT_RACE_CONN); |
npf_stats_inc(npf, NPF_STAT_RACE_CONN); |
} else { |
} else { |
NPF_PRINTF(("NPF: establish conn %p\n", con)); |
NPF_PRINTF(("NPF: establish conn %p\n", con)); |
} |
} |
|
|
/* Finally, insert into the connection list. */ |
/* Finally, insert into the connection list. */ |
npf_conndb_enqueue(conn_db, con); |
npf_conndb_enqueue(npf->conn_db, con); |
mutex_exit(&con->c_lock); |
mutex_exit(&con->c_lock); |
|
|
return error ? NULL : con; |
return error ? NULL : con; |
} |
} |
|
|
static void |
static void |
npf_conn_destroy(npf_conn_t *con) |
npf_conn_destroy(npf_t *npf, npf_conn_t *con) |
{ |
{ |
KASSERT(con->c_refcnt == 0); |
KASSERT(con->c_refcnt == 0); |
|
|
Line 615 npf_conn_destroy(npf_conn_t *con) |
|
Line 623 npf_conn_destroy(npf_conn_t *con) |
|
mutex_destroy(&con->c_lock); |
mutex_destroy(&con->c_lock); |
|
|
/* Free the structure, increase the counter. */ |
/* Free the structure, increase the counter. */ |
pool_cache_put(conn_cache, con); |
pool_cache_put(npf->conn_cache, con); |
npf_stats_inc(NPF_STAT_CONN_DESTROY); |
npf_stats_inc(npf, NPF_STAT_CONN_DESTROY); |
NPF_PRINTF(("NPF: conn %p destroyed\n", con)); |
NPF_PRINTF(("NPF: conn %p destroyed\n", con)); |
} |
} |
|
|
Line 634 npf_conn_setnat(const npf_cache_t *npc, |
|
Line 642 npf_conn_setnat(const npf_cache_t *npc, |
|
[NPF_NATOUT] = NPF_DST, |
[NPF_NATOUT] = NPF_DST, |
[NPF_NATIN] = NPF_SRC, |
[NPF_NATIN] = NPF_SRC, |
}; |
}; |
|
npf_t *npf = npc->npc_ctx; |
npf_connkey_t key, *bk; |
npf_connkey_t key, *bk; |
npf_conn_t *ret __diagused; |
npf_conn_t *ret __diagused; |
npf_addr_t *taddr; |
npf_addr_t *taddr; |
Line 663 npf_conn_setnat(const npf_cache_t *npc, |
|
Line 672 npf_conn_setnat(const npf_cache_t *npc, |
|
if (__predict_false(con->c_nat != NULL)) { |
if (__predict_false(con->c_nat != NULL)) { |
/* Race with a duplicate packet. */ |
/* Race with a duplicate packet. */ |
mutex_exit(&con->c_lock); |
mutex_exit(&con->c_lock); |
npf_stats_inc(NPF_STAT_RACE_NAT); |
npf_stats_inc(npc->npc_ctx, NPF_STAT_RACE_NAT); |
return EISCONN; |
return EISCONN; |
} |
} |
|
|
/* Remove the "backwards" entry. */ |
/* Remove the "backwards" entry. */ |
ret = npf_conndb_remove(conn_db, &con->c_back_entry); |
ret = npf_conndb_remove(npf->conn_db, &con->c_back_entry); |
KASSERT(ret == con); |
KASSERT(ret == con); |
|
|
/* Set the source/destination IDs to the translation values. */ |
/* Set the source/destination IDs to the translation values. */ |
Line 679 npf_conn_setnat(const npf_cache_t *npc, |
|
Line 688 npf_conn_setnat(const npf_cache_t *npc, |
|
} |
} |
|
|
/* Finally, re-insert the "backwards" entry. */ |
/* Finally, re-insert the "backwards" entry. */ |
if (!npf_conndb_insert(conn_db, bk, con)) { |
if (!npf_conndb_insert(npf->conn_db, bk, con)) { |
/* |
/* |
* Race: we have hit the duplicate, remove the "forwards" |
* Race: we have hit the duplicate, remove the "forwards" |
* entry and expire our connection; it is no longer valid. |
* entry and expire our connection; it is no longer valid. |
*/ |
*/ |
ret = npf_conndb_remove(conn_db, &con->c_forw_entry); |
ret = npf_conndb_remove(npf->conn_db, &con->c_forw_entry); |
KASSERT(ret == con); |
KASSERT(ret == con); |
|
|
atomic_or_uint(&con->c_flags, CONN_REMOVED | CONN_EXPIRE); |
atomic_or_uint(&con->c_flags, CONN_REMOVED | CONN_EXPIRE); |
mutex_exit(&con->c_lock); |
mutex_exit(&con->c_lock); |
|
|
npf_stats_inc(NPF_STAT_RACE_NAT); |
npf_stats_inc(npc->npc_ctx, NPF_STAT_RACE_NAT); |
return EISCONN; |
return EISCONN; |
} |
} |
|
|
|
|
npf_conn_getnat(npf_conn_t *con, const int di, bool *forw) |
npf_conn_getnat(npf_conn_t *con, const int di, bool *forw) |
{ |
{ |
KASSERT(con->c_refcnt > 0); |
KASSERT(con->c_refcnt > 0); |
*forw = (con->c_flags & PFIL_ALL) == di; |
*forw = (con->c_flags & PFIL_ALL) == (u_int)di; |
return con->c_nat; |
return con->c_nat; |
} |
} |
|
|
Line 775 npf_conn_getnat(npf_conn_t *con, const i |
|
Line 784 npf_conn_getnat(npf_conn_t *con, const i |
|
* npf_conn_expired: criterion to check if connection is expired. |
* npf_conn_expired: criterion to check if connection is expired. |
*/ |
*/ |
static inline bool |
static inline bool |
npf_conn_expired(const npf_conn_t *con, const struct timespec *tsnow) |
npf_conn_expired(const npf_conn_t *con, uint64_t tsnow) |
{ |
{ |
const int etime = npf_state_etime(&con->c_state, con->c_proto); |
const int etime = npf_state_etime(&con->c_state, con->c_proto); |
struct timespec tsdiff; |
int elapsed; |
|
|
if (__predict_false(con->c_flags & CONN_EXPIRE)) { |
if (__predict_false(con->c_flags & CONN_EXPIRE)) { |
/* Explicitly marked to be expired. */ |
/* Explicitly marked to be expired. */ |
return true; |
return true; |
} |
} |
timespecsub(tsnow, &con->c_atime, &tsdiff); |
|
return tsdiff.tv_sec > etime; |
/* |
|
* Note: another thread may update 'atime' and it might |
|
* become greater than 'now'. |
|
*/ |
|
elapsed = (int64_t)tsnow - con->c_atime; |
|
return elapsed > etime; |
} |
} |
|
|
/* |
/* |
Line 796 npf_conn_expired(const npf_conn_t *con, |
|
Line 810 npf_conn_expired(const npf_conn_t *con, |
|
* => If 'sync' is true, then perform passive serialisation. |
* => If 'sync' is true, then perform passive serialisation. |
*/ |
*/ |
void |
void |
npf_conn_gc(npf_conndb_t *cd, bool flush, bool sync) |
npf_conn_gc(npf_t *npf, npf_conndb_t *cd, bool flush, bool sync) |
{ |
{ |
npf_conn_t *con, *prev, *gclist = NULL; |
npf_conn_t *con, *prev, *gclist = NULL; |
struct timespec tsnow; |
struct timespec tsnow; |
Line 812 npf_conn_gc(npf_conndb_t *cd, bool flush |
|
Line 826 npf_conn_gc(npf_conndb_t *cd, bool flush |
|
npf_conn_t *next = con->c_next; |
npf_conn_t *next = con->c_next; |
|
|
/* Expired? Flushing all? */ |
/* Expired? Flushing all? */ |
if (!npf_conn_expired(con, &tsnow) && !flush) { |
if (!npf_conn_expired(con, tsnow.tv_sec) && !flush) { |
prev = con; |
prev = con; |
con = next; |
con = next; |
continue; |
continue; |
Line 848 npf_conn_gc(npf_conndb_t *cd, bool flush |
|
Line 862 npf_conn_gc(npf_conndb_t *cd, bool flush |
|
* Note: drop the conn_lock (see the lock order). |
* Note: drop the conn_lock (see the lock order). |
*/ |
*/ |
if (sync) { |
if (sync) { |
mutex_exit(&conn_lock); |
mutex_exit(&npf->conn_lock); |
if (gclist) { |
if (gclist) { |
npf_config_enter(); |
npf_config_enter(npf); |
npf_config_sync(); |
npf_config_sync(npf); |
npf_config_exit(); |
npf_config_exit(npf); |
} |
} |
} |
} |
|
|
Line 872 npf_conn_gc(npf_conndb_t *cd, bool flush |
|
Line 886 npf_conn_gc(npf_conndb_t *cd, bool flush |
|
kpause("npfcongc", false, 1, NULL); |
kpause("npfcongc", false, 1, NULL); |
continue; |
continue; |
} |
} |
npf_conn_destroy(con); |
npf_conn_destroy(npf, con); |
con = next; |
con = next; |
} |
} |
} |
} |
Line 880 npf_conn_gc(npf_conndb_t *cd, bool flush |
|
Line 894 npf_conn_gc(npf_conndb_t *cd, bool flush |
|
/* |
/* |
* npf_conn_worker: G/C to run from a worker thread. |
* npf_conn_worker: G/C to run from a worker thread. |
*/ |
*/ |
static void |
void |
npf_conn_worker(void) |
npf_conn_worker(npf_t *npf) |
{ |
{ |
mutex_enter(&conn_lock); |
mutex_enter(&npf->conn_lock); |
/* Note: the conn_lock will be released (sync == true). */ |
/* Note: the conn_lock will be released (sync == true). */ |
npf_conn_gc(conn_db, false, true); |
npf_conn_gc(npf, npf->conn_db, false, true); |
} |
} |
|
|
/* |
/* |
Line 893 npf_conn_worker(void) |
|
Line 907 npf_conn_worker(void) |
|
* Note: this is expected to be an expensive operation. |
* Note: this is expected to be an expensive operation. |
*/ |
*/ |
int |
int |
npf_conndb_export(prop_array_t conlist) |
npf_conndb_export(npf_t *npf, prop_array_t conlist) |
{ |
{ |
npf_conn_t *con, *prev; |
npf_conn_t *con, *prev; |
|
|
Line 901 npf_conndb_export(prop_array_t conlist) |
|
Line 915 npf_conndb_export(prop_array_t conlist) |
|
* Note: acquire conn_lock to prevent from the database |
* Note: acquire conn_lock to prevent from the database |
* destruction and G/C thread. |
* destruction and G/C thread. |
*/ |
*/ |
mutex_enter(&conn_lock); |
mutex_enter(&npf->conn_lock); |
if (conn_tracking != CONN_TRACKING_ON) { |
if (npf->conn_tracking != CONN_TRACKING_ON) { |
mutex_exit(&conn_lock); |
mutex_exit(&npf->conn_lock); |
return 0; |
return 0; |
} |
} |
prev = NULL; |
prev = NULL; |
con = npf_conndb_getlist(conn_db); |
con = npf_conndb_getlist(npf->conn_db); |
while (con) { |
while (con) { |
npf_conn_t *next = con->c_next; |
npf_conn_t *next = con->c_next; |
prop_dictionary_t cdict; |
prop_dictionary_t cdict; |
|
|
if ((cdict = npf_conn_export(con)) != NULL) { |
if ((cdict = npf_conn_export(npf, con)) != NULL) { |
prop_array_add(conlist, cdict); |
prop_array_add(conlist, cdict); |
prop_object_release(cdict); |
prop_object_release(cdict); |
} |
} |
prev = con; |
prev = con; |
con = next; |
con = next; |
} |
} |
npf_conndb_settail(conn_db, prev); |
npf_conndb_settail(npf->conn_db, prev); |
mutex_exit(&conn_lock); |
mutex_exit(&npf->conn_lock); |
return 0; |
return 0; |
} |
} |
|
|
Line 928 static prop_dictionary_t |
|
Line 942 static prop_dictionary_t |
|
npf_connkey_export(const npf_connkey_t *key) |
npf_connkey_export(const npf_connkey_t *key) |
{ |
{ |
uint16_t id[2], alen, proto; |
uint16_t id[2], alen, proto; |
|
prop_dictionary_t kdict; |
npf_addr_t ips[2]; |
npf_addr_t ips[2]; |
prop_data_t d; |
prop_data_t d; |
prop_dictionary_t kdict = prop_dictionary_create(); |
|
|
|
|
kdict = prop_dictionary_create(); |
connkey_getkey(key, &proto, ips, id, &alen); |
connkey_getkey(key, &proto, ips, id, &alen); |
|
|
prop_dictionary_set_uint16(kdict, "proto", proto); |
prop_dictionary_set_uint16(kdict, "proto", proto); |
Line 952 npf_connkey_export(const npf_connkey_t * |
|
Line 967 npf_connkey_export(const npf_connkey_t * |
|
* npf_conn_export: serialise a single connection. |
* npf_conn_export: serialise a single connection. |
*/ |
*/ |
prop_dictionary_t |
prop_dictionary_t |
npf_conn_export(const npf_conn_t *con) |
npf_conn_export(npf_t *npf, const npf_conn_t *con) |
{ |
{ |
prop_dictionary_t cdict, kdict; |
prop_dictionary_t cdict, kdict; |
prop_data_t d; |
prop_data_t d; |
Line 964 npf_conn_export(const npf_conn_t *con) |
|
Line 979 npf_conn_export(const npf_conn_t *con) |
|
prop_dictionary_set_uint32(cdict, "flags", con->c_flags); |
prop_dictionary_set_uint32(cdict, "flags", con->c_flags); |
prop_dictionary_set_uint32(cdict, "proto", con->c_proto); |
prop_dictionary_set_uint32(cdict, "proto", con->c_proto); |
if (con->c_ifid) { |
if (con->c_ifid) { |
const char *ifname = npf_ifmap_getname(con->c_ifid); |
const char *ifname = npf_ifmap_getname(npf, con->c_ifid); |
prop_dictionary_set_cstring(cdict, "ifname", ifname); |
prop_dictionary_set_cstring(cdict, "ifname", ifname); |
} |
} |
|
|
Line 986 npf_conn_export(const npf_conn_t *con) |
|
Line 1001 npf_conn_export(const npf_conn_t *con) |
|
static uint32_t |
static uint32_t |
npf_connkey_import(prop_dictionary_t kdict, npf_connkey_t *key) |
npf_connkey_import(prop_dictionary_t kdict, npf_connkey_t *key) |
{ |
{ |
uint16_t proto; |
|
prop_object_t sobj, dobj; |
prop_object_t sobj, dobj; |
uint16_t id[2]; |
|
npf_addr_t const * ips[2]; |
npf_addr_t const * ips[2]; |
|
uint16_t alen, proto, id[2]; |
|
|
if (!prop_dictionary_get_uint16(kdict, "proto", &proto)) |
if (!prop_dictionary_get_uint16(kdict, "proto", &proto)) |
return 0; |
return 0; |
Line 1008 npf_connkey_import(prop_dictionary_t kdi |
|
Line 1022 npf_connkey_import(prop_dictionary_t kdi |
|
if ((ips[NPF_DST] = prop_data_data_nocopy(dobj)) == NULL) |
if ((ips[NPF_DST] = prop_data_data_nocopy(dobj)) == NULL) |
return 0; |
return 0; |
|
|
uint16_t alen = prop_data_size(sobj); |
alen = prop_data_size(sobj); |
if (alen != prop_data_size(dobj)) |
if (alen != prop_data_size(dobj)) |
return 0; |
return 0; |
|
|
Line 1020 npf_connkey_import(prop_dictionary_t kdi |
|
Line 1034 npf_connkey_import(prop_dictionary_t kdi |
|
* directory and insert into the given database. |
* directory and insert into the given database. |
*/ |
*/ |
int |
int |
npf_conn_import(npf_conndb_t *cd, prop_dictionary_t cdict, |
npf_conn_import(npf_t *npf, npf_conndb_t *cd, prop_dictionary_t cdict, |
npf_ruleset_t *natlist) |
npf_ruleset_t *natlist) |
{ |
{ |
npf_conn_t *con; |
npf_conn_t *con; |
Line 1030 npf_conn_import(npf_conndb_t *cd, prop_d |
|
Line 1044 npf_conn_import(npf_conndb_t *cd, prop_d |
|
const void *d; |
const void *d; |
|
|
/* Allocate a connection and initialise it (clear first). */ |
/* Allocate a connection and initialise it (clear first). */ |
con = pool_cache_get(conn_cache, PR_WAITOK); |
con = pool_cache_get(npf->conn_cache, PR_WAITOK); |
memset(con, 0, sizeof(npf_conn_t)); |
memset(con, 0, sizeof(npf_conn_t)); |
mutex_init(&con->c_lock, MUTEX_DEFAULT, IPL_SOFTNET); |
mutex_init(&con->c_lock, MUTEX_DEFAULT, IPL_SOFTNET); |
npf_stats_inc(NPF_STAT_CONN_CREATE); |
npf_stats_inc(npf, NPF_STAT_CONN_CREATE); |
|
|
prop_dictionary_get_uint32(cdict, "proto", &con->c_proto); |
prop_dictionary_get_uint32(cdict, "proto", &con->c_proto); |
prop_dictionary_get_uint32(cdict, "flags", &con->c_flags); |
prop_dictionary_get_uint32(cdict, "flags", &con->c_flags); |
con->c_flags &= PFIL_ALL | CONN_ACTIVE | CONN_PASS; |
con->c_flags &= PFIL_ALL | CONN_ACTIVE | CONN_PASS; |
getnanouptime(&con->c_atime); |
conn_update_atime(con); |
|
|
if (prop_dictionary_get_cstring_nocopy(cdict, "ifname", &ifname) && |
if (prop_dictionary_get_cstring_nocopy(cdict, "ifname", &ifname) && |
(con->c_ifid = npf_ifmap_register(ifname)) == 0) { |
(con->c_ifid = npf_ifmap_register(npf, ifname)) == 0) { |
goto err; |
goto err; |
} |
} |
|
|
Line 1054 npf_conn_import(npf_conndb_t *cd, prop_d |
|
Line 1068 npf_conn_import(npf_conndb_t *cd, prop_d |
|
|
|
/* Reconstruct NAT association, if any. */ |
/* Reconstruct NAT association, if any. */ |
if ((obj = prop_dictionary_get(cdict, "nat")) != NULL && |
if ((obj = prop_dictionary_get(cdict, "nat")) != NULL && |
(con->c_nat = npf_nat_import(obj, natlist, con)) == NULL) { |
(con->c_nat = npf_nat_import(npf, obj, natlist, con)) == NULL) { |
goto err; |
goto err; |
} |
} |
|
|
Line 1088 npf_conn_import(npf_conndb_t *cd, prop_d |
|
Line 1102 npf_conn_import(npf_conndb_t *cd, prop_d |
|
npf_conndb_enqueue(cd, con); |
npf_conndb_enqueue(cd, con); |
return 0; |
return 0; |
err: |
err: |
npf_conn_destroy(con); |
npf_conn_destroy(npf, con); |
return EINVAL; |
return EINVAL; |
} |
} |
|
|
int |
int |
npf_conn_find(prop_dictionary_t idict, prop_dictionary_t *odict) |
npf_conn_find(npf_t *npf, prop_dictionary_t idict, prop_dictionary_t *odict) |
{ |
{ |
|
prop_dictionary_t kdict; |
npf_connkey_t key; |
npf_connkey_t key; |
npf_conn_t *con; |
npf_conn_t *con; |
uint16_t dir; |
uint16_t dir; |
bool forw; |
bool forw; |
prop_dictionary_t kdict; |
|
|
|
if ((kdict = prop_dictionary_get(idict, "key")) == NULL) |
if ((kdict = prop_dictionary_get(idict, "key")) == NULL) |
return EINVAL; |
return EINVAL; |
Line 1110 npf_conn_find(prop_dictionary_t idict, p |
|
Line 1124 npf_conn_find(prop_dictionary_t idict, p |
|
if (!prop_dictionary_get_uint16(idict, "direction", &dir)) |
if (!prop_dictionary_get_uint16(idict, "direction", &dir)) |
return EINVAL; |
return EINVAL; |
|
|
con = npf_conndb_lookup(conn_db, &key, &forw); |
con = npf_conndb_lookup(npf->conn_db, &key, &forw); |
if (con == NULL) { |
if (con == NULL) { |
return ESRCH; |
return ESRCH; |
} |
} |
Line 1120 npf_conn_find(prop_dictionary_t idict, p |
|
Line 1134 npf_conn_find(prop_dictionary_t idict, p |
|
return ESRCH; |
return ESRCH; |
} |
} |
|
|
*odict = npf_conn_export(con); |
*odict = npf_conn_export(npf, con); |
if (*odict == NULL) { |
if (*odict == NULL) { |
atomic_dec_uint(&con->c_refcnt); |
atomic_dec_uint(&con->c_refcnt); |
return ENOSPC; |
return ENOSPC; |
Line 1139 npf_conn_print(const npf_conn_t *con) |
|
Line 1153 npf_conn_print(const npf_conn_t *con) |
|
const uint32_t *fkey = con->c_forw_entry.ck_key; |
const uint32_t *fkey = con->c_forw_entry.ck_key; |
const uint32_t *bkey = con->c_back_entry.ck_key; |
const uint32_t *bkey = con->c_back_entry.ck_key; |
const u_int proto = con->c_proto; |
const u_int proto = con->c_proto; |
struct timespec tsnow, tsdiff; |
struct timespec tspnow; |
const void *src, *dst; |
const void *src, *dst; |
int etime; |
int etime; |
|
|
getnanouptime(&tsnow); |
getnanouptime(&tspnow); |
timespecsub(&tsnow, &con->c_atime, &tsdiff); |
|
etime = npf_state_etime(&con->c_state, proto); |
etime = npf_state_etime(&con->c_state, proto); |
|
|
printf("%p:\n\tproto %d flags 0x%x tsdiff %d etime %d\n", |
printf("%p:\n\tproto %d flags 0x%x tsdiff %ld etime %d\n", con, |
con, proto, con->c_flags, (int)tsdiff.tv_sec, etime); |
proto, con->c_flags, (long)(tspnow.tv_sec - con->c_atime), etime); |
|
|
src = &fkey[2], dst = &fkey[2 + (alen >> 2)]; |
src = &fkey[2], dst = &fkey[2 + (alen >> 2)]; |
printf("\tforw %s:%d", npf_addr_dump(src, alen), ntohs(fkey[1] >> 16)); |
printf("\tforw %s:%d", npf_addr_dump(src, alen), ntohs(fkey[1] >> 16)); |