+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;
+ }
+ if (sah == NULL) {
+ lck_mtx_unlock(sadb_mutex);
+ ipseclog((LOG_DEBUG, "key_migrate: no mature SAH found.\n"));
+ return key_senderror(so, m, ENOENT);
+ }
+
+ if (sav == NULL) {
+ lck_mtx_unlock(sadb_mutex);
+ ipseclog((LOG_DEBUG, "key_migrate: no SA found.\n"));
+ return key_senderror(so, m, ENOENT);
+ }
+
+ /* Find or create new SAH */
+ KEY_SETSECASIDX(proto, sah->saidx.mode, sah->saidx.reqid, src1 + 1, dst1 + 1, ipsec_if1 ? ipsec_if1->if_index : 0, &saidx1);
+
+ if ((newsah = key_getsah(&saidx1)) == NULL) {
+ if ((newsah = key_newsah(&saidx1, ipsec_if1, key_get_outgoing_ifindex_from_message(mhp, SADB_X_EXT_MIGRATE_IPSECIF), sah->dir)) == NULL) {
+ lck_mtx_unlock(sadb_mutex);
+ ipseclog((LOG_DEBUG, "key_migrate: No more memory.\n"));
+ return key_senderror(so, m, ENOBUFS);
+ }
+ }
+
+ /* Migrate SAV in to new SAH */
+ if (key_migratesav(sav, newsah) != 0) {
+ lck_mtx_unlock(sadb_mutex);
+ ipseclog((LOG_DEBUG, "key_migrate: Failed to migrate SA to new SAH.\n"));
+ return key_senderror(so, m, EINVAL);