Module Name:    src
Committed By:   rmind
Date:           Fri May 30 23:26:06 UTC 2014

Modified Files:
        src/sys/net/npf: npf.h npf_conf.c npf_impl.h npf_nat.c npf_ruleset.c

Log Message:
- npf_nat_freepolicy: handle a race condition when a new connection might
  be associated with a NAT policy which is going away and npfctl reload
  would wait for its natural expiration (potentially long time).
- Remove npf_ruleset_natreload() by merging into npf_ruleset_reload().
- npf_ruleset_reload: eliminate a small time period when a valid NAT
  policy might be inactive during the reload operation.


To generate a diff of this commit:
cvs rdiff -u -r1.39 -r1.40 src/sys/net/npf/npf.h
cvs rdiff -u -r1.5 -r1.6 src/sys/net/npf/npf_conf.c
cvs rdiff -u -r1.51 -r1.52 src/sys/net/npf/npf_impl.h
cvs rdiff -u -r1.27 -r1.28 src/sys/net/npf/npf_nat.c
cvs rdiff -u -r1.30 -r1.31 src/sys/net/npf/npf_ruleset.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.h
diff -u src/sys/net/npf/npf.h:1.39 src/sys/net/npf/npf.h:1.40
--- src/sys/net/npf/npf.h:1.39	Mon May 19 18:45:51 2014
+++ src/sys/net/npf/npf.h	Fri May 30 23:26:06 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.h,v 1.39 2014/05/19 18:45:51 jakllsch Exp $	*/
+/*	$NetBSD: npf.h,v 1.40 2014/05/30 23:26:06 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
@@ -203,14 +203,14 @@ bool		npf_autounload_p(void);
 #endif	/* _KERNEL */
 
 /* Rule attributes. */
-#define	NPF_RULE_PASS			0x0001
-#define	NPF_RULE_GROUP			0x0002
-#define	NPF_RULE_FINAL			0x0004
-#define	NPF_RULE_STATEFUL		0x0008
-#define	NPF_RULE_RETRST			0x0010
-#define	NPF_RULE_RETICMP		0x0020
-#define	NPF_RULE_DYNAMIC		0x0040
-#define	NPF_RULE_MULTIENDS		0x0080
+#define	NPF_RULE_PASS			0x00000001
+#define	NPF_RULE_GROUP			0x00000002
+#define	NPF_RULE_FINAL			0x00000004
+#define	NPF_RULE_STATEFUL		0x00000008
+#define	NPF_RULE_RETRST			0x00000010
+#define	NPF_RULE_RETICMP		0x00000020
+#define	NPF_RULE_DYNAMIC		0x00000040
+#define	NPF_RULE_MULTIENDS		0x00000080
 
 #define	NPF_DYNAMIC_GROUP		(NPF_RULE_GROUP | NPF_RULE_DYNAMIC)
 
@@ -219,6 +219,9 @@ bool		npf_autounload_p(void);
 #define	NPF_RULE_DIMASK			(NPF_RULE_IN | NPF_RULE_OUT)
 #define	NPF_RULE_FORW			0x40000000
 
+/* Private range of rule attributes (not public and should not be set). */
+#define	NPF_RULE_PRIVMASK		0x0f000000
+
 #define	NPF_RULE_MAXNAMELEN		64
 #define	NPF_RULE_MAXKEYLEN		32
 

