aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@mandriva.com>2005-12-14 02:25:31 -0500
committerDavid S. Miller <davem@sunset.davemloft.net>2006-01-03 16:10:55 -0500
commita7f5e7f164788a22eb5d3de8e2d3cee1bf58fdca (patch)
tree809ed01d61aa9548124b9958a5a500068b1db670 /net
parent6d6ee43e0b8b8d4847627fd43739b98ec2b9404f (diff)
[INET]: Generalise tcp_v4_hash_connect
Renaming it to inet_hash_connect, making it possible to ditch dccp_v4_hash_connect and share the same code with TCP instead. Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/dccp/ipv4.c160
-rw-r--r--net/ipv4/inet_hashtables.c178
-rw-r--r--net/ipv4/tcp_ipv4.c173
3 files changed, 180 insertions, 331 deletions
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index e11cda0cb6b3..671fbf3b2379 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -54,164 +54,6 @@ void dccp_unhash(struct sock *sk)
54 54
55EXPORT_SYMBOL_GPL(dccp_unhash); 55EXPORT_SYMBOL_GPL(dccp_unhash);
56 56
57/* called with local bh disabled */
58static int __dccp_v4_check_established(struct sock *sk, const __u16 lport,
59 struct inet_timewait_sock **twp)
60{
61 struct inet_sock *inet = inet_sk(sk);
62 const u32 daddr = inet->rcv_saddr;
63 const u32 saddr = inet->daddr;
64 const int dif = sk->sk_bound_dev_if;
65 INET_ADDR_COOKIE(acookie, saddr, daddr)
66 const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
67 unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport);
68 struct inet_ehash_bucket *head = inet_ehash_bucket(&dccp_hashinfo, hash);
69 const struct sock *sk2;
70 const struct hlist_node *node;
71 struct inet_timewait_sock *tw;
72
73 prefetch(head->chain.first);
74 write_lock(&head->lock);
75
76 /* Check TIME-WAIT sockets first. */
77 sk_for_each(sk2, node, &(head + dccp_hashinfo.ehash_size)->chain) {
78 tw = inet_twsk(sk2);
79
80 if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
81 goto not_unique;
82 }
83 tw = NULL;
84
85 /* And established part... */
86 sk_for_each(sk2, node, &head->chain) {
87 if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
88 goto not_unique;
89 }
90
91 /* Must record num and sport now. Otherwise we will see
92 * in hash table socket with a funny identity. */
93 inet->num = lport;
94 inet->sport = htons(lport);
95 sk->sk_hash = hash;
96 BUG_TRAP(sk_unhashed(sk));
97 __sk_add_node(sk, &head->chain);
98 sock_prot_inc_use(sk->sk_prot);
99 write_unlock(&head->lock);
100
101 if (twp != NULL) {
102 *twp = tw;
103 NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
104 } else if (tw != NULL) {
105 /* Silly. Should hash-dance instead... */
106 inet_twsk_deschedule(tw, &dccp_death_row);
107 NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
108
109 inet_twsk_put(tw);
110 }
111
112 return 0;
113
114not_unique:
115 write_unlock(&head->lock);
116 return -EADDRNOTAVAIL;
117}
118
119/*
120 * Bind a port for a connect operation and hash it.
121 */
122static int dccp_v4_hash_connect(struct sock *sk)
123{
124 const unsigned short snum = inet_sk(sk)->num;
125 struct inet_bind_hashbucket *head;
126 struct inet_bind_bucket *tb;
127 int ret;
128
129 if (snum == 0) {
130 int low = sysctl_local_port_range[0];
131 int high = sysctl_local_port_range[1];
132 int remaining = (high - low) + 1;
133 int rover = net_random() % (high - low) + low;
134 struct hlist_node *node;
135 struct inet_timewait_sock *tw = NULL;
136
137 local_bh_disable();
138 do {
139 head = &dccp_hashinfo.bhash[inet_bhashfn(rover,
140 dccp_hashinfo.bhash_size)];
141 spin_lock(&head->lock);
142
143 /* Does not bother with rcv_saddr checks,
144 * because the established check is already
145 * unique enough.
146 */
147 inet_bind_bucket_for_each(tb, node, &head->chain) {
148 if (tb->port == rover) {
149 BUG_TRAP(!hlist_empty(&tb->owners));
150 if (tb->fastreuse >= 0)
151 goto next_port;
152 if (!__dccp_v4_check_established(sk,
153 rover,
154 &tw))
155 goto ok;
156 goto next_port;
157 }
158 }
159
160 tb = inet_bind_bucket_create(dccp_hashinfo.bind_bucket_cachep,
161 head, rover);
162 if (tb == NULL) {
163 spin_unlock(&head->lock);
164 break;
165 }
166 tb->fastreuse = -1;
167 goto ok;
168
169 next_port:
170 spin_unlock(&head->lock);
171 if (++rover > high)
172 rover = low;
173 } while (--remaining > 0);
174
175 local_bh_enable();
176
177 return -EADDRNOTAVAIL;
178
179ok:
180 /* All locks still held and bhs disabled */
181 inet_bind_hash(sk, tb, rover);
182 if (sk_unhashed(sk)) {
183 inet_sk(sk)->sport = htons(rover);
184 __inet_hash(&dccp_hashinfo, sk, 0);
185 }
186 spin_unlock(&head->lock);
187
188 if (tw != NULL) {
189 inet_twsk_deschedule(tw, &dccp_death_row);
190 inet_twsk_put(tw);
191 }
192
193 ret = 0;
194 goto out;
195 }
196
197 head = &dccp_hashinfo.bhash[inet_bhashfn(snum,
198 dccp_hashinfo.bhash_size)];
199 tb = inet_csk(sk)->icsk_bind_hash;
200 spin_lock_bh(&head->lock);
201 if (sk_head(&tb->owners) == sk && sk->sk_bind_node.next == NULL) {
202 __inet_hash(&dccp_hashinfo, sk, 0);
203 spin_unlock_bh(&head->lock);
204 return 0;
205 } else {
206 spin_unlock(&head->lock);
207 /* No definite answer... Walk to established hash table */
208 ret = __dccp_v4_check_established(sk, snum, NULL);
209out:
210 local_bh_enable();
211 return ret;
212 }
213}
214
215int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) 57int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
216{ 58{
217 struct inet_sock *inet = inet_sk(sk); 59 struct inet_sock *inet = inet_sk(sk);
@@ -272,7 +114,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
272 * complete initialization after this. 114 * complete initialization after this.
273 */ 115 */
274 dccp_set_state(sk, DCCP_REQUESTING); 116 dccp_set_state(sk, DCCP_REQUESTING);
275 err = dccp_v4_hash_connect(sk); 117 err = inet_hash_connect(&dccp_death_row, sk);
276 if (err != 0) 118 if (err != 0)
277 goto failure; 119 goto failure;
278 120
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index e8d29fe736d2..33228115cda4 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -15,12 +15,14 @@
15 15
16#include <linux/config.h> 16#include <linux/config.h>
17#include <linux/module.h> 17#include <linux/module.h>
18#include <linux/random.h>
18#include <linux/sched.h> 19#include <linux/sched.h>
19#include <linux/slab.h> 20#include <linux/slab.h>
20#include <linux/wait.h> 21#include <linux/wait.h>
21 22
22#include <net/inet_connection_sock.h> 23#include <net/inet_connection_sock.h>
23#include <net/inet_hashtables.h> 24#include <net/inet_hashtables.h>
25#include <net/ip.h>
24 26
25/* 27/*
26 * Allocate and initialize a new local port bind bucket. 28 * Allocate and initialize a new local port bind bucket.
@@ -163,3 +165,179 @@ struct sock *__inet_lookup_listener(const struct hlist_head *head, const u32 dad
163} 165}
164 166
165EXPORT_SYMBOL_GPL(__inet_lookup_listener); 167EXPORT_SYMBOL_GPL(__inet_lookup_listener);
168
169/* called with local bh disabled */
170static int __inet_check_established(struct inet_timewait_death_row *death_row,
171 struct sock *sk, __u16 lport,
172 struct inet_timewait_sock **twp)
173{
174 struct inet_hashinfo *hinfo = death_row->hashinfo;
175 struct inet_sock *inet = inet_sk(sk);
176 u32 daddr = inet->rcv_saddr;
177 u32 saddr = inet->daddr;
178 int dif = sk->sk_bound_dev_if;
179 INET_ADDR_COOKIE(acookie, saddr, daddr)
180 const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
181 unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport);
182 struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
183 struct sock *sk2;
184 const struct hlist_node *node;
185 struct inet_timewait_sock *tw;
186
187 prefetch(head->chain.first);
188 write_lock(&head->lock);
189
190 /* Check TIME-WAIT sockets first. */
191 sk_for_each(sk2, node, &(head + hinfo->ehash_size)->chain) {
192 tw = inet_twsk(sk2);
193
194 if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) {
195 if (twsk_unique(sk, sk2, twp))
196 goto unique;
197 else
198 goto not_unique;
199 }
200 }
201 tw = NULL;
202
203 /* And established part... */
204 sk_for_each(sk2, node, &head->chain) {
205 if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
206 goto not_unique;
207 }
208
209unique:
210 /* Must record num and sport now. Otherwise we will see
211 * in hash table socket with a funny identity. */
212 inet->num = lport;
213 inet->sport = htons(lport);
214 sk->sk_hash = hash;
215 BUG_TRAP(sk_unhashed(sk));
216 __sk_add_node(sk, &head->chain);
217 sock_prot_inc_use(sk->sk_prot);
218 write_unlock(&head->lock);
219
220 if (twp) {
221 *twp = tw;
222 NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
223 } else if (tw) {
224 /* Silly. Should hash-dance instead... */
225 inet_twsk_deschedule(tw, death_row);
226 NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
227
228 inet_twsk_put(tw);
229 }
230
231 return 0;
232
233not_unique:
234 write_unlock(&head->lock);
235 return -EADDRNOTAVAIL;
236}
237
238static inline u32 inet_sk_port_offset(const struct sock *sk)
239{
240 const struct inet_sock *inet = inet_sk(sk);
241 return secure_ipv4_port_ephemeral(inet->rcv_saddr, inet->daddr,
242 inet->dport);
243}
244
245/*
246 * Bind a port for a connect operation and hash it.
247 */
248int inet_hash_connect(struct inet_timewait_death_row *death_row,
249 struct sock *sk)
250{
251 struct inet_hashinfo *hinfo = death_row->hashinfo;
252 const unsigned short snum = inet_sk(sk)->num;
253 struct inet_bind_hashbucket *head;
254 struct inet_bind_bucket *tb;
255 int ret;
256
257 if (!snum) {
258 int low = sysctl_local_port_range[0];
259 int high = sysctl_local_port_range[1];
260 int range = high - low;
261 int i;
262 int port;
263 static u32 hint;
264 u32 offset = hint + inet_sk_port_offset(sk);
265 struct hlist_node *node;
266 struct inet_timewait_sock *tw = NULL;
267
268 local_bh_disable();
269 for (i = 1; i <= range; i++) {
270 port = low + (i + offset) % range;
271 head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)];
272 spin_lock(&head->lock);
273
274 /* Does not bother with rcv_saddr checks,
275 * because the established check is already
276 * unique enough.
277 */
278 inet_bind_bucket_for_each(tb, node, &head->chain) {
279 if (tb->port == port) {
280 BUG_TRAP(!hlist_empty(&tb->owners));
281 if (tb->fastreuse >= 0)
282 goto next_port;
283 if (!__inet_check_established(death_row,
284 sk, port,
285 &tw))
286 goto ok;
287 goto next_port;
288 }
289 }
290
291 tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep, head, port);
292 if (!tb) {
293 spin_unlock(&head->lock);
294 break;
295 }
296 tb->fastreuse = -1;
297 goto ok;
298
299 next_port:
300 spin_unlock(&head->lock);
301 }
302 local_bh_enable();
303
304 return -EADDRNOTAVAIL;
305
306ok:
307 hint += i;
308
309 /* Head lock still held and bh's disabled */
310 inet_bind_hash(sk, tb, port);
311 if (sk_unhashed(sk)) {
312 inet_sk(sk)->sport = htons(port);
313 __inet_hash(hinfo, sk, 0);
314 }
315 spin_unlock(&head->lock);
316
317 if (tw) {
318 inet_twsk_deschedule(tw, death_row);;
319 inet_twsk_put(tw);
320 }
321
322 ret = 0;
323 goto out;
324 }
325
326 head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)];
327 tb = inet_csk(sk)->icsk_bind_hash;
328 spin_lock_bh(&head->lock);
329 if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
330 __inet_hash(hinfo, sk, 0);
331 spin_unlock_bh(&head->lock);
332 return 0;
333 } else {
334 spin_unlock(&head->lock);
335 /* No definite answer... Walk to established hash table */
336 ret = __inet_check_established(death_row, sk, snum, NULL);
337out:
338 local_bh_enable();
339 return ret;
340 }
341}
342
343EXPORT_SYMBOL_GPL(inet_hash_connect);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 6728772a943a..c2fe61becd61 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -152,177 +152,6 @@ int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
152 152
153EXPORT_SYMBOL_GPL(tcp_twsk_unique); 153EXPORT_SYMBOL_GPL(tcp_twsk_unique);
154 154
155/* called with local bh disabled */
156static int __tcp_v4_check_established(struct sock *sk, __u16 lport,
157 struct inet_timewait_sock **twp)
158{
159 struct inet_sock *inet = inet_sk(sk);
160 u32 daddr = inet->rcv_saddr;
161 u32 saddr = inet->daddr;
162 int dif = sk->sk_bound_dev_if;
163 INET_ADDR_COOKIE(acookie, saddr, daddr)
164 const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
165 unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport);
166 struct inet_ehash_bucket *head = inet_ehash_bucket(&tcp_hashinfo, hash);
167 struct sock *sk2;
168 const struct hlist_node *node;
169 struct inet_timewait_sock *tw;
170
171 prefetch(head->chain.first);
172 write_lock(&head->lock);
173
174 /* Check TIME-WAIT sockets first. */
175 sk_for_each(sk2, node, &(head + tcp_hashinfo.ehash_size)->chain) {
176 tw = inet_twsk(sk2);
177
178 if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) {
179 if (twsk_unique(sk, sk2, twp))
180 goto unique;
181 else
182 goto not_unique;
183 }
184 }
185 tw = NULL;
186
187 /* And established part... */
188 sk_for_each(sk2, node, &head->chain) {
189 if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
190 goto not_unique;
191 }
192
193unique:
194 /* Must record num and sport now. Otherwise we will see
195 * in hash table socket with a funny identity. */
196 inet->num = lport;
197 inet->sport = htons(lport);
198 sk->sk_hash = hash;
199 BUG_TRAP(sk_unhashed(sk));
200 __sk_add_node(sk, &head->chain);
201 sock_prot_inc_use(sk->sk_prot);
202 write_unlock(&head->lock);
203
204 if (twp) {
205 *twp = tw;
206 NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
207 } else if (tw) {
208 /* Silly. Should hash-dance instead... */
209 inet_twsk_deschedule(tw, &tcp_death_row);
210 NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
211
212 inet_twsk_put(tw);
213 }
214
215 return 0;
216
217not_unique:
218 write_unlock(&head->lock);
219 return -EADDRNOTAVAIL;
220}
221
222static inline u32 connect_port_offset(const struct sock *sk)
223{
224 const struct inet_sock *inet = inet_sk(sk);
225
226 return secure_tcp_port_ephemeral(inet->rcv_saddr, inet->daddr,
227 inet->dport);
228}
229
230/*
231 * Bind a port for a connect operation and hash it.
232 */
233static inline int tcp_v4_hash_connect(struct sock *sk)
234{
235 const unsigned short snum = inet_sk(sk)->num;
236 struct inet_bind_hashbucket *head;
237 struct inet_bind_bucket *tb;
238 int ret;
239
240 if (!snum) {
241 int low = sysctl_local_port_range[0];
242 int high = sysctl_local_port_range[1];
243 int range = high - low;
244 int i;
245 int port;
246 static u32 hint;
247 u32 offset = hint + connect_port_offset(sk);
248 struct hlist_node *node;
249 struct inet_timewait_sock *tw = NULL;
250
251 local_bh_disable();
252 for (i = 1; i <= range; i++) {
253 port = low + (i + offset) % range;
254 head = &tcp_hashinfo.bhash[inet_bhashfn(port, tcp_hashinfo.bhash_size)];
255 spin_lock(&head->lock);
256
257 /* Does not bother with rcv_saddr checks,
258 * because the established check is already
259 * unique enough.
260 */
261 inet_bind_bucket_for_each(tb, node, &head->chain) {
262 if (tb->port == port) {
263 BUG_TRAP(!hlist_empty(&tb->owners));
264 if (tb->fastreuse >= 0)
265 goto next_port;
266 if (!__tcp_v4_check_established(sk,
267 port,
268 &tw))
269 goto ok;
270 goto next_port;
271 }
272 }
273
274 tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, port);
275 if (!tb) {
276 spin_unlock(&head->lock);
277 break;
278 }
279 tb->fastreuse = -1;
280 goto ok;
281
282 next_port:
283 spin_unlock(&head->lock);
284 }
285 local_bh_enable();
286
287 return -EADDRNOTAVAIL;
288
289ok:
290 hint += i;
291
292 /* Head lock still held and bh's disabled */
293 inet_bind_hash(sk, tb, port);
294 if (sk_unhashed(sk)) {
295 inet_sk(sk)->sport = htons(port);
296 __inet_hash(&tcp_hashinfo, sk, 0);
297 }
298 spin_unlock(&head->lock);
299
300 if (tw) {
301 inet_twsk_deschedule(tw, &tcp_death_row);;
302 inet_twsk_put(tw);
303 }
304
305 ret = 0;
306 goto out;
307 }
308
309 head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)];
310 tb = inet_csk(sk)->icsk_bind_hash;
311 spin_lock_bh(&head->lock);
312 if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
313 __inet_hash(&tcp_hashinfo, sk, 0);
314 spin_unlock_bh(&head->lock);
315 return 0;
316 } else {
317 spin_unlock(&head->lock);
318 /* No definite answer... Walk to established hash table */
319 ret = __tcp_v4_check_established(sk, snum, NULL);
320out:
321 local_bh_enable();
322 return ret;
323 }
324}
325
326/* This will initiate an outgoing connection. */ 155/* This will initiate an outgoing connection. */
327int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) 156int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
328{ 157{
@@ -403,7 +232,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
403 * complete initialization after this. 232 * complete initialization after this.
404 */ 233 */
405 tcp_set_state(sk, TCP_SYN_SENT); 234 tcp_set_state(sk, TCP_SYN_SENT);
406 err = tcp_v4_hash_connect(sk); 235 err = inet_hash_connect(&tcp_death_row, sk);
407 if (err) 236 if (err)
408 goto failure; 237 goto failure;
409 238