#include <sys/param.h>
#include <sys/systm.h>
#include <netinet/in_systm.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <net/route.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/in_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcpip.h>
#include <netinet/tcp_fsm.h>
#include <netinet/mptcp_var.h>
#include <netinet/mptcp.h>
#include <netinet/mptcp_opt.h>
#include <netinet/mptcp_seq.h>
#include <libkern/crypto/sha1.h>
#include <netinet/mptcp_timer.h>
#include <mach/sdt.h>
static int mptcp_validate_join_hmac(struct tcpcb *, u_char*, int);
static int mptcp_snd_mpprio(struct tcpcb *tp, u_char *cp, int optlen);
static unsigned
mptcp_setup_first_subflow_syn_opts(struct socket *so, int flags, u_char *opt,
unsigned optlen)
{
struct tcpcb *tp = sototcpcb(so);
struct mptcb *mp_tp = NULL;
mp_tp = tptomptp(tp);
if (!(so->so_flags & SOF_MP_SUBFLOW))
return (optlen);
if (tp->t_rxtshift > mptcp_mpcap_retries)
return (optlen);
if ((flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)) {
struct mptcp_mpcapable_opt_rsp mptcp_opt;
mptcp_key_t mp_localkey = 0;
mp_localkey = mptcp_get_localkey(mp_tp);
if (mp_localkey == 0) {
return (optlen);
}
bzero(&mptcp_opt,
sizeof (struct mptcp_mpcapable_opt_rsp));
mptcp_opt.mmc_common.mmco_kind = TCPOPT_MULTIPATH;
mptcp_opt.mmc_common.mmco_len =
sizeof (struct mptcp_mpcapable_opt_rsp);
mptcp_opt.mmc_common.mmco_subtype = MPO_CAPABLE;
MPT_LOCK_SPIN(mp_tp);
mptcp_opt.mmc_common.mmco_version = mp_tp->mpt_version;
mptcp_opt.mmc_common.mmco_flags |= MPCAP_PROPOSAL_SBIT;
if (mp_tp->mpt_flags & MPTCPF_CHECKSUM)
mptcp_opt.mmc_common.mmco_flags |=
MPCAP_CHECKSUM_CBIT;
MPT_UNLOCK(mp_tp);
mptcp_opt.mmc_localkey = mp_localkey;
memcpy(opt + optlen, &mptcp_opt,
mptcp_opt.mmc_common.mmco_len);
optlen += mptcp_opt.mmc_common.mmco_len;
if (mptcp_dbg >= MP_VERBOSE_DEBUG_2) {
printf("%s: SYN_ACK localkey = %llx \n",
__func__, mp_localkey);
}
} else {
struct mptcp_mpcapable_opt_common mptcp_opt;
mptcp_key_t mp_localkey = 0;
mp_localkey = mptcp_get_localkey(mp_tp);
so->so_flags |= SOF_MPTCP_CLIENT;
if (mp_localkey == 0) {
return (optlen);
}
bzero(&mptcp_opt,
sizeof (struct mptcp_mpcapable_opt_common));
mptcp_opt.mmco_kind = TCPOPT_MULTIPATH;
mptcp_opt.mmco_len =
sizeof (struct mptcp_mpcapable_opt_common) +
sizeof (mptcp_key_t);
mptcp_opt.mmco_subtype = MPO_CAPABLE;
MPT_LOCK_SPIN(mp_tp);
mptcp_opt.mmco_version = mp_tp->mpt_version;
mptcp_opt.mmco_flags |= MPCAP_PROPOSAL_SBIT;
if (mp_tp->mpt_flags & MPTCPF_CHECKSUM)
mptcp_opt.mmco_flags |= MPCAP_CHECKSUM_CBIT;
MPT_UNLOCK(mp_tp);
(void) memcpy(opt + optlen, &mptcp_opt,
sizeof (struct mptcp_mpcapable_opt_common));
optlen += sizeof (struct mptcp_mpcapable_opt_common);
(void) memcpy(opt + optlen, &mp_localkey,
sizeof (mptcp_key_t));
optlen += sizeof (mptcp_key_t);
}
return (optlen);
}
static unsigned
mptcp_setup_join_subflow_syn_opts(struct socket *so, int flags, u_char *opt,
unsigned optlen)
{
struct inpcb *inp = sotoinpcb(so);
struct tcpcb *tp = NULL;
if (!inp)
return (optlen);
tp = intotcpcb(inp);
if (!tp)
return (optlen);
if (!tp->t_mptcb)
return (optlen);
if ((flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)) {
struct mptcp_mpjoin_opt_rsp mpjoin_rsp;
bzero(&mpjoin_rsp, sizeof (mpjoin_rsp));
mpjoin_rsp.mmjo_kind = TCPOPT_MULTIPATH;
mpjoin_rsp.mmjo_len = sizeof (mpjoin_rsp);
mpjoin_rsp.mmjo_subtype_bkp = MPO_JOIN << 4;
if (tp->t_mpflags & TMPF_BACKUP_PATH)
mpjoin_rsp.mmjo_subtype_bkp |= MPTCP_BACKUP;
mpjoin_rsp.mmjo_addr_id = tp->t_local_aid;
mptcp_get_rands(tp->t_local_aid, tptomptp(tp),
&mpjoin_rsp.mmjo_rand, NULL);
mpjoin_rsp.mmjo_mac = mptcp_get_trunced_hmac(tp->t_local_aid,
tptomptp(tp));
memcpy(opt + optlen, &mpjoin_rsp, mpjoin_rsp.mmjo_len);
optlen += mpjoin_rsp.mmjo_len;
} else {
struct mptcp_mpjoin_opt_req mpjoin_req;
bzero(&mpjoin_req, sizeof (mpjoin_req));
mpjoin_req.mmjo_kind = TCPOPT_MULTIPATH;
mpjoin_req.mmjo_len = sizeof (mpjoin_req);
mpjoin_req.mmjo_subtype_bkp = MPO_JOIN << 4;
mpjoin_req.mmjo_subtype_bkp |= MPTCP_BACKUP;
tp->t_mpflags |= TMPF_BACKUP_PATH;
mpjoin_req.mmjo_addr_id = tp->t_local_aid;
mpjoin_req.mmjo_peer_token = mptcp_get_remotetoken(tp->t_mptcb);
if (mpjoin_req.mmjo_peer_token == 0) {
if (mptcp_dbg >= MP_ERR_DEBUG)
printf("%s: zero peer token \n", __func__);
}
mptcp_get_rands(tp->t_local_aid, tptomptp(tp),
&mpjoin_req.mmjo_rand, NULL);
memcpy(opt + optlen, &mpjoin_req, mpjoin_req.mmjo_len);
optlen += mpjoin_req.mmjo_len;
}
return (optlen);
}
unsigned
mptcp_setup_join_ack_opts(struct tcpcb *tp, u_char *opt, unsigned optlen)
{
unsigned new_optlen;
struct mptcp_mpjoin_opt_rsp2 join_rsp2;
if ((MAX_TCPOPTLEN - optlen) < sizeof (struct mptcp_mpjoin_opt_rsp2)) {
printf("%s: no space left %d \n", __func__, optlen);
return (optlen);
}
bzero(&join_rsp2, sizeof (struct mptcp_mpjoin_opt_rsp2));
join_rsp2.mmjo_kind = TCPOPT_MULTIPATH;
join_rsp2.mmjo_len = sizeof (struct mptcp_mpjoin_opt_rsp2);
join_rsp2.mmjo_subtype = MPO_JOIN;
mptcp_get_hmac(tp->t_local_aid, tptomptp(tp),
(u_char*)&join_rsp2.mmjo_mac,
sizeof (join_rsp2.mmjo_mac));
memcpy(opt + optlen, &join_rsp2, join_rsp2.mmjo_len);
new_optlen = optlen + join_rsp2.mmjo_len;
return (new_optlen);
}
unsigned
mptcp_setup_syn_opts(struct socket *so, int flags, u_char *opt, unsigned optlen)
{
unsigned new_optlen;
if (mptcp_enable == 0) {
return (optlen);
}
if (!(so->so_flags & SOF_MP_SEC_SUBFLOW)) {
new_optlen = mptcp_setup_first_subflow_syn_opts(so, flags, opt,
optlen);
} else {
new_optlen = mptcp_setup_join_subflow_syn_opts(so, flags, opt,
optlen);
}
return (new_optlen);
}
static int
mptcp_send_mpfail(struct tcpcb *tp, u_char *opt, unsigned int optlen)
{
#pragma unused(tp, opt, optlen)
struct mptcb *mp_tp = NULL;
struct mptcp_mpfail_opt fail_opt;
uint64_t dsn;
int len = sizeof (struct mptcp_mpfail_opt);
mp_tp = tptomptp(tp);
if (mp_tp == NULL) {
tp->t_mpflags &= ~TMPF_SND_MPFAIL;
return (optlen);
}
if ((MAX_TCPOPTLEN - optlen) < sizeof (struct mptcp_mpfail_opt)) {
tp->t_mpflags &= ~TMPF_SND_MPFAIL;
return (optlen);
}
MPT_LOCK(mp_tp);
dsn = mp_tp->mpt_rcvnxt;
MPT_UNLOCK(mp_tp);
bzero(&fail_opt, sizeof (fail_opt));
fail_opt.mfail_kind = TCPOPT_MULTIPATH;
fail_opt.mfail_len = len;
fail_opt.mfail_subtype = MPO_FAIL;
fail_opt.mfail_dsn = mptcp_hton64(dsn);
memcpy(opt + optlen, &fail_opt, len);
optlen += len;
tp->t_mpflags &= ~TMPF_SND_MPFAIL;
if (mptcp_dbg >= MP_ERR_DEBUG)
printf("%s: %d \n", __func__, tp->t_local_aid);
return (optlen);
}
static int
mptcp_send_infinite_mapping(struct tcpcb *tp, u_char *opt, unsigned int optlen)
{
struct mptcp_dsn_opt infin_opt;
struct mptcb *mp_tp = NULL;
size_t len = sizeof (struct mptcp_dsn_opt);
struct socket *so = tp->t_inpcb->inp_socket;
int error = 0;
int csum_len = 0;
if (!so)
return (optlen);
mp_tp = tptomptp(tp);
if (mp_tp == NULL)
return (optlen);
MPT_LOCK(mp_tp);
if (mp_tp->mpt_flags & MPTCPF_CHECKSUM)
csum_len = 2;
if ((MAX_TCPOPTLEN - optlen) < (len + csum_len)) {
MPT_UNLOCK(mp_tp);
return (optlen);
}
bzero(&infin_opt, sizeof (infin_opt));
infin_opt.mdss_copt.mdss_kind = TCPOPT_MULTIPATH;
infin_opt.mdss_copt.mdss_len = len + csum_len;
infin_opt.mdss_copt.mdss_subtype = MPO_DSS;
infin_opt.mdss_copt.mdss_flags |= MDSS_M;
if (mp_tp->mpt_flags & MPTCPF_RECVD_MPFAIL) {
infin_opt.mdss_dsn = (u_int32_t)
MPTCP_DATASEQ_LOW32(mp_tp->mpt_dsn_at_csum_fail);
error = mptcp_get_map_for_dsn(so, mp_tp->mpt_dsn_at_csum_fail,
&infin_opt.mdss_subflow_seqn);
} else {
infin_opt.mdss_dsn = (u_int32_t)
MPTCP_DATASEQ_LOW32(mp_tp->mpt_snduna);
infin_opt.mdss_subflow_seqn = tp->snd_una - tp->iss;
}
MPT_UNLOCK(mp_tp);
if (error != 0)
return (optlen);
if ((infin_opt.mdss_dsn == 0) || (infin_opt.mdss_subflow_seqn == 0)) {
return (optlen);
}
infin_opt.mdss_dsn = htonl(infin_opt.mdss_dsn);
infin_opt.mdss_subflow_seqn = htonl(infin_opt.mdss_subflow_seqn);
infin_opt.mdss_data_len = 0;
memcpy(opt + optlen, &infin_opt, len);
optlen += len;
if (csum_len != 0) {
uint16_t csum = 0;
memcpy(opt + optlen, &csum, csum_len);
optlen += csum_len;
}
if (mptcp_dbg == MP_VERBOSE_DEBUG_1) {
printf("%s: dsn = %x, seq = %x len = %x\n", __func__,
ntohl(infin_opt.mdss_dsn),
ntohl(infin_opt.mdss_subflow_seqn),
ntohs(infin_opt.mdss_data_len));
}
tp->t_mpflags |= TMPF_INFIN_SENT;
tcpstat.tcps_estab_fallback++;
return (optlen);
}
static int
mptcp_ok_to_fin(struct tcpcb *tp, u_int64_t dsn, u_int32_t datalen)
{
struct mptcb *mp_tp = NULL;
mp_tp = tptomptp(tp);
MPT_LOCK(mp_tp);
dsn = (mp_tp->mpt_sndmax & MPTCP_DATASEQ_LOW32_MASK) | dsn;
if ((dsn + datalen) == mp_tp->mpt_sndmax) {
MPT_UNLOCK(mp_tp);
return (1);
}
MPT_UNLOCK(mp_tp);
return (0);
}
static int
mptcp_send_fastclose(struct tcpcb *tp, u_char *opt, unsigned int optlen,
int flags)
{
struct mptcp_fastclose_opt fastclose_opt;
struct mptcb *mp_tp = tptomptp(tp);
if (flags != TH_ACK)
return (optlen);
if ((MAX_TCPOPTLEN - optlen) <
sizeof (struct mptcp_fastclose_opt)) {
return (optlen);
}
bzero(&fastclose_opt, sizeof (struct mptcp_fastclose_opt));
fastclose_opt.mfast_kind = TCPOPT_MULTIPATH;
fastclose_opt.mfast_len = sizeof (struct mptcp_fastclose_opt);
fastclose_opt.mfast_subtype = MPO_FASTCLOSE;
MPT_LOCK_SPIN(mp_tp);
fastclose_opt.mfast_key = mptcp_get_remotekey(mp_tp);
MPT_UNLOCK(mp_tp);
memcpy(opt + optlen, &fastclose_opt, fastclose_opt.mfast_len);
optlen += fastclose_opt.mfast_len;
return (optlen);
}
unsigned int
mptcp_setup_opts(struct tcpcb *tp, int32_t off, u_char *opt,
unsigned int optlen, int flags, int datalen,
unsigned int **dss_lenp, u_int8_t **finp, u_int64_t *dss_valp,
u_int32_t **sseqp)
{
struct inpcb *inp = (struct inpcb *)tp->t_inpcb;
struct socket *so = inp->inp_socket;
struct mptcb *mp_tp = tptomptp(tp);
boolean_t do_csum = FALSE;
boolean_t send_64bit_dsn = FALSE;
boolean_t send_64bit_ack = FALSE;
if (mptcp_enable == 0) {
return (optlen);
}
if (mp_tp == NULL) {
return (optlen);
}
if (mp_tp->mpt_flags & MPTCPF_CHECKSUM)
do_csum = TRUE;
if (flags & TH_SYN)
return (optlen);
if ((MAX_TCPOPTLEN - optlen) <
sizeof (struct mptcp_mpcapable_opt_common)) {
if (mptcp_dbg >= MP_ERR_DEBUG) {
printf("MPTCP ERROR %s: no space left %d flags %x "
"tp->t_mpflags %x"
"len %d\n", __func__, optlen, flags, tp->t_mpflags,
datalen);
}
return (optlen);
}
if (tp->t_mpflags & TMPF_FASTCLOSE) {
optlen = mptcp_send_fastclose(tp, opt, optlen, flags);
VERIFY(datalen == 0);
return (optlen);
}
if (tp->t_mpflags & TMPF_TCP_FALLBACK) {
if (tp->t_mpflags & TMPF_SND_MPFAIL)
optlen = mptcp_send_mpfail(tp, opt, optlen);
else if (!(tp->t_mpflags & TMPF_INFIN_SENT))
optlen = mptcp_send_infinite_mapping(tp, opt, optlen);
return (optlen);
}
if (tp->t_mpflags & TMPF_SND_MPPRIO) {
optlen = mptcp_snd_mpprio(tp, opt, optlen);
return (optlen);
}
if ((tp->t_mpflags & TMPF_PREESTABLISHED) &&
(!(tp->t_mpflags & TMPF_SENT_KEYS)) &&
(!(tp->t_mpflags & TMPF_JOINED_FLOW))) {
struct mptcp_mpcapable_opt_rsp1 mptcp_opt;
if ((MAX_TCPOPTLEN - optlen) <
sizeof (struct mptcp_mpcapable_opt_rsp1))
return (optlen);
bzero(&mptcp_opt, sizeof (struct mptcp_mpcapable_opt_rsp1));
mptcp_opt.mmc_common.mmco_kind = TCPOPT_MULTIPATH;
mptcp_opt.mmc_common.mmco_len =
sizeof (struct mptcp_mpcapable_opt_rsp1);
mptcp_opt.mmc_common.mmco_subtype = MPO_CAPABLE;
mptcp_opt.mmc_common.mmco_version = MP_DRAFT_VERSION_12;
mptcp_opt.mmc_common.mmco_flags |= MPCAP_PROPOSAL_SBIT;
MPT_LOCK(mp_tp);
if (mp_tp->mpt_flags & MPTCPF_CHECKSUM)
mptcp_opt.mmc_common.mmco_flags |= MPCAP_CHECKSUM_CBIT;
mptcp_opt.mmc_localkey = mptcp_get_localkey(mp_tp);
mptcp_opt.mmc_remotekey = mptcp_get_remotekey(mp_tp);
MPT_UNLOCK(mp_tp);
memcpy(opt + optlen, &mptcp_opt, mptcp_opt.mmc_common.mmco_len);
optlen += mptcp_opt.mmc_common.mmco_len;
tp->t_mpflags |= TMPF_SENT_KEYS;
so->so_flags |= SOF_MPTCP_TRUE;
tp->t_mpflags &= ~TMPF_PREESTABLISHED;
tp->t_mpflags |= TMPF_MPTCP_TRUE;
if (!tp->t_mpuna) {
tp->t_mpuna = tp->snd_una;
} else {
}
if (mptcp_dbg >= MP_ERR_DEBUG) {
printf("MPTCP SUCCESS %s: established.\n", __func__);
}
return (optlen);
} else if (tp->t_mpflags & TMPF_MPTCP_TRUE) {
if (tp->t_mpflags & TMPF_SND_REM_ADDR) {
int rem_opt_len = sizeof (struct mptcp_remaddr_opt);
if ((optlen + rem_opt_len) <= MAX_TCPOPTLEN) {
mptcp_send_remaddr_opt(tp,
(struct mptcp_remaddr_opt *)(opt + optlen));
optlen += rem_opt_len;
return (optlen);
} else {
tp->t_mpflags &= ~TMPF_SND_REM_ADDR;
}
}
}
if ((tp->t_mpflags & TMPF_JOINED_FLOW) &&
(tp->t_mpflags & TMPF_PREESTABLISHED) &&
(!(tp->t_mpflags & TMPF_RECVD_JOIN)) &&
(tp->t_mpflags & TMPF_SENT_JOIN) &&
(!(tp->t_mpflags & TMPF_MPTCP_TRUE))) {
optlen = mptcp_setup_join_ack_opts(tp, opt, optlen);
if (!tp->t_mpuna) {
tp->t_mpuna = tp->snd_una;
}
tp->t_timer[TCPT_JACK_RXMT] =
OFFSET_FROM_START(tp, tcp_jack_rxmt);
return (optlen);
}
if (!(tp->t_mpflags & TMPF_MPTCP_TRUE))
return (optlen);
MPT_LOCK(mp_tp);
if (mp_tp->mpt_flags & MPTCPF_SND_64BITDSN) {
send_64bit_dsn = TRUE;
}
if (mp_tp->mpt_flags & MPTCPF_SND_64BITACK) {
send_64bit_ack = TRUE;
}
MPT_UNLOCK(mp_tp);
#define CHECK_OPTLEN { \
if ((MAX_TCPOPTLEN - optlen) < len) { \
if (mptcp_dbg >= MP_ERR_DEBUG) { \
printf("MPTCP ERROR %s: len %d optlen %d \n", \
__func__, \
len, optlen); \
} \
return (optlen); \
} \
}
#define DO_FIN(dsn_opt) { \
int sndfin = 0; \
sndfin = mptcp_ok_to_fin(tp, dsn_opt.mdss_dsn, datalen); \
if (sndfin) { \
dsn_opt.mdss_copt.mdss_flags |= MDSS_F; \
*finp = opt + optlen + offsetof(struct mptcp_dss_copt, \
mdss_flags); \
dsn_opt.mdss_data_len += 1; \
} \
}
#define CHECK_DATALEN { \
\
if ((datalen + optlen + len) > tp->t_maxopd) { \
if (mptcp_dbg >= MP_VERBOSE_DEBUG_2) \
printf("%s: nosp %d len %d opt %d %d %d\n", \
__func__, datalen, len, optlen, \
tp->t_maxseg, tp->t_maxopd); \
\
datalen = tp->t_maxopd - optlen - len; \
} \
}
if ((tp->t_mpflags & TMPF_SEND_DSN) &&
(send_64bit_dsn)) {
struct mptcp_dss64_ack32_opt dsn_ack_opt;
unsigned int len = sizeof (dsn_ack_opt);
if (do_csum) {
len += 2;
}
CHECK_OPTLEN;
bzero(&dsn_ack_opt, sizeof (dsn_ack_opt));
dsn_ack_opt.mdss_copt.mdss_kind = TCPOPT_MULTIPATH;
dsn_ack_opt.mdss_copt.mdss_subtype = MPO_DSS;
dsn_ack_opt.mdss_copt.mdss_len = len;
dsn_ack_opt.mdss_copt.mdss_flags |=
MDSS_M | MDSS_m | MDSS_A;
CHECK_DATALEN;
mptcp_output_getm_dsnmap64(so, off, (u_int32_t)datalen,
&dsn_ack_opt.mdss_dsn,
&dsn_ack_opt.mdss_subflow_seqn,
&dsn_ack_opt.mdss_data_len);
*dss_valp = dsn_ack_opt.mdss_dsn;
if ((dsn_ack_opt.mdss_data_len == 0) ||
(dsn_ack_opt.mdss_dsn == 0)) {
return (optlen);
}
if (tp->t_mpflags & TMPF_SEND_DFIN) {
DO_FIN(dsn_ack_opt);
}
MPT_LOCK(mp_tp);
dsn_ack_opt.mdss_ack =
htonl(MPTCP_DATAACK_LOW32(mp_tp->mpt_rcvnxt));
MPT_UNLOCK(mp_tp);
dsn_ack_opt.mdss_dsn = mptcp_hton64(dsn_ack_opt.mdss_dsn);
dsn_ack_opt.mdss_subflow_seqn = htonl(
dsn_ack_opt.mdss_subflow_seqn);
dsn_ack_opt.mdss_data_len = htons(
dsn_ack_opt.mdss_data_len);
*dss_lenp = (unsigned int *)(void *)(opt + optlen +
offsetof(struct mptcp_dss64_ack32_opt, mdss_data_len));
memcpy(opt + optlen, &dsn_ack_opt, sizeof (dsn_ack_opt));
if (do_csum) {
*sseqp = (u_int32_t *)(void *)(opt + optlen +
offsetof(struct mptcp_dss64_ack32_opt,
mdss_subflow_seqn));
}
optlen += len;
if (mptcp_dbg == MP_VERBOSE_DEBUG_2) {
printf("%s: long DSS = %llx ACK = %llx \n",
__func__,
mptcp_ntoh64(dsn_ack_opt.mdss_dsn),
mptcp_ntoh64(dsn_ack_opt.mdss_ack));
}
tp->t_mpflags &= ~TMPF_MPTCP_ACKNOW;
return (optlen);
}
if ((tp->t_mpflags & TMPF_SEND_DSN) &&
(!send_64bit_dsn) &&
!(tp->t_mpflags & TMPF_MPTCP_ACKNOW)) {
struct mptcp_dsn_opt dsn_opt;
unsigned int len = sizeof (struct mptcp_dsn_opt);
if (do_csum) {
len += 2;
}
CHECK_OPTLEN;
bzero(&dsn_opt, sizeof (dsn_opt));
dsn_opt.mdss_copt.mdss_kind = TCPOPT_MULTIPATH;
dsn_opt.mdss_copt.mdss_subtype = MPO_DSS;
dsn_opt.mdss_copt.mdss_len = len;
dsn_opt.mdss_copt.mdss_flags |= MDSS_M;
CHECK_DATALEN;
mptcp_output_getm_dsnmap32(so, off, (u_int32_t)datalen,
&dsn_opt.mdss_dsn,
&dsn_opt.mdss_subflow_seqn, &dsn_opt.mdss_data_len,
dss_valp);
if ((dsn_opt.mdss_data_len == 0) ||
(dsn_opt.mdss_dsn == 0)) {
return (optlen);
}
if (tp->t_mpflags & TMPF_SEND_DFIN) {
DO_FIN(dsn_opt);
}
dsn_opt.mdss_dsn = htonl(dsn_opt.mdss_dsn);
dsn_opt.mdss_subflow_seqn = htonl(dsn_opt.mdss_subflow_seqn);
dsn_opt.mdss_data_len = htons(dsn_opt.mdss_data_len);
*dss_lenp = (unsigned int *)(void *)(opt + optlen +
offsetof(struct mptcp_dsn_opt, mdss_data_len));
memcpy(opt + optlen, &dsn_opt, sizeof (dsn_opt));
if (do_csum) {
*sseqp = (u_int32_t *)(void *)(opt + optlen +
offsetof(struct mptcp_dsn_opt, mdss_subflow_seqn));
}
optlen += len;
if (mptcp_dbg == MP_VERBOSE_DEBUG_2) {
printf("%s: DSS option. dsn = %x, seq = %x len = %x\n",
__func__,
ntohl(dsn_opt.mdss_dsn),
ntohl(dsn_opt.mdss_subflow_seqn),
ntohs(dsn_opt.mdss_data_len));
}
tp->t_mpflags &= ~TMPF_MPTCP_ACKNOW;
return (optlen);
}
if ((tp->t_mpflags & TMPF_MPTCP_ACKNOW) &&
(!send_64bit_ack) &&
!(tp->t_mpflags & TMPF_SEND_DSN) &&
!(tp->t_mpflags & TMPF_SEND_DFIN)) {
struct mptcp_data_ack_opt dack_opt;
unsigned int len = 0;
do_ack32_only:
len = sizeof (dack_opt);
CHECK_OPTLEN;
bzero(&dack_opt, len);
dack_opt.mdss_copt.mdss_kind = TCPOPT_MULTIPATH;
dack_opt.mdss_copt.mdss_len = len;
dack_opt.mdss_copt.mdss_subtype = MPO_DSS;
dack_opt.mdss_copt.mdss_flags |= MDSS_A;
MPT_LOCK_SPIN(mp_tp);
dack_opt.mdss_ack =
htonl(MPTCP_DATAACK_LOW32(mp_tp->mpt_rcvnxt));
MPT_UNLOCK(mp_tp);
memcpy(opt + optlen, &dack_opt, len);
optlen += len;
VERIFY(optlen <= MAX_TCPOPTLEN);
tp->t_mpflags &= ~TMPF_MPTCP_ACKNOW;
return (optlen);
}
if ((tp->t_mpflags & TMPF_MPTCP_ACKNOW) &&
(send_64bit_ack) &&
!(tp->t_mpflags & TMPF_SEND_DSN) &&
!(tp->t_mpflags & TMPF_SEND_DFIN)) {
struct mptcp_data_ack64_opt dack_opt;
unsigned int len = 0;
do_ack64_only:
len = sizeof (dack_opt);
CHECK_OPTLEN;
bzero(&dack_opt, len);
dack_opt.mdss_copt.mdss_kind = TCPOPT_MULTIPATH;
dack_opt.mdss_copt.mdss_len = len;
dack_opt.mdss_copt.mdss_subtype = MPO_DSS;
dack_opt.mdss_copt.mdss_flags |= (MDSS_A | MDSS_a);
MPT_LOCK_SPIN(mp_tp);
dack_opt.mdss_ack = mptcp_hton64(mp_tp->mpt_rcvnxt);
mp_tp->mpt_flags &= ~MPTCPF_SND_64BITACK;
MPT_UNLOCK(mp_tp);
memcpy(opt + optlen, &dack_opt, len);
optlen += len;
VERIFY(optlen <= MAX_TCPOPTLEN);
tp->t_mpflags &= ~TMPF_MPTCP_ACKNOW;
return (optlen);
}
if ((tp->t_mpflags & TMPF_SEND_DSN) &&
(!send_64bit_dsn) &&
(!send_64bit_ack) &&
(tp->t_mpflags & TMPF_MPTCP_ACKNOW)) {
struct mptcp_dss_ack_opt dss_ack_opt;
unsigned int len = sizeof (dss_ack_opt);
if (do_csum)
len += 2;
CHECK_OPTLEN;
bzero(&dss_ack_opt, sizeof (dss_ack_opt));
dss_ack_opt.mdss_copt.mdss_kind = TCPOPT_MULTIPATH;
dss_ack_opt.mdss_copt.mdss_len = len;
dss_ack_opt.mdss_copt.mdss_subtype = MPO_DSS;
dss_ack_opt.mdss_copt.mdss_flags |= MDSS_A | MDSS_M;
MPT_LOCK_SPIN(mp_tp);
dss_ack_opt.mdss_ack =
htonl(MPTCP_DATAACK_LOW32(mp_tp->mpt_rcvnxt));
MPT_UNLOCK(mp_tp);
CHECK_DATALEN;
mptcp_output_getm_dsnmap32(so, off, (u_int32_t)datalen,
&dss_ack_opt.mdss_dsn,
&dss_ack_opt.mdss_subflow_seqn,
&dss_ack_opt.mdss_data_len,
dss_valp);
if ((dss_ack_opt.mdss_data_len == 0) ||
(dss_ack_opt.mdss_dsn == 0)) {
goto do_ack32_only;
}
if (tp->t_mpflags & TMPF_SEND_DFIN) {
DO_FIN(dss_ack_opt);
}
dss_ack_opt.mdss_dsn = htonl(dss_ack_opt.mdss_dsn);
dss_ack_opt.mdss_subflow_seqn =
htonl(dss_ack_opt.mdss_subflow_seqn);
dss_ack_opt.mdss_data_len = htons(dss_ack_opt.mdss_data_len);
*dss_lenp = (unsigned int *)(void *)(opt + optlen +
offsetof(struct mptcp_dss_ack_opt, mdss_data_len));
memcpy(opt + optlen, &dss_ack_opt, sizeof (dss_ack_opt));
if (do_csum) {
*sseqp = (u_int32_t *)(void *)(opt + optlen +
offsetof(struct mptcp_dss_ack_opt,
mdss_subflow_seqn));
}
optlen += len;
if (optlen > MAX_TCPOPTLEN)
panic("optlen too large");
tp->t_mpflags &= ~TMPF_MPTCP_ACKNOW;
return (optlen);
}
if ((tp->t_mpflags & TMPF_SEND_DSN) &&
(!send_64bit_dsn) &&
(send_64bit_ack) &&
(tp->t_mpflags & TMPF_MPTCP_ACKNOW)) {
struct mptcp_dss32_ack64_opt dss_ack_opt;
unsigned int len = sizeof (dss_ack_opt);
if (do_csum)
len += 2;
CHECK_OPTLEN;
bzero(&dss_ack_opt, sizeof (dss_ack_opt));
dss_ack_opt.mdss_copt.mdss_kind = TCPOPT_MULTIPATH;
dss_ack_opt.mdss_copt.mdss_len = len;
dss_ack_opt.mdss_copt.mdss_subtype = MPO_DSS;
dss_ack_opt.mdss_copt.mdss_flags |= MDSS_M | MDSS_A | MDSS_a;
MPT_LOCK_SPIN(mp_tp);
dss_ack_opt.mdss_ack =
mptcp_hton64(mp_tp->mpt_rcvnxt);
MPT_UNLOCK(mp_tp);
CHECK_DATALEN;
mptcp_output_getm_dsnmap32(so, off, (u_int32_t)datalen,
&dss_ack_opt.mdss_dsn, &dss_ack_opt.mdss_subflow_seqn,
&dss_ack_opt.mdss_data_len, dss_valp);
if ((dss_ack_opt.mdss_data_len == 0) ||
(dss_ack_opt.mdss_dsn == 0)) {
goto do_ack64_only;
}
if (tp->t_mpflags & TMPF_SEND_DFIN) {
DO_FIN(dss_ack_opt);
}
dss_ack_opt.mdss_dsn = htonl(dss_ack_opt.mdss_dsn);
dss_ack_opt.mdss_subflow_seqn =
htonl(dss_ack_opt.mdss_subflow_seqn);
dss_ack_opt.mdss_data_len = htons(dss_ack_opt.mdss_data_len);
*dss_lenp = (unsigned int *)(void *)(opt + optlen +
offsetof(struct mptcp_dss32_ack64_opt, mdss_data_len));
memcpy(opt + optlen, &dss_ack_opt, sizeof (dss_ack_opt));
if (do_csum) {
*sseqp = (u_int32_t *)(void *)(opt + optlen +
offsetof(struct mptcp_dss32_ack64_opt,
mdss_subflow_seqn));
}
optlen += len;
if (optlen > MAX_TCPOPTLEN)
panic("optlen too large");
tp->t_mpflags &= ~TMPF_MPTCP_ACKNOW;
return (optlen);
}
if (tp->t_mpflags & TMPF_SEND_DFIN) {
struct mptcp_dss_ack_opt dss_ack_opt;
unsigned int len = sizeof (struct mptcp_dss_ack_opt);
if (do_csum)
len += 2;
CHECK_OPTLEN;
bzero(&dss_ack_opt, sizeof (dss_ack_opt));
MPT_LOCK(mp_tp);
if ((mp_tp->mpt_sndnxt + 1) != mp_tp->mpt_sndmax) {
MPT_UNLOCK(mp_tp);
if (mptcp_dbg == MP_VERBOSE_DEBUG_2)
printf("%s: Fin state %d %llu %llu\n", __func__,
mp_tp->mpt_state, mp_tp->mpt_sndnxt,
mp_tp->mpt_sndmax);
return (optlen);
}
dss_ack_opt.mdss_copt.mdss_kind = TCPOPT_MULTIPATH;
dss_ack_opt.mdss_copt.mdss_len = len;
dss_ack_opt.mdss_copt.mdss_subtype = MPO_DSS;
dss_ack_opt.mdss_copt.mdss_flags |= MDSS_A | MDSS_M | MDSS_F;
dss_ack_opt.mdss_ack =
htonl(MPTCP_DATAACK_LOW32(mp_tp->mpt_rcvnxt));
dss_ack_opt.mdss_dsn =
htonl(MPTCP_DATASEQ_LOW32(mp_tp->mpt_sndnxt));
MPT_UNLOCK(mp_tp);
dss_ack_opt.mdss_subflow_seqn = 0;
dss_ack_opt.mdss_data_len = 1;
dss_ack_opt.mdss_data_len = htons(dss_ack_opt.mdss_data_len);
memcpy(opt + optlen, &dss_ack_opt, sizeof (dss_ack_opt));
if (do_csum) {
*dss_valp = mp_tp->mpt_sndnxt;
*sseqp = (u_int32_t *)(void *)(opt + optlen +
offsetof(struct mptcp_dss_ack_opt,
mdss_subflow_seqn));
}
optlen += len;
}
return (optlen);
}
static int
mptcp_valid_mpcapable_common_opt(u_char *cp, u_int32_t mptcp_version)
{
struct mptcp_mpcapable_opt_common *rsp =
(struct mptcp_mpcapable_opt_common *)cp;
if (rsp->mmco_version != mptcp_version)
return (0);
if (!(rsp->mmco_flags & MPCAP_PROPOSAL_SBIT))
return (0);
if (rsp->mmco_flags & (MPCAP_BBIT | MPCAP_CBIT | MPCAP_DBIT |
MPCAP_EBIT | MPCAP_FBIT | MPCAP_GBIT))
return (0);
return (1);
}
static void
mptcp_do_mpcapable_opt(struct tcpcb *tp, u_char *cp, struct tcphdr *th,
int optlen)
{
struct mptcp_mpcapable_opt_rsp1 *rsp1 = NULL;
struct mptcp_mpcapable_opt_rsp *rsp = NULL;
struct mptcb *mp_tp = tptomptp(tp);
#define MPTCP_OPT_ERROR_PATH(tp) { \
tp->t_mpflags |= TMPF_RESET; \
tcpstat.tcps_invalid_mpcap++; \
if (tp->t_inpcb->inp_socket != NULL) { \
soevent(tp->t_inpcb->inp_socket, \
SO_FILT_HINT_LOCKED | SO_FILT_HINT_MUSTRST); \
} \
}
if (mp_tp == NULL) {
if (mptcp_dbg == MP_ERR_DEBUG)
printf("MPTCP ERROR %s: NULL mpsocket \n", __func__);
tcpstat.tcps_invalid_mpcap++;
return;
}
if (mptcp_valid_mpcapable_common_opt(cp, mp_tp->mpt_version) != 1) {
tcpstat.tcps_invalid_mpcap++;
return;
}
if ((th->th_flags & (TH_SYN | TH_ACK)) == TH_SYN) {
return;
} else if ((th->th_flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)) {
if (optlen != sizeof (struct mptcp_mpcapable_opt_rsp)) {
if (mptcp_dbg == MP_ERR_DEBUG) {
printf("%s: SYN_ACK optlen = %d, sizeof mp opt \
= %lu \n", __func__, optlen,
sizeof (struct mptcp_mpcapable_opt_rsp));
}
tcpstat.tcps_invalid_mpcap++;
return;
}
if (((struct mptcp_mpcapable_opt_common *)cp)->mmco_flags &
MPCAP_CHECKSUM_CBIT)
mp_tp->mpt_flags |= MPTCPF_CHECKSUM;
rsp = (struct mptcp_mpcapable_opt_rsp *)cp;
MPT_LOCK_SPIN(mp_tp);
mp_tp->mpt_remotekey = rsp->mmc_localkey;
MPT_UNLOCK(mp_tp);
tp->t_mpflags |= TMPF_PREESTABLISHED;
if (mptcp_dbg > MP_VERBOSE_DEBUG_1) {
printf("SYN_ACK pre established, optlen = %d, tp \
state = %d sport = %x dport = %x key = %llx \n",
optlen, tp->t_state, th->th_sport, th->th_dport,
mp_tp->mpt_remotekey);
}
} else if ((th->th_flags & TH_ACK) &&
(tp->t_mpflags & TMPF_PREESTABLISHED)) {
if ((mp_tp->mpt_flags & MPTCPF_CHECKSUM) &&
!(((struct mptcp_mpcapable_opt_common *)cp)->mmco_flags &
MPCAP_CHECKSUM_CBIT)) {
if (mptcp_dbg == MP_ERR_DEBUG) {
printf("%s: checksum negotiation failure \n",
__func__);
}
MPTCP_OPT_ERROR_PATH(tp);
return;
}
if (!(mp_tp->mpt_flags & MPTCPF_CHECKSUM) &&
(((struct mptcp_mpcapable_opt_common *)cp)->mmco_flags &
MPCAP_CHECKSUM_CBIT)) {
if (mptcp_dbg == MP_ERR_DEBUG) {
printf("%s: checksum negotiation failure 2.\n",
__func__);
}
MPTCP_OPT_ERROR_PATH(tp);
return;
}
if (optlen != sizeof (struct mptcp_mpcapable_opt_rsp1)) {
if (mptcp_dbg == MP_ERR_DEBUG) {
printf("%s: ACK optlen = %d , sizeof mp option \
= %lu, state = %d \n",
__func__,
optlen,
sizeof (struct mptcp_mpcapable_opt_rsp1),
tp->t_state);
}
MPTCP_OPT_ERROR_PATH(tp);
return;
}
rsp1 = (struct mptcp_mpcapable_opt_rsp1 *)cp;
if (rsp1->mmc_remotekey != *mp_tp->mpt_localkey) {
if (mptcp_dbg == MP_ERR_DEBUG) {
printf("MPTCP ERROR %s: key mismatch locally "
"stored key. rsp = %llx local = %llx \n",
__func__, rsp1->mmc_remotekey,
*mp_tp->mpt_localkey);
}
tp->t_mpflags &= ~TMPF_PREESTABLISHED;
MPTCP_OPT_ERROR_PATH(tp);
return;
} else {
if (mp_tp->mpt_remotekey != rsp1->mmc_localkey) {
if (mptcp_dbg == MP_ERR_DEBUG) {
printf("MPTCP ERROR %s: keys don't"
" match\n", __func__);
}
tp->t_mpflags &= ~TMPF_PREESTABLISHED;
MPTCP_OPT_ERROR_PATH(tp);
return;
}
tp->t_mpflags &= ~TMPF_PREESTABLISHED;
tp->t_mpflags |= TMPF_MPTCP_RCVD_KEY;
tp->t_mpflags |= TMPF_MPTCP_TRUE;
tp->t_inpcb->inp_socket->so_flags |= SOF_MPTCP_TRUE;
MPT_LOCK(mp_tp);
DTRACE_MPTCP2(state__change, struct mptcb *, mp_tp,
uint32_t, 0 );
mp_tp->mpt_state = MPTCPS_ESTABLISHED;
MPT_UNLOCK(mp_tp);
if (mptcp_dbg >= MP_VERBOSE_DEBUG_2) {
printf("MPTCP SUCCESS %s: rem key = %llx local \
key = %llx \n",
__func__, mp_tp->mpt_remotekey,
*mp_tp->mpt_localkey);
}
}
if (tp->t_mpuna) {
tp->t_mpuna = 0;
}
}
}
static void
mptcp_do_mpjoin_opt(struct tcpcb *tp, u_char *cp, struct tcphdr *th, int optlen)
{
#define MPTCP_JOPT_ERROR_PATH(tp) { \
tp->t_mpflags |= TMPF_RESET; \
tcpstat.tcps_invalid_joins++; \
if (tp->t_inpcb->inp_socket != NULL) { \
soevent(tp->t_inpcb->inp_socket, \
SO_FILT_HINT_LOCKED | SO_FILT_HINT_MUSTRST); \
} \
}
int error = 0;
struct mptcb *mp_tp = tptomptp(tp);
if ((th->th_flags & (TH_SYN | TH_ACK)) == TH_SYN) {
if (tp->t_inpcb->inp_socket->so_flags & SOF_MPTCP_CLIENT) {
MPTCP_JOPT_ERROR_PATH(tp);
return;
}
if (optlen != sizeof (struct mptcp_mpjoin_opt_req)) {
if (mptcp_dbg == MP_ERR_DEBUG) {
printf("SYN: unexpected optlen = %d, mp option"
"= %lu\n",
optlen,
sizeof (struct mptcp_mpjoin_opt_req));
}
MPTCP_JOPT_ERROR_PATH(tp);
return;
}
return;
#ifdef MPTCP_NOTYET
struct mptcp_mpjoin_opt_req *join_req =
(struct mptcp_mpjoin_opt_req *)cp;
mp_so = mptcp_find_mpso(join_req->mmjo_peer_token);
if (!mp_so) {
if (mptcp_dbg >= MP_ERR_DEBUG)
printf("%s: cannot find mp_so token = %x\n",
__func__, join_req->mmjo_peer_token);
MPTCP_JOPT_ERROR_PATH(tp);
return;
}
if (tp->t_mpflags & TMPF_PREESTABLISHED) {
return;
}
mp_so->ms_remote_addr_id = join_req->mmjo_addr_id;
mp_so->ms_remote_rand = join_req->mmjo_rand;
tp->t_mpflags |= TMPF_PREESTABLISHED | TMPF_JOINED_FLOW;
tp->t_mpflags |= TMPF_RECVD_JOIN;
tp->t_inpcb->inp_socket->so_flags |= SOF_MP_SEC_SUBFLOW;
if (join_req->mmjo_subtype & MPTCP_BACKUP) {
tp->t_mpflags |= TMPF_BACKUP_PATH;
}
#endif
} else if ((th->th_flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)) {
struct mptcp_mpjoin_opt_rsp *join_rsp =
(struct mptcp_mpjoin_opt_rsp *)cp;
if (optlen != sizeof (struct mptcp_mpjoin_opt_rsp)) {
if (mptcp_dbg >= MP_ERR_DEBUG) {
printf("SYN_ACK: unexpected optlen = %d mp "
"option = %lu\n", optlen,
sizeof (struct mptcp_mpjoin_opt_rsp));
}
tp->t_mpflags &= ~TMPF_PREESTABLISHED;
MPTCP_JOPT_ERROR_PATH(tp);
return;
}
if (mp_tp == NULL) {
if (mptcp_dbg >= MP_ERR_DEBUG)
printf("%s: cannot find mp_tp in SYN_ACK\n",
__func__);
tp->t_mpflags &= ~TMPF_PREESTABLISHED;
MPTCP_JOPT_ERROR_PATH(tp);
return;
}
mptcp_set_raddr_rand(tp->t_local_aid,
tptomptp(tp),
join_rsp->mmjo_addr_id, join_rsp->mmjo_rand);
error = mptcp_validate_join_hmac(tp,
(u_char*)&join_rsp->mmjo_mac, SHA1_TRUNCATED);
if (error) {
if (mptcp_dbg >= MP_ERR_DEBUG) {
printf("%s: SYN_ACK error = %d \n", __func__,
error);
}
tp->t_mpflags &= ~TMPF_PREESTABLISHED;
MPTCP_JOPT_ERROR_PATH(tp);
return;
}
tp->t_mpflags |= TMPF_SENT_JOIN;
} else if ((th->th_flags & TH_ACK) &&
(tp->t_mpflags & TMPF_PREESTABLISHED)) {
struct mptcp_mpjoin_opt_rsp2 *join_rsp2 =
(struct mptcp_mpjoin_opt_rsp2 *)cp;
if (optlen != sizeof (struct mptcp_mpjoin_opt_rsp2)) {
if (mptcp_dbg >= MP_ERR_DEBUG) {
printf("ACK: unexpected optlen = %d mp option "
"= %lu \n", optlen,
sizeof (struct mptcp_mpjoin_opt_rsp2));
}
tp->t_mpflags &= ~TMPF_PREESTABLISHED;
MPTCP_JOPT_ERROR_PATH(tp);
return;
}
if (mp_tp == NULL) {
tp->t_mpflags &= ~TMPF_PREESTABLISHED;
MPTCP_JOPT_ERROR_PATH(tp);
return;
}
error = mptcp_validate_join_hmac(tp, join_rsp2->mmjo_mac,
SHA1_RESULTLEN);
if (error) {
if (mptcp_dbg >= MP_ERR_DEBUG) {
printf("%s: ACK error = %d\n", __func__,
error);
}
tp->t_mpflags &= ~TMPF_PREESTABLISHED;
MPTCP_JOPT_ERROR_PATH(tp);
return;
}
tp->t_mpflags |= TMPF_MPTCP_TRUE;
tp->t_mpflags &= ~TMPF_PREESTABLISHED;
tp->t_flags |= TF_ACKNOW;
tp->t_mpflags |= TMPF_MPTCP_ACKNOW;
tp->t_inpcb->inp_socket->so_flags |= SOF_MPTCP_TRUE;
if (mptcp_dbg >= MP_ERR_DEBUG) {
printf("MPTCP SUCCESS %s: join \n", __func__);
}
}
}
static int
mptcp_validate_join_hmac(struct tcpcb *tp, u_char* hmac, int mac_len)
{
u_char digest[SHA1_RESULTLEN] = {0};
struct mptcb *mp_tp = NULL;
mptcp_key_t rem_key, loc_key;
u_int32_t rem_rand, loc_rand;
mp_tp = tp->t_mptcb;
if (mp_tp == NULL)
return (-1);
rem_rand = loc_rand = 0;
MPT_LOCK(mp_tp);
rem_key = mp_tp->mpt_remotekey;
loc_key = *mp_tp->mpt_localkey;
MPT_UNLOCK(mp_tp);
mptcp_get_rands(tp->t_local_aid, mp_tp, &loc_rand, &rem_rand);
if ((rem_rand == 0) || (loc_rand == 0))
return (-1);
mptcp_hmac_sha1(rem_key, loc_key, rem_rand, loc_rand,
digest, sizeof (digest));
if (bcmp(digest, hmac, mac_len) == 0)
return (0);
else {
printf("%s: remote key %llx local key %llx remote rand %x "
"local rand %x \n", __func__, rem_key, loc_key,
rem_rand, loc_rand);
return (-1);
}
}
static void
mptcp_do_dss_opt_ack_meat(u_int64_t full_dack, struct tcpcb *tp)
{
struct mptcb *mp_tp = tptomptp(tp);
int close_notify = 0;
if (mp_tp == NULL)
return;
MPT_LOCK(mp_tp);
if (MPTCP_SEQ_LEQ(full_dack, mp_tp->mpt_sndmax) &&
MPTCP_SEQ_GEQ(full_dack, mp_tp->mpt_snduna)) {
mptcp_data_ack_rcvd(mp_tp, tp, full_dack);
if ((mp_tp->mpt_state == MPTCPS_CLOSED) ||
(mp_tp->mpt_state > MPTCPS_FIN_WAIT_2))
close_notify = 1;
MPT_UNLOCK(mp_tp);
mptcp_notify_mpready(tp->t_inpcb->inp_socket);
if (close_notify)
mptcp_notify_close(tp->t_inpcb->inp_socket);
if (mp_tp->mpt_flags & MPTCPF_RCVD_64BITACK) {
mp_tp->mpt_flags &= ~MPTCPF_RCVD_64BITACK;
mp_tp->mpt_flags &= ~MPTCPF_SND_64BITDSN;
}
} else {
MPT_UNLOCK(mp_tp);
if (mptcp_dbg == MP_VERBOSE_DEBUG_2) {
printf("%s: unexpected dack %llx snduna %llx "
"sndmax %llx\n", __func__, full_dack,
mp_tp->mpt_snduna, mp_tp->mpt_sndmax);
}
}
if (mptcp_dbg == MP_VERBOSE_DEBUG_2) {
printf("%s: full_dack = %llu \n", __func__, full_dack);
}
}
static void
mptcp_do_dss_opt_meat(u_char *cp, struct tcpcb *tp)
{
struct mptcp_dss_copt *dss_rsp = (struct mptcp_dss_copt *)cp;
u_int64_t full_dack = 0;
struct mptcb *mp_tp = tptomptp(tp);
int csum_len = 0;
#define MPTCP_DSS_OPT_SZ_CHK(len, expected_len) { \
if (len != expected_len) { \
if (mptcp_dbg >= MP_ERR_DEBUG) { \
printf("MPTCP ERROR %s: bad len = %d" \
"dss: %x \n", __func__, \
len, \
dss_rsp->mdss_flags); \
} \
return; \
} \
}
if (mp_tp == NULL)
return;
if (mp_tp->mpt_flags & MPTCPF_CHECKSUM)
csum_len = 2;
dss_rsp->mdss_flags &= (MDSS_A|MDSS_a|MDSS_M|MDSS_m);
switch (dss_rsp->mdss_flags) {
case (MDSS_M):
{
struct mptcp_dsn_opt *dss_rsp1;
dss_rsp1 = (struct mptcp_dsn_opt *)cp;
MPTCP_DSS_OPT_SZ_CHK(dss_rsp1->mdss_copt.mdss_len,
sizeof (struct mptcp_dsn_opt) + csum_len);
if (csum_len == 0)
mptcp_update_dss_rcv_state(dss_rsp1, tp, 0);
else
mptcp_update_dss_rcv_state(dss_rsp1, tp,
*(uint16_t *)(void *)(cp +
(dss_rsp1->mdss_copt.mdss_len - csum_len)));
break;
}
case (MDSS_A):
{
struct mptcp_data_ack_opt *dack_opt;
dack_opt = (struct mptcp_data_ack_opt *)cp;
MPTCP_DSS_OPT_SZ_CHK(dack_opt->mdss_copt.mdss_len,
sizeof (struct mptcp_data_ack_opt));
u_int32_t dack = dack_opt->mdss_ack;
NTOHL(dack);
MPT_LOCK_SPIN(mp_tp);
MPTCP_EXTEND_DSN(mp_tp->mpt_snduna, dack, full_dack);
MPT_UNLOCK(mp_tp);
mptcp_do_dss_opt_ack_meat(full_dack, tp);
break;
}
case (MDSS_M | MDSS_A):
{
struct mptcp_dss_ack_opt *dss_ack_rsp;
dss_ack_rsp = (struct mptcp_dss_ack_opt *)cp;
MPTCP_DSS_OPT_SZ_CHK(dss_ack_rsp->mdss_copt.mdss_len,
sizeof (struct mptcp_dss_ack_opt) + csum_len);
u_int32_t dack = dss_ack_rsp->mdss_ack;
NTOHL(dack);
MPT_LOCK_SPIN(mp_tp);
MPTCP_EXTEND_DSN(mp_tp->mpt_snduna, dack, full_dack);
MPT_UNLOCK(mp_tp);
mptcp_do_dss_opt_ack_meat(full_dack, tp);
if (csum_len == 0)
mptcp_update_rcv_state_f(dss_ack_rsp, tp, 0);
else
mptcp_update_rcv_state_f(dss_ack_rsp, tp,
*(uint16_t *)(void *)(cp +
(dss_ack_rsp->mdss_copt.mdss_len -
csum_len)));
break;
}
case (MDSS_M | MDSS_m):
{
struct mptcp_dsn64_opt *dsn64;
dsn64 = (struct mptcp_dsn64_opt *)cp;
u_int64_t full_dsn;
MPTCP_DSS_OPT_SZ_CHK(dsn64->mdss_copt.mdss_len,
sizeof (struct mptcp_dsn64_opt) + csum_len);
if (mptcp_dbg == MP_VERBOSE_DEBUG_4) {
printf("%s: 64-bit M present.\n", __func__);
}
MPT_LOCK_SPIN(mp_tp);
mp_tp->mpt_flags |= MPTCPF_SND_64BITACK;
MPT_UNLOCK(mp_tp);
full_dsn = mptcp_ntoh64(dsn64->mdss_dsn);
NTOHL(dsn64->mdss_subflow_seqn);
NTOHS(dsn64->mdss_data_len);
if (csum_len == 0)
mptcp_update_rcv_state_meat(mp_tp, tp, full_dsn,
dsn64->mdss_subflow_seqn,
dsn64->mdss_data_len,
0);
else
mptcp_update_rcv_state_meat(mp_tp, tp, full_dsn,
dsn64->mdss_subflow_seqn,
dsn64->mdss_data_len,
*(uint16_t *)(void *)(cp +
dsn64->mdss_copt.mdss_len - csum_len));
break;
}
case (MDSS_A | MDSS_a):
{
struct mptcp_data_ack64_opt *dack64;
dack64 = (struct mptcp_data_ack64_opt *)cp;
MPTCP_DSS_OPT_SZ_CHK(dack64->mdss_copt.mdss_len,
sizeof (struct mptcp_data_ack64_opt));
if (mptcp_dbg == MP_VERBOSE_DEBUG_4) {
printf("%s: 64-bit A present. \n", __func__);
}
MPT_LOCK_SPIN(mp_tp);
mp_tp->mpt_flags |= MPTCPF_RCVD_64BITACK;
MPT_UNLOCK(mp_tp);
full_dack = mptcp_ntoh64(dack64->mdss_ack);
mptcp_do_dss_opt_ack_meat(full_dack, tp);
break;
}
case (MDSS_M | MDSS_m | MDSS_A):
{
struct mptcp_dss64_ack32_opt *dss_ack_rsp;
dss_ack_rsp = (struct mptcp_dss64_ack32_opt *)cp;
MPTCP_DSS_OPT_SZ_CHK(dss_ack_rsp->mdss_copt.mdss_len,
sizeof (struct mptcp_dss64_ack32_opt) + csum_len);
if (mptcp_dbg == MP_VERBOSE_DEBUG_4) {
printf("%s: 64-bit M and 32-bit A present.\n",
__func__);
}
u_int32_t dack = dss_ack_rsp->mdss_ack;
NTOHL(dack);
MPT_LOCK_SPIN(mp_tp);
mp_tp->mpt_flags |= MPTCPF_SND_64BITACK;
MPTCP_EXTEND_DSN(mp_tp->mpt_snduna, dack, full_dack);
MPT_UNLOCK(mp_tp);
mptcp_do_dss_opt_ack_meat(full_dack, tp);
if (csum_len == 0)
mptcp_update_rcv_state_g(dss_ack_rsp, tp, 0);
else
mptcp_update_rcv_state_g(dss_ack_rsp, tp,
*(uint16_t *)(void *)(cp +
dss_ack_rsp->mdss_copt.mdss_len -
csum_len));
break;
}
case (MDSS_M | MDSS_A | MDSS_a):
{
struct mptcp_dss32_ack64_opt *dss32_ack64_opt;
dss32_ack64_opt = (struct mptcp_dss32_ack64_opt *)cp;
u_int64_t full_dsn;
MPTCP_DSS_OPT_SZ_CHK(
dss32_ack64_opt->mdss_copt.mdss_len,
sizeof (struct mptcp_dss32_ack64_opt) + csum_len);
if (mptcp_dbg == MP_VERBOSE_DEBUG_4) {
printf("%s: 32-bit M and 64-bit A present.\n",
__func__);
}
full_dack = mptcp_ntoh64(dss32_ack64_opt->mdss_ack);
mptcp_do_dss_opt_ack_meat(full_dack, tp);
NTOHL(dss32_ack64_opt->mdss_dsn);
MPT_LOCK_SPIN(mp_tp);
mp_tp->mpt_flags |= MPTCPF_RCVD_64BITACK;
MPTCP_EXTEND_DSN(mp_tp->mpt_rcvnxt,
dss32_ack64_opt->mdss_dsn, full_dsn);
MPT_UNLOCK(mp_tp);
NTOHL(dss32_ack64_opt->mdss_subflow_seqn);
NTOHS(dss32_ack64_opt->mdss_data_len);
if (csum_len == 0)
mptcp_update_rcv_state_meat(mp_tp, tp, full_dsn,
dss32_ack64_opt->mdss_subflow_seqn,
dss32_ack64_opt->mdss_data_len, 0);
else
mptcp_update_rcv_state_meat(mp_tp, tp, full_dsn,
dss32_ack64_opt->mdss_subflow_seqn,
dss32_ack64_opt->mdss_data_len,
*(uint16_t *)(void *)(cp +
dss32_ack64_opt->mdss_copt.mdss_len -
csum_len));
break;
}
case (MDSS_M | MDSS_m | MDSS_A | MDSS_a):
{
struct mptcp_dss64_ack64_opt *dss64_ack64;
dss64_ack64 = (struct mptcp_dss64_ack64_opt *)cp;
u_int64_t full_dsn;
MPTCP_DSS_OPT_SZ_CHK(dss64_ack64->mdss_copt.mdss_len,
sizeof (struct mptcp_dss64_ack64_opt) + csum_len);
if (mptcp_dbg == MP_VERBOSE_DEBUG_4) {
printf("%s: 64-bit M and 64-bit A present.\n",
__func__);
}
MPT_LOCK_SPIN(mp_tp);
mp_tp->mpt_flags |= MPTCPF_RCVD_64BITACK;
mp_tp->mpt_flags |= MPTCPF_SND_64BITACK;
MPT_UNLOCK(mp_tp);
full_dsn = mptcp_ntoh64(dss64_ack64->mdss_dsn);
full_dack = mptcp_ntoh64(dss64_ack64->mdss_dsn);
mptcp_do_dss_opt_ack_meat(full_dack, tp);
NTOHL(dss64_ack64->mdss_subflow_seqn);
NTOHS(dss64_ack64->mdss_data_len);
if (csum_len == 0)
mptcp_update_rcv_state_meat(mp_tp, tp, full_dsn,
dss64_ack64->mdss_subflow_seqn,
dss64_ack64->mdss_data_len, 0);
else
mptcp_update_rcv_state_meat(mp_tp, tp, full_dsn,
dss64_ack64->mdss_subflow_seqn,
dss64_ack64->mdss_data_len,
*(uint16_t *)(void *)(cp +
dss64_ack64->mdss_copt.mdss_len -
csum_len));
break;
}
default:
if (mptcp_dbg >= MP_ERR_DEBUG) {
printf("MPTCP ERROR %s: File bug, DSS flags = %x\n",
__func__, dss_rsp->mdss_flags);
}
break;
}
}
static void
mptcp_do_fin_opt(struct tcpcb *tp)
{
struct mptcb *mp_tp = (struct mptcb *)tp->t_mptcb;
if (!(tp->t_mpflags & TMPF_RECV_DFIN)) {
if (mp_tp != NULL) {
MPT_LOCK(mp_tp);
mp_tp->mpt_rcvnxt += 1;
mptcp_close_fsm(mp_tp, MPCE_RECV_DATA_FIN);
MPT_UNLOCK(mp_tp);
}
tp->t_mpflags |= TMPF_RECV_DFIN;
}
tp->t_mpflags |= TMPF_MPTCP_ACKNOW;
tp->t_flags |= TF_ACKNOW;
}
static void
mptcp_do_dss_opt(struct tcpcb *tp, u_char *cp, struct tcphdr *th, int optlen)
{
#pragma unused(th, optlen)
struct mptcb *mp_tp = (struct mptcb *)tp->t_mptcb;
if (!mp_tp)
return;
if (tp->t_mpflags & TMPF_MPTCP_TRUE) {
struct mptcp_dss_copt *dss_rsp = (struct mptcp_dss_copt *)cp;
if (dss_rsp->mdss_subtype == MPO_DSS) {
if (mptcp_dbg > MP_VERBOSE_DEBUG_4) {
printf("%s: DSS option received: %d ",
__func__, dss_rsp->mdss_flags);
}
if (dss_rsp->mdss_flags & MDSS_F) {
if (mptcp_dbg >= MP_VERBOSE_DEBUG_1)
printf("%s: received FIN\n", __func__);
mptcp_do_fin_opt(tp);
}
mptcp_do_dss_opt_meat(cp, tp);
}
}
}
static void
mptcp_do_fastclose_opt(struct tcpcb *tp, u_char *cp, struct tcphdr *th)
{
struct mptcb *mp_tp = NULL;
struct mptcp_fastclose_opt *fc_opt = (struct mptcp_fastclose_opt *)cp;
if (th->th_flags != TH_ACK)
return;
if (mptcp_dbg > MP_VERBOSE_DEBUG_2)
printf("%s: received \n", __func__);
if (fc_opt->mfast_len != sizeof (struct mptcp_fastclose_opt)) {
tcpstat.tcps_invalid_opt++;
return;
}
mp_tp = (struct mptcb *)tp->t_mptcb;
if (!mp_tp)
return;
if (fc_opt->mfast_key != mptcp_get_localkey(mp_tp)) {
tcpstat.tcps_invalid_opt++;
return;
}
if (th->th_seq != tp->rcv_nxt) {
tcpstat.tcps_invalid_opt++;
return;
}
MPT_LOCK(mp_tp);
if (mp_tp->mpt_state != MPTCPS_FASTCLOSE_WAIT) {
mp_tp->mpt_state = MPTCPS_FASTCLOSE_WAIT;
DTRACE_MPTCP2(state__change, struct mptcb *, mp_tp,
uint32_t, 0 );
mptcp_start_timer(mp_tp, MPTT_FASTCLOSE);
}
MPT_UNLOCK(mp_tp);
tp->t_mpflags |= TMPF_RESET;
if (tp->t_inpcb->inp_socket != NULL) {
soevent(tp->t_inpcb->inp_socket,
SO_FILT_HINT_LOCKED | SO_FILT_HINT_MUSTRST);
}
}
static void
mptcp_do_mpfail_opt(struct tcpcb *tp, u_char *cp, struct tcphdr *th)
{
struct mptcb *mp_tp = NULL;
struct mptcp_mpfail_opt *fail_opt = (struct mptcp_mpfail_opt *)cp;
if ((th->th_flags != TH_ACK) || (th->th_flags != TH_RST))
return;
if (fail_opt->mfail_len != sizeof (struct mptcp_mpfail_opt))
return;
mp_tp = (struct mptcb *)tp->t_mptcb;
if (mp_tp == NULL)
return;
MPT_LOCK(mp_tp);
mp_tp->mpt_flags |= MPTCPF_RECVD_MPFAIL;
mp_tp->mpt_dsn_at_csum_fail = mptcp_hton64(fail_opt->mfail_dsn);
MPT_UNLOCK(mp_tp);
mptcp_notify_mpfail(tp->t_inpcb->inp_socket);
}
int
tcp_do_mptcp_options(struct tcpcb *tp, u_char *cp, struct tcphdr *th,
struct tcpopt *to, int optlen)
{
int mptcp_subtype;
if (optlen < 4)
return (0);
mptcp_subtype = (cp[2] >> 4);
switch (mptcp_subtype) {
case MPO_CAPABLE:
mptcp_do_mpcapable_opt(tp, cp, th, optlen);
break;
case MPO_JOIN:
mptcp_do_mpjoin_opt(tp, cp, th, optlen);
break;
case MPO_DSS:
mptcp_do_dss_opt(tp, cp, th, optlen);
break;
case MPO_FASTCLOSE:
mptcp_do_fastclose_opt(tp, cp, th);
break;
case MPO_FAIL:
mptcp_do_mpfail_opt(tp, cp, th);
break;
case MPO_ADD_ADDR:
case MPO_REMOVE_ADDR:
case MPO_PRIO:
to->to_flags |= TOF_MPTCP;
break;
default:
printf("%s: type = %d\n", __func__, mptcp_subtype);
break;
}
return (0);
}
void
mptcp_send_addaddr_opt(struct tcpcb *tp, struct mptcp_addaddr_opt *opt)
{
opt->ma_kind = TCPOPT_MULTIPATH;
opt->ma_len = sizeof (struct mptcp_addaddr_opt);
opt->ma_subtype = MPO_ADD_ADDR;
opt->ma_addr_id = tp->t_local_aid;
#ifdef MPTCP_NOTYET
struct inpcb *inp = tp->t_inpcb;
if (inp->inp_vflag == AF_INET) {
opt->ma_ipver = MA_IPVer_V4;
bcopy((char *)&sin->sin_addr.s_addr, (char *)opt + opt->ma_len,
sizeof (in_addr_t));
opt->ma_len += sizeof (in_addr_t);
} else if (inp->inp_vflag == AF_INET6) {
opt->ma_ipver = MA_IPVer_V6;
bcopy((char *)&sin6->sin6_addr, (char *)opt + opt->ma_len,
sizeof (struct in6_addr));
opt->ma_len += sizeof (struct in6_addr);
}
#if 0
if (tp->t_mp_port) {
}
#endif
#endif
}
void
mptcp_send_remaddr_opt(struct tcpcb *tp, struct mptcp_remaddr_opt *opt)
{
if (mptcp_dbg >= MP_ERR_DEBUG)
printf("%s: local id %d remove id %d \n", __func__,
tp->t_local_aid, tp->t_rem_aid);
bzero(opt, sizeof (opt));
opt->mr_kind = TCPOPT_MULTIPATH;
opt->mr_len = sizeof (opt);
opt->mr_subtype = MPO_REMOVE_ADDR;
opt->mr_addr_id = tp->t_rem_aid;
tp->t_mpflags &= ~TMPF_SND_REM_ADDR;
}
#if 0
void
mptcp_do_mpprio_opt(struct tcpcb *tp, u_char *cp, struct tcphdr *th,
int optlen)
{
int bkp = 0;
struct mptcp_mpprio_opt *mpprio = (struct mptcp_mpprio_opt *)cp;
if ((tp == NULL) || !(tp->t_mpflags & TMPF_MPTCP_TRUE))
return;
if ((mpprio->mpprio_len != sizeof (struct mptcp_mpprio_addr_opt)) &&
(mpprio->mpprio_len != sizeof (struct mptcp_mpprio_opt)))
return;
}
#endif
static int
mptcp_snd_mpprio(struct tcpcb *tp, u_char *cp, int optlen)
{
struct mptcp_mpprio_addr_opt mpprio;
if (tp->t_state != TCPS_ESTABLISHED) {
tp->t_mpflags &= ~TMPF_SND_MPPRIO;
return (optlen);
}
if (mptcp_mpprio_enable != 1) {
tp->t_mpflags &= ~TMPF_SND_MPPRIO;
return (optlen);
}
if ((MAX_TCPOPTLEN - optlen) <
(int)sizeof (mpprio))
return (optlen);
bzero(&mpprio, sizeof (mpprio));
mpprio.mpprio_kind = TCPOPT_MULTIPATH;
mpprio.mpprio_len = sizeof (mpprio);
mpprio.mpprio_subtype = MPO_PRIO;
if (tp->t_mpflags & TMPF_BACKUP_PATH)
mpprio.mpprio_flags |= MPTCP_MPPRIO_BKP;
mpprio.mpprio_addrid = tp->t_local_aid;
memcpy(cp + optlen, &mpprio, sizeof (mpprio));
optlen += sizeof (mpprio);
tp->t_mpflags &= ~TMPF_SND_MPPRIO;
if (mptcp_dbg >= MP_ERR_DEBUG)
printf("%s: aid = %d \n", __func__, tp->t_local_aid);
return (optlen);
}