mptcp: Add ADD_ADDR handling
authorPeter Krystad <peter.krystad@linux.intel.com>
Fri, 27 Mar 2020 21:48:37 +0000 (14:48 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 30 Mar 2020 05:14:48 +0000 (22:14 -0700)
Add handling for sending and receiving the ADD_ADDR, ADD_ADDR6,
and RM_ADDR suboptions.

Co-developed-by: Matthieu Baerts <matthieu.baerts@tessares.net>
Signed-off-by: Matthieu Baerts <matthieu.baerts@tessares.net>
Co-developed-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Peter Krystad <peter.krystad@linux.intel.com>
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/tcp.h
include/net/mptcp.h
net/mptcp/crypto.c
net/mptcp/options.c
net/mptcp/protocol.h

index 3dc964010feff7ccd4b91f98e8871e436d880676..1225db308957a5f937c71d0a03a2a2ac9ff95aee 100644 (file)
@@ -86,9 +86,13 @@ struct mptcp_options_received {
        u64     data_seq;
        u32     subflow_seq;
        u16     data_len;
-       u     mp_capable : 1,
+       u16     mp_capable : 1,
                mp_join : 1,
-               dss : 1;
+               dss : 1,
+               add_addr : 1,
+               rm_addr : 1,
+               family : 4,
+               echo : 1;
        u8      use_map:1,
                dsn64:1,
                data_fin:1,
@@ -96,6 +100,16 @@ struct mptcp_options_received {
                ack64:1,
                mpc_map:1,
                __unused:2;
+       u8      addr_id;
+       u8      rm_id;
+       union {
+               struct in_addr  addr;
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+               struct in6_addr addr6;
+#endif
+       };
+       u64     ahmac;
+       u16     port;
 };
 #endif
 
@@ -131,6 +145,8 @@ static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
 #if IS_ENABLED(CONFIG_MPTCP)
        rx_opt->mptcp.mp_capable = 0;
        rx_opt->mptcp.mp_join = 0;
+       rx_opt->mptcp.add_addr = 0;
+       rx_opt->mptcp.rm_addr = 0;
        rx_opt->mptcp.dss = 0;
 #endif
 }
index c971d25431ea941238a1686955f6db85ca5b512a..0d5ea71dd3d0032d038acaf432c6c87cec003e48 100644 (file)
@@ -33,6 +33,15 @@ struct mptcp_out_options {
        u16 suboptions;
        u64 sndr_key;
        u64 rcvr_key;
+       union {
+               struct in_addr addr;
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+               struct in6_addr addr6;
+#endif
+       };
+       u8 addr_id;
+       u64 ahmac;
+       u8 rm_id;
        struct mptcp_ext ext_copy;
 #endif
 };
index 40d1bb18fd6062925630fb39086650ca494c26e9..c151628bd4161ac937de0bcd29d038123d03bc9c 100644 (file)
@@ -44,8 +44,7 @@ void mptcp_crypto_key_sha(u64 key, u32 *token, u64 *idsn)
                *idsn = be64_to_cpu(*((__be64 *)&mptcp_hashed_key[6]));
 }
 
