+ /* find a SA with sequence number. */
+#if IPSEC_DOSEQCHECK
+ if (mhp->msg->sadb_msg_seq != 0
+ && (sav = key_getsavbyseq(sah, mhp->msg->sadb_msg_seq)) == NULL) {
+ lck_mtx_unlock(sadb_mutex);
+ ipseclog((LOG_DEBUG,
+ "key_update: no larval SA with sequence %u exists.\n",
+ mhp->msg->sadb_msg_seq));
+ return key_senderror(so, m, ENOENT);
+ }
+#else
+ if ((sav = key_getsavbyspi(sah, sa0->sadb_sa_spi)) == NULL) {
+ lck_mtx_unlock(sadb_mutex);
+ ipseclog((LOG_DEBUG,
+ "key_update: no such a SA found (spi:%u)\n",
+ (u_int32_t)ntohl(sa0->sadb_sa_spi)));
+ return key_senderror(so, m, EINVAL);
+ }
+#endif
+
+ /* validity check */
+ if (sav->sah->saidx.proto != proto) {
+ lck_mtx_unlock(sadb_mutex);
+ ipseclog((LOG_DEBUG,
+ "key_update: protocol mismatched (DB=%u param=%u)\n",
+ sav->sah->saidx.proto, proto));
+ return key_senderror(so, m, EINVAL);
+ }
+#if IPSEC_DOSEQCHECK
+ if (sav->spi != sa0->sadb_sa_spi) {
+ lck_mtx_unlock(sadb_mutex);
+ ipseclog((LOG_DEBUG,
+ "key_update: SPI mismatched (DB:%u param:%u)\n",
+ (u_int32_t)ntohl(sav->spi),
+ (u_int32_t)ntohl(sa0->sadb_sa_spi)));
+ return key_senderror(so, m, EINVAL);
+ }
+#endif
+ if (sav->pid != mhp->msg->sadb_msg_pid) {
+ lck_mtx_unlock(sadb_mutex);
+ ipseclog((LOG_DEBUG,
+ "key_update: pid mismatched (DB:%u param:%u)\n",
+ sav->pid, mhp->msg->sadb_msg_pid));
+ return key_senderror(so, m, EINVAL);
+ }
+
+ /* copy sav values */
+ error = key_setsaval(sav, m, mhp);
+ if (error) {
+ key_freesav(sav, KEY_SADB_LOCKED);
+ lck_mtx_unlock(sadb_mutex);
+ return key_senderror(so, m, error);
+ }
+
+ sav->flags2 = flags2;
+ if (flags2 & SADB_X_EXT_SA2_DELETE_ON_DETACH) {
+ sav->so = so;
+ }
+
+ /*
+ * Verify if SADB_X_EXT_NATT_MULTIPLEUSERS flag is set that
+ * this SA is for transport mode - otherwise clear it.
+ */
+ if ((sav->flags & SADB_X_EXT_NATT_MULTIPLEUSERS) != 0 &&
+ (sav->sah->saidx.mode != IPSEC_MODE_TRANSPORT ||
+ sav->sah->saidx.src.ss_family != AF_INET)) {
+ sav->flags &= ~SADB_X_EXT_NATT_MULTIPLEUSERS;
+ }
+
+ /* check SA values to be mature. */
+ if ((error = key_mature(sav)) != 0) {
+ key_freesav(sav, KEY_SADB_LOCKED);
+ lck_mtx_unlock(sadb_mutex);
+ return key_senderror(so, m, error);
+ }
+
+ lck_mtx_unlock(sadb_mutex);
+
+ {
+ struct mbuf *n;
+
+ /* set msg buf from mhp */
+ n = key_getmsgbuf_x1(m, mhp);
+ if (n == NULL) {
+ ipseclog((LOG_DEBUG, "key_update: No more memory.\n"));
+ return key_senderror(so, m, ENOBUFS);
+ }
+
+ m_freem(m);
+ return key_sendup_mbuf(so, n, KEY_SENDUP_ALL);
+ }
+}
+
+static int
+key_migrate(struct socket *so,
+ struct mbuf *m,
+ const struct sadb_msghdr *mhp)
+{
+ struct sadb_sa *sa0 = NULL;
+ struct sadb_address *src0 = NULL;
+ struct sadb_address *dst0 = NULL;
+ struct sadb_address *src1 = NULL;
+ struct sadb_address *dst1 = NULL;
+ ifnet_t ipsec_if0 = NULL;
+ ifnet_t ipsec_if1 = NULL;
+ struct secasindex saidx0;
+ struct secasindex saidx1;
+ struct secashead *sah = NULL;
+ struct secashead *newsah = NULL;
+ struct secasvar *sav = NULL;
+ u_int16_t proto;
+
+ LCK_MTX_ASSERT(sadb_mutex, LCK_MTX_ASSERT_NOTOWNED);
+
+ /* sanity check */
+ if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) {
+ panic("key_migrate: NULL pointer is passed.\n");
+ }
+
+ /* map satype to proto */
+ if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
+ ipseclog((LOG_DEBUG, "key_migrate: invalid satype is passed.\n"));
+ return key_senderror(so, m, EINVAL);
+ }
+
+ if (mhp->ext[SADB_EXT_SA] == NULL ||
+ mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
+ mhp->ext[SADB_EXT_ADDRESS_DST] == NULL ||
+ mhp->ext[SADB_EXT_MIGRATE_ADDRESS_SRC] == NULL ||
+ mhp->ext[SADB_EXT_MIGRATE_ADDRESS_DST] == NULL) {
+ ipseclog((LOG_DEBUG, "key_migrate: invalid message is passed.\n"));
+ return key_senderror(so, m, EINVAL);
+ }
+
+ if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) ||
+ mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
+ mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) ||
+ mhp->extlen[SADB_EXT_MIGRATE_ADDRESS_SRC] < sizeof(struct sadb_address) ||
+ mhp->extlen[SADB_EXT_MIGRATE_ADDRESS_DST] < sizeof(struct sadb_address)) {
+ ipseclog((LOG_DEBUG, "key_migrate: invalid message is passed.\n"));
+ return key_senderror(so, m, EINVAL);
+ }
+
+ lck_mtx_lock(sadb_mutex);
+
+ sa0 = (struct sadb_sa *)(void *)mhp->ext[SADB_EXT_SA];
+ src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
+ dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
+ src1 = (struct sadb_address *)(mhp->ext[SADB_EXT_MIGRATE_ADDRESS_SRC]);
+ dst1 = (struct sadb_address *)(mhp->ext[SADB_EXT_MIGRATE_ADDRESS_DST]);
+ ipsec_if0 = key_get_ipsec_if_from_message(mhp, SADB_X_EXT_IPSECIF);
+ ipsec_if1 = key_get_ipsec_if_from_message(mhp, SADB_X_EXT_MIGRATE_IPSECIF);
+
+ /* Find existing SAH and SAV */
+ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, ipsec_if0 ? ipsec_if0->if_index : 0, &saidx0);
+
+ LIST_FOREACH(sah, &sahtree, chain) {
+ if (sah->state != SADB_SASTATE_MATURE) {
+ continue;
+ }
+ if (key_cmpsaidx(&sah->saidx, &saidx0, CMP_HEAD) == 0) {
+ continue;
+ }
+
+ sav = key_getsavbyspi(sah, sa0->sadb_sa_spi);
+ if (sav && sav->state == SADB_SASTATE_MATURE) {
+ break;
+ }