blob: e3f5d37b84be1817f2b69b0fe31c65e999ceb05d [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 Melo62731722007-10-23 20:23:30 -070029#include <asm/ioctls.h>
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070030#include <asm/semaphore.h>
31#include <linux/spinlock.h>
32#include <linux/timer.h>
33#include <linux/delay.h>
34#include <linux/poll.h>
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070035
36#include "ccid.h"
37#include "dccp.h"
Andrea Bittauafe00252006-03-20 17:43:56 -080038#include "feat.h"
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070039
Eric Dumazetba899662005-08-26 12:05:31 -070040DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070041
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -080042EXPORT_SYMBOL_GPL(dccp_statistics);
43
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070044atomic_t dccp_orphan_count = ATOMIC_INIT(0);
45
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -080046EXPORT_SYMBOL_GPL(dccp_orphan_count);
47
Arnaldo Carvalho de Melo075ae862006-03-20 21:24:19 -080048struct inet_hashinfo __cacheline_aligned dccp_hashinfo = {
49 .lhash_lock = RW_LOCK_UNLOCKED,
50 .lhash_users = ATOMIC_INIT(0),
51 .lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(dccp_hashinfo.lhash_wait),
52};
53
54EXPORT_SYMBOL_GPL(dccp_hashinfo);
55
Ian McDonaldb1308dc2006-11-20 18:30:17 -020056/* the maximum queue length for tx in packets. 0 is no limit */
57int sysctl_dccp_tx_qlen __read_mostly = 5;
58
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080059void dccp_set_state(struct sock *sk, const int state)
60{
61 const int oldstate = sk->sk_state;
62
Gerrit Renkerf11135a2007-11-28 11:34:53 -020063 dccp_pr_debug("%s(%p) %s --> %s\n", dccp_role(sk), sk,
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080064 dccp_state_name(oldstate), dccp_state_name(state));
65 WARN_ON(state == oldstate);
66
67 switch (state) {
68 case DCCP_OPEN:
69 if (oldstate != DCCP_OPEN)
70 DCCP_INC_STATS(DCCP_MIB_CURRESTAB);
71 break;
72
73 case DCCP_CLOSED:
Gerrit Renker0c869622007-11-28 11:59:48 -020074 if (oldstate == DCCP_OPEN || oldstate == DCCP_ACTIVE_CLOSEREQ ||
75 oldstate == DCCP_CLOSING)
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080076 DCCP_INC_STATS(DCCP_MIB_ESTABRESETS);
77
78 sk->sk_prot->unhash(sk);
79 if (inet_csk(sk)->icsk_bind_hash != NULL &&
80 !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))
Arnaldo Carvalho de Meloab1e0a12008-02-03 04:06:04 -080081 inet_put_port(sk);
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080082 /* fall through */
83 default:
84 if (oldstate == DCCP_OPEN)
85 DCCP_DEC_STATS(DCCP_MIB_CURRESTAB);
86 }
87
88 /* Change state AFTER socket is unhashed to avoid closed
89 * socket sitting in hash tables.
90 */
91 sk->sk_state = state;
92}
93
94EXPORT_SYMBOL_GPL(dccp_set_state);
95
Gerrit Renker0c869622007-11-28 11:59:48 -020096static void dccp_finish_passive_close(struct sock *sk)
97{
98 switch (sk->sk_state) {
99 case DCCP_PASSIVE_CLOSE:
100 /* Node (client or server) has received Close packet. */
101 dccp_send_reset(sk, DCCP_RESET_CODE_CLOSED);
102 dccp_set_state(sk, DCCP_CLOSED);
103 break;
104 case DCCP_PASSIVE_CLOSEREQ:
105 /*
106 * Client received CloseReq. We set the `active' flag so that
107 * dccp_send_close() retransmits the Close as per RFC 4340, 8.3.
108 */
109 dccp_send_close(sk, 1);
110 dccp_set_state(sk, DCCP_CLOSING);
111 }
112}
113
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -0800114void dccp_done(struct sock *sk)
115{
116 dccp_set_state(sk, DCCP_CLOSED);
117 dccp_clear_xmit_timers(sk);
118
119 sk->sk_shutdown = SHUTDOWN_MASK;
120
121 if (!sock_flag(sk, SOCK_DEAD))
122 sk->sk_state_change(sk);
123 else
124 inet_csk_destroy_sock(sk);
125}
126
127EXPORT_SYMBOL_GPL(dccp_done);
128
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700129const char *dccp_packet_name(const int type)
130{
131 static const char *dccp_packet_names[] = {
132 [DCCP_PKT_REQUEST] = "REQUEST",
133 [DCCP_PKT_RESPONSE] = "RESPONSE",
134 [DCCP_PKT_DATA] = "DATA",
135 [DCCP_PKT_ACK] = "ACK",
136 [DCCP_PKT_DATAACK] = "DATAACK",
137 [DCCP_PKT_CLOSEREQ] = "CLOSEREQ",
138 [DCCP_PKT_CLOSE] = "CLOSE",
139 [DCCP_PKT_RESET] = "RESET",
140 [DCCP_PKT_SYNC] = "SYNC",
141 [DCCP_PKT_SYNCACK] = "SYNCACK",
142 };
143
144 if (type >= DCCP_NR_PKT_TYPES)
145 return "INVALID";
146 else
147 return dccp_packet_names[type];
148}
149
150EXPORT_SYMBOL_GPL(dccp_packet_name);
151
152const char *dccp_state_name(const int state)
153{
154 static char *dccp_state_names[] = {
Gerrit Renkerf11135a2007-11-28 11:34:53 -0200155 [DCCP_OPEN] = "OPEN",
156 [DCCP_REQUESTING] = "REQUESTING",
157 [DCCP_PARTOPEN] = "PARTOPEN",
158 [DCCP_LISTEN] = "LISTEN",
159 [DCCP_RESPOND] = "RESPOND",
160 [DCCP_CLOSING] = "CLOSING",
161 [DCCP_ACTIVE_CLOSEREQ] = "CLOSEREQ",
162 [DCCP_PASSIVE_CLOSE] = "PASSIVE_CLOSE",
163 [DCCP_PASSIVE_CLOSEREQ] = "PASSIVE_CLOSEREQ",
164 [DCCP_TIME_WAIT] = "TIME_WAIT",
165 [DCCP_CLOSED] = "CLOSED",
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700166 };
167
168 if (state >= DCCP_MAX_STATES)
169 return "INVALID STATE!";
170 else
171 return dccp_state_names[state];
172}
173
174EXPORT_SYMBOL_GPL(dccp_state_name);
175
Arnaldo Carvalho de Melo72478872006-03-20 22:00:37 -0800176int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800177{
178 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800179 struct dccp_minisock *dmsk = dccp_msk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800180 struct inet_connection_sock *icsk = inet_csk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800181
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800182 dccp_minisock_init(&dp->dccps_minisock);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800183
Arnaldo Carvalho de Meloe18d7a92007-11-24 21:42:53 -0200184 icsk->icsk_rto = DCCP_TIMEOUT_INIT;
185 icsk->icsk_syn_retries = sysctl_dccp_request_retries;
186 sk->sk_state = DCCP_CLOSED;
187 sk->sk_write_space = dccp_write_space;
188 icsk->icsk_sync_mss = dccp_sync_mss;
189 dp->dccps_mss_cache = 536;
190 dp->dccps_rate_last = jiffies;
191 dp->dccps_role = DCCP_ROLE_UNDEFINED;
192 dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT;
193 dp->dccps_l_ack_ratio = dp->dccps_r_ack_ratio = 1;
194
195 dccp_init_xmit_timers(sk);
196
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800197 /*
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 */
Arnaldo Carvalho de Melo72478872006-03-20 22:00:37 -0800204 if (likely(ctl_sock_initialized)) {
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800205 int rc = dccp_feat_init(dmsk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800206
207 if (rc)
208 return rc;
209
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800210 if (dmsk->dccpms_send_ack_vector) {
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800211 dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL);
212 if (dp->dccps_hc_rx_ackvec == NULL)
213 return -ENOMEM;
214 }
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800215 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);
Arnaldo Carvalho de Melo8109b022006-12-10 16:01:18 -0200219 if (unlikely(dp->dccps_hc_rx_ccid == NULL ||
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800220 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);
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800223 if (dmsk->dccpms_send_ack_vector) {
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800224 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 */
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800232 INIT_LIST_HEAD(&dmsk->dccpms_pending);
233 INIT_LIST_HEAD(&dmsk->dccpms_conf);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800234 }
235
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800236 return 0;
237}
238
239EXPORT_SYMBOL_GPL(dccp_init_sock);
240
241int dccp_destroy_sock(struct sock *sk)
242{
243 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800244 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
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800262 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 */
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800271 dccp_feat_clean(dmsk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800272
273 return 0;
274}
275
276EXPORT_SYMBOL_GPL(dccp_destroy_sock);
277
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800278static inline int dccp_listen_start(struct sock *sk, int backlog)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700279{
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700280 struct dccp_sock *dp = dccp_sk(sk);
281
282 dp->dccps_role = DCCP_ROLE_LISTEN;
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800283 return inet_csk_listen_start(sk, backlog);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700284}
285
Gerrit Renkerce865a62007-11-24 22:14:15 -0200286static inline int dccp_need_reset(int state)
287{
288 return state != DCCP_CLOSED && state != DCCP_LISTEN &&
289 state != DCCP_REQUESTING;
290}
291
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700292int dccp_disconnect(struct sock *sk, int flags)
293{
294 struct inet_connection_sock *icsk = inet_csk(sk);
295 struct inet_sock *inet = inet_sk(sk);
296 int err = 0;
297 const int old_state = sk->sk_state;
298
299 if (old_state != DCCP_CLOSED)
300 dccp_set_state(sk, DCCP_CLOSED);
301
Gerrit Renkerce865a62007-11-24 22:14:15 -0200302 /*
303 * This corresponds to the ABORT function of RFC793, sec. 3.8
304 * TCP uses a RST segment, DCCP a Reset packet with Code 2, "Aborted".
305 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700306 if (old_state == DCCP_LISTEN) {
307 inet_csk_listen_stop(sk);
Gerrit Renkerce865a62007-11-24 22:14:15 -0200308 } else if (dccp_need_reset(old_state)) {
309 dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
310 sk->sk_err = ECONNRESET;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700311 } else if (old_state == DCCP_REQUESTING)
312 sk->sk_err = ECONNRESET;
313
314 dccp_clear_xmit_timers(sk);
315 __skb_queue_purge(&sk->sk_receive_queue);
316 if (sk->sk_send_head != NULL) {
317 __kfree_skb(sk->sk_send_head);
318 sk->sk_send_head = NULL;
319 }
320
321 inet->dport = 0;
322
323 if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
324 inet_reset_saddr(sk);
325
326 sk->sk_shutdown = 0;
327 sock_reset_flag(sk, SOCK_DONE);
328
329 icsk->icsk_backoff = 0;
330 inet_csk_delack_init(sk);
331 __sk_dst_reset(sk);
332
333 BUG_TRAP(!inet->num || icsk->icsk_bind_hash);
334
335 sk->sk_error_report(sk);
336 return err;
337}
338
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800339EXPORT_SYMBOL_GPL(dccp_disconnect);
340
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700341/*
342 * Wait for a DCCP event.
343 *
344 * Note that we don't need to lock the socket, as the upper poll layers
345 * take care of normal races (between the test and the event) and we don't
346 * go look at any of the socket buffers directly.
347 */
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800348unsigned int dccp_poll(struct file *file, struct socket *sock,
349 poll_table *wait)
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700350{
351 unsigned int mask;
352 struct sock *sk = sock->sk;
353
354 poll_wait(file, sk->sk_sleep, wait);
355 if (sk->sk_state == DCCP_LISTEN)
356 return inet_csk_listen_poll(sk);
357
358 /* Socket is not locked. We are protected from async events
359 by poll logic and correct handling of state changes
360 made by another threads is impossible in any case.
361 */
362
363 mask = 0;
364 if (sk->sk_err)
365 mask = POLLERR;
366
367 if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED)
368 mask |= POLLHUP;
369 if (sk->sk_shutdown & RCV_SHUTDOWN)
Davide Libenzif348d702006-03-25 03:07:39 -0800370 mask |= POLLIN | POLLRDNORM | POLLRDHUP;
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700371
372 /* Connected? */
373 if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) {
374 if (atomic_read(&sk->sk_rmem_alloc) > 0)
375 mask |= POLLIN | POLLRDNORM;
376
377 if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {
378 if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {
379 mask |= POLLOUT | POLLWRNORM;
380 } else { /* send SIGIO later */
381 set_bit(SOCK_ASYNC_NOSPACE,
382 &sk->sk_socket->flags);
383 set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
384
385 /* Race breaker. If space is freed after
386 * wspace test but before the flags are set,
387 * IO signal will be lost.
388 */
389 if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk))
390 mask |= POLLOUT | POLLWRNORM;
391 }
392 }
393 }
394 return mask;
395}
396
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800397EXPORT_SYMBOL_GPL(dccp_poll);
398
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700399int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
400{
Arnaldo Carvalho de Melo62731722007-10-23 20:23:30 -0700401 int rc = -ENOTCONN;
402
403 lock_sock(sk);
404
405 if (sk->sk_state == DCCP_LISTEN)
406 goto out;
407
408 switch (cmd) {
409 case SIOCINQ: {
410 struct sk_buff *skb;
411 unsigned long amount = 0;
412
413 skb = skb_peek(&sk->sk_receive_queue);
414 if (skb != NULL) {
415 /*
416 * We will only return the amount of this packet since
417 * that is all that will be read.
418 */
419 amount = skb->len;
420 }
421 rc = put_user(amount, (int __user *)arg);
422 }
423 break;
424 default:
425 rc = -ENOIOCTLCMD;
426 break;
427 }
428out:
429 release_sock(sk);
430 return rc;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700431}
432
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800433EXPORT_SYMBOL_GPL(dccp_ioctl);
434
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800435static int dccp_setsockopt_service(struct sock *sk, const __be32 service,
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700436 char __user *optval, int optlen)
437{
438 struct dccp_sock *dp = dccp_sk(sk);
439 struct dccp_service_list *sl = NULL;
440
Arnaldo Carvalho de Melo8109b022006-12-10 16:01:18 -0200441 if (service == DCCP_SERVICE_INVALID_VALUE ||
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700442 optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32))
443 return -EINVAL;
444
445 if (optlen > sizeof(service)) {
446 sl = kmalloc(optlen, GFP_KERNEL);
447 if (sl == NULL)
448 return -ENOMEM;
449
450 sl->dccpsl_nr = optlen / sizeof(u32) - 1;
451 if (copy_from_user(sl->dccpsl_list,
452 optval + sizeof(service),
453 optlen - sizeof(service)) ||
454 dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) {
455 kfree(sl);
456 return -EFAULT;
457 }
458 }
459
460 lock_sock(sk);
461 dp->dccps_service = service;
462
Jesper Juhla51482b2005-11-08 09:41:34 -0800463 kfree(dp->dccps_service_list);
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700464
465 dp->dccps_service_list = sl;
466 release_sock(sk);
467 return 0;
468}
469
Andrea Bittauafe00252006-03-20 17:43:56 -0800470/* byte 1 is feature. the rest is the preference list */
471static int dccp_setsockopt_change(struct sock *sk, int type,
472 struct dccp_so_feat __user *optval)
473{
474 struct dccp_so_feat opt;
475 u8 *val;
476 int rc;
477
478 if (copy_from_user(&opt, optval, sizeof(opt)))
479 return -EFAULT;
480
481 val = kmalloc(opt.dccpsf_len, GFP_KERNEL);
482 if (!val)
483 return -ENOMEM;
484
485 if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) {
486 rc = -EFAULT;
487 goto out_free_val;
488 }
489
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800490 rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat,
491 val, opt.dccpsf_len, GFP_KERNEL);
Andrea Bittauafe00252006-03-20 17:43:56 -0800492 if (rc)
493 goto out_free_val;
494
495out:
496 return rc;
497
498out_free_val:
499 kfree(val);
500 goto out;
501}
502
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800503static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
504 char __user *optval, int optlen)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700505{
Gerrit Renker09dbc382006-11-14 12:57:34 -0200506 struct dccp_sock *dp = dccp_sk(sk);
507 int val, err = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700508
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300509 if (optlen < sizeof(int))
510 return -EINVAL;
511
512 if (get_user(val, (int __user *)optval))
513 return -EFAULT;
514
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700515 if (optname == DCCP_SOCKOPT_SERVICE)
516 return dccp_setsockopt_service(sk, val, optval, optlen);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300517
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700518 lock_sock(sk);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300519 switch (optname) {
520 case DCCP_SOCKOPT_PACKET_SIZE:
Gerrit Renker5aed3242006-11-28 19:33:36 -0200521 DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
Arnaldo Carvalho de Melo841bac12006-11-28 19:42:03 -0200522 err = 0;
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300523 break;
Andrea Bittauafe00252006-03-20 17:43:56 -0800524 case DCCP_SOCKOPT_CHANGE_L:
525 if (optlen != sizeof(struct dccp_so_feat))
526 err = -EINVAL;
527 else
528 err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L,
YOSHIFUJI Hideakic9eaf172007-02-09 23:24:38 +0900529 (struct dccp_so_feat __user *)
Andrea Bittauafe00252006-03-20 17:43:56 -0800530 optval);
531 break;
Andrea Bittauafe00252006-03-20 17:43:56 -0800532 case DCCP_SOCKOPT_CHANGE_R:
533 if (optlen != sizeof(struct dccp_so_feat))
534 err = -EINVAL;
535 else
536 err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R,
Alan Cox9faefb62006-07-10 14:24:23 -0700537 (struct dccp_so_feat __user *)
Andrea Bittauafe00252006-03-20 17:43:56 -0800538 optval);
539 break;
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200540 case DCCP_SOCKOPT_SERVER_TIMEWAIT:
541 if (dp->dccps_role != DCCP_ROLE_SERVER)
542 err = -EOPNOTSUPP;
543 else
544 dp->dccps_server_timewait = (val != 0);
545 break;
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200546 case DCCP_SOCKOPT_SEND_CSCOV: /* sender side, RFC 4340, sec. 9.2 */
547 if (val < 0 || val > 15)
548 err = -EINVAL;
549 else
550 dp->dccps_pcslen = val;
551 break;
552 case DCCP_SOCKOPT_RECV_CSCOV: /* receiver side, RFC 4340 sec. 9.2.1 */
553 if (val < 0 || val > 15)
554 err = -EINVAL;
555 else {
556 dp->dccps_pcrlen = val;
557 /* FIXME: add feature negotiation,
558 * ChangeL(MinimumChecksumCoverage, val) */
559 }
560 break;
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300561 default:
562 err = -ENOPROTOOPT;
563 break;
564 }
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200565
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300566 release_sock(sk);
567 return err;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700568}
569
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800570int dccp_setsockopt(struct sock *sk, int level, int optname,
571 char __user *optval, int optlen)
572{
573 if (level != SOL_DCCP)
574 return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
575 optname, optval,
576 optlen);
577 return do_dccp_setsockopt(sk, level, optname, optval, optlen);
578}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800579
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800580EXPORT_SYMBOL_GPL(dccp_setsockopt);
581
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800582#ifdef CONFIG_COMPAT
583int compat_dccp_setsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800584 char __user *optval, int optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800585{
Arnaldo Carvalho de Melodec73ff2006-03-20 22:46:16 -0800586 if (level != SOL_DCCP)
587 return inet_csk_compat_setsockopt(sk, level, optname,
588 optval, optlen);
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800589 return do_dccp_setsockopt(sk, level, optname, optval, optlen);
590}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800591
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800592EXPORT_SYMBOL_GPL(compat_dccp_setsockopt);
593#endif
594
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700595static int dccp_getsockopt_service(struct sock *sk, int len,
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800596 __be32 __user *optval,
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700597 int __user *optlen)
598{
599 const struct dccp_sock *dp = dccp_sk(sk);
600 const struct dccp_service_list *sl;
601 int err = -ENOENT, slen = 0, total_len = sizeof(u32);
602
603 lock_sock(sk);
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700604 if ((sl = dp->dccps_service_list) != NULL) {
605 slen = sl->dccpsl_nr * sizeof(u32);
606 total_len += slen;
607 }
608
609 err = -EINVAL;
610 if (total_len > len)
611 goto out;
612
613 err = 0;
614 if (put_user(total_len, optlen) ||
615 put_user(dp->dccps_service, optval) ||
616 (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen)))
617 err = -EFAULT;
618out:
619 release_sock(sk);
620 return err;
621}
622
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800623static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -0300624 char __user *optval, int __user *optlen)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700625{
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300626 struct dccp_sock *dp;
627 int val, len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700628
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300629 if (get_user(len, optlen))
630 return -EFAULT;
631
Arnaldo Carvalho de Melo39ebc022007-03-28 11:54:32 -0700632 if (len < (int)sizeof(int))
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300633 return -EINVAL;
634
635 dp = dccp_sk(sk);
636
637 switch (optname) {
638 case DCCP_SOCKOPT_PACKET_SIZE:
Gerrit Renker5aed3242006-11-28 19:33:36 -0200639 DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
Arnaldo Carvalho de Melo841bac12006-11-28 19:42:03 -0200640 return 0;
Arnaldo Carvalho de Melo88f964d2005-09-18 00:19:32 -0700641 case DCCP_SOCKOPT_SERVICE:
642 return dccp_getsockopt_service(sk, len,
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800643 (__be32 __user *)optval, optlen);
Gerrit Renker7c559a92007-10-04 14:39:22 -0700644 case DCCP_SOCKOPT_GET_CUR_MPS:
645 val = dp->dccps_mss_cache;
Gerrit Renker7c559a92007-10-04 14:39:22 -0700646 break;
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200647 case DCCP_SOCKOPT_SERVER_TIMEWAIT:
648 val = dp->dccps_server_timewait;
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200649 break;
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200650 case DCCP_SOCKOPT_SEND_CSCOV:
651 val = dp->dccps_pcslen;
652 break;
653 case DCCP_SOCKOPT_RECV_CSCOV:
654 val = dp->dccps_pcrlen;
655 break;
Arnaldo Carvalho de Melo88f964d2005-09-18 00:19:32 -0700656 case 128 ... 191:
657 return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
658 len, (u32 __user *)optval, optlen);
659 case 192 ... 255:
660 return ccid_hc_tx_getsockopt(dp->dccps_hc_tx_ccid, sk, optname,
661 len, (u32 __user *)optval, optlen);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300662 default:
663 return -ENOPROTOOPT;
664 }
665
Gerrit Renker79133502007-12-13 12:27:14 -0200666 len = sizeof(val);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300667 if (put_user(len, optlen) || copy_to_user(optval, &val, len))
668 return -EFAULT;
669
670 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700671}
672
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800673int dccp_getsockopt(struct sock *sk, int level, int optname,
674 char __user *optval, int __user *optlen)
675{
676 if (level != SOL_DCCP)
677 return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
678 optname, optval,
679 optlen);
680 return do_dccp_getsockopt(sk, level, optname, optval, optlen);
681}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800682
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800683EXPORT_SYMBOL_GPL(dccp_getsockopt);
684
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800685#ifdef CONFIG_COMPAT
686int compat_dccp_getsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800687 char __user *optval, int __user *optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800688{
Arnaldo Carvalho de Melodec73ff2006-03-20 22:46:16 -0800689 if (level != SOL_DCCP)
690 return inet_csk_compat_getsockopt(sk, level, optname,
691 optval, optlen);
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800692 return do_dccp_getsockopt(sk, level, optname, optval, optlen);
693}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800694
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800695EXPORT_SYMBOL_GPL(compat_dccp_getsockopt);
696#endif
697
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700698int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
699 size_t len)
700{
701 const struct dccp_sock *dp = dccp_sk(sk);
702 const int flags = msg->msg_flags;
703 const int noblock = flags & MSG_DONTWAIT;
704 struct sk_buff *skb;
705 int rc, size;
706 long timeo;
707
708 if (len > dp->dccps_mss_cache)
709 return -EMSGSIZE;
710
711 lock_sock(sk);
Ian McDonaldb1308dc2006-11-20 18:30:17 -0200712
713 if (sysctl_dccp_tx_qlen &&
714 (sk->sk_write_queue.qlen >= sysctl_dccp_tx_qlen)) {
715 rc = -EAGAIN;
716 goto out_release;
717 }
718
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700719 timeo = sock_sndtimeo(sk, noblock);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700720
721 /*
722 * We have to use sk_stream_wait_connect here to set sk_write_pending,
723 * so that the trick in dccp_rcv_request_sent_state_process.
724 */
725 /* Wait for a connection to finish. */
Gerrit Renkercecd8d02007-09-26 19:36:08 -0300726 if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN))
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700727 if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0)
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700728 goto out_release;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700729
730 size = sk->sk_prot->max_header + len;
731 release_sock(sk);
732 skb = sock_alloc_send_skb(sk, size, noblock, &rc);
733 lock_sock(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700734 if (skb == NULL)
735 goto out_release;
736
737 skb_reserve(skb, sk->sk_prot->max_header);
738 rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700739 if (rc != 0)
740 goto out_discard;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700741
Ian McDonald97e58482006-08-26 19:16:45 -0700742 skb_queue_tail(&sk->sk_write_queue, skb);
743 dccp_write_xmit(sk,0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700744out_release:
745 release_sock(sk);
746 return rc ? : len;
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700747out_discard:
748 kfree_skb(skb);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700749 goto out_release;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700750}
751
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800752EXPORT_SYMBOL_GPL(dccp_sendmsg);
753
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700754int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
755 size_t len, int nonblock, int flags, int *addr_len)
756{
757 const struct dccp_hdr *dh;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700758 long timeo;
759
760 lock_sock(sk);
761
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300762 if (sk->sk_state == DCCP_LISTEN) {
763 len = -ENOTCONN;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700764 goto out;
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300765 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700766
767 timeo = sock_rcvtimeo(sk, nonblock);
768
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700769 do {
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300770 struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700771
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300772 if (skb == NULL)
773 goto verify_sock_status;
774
775 dh = dccp_hdr(skb);
776
Gerrit Renker0c869622007-11-28 11:59:48 -0200777 switch (dh->dccph_type) {
778 case DCCP_PKT_DATA:
779 case DCCP_PKT_DATAACK:
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300780 goto found_ok_skb;
781
Gerrit Renker0c869622007-11-28 11:59:48 -0200782 case DCCP_PKT_CLOSE:
783 case DCCP_PKT_CLOSEREQ:
784 if (!(flags & MSG_PEEK))
785 dccp_finish_passive_close(sk);
786 /* fall through */
787 case DCCP_PKT_RESET:
788 dccp_pr_debug("found fin (%s) ok!\n",
789 dccp_packet_name(dh->dccph_type));
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300790 len = 0;
791 goto found_fin_ok;
Gerrit Renker0c869622007-11-28 11:59:48 -0200792 default:
793 dccp_pr_debug("packet_type=%s\n",
794 dccp_packet_name(dh->dccph_type));
795 sk_eat_skb(sk, skb, 0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700796 }
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300797verify_sock_status:
798 if (sock_flag(sk, SOCK_DONE)) {
799 len = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700800 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700801 }
802
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300803 if (sk->sk_err) {
804 len = sock_error(sk);
805 break;
806 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700807
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300808 if (sk->sk_shutdown & RCV_SHUTDOWN) {
809 len = 0;
810 break;
811 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700812
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300813 if (sk->sk_state == DCCP_CLOSED) {
814 if (!sock_flag(sk, SOCK_DONE)) {
815 /* This occurs when user tries to read
816 * from never connected socket.
817 */
818 len = -ENOTCONN;
819 break;
820 }
821 len = 0;
822 break;
823 }
824
825 if (!timeo) {
826 len = -EAGAIN;
827 break;
828 }
829
830 if (signal_pending(current)) {
831 len = sock_intr_errno(timeo);
832 break;
833 }
834
835 sk_wait_data(sk, &timeo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700836 continue;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700837 found_ok_skb:
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300838 if (len > skb->len)
839 len = skb->len;
840 else if (len < skb->len)
841 msg->msg_flags |= MSG_TRUNC;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700842
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300843 if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len)) {
844 /* Exception. Bailout! */
845 len = -EFAULT;
846 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700847 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700848 found_fin_ok:
849 if (!(flags & MSG_PEEK))
Chris Leech624d1162006-05-23 18:01:28 -0700850 sk_eat_skb(sk, skb, 0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700851 break;
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300852 } while (1);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700853out:
854 release_sock(sk);
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300855 return len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700856}
857
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800858EXPORT_SYMBOL_GPL(dccp_recvmsg);
859
860int inet_dccp_listen(struct socket *sock, int backlog)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700861{
862 struct sock *sk = sock->sk;
863 unsigned char old_state;
864 int err;
865
866 lock_sock(sk);
867
868 err = -EINVAL;
869 if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP)
870 goto out;
871
872 old_state = sk->sk_state;
873 if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN)))
874 goto out;
875
876 /* Really, if the socket is already in listen state
877 * we can only allow the backlog to be adjusted.
878 */
879 if (old_state != DCCP_LISTEN) {
880 /*
881 * FIXME: here it probably should be sk->sk_prot->listen_start
882 * see tcp_listen_start
883 */
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800884 err = dccp_listen_start(sk, backlog);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700885 if (err)
886 goto out;
887 }
888 sk->sk_max_ack_backlog = backlog;
889 err = 0;
890
891out:
892 release_sock(sk);
893 return err;
894}
895
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800896EXPORT_SYMBOL_GPL(inet_dccp_listen);
897
Gerrit Renker0c869622007-11-28 11:59:48 -0200898static void dccp_terminate_connection(struct sock *sk)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700899{
Gerrit Renker0c869622007-11-28 11:59:48 -0200900 u8 next_state = DCCP_CLOSED;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700901
Gerrit Renker0c869622007-11-28 11:59:48 -0200902 switch (sk->sk_state) {
903 case DCCP_PASSIVE_CLOSE:
904 case DCCP_PASSIVE_CLOSEREQ:
905 dccp_finish_passive_close(sk);
906 break;
907 case DCCP_PARTOPEN:
908 dccp_pr_debug("Stop PARTOPEN timer (%p)\n", sk);
909 inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);
910 /* fall through */
911 case DCCP_OPEN:
912 dccp_send_close(sk, 1);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700913
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200914 if (dccp_sk(sk)->dccps_role == DCCP_ROLE_SERVER &&
915 !dccp_sk(sk)->dccps_server_timewait)
Gerrit Renker0c869622007-11-28 11:59:48 -0200916 next_state = DCCP_ACTIVE_CLOSEREQ;
917 else
918 next_state = DCCP_CLOSING;
919 /* fall through */
920 default:
921 dccp_set_state(sk, next_state);
922 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700923}
924
925void dccp_close(struct sock *sk, long timeout)
926{
Ian McDonald97e58482006-08-26 19:16:45 -0700927 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700928 struct sk_buff *skb;
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800929 u32 data_was_unread = 0;
Herbert Xu134af342006-05-05 17:09:13 -0700930 int state;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700931
932 lock_sock(sk);
933
934 sk->sk_shutdown = SHUTDOWN_MASK;
935
936 if (sk->sk_state == DCCP_LISTEN) {
937 dccp_set_state(sk, DCCP_CLOSED);
938
939 /* Special case. */
940 inet_csk_listen_stop(sk);
941
942 goto adjudge_to_death;
943 }
944
Ian McDonald97e58482006-08-26 19:16:45 -0700945 sk_stop_timer(sk, &dp->dccps_xmit_timer);
946
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700947 /*
948 * We need to flush the recv. buffs. We do this only on the
949 * descriptor close, not protocol-sourced closes, because the
950 *reader process may not have drained the data yet!
951 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700952 while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800953 data_was_unread += skb->len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700954 __kfree_skb(skb);
955 }
956
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800957 if (data_was_unread) {
958 /* Unread data was tossed, send an appropriate Reset Code */
959 DCCP_WARN("DCCP: ABORT -- %u bytes unread\n", data_was_unread);
960 dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
961 dccp_set_state(sk, DCCP_CLOSED);
962 } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700963 /* Check zero linger _after_ checking for unread data. */
964 sk->sk_prot->disconnect(sk, 0);
Gerrit Renker0c869622007-11-28 11:59:48 -0200965 } else if (sk->sk_state != DCCP_CLOSED) {
966 dccp_terminate_connection(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700967 }
968
969 sk_stream_wait_close(sk, timeout);
970
971adjudge_to_death:
Herbert Xu134af342006-05-05 17:09:13 -0700972 state = sk->sk_state;
973 sock_hold(sk);
974 sock_orphan(sk);
975 atomic_inc(sk->sk_prot->orphan_count);
976
Arnaldo Carvalho de Melo7ad07e72005-08-23 21:50:06 -0700977 /*
978 * It is the last release_sock in its life. It will remove backlog.
979 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700980 release_sock(sk);
981 /*
982 * Now socket is owned by kernel and we acquire BH lock
983 * to finish close. No need to check for user refs.
984 */
985 local_bh_disable();
986 bh_lock_sock(sk);
987 BUG_TRAP(!sock_owned_by_user(sk));
988
Herbert Xu134af342006-05-05 17:09:13 -0700989 /* Have we already been destroyed by a softirq or backlog? */
990 if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
991 goto out;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700992
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700993 if (sk->sk_state == DCCP_CLOSED)
994 inet_csk_destroy_sock(sk);
995
996 /* Otherwise, socket is reprieved until protocol close. */
997
Herbert Xu134af342006-05-05 17:09:13 -0700998out:
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700999 bh_unlock_sock(sk);
1000 local_bh_enable();
1001 sock_put(sk);
1002}
1003
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001004EXPORT_SYMBOL_GPL(dccp_close);
1005
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001006void dccp_shutdown(struct sock *sk, int how)
1007{
Gerrit Renker8e8c71f2007-11-21 09:56:48 -02001008 dccp_pr_debug("called shutdown(%x)\n", how);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001009}
1010
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001011EXPORT_SYMBOL_GPL(dccp_shutdown);
1012
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001013static int __init dccp_mib_init(void)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001014{
1015 int rc = -ENOMEM;
1016
1017 dccp_statistics[0] = alloc_percpu(struct dccp_mib);
1018 if (dccp_statistics[0] == NULL)
1019 goto out;
1020
1021 dccp_statistics[1] = alloc_percpu(struct dccp_mib);
1022 if (dccp_statistics[1] == NULL)
1023 goto out_free_one;
1024
1025 rc = 0;
1026out:
1027 return rc;
1028out_free_one:
1029 free_percpu(dccp_statistics[0]);
1030 dccp_statistics[0] = NULL;
1031 goto out;
1032
1033}
1034
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001035static void dccp_mib_exit(void)
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001036{
1037 free_percpu(dccp_statistics[0]);
1038 free_percpu(dccp_statistics[1]);
1039 dccp_statistics[0] = dccp_statistics[1] = NULL;
1040}
1041
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001042static int thash_entries;
1043module_param(thash_entries, int, 0444);
1044MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
1045
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -03001046#ifdef CONFIG_IP_DCCP_DEBUG
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001047int dccp_debug;
Gerrit Renker042d18f2007-10-04 14:39:53 -07001048module_param(dccp_debug, bool, 0444);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001049MODULE_PARM_DESC(dccp_debug, "Enable debug messages");
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001050
1051EXPORT_SYMBOL_GPL(dccp_debug);
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -03001052#endif
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001053
1054static int __init dccp_init(void)
1055{
1056 unsigned long goal;
1057 int ehash_order, bhash_order, i;
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001058 int rc = -ENOBUFS;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001059
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001060 dccp_hashinfo.bind_bucket_cachep =
1061 kmem_cache_create("dccp_bind_bucket",
1062 sizeof(struct inet_bind_bucket), 0,
Paul Mundt20c2df82007-07-20 10:11:58 +09001063 SLAB_HWCACHE_ALIGN, NULL);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001064 if (!dccp_hashinfo.bind_bucket_cachep)
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001065 goto out;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001066
1067 /*
1068 * Size and allocate the main established and bind bucket
1069 * hash tables.
1070 *
1071 * The methodology is similar to that of the buffer cache.
1072 */
1073 if (num_physpages >= (128 * 1024))
1074 goal = num_physpages >> (21 - PAGE_SHIFT);
1075 else
1076 goal = num_physpages >> (23 - PAGE_SHIFT);
1077
1078 if (thash_entries)
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001079 goal = (thash_entries *
1080 sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001081 for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++)
1082 ;
1083 do {
1084 dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE /
1085 sizeof(struct inet_ehash_bucket);
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001086 while (dccp_hashinfo.ehash_size &
1087 (dccp_hashinfo.ehash_size - 1))
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001088 dccp_hashinfo.ehash_size--;
1089 dccp_hashinfo.ehash = (struct inet_ehash_bucket *)
1090 __get_free_pages(GFP_ATOMIC, ehash_order);
1091 } while (!dccp_hashinfo.ehash && --ehash_order > 0);
1092
1093 if (!dccp_hashinfo.ehash) {
Gerrit Renker59348b12006-11-20 18:39:23 -02001094 DCCP_CRIT("Failed to allocate DCCP established hash table");
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001095 goto out_free_bind_bucket_cachep;
1096 }
1097
Eric Dumazetdbca9b2752007-02-08 14:16:46 -08001098 for (i = 0; i < dccp_hashinfo.ehash_size; i++) {
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001099 INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain);
Eric Dumazetdbca9b2752007-02-08 14:16:46 -08001100 INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].twchain);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001101 }
1102
Eric Dumazet230140c2007-11-07 02:40:20 -08001103 if (inet_ehash_locks_alloc(&dccp_hashinfo))
1104 goto out_free_dccp_ehash;
1105
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001106 bhash_order = ehash_order;
1107
1108 do {
1109 dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE /
1110 sizeof(struct inet_bind_hashbucket);
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001111 if ((dccp_hashinfo.bhash_size > (64 * 1024)) &&
1112 bhash_order > 0)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001113 continue;
1114 dccp_hashinfo.bhash = (struct inet_bind_hashbucket *)
1115 __get_free_pages(GFP_ATOMIC, bhash_order);
1116 } while (!dccp_hashinfo.bhash && --bhash_order >= 0);
1117
1118 if (!dccp_hashinfo.bhash) {
Gerrit Renker59348b12006-11-20 18:39:23 -02001119 DCCP_CRIT("Failed to allocate DCCP bind hash table");
Eric Dumazet230140c2007-11-07 02:40:20 -08001120 goto out_free_dccp_locks;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001121 }
1122
1123 for (i = 0; i < dccp_hashinfo.bhash_size; i++) {
1124 spin_lock_init(&dccp_hashinfo.bhash[i].lock);
1125 INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
1126 }
1127
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001128 rc = dccp_mib_init();
Arnaldo Carvalho de Melofa23e2e2006-03-20 17:16:01 -08001129 if (rc)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001130 goto out_free_dccp_bhash;
1131
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001132 rc = dccp_ackvec_init();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001133 if (rc)
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001134 goto out_free_dccp_mib;
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001135
Arnaldo Carvalho de Meloe55d9122006-03-20 19:25:02 -08001136 rc = dccp_sysctl_init();
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001137 if (rc)
1138 goto out_ackvec_exit;
Gerrit Renker4c70f382007-09-25 22:40:13 -07001139
1140 dccp_timestamping_init();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001141out:
1142 return rc;
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001143out_ackvec_exit:
1144 dccp_ackvec_exit();
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001145out_free_dccp_mib:
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001146 dccp_mib_exit();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001147out_free_dccp_bhash:
1148 free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
1149 dccp_hashinfo.bhash = NULL;
Eric Dumazet230140c2007-11-07 02:40:20 -08001150out_free_dccp_locks:
1151 inet_ehash_locks_free(&dccp_hashinfo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001152out_free_dccp_ehash:
1153 free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order);
1154 dccp_hashinfo.ehash = NULL;
1155out_free_bind_bucket_cachep:
1156 kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
1157 dccp_hashinfo.bind_bucket_cachep = NULL;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001158 goto out;
1159}
1160
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001161static void __exit dccp_fini(void)
1162{
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001163 dccp_mib_exit();
Arnaldo Carvalho de Melo725ba8e2005-08-13 20:35:39 -03001164 free_pages((unsigned long)dccp_hashinfo.bhash,
1165 get_order(dccp_hashinfo.bhash_size *
1166 sizeof(struct inet_bind_hashbucket)));
1167 free_pages((unsigned long)dccp_hashinfo.ehash,
1168 get_order(dccp_hashinfo.ehash_size *
1169 sizeof(struct inet_ehash_bucket)));
Eric Dumazet230140c2007-11-07 02:40:20 -08001170 inet_ehash_locks_free(&dccp_hashinfo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001171 kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001172 dccp_ackvec_exit();
Arnaldo Carvalho de Meloe55d9122006-03-20 19:25:02 -08001173 dccp_sysctl_exit();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001174}
1175
1176module_init(dccp_init);
1177module_exit(dccp_fini);
1178
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001179MODULE_LICENSE("GPL");
1180MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>");
1181MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");