+
+void
+mptcp_handle_deferred_upcalls(struct mppcb *mpp, uint32_t flag)
+{
+ VERIFY(mpp->mpp_flags & flag);
+ mpp->mpp_flags &= ~flag;
+
+ if (mptcp_should_defer_upcall(mpp))
+ return;
+
+ if (mpp->mpp_flags & MPP_SHOULD_WORKLOOP) {
+ mpp->mpp_flags &= ~MPP_SHOULD_WORKLOOP;
+
+ mptcp_subflow_workloop(mpp->mpp_pcbe);
+ }
+
+ if (mpp->mpp_flags & MPP_SHOULD_RWAKEUP) {
+ mpp->mpp_flags &= ~MPP_SHOULD_RWAKEUP;
+
+ sorwakeup(mpp->mpp_socket);
+ }
+
+ if (mpp->mpp_flags & MPP_SHOULD_WWAKEUP) {
+ mpp->mpp_flags &= ~MPP_SHOULD_WWAKEUP;
+
+ sowwakeup(mpp->mpp_socket);
+ }
+
+ if (mpp->mpp_flags & MPP_SET_CELLICON) {
+ mpp->mpp_flags &= ~MPP_SET_CELLICON;
+
+ mptcp_set_cellicon(mpp->mpp_pcbe);
+ }
+
+ if (mpp->mpp_flags & MPP_UNSET_CELLICON) {
+ mpp->mpp_flags &= ~MPP_UNSET_CELLICON;
+
+ mptcp_unset_cellicon();
+ }
+}
+
+static void
+mptcp_ask_for_nat64(struct ifnet *ifp)
+{
+ in6_post_msg(ifp, KEV_INET6_REQUEST_NAT64_PREFIX, NULL, NULL);
+
+ mptcplog((LOG_DEBUG, "%s: asked for NAT64-prefix on %s\n",
+ __func__, ifp->if_name), MPTCP_SOCKET_DBG, MPTCP_LOGLVL_VERBOSE);
+}
+
+static void
+mptcp_reset_itfinfo(struct mpt_itf_info *info)
+{
+ info->ifindex = 0;
+ info->has_v4_conn = 0;
+ info->has_v6_conn = 0;
+}
+
+void
+mptcp_session_necp_cb(void *handle, int action, struct necp_client_flow *flow)
+{
+ struct mppcb *mp = (struct mppcb *)handle;
+ struct mptses *mpte = mptompte(mp);
+ struct socket *mp_so;
+ struct mptcb *mp_tp;
+ int locked = 0;
+ uint32_t i, ifindex;
+
+ ifindex = flow->interface_index;
+ VERIFY(ifindex != IFSCOPE_NONE);
+
+ /* ToDo - remove after rdar://problem/32007628 */
+ if (!IF_INDEX_IN_RANGE(ifindex))
+ printf("%s 1 ifindex %u not in range of flow %p action %d\n",
+ __func__, ifindex, flow, action);
+
+ /* About to be garbage-collected (see note about MPTCP/NECP interactions) */
+ if (mp->mpp_socket->so_usecount == 0)
+ return;
+
+ if (action != NECP_CLIENT_CBACTION_INITIAL) {
+ mpte_lock(mpte);
+ locked = 1;
+
+ /* Check again, because it might have changed while waiting */
+ if (mp->mpp_socket->so_usecount == 0)
+ goto out;
+ }
+
+ mp_tp = mpte->mpte_mptcb;
+ mp_so = mptetoso(mpte);
+
+ mptcplog((LOG_DEBUG, "%s, action: %u ifindex %u usecount %u mpt_flags %#x state %u\n",
+ __func__, action, ifindex, mp->mpp_socket->so_usecount, mp_tp->mpt_flags, mp_tp->mpt_state),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_VERBOSE);
+
+ /* No need on fallen back sockets */
+ if (mp_tp->mpt_flags & MPTCPF_FALLBACK_TO_TCP)
+ goto out;
+
+ if (action == NECP_CLIENT_CBACTION_NONVIABLE) {
+ for (i = 0; i < mpte->mpte_itfinfo_size; i++) {
+ if (mpte->mpte_itfinfo[i].ifindex == ifindex)
+ mptcp_reset_itfinfo(&mpte->mpte_itfinfo[i]);
+ }
+
+ mptcp_sched_create_subflows(mpte);
+ } else if (action == NECP_CLIENT_CBACTION_VIABLE ||
+ action == NECP_CLIENT_CBACTION_INITIAL) {
+ int found_empty = 0, empty_index = -1;
+ struct ifnet *ifp;
+
+ /* ToDo - remove after rdar://problem/32007628 */
+ if (!IF_INDEX_IN_RANGE(ifindex))
+ printf("%s 2 ifindex %u not in range of flow %p action %d\n",
+ __func__, ifindex, flow, action);
+
+ ifnet_head_lock_shared();
+ ifp = ifindex2ifnet[ifindex];
+ ifnet_head_done();
+
+ /* ToDo - remove after rdar://problem/32007628 */
+ if (!IF_INDEX_IN_RANGE(ifindex))
+ printf("%s 3 ifindex %u not in range of flow %p action %d\n",
+ __func__, ifindex, flow, action);
+
+ if (ifp == NULL)
+ goto out;
+
+ if (IFNET_IS_EXPENSIVE(ifp) &&
+ (mp_so->so_restrictions & SO_RESTRICT_DENY_EXPENSIVE))
+ goto out;
+
+ if (IFNET_IS_CELLULAR(ifp) &&
+ (mp_so->so_restrictions & SO_RESTRICT_DENY_CELLULAR))
+ goto out;
+
+ for (i = 0; i < mpte->mpte_itfinfo_size; i++) {
+ if (mpte->mpte_itfinfo[i].ifindex == 0) {
+ found_empty = 1;
+ empty_index = i;
+ }
+
+ if (mpte->mpte_itfinfo[i].ifindex == ifindex) {
+ /* Ok, it's already there */
+ goto out;
+ }
+ }
+
+ if ((mpte->mpte_dst.sa_family == AF_INET || mpte->mpte_dst.sa_family == 0) &&
+ !(flow->necp_flow_flags & NECP_CLIENT_RESULT_FLAG_HAS_IPV4) &&
+ ifnet_get_nat64prefix(ifp, NULL) == ENOENT) {
+ mptcp_ask_for_nat64(ifp);
+ goto out;
+ }
+
+ if (found_empty == 0) {
+ int new_size = mpte->mpte_itfinfo_size * 2;
+ struct mpt_itf_info *info = _MALLOC(sizeof(*info) * new_size, M_TEMP, M_ZERO);
+
+ if (info == NULL) {
+ mptcplog((LOG_ERR, "%s malloc failed for %u\n", __func__, new_size),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
+ goto out;
+ }
+
+ memcpy(info, mpte->mpte_itfinfo, mpte->mpte_itfinfo_size * sizeof(*info));
+
+ if (mpte->mpte_itfinfo_size > MPTE_ITFINFO_SIZE)
+ _FREE(mpte->mpte_itfinfo, M_TEMP);
+
+ /* We allocated a new one, thus the first must be empty */
+ empty_index = mpte->mpte_itfinfo_size;
+
+ mpte->mpte_itfinfo = info;
+ mpte->mpte_itfinfo_size = new_size;
+
+ mptcplog((LOG_DEBUG, "%s Needed to realloc to %u\n", __func__, new_size),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_VERBOSE);
+ }
+
+ VERIFY(empty_index >= 0 && empty_index < (int)mpte->mpte_itfinfo_size);
+ mpte->mpte_itfinfo[empty_index].ifindex = ifindex;
+ mpte->mpte_itfinfo[empty_index].has_v4_conn = !!(flow->necp_flow_flags & NECP_CLIENT_RESULT_FLAG_HAS_IPV4);
+ mpte->mpte_itfinfo[empty_index].has_v6_conn = !!(flow->necp_flow_flags & NECP_CLIENT_RESULT_FLAG_HAS_IPV6);
+
+ mptcp_sched_create_subflows(mpte);
+ }
+
+out:
+ if (locked)
+ mpte_unlock(mpte);
+}
+
+void
+mptcp_set_restrictions(struct socket *mp_so)
+{
+ struct mptses *mpte = mpsotompte(mp_so);
+ uint32_t i;
+
+ mpte_lock_assert_held(mpte);
+
+ ifnet_head_lock_shared();
+
+ for (i = 0; i < mpte->mpte_itfinfo_size; i++) {
+ struct mpt_itf_info *info = &mpte->mpte_itfinfo[i];
+ uint32_t ifindex = info->ifindex;
+ struct ifnet *ifp;
+
+ if (ifindex == IFSCOPE_NONE)
+ continue;
+
+ ifp = ifindex2ifnet[ifindex];
+
+ if (IFNET_IS_EXPENSIVE(ifp) &&
+ (mp_so->so_restrictions & SO_RESTRICT_DENY_EXPENSIVE))
+ info->ifindex = IFSCOPE_NONE;
+
+ if (IFNET_IS_CELLULAR(ifp) &&
+ (mp_so->so_restrictions & SO_RESTRICT_DENY_CELLULAR))
+ info->ifindex = IFSCOPE_NONE;
+ }
+
+ ifnet_head_done();
+}
+