+static int
+tcp_usr_connectx_common(struct socket *so, int af,
+ struct sockaddr_list **src_sl, struct sockaddr_list **dst_sl,
+ struct proc *p, uint32_t ifscope, sae_associd_t aid, sae_connid_t *pcid,
+ uint32_t flags, void *arg, uint32_t arglen, struct uio *auio,
+ user_ssize_t *bytes_written)
+{
+#pragma unused(aid)
+#if !MPTCP
+#pragma unused(flags, arg, arglen)
+#endif /* !MPTCP */
+ struct sockaddr_entry *src_se = NULL, *dst_se = NULL;
+ struct inpcb *inp = sotoinpcb(so);
+ int error;
+ user_ssize_t datalen = 0;
+
+ if (inp == NULL)
+ return (EINVAL);
+
+ VERIFY(dst_sl != NULL);
+
+ /* select source (if specified) and destination addresses */
+ error = in_selectaddrs(af, src_sl, &src_se, dst_sl, &dst_se);
+ if (error != 0)
+ return (error);
+
+ VERIFY(*dst_sl != NULL && dst_se != NULL);
+ VERIFY(src_se == NULL || *src_sl != NULL);
+ VERIFY(dst_se->se_addr->sa_family == af);
+ VERIFY(src_se == NULL || src_se->se_addr->sa_family == af);
+
+#if NECP
+ inp_update_necp_policy(inp, src_se ? src_se->se_addr : NULL, dst_se ? dst_se->se_addr : NULL, ifscope);
+#endif /* NECP */
+
+ if ((so->so_flags1 & SOF1_DATA_IDEMPOTENT) &&
+ (tcp_fastopen & TCP_FASTOPEN_CLIENT))
+ sototcpcb(so)->t_flagsext |= TF_FASTOPEN;
+
+ /*
+ * We get here for 2 cases:
+ *
+ * a. From MPTCP, to connect a subflow. There is no need to
+ * bind the socket to the source address and/or interface,
+ * since everything has been taken care of by MPTCP. We
+ * simply check whether or not this is for the initial
+ * MPTCP connection attempt, or to join an existing one.
+ *
+ * b. From the socket layer, to connect a TCP. Perform the
+ * bind to source address and/or interface as necessary.
+ */
+#if MPTCP
+ if (flags & CONNREQF_MPTCP) {
+ struct mptsub_connreq *mpcr = arg;
+
+ /* Check to make sure this came down from MPTCP */
+ if (arg == NULL || arglen != sizeof (*mpcr))
+ return (EOPNOTSUPP);
+
+ switch (mpcr->mpcr_type) {
+ case MPTSUB_CONNREQ_MP_ENABLE:
+ break;
+ case MPTSUB_CONNREQ_MP_ADD:
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+ } else
+#endif /* MPTCP */
+ {
+ /* bind socket to the specified interface, if requested */
+ if (ifscope != IFSCOPE_NONE &&
+ (error = inp_bindif(inp, ifscope, NULL)) != 0)
+ return (error);
+
+ /* if source address and/or port is specified, bind to it */
+ if (src_se != NULL) {
+ struct sockaddr *sa = src_se->se_addr;
+ error = sobindlock(so, sa, 0); /* already locked */
+ if (error != 0)
+ return (error);
+ }
+ }
+
+ switch (af) {
+ case AF_INET:
+ error = tcp_usr_connect(so, dst_se->se_addr, p);
+ break;
+#if INET6
+ case AF_INET6:
+ error = tcp6_usr_connect(so, dst_se->se_addr, p);
+ break;
+#endif /* INET6 */
+ default:
+ VERIFY(0);
+ /* NOTREACHED */
+ }
+
+ if (error != 0)
+ return (error);
+
+ /* if there is data, copy it */
+ if (auio != NULL) {
+ socket_unlock(so, 0);
+
+ VERIFY(bytes_written != NULL);
+
+ datalen = uio_resid(auio);
+ error = so->so_proto->pr_usrreqs->pru_sosend(so, NULL,
+ (uio_t)auio, NULL, NULL, 0);
+ socket_lock(so, 0);
+
+ if (error == 0 || error == EWOULDBLOCK)
+ *bytes_written = datalen - uio_resid(auio);
+
+ /*
+ * sosend returns EWOULDBLOCK if it's a non-blocking
+ * socket or a timeout occured (this allows to return
+ * the amount of queued data through sendit()).
+ *
+ * However, connectx() returns EINPROGRESS in case of a
+ * blocking socket. So we change the return value here.
+ */
+ if (error == EWOULDBLOCK)
+ error = EINPROGRESS;
+ }
+
+ if (error == 0 && pcid != NULL)
+ *pcid = 1; /* there is only one connection in regular TCP */
+
+ return (error);
+}
+
+static int
+tcp_usr_connectx(struct socket *so, struct sockaddr_list **src_sl,
+ struct sockaddr_list **dst_sl, struct proc *p, uint32_t ifscope,
+ sae_associd_t aid, sae_connid_t *pcid, uint32_t flags, void *arg,
+ uint32_t arglen, struct uio *uio, user_ssize_t *bytes_written)
+{
+ return (tcp_usr_connectx_common(so, AF_INET, src_sl, dst_sl,
+ p, ifscope, aid, pcid, flags, arg, arglen, uio,
+ bytes_written));
+}
+