Index: src/sys/net/npf/npf_conf.c
diff -u src/sys/net/npf/npf_conf.c:1.5 src/sys/net/npf/npf_conf.c:1.6
--- src/sys/net/npf/npf_conf.c:1.5	Fri Nov 22 00:25:51 2013
+++ src/sys/net/npf/npf_conf.c	Fri May 30 23:26:06 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_conf.c,v 1.5 2013/11/22 00:25:51 rmind Exp $	*/
+/*	$NetBSD: npf_conf.c,v 1.6 2014/05/30 23:26:06 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2013 The NetBSD Foundation, Inc.
@@ -48,7 +48,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_conf.c,v 1.5 2013/11/22 00:25:51 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_conf.c,v 1.6 2014/05/30 23:26:06 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -146,7 +146,7 @@ npf_config_reload(prop_dictionary_t dict
 	if ((onc = npf_config) != NULL) {
 		npf_ruleset_reload(rset, onc->n_rules);
 		npf_tableset_reload(tset, onc->n_tables);
-		npf_ruleset_natreload(nset, onc->n_nat_rules);
+		npf_ruleset_reload(nset, onc->n_nat_rules);
 	}
 
 	/*

Index: src/sys/net/npf/npf_impl.h
diff -u src/sys/net/npf/npf_impl.h:1.51 src/sys/net/npf/npf_impl.h:1.52
--- src/sys/net/npf/npf_impl.h:1.51	Mon May 19 18:45:51 2014
+++ src/sys/net/npf/npf_impl.h	Fri May 30 23:26:06 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_impl.h,v 1.51 2014/05/19 18:45:51 jakllsch Exp $	*/
+/*	$NetBSD: npf_impl.h,v 1.52 2014/05/30 23:26:06 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
@@ -258,7 +258,6 @@ npf_ruleset_t *	npf_ruleset_create(size_
 void		npf_ruleset_destroy(npf_ruleset_t *);
 void		npf_ruleset_insert(npf_ruleset_t *, npf_rule_t *);
 void		npf_ruleset_reload(npf_ruleset_t *, npf_ruleset_t *);
-void		npf_ruleset_natreload(npf_ruleset_t *, npf_ruleset_t *);
 npf_rule_t *	npf_ruleset_matchnat(npf_ruleset_t *, npf_natpolicy_t *);
 npf_rule_t *	npf_ruleset_sharepm(npf_ruleset_t *, npf_natpolicy_t *);
 void		npf_ruleset_freealg(npf_ruleset_t *, npf_alg_t *);

Index: src/sys/net/npf/npf_nat.c
diff -u src/sys/net/npf/npf_nat.c:1.27 src/sys/net/npf/npf_nat.c:1.28
--- src/sys/net/npf/npf_nat.c:1.27	Fri Mar 14 11:29:44 2014
+++ src/sys/net/npf/npf_nat.c	Fri May 30 23:26:06 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_nat.c,v 1.27 2014/03/14 11:29:44 rmind Exp $	*/
+/*	$NetBSD: npf_nat.c,v 1.28 2014/05/30 23:26:06 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2014 Mindaugas Rasiukevicius <rmind at netbsd org>
@@ -71,7 +71,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.27 2014/03/14 11:29:44 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.28 2014/05/30 23:26:06 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -115,7 +115,6 @@ struct npf_natpolicy {
 	LIST_HEAD(, npf_nat)	n_nat_list;
 	volatile u_int		n_refcnt;
 	kmutex_t		n_lock;
-	kcondvar_t		n_cv;
 	npf_portmap_t *		n_portmap;
 	/* NPF_NP_CMP_START */
 	int			n_type;
@@ -137,18 +136,23 @@ struct npf_natpolicy {
  * NAT translation entry for a session.
  */
 struct npf_nat {
-	/* Association (list entry and a link pointer) with NAT policy. */
-	LIST_ENTRY(npf_nat)	nt_entry;
+	/* Associated NAT policy. */
 	npf_natpolicy_t *	nt_natpolicy;
-	npf_session_t *		nt_session;
-	/* Original address and port (for backwards translation). */
+
+	/*
+	 * Original address and port (for backwards translation).
+	 * Translation port (for redirects).
+	 */
 	npf_addr_t		nt_oaddr;
 	in_port_t		nt_oport;
-	/* Translation port (for redirects). */
 	in_port_t		nt_tport;
+
 	/* ALG (if any) associated with this NAT entry. */
 	npf_alg_t *		nt_alg;
 	uintptr_t		nt_alg_arg;
+
+	LIST_ENTRY(npf_nat)	nt_entry;
+	npf_session_t *		nt_session;
 };
 
 static pool_cache_t		nat_cache	__read_mostly;
@@ -195,7 +199,6 @@ npf_nat_newpolicy(prop_dictionary_t natd
 		goto err;
 	}
 	mutex_init(&np->n_lock, MUTEX_DEFAULT, IPL_SOFTNET);
-	cv_init(&np->n_cv, "npfnatcv");
 	LIST_INIT(&np->n_nat_list);
 
 	/* Translation IP, mask and port (if applicable). */
@@ -262,20 +265,17 @@ npf_nat_freepolicy(npf_natpolicy_t *np)
 	 * 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;
-		KASSERT(se != NULL);
-		npf_session_expire(se);
-	}
-	while (!LIST_EMPTY(&np->n_nat_list)) {
-		cv_wait(&np->n_cv, &np->n_lock);
-	}
-	mutex_exit(&np->n_lock);
-
-	/* Kick the worker - all references should be going away. */
-	npf_worker_signal();
 	while (np->n_refcnt) {
+		mutex_enter(&np->n_lock);
+		LIST_FOREACH(nt, &np->n_nat_list, nt_entry) {
+			se = nt->nt_session;
+			KASSERT(se != NULL);
+			npf_session_expire(se);
+		}
+		mutex_exit(&np->n_lock);
+
+		/* Kick the worker - all references should be going away. */
+		npf_worker_signal();
 		kpause("npfgcnat", false, 1, NULL);
 	}
 	KASSERT(LIST_EMPTY(&np->n_nat_list));
