Blame view

net/ipv4/inet_hashtables.c 15 KB
77d8bf9c6   Arnaldo Carvalho de Melo   [INET]: Move the ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * INET		An implementation of the TCP/IP protocol suite for the LINUX
   *		operating system.  INET is implemented using the BSD Socket
   *		interface as the means of communication with the user level.
   *
   *		Generic INET transport hashtables
   *
   * Authors:	Lotsa people, from code originally in tcp
   *
   *	This program is free software; you can redistribute it and/or
   *      modify it under the terms of the GNU General Public License
   *      as published by the Free Software Foundation; either version
   *      2 of the License, or (at your option) any later version.
   */
2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
15
  #include <linux/module.h>
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
16
  #include <linux/random.h>
f3f05f704   Arnaldo Carvalho de Melo   [INET]: Generalis...
17
  #include <linux/sched.h>
77d8bf9c6   Arnaldo Carvalho de Melo   [INET]: Move the ...
18
  #include <linux/slab.h>
f3f05f704   Arnaldo Carvalho de Melo   [INET]: Generalis...
19
  #include <linux/wait.h>
77d8bf9c6   Arnaldo Carvalho de Melo   [INET]: Move the ...
20

463c84b97   Arnaldo Carvalho de Melo   [NET]: Introduce ...
21
  #include <net/inet_connection_sock.h>
77d8bf9c6   Arnaldo Carvalho de Melo   [INET]: Move the ...
22
  #include <net/inet_hashtables.h>
6e5714eaf   David S. Miller   net: Compute prot...
23
  #include <net/secure_seq.h>
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
24
  #include <net/ip.h>
77d8bf9c6   Arnaldo Carvalho de Melo   [INET]: Move the ...
25
26
27
28
29
  
  /*
   * Allocate and initialize a new local port bind bucket.
   * The bindhash mutex for snum's hash chain must be held here.
   */
e18b890bb   Christoph Lameter   [PATCH] slab: rem...
30
  struct inet_bind_bucket *inet_bind_bucket_create(struct kmem_cache *cachep,
941b1d22c   Pavel Emelyanov   [NETNS]: Make bin...
31
  						 struct net *net,
77d8bf9c6   Arnaldo Carvalho de Melo   [INET]: Move the ...
32
33
34
  						 struct inet_bind_hashbucket *head,
  						 const unsigned short snum)
  {
54e6ecb23   Christoph Lameter   [PATCH] slab: rem...
35
  	struct inet_bind_bucket *tb = kmem_cache_alloc(cachep, GFP_ATOMIC);
77d8bf9c6   Arnaldo Carvalho de Melo   [INET]: Move the ...
36
37
  
  	if (tb != NULL) {
7a9546ee3   Eric Dumazet   net: ib_net point...
38
  		write_pnet(&tb->ib_net, hold_net(net));
77d8bf9c6   Arnaldo Carvalho de Melo   [INET]: Move the ...
39
40
  		tb->port      = snum;
  		tb->fastreuse = 0;
a9d8f9110   Evgeniy Polyakov   inet: Allowing mo...
41
  		tb->num_owners = 0;
77d8bf9c6   Arnaldo Carvalho de Melo   [INET]: Move the ...
42
43
44
45
46
  		INIT_HLIST_HEAD(&tb->owners);
  		hlist_add_head(&tb->node, &head->chain);
  	}
  	return tb;
  }
77d8bf9c6   Arnaldo Carvalho de Melo   [INET]: Move the ...
47
48
49
  /*
   * Caller must hold hashbucket lock for this tb with local BH disabled
   */
e18b890bb   Christoph Lameter   [PATCH] slab: rem...
50
  void inet_bind_bucket_destroy(struct kmem_cache *cachep, struct inet_bind_bucket *tb)
77d8bf9c6   Arnaldo Carvalho de Melo   [INET]: Move the ...
51
52
53
  {
  	if (hlist_empty(&tb->owners)) {
  		__hlist_del(&tb->node);
7a9546ee3   Eric Dumazet   net: ib_net point...
54
  		release_net(ib_net(tb));
77d8bf9c6   Arnaldo Carvalho de Melo   [INET]: Move the ...
55
56
57
  		kmem_cache_free(cachep, tb);
  	}
  }
