diff options
Diffstat (limited to 'sys/netipsec/key.c')
-rw-r--r-- | sys/netipsec/key.c | 455 |
1 files changed, 232 insertions, 223 deletions
diff --git a/sys/netipsec/key.c b/sys/netipsec/key.c index 5dc42e4..f5b0fee 100644 --- a/sys/netipsec/key.c +++ b/sys/netipsec/key.c @@ -48,6 +48,7 @@ #include <sys/domain.h> #include <sys/protosw.h> #include <sys/malloc.h> +#include <sys/rmlock.h> #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/sysctl.h> @@ -140,16 +141,19 @@ static VNET_DEFINE(u_int32_t, acq_seq) = 0; #define V_acq_seq VNET(acq_seq) /* SPD */ -static VNET_DEFINE(LIST_HEAD(_sptree, secpolicy), sptree[IPSEC_DIR_MAX]); +static VNET_DEFINE(TAILQ_HEAD(_sptree, secpolicy), sptree[IPSEC_DIR_MAX]); +static struct rmlock sptree_lock; #define V_sptree VNET(sptree) -static struct mtx sptree_lock; -#define SPTREE_LOCK_INIT() \ - mtx_init(&sptree_lock, "sptree", \ - "fast ipsec security policy database", MTX_DEF) -#define SPTREE_LOCK_DESTROY() mtx_destroy(&sptree_lock) -#define SPTREE_LOCK() mtx_lock(&sptree_lock) -#define SPTREE_UNLOCK() mtx_unlock(&sptree_lock) -#define SPTREE_LOCK_ASSERT() mtx_assert(&sptree_lock, MA_OWNED) +#define SPTREE_LOCK_INIT() rm_init(&sptree_lock, "sptree") +#define SPTREE_LOCK_DESTROY() rm_destroy(&sptree_lock) +#define SPTREE_RLOCK_TRACKER struct rm_priotracker sptree_tracker +#define SPTREE_RLOCK() rm_rlock(&sptree_lock, &sptree_tracker) +#define SPTREE_RUNLOCK() rm_runlock(&sptree_lock, &sptree_tracker) +#define SPTREE_RLOCK_ASSERT() rm_assert(&sptree_lock, RA_RLOCKED) +#define SPTREE_WLOCK() rm_wlock(&sptree_lock) +#define SPTREE_WUNLOCK() rm_wunlock(&sptree_lock) +#define SPTREE_WLOCK_ASSERT() rm_assert(&sptree_lock, RA_WLOCKED) +#define SPTREE_UNLOCK_ASSERT() rm_assert(&sptree_lock, RA_UNLOCKED) static VNET_DEFINE(LIST_HEAD(_sahtree, secashead), sahtree); /* SAD */ #define V_sahtree VNET(sahtree) @@ -411,9 +415,8 @@ struct sadb_msghdr { static struct secasvar *key_allocsa_policy(const struct secasindex *); static void key_freesp_so(struct secpolicy **); static struct secasvar *key_do_allocsa_policy(struct secashead *, u_int); -static void key_delsp(struct secpolicy *); +static void key_unlink(struct secpolicy *); static struct secpolicy *key_getsp(struct secpolicyindex *); -static void _key_delsp(struct secpolicy *sp); static struct secpolicy *key_getspbyid(u_int32_t); static u_int32_t key_newreqid(void); static struct mbuf *key_gather_mbuf(struct mbuf *, @@ -465,7 +468,7 @@ static void key_porttosaddr(struct sockaddr *, u_int16_t); key_porttosaddr((struct sockaddr *)(saddr), (port)) static struct mbuf *key_setsadbxsa2(u_int8_t, u_int32_t, u_int32_t); static struct mbuf *key_setsadbxpolicy(u_int16_t, u_int8_t, - u_int32_t); + u_int32_t, u_int32_t); static struct seckey *key_dup_keymsg(const struct sadb_key *, u_int, struct malloc_type *); static struct seclifetime *key_dup_lifemsg(const struct sadb_lifetime *src, @@ -570,15 +573,8 @@ sa_delref(struct secasvar *sav) return (refcount_release(&sav->refcnt)); } -#define SP_ADDREF(p) do { \ - (p)->refcnt++; \ - IPSEC_ASSERT((p)->refcnt != 0, ("SP refcnt overflow")); \ -} while (0) -#define SP_DELREF(p) do { \ - IPSEC_ASSERT((p)->refcnt > 0, ("SP refcnt underflow")); \ - (p)->refcnt--; \ -} while (0) - +#define SP_ADDREF(p) refcount_acquire(&(p)->refcnt) +#define SP_DELREF(p) refcount_release(&(p)->refcnt) /* * Update the refcnt while holding the SPTREE lock. @@ -586,9 +582,8 @@ sa_delref(struct secasvar *sav) void key_addref(struct secpolicy *sp) { - SPTREE_LOCK(); + SP_ADDREF(sp); - SPTREE_UNLOCK(); } /* @@ -601,7 +596,7 @@ key_havesp(u_int dir) { return (dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND ? - LIST_FIRST(&V_sptree[dir]) != NULL : 1); + TAILQ_FIRST(&V_sptree[dir]) != NULL : 1); } /* %%% IPsec policy management */ @@ -615,6 +610,7 @@ struct secpolicy * key_allocsp(struct secpolicyindex *spidx, u_int dir, const char* where, int tag) { + SPTREE_RLOCK_TRACKER; struct secpolicy *sp; IPSEC_ASSERT(spidx != NULL, ("null spidx")); @@ -629,14 +625,11 @@ key_allocsp(struct secpolicyindex *spidx, u_int dir, const char* where, printf("*** objects\n"); kdebug_secpolicyindex(spidx)); - SPTREE_LOCK(); - LIST_FOREACH(sp, &V_sptree[dir], chain) { + SPTREE_RLOCK(); + TAILQ_FOREACH(sp, &V_sptree[dir], chain) { KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("*** in SPD\n"); kdebug_secpolicyindex(&sp->spidx)); - - if (sp->state == IPSEC_SPSTATE_DEAD) - continue; if (key_cmpspidx_withmask(&sp->spidx, spidx)) goto found; } @@ -650,7 +643,7 @@ found: sp->lastused = time_second; SP_ADDREF(sp); } - SPTREE_UNLOCK(); + SPTREE_RUNLOCK(); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP %s return SP:%p (ID=%u) refcnt %u\n", __func__, @@ -668,6 +661,7 @@ struct secpolicy * key_allocsp2(u_int32_t spi, union sockaddr_union *dst, u_int8_t proto, u_int dir, const char* where, int tag) { + SPTREE_RLOCK_TRACKER; struct secpolicy *sp; IPSEC_ASSERT(dst != NULL, ("null dst")); @@ -683,14 +677,11 @@ key_allocsp2(u_int32_t spi, union sockaddr_union *dst, u_int8_t proto, printf("spi %u proto %u dir %u\n", spi, proto, dir); kdebug_sockaddr(&dst->sa)); - SPTREE_LOCK(); - LIST_FOREACH(sp, &V_sptree[dir], chain) { + SPTREE_RLOCK(); + TAILQ_FOREACH(sp, &V_sptree[dir], chain) { KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("*** in SPD\n"); kdebug_secpolicyindex(&sp->spidx)); - - if (sp->state == IPSEC_SPSTATE_DEAD) - continue; /* compare simple values, then dst address */ if (sp->spidx.ul_proto != proto) continue; @@ -710,7 +701,7 @@ found: sp->lastused = time_second; SP_ADDREF(sp); } - SPTREE_UNLOCK(); + SPTREE_RUNLOCK(); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP %s return SP:%p (ID=%u) refcnt %u\n", __func__, @@ -1162,6 +1153,66 @@ done: return sav; } +struct secasvar * +key_allocsa_tunnel(union sockaddr_union *src, union sockaddr_union *dst, + u_int proto, const char* where, int tag) +{ + struct secashead *sah; + struct secasvar *sav; + u_int stateidx, arraysize, state; + const u_int *saorder_state_valid; + + IPSEC_ASSERT(src != NULL, ("null src address")); + IPSEC_ASSERT(dst != NULL, ("null dst address")); + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP %s from %s:%u\n", __func__, where, tag)); + + SAHTREE_LOCK(); + if (V_key_preferred_oldsa) { + saorder_state_valid = saorder_state_valid_prefer_old; + arraysize = _ARRAYLEN(saorder_state_valid_prefer_old); + } else { + saorder_state_valid = saorder_state_valid_prefer_new; + arraysize = _ARRAYLEN(saorder_state_valid_prefer_new); + } + LIST_FOREACH(sah, &V_sahtree, chain) { + /* search valid state */ + for (stateidx = 0; stateidx < arraysize; stateidx++) { + state = saorder_state_valid[stateidx]; + LIST_FOREACH(sav, &sah->savtree[state], chain) { + /* sanity check */ + KEY_CHKSASTATE(sav->state, state, __func__); + /* do not return entries w/ unusable state */ + if (sav->state != SADB_SASTATE_MATURE && + sav->state != SADB_SASTATE_DYING) + continue; + if (IPSEC_MODE_TUNNEL != sav->sah->saidx.mode) + continue; + if (proto != sav->sah->saidx.proto) + continue; + /* check src address */ + if (key_sockaddrcmp(&src->sa, + &sav->sah->saidx.src.sa, 0) != 0) + continue; + /* check dst address */ + if (key_sockaddrcmp(&dst->sa, + &sav->sah->saidx.dst.sa, 0) != 0) + continue; + sa_addref(sav); + goto done; + } + } + } + sav = NULL; +done: + SAHTREE_UNLOCK(); + + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP %s return SA:%p; refcnt %u\n", __func__, + sav, sav ? sav->refcnt : 0)); + return (sav); +} + /* * Must be called after calling key_allocsp(). * For both the packet without socket and key_freeso(). @@ -1169,22 +1220,70 @@ done: void _key_freesp(struct secpolicy **spp, const char* where, int tag) { + struct ipsecrequest *isr, *nextisr; struct secpolicy *sp = *spp; IPSEC_ASSERT(sp != NULL, ("null sp")); - - SPTREE_LOCK(); - SP_DELREF(sp); - KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP %s SP:%p (ID=%u) from %s:%u; refcnt now %u\n", __func__, sp, sp->id, where, tag, sp->refcnt)); - if (sp->refcnt == 0) { - *spp = NULL; - key_delsp(sp); + if (SP_DELREF(sp) == 0) + return; + *spp = NULL; + for (isr = sp->req; isr != NULL; isr = nextisr) { + if (isr->sav != NULL) { + KEY_FREESAV(&isr->sav); + isr->sav = NULL; + } + nextisr = isr->next; + ipsec_delisr(isr); + } + free(sp, M_IPSEC_SP); +} + +static void +key_unlink(struct secpolicy *sp) +{ + + IPSEC_ASSERT(sp != NULL, ("null sp")); + IPSEC_ASSERT(sp->spidx.dir == IPSEC_DIR_INBOUND || + sp->spidx.dir == IPSEC_DIR_OUTBOUND, + ("invalid direction %u", sp->spidx.dir)); + SPTREE_UNLOCK_ASSERT(); + + SPTREE_WLOCK(); + if (sp->state == IPSEC_SPSTATE_DEAD) { + SPTREE_WUNLOCK(); + return; } - SPTREE_UNLOCK(); + sp->state = IPSEC_SPSTATE_DEAD; + TAILQ_REMOVE(&V_sptree[sp->spidx.dir], sp, chain); + SPTREE_WUNLOCK(); + KEY_FREESP(&sp); +} + +/* + * insert a secpolicy into the SP database. Lower priorities first + */ +static void +key_insertsp(struct secpolicy *newsp) +{ + struct secpolicy *sp; + + SPTREE_WLOCK(); + TAILQ_FOREACH(sp, &V_sptree[newsp->spidx.dir], chain) { + if (newsp->priority < sp->priority) { + TAILQ_INSERT_BEFORE(sp, newsp, chain); + goto done; + } + } + + TAILQ_INSERT_TAIL(&V_sptree[newsp->spidx.dir], newsp, chain); + +done: + newsp->state = IPSEC_SPSTATE_ALIVE; + SPTREE_WUNLOCK(); } /* @@ -1273,38 +1372,6 @@ key_freesav(struct secasvar **psav, const char* where, int tag) /* %%% SPD management */ /* - * free security policy entry. - */ -static void -key_delsp(struct secpolicy *sp) -{ - struct ipsecrequest *isr, *nextisr; - - IPSEC_ASSERT(sp != NULL, ("null sp")); - SPTREE_LOCK_ASSERT(); - - sp->state = IPSEC_SPSTATE_DEAD; - - IPSEC_ASSERT(sp->refcnt == 0, - ("SP with references deleted (refcnt %u)", sp->refcnt)); - - /* remove from SP index */ - if (__LIST_CHAINED(sp)) - LIST_REMOVE(sp, chain); - - for (isr = sp->req; isr != NULL; isr = nextisr) { - if (isr->sav != NULL) { - KEY_FREESAV(&isr->sav); - isr->sav = NULL; - } - - nextisr = isr->next; - ipsec_delisr(isr); - } - _key_delsp(sp); -} - -/* * search SPD * OUT: NULL : not found * others : found, pointer to a SP. @@ -1312,20 +1379,19 @@ key_delsp(struct secpolicy *sp) static struct secpolicy * key_getsp(struct secpolicyindex *spidx) { + SPTREE_RLOCK_TRACKER; struct secpolicy *sp; IPSEC_ASSERT(spidx != NULL, ("null spidx")); - SPTREE_LOCK(); - LIST_FOREACH(sp, &V_sptree[spidx->dir], chain) { - if (sp->state == IPSEC_SPSTATE_DEAD) - continue; + SPTREE_RLOCK(); + TAILQ_FOREACH(sp, &V_sptree[spidx->dir], chain) { if (key_cmpspidx_exactly(spidx, &sp->spidx)) { SP_ADDREF(sp); break; } } - SPTREE_UNLOCK(); + SPTREE_RUNLOCK(); return sp; } @@ -1338,28 +1404,25 @@ key_getsp(struct secpolicyindex *spidx) static struct secpolicy * key_getspbyid(u_int32_t id) { + SPTREE_RLOCK_TRACKER; struct secpolicy *sp; - SPTREE_LOCK(); - LIST_FOREACH(sp, &V_sptree[IPSEC_DIR_INBOUND], chain) { - if (sp->state == IPSEC_SPSTATE_DEAD) - continue; + SPTREE_RLOCK(); + TAILQ_FOREACH(sp, &V_sptree[IPSEC_DIR_INBOUND], chain) { if (sp->id == id) { SP_ADDREF(sp); goto done; } } - LIST_FOREACH(sp, &V_sptree[IPSEC_DIR_OUTBOUND], chain) { - if (sp->state == IPSEC_SPSTATE_DEAD) - continue; + TAILQ_FOREACH(sp, &V_sptree[IPSEC_DIR_OUTBOUND], chain) { if (sp->id == id) { SP_ADDREF(sp); goto done; } } done: - SPTREE_UNLOCK(); + SPTREE_RUNLOCK(); return sp; } @@ -1371,11 +1434,8 @@ key_newsp(const char* where, int tag) newsp = (struct secpolicy *) malloc(sizeof(struct secpolicy), M_IPSEC_SP, M_NOWAIT|M_ZERO); - if (newsp) { - SECPOLICY_LOCK_INIT(newsp); - newsp->refcnt = 1; - newsp->req = NULL; - } + if (newsp) + refcount_init(&newsp->refcnt, 1); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP %s from %s:%u return SP:%p\n", __func__, @@ -1383,13 +1443,6 @@ key_newsp(const char* where, int tag) return newsp; } -static void -_key_delsp(struct secpolicy *sp) -{ - SECPOLICY_LOCK_DESTROY(sp); - free(sp, M_IPSEC_SP); -} - /* * create secpolicy structure from sadb_x_policy structure. * NOTE: `state', `secpolicyindex' in secpolicy structure are not set, @@ -1416,6 +1469,7 @@ key_msg2sp(struct sadb_x_policy *xpl0, size_t len, int *error) newsp->spidx.dir = xpl0->sadb_x_policy_dir; newsp->policy = xpl0->sadb_x_policy_type; + newsp->priority = xpl0->sadb_x_policy_priority; /* check policy */ switch (xpl0->sadb_x_policy_type) { @@ -1652,6 +1706,7 @@ key_sp2msg(struct secpolicy *sp) xpl->sadb_x_policy_type = sp->policy; xpl->sadb_x_policy_dir = sp->spidx.dir; xpl->sadb_x_policy_id = sp->id; + xpl->sadb_x_policy_priority = sp->priority; p = (caddr_t)xpl + sizeof(*xpl); /* if is the policy for ipsec ? */ @@ -1869,9 +1924,7 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp) newsp = key_getsp(&spidx); if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) { if (newsp) { - SPTREE_LOCK(); - newsp->state = IPSEC_SPSTATE_DEAD; - SPTREE_UNLOCK(); + key_unlink(newsp); KEY_FREESP(&newsp); } } else { @@ -1883,13 +1936,15 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp) } } + /* XXX: there is race between key_getsp and key_msg2sp. */ + /* allocation new SP entry */ if ((newsp = key_msg2sp(xpl0, PFKEY_EXTLEN(xpl0), &error)) == NULL) { return key_senderror(so, m, error); } if ((newsp->id = key_getnewspid()) == 0) { - _key_delsp(newsp); + KEY_FREESP(&newsp); return key_senderror(so, m, ENOBUFS); } @@ -1905,18 +1960,20 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp) /* sanity check on addr pair */ if (((struct sockaddr *)(src0 + 1))->sa_family != ((struct sockaddr *)(dst0+ 1))->sa_family) { - _key_delsp(newsp); + KEY_FREESP(&newsp); return key_senderror(so, m, EINVAL); } if (((struct sockaddr *)(src0 + 1))->sa_len != ((struct sockaddr *)(dst0+ 1))->sa_len) { - _key_delsp(newsp); + KEY_FREESP(&newsp); return key_senderror(so, m, EINVAL); } #if 1 - if (newsp->req && newsp->req->saidx.src.sa.sa_family && newsp->req->saidx.dst.sa.sa_family) { - if (newsp->req->saidx.src.sa.sa_family != newsp->req->saidx.dst.sa.sa_family) { - _key_delsp(newsp); + if (newsp->req && newsp->req->saidx.src.sa.sa_family && + newsp->req->saidx.dst.sa.sa_family) { + if (newsp->req->saidx.src.sa.sa_family != + newsp->req->saidx.dst.sa.sa_family) { + KEY_FREESP(&newsp); return key_senderror(so, m, EINVAL); } } @@ -1927,9 +1984,7 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp) 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; - LIST_INSERT_TAIL(&V_sptree[newsp->spidx.dir], newsp, secpolicy, chain); + key_insertsp(newsp); /* delete the entry in spacqtree */ if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) { @@ -2104,9 +2159,7 @@ key_spddelete(struct socket *so, struct mbuf *m, /* save policy id to buffer to be returned. */ xpl0->sadb_x_policy_id = sp->id; - SPTREE_LOCK(); - sp->state = IPSEC_SPSTATE_DEAD; - SPTREE_UNLOCK(); + key_unlink(sp); KEY_FREESP(&sp); { @@ -2171,9 +2224,7 @@ key_spddelete2(struct socket *so, struct mbuf *m, return key_senderror(so, m, EINVAL); } - SPTREE_LOCK(); - sp->state = IPSEC_SPSTATE_DEAD; - SPTREE_UNLOCK(); + key_unlink(sp); KEY_FREESP(&sp); { @@ -2352,8 +2403,9 @@ key_spdacquire(struct secpolicy *sp) static int key_spdflush(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp) { + TAILQ_HEAD(, secpolicy) drainq; struct sadb_msg *newmsg; - struct secpolicy *sp; + struct secpolicy *sp, *nextsp; u_int dir; IPSEC_ASSERT(so != NULL, ("null socket")); @@ -2364,11 +2416,23 @@ key_spdflush(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp) if (m->m_len != PFKEY_ALIGN8(sizeof(struct sadb_msg))) return key_senderror(so, m, EINVAL); + TAILQ_INIT(&drainq); + SPTREE_WLOCK(); for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { - SPTREE_LOCK(); - LIST_FOREACH(sp, &V_sptree[dir], chain) - sp->state = IPSEC_SPSTATE_DEAD; - SPTREE_UNLOCK(); + TAILQ_CONCAT(&drainq, &V_sptree[dir], chain); + } + /* + * We need to set state to DEAD for each policy to be sure, + * that another thread won't try to unlink it. + */ + TAILQ_FOREACH(sp, &drainq, chain) + sp->state = IPSEC_SPSTATE_DEAD; + SPTREE_WUNLOCK(); + sp = TAILQ_FIRST(&drainq); + while (sp != NULL) { + nextsp = TAILQ_NEXT(sp, chain); + KEY_FREESP(&sp); + sp = nextsp; } if (sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) { @@ -2401,6 +2465,7 @@ key_spdflush(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp) static int key_spddump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp) { + SPTREE_RLOCK_TRACKER; struct secpolicy *sp; int cnt; u_int dir; @@ -2413,20 +2478,20 @@ key_spddump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp) /* search SPD entry and get buffer size. */ cnt = 0; - SPTREE_LOCK(); + SPTREE_RLOCK(); for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { - LIST_FOREACH(sp, &V_sptree[dir], chain) { + TAILQ_FOREACH(sp, &V_sptree[dir], chain) { cnt++; } } if (cnt == 0) { - SPTREE_UNLOCK(); + SPTREE_RUNLOCK(); return key_senderror(so, m, ENOENT); } for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { - LIST_FOREACH(sp, &V_sptree[dir], chain) { + TAILQ_FOREACH(sp, &V_sptree[dir], chain) { --cnt; n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt, mhp->msg->sadb_msg_pid); @@ -2436,7 +2501,7 @@ key_spddump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp) } } - SPTREE_UNLOCK(); + SPTREE_RUNLOCK(); m_freem(m); return 0; } @@ -2848,7 +2913,6 @@ key_cleansav(struct secasvar *sav) sav->tdb_xform->xf_zeroize(sav); sav->tdb_xform = NULL; } else { - KASSERT(sav->iv == NULL, ("iv but no xform")); if (sav->key_auth != NULL) bzero(sav->key_auth->key_data, _KEYLEN(sav->key_auth)); if (sav->key_enc != NULL) @@ -3026,7 +3090,6 @@ key_setsaval(struct secasvar *sav, struct mbuf *m, sav->key_enc = NULL; sav->sched = NULL; sav->schedlen = 0; - sav->iv = NULL; sav->lft_c = NULL; sav->lft_h = NULL; sav->lft_s = NULL; @@ -3483,6 +3546,7 @@ key_setdumpsa(struct secasvar *sav, u_int8_t type, u_int8_t satype, } m_cat(result, tres); + tres = NULL; if (result->m_len < sizeof(struct sadb_msg)) { result = m_pullup(result, sizeof(struct sadb_msg)); if (result == NULL) @@ -3760,7 +3824,7 @@ key_porttosaddr(struct sockaddr *sa, u_int16_t port) * set data into sadb_x_policy */ static struct mbuf * -key_setsadbxpolicy(u_int16_t type, u_int8_t dir, u_int32_t id) +key_setsadbxpolicy(u_int16_t type, u_int8_t dir, u_int32_t id, u_int32_t priority) { struct mbuf *m; struct sadb_x_policy *p; @@ -3780,6 +3844,7 @@ key_setsadbxpolicy(u_int16_t type, u_int8_t dir, u_int32_t id) p->sadb_x_policy_type = type; p->sadb_x_policy_dir = dir; p->sadb_x_policy_id = id; + p->sadb_x_policy_priority = priority; return m; } @@ -3871,48 +3936,19 @@ key_ismyaddr(struct sockaddr *sa) * compare my own address for IPv6. * 1: ours * 0: other - * NOTE: derived ip6_input() in KAME. This is necessary to modify more. */ -#include <netinet6/in6_var.h> - static int key_ismyaddr6(struct sockaddr_in6 *sin6) { - struct in6_ifaddr *ia; -#if 0 - struct in6_multi *in6m; -#endif - - IN6_IFADDR_RLOCK(); - TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) { - if (key_sockaddrcmp((struct sockaddr *)sin6, - (struct sockaddr *)&ia->ia_addr, 0) == 0) { - IN6_IFADDR_RUNLOCK(); - return 1; - } - -#if 0 - /* - * XXX Multicast - * XXX why do we care about multlicast here while we don't care - * about IPv4 multicast?? - * XXX scope - */ - in6m = NULL; - IN6_LOOKUP_MULTI(sin6->sin6_addr, ia->ia_ifp, in6m); - if (in6m) { - IN6_IFADDR_RUNLOCK(); - return 1; - } -#endif - } - IN6_IFADDR_RUNLOCK(); + struct in6_addr in6; - /* loopback, just for safety */ - if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) - return 1; + if (!IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) + return (in6_localip(&sin6->sin6_addr)); - return 0; + /* Convert address into kernel-internal form */ + in6 = sin6->sin6_addr; + in6.s6_addr16[1] = htons(sin6->sin6_scope_id & 0xffff); + return (in6_localip(&in6)); } #endif /*INET6*/ @@ -4222,47 +4258,30 @@ key_bbcmp(const void *a1, const void *a2, u_int bits) static void key_flush_spd(time_t now) { - static u_int16_t sptree_scangen = 0; - u_int16_t gen = sptree_scangen++; + SPTREE_RLOCK_TRACKER; struct secpolicy *sp; u_int dir; /* SPD */ for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { restart: - SPTREE_LOCK(); - LIST_FOREACH(sp, &V_sptree[dir], chain) { - if (sp->scangen == gen) /* previously handled */ - continue; - sp->scangen = gen; - if (sp->state == IPSEC_SPSTATE_DEAD && - sp->refcnt == 1) { - /* - * Ensure that we only decrease refcnt once, - * when we're the last consumer. - * Directly call SP_DELREF/key_delsp instead - * of KEY_FREESP to avoid unlocking/relocking - * SPTREE_LOCK before key_delsp: may refcnt - * be increased again during that time ? - * NB: also clean entries created by - * key_spdflush - */ - SP_DELREF(sp); - key_delsp(sp); - SPTREE_UNLOCK(); - goto restart; - } + SPTREE_RLOCK(); + TAILQ_FOREACH(sp, &V_sptree[dir], chain) { if (sp->lifetime == 0 && sp->validtime == 0) continue; - if ((sp->lifetime && now - sp->created > sp->lifetime) - || (sp->validtime && now - sp->lastused > sp->validtime)) { - sp->state = IPSEC_SPSTATE_DEAD; - SPTREE_UNLOCK(); + if ((sp->lifetime && + now - sp->created > sp->lifetime) || + (sp->validtime && + now - sp->lastused > sp->validtime)) { + SP_ADDREF(sp); + SPTREE_RUNLOCK(); key_spdexpire(sp); + key_unlink(sp); + KEY_FREESP(&sp); goto restart; } } - SPTREE_UNLOCK(); + SPTREE_RUNLOCK(); } } @@ -6268,7 +6287,7 @@ key_acquire(const struct secasindex *saidx, struct secpolicy *sp) /* set sadb_x_policy */ if (sp) { - m = key_setsadbxpolicy(sp->policy, sp->spidx.dir, sp->id); + m = key_setsadbxpolicy(sp->policy, sp->spidx.dir, sp->id, sp->priority); if (!m) { error = ENOBUFS; goto fail; @@ -6770,7 +6789,7 @@ key_register(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp) continue; alg = (struct sadb_alg *)(mtod(n, caddr_t) + off); alg->sadb_alg_id = i; - alg->sadb_alg_ivlen = ealgo->blocksize; + alg->sadb_alg_ivlen = ealgo->ivsize; alg->sadb_alg_minbits = _BITS(ealgo->minkey); alg->sadb_alg_maxbits = _BITS(ealgo->maxkey); off += PFKEY_ALIGN8(sizeof(struct sadb_alg)); @@ -7654,7 +7673,7 @@ key_init(void) int i; for (i = 0; i < IPSEC_DIR_MAX; i++) - LIST_INIT(&V_sptree[i]); + TAILQ_INIT(&V_sptree[i]); LIST_INIT(&V_sahtree); @@ -7664,10 +7683,6 @@ key_init(void) LIST_INIT(&V_acqtree); LIST_INIT(&V_spacqtree); - /* system default */ - V_ip4_def_policy.policy = IPSEC_POLICY_NONE; - V_ip4_def_policy.refcnt++; /*never reclaim this*/ - if (!IS_DEFAULT_VNET(curvnet)) return; @@ -7684,13 +7699,15 @@ key_init(void) /* initialize key statistics */ keystat.getspi_count = 1; - printf("IPsec: Initialized Security Association Processing.\n"); + if (bootverbose) + printf("IPsec: Initialized Security Association Processing.\n"); } #ifdef VIMAGE void key_destroy(void) { + TAILQ_HEAD(, secpolicy) drainq; struct secpolicy *sp, *nextsp; struct secacq *acq, *nextacq; struct secspacq *spacq, *nextspacq; @@ -7698,18 +7715,18 @@ key_destroy(void) struct secreg *reg; int i; - SPTREE_LOCK(); + TAILQ_INIT(&drainq); + SPTREE_WLOCK(); for (i = 0; i < IPSEC_DIR_MAX; i++) { - for (sp = LIST_FIRST(&V_sptree[i]); - sp != NULL; sp = nextsp) { - nextsp = LIST_NEXT(sp, chain); - if (__LIST_CHAINED(sp)) { - LIST_REMOVE(sp, chain); - free(sp, M_IPSEC_SP); - } - } + TAILQ_CONCAT(&drainq, &V_sptree[i], chain); + } + SPTREE_WUNLOCK(); + sp = TAILQ_FIRST(&drainq); + while (sp != NULL) { + nextsp = TAILQ_NEXT(sp, chain); + KEY_FREESP(&sp); + sp = nextsp; } - SPTREE_UNLOCK(); SAHTREE_LOCK(); for (sah = LIST_FIRST(&V_sahtree); sah != NULL; sah = nextsah) { @@ -7830,14 +7847,6 @@ key_sa_chgstate(struct secasvar *sav, u_int8_t state) } } -void -key_sa_stir_iv(struct secasvar *sav) -{ - - IPSEC_ASSERT(sav->iv != NULL, ("null IV")); - key_randomfill(sav->iv, sav->ivlen); -} - /* * Take one of the kernel's security keys and convert it into a PF_KEY * structure within an mbuf, suitable for sending up to a waiting |