mptcp: Add path manager interface
authorPeter Krystad <peter.krystad@linux.intel.com>
Fri, 27 Mar 2020 21:48:38 +0000 (14:48 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 30 Mar 2020 05:14:48 +0000 (22:14 -0700)
Add enough of a path manager interface to allow sending of ADD_ADDR
when an incoming MPTCP connection is created. Capable of sending only
a single IPv4 ADD_ADDR option. The 'pm_data' element of the connection
sock will need to be expanded to handle multiple interfaces and IPv6.
Partial processing of the incoming ADD_ADDR is included so the path
manager notification of that event happens at the proper time, which
involves validating the incoming address information.

This is a skeleton interface definition for events generated by
MPTCP.

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: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Co-developed-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: Peter Krystad <peter.krystad@linux.intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/mptcp/Makefile
net/mptcp/options.c
net/mptcp/pm.c [new file with mode: 0644]
net/mptcp/protocol.c
net/mptcp/protocol.h
net/mptcp/subflow.c

index 4e98d9e..2848d72 100644 (file)
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_MPTCP) += mptcp.o
 
-mptcp-y := protocol.o subflow.o options.o token.o crypto.o ctrl.o
+mptcp-y := protocol.o subflow.o options.o token.o crypto.o ctrl.o pm.o
index 6c6c18a..a366131 100644 (file)
@@ -492,36 +492,35 @@ static bool mptcp_established_options_addr(struct sock *sk,
 {
        struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
        struct mptcp_sock *msk = mptcp_sk(subflow->conn);
-       struct sockaddr_storage saddr;
-       u8 id;
+       struct mptcp_addr_info saddr;
+       int len;
 
-       id = 0;
-       memset(&saddr, 0, sizeof(saddr));
+       if (!mptcp_pm_should_signal(msk) ||
+           !(mptcp_pm_addr_signal(msk, remaining, &saddr)))
+               return false;
+
+       len = mptcp_add_addr_len(saddr.family);
+       if (remaining < len)
+               return false;
 
-       if (saddr.ss_family == AF_INET) {
-               if (remaining < TCPOLEN_MPTCP_ADD_ADDR)
-                       return false;
+       *size = len;
+       opts->addr_id = saddr.id;
+       if (saddr.family == AF_INET) {
                opts->suboptions |= OPTION_MPTCP_ADD_ADDR;
-               opts->addr_id = id;
-               opts->addr = ((struct sockaddr_in *)&saddr)->sin_addr;
+               opts->addr = saddr.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;
+       else if (saddr.family == AF_INET6) {
                opts->suboptions |= OPTION_MPTCP_ADD_ADDR6;
-               opts->addr_id = id;
+               opts->addr6 = saddr.addr6;
                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);
@@ -607,10 +606,37 @@ static bool check_fully_established(struct mptcp_subflow_context *subflow,
        return true;
 }
 
+static bool add_addr_hmac_valid(struct mptcp_sock *msk,
+                               struct mptcp_options_received *mp_opt)
+{
+       u64 hmac = 0;
+
+       if (mp_opt->echo)
+               return true;
+
+       if (mp_opt->family == MPTCP_ADDR_IPVERSION_4)
+               hmac = add_addr_generate_hmac(msk->remote_key,
+                                             msk->local_key,
+                                             mp_opt->addr_id, &mp_opt->addr);
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+       else
+               hmac = add_addr6_generate_hmac(msk->remote_key,
+                                              msk->local_key,
+                                              mp_opt->addr_id, &mp_opt->addr6);
+#endif
+
+       pr_debug("msk=%p, ahmac=%llu, mp_opt->ahmac=%llu\n",
+                msk, (unsigned long long)hmac,
+                (unsigned long long)mp_opt->ahmac);
+
+       return hmac == mp_opt->ahmac;
+}
+
 void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb,
                            struct tcp_options_received *opt_rx)
 {
        struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
+       struct mptcp_sock *msk = mptcp_sk(subflow->conn);
        struct mptcp_options_received *mp_opt;
        struct mptcp_ext *mpext;
 
@@ -618,6 +644,26 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb,
        if (!check_fully_established(subflow, skb, mp_opt))
                return;
 
+       if (mp_opt->add_addr && add_addr_hmac_valid(msk, mp_opt)) {
+               struct mptcp_addr_info addr;
+
+               addr.port = htons(mp_opt->port);
+               addr.id = mp_opt->addr_id;
+               if (mp_opt->family == MPTCP_ADDR_IPVERSION_4) {
+                       addr.family = AF_INET;
+                       addr.addr = mp_opt->addr;
+               }
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+               else if (mp_opt->family == MPTCP_ADDR_IPVERSION_6) {
+                       addr.family = AF_INET6;
+                       addr.addr6 = mp_opt->addr6;
+               }
+#endif
+               if (!mp_opt->echo)
+                       mptcp_pm_add_addr_received(msk, &addr);
+               mp_opt->add_addr = 0;
+       }
+
        if (!mp_opt->dss)
                return;
 
@@ -654,6 +700,8 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb,
        }
 
        mpext->data_fin = mp_opt->data_fin;
+
+       mptcp_pm_fully_established(msk);
 }
 
 void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
new file mode 100644 (file)
index 0000000..ad837da
--- /dev/null
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Multipath TCP
+ *
+ * Copyright (c) 2019, Intel Corporation.
+ */
+#include <linux/kernel.h>
+#include <net/tcp.h>
+#include <net/mptcp.h>
+#include "protocol.h"
+
+static struct workqueue_struct *pm_wq;
+
+/* path manager command handlers */
+
+int mptcp_pm_announce_addr(struct mptcp_sock *msk,
+                          const struct mptcp_addr_info *addr)
+{
+       return -ENOTSUPP;
+}
+
+int mptcp_pm_remove_addr(struct mptcp_sock *msk, u8 local_id)
+{
+       return -ENOTSUPP;
+}
+
+int mptcp_pm_remove_subflow(struct mptcp_sock *msk, u8 remote_id)
+{
+       return -ENOTSUPP;
+}
+
+/* path manager event handlers */
+
+void mptcp_pm_new_connection(struct mptcp_sock *msk, int server_side)
+{
+       struct mptcp_pm_data *pm = &msk->pm;
+
+       pr_debug("msk=%p, token=%u side=%d", msk, msk->token, server_side);
+
+       WRITE_ONCE(pm->server_side, server_side);
+}
+
+bool mptcp_pm_allow_new_subflow(struct mptcp_sock *msk)
+{
+       pr_debug("msk=%p", msk);
+       return false;
+}
+
+void mptcp_pm_fully_established(struct mptcp_sock *msk)
+{
+       pr_debug("msk=%p", msk);
+}
+
+void mptcp_pm_connection_closed(struct mptcp_sock *msk)
+{
+       pr_debug("msk=%p", msk);
+}
+
+void mptcp_pm_subflow_established(struct mptcp_sock *msk,
+                                 struct mptcp_subflow_context *subflow)
+{
+       pr_debug("msk=%p", msk);
+}
+
+void mptcp_pm_subflow_closed(struct mptcp_sock *msk, u8 id)
+{
+       pr_debug("msk=%p", msk);
+}
+
+void mptcp_pm_add_addr_received(struct mptcp_sock *msk,
+                               const struct mptcp_addr_info *addr)
+{
+       pr_debug("msk=%p, remote_id=%d", msk, addr->id);
+}
+
+/* path manager helpers */
+
+bool mptcp_pm_addr_signal(struct mptcp_sock *msk, unsigned int remaining,
+                         struct mptcp_addr_info *saddr)
+{
+       return false;
+}
+
+int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc)
+{
+       return 0;
+}
+
+static void pm_worker(struct work_struct *work)
+{
+}
+
+void mptcp_pm_data_init(struct mptcp_sock *msk)
+{
+       msk->pm.add_addr_signaled = 0;
+       msk->pm.add_addr_accepted = 0;
+       msk->pm.local_addr_used = 0;
+       msk->pm.subflows = 0;
+       WRITE_ONCE(msk->pm.work_pending, false);
+       WRITE_ONCE(msk->pm.addr_signal, false);
+       WRITE_ONCE(msk->pm.accept_addr, false);
+       WRITE_ONCE(msk->pm.accept_subflow, false);
+       msk->pm.status = 0;
+
+       spin_lock_init(&msk->pm.lock);
+       INIT_WORK(&msk->pm.work, pm_worker);
+}
+
+void mptcp_pm_init(void)
+{
+       pm_wq = alloc_workqueue("pm_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 8);
+       if (!pm_wq)
+               panic("Failed to allocate workqueue");
+}
index 92d5382..5f93e8c 100644 (file)
@@ -703,6 +703,8 @@ static int __mptcp_init_sock(struct sock *sk)
        msk->first = NULL;
        inet_csk(sk)->icsk_sync_mss = mptcp_sync_mss;
 
+       mptcp_pm_data_init(msk);
+
        return 0;
 }
 
@@ -1055,6 +1057,8 @@ void mptcp_finish_connect(struct sock *ssk)
        WRITE_ONCE(msk->write_seq, subflow->idsn + 1);
        WRITE_ONCE(msk->ack_seq, ack_seq);
        WRITE_ONCE(msk->can_ack, 1);
+
+       mptcp_pm_new_connection(msk, 0);
 }
 
 static void mptcp_sock_graft(struct sock *sk, struct socket *parent)