2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
58
59
60
61
  
  void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb,
  		    const unsigned short snum)
  {
a9d8f9110   Evgeniy Polyakov   inet: Allowing mo...
62
  	struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
24dd1fa18   Eric Dumazet   net: move bsocket...
63
  	atomic_inc(&hashinfo->bsockets);
a9d8f9110   Evgeniy Polyakov   inet: Allowing mo...
64

c720c7e83   Eric Dumazet   inet: rename some...
65
  	inet_sk(sk)->inet_num = snum;
2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
66
  	sk_add_bind_node(sk, &tb->owners);
a9d8f9110   Evgeniy Polyakov   inet: Allowing mo...
67
  	tb->num_owners++;
463c84b97   Arnaldo Carvalho de Melo   [NET]: Introduce ...
68
  	inet_csk(sk)->icsk_bind_hash = tb;
2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
69
  }
2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
70
71
72
  /*
   * Get rid of any references to a local port held by the given sock.
   */
ab1e0a13d   Arnaldo Carvalho de Melo   [SOCK] proto: Add...
73
  static void __inet_put_port(struct sock *sk)
2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
74
  {
39d8cda76   Pavel Emelyanov   [SOCK]: Add udp_h...
75
  	struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
c720c7e83   Eric Dumazet   inet: rename some...
76
  	const int bhash = inet_bhashfn(sock_net(sk), inet_sk(sk)->inet_num,
7f635ab71   Pavel Emelyanov   inet: add struct ...
77
  			hashinfo->bhash_size);
2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
78
79
  	struct inet_bind_hashbucket *head = &hashinfo->bhash[bhash];
  	struct inet_bind_bucket *tb;
24dd1fa18   Eric Dumazet   net: move bsocket...
80
  	atomic_dec(&hashinfo->bsockets);
a9d8f9110   Evgeniy Polyakov   inet: Allowing mo...
81

2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
82
  	spin_lock(&head->lock);
463c84b97   Arnaldo Carvalho de Melo   [NET]: Introduce ...
83
  	tb = inet_csk(sk)->icsk_bind_hash;
2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
84
  	__sk_del_bind_node(sk);
a9d8f9110   Evgeniy Polyakov   inet: Allowing mo...
85
  	tb->num_owners--;
463c84b97   Arnaldo Carvalho de Melo   [NET]: Introduce ...
86
  	inet_csk(sk)->icsk_bind_hash = NULL;
c720c7e83   Eric Dumazet   inet: rename some...
87
  	inet_sk(sk)->inet_num = 0;
2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
88
89
90
  	inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb);
  	spin_unlock(&head->lock);
  }
ab1e0a13d   Arnaldo Carvalho de Melo   [SOCK] proto: Add...
91
  void inet_put_port(struct sock *sk)
2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
92
93
  {
  	local_bh_disable();
ab1e0a13d   Arnaldo Carvalho de Melo   [SOCK] proto: Add...
94
  	__inet_put_port(sk);
2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
95
96
  	local_bh_enable();
  }
2d8c4ce51   Arnaldo Carvalho de Melo   [INET]: Generalis...
97
  EXPORT_SYMBOL(inet_put_port);
f3f05f704   Arnaldo Carvalho de Melo   [INET]: Generalis...
98

093d28232   Balazs Scheidler   tproxy: fix hash ...
99
  int __inet_inherit_port(struct sock *sk, struct sock *child)
53083773d   Pavel Emelyanov   [INET]: Uninline ...
100
101
  {
  	struct inet_hashinfo *table = sk->sk_prot->h.hashinfo;
093d28232   Balazs Scheidler   tproxy: fix hash ...
102
103
  	unsigned short port = inet_sk(child)->inet_num;
  	const int bhash = inet_bhashfn(sock_net(sk), port,
7f635ab71   Pavel Emelyanov   inet: add struct ...
104
  			table->bhash_size);
53083773d   Pavel Emelyanov   [INET]: Uninline ...
105
106
107
108
109
  	struct inet_bind_hashbucket *head = &table->bhash[bhash];
  	struct inet_bind_bucket *tb;
  
  	spin_lock(&head->lock);
  	tb = inet_csk(sk)->icsk_bind_hash;
093d28232   Balazs Scheidler   tproxy: fix hash ...
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  	if (tb->port != port) {
  		/* NOTE: using tproxy and redirecting skbs to a proxy
  		 * on a different listener port breaks the assumption
  		 * that the listener socket's icsk_bind_hash is the same
  		 * as that of the child socket. We have to look up or
  		 * create a new bind bucket for the child here. */
  		struct hlist_node *node;
  		inet_bind_bucket_for_each(tb, node, &head->chain) {
  			if (net_eq(ib_net(tb), sock_net(sk)) &&
  			    tb->port == port)
  				break;
  		}
  		if (!node) {
  			tb = inet_bind_bucket_create(table->bind_bucket_cachep,
  						     sock_net(sk), head, port);
  			if (!tb) {
  				spin_unlock(&head->lock);
  				return -ENOMEM;
  			}
  		}
  	}
b4ff3c90e   Nagendra Tomar   inet: Fix __inet_...
131
  	inet_bind_hash(child, tb, port);
53083773d   Pavel Emelyanov   [INET]: Uninline ...
132
  	spin_unlock(&head->lock);
093d28232   Balazs Scheidler   tproxy: fix hash ...
133
134
  
  	return 0;
53083773d   Pavel Emelyanov   [INET]: Uninline ...
135
  }
