e498e389fcccb21670e5cc87621eff46136280b7
[pandora-kernel.git] / net / dccp / minisocks.c
1 /*
2  *  net/dccp/minisocks.c
3  *
4  *  An implementation of the DCCP protocol
5  *  Arnaldo Carvalho de Melo <acme@conectiva.com.br>
6  *
7  *      This program is free software; you can redistribute it and/or
8  *      modify it under the terms of the GNU General Public License
9  *      as published by the Free Software Foundation; either version
10  *      2 of the License, or (at your option) any later version.
11  */
12
13 #include <linux/config.h>
14 #include <linux/dccp.h>
15 #include <linux/skbuff.h>
16 #include <linux/timer.h>
17
18 #include <net/sock.h>
19 #include <net/xfrm.h>
20 #include <net/inet_timewait_sock.h>
21
22 #include "ccid.h"
23 #include "dccp.h"
24
25 void dccp_time_wait(struct sock *sk, int state, int timeo)
26 {
27         /* FIXME: Implement */
28         dccp_pr_debug("Want to help? Start here\n");
29         dccp_set_state(sk, state);
30 }
31
32 /* This is for handling early-kills of TIME_WAIT sockets. */
33 void dccp_tw_deschedule(struct inet_timewait_sock *tw)
34 {
35         dccp_pr_debug("Want to help? Start here\n");
36         __inet_twsk_kill(tw, &dccp_hashinfo);
37 }
38
39 struct sock *dccp_create_openreq_child(struct sock *sk,
40                                        const struct request_sock *req,
41                                        const struct sk_buff *skb)
42 {
43         /*
44          * Step 3: Process LISTEN state
45          *
46          * // Generate a new socket and switch to that socket
47          * Set S := new socket for this port pair
48          */
49         struct sock *newsk = inet_csk_clone(sk, req, GFP_ATOMIC);
50
51         if (newsk != NULL) {
52                 const struct dccp_request_sock *dreq = dccp_rsk(req);
53                 struct inet_connection_sock *newicsk = inet_csk(sk);
54                 struct dccp_sock *newdp = dccp_sk(newsk);
55
56                 newdp->dccps_hc_rx_ackpkts = NULL;
57                 newdp->dccps_role = DCCP_ROLE_SERVER;
58                 newicsk->icsk_rto = TCP_TIMEOUT_INIT;
59
60                 if (newdp->dccps_options.dccpo_send_ack_vector) {
61                         newdp->dccps_hc_rx_ackpkts = dccp_ackpkts_alloc(DCCP_MAX_ACK_VECTOR_LEN,
62                                                                         GFP_ATOMIC);
63                         /*
64                          * XXX: We're using the same CCIDs set on the parent, i.e. sk_clone
65                          * copied the master sock and left the CCID pointers for this child,
66                          * that is why we do the __ccid_get calls.
67                          */
68                         if (unlikely(newdp->dccps_hc_rx_ackpkts == NULL))
69                                 goto out_free;
70                 }
71
72                 if (unlikely(ccid_hc_rx_init(newdp->dccps_hc_rx_ccid, newsk) != 0 ||
73                              ccid_hc_tx_init(newdp->dccps_hc_tx_ccid, newsk) != 0)) {
74                         dccp_ackpkts_free(newdp->dccps_hc_rx_ackpkts);
75                         ccid_hc_rx_exit(newdp->dccps_hc_rx_ccid, newsk);
76                         ccid_hc_tx_exit(newdp->dccps_hc_tx_ccid, newsk);
77 out_free:
78                         /* It is still raw copy of parent, so invalidate
79                          * destructor and make plain sk_free() */
80                         newsk->sk_destruct = NULL;
81                         sk_free(newsk);
82                         return NULL;
83                 }
84
85                 __ccid_get(newdp->dccps_hc_rx_ccid);
86                 __ccid_get(newdp->dccps_hc_tx_ccid);
87
88                 /*
89                  * Step 3: Process LISTEN state
90                  *
91                  *      Choose S.ISS (initial seqno) or set from Init Cookie
92                  *      Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie
93                  */
94
95                 /* See dccp_v4_conn_request */
96                 newdp->dccps_options.dccpo_sequence_window = req->rcv_wnd;
97
98                 newdp->dccps_gar = newdp->dccps_isr = dreq->dreq_isr;
99                 dccp_update_gsr(newsk, dreq->dreq_isr);
100
101                 newdp->dccps_iss = dreq->dreq_iss;
102                 dccp_update_gss(newsk, dreq->dreq_iss);
103
104                 dccp_init_xmit_timers(newsk);
105
106                 DCCP_INC_STATS_BH(DCCP_MIB_PASSIVEOPENS);
107         }
108         return newsk;
109 }
110
111 /* 
112  * Process an incoming packet for RESPOND sockets represented
113  * as an request_sock.
114  */
115 struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
116                             struct request_sock *req,
117                             struct request_sock **prev)
118 {
119         struct sock *child = NULL;
120
121         /* Check for retransmitted REQUEST */
122         if (dccp_hdr(skb)->dccph_type == DCCP_PKT_REQUEST) {
123                 if (after48(DCCP_SKB_CB(skb)->dccpd_seq, dccp_rsk(req)->dreq_isr)) {
124                         struct dccp_request_sock *dreq = dccp_rsk(req);
125
126                         dccp_pr_debug("Retransmitted REQUEST\n");
127                         /* Send another RESPONSE packet */
128                         dccp_set_seqno(&dreq->dreq_iss, dreq->dreq_iss + 1);
129                         dccp_set_seqno(&dreq->dreq_isr, DCCP_SKB_CB(skb)->dccpd_seq);
130                         req->rsk_ops->rtx_syn_ack(sk, req, NULL);
131                 }
132                 /* Network Duplicate, discard packet */
133                 return NULL;
134         }
135
136         DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_PACKET_ERROR;
137
138         if (dccp_hdr(skb)->dccph_type != DCCP_PKT_ACK &&
139             dccp_hdr(skb)->dccph_type != DCCP_PKT_DATAACK)
140                 goto drop;
141
142         /* Invalid ACK */
143         if (DCCP_SKB_CB(skb)->dccpd_ack_seq != dccp_rsk(req)->dreq_iss) {
144                 dccp_pr_debug("Invalid ACK number: ack_seq=%llu, dreq_iss=%llu\n",
145                               (unsigned long long)
146                               DCCP_SKB_CB(skb)->dccpd_ack_seq,
147                               (unsigned long long)
148                               dccp_rsk(req)->dreq_iss);
149                 goto drop;
150         }
151
152         child = dccp_v4_request_recv_sock(sk, skb, req, NULL);
153         if (child == NULL)
154                 goto listen_overflow;
155
156         /* FIXME: deal with options */
157
158         inet_csk_reqsk_queue_unlink(sk, req, prev);
159         inet_csk_reqsk_queue_removed(sk, req);
160         inet_csk_reqsk_queue_add(sk, req, child);
161 out:
162         return child;
163 listen_overflow:
164         dccp_pr_debug("listen_overflow!\n");
165         DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY;
166 drop:
167         if (dccp_hdr(skb)->dccph_type != DCCP_PKT_RESET)
168                 req->rsk_ops->send_reset(skb);
169
170         inet_csk_reqsk_queue_drop(sk, req, prev);
171         goto out;
172 }
173
174 /*
175  *  Queue segment on the new socket if the new socket is active,
176  *  otherwise we just shortcircuit this and continue with
177  *  the new socket.
178  */
179 int dccp_child_process(struct sock *parent, struct sock *child,
180                        struct sk_buff *skb)
181 {
182         int ret = 0;
183         const int state = child->sk_state;
184
185         if (!sock_owned_by_user(child)) {
186                 ret = dccp_rcv_state_process(child, skb, dccp_hdr(skb), skb->len);
187
188                 /* Wakeup parent, send SIGIO */
189                 if (state == DCCP_RESPOND && child->sk_state != state)
190                         parent->sk_data_ready(parent, 0);
191         } else {
192                 /* Alas, it is possible again, because we do lookup
193                  * in main socket hash table and lock on listening
194                  * socket does not protect us more.
195                  */
196                 sk_add_backlog(child, skb);
197         }
198
199         bh_unlock_sock(child);
200         sock_put(child);
201         return ret;
202 }