Source-Changes-HG archive

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

[src/trunk]: src Backport selected NPF fixes from the upstream (to be pulled ...



details:   https://anonhg.NetBSD.org/src/rev/b064114f8a2d
branches:  trunk
changeset: 933274:b064114f8a2d
user:      rmind <rmind%NetBSD.org@localhost>
date:      Sat May 23 19:56:00 2020 +0000

description:
Backport selected NPF fixes from the upstream (to be pulled up):

- npf_conndb_lookup: protect the connection lookup with pserialize(9),
  instead of incorrectly assuming that the handler always runs at IPL_SOFNET.
  Should fix crashes reported on high load (PR/55182).

- npf_config_destroy: handle partially initialized config; fixes crashes
  with some invalid configurations.

- NAT policy creation / destruction: set the initial reference and do not
  wait for reference draining on destruction; destroy the policy on the
  last reference drop instead.  Fixes a lockup with the dynamic NAT rules.

- npf_nat_{export,import}: fix a regression since dynamic NAT rules.

- npfctl: fix a regression and restore the default group behaviour.

- Add npf_cache_tcp() and validate the TCP data offset (from maxv@).

diffstat:

 sys/net/npf/npf_conf.c          |  18 +++++--
 sys/net/npf/npf_conn.c          |   6 +-
 sys/net/npf/npf_conn.h          |   2 +-
 sys/net/npf/npf_conndb.c        |   8 ++-
 sys/net/npf/npf_inet.c          |  22 +++++++-
 sys/net/npf/npf_nat.c           |  93 ++++++++++++++++++++++++++++------------
 usr.sbin/npf/npfctl/npf_build.c |  48 +++++++++++++++++---
 usr.sbin/npf/npfctl/npf_show.c  |   4 +-
 usr.sbin/npf/npfctl/npfctl.h    |   1 +
 9 files changed, 149 insertions(+), 53 deletions(-)

diffs (truncated from 506 to 300 lines):

diff -r 71ce3e87889e -r b064114f8a2d sys/net/npf/npf_conf.c
--- a/sys/net/npf/npf_conf.c    Sat May 23 19:52:12 2020 +0000
+++ b/sys/net/npf/npf_conf.c    Sat May 23 19:56:00 2020 +0000
@@ -47,7 +47,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_conf.c,v 1.15 2019/08/25 13:21:03 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_conf.c,v 1.16 2020/05/23 19:56:00 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -94,10 +94,18 @@
         * Note: the rulesets must be destroyed first, in order to drop
         * any references to the tableset.
         */
-       npf_ruleset_destroy(nc->ruleset);
-       npf_ruleset_destroy(nc->nat_ruleset);
-       npf_rprocset_destroy(nc->rule_procs);
-       npf_tableset_destroy(nc->tableset);
+       if (nc->ruleset) {
+               npf_ruleset_destroy(nc->ruleset);
+       }
+       if (nc->nat_ruleset) {
+               npf_ruleset_destroy(nc->nat_ruleset);
+       }
+       if (nc->rule_procs) {
+               npf_rprocset_destroy(nc->rule_procs);
+       }
+       if (nc->tableset) {
+               npf_tableset_destroy(nc->tableset);
+       }
        kmem_free(nc, sizeof(npf_config_t));
 }
 
diff -r 71ce3e87889e -r b064114f8a2d sys/net/npf/npf_conn.c
--- a/sys/net/npf/npf_conn.c    Sat May 23 19:52:12 2020 +0000
+++ b/sys/net/npf/npf_conn.c    Sat May 23 19:56:00 2020 +0000
@@ -107,7 +107,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.30 2019/09/29 17:00:29 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.31 2020/05/23 19:56:00 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -311,7 +311,7 @@
        if (!npf_conn_conkey(npc, &key, true)) {
                return NULL;
        }
-       con = npf_conndb_lookup(npf->conn_db, &key, forw);
+       con = npf_conndb_lookup(npf, &key, forw);
        if (con == NULL) {
                return NULL;
        }
@@ -908,7 +908,7 @@
        if (!kdict || !npf_connkey_import(kdict, &key)) {
                return EINVAL;
        }
-       con = npf_conndb_lookup(npf->conn_db, &key, &forw);
+       con = npf_conndb_lookup(npf, &key, &forw);
        if (con == NULL) {
                return ESRCH;
        }