53083773d   Pavel Emelyanov   [INET]: Uninline ...
136
  EXPORT_SYMBOL_GPL(__inet_inherit_port);
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
137
138
139
140
141
142
  static inline int compute_score(struct sock *sk, struct net *net,
  				const unsigned short hnum, const __be32 daddr,
  				const int dif)
  {
  	int score = -1;
  	struct inet_sock *inet = inet_sk(sk);
c720c7e83   Eric Dumazet   inet: rename some...
143
  	if (net_eq(sock_net(sk), net) && inet->inet_num == hnum &&
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
144
  			!ipv6_only_sock(sk)) {
c720c7e83   Eric Dumazet   inet: rename some...
145
  		__be32 rcv_saddr = inet->inet_rcv_saddr;
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
  		score = sk->sk_family == PF_INET ? 1 : 0;
  		if (rcv_saddr) {
  			if (rcv_saddr != daddr)
  				return -1;
  			score += 2;
  		}
  		if (sk->sk_bound_dev_if) {
  			if (sk->sk_bound_dev_if != dif)
  				return -1;
  			score += 2;
  		}
  	}
  	return score;
  }
f3f05f704   Arnaldo Carvalho de Melo   [INET]: Generalis...
160
  /*
33b622319   Arnaldo Carvalho de Melo   [INET]: Generalis...
161
162
163
164
165
   * Don't inline this cruft. Here are some nice properties to exploit here. The
   * BSD API does not allow a listening sock to specify the remote port nor the
   * remote address for the connection. So always assume those are both
   * wildcarded during the search since they can never be otherwise.
   */
e48c414ee   Arnaldo Carvalho de Melo   [INET]: Generalis...
166

c25eb3bfb   Eric Dumazet   net: Convert TCP/...
167

c67499c0e   Pavel Emelyanov   [NETNS]: Tcp-v4 s...
168
169
  struct sock *__inet_lookup_listener(struct net *net,
  				    struct inet_hashinfo *hashinfo,
fb99c848e   Al Viro   [IPV4]: annotate ...
170
  				    const __be32 daddr, const unsigned short hnum,
8f491069b   Herbert Xu   [IPV4]: Use netwo...
171
  				    const int dif)
99a92ff50   Herbert Xu   [IPV4]: Uninline ...
172
  {
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
173
174
175
176
177
  	struct sock *sk, *result;
  	struct hlist_nulls_node *node;
  	unsigned int hash = inet_lhashfn(net, hnum);
  	struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
  	int score, hiscore;
99a92ff50   Herbert Xu   [IPV4]: Uninline ...
178

c25eb3bfb   Eric Dumazet   net: Convert TCP/...
179
180
181
182
183
184
185
186
187
188
  	rcu_read_lock();
  begin:
  	result = NULL;
  	hiscore = -1;
  	sk_nulls_for_each_rcu(sk, node, &ilb->head) {
  		score = compute_score(sk, net, hnum, daddr, dif);
  		if (score > hiscore) {
  			result = sk;
  			hiscore = score;
  		}
99a92ff50   Herbert Xu   [IPV4]: Uninline ...
189
  	}
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
  	/*
  	 * if the nulls value we got at the end of this lookup is
  	 * not the expected one, we must restart lookup.
  	 * We probably met an item that was moved to another chain.
  	 */
  	if (get_nulls_value(node) != hash + LISTENING_NULLS_BASE)
  		goto begin;
  	if (result) {
  		if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt)))
  			result = NULL;
  		else if (unlikely(compute_score(result, net, hnum, daddr,
  				  dif) < hiscore)) {
  			sock_put(result);
  			goto begin;
  		}
99a92ff50   Herbert Xu   [IPV4]: Uninline ...
205
  	}
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
206
207
  	rcu_read_unlock();
  	return result;