@@ -1377,6 +1381,7 @@ void mptcp_proto_init(void)
        mptcp_prot.h.hashinfo = tcp_prot.h.hashinfo;
 
        mptcp_subflow_init();
+       mptcp_pm_init();
 
        if (proto_register(&mptcp_prot, 1) != 0)
                panic("Failed to register MPTCP proto.\n");
index 471e013..8d4761a 100644 (file)
@@ -84,6 +84,50 @@ static inline __be32 mptcp_option(u8 subopt, u8 len, u8 nib, u8 field)
                     ((nib & 0xF) << 8) | field);
 }
 
+#define MPTCP_PM_MAX_ADDR      4
+
+struct mptcp_addr_info {
+       sa_family_t             family;
+       __be16                  port;
+       u8                      id;
+       union {
+               struct in_addr addr;
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+               struct in6_addr addr6;
+#endif
+       };
+};
+
+enum mptcp_pm_status {
+       MPTCP_PM_ADD_ADDR_RECEIVED,
+       MPTCP_PM_ESTABLISHED,
+       MPTCP_PM_SUBFLOW_ESTABLISHED,
+};
+
+struct mptcp_pm_data {
+       struct mptcp_addr_info local;
+       struct mptcp_addr_info remote;
+
+       spinlock_t      lock;           /*protects the whole PM data */
+
+       bool            addr_signal;
+       bool            server_side;
+       bool            work_pending;
+       bool            accept_addr;
+       bool            accept_subflow;
+       u8              add_addr_signaled;
+       u8              add_addr_accepted;
+       u8              local_addr_used;
+       u8              subflows;
+       u8              add_addr_signal_max;
+       u8              add_addr_accept_max;
+       u8              local_addr_max;
+       u8              subflows_max;
+       u8              status;
+
+       struct          work_struct work;
+};
+
 /* MPTCP connection sock */
 struct mptcp_sock {
        /* inet_connection_sock must be the first member */
@@ -100,6 +144,7 @@ struct mptcp_sock {
        struct skb_ext  *cached_ext;    /* for the next sendmsg */
        struct socket   *subflow; /* outgoing connect/listener/!mp_capable */
        struct sock     *first;
+       struct mptcp_pm_data    pm;
 };
 
 #define mptcp_for_each_subflow(__msk, __subflow)                       \
@@ -116,6 +161,7 @@ struct mptcp_subflow_request_sock {
                mp_join : 1,
                backup : 1,
                remote_key_valid : 1;
+       u8      local_id;
        u64     local_key;
        u64     remote_key;
        u64     idsn;
@@ -246,6 +292,39 @@ static inline void mptcp_crypto_key_gen_sha(u64 *key, u32 *token, u64 *idsn)
 
 void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u8 *msg, int len, void *hmac);
 
+void mptcp_pm_init(void);
+void mptcp_pm_data_init(struct mptcp_sock *msk);
+void mptcp_pm_new_connection(struct mptcp_sock *msk, int server_side);
+void mptcp_pm_fully_established(struct mptcp_sock *msk);
+bool mptcp_pm_allow_new_subflow(struct mptcp_sock *msk);
+void mptcp_pm_connection_closed(struct mptcp_sock *msk);
+void mptcp_pm_subflow_established(struct mptcp_sock *msk,
+                                 struct mptcp_subflow_context *subflow);
+void mptcp_pm_subflow_closed(struct mptcp_sock *msk, u8 id);
+void mptcp_pm_add_addr_received(struct mptcp_sock *msk,
+                               const struct mptcp_addr_info *addr);
+
+int mptcp_pm_announce_addr(struct mptcp_sock *msk,
+                          const struct mptcp_addr_info *addr);
+int mptcp_pm_remove_addr(struct mptcp_sock *msk, u8 local_id);
+int mptcp_pm_remove_subflow(struct mptcp_sock *msk, u8 remote_id);
+
+static inline bool mptcp_pm_should_signal(struct mptcp_sock *msk)
+{
+       return READ_ONCE(msk->pm.addr_signal);
+}
+
+static inline unsigned int mptcp_add_addr_len(int family)
+{
+       if (family == AF_INET)
+               return TCPOLEN_MPTCP_ADD_ADDR;
+       return TCPOLEN_MPTCP_ADD_ADDR6;
+}
+
+bool mptcp_pm_addr_signal(struct mptcp_sock *msk, unsigned int remaining,
+                         struct mptcp_addr_info *saddr);
+int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc);
+
 static inline struct mptcp_ext *mptcp_get_ext(struct sk_buff *skb)
 {
        return (struct mptcp_ext *)skb_ext_find(skb, SKB_EXT_MPTCP);
index 5bae12d..eb82cb3 100644 (file)
@@ -240,8 +240,8 @@ create_child:
                        /* new mpc subflow takes ownership of the newly
                         * created mptcp socket
                         */
-                       inet_sk_state_store((struct sock *)new_msk,
-                                           TCP_ESTABLISHED);
+                       inet_sk_state_store(new_msk, TCP_ESTABLISHED);
+                       mptcp_pm_new_connection(mptcp_sk(new_msk), 1);
                        ctx->conn = new_msk;
                        new_msk = NULL;
                }