mptcp: Handle MPTCP TCP options
authorPeter Krystad <peter.krystad@linux.intel.com>
Wed, 22 Jan 2020 00:56:16 +0000 (16:56 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 24 Jan 2020 12:44:07 +0000 (13:44 +0100)
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>
include/linux/tcp.h
include/net/mptcp.h
net/ipv4/tcp_input.c
net/ipv4/tcp_output.c
net/mptcp/Makefile
net/mptcp/options.c [new file with mode: 0644]
net/mptcp/protocol.h

index ca6f01531e64acd689db559e10239dd21ee9209a..52798ab003949f67e2943d68c03a81929d26e32b 100644 (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
index 98ba22379117e5f6404f805a87de983d96e3de09..3daec2ceb3ff0b154fa22d3ef7cfa93c32931654 100644 (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)
 {
index 3583655982160c7b1d5fbc2d1aefda027537c5cc..3458ee13e6f056d385858b8a2b8792ea0d915fe3 100644 (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,
index 786978cb2db78b744c7301c113328c5387c09ee9..0f0984f39f67f502e65e6e50afb5913b0932cf52 100644 (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,
index 659129d1fcbfa0409519a6304df5aec4977fea13..27a846263f08d464cf0ebf602a96c8c46c09a651 100644 (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
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
new file mode 100644 (file)
index 0000000..b7a31c0
--- /dev/null
@@ -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;
+               }
+       }
+}
index ee04a01bffd3b0eb2751964bf840a32aef495440..c59cf8b220b01e65c55d8db5c3e3fca7f9964a61 100644 (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 */