99a92ff50   Herbert Xu   [IPV4]: Uninline ...
208
  }
8f491069b   Herbert Xu   [IPV4]: Use netwo...
209
  EXPORT_SYMBOL_GPL(__inet_lookup_listener);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
210

c67499c0e   Pavel Emelyanov   [NETNS]: Tcp-v4 s...
211
212
  struct sock * __inet_lookup_established(struct net *net,
  				  struct inet_hashinfo *hashinfo,
77a5ba55d   Pavel Emelyanov   [INET]: Uninline ...
213
214
215
216
217
218
219
  				  const __be32 saddr, const __be16 sport,
  				  const __be32 daddr, const u16 hnum,
  				  const int dif)
  {
  	INET_ADDR_COOKIE(acookie, saddr, daddr)
  	const __portpair ports = INET_COMBINED_PORTS(sport, hnum);
  	struct sock *sk;
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
220
  	const struct hlist_nulls_node *node;
77a5ba55d   Pavel Emelyanov   [INET]: Uninline ...
221
222
223
  	/* Optimize here for direct hit, only listening connections can
  	 * have wildcards anyways.
  	 */
9f26b3add   Pavel Emelyanov   inet: add struct ...
224
  	unsigned int hash = inet_ehashfn(net, daddr, hnum, saddr, sport);
f373b53b5   Eric Dumazet   tcp: replace ehas...
225
  	unsigned int slot = hash & hashinfo->ehash_mask;
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
226
  	struct inet_ehash_bucket *head = &hashinfo->ehash[slot];
77a5ba55d   Pavel Emelyanov   [INET]: Uninline ...
227

3ab5aee7f   Eric Dumazet   net: Convert TCP ...
228
229
230
  	rcu_read_lock();
  begin:
  	sk_nulls_for_each_rcu(sk, node, &head->chain) {
c67499c0e   Pavel Emelyanov   [NETNS]: Tcp-v4 s...
231
  		if (INET_MATCH(sk, net, hash, acookie,
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
232
233
234
235
236
237
238
239
240
241
  					saddr, daddr, ports, dif)) {
  			if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt)))
  				goto begintw;
  			if (unlikely(!INET_MATCH(sk, net, hash, acookie,
  				saddr, daddr, ports, dif))) {
  				sock_put(sk);
  				goto begin;
  			}
  			goto out;
  		}
77a5ba55d   Pavel Emelyanov   [INET]: Uninline ...
242
  	}
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
243
244
245
246
247
248
249
  	/*
  	 * if the nulls value we got at the end of this lookup is
  	 * not the expected one, we must restart lookup.
  	 * We probably met an item that was moved to another chain.
  	 */
  	if (get_nulls_value(node) != slot)
  		goto begin;
77a5ba55d   Pavel Emelyanov   [INET]: Uninline ...
250

3ab5aee7f   Eric Dumazet   net: Convert TCP ...
251
  begintw:
77a5ba55d   Pavel Emelyanov   [INET]: Uninline ...
252
  	/* Must check for a TIME_WAIT'er before going to listener hash. */
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
253
  	sk_nulls_for_each_rcu(sk, node, &head->twchain) {
c67499c0e   Pavel Emelyanov   [NETNS]: Tcp-v4 s...
254
  		if (INET_TW_MATCH(sk, net, hash, acookie,
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
255
256
257
258
259
260
261
262
263
264
265
266
  					saddr, daddr, ports, dif)) {
  			if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) {
  				sk = NULL;
  				goto out;
  			}
  			if (unlikely(!INET_TW_MATCH(sk, net, hash, acookie,
  				 saddr, daddr, ports, dif))) {
  				sock_put(sk);
  				goto begintw;
  			}
  			goto out;
  		}
77a5ba55d   Pavel Emelyanov   [INET]: Uninline ...
267
  	}
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
268
269
270
271
272
273
274
  	/*
  	 * if the nulls value we got at the end of this lookup is
  	 * not the expected one, we must restart lookup.
  	 * We probably met an item that was moved to another chain.
  	 */
  	if (get_nulls_value(node) != slot)
  		goto begintw;
77a5ba55d   Pavel Emelyanov   [INET]: Uninline ...
275
276
  	sk = NULL;
  out:
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
277
  	rcu_read_unlock();
77a5ba55d   Pavel Emelyanov   [INET]: Uninline ...
278
  	return sk;
77a5ba55d   Pavel Emelyanov   [INET]: Uninline ...
279
280
  }
  EXPORT_SYMBOL_GPL(__inet_lookup_established);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
