Date: Tue, 22 Nov 2016 13:43:06 +0000 (UTC) From: "Andrey V. Elsukov" <ae@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r308997 - projects/ipsec/sys/netipsec Message-ID: <201611221343.uAMDh6jk060663@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: ae Date: Tue Nov 22 13:43:06 2016 New Revision: 308997 URL: https://svnweb.freebsd.org/changeset/base/308997 Log: Rework key_newsav(). This function used by SADB_GETSPI and SADB_ADD handlers. It allocates new SA and depending from SADB message does some initialization. SADB_GETSPI initializes only few SA's fields. This SA stored with LARVAL state in savtree_larval list of SA head. Then it can be updated by SADB_UPDATE message. SADB_ADD initializes all SA's fields and this SA stored with MATURE state in savtree_alive list, after initialization it is allowed to change only SA's lifetime fields. Modified: projects/ipsec/sys/netipsec/key.c Modified: projects/ipsec/sys/netipsec/key.c ============================================================================== --- projects/ipsec/sys/netipsec/key.c Tue Nov 22 13:30:07 2016 (r308996) +++ projects/ipsec/sys/netipsec/key.c Tue Nov 22 13:43:06 2016 (r308997) @@ -2545,103 +2545,150 @@ key_delsah(struct secashead *sah) } /* - * allocating a new SA with LARVAL state. key_add() and key_getspi() call, + * allocating a new SA for key_add() and key_getspi() call, * and copy the values of mhp into new buffer. - * When SAD message type is GETSPI: - * to set sequence number from acq_seq++, - * to set zero to SPI. - * not to call key_setsava(). + * When SAD message type is SADB_GETSPI set SA state to LARVAL. + * For SADB_ADD create and initialize SA with MATURE state. * OUT: NULL : fail * others : pointer to new secasvar. - * - * does not modify mbuf. does not free mbuf on error. */ static struct secasvar * -key_newsav(struct mbuf *m, const struct sadb_msghdr *mhp, - struct secashead *sah, int *errp, const char *where, int tag) +key_newsav(const struct sadb_msghdr *mhp, struct secasindex *saidx, + uint32_t spi, int *errp) { - struct secasvar *newsav; - const struct sadb_sa *xsa; + struct secashead *sah; + struct secasvar *sav; + int isnew; - IPSEC_ASSERT(m != NULL, ("null mbuf")); IPSEC_ASSERT(mhp != NULL, ("null msghdr")); IPSEC_ASSERT(mhp->msg != NULL, ("null msg")); - IPSEC_ASSERT(sah != NULL, ("null secashead")); + IPSEC_ASSERT(mhp->msg->sadb_msg_type == SADB_GETSPI || + mhp->msg->sadb_msg_type == SADB_ADD, ("wrong message type")); - newsav = malloc(sizeof(struct secasvar), M_IPSEC_SA, M_NOWAIT|M_ZERO); - if (newsav == NULL) { - ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); - *errp = ENOBUFS; - goto done; - } - - switch (mhp->msg->sadb_msg_type) { - case SADB_GETSPI: - newsav->spi = 0; - -#ifdef IPSEC_DOSEQCHECK - /* sync sequence number */ - if (mhp->msg->sadb_msg_seq == 0) - newsav->seq = - (V_acq_seq = (V_acq_seq == ~0 ? 1 : ++V_acq_seq)); - else -#endif - newsav->seq = mhp->msg->sadb_msg_seq; - break; - - case SADB_ADD: - /* sanity check */ - if (mhp->ext[SADB_EXT_SA] == NULL) { - free(newsav, M_IPSEC_SA); - newsav = NULL; - ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", - __func__)); + sav = NULL; + sah = NULL; + /* check SPI value */ + switch (saidx->proto) { + case IPPROTO_ESP: + case IPPROTO_AH: + /* + * RFC 4302, 2.4. Security Parameters Index (SPI), SPI values + * 1-255 reserved by IANA for future use, + * 0 for implementation specific, local use. + */ + if (ntohl(spi) <= 255) { + ipseclog((LOG_DEBUG, "%s: illegal range of SPI %u.\n", + __func__, ntohl(spi))); *errp = EINVAL; goto done; } - xsa = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA]; - newsav->spi = xsa->sadb_sa_spi; - newsav->seq = mhp->msg->sadb_msg_seq; break; - default: - free(newsav, M_IPSEC_SA); - newsav = NULL; - *errp = EINVAL; - goto done; } + sav = malloc(sizeof(struct secasvar), M_IPSEC_SA, M_NOWAIT | M_ZERO); + if (sav == NULL) { + ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); + *errp = ENOBUFS; + goto done; + } + sav->lft_c = uma_zalloc(V_key_lft_zone, M_NOWAIT); + if (sav->lft_c == NULL) { + ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); + free(sav, M_IPSEC_SA), sav = NULL; + *errp = ENOBUFS; + goto done; + } + counter_u64_zero(sav->lft_c_allocations); + counter_u64_zero(sav->lft_c_bytes); - /* copy sav values */ - if (mhp->msg->sadb_msg_type != SADB_GETSPI) { - *errp = key_setsaval(newsav, m, mhp); - if (*errp) { - free(newsav, M_IPSEC_SA); - newsav = NULL; + sav->spi = spi; + sav->seq = mhp->msg->sadb_msg_seq; + sav->state = SADB_SASTATE_LARVAL; + sav->pid = (pid_t)mhp->msg->sadb_msg_pid; + SAV_INITREF(sav); + SECASVAR_LOCK_INIT(sav); +again: + sah = key_getsah(saidx); + if (sah == NULL) { + /* create a new SA index */ + sah = key_newsah(saidx); + if (sah == NULL) { + ipseclog((LOG_DEBUG, + "%s: No more memory.\n", __func__)); + *errp = ENOBUFS; goto done; } + isnew = 1; + } else + isnew = 0; + + sav->sah = sah; + if (mhp->msg->sadb_msg_type == SADB_GETSPI) { + sav->created = time_second; + } else if (sav->state == SADB_SASTATE_LARVAL) { + /* + * Do not call key_setsaval() second time in case + * of `goto again`. We will have MATURE state. + */ + *errp = key_setsaval(sav, mhp); + if (*errp != 0) + goto done; + sav->state = SADB_SASTATE_MATURE; } - SECASVAR_LOCK_INIT(newsav); - - /* reset created */ - newsav->created = time_second; - newsav->pid = mhp->msg->sadb_msg_pid; - - /* add to satree */ - newsav->sah = sah; - sa_initref(newsav); - newsav->state = SADB_SASTATE_LARVAL; - - SAHTREE_LOCK(); - LIST_INSERT_TAIL(&sah->savtree[SADB_SASTATE_LARVAL], newsav, - secasvar, chain); - SAHTREE_UNLOCK(); + SAHTREE_WLOCK(); + /* + * Check that existing SAH wasn't unlinked. + * Since we didn't hold the SAHTREE lock, it is possible, + * that callout handler or key_flush() or key_delete() could + * unlink this SAH. + */ + if (isnew == 0 && sah->state == SADB_SASTATE_DEAD) { + SAHTREE_WUNLOCK(); + key_freesah(&sah); /* reference from key_getsah() */ + goto again; + } + if (isnew != 0) { + /* + * Add new SAH into SADB. + * + * XXXAE: we can serialize key_add and key_getspi calls, so + * several threads will not fight in the race. + * Otherwise we should check under SAHTREE lock, that this + * SAH would not added twice. + */ + TAILQ_INSERT_HEAD(&V_sahtree, sah, chain); + /* Add new SAH into hash by addresses */ + LIST_INSERT_HEAD(SAHADDRHASH_HASH(saidx), sah, addrhash); + /* Now we are linked in the chain */ + sah->state = SADB_SASTATE_MATURE; + /* + * SAV references this new SAH. + * In case of existing SAH we reuse reference + * from key_getsah(). + */ + SAH_ADDREF(sah); + } + /* Link SAV with SAH */ + if (sav->state == SADB_SASTATE_MATURE) + TAILQ_INSERT_HEAD(&sah->savtree_alive, sav, chain); + else + TAILQ_INSERT_HEAD(&sah->savtree_larval, sav, chain); + /* Add SAV into SPI hash */ + LIST_INSERT_HEAD(SAVHASH_HASH(sav->spi), sav, spihash); + SAHTREE_WUNLOCK(); + *errp = 0; /* success */ done: - KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP %s from %s:%u return SP:%p\n", __func__, - where, tag, newsav)); - - return newsav; + if (*errp != 0) { + if (sav != NULL) { + SECASVAR_LOCK_DESTROY(sav); + uma_zfree(V_key_lft_zone, sav->lft_c); + free(sav, M_IPSEC_SA), sav = NULL; + } + if (sah != NULL) + key_freesah(&sah); + } + return (sav); } /*
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201611221343.uAMDh6jk060663>