[NET]: Introduce inet_connection_sock
[linux-2.6] / net / ipv4 / inet_timewait_sock.c
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. */
17 void __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  */
55 void __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
86 struct 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 }