+ ifindex = info->ifindex;
+ if (ifindex == IFSCOPE_NONE)
+ continue;
+
+ TAILQ_FOREACH(mpts, &mpte->mpte_subflows, mpts_entry) {
+ const struct ifnet *ifp = sotoinpcb(mpts->mpts_socket)->inp_last_outifp;
+
+ if (ifp == NULL)
+ continue;
+
+ if (ifp->if_index == ifindex &&
+ !(mpts->mpts_socket->so_state & SS_ISDISCONNECTED) &&
+ sototcpcb(mpts->mpts_socket)->t_state != TCPS_CLOSED) {
+ /*
+ * We found a subflow on this interface.
+ * No need to create a new one.
+ */
+ found = 1;
+ break;
+ }
+
+ /*
+ * In Handover mode, only create cell subflow if
+ * 1. Wi-Fi Assist is active
+ * 2. Symptoms marked WiFi as weak
+ * 3. We are experiencing RTOs or we are not sending data.
+ *
+ * This covers the scenario, where:
+ * 1. We send and get retransmission timeouts (thus,
+ * we confirmed that WiFi is indeed bad).
+ * 2. We are not sending and the server tries to send.
+ * Establshing a cell-subflow gives the server a
+ * chance to send us some data over cell if WiFi
+ * is dead. We establish the subflow with the
+ * backup-bit set, so the server is not allowed to
+ * send on this subflow as long as WiFi is providing
+ * good performance.
+ */
+ if (mpte->mpte_svctype == MPTCP_SVCTYPE_HANDOVER &&
+ !IFNET_IS_CELLULAR(ifp) &&
+ !(mpts->mpts_flags & (MPTSF_DISCONNECTING | MPTSF_DISCONNECTED | MPTSF_CLOSE_REQD)) &&
+ (!mptcp_is_wifi_unusable() ||
+ (sototcpcb(mpts->mpts_socket)->t_rxtshift < mptcp_fail_thresh &&
+ mptetoso(mpte)->so_snd.sb_cc))) {
+ mptcplog((LOG_DEBUG, "%s handover, wifi state %u rxt %u ifindex %u this %u\n",
+ __func__, mptcp_is_wifi_unusable(), sototcpcb(mpts->mpts_socket)->t_rxtshift, ifindex,
+ ifp->if_index),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_VERBOSE);
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found && !(mpte->mpte_flags & MPTE_FIRSTPARTY) &&
+ !(mpte->mpte_flags & MPTE_ACCESS_GRANTED) &&
+ mptcp_developer_mode == 0) {
+ mptcp_ask_symptoms(mpte);
+ return;
+ }
+
+ if (!found) {
+ struct sockaddr *dst = &mpte->mpte_dst;
+ struct sockaddr_in6 nat64pre;
+
+ if (mpte->mpte_dst.sa_family == AF_INET &&
+ !info->has_v4_conn && info->has_v6_conn) {
+ struct ipv6_prefix nat64prefixes[NAT64_MAX_NUM_PREFIXES];
+ struct ifnet *ifp;
+ int error, j;
+
+ bzero(&nat64pre, sizeof(struct sockaddr_in6));
+
+ ifnet_head_lock_shared();
+ ifp = ifindex2ifnet[ifindex];
+ ifnet_head_done();
+
+ error = ifnet_get_nat64prefix(ifp, nat64prefixes);
+ if (error) {
+ mptcplog((LOG_ERR, "%s: no NAT64-prefix on itf %s, error %d\n",
+ __func__, ifp->if_name, error),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
+ continue;
+ }
+
+ for (j = 0; j < NAT64_MAX_NUM_PREFIXES; j++) {
+ if (nat64prefixes[j].prefix_len != 0)
+ break;
+ }
+
+ VERIFY(j < NAT64_MAX_NUM_PREFIXES);
+
+ error = mptcp_synthesize_nat64(&nat64prefixes[j].ipv6_prefix,
+ nat64prefixes[j].prefix_len,
+ &mpte->__mpte_dst_v4.sin_addr);
+ if (error != 0) {
+ mptcplog((LOG_INFO, "%s: cannot synthesize this addr\n", __func__),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_LOG);
+ continue;
+ }
+
+ memcpy(&nat64pre.sin6_addr,
+ &nat64prefixes[j].ipv6_prefix,
+ sizeof(nat64pre.sin6_addr));
+ nat64pre.sin6_len = sizeof(struct sockaddr_in6);
+ nat64pre.sin6_family = AF_INET6;
+ nat64pre.sin6_port = mpte->__mpte_dst_v6.sin6_port;
+ nat64pre.sin6_flowinfo = 0;
+ nat64pre.sin6_scope_id = 0;
+
+ dst = (struct sockaddr *)&nat64pre;
+ }
+
+ /* Initial subflow started on a NAT64'd address? */
+ if (mpte->mpte_dst.sa_family == AF_INET6 &&
+ mpte->mpte_dst_v4_nat64.sin_family == AF_INET) {
+ dst = (struct sockaddr *)&mpte->mpte_dst_v4_nat64;
+ }
+
+ if (dst->sa_family == AF_INET && !info->has_v4_conn)
+ continue;
+ if (dst->sa_family == AF_INET6 && !info->has_v6_conn)
+ continue;
+
+ mptcp_subflow_add(mpte, NULL, dst, ifindex, NULL);
+ }
+ }
+}
+
+/*
+ * Based on the MPTCP Service-type and the state of the subflows, we
+ * will destroy subflows here.
+ */
+static void
+mptcp_check_subflows_and_remove(struct mptses *mpte)
+{
+ struct mptsub *mpts, *tmpts;
+ int found_working_subflow = 0, removed_some = 0;
+ int wifi_unusable = mptcp_is_wifi_unusable();
+
+ if (mpte->mpte_svctype != MPTCP_SVCTYPE_HANDOVER)
+ return;
+
+ /*
+ * Look for a subflow that is on a non-cellular interface
+ * and actually works (aka, no retransmission timeout).
+ */
+ TAILQ_FOREACH(mpts, &mpte->mpte_subflows, mpts_entry) {
+ const struct ifnet *ifp = sotoinpcb(mpts->mpts_socket)->inp_last_outifp;
+ struct socket *so;
+ struct tcpcb *tp;
+
+ if (ifp == NULL || IFNET_IS_CELLULAR(ifp))
+ continue;
+
+ so = mpts->mpts_socket;
+ tp = sototcpcb(so);
+
+ if (!(mpts->mpts_flags & MPTSF_CONNECTED) ||
+ tp->t_state != TCPS_ESTABLISHED)
+ continue;
+
+ /* Either this subflow is in good condition while we try to send */
+ if (tp->t_rxtshift == 0 && mptetoso(mpte)->so_snd.sb_cc)
+ found_working_subflow = 1;
+
+ /* Or WiFi is fine */
+ if (!wifi_unusable)
+ found_working_subflow = 1;
+ }
+
+ /*
+ * Couldn't find a working subflow, let's not remove those on a cellular
+ * interface.
+ */
+ if (!found_working_subflow)
+ return;
+
+ TAILQ_FOREACH_SAFE(mpts, &mpte->mpte_subflows, mpts_entry, tmpts) {
+ const struct ifnet *ifp = sotoinpcb(mpts->mpts_socket)->inp_last_outifp;
+
+ /* Only remove cellular subflows */
+ if (ifp == NULL || !IFNET_IS_CELLULAR(ifp))
+ continue;
+
+ soevent(mpts->mpts_socket, SO_FILT_HINT_LOCKED | SO_FILT_HINT_MUSTRST);
+ removed_some = 1;
+ }
+
+ if (removed_some)
+ mptcp_unset_cellicon();
+}
+
+static void
+mptcp_remove_subflows(struct mptses *mpte)
+{
+ struct mptsub *mpts, *tmpts;
+
+ TAILQ_FOREACH_SAFE(mpts, &mpte->mpte_subflows, mpts_entry, tmpts) {
+ if (mpts->mpts_flags & MPTSF_CLOSE_REQD) {
+ mpts->mpts_flags &= ~MPTSF_CLOSE_REQD;
+
+ soevent(mpts->mpts_socket,
+ SO_FILT_HINT_LOCKED | SO_FILT_HINT_NOSRCADDR);
+ }
+ }
+}
+
+static void
+mptcp_create_subflows(__unused void *arg)
+{
+ struct mppcb *mpp;
+
+ /*
+ * Start with clearing, because we might be processing connections
+ * while a new event comes in.
+ */
+ if (OSTestAndClear(0x01, &mptcp_create_subflows_scheduled))
+ mptcplog((LOG_ERR, "%s: bit was already cleared!\n", __func__),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
+
+ /* Iterate over all MPTCP connections */
+
+ lck_mtx_lock(&mtcbinfo.mppi_lock);
+
+ TAILQ_FOREACH(mpp, &mtcbinfo.mppi_pcbs, mpp_entry) {
+ struct mptses *mpte;
+ struct socket *mp_so;
+
+ if (!(mpp->mpp_flags & MPP_CREATE_SUBFLOWS))
+ continue;
+
+ mpp_lock(mpp);
+
+ mpp->mpp_flags &= ~MPP_CREATE_SUBFLOWS;
+
+ mpte = mpp->mpp_pcbe;
+ mp_so = mpp->mpp_socket;
+
+ VERIFY(mp_so->so_usecount > 0);
+
+ mptcp_check_subflows_and_add(mpte);
+ mptcp_remove_subflows(mpte);
+
+ mp_so->so_usecount--; /* See mptcp_sched_create_subflows */
+ mpp_unlock(mpp);
+ }
+
+ lck_mtx_unlock(&mtcbinfo.mppi_lock);
+}
+
+/*
+ * We need this because we are coming from an NECP-event. This event gets posted
+ * while holding NECP-locks. The creation of the subflow however leads us back
+ * into NECP (e.g., to add the necp_cb and also from tcp_connect).
+ * So, we would deadlock there as we already hold the NECP-lock.
+ *
+ * So, let's schedule this separately. It also gives NECP the chance to make
+ * progress, without having to wait for MPTCP to finish its subflow creation.
+ */
+void
+mptcp_sched_create_subflows(struct mptses *mpte)
+{
+ struct mppcb *mpp = mpte->mpte_mppcb;
+ struct mptcb *mp_tp = mpte->mpte_mptcb;
+ struct socket *mp_so = mpp->mpp_socket;
+
+ if (!mptcp_ok_to_create_subflows(mp_tp)) {
+ mptcplog((LOG_DEBUG, "%s: not a good time for subflows, state %u flags %#x",
+ __func__, mp_tp->mpt_state, mp_tp->mpt_flags),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_VERBOSE);
+ return;
+ }
+
+ if (!(mpp->mpp_flags & MPP_CREATE_SUBFLOWS)) {
+ mp_so->so_usecount++; /* To prevent it from being free'd in-between */
+ mpp->mpp_flags |= MPP_CREATE_SUBFLOWS;
+ }
+
+ if (OSTestAndSet(0x01, &mptcp_create_subflows_scheduled))
+ return;
+
+ /* Do the call in 100ms to allow NECP to schedule it on all sockets */
+ timeout(mptcp_create_subflows, NULL, hz/10);
+}
+
+/*
+ * Allocate an MPTCP socket option structure.
+ */
+struct mptopt *
+mptcp_sopt_alloc(int how)
+{
+ struct mptopt *mpo;
+
+ mpo = (how == M_WAITOK) ? zalloc(mptopt_zone) :
+ zalloc_noblock(mptopt_zone);
+ if (mpo != NULL) {
+ bzero(mpo, mptopt_zone_size);
+ }
+
+ return (mpo);
+}
+
+/*
+ * Free an MPTCP socket option structure.
+ */
+void
+mptcp_sopt_free(struct mptopt *mpo)
+{
+ VERIFY(!(mpo->mpo_flags & MPOF_ATTACHED));
+
+ zfree(mptopt_zone, mpo);
+}
+
+/*
+ * Add a socket option to the MPTCP socket option list.
+ */
+void
+mptcp_sopt_insert(struct mptses *mpte, struct mptopt *mpo)
+{
+ mpte_lock_assert_held(mpte); /* same as MP socket lock */
+ mpo->mpo_flags |= MPOF_ATTACHED;
+ TAILQ_INSERT_TAIL(&mpte->mpte_sopts, mpo, mpo_entry);
+}
+
+/*
+ * Remove a socket option from the MPTCP socket option list.
+ */
+void
+mptcp_sopt_remove(struct mptses *mpte, struct mptopt *mpo)
+{
+ mpte_lock_assert_held(mpte); /* same as MP socket lock */
+ VERIFY(mpo->mpo_flags & MPOF_ATTACHED);
+ mpo->mpo_flags &= ~MPOF_ATTACHED;
+ TAILQ_REMOVE(&mpte->mpte_sopts, mpo, mpo_entry);
+}
+
+/*
+ * Search for an existing <sopt_level,sopt_name> socket option.
+ */
+struct mptopt *
+mptcp_sopt_find(struct mptses *mpte, struct sockopt *sopt)
+{
+ struct mptopt *mpo;
+
+ mpte_lock_assert_held(mpte); /* same as MP socket lock */
+
+ TAILQ_FOREACH(mpo, &mpte->mpte_sopts, mpo_entry) {
+ if (mpo->mpo_level == sopt->sopt_level &&
+ mpo->mpo_name == sopt->sopt_name)
+ break;
+ }
+ return (mpo);
+}
+
+/*
+ * Allocate a MPTCP subflow structure.
+ */
+static struct mptsub *
+mptcp_subflow_alloc(void)
+{
+ struct mptsub *mpts = zalloc(mptsub_zone);
+
+ if (mpts == NULL)
+ return (NULL);
+
+ bzero(mpts, mptsub_zone_size);
+ return (mpts);
+}