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