1e48c414eSArnaldo Carvalho de Melo /* 2e48c414eSArnaldo Carvalho de Melo * INET An implementation of the TCP/IP protocol suite for the LINUX 3e48c414eSArnaldo Carvalho de Melo * operating system. INET is implemented using the BSD Socket 4e48c414eSArnaldo Carvalho de Melo * interface as the means of communication with the user level. 5e48c414eSArnaldo Carvalho de Melo * 6e48c414eSArnaldo Carvalho de Melo * Generic TIME_WAIT sockets functions 7e48c414eSArnaldo Carvalho de Melo * 8e48c414eSArnaldo Carvalho de Melo * From code orinally in TCP 9e48c414eSArnaldo Carvalho de Melo */ 10e48c414eSArnaldo Carvalho de Melo 11e48c414eSArnaldo Carvalho de Melo #include <linux/config.h> 12e48c414eSArnaldo Carvalho de Melo 13e48c414eSArnaldo Carvalho de Melo #include <net/inet_hashtables.h> 14e48c414eSArnaldo Carvalho de Melo #include <net/inet_timewait_sock.h> 15e48c414eSArnaldo Carvalho de Melo 16e48c414eSArnaldo Carvalho de Melo /* Must be called with locally disabled BHs. */ 17e48c414eSArnaldo Carvalho de Melo void __inet_twsk_kill(struct inet_timewait_sock *tw, struct inet_hashinfo *hashinfo) 18e48c414eSArnaldo Carvalho de Melo { 19e48c414eSArnaldo Carvalho de Melo struct inet_bind_hashbucket *bhead; 20e48c414eSArnaldo Carvalho de Melo struct inet_bind_bucket *tb; 21e48c414eSArnaldo Carvalho de Melo /* Unlink from established hashes. */ 22e48c414eSArnaldo Carvalho de Melo struct inet_ehash_bucket *ehead = &hashinfo->ehash[tw->tw_hashent]; 23e48c414eSArnaldo Carvalho de Melo 24e48c414eSArnaldo Carvalho de Melo write_lock(&ehead->lock); 25e48c414eSArnaldo Carvalho de Melo if (hlist_unhashed(&tw->tw_node)) { 26e48c414eSArnaldo Carvalho de Melo write_unlock(&ehead->lock); 27e48c414eSArnaldo Carvalho de Melo return; 28e48c414eSArnaldo Carvalho de Melo } 29e48c414eSArnaldo Carvalho de Melo __hlist_del(&tw->tw_node); 30e48c414eSArnaldo Carvalho de Melo sk_node_init(&tw->tw_node); 31e48c414eSArnaldo Carvalho de Melo write_unlock(&ehead->lock); 32e48c414eSArnaldo Carvalho de Melo 33e48c414eSArnaldo Carvalho de Melo /* Disassociate with bind bucket. */ 34e48c414eSArnaldo Carvalho de Melo bhead = &hashinfo->bhash[inet_bhashfn(tw->tw_num, hashinfo->bhash_size)]; 35e48c414eSArnaldo Carvalho de Melo spin_lock(&bhead->lock); 36e48c414eSArnaldo Carvalho de Melo tb = tw->tw_tb; 37e48c414eSArnaldo Carvalho de Melo __hlist_del(&tw->tw_bind_node); 38e48c414eSArnaldo Carvalho de Melo tw->tw_tb = NULL; 39e48c414eSArnaldo Carvalho de Melo inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb); 40e48c414eSArnaldo Carvalho de Melo spin_unlock(&bhead->lock); 41e48c414eSArnaldo Carvalho de Melo #ifdef SOCK_REFCNT_DEBUG 42e48c414eSArnaldo Carvalho de Melo if (atomic_read(&tw->tw_refcnt) != 1) { 43e48c414eSArnaldo Carvalho de Melo printk(KERN_DEBUG "%s timewait_sock %p refcnt=%d\n", 44e48c414eSArnaldo Carvalho de Melo tw->tw_prot->name, tw, atomic_read(&tw->tw_refcnt)); 45e48c414eSArnaldo Carvalho de Melo } 46e48c414eSArnaldo Carvalho de Melo #endif 47e48c414eSArnaldo Carvalho de Melo inet_twsk_put(tw); 48e48c414eSArnaldo Carvalho de Melo } 49e48c414eSArnaldo Carvalho de Melo 50e48c414eSArnaldo Carvalho de Melo /* 51e48c414eSArnaldo Carvalho de Melo * Enter the time wait state. This is called with locally disabled BH. 52e48c414eSArnaldo Carvalho de Melo * Essentially we whip up a timewait bucket, copy the relevant info into it 53e48c414eSArnaldo Carvalho de Melo * from the SK, and mess with hash chains and list linkage. 54e48c414eSArnaldo Carvalho de Melo */ 55e48c414eSArnaldo Carvalho de Melo void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk, 56e48c414eSArnaldo Carvalho de Melo struct inet_hashinfo *hashinfo) 57e48c414eSArnaldo Carvalho de Melo { 58e48c414eSArnaldo Carvalho de Melo const struct inet_sock *inet = inet_sk(sk); 59e48c414eSArnaldo Carvalho de Melo struct inet_ehash_bucket *ehead = &hashinfo->ehash[sk->sk_hashent]; 60e48c414eSArnaldo Carvalho de Melo struct inet_bind_hashbucket *bhead; 61e48c414eSArnaldo Carvalho de Melo /* Step 1: Put TW into bind hash. Original socket stays there too. 62e48c414eSArnaldo Carvalho de Melo Note, that any socket with inet->num != 0 MUST be bound in 63e48c414eSArnaldo Carvalho de Melo binding cache, even if it is closed. 64e48c414eSArnaldo Carvalho de Melo */ 65e48c414eSArnaldo Carvalho de Melo bhead = &hashinfo->bhash[inet_bhashfn(inet->num, hashinfo->bhash_size)]; 66e48c414eSArnaldo Carvalho de Melo spin_lock(&bhead->lock); 67e48c414eSArnaldo Carvalho de Melo tw->tw_tb = inet->bind_hash; 68e48c414eSArnaldo Carvalho de Melo BUG_TRAP(inet->bind_hash); 69e48c414eSArnaldo Carvalho de Melo inet_twsk_add_bind_node(tw, &tw->tw_tb->owners); 70e48c414eSArnaldo Carvalho de Melo spin_unlock(&bhead->lock); 71e48c414eSArnaldo Carvalho de Melo 72e48c414eSArnaldo Carvalho de Melo write_lock(&ehead->lock); 73e48c414eSArnaldo Carvalho de Melo 74e48c414eSArnaldo Carvalho de Melo /* Step 2: Remove SK from established hash. */ 75e48c414eSArnaldo Carvalho de Melo if (__sk_del_node_init(sk)) 76e48c414eSArnaldo Carvalho de Melo sock_prot_dec_use(sk->sk_prot); 77e48c414eSArnaldo Carvalho de Melo 78e48c414eSArnaldo Carvalho de Melo /* Step 3: Hash TW into TIMEWAIT half of established hash table. */ 79e48c414eSArnaldo Carvalho de Melo inet_twsk_add_node(tw, &(ehead + hashinfo->ehash_size)->chain); 80e48c414eSArnaldo Carvalho de Melo atomic_inc(&tw->tw_refcnt); 81e48c414eSArnaldo Carvalho de Melo 82e48c414eSArnaldo Carvalho de Melo write_unlock(&ehead->lock); 83e48c414eSArnaldo Carvalho de Melo } 84*c676270bSArnaldo Carvalho de Melo 85*c676270bSArnaldo Carvalho de Melo struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int state) 86*c676270bSArnaldo Carvalho de Melo { 87*c676270bSArnaldo Carvalho de Melo struct inet_timewait_sock *tw = kmem_cache_alloc(sk->sk_prot_creator->twsk_slab, 88*c676270bSArnaldo Carvalho de Melo SLAB_ATOMIC); 89*c676270bSArnaldo Carvalho de Melo if (tw != NULL) { 90*c676270bSArnaldo Carvalho de Melo const struct inet_sock *inet = inet_sk(sk); 91*c676270bSArnaldo Carvalho de Melo 92*c676270bSArnaldo Carvalho de Melo /* Give us an identity. */ 93*c676270bSArnaldo Carvalho de Melo tw->tw_daddr = inet->daddr; 94*c676270bSArnaldo Carvalho de Melo tw->tw_rcv_saddr = inet->rcv_saddr; 95*c676270bSArnaldo Carvalho de Melo tw->tw_bound_dev_if = sk->sk_bound_dev_if; 96*c676270bSArnaldo Carvalho de Melo tw->tw_num = inet->num; 97*c676270bSArnaldo Carvalho de Melo tw->tw_state = TCP_TIME_WAIT; 98*c676270bSArnaldo Carvalho de Melo tw->tw_substate = state; 99*c676270bSArnaldo Carvalho de Melo tw->tw_sport = inet->sport; 100*c676270bSArnaldo Carvalho de Melo tw->tw_dport = inet->dport; 101*c676270bSArnaldo Carvalho de Melo tw->tw_family = sk->sk_family; 102*c676270bSArnaldo Carvalho de Melo tw->tw_reuse = sk->sk_reuse; 103*c676270bSArnaldo Carvalho de Melo tw->tw_hashent = sk->sk_hashent; 104*c676270bSArnaldo Carvalho de Melo tw->tw_ipv6only = 0; 105*c676270bSArnaldo Carvalho de Melo tw->tw_prot = sk->sk_prot_creator; 106*c676270bSArnaldo Carvalho de Melo atomic_set(&tw->tw_refcnt, 1); 107*c676270bSArnaldo Carvalho de Melo inet_twsk_dead_node_init(tw); 108*c676270bSArnaldo Carvalho de Melo } 109*c676270bSArnaldo Carvalho de Melo 110*c676270bSArnaldo Carvalho de Melo return tw; 111*c676270bSArnaldo Carvalho de Melo } 112