-void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u32 nonce1, u32 nonce2,
-                          void *hmac)
+void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u8 *msg, int len, void *hmac)
 {
        u8 input[SHA256_BLOCK_SIZE + SHA256_DIGEST_SIZE];
        __be32 mptcp_hashed_key[SHA256_DIGEST_WORDS];
@@ -55,6 +54,9 @@ void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u32 nonce1, u32 nonce2,
        u8 key2be[8];
        int i;
 
+       if (WARN_ON_ONCE(len > SHA256_DIGEST_SIZE))
+               len = SHA256_DIGEST_SIZE;
+
        put_unaligned_be64(key1, key1be);
        put_unaligned_be64(key2, key2be);
 
@@ -65,11 +67,10 @@ void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u32 nonce1, u32 nonce2,
        for (i = 0; i < 8; i++)
                input[i + 8] ^= key2be[i];
 
-       put_unaligned_be32(nonce1, &input[SHA256_BLOCK_SIZE]);
-       put_unaligned_be32(nonce2, &input[SHA256_BLOCK_SIZE + 4]);
+       memcpy(&input[SHA256_BLOCK_SIZE], msg, len);
 
        sha256_init(&state);
-       sha256_update(&state, input, SHA256_BLOCK_SIZE + 8);
+       sha256_update(&state, input, SHA256_BLOCK_SIZE + len);
 
        /* emit sha256(K1 || msg) on the second input block, so we can
         * reuse 'input' for the last hashing
@@ -125,6 +126,7 @@ static int __init test_mptcp_crypto(void)
        char hmac[20], hmac_hex[41];
        u32 nonce1, nonce2;
        u64 key1, key2;
+       u8 msg[8];
        int i, j;
 
        for (i = 0; i < ARRAY_SIZE(tests); ++i) {
@@ -134,7 +136,10 @@ static int __init test_mptcp_crypto(void)
                nonce1 = be32_to_cpu(*((__be32 *)&tests[i].msg[0]));
                nonce2 = be32_to_cpu(*((__be32 *)&tests[i].msg[4]));
 
-               mptcp_crypto_hmac_sha(key1, key2, nonce1, nonce2, hmac);
+               put_unaligned_be32(nonce1, &msg[0]);
+               put_unaligned_be32(nonce2, &msg[4]);
+
+               mptcp_crypto_hmac_sha(key1, key2, msg, 8, hmac);
                for (j = 0; j < 20; ++j)
                        sprintf(&hmac_hex[j << 1], "%02x", hmac[j] & 0xff);
                hmac_hex[40] = 0;
index aea1a62d9999cae5be91f4a0cec4fa1c308027af..6c6c18a09a40a51176aa5cc243349b3adf6c7ca8 100644 (file)
@@ -178,6 +178,71 @@ void mptcp_parse_option(const struct sk_buff *skb, const unsigned char *ptr,
 
                break;
 
+       case MPTCPOPT_ADD_ADDR:
+               mp_opt->echo = (*ptr++) & MPTCP_ADDR_ECHO;
+               if (!mp_opt->echo) {
+                       if (opsize == TCPOLEN_MPTCP_ADD_ADDR ||
+                           opsize == TCPOLEN_MPTCP_ADD_ADDR_PORT)
+                               mp_opt->family = MPTCP_ADDR_IPVERSION_4;
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+                       else if (opsize == TCPOLEN_MPTCP_ADD_ADDR6 ||
+                                opsize == TCPOLEN_MPTCP_ADD_ADDR6_PORT)
+                               mp_opt->family = MPTCP_ADDR_IPVERSION_6;
+#endif
+                       else
+                               break;
+               } else {
+                       if (opsize == TCPOLEN_MPTCP_ADD_ADDR_BASE ||
+                           opsize == TCPOLEN_MPTCP_ADD_ADDR_BASE_PORT)
+                               mp_opt->family = MPTCP_ADDR_IPVERSION_4;
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+                       else if (opsize == TCPOLEN_MPTCP_ADD_ADDR6_BASE ||
+                                opsize == TCPOLEN_MPTCP_ADD_ADDR6_BASE_PORT)
+                               mp_opt->family = MPTCP_ADDR_IPVERSION_6;
+#endif
+                       else
+                               break;
+               }
+
+               mp_opt->add_addr = 1;
+               mp_opt->port = 0;
+               mp_opt->addr_id = *ptr++;
+               pr_debug("ADD_ADDR: id=%d", mp_opt->addr_id);
+               if (mp_opt->family == MPTCP_ADDR_IPVERSION_4) {
+                       memcpy((u8 *)&mp_opt->addr.s_addr, (u8 *)ptr, 4);
+                       ptr += 4;
+                       if (opsize == TCPOLEN_MPTCP_ADD_ADDR_PORT ||
+                           opsize == TCPOLEN_MPTCP_ADD_ADDR_BASE_PORT) {
+                               mp_opt->port = get_unaligned_be16(ptr);
+                               ptr += 2;
+                       }
+               }
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+               else {
+                       memcpy(mp_opt->addr6.s6_addr, (u8 *)ptr, 16);
+                       ptr += 16;
+                       if (opsize == TCPOLEN_MPTCP_ADD_ADDR6_PORT ||
+                           opsize == TCPOLEN_MPTCP_ADD_ADDR6_BASE_PORT) {
+                               mp_opt->port = get_unaligned_be16(ptr);
+                               ptr += 2;
+                       }
+               }
+#endif
+               if (!mp_opt->echo) {
+                       mp_opt->ahmac = get_unaligned_be64(ptr);
+                       ptr += 8;
+               }
+               break;
+
+       case MPTCPOPT_RM_ADDR:
+               if (opsize != TCPOLEN_MPTCP_RM_ADDR_BASE)
+                       break;
+
+               mp_opt->rm_addr = 1;
+               mp_opt->rm_id = *ptr++;
+               pr_debug("RM_ADDR: id=%d", mp_opt->rm_id);
+               break;
+
        default:
                break;
        }
@@ -386,6 +451,84 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb,
        return true;
 }
 
+static u64 add_addr_generate_hmac(u64 key1, u64 key2, u8 addr_id,
+                                 struct in_addr *addr)
+{
+       u8 hmac[MPTCP_ADDR_HMAC_LEN];
+       u8 msg[7];
+
+       msg[0] = addr_id;
+       memcpy(&msg[1], &addr->s_addr, 4);
+       msg[5] = 0;
+       msg[6] = 0;
+
+       mptcp_crypto_hmac_sha(key1, key2, msg, 7, hmac);
+
+       return get_unaligned_be64(hmac);
+}
+
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+static u64 add_addr6_generate_hmac(u64 key1, u64 key2, u8 addr_id,
+                                  struct in6_addr *addr)
+{
+       u8 hmac[MPTCP_ADDR_HMAC_LEN];
+       u8 msg[19];
+
+       msg[0] = addr_id;
+       memcpy(&msg[1], &addr->s6_addr, 16);
+       msg[17] = 0;
+       msg[18] = 0;
+
+       mptcp_crypto_hmac_sha(key1, key2, msg, 19, hmac);
+
+       return get_unaligned_be64(hmac);
+}
+#endif
+
+static bool mptcp_established_options_addr(struct sock *sk,
+                                          unsigned int *size,
+                                          unsigned int remaining,
+                                          struct mptcp_out_options *opts)
+{
+       struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
+       struct mptcp_sock *msk = mptcp_sk(subflow->conn);
+       struct sockaddr_storage saddr;
+       u8 id;
+
+       id = 0;
+       memset(&saddr, 0, sizeof(saddr));
+
+       if (saddr.ss_family == AF_INET) {
+               if (remaining < TCPOLEN_MPTCP_ADD_ADDR)
+                       return false;
+               opts->suboptions |= OPTION_MPTCP_ADD_ADDR;
+               opts->addr_id = id;
+               opts->addr = ((struct sockaddr_in *)&saddr)->sin_addr;
+               opts->ahmac = add_addr_generate_hmac(msk->local_key,
+                                                    msk->remote_key,
+                                                    opts->addr_id,
+                                                    &opts->addr);
+               *size = TCPOLEN_MPTCP_ADD_ADDR;
+       }
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+       else if (saddr.ss_family == AF_INET6) {
+               if (remaining < TCPOLEN_MPTCP_ADD_ADDR6)
+                       return false;
+               opts->suboptions |= OPTION_MPTCP_ADD_ADDR6;
+               opts->addr_id = id;
+               opts->ahmac = add_addr6_generate_hmac(msk->local_key,
+                                                     msk->remote_key,
+                                                     opts->addr_id,
+                                                     &opts->addr6);
+               opts->addr6 = ((struct sockaddr_in6 *)&saddr)->sin6_addr;
+               *size = TCPOLEN_MPTCP_ADD_ADDR6;
+       }
+#endif
+       pr_debug("addr_id=%d, ahmac=%llu", opts->addr_id, opts->ahmac);
+
+       return true;
+}
+
 bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
                               unsigned int *size, unsigned int remaining,
                               struct mptcp_out_options *opts)
@@ -393,6 +536,8 @@ bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
        unsigned int opt_size = 0;
        bool ret = false;
 
+       opts->suboptions = 0;
+
        if (mptcp_established_options_mp(sk, skb, &opt_size, remaining, opts))
                ret = true;
        else if (mptcp_established_options_dss(sk, skb, &opt_size, remaining,
@@ -407,6 +552,11 @@ bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
 
        *size += opt_size;
        remaining -= opt_size;
+       if (mptcp_established_options_addr(sk, &opt_size, remaining, opts)) {
+               *size += opt_size;
+               remaining -= opt_size;
+               ret = true;
+       }
 
        return ret;
 }
@@ -521,10 +671,9 @@ void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
                else
                        len = TCPOLEN_MPTCP_MPC_ACK;
 
-               *ptr++ = htonl((TCPOPT_MPTCP << 24) | (len << 16) |
-                              (MPTCPOPT_MP_CAPABLE << 12) |
-                              (MPTCP_SUPPORTED_VERSION << 8) |
-                              MPTCP_CAP_HMAC_SHA256);
+               *ptr++ = mptcp_option(MPTCPOPT_MP_CAPABLE, len,
+                                     MPTCP_SUPPORTED_VERSION,
+                                     MPTCP_CAP_HMAC_SHA256);
 
                if (!((OPTION_MPTCP_MPC_SYNACK | OPTION_MPTCP_MPC_ACK) &
                    opts->suboptions))
@@ -546,6 +695,50 @@ void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
        }
 
 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 (opts->ahmac) {
+                       put_unaligned_be64(opts->ahmac, ptr);
+                       ptr += 2;
+               }
+       }
+
+#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;
+               }
+       }
+#endif
+
+       if (OPTION_MPTCP_RM_ADDR & opts->suboptions) {
+               *ptr++ = mptcp_option(MPTCPOPT_RM_ADDR,
+                                     TCPOLEN_MPTCP_RM_ADDR_BASE,
+                                     0, opts->rm_id);
+       }
+
        if (opts->ext_copy.use_ack || opts->ext_copy.use_map) {
                struct mptcp_ext *mpext = &opts->ext_copy;
                u8 len = TCPOLEN_MPTCP_DSS_BASE;
@@ -567,10 +760,7 @@ mp_capable_done:
                                flags |= MPTCP_DSS_DATA_FIN;
                }
 
-               *ptr++ = htonl((TCPOPT_MPTCP << 24) |
-                              (len  << 16) |
-                              (MPTCPOPT_DSS << 12) |
-                              (flags));
+               *ptr++ = mptcp_option(MPTCPOPT_DSS, len, 0, flags);
 
                if (mpext->use_ack) {
                        put_unaligned_be64(mpext->data_ack, ptr);
index eb3f65264a40e9bf3a3bd20adecdd0723ccf2975..471e013d1c32b56a0f57071b9bf477c6a025b253 100644 (file)
@@ -17,6 +17,9 @@
 #define OPTION_MPTCP_MPC_SYN   BIT(0)
 #define OPTION_MPTCP_MPC_SYNACK        BIT(1)
 #define OPTION_MPTCP_MPC_ACK   BIT(2)
+#define OPTION_MPTCP_ADD_ADDR  BIT(6)
+#define OPTION_MPTCP_ADD_ADDR6 BIT(7)
+#define OPTION_MPTCP_RM_ADDR   BIT(8)
 
 /* MPTCP option subtypes */
 #define MPTCPOPT_MP_CAPABLE    0
 #define TCPOLEN_MPTCP_DSS_MAP32                10
 #define TCPOLEN_MPTCP_DSS_MAP64                14
 #define TCPOLEN_MPTCP_DSS_CHECKSUM     2
+#define TCPOLEN_MPTCP_ADD_ADDR         16
+#define TCPOLEN_MPTCP_ADD_ADDR_PORT    18
+#define TCPOLEN_MPTCP_ADD_ADDR_BASE    8
+#define TCPOLEN_MPTCP_ADD_ADDR_BASE_PORT       10
+#define TCPOLEN_MPTCP_ADD_ADDR6                28
+#define TCPOLEN_MPTCP_ADD_ADDR6_PORT   30
+#define TCPOLEN_MPTCP_ADD_ADDR6_BASE   20
+#define TCPOLEN_MPTCP_ADD_ADDR6_BASE_PORT      22
+#define TCPOLEN_MPTCP_PORT_LEN         2
+#define TCPOLEN_MPTCP_RM_ADDR_BASE     4
 
 /* MPTCP MP_CAPABLE flags */
 #define MPTCP_VERSION_MASK     (0x0F)
 #define MPTCP_DSS_HAS_ACK      BIT(0)
 #define MPTCP_DSS_FLAG_MASK    (0x1F)
 
+/* MPTCP ADD_ADDR flags */
+#define MPTCP_ADDR_ECHO                BIT(0)
+#define MPTCP_ADDR_HMAC_LEN    20
+#define MPTCP_ADDR_IPVERSION_4 4
+#define MPTCP_ADDR_IPVERSION_6 6
+
 /* MPTCP socket flags */
 #define MPTCP_DATA_READY       0
 #define MPTCP_SEND_SPACE       1
 
+static inline __be32 mptcp_option(u8 subopt, u8 len, u8 nib, u8 field)
+{
+       return htonl((TCPOPT_MPTCP << 24) | (len << 16) | (subopt << 12) |
+                    ((nib & 0xF) << 8) | field);
+}
+
 /* MPTCP connection sock */
 struct mptcp_sock {
        /* inet_connection_sock must be the first member */
@@ -219,8 +244,7 @@ static inline void mptcp_crypto_key_gen_sha(u64 *key, u32 *token, u64 *idsn)
        mptcp_crypto_key_sha(*key, token, idsn);
 }
 
-void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u32 nonce1, u32 nonce2,
-                          void *hash_out);
+void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u8 *msg, int len, void *hmac);
 
 static inline struct mptcp_ext *mptcp_get_ext(struct sk_buff *skb)
 {