Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/net/npf - npf_do_nat: fix a race condition and simplify ...



details:   https://anonhg.NetBSD.org/src/rev/c3e99c8b2b42
branches:  trunk
changeset: 325081:c3e99c8b2b42
user:      rmind <rmind%NetBSD.org@localhost>
date:      Wed Dec 04 01:38:49 2013 +0000

description:
- npf_do_nat: fix a race condition and simplify the logic.
- npf_session_setnat: clear the NAT association on failure.

diffstat:

 sys/net/npf/npf_impl.h    |    6 +-
 sys/net/npf/npf_nat.c     |  107 +++++++++++++++++++++------------------------
 sys/net/npf/npf_ruleset.c |   12 ++--
 sys/net/npf/npf_session.c |    9 ++-
 4 files changed, 63 insertions(+), 71 deletions(-)

diffs (truncated from 347 to 300 lines):

diff -r 72ae3f3bdc19 -r c3e99c8b2b42 sys/net/npf/npf_impl.h
--- a/sys/net/npf/npf_impl.h    Wed Dec 04 01:03:15 2013 +0000
+++ b/sys/net/npf/npf_impl.h    Wed Dec 04 01:38:49 2013 +0000
@@ -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 @@
 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 @@
 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);
diff -r 72ae3f3bdc19 -r c3e99c8b2b42 sys/net/npf/npf_nat.c
--- a/sys/net/npf/npf_nat.c     Wed Dec 04 01:03:15 2013 +0000
+++ b/sys/net/npf/npf_nat.c     Wed Dec 04 01:38:49 2013 +0000
@@ -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_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 @@
        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_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 @@
        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_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 @@
         */
        if (se && (nt = npf_session_retnat(se, di, &forw)) != NULL) {
                np = nt->nt_natpolicy;
-               new = false;
                goto translate;
        }
 
@@ -635,22 +636,6 @@
        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 @@
        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);
-       if (error) {
+
+       /*
+        * 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;
        }
 
-       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);
+       /* 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;
+       }
+
+       /* 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 @@
 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 @@
 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_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);
 }
diff -r 72ae3f3bdc19 -r c3e99c8b2b42 sys/net/npf/npf_ruleset.c
--- a/sys/net/npf/npf_ruleset.c Wed Dec 04 01:03:15 2013 +0000
+++ b/sys/net/npf/npf_ruleset.c Wed Dec 04 01:38:49 2013 +0000
@@ -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_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_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_rproc_t *
-npf_rule_getrproc(npf_rule_t *rl)
+npf_rule_getrproc(const npf_rule_t *rl)



Home | Main Index | Thread Index | Old Index