X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/bd504ef0e0b883cdd7917b73b3574eb9ce669905..39236c6e673c41db228275375ab7fdb0f837b292:/bsd/netinet/mptcp_opt.c diff --git a/bsd/netinet/mptcp_opt.c b/bsd/netinet/mptcp_opt.c new file mode 100644 index 000000000..a4ea96ef6 --- /dev/null +++ b/bsd/netinet/mptcp_opt.c @@ -0,0 +1,1887 @@ +/* + * Copyright (c) 2012-2013 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +static int mptcp_validate_join_hmac(struct tcpcb *, u_char*, int); +static int mptcp_snd_mpprio(struct tcpcb *tp, u_char *cp, int optlen); + +/* + * MPTCP Options Output Processing + */ + +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); + + /* + * Avoid retransmitting the MP_CAPABLE option. + */ + 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) { + /* an embryonic connection was closed from above */ + 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 { + /* Only the SYN flag is set */ + 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) { + /* an embryonic connection was closed */ + 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; + /* A secondary subflow is started off as backup */ + 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) { + /* do nothing */ + return (optlen); + } + + if (!(so->so_flags & SOF_MP_SEC_SUBFLOW)) { + new_optlen = mptcp_setup_first_subflow_syn_opts(so, flags, opt, + optlen); + } else { + /* + * To simulate SYN_ACK with no join opt, comment this line on + * OS X server side. This serves as a testing hook. + */ + 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 option space low give up */ + 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; + + /* try later */ + 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) { + /* The checksum field is set to 0 for infinite mapping */ + 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)); + } + + /* so->so_flags &= ~SOF_MPTCP_CLIENT; */ + 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); +} + + +/* Must be called from tcp_output to fill in the fast close option */ +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); + + /* Only ACK flag should be set */ + 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) { + /* do nothing */ + return (optlen); + } + + if (mp_tp == NULL) { + return (optlen); + } + + if (mp_tp->mpt_flags & MPTCPF_CHECKSUM) + do_csum = TRUE; + + /* tcp_output handles the SYN path separately */ + 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; + /* HMAC-SHA1 is the proposal */ + 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 { + /* its a retransmission of the MP_CAPABLE ACK */ + } + 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))) { + /* Do the ACK part */ + optlen = mptcp_setup_join_ack_opts(tp, opt, optlen); + if (!tp->t_mpuna) { + tp->t_mpuna = tp->snd_una; + } + /* Start a timer to retransmit the ACK */ + tp->t_timer[TCPT_JACK_RXMT] = + OFFSET_FROM_START(tp, tcp_jack_rxmt); + return (optlen); + } + + if (!(tp->t_mpflags & TMPF_MPTCP_TRUE)) + return (optlen); + + /* From here on, all options are sent only if MPTCP_TRUE */ + + 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 { \ + /* MPTCP socket does not support IP options */ \ + 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); \ + /* remove option length from payload len */ \ + datalen = tp->t_maxopd - optlen - len; \ + } \ +} + + if ((tp->t_mpflags & TMPF_SEND_DSN) && + (send_64bit_dsn)) { + /* + * If there was the need to send 64-bit Data ACK along + * with 64-bit DSN, then 26 or 28 bytes would be used. + * With timestamps and NOOP padding that will cause + * overflow. Hence, in the rare event that both 64-bit + * DSN and 64-bit ACK have to be sent, delay the send of + * 64-bit ACK until our 64-bit DSN is acked with a 64-bit ack. + * XXX If this delay causes issue, remove the 2-byte padding. + */ + 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); + } + + /* 32-bit Data ACK option */ + 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); + } + + /* 64-bit Data ACK option */ + 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); + /* + * The other end should retransmit 64-bit DSN until it + * receives a 64-bit ACK. + */ + 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); + } + + /* 32-bit DSS+Data ACK option */ + 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); + } + + /* 32-bit DSS + 64-bit DACK option */ + 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); + /* Data FIN occupies one sequence space */ + 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); +} + +/* + * MPTCP Options Input Processing + */ + + +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; + + /* mmco_kind, mmco_len and mmco_subtype are validated before */ + + /* In future, there can be more than one version supported */ + 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; + } + + /* Validate the kind, len, flags */ + if (mptcp_valid_mpcapable_common_opt(cp, mp_tp->mpt_version) != 1) { + tcpstat.tcps_invalid_mpcap++; + return; + } + + /* A SYN contains only the MP_CAPABLE option */ + if ((th->th_flags & (TH_SYN | TH_ACK)) == TH_SYN) { + /* XXX passive side not supported yet */ + return; + } else if ((th->th_flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)) { + + /* A SYN/ACK contains peer's key and flags */ + if (optlen != sizeof (struct mptcp_mpcapable_opt_rsp)) { + /* complain */ + 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 checksum flag is set, enable MPTCP checksum, even if + * it was not negotiated on the first SYN. + */ + 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)) { + + /* + * Verify checksum flag is set, if we initially negotiated + * checksum. + */ + 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; + } + + /* + * The ACK of a three way handshake contains peer's key and + * flags. + */ + if (optlen != sizeof (struct mptcp_mpcapable_opt_rsp1)) { + /* complain */ + 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; + /* Skipping MPT_LOCK for invariant key */ + 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 { + /* We received both keys. Almost an MPTCP connection */ + /* Skipping MPT_LOCK for invariant key */ + 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 /* event */); + 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) { + /* We won't accept join requests as an active opener */ + 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)); + } + /* send RST and close */ + MPTCP_JOPT_ERROR_PATH(tp); + return; + } + /* not supported yet */ + 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); + /* send RST */ + 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; + /* send RST and close */ + 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; + /* send RST and close */ + 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; + /* send RST and close */ + 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; + /* send RST and close */ + 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); /* matches */ + 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): + { + /* 32-bit DSS, No Data ACK */ + 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): + { + /* 32-bit Data ACK, no DSS */ + 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): + { + /* 32-bit Data ACK + 32-bit DSS */ + 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): + { + /* 64-bit DSS , No Data ACK */ + 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): + { + /* 64-bit Data ACK, no DSS */ + 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): + { + /* 64-bit DSS + 32-bit Data ACK */ + 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): + { + /* 32-bit DSS + 64-bit Data ACK */ + 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): + { + /* 64-bit DSS + 64-bit Data ACK */ + 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; + /* + * Since this is a data level FIN, TCP needs to be explicitly told + * to send back an ACK on which the Data ACK is piggybacked. + */ + 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; + } + + /* + * fastclose could make us more vulnerable to attacks, hence + * accept only those that are at the next expected sequence number. + */ + 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 /* event */); + mptcp_start_timer(mp_tp, MPTT_FASTCLOSE); + } + MPT_UNLOCK(mp_tp); + + /* Reset this flow */ + 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; + + /* All MPTCP options have atleast 4 bytes */ + 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: /* fall through */ + case MPO_REMOVE_ADDR: /* fall through */ + case MPO_PRIO: + to->to_flags |= TOF_MPTCP; + break; + default: + printf("%s: type = %d\n", __func__, mptcp_subtype); + break; + } + return (0); +} + +/* + * MPTCP ADD_ADDR and REMOVE_ADDR options + */ + +/* + * ADD_ADDR is only placeholder code - not sent on wire + * The ADD_ADDR option is not sent on wire because of security issues + * around connection hijacking. + */ +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) { + /* add ports XXX */ + } +#endif +#endif +} + +/* REMOVE_ADDR option is sent when a source address goes away */ +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; +} + +/* + * MPTCP MP_PRIO option + */ + +#if 0 +/* + * Current implementation drops incoming MP_PRIO option and this code is + * just a placeholder. The option is dropped because only the mobile client can + * decide which of the subflows is preferred (usually wifi is preferred + * over Cellular). + */ +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 + +/* We send MP_PRIO option based on the values set by the SIOCSCONNORDER ioctl */ +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); +}