diff -r 71ce3e87889e -r b064114f8a2d sys/net/npf/npf_conn.h
--- a/sys/net/npf/npf_conn.h    Sat May 23 19:52:12 2020 +0000
+++ b/sys/net/npf/npf_conn.h    Sat May 23 19:56:00 2020 +0000
@@ -157,7 +157,7 @@
 npf_conndb_t * npf_conndb_create(void);
 void           npf_conndb_destroy(npf_conndb_t *);
 
-npf_conn_t *   npf_conndb_lookup(npf_conndb_t *, const npf_connkey_t *, bool *);
+npf_conn_t *   npf_conndb_lookup(npf_t *, const npf_connkey_t *, bool *);
 bool           npf_conndb_insert(npf_conndb_t *, const npf_connkey_t *,
                    npf_conn_t *, bool);
 npf_conn_t *   npf_conndb_remove(npf_conndb_t *, npf_connkey_t *);
diff -r 71ce3e87889e -r b064114f8a2d sys/net/npf/npf_conndb.c
--- a/sys/net/npf/npf_conndb.c  Sat May 23 19:52:12 2020 +0000
+++ b/sys/net/npf/npf_conndb.c  Sat May 23 19:56:00 2020 +0000
@@ -46,7 +46,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_conndb.c,v 1.7 2019/12/14 15:21:51 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_conndb.c,v 1.8 2020/05/23 19:56:00 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -143,8 +143,9 @@
  * npf_conndb_lookup: find a connection given the key.
  */
 npf_conn_t *
-npf_conndb_lookup(npf_conndb_t *cd, const npf_connkey_t *ck, bool *forw)
+npf_conndb_lookup(npf_t *npf, const npf_connkey_t *ck, bool *forw)
 {
+       npf_conndb_t *cd = atomic_load_relaxed(&npf->conn_db);
        const unsigned keylen = NPF_CONNKEY_LEN(ck);
        npf_conn_t *con;
        void *val;
@@ -152,8 +153,10 @@
        /*
         * Lookup the connection key in the key-value map.
         */
+       int s = npf_config_read_enter(npf);
        val = thmap_get(cd->cd_map, ck->ck_key, keylen);
        if (!val) {
+               npf_config_read_exit(npf, s);
                return NULL;
        }
 
@@ -169,6 +172,7 @@
         * Acquire a reference and return the connection.
         */
        atomic_inc_uint(&con->c_refcnt);
+       npf_config_read_exit(npf, s);
        return con;
 }
 
diff -r 71ce3e87889e -r b064114f8a2d sys/net/npf/npf_inet.c
--- a/sys/net/npf/npf_inet.c    Sat May 23 19:52:12 2020 +0000
+++ b/sys/net/npf/npf_inet.c    Sat May 23 19:56:00 2020 +0000
@@ -38,7 +38,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.55 2019/08/11 20:26:34 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.56 2020/05/23 19:56:00 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -562,6 +562,22 @@
        return flags;
 }
 
+static inline int
+npf_cache_tcp(npf_cache_t *npc, nbuf_t *nbuf, unsigned hlen)
+{
+       struct tcphdr *th;
+
+       th = nbuf_advance(nbuf, hlen, sizeof(struct tcphdr));
+       if (__predict_false(th == NULL)) {
+               return NPC_FMTERR;
+       }
+       if (__predict_false(th->th_off < 5)) {
+               return NPC_FMTERR;
+       }
+       npc->npc_l4.tcp = th;
+       return NPC_LAYER4 | NPC_TCP;
+}
+
 /*
  * npf_cache_all: general routine to cache all relevant IP (v4 or v6)
  * and TCP, UDP or ICMP headers.
@@ -601,9 +617,7 @@
        switch (npc->npc_proto) {
        case IPPROTO_TCP:
                /* Cache: layer 4 - TCP. */
-               npc->npc_l4.tcp = nbuf_advance(nbuf, hlen,
-                   sizeof(struct tcphdr));
-               l4flags = NPC_LAYER4 | NPC_TCP;
+               l4flags = npf_cache_tcp(npc, nbuf, hlen);
                break;
        case IPPROTO_UDP:
                /* Cache: layer 4 - UDP. */
diff -r 71ce3e87889e -r b064114f8a2d sys/net/npf/npf_nat.c
--- a/sys/net/npf/npf_nat.c     Sat May 23 19:52:12 2020 +0000
+++ b/sys/net/npf/npf_nat.c     Sat May 23 19:56:00 2020 +0000
@@ -67,7 +67,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.48 2019/08/25 13:21:03 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.49 2020/05/23 19:56:00 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -182,6 +182,7 @@
        size_t len;
 
        np = kmem_zalloc(sizeof(npf_natpolicy_t), KM_SLEEP);
