Module Name: src Committed By: rmind Date: Wed Dec 4 01:38:49 UTC 2013
Modified Files: src/sys/net/npf: npf_impl.h npf_nat.c npf_ruleset.c npf_session.c Log Message: - npf_do_nat: fix a race condition and simplify the logic. - npf_session_setnat: clear the NAT association on failure. To generate a diff of this commit: cvs rdiff -u -r1.43 -r1.44 src/sys/net/npf/npf_impl.h cvs rdiff -u -r1.21 -r1.22 src/sys/net/npf/npf_nat.c cvs rdiff -u -r1.29 -r1.30 src/sys/net/npf/npf_ruleset.c cvs rdiff -u -r1.28 -r1.29 src/sys/net/npf/npf_session.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/npf/npf_impl.h diff -u src/sys/net/npf/npf_impl.h:1.43 src/sys/net/npf/npf_impl.h:1.44 --- src/sys/net/npf/npf_impl.h:1.43 Sat Nov 23 19:32:20 2013 +++ src/sys/net/npf/npf_impl.h Wed Dec 4 01:38:49 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_impl.h,v 1.43 2013/11/23 19:32:20 rmind Exp $ */ +/* $NetBSD: npf_impl.h,v 1.44 2013/12/04 01:38:49 rmind Exp $ */ /*- * Copyright (c) 2009-2013 The NetBSD Foundation, Inc. @@ -267,7 +267,7 @@ void npf_rule_free(npf_rule_t *); uint64_t npf_rule_getid(const npf_rule_t *); npf_natpolicy_t *npf_rule_getnat(const npf_rule_t *); void npf_rule_setnat(npf_rule_t *, npf_natpolicy_t *); -npf_rproc_t * npf_rule_getrproc(npf_rule_t *); +npf_rproc_t * npf_rule_getrproc(const npf_rule_t *); void npf_ext_sysinit(void); void npf_ext_sysfini(void); @@ -329,7 +329,7 @@ void npf_nat_freealg(npf_natpolicy_t *, int npf_do_nat(npf_cache_t *, npf_session_t *, nbuf_t *, const int); int npf_nat_translate(npf_cache_t *, nbuf_t *, npf_nat_t *, const bool, const int); -void npf_nat_expire(npf_nat_t *); +void npf_nat_destroy(npf_nat_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); Index: src/sys/net/npf/npf_nat.c diff -u src/sys/net/npf/npf_nat.c:1.21 src/sys/net/npf/npf_nat.c:1.22 --- src/sys/net/npf/npf_nat.c:1.21 Tue Oct 29 16:39:10 2013 +++ src/sys/net/npf/npf_nat.c Wed Dec 4 01:38:49 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_nat.c,v 1.21 2013/10/29 16:39:10 rmind Exp $ */ +/* $NetBSD: npf_nat.c,v 1.22 2013/12/04 01:38:49 rmind Exp $ */ /*- * Copyright (c) 2010-2013 The NetBSD Foundation, Inc. @@ -76,7 +76,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.21 2013/10/29 16:39:10 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.22 2013/12/04 01:38:49 rmind Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -246,13 +246,14 @@ npf_nat_freepolicy(npf_natpolicy_t *np) npf_session_t *se; npf_nat_t *nt; - /* De-associate all entries from the policy. */ + /* + * Disassociate all entries from the policy. At this point, + * new entries can no longer be created for this policy. + */ mutex_enter(&np->n_lock); LIST_FOREACH(nt, &np->n_nat_list, nt_entry) { - se = nt->nt_session; /* XXXSMP */ - if (se == NULL) { - continue; - } + se = nt->nt_session; + KASSERT(se != NULL); npf_session_expire(se); } while (!LIST_EMPTY(&np->n_nat_list)) { @@ -265,6 +266,7 @@ npf_nat_freepolicy(npf_natpolicy_t *np) while (np->n_refcnt) { kpause("npfgcnat", false, 1, NULL); } + KASSERT(LIST_EMPTY(&np->n_nat_list)); /* Destroy the port map, on last reference. */ if (pm && --pm->p_refcnt == 0) { @@ -449,7 +451,7 @@ npf_nat_inspect(npf_cache_t *npc, nbuf_t * npf_nat_create: create a new NAT translation entry. */ static npf_nat_t * -npf_nat_create(npf_cache_t *npc, npf_natpolicy_t *np) +npf_nat_create(npf_cache_t *npc, npf_natpolicy_t *np, npf_session_t *se) { const int proto = npc->npc_proto; npf_nat_t *nt; @@ -457,14 +459,14 @@ npf_nat_create(npf_cache_t *npc, npf_nat KASSERT(npf_iscached(npc, NPC_IP46)); KASSERT(npf_iscached(npc, NPC_LAYER4)); - /* New NAT association. */ + /* Construct a new NAT entry and associate it with the session. */ nt = pool_cache_get(nat_cache, PR_NOWAIT); if (nt == NULL){ return NULL; } npf_stats_inc(NPF_STAT_NAT_CREATE); nt->nt_natpolicy = np; - nt->nt_session = NULL; + nt->nt_session = se; nt->nt_alg = NULL; /* Save the original address which may be rewritten. */ @@ -604,7 +606,7 @@ npf_do_nat(npf_cache_t *npc, npf_session npf_natpolicy_t *np; npf_nat_t *nt; int error; - bool forw, new; + bool forw; /* All relevant IPv4 data should be already cached. */ if (!npf_iscached(npc, NPC_IP46) || !npf_iscached(npc, NPC_LAYER4)) { @@ -619,7 +621,6 @@ npf_do_nat(npf_cache_t *npc, npf_session */ if (se && (nt = npf_session_retnat(se, di, &forw)) != NULL) { np = nt->nt_natpolicy; - new = false; goto translate; } @@ -635,22 +636,6 @@ npf_do_nat(npf_cache_t *npc, npf_session forw = true; /* - * Create a new NAT entry (not yet associated with any session). - * We will consume the reference on success (release on error). - */ - nt = npf_nat_create(npc, np); - if (nt == NULL) { - atomic_dec_uint(&np->n_refcnt); - return ENOMEM; - } - new = true; - - /* Determine whether any ALG matches. */ - if (npf_alg_match(npc, nbuf, nt, di)) { - KASSERT(nt->nt_alg != NULL); - } - - /* * If there is no local session (no "stateful" rule - unusual, but * possible configuration), establish one before translation. Note * that it is not a "pass" session, therefore passing of "backwards" @@ -659,37 +644,46 @@ npf_do_nat(npf_cache_t *npc, npf_session if (se == NULL) { nse = npf_session_establish(npc, nbuf, di); if (nse == NULL) { - error = ENOMEM; - goto out; + atomic_dec_uint(&np->n_refcnt); + return ENOMEM; } se = nse; } -translate: - /* Perform the translation. */ - error = npf_nat_translate(npc, nbuf, nt, forw, di); + + /* + * Create a new NAT entry and associate with the session. + * We will consume the reference on success (release on error). + */ + nt = npf_nat_create(npc, np, se); + if (nt == NULL) { + atomic_dec_uint(&np->n_refcnt); + error = ENOMEM; + goto out; + } + + /* Associate the NAT translation entry with the session. */ + error = npf_session_setnat(se, nt, np->n_type); if (error) { + /* Will release the reference. */ + npf_nat_destroy(nt); goto out; } - if (__predict_false(new)) { - /* - * Associate NAT translation entry with the session. - * Note: packet now has a translated address in the cache. - */ - nt->nt_session = se; - error = npf_session_setnat(se, nt, np->n_type); + /* Determine whether any ALG matches. */ + if (npf_alg_match(npc, nbuf, nt, di)) { + KASSERT(nt->nt_alg != NULL); + } + +translate: + /* Perform the translation. */ + error = npf_nat_translate(npc, nbuf, nt, forw, di); out: - if (error) { - /* If session was for NAT only - expire it. */ - if (nse) { - npf_session_expire(nse); - } - /* Will free the structure and return the port. */ - npf_nat_expire(nt); - } - if (nse) { - npf_session_release(nse); - } + if (error && nse) { + /* It created for NAT - just expire. */ + npf_session_expire(nse); + } + if (nse) { + npf_session_release(nse); } return error; } @@ -712,7 +706,6 @@ npf_nat_gettrans(npf_nat_t *nt, npf_addr void npf_nat_getorig(npf_nat_t *nt, npf_addr_t **addr, in_port_t *port) { - *addr = &nt->nt_oaddr; *port = nt->nt_oport; } @@ -723,16 +716,15 @@ npf_nat_getorig(npf_nat_t *nt, npf_addr_ void npf_nat_setalg(npf_nat_t *nt, npf_alg_t *alg, uintptr_t arg) { - nt->nt_alg = alg; nt->nt_alg_arg = arg; } /* - * npf_nat_expire: free NAT-related data structures on session expiration. + * npf_nat_destroy: destroy NAT structure (performed on session expiration). */ void -npf_nat_expire(npf_nat_t *nt) +npf_nat_destroy(npf_nat_t *nt) { npf_natpolicy_t *np = nt->nt_natpolicy; @@ -741,16 +733,15 @@ npf_nat_expire(npf_nat_t *nt) 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)) { + /* Notify any waiters if empty. */ cv_broadcast(&np->n_cv); } atomic_dec_uint(&np->n_refcnt); mutex_exit(&np->n_lock); - /* Free structure, increase the counter. */ pool_cache_put(nat_cache, nt); npf_stats_inc(NPF_STAT_NAT_DESTROY); } Index: src/sys/net/npf/npf_ruleset.c diff -u src/sys/net/npf/npf_ruleset.c:1.29 src/sys/net/npf/npf_ruleset.c:1.30 --- src/sys/net/npf/npf_ruleset.c:1.29 Sat Nov 23 19:32:20 2013 +++ src/sys/net/npf/npf_ruleset.c Wed Dec 4 01:38:49 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_ruleset.c,v 1.29 2013/11/23 19:32:20 rmind Exp $ */ +/* $NetBSD: npf_ruleset.c,v 1.30 2013/12/04 01:38:49 rmind Exp $ */ /*- * Copyright (c) 2009-2013 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.29 2013/11/23 19:32:20 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.30 2013/12/04 01:38:49 rmind Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -469,10 +469,8 @@ npf_ruleset_freealg(npf_ruleset_t *rlset } /* - * npf_ruleset_natreload: minimum reload of NAT policies by maching + * npf_ruleset_natreload: minimum reload of NAT policies by matching * two (active and new) NAT rulesets. - * - * => Active ruleset should be exclusively locked. */ void npf_ruleset_natreload(npf_ruleset_t *nrlset, npf_ruleset_t *arlset) @@ -480,6 +478,8 @@ npf_ruleset_natreload(npf_ruleset_t *nrl npf_natpolicy_t *np, *anp; npf_rule_t *rl, *arl; + KASSERT(npf_config_locked_p()); + /* Scan a new NAT ruleset against NAT policies in old ruleset. */ LIST_FOREACH(rl, &nrlset->rs_all, r_aentry) { np = rl->r_natp; @@ -629,7 +629,7 @@ npf_rule_getid(const npf_rule_t *rl) } npf_rproc_t * -npf_rule_getrproc(npf_rule_t *rl) +npf_rule_getrproc(const npf_rule_t *rl) { npf_rproc_t *rp = rl->r_rproc; Index: src/sys/net/npf/npf_session.c diff -u src/sys/net/npf/npf_session.c:1.28 src/sys/net/npf/npf_session.c:1.29 --- src/sys/net/npf/npf_session.c:1.28 Fri Nov 22 01:24:21 2013 +++ src/sys/net/npf/npf_session.c Wed Dec 4 01:38:49 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_session.c,v 1.28 2013/11/22 01:24:21 rmind Exp $ */ +/* $NetBSD: npf_session.c,v 1.29 2013/12/04 01:38:49 rmind Exp $ */ /*- * Copyright (c) 2010-2013 The NetBSD Foundation, Inc. @@ -92,7 +92,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_session.c,v 1.28 2013/11/22 01:24:21 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_session.c,v 1.29 2013/12/04 01:38:49 rmind Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -701,7 +701,7 @@ npf_session_destroy(npf_session_t *se) { if (se->s_nat) { /* Release any NAT related structures. */ - npf_nat_expire(se->s_nat); + npf_nat_destroy(se->s_nat); } if (se->s_rproc) { /* Release rule procedure. */ @@ -788,6 +788,7 @@ npf_session_setnat(npf_session_t *se, np /* Race: mark a removed entry and explicitly expire. */ atomic_or_uint(&se->s_flags, SE_REMBACK | SE_EXPIRE); npf_stats_inc(NPF_STAT_RACE_NAT); + se->s_nat = NULL; } rw_exit(&sh->sh_lock); return ok ? 0 : EISCONN; @@ -845,7 +846,7 @@ void npf_session_release(npf_session_t *se) { KASSERT(se->s_refcnt > 0); - if ((se->s_flags & SE_ACTIVE) == 0) { + if ((se->s_flags & (SE_ACTIVE | SE_EXPIRE)) == 0) { /* Activate: after this point, session is globally visible. */ se->s_flags |= SE_ACTIVE; }