281
282
283
284
285
286
287
  /* called with local bh disabled */
  static int __inet_check_established(struct inet_timewait_death_row *death_row,
  				    struct sock *sk, __u16 lport,
  				    struct inet_timewait_sock **twp)
  {
  	struct inet_hashinfo *hinfo = death_row->hashinfo;
  	struct inet_sock *inet = inet_sk(sk);
c720c7e83   Eric Dumazet   inet: rename some...
288
289
  	__be32 daddr = inet->inet_rcv_saddr;
  	__be32 saddr = inet->inet_daddr;
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
290
291
  	int dif = sk->sk_bound_dev_if;
  	INET_ADDR_COOKIE(acookie, saddr, daddr)
c720c7e83   Eric Dumazet   inet: rename some...
292
  	const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport);
9f26b3add   Pavel Emelyanov   inet: add struct ...
293
  	struct net *net = sock_net(sk);
c720c7e83   Eric Dumazet   inet: rename some...
294
295
  	unsigned int hash = inet_ehashfn(net, daddr, lport,
  					 saddr, inet->inet_dport);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
296
  	struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
9db66bdcc   Eric Dumazet   net: convert TCP/...
297
  	spinlock_t *lock = inet_ehash_lockp(hinfo, hash);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
298
  	struct sock *sk2;
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
299
  	const struct hlist_nulls_node *node;
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
300
  	struct inet_timewait_sock *tw;
13475a30b   Eric Dumazet   tcp: connect() ra...
301
  	int twrefcnt = 0;
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
302

9db66bdcc   Eric Dumazet   net: convert TCP/...
303
  	spin_lock(lock);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
304
305
  
  	/* Check TIME-WAIT sockets first. */
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
306
  	sk_nulls_for_each(sk2, node, &head->twchain) {
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
307
  		tw = inet_twsk(sk2);
c67499c0e   Pavel Emelyanov   [NETNS]: Tcp-v4 s...
308
309
  		if (INET_TW_MATCH(sk2, net, hash, acookie,
  					saddr, daddr, ports, dif)) {
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
310
311
312
313
314
315
316
317
318
  			if (twsk_unique(sk, sk2, twp))
  				goto unique;
  			else
  				goto not_unique;
  		}
  	}
  	tw = NULL;
  
  	/* And established part... */
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
319
  	sk_nulls_for_each(sk2, node, &head->chain) {
c67499c0e   Pavel Emelyanov   [NETNS]: Tcp-v4 s...
320
321
  		if (INET_MATCH(sk2, net, hash, acookie,
  					saddr, daddr, ports, dif))
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
322
323
324
325
326
327
  			goto not_unique;
  	}
  
  unique:
  	/* Must record num and sport now. Otherwise we will see
  	 * in hash table socket with a funny identity. */
c720c7e83   Eric Dumazet   inet: rename some...
328
329
  	inet->inet_num = lport;
  	inet->inet_sport = htons(lport);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
330
  	sk->sk_hash = hash;
547b792ca   Ilpo Järvinen   net: convert BUG_...
331
  	WARN_ON(!sk_unhashed(sk));
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
332
  	__sk_nulls_add_node_rcu(sk, &head->chain);
13475a30b   Eric Dumazet   tcp: connect() ra...
333
334
335
336
  	if (tw) {
  		twrefcnt = inet_twsk_unhash(tw);
  		NET_INC_STATS_BH(net, LINUX_MIB_TIMEWAITRECYCLED);
  	}
9db66bdcc   Eric Dumazet   net: convert TCP/...
337
  	spin_unlock(lock);
13475a30b   Eric Dumazet   tcp: connect() ra...
338
339
  	if (twrefcnt)
  		inet_twsk_put(tw);
c29a0bc4d   Pavel Emelyanov   [SOCK][NETNS]: Ad...
340
  	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
341
342
343
  
  	if (twp) {
  		*twp = tw;
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
344
345
346
  	} else if (tw) {
  		/* Silly. Should hash-dance instead... */
  		inet_twsk_deschedule(tw, death_row);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
347
348
349
  
  		inet_twsk_put(tw);
  	}
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
350
351
352
  	return 0;
  
  not_unique:
9db66bdcc   Eric Dumazet   net: convert TCP/...
353
  	spin_unlock(lock);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
354
355
356
357
358
359
  	return -EADDRNOTAVAIL;
  }
  
  static inline u32 inet_sk_port_offset(const struct sock *sk)
  {
  	const struct inet_sock *inet = inet_sk(sk);
c720c7e83   Eric Dumazet   inet: rename some...
360
361
362
  	return secure_ipv4_port_ephemeral(inet->inet_rcv_saddr,
  					  inet->inet_daddr,
  					  inet->inet_dport);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
363
  }
