blob: 3090fc40eebdaab1111de51f4dec2d034d34e5b0 [file] [log] [blame]
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001/*
2 * net/dccp/proto.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 modify it
8 * under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070012#include <linux/dccp.h>
13#include <linux/module.h>
14#include <linux/types.h>
15#include <linux/sched.h>
16#include <linux/kernel.h>
17#include <linux/skbuff.h>
18#include <linux/netdevice.h>
19#include <linux/in.h>
20#include <linux/if_arp.h>
21#include <linux/init.h>
22#include <linux/random.h>
23#include <net/checksum.h>
24
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020025#include <net/inet_sock.h>
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070026#include <net/sock.h>
27#include <net/xfrm.h>
28
Arnaldo Carvalho de Melo6273172e2007-10-23 20:23:30 -070029#include <asm/ioctls.h>
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070030#include <linux/spinlock.h>
31#include <linux/timer.h>
32#include <linux/delay.h>
33#include <linux/poll.h>
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070034
35#include "ccid.h"
36#include "dccp.h"
Andrea Bittauafe00252006-03-20 17:43:56 -080037#include "feat.h"
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070038
Eric Dumazetba899662005-08-26 12:05:31 -070039DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070040
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -080041EXPORT_SYMBOL_GPL(dccp_statistics);
42
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070043atomic_t dccp_orphan_count = ATOMIC_INIT(0);
44
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -080045EXPORT_SYMBOL_GPL(dccp_orphan_count);
46
Arnaldo Carvalho de Melo075ae862006-03-20 21:24:19 -080047struct inet_hashinfo __cacheline_aligned dccp_hashinfo = {
48 .lhash_lock = RW_LOCK_UNLOCKED,
49 .lhash_users = ATOMIC_INIT(0),
50 .lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(dccp_hashinfo.lhash_wait),
51};
52
53EXPORT_SYMBOL_GPL(dccp_hashinfo);
54
Ian McDonaldb1308dc2006-11-20 18:30:17 -020055/* the maximum queue length for tx in packets. 0 is no limit */
56int sysctl_dccp_tx_qlen __read_mostly = 5;
57
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080058void dccp_set_state(struct sock *sk, const int state)
59{
60 const int oldstate = sk->sk_state;
61
Gerrit Renkerf11135a2007-11-28 11:34:53 -020062 dccp_pr_debug("%s(%p) %s --> %s\n", dccp_role(sk), sk,
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080063 dccp_state_name(oldstate), dccp_state_name(state));
64 WARN_ON(state == oldstate);
65
66 switch (state) {
67 case DCCP_OPEN:
68 if (oldstate != DCCP_OPEN)
69 DCCP_INC_STATS(DCCP_MIB_CURRESTAB);
70 break;
71
72 case DCCP_CLOSED:
Gerrit Renker0c869622007-11-28 11:59:48 -020073 if (oldstate == DCCP_OPEN || oldstate == DCCP_ACTIVE_CLOSEREQ ||
74 oldstate == DCCP_CLOSING)
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080075 DCCP_INC_STATS(DCCP_MIB_ESTABRESETS);
76
77 sk->sk_prot->unhash(sk);
78 if (inet_csk(sk)->icsk_bind_hash != NULL &&
79 !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))
Arnaldo Carvalho de Meloab1e0a12008-02-03 04:06:04 -080080 inet_put_port(sk);
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080081 /* fall through */
82 default:
83 if (oldstate == DCCP_OPEN)
84 DCCP_DEC_STATS(DCCP_MIB_CURRESTAB);
85 }
86
87 /* Change state AFTER socket is unhashed to avoid closed
88 * socket sitting in hash tables.
89 */
90 sk->sk_state = state;
91}
92
93EXPORT_SYMBOL_GPL(dccp_set_state);
94
Gerrit Renker0c869622007-11-28 11:59:48 -020095static void dccp_finish_passive_close(struct sock *sk)
96{
97 switch (sk->sk_state) {
98 case DCCP_PASSIVE_CLOSE:
99 /* Node (client or server) has received Close packet. */
100 dccp_send_reset(sk, DCCP_RESET_CODE_CLOSED);
101 dccp_set_state(sk, DCCP_CLOSED);
102 break;
103 case DCCP_PASSIVE_CLOSEREQ:
104 /*
105 * Client received CloseReq. We set the `active' flag so that
106 * dccp_send_close() retransmits the Close as per RFC 4340, 8.3.
107 */
108 dccp_send_close(sk, 1);
109 dccp_set_state(sk, DCCP_CLOSING);
110 }
111}
112
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -0800113void dccp_done(struct sock *sk)
114{
115 dccp_set_state(sk, DCCP_CLOSED);
116 dccp_clear_xmit_timers(sk);
117
118 sk->sk_shutdown = SHUTDOWN_MASK;
119
120 if (!sock_flag(sk, SOCK_DEAD))
121 sk->sk_state_change(sk);
122 else
123 inet_csk_destroy_sock(sk);
124}
125
126EXPORT_SYMBOL_GPL(dccp_done);
127
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700128const char *dccp_packet_name(const int type)
129{
130 static const char *dccp_packet_names[] = {
131 [DCCP_PKT_REQUEST] = "REQUEST",
132 [DCCP_PKT_RESPONSE] = "RESPONSE",
133 [DCCP_PKT_DATA] = "DATA",
134 [DCCP_PKT_ACK] = "ACK",
135 [DCCP_PKT_DATAACK] = "DATAACK",
136 [DCCP_PKT_CLOSEREQ] = "CLOSEREQ",
137 [DCCP_PKT_CLOSE] = "CLOSE",
138 [DCCP_PKT_RESET] = "RESET",
139 [DCCP_PKT_SYNC] = "SYNC",
140 [DCCP_PKT_SYNCACK] = "SYNCACK",
141 };
142
143 if (type >= DCCP_NR_PKT_TYPES)
144 return "INVALID";
145 else
146 return dccp_packet_names[type];
147}
148
149EXPORT_SYMBOL_GPL(dccp_packet_name);
150
151const char *dccp_state_name(const int state)
152{
153 static char *dccp_state_names[] = {
Gerrit Renkerf11135a2007-11-28 11:34:53 -0200154 [DCCP_OPEN] = "OPEN",
155 [DCCP_REQUESTING] = "REQUESTING",
156 [DCCP_PARTOPEN] = "PARTOPEN",
157 [DCCP_LISTEN] = "LISTEN",
158 [DCCP_RESPOND] = "RESPOND",
159 [DCCP_CLOSING] = "CLOSING",
160 [DCCP_ACTIVE_CLOSEREQ] = "CLOSEREQ",
161 [DCCP_PASSIVE_CLOSE] = "PASSIVE_CLOSE",
162 [DCCP_PASSIVE_CLOSEREQ] = "PASSIVE_CLOSEREQ",
163 [DCCP_TIME_WAIT] = "TIME_WAIT",
164 [DCCP_CLOSED] = "CLOSED",
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700165 };
166
167 if (state >= DCCP_MAX_STATES)
168 return "INVALID STATE!";
169 else
170 return dccp_state_names[state];
171}
172
173EXPORT_SYMBOL_GPL(dccp_state_name);
174
Arnaldo Carvalho de Melo72478872006-03-20 22:00:37 -0800175int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800176{
177 struct dccp_sock *dp = dccp_sk(sk);
Gerrit Renker410e27a2008-09-09 13:27:22 +0200178 struct dccp_minisock *dmsk = dccp_msk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800179 struct inet_connection_sock *icsk = inet_csk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800180
Gerrit Renker410e27a2008-09-09 13:27:22 +0200181 dccp_minisock_init(&dp->dccps_minisock);
182
Arnaldo Carvalho de Meloe18d7a92007-11-24 21:42:53 -0200183 icsk->icsk_rto = DCCP_TIMEOUT_INIT;
184 icsk->icsk_syn_retries = sysctl_dccp_request_retries;
185 sk->sk_state = DCCP_CLOSED;
186 sk->sk_write_space = dccp_write_space;
187 icsk->icsk_sync_mss = dccp_sync_mss;
Gerrit Renker410e27a2008-09-09 13:27:22 +0200188 dp->dccps_mss_cache = 536;
Arnaldo Carvalho de Meloe18d7a92007-11-24 21:42:53 -0200189 dp->dccps_rate_last = jiffies;
190 dp->dccps_role = DCCP_ROLE_UNDEFINED;
191 dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT;
Gerrit Renker410e27a2008-09-09 13:27:22 +0200192 dp->dccps_l_ack_ratio = dp->dccps_r_ack_ratio = 1;
Arnaldo Carvalho de Meloe18d7a92007-11-24 21:42:53 -0200193
194 dccp_init_xmit_timers(sk);
195
Gerrit Renkerac757732008-11-04 23:55:49 -0800196 INIT_LIST_HEAD(&dp->dccps_featneg);
Gerrit Renker410e27a2008-09-09 13:27:22 +0200197 /*
198 * FIXME: We're hardcoding the CCID, and doing this at this point makes
199 * the listening (master) sock get CCID control blocks, which is not
200 * necessary, but for now, to not mess with the test userspace apps,
201 * lets leave it here, later the real solution is to do this in a
202 * setsockopt(CCIDs-I-want/accept). -acme
203 */
204 if (likely(ctl_sock_initialized)) {
Gerrit Renkere8ef9672008-11-12 00:43:40 -0800205 int rc = dccp_feat_init(sk);
Gerrit Renker410e27a2008-09-09 13:27:22 +0200206
207 if (rc)
208 return rc;
209
210 if (dmsk->dccpms_send_ack_vector) {
211 dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL);
212 if (dp->dccps_hc_rx_ackvec == NULL)
213 return -ENOMEM;
214 }
215 dp->dccps_hc_rx_ccid = ccid_hc_rx_new(dmsk->dccpms_rx_ccid,
216 sk, GFP_KERNEL);
217 dp->dccps_hc_tx_ccid = ccid_hc_tx_new(dmsk->dccpms_tx_ccid,
218 sk, GFP_KERNEL);
219 if (unlikely(dp->dccps_hc_rx_ccid == NULL ||
220 dp->dccps_hc_tx_ccid == NULL)) {
221 ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
222 ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
223 if (dmsk->dccpms_send_ack_vector) {
224 dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
225 dp->dccps_hc_rx_ackvec = NULL;
226 }
227 dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
228 return -ENOMEM;
229 }
230 } else {
231 /* control socket doesn't need feat nego */
232 INIT_LIST_HEAD(&dmsk->dccpms_pending);
233 INIT_LIST_HEAD(&dmsk->dccpms_conf);
234 }
235
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800236 return 0;
237}
238
239EXPORT_SYMBOL_GPL(dccp_init_sock);
240
Brian Haley7d06b2e2008-06-14 17:04:49 -0700241void dccp_destroy_sock(struct sock *sk)
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800242{
243 struct dccp_sock *dp = dccp_sk(sk);
Gerrit Renker410e27a2008-09-09 13:27:22 +0200244 struct dccp_minisock *dmsk = dccp_msk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800245
246 /*
247 * DCCP doesn't use sk_write_queue, just sk_send_head
248 * for retransmissions
249 */
250 if (sk->sk_send_head != NULL) {
251 kfree_skb(sk->sk_send_head);
252 sk->sk_send_head = NULL;
253 }
254
255 /* Clean up a referenced DCCP bind bucket. */
256 if (inet_csk(sk)->icsk_bind_hash != NULL)
Arnaldo Carvalho de Meloab1e0a12008-02-03 04:06:04 -0800257 inet_put_port(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800258
259 kfree(dp->dccps_service_list);
260 dp->dccps_service_list = NULL;
261
Gerrit Renker410e27a2008-09-09 13:27:22 +0200262 if (dmsk->dccpms_send_ack_vector) {
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800263 dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
264 dp->dccps_hc_rx_ackvec = NULL;
265 }
266 ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
267 ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
268 dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
269
270 /* clean up feature negotiation state */
Gerrit Renkerd99a7bd2008-11-04 23:56:30 -0800271 dccp_feat_list_purge(&dp->dccps_featneg);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800272}
273
274EXPORT_SYMBOL_GPL(dccp_destroy_sock);
275
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800276static inline int dccp_listen_start(struct sock *sk, int backlog)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700277{
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700278 struct dccp_sock *dp = dccp_sk(sk);
279
280 dp->dccps_role = DCCP_ROLE_LISTEN;
Gerrit Renker9eca0a42008-11-12 00:48:44 -0800281 /* do not start to listen if feature negotiation setup fails */
282 if (dccp_feat_finalise_settings(dp))
283 return -EPROTO;
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800284 return inet_csk_listen_start(sk, backlog);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700285}
286
Gerrit Renkerce865a62007-11-24 22:14:15 -0200287static inline int dccp_need_reset(int state)
288{
289 return state != DCCP_CLOSED && state != DCCP_LISTEN &&
290 state != DCCP_REQUESTING;
291}
292
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700293int dccp_disconnect(struct sock *sk, int flags)
294{
295 struct inet_connection_sock *icsk = inet_csk(sk);
296 struct inet_sock *inet = inet_sk(sk);
297 int err = 0;
298 const int old_state = sk->sk_state;
299
300 if (old_state != DCCP_CLOSED)
301 dccp_set_state(sk, DCCP_CLOSED);
302
Gerrit Renkerce865a62007-11-24 22:14:15 -0200303 /*
304 * This corresponds to the ABORT function of RFC793, sec. 3.8
305 * TCP uses a RST segment, DCCP a Reset packet with Code 2, "Aborted".
306 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700307 if (old_state == DCCP_LISTEN) {
308 inet_csk_listen_stop(sk);
Gerrit Renkerce865a62007-11-24 22:14:15 -0200309 } else if (dccp_need_reset(old_state)) {
310 dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
311 sk->sk_err = ECONNRESET;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700312 } else if (old_state == DCCP_REQUESTING)
313 sk->sk_err = ECONNRESET;
314
315 dccp_clear_xmit_timers(sk);
Gerrit Renker48816322008-08-23 13:28:27 +0200316
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700317 __skb_queue_purge(&sk->sk_receive_queue);
Gerrit Renker48816322008-08-23 13:28:27 +0200318 __skb_queue_purge(&sk->sk_write_queue);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700319 if (sk->sk_send_head != NULL) {
320 __kfree_skb(sk->sk_send_head);
321 sk->sk_send_head = NULL;
322 }
323
324 inet->dport = 0;
325
326 if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
327 inet_reset_saddr(sk);
328
329 sk->sk_shutdown = 0;
330 sock_reset_flag(sk, SOCK_DONE);
331
332 icsk->icsk_backoff = 0;
333 inet_csk_delack_init(sk);
334 __sk_dst_reset(sk);
335
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700336 WARN_ON(inet->num && !icsk->icsk_bind_hash);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700337
338 sk->sk_error_report(sk);
339 return err;
340}
341
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800342EXPORT_SYMBOL_GPL(dccp_disconnect);
343
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700344/*
345 * Wait for a DCCP event.
346 *
347 * Note that we don't need to lock the socket, as the upper poll layers
348 * take care of normal races (between the test and the event) and we don't
349 * go look at any of the socket buffers directly.
350 */
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800351unsigned int dccp_poll(struct file *file, struct socket *sock,
352 poll_table *wait)
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700353{
354 unsigned int mask;
355 struct sock *sk = sock->sk;
356
357 poll_wait(file, sk->sk_sleep, wait);
358 if (sk->sk_state == DCCP_LISTEN)
359 return inet_csk_listen_poll(sk);
360
361 /* Socket is not locked. We are protected from async events
362 by poll logic and correct handling of state changes
363 made by another threads is impossible in any case.
364 */
365
366 mask = 0;
367 if (sk->sk_err)
368 mask = POLLERR;
369
370 if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED)
371 mask |= POLLHUP;
372 if (sk->sk_shutdown & RCV_SHUTDOWN)
Davide Libenzif348d702006-03-25 03:07:39 -0800373 mask |= POLLIN | POLLRDNORM | POLLRDHUP;
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700374
375 /* Connected? */
376 if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) {
377 if (atomic_read(&sk->sk_rmem_alloc) > 0)
378 mask |= POLLIN | POLLRDNORM;
379
380 if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {
381 if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {
382 mask |= POLLOUT | POLLWRNORM;
383 } else { /* send SIGIO later */
384 set_bit(SOCK_ASYNC_NOSPACE,
385 &sk->sk_socket->flags);
386 set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
387
388 /* Race breaker. If space is freed after
389 * wspace test but before the flags are set,
390 * IO signal will be lost.
391 */
392 if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk))
393 mask |= POLLOUT | POLLWRNORM;
394 }
395 }
396 }
397 return mask;
398}
399
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800400EXPORT_SYMBOL_GPL(dccp_poll);
401
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700402int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
403{
Arnaldo Carvalho de Melo6273172e2007-10-23 20:23:30 -0700404 int rc = -ENOTCONN;
405
406 lock_sock(sk);
407
408 if (sk->sk_state == DCCP_LISTEN)
409 goto out;
410
411 switch (cmd) {
412 case SIOCINQ: {
413 struct sk_buff *skb;
414 unsigned long amount = 0;
415
416 skb = skb_peek(&sk->sk_receive_queue);
417 if (skb != NULL) {
418 /*
419 * We will only return the amount of this packet since
420 * that is all that will be read.
421 */
422 amount = skb->len;
423 }
424 rc = put_user(amount, (int __user *)arg);
425 }
426 break;
427 default:
428 rc = -ENOIOCTLCMD;
429 break;
430 }
431out:
432 release_sock(sk);
433 return rc;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700434}
435
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800436EXPORT_SYMBOL_GPL(dccp_ioctl);
437
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800438static int dccp_setsockopt_service(struct sock *sk, const __be32 service,
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700439 char __user *optval, int optlen)
440{
441 struct dccp_sock *dp = dccp_sk(sk);
442 struct dccp_service_list *sl = NULL;
443
Arnaldo Carvalho de Melo8109b022006-12-10 16:01:18 -0200444 if (service == DCCP_SERVICE_INVALID_VALUE ||
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700445 optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32))
446 return -EINVAL;
447
448 if (optlen > sizeof(service)) {
449 sl = kmalloc(optlen, GFP_KERNEL);
450 if (sl == NULL)
451 return -ENOMEM;
452
453 sl->dccpsl_nr = optlen / sizeof(u32) - 1;
454 if (copy_from_user(sl->dccpsl_list,
455 optval + sizeof(service),
456 optlen - sizeof(service)) ||
457 dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) {
458 kfree(sl);
459 return -EFAULT;
460 }
461 }
462
463 lock_sock(sk);
464 dp->dccps_service = service;
465
Jesper Juhla51482b2005-11-08 09:41:34 -0800466 kfree(dp->dccps_service_list);
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700467
468 dp->dccps_service_list = sl;
469 release_sock(sk);
470 return 0;
471}
472
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800473static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
474 char __user *optval, int optlen)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700475{
Gerrit Renker09dbc382006-11-14 12:57:34 -0200476 struct dccp_sock *dp = dccp_sk(sk);
477 int val, err = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700478
Gerrit Renker410e27a2008-09-09 13:27:22 +0200479 if (optlen < sizeof(int))
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300480 return -EINVAL;
481
482 if (get_user(val, (int __user *)optval))
483 return -EFAULT;
484
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700485 if (optname == DCCP_SOCKOPT_SERVICE)
486 return dccp_setsockopt_service(sk, val, optval, optlen);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300487
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700488 lock_sock(sk);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300489 switch (optname) {
Gerrit Renker410e27a2008-09-09 13:27:22 +0200490 case DCCP_SOCKOPT_PACKET_SIZE:
491 DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
492 err = 0;
493 break;
494 case DCCP_SOCKOPT_CHANGE_L:
Gerrit Renker410e27a2008-09-09 13:27:22 +0200495 case DCCP_SOCKOPT_CHANGE_R:
Gerrit Renker49aebc62008-11-16 22:51:23 -0800496 DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n");
497 err = 0;
Gerrit Renker410e27a2008-09-09 13:27:22 +0200498 break;
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200499 case DCCP_SOCKOPT_SERVER_TIMEWAIT:
500 if (dp->dccps_role != DCCP_ROLE_SERVER)
501 err = -EOPNOTSUPP;
502 else
503 dp->dccps_server_timewait = (val != 0);
504 break;
Gerrit Renker410e27a2008-09-09 13:27:22 +0200505 case DCCP_SOCKOPT_SEND_CSCOV: /* sender side, RFC 4340, sec. 9.2 */
506 if (val < 0 || val > 15)
Tomasz Grobelnyd6da3512008-09-04 07:30:19 +0200507 err = -EINVAL;
508 else
Gerrit Renker410e27a2008-09-09 13:27:22 +0200509 dp->dccps_pcslen = val;
Tomasz Grobelnyd6da3512008-09-04 07:30:19 +0200510 break;
Gerrit Renker410e27a2008-09-09 13:27:22 +0200511 case DCCP_SOCKOPT_RECV_CSCOV: /* receiver side, RFC 4340 sec. 9.2.1 */
512 if (val < 0 || val > 15)
Tomasz Grobelnyd6da3512008-09-04 07:30:19 +0200513 err = -EINVAL;
Gerrit Renker410e27a2008-09-09 13:27:22 +0200514 else {
515 dp->dccps_pcrlen = val;
516 /* FIXME: add feature negotiation,
517 * ChangeL(MinimumChecksumCoverage, val) */
518 }
Tomasz Grobelnyd6da3512008-09-04 07:30:19 +0200519 break;
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300520 default:
521 err = -ENOPROTOOPT;
522 break;
523 }
Gerrit Renker73bbe092008-09-04 07:30:19 +0200524
Gerrit Renker410e27a2008-09-09 13:27:22 +0200525 release_sock(sk);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300526 return err;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700527}
528
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800529int dccp_setsockopt(struct sock *sk, int level, int optname,
530 char __user *optval, int optlen)
531{
532 if (level != SOL_DCCP)
533 return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
534 optname, optval,
535 optlen);
536 return do_dccp_setsockopt(sk, level, optname, optval, optlen);
537}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800538
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800539EXPORT_SYMBOL_GPL(dccp_setsockopt);
540
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800541#ifdef CONFIG_COMPAT
542int compat_dccp_setsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800543 char __user *optval, int optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800544{
Arnaldo Carvalho de Melodec73ff2006-03-20 22:46:16 -0800545 if (level != SOL_DCCP)
546 return inet_csk_compat_setsockopt(sk, level, optname,
547 optval, optlen);
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800548 return do_dccp_setsockopt(sk, level, optname, optval, optlen);
549}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800550
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800551EXPORT_SYMBOL_GPL(compat_dccp_setsockopt);
552#endif
553
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700554static int dccp_getsockopt_service(struct sock *sk, int len,
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800555 __be32 __user *optval,
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700556 int __user *optlen)
557{
558 const struct dccp_sock *dp = dccp_sk(sk);
559 const struct dccp_service_list *sl;
560 int err = -ENOENT, slen = 0, total_len = sizeof(u32);
561
562 lock_sock(sk);
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700563 if ((sl = dp->dccps_service_list) != NULL) {
564 slen = sl->dccpsl_nr * sizeof(u32);
565 total_len += slen;
566 }
567
568 err = -EINVAL;
569 if (total_len > len)
570 goto out;
571
572 err = 0;
573 if (put_user(total_len, optlen) ||
574 put_user(dp->dccps_service, optval) ||
575 (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen)))
576 err = -EFAULT;
577out:
578 release_sock(sk);
579 return err;
580}
581
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800582static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -0300583 char __user *optval, int __user *optlen)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700584{
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300585 struct dccp_sock *dp;
586 int val, len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700587
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300588 if (get_user(len, optlen))
589 return -EFAULT;
590
Arnaldo Carvalho de Melo39ebc022007-03-28 11:54:32 -0700591 if (len < (int)sizeof(int))
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300592 return -EINVAL;
593
594 dp = dccp_sk(sk);
595
596 switch (optname) {
597 case DCCP_SOCKOPT_PACKET_SIZE:
Gerrit Renker5aed3242006-11-28 19:33:36 -0200598 DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
Arnaldo Carvalho de Melo841bac12006-11-28 19:42:03 -0200599 return 0;
Arnaldo Carvalho de Melo88f964d2005-09-18 00:19:32 -0700600 case DCCP_SOCKOPT_SERVICE:
601 return dccp_getsockopt_service(sk, len,
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800602 (__be32 __user *)optval, optlen);
Gerrit Renker7c559a92007-10-04 14:39:22 -0700603 case DCCP_SOCKOPT_GET_CUR_MPS:
604 val = dp->dccps_mss_cache;
Gerrit Renker7c559a92007-10-04 14:39:22 -0700605 break;
Gerrit Renkerd90ebcb2008-11-12 00:47:26 -0800606 case DCCP_SOCKOPT_AVAILABLE_CCIDS:
607 return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen);
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200608 case DCCP_SOCKOPT_SERVER_TIMEWAIT:
609 val = dp->dccps_server_timewait;
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200610 break;
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200611 case DCCP_SOCKOPT_SEND_CSCOV:
612 val = dp->dccps_pcslen;
613 break;
614 case DCCP_SOCKOPT_RECV_CSCOV:
615 val = dp->dccps_pcrlen;
616 break;
Arnaldo Carvalho de Melo88f964d2005-09-18 00:19:32 -0700617 case 128 ... 191:
618 return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
619 len, (u32 __user *)optval, optlen);
620 case 192 ... 255:
621 return ccid_hc_tx_getsockopt(dp->dccps_hc_tx_ccid, sk, optname,
622 len, (u32 __user *)optval, optlen);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300623 default:
624 return -ENOPROTOOPT;
625 }
626
Gerrit Renker79133502007-12-13 12:27:14 -0200627 len = sizeof(val);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300628 if (put_user(len, optlen) || copy_to_user(optval, &val, len))
629 return -EFAULT;
630
631 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700632}
633
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800634int dccp_getsockopt(struct sock *sk, int level, int optname,
635 char __user *optval, int __user *optlen)
636{
637 if (level != SOL_DCCP)
638 return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
639 optname, optval,
640 optlen);
641 return do_dccp_getsockopt(sk, level, optname, optval, optlen);
642}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800643
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800644EXPORT_SYMBOL_GPL(dccp_getsockopt);
645
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800646#ifdef CONFIG_COMPAT
647int compat_dccp_getsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800648 char __user *optval, int __user *optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800649{
Arnaldo Carvalho de Melodec73ff2006-03-20 22:46:16 -0800650 if (level != SOL_DCCP)
651 return inet_csk_compat_getsockopt(sk, level, optname,
652 optval, optlen);
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800653 return do_dccp_getsockopt(sk, level, optname, optval, optlen);
654}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800655
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800656EXPORT_SYMBOL_GPL(compat_dccp_getsockopt);
657#endif
658
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700659int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
660 size_t len)
661{
662 const struct dccp_sock *dp = dccp_sk(sk);
663 const int flags = msg->msg_flags;
664 const int noblock = flags & MSG_DONTWAIT;
665 struct sk_buff *skb;
666 int rc, size;
667 long timeo;
668
669 if (len > dp->dccps_mss_cache)
670 return -EMSGSIZE;
671
672 lock_sock(sk);
Ian McDonaldb1308dc2006-11-20 18:30:17 -0200673
Gerrit Renker410e27a2008-09-09 13:27:22 +0200674 if (sysctl_dccp_tx_qlen &&
675 (sk->sk_write_queue.qlen >= sysctl_dccp_tx_qlen)) {
Ian McDonaldb1308dc2006-11-20 18:30:17 -0200676 rc = -EAGAIN;
677 goto out_release;
678 }
679
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700680 timeo = sock_sndtimeo(sk, noblock);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700681
682 /*
683 * We have to use sk_stream_wait_connect here to set sk_write_pending,
684 * so that the trick in dccp_rcv_request_sent_state_process.
685 */
686 /* Wait for a connection to finish. */
Gerrit Renkercecd8d02007-09-26 19:36:08 -0300687 if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN))
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700688 if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0)
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700689 goto out_release;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700690
691 size = sk->sk_prot->max_header + len;
692 release_sock(sk);
693 skb = sock_alloc_send_skb(sk, size, noblock, &rc);
694 lock_sock(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700695 if (skb == NULL)
696 goto out_release;
697
698 skb_reserve(skb, sk->sk_prot->max_header);
699 rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700700 if (rc != 0)
701 goto out_discard;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700702
Gerrit Renker410e27a2008-09-09 13:27:22 +0200703 skb_queue_tail(&sk->sk_write_queue, skb);
704 dccp_write_xmit(sk,0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700705out_release:
706 release_sock(sk);
707 return rc ? : len;
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700708out_discard:
709 kfree_skb(skb);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700710 goto out_release;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700711}
712
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800713EXPORT_SYMBOL_GPL(dccp_sendmsg);
714
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700715int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
716 size_t len, int nonblock, int flags, int *addr_len)
717{
718 const struct dccp_hdr *dh;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700719 long timeo;
720
721 lock_sock(sk);
722
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300723 if (sk->sk_state == DCCP_LISTEN) {
724 len = -ENOTCONN;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700725 goto out;
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300726 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700727
728 timeo = sock_rcvtimeo(sk, nonblock);
729
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700730 do {
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300731 struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700732
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300733 if (skb == NULL)
734 goto verify_sock_status;
735
736 dh = dccp_hdr(skb);
737
Gerrit Renker0c869622007-11-28 11:59:48 -0200738 switch (dh->dccph_type) {
739 case DCCP_PKT_DATA:
740 case DCCP_PKT_DATAACK:
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300741 goto found_ok_skb;
742
Gerrit Renker0c869622007-11-28 11:59:48 -0200743 case DCCP_PKT_CLOSE:
744 case DCCP_PKT_CLOSEREQ:
745 if (!(flags & MSG_PEEK))
746 dccp_finish_passive_close(sk);
747 /* fall through */
748 case DCCP_PKT_RESET:
749 dccp_pr_debug("found fin (%s) ok!\n",
750 dccp_packet_name(dh->dccph_type));
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300751 len = 0;
752 goto found_fin_ok;
Gerrit Renker0c869622007-11-28 11:59:48 -0200753 default:
754 dccp_pr_debug("packet_type=%s\n",
755 dccp_packet_name(dh->dccph_type));
756 sk_eat_skb(sk, skb, 0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700757 }
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300758verify_sock_status:
759 if (sock_flag(sk, SOCK_DONE)) {
760 len = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700761 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700762 }
763
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300764 if (sk->sk_err) {
765 len = sock_error(sk);
766 break;
767 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700768
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300769 if (sk->sk_shutdown & RCV_SHUTDOWN) {
770 len = 0;
771 break;
772 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700773
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300774 if (sk->sk_state == DCCP_CLOSED) {
775 if (!sock_flag(sk, SOCK_DONE)) {
776 /* This occurs when user tries to read
777 * from never connected socket.
778 */
779 len = -ENOTCONN;
780 break;
781 }
782 len = 0;
783 break;
784 }
785
786 if (!timeo) {
787 len = -EAGAIN;
788 break;
789 }
790
791 if (signal_pending(current)) {
792 len = sock_intr_errno(timeo);
793 break;
794 }
795
796 sk_wait_data(sk, &timeo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700797 continue;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700798 found_ok_skb:
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300799 if (len > skb->len)
800 len = skb->len;
801 else if (len < skb->len)
802 msg->msg_flags |= MSG_TRUNC;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700803
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300804 if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len)) {
805 /* Exception. Bailout! */
806 len = -EFAULT;
807 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700808 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700809 found_fin_ok:
810 if (!(flags & MSG_PEEK))
Chris Leech624d1162006-05-23 18:01:28 -0700811 sk_eat_skb(sk, skb, 0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700812 break;
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300813 } while (1);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700814out:
815 release_sock(sk);
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300816 return len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700817}
818
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800819EXPORT_SYMBOL_GPL(dccp_recvmsg);
820
821int inet_dccp_listen(struct socket *sock, int backlog)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700822{
823 struct sock *sk = sock->sk;
824 unsigned char old_state;
825 int err;
826
827 lock_sock(sk);
828
829 err = -EINVAL;
830 if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP)
831 goto out;
832
833 old_state = sk->sk_state;
834 if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN)))
835 goto out;
836
837 /* Really, if the socket is already in listen state
838 * we can only allow the backlog to be adjusted.
839 */
840 if (old_state != DCCP_LISTEN) {
841 /*
842 * FIXME: here it probably should be sk->sk_prot->listen_start
843 * see tcp_listen_start
844 */
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800845 err = dccp_listen_start(sk, backlog);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700846 if (err)
847 goto out;
848 }
849 sk->sk_max_ack_backlog = backlog;
850 err = 0;
851
852out:
853 release_sock(sk);
854 return err;
855}
856
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800857EXPORT_SYMBOL_GPL(inet_dccp_listen);
858
Gerrit Renker0c869622007-11-28 11:59:48 -0200859static void dccp_terminate_connection(struct sock *sk)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700860{
Gerrit Renker0c869622007-11-28 11:59:48 -0200861 u8 next_state = DCCP_CLOSED;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700862
Gerrit Renker0c869622007-11-28 11:59:48 -0200863 switch (sk->sk_state) {
864 case DCCP_PASSIVE_CLOSE:
865 case DCCP_PASSIVE_CLOSEREQ:
866 dccp_finish_passive_close(sk);
867 break;
868 case DCCP_PARTOPEN:
869 dccp_pr_debug("Stop PARTOPEN timer (%p)\n", sk);
870 inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);
871 /* fall through */
872 case DCCP_OPEN:
873 dccp_send_close(sk, 1);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700874
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200875 if (dccp_sk(sk)->dccps_role == DCCP_ROLE_SERVER &&
876 !dccp_sk(sk)->dccps_server_timewait)
Gerrit Renker0c869622007-11-28 11:59:48 -0200877 next_state = DCCP_ACTIVE_CLOSEREQ;
878 else
879 next_state = DCCP_CLOSING;
880 /* fall through */
881 default:
882 dccp_set_state(sk, next_state);
883 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700884}
885
886void dccp_close(struct sock *sk, long timeout)
887{
Ian McDonald97e58482006-08-26 19:16:45 -0700888 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700889 struct sk_buff *skb;
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800890 u32 data_was_unread = 0;
Herbert Xu134af3462006-05-05 17:09:13 -0700891 int state;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700892
893 lock_sock(sk);
894
895 sk->sk_shutdown = SHUTDOWN_MASK;
896
897 if (sk->sk_state == DCCP_LISTEN) {
898 dccp_set_state(sk, DCCP_CLOSED);
899
900 /* Special case. */
901 inet_csk_listen_stop(sk);
902
903 goto adjudge_to_death;
904 }
905
Ian McDonald97e58482006-08-26 19:16:45 -0700906 sk_stop_timer(sk, &dp->dccps_xmit_timer);
907
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700908 /*
909 * We need to flush the recv. buffs. We do this only on the
910 * descriptor close, not protocol-sourced closes, because the
911 *reader process may not have drained the data yet!
912 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700913 while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800914 data_was_unread += skb->len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700915 __kfree_skb(skb);
916 }
917
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800918 if (data_was_unread) {
919 /* Unread data was tossed, send an appropriate Reset Code */
920 DCCP_WARN("DCCP: ABORT -- %u bytes unread\n", data_was_unread);
921 dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
922 dccp_set_state(sk, DCCP_CLOSED);
923 } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700924 /* Check zero linger _after_ checking for unread data. */
925 sk->sk_prot->disconnect(sk, 0);
Gerrit Renker0c869622007-11-28 11:59:48 -0200926 } else if (sk->sk_state != DCCP_CLOSED) {
927 dccp_terminate_connection(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700928 }
929
930 sk_stream_wait_close(sk, timeout);
931
932adjudge_to_death:
Herbert Xu134af3462006-05-05 17:09:13 -0700933 state = sk->sk_state;
934 sock_hold(sk);
935 sock_orphan(sk);
936 atomic_inc(sk->sk_prot->orphan_count);
937
Arnaldo Carvalho de Melo7ad07e72005-08-23 21:50:06 -0700938 /*
939 * It is the last release_sock in its life. It will remove backlog.
940 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700941 release_sock(sk);
942 /*
943 * Now socket is owned by kernel and we acquire BH lock
944 * to finish close. No need to check for user refs.
945 */
946 local_bh_disable();
947 bh_lock_sock(sk);
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700948 WARN_ON(sock_owned_by_user(sk));
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700949
Herbert Xu134af3462006-05-05 17:09:13 -0700950 /* Have we already been destroyed by a softirq or backlog? */
951 if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
952 goto out;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700953
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700954 if (sk->sk_state == DCCP_CLOSED)
955 inet_csk_destroy_sock(sk);
956
957 /* Otherwise, socket is reprieved until protocol close. */
958
Herbert Xu134af3462006-05-05 17:09:13 -0700959out:
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700960 bh_unlock_sock(sk);
961 local_bh_enable();
962 sock_put(sk);
963}
964
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800965EXPORT_SYMBOL_GPL(dccp_close);
966
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700967void dccp_shutdown(struct sock *sk, int how)
968{
Gerrit Renker8e8c71f2007-11-21 09:56:48 -0200969 dccp_pr_debug("called shutdown(%x)\n", how);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700970}
971
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800972EXPORT_SYMBOL_GPL(dccp_shutdown);
973
YOSHIFUJI Hideaki24e8b7e2008-04-10 03:48:43 -0700974static inline int dccp_mib_init(void)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700975{
YOSHIFUJI Hideaki24e8b7e2008-04-10 03:48:43 -0700976 return snmp_mib_init((void**)dccp_statistics, sizeof(struct dccp_mib));
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700977}
978
YOSHIFUJI Hideaki24e8b7e2008-04-10 03:48:43 -0700979static inline void dccp_mib_exit(void)
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -0800980{
YOSHIFUJI Hideaki24e8b7e2008-04-10 03:48:43 -0700981 snmp_mib_free((void**)dccp_statistics);
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -0800982}
983
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700984static int thash_entries;
985module_param(thash_entries, int, 0444);
986MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
987
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -0300988#ifdef CONFIG_IP_DCCP_DEBUG
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700989int dccp_debug;
Gerrit Renker43264992008-08-23 13:28:27 +0200990module_param(dccp_debug, bool, 0644);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700991MODULE_PARM_DESC(dccp_debug, "Enable debug messages");
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800992
993EXPORT_SYMBOL_GPL(dccp_debug);
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -0300994#endif
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700995
996static int __init dccp_init(void)
997{
998 unsigned long goal;
999 int ehash_order, bhash_order, i;
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001000 int rc = -ENOBUFS;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001001
Patrick McHardy028b0272008-04-12 18:35:41 -07001002 BUILD_BUG_ON(sizeof(struct dccp_skb_cb) >
1003 FIELD_SIZEOF(struct sk_buff, cb));
1004
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001005 dccp_hashinfo.bind_bucket_cachep =
1006 kmem_cache_create("dccp_bind_bucket",
1007 sizeof(struct inet_bind_bucket), 0,
Paul Mundt20c2df82007-07-20 10:11:58 +09001008 SLAB_HWCACHE_ALIGN, NULL);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001009 if (!dccp_hashinfo.bind_bucket_cachep)
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001010 goto out;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001011
1012 /*
1013 * Size and allocate the main established and bind bucket
1014 * hash tables.
1015 *
1016 * The methodology is similar to that of the buffer cache.
1017 */
1018 if (num_physpages >= (128 * 1024))
1019 goal = num_physpages >> (21 - PAGE_SHIFT);
1020 else
1021 goal = num_physpages >> (23 - PAGE_SHIFT);
1022
1023 if (thash_entries)
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001024 goal = (thash_entries *
1025 sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001026 for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++)
1027 ;
1028 do {
1029 dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE /
1030 sizeof(struct inet_ehash_bucket);
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001031 while (dccp_hashinfo.ehash_size &
1032 (dccp_hashinfo.ehash_size - 1))
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001033 dccp_hashinfo.ehash_size--;
1034 dccp_hashinfo.ehash = (struct inet_ehash_bucket *)
1035 __get_free_pages(GFP_ATOMIC, ehash_order);
1036 } while (!dccp_hashinfo.ehash && --ehash_order > 0);
1037
1038 if (!dccp_hashinfo.ehash) {
Gerrit Renker59348b12006-11-20 18:39:23 -02001039 DCCP_CRIT("Failed to allocate DCCP established hash table");
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001040 goto out_free_bind_bucket_cachep;
1041 }
1042
Eric Dumazetdbca9b2752007-02-08 14:16:46 -08001043 for (i = 0; i < dccp_hashinfo.ehash_size; i++) {
Eric Dumazet3ab5aee2008-11-16 19:40:17 -08001044 INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].chain, i);
1045 INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].twchain, i);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001046 }
1047
Eric Dumazet230140c2007-11-07 02:40:20 -08001048 if (inet_ehash_locks_alloc(&dccp_hashinfo))
1049 goto out_free_dccp_ehash;
1050
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001051 bhash_order = ehash_order;
1052
1053 do {
1054 dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE /
1055 sizeof(struct inet_bind_hashbucket);
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001056 if ((dccp_hashinfo.bhash_size > (64 * 1024)) &&
1057 bhash_order > 0)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001058 continue;
1059 dccp_hashinfo.bhash = (struct inet_bind_hashbucket *)
1060 __get_free_pages(GFP_ATOMIC, bhash_order);
1061 } while (!dccp_hashinfo.bhash && --bhash_order >= 0);
1062
1063 if (!dccp_hashinfo.bhash) {
Gerrit Renker59348b12006-11-20 18:39:23 -02001064 DCCP_CRIT("Failed to allocate DCCP bind hash table");
Eric Dumazet230140c2007-11-07 02:40:20 -08001065 goto out_free_dccp_locks;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001066 }
1067
1068 for (i = 0; i < dccp_hashinfo.bhash_size; i++) {
1069 spin_lock_init(&dccp_hashinfo.bhash[i].lock);
1070 INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
1071 }
1072
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001073 rc = dccp_mib_init();
Arnaldo Carvalho de Melofa23e2e2006-03-20 17:16:01 -08001074 if (rc)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001075 goto out_free_dccp_bhash;
1076
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001077 rc = dccp_ackvec_init();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001078 if (rc)
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001079 goto out_free_dccp_mib;
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001080
Arnaldo Carvalho de Meloe55d9122006-03-20 19:25:02 -08001081 rc = dccp_sysctl_init();
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001082 if (rc)
1083 goto out_ackvec_exit;
Gerrit Renker4c70f382007-09-25 22:40:13 -07001084
1085 dccp_timestamping_init();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001086out:
1087 return rc;
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001088out_ackvec_exit:
1089 dccp_ackvec_exit();
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001090out_free_dccp_mib:
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001091 dccp_mib_exit();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001092out_free_dccp_bhash:
1093 free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
1094 dccp_hashinfo.bhash = NULL;
Eric Dumazet230140c2007-11-07 02:40:20 -08001095out_free_dccp_locks:
1096 inet_ehash_locks_free(&dccp_hashinfo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001097out_free_dccp_ehash:
1098 free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order);
1099 dccp_hashinfo.ehash = NULL;
1100out_free_bind_bucket_cachep:
1101 kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
1102 dccp_hashinfo.bind_bucket_cachep = NULL;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001103 goto out;
1104}
1105
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001106static void __exit dccp_fini(void)
1107{
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001108 dccp_mib_exit();
Arnaldo Carvalho de Melo725ba8e2005-08-13 20:35:39 -03001109 free_pages((unsigned long)dccp_hashinfo.bhash,
1110 get_order(dccp_hashinfo.bhash_size *
1111 sizeof(struct inet_bind_hashbucket)));
1112 free_pages((unsigned long)dccp_hashinfo.ehash,
1113 get_order(dccp_hashinfo.ehash_size *
1114 sizeof(struct inet_ehash_bucket)));
Eric Dumazet230140c2007-11-07 02:40:20 -08001115 inet_ehash_locks_free(&dccp_hashinfo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001116 kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001117 dccp_ackvec_exit();
Arnaldo Carvalho de Meloe55d9122006-03-20 19:25:02 -08001118 dccp_sysctl_exit();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001119}
1120
1121module_init(dccp_init);
1122module_exit(dccp_fini);
1123
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001124MODULE_LICENSE("GPL");
1125MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>");
1126MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");