Blame view

net/mptcp/syncookies.c 3.6 KB
9466a1cce   Florian Westphal   mptcp: enable JOI...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  // SPDX-License-Identifier: GPL-2.0
  #include <linux/skbuff.h>
  
  #include "protocol.h"
  
  /* Syncookies do not work for JOIN requests.
   *
   * Unlike MP_CAPABLE, where the ACK cookie contains the needed MPTCP
   * options to reconstruct the initial syn state, MP_JOIN does not contain
   * the token to obtain the mptcp socket nor the server-generated nonce
   * that was used in the cookie SYN/ACK response.
   *
   * Keep a small best effort state table to store the syn/synack data,
   * indexed by skb hash.
   *
   * A MP_JOIN SYN packet handled by syn cookies is only stored if the 32bit
   * token matches a known mptcp connection that can still accept more subflows.
   *
   * There is no timeout handling -- state is only re-constructed
   * when the TCP ACK passed the cookie validation check.
   */
  
  struct join_entry {
  	u32 token;
  	u32 remote_nonce;
  	u32 local_nonce;
  	u8 join_id;
  	u8 local_id;
  	u8 backup;
  	u8 valid;
  };
  
  #define COOKIE_JOIN_SLOTS	1024
  
  static struct join_entry join_entries[COOKIE_JOIN_SLOTS] __cacheline_aligned_in_smp;
  static spinlock_t join_entry_locks[COOKIE_JOIN_SLOTS] __cacheline_aligned_in_smp;
  
  static u32 mptcp_join_entry_hash(struct sk_buff *skb, struct net *net)
  {
  	u32 i = skb_get_hash(skb) ^ net_hash_mix(net);
  
  	return i % ARRAY_SIZE(join_entries);
  }
  
  static void mptcp_join_store_state(struct join_entry *entry,
  				   const struct mptcp_subflow_request_sock *subflow_req)
  {
  	entry->token = subflow_req->token;
  	entry->remote_nonce = subflow_req->remote_nonce;
  	entry->local_nonce = subflow_req->local_nonce;
  	entry->backup = subflow_req->backup;
  	entry->join_id = subflow_req->remote_id;
  	entry->local_id = subflow_req->local_id;
  	entry->valid = 1;
  }
  
  void subflow_init_req_cookie_join_save(const struct mptcp_subflow_request_sock *subflow_req,
  				       struct sk_buff *skb)
  {
  	struct net *net = read_pnet(&subflow_req->sk.req.ireq_net);
  	u32 i = mptcp_join_entry_hash(skb, net);
  
  	/* No use in waiting if other cpu is already using this slot --
  	 * would overwrite the data that got stored.
  	 */
  	spin_lock_bh(&join_entry_locks[i]);
  	mptcp_join_store_state(&join_entries[i], subflow_req);
  	spin_unlock_bh(&join_entry_locks[i]);
  }
  
  /* Called for a cookie-ack with MP_JOIN option present.
   * Look up the saved state based on skb hash & check token matches msk
   * in same netns.
   *
   * Caller will check msk can still accept another subflow.  The hmac
   * present in the cookie ACK mptcp option space will be checked later.
   */
  bool mptcp_token_join_cookie_init_state(struct mptcp_subflow_request_sock *subflow_req,
  					struct sk_buff *skb)
  {
  	struct net *net = read_pnet(&subflow_req->sk.req.ireq_net);
  	u32 i = mptcp_join_entry_hash(skb, net);
  	struct mptcp_sock *msk;
  	struct join_entry *e;
  
  	e = &join_entries[i];
  
  	spin_lock_bh(&join_entry_locks[i]);
  
  	if (e->valid == 0) {
  		spin_unlock_bh(&join_entry_locks[i]);
  		return false;
  	}
  
  	e->valid = 0;
  
  	msk = mptcp_token_get_sock(e->token);
  	if (!msk) {
  		spin_unlock_bh(&join_entry_locks[i]);
  		return false;
  	}
  
  	/* If this fails, the token got re-used in the mean time by another
  	 * mptcp socket in a different netns, i.e. entry is outdated.
  	 */
  	if (!net_eq(sock_net((struct sock *)msk), net))
  		goto err_put;
  
  	subflow_req->remote_nonce = e->remote_nonce;
  	subflow_req->local_nonce = e->local_nonce;
  	subflow_req->backup = e->backup;
  	subflow_req->remote_id = e->join_id;
  	subflow_req->token = e->token;
  	subflow_req->msk = msk;
  	spin_unlock_bh(&join_entry_locks[i]);
  	return true;
  
  err_put:
  	spin_unlock_bh(&join_entry_locks[i]);
  	sock_put((struct sock *)msk);
  	return false;
  }
  
  void __init mptcp_join_cookie_init(void)
  {
  	int i;
7126bd5c8   Florian Westphal   mptcp: fix syncoo...
127
  	for (i = 0; i < COOKIE_JOIN_SLOTS; i++)
9466a1cce   Florian Westphal   mptcp: enable JOI...
128
  		spin_lock_init(&join_entry_locks[i]);
9466a1cce   Florian Westphal   mptcp: enable JOI...
129
  }