9327f7053   Eric Dumazet   tcp: Fix a connec...
364
  int __inet_hash_nolisten(struct sock *sk, struct inet_timewait_sock *tw)
152da81de   Pavel Emelyanov   [INET]: Uninline ...
365
  {
39d8cda76   Pavel Emelyanov   [SOCK]: Add udp_h...
366
  	struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
367
  	struct hlist_nulls_head *list;
9db66bdcc   Eric Dumazet   net: convert TCP/...
368
  	spinlock_t *lock;
152da81de   Pavel Emelyanov   [INET]: Uninline ...
369
  	struct inet_ehash_bucket *head;
9327f7053   Eric Dumazet   tcp: Fix a connec...
370
  	int twrefcnt = 0;
152da81de   Pavel Emelyanov   [INET]: Uninline ...
371

547b792ca   Ilpo Järvinen   net: convert BUG_...
372
  	WARN_ON(!sk_unhashed(sk));
152da81de   Pavel Emelyanov   [INET]: Uninline ...
373
374
375
376
377
  
  	sk->sk_hash = inet_sk_ehashfn(sk);
  	head = inet_ehash_bucket(hashinfo, sk->sk_hash);
  	list = &head->chain;
  	lock = inet_ehash_lockp(hashinfo, sk->sk_hash);
9db66bdcc   Eric Dumazet   net: convert TCP/...
378
  	spin_lock(lock);
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
379
  	__sk_nulls_add_node_rcu(sk, list);
9327f7053   Eric Dumazet   tcp: Fix a connec...
380
381
382
383
  	if (tw) {
  		WARN_ON(sk->sk_hash != tw->tw_hash);
  		twrefcnt = inet_twsk_unhash(tw);
  	}
9db66bdcc   Eric Dumazet   net: convert TCP/...
384
  	spin_unlock(lock);
c29a0bc4d   Pavel Emelyanov   [SOCK][NETNS]: Ad...
385
  	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
9327f7053   Eric Dumazet   tcp: Fix a connec...
386
  	return twrefcnt;
152da81de   Pavel Emelyanov   [INET]: Uninline ...
387
388
  }
  EXPORT_SYMBOL_GPL(__inet_hash_nolisten);
ab1e0a13d   Arnaldo Carvalho de Melo   [SOCK] proto: Add...
389
  static void __inet_hash(struct sock *sk)
152da81de   Pavel Emelyanov   [INET]: Uninline ...
390
  {
39d8cda76   Pavel Emelyanov   [SOCK]: Add udp_h...
391
  	struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
5caea4ea7   Eric Dumazet   net: listening_ha...
392
  	struct inet_listen_hashbucket *ilb;
152da81de   Pavel Emelyanov   [INET]: Uninline ...
393
394
  
  	if (sk->sk_state != TCP_LISTEN) {
9327f7053   Eric Dumazet   tcp: Fix a connec...
395
  		__inet_hash_nolisten(sk, NULL);
152da81de   Pavel Emelyanov   [INET]: Uninline ...
396
397
  		return;
  	}
547b792ca   Ilpo Järvinen   net: convert BUG_...
398
  	WARN_ON(!sk_unhashed(sk));
5caea4ea7   Eric Dumazet   net: listening_ha...
399
  	ilb = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
152da81de   Pavel Emelyanov   [INET]: Uninline ...
400

5caea4ea7   Eric Dumazet   net: listening_ha...
401
  	spin_lock(&ilb->lock);
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
402
  	__sk_nulls_add_node_rcu(sk, &ilb->head);
c29a0bc4d   Pavel Emelyanov   [SOCK][NETNS]: Ad...
403
  	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
5caea4ea7   Eric Dumazet   net: listening_ha...
404
  	spin_unlock(&ilb->lock);
152da81de   Pavel Emelyanov   [INET]: Uninline ...
405
  }
