mptcp: Handle MPTCP TCP options

Add hooks to parse and format the MP_CAPABLE option.

This option is handled according to MPTCP version 0 (RFC6824).
MPTCP version 1 MP_CAPABLE (RFC6824bis/RFC8684) will be added later in
coordination with related code changes.

Co-developed-by: Matthieu Baerts <matthieu.baerts@tessares.net>
Signed-off-by: Matthieu Baerts <matthieu.baerts@tessares.net>
Co-developed-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Florian Westphal <fw@strlen.de>
Co-developed-by: Davide Caratti <dcaratti@redhat.com>
Signed-off-by: Davide Caratti <dcaratti@redhat.com>
Signed-off-by: Peter Krystad <peter.krystad@linux.intel.com>
Signed-off-by: Christoph Paasch <cpaasch@apple.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Peter Krystad 2020-01-21 16:56:16 -08:00 committed by David S. Miller
parent f870fa0b57
commit eda7acddf8
7 changed files with 181 additions and 1 deletions

View File

@ -78,6 +78,16 @@ struct tcp_sack_block {
#define TCP_SACK_SEEN (1 << 0) /*1 = peer is SACK capable, */
#define TCP_DSACK_SEEN (1 << 2) /*1 = DSACK was received from peer*/
#if IS_ENABLED(CONFIG_MPTCP)
struct mptcp_options_received {
u64 sndr_key;
u64 rcvr_key;
u8 mp_capable : 1,
mp_join : 1,
dss : 1;
};
#endif
struct tcp_options_received {
/* PAWS/RTTM data */
int ts_recent_stamp;/* Time we stored ts_recent (for aging) */
@ -95,6 +105,9 @@ struct tcp_options_received {
u8 num_sacks; /* Number of SACK blocks */
u16 user_mss; /* mss requested by user in ioctl */
u16 mss_clamp; /* Maximal mss, negotiated at connection setup */
#if IS_ENABLED(CONFIG_MPTCP)
struct mptcp_options_received mptcp;
#endif
};
static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
@ -104,6 +117,11 @@ static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
#if IS_ENABLED(CONFIG_SMC)
rx_opt->smc_ok = 0;
#endif
#if IS_ENABLED(CONFIG_MPTCP)
rx_opt->mptcp.mp_capable = 0;
rx_opt->mptcp.mp_join = 0;
rx_opt->mptcp.dss = 0;
#endif
}
/* This is the max number of SACKS that we'll generate and process. It's safe

View File

@ -9,6 +9,7 @@
#define __NET_MPTCP_H
#include <linux/skbuff.h>
#include <linux/tcp.h>
#include <linux/types.h>
/* MPTCP sk_buff extension data */
@ -26,10 +27,22 @@ struct mptcp_ext {
/* one byte hole */
};
struct mptcp_out_options {
#if IS_ENABLED(CONFIG_MPTCP)
u16 suboptions;
u64 sndr_key;
u64 rcvr_key;
#endif
};
#ifdef CONFIG_MPTCP
void mptcp_init(void);
void mptcp_parse_option(const unsigned char *ptr, int opsize,
struct tcp_options_received *opt_rx);
void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts);
/* move the skb extension owership, with the assumption that 'to' is
* newly allocated
*/
@ -76,6 +89,11 @@ static inline void mptcp_init(void)
{
}
static inline void mptcp_parse_option(const unsigned char *ptr, int opsize,
struct tcp_options_received *opt_rx)
{
}
static inline void mptcp_skb_ext_move(struct sk_buff *to,
const struct sk_buff *from)
{

View File

@ -79,6 +79,7 @@
#include <trace/events/tcp.h>
#include <linux/jump_label_ratelimit.h>
#include <net/busy_poll.h>
#include <net/mptcp.h>
int sysctl_tcp_max_orphans __read_mostly = NR_FILE;
@ -3924,6 +3925,10 @@ void tcp_parse_options(const struct net *net,
*/
break;
#endif
case TCPOPT_MPTCP:
mptcp_parse_option(ptr, opsize, opt_rx);
break;
case TCPOPT_FASTOPEN:
tcp_parse_fastopen_option(
opsize - TCPOLEN_FASTOPEN_BASE,

View File

@ -38,6 +38,7 @@
#define pr_fmt(fmt) "TCP: " fmt
#include <net/tcp.h>
#include <net/mptcp.h>
#include <linux/compiler.h>
#include <linux/gfp.h>
@ -414,6 +415,7 @@ static inline bool tcp_urg_mode(const struct tcp_sock *tp)
#define OPTION_WSCALE (1 << 3)
#define OPTION_FAST_OPEN_COOKIE (1 << 8)
#define OPTION_SMC (1 << 9)
#define OPTION_MPTCP (1 << 10)
static void smc_options_write(__be32 *ptr, u16 *options)
{
@ -439,8 +441,17 @@ struct tcp_out_options {
__u8 *hash_location; /* temporary pointer, overloaded */
__u32 tsval, tsecr; /* need to include OPTION_TS */
struct tcp_fastopen_cookie *fastopen_cookie; /* Fast open cookie */
struct mptcp_out_options mptcp;
};
static void mptcp_options_write(__be32 *ptr, struct tcp_out_options *opts)
{
#if IS_ENABLED(CONFIG_MPTCP)
if (unlikely(OPTION_MPTCP & opts->options))
mptcp_write_options(ptr, &opts->mptcp);
#endif
}
/* Write previously computed TCP options to the packet.
*
* Beware: Something in the Internet is very sensitive to the ordering of
@ -549,6 +560,8 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp,
}
smc_options_write(ptr, &options);
mptcp_options_write(ptr, opts);
}
static void smc_set_option(const struct tcp_sock *tp,

View File

@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MPTCP) += mptcp.o
mptcp-y := protocol.o
mptcp-y := protocol.o options.o

97
net/mptcp/options.c Normal file
View File

@ -0,0 +1,97 @@
// SPDX-License-Identifier: GPL-2.0
/* Multipath TCP
*
* Copyright (c) 2017 - 2019, Intel Corporation.
*/
#include <linux/kernel.h>
#include <net/tcp.h>
#include <net/mptcp.h>
#include "protocol.h"
void mptcp_parse_option(const unsigned char *ptr, int opsize,
struct tcp_options_received *opt_rx)
{
struct mptcp_options_received *mp_opt = &opt_rx->mptcp;
u8 subtype = *ptr >> 4;
u8 version;
u8 flags;
switch (subtype) {
case MPTCPOPT_MP_CAPABLE:
if (opsize != TCPOLEN_MPTCP_MPC_SYN &&
opsize != TCPOLEN_MPTCP_MPC_ACK)
break;
version = *ptr++ & MPTCP_VERSION_MASK;
if (version != MPTCP_SUPPORTED_VERSION)
break;
flags = *ptr++;
if (!((flags & MPTCP_CAP_FLAG_MASK) == MPTCP_CAP_HMAC_SHA1) ||
(flags & MPTCP_CAP_EXTENSIBILITY))
break;
/* RFC 6824, Section 3.1:
* "For the Checksum Required bit (labeled "A"), if either
* host requires the use of checksums, checksums MUST be used.
* In other words, the only way for checksums not to be used
* is if both hosts in their SYNs set A=0."
*
* Section 3.3.0:
* "If a checksum is not present when its use has been
* negotiated, the receiver MUST close the subflow with a RST as
* it is considered broken."
*
* We don't implement DSS checksum - fall back to TCP.
*/
if (flags & MPTCP_CAP_CHECKSUM_REQD)
break;
mp_opt->mp_capable = 1;
mp_opt->sndr_key = get_unaligned_be64(ptr);
ptr += 8;
if (opsize == TCPOLEN_MPTCP_MPC_ACK) {
mp_opt->rcvr_key = get_unaligned_be64(ptr);
ptr += 8;
pr_debug("MP_CAPABLE sndr=%llu, rcvr=%llu",
mp_opt->sndr_key, mp_opt->rcvr_key);
} else {
pr_debug("MP_CAPABLE sndr=%llu", mp_opt->sndr_key);
}
break;
case MPTCPOPT_DSS:
pr_debug("DSS");
mp_opt->dss = 1;
break;
default:
break;
}
}
void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
{
if ((OPTION_MPTCP_MPC_SYN |
OPTION_MPTCP_MPC_ACK) & opts->suboptions) {
u8 len;
if (OPTION_MPTCP_MPC_SYN & opts->suboptions)
len = TCPOLEN_MPTCP_MPC_SYN;
else
len = TCPOLEN_MPTCP_MPC_ACK;
*ptr++ = htonl((TCPOPT_MPTCP << 24) | (len << 16) |
(MPTCPOPT_MP_CAPABLE << 12) |
(MPTCP_SUPPORTED_VERSION << 8) |
MPTCP_CAP_HMAC_SHA1);
put_unaligned_be64(opts->sndr_key, ptr);
ptr += 2;
if (OPTION_MPTCP_MPC_ACK & opts->suboptions) {
put_unaligned_be64(opts->rcvr_key, ptr);
ptr += 2;
}
}
}

View File

@ -7,6 +7,35 @@
#ifndef __MPTCP_PROTOCOL_H
#define __MPTCP_PROTOCOL_H
#define MPTCP_SUPPORTED_VERSION 0
/* MPTCP option bits */
#define OPTION_MPTCP_MPC_SYN BIT(0)
#define OPTION_MPTCP_MPC_SYNACK BIT(1)
#define OPTION_MPTCP_MPC_ACK BIT(2)
/* MPTCP option subtypes */
#define MPTCPOPT_MP_CAPABLE 0
#define MPTCPOPT_MP_JOIN 1
#define MPTCPOPT_DSS 2
#define MPTCPOPT_ADD_ADDR 3
#define MPTCPOPT_RM_ADDR 4
#define MPTCPOPT_MP_PRIO 5
#define MPTCPOPT_MP_FAIL 6
#define MPTCPOPT_MP_FASTCLOSE 7
/* MPTCP suboption lengths */
#define TCPOLEN_MPTCP_MPC_SYN 12
#define TCPOLEN_MPTCP_MPC_SYNACK 12
#define TCPOLEN_MPTCP_MPC_ACK 20
/* MPTCP MP_CAPABLE flags */
#define MPTCP_VERSION_MASK (0x0F)
#define MPTCP_CAP_CHECKSUM_REQD BIT(7)
#define MPTCP_CAP_EXTENSIBILITY BIT(6)
#define MPTCP_CAP_HMAC_SHA1 BIT(0)
#define MPTCP_CAP_FLAG_MASK (0x3F)
/* MPTCP connection sock */
struct mptcp_sock {
/* inet_connection_sock must be the first member */