@@ -285,7 +285,6 @@ npf_nat_freepolicy(npf_natpolicy_t *np)
 		KASSERT((np->n_flags & NPF_NAT_PORTMAP) != 0);
 		kmem_free(pm, PORTMAP_MEM_SIZE);
 	}
-	cv_destroy(&np->n_cv);
 	mutex_destroy(&np->n_lock);
 	kmem_free(np, sizeof(npf_natpolicy_t));
 }
@@ -767,10 +766,6 @@ npf_nat_destroy(npf_nat_t *nt)
 
 	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);
 

Index: src/sys/net/npf/npf_ruleset.c
diff -u src/sys/net/npf/npf_ruleset.c:1.30 src/sys/net/npf/npf_ruleset.c:1.31
--- src/sys/net/npf/npf_ruleset.c:1.30	Wed Dec  4 01:38:49 2013
+++ src/sys/net/npf/npf_ruleset.c	Fri May 30 23:26:06 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ruleset.c,v 1.30 2013/12/04 01:38:49 rmind Exp $	*/
+/*	$NetBSD: npf_ruleset.c,v 1.31 2014/05/30 23:26:06 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.30 2013/12/04 01:38:49 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.31 2014/05/30 23:26:06 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -117,6 +117,11 @@ struct npf_rule {
 	uint8_t			r_key[NPF_RULE_MAXKEYLEN];
 };
 
+/*
+ * Private attributes - must be in the NPF_RULE_PRIVMASK range.
+ */
+#define	NPF_RULE_KEEPNAT	(0x01000000 & NPF_RULE_PRIVMASK)
+
 #define	NPF_DYNAMIC_GROUP_P(attr) \
     (((attr) & NPF_DYNAMIC_GROUP) == NPF_DYNAMIC_GROUP)
 
@@ -374,21 +379,27 @@ npf_ruleset_gc(npf_ruleset_t *rlset)
 }
 
 /*
- * npf_ruleset_reload: share the dynamic rules.
+ * npf_ruleset_reload: prepare the new ruleset by scanning the active
+ * ruleset and 1) sharing the dynamic rules 2) sharing NAT policies.
  *
- * => Active ruleset should be exclusively locked.
+ * => The active (old) ruleset should be exclusively locked.
  */
 void