ab1e0a13d   Arnaldo Carvalho de Melo   [SOCK] proto: Add...
406
407
408
409
410
411
412
413
414
415
416
417
418
  
  void inet_hash(struct sock *sk)
  {
  	if (sk->sk_state != TCP_CLOSE) {
  		local_bh_disable();
  		__inet_hash(sk);
  		local_bh_enable();
  	}
  }
  EXPORT_SYMBOL_GPL(inet_hash);
  
  void inet_unhash(struct sock *sk)
  {
39d8cda76   Pavel Emelyanov   [SOCK]: Add udp_h...
419
  	struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
420
421
  	spinlock_t *lock;
  	int done;
ab1e0a13d   Arnaldo Carvalho de Melo   [SOCK] proto: Add...
422
423
  
  	if (sk_unhashed(sk))
5caea4ea7   Eric Dumazet   net: listening_ha...
424
  		return;
ab1e0a13d   Arnaldo Carvalho de Melo   [SOCK] proto: Add...
425

c25eb3bfb   Eric Dumazet   net: Convert TCP/...
426
427
428
429
  	if (sk->sk_state == TCP_LISTEN)
  		lock = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)].lock;
  	else
  		lock = inet_ehash_lockp(hashinfo, sk->sk_hash);
5caea4ea7   Eric Dumazet   net: listening_ha...
430

c25eb3bfb   Eric Dumazet   net: Convert TCP/...
431
432
  	spin_lock_bh(lock);
  	done =__sk_nulls_del_node_init_rcu(sk);
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
433
434
  	if (done)
  		sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
920de804b   Eric Dumazet   net: Make sure BH...
435
  	spin_unlock_bh(lock);
ab1e0a13d   Arnaldo Carvalho de Melo   [SOCK] proto: Add...
436
437
  }
  EXPORT_SYMBOL_GPL(inet_unhash);
152da81de   Pavel Emelyanov   [INET]: Uninline ...
438

5ee31fc1e   Pavel Emelyanov   [INET]: Consolida...
439
  int __inet_hash_connect(struct inet_timewait_death_row *death_row,
5d8c0aa94   Pavel Emelyanov   [INET]: Fix accid...
440
  		struct sock *sk, u32 port_offset,
5ee31fc1e   Pavel Emelyanov   [INET]: Consolida...
441
442
  		int (*check_established)(struct inet_timewait_death_row *,
  			struct sock *, __u16, struct inet_timewait_sock **),
9327f7053   Eric Dumazet   tcp: Fix a connec...
443
  		int (*hash)(struct sock *sk, struct inet_timewait_sock *twp))
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
444
445
  {
  	struct inet_hashinfo *hinfo = death_row->hashinfo;
c720c7e83   Eric Dumazet   inet: rename some...
446
  	const unsigned short snum = inet_sk(sk)->inet_num;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
447
448
  	struct inet_bind_hashbucket *head;
  	struct inet_bind_bucket *tb;
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
449
  	int ret;
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
450
  	struct net *net = sock_net(sk);
9327f7053   Eric Dumazet   tcp: Fix a connec...
451
  	int twrefcnt = 1;
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
452

e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
453
  	if (!snum) {
227b60f51   Stephen Hemminger   [INET]: local por...
454
  		int i, remaining, low, high, port;
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
455
  		static u32 hint;
5d8c0aa94   Pavel Emelyanov   [INET]: Fix accid...
456
  		u32 offset = hint + port_offset;
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
457
  		struct hlist_node *node;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
458
  		struct inet_timewait_sock *tw = NULL;
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
459

227b60f51   Stephen Hemminger   [INET]: local por...
460
  		inet_get_local_port_range(&low, &high);
a25de534f   Anton Arapov   [INET]: Justifica...
461
  		remaining = (high - low) + 1;
227b60f51   Stephen Hemminger   [INET]: local por...
462

e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
463
  		local_bh_disable();
227b60f51   Stephen Hemminger   [INET]: local por...
464
465
  		for (i = 1; i <= remaining; i++) {
  			port = low + (i + offset) % remaining;
e3826f1e9   Amerigo Wang   net: reserve port...
466
467
  			if (inet_is_reserved_local_port(port))
  				continue;
7f635ab71   Pavel Emelyanov   inet: add struct ...
468
469
  			head = &hinfo->bhash[inet_bhashfn(net, port,
  					hinfo->bhash_size)];
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
470
  			spin_lock(&head->lock);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
471

e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
472
473
474
475
  			/* Does not bother with rcv_saddr checks,
  			 * because the established check is already
  			 * unique enough.
  			 */
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
476
  			inet_bind_bucket_for_each(tb, node, &head->chain) {
09ad9bc75   Octavian Purdila   net: use net_eq t...
477
478
  				if (net_eq(ib_net(tb), net) &&
  				    tb->port == port) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
479
480
  					if (tb->fastreuse >= 0)
  						goto next_port;
a9d8f9110   Evgeniy Polyakov   inet: Allowing mo...
481
  					WARN_ON(hlist_empty(&tb->owners));
5ee31fc1e   Pavel Emelyanov   [INET]: Consolida...
482
483
  					if (!check_established(death_row, sk,
  								port, &tw))
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
484
485
486
487
  						goto ok;
  					goto next_port;
  				}
  			}
941b1d22c   Pavel Emelyanov   [NETNS]: Make bin...
488
489
  			tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
  					net, head, port);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
490
491
492
493
494
495
496
497
498
499
500
501
502
  			if (!tb) {
  				spin_unlock(&head->lock);
  				break;
  			}
  			tb->fastreuse = -1;
  			goto ok;
  
  		next_port:
  			spin_unlock(&head->lock);
  		}
  		local_bh_enable();
  
  		return -EADDRNOTAVAIL;
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
503
504
505
  
  ok:
  		hint += i;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
