aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/inet_hashtables.h5
-rw-r--r--net/ipv4/inet_hashtables.c32
-rw-r--r--net/ipv6/inet6_hashtables.c93
3 files changed, 28 insertions, 102 deletions
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index 761bdc01425d..a34a8f25fc57 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -413,6 +413,11 @@ static inline struct sock *inet_lookup(struct inet_hashinfo *hashinfo,
413 return sk; 413 return sk;
414} 414}
415 415
416extern int __inet_hash_connect(struct inet_timewait_death_row *death_row,
417 struct sock *sk,
418 int (*check_established)(struct inet_timewait_death_row *,
419 struct sock *, __u16, struct inet_timewait_sock **),
420 void (*hash)(struct inet_hashinfo *, struct sock *));
416extern int inet_hash_connect(struct inet_timewait_death_row *death_row, 421extern int inet_hash_connect(struct inet_timewait_death_row *death_row,
417 struct sock *sk); 422 struct sock *sk);
418#endif /* _INET_HASHTABLES_H */ 423#endif /* _INET_HASHTABLES_H */
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 619c63c6948a..b93d40ff6ef4 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -348,11 +348,11 @@ void __inet_hash(struct inet_hashinfo *hashinfo, struct sock *sk)
348} 348}
349EXPORT_SYMBOL_GPL(__inet_hash); 349EXPORT_SYMBOL_GPL(__inet_hash);
350 350
351/* 351int __inet_hash_connect(struct inet_timewait_death_row *death_row,
352 * Bind a port for a connect operation and hash it. 352 struct sock *sk,
353 */ 353 int (*check_established)(struct inet_timewait_death_row *,
354int inet_hash_connect(struct inet_timewait_death_row *death_row, 354 struct sock *, __u16, struct inet_timewait_sock **),
355 struct sock *sk) 355 void (*hash)(struct inet_hashinfo *, struct sock *))
356{ 356{
357 struct inet_hashinfo *hinfo = death_row->hashinfo; 357 struct inet_hashinfo *hinfo = death_row->hashinfo;
358 const unsigned short snum = inet_sk(sk)->num; 358 const unsigned short snum = inet_sk(sk)->num;
@@ -385,9 +385,8 @@ int inet_hash_connect(struct inet_timewait_death_row *death_row,
385 BUG_TRAP(!hlist_empty(&tb->owners)); 385 BUG_TRAP(!hlist_empty(&tb->owners));
386 if (tb->fastreuse >= 0) 386 if (tb->fastreuse >= 0)
387 goto next_port; 387 goto next_port;
388 if (!__inet_check_established(death_row, 388 if (!check_established(death_row, sk,
389 sk, port, 389 port, &tw))
390 &tw))
391 goto ok; 390 goto ok;
392 goto next_port; 391 goto next_port;
393 } 392 }
@@ -415,7 +414,7 @@ ok:
415 inet_bind_hash(sk, tb, port); 414 inet_bind_hash(sk, tb, port);
416 if (sk_unhashed(sk)) { 415 if (sk_unhashed(sk)) {
417 inet_sk(sk)->sport = htons(port); 416 inet_sk(sk)->sport = htons(port);
418 __inet_hash_nolisten(hinfo, sk); 417 hash(hinfo, sk);
419 } 418 }
420 spin_unlock(&head->lock); 419 spin_unlock(&head->lock);
421 420
@@ -432,17 +431,28 @@ ok:
432 tb = inet_csk(sk)->icsk_bind_hash; 431 tb = inet_csk(sk)->icsk_bind_hash;
433 spin_lock_bh(&head->lock); 432 spin_lock_bh(&head->lock);
434 if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) { 433 if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
435 __inet_hash_nolisten(hinfo, sk); 434 hash(hinfo, sk);
436 spin_unlock_bh(&head->lock); 435 spin_unlock_bh(&head->lock);
437 return 0; 436 return 0;
438 } else { 437 } else {
439 spin_unlock(&head->lock); 438 spin_unlock(&head->lock);
440 /* No definite answer... Walk to established hash table */ 439 /* No definite answer... Walk to established hash table */
441 ret = __inet_check_established(death_row, sk, snum, NULL); 440 ret = check_established(death_row, sk, snum, NULL);
442out: 441out:
443 local_bh_enable(); 442 local_bh_enable();
444 return ret; 443 return ret;
445 } 444 }
446} 445}
446EXPORT_SYMBOL_GPL(__inet_hash_connect);
447
448/*
449 * Bind a port for a connect operation and hash it.
450 */
451int inet_hash_connect(struct inet_timewait_death_row *death_row,
452 struct sock *sk)
453{
454 return __inet_hash_connect(death_row, sk,
455 __inet_check_established, __inet_hash_nolisten);
456}
447 457
448EXPORT_SYMBOL_GPL(inet_hash_connect); 458EXPORT_SYMBOL_GPL(inet_hash_connect);
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index 06b01befc90e..ece6d0ee2da5 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -233,97 +233,8 @@ static inline u32 inet6_sk_port_offset(const struct sock *sk)
233int inet6_hash_connect(struct inet_timewait_death_row *death_row, 233int inet6_hash_connect(struct inet_timewait_death_row *death_row,
234 struct sock *sk) 234 struct sock *sk)
235{ 235{
236 struct inet_hashinfo *hinfo = death_row->hashinfo; 236 return __inet_hash_connect(death_row, sk,
237 const unsigned short snum = inet_sk(sk)->num; 237 __inet6_check_established, __inet6_hash);
238 struct inet_bind_hashbucket *head;
239 struct inet_bind_bucket *tb;
240 int ret;
241
242 if (snum == 0) {
243 int i, port, low, high, remaining;
244 static u32 hint;
245 const u32 offset = hint + inet6_sk_port_offset(sk);
246 struct hlist_node *node;
247 struct inet_timewait_sock *tw = NULL;
248
249 inet_get_local_port_range(&low, &high);
250 remaining = (high - low) + 1;
251
252 local_bh_disable();
253 for (i = 1; i <= remaining; i++) {
254 port = low + (i + offset) % remaining;
255 head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)];
256 spin_lock(&head->lock);
257
258 /* Does not bother with rcv_saddr checks,
259 * because the established check is already
260 * unique enough.
261 */
262 inet_bind_bucket_for_each(tb, node, &head->chain) {
263 if (tb->port == port) {
264 BUG_TRAP(!hlist_empty(&tb->owners));
265 if (tb->fastreuse >= 0)
266 goto next_port;
267 if (!__inet6_check_established(death_row,
268 sk, port,
269 &tw))
270 goto ok;
271 goto next_port;
272 }
273 }
274
275 tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
276 head, port);
277 if (!tb) {
278 spin_unlock(&head->lock);
279 break;
280 }
281 tb->fastreuse = -1;
282 goto ok;
283
284 next_port:
285 spin_unlock(&head->lock);
286 }
287 local_bh_enable();
288
289 return -EADDRNOTAVAIL;
290
291ok:
292 hint += i;
293
294 /* Head lock still held and bh's disabled */
295 inet_bind_hash(sk, tb, port);
296 if (sk_unhashed(sk)) {
297 inet_sk(sk)->sport = htons(port);
298 __inet6_hash(hinfo, sk);
299 }
300 spin_unlock(&head->lock);
301
302 if (tw) {
303 inet_twsk_deschedule(tw, death_row);
304 inet_twsk_put(tw);
305 }
306
307 ret = 0;
308 goto out;
309 }
310
311 head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)];
312 tb = inet_csk(sk)->icsk_bind_hash;
313 spin_lock_bh(&head->lock);
314
315 if (sk_head(&tb->owners) == sk && sk->sk_bind_node.next == NULL) {
316 __inet6_hash(hinfo, sk);
317 spin_unlock_bh(&head->lock);
318 return 0;
319 } else {
320 spin_unlock(&head->lock);
321 /* No definite answer... Walk to established hash table */
322 ret = __inet6_check_established(death_row, sk, snum, NULL);
323out:
324 local_bh_enable();
325 return ret;
326 }
327} 238}
328 239
329EXPORT_SYMBOL_GPL(inet6_hash_connect); 240EXPORT_SYMBOL_GPL(inet6_hash_connect);