xref: /linux/net/ipv4/inet_timewait_sock.c (revision c676270bcd25015b978722ec0352c330dcc87883)
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