aboutsummaryrefslogtreecommitdiff
path: root/net/mptcp/options.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mptcp/options.c')
-rw-r--r--net/mptcp/options.c157
1 files changed, 96 insertions, 61 deletions
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index 8a59b3e44599..1ca60d9da3ef 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -242,9 +242,6 @@ static void mptcp_parse_option(const struct sk_buff *skb,
mp_opt->add_addr = 1;
mp_opt->addr_id = *ptr++;
- pr_debug("ADD_ADDR%s: id=%d, echo=%d",
- (mp_opt->family == MPTCP_ADDR_IPVERSION_6) ? "6" : "",
- mp_opt->addr_id, mp_opt->echo);
if (mp_opt->family == MPTCP_ADDR_IPVERSION_4) {
memcpy((u8 *)&mp_opt->addr.s_addr, (u8 *)ptr, 4);
ptr += 4;
@@ -269,6 +266,9 @@ static void mptcp_parse_option(const struct sk_buff *skb,
mp_opt->ahmac = get_unaligned_be64(ptr);
ptr += 8;
}
+ pr_debug("ADD_ADDR%s: id=%d, ahmac=%llu, echo=%d, port=%d",
+ (mp_opt->family == MPTCP_ADDR_IPVERSION_6) ? "6" : "",
+ mp_opt->addr_id, mp_opt->ahmac, mp_opt->echo, mp_opt->port);
break;
case MPTCPOPT_RM_ADDR:
@@ -587,9 +587,11 @@ static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff *
unsigned int opt_size = *size;
struct mptcp_addr_info saddr;
bool echo;
+ bool port;
int len;
- if (mptcp_pm_should_add_signal_ipv6(msk) &&
+ if ((mptcp_pm_should_add_signal_ipv6(msk) ||
+ mptcp_pm_should_add_signal_port(msk)) &&
skb && skb_is_tcp_pure_ack(skb)) {
pr_debug("drop other suboptions");
opts->suboptions = 0;
@@ -598,10 +600,10 @@ static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff *
}
if (!mptcp_pm_should_add_signal(msk) ||
- !(mptcp_pm_add_addr_signal(msk, remaining, &saddr, &echo)))
+ !(mptcp_pm_add_addr_signal(msk, remaining, &saddr, &echo, &port)))
return false;
- len = mptcp_add_addr_len(saddr.family, echo);
+ len = mptcp_add_addr_len(saddr.family, echo, port);
if (remaining < len)
return false;
@@ -609,6 +611,8 @@ static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff *
if (drop_other_suboptions)
*size -= opt_size;
opts->addr_id = saddr.id;
+ if (port)
+ opts->port = ntohs(saddr.port);
if (saddr.family == AF_INET) {
opts->suboptions |= OPTION_MPTCP_ADD_ADDR;
opts->addr = saddr.addr;
@@ -631,7 +635,8 @@ static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff *
}
}
#endif
- pr_debug("addr_id=%d, ahmac=%llu, echo=%d", opts->addr_id, opts->ahmac, echo);
+ pr_debug("addr_id=%d, ahmac=%llu, echo=%d, port=%d",
+ opts->addr_id, opts->ahmac, echo, opts->port);
return true;
}
@@ -797,7 +802,12 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk,
mptcp_subflow_fully_established(subflow, mp_opt);
fully_established:
- if (likely(subflow->pm_notified))
+ /* if the subflow is not already linked into the conn_list, we can't
+ * notify the PM: this subflow is still on the listener queue
+ * and the PM possibly acquiring the subflow lock could race with
+ * the listener close
+ */
+ if (likely(subflow->pm_notified) || list_empty(&subflow->node))
return true;
subflow->pm_notified = 1;
@@ -830,18 +840,20 @@ static u64 expand_ack(u64 old_ack, u64 cur_ack, bool use_64bit)
}
static void ack_update_msk(struct mptcp_sock *msk,
- const struct sock *ssk,
+ struct sock *ssk,
struct mptcp_options_received *mp_opt)
{
- u64 new_snd_una, snd_una, old_snd_una = atomic64_read(&msk->snd_una);
- u64 new_wnd_end, wnd_end, old_wnd_end = atomic64_read(&msk->wnd_end);
- u64 snd_nxt = READ_ONCE(msk->snd_nxt);
+ u64 new_wnd_end, new_snd_una, snd_nxt = READ_ONCE(msk->snd_nxt);
struct sock *sk = (struct sock *)msk;
+ u64 old_snd_una;
+
+ mptcp_data_lock(sk);
/* avoid ack expansion on update conflict, to reduce the risk of
* wrongly expanding to a future ack sequence number, which is way
* more dangerous than missing an ack
*/
+ old_snd_una = msk->snd_una;
new_snd_una = expand_ack(old_snd_una, mp_opt->data_ack, mp_opt->ack64);
/* ACK for data not even sent yet? Ignore. */
@@ -850,26 +862,16 @@ static void ack_update_msk(struct mptcp_sock *msk,
new_wnd_end = new_snd_una + tcp_sk(ssk)->snd_wnd;
- while (after64(new_wnd_end, old_wnd_end)) {
- wnd_end = old_wnd_end;
- old_wnd_end = atomic64_cmpxchg(&msk->wnd_end, wnd_end,
- new_wnd_end);
- if (old_wnd_end == wnd_end) {
- if (mptcp_send_head(sk))
- mptcp_schedule_work(sk);
- break;
- }
+ if (after64(new_wnd_end, msk->wnd_end)) {
+ msk->wnd_end = new_wnd_end;
+ __mptcp_wnd_updated(sk, ssk);
}
- while (after64(new_snd_una, old_snd_una)) {
- snd_una = old_snd_una;
- old_snd_una = atomic64_cmpxchg(&msk->snd_una, snd_una,
- new_snd_una);
- if (old_snd_una == snd_una) {
- mptcp_data_acked(sk);
- break;
- }
+ if (after64(new_snd_una, old_snd_una)) {
+ msk->snd_una = new_snd_una;
+ __mptcp_data_acked(sk);
}
+ mptcp_data_unlock(sk);
}
bool mptcp_update_rcv_data_fin(struct mptcp_sock *msk, u64 data_fin_seq, bool use_64bit)
@@ -922,8 +924,19 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)
struct mptcp_options_received mp_opt;
struct mptcp_ext *mpext;
- if (__mptcp_check_fallback(msk))
+ if (__mptcp_check_fallback(msk)) {
+ /* Keep it simple and unconditionally trigger send data cleanup and
+ * pending queue spooling. We will need to acquire the data lock
+ * for more accurate checks, and once the lock is acquired, such
+ * helpers are cheap.
+ */
+ mptcp_data_lock(subflow->conn);
+ if (mptcp_send_head(subflow->conn))
+ __mptcp_wnd_updated(subflow->conn, sk);
+ __mptcp_data_acked(subflow->conn);
+ mptcp_data_unlock(subflow->conn);
return;
+ }
mptcp_get_options(skb, &mp_opt);
if (!check_fully_established(msk, sk, subflow, skb, &mp_opt))
@@ -1067,44 +1080,66 @@ void mptcp_write_options(__be32 *ptr, const struct tcp_sock *tp,
}
mp_capable_done:
- if (OPTION_MPTCP_ADD_ADDR & opts->suboptions) {
- if (opts->ahmac)
- *ptr++ = mptcp_option(MPTCPOPT_ADD_ADDR,
- TCPOLEN_MPTCP_ADD_ADDR, 0,
- opts->addr_id);
- else
- *ptr++ = mptcp_option(MPTCPOPT_ADD_ADDR,
- TCPOLEN_MPTCP_ADD_ADDR_BASE,
- MPTCP_ADDR_ECHO,
- opts->addr_id);
- memcpy((u8 *)ptr, (u8 *)&opts->addr.s_addr, 4);
- ptr += 1;
+ if ((OPTION_MPTCP_ADD_ADDR
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+ | OPTION_MPTCP_ADD_ADDR6
+#endif
+ ) & opts->suboptions) {
+ u8 len = TCPOLEN_MPTCP_ADD_ADDR_BASE;
+ u8 echo = MPTCP_ADDR_ECHO;
+
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+ if (OPTION_MPTCP_ADD_ADDR6 & opts->suboptions)
+ len = TCPOLEN_MPTCP_ADD_ADDR6_BASE;
+#endif
+
+ if (opts->port)
+ len += TCPOLEN_MPTCP_PORT_LEN;
+
if (opts->ahmac) {
- put_unaligned_be64(opts->ahmac, ptr);
- ptr += 2;
+ len += sizeof(opts->ahmac);
+ echo = 0;
}
- }
+ *ptr++ = mptcp_option(MPTCPOPT_ADD_ADDR,
+ len, echo, opts->addr_id);
+ if (OPTION_MPTCP_ADD_ADDR & opts->suboptions) {
+ memcpy((u8 *)ptr, (u8 *)&opts->addr.s_addr, 4);
+ ptr += 1;
+ }
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
- if (OPTION_MPTCP_ADD_ADDR6 & opts->suboptions) {
- if (opts->ahmac)
- *ptr++ = mptcp_option(MPTCPOPT_ADD_ADDR,
- TCPOLEN_MPTCP_ADD_ADDR6, 0,
- opts->addr_id);
- else
- *ptr++ = mptcp_option(MPTCPOPT_ADD_ADDR,
- TCPOLEN_MPTCP_ADD_ADDR6_BASE,
- MPTCP_ADDR_ECHO,
- opts->addr_id);
- memcpy((u8 *)ptr, opts->addr6.s6_addr, 16);
- ptr += 4;
- if (opts->ahmac) {
- put_unaligned_be64(opts->ahmac, ptr);
- ptr += 2;
+ else if (OPTION_MPTCP_ADD_ADDR6 & opts->suboptions) {
+ memcpy((u8 *)ptr, opts->addr6.s6_addr, 16);
+ ptr += 4;
}
- }
#endif
+ if (!opts->port) {
+ if (opts->ahmac) {
+ put_unaligned_be64(opts->ahmac, ptr);
+ ptr += 2;
+ }
+ } else {
+ if (opts->ahmac) {
+ u8 *bptr = (u8 *)ptr;
+
+ put_unaligned_be16(opts->port, bptr);
+ bptr += 2;
+ put_unaligned_be64(opts->ahmac, bptr);
+ bptr += 8;
+ put_unaligned_be16(TCPOPT_NOP << 8 |
+ TCPOPT_NOP, bptr);
+
+ ptr += 3;
+ } else {
+ put_unaligned_be32(opts->port << 16 |
+ TCPOPT_NOP << 8 |
+ TCPOPT_NOP, ptr);
+ ptr += 1;
+ }
+ }
+ }
+
if (OPTION_MPTCP_RM_ADDR & opts->suboptions) {
*ptr++ = mptcp_option(MPTCPOPT_RM_ADDR,
TCPOLEN_MPTCP_RM_ADDR_BASE,