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