@@ -41,7 +41,7 @@ struct pm_nl_pernet {
unsigned int next_id;
};
-#define MPTCP_PM_ADDR_MAX 8
+#define MPTCP_PM_ADDR_MAX MPTCP_SUBFLOWS_MAX
static bool addresses_equal(const struct mptcp_addr_info *a,
struct mptcp_addr_info *b, bool use_port)
@@ -154,6 +154,8 @@ enum mptcp_pm_status {
MPTCP_PM_SUBFLOW_ESTABLISHED,
};
+#define MPTCP_SUBFLOWS_MAX 8
+
struct mptcp_pm_data {
struct mptcp_addr_info local;
struct mptcp_addr_info remote;
@@ -126,6 +126,49 @@ static int __subflow_check_options(const struct mptcp_options_received *mp_opt,
return 0;
}
+static bool mptcp_join_store(struct mptcp_subflow_request_sock *req,
+ struct sock *sk_listener,
+ bool want_cookie)
+{
+ struct mptcp_sock *msk = req->msk;
+ struct inet_connection_sock *icsk;
+ struct sock *sk;
+
+ icsk = &msk->sk;
+ sk = &icsk->icsk_inet.sk;
+
+ if (inet_csk_reqsk_queue_len(sk) >= MPTCP_SUBFLOWS_MAX ||
+ !mptcp_can_accept_new_subflow(msk))
+ return false;
+
+ atomic_inc(&inet_csk(sk)->icsk_accept_queue.qlen);
+
+ if (likely(!want_cookie))
+ return true;
+
+ /* Syncookies are used.
+ * We can't do this for JOIN requests because we need to store
+ * the initiators nonce for HMAC validation.
+ *
+ * At this point we know:
+ * 1. a valid parent connection that should be joined
+ * 2. the parent socket has less than MPTCP_SUBFLOWS_MAX joined
+ * connections (includes those in progress).
+ *
+ * We add the request to the accept queue backlog ourselves
+ * in this case.
+ */
+ if (unlikely(!refcount_inc_not_zero(&sk_listener->sk_refcnt))) {
+ atomic_dec(&inet_csk(sk)->icsk_accept_queue.qlen);
+ return false;
+ }
+
+ req->sk.req.req.rsk_listener = sk_listener;
+ inet_csk_reqsk_queue_hash_add(sk, &req->sk.req.req,
+ tcp_timeout_init((struct sock *)req));
+ return true;
+}
+
static void subflow_init_req(struct request_sock *req,
const struct sock *sk_listener,
struct sk_buff *skb,
@@ -177,12 +220,18 @@ static void subflow_init_req(struct request_sock *req,
} else if (mp_opt.mp_join && listener->request_mptcp) {
subflow_req->ssn_offset = TCP_SKB_CB(skb)->seq;
- subflow_req->mp_join = 1;
subflow_req->backup = mp_opt.backup;
subflow_req->remote_id = mp_opt.join_id;
subflow_req->token = mp_opt.token;
subflow_req->remote_nonce = mp_opt.nonce;
subflow_req->msk = subflow_token_join_request(req, skb);
+ if (!subflow_req->msk)
+ return;
+
+ if (!mptcp_join_store(subflow_req, (void *)sk_listener, want_cookie))
+ return;
+
+ subflow_req->mp_join = 1;
pr_debug("token=%u, remote_nonce=%u msk=%p", subflow_req->token,
subflow_req->remote_nonce, subflow_req->msk);
}
@@ -1285,9 +1334,14 @@ static void subflow_ulp_release(struct sock *sk)
if (!ctx)
return;
- if (ctx->conn)
- sock_put(ctx->conn);
+ if (ctx->conn) {
+ struct sock *msk = ctx->conn;
+
+ if (ctx->mp_join)
+ atomic_add_unless(&inet_csk(msk)->icsk_accept_queue.qlen, -1, 0);
+ sock_put(msk);
+ }
kfree_rcu(ctx, rcu);
}
JOIN requests do not work in syncookie mode -- for HMAC validation, the peers nonce is required, but this nonce is only present in the SYN. So either we need to drop or reject all JOIN requests once a listening socket enters syncookie mode, or we need to store enough state to validate the ACKs HMAC later on. This allows the subflow request initialisation function to store the request socket even when syncookies are used, i.e. the listener socket queue will grow past its upper limit. Following restrictions apply: 1. The (32bit) token contained in the MP_JOIN SYN packet returns a valid parent connection. 2. The parent connection can accept one more subflow. To ensure 2), all MP_JOIN requests (new incoming and existing) are accounted in the mptcp parent socket. If the token is invalid or the parent cannot accept a new subflow, no information is stored and TCP fallback path is used. The parent socket can't be used without further changes in TCP stack, because socket creation after 3whs completion checks that the associated socket is in LISTEN state. Signed-off-by: Florian Westphal <fw@strlen.de> --- To ease review I think it would also be possible to defer the JOIN stuff for later and just focus on initial MP_CAPABLE request. OTOH, doing so yields MPTCP-on-wire but with no Multipath capability so I'm not sure having MP_CAPABLE without MP_JOIN is useful. net/mptcp/pm_netlink.c | 2 +- net/mptcp/protocol.h | 2 ++ net/mptcp/subflow.c | 60 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 60 insertions(+), 4 deletions(-)