+ 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);
+ }
+
+ /* Reset NAT values */
+ sav->flags = sa0->sadb_sa_flags;
+ sav->natt_encapsulated_src_port = ((const struct sadb_sa_2*)(sa0))->sadb_sa_natt_src_port;
+ sav->remote_ike_port = ((const struct sadb_sa_2*)(sa0))->sadb_sa_natt_port;
+ sav->natt_interval = ((const struct sadb_sa_2*)(sa0))->sadb_sa_natt_interval;
+ sav->natt_offload_interval = ((const struct sadb_sa_2*)(sa0))->sadb_sa_natt_offload_interval;
+ sav->natt_last_activity = natt_now;
+
+ /*
+ * Verify if SADB_X_EXT_NATT_MULTIPLEUSERS flag is set that
+ * SADB_X_EXT_NATT is set and SADB_X_EXT_NATT_KEEPALIVE is not
+ * set (we're not behind nat) - otherwise clear it.
+ */
+ if ((sav->flags & SADB_X_EXT_NATT_MULTIPLEUSERS) != 0) {
+ if ((sav->flags & SADB_X_EXT_NATT) == 0 ||
+ (sav->flags & SADB_X_EXT_NATT_KEEPALIVE) != 0) {
+ sav->flags &= ~SADB_X_EXT_NATT_MULTIPLEUSERS;
+ }
+ }
+
+ lck_mtx_unlock(sadb_mutex);
+ {
+ struct mbuf *n;
+ struct sadb_msg *newmsg;
+ int mbufItems[] = {SADB_EXT_RESERVED, SADB_EXT_SA,
+ SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST, SADB_X_EXT_IPSECIF,
+ SADB_EXT_MIGRATE_ADDRESS_SRC, SADB_EXT_MIGRATE_ADDRESS_DST, SADB_X_EXT_MIGRATE_IPSECIF};
+
+ /* create new sadb_msg to reply. */
+ n = key_gather_mbuf(m, mhp, 1, sizeof(mbufItems) / sizeof(int), mbufItems);
+ if (!n) {
+ return key_senderror(so, m, ENOBUFS);
+ }
+
+ if (n->m_len < sizeof(struct sadb_msg)) {
+ n = m_pullup(n, sizeof(struct sadb_msg));
+ if (n == NULL) {
+ return key_senderror(so, m, ENOBUFS);
+ }
+ }
+ newmsg = mtod(n, struct sadb_msg *);
+ newmsg->sadb_msg_errno = 0;
+ newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len);
+
+ m_freem(m);
+ return key_sendup_mbuf(so, n, KEY_SENDUP_ALL);
+ }
+}
+
+/*
+ * search SAD with sequence for a SA which state is SADB_SASTATE_LARVAL.
+ * only called by key_update().
+ * OUT:
+ * NULL : not found
+ * others : found, pointer to a SA.
+ */
+#if IPSEC_DOSEQCHECK
+static struct secasvar *
+key_getsavbyseq(
+ struct secashead *sah,
+ u_int32_t seq)
+{
+ struct secasvar *sav;
+ u_int state;
+
+ LCK_MTX_ASSERT(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
+ state = SADB_SASTATE_LARVAL;
+
+ /* search SAD with sequence number ? */
+ LIST_FOREACH(sav, &sah->savtree[state], chain) {
+ KEY_CHKSASTATE(state, sav->state, "key_getsabyseq");
+
+ if (sav->seq == seq) {
+ sav->refcnt++;
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP key_getsavbyseq cause "
+ "refcnt++:%d SA:0x%llx\n", sav->refcnt,
+ (uint64_t)VM_KERNEL_ADDRPERM(sav)));
+ return sav;
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+/*
+ * SADB_ADD processing
+ * add a entry to SA database, when received
+ * <base, SA, (SA2), (lifetime(HSC),) address(SD), (address(P),)
+ * key(AE), (identity(SD),) (sensitivity)>
+ * from the ikmpd,
+ * and send
+ * <base, SA, (SA2), (lifetime(HSC),) address(SD), (address(P),)
+ * (identity(SD),) (sensitivity)>
+ * to the ikmpd.