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