/*
- * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2012-2015 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
static int mptcp_attach(struct socket *, struct proc *);
static int mptcp_detach(struct socket *, struct mppcb *);
static int mptcp_connectx(struct mptses *, struct sockaddr_list **,
- struct sockaddr_list **, struct proc *, uint32_t, associd_t, connid_t *,
- uint32_t, void *, uint32_t);
+ struct sockaddr_list **, struct proc *, uint32_t, sae_associd_t,
+ sae_connid_t *, uint32_t, void *, uint32_t);
static int mptcp_usr_connectx(struct socket *, struct sockaddr_list **,
- struct sockaddr_list **, struct proc *, uint32_t, associd_t, connid_t *,
- uint32_t, void *, uint32_t);
+ struct sockaddr_list **, struct proc *, uint32_t, sae_associd_t,
+ sae_connid_t *, uint32_t, void *, uint32_t, struct uio *, user_ssize_t *);
static int mptcp_getassocids(struct mptses *, uint32_t *, user_addr_t);
-static int mptcp_getconnids(struct mptses *, associd_t, uint32_t *,
+static int mptcp_getconnids(struct mptses *, sae_associd_t, uint32_t *,
user_addr_t);
-static int mptcp_getconninfo(struct mptses *, connid_t *, uint32_t *,
+static int mptcp_getconninfo(struct mptses *, sae_connid_t *, uint32_t *,
uint32_t *, int32_t *, user_addr_t, socklen_t *, user_addr_t, socklen_t *,
uint32_t *, user_addr_t, uint32_t *);
static int mptcp_usr_control(struct socket *, u_long, caddr_t, struct ifnet *,
struct proc *);
-static int mptcp_disconnectx(struct mptses *, associd_t, connid_t);
-static int mptcp_usr_disconnectx(struct socket *, associd_t, connid_t);
+static int mptcp_disconnectx(struct mptses *, sae_associd_t, sae_connid_t);
+static int mptcp_usr_disconnect(struct socket *);
+static int mptcp_usr_disconnectx(struct socket *, sae_associd_t, sae_connid_t);
static struct mptses *mptcp_usrclosed(struct mptses *);
-static int mptcp_usr_peeloff(struct socket *, associd_t, struct socket **);
-static int mptcp_peeloff(struct mptses *, associd_t, struct socket **);
+static int mptcp_usr_peeloff(struct socket *, sae_associd_t, struct socket **);
+static int mptcp_peeloff(struct mptses *, sae_associd_t, struct socket **);
static int mptcp_usr_rcvd(struct socket *, int);
static int mptcp_usr_send(struct socket *, int, struct mbuf *,
struct sockaddr *, struct mbuf *, struct proc *);
static int mptcp_getopt(struct mptses *, struct sockopt *);
static int mptcp_default_tcp_optval(struct mptses *, struct sockopt *, int *);
static void mptcp_connorder_helper(struct mptsub *mpts);
+static int mptcp_usr_preconnect(struct socket *so);
struct pr_usrreqs mptcp_usrreqs = {
.pru_attach = mptcp_usr_attach,
.pru_connectx = mptcp_usr_connectx,
.pru_control = mptcp_usr_control,
.pru_detach = mptcp_usr_detach,
+ .pru_disconnect = mptcp_usr_disconnect,
.pru_disconnectx = mptcp_usr_disconnectx,
.pru_peeloff = mptcp_usr_peeloff,
.pru_rcvd = mptcp_usr_rcvd,
.pru_sosend = mptcp_usr_sosend,
.pru_soreceive = soreceive,
.pru_socheckopt = mptcp_usr_socheckopt,
+ .pru_preconnect = mptcp_usr_preconnect,
};
+/*
+ * Sysctl for testing and tuning mptcp connectx with data api.
+ * Mirrors tcp_preconnect_sbspace for now.
+ */
+#define MPTCP_PRECONNECT_SBSZ_MAX 1460
+#define MPTCP_PRECONNECT_SBSZ_MIN (TCP_MSS)
+#define MPTCP_PRECONNECT_SBSZ_DEF (TCP6_MSS)
+static int mptcp_preconnect_sbspace = MPTCP_PRECONNECT_SBSZ_DEF;
+SYSCTL_INT(_net_inet_mptcp, OID_AUTO, mp_preconn_sbsz, CTLFLAG_RW | CTLFLAG_LOCKED,
+ &mptcp_preconnect_sbspace, 0, "Maximum preconnect space");
+
+
/*
* Attaches an MPTCP control block to a socket.
*/
mptcp_attach(struct socket *mp_so, struct proc *p)
{
#pragma unused(p)
- struct mptses *mpte;
- struct mptcb *mp_tp;
- struct mppcb *mpp;
+ struct mptses *mpte = NULL;
+ struct mptcb *mp_tp = NULL;
+ struct mppcb *mpp = NULL;
int error = 0;
if (mp_so->so_snd.sb_hiwat == 0 || mp_so->so_rcv.sb_hiwat == 0) {
goto out;
}
+ if (mp_so->so_snd.sb_preconn_hiwat == 0) {
+ soreserve_preconnect(mp_so, imin(MPTCP_PRECONNECT_SBSZ_MAX,
+ imax(mptcp_preconnect_sbspace, MPTCP_PRECONNECT_SBSZ_MIN)));
+ }
+
/*
* MPTCP socket buffers cannot be compressed, due to the
* fact that each mbuf chained via m_next is a M_PKTHDR
mp_so->so_rcv.sb_flags &= ~SB_AUTOSIZE;
mp_so->so_snd.sb_flags &= ~SB_AUTOSIZE;
- if ((error = mp_pcballoc(mp_so, &mtcbinfo)) != 0)
+ if ((error = mp_pcballoc(mp_so, &mtcbinfo)) != 0) {
goto out;
+ }
mpp = sotomppcb(mp_so);
VERIFY(mpp != NULL);
-
- mpte = mptcp_sescreate(mp_so, mpp);
- if (mpte == NULL) {
- mp_pcbdetach(mpp);
- error = ENOBUFS;
- goto out;
- }
+ mpte = (struct mptses *)mpp->mpp_pcbe;
+ VERIFY(mpte != NULL);
mp_tp = mpte->mpte_mptcb;
VERIFY(mp_tp != NULL);
-
- MPT_LOCK(mp_tp);
- mp_tp->mpt_state = MPTCPS_CLOSED;
- MPT_UNLOCK(mp_tp);
-
out:
return (error);
}
mppi = mpp->mpp_pcbinfo;
VERIFY(mppi != NULL);
- mpte = &((struct mpp_mtp *)mpp)->mpp_ses;
+ __IGNORE_WCASTALIGN(mpte = &((struct mpp_mtp *)mpp)->mpp_ses);
VERIFY(mpte->mpte_mppcb == mpp);
MPTE_LOCK_ASSERT_HELD(mpte); /* same as MP socket lock */
*/
mp_pcbdetach(mpp);
- (void) mptcp_disconnectx(mpte, ASSOCID_ALL, CONNID_ALL);
+ (void) mptcp_disconnectx(mpte, SAE_ASSOCID_ALL, SAE_CONNID_ALL);
/*
* XXX: adi@apple.com
static int
mptcp_connectx(struct mptses *mpte, struct sockaddr_list **src_sl,
struct sockaddr_list **dst_sl, struct proc *p, uint32_t ifscope,
- associd_t aid, connid_t *pcid, uint32_t flags, void *arg,
+ sae_associd_t aid, sae_connid_t *pcid, uint32_t flags, void *arg,
uint32_t arglen)
{
#pragma unused(p, aid, flags, arg, arglen)
VERIFY(dst_sl != NULL && *dst_sl != NULL);
VERIFY(pcid != NULL);
- mptcplog((LOG_DEBUG, "%s: mp_so 0x%llx\n", __func__,
- (u_int64_t)VM_KERNEL_ADDRPERM(mp_so)));
- DTRACE_MPTCP3(connectx, struct mptses *, mpte, associd_t, aid,
+ mptcplog((LOG_DEBUG, "MPTCP Socket: "
+ "%s: mp_so 0x%llx\n", __func__,
+ (u_int64_t)VM_KERNEL_ADDRPERM(mp_so)),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_LOG);
+
+ DTRACE_MPTCP3(connectx, struct mptses *, mpte, sae_associd_t, aid,
struct socket *, mp_so);
mpts = mptcp_subflow_alloc(M_WAITOK);
static int
mptcp_usr_connectx(struct socket *mp_so, struct sockaddr_list **src_sl,
struct sockaddr_list **dst_sl, struct proc *p, uint32_t ifscope,
- associd_t aid, connid_t *pcid, uint32_t flags, void *arg,
- uint32_t arglen)
+ 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(arg, arglen)
struct mppcb *mpp = sotomppcb(mp_so);
- struct mptses *mpte;
+ struct mptses *mpte = NULL;
+ struct mptcb *mp_tp = NULL;
+ user_ssize_t datalen;
+
int error = 0;
if (mpp == NULL || mpp->mpp_state == MPPCB_STATE_DEAD) {
mpte = mptompte(mpp);
VERIFY(mpte != NULL);
+ mp_tp = mpte->mpte_mptcb;
+ VERIFY(mp_tp != NULL);
+
+ if (mp_tp->mpt_flags & MPTCPF_FALLBACK_TO_TCP) {
+ error = EINVAL;
+ goto out;
+ }
+
error = mptcp_connectx(mpte, src_sl, dst_sl, p, ifscope,
aid, pcid, flags, arg, arglen);
+
+ /* If there is data, copy it */
+ if (auio != NULL) {
+ datalen = uio_resid(auio);
+ socket_unlock(mp_so, 0);
+ error = mp_so->so_proto->pr_usrreqs->pru_sosend(mp_so, NULL,
+ (uio_t) auio, NULL, NULL, 0);
+ /* check if this can be supported with fast Join also. XXX */
+ if (error == 0 || error == EWOULDBLOCK)
+ *bytes_written = datalen - uio_resid(auio);
+
+ if (error == EWOULDBLOCK)
+ error = EINPROGRESS;
+
+ socket_lock(mp_so, 0);
+ MPT_LOCK(mp_tp);
+ if (mp_tp->mpt_flags & MPTCPF_PEEL_OFF) {
+ *bytes_written = datalen - uio_resid(auio);
+ /*
+ * Override errors like EPIPE that occur as
+ * a result of doing TFO during TCP fallback.
+ */
+ error = EPROTO;
+ }
+ MPT_UNLOCK(mp_tp);
+ }
+
out:
return (error);
}
MPTE_LOCK_ASSERT_HELD(mpte); /* same as MP socket lock */
/* MPTCP has at most 1 association */
- *cnt = (mpte->mpte_associd != ASSOCID_ANY) ? 1 : 0;
+ *cnt = (mpte->mpte_associd != SAE_ASSOCID_ANY) ? 1 : 0;
/* just asking how many there are? */
if (aidp == USER_ADDR_NULL)
* Handle SIOCGCONNIDS ioctl for PF_MULTIPATH domain.
*/
static int
-mptcp_getconnids(struct mptses *mpte, associd_t aid, uint32_t *cnt,
+mptcp_getconnids(struct mptses *mpte, sae_associd_t aid, uint32_t *cnt,
user_addr_t cidp)
{
struct mptsub *mpts;
MPTE_LOCK_ASSERT_HELD(mpte); /* same as MP socket lock */
- if (aid != ASSOCID_ANY && aid != ASSOCID_ALL &&
+ if (aid != SAE_ASSOCID_ANY && aid != SAE_ASSOCID_ALL &&
aid != mpte->mpte_associd)
return (EINVAL);
* Handle SIOCGCONNINFO ioctl for PF_MULTIPATH domain.
*/
static int
-mptcp_getconninfo(struct mptses *mpte, connid_t *cid, uint32_t *flags,
+mptcp_getconninfo(struct mptses *mpte, sae_connid_t *cid, uint32_t *flags,
uint32_t *ifindex, int32_t *soerror, user_addr_t src, socklen_t *src_len,
user_addr_t dst, socklen_t *dst_len, uint32_t *aux_type,
user_addr_t aux_data, uint32_t *aux_len)
MPTE_LOCK_ASSERT_HELD(mpte); /* same as MP socket lock */
- if (*cid == CONNID_ALL)
+ if (*cid == SAE_CONNID_ALL)
return (EINVAL);
TAILQ_FOREACH(mpts, &mpte->mpte_subflows, mpts_entry) {
- if (mpts->mpts_connid == *cid || *cid == CONNID_ANY)
+ if (mpts->mpts_connid == *cid || *cid == SAE_CONNID_ANY)
break;
}
if (mpts == NULL)
- return ((*cid == CONNID_ANY) ? ENXIO : EINVAL);
+ return ((*cid == SAE_CONNID_ANY) ? ENXIO : EINVAL);
MPTS_LOCK(mpts);
ifp = mpts->mpts_outif;
goto out;
}
}
+ mptcplog((LOG_DEBUG, "MPTCP Socket: "
+ "%s: cid %d flags %x \n",
+ __func__, mpts->mpts_connid, mpts->mpts_flags),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_VERBOSE);
+
out:
MPTS_UNLOCK(mpts);
return (error);
* Handle SIOCSCONNORDER
*/
int
-mptcp_setconnorder(struct mptses *mpte, connid_t cid, uint32_t rank)
+mptcp_setconnorder(struct mptses *mpte, sae_connid_t cid, uint32_t rank)
{
struct mptsub *mpts, *mpts1;
int error = 0;
MPTE_LOCK_ASSERT_HELD(mpte); /* same as MP socket lock */
- mptcplog((LOG_DEBUG, "%s: cid %d rank %d \n", __func__, cid, rank));
+ mptcplog((LOG_DEBUG, "MPTCP Socket: "
+ "%s: cid %d rank %d \n", __func__, cid, rank),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_VERBOSE);
- if (cid == CONNID_ANY || cid == CONNID_ALL) {
+ if (cid == SAE_CONNID_ANY || cid == SAE_CONNID_ALL) {
error = EINVAL;
goto out;
}
if (mpts1 != mpts &&
(mpts1->mpts_flags & MPTSF_PREFERRED)) {
mpts1->mpts_flags &= ~MPTSF_PREFERRED;
- if (mpte->mpte_nummpcapflows > 1)
+ if (mpte->mpte_nummpcapflows > 1)
mptcp_connorder_helper(mpts1);
} else if (mpts1 == mpts) {
mpts1->mpts_rank = 1;
tp->t_mpflags &= ~TMPF_BACKUP_PATH;
else
tp->t_mpflags |= TMPF_BACKUP_PATH;
- mptcplog((LOG_DEBUG, "%s cid %d flags %x", __func__,
- mpts->mpts_connid, mpts->mpts_flags));
+
socket_unlock(so, 0);
}
* Handle SIOCSGONNORDER
*/
int
-mptcp_getconnorder(struct mptses *mpte, connid_t cid, uint32_t *rank)
+mptcp_getconnorder(struct mptses *mpte, sae_connid_t cid, uint32_t *rank)
{
struct mptsub *mpts;
int error = 0;
VERIFY(rank != NULL);
*rank = 0;
- if (cid == CONNID_ANY || cid == CONNID_ALL) {
+ if (cid == SAE_CONNID_ANY || cid == SAE_CONNID_ALL) {
error = EINVAL;
goto out;
}
* connection while keeping the MPTCP-level connection (association).
*/
static int
-mptcp_disconnectx(struct mptses *mpte, associd_t aid, connid_t cid)
+mptcp_disconnectx(struct mptses *mpte, sae_associd_t aid, sae_connid_t cid)
{
struct mptsub *mpts;
struct socket *mp_so;
mp_so = mpte->mpte_mppcb->mpp_socket;
mp_tp = mpte->mpte_mptcb;
- mptcplog((LOG_DEBUG, "%s: mp_so 0x%llx aid %d cid %d\n", __func__,
- (u_int64_t)VM_KERNEL_ADDRPERM(mp_so), aid, cid));
- DTRACE_MPTCP5(disconnectx, struct mptses *, mpte, associd_t, aid,
- connid_t, cid, struct socket *, mp_so, struct mptcb *, mp_tp);
+ mptcplog((LOG_DEBUG, "MPTCP Socket: "
+ "%s: mp_so 0x%llx aid %d cid %d %d\n", __func__,
+ (u_int64_t)VM_KERNEL_ADDRPERM(mp_so), aid, cid, mp_so->so_error),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_LOG);
- VERIFY(aid == ASSOCID_ANY || aid == ASSOCID_ALL ||
+ DTRACE_MPTCP5(disconnectx, struct mptses *, mpte, sae_associd_t, aid,
+ sae_connid_t, cid, struct socket *, mp_so, struct mptcb *, mp_tp);
+
+ VERIFY(aid == SAE_ASSOCID_ANY || aid == SAE_ASSOCID_ALL ||
aid == mpte->mpte_associd);
/* terminate the association? */
- if (cid == CONNID_ANY || cid == CONNID_ALL) {
+ if (cid == SAE_CONNID_ANY || cid == SAE_CONNID_ALL) {
/* if we're not detached, go thru socket state checks */
if (!(mp_so->so_flags & SOF_PCBCLEARING)) {
if (!(mp_so->so_state & (SS_ISCONNECTED|
(void) mptcp_output(mpte);
}
} else {
+ bool disconnect_embryonic_subflows = false;
+ struct socket *so = NULL;
+
TAILQ_FOREACH(mpts, &mpte->mpte_subflows, mpts_entry) {
if (mpts->mpts_connid != cid)
continue;
+
MPTS_LOCK(mpts);
+ /*
+ * Check if disconnected subflow is the one used
+ * to initiate MPTCP connection.
+ * If it is and the connection is not yet join ready
+ * disconnect all other subflows.
+ */
+ so = mpts->mpts_socket;
+ if (!(mp_tp->mpt_flags & MPTCPF_JOIN_READY) &&
+ so && !(so->so_flags & SOF_MP_SEC_SUBFLOW)) {
+ disconnect_embryonic_subflows = true;
+ }
+
+ mpts->mpts_flags |= MPTSF_USER_DISCONNECT;
mptcp_subflow_disconnect(mpte, mpts, FALSE);
MPTS_UNLOCK(mpts);
break;
error = EINVAL;
goto out;
}
+
+ if (disconnect_embryonic_subflows) {
+ TAILQ_FOREACH(mpts, &mpte->mpte_subflows, mpts_entry) {
+ if (mpts->mpts_connid == cid)
+ continue;
+ MPTS_LOCK(mpts);
+ mptcp_subflow_disconnect(mpte, mpts, TRUE);
+ MPTS_UNLOCK(mpts);
+ }
+ }
}
if (error == 0)
return (error);
}
+/*
+ * Wrapper function to support disconnect on socket
+ */
+static int
+mptcp_usr_disconnect(struct socket *mp_so)
+{
+ int error = 0;
+
+ error = mptcp_usr_disconnectx(mp_so, SAE_ASSOCID_ALL, SAE_CONNID_ALL);
+ return (error);
+}
+
/*
* User-protocol pru_disconnectx callback.
*/
static int
-mptcp_usr_disconnectx(struct socket *mp_so, associd_t aid, connid_t cid)
+mptcp_usr_disconnectx(struct socket *mp_so, sae_associd_t aid, sae_connid_t cid)
{
struct mppcb *mpp = sotomppcb(mp_so);
struct mptses *mpte;
VERIFY(mpte != NULL);
MPTE_LOCK_ASSERT_HELD(mpte); /* same as MP socket lock */
- if (aid != ASSOCID_ANY && aid != ASSOCID_ALL &&
+ if (aid != SAE_ASSOCID_ANY && aid != SAE_ASSOCID_ALL &&
aid != mpte->mpte_associd) {
error = EINVAL;
goto out;
MPT_LOCK(mp_tp);
mptcp_close_fsm(mp_tp, MPCE_CLOSE);
- if (mp_tp->mpt_state == TCPS_CLOSED) {
+ if (mp_tp->mpt_state == MPTCPS_CLOSED) {
mpte = mptcp_close(mpte, mp_tp);
MPT_UNLOCK(mp_tp);
} else if (mp_tp->mpt_state >= MPTCPS_FIN_WAIT_2) {
MPT_UNLOCK(mp_tp);
soisdisconnected(mp_so);
+ TAILQ_FOREACH(mpts, &mpte->mpte_subflows, mpts_entry) {
+ MPTS_LOCK(mpts);
+ mpts->mpts_flags |= MPTSF_USER_DISCONNECT;
+ MPTS_UNLOCK(mpts);
+ }
} else {
- mp_tp->mpt_sndmax += 1; /* adjust for Data FIN */
MPT_UNLOCK(mp_tp);
TAILQ_FOREACH(mpts, &mpte->mpte_subflows, mpts_entry) {
MPTS_LOCK(mpts);
+ mpts->mpts_flags |= MPTSF_USER_DISCONNECT;
mptcp_subflow_disconnect(mpte, mpts, FALSE);
MPTS_UNLOCK(mpts);
}
}
- /*
- * XXX: adi@apple.com
- *
- * Do we need to handle time wait specially here? We need to handle
- * the case where MPTCP has been established, but we have not usable
- * subflow to use. Do we want to wait a while before forcibly
- * tearing this MPTCP down, in case we have one or more subflows
- * that are flow controlled?
- */
return (mpte);
}
* User-protocol pru_peeloff callback.
*/
static int
-mptcp_usr_peeloff(struct socket *mp_so, associd_t aid, struct socket **psop)
+mptcp_usr_peeloff(struct socket *mp_so, sae_associd_t aid, struct socket **psop)
{
struct mppcb *mpp = sotomppcb(mp_so);
struct mptses *mpte;
* yet associated (MPTCP-level connection has not been established.)
*/
static int
-mptcp_peeloff(struct mptses *mpte, associd_t aid, struct socket **psop)
+mptcp_peeloff(struct mptses *mpte, sae_associd_t aid, struct socket **psop)
{
struct socket *so = NULL, *mp_so;
struct mptsub *mpts;
VERIFY(psop != NULL);
*psop = NULL;
- DTRACE_MPTCP3(peeloff, struct mptses *, mpte, associd_t, aid,
+ DTRACE_MPTCP3(peeloff, struct mptses *, mpte, sae_associd_t, aid,
struct socket *, mp_so);
/* peeloff cannot happen after an association is established */
- if (mpte->mpte_associd != ASSOCID_ANY) {
+ if (mpte->mpte_associd != SAE_ASSOCID_ANY) {
error = EINVAL;
goto out;
}
- if (aid != ASSOCID_ANY && aid != ASSOCID_ALL) {
+ if (aid != SAE_ASSOCID_ANY && aid != SAE_ASSOCID_ALL) {
error = EINVAL;
goto out;
}
}
*psop = so;
- mptcplog((LOG_DEBUG, "%s: mp_so 0x%llx\n", __func__,
- (u_int64_t)VM_KERNEL_ADDRPERM(mp_so)));
+ mptcplog((LOG_DEBUG, "MPTCP Socket: "
+ "%s: mp_so 0x%llx\n", __func__,
+ (u_int64_t)VM_KERNEL_ADDRPERM(mp_so)),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_LOG);
+
out:
return (error);
}
mpte = mptompte(mpp);
VERIFY(mpte != NULL);
- if (!(mp_so->so_state & SS_ISCONNECTED)) {
+ if (!(mp_so->so_state & SS_ISCONNECTED) &&
+ (!(mp_so->so_flags1 & SOF1_PRECONNECT_DATA))) {
error = ENOTCONN;
goto out;
}
(void) sbappendstream(&mp_so->so_snd, m);
m = NULL;
- if (mpte != NULL) {
- /*
- * XXX: adi@apple.com
- *
- * PRUS_MORETOCOME could be set, but we don't check it now.
- */
- error = mptcp_output(mpte);
+ /*
+ * XXX: adi@apple.com
+ *
+ * PRUS_MORETOCOME could be set, but we don't check it now.
+ */
+ error = mptcp_output(mpte);
+ if (error != 0)
+ goto out;
+
+ if (mp_so->so_state & SS_ISCONNECTING) {
+ if (mp_so->so_state & SS_NBIO)
+ error = EWOULDBLOCK;
+ else
+ error = sbwait(&mp_so->so_snd);
}
-
+
out:
if (error) {
if (m != NULL)
if (control != NULL)
m_freem(control);
+ /* clear SOF1_PRECONNECT_DATA after one write */
+ if (mp_so->so_flags1 & SOF1_PRECONNECT_DATA)
+ mp_so->so_flags1 &= ~SOF1_PRECONNECT_DATA;
+
return (error);
}
case SO_RECV_ANYIF: /* MP + subflow */
case SO_RESTRICTIONS: /* MP + subflow */
case SO_FLUSH: /* MP + subflow */
+ case SO_MPTCP_FASTJOIN: /* MP + subflow */
+ case SO_NOWAKEFROMSLEEP:
/*
* Tell the caller that these options are to be processed;
* these will also be recorded later by mptcp_setopt().
case SO_PRIVILEGED_TRAFFIC_CLASS:
case SO_RECV_ANYIF:
case SO_RESTRICTIONS:
+ case SO_NOWAKEFROMSLEEP:
+ case SO_MPTCP_FASTJOIN:
/* record it */
break;
case SO_FLUSH:
case PERSIST_TIMEOUT:
/* eligible; record it */
break;
+ case TCP_NOTSENT_LOWAT:
+ /* record at MPTCP level */
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error)
+ goto out;
+ if (optval < 0) {
+ error = EINVAL;
+ goto out;
+ } else {
+ if (optval == 0) {
+ mp_so->so_flags &= ~SOF_NOTSENT_LOWAT;
+ error = mptcp_set_notsent_lowat(mpte,0);
+ } else {
+ mp_so->so_flags |= SOF_NOTSENT_LOWAT;
+ error = mptcp_set_notsent_lowat(mpte,
+ optval);
+ }
+ }
+ goto out;
default:
/* not eligible */
error = ENOPROTOOPT;
if (mpo == NULL) {
error = ENOBUFS;
} else {
- mptcplog((LOG_DEBUG, "%s: mp_so 0x%llx sopt %s "
+ mptcplog((LOG_DEBUG, "MPTCP Socket: "
+ "%s: mp_so 0x%llx sopt %s "
"val %d %s\n", __func__,
(u_int64_t)VM_KERNEL_ADDRPERM(mp_so),
mptcp_sopt2str(level, optname, buf,
sizeof (buf)), optval,
(mpo->mpo_flags & MPOF_ATTACHED) ?
- "updated" : "recorded"));
+ "updated" : "recorded"),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_LOG);
/* initialize or update, as needed */
mpo->mpo_intval = optval;
}
out:
if (error == 0 && mpo != NULL) {
- mptcplog((LOG_ERR, "%s: mp_so 0x%llx sopt %s val %d set %s\n",
+ mptcplog((LOG_ERR, "MPTCP Socket: "
+ "%s: mp_so 0x%llx sopt %s val %d set %s\n",
__func__, (u_int64_t)VM_KERNEL_ADDRPERM(mp_so),
mptcp_sopt2str(level, optname, buf,
sizeof (buf)), optval, (mpo->mpo_flags & MPOF_INTERIM) ?
- "pending" : "successful"));
+ "pending" : "successful"),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
} else if (error != 0) {
- mptcplog((LOG_ERR, "%s: mp_so 0x%llx sopt %s can't be issued "
+ mptcplog((LOG_ERR, "MPTCP Socket: "
+ "%s: mp_so 0x%llx sopt %s can't be issued "
"error %d\n", __func__,
(u_int64_t)VM_KERNEL_ADDRPERM(mp_so), mptcp_sopt2str(level,
- optname, buf, sizeof (buf)), error));
+ optname, buf, sizeof (buf)), error),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
}
return (error);
}
case TCP_CONNECTIONTIMEOUT:
case TCP_RXT_CONNDROPTIME:
case PERSIST_TIMEOUT:
+ case TCP_NOTSENT_LOWAT:
/* eligible; get the default value just in case */
error = mptcp_default_tcp_optval(mpte, sopt, &optval);
break;
break;
}
+ switch (sopt->sopt_name) {
+ case TCP_NOTSENT_LOWAT:
+ if (mpte->mpte_mppcb->mpp_socket->so_flags & SOF_NOTSENT_LOWAT)
+ optval = mptcp_get_notsent_lowat(mpte);
+ else
+ optval = 0;
+ goto out;
+ }
+
/*
* Search for a previously-issued TCP level socket option and
* return the recorded option value. This assumes that the
case TCP_KEEPCNT:
case TCP_CONNECTIONTIMEOUT:
case TCP_RXT_CONNDROPTIME:
+ case TCP_NOTSENT_LOWAT:
*optval = 0;
break;
/* we only handle socket and TCP-level socket options for MPTCP */
if (sopt->sopt_level != SOL_SOCKET && sopt->sopt_level != IPPROTO_TCP) {
char buf[32];
- mptcplog((LOG_DEBUG, "%s: mp_so 0x%llx sopt %s level not "
+ mptcplog((LOG_DEBUG, "MPTCP Socket: "
+ "%s: mp_so 0x%llx sopt %s level not "
"handled\n", __func__, (u_int64_t)VM_KERNEL_ADDRPERM(mp_so),
mptcp_sopt2str(sopt->sopt_level,
- sopt->sopt_name, buf, sizeof (buf))));
+ sopt->sopt_name, buf, sizeof (buf))),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_LOG);
error = EINVAL;
goto out;
}
case SO_RECV_ANYIF:
o = "SO_RECV_ANYIF";
break;
+ case SO_NOWAKEFROMSLEEP:
+ o = "SO_NOWAKEFROMSLEEP";
+ break;
+ case SO_MPTCP_FASTJOIN:
+ o = "SO_MPTCP_FASTJOIN";
+ break;
}
break;
case IPPROTO_TCP:
(void) snprintf(dst, size, "<%s,%s>", l, o);
return (dst);
}
+
+static int
+mptcp_usr_preconnect(struct socket *mp_so)
+{
+ struct mptsub *mpts = NULL;
+ struct mppcb *mpp = sotomppcb(mp_so);
+ struct mptses *mpte;
+ struct socket *so;
+ struct tcpcb *tp = NULL;
+
+ mpte = mptompte(mpp);
+ VERIFY(mpte != NULL);
+ MPTE_LOCK_ASSERT_HELD(mpte); /* same as MP socket lock */
+
+ mpts = mptcp_get_subflow(mpte, NULL, NULL);
+ if (mpts == NULL) {
+ mptcplog((LOG_ERR, "MPTCP Socket: "
+ "%s: mp_so 0x%llx invalid preconnect ", __func__,
+ (u_int64_t)VM_KERNEL_ADDRPERM(mp_so)),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
+ return (EINVAL);
+ }
+ MPTS_LOCK(mpts);
+ mpts->mpts_flags &= ~MPTSF_TFO_REQD;
+ so = mpts->mpts_socket;
+ socket_lock(so, 0);
+ tp = intotcpcb(sotoinpcb(so));
+ tp->t_mpflags &= ~TMPF_TFO_REQUEST;
+ int error = tcp_output(sototcpcb(so));
+ socket_unlock(so, 0);
+ MPTS_UNLOCK(mpts);
+ mp_so->so_flags1 &= ~SOF1_PRECONNECT_DATA;
+ return (error);
+}