diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/inet_hashtables.c | 32 | ||||
-rw-r--r-- | net/ipv6/inet6_hashtables.c | 93 |
2 files changed, 23 insertions, 102 deletions
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 | } |
349 | EXPORT_SYMBOL_GPL(__inet_hash); | 349 | EXPORT_SYMBOL_GPL(__inet_hash); |
350 | 350 | ||
351 | /* | 351 | int __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 *, |
354 | int 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); |
442 | out: | 441 | out: |
443 | local_bh_enable(); | 442 | local_bh_enable(); |
444 | return ret; | 443 | return ret; |
445 | } | 444 | } |
446 | } | 445 | } |
446 | EXPORT_SYMBOL_GPL(__inet_hash_connect); | ||
447 | |||
448 | /* | ||
449 | * Bind a port for a connect operation and hash it. | ||
450 | */ | ||
451 | int 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 | ||
448 | EXPORT_SYMBOL_GPL(inet_hash_connect); | 458 | EXPORT_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) | |||
233 | int inet6_hash_connect(struct inet_timewait_death_row *death_row, | 233 | int 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 | |||
291 | ok: | ||
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); | ||
323 | out: | ||
324 | local_bh_enable(); | ||
325 | return ret; | ||
326 | } | ||
327 | } | 238 | } |
328 | 239 | ||
329 | EXPORT_SYMBOL_GPL(inet6_hash_connect); | 240 | EXPORT_SYMBOL_GPL(inet6_hash_connect); |