-npf_ruleset_reload(npf_ruleset_t *rlset, npf_ruleset_t *arlset)
+npf_ruleset_reload(npf_ruleset_t *newset, npf_ruleset_t *oldset)
 {
-	npf_rule_t *rg;
+	npf_rule_t *rg, *rl;
 
 	KASSERT(npf_config_locked_p());
 
-	LIST_FOREACH(rg, &rlset->rs_dynamic, r_dentry) {
-		npf_rule_t *arg, *rl;
+	/*
+	 * Scan the dynamic rules and share (migrate) if needed.
+	 */
+	LIST_FOREACH(rg, &newset->rs_dynamic, r_dentry) {
+		npf_rule_t *actrg;
 
-		if ((arg = npf_ruleset_lookup(arlset, rg->r_name)) == NULL) {
+		/* Look for a dynamic ruleset group with such name. */
+		actrg = npf_ruleset_lookup(oldset, rg->r_name);
+		if (actrg == NULL) {
 			continue;
 		}
 
@@ -397,20 +408,52 @@ npf_ruleset_reload(npf_ruleset_t *rlset,
 		 * the rules are still active and therefore accessible for
 		 * inspection via the old ruleset.
 		 */
-		memcpy(&rg->r_subset, &arg->r_subset, sizeof(rg->r_subset));
+		memcpy(&rg->r_subset, &actrg->r_subset, sizeof(rg->r_subset));
 		TAILQ_FOREACH(rl, &rg->r_subset, r_entry) {
 			/*
 			 * We can safely migrate to the new all-rule list
 			 * and re-set the parent rule, though.
 			 */
 			LIST_REMOVE(rl, r_aentry);
-			LIST_INSERT_HEAD(&rlset->rs_all, rl, r_aentry);
+			LIST_INSERT_HEAD(&newset->rs_all, rl, r_aentry);
 			rl->r_parent = rg;
 		}
 	}
 
+	/*
+	 * Scan all rules in the new ruleset and share NAT policies.
+	 */
+	LIST_FOREACH(rl, &newset->rs_all, r_aentry) {
+		npf_natpolicy_t *np;
+		npf_rule_t *actrl;
+
+		/* Does the rule have a NAT policy associated? */
+		if ((np = rl->r_natp) == NULL) {
+			continue;
+		}
+		/* Does it match with any policy in the active ruleset? */
+		if ((actrl = npf_ruleset_matchnat(oldset, np)) == NULL) {
+			continue;
+		}
+
+		/*
+		 * Inherit the matching NAT policy and check other ones
+		 * in the new ruleset for sharing the portmap.
+		 */
+		rl->r_natp = actrl->r_natp;
+		npf_ruleset_sharepm(newset, rl->r_natp);
+
+		/*
+		 * Finally, mark the active rule to not destroy its NAT
+		 * policy later as we inherited it (but the rule must be
+		 * kept active for now).  Destroy the new/unused policy.
+		 */
+		actrl->r_attr |= NPF_RULE_KEEPNAT;
+		npf_nat_freepolicy(np);
+	}
+
 	/* Inherit the ID counter. */
-	rlset->rs_idcnt = arlset->rs_idcnt;
+	newset->rs_idcnt = oldset->rs_idcnt;
 }
 
 /*
@@ -423,7 +466,7 @@ npf_ruleset_matchnat(npf_ruleset_t *rlse
 
 	/* Find a matching NAT policy in the old ruleset. */
 	LIST_FOREACH(rl, &rlset->rs_all, r_aentry) {
-		if (npf_nat_matchpolicy(rl->r_natp, mnp))
+		if (rl->r_natp && npf_nat_matchpolicy(rl->r_natp, mnp))
 			break;
 	}
 	return rl;
@@ -469,34 +512,6 @@ npf_ruleset_freealg(npf_ruleset_t *rlset
 }
 
 /*
- * npf_ruleset_natreload: minimum reload of NAT policies by matching
- * two (active and new) NAT rulesets.
- */
-void
-npf_ruleset_natreload(npf_ruleset_t *nrlset, npf_ruleset_t *arlset)
-{
-	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;
-		arl = npf_ruleset_matchnat(arlset, np);
-		if (arl == NULL) {
-			continue;
-		}
-		/* On match - we exchange NAT policies. */
-		anp = arl->r_natp;
-		rl->r_natp = anp;
-		arl->r_natp = np;
-		/* Update other NAT policies to share portmap. */
-		(void)npf_ruleset_sharepm(nrlset, anp);
-	}
-}
-
-/*
  * npf_rule_alloc: allocate a rule and initialise it.
  */
 npf_rule_t *
@@ -520,6 +535,7 @@ npf_rule_alloc(prop_dictionary_t rldict)
 	/* Attributes, priority and interface ID (optional). */
 	prop_dictionary_get_uint32(rldict, "attributes", &rl->r_attr);
 	prop_dictionary_get_int32(rldict, "priority", &rl->r_priority);
+	rl->r_attr &= ~NPF_RULE_PRIVMASK;
 
 	if (prop_dictionary_get_cstring_nocopy(rldict, "interface", &rname)) {
 		if ((rl->r_ifid = npf_ifmap_register(rname)) == 0) {
@@ -592,7 +608,7 @@ npf_rule_free(npf_rule_t *rl)
 	npf_natpolicy_t *np = rl->r_natp;
 	npf_rproc_t *rp = rl->r_rproc;
 
-	if (np) {
+	if (np && (rl->r_attr & NPF_RULE_KEEPNAT) == 0) {
 		/* Free NAT policy. */
 		npf_nat_freepolicy(np);
 	}
@@ -652,7 +668,6 @@ npf_rule_getnat(const npf_rule_t *rl)
 void
 npf_rule_setnat(npf_rule_t *rl, npf_natpolicy_t *np)
 {
-
 	KASSERT(rl->r_natp == NULL);
 	rl->r_natp = np;
 }

Reply via email to