+       atomic_store_relaxed(&np->n_refcnt, 1);
        np->n_npfctx = npf;
 
        /* The translation type, flags and policy ID. */
@@ -271,37 +272,53 @@
        return 0;
 }
 
+static void
+npf_natpolicy_release(npf_natpolicy_t *np)
+{
+       KASSERT(atomic_load_relaxed(&np->n_refcnt) > 0);
+
+       if (atomic_dec_uint_nv(&np->n_refcnt) != 0) {
+               return;
+       }
+       KASSERT(LIST_EMPTY(&np->n_nat_list));
+       mutex_destroy(&np->n_lock);
+       kmem_free(np, sizeof(npf_natpolicy_t));
+}
+
 /*
  * npf_nat_freepolicy: free the NAT policy.
  *
  * => Called from npf_rule_free() during the reload via npf_ruleset_destroy().
+ * => At this point, NAT policy cannot acquire new references.
  */
 void
 npf_nat_freepolicy(npf_natpolicy_t *np)
 {
-       npf_conn_t *con;
-       npf_nat_t *nt;
+       /*
+        * Drain the references.  If there are active NAT connections,
+        * then expire them and kick the worker.
+        */
+       if (atomic_load_relaxed(&np->n_refcnt) > 1) {
+               npf_nat_t *nt;
 
-       /*
-        * Disassociate all entries from the policy.  At this point,
-        * new entries can no longer be created for this policy.
-        */
-       while (np->n_refcnt) {
                mutex_enter(&np->n_lock);
                LIST_FOREACH(nt, &np->n_nat_list, nt_entry) {
-                       con = nt->nt_conn;
+                       npf_conn_t *con = nt->nt_conn;
                        KASSERT(con != NULL);
                        npf_conn_expire(con);
                }
                mutex_exit(&np->n_lock);
+               npf_worker_signal(np->n_npfctx);
+       }
+       KASSERT(atomic_load_relaxed(&np->n_refcnt) >= 1);
 
-               /* Kick the worker - all references should be going away. */
-               npf_worker_signal(np->n_npfctx);
-               kpause("npfgcnat", false, 1, NULL);
-       }
-       KASSERT(LIST_EMPTY(&np->n_nat_list));
-       mutex_destroy(&np->n_lock);
-       kmem_free(np, sizeof(npf_natpolicy_t));
+       /*
+        * Drop the initial reference, but it might not be the last one.
+        * If so, the last reference will be triggered via:
+        *
+        * npf_conn_destroy() -> npf_nat_destroy() -> npf_natpolicy_release()
+        */
+       npf_natpolicy_release(np);
 }
 
 void
@@ -649,7 +666,7 @@
                        npf_recache(npc);
                }
                error = npf_nat_algo(npc, np, forw);
-               atomic_dec_uint(&np->n_refcnt);
+               npf_natpolicy_release(np);
                return error;
        }
 
@@ -662,7 +679,7 @@
        if (con == NULL) {
                ncon = npf_conn_establish(npc, di, true);
                if (ncon == NULL) {
-                       atomic_dec_uint(&np->n_refcnt);
+                       npf_natpolicy_release(np);
                        return ENOMEM;
                }
                con = ncon;
@@ -674,7 +691,7 @@
         */
        nt = npf_nat_create(npc, np, con);
        if (nt == NULL) {
-               atomic_dec_uint(&np->n_refcnt);
+               npf_natpolicy_release(np);
                error = ENOMEM;
                goto out;
        }
@@ -757,11 +774,15 @@
        }
        npf_stats_inc(np->n_npfctx, NPF_STAT_NAT_DESTROY);
 
+       /*
+        * Remove the connection from the list and drop the reference on
+        * the NAT policy.  Note: this might trigger its destruction.
+        */
        mutex_enter(&np->n_lock);
        LIST_REMOVE(nt, nt_entry);
-       KASSERT(np->n_refcnt > 0);
-       atomic_dec_uint(&np->n_refcnt);
        mutex_exit(&np->n_lock);
+       npf_natpolicy_release(np);
+
        pool_cache_put(nat_cache, nt);
 }
 
@@ -772,10 +793,13 @@



Home | Main Index | Thread Index | Old Index