Changeset View
Changeset View
Standalone View
Standalone View
sys/netipsec/key.c
Show First 20 Lines • Show All 210 Lines • ▼ Show 20 Lines | |||||
#define SPDCACHE_LOCK_INIT(a) \ | #define SPDCACHE_LOCK_INIT(a) \ | ||||
mtx_init(&V_spdcache_lock[a], "spdcache", \ | mtx_init(&V_spdcache_lock[a], "spdcache", \ | ||||
"fast ipsec SPD cache", MTX_DEF|MTX_DUPOK) | "fast ipsec SPD cache", MTX_DEF|MTX_DUPOK) | ||||
#define SPDCACHE_LOCK_DESTROY(a) mtx_destroy(&V_spdcache_lock[a]) | #define SPDCACHE_LOCK_DESTROY(a) mtx_destroy(&V_spdcache_lock[a]) | ||||
#define SPDCACHE_LOCK(a) mtx_lock(&V_spdcache_lock[a]); | #define SPDCACHE_LOCK(a) mtx_lock(&V_spdcache_lock[a]); | ||||
#define SPDCACHE_UNLOCK(a) mtx_unlock(&V_spdcache_lock[a]); | #define SPDCACHE_UNLOCK(a) mtx_unlock(&V_spdcache_lock[a]); | ||||
static struct sx spi_alloc_lock; | |||||
mjg: i tried an mtx lock but it ran into problems with apparent malloc called with M_WAITOK down the… | |||||
#define SPI_ALLOC_LOCK_INIT() sx_init(&spi_alloc_lock, "spialloc") | |||||
#define SPI_ALLOC_LOCK_DESTROY() sx_destroy(&spi_alloc_lock) | |||||
#define SPI_ALLOC_LOCK() sx_xlock(&spi_alloc_lock) | |||||
#define SPI_ALLOC_UNLOCK() sx_unlock(&spi_alloc_lock) | |||||
#define SPI_ALLOC_LOCK_ASSERT() sx_assert(&spi_alloc_lock, SA_XLOCKED) | |||||
/* SAD */ | /* SAD */ | ||||
TAILQ_HEAD(secashead_queue, secashead); | TAILQ_HEAD(secashead_queue, secashead); | ||||
LIST_HEAD(secashead_list, secashead); | LIST_HEAD(secashead_list, secashead); | ||||
VNET_DEFINE_STATIC(struct secashead_queue, sahtree); | VNET_DEFINE_STATIC(struct secashead_queue, sahtree); | ||||
static struct rmlock sahtree_lock; | static struct rmlock sahtree_lock; | ||||
#define V_sahtree VNET(sahtree) | #define V_sahtree VNET(sahtree) | ||||
#define SAHTREE_LOCK_INIT() rm_init(&sahtree_lock, "sahtree") | #define SAHTREE_LOCK_INIT() rm_init(&sahtree_lock, "sahtree") | ||||
#define SAHTREE_LOCK_DESTROY() rm_destroy(&sahtree_lock) | #define SAHTREE_LOCK_DESTROY() rm_destroy(&sahtree_lock) | ||||
▲ Show 20 Lines • Show All 4,670 Lines • ▼ Show 20 Lines | #endif | ||||
if (error != 0) { | if (error != 0) { | ||||
ipseclog((LOG_DEBUG, "%s: invalid sockaddr.\n", __func__)); | ipseclog((LOG_DEBUG, "%s: invalid sockaddr.\n", __func__)); | ||||
error = EINVAL; | error = EINVAL; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); | KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); | ||||
/* SPI allocation */ | /* SPI allocation */ | ||||
SPI_ALLOC_LOCK(); | |||||
spi = key_do_getnewspi( | spi = key_do_getnewspi( | ||||
(struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE], &saidx); | (struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE], &saidx); | ||||
if (spi == 0) { | if (spi == 0) { | ||||
/* | /* | ||||
* Requested SPI or SPI range is not available or | * Requested SPI or SPI range is not available or | ||||
* already used. | * already used. | ||||
*/ | */ | ||||
SPI_ALLOC_UNLOCK(); | |||||
error = EEXIST; | error = EEXIST; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
sav = key_newsav(mhp, &saidx, spi, &error); | sav = key_newsav(mhp, &saidx, spi, &error); | ||||
SPI_ALLOC_UNLOCK(); | |||||
if (sav == NULL) | if (sav == NULL) | ||||
goto fail; | goto fail; | ||||
if (sav->seq != 0) { | if (sav->seq != 0) { | ||||
/* | /* | ||||
* RFC2367: | * RFC2367: | ||||
* If the SADB_GETSPI message is in response to a | * If the SADB_GETSPI message is in response to a | ||||
* kernel-generated SADB_ACQUIRE, the sadb_msg_seq | * kernel-generated SADB_ACQUIRE, the sadb_msg_seq | ||||
▲ Show 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | |||||
* OUT: | * OUT: | ||||
* 0: failure. | * 0: failure. | ||||
* others: success, SPI in network byte order. | * others: success, SPI in network byte order. | ||||
*/ | */ | ||||
static uint32_t | static uint32_t | ||||
key_do_getnewspi(struct sadb_spirange *spirange, struct secasindex *saidx) | key_do_getnewspi(struct sadb_spirange *spirange, struct secasindex *saidx) | ||||
{ | { | ||||
uint32_t min, max, newspi, t; | uint32_t min, max, newspi, t; | ||||
int count = V_key_spi_trycnt; | int tries, limit; | ||||
SPI_ALLOC_LOCK_ASSERT(); | |||||
/* set spi range to allocate */ | /* set spi range to allocate */ | ||||
if (spirange != NULL) { | if (spirange != NULL) { | ||||
min = spirange->sadb_spirange_min; | min = spirange->sadb_spirange_min; | ||||
max = spirange->sadb_spirange_max; | max = spirange->sadb_spirange_max; | ||||
} else { | } else { | ||||
min = V_key_spi_minval; | min = V_key_spi_minval; | ||||
max = V_key_spi_maxval; | max = V_key_spi_maxval; | ||||
} | } | ||||
Show All 10 Lines | key_do_getnewspi(struct sadb_spirange *spirange, struct secasindex *saidx) | ||||
if (min == max) { | if (min == max) { | ||||
if (!key_checkspidup(htonl(min))) { | if (!key_checkspidup(htonl(min))) { | ||||
ipseclog((LOG_DEBUG, "%s: SPI %u exists already.\n", | ipseclog((LOG_DEBUG, "%s: SPI %u exists already.\n", | ||||
__func__, min)); | __func__, min)); | ||||
return 0; | return 0; | ||||
} | } | ||||
count--; /* taking one cost. */ | tries = 1; | ||||
newspi = min; | newspi = min; | ||||
} else { | } else { | ||||
/* init SPI */ | /* init SPI */ | ||||
newspi = 0; | newspi = 0; | ||||
limit = atomic_load_int(&V_key_spi_trycnt); | |||||
/* when requesting to allocate spi ranged */ | /* when requesting to allocate spi ranged */ | ||||
while (count--) { | for (tries = 0; tries < limit; tries++) { | ||||
/* generate pseudo-random SPI value ranged. */ | /* generate pseudo-random SPI value ranged. */ | ||||
newspi = min + (key_random() % (max - min + 1)); | newspi = min + (key_random() % (max - min + 1)); | ||||
if (!key_checkspidup(htonl(newspi))) | if (!key_checkspidup(htonl(newspi))) | ||||
break; | break; | ||||
} | } | ||||
if (count == 0 || newspi == 0) { | if (tries == limit || newspi == 0) { | ||||
ipseclog((LOG_DEBUG, | ipseclog((LOG_DEBUG, | ||||
"%s: failed to allocate SPI.\n", __func__)); | "%s: failed to allocate SPI.\n", __func__)); | ||||
return 0; | return 0; | ||||
} | } | ||||
} | } | ||||
/* statistics */ | /* statistics */ | ||||
keystat.getspi_count = | keystat.getspi_count = | ||||
(keystat.getspi_count + V_key_spi_trycnt - count) / 2; | (keystat.getspi_count + tries) / 2; | ||||
return (htonl(newspi)); | return (htonl(newspi)); | ||||
} | } | ||||
/* | /* | ||||
* Find TCP-MD5 SA with corresponding secasindex. | * Find TCP-MD5 SA with corresponding secasindex. | ||||
* If not found, return NULL and fill SPI with usable value if needed. | * If not found, return NULL and fill SPI with usable value if needed. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 541 Lines • ▼ Show 20 Lines | #endif | ||||
} | } | ||||
KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); | KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); | ||||
spi = sa0->sadb_sa_spi; | spi = sa0->sadb_sa_spi; | ||||
/* | /* | ||||
* For TCP-MD5 SAs we don't use SPI. Check the uniqueness using | * For TCP-MD5 SAs we don't use SPI. Check the uniqueness using | ||||
* secasindex. | * secasindex. | ||||
* XXXAE: IPComp seems also doesn't use SPI. | * XXXAE: IPComp seems also doesn't use SPI. | ||||
*/ | */ | ||||
SPI_ALLOC_LOCK(); | |||||
if (proto == IPPROTO_TCP) { | if (proto == IPPROTO_TCP) { | ||||
sav = key_getsav_tcpmd5(&saidx, &spi); | sav = key_getsav_tcpmd5(&saidx, &spi); | ||||
if (sav == NULL && spi == 0) { | if (sav == NULL && spi == 0) { | ||||
/* Failed to allocate SPI */ | /* Failed to allocate SPI */ | ||||
ipseclog((LOG_DEBUG, "%s: SA already exists.\n", | ipseclog((LOG_DEBUG, "%s: SA already exists.\n", | ||||
__func__)); | __func__)); | ||||
return key_senderror(so, m, EEXIST); | return key_senderror(so, m, EEXIST); | ||||
aeUnsubmitted Not Done Inline Actionsmissing SPI_ALLOC_UNLOCK(); ae: missing SPI_ALLOC_UNLOCK(); | |||||
} | } | ||||
/* XXX: SPI that we report back can have another value */ | /* XXX: SPI that we report back can have another value */ | ||||
} else { | } else { | ||||
/* We can create new SA only if SPI is different. */ | /* We can create new SA only if SPI is different. */ | ||||
sav = key_getsavbyspi(spi); | sav = key_getsavbyspi(spi); | ||||
} | } | ||||
if (sav != NULL) { | if (sav != NULL) { | ||||
SPI_ALLOC_UNLOCK(); | |||||
key_freesav(&sav); | key_freesav(&sav); | ||||
ipseclog((LOG_DEBUG, "%s: SA already exists.\n", __func__)); | ipseclog((LOG_DEBUG, "%s: SA already exists.\n", __func__)); | ||||
return key_senderror(so, m, EEXIST); | return key_senderror(so, m, EEXIST); | ||||
} | } | ||||
sav = key_newsav(mhp, &saidx, spi, &error); | sav = key_newsav(mhp, &saidx, spi, &error); | ||||
SPI_ALLOC_UNLOCK(); | |||||
if (sav == NULL) | if (sav == NULL) | ||||
return key_senderror(so, m, error); | return key_senderror(so, m, error); | ||||
KEYDBG(KEY_STAMP, | KEYDBG(KEY_STAMP, | ||||
printf("%s: return SA(%p)\n", __func__, sav)); | printf("%s: return SA(%p)\n", __func__, sav)); | ||||
KEYDBG(KEY_DATA, kdebug_secasv(sav)); | KEYDBG(KEY_DATA, kdebug_secasv(sav)); | ||||
/* | /* | ||||
* If SADB_ADD was in response to SADB_ACQUIRE, we need to schedule | * If SADB_ADD was in response to SADB_ACQUIRE, we need to schedule | ||||
* ACQ for deletion. | * ACQ for deletion. | ||||
▲ Show 20 Lines • Show All 358 Lines • ▼ Show 20 Lines | if (SADB_CHECKHDR(mhp, SADB_EXT_SA)) { | ||||
return (key_delete_all(so, m, mhp, &saidx)); | return (key_delete_all(so, m, mhp, &saidx)); | ||||
} | } | ||||
if (SADB_CHECKLEN(mhp, SADB_EXT_SA)) { | if (SADB_CHECKLEN(mhp, SADB_EXT_SA)) { | ||||
ipseclog((LOG_DEBUG, | ipseclog((LOG_DEBUG, | ||||
"%s: invalid message: wrong header size.\n", __func__)); | "%s: invalid message: wrong header size.\n", __func__)); | ||||
return (key_senderror(so, m, EINVAL)); | return (key_senderror(so, m, EINVAL)); | ||||
} | } | ||||
sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; | sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; | ||||
SPI_ALLOC_LOCK(); | |||||
if (proto == IPPROTO_TCP) | if (proto == IPPROTO_TCP) | ||||
sav = key_getsav_tcpmd5(&saidx, NULL); | sav = key_getsav_tcpmd5(&saidx, NULL); | ||||
else | else | ||||
sav = key_getsavbyspi(sa0->sadb_sa_spi); | sav = key_getsavbyspi(sa0->sadb_sa_spi); | ||||
if (sav == NULL) { | if (sav == NULL) { | ||||
aeUnsubmitted Not Done Inline ActionsIt seems the locking here needed only to satisfy KASSERT in key_do_getnewspi(). So you can unlock here and remove SPI_ALLOC_UNLOCK() in each following condition. ae: It seems the locking here needed only to satisfy KASSERT in key_do_getnewspi(). So you can… | |||||
SPI_ALLOC_UNLOCK(); | |||||
ipseclog((LOG_DEBUG, "%s: no SA found for SPI %u.\n", | ipseclog((LOG_DEBUG, "%s: no SA found for SPI %u.\n", | ||||
__func__, ntohl(sa0->sadb_sa_spi))); | __func__, ntohl(sa0->sadb_sa_spi))); | ||||
return (key_senderror(so, m, ESRCH)); | return (key_senderror(so, m, ESRCH)); | ||||
} | } | ||||
if (key_cmpsaidx(&sav->sah->saidx, &saidx, CMP_HEAD) == 0) { | if (key_cmpsaidx(&sav->sah->saidx, &saidx, CMP_HEAD) == 0) { | ||||
SPI_ALLOC_UNLOCK(); | |||||
ipseclog((LOG_DEBUG, "%s: saidx mismatched for SPI %u.\n", | ipseclog((LOG_DEBUG, "%s: saidx mismatched for SPI %u.\n", | ||||
__func__, ntohl(sav->spi))); | __func__, ntohl(sav->spi))); | ||||
key_freesav(&sav); | key_freesav(&sav); | ||||
return (key_senderror(so, m, ESRCH)); | return (key_senderror(so, m, ESRCH)); | ||||
} | } | ||||
SPI_ALLOC_UNLOCK(); | |||||
KEYDBG(KEY_STAMP, | KEYDBG(KEY_STAMP, | ||||
printf("%s: SA(%p)\n", __func__, sav)); | printf("%s: SA(%p)\n", __func__, sav)); | ||||
KEYDBG(KEY_DATA, kdebug_secasv(sav)); | KEYDBG(KEY_DATA, kdebug_secasv(sav)); | ||||
key_unlinksav(sav); | key_unlinksav(sav); | ||||
key_freesav(&sav); | key_freesav(&sav); | ||||
{ | { | ||||
struct mbuf *n; | struct mbuf *n; | ||||
▲ Show 20 Lines • Show All 181 Lines • ▼ Show 20 Lines | key_get(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp) | ||||
if (key_checksockaddrs((struct sockaddr *)(src0 + 1), | if (key_checksockaddrs((struct sockaddr *)(src0 + 1), | ||||
(struct sockaddr *)(dst0 + 1)) != 0) { | (struct sockaddr *)(dst0 + 1)) != 0) { | ||||
ipseclog((LOG_DEBUG, "%s: invalid sockaddr.\n", __func__)); | ipseclog((LOG_DEBUG, "%s: invalid sockaddr.\n", __func__)); | ||||
return key_senderror(so, m, EINVAL); | return key_senderror(so, m, EINVAL); | ||||
} | } | ||||
KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); | KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); | ||||
SPI_ALLOC_LOCK(); | |||||
if (proto == IPPROTO_TCP) | if (proto == IPPROTO_TCP) | ||||
sav = key_getsav_tcpmd5(&saidx, NULL); | sav = key_getsav_tcpmd5(&saidx, NULL); | ||||
else | else | ||||
sav = key_getsavbyspi(sa0->sadb_sa_spi); | sav = key_getsavbyspi(sa0->sadb_sa_spi); | ||||
Done Inline Actionskey_getsavbyspi does not need the lock per se but I would argue it is more future-proof to keep it mjg: key_getsavbyspi does not need the lock per se but I would argue it is more future-proof to keep… | |||||
if (sav == NULL) { | if (sav == NULL) { | ||||
aeUnsubmitted Not Done Inline Actionsthe same, only one SPI_ALLOCK_UNLOCK() here needed. ae: the same, only one SPI_ALLOCK_UNLOCK() here needed. | |||||
SPI_ALLOC_UNLOCK(); | |||||
ipseclog((LOG_DEBUG, "%s: no SA found.\n", __func__)); | ipseclog((LOG_DEBUG, "%s: no SA found.\n", __func__)); | ||||
return key_senderror(so, m, ESRCH); | return key_senderror(so, m, ESRCH); | ||||
} | } | ||||
if (key_cmpsaidx(&sav->sah->saidx, &saidx, CMP_HEAD) == 0) { | if (key_cmpsaidx(&sav->sah->saidx, &saidx, CMP_HEAD) == 0) { | ||||
SPI_ALLOC_UNLOCK(); | |||||
ipseclog((LOG_DEBUG, "%s: saidx mismatched for SPI %u.\n", | ipseclog((LOG_DEBUG, "%s: saidx mismatched for SPI %u.\n", | ||||
__func__, ntohl(sa0->sadb_sa_spi))); | __func__, ntohl(sa0->sadb_sa_spi))); | ||||
key_freesav(&sav); | key_freesav(&sav); | ||||
return (key_senderror(so, m, ESRCH)); | return (key_senderror(so, m, ESRCH)); | ||||
} | } | ||||
SPI_ALLOC_UNLOCK(); | |||||
{ | { | ||||
struct mbuf *n; | struct mbuf *n; | ||||
uint8_t satype; | uint8_t satype; | ||||
/* map proto to satype */ | /* map proto to satype */ | ||||
if ((satype = key_proto2satype(sav->sah->saidx.proto)) == 0) { | if ((satype = key_proto2satype(sav->sah->saidx.proto)) == 0) { | ||||
ipseclog((LOG_DEBUG, "%s: there was invalid proto in SAD.\n", | ipseclog((LOG_DEBUG, "%s: there was invalid proto in SAD.\n", | ||||
▲ Show 20 Lines • Show All 2,053 Lines • ▼ Show 20 Lines | ipsec_key_lft_zone = uma_zcreate("IPsec SA lft_c", | ||||
sizeof(uint64_t) * 2, NULL, NULL, NULL, NULL, | sizeof(uint64_t) * 2, NULL, NULL, NULL, NULL, | ||||
UMA_ALIGN_PTR, UMA_ZONE_PCPU); | UMA_ALIGN_PTR, UMA_ZONE_PCPU); | ||||
SPTREE_LOCK_INIT(); | SPTREE_LOCK_INIT(); | ||||
REGTREE_LOCK_INIT(); | REGTREE_LOCK_INIT(); | ||||
SAHTREE_LOCK_INIT(); | SAHTREE_LOCK_INIT(); | ||||
ACQ_LOCK_INIT(); | ACQ_LOCK_INIT(); | ||||
SPACQ_LOCK_INIT(); | SPACQ_LOCK_INIT(); | ||||
SPI_ALLOC_LOCK_INIT(); | |||||
#ifndef IPSEC_DEBUG2 | #ifndef IPSEC_DEBUG2 | ||||
callout_init(&key_timer, 1); | callout_init(&key_timer, 1); | ||||
callout_reset(&key_timer, hz, key_timehandler, NULL); | callout_reset(&key_timer, hz, key_timehandler, NULL); | ||||
#endif /*IPSEC_DEBUG2*/ | #endif /*IPSEC_DEBUG2*/ | ||||
/* initialize key statistics */ | /* initialize key statistics */ | ||||
keystat.getspi_count = 1; | keystat.getspi_count = 1; | ||||
▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | |||||
#ifndef IPSEC_DEBUG2 | #ifndef IPSEC_DEBUG2 | ||||
callout_drain(&key_timer); | callout_drain(&key_timer); | ||||
#endif | #endif | ||||
SPTREE_LOCK_DESTROY(); | SPTREE_LOCK_DESTROY(); | ||||
REGTREE_LOCK_DESTROY(); | REGTREE_LOCK_DESTROY(); | ||||
SAHTREE_LOCK_DESTROY(); | SAHTREE_LOCK_DESTROY(); | ||||
ACQ_LOCK_DESTROY(); | ACQ_LOCK_DESTROY(); | ||||
SPACQ_LOCK_DESTROY(); | SPACQ_LOCK_DESTROY(); | ||||
SPI_ALLOC_LOCK_DESTROY(); | |||||
} | } | ||||
#endif | #endif | ||||
/* record data transfer on SA, and update timestamps */ | /* record data transfer on SA, and update timestamps */ | ||||
void | void | ||||
key_sa_recordxfer(struct secasvar *sav, struct mbuf *m) | key_sa_recordxfer(struct secasvar *sav, struct mbuf *m) | ||||
{ | { | ||||
IPSEC_ASSERT(sav != NULL, ("Null secasvar")); | IPSEC_ASSERT(sav != NULL, ("Null secasvar")); | ||||
▲ Show 20 Lines • Show All 144 Lines • Show Last 20 Lines |
i tried an mtx lock but it ran into problems with apparent malloc called with M_WAITOK down the line