commit 9e7549dba703cb083e5c439c0229bc47cead9e1a Author: Ryota Ozaki Date: Mon Jul 24 15:55:25 2017 +0900 MP-ify SPD diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index 3e7d4453da9..919eddb9c51 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -462,7 +462,7 @@ ip6_forward(struct mbuf *m, int srcrt) out: #ifdef IPSEC if (sp != NULL) - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); #endif rtcache_unref(rt, ro); if (ro != NULL) diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 875f4bbf687..e55e4a69659 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -1069,7 +1069,7 @@ done: #ifdef IPSEC if (sp != NULL) - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); #endif /* IPSEC */ if_put(ifp, &psref); diff --git a/sys/netipsec/ipsec.c b/sys/netipsec/ipsec.c index 2ec2bd54656..92002590caa 100644 --- a/sys/netipsec/ipsec.c +++ b/sys/netipsec/ipsec.c @@ -59,6 +59,7 @@ __KERNEL_RCSID(0, "$NetBSD: ipsec.c,v 1.112 2017/07/26 07:39:54 ozaki-r Exp $"); #include #include #include +#include #include #include @@ -201,6 +202,7 @@ static struct secpolicy *ipsec_deepcopy_policy (const struct secpolicy *); static int ipsec_set_policy (struct secpolicy **, int, const void *, size_t, kauth_cred_t); static int ipsec_get_policy (struct secpolicy *, struct mbuf **); +static void ipsec_destroy_policy(struct secpolicy *); static void vshiftl (unsigned char *, int, int); static size_t ipsec_hdrsiz (const struct secpolicy *); @@ -211,34 +213,49 @@ static struct secpolicy * ipsec_checkpcbcache(struct mbuf *m, struct inpcbpolicy *pcbsp, int dir) { struct secpolicyindex spidx; + struct secpolicy *sp = NULL; + int s; KASSERT(IPSEC_DIR_IS_VALID(dir)); KASSERT(pcbsp != NULL); KASSERT(dir < __arraycount(pcbsp->sp_cache)); KASSERT(inph_locked(pcbsp->sp_inph)); + /* + * Checking the generation and sp->state and taking a reference to an SP + * must be in a critical section of pserialize. See key_unlink_sp. + */ + s = pserialize_read_enter(); /* SPD table change invalidate all the caches. */ if (ipsec_spdgen != pcbsp->sp_cache[dir].cachegen) { ipsec_invalpcbcache(pcbsp, dir); - return NULL; + goto out; } - if (!pcbsp->sp_cache[dir].cachesp) - return NULL; - if (pcbsp->sp_cache[dir].cachesp->state != IPSEC_SPSTATE_ALIVE) { + sp = pcbsp->sp_cache[dir].cachesp; + if (sp == NULL) + goto out; + if (sp->state != IPSEC_SPSTATE_ALIVE) { + sp = NULL; ipsec_invalpcbcache(pcbsp, dir); - return NULL; + goto out; } if ((pcbsp->sp_cacheflags & IPSEC_PCBSP_CONNECTED) == 0) { - if (ipsec_setspidx(m, &spidx, 1) != 0) - return NULL; + /* NB: assume ipsec_setspidx never sleep */ + if (ipsec_setspidx(m, &spidx, 1) != 0) { + sp = NULL; + goto out; + } /* * We have to make an exact match here since the cached rule * might have lower priority than a rule that would otherwise * have matched the packet. */ - if (memcmp(&pcbsp->sp_cache[dir].cacheidx, &spidx, sizeof(spidx))) - return NULL; + if (memcmp(&pcbsp->sp_cache[dir].cacheidx, &spidx, + sizeof(spidx))) { + sp = NULL; + goto out; + } } else { /* * The pcb is connected, and the L4 code is sure that: @@ -252,13 +269,14 @@ ipsec_checkpcbcache(struct mbuf *m, struct inpcbpolicy *pcbsp, int dir) */ } - pcbsp->sp_cache[dir].cachesp->lastused = time_second; - KEY_SP_REF(pcbsp->sp_cache[dir].cachesp); + sp->lastused = time_second; + KEY_SP_REF(sp); KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, "DP cause refcnt++:%d SP:%p\n", - key_sp_refcnt(pcbsp->sp_cache[dir].cachesp), - pcbsp->sp_cache[dir].cachesp); - return pcbsp->sp_cache[dir].cachesp; + key_sp_refcnt(sp), pcbsp->sp_cache[dir].cachesp); +out: + pserialize_read_exit(s); + return sp; } static int @@ -270,8 +288,6 @@ ipsec_fillpcbcache(struct inpcbpolicy *pcbsp, struct mbuf *m, KASSERT(dir < __arraycount(pcbsp->sp_cache)); KASSERT(inph_locked(pcbsp->sp_inph)); - if (pcbsp->sp_cache[dir].cachesp) - KEY_FREESP(&pcbsp->sp_cache[dir].cachesp); pcbsp->sp_cache[dir].cachesp = NULL; pcbsp->sp_cache[dir].cachehint = IPSEC_PCBHINT_UNKNOWN; if (ipsec_setspidx(m, &pcbsp->sp_cache[dir].cacheidx, 1) != 0) { @@ -279,7 +295,6 @@ ipsec_fillpcbcache(struct inpcbpolicy *pcbsp, struct mbuf *m, } pcbsp->sp_cache[dir].cachesp = sp; if (pcbsp->sp_cache[dir].cachesp) { - KEY_SP_REF(pcbsp->sp_cache[dir].cachesp); KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, "DP cause refcnt++:%d SP:%p\n", key_sp_refcnt(pcbsp->sp_cache[dir].cachesp), @@ -317,8 +332,6 @@ ipsec_invalpcbcache(struct inpcbpolicy *pcbsp, int dir) for (i = IPSEC_DIR_INBOUND; i <= IPSEC_DIR_OUTBOUND; i++) { if (dir != IPSEC_DIR_ANY && i != dir) continue; - if (pcbsp->sp_cache[i].cachesp) - KEY_FREESP(&pcbsp->sp_cache[i].cachesp); pcbsp->sp_cache[i].cachesp = NULL; pcbsp->sp_cache[i].cachehint = IPSEC_PCBHINT_UNKNOWN; pcbsp->sp_cache[i].cachegen = 0; @@ -609,7 +622,7 @@ ipsec4_checkpolicy(struct mbuf *m, u_int dir, u_int flag, int *error, break; case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); sp = NULL; /* NB: force NULL result */ break; case IPSEC_POLICY_IPSEC: @@ -617,7 +630,7 @@ ipsec4_checkpolicy(struct mbuf *m, u_int dir, u_int flag, int *error, break; } if (*error != 0) { - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); sp = NULL; IPSECLOG(LOG_DEBUG, "done, error %d\n", *error); } @@ -697,7 +710,7 @@ ipsec4_output(struct mbuf *m, struct inpcb *inp, int flags, */ *mtu = _mtu; *natt_frag = true; - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); splx(s); return 0; } @@ -711,7 +724,7 @@ ipsec4_output(struct mbuf *m, struct inpcb *inp, int flags, */ if (error == ENOENT) error = 0; - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); splx(s); *done = true; return error; @@ -734,7 +747,7 @@ ipsec4_input(struct mbuf *m, int flags) * Check security policy against packet attributes. */ error = ipsec_in_reject(sp, m); - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); splx(s); if (error) { return error; @@ -753,7 +766,7 @@ ipsec4_input(struct mbuf *m, int flags) sp = ipsec4_checkpolicy(m, IPSEC_DIR_OUTBOUND, flags, &error, NULL); if (sp != NULL) { m->m_flags &= ~M_CANFASTFWD; - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); } splx(s); return 0; @@ -802,7 +815,7 @@ ipsec4_forward(struct mbuf *m, int *destmtu) rtcache_unref(rt, ro); KEY_FREESAV(&sav); } - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); return 0; } @@ -838,7 +851,7 @@ ipsec6_checkpolicy(struct mbuf *m, u_int dir, u_int flag, int *error, break; case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); sp = NULL; /* NB: force NULL result */ break; case IPSEC_POLICY_IPSEC: @@ -846,7 +859,7 @@ ipsec6_checkpolicy(struct mbuf *m, u_int dir, u_int flag, int *error, break; } if (*error != 0) { - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); sp = NULL; IPSECLOG(LOG_DEBUG, "done, error %d\n", *error); } @@ -1236,20 +1249,26 @@ ipsec_init_policy(struct socket *so, struct inpcbpolicy **policy) else new->priv = 0; + /* + * These SPs are dummy. Never be used because the policy + * is ENTRUST. See ipsec_getpolicybysock. + */ if ((new->sp_in = KEY_NEWSP()) == NULL) { ipsec_delpcbpolicy(new); return ENOBUFS; } new->sp_in->state = IPSEC_SPSTATE_ALIVE; new->sp_in->policy = IPSEC_POLICY_ENTRUST; + new->sp_in->created = 0; /* Indicates dummy */ if ((new->sp_out = KEY_NEWSP()) == NULL) { - KEY_FREESP(&new->sp_in); + KEY_SP_UNREF(&new->sp_in); ipsec_delpcbpolicy(new); return ENOBUFS; } new->sp_out->state = IPSEC_SPSTATE_ALIVE; new->sp_out->policy = IPSEC_POLICY_ENTRUST; + new->sp_out->created = 0; /* Indicates dummy */ *policy = new; @@ -1264,14 +1283,14 @@ ipsec_copy_policy(const struct inpcbpolicy *old, struct inpcbpolicy *new) sp = ipsec_deepcopy_policy(old->sp_in); if (sp) { - KEY_FREESP(&new->sp_in); + KEY_SP_UNREF(&new->sp_in); new->sp_in = sp; } else return ENOBUFS; sp = ipsec_deepcopy_policy(old->sp_out); if (sp) { - KEY_FREESP(&new->sp_out); + KEY_SP_UNREF(&new->sp_out); new->sp_out = sp; } else return ENOBUFS; @@ -1326,6 +1345,23 @@ ipsec_deepcopy_policy(const struct secpolicy *src) return dst; } +static void +ipsec_destroy_policy(struct secpolicy *sp) +{ + + if (sp->created == 0) + /* It's dummy. We can simply free it */ + key_free_sp(sp); + else { + /* + * We cannot destroy here because it can be called in + * softint. So mark the SP as DEAD and let the timer + * destroy it. See key_timehandler_spd. + */ + sp->state = IPSEC_SPSTATE_DEAD; + } +} + /* set policy and ipsec request if present. */ static int ipsec_set_policy( @@ -1337,7 +1373,7 @@ ipsec_set_policy( ) { const struct sadb_x_policy *xpl; - struct secpolicy *newsp = NULL; + struct secpolicy *newsp = NULL, *oldsp; int error; KASSERT(!cpu_softintr_p()); @@ -1372,11 +1408,16 @@ ipsec_set_policy( if ((newsp = key_msg2sp(xpl, len, &error)) == NULL) return error; - newsp->state = IPSEC_SPSTATE_ALIVE; + key_init_sp(newsp); + newsp->created = time_uptime; + /* Insert the global list for SPs for sockets */ + key_socksplist_add(newsp); /* clear old SP and set new SP */ - KEY_FREESP(policy); + oldsp = *policy; *policy = newsp; + ipsec_destroy_policy(oldsp); + if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DUMP)) { printf("%s: new policy\n", __func__); kdebug_secpolicy(newsp); @@ -1416,6 +1457,7 @@ ipsec4_set_policy(struct inpcb *inp, int optname, const void *request, struct secpolicy **policy; KASSERT(!cpu_softintr_p()); + KASSERT(inp_locked(inp)); /* sanity check. */ if (inp == NULL || request == NULL) @@ -1486,10 +1528,10 @@ ipsec4_delete_pcbpolicy(struct inpcb *inp) return 0; if (inp->inp_sp->sp_in != NULL) - KEY_FREESP(&inp->inp_sp->sp_in); + ipsec_destroy_policy(inp->inp_sp->sp_in); if (inp->inp_sp->sp_out != NULL) - KEY_FREESP(&inp->inp_sp->sp_out); + ipsec_destroy_policy(inp->inp_sp->sp_out); ipsec_invalpcbcache(inp->inp_sp, IPSEC_DIR_ANY); @@ -1508,6 +1550,7 @@ ipsec6_set_policy(struct in6pcb *in6p, int optname, const void *request, struct secpolicy **policy; KASSERT(!cpu_softintr_p()); + KASSERT(in6p_locked(in6p)); /* sanity check. */ if (in6p == NULL || request == NULL) @@ -1575,10 +1618,10 @@ ipsec6_delete_pcbpolicy(struct in6pcb *in6p) return 0; if (in6p->in6p_sp->sp_in != NULL) - KEY_FREESP(&in6p->in6p_sp->sp_in); + ipsec_destroy_policy(in6p->in6p_sp->sp_in); if (in6p->in6p_sp->sp_out != NULL) - KEY_FREESP(&in6p->in6p_sp->sp_out); + ipsec_destroy_policy(in6p->in6p_sp->sp_out); ipsec_invalpcbcache(in6p->in6p_sp, IPSEC_DIR_ANY); @@ -1778,7 +1821,7 @@ ipsec4_in_reject(struct mbuf *m, struct inpcb *inp) result = ipsec_in_reject(sp, m); if (result) IPSEC_STATINC(IPSEC_STAT_IN_POLVIO); - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); } else { result = 0; /* XXX should be panic ? * -> No, there may be error. */ @@ -1817,7 +1860,7 @@ ipsec6_in_reject(struct mbuf *m, struct in6pcb *in6p) result = ipsec_in_reject(sp, m); if (result) IPSEC_STATINC(IPSEC_STAT_IN_POLVIO); - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); } else { result = 0; } @@ -1929,7 +1972,7 @@ ipsec4_hdrsiz(struct mbuf *m, u_int dir, struct inpcb *inp) KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DATA, "size:%lu.\n", (unsigned long)size); - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); } else { size = 0; /* XXX should be panic ? */ } @@ -1964,7 +2007,7 @@ ipsec6_hdrsiz(struct mbuf *m, u_int dir, struct in6pcb *in6p) return 0; size = ipsec_hdrsiz(sp); KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DATA, "size:%zu.\n", size); - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); return size; } @@ -2279,7 +2322,7 @@ ipsec6_input(struct mbuf *m) * attributes. */ error = ipsec_in_reject(sp, m); - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); } else { /* XXX error stat??? */ error = EINVAL; diff --git a/sys/netipsec/ipsec.h b/sys/netipsec/ipsec.h index 607ef557cc1..afbb1bd8447 100644 --- a/sys/netipsec/ipsec.h +++ b/sys/netipsec/ipsec.h @@ -47,6 +47,7 @@ #ifdef _KERNEL #include +#include #include #include @@ -76,7 +77,7 @@ struct secpolicyindex { struct secpolicy { struct pslist_entry pslist_entry; - u_int refcnt; /* reference count */ + struct localcount localcount; /* reference count */ struct secpolicyindex spidx; /* selector */ u_int32_t id; /* It's unique number on the system. */ u_int state; /* 0: dead, others: alive */ diff --git a/sys/netipsec/key.c b/sys/netipsec/key.c index a515b0fd37a..bf570df72d9 100644 --- a/sys/netipsec/key.c +++ b/sys/netipsec/key.c @@ -42,6 +42,7 @@ __KERNEL_RCSID(0, "$NetBSD: key.c,v 1.196 2017/07/27 09:53:57 ozaki-r Exp $"); #include "opt_inet.h" #include "opt_ipsec.h" #include "opt_gateway.h" +#include "opt_net_mpsafe.h" #endif #include @@ -67,6 +68,10 @@ __KERNEL_RCSID(0, "$NetBSD: key.c,v 1.196 2017/07/27 09:53:57 ozaki-r Exp $"); #include #include #include +#include +#include +#include +#include #include #include @@ -130,6 +135,50 @@ percpu_t *pfkeystat_percpu; * field hits 0 (= no external reference other than from SA header. */ +/* + * Locking notes on SPD: + * - Modifications to the sptree must be done with holding key_sp_mtx + * which is a adaptive mutex + * - Read accesses to the sptree must be in critical sections of pserialize(9) + * - SP's lifetime is managed by localcount(9) + * - An SP that has been inserted to the sptree is initially referenced by none, + * i.e., a reference from the pstree isn't counted + * - When an SP is being destroyed, we change its state as DEAD, wait for + * references to the SP to be released, and then deallocate the SP + * (see key_unlink_sp) + * - Getting an SP + * - Normally we get an SP from the sptree by incrementing the reference count + * of the SP + * - We can gain another reference from a held SP only if we check its state + * and take its reference in a critical section of pserialize + * (see esp_output for example) + * - We may get an SP from an SP cache. See below + * - Updating member variables of an SP + * - Most member variables of an SP are immutable + * - Only sp->state and sp->lastused can be changed + * - sp->state of an SP is updated only when destroying it under key_sp_mtx + * - SP caches + * - SPs can be cached in PCBs + * - The lifetime of the caches is controlled by the global generation counter + * (ipsec_spdgen) + * - The global counter value is stored when an SP is cached + * - If the stored value is different from the global counter then the cache + * is considered invalidated + * - The counter is incremented when an SP is being destroyed + * - So checking the generation and taking a reference to an SP should be + * in a critical section of pserialize + * - Note that caching doesn't increment the reference counter of an SP + * - SPs in sockets + * - Userland programs can set a policy to a socket by + * setsockopt(IP_IPSEC_POLICY) + * - Such policies (SPs) are set to a socket (PCB) and also inserted to + * the key_socksplist list (not the sptree) + * - Such a policy is destroyed when a corresponding socket is destroed, + * however, a socket can be destroyed in softint so we cannot destroy + * it directly instead we just mark it DEAD and delay the destruction + * until GC by the timer + */ + u_int32_t key_debug_level = 0; static u_int key_spi_trycnt = 1000; static u_int32_t key_spi_minval = 0x100; @@ -194,10 +243,23 @@ static LIST_HEAD(_spacqtree, secspacq) spacqtree; /* SP acquiring list */ } \ } while (0) +/* + * The list has SPs that are set to a socket via setsockopt(IP_IPSEC_POLICY) + * from userland. See ipsec_set_policy. + */ +static struct pslist_head key_socksplist; + +#define SOCKSPLIST_WRITER_FOREACH(sp) \ + PSLIST_WRITER_FOREACH((sp), &key_socksplist, struct secpolicy, \ + pslist_entry) + /* * Protect regtree, acqtree and items stored in the lists. */ static kmutex_t key_mtx __cacheline_aligned; +static pserialize_t key_psz; +static kmutex_t key_sp_mtx __cacheline_aligned; +static kcondvar_t key_sp_cv __cacheline_aligned; /* search order for SAs */ /* @@ -432,9 +494,11 @@ static struct secasvar *key_lookup_sa_bysaidx(const struct secasindex *); static void key_freeso(struct socket *); static void key_freesp_so(struct secpolicy **); #endif -static void key_delsp (struct secpolicy *); static struct secpolicy *key_getsp (const struct secpolicyindex *); static struct secpolicy *key_getspbyid (u_int32_t); +static struct secpolicy *key_lookup_and_remove_sp(const struct secpolicyindex *); +static struct secpolicy *key_lookupbyid_and_remove_sp(u_int32_t); +static void key_destroy_sp(struct secpolicy *); static u_int16_t key_newreqid (void); static struct mbuf *key_gather_mbuf (struct mbuf *, const struct sadb_msghdr *, int, int, ...); @@ -582,8 +646,6 @@ static const char *key_getfqdn (void); static const char *key_getuserfqdn (void); #endif static void key_sa_chgstate (struct secasvar *, u_int8_t); -static inline void key_sp_dead (struct secpolicy *); -static void key_sp_unlink (struct secpolicy *sp); static struct mbuf *key_alloc_mbuf (int); @@ -622,56 +684,38 @@ static struct work key_timehandler_wk; REFLOG("SA_DELREF", (p), (where), (tag)); \ } while (0) -#define SP_ADDREF(p) do { \ - atomic_inc_uint(&(p)->refcnt); \ - REFLOG("SP_ADDREF", (p), __func__, __LINE__); \ - KASSERTMSG((p)->refcnt != 0, "SP refcnt overflow"); \ -} while (0) -#define SP_ADDREF2(p, where, tag) do { \ - atomic_inc_uint(&(p)->refcnt); \ - REFLOG("SP_ADDREF", (p), (where), (tag)); \ - KASSERTMSG((p)->refcnt != 0, "SP refcnt overflow"); \ -} while (0) -#define SP_DELREF(p) do { \ - KASSERTMSG((p)->refcnt > 0, "SP refcnt underflow"); \ - atomic_dec_uint(&(p)->refcnt); \ - REFLOG("SP_DELREF", (p), __func__, __LINE__); \ -} while (0) -#define SP_DELREF2(p, nv, where, tag) do { \ - KASSERTMSG((p)->refcnt > 0, "SP refcnt underflow"); \ - nv = atomic_dec_uint_nv(&(p)->refcnt); \ - REFLOG("SP_DELREF", (p), (where), (tag)); \ -} while (0) - u_int key_sp_refcnt(const struct secpolicy *sp) { - if (sp == NULL) - return 0; - - return sp->refcnt; + /* FIXME */ + return 0; } -static inline void -key_sp_dead(struct secpolicy *sp) +/* + * Remove the sp from the sptree and wait for references to the sp + * to be released. key_sp_mtx must be held. + */ +static void +key_unlink_sp(struct secpolicy *sp) { - /* mark the SP dead */ + KASSERT(mutex_owned(&key_sp_mtx)); + sp->state = IPSEC_SPSTATE_DEAD; -} - -static void -key_sp_unlink(struct secpolicy *sp) -{ - - /* remove from SP index */ SPLIST_WRITER_REMOVE(sp); - /* Release refcount held just for being on chain */ - KEY_FREESP(&sp); + + /* Invalidate all cached SPD pointers in the PCBs. */ + ipsec_invalpcbcacheall(); + +#ifdef NET_MPSAFE + KASSERT(mutex_ownable(softnet_lock)); + pserialize_perform(key_psz); +#endif + + localcount_drain(&sp->localcount, &key_sp_cv, &key_sp_mtx); } - /* * Return 0 when there are known to be no SP's for the specified * direction. Otherwise return 1. This is used by IPsec code @@ -704,12 +748,12 @@ key_lookup_sp_byspidx(const struct secpolicyindex *spidx, KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, "DP from %s:%u\n", where, tag); /* get a SP entry */ - s = splsoftnet(); /*called from softclock()*/ if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DATA)) { printf("*** objects\n"); kdebug_secpolicyindex(spidx); } + s = pserialize_read_enter(); SPLIST_READER_FOREACH(sp, dir) { if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DATA)) { printf("*** in SPD\n"); @@ -729,9 +773,9 @@ found: /* found a SPD entry */ sp->lastused = time_uptime; - SP_ADDREF2(sp, where, tag); + key_sp_ref(sp, where, tag); } - splx(s); + pserialize_read_exit(s); KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, "DP return SP:%p (ID=%u) refcnt %u\n", @@ -765,7 +809,7 @@ key_gettunnel(const struct sockaddr *osrc, goto done; } - s = splsoftnet(); /*called from softclock()*/ + s = pserialize_read_enter(); SPLIST_READER_FOREACH(sp, dir) { if (sp->state == IPSEC_SPSTATE_DEAD) continue; @@ -805,9 +849,9 @@ key_gettunnel(const struct sockaddr *osrc, found: if (sp) { sp->lastused = time_uptime; - SP_ADDREF2(sp, where, tag); + key_sp_ref(sp, where, tag); } - splx(s); + pserialize_read_exit(s); done: KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, "DP return SP:%p (ID=%u) refcnt %u\n", @@ -1153,17 +1197,43 @@ key_validate_savlist(const struct secashead *sah, const u_int state) #endif } +void +key_init_sp(struct secpolicy *sp) +{ + + ASSERT_SLEEPABLE(); + + sp->state = IPSEC_SPSTATE_ALIVE; + if (sp->policy == IPSEC_POLICY_IPSEC) + KASSERT(sp->req != NULL); + localcount_init(&sp->localcount); + SPLIST_ENTRY_INIT(sp); +} + void key_sp_ref(struct secpolicy *sp, const char* where, int tag) { - SP_ADDREF2(sp, where, tag); + localcount_acquire(&sp->localcount); KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, - "DP SP:%p (ID=%u) from %s:%u; refcnt now %u\n", + "DP SP:%p (ID=%u) from %s:%u; refcnt++ now %u\n", sp, sp->id, where, tag, key_sp_refcnt(sp)); } +void +key_sp_unref(struct secpolicy *sp, const char* where, int tag) +{ + + KASSERT(mutex_ownable(&key_sp_mtx)); + + KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, + "DP SP:%p (ID=%u) from %s:%u; refcnt-- now %u\n", + sp, sp->id, where, tag, key_sp_refcnt(sp)); + + localcount_release(&sp->localcount, &key_sp_cv, &key_sp_mtx); +} + void key_sa_ref(struct secasvar *sav, const char* where, int tag) { @@ -1175,30 +1245,6 @@ key_sa_ref(struct secasvar *sav, const char* where, int tag) sav->refcnt, sav, where, tag); } -/* - * Must be called after calling key_lookup_sp*(). - * For both the packet without socket and key_freeso(). - */ -void -_key_freesp(struct secpolicy **spp, const char* where, int tag) -{ - struct secpolicy *sp = *spp; - unsigned int nv; - - KASSERT(sp != NULL); - - SP_DELREF2(sp, nv, where, tag); - - KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, - "DP SP:%p (ID=%u) from %s:%u; refcnt now %u\n", - sp, sp->id, where, tag, nv); - - if (nv == 0) { - *spp = NULL; - key_delsp(sp); - } -} - #if 0 /* * Must be called after calling key_lookup_sp*(). @@ -1270,7 +1316,7 @@ key_freesp_so(struct secpolicy **sp) KASSERTMSG((*sp)->policy == IPSEC_POLICY_IPSEC, "invalid policy %u", (*sp)->policy); - KEY_FREESP(sp); + KEY_SP_UNREF(&sp); } #endif @@ -1309,20 +1355,18 @@ key_freesav(struct secasvar **psav, const char* where, int tag) * free security policy entry. */ static void -key_delsp(struct secpolicy *sp) +key_destroy_sp(struct secpolicy *sp) { - int s; - KASSERT(sp != NULL); + SPLIST_ENTRY_DESTROY(sp); + localcount_fini(&sp->localcount); - key_sp_dead(sp); + key_free_sp(sp); +} - KASSERTMSG(sp->refcnt == 0, - "SP with references deleted (refcnt %u)", sp->refcnt); - - s = splsoftnet(); /*called from softclock()*/ - - { +void +key_free_sp(struct secpolicy *sp) +{ struct ipsecrequest *isr = sp->req, *nextisr; while (isr != NULL) { @@ -1330,12 +1374,17 @@ key_delsp(struct secpolicy *sp) kmem_intr_free(isr, sizeof(*isr)); isr = nextisr; } - } - SPLIST_ENTRY_DESTROY(sp); kmem_intr_free(sp, sizeof(*sp)); +} - splx(s); +void +key_socksplist_add(struct secpolicy *sp) +{ + + mutex_enter(&key_sp_mtx); + PSLIST_WRITER_INSERT_HEAD(&key_socksplist, sp, pslist_entry); + mutex_exit(&key_sp_mtx); } /* @@ -1347,21 +1396,51 @@ static struct secpolicy * key_getsp(const struct secpolicyindex *spidx) { struct secpolicy *sp; + int s; KASSERT(spidx != NULL); + s = pserialize_read_enter(); SPLIST_READER_FOREACH(sp, spidx->dir) { if (sp->state == IPSEC_SPSTATE_DEAD) continue; if (key_spidx_match_exactly(spidx, &sp->spidx)) { - SP_ADDREF(sp); + KEY_SP_REF(sp); + pserialize_read_exit(s); return sp; } } + pserialize_read_exit(s); return NULL; } +/* + * search SPD and remove found SP + * OUT: NULL : not found + * others : found, pointer to a SP. + */ +static struct secpolicy * +key_lookup_and_remove_sp(const struct secpolicyindex *spidx) +{ + struct secpolicy *sp = NULL; + + mutex_enter(&key_sp_mtx); + SPLIST_WRITER_FOREACH(sp, spidx->dir) { + KASSERT(sp->state != IPSEC_SPSTATE_DEAD); + + if (key_spidx_match_exactly(spidx, &sp->spidx)) { + key_unlink_sp(sp); + goto out; + } + } + sp = NULL; +out: + mutex_exit(&key_sp_mtx); + + return sp; +} + /* * get SP by index. * OUT: NULL : not found @@ -1371,13 +1450,15 @@ static struct secpolicy * key_getspbyid(u_int32_t id) { struct secpolicy *sp; + int s; + s = pserialize_read_enter(); SPLIST_READER_FOREACH(sp, IPSEC_DIR_INBOUND) { if (sp->state == IPSEC_SPSTATE_DEAD) continue; if (sp->id == id) { - SP_ADDREF(sp); - return sp; + KEY_SP_REF(sp); + goto out; } } @@ -1385,12 +1466,42 @@ key_getspbyid(u_int32_t id) if (sp->state == IPSEC_SPSTATE_DEAD) continue; if (sp->id == id) { - SP_ADDREF(sp); - return sp; + KEY_SP_REF(sp); + goto out; } } +out: + pserialize_read_exit(s); + return sp; +} - return NULL; +/* + * get SP by index, remove and return it. + * OUT: NULL : not found + * others : found, pointer to a SP. + */ +static struct secpolicy * +key_lookupbyid_and_remove_sp(u_int32_t id) +{ + struct secpolicy *sp; + + mutex_enter(&key_sp_mtx); + SPLIST_READER_FOREACH(sp, IPSEC_DIR_INBOUND) { + KASSERT(sp->state != IPSEC_SPSTATE_DEAD); + if (sp->id == id) + goto out; + } + + SPLIST_READER_FOREACH(sp, IPSEC_DIR_OUTBOUND) { + KASSERT(sp->state != IPSEC_SPSTATE_DEAD); + if (sp->id == id) + goto out; + } +out: + if (sp != NULL) + key_unlink_sp(sp); + mutex_exit(&key_sp_mtx); + return sp; } struct secpolicy * @@ -1399,8 +1510,6 @@ key_newsp(const char* where, int tag) struct secpolicy *newsp = NULL; newsp = kmem_intr_zalloc(sizeof(struct secpolicy), KM_NOSLEEP); - if (newsp != NULL) - newsp->refcnt = 1; KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, "DP from %s:%u return SP:%p\n", where, tag, newsp); @@ -1451,7 +1560,7 @@ key_msg2sp(const struct sadb_x_policy *xpl0, size_t len, int *error) break; default: IPSECLOG(LOG_DEBUG, "invalid policy type.\n"); - KEY_FREESP(&newsp); + key_free_sp(newsp); *error = EINVAL; return NULL; } @@ -1605,7 +1714,7 @@ key_msg2sp(const struct sadb_x_policy *xpl0, size_t len, int *error) return newsp; free_exit: - KEY_FREESP(&newsp); + key_free_sp(newsp); return NULL; } @@ -1857,16 +1966,14 @@ key_api_spdadd(struct socket *so, struct mbuf *m, { struct secpolicy *sp; - sp = key_getsp(&spidx); if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) { - if (sp) { - key_sp_dead(sp); - key_sp_unlink(sp); /* XXX jrs ordering */ - KEY_FREESP(&sp); - } + sp = key_lookup_and_remove_sp(&spidx); + if (sp != NULL) + key_destroy_sp(sp); } else { + sp = key_getsp(&spidx); if (sp != NULL) { - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); IPSECLOG(LOG_DEBUG, "a SP entry exists already.\n"); return key_senderror(so, m, EEXIST); } @@ -1891,12 +1998,11 @@ key_api_spdadd(struct socket *so, struct mbuf *m, newsp->lifetime = lft ? lft->sadb_lifetime_addtime : 0; newsp->validtime = lft ? lft->sadb_lifetime_usetime : 0; - newsp->refcnt = 1; /* do not reclaim until I say I do */ - newsp->state = IPSEC_SPSTATE_ALIVE; - if (newsp->policy == IPSEC_POLICY_IPSEC) - KASSERT(newsp->req != NULL); - SPLIST_ENTRY_INIT(newsp); + key_init_sp(newsp); + + mutex_enter(&key_sp_mtx); SPLIST_WRITER_INSERT_TAIL(newsp->spidx.dir, newsp); + mutex_exit(&key_sp_mtx); #ifdef notyet /* delete the entry in spacqtree */ @@ -1984,7 +2090,7 @@ key_getnewspid(void) if (sp == NULL) break; - KEY_FREESP(&sp); + KEY_SP_UNREF(&sp); } if (count == 0 || newid == 0) { @@ -2044,7 +2150,7 @@ key_api_spddelete(struct socket *so, struct mbuf *m, key_init_spidx_bymsghdr(&spidx, mhp); /* Is there SP in SPD ? */ - sp = key_getsp(&spidx); + sp = key_lookup_and_remove_sp(&spidx); if (sp == NULL) { IPSECLOG(LOG_DEBUG, "no SP found.\n"); return key_senderror(so, m, EINVAL); @@ -2053,12 +2159,7 @@ key_api_spddelete(struct socket *so, struct mbuf *m, /* save policy id to buffer to be returned. */ xpl0->sadb_x_policy_id = sp->id; - key_sp_dead(sp); - key_sp_unlink(sp); /* XXX jrs ordering */ - KEY_FREESP(&sp); /* ref gained by key_getspbyid */ - - /* Invalidate all cached SPD pointers in the PCBs. */ - ipsec_invalpcbcacheall(); + key_destroy_sp(sp); /* We're deleting policy; no need to invalidate the ipflow cache. */ @@ -2109,19 +2210,13 @@ key_api_spddelete2(struct socket *so, struct mbuf *m, id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id; /* Is there SP in SPD ? */ - sp = key_getspbyid(id); + sp = key_lookupbyid_and_remove_sp(id); if (sp == NULL) { IPSECLOG(LOG_DEBUG, "no SP found id:%u.\n", id); return key_senderror(so, m, EINVAL); } - key_sp_dead(sp); - key_sp_unlink(sp); /* XXX jrs ordering */ - KEY_FREESP(&sp); /* ref gained by key_getsp */ - sp = NULL; - - /* Invalidate all cached SPD pointers in the PCBs. */ - ipsec_invalpcbcacheall(); + key_destroy_sp(sp); /* We're deleting policy; no need to invalidate the ipflow cache. */ @@ -2211,7 +2306,7 @@ key_api_spdget(struct socket *so, struct mbuf *m, n = key_setdumpsp(sp, SADB_X_SPDGET, mhp->msg->sadb_msg_seq, mhp->msg->sadb_msg_pid); - KEY_FREESP(&sp); /* ref gained by key_getspbyid */ + KEY_SP_UNREF(&sp); /* ref gained by key_getspbyid */ if (n != NULL) { m_freem(m); return key_sendup_mbuf(so, n, KEY_SENDUP_ONE); @@ -2317,18 +2412,17 @@ key_api_spdflush(struct socket *so, struct mbuf *m, for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { retry: + mutex_enter(&key_sp_mtx); SPLIST_WRITER_FOREACH(sp, dir) { - if (sp->state == IPSEC_SPSTATE_DEAD) - continue; - key_sp_dead(sp); - key_sp_unlink(sp); + KASSERT(sp->state != IPSEC_SPSTATE_DEAD); + key_unlink_sp(sp); + mutex_exit(&key_sp_mtx); + key_destroy_sp(sp); goto retry; } + mutex_exit(&key_sp_mtx); } - /* Invalidate all cached SPD pointers in the PCBs. */ - ipsec_invalpcbcacheall(); - /* We're deleting policy; no need to invalidate the ipflow cache. */ if (sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) { @@ -2361,12 +2455,14 @@ key_setspddump_chain(int *errorp, int *lenp, pid_t pid) struct mbuf *m, *n, *prev; int totlen; + KASSERT(mutex_owned(&key_sp_mtx)); + *lenp = 0; /* search SPD entry and get buffer size. */ cnt = 0; for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { - SPLIST_READER_FOREACH(sp, dir) { + SPLIST_WRITER_FOREACH(sp, dir) { cnt++; } } @@ -2380,7 +2476,7 @@ key_setspddump_chain(int *errorp, int *lenp, pid_t pid) prev = m; totlen = 0; for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { - SPLIST_READER_FOREACH(sp, dir) { + SPLIST_WRITER_FOREACH(sp, dir) { --cnt; n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt, pid); @@ -2423,7 +2519,7 @@ key_api_spddump(struct socket *so, struct mbuf *m0, { struct mbuf *n; int error, len; - int ok, s; + int ok; pid_t pid; pid = mhp->msg->sadb_msg_pid; @@ -2438,9 +2534,9 @@ key_api_spddump(struct socket *so, struct mbuf *m0, return key_senderror(so, m0, ENOBUFS); } - s = splsoftnet(); + mutex_enter(&key_sp_mtx); n = key_setspddump_chain(&error, &len, pid); - splx(s); + mutex_exit(&key_sp_mtx); if (n == NULL) { return key_senderror(so, m0, ENOENT); @@ -4341,24 +4437,37 @@ key_timehandler_spd(time_t now) for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { retry: + mutex_enter(&key_sp_mtx); SPLIST_WRITER_FOREACH(sp, dir) { - if (sp->state == IPSEC_SPSTATE_DEAD) { - key_sp_unlink(sp); /*XXX*/ - goto retry; - } + KASSERT(sp->state != IPSEC_SPSTATE_DEAD); if (sp->lifetime == 0 && sp->validtime == 0) continue; - /* the deletion will occur next time */ if ((sp->lifetime && now - sp->created > sp->lifetime) || (sp->validtime && now - sp->lastused > sp->validtime)) { - key_sp_dead(sp); + key_unlink_sp(sp); + mutex_exit(&key_sp_mtx); key_spdexpire(sp); + key_destroy_sp(sp); goto retry; } } + mutex_exit(&key_sp_mtx); } + + retry_socksplist: + mutex_enter(&key_sp_mtx); + SOCKSPLIST_WRITER_FOREACH(sp) { + if (sp->state != IPSEC_SPSTATE_DEAD) + continue; + + key_unlink_sp(sp); + mutex_exit(&key_sp_mtx); + key_destroy_sp(sp); + goto retry_socksplist; + } + mutex_exit(&key_sp_mtx); } static void @@ -7537,6 +7646,9 @@ key_do_init(void) int i, error; mutex_init(&key_mtx, MUTEX_DEFAULT, IPL_NONE); + key_psz = pserialize_create(); + mutex_init(&key_sp_mtx, MUTEX_DEFAULT, IPL_NONE); + cv_init(&key_sp_cv, "key_sp"); pfkeystat_percpu = percpu_alloc(sizeof(uint64_t) * PFKEY_NSTATS); @@ -7550,6 +7662,8 @@ key_do_init(void) PSLIST_INIT(&sptree[i]); } + PSLIST_INIT(&key_socksplist); + LIST_INIT(&sahtree); for (i = 0; i <= SADB_SATYPE_MAX; i++) { @@ -7565,11 +7679,13 @@ key_do_init(void) /* system default */ ip4_def_policy.policy = IPSEC_POLICY_NONE; - ip4_def_policy.refcnt++; /*never reclaim this*/ + ip4_def_policy.state = IPSEC_SPSTATE_ALIVE; + localcount_init(&ip4_def_policy.localcount); #ifdef INET6 ip6_def_policy.policy = IPSEC_POLICY_NONE; - ip6_def_policy.refcnt++; /*never reclaim this*/ + ip6_def_policy.state = IPSEC_SPSTATE_ALIVE; + localcount_init(&ip6_def_policy.localcount); #endif callout_reset(&key_timehandler_ch, hz, key_timehandler, NULL); @@ -7909,10 +8025,12 @@ key_setspddump(int *errorp, pid_t pid) u_int dir; struct mbuf *m, *n; + KASSERT(mutex_owned(&key_sp_mtx)); + /* search SPD entry and get buffer size. */ cnt = 0; for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { - SPLIST_READER_FOREACH(sp, dir) { + SPLIST_WRITER_FOREACH(sp, dir) { cnt++; } } @@ -7924,7 +8042,7 @@ key_setspddump(int *errorp, pid_t pid) m = NULL; for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { - SPLIST_READER_FOREACH(sp, dir) { + SPLIST_WRITER_FOREACH(sp, dir) { --cnt; n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt, pid); @@ -8029,16 +8147,16 @@ sysctl_net_key_dumpsp(SYSCTLFN_ARGS) int err2 = 0; char *p, *ep; size_t len; - int s, error; + int error; if (newp) return (EPERM); if (namelen != 0) return (EINVAL); - s = splsoftnet(); + mutex_enter(&key_sp_mtx); m = key_setspddump(&error, l->l_proc->p_pid); - splx(s); + mutex_exit(&key_sp_mtx); if (!m) return (error); if (!oldp) diff --git a/sys/netipsec/key.h b/sys/netipsec/key.h index e032bd94304..70a096d4ccf 100644 --- a/sys/netipsec/key.h +++ b/sys/netipsec/key.h @@ -55,11 +55,15 @@ struct secpolicy *key_gettunnel(const struct sockaddr *, const struct sockaddr *, const struct sockaddr *, const struct sockaddr *, const char*, int); /* NB: prepend with _ for KAME IPv6 compatbility */ -void _key_freesp(struct secpolicy **, const char*, int); +void key_init_sp(struct secpolicy *); +void key_free_sp(struct secpolicy *); u_int key_sp_refcnt(const struct secpolicy *); void key_sp_ref(struct secpolicy *, const char*, int); +void key_sp_unref(struct secpolicy *, const char*, int); void key_sa_ref(struct secasvar *, const char*, int); +void key_socksplist_add(struct secpolicy *); + /* * Access to the SADB are interlocked with splsoftnet. In particular, * holders of SA's use this to block accesses by protocol processing @@ -73,8 +77,8 @@ void key_sa_ref(struct secasvar *, const char*, int); key_newsp(__func__, __LINE__) #define KEY_GETTUNNEL(osrc, odst, isrc, idst) \ key_gettunnel(osrc, odst, isrc, idst, __func__, __LINE__) -#define KEY_FREESP(spp) \ - _key_freesp(spp, __func__, __LINE__) +#define KEY_SP_UNREF(spp) \ + key_sp_unref(*(spp), __func__, __LINE__) #define KEY_SP_REF(sp) \ key_sp_ref(sp, __func__, __LINE__) #define KEY_SA_REF(sav) \ diff --git a/sys/netipsec/xform_ah.c b/sys/netipsec/xform_ah.c index 6c60c57b422..3da807fc1f5 100644 --- a/sys/netipsec/xform_ah.c +++ b/sys/netipsec/xform_ah.c @@ -54,6 +54,7 @@ __KERNEL_RCSID(0, "$NetBSD: xform_ah.c,v 1.69 2017/07/27 06:59:28 ozaki-r Exp $" #include #include #include +#include #include @@ -1140,6 +1141,21 @@ ah_output( goto bad; } + { + int s = pserialize_read_enter(); + + if (__predict_false(isr->sp->state == IPSEC_SPSTATE_DEAD)) { + pserialize_read_exit(s); + pool_put(&ah_tdb_crypto_pool, tc); + crypto_freereq(crp); + AH_STATINC(AH_STAT_NOTDB); + error = ENOENT; + goto bad; + } + KEY_SP_REF(isr->sp); + pserialize_read_exit(s); + } + /* Crypto operation descriptor. */ crp->crp_ilen = m->m_pkthdr.len; /* Total input length. */ crp->crp_flags = CRYPTO_F_IMBUF; @@ -1150,7 +1166,6 @@ ah_output( /* These are passed as-is to the callback. */ tc->tc_isr = isr; - KEY_SP_REF(isr->sp); tc->tc_spi = sav->spi; tc->tc_dst = sav->sah->saidx.dst; tc->tc_proto = sav->sah->saidx.proto; @@ -1255,13 +1270,13 @@ ah_output_cb(struct cryptop *crp) /* NB: m is reclaimed by ipsec_process_done. */ err = ipsec_process_done(m, isr, sav); KEY_FREESAV(&sav); - KEY_FREESP(&isr->sp); + KEY_SP_UNREF(&isr->sp); IPSEC_RELEASE_GLOBAL_LOCKS(); return err; bad: if (sav) KEY_FREESAV(&sav); - KEY_FREESP(&isr->sp); + KEY_SP_UNREF(&isr->sp); IPSEC_RELEASE_GLOBAL_LOCKS(); if (m) m_freem(m); diff --git a/sys/netipsec/xform_esp.c b/sys/netipsec/xform_esp.c index ceb5e984353..50e95f60eac 100644 --- a/sys/netipsec/xform_esp.c +++ b/sys/netipsec/xform_esp.c @@ -55,6 +55,7 @@ __KERNEL_RCSID(0, "$NetBSD: xform_esp.c,v 1.67 2017/07/27 06:59:28 ozaki-r Exp $ #include #include #include +#include #include @@ -897,9 +898,23 @@ esp_output( goto bad; } + { + int s = pserialize_read_enter(); + + if (__predict_false(isr->sp->state == IPSEC_SPSTATE_DEAD)) { + pserialize_read_exit(s); + pool_put(&esp_tdb_crypto_pool, tc); + crypto_freereq(crp); + ESP_STATINC(ESP_STAT_NOTDB); + error = ENOENT; + goto bad; + } + KEY_SP_REF(isr->sp); + pserialize_read_exit(s); + } + /* Callback parameters */ tc->tc_isr = isr; - KEY_SP_REF(isr->sp); tc->tc_spi = sav->spi; tc->tc_dst = saidx->dst; tc->tc_proto = saidx->proto; @@ -1032,13 +1047,13 @@ esp_output_cb(struct cryptop *crp) /* NB: m is reclaimed by ipsec_process_done. */ err = ipsec_process_done(m, isr, sav); KEY_FREESAV(&sav); - KEY_FREESP(&isr->sp); + KEY_SP_UNREF(&isr->sp); IPSEC_RELEASE_GLOBAL_LOCKS(); return err; bad: if (sav) KEY_FREESAV(&sav); - KEY_FREESP(&isr->sp); + KEY_SP_UNREF(&isr->sp); IPSEC_RELEASE_GLOBAL_LOCKS(); if (m) m_freem(m); diff --git a/sys/netipsec/xform_ipcomp.c b/sys/netipsec/xform_ipcomp.c index 4e52df7d6c0..1480add9c3c 100644 --- a/sys/netipsec/xform_ipcomp.c +++ b/sys/netipsec/xform_ipcomp.c @@ -45,6 +45,7 @@ __KERNEL_RCSID(0, "$NetBSD: xform_ipcomp.c,v 1.48 2017/07/27 06:59:28 ozaki-r Ex #include #include #include +#include #include #include @@ -479,8 +480,22 @@ ipcomp_output( goto bad; } - tc->tc_isr = isr; + { + int s = pserialize_read_enter(); + + if (__predict_false(isr->sp->state == IPSEC_SPSTATE_DEAD)) { + pserialize_read_exit(s); + pool_put(&ipcomp_tdb_crypto_pool, tc); + crypto_freereq(crp); + IPCOMP_STATINC(IPCOMP_STAT_NOTDB); + error = ENOENT; + goto bad; + } KEY_SP_REF(isr->sp); + pserialize_read_exit(s); + } + + tc->tc_isr = isr; tc->tc_spi = sav->spi; tc->tc_dst = sav->sah->saidx.dst; tc->tc_proto = sav->sah->saidx.proto; @@ -646,13 +661,13 @@ ipcomp_output_cb(struct cryptop *crp) /* NB: m is reclaimed by ipsec_process_done. */ error = ipsec_process_done(m, isr, sav); KEY_FREESAV(&sav); - KEY_FREESP(&isr->sp); + KEY_SP_UNREF(&isr->sp); IPSEC_RELEASE_GLOBAL_LOCKS(); return error; bad: if (sav) KEY_FREESAV(&sav); - KEY_FREESP(&isr->sp); + KEY_SP_UNREF(&isr->sp); IPSEC_RELEASE_GLOBAL_LOCKS(); if (m) m_freem(m); diff --git a/sys/rump/librump/rumpnet/net_stub.c b/sys/rump/librump/rumpnet/net_stub.c index c5be9a1968b..99e5e8a177d 100644 --- a/sys/rump/librump/rumpnet/net_stub.c +++ b/sys/rump/librump/rumpnet/net_stub.c @@ -108,7 +108,7 @@ __weak_alias(ipsec_init_policy,rumpnet_stub); __weak_alias(ipsec_pcbconn,rumpnet_stub); __weak_alias(ipsec_pcbdisconn,rumpnet_stub); __weak_alias(key_sa_routechange,rumpnet_stub); -__weak_alias(_key_freesp,rumpnet_stub); +__weak_alias(key_sp_unref,rumpnet_stub); struct ifnet_head ifnet_list; struct pslist_head ifnet_pslist;