506
507
  		/* Head lock still held and bh's disabled */
  		inet_bind_hash(sk, tb, port);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
508
  		if (sk_unhashed(sk)) {
c720c7e83   Eric Dumazet   inet: rename some...
509
  			inet_sk(sk)->inet_sport = htons(port);
9327f7053   Eric Dumazet   tcp: Fix a connec...
510
  			twrefcnt += hash(sk, tw);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
511
  		}
3cdaedae6   Eric Dumazet   tcp: Fix a connec...
512
513
  		if (tw)
  			twrefcnt += inet_twsk_bind_unhash(tw, hinfo);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
514
  		spin_unlock(&head->lock);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
515

e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
516
517
  		if (tw) {
  			inet_twsk_deschedule(tw, death_row);
9327f7053   Eric Dumazet   tcp: Fix a connec...
518
519
520
521
  			while (twrefcnt) {
  				twrefcnt--;
  				inet_twsk_put(tw);
  			}
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
522
  		}
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
523
524
525
  
  		ret = 0;
  		goto out;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
526
  	}
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
527

7f635ab71   Pavel Emelyanov   inet: add struct ...
528
  	head = &hinfo->bhash[inet_bhashfn(net, snum, hinfo->bhash_size)];
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
529
  	tb  = inet_csk(sk)->icsk_bind_hash;
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
530
531
  	spin_lock_bh(&head->lock);
  	if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
9327f7053   Eric Dumazet   tcp: Fix a connec...
532
  		hash(sk, NULL);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
533
534
535
536
537
  		spin_unlock_bh(&head->lock);
  		return 0;
  	} else {
  		spin_unlock(&head->lock);
  		/* No definite answer... Walk to established hash table */
5ee31fc1e   Pavel Emelyanov   [INET]: Consolida...
538
  		ret = check_established(death_row, sk, snum, NULL);
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
539
540
541
542
543
  out:
  		local_bh_enable();
  		return ret;
  	}
  }
5ee31fc1e   Pavel Emelyanov   [INET]: Consolida...
544
545
546
547
548
549
550
  
  /*
   * Bind a port for a connect operation and hash it.
   */
  int inet_hash_connect(struct inet_timewait_death_row *death_row,
  		      struct sock *sk)
  {
5d8c0aa94   Pavel Emelyanov   [INET]: Fix accid...
551
  	return __inet_hash_connect(death_row, sk, inet_sk_port_offset(sk),
5ee31fc1e   Pavel Emelyanov   [INET]: Consolida...
552
553
  			__inet_check_established, __inet_hash_nolisten);
  }
a7f5e7f16   Arnaldo Carvalho de Melo   [INET]: Generalis...
554
  EXPORT_SYMBOL_GPL(inet_hash_connect);
5caea4ea7   Eric Dumazet   net: listening_ha...
555
556
557
558
  
  void inet_hashinfo_init(struct inet_hashinfo *h)
  {
  	int i;
24dd1fa18   Eric Dumazet   net: move bsocket...
559
  	atomic_set(&h->bsockets, 0);
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
560
  	for (i = 0; i < INET_LHTABLE_SIZE; i++) {
5caea4ea7   Eric Dumazet   net: listening_ha...
561
  		spin_lock_init(&h->listening_hash[i].lock);
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
562
563
564
  		INIT_HLIST_NULLS_HEAD(&h->listening_hash[i].head,
  				      i + LISTENING_NULLS_BASE);
  		}
5caea4ea7   Eric Dumazet   net: listening_ha...
565
  }
5caea4ea7   Eric Dumazet   net: listening_ha...
566
  EXPORT_SYMBOL_GPL(inet_hashinfo_init);