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;
}