177d8bf9cSArnaldo Carvalho de Melo /* 277d8bf9cSArnaldo Carvalho de Melo * INET An implementation of the TCP/IP protocol suite for the LINUX 377d8bf9cSArnaldo Carvalho de Melo * operating system. INET is implemented using the BSD Socket 477d8bf9cSArnaldo Carvalho de Melo * interface as the means of communication with the user level. 577d8bf9cSArnaldo Carvalho de Melo * 677d8bf9cSArnaldo Carvalho de Melo * Generic INET transport hashtables 777d8bf9cSArnaldo Carvalho de Melo * 877d8bf9cSArnaldo Carvalho de Melo * Authors: Lotsa people, from code originally in tcp 977d8bf9cSArnaldo Carvalho de Melo * 1077d8bf9cSArnaldo Carvalho de Melo * This program is free software; you can redistribute it and/or 1177d8bf9cSArnaldo Carvalho de Melo * modify it under the terms of the GNU General Public License 1277d8bf9cSArnaldo Carvalho de Melo * as published by the Free Software Foundation; either version 1377d8bf9cSArnaldo Carvalho de Melo * 2 of the License, or (at your option) any later version. 1477d8bf9cSArnaldo Carvalho de Melo */ 1577d8bf9cSArnaldo Carvalho de Melo 162d8c4ce5SArnaldo Carvalho de Melo #include <linux/module.h> 17a7f5e7f1SArnaldo Carvalho de Melo #include <linux/random.h> 18f3f05f70SArnaldo Carvalho de Melo #include <linux/sched.h> 1977d8bf9cSArnaldo Carvalho de Melo #include <linux/slab.h> 20f3f05f70SArnaldo Carvalho de Melo #include <linux/wait.h> 2177d8bf9cSArnaldo Carvalho de Melo 22463c84b9SArnaldo Carvalho de Melo #include <net/inet_connection_sock.h> 2377d8bf9cSArnaldo Carvalho de Melo #include <net/inet_hashtables.h> 24a7f5e7f1SArnaldo Carvalho de Melo #include <net/ip.h> 2577d8bf9cSArnaldo Carvalho de Melo 2677d8bf9cSArnaldo Carvalho de Melo /* 2777d8bf9cSArnaldo Carvalho de Melo * Allocate and initialize a new local port bind bucket. 2877d8bf9cSArnaldo Carvalho de Melo * The bindhash mutex for snum's hash chain must be held here. 2977d8bf9cSArnaldo Carvalho de Melo */ 30e18b890bSChristoph Lameter struct inet_bind_bucket *inet_bind_bucket_create(struct kmem_cache *cachep, 3177d8bf9cSArnaldo Carvalho de Melo struct inet_bind_hashbucket *head, 3277d8bf9cSArnaldo Carvalho de Melo const unsigned short snum) 3377d8bf9cSArnaldo Carvalho de Melo { 3454e6ecb2SChristoph Lameter struct inet_bind_bucket *tb = kmem_cache_alloc(cachep, GFP_ATOMIC); 3577d8bf9cSArnaldo Carvalho de Melo 3677d8bf9cSArnaldo Carvalho de Melo if (tb != NULL) { 3777d8bf9cSArnaldo Carvalho de Melo tb->port = snum; 3877d8bf9cSArnaldo Carvalho de Melo tb->fastreuse = 0; 3977d8bf9cSArnaldo Carvalho de Melo INIT_HLIST_HEAD(&tb->owners); 4077d8bf9cSArnaldo Carvalho de Melo hlist_add_head(&tb->node, &head->chain); 4177d8bf9cSArnaldo Carvalho de Melo } 4277d8bf9cSArnaldo Carvalho de Melo return tb; 4377d8bf9cSArnaldo Carvalho de Melo } 4477d8bf9cSArnaldo Carvalho de Melo 4577d8bf9cSArnaldo Carvalho de Melo /* 4677d8bf9cSArnaldo Carvalho de Melo * Caller must hold hashbucket lock for this tb with local BH disabled 4777d8bf9cSArnaldo Carvalho de Melo */ 48e18b890bSChristoph Lameter void inet_bind_bucket_destroy(struct kmem_cache *cachep, struct inet_bind_bucket *tb) 4977d8bf9cSArnaldo Carvalho de Melo { 5077d8bf9cSArnaldo Carvalho de Melo if (hlist_empty(&tb->owners)) { 5177d8bf9cSArnaldo Carvalho de Melo __hlist_del(&tb->node); 5277d8bf9cSArnaldo Carvalho de Melo kmem_cache_free(cachep, tb); 5377d8bf9cSArnaldo Carvalho de Melo } 5477d8bf9cSArnaldo Carvalho de Melo } 552d8c4ce5SArnaldo Carvalho de Melo 562d8c4ce5SArnaldo Carvalho de Melo void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb, 572d8c4ce5SArnaldo Carvalho de Melo const unsigned short snum) 582d8c4ce5SArnaldo Carvalho de Melo { 59463c84b9SArnaldo Carvalho de Melo inet_sk(sk)->num = snum; 602d8c4ce5SArnaldo Carvalho de Melo sk_add_bind_node(sk, &tb->owners); 61463c84b9SArnaldo Carvalho de Melo inet_csk(sk)->icsk_bind_hash = tb; 622d8c4ce5SArnaldo Carvalho de Melo } 632d8c4ce5SArnaldo Carvalho de Melo 642d8c4ce5SArnaldo Carvalho de Melo /* 652d8c4ce5SArnaldo Carvalho de Melo * Get rid of any references to a local port held by the given sock. 662d8c4ce5SArnaldo Carvalho de Melo */ 672d8c4ce5SArnaldo Carvalho de Melo static void __inet_put_port(struct inet_hashinfo *hashinfo, struct sock *sk) 682d8c4ce5SArnaldo Carvalho de Melo { 69463c84b9SArnaldo Carvalho de Melo const int bhash = inet_bhashfn(inet_sk(sk)->num, hashinfo->bhash_size); 702d8c4ce5SArnaldo Carvalho de Melo struct inet_bind_hashbucket *head = &hashinfo->bhash[bhash]; 712d8c4ce5SArnaldo Carvalho de Melo struct inet_bind_bucket *tb; 722d8c4ce5SArnaldo Carvalho de Melo 732d8c4ce5SArnaldo Carvalho de Melo spin_lock(&head->lock); 74463c84b9SArnaldo Carvalho de Melo tb = inet_csk(sk)->icsk_bind_hash; 752d8c4ce5SArnaldo Carvalho de Melo __sk_del_bind_node(sk); 76463c84b9SArnaldo Carvalho de Melo inet_csk(sk)->icsk_bind_hash = NULL; 77463c84b9SArnaldo Carvalho de Melo inet_sk(sk)->num = 0; 782d8c4ce5SArnaldo Carvalho de Melo inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb); 792d8c4ce5SArnaldo Carvalho de Melo spin_unlock(&head->lock); 802d8c4ce5SArnaldo Carvalho de Melo } 812d8c4ce5SArnaldo Carvalho de Melo 822d8c4ce5SArnaldo Carvalho de Melo void inet_put_port(struct inet_hashinfo *hashinfo, struct sock *sk) 832d8c4ce5SArnaldo Carvalho de Melo { 842d8c4ce5SArnaldo Carvalho de Melo local_bh_disable(); 852d8c4ce5SArnaldo Carvalho de Melo __inet_put_port(hashinfo, sk); 862d8c4ce5SArnaldo Carvalho de Melo local_bh_enable(); 872d8c4ce5SArnaldo Carvalho de Melo } 882d8c4ce5SArnaldo Carvalho de Melo 892d8c4ce5SArnaldo Carvalho de Melo EXPORT_SYMBOL(inet_put_port); 90f3f05f70SArnaldo Carvalho de Melo 91f3f05f70SArnaldo Carvalho de Melo /* 92f3f05f70SArnaldo Carvalho de Melo * This lock without WQ_FLAG_EXCLUSIVE is good on UP and it can be very bad on SMP. 93f3f05f70SArnaldo Carvalho de Melo * Look, when several writers sleep and reader wakes them up, all but one 94f3f05f70SArnaldo Carvalho de Melo * immediately hit write lock and grab all the cpus. Exclusive sleep solves 95f3f05f70SArnaldo Carvalho de Melo * this, _but_ remember, it adds useless work on UP machines (wake up each 96f3f05f70SArnaldo Carvalho de Melo * exclusive lock release). It should be ifdefed really. 97f3f05f70SArnaldo Carvalho de Melo */ 98f3f05f70SArnaldo Carvalho de Melo void inet_listen_wlock(struct inet_hashinfo *hashinfo) 99*9a429c49SEric Dumazet __acquires(hashinfo->lhash_lock) 100f3f05f70SArnaldo Carvalho de Melo { 101f3f05f70SArnaldo Carvalho de Melo write_lock(&hashinfo->lhash_lock); 102f3f05f70SArnaldo Carvalho de Melo 103f3f05f70SArnaldo Carvalho de Melo if (atomic_read(&hashinfo->lhash_users)) { 104f3f05f70SArnaldo Carvalho de Melo DEFINE_WAIT(wait); 105f3f05f70SArnaldo Carvalho de Melo 106f3f05f70SArnaldo Carvalho de Melo for (;;) { 107f3f05f70SArnaldo Carvalho de Melo prepare_to_wait_exclusive(&hashinfo->lhash_wait, 108f3f05f70SArnaldo Carvalho de Melo &wait, TASK_UNINTERRUPTIBLE); 109f3f05f70SArnaldo Carvalho de Melo if (!atomic_read(&hashinfo->lhash_users)) 110f3f05f70SArnaldo Carvalho de Melo break; 111f3f05f70SArnaldo Carvalho de Melo write_unlock_bh(&hashinfo->lhash_lock); 112f3f05f70SArnaldo Carvalho de Melo schedule(); 113f3f05f70SArnaldo Carvalho de Melo write_lock_bh(&hashinfo->lhash_lock); 114f3f05f70SArnaldo Carvalho de Melo } 115f3f05f70SArnaldo Carvalho de Melo 116f3f05f70SArnaldo Carvalho de Melo finish_wait(&hashinfo->lhash_wait, &wait); 117f3f05f70SArnaldo Carvalho de Melo } 118f3f05f70SArnaldo Carvalho de Melo } 119f3f05f70SArnaldo Carvalho de Melo 120f3f05f70SArnaldo Carvalho de Melo EXPORT_SYMBOL(inet_listen_wlock); 12133b62231SArnaldo Carvalho de Melo 12233b62231SArnaldo Carvalho de Melo /* 12333b62231SArnaldo Carvalho de Melo * Don't inline this cruft. Here are some nice properties to exploit here. The 12433b62231SArnaldo Carvalho de Melo * BSD API does not allow a listening sock to specify the remote port nor the 12533b62231SArnaldo Carvalho de Melo * remote address for the connection. So always assume those are both 12633b62231SArnaldo Carvalho de Melo * wildcarded during the search since they can never be otherwise. 12733b62231SArnaldo Carvalho de Melo */ 1288f491069SHerbert Xu static struct sock *inet_lookup_listener_slow(const struct hlist_head *head, 129fb99c848SAl Viro const __be32 daddr, 13099a92ff5SHerbert Xu const unsigned short hnum, 13199a92ff5SHerbert Xu const int dif) 13233b62231SArnaldo Carvalho de Melo { 13333b62231SArnaldo Carvalho de Melo struct sock *result = NULL, *sk; 13433b62231SArnaldo Carvalho de Melo const struct hlist_node *node; 13533b62231SArnaldo Carvalho de Melo int hiscore = -1; 13633b62231SArnaldo Carvalho de Melo 13733b62231SArnaldo Carvalho de Melo sk_for_each(sk, node, head) { 13833b62231SArnaldo Carvalho de Melo const struct inet_sock *inet = inet_sk(sk); 13933b62231SArnaldo Carvalho de Melo 14033b62231SArnaldo Carvalho de Melo if (inet->num == hnum && !ipv6_only_sock(sk)) { 141fb99c848SAl Viro const __be32 rcv_saddr = inet->rcv_saddr; 14233b62231SArnaldo Carvalho de Melo int score = sk->sk_family == PF_INET ? 1 : 0; 14333b62231SArnaldo Carvalho de Melo 14433b62231SArnaldo Carvalho de Melo if (rcv_saddr) { 14533b62231SArnaldo Carvalho de Melo if (rcv_saddr != daddr) 14633b62231SArnaldo Carvalho de Melo continue; 14733b62231SArnaldo Carvalho de Melo score += 2; 14833b62231SArnaldo Carvalho de Melo } 14933b62231SArnaldo Carvalho de Melo if (sk->sk_bound_dev_if) { 15033b62231SArnaldo Carvalho de Melo if (sk->sk_bound_dev_if != dif) 15133b62231SArnaldo Carvalho de Melo continue; 15233b62231SArnaldo Carvalho de Melo score += 2; 15333b62231SArnaldo Carvalho de Melo } 15433b62231SArnaldo Carvalho de Melo if (score == 5) 15533b62231SArnaldo Carvalho de Melo return sk; 15633b62231SArnaldo Carvalho de Melo if (score > hiscore) { 15733b62231SArnaldo Carvalho de Melo hiscore = score; 15833b62231SArnaldo Carvalho de Melo result = sk; 15933b62231SArnaldo Carvalho de Melo } 16033b62231SArnaldo Carvalho de Melo } 16133b62231SArnaldo Carvalho de Melo } 16233b62231SArnaldo Carvalho de Melo return result; 16333b62231SArnaldo Carvalho de Melo } 164e48c414eSArnaldo Carvalho de Melo 16599a92ff5SHerbert Xu /* Optimize the common listener case. */ 1668f491069SHerbert Xu struct sock *__inet_lookup_listener(struct inet_hashinfo *hashinfo, 167fb99c848SAl Viro const __be32 daddr, const unsigned short hnum, 16899a92ff5SHerbert Xu const int dif) 16999a92ff5SHerbert Xu { 17099a92ff5SHerbert Xu struct sock *sk = NULL; 17199a92ff5SHerbert Xu const struct hlist_head *head; 17299a92ff5SHerbert Xu 17399a92ff5SHerbert Xu read_lock(&hashinfo->lhash_lock); 17499a92ff5SHerbert Xu head = &hashinfo->listening_hash[inet_lhashfn(hnum)]; 17599a92ff5SHerbert Xu if (!hlist_empty(head)) { 17699a92ff5SHerbert Xu const struct inet_sock *inet = inet_sk((sk = __sk_head(head))); 17799a92ff5SHerbert Xu 17899a92ff5SHerbert Xu if (inet->num == hnum && !sk->sk_node.next && 17999a92ff5SHerbert Xu (!inet->rcv_saddr || inet->rcv_saddr == daddr) && 18099a92ff5SHerbert Xu (sk->sk_family == PF_INET || !ipv6_only_sock(sk)) && 18199a92ff5SHerbert Xu !sk->sk_bound_dev_if) 18299a92ff5SHerbert Xu goto sherry_cache; 1838f491069SHerbert Xu sk = inet_lookup_listener_slow(head, daddr, hnum, dif); 18499a92ff5SHerbert Xu } 18599a92ff5SHerbert Xu if (sk) { 18699a92ff5SHerbert Xu sherry_cache: 18799a92ff5SHerbert Xu sock_hold(sk); 18899a92ff5SHerbert Xu } 18999a92ff5SHerbert Xu read_unlock(&hashinfo->lhash_lock); 19099a92ff5SHerbert Xu return sk; 19199a92ff5SHerbert Xu } 1928f491069SHerbert Xu EXPORT_SYMBOL_GPL(__inet_lookup_listener); 193a7f5e7f1SArnaldo Carvalho de Melo 19477a5ba55SPavel Emelyanov struct sock * __inet_lookup_established(struct inet_hashinfo *hashinfo, 19577a5ba55SPavel Emelyanov const __be32 saddr, const __be16 sport, 19677a5ba55SPavel Emelyanov const __be32 daddr, const u16 hnum, 19777a5ba55SPavel Emelyanov const int dif) 19877a5ba55SPavel Emelyanov { 19977a5ba55SPavel Emelyanov INET_ADDR_COOKIE(acookie, saddr, daddr) 20077a5ba55SPavel Emelyanov const __portpair ports = INET_COMBINED_PORTS(sport, hnum); 20177a5ba55SPavel Emelyanov struct sock *sk; 20277a5ba55SPavel Emelyanov const struct hlist_node *node; 20377a5ba55SPavel Emelyanov /* Optimize here for direct hit, only listening connections can 20477a5ba55SPavel Emelyanov * have wildcards anyways. 20577a5ba55SPavel Emelyanov */ 20677a5ba55SPavel Emelyanov unsigned int hash = inet_ehashfn(daddr, hnum, saddr, sport); 20777a5ba55SPavel Emelyanov struct inet_ehash_bucket *head = inet_ehash_bucket(hashinfo, hash); 20877a5ba55SPavel Emelyanov rwlock_t *lock = inet_ehash_lockp(hashinfo, hash); 20977a5ba55SPavel Emelyanov 21077a5ba55SPavel Emelyanov prefetch(head->chain.first); 21177a5ba55SPavel Emelyanov read_lock(lock); 21277a5ba55SPavel Emelyanov sk_for_each(sk, node, &head->chain) { 21377a5ba55SPavel Emelyanov if (INET_MATCH(sk, hash, acookie, saddr, daddr, ports, dif)) 21477a5ba55SPavel Emelyanov goto hit; /* You sunk my battleship! */ 21577a5ba55SPavel Emelyanov } 21677a5ba55SPavel Emelyanov 21777a5ba55SPavel Emelyanov /* Must check for a TIME_WAIT'er before going to listener hash. */ 21877a5ba55SPavel Emelyanov sk_for_each(sk, node, &head->twchain) { 21977a5ba55SPavel Emelyanov if (INET_TW_MATCH(sk, hash, acookie, saddr, daddr, ports, dif)) 22077a5ba55SPavel Emelyanov goto hit; 22177a5ba55SPavel Emelyanov } 22277a5ba55SPavel Emelyanov sk = NULL; 22377a5ba55SPavel Emelyanov out: 22477a5ba55SPavel Emelyanov read_unlock(lock); 22577a5ba55SPavel Emelyanov return sk; 22677a5ba55SPavel Emelyanov hit: 22777a5ba55SPavel Emelyanov sock_hold(sk); 22877a5ba55SPavel Emelyanov goto out; 22977a5ba55SPavel Emelyanov } 23077a5ba55SPavel Emelyanov EXPORT_SYMBOL_GPL(__inet_lookup_established); 23177a5ba55SPavel Emelyanov 232a7f5e7f1SArnaldo Carvalho de Melo /* called with local bh disabled */ 233a7f5e7f1SArnaldo Carvalho de Melo static int __inet_check_established(struct inet_timewait_death_row *death_row, 234a7f5e7f1SArnaldo Carvalho de Melo struct sock *sk, __u16 lport, 235a7f5e7f1SArnaldo Carvalho de Melo struct inet_timewait_sock **twp) 236a7f5e7f1SArnaldo Carvalho de Melo { 237a7f5e7f1SArnaldo Carvalho de Melo struct inet_hashinfo *hinfo = death_row->hashinfo; 238a7f5e7f1SArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 239fb99c848SAl Viro __be32 daddr = inet->rcv_saddr; 240fb99c848SAl Viro __be32 saddr = inet->daddr; 241a7f5e7f1SArnaldo Carvalho de Melo int dif = sk->sk_bound_dev_if; 242a7f5e7f1SArnaldo Carvalho de Melo INET_ADDR_COOKIE(acookie, saddr, daddr) 2434f765d84SAl Viro const __portpair ports = INET_COMBINED_PORTS(inet->dport, lport); 244a7f5e7f1SArnaldo Carvalho de Melo unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport); 245a7f5e7f1SArnaldo Carvalho de Melo struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash); 246230140cfSEric Dumazet rwlock_t *lock = inet_ehash_lockp(hinfo, hash); 247a7f5e7f1SArnaldo Carvalho de Melo struct sock *sk2; 248a7f5e7f1SArnaldo Carvalho de Melo const struct hlist_node *node; 249a7f5e7f1SArnaldo Carvalho de Melo struct inet_timewait_sock *tw; 250a7f5e7f1SArnaldo Carvalho de Melo 251a7f5e7f1SArnaldo Carvalho de Melo prefetch(head->chain.first); 252230140cfSEric Dumazet write_lock(lock); 253a7f5e7f1SArnaldo Carvalho de Melo 254a7f5e7f1SArnaldo Carvalho de Melo /* Check TIME-WAIT sockets first. */ 255dbca9b27SEric Dumazet sk_for_each(sk2, node, &head->twchain) { 256a7f5e7f1SArnaldo Carvalho de Melo tw = inet_twsk(sk2); 257a7f5e7f1SArnaldo Carvalho de Melo 258a7f5e7f1SArnaldo Carvalho de Melo if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) { 259a7f5e7f1SArnaldo Carvalho de Melo if (twsk_unique(sk, sk2, twp)) 260a7f5e7f1SArnaldo Carvalho de Melo goto unique; 261a7f5e7f1SArnaldo Carvalho de Melo else 262a7f5e7f1SArnaldo Carvalho de Melo goto not_unique; 263a7f5e7f1SArnaldo Carvalho de Melo } 264a7f5e7f1SArnaldo Carvalho de Melo } 265a7f5e7f1SArnaldo Carvalho de Melo tw = NULL; 266a7f5e7f1SArnaldo Carvalho de Melo 267a7f5e7f1SArnaldo Carvalho de Melo /* And established part... */ 268a7f5e7f1SArnaldo Carvalho de Melo sk_for_each(sk2, node, &head->chain) { 269a7f5e7f1SArnaldo Carvalho de Melo if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) 270a7f5e7f1SArnaldo Carvalho de Melo goto not_unique; 271a7f5e7f1SArnaldo Carvalho de Melo } 272a7f5e7f1SArnaldo Carvalho de Melo 273a7f5e7f1SArnaldo Carvalho de Melo unique: 274a7f5e7f1SArnaldo Carvalho de Melo /* Must record num and sport now. Otherwise we will see 275a7f5e7f1SArnaldo Carvalho de Melo * in hash table socket with a funny identity. */ 276a7f5e7f1SArnaldo Carvalho de Melo inet->num = lport; 277a7f5e7f1SArnaldo Carvalho de Melo inet->sport = htons(lport); 278a7f5e7f1SArnaldo Carvalho de Melo sk->sk_hash = hash; 279a7f5e7f1SArnaldo Carvalho de Melo BUG_TRAP(sk_unhashed(sk)); 280a7f5e7f1SArnaldo Carvalho de Melo __sk_add_node(sk, &head->chain); 281a7f5e7f1SArnaldo Carvalho de Melo sock_prot_inc_use(sk->sk_prot); 282230140cfSEric Dumazet write_unlock(lock); 283a7f5e7f1SArnaldo Carvalho de Melo 284a7f5e7f1SArnaldo Carvalho de Melo if (twp) { 285a7f5e7f1SArnaldo Carvalho de Melo *twp = tw; 286a7f5e7f1SArnaldo Carvalho de Melo NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED); 287a7f5e7f1SArnaldo Carvalho de Melo } else if (tw) { 288a7f5e7f1SArnaldo Carvalho de Melo /* Silly. Should hash-dance instead... */ 289a7f5e7f1SArnaldo Carvalho de Melo inet_twsk_deschedule(tw, death_row); 290a7f5e7f1SArnaldo Carvalho de Melo NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED); 291a7f5e7f1SArnaldo Carvalho de Melo 292a7f5e7f1SArnaldo Carvalho de Melo inet_twsk_put(tw); 293a7f5e7f1SArnaldo Carvalho de Melo } 294a7f5e7f1SArnaldo Carvalho de Melo 295a7f5e7f1SArnaldo Carvalho de Melo return 0; 296a7f5e7f1SArnaldo Carvalho de Melo 297a7f5e7f1SArnaldo Carvalho de Melo not_unique: 298230140cfSEric Dumazet write_unlock(lock); 299a7f5e7f1SArnaldo Carvalho de Melo return -EADDRNOTAVAIL; 300a7f5e7f1SArnaldo Carvalho de Melo } 301a7f5e7f1SArnaldo Carvalho de Melo 302a7f5e7f1SArnaldo Carvalho de Melo static inline u32 inet_sk_port_offset(const struct sock *sk) 303a7f5e7f1SArnaldo Carvalho de Melo { 304a7f5e7f1SArnaldo Carvalho de Melo const struct inet_sock *inet = inet_sk(sk); 305a7f5e7f1SArnaldo Carvalho de Melo return secure_ipv4_port_ephemeral(inet->rcv_saddr, inet->daddr, 306a7f5e7f1SArnaldo Carvalho de Melo inet->dport); 307a7f5e7f1SArnaldo Carvalho de Melo } 308a7f5e7f1SArnaldo Carvalho de Melo 309152da81dSPavel Emelyanov void __inet_hash_nolisten(struct inet_hashinfo *hashinfo, struct sock *sk) 310152da81dSPavel Emelyanov { 311152da81dSPavel Emelyanov struct hlist_head *list; 312152da81dSPavel Emelyanov rwlock_t *lock; 313152da81dSPavel Emelyanov struct inet_ehash_bucket *head; 314152da81dSPavel Emelyanov 315152da81dSPavel Emelyanov BUG_TRAP(sk_unhashed(sk)); 316152da81dSPavel Emelyanov 317152da81dSPavel Emelyanov sk->sk_hash = inet_sk_ehashfn(sk); 318152da81dSPavel Emelyanov head = inet_ehash_bucket(hashinfo, sk->sk_hash); 319152da81dSPavel Emelyanov list = &head->chain; 320152da81dSPavel Emelyanov lock = inet_ehash_lockp(hashinfo, sk->sk_hash); 321152da81dSPavel Emelyanov 322152da81dSPavel Emelyanov write_lock(lock); 323152da81dSPavel Emelyanov __sk_add_node(sk, list); 324152da81dSPavel Emelyanov sock_prot_inc_use(sk->sk_prot); 325152da81dSPavel Emelyanov write_unlock(lock); 326152da81dSPavel Emelyanov } 327152da81dSPavel Emelyanov EXPORT_SYMBOL_GPL(__inet_hash_nolisten); 328152da81dSPavel Emelyanov 329152da81dSPavel Emelyanov void __inet_hash(struct inet_hashinfo *hashinfo, struct sock *sk) 330152da81dSPavel Emelyanov { 331152da81dSPavel Emelyanov struct hlist_head *list; 332152da81dSPavel Emelyanov rwlock_t *lock; 333152da81dSPavel Emelyanov 334152da81dSPavel Emelyanov if (sk->sk_state != TCP_LISTEN) { 335152da81dSPavel Emelyanov __inet_hash_nolisten(hashinfo, sk); 336152da81dSPavel Emelyanov return; 337152da81dSPavel Emelyanov } 338152da81dSPavel Emelyanov 339152da81dSPavel Emelyanov BUG_TRAP(sk_unhashed(sk)); 340152da81dSPavel Emelyanov list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)]; 341152da81dSPavel Emelyanov lock = &hashinfo->lhash_lock; 342152da81dSPavel Emelyanov 343152da81dSPavel Emelyanov inet_listen_wlock(hashinfo); 344152da81dSPavel Emelyanov __sk_add_node(sk, list); 345152da81dSPavel Emelyanov sock_prot_inc_use(sk->sk_prot); 346152da81dSPavel Emelyanov write_unlock(lock); 347152da81dSPavel Emelyanov wake_up(&hashinfo->lhash_wait); 348152da81dSPavel Emelyanov } 349152da81dSPavel Emelyanov EXPORT_SYMBOL_GPL(__inet_hash); 350152da81dSPavel Emelyanov 351a7f5e7f1SArnaldo Carvalho de Melo /* 352a7f5e7f1SArnaldo Carvalho de Melo * Bind a port for a connect operation and hash it. 353a7f5e7f1SArnaldo Carvalho de Melo */ 354a7f5e7f1SArnaldo Carvalho de Melo int inet_hash_connect(struct inet_timewait_death_row *death_row, 355a7f5e7f1SArnaldo Carvalho de Melo struct sock *sk) 356a7f5e7f1SArnaldo Carvalho de Melo { 357a7f5e7f1SArnaldo Carvalho de Melo struct inet_hashinfo *hinfo = death_row->hashinfo; 358a7f5e7f1SArnaldo Carvalho de Melo const unsigned short snum = inet_sk(sk)->num; 359a7f5e7f1SArnaldo Carvalho de Melo struct inet_bind_hashbucket *head; 360a7f5e7f1SArnaldo Carvalho de Melo struct inet_bind_bucket *tb; 361a7f5e7f1SArnaldo Carvalho de Melo int ret; 362a7f5e7f1SArnaldo Carvalho de Melo 363a7f5e7f1SArnaldo Carvalho de Melo if (!snum) { 364227b60f5SStephen Hemminger int i, remaining, low, high, port; 365a7f5e7f1SArnaldo Carvalho de Melo static u32 hint; 366a7f5e7f1SArnaldo Carvalho de Melo u32 offset = hint + inet_sk_port_offset(sk); 367a7f5e7f1SArnaldo Carvalho de Melo struct hlist_node *node; 368a7f5e7f1SArnaldo Carvalho de Melo struct inet_timewait_sock *tw = NULL; 369a7f5e7f1SArnaldo Carvalho de Melo 370227b60f5SStephen Hemminger inet_get_local_port_range(&low, &high); 371a25de534SAnton Arapov remaining = (high - low) + 1; 372227b60f5SStephen Hemminger 373a7f5e7f1SArnaldo Carvalho de Melo local_bh_disable(); 374227b60f5SStephen Hemminger for (i = 1; i <= remaining; i++) { 375227b60f5SStephen Hemminger port = low + (i + offset) % remaining; 376a7f5e7f1SArnaldo Carvalho de Melo head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)]; 377a7f5e7f1SArnaldo Carvalho de Melo spin_lock(&head->lock); 378a7f5e7f1SArnaldo Carvalho de Melo 379a7f5e7f1SArnaldo Carvalho de Melo /* Does not bother with rcv_saddr checks, 380a7f5e7f1SArnaldo Carvalho de Melo * because the established check is already 381a7f5e7f1SArnaldo Carvalho de Melo * unique enough. 382a7f5e7f1SArnaldo Carvalho de Melo */ 383a7f5e7f1SArnaldo Carvalho de Melo inet_bind_bucket_for_each(tb, node, &head->chain) { 384a7f5e7f1SArnaldo Carvalho de Melo if (tb->port == port) { 385a7f5e7f1SArnaldo Carvalho de Melo BUG_TRAP(!hlist_empty(&tb->owners)); 386a7f5e7f1SArnaldo Carvalho de Melo if (tb->fastreuse >= 0) 387a7f5e7f1SArnaldo Carvalho de Melo goto next_port; 388a7f5e7f1SArnaldo Carvalho de Melo if (!__inet_check_established(death_row, 389a7f5e7f1SArnaldo Carvalho de Melo sk, port, 390a7f5e7f1SArnaldo Carvalho de Melo &tw)) 391a7f5e7f1SArnaldo Carvalho de Melo goto ok; 392a7f5e7f1SArnaldo Carvalho de Melo goto next_port; 393a7f5e7f1SArnaldo Carvalho de Melo } 394a7f5e7f1SArnaldo Carvalho de Melo } 395a7f5e7f1SArnaldo Carvalho de Melo 396a7f5e7f1SArnaldo Carvalho de Melo tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep, head, port); 397a7f5e7f1SArnaldo Carvalho de Melo if (!tb) { 398a7f5e7f1SArnaldo Carvalho de Melo spin_unlock(&head->lock); 399a7f5e7f1SArnaldo Carvalho de Melo break; 400a7f5e7f1SArnaldo Carvalho de Melo } 401a7f5e7f1SArnaldo Carvalho de Melo tb->fastreuse = -1; 402a7f5e7f1SArnaldo Carvalho de Melo goto ok; 403a7f5e7f1SArnaldo Carvalho de Melo 404a7f5e7f1SArnaldo Carvalho de Melo next_port: 405a7f5e7f1SArnaldo Carvalho de Melo spin_unlock(&head->lock); 406a7f5e7f1SArnaldo Carvalho de Melo } 407a7f5e7f1SArnaldo Carvalho de Melo local_bh_enable(); 408a7f5e7f1SArnaldo Carvalho de Melo 409a7f5e7f1SArnaldo Carvalho de Melo return -EADDRNOTAVAIL; 410a7f5e7f1SArnaldo Carvalho de Melo 411a7f5e7f1SArnaldo Carvalho de Melo ok: 412a7f5e7f1SArnaldo Carvalho de Melo hint += i; 413a7f5e7f1SArnaldo Carvalho de Melo 414a7f5e7f1SArnaldo Carvalho de Melo /* Head lock still held and bh's disabled */ 415a7f5e7f1SArnaldo Carvalho de Melo inet_bind_hash(sk, tb, port); 416a7f5e7f1SArnaldo Carvalho de Melo if (sk_unhashed(sk)) { 417a7f5e7f1SArnaldo Carvalho de Melo inet_sk(sk)->sport = htons(port); 418152da81dSPavel Emelyanov __inet_hash_nolisten(hinfo, sk); 419a7f5e7f1SArnaldo Carvalho de Melo } 420a7f5e7f1SArnaldo Carvalho de Melo spin_unlock(&head->lock); 421a7f5e7f1SArnaldo Carvalho de Melo 422a7f5e7f1SArnaldo Carvalho de Melo if (tw) { 42353b3531bSAlexey Dobriyan inet_twsk_deschedule(tw, death_row); 424a7f5e7f1SArnaldo Carvalho de Melo inet_twsk_put(tw); 425a7f5e7f1SArnaldo Carvalho de Melo } 426a7f5e7f1SArnaldo Carvalho de Melo 427a7f5e7f1SArnaldo Carvalho de Melo ret = 0; 428a7f5e7f1SArnaldo Carvalho de Melo goto out; 429a7f5e7f1SArnaldo Carvalho de Melo } 430a7f5e7f1SArnaldo Carvalho de Melo 431a7f5e7f1SArnaldo Carvalho de Melo head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)]; 432a7f5e7f1SArnaldo Carvalho de Melo tb = inet_csk(sk)->icsk_bind_hash; 433a7f5e7f1SArnaldo Carvalho de Melo spin_lock_bh(&head->lock); 434a7f5e7f1SArnaldo Carvalho de Melo if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) { 435152da81dSPavel Emelyanov __inet_hash_nolisten(hinfo, sk); 436a7f5e7f1SArnaldo Carvalho de Melo spin_unlock_bh(&head->lock); 437a7f5e7f1SArnaldo Carvalho de Melo return 0; 438a7f5e7f1SArnaldo Carvalho de Melo } else { 439a7f5e7f1SArnaldo Carvalho de Melo spin_unlock(&head->lock); 440a7f5e7f1SArnaldo Carvalho de Melo /* No definite answer... Walk to established hash table */ 441a7f5e7f1SArnaldo Carvalho de Melo ret = __inet_check_established(death_row, sk, snum, NULL); 442a7f5e7f1SArnaldo Carvalho de Melo out: 443a7f5e7f1SArnaldo Carvalho de Melo local_bh_enable(); 444a7f5e7f1SArnaldo Carvalho de Melo return ret; 445a7f5e7f1SArnaldo Carvalho de Melo } 446a7f5e7f1SArnaldo Carvalho de Melo } 447a7f5e7f1SArnaldo Carvalho de Melo 448a7f5e7f1SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(inet_hash_connect); 449