- npf_conn_establish: remove a rare race condition when we might destroy a
connection when it is still referenced by another thread. - npf_conn_destroy: remove the backwards entry using the saved key, PR/49488. - Sprinkle some asserts.
This commit is contained in:
parent
22831f5cba
commit
518c0b96b2
@ -1,7 +1,7 @@
|
||||
/* $NetBSD: npf_conn.c,v 1.14 2014/12/20 16:19:43 rmind Exp $ */
|
||||
/* $NetBSD: npf_conn.c,v 1.15 2015/02/01 22:41:22 rmind Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2014 Mindaugas Rasiukevicius <rmind at netbsd org>
|
||||
* Copyright (c) 2014-2015 Mindaugas Rasiukevicius <rmind at netbsd org>
|
||||
* Copyright (c) 2010-2014 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
@ -99,7 +99,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.14 2014/12/20 16:19:43 rmind Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.15 2015/02/01 22:41:22 rmind Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
@ -369,7 +369,6 @@ npf_conn_lookup(const npf_cache_t *npc, const int di, bool *forw)
|
||||
/* Check if connection is active and not expired. */
|
||||
flags = con->c_flags;
|
||||
ok = (flags & (CONN_ACTIVE | CONN_EXPIRE)) == CONN_ACTIVE;
|
||||
|
||||
if (__predict_false(!ok)) {
|
||||
atomic_dec_uint(&con->c_refcnt);
|
||||
return NULL;
|
||||
@ -453,6 +452,7 @@ npf_conn_establish(npf_cache_t *npc, int di, bool per_if)
|
||||
{
|
||||
const nbuf_t *nbuf = npc->npc_nbuf;
|
||||
npf_conn_t *con;
|
||||
int error = 0;
|
||||
|
||||
KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
|
||||
|
||||
@ -468,16 +468,16 @@ npf_conn_establish(npf_cache_t *npc, int di, bool per_if)
|
||||
NPF_PRINTF(("NPF: create conn %p\n", con));
|
||||
npf_stats_inc(NPF_STAT_CONN_CREATE);
|
||||
|
||||
/* Reference count and flags (indicate direction). */
|
||||
mutex_init(&con->c_lock, MUTEX_DEFAULT, IPL_SOFTNET);
|
||||
con->c_flags = (di & PFIL_ALL);
|
||||
con->c_refcnt = 1;
|
||||
con->c_refcnt = 0;
|
||||
con->c_rproc = NULL;
|
||||
con->c_nat = NULL;
|
||||
|
||||
/* Initialize protocol state. */
|
||||
/* Initialize the protocol state. */
|
||||
if (!npf_state_init(npc, &con->c_state)) {
|
||||
goto err;
|
||||
npf_conn_destroy(con);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
KASSERT(npf_iscached(npc, NPC_IP46));
|
||||
@ -488,45 +488,65 @@ npf_conn_establish(npf_cache_t *npc, int di, bool per_if)
|
||||
* Construct "forwards" and "backwards" keys. Also, set the
|
||||
* interface ID for this connection (unless it is global).
|
||||
*/
|
||||
if (!npf_conn_conkey(npc, fw, true)) {
|
||||
goto err;
|
||||
}
|
||||
if (!npf_conn_conkey(npc, bk, false)) {
|
||||
goto err;
|
||||
if (!npf_conn_conkey(npc, fw, true) ||
|
||||
!npf_conn_conkey(npc, bk, false)) {
|
||||
npf_conn_destroy(con);
|
||||
return NULL;
|
||||
}
|
||||
fw->ck_backptr = bk->ck_backptr = con;
|
||||
con->c_ifid = per_if ? nbuf->nb_ifid : 0;
|
||||
con->c_proto = npc->npc_proto;
|
||||
|
||||
/* Set last activity time for a new connection. */
|
||||
/*
|
||||
* Set last activity time for a new connection and acquire
|
||||
* a reference for the caller before we make it visible.
|
||||
*/
|
||||
getnanouptime(&con->c_atime);
|
||||
con->c_refcnt = 1;
|
||||
|
||||
/*
|
||||
* Insert both keys (entries representing directions) of the
|
||||
* connection. At this point, it becomes visible.
|
||||
* connection. At this point it becomes visible, but we activate
|
||||
* the connection later.
|
||||
*/
|
||||
mutex_enter(&con->c_lock);
|
||||
if (!npf_conndb_insert(conn_db, fw, con)) {
|
||||
error = EISCONN;
|
||||
goto err;
|
||||
}
|
||||
if (!npf_conndb_insert(conn_db, bk, con)) {
|
||||
/* We have hit the duplicate. */
|
||||
npf_conndb_remove(conn_db, fw);
|
||||
npf_stats_inc(NPF_STAT_RACE_CONN);
|
||||
npf_conn_t *ret __diagused;
|
||||
ret = npf_conndb_remove(conn_db, fw);
|
||||
KASSERT(ret == con);
|
||||
error = EISCONN;
|
||||
goto err;
|
||||
}
|
||||
err:
|
||||
/*
|
||||
* If we have hit the duplicate: mark the connection as expired
|
||||
* and let the G/C thread to take care of it. We cannot do it
|
||||
* here since there might be references acquired already.
|
||||
*/
|
||||
if (error) {
|
||||
const u_int dflags = CONN_REMOVED | CONN_EXPIRE;
|
||||
atomic_or_uint(&con->c_flags, dflags);
|
||||
npf_stats_inc(NPF_STAT_RACE_CONN);
|
||||
} else {
|
||||
NPF_PRINTF(("NPF: establish conn %p\n", con));
|
||||
}
|
||||
|
||||
/* Finally, insert into the connection list. */
|
||||
NPF_PRINTF(("NPF: establish conn %p\n", con));
|
||||
npf_conndb_enqueue(conn_db, con);
|
||||
return con;
|
||||
err:
|
||||
npf_conn_destroy(con);
|
||||
return NULL;
|
||||
mutex_exit(&con->c_lock);
|
||||
|
||||
return error ? NULL : con;
|
||||
}
|
||||
|
||||
static void
|
||||
npf_conn_destroy(npf_conn_t *con)
|
||||
{
|
||||
KASSERT(con->c_refcnt == 0);
|
||||
|
||||
if (con->c_nat) {
|
||||
/* Release any NAT structures. */
|
||||
npf_nat_destroy(con->c_nat);
|
||||
@ -582,6 +602,8 @@ npf_conn_setnat(const npf_cache_t *npc, npf_conn_t *con,
|
||||
mutex_exit(&con->c_lock);
|
||||
return EINVAL;
|
||||
}
|
||||
KASSERT((con->c_flags & CONN_REMOVED) == 0);
|
||||
|
||||
if (__predict_false(con->c_nat != NULL)) {
|
||||
/* Race with a duplicate packet. */
|
||||
mutex_exit(&con->c_lock);
|
||||
@ -590,7 +612,7 @@ npf_conn_setnat(const npf_cache_t *npc, npf_conn_t *con,
|
||||
}
|
||||
|
||||
/* Remove the "backwards" entry. */
|
||||
ret = npf_conndb_remove(conn_db, &key);
|
||||
ret = npf_conndb_remove(conn_db, &con->c_back_entry);
|
||||
KASSERT(ret == con);
|
||||
|
||||
/* Set the source/destination IDs to the translation values. */
|
||||
@ -606,7 +628,9 @@ npf_conn_setnat(const npf_cache_t *npc, npf_conn_t *con,
|
||||
* Race: we have hit the duplicate, remove the "forwards"
|
||||
* entry and expire our connection; it is no longer valid.
|
||||
*/
|
||||
(void)npf_conndb_remove(conn_db, &con->c_forw_entry);
|
||||
ret = npf_conndb_remove(conn_db, &con->c_forw_entry);
|
||||
KASSERT(ret == con);
|
||||
|
||||
atomic_or_uint(&con->c_flags, CONN_REMOVED | CONN_EXPIRE);
|
||||
mutex_exit(&con->c_lock);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user