blob: 9c07717748153a4a0dfebedebb4b567bbcf1103d [file] [log] [blame]
Peter Krystad79c09492020-01-21 16:56:20 -08001// SPDX-License-Identifier: GPL-2.0
2/* Multipath TCP token management
3 * Copyright (c) 2017 - 2019, Intel Corporation.
4 *
5 * Note: This code is based on mptcp_ctrl.c from multipath-tcp.org,
6 * authored by:
7 *
8 * Sébastien Barré <sebastien.barre@uclouvain.be>
9 * Christoph Paasch <christoph.paasch@uclouvain.be>
10 * Jaakko Korkeaniemi <jaakko.korkeaniemi@aalto.fi>
11 * Gregory Detal <gregory.detal@uclouvain.be>
12 * Fabien Duchêne <fabien.duchene@uclouvain.be>
13 * Andreas Seelinger <Andreas.Seelinger@rwth-aachen.de>
14 * Lavkesh Lahngir <lavkesh51@gmail.com>
15 * Andreas Ripke <ripke@neclab.eu>
16 * Vlad Dogaru <vlad.dogaru@intel.com>
17 * Octavian Purdila <octavian.purdila@intel.com>
18 * John Ronan <jronan@tssg.org>
19 * Catalin Nicutar <catalin.nicutar@gmail.com>
20 * Brandon Heller <brandonh@stanford.edu>
21 */
22
23#define pr_fmt(fmt) "MPTCP: " fmt
24
25#include <linux/kernel.h>
26#include <linux/module.h>
Paolo Abeni2c5ebd02020-06-26 19:30:00 +020027#include <linux/memblock.h>
Peter Krystad79c09492020-01-21 16:56:20 -080028#include <linux/ip.h>
29#include <linux/tcp.h>
30#include <net/sock.h>
31#include <net/inet_common.h>
32#include <net/protocol.h>
33#include <net/mptcp.h>
34#include "protocol.h"
35
Paolo Abeni2c5ebd02020-06-26 19:30:00 +020036#define TOKEN_MAX_RETRIES 4
37#define TOKEN_MAX_CHAIN_LEN 4
38
39struct token_bucket {
40 spinlock_t lock;
41 int chain_len;
42 struct hlist_nulls_head req_chain;
43 struct hlist_nulls_head msk_chain;
44};
45
46static struct token_bucket *token_hash __read_mostly;
47static unsigned int token_mask __read_mostly;
48
49static struct token_bucket *token_bucket(u32 token)
50{
51 return &token_hash[token & token_mask];
52}
53
54/* called with bucket lock held */
55static struct mptcp_subflow_request_sock *
56__token_lookup_req(struct token_bucket *t, u32 token)
57{
58 struct mptcp_subflow_request_sock *req;
59 struct hlist_nulls_node *pos;
60
61 hlist_nulls_for_each_entry_rcu(req, pos, &t->req_chain, token_node)
62 if (req->token == token)
63 return req;
64 return NULL;
65}
66
67/* called with bucket lock held */
68static struct mptcp_sock *
69__token_lookup_msk(struct token_bucket *t, u32 token)
70{
71 struct hlist_nulls_node *pos;
72 struct sock *sk;
73
74 sk_nulls_for_each_rcu(sk, pos, &t->msk_chain)
75 if (mptcp_sk(sk)->token == token)
76 return mptcp_sk(sk);
77 return NULL;
78}
79
80static bool __token_bucket_busy(struct token_bucket *t, u32 token)
81{
82 return !token || t->chain_len >= TOKEN_MAX_CHAIN_LEN ||
83 __token_lookup_req(t, token) || __token_lookup_msk(t, token);
84}
Peter Krystad79c09492020-01-21 16:56:20 -080085
86/**
87 * mptcp_token_new_request - create new key/idsn/token for subflow_request
Matthieu Baerts564cf2f2020-04-02 13:44:54 +020088 * @req: the request socket
Peter Krystad79c09492020-01-21 16:56:20 -080089 *
90 * This function is called when a new mptcp connection is coming in.
91 *
92 * It creates a unique token to identify the new mptcp connection,
93 * a secret local key and the initial data sequence number (idsn).
94 *
95 * Returns 0 on success.
96 */
97int mptcp_token_new_request(struct request_sock *req)
98{
99 struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req);
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200100 int retries = TOKEN_MAX_RETRIES;
101 struct token_bucket *bucket;
102 u32 token;
Peter Krystad79c09492020-01-21 16:56:20 -0800103
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200104again:
105 mptcp_crypto_key_gen_sha(&subflow_req->local_key,
106 &subflow_req->token,
107 &subflow_req->idsn);
108 pr_debug("req=%p local_key=%llu, token=%u, idsn=%llu\n",
109 req, subflow_req->local_key, subflow_req->token,
110 subflow_req->idsn);
Peter Krystad79c09492020-01-21 16:56:20 -0800111
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200112 token = subflow_req->token;
113 bucket = token_bucket(token);
114 spin_lock_bh(&bucket->lock);
115 if (__token_bucket_busy(bucket, token)) {
116 spin_unlock_bh(&bucket->lock);
117 if (!--retries)
118 return -EBUSY;
119 goto again;
Peter Krystad79c09492020-01-21 16:56:20 -0800120 }
121
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200122 hlist_nulls_add_head_rcu(&subflow_req->token_node, &bucket->req_chain);
123 bucket->chain_len++;
124 spin_unlock_bh(&bucket->lock);
125 return 0;
Peter Krystad79c09492020-01-21 16:56:20 -0800126}
127
128/**
129 * mptcp_token_new_connect - create new key/idsn/token for subflow
Matthieu Baerts564cf2f2020-04-02 13:44:54 +0200130 * @sk: the socket that will initiate a connection
Peter Krystad79c09492020-01-21 16:56:20 -0800131 *
132 * This function is called when a new outgoing mptcp connection is
133 * initiated.
134 *
135 * It creates a unique token to identify the new mptcp connection,
136 * a secret local key and the initial data sequence number (idsn).
137 *
138 * On success, the mptcp connection can be found again using
139 * the computed token at a later time, this is needed to process
140 * join requests.
141 *
142 * returns 0 on success.
143 */
144int mptcp_token_new_connect(struct sock *sk)
145{
146 struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200147 struct mptcp_sock *msk = mptcp_sk(subflow->conn);
148 int retries = TOKEN_MAX_RETRIES;
149 struct token_bucket *bucket;
Peter Krystad79c09492020-01-21 16:56:20 -0800150
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200151 pr_debug("ssk=%p, local_key=%llu, token=%u, idsn=%llu\n",
152 sk, subflow->local_key, subflow->token, subflow->idsn);
Peter Krystad79c09492020-01-21 16:56:20 -0800153
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200154again:
155 mptcp_crypto_key_gen_sha(&subflow->local_key, &subflow->token,
156 &subflow->idsn);
Peter Krystad79c09492020-01-21 16:56:20 -0800157
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200158 bucket = token_bucket(subflow->token);
159 spin_lock_bh(&bucket->lock);
160 if (__token_bucket_busy(bucket, subflow->token)) {
161 spin_unlock_bh(&bucket->lock);
162 if (!--retries)
163 return -EBUSY;
164 goto again;
Peter Krystad79c09492020-01-21 16:56:20 -0800165 }
Peter Krystad79c09492020-01-21 16:56:20 -0800166
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200167 WRITE_ONCE(msk->token, subflow->token);
168 __sk_nulls_add_node_rcu((struct sock *)msk, &bucket->msk_chain);
169 bucket->chain_len++;
170 spin_unlock_bh(&bucket->lock);
171 return 0;
Peter Krystad79c09492020-01-21 16:56:20 -0800172}
173
174/**
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200175 * mptcp_token_accept - replace a req sk with full sock in token hash
176 * @req: the request socket to be removed
177 * @msk: the just cloned socket linked to the new connection
Peter Krystad79c09492020-01-21 16:56:20 -0800178 *
179 * Called when a SYN packet creates a new logical connection, i.e.
180 * is not a join request.
Peter Krystad79c09492020-01-21 16:56:20 -0800181 */
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200182void mptcp_token_accept(struct mptcp_subflow_request_sock *req,
183 struct mptcp_sock *msk)
Peter Krystad79c09492020-01-21 16:56:20 -0800184{
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200185 struct mptcp_subflow_request_sock *pos;
186 struct token_bucket *bucket;
Peter Krystad79c09492020-01-21 16:56:20 -0800187
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200188 bucket = token_bucket(req->token);
189 spin_lock_bh(&bucket->lock);
Peter Krystad79c09492020-01-21 16:56:20 -0800190
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200191 /* pedantic lookup check for the moved token */
192 pos = __token_lookup_req(bucket, req->token);
193 if (!WARN_ON_ONCE(pos != req))
194 hlist_nulls_del_init_rcu(&req->token_node);
195 __sk_nulls_add_node_rcu((struct sock *)msk, &bucket->msk_chain);
196 spin_unlock_bh(&bucket->lock);
Peter Krystad79c09492020-01-21 16:56:20 -0800197}
198
199/**
Peter Krystadf2962342020-03-27 14:48:39 -0700200 * mptcp_token_get_sock - retrieve mptcp connection sock using its token
201 * @token: token of the mptcp connection to retrieve
202 *
203 * This function returns the mptcp connection structure with the given token.
204 * A reference count on the mptcp socket returned is taken.
205 *
206 * returns NULL if no connection with the given token value exists.
207 */
208struct mptcp_sock *mptcp_token_get_sock(u32 token)
209{
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200210 struct hlist_nulls_node *pos;
211 struct token_bucket *bucket;
212 struct mptcp_sock *msk;
213 struct sock *sk;
Peter Krystadf2962342020-03-27 14:48:39 -0700214
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200215 rcu_read_lock();
216 bucket = token_bucket(token);
217
218again:
219 sk_nulls_for_each_rcu(sk, pos, &bucket->msk_chain) {
220 msk = mptcp_sk(sk);
221 if (READ_ONCE(msk->token) != token)
222 continue;
223 if (!refcount_inc_not_zero(&sk->sk_refcnt))
224 goto not_found;
225 if (READ_ONCE(msk->token) != token) {
226 sock_put(sk);
227 goto again;
228 }
229 goto found;
Peter Krystadf2962342020-03-27 14:48:39 -0700230 }
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200231 if (get_nulls_value(pos) != (token & token_mask))
232 goto again;
Peter Krystadf2962342020-03-27 14:48:39 -0700233
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200234not_found:
235 msk = NULL;
236
237found:
238 rcu_read_unlock();
239 return msk;
Peter Krystadf2962342020-03-27 14:48:39 -0700240}
241
242/**
Peter Krystad79c09492020-01-21 16:56:20 -0800243 * mptcp_token_destroy_request - remove mptcp connection/token
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200244 * @req: mptcp request socket dropping the token
Peter Krystad79c09492020-01-21 16:56:20 -0800245 *
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200246 * Remove the token associated to @req.
Peter Krystad79c09492020-01-21 16:56:20 -0800247 */
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200248void mptcp_token_destroy_request(struct request_sock *req)
Peter Krystad79c09492020-01-21 16:56:20 -0800249{
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200250 struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req);
251 struct mptcp_subflow_request_sock *pos;
252 struct token_bucket *bucket;
253
254 if (hlist_nulls_unhashed(&subflow_req->token_node))
255 return;
256
257 bucket = token_bucket(subflow_req->token);
258 spin_lock_bh(&bucket->lock);
259 pos = __token_lookup_req(bucket, subflow_req->token);
260 if (!WARN_ON_ONCE(pos != subflow_req)) {
261 hlist_nulls_del_init_rcu(&pos->token_node);
262 bucket->chain_len--;
263 }
264 spin_unlock_bh(&bucket->lock);
Peter Krystad79c09492020-01-21 16:56:20 -0800265}
266
267/**
268 * mptcp_token_destroy - remove mptcp connection/token
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200269 * @msk: mptcp connection dropping the token
Peter Krystad79c09492020-01-21 16:56:20 -0800270 *
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200271 * Remove the token associated to @msk
Peter Krystad79c09492020-01-21 16:56:20 -0800272 */
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200273void mptcp_token_destroy(struct mptcp_sock *msk)
Peter Krystad79c09492020-01-21 16:56:20 -0800274{
Paolo Abeni2c5ebd02020-06-26 19:30:00 +0200275 struct token_bucket *bucket;
276 struct mptcp_sock *pos;
277
278 if (sk_unhashed((struct sock *)msk))
279 return;
280
281 bucket = token_bucket(msk->token);
282 spin_lock_bh(&bucket->lock);
283 pos = __token_lookup_msk(bucket, msk->token);
284 if (!WARN_ON_ONCE(pos != msk)) {
285 __sk_nulls_del_node_init_rcu((struct sock *)pos);
286 bucket->chain_len--;
287 }
288 spin_unlock_bh(&bucket->lock);
289}
290
291void __init mptcp_token_init(void)
292{
293 int i;
294
295 token_hash = alloc_large_system_hash("MPTCP token",
296 sizeof(struct token_bucket),
297 0,
298 20,/* one slot per 1MB of memory */
299 0,
300 NULL,
301 &token_mask,
302 0,
303 64 * 1024);
304 for (i = 0; i < token_mask + 1; ++i) {
305 INIT_HLIST_NULLS_HEAD(&token_hash[i].req_chain, i);
306 INIT_HLIST_NULLS_HEAD(&token_hash[i].msk_chain, i);
307 spin_lock_init(&token_hash[i].lock);
308 }
Peter Krystad79c09492020-01-21 16:56:20 -0800309}