blob: 60f40ec72ff3c0507ac319dca5a45f31183fef88 [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))
81 inet_put_port(&dccp_hashinfo, sk);
82 /* 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 Meloc985ed72006-03-20 21:23:39 -0800176void dccp_hash(struct sock *sk)
177{
178 inet_hash(&dccp_hashinfo, sk);
179}
180
181EXPORT_SYMBOL_GPL(dccp_hash);
182
183void dccp_unhash(struct sock *sk)
184{
185 inet_unhash(&dccp_hashinfo, sk);
186}
187
188EXPORT_SYMBOL_GPL(dccp_unhash);
189
Arnaldo Carvalho de Melo72478872006-03-20 22:00:37 -0800190int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800191{
192 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800193 struct dccp_minisock *dmsk = dccp_msk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800194 struct inet_connection_sock *icsk = inet_csk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800195
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800196 dccp_minisock_init(&dp->dccps_minisock);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800197
Arnaldo Carvalho de Meloe18d7a92007-11-24 21:42:53 -0200198 icsk->icsk_rto = DCCP_TIMEOUT_INIT;
199 icsk->icsk_syn_retries = sysctl_dccp_request_retries;
200 sk->sk_state = DCCP_CLOSED;
201 sk->sk_write_space = dccp_write_space;
202 icsk->icsk_sync_mss = dccp_sync_mss;
203 dp->dccps_mss_cache = 536;
204 dp->dccps_rate_last = jiffies;
205 dp->dccps_role = DCCP_ROLE_UNDEFINED;
206 dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT;
207 dp->dccps_l_ack_ratio = dp->dccps_r_ack_ratio = 1;
208
209 dccp_init_xmit_timers(sk);
210
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800211 /*
212 * FIXME: We're hardcoding the CCID, and doing this at this point makes
213 * the listening (master) sock get CCID control blocks, which is not
214 * necessary, but for now, to not mess with the test userspace apps,
215 * lets leave it here, later the real solution is to do this in a
216 * setsockopt(CCIDs-I-want/accept). -acme
217 */
Arnaldo Carvalho de Melo72478872006-03-20 22:00:37 -0800218 if (likely(ctl_sock_initialized)) {
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800219 int rc = dccp_feat_init(dmsk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800220
221 if (rc)
222 return rc;
223
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800224 if (dmsk->dccpms_send_ack_vector) {
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800225 dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL);
226 if (dp->dccps_hc_rx_ackvec == NULL)
227 return -ENOMEM;
228 }
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800229 dp->dccps_hc_rx_ccid = ccid_hc_rx_new(dmsk->dccpms_rx_ccid,
230 sk, GFP_KERNEL);
231 dp->dccps_hc_tx_ccid = ccid_hc_tx_new(dmsk->dccpms_tx_ccid,
232 sk, GFP_KERNEL);
Arnaldo Carvalho de Melo8109b022006-12-10 16:01:18 -0200233 if (unlikely(dp->dccps_hc_rx_ccid == NULL ||
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800234 dp->dccps_hc_tx_ccid == NULL)) {
235 ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
236 ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800237 if (dmsk->dccpms_send_ack_vector) {
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800238 dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
239 dp->dccps_hc_rx_ackvec = NULL;
240 }
241 dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
242 return -ENOMEM;
243 }
244 } else {
245 /* control socket doesn't need feat nego */
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800246 INIT_LIST_HEAD(&dmsk->dccpms_pending);
247 INIT_LIST_HEAD(&dmsk->dccpms_conf);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800248 }
249
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800250 return 0;
251}
252
253EXPORT_SYMBOL_GPL(dccp_init_sock);
254
255int dccp_destroy_sock(struct sock *sk)
256{
257 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800258 struct dccp_minisock *dmsk = dccp_msk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800259
260 /*
261 * DCCP doesn't use sk_write_queue, just sk_send_head
262 * for retransmissions
263 */
264 if (sk->sk_send_head != NULL) {
265 kfree_skb(sk->sk_send_head);
266 sk->sk_send_head = NULL;
267 }
268
269 /* Clean up a referenced DCCP bind bucket. */
270 if (inet_csk(sk)->icsk_bind_hash != NULL)
271 inet_put_port(&dccp_hashinfo, sk);
272
273 kfree(dp->dccps_service_list);
274 dp->dccps_service_list = NULL;
275
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800276 if (dmsk->dccpms_send_ack_vector) {
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800277 dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
278 dp->dccps_hc_rx_ackvec = NULL;
279 }
280 ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
281 ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
282 dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
283
284 /* clean up feature negotiation state */
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800285 dccp_feat_clean(dmsk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800286
287 return 0;
288}
289
290EXPORT_SYMBOL_GPL(dccp_destroy_sock);
291
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800292static inline int dccp_listen_start(struct sock *sk, int backlog)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700293{
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700294 struct dccp_sock *dp = dccp_sk(sk);
295
296 dp->dccps_role = DCCP_ROLE_LISTEN;
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800297 return inet_csk_listen_start(sk, backlog);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700298}
299
Gerrit Renkerce865a62007-11-24 22:14:15 -0200300static inline int dccp_need_reset(int state)
301{
302 return state != DCCP_CLOSED && state != DCCP_LISTEN &&
303 state != DCCP_REQUESTING;
304}
305
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700306int dccp_disconnect(struct sock *sk, int flags)
307{
308 struct inet_connection_sock *icsk = inet_csk(sk);
309 struct inet_sock *inet = inet_sk(sk);
310 int err = 0;
311 const int old_state = sk->sk_state;
312
313 if (old_state != DCCP_CLOSED)
314 dccp_set_state(sk, DCCP_CLOSED);
315
Gerrit Renkerce865a62007-11-24 22:14:15 -0200316 /*
317 * This corresponds to the ABORT function of RFC793, sec. 3.8
318 * TCP uses a RST segment, DCCP a Reset packet with Code 2, "Aborted".
319 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700320 if (old_state == DCCP_LISTEN) {
321 inet_csk_listen_stop(sk);
Gerrit Renkerce865a62007-11-24 22:14:15 -0200322 } else if (dccp_need_reset(old_state)) {
323 dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
324 sk->sk_err = ECONNRESET;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700325 } else if (old_state == DCCP_REQUESTING)
326 sk->sk_err = ECONNRESET;
327
328 dccp_clear_xmit_timers(sk);
329 __skb_queue_purge(&sk->sk_receive_queue);
330 if (sk->sk_send_head != NULL) {
331 __kfree_skb(sk->sk_send_head);
332 sk->sk_send_head = NULL;
333 }
334
335 inet->dport = 0;
336
337 if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
338 inet_reset_saddr(sk);
339
340 sk->sk_shutdown = 0;
341 sock_reset_flag(sk, SOCK_DONE);
342
343 icsk->icsk_backoff = 0;
344 inet_csk_delack_init(sk);
345 __sk_dst_reset(sk);
346
347 BUG_TRAP(!inet->num || icsk->icsk_bind_hash);
348
349 sk->sk_error_report(sk);
350 return err;
351}
352
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800353EXPORT_SYMBOL_GPL(dccp_disconnect);
354
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700355/*
356 * Wait for a DCCP event.
357 *
358 * Note that we don't need to lock the socket, as the upper poll layers
359 * take care of normal races (between the test and the event) and we don't
360 * go look at any of the socket buffers directly.
361 */
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800362unsigned int dccp_poll(struct file *file, struct socket *sock,
363 poll_table *wait)
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700364{
365 unsigned int mask;
366 struct sock *sk = sock->sk;
367
368 poll_wait(file, sk->sk_sleep, wait);
369 if (sk->sk_state == DCCP_LISTEN)
370 return inet_csk_listen_poll(sk);
371
372 /* Socket is not locked. We are protected from async events
373 by poll logic and correct handling of state changes
374 made by another threads is impossible in any case.
375 */
376
377 mask = 0;
378 if (sk->sk_err)
379 mask = POLLERR;
380
381 if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED)
382 mask |= POLLHUP;
383 if (sk->sk_shutdown & RCV_SHUTDOWN)
Davide Libenzif348d702006-03-25 03:07:39 -0800384 mask |= POLLIN | POLLRDNORM | POLLRDHUP;
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700385
386 /* Connected? */
387 if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) {
388 if (atomic_read(&sk->sk_rmem_alloc) > 0)
389 mask |= POLLIN | POLLRDNORM;
390
391 if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {
392 if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {
393 mask |= POLLOUT | POLLWRNORM;
394 } else { /* send SIGIO later */
395 set_bit(SOCK_ASYNC_NOSPACE,
396 &sk->sk_socket->flags);
397 set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
398
399 /* Race breaker. If space is freed after
400 * wspace test but before the flags are set,
401 * IO signal will be lost.
402 */
403 if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk))
404 mask |= POLLOUT | POLLWRNORM;
405 }
406 }
407 }
408 return mask;
409}
410
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800411EXPORT_SYMBOL_GPL(dccp_poll);
412
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700413int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
414{
Arnaldo Carvalho de Melo62731722007-10-23 20:23:30 -0700415 int rc = -ENOTCONN;
416
417 lock_sock(sk);
418
419 if (sk->sk_state == DCCP_LISTEN)
420 goto out;
421
422 switch (cmd) {
423 case SIOCINQ: {
424 struct sk_buff *skb;
425 unsigned long amount = 0;
426
427 skb = skb_peek(&sk->sk_receive_queue);
428 if (skb != NULL) {
429 /*
430 * We will only return the amount of this packet since
431 * that is all that will be read.
432 */
433 amount = skb->len;
434 }
435 rc = put_user(amount, (int __user *)arg);
436 }
437 break;
438 default:
439 rc = -ENOIOCTLCMD;
440 break;
441 }
442out:
443 release_sock(sk);
444 return rc;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700445}
446
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800447EXPORT_SYMBOL_GPL(dccp_ioctl);
448
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800449static int dccp_setsockopt_service(struct sock *sk, const __be32 service,
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700450 char __user *optval, int optlen)
451{
452 struct dccp_sock *dp = dccp_sk(sk);
453 struct dccp_service_list *sl = NULL;
454
Arnaldo Carvalho de Melo8109b022006-12-10 16:01:18 -0200455 if (service == DCCP_SERVICE_INVALID_VALUE ||
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700456 optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32))
457 return -EINVAL;
458
459 if (optlen > sizeof(service)) {
460 sl = kmalloc(optlen, GFP_KERNEL);
461 if (sl == NULL)
462 return -ENOMEM;
463
464 sl->dccpsl_nr = optlen / sizeof(u32) - 1;
465 if (copy_from_user(sl->dccpsl_list,
466 optval + sizeof(service),
467 optlen - sizeof(service)) ||
468 dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) {
469 kfree(sl);
470 return -EFAULT;
471 }
472 }
473
474 lock_sock(sk);
475 dp->dccps_service = service;
476
Jesper Juhla51482b2005-11-08 09:41:34 -0800477 kfree(dp->dccps_service_list);
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700478
479 dp->dccps_service_list = sl;
480 release_sock(sk);
481 return 0;
482}
483
Andrea Bittauafe00252006-03-20 17:43:56 -0800484/* byte 1 is feature. the rest is the preference list */
485static int dccp_setsockopt_change(struct sock *sk, int type,
486 struct dccp_so_feat __user *optval)
487{
488 struct dccp_so_feat opt;
489 u8 *val;
490 int rc;
491
492 if (copy_from_user(&opt, optval, sizeof(opt)))
493 return -EFAULT;
494
495 val = kmalloc(opt.dccpsf_len, GFP_KERNEL);
496 if (!val)
497 return -ENOMEM;
498
499 if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) {
500 rc = -EFAULT;
501 goto out_free_val;
502 }
503
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800504 rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat,
505 val, opt.dccpsf_len, GFP_KERNEL);
Andrea Bittauafe00252006-03-20 17:43:56 -0800506 if (rc)
507 goto out_free_val;
508
509out:
510 return rc;
511
512out_free_val:
513 kfree(val);
514 goto out;
515}
516
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800517static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
518 char __user *optval, int optlen)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700519{
Gerrit Renker09dbc382006-11-14 12:57:34 -0200520 struct dccp_sock *dp = dccp_sk(sk);
521 int val, err = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700522
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300523 if (optlen < sizeof(int))
524 return -EINVAL;
525
526 if (get_user(val, (int __user *)optval))
527 return -EFAULT;
528
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700529 if (optname == DCCP_SOCKOPT_SERVICE)
530 return dccp_setsockopt_service(sk, val, optval, optlen);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300531
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700532 lock_sock(sk);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300533 switch (optname) {
534 case DCCP_SOCKOPT_PACKET_SIZE:
Gerrit Renker5aed3242006-11-28 19:33:36 -0200535 DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
Arnaldo Carvalho de Melo841bac12006-11-28 19:42:03 -0200536 err = 0;
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300537 break;
Andrea Bittauafe00252006-03-20 17:43:56 -0800538 case DCCP_SOCKOPT_CHANGE_L:
539 if (optlen != sizeof(struct dccp_so_feat))
540 err = -EINVAL;
541 else
542 err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L,
YOSHIFUJI Hideakic9eaf172007-02-09 23:24:38 +0900543 (struct dccp_so_feat __user *)
Andrea Bittauafe00252006-03-20 17:43:56 -0800544 optval);
545 break;
Andrea Bittauafe00252006-03-20 17:43:56 -0800546 case DCCP_SOCKOPT_CHANGE_R:
547 if (optlen != sizeof(struct dccp_so_feat))
548 err = -EINVAL;
549 else
550 err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R,
Alan Cox9faefb62006-07-10 14:24:23 -0700551 (struct dccp_so_feat __user *)
Andrea Bittauafe00252006-03-20 17:43:56 -0800552 optval);
553 break;
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200554 case DCCP_SOCKOPT_SEND_CSCOV: /* sender side, RFC 4340, sec. 9.2 */
555 if (val < 0 || val > 15)
556 err = -EINVAL;
557 else
558 dp->dccps_pcslen = val;
559 break;
560 case DCCP_SOCKOPT_RECV_CSCOV: /* receiver side, RFC 4340 sec. 9.2.1 */
561 if (val < 0 || val > 15)
562 err = -EINVAL;
563 else {
564 dp->dccps_pcrlen = val;
565 /* FIXME: add feature negotiation,
566 * ChangeL(MinimumChecksumCoverage, val) */
567 }
568 break;
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300569 default:
570 err = -ENOPROTOOPT;
571 break;
572 }
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200573
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300574 release_sock(sk);
575 return err;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700576}
577
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800578int dccp_setsockopt(struct sock *sk, int level, int optname,
579 char __user *optval, int optlen)
580{
581 if (level != SOL_DCCP)
582 return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
583 optname, optval,
584 optlen);
585 return do_dccp_setsockopt(sk, level, optname, optval, optlen);
586}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800587
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800588EXPORT_SYMBOL_GPL(dccp_setsockopt);
589
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800590#ifdef CONFIG_COMPAT
591int compat_dccp_setsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800592 char __user *optval, int optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800593{
Arnaldo Carvalho de Melodec73ff2006-03-20 22:46:16 -0800594 if (level != SOL_DCCP)
595 return inet_csk_compat_setsockopt(sk, level, optname,
596 optval, optlen);
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800597 return do_dccp_setsockopt(sk, level, optname, optval, optlen);
598}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800599
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800600EXPORT_SYMBOL_GPL(compat_dccp_setsockopt);
601#endif
602
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700603static int dccp_getsockopt_service(struct sock *sk, int len,
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800604 __be32 __user *optval,
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700605 int __user *optlen)
606{
607 const struct dccp_sock *dp = dccp_sk(sk);
608 const struct dccp_service_list *sl;
609 int err = -ENOENT, slen = 0, total_len = sizeof(u32);
610
611 lock_sock(sk);
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700612 if ((sl = dp->dccps_service_list) != NULL) {
613 slen = sl->dccpsl_nr * sizeof(u32);
614 total_len += slen;
615 }
616
617 err = -EINVAL;
618 if (total_len > len)
619 goto out;
620
621 err = 0;
622 if (put_user(total_len, optlen) ||
623 put_user(dp->dccps_service, optval) ||
624 (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen)))
625 err = -EFAULT;
626out:
627 release_sock(sk);
628 return err;
629}
630
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800631static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -0300632 char __user *optval, int __user *optlen)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700633{
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300634 struct dccp_sock *dp;
635 int val, len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700636
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300637 if (get_user(len, optlen))
638 return -EFAULT;
639
Arnaldo Carvalho de Melo39ebc022007-03-28 11:54:32 -0700640 if (len < (int)sizeof(int))
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300641 return -EINVAL;
642
643 dp = dccp_sk(sk);
644
645 switch (optname) {
646 case DCCP_SOCKOPT_PACKET_SIZE:
Gerrit Renker5aed3242006-11-28 19:33:36 -0200647 DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
Arnaldo Carvalho de Melo841bac12006-11-28 19:42:03 -0200648 return 0;
Arnaldo Carvalho de Melo88f964d2005-09-18 00:19:32 -0700649 case DCCP_SOCKOPT_SERVICE:
650 return dccp_getsockopt_service(sk, len,
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800651 (__be32 __user *)optval, optlen);
Gerrit Renker7c559a92007-10-04 14:39:22 -0700652 case DCCP_SOCKOPT_GET_CUR_MPS:
653 val = dp->dccps_mss_cache;
654 len = sizeof(val);
655 break;
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200656 case DCCP_SOCKOPT_SEND_CSCOV:
657 val = dp->dccps_pcslen;
Arnaldo Carvalho de Melo39ebc022007-03-28 11:54:32 -0700658 len = sizeof(val);
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200659 break;
660 case DCCP_SOCKOPT_RECV_CSCOV:
661 val = dp->dccps_pcrlen;
Arnaldo Carvalho de Melo39ebc022007-03-28 11:54:32 -0700662 len = sizeof(val);
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200663 break;
Arnaldo Carvalho de Melo88f964d2005-09-18 00:19:32 -0700664 case 128 ... 191:
665 return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
666 len, (u32 __user *)optval, optlen);
667 case 192 ... 255:
668 return ccid_hc_tx_getsockopt(dp->dccps_hc_tx_ccid, sk, optname,
669 len, (u32 __user *)optval, optlen);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300670 default:
671 return -ENOPROTOOPT;
672 }
673
674 if (put_user(len, optlen) || copy_to_user(optval, &val, len))
675 return -EFAULT;
676
677 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700678}
679
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800680int dccp_getsockopt(struct sock *sk, int level, int optname,
681 char __user *optval, int __user *optlen)
682{
683 if (level != SOL_DCCP)
684 return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
685 optname, optval,
686 optlen);
687 return do_dccp_getsockopt(sk, level, optname, optval, optlen);
688}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800689
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800690EXPORT_SYMBOL_GPL(dccp_getsockopt);
691
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800692#ifdef CONFIG_COMPAT
693int compat_dccp_getsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800694 char __user *optval, int __user *optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800695{
Arnaldo Carvalho de Melodec73ff2006-03-20 22:46:16 -0800696 if (level != SOL_DCCP)
697 return inet_csk_compat_getsockopt(sk, level, optname,
698 optval, optlen);
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800699 return do_dccp_getsockopt(sk, level, optname, optval, optlen);
700}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800701
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800702EXPORT_SYMBOL_GPL(compat_dccp_getsockopt);
703#endif
704
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700705int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
706 size_t len)
707{
708 const struct dccp_sock *dp = dccp_sk(sk);
709 const int flags = msg->msg_flags;
710 const int noblock = flags & MSG_DONTWAIT;
711 struct sk_buff *skb;
712 int rc, size;
713 long timeo;
714
715 if (len > dp->dccps_mss_cache)
716 return -EMSGSIZE;
717
718 lock_sock(sk);
Ian McDonaldb1308dc2006-11-20 18:30:17 -0200719
720 if (sysctl_dccp_tx_qlen &&
721 (sk->sk_write_queue.qlen >= sysctl_dccp_tx_qlen)) {
722 rc = -EAGAIN;
723 goto out_release;
724 }
725
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700726 timeo = sock_sndtimeo(sk, noblock);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700727
728 /*
729 * We have to use sk_stream_wait_connect here to set sk_write_pending,
730 * so that the trick in dccp_rcv_request_sent_state_process.
731 */
732 /* Wait for a connection to finish. */
Gerrit Renkercecd8d02007-09-26 19:36:08 -0300733 if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN))
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700734 if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0)
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700735 goto out_release;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700736
737 size = sk->sk_prot->max_header + len;
738 release_sock(sk);
739 skb = sock_alloc_send_skb(sk, size, noblock, &rc);
740 lock_sock(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700741 if (skb == NULL)
742 goto out_release;
743
744 skb_reserve(skb, sk->sk_prot->max_header);
745 rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700746 if (rc != 0)
747 goto out_discard;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700748
Ian McDonald97e58482006-08-26 19:16:45 -0700749 skb_queue_tail(&sk->sk_write_queue, skb);
750 dccp_write_xmit(sk,0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700751out_release:
752 release_sock(sk);
753 return rc ? : len;
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700754out_discard:
755 kfree_skb(skb);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700756 goto out_release;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700757}
758
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800759EXPORT_SYMBOL_GPL(dccp_sendmsg);
760
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700761int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
762 size_t len, int nonblock, int flags, int *addr_len)
763{
764 const struct dccp_hdr *dh;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700765 long timeo;
766
767 lock_sock(sk);
768
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300769 if (sk->sk_state == DCCP_LISTEN) {
770 len = -ENOTCONN;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700771 goto out;
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300772 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700773
774 timeo = sock_rcvtimeo(sk, nonblock);
775
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700776 do {
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300777 struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700778
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300779 if (skb == NULL)
780 goto verify_sock_status;
781
782 dh = dccp_hdr(skb);
783
Gerrit Renker0c869622007-11-28 11:59:48 -0200784 switch (dh->dccph_type) {
785 case DCCP_PKT_DATA:
786 case DCCP_PKT_DATAACK:
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300787 goto found_ok_skb;
788
Gerrit Renker0c869622007-11-28 11:59:48 -0200789 case DCCP_PKT_CLOSE:
790 case DCCP_PKT_CLOSEREQ:
791 if (!(flags & MSG_PEEK))
792 dccp_finish_passive_close(sk);
793 /* fall through */
794 case DCCP_PKT_RESET:
795 dccp_pr_debug("found fin (%s) ok!\n",
796 dccp_packet_name(dh->dccph_type));
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300797 len = 0;
798 goto found_fin_ok;
Gerrit Renker0c869622007-11-28 11:59:48 -0200799 default:
800 dccp_pr_debug("packet_type=%s\n",
801 dccp_packet_name(dh->dccph_type));
802 sk_eat_skb(sk, skb, 0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700803 }
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300804verify_sock_status:
805 if (sock_flag(sk, SOCK_DONE)) {
806 len = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700807 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700808 }
809
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300810 if (sk->sk_err) {
811 len = sock_error(sk);
812 break;
813 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700814
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300815 if (sk->sk_shutdown & RCV_SHUTDOWN) {
816 len = 0;
817 break;
818 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700819
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300820 if (sk->sk_state == DCCP_CLOSED) {
821 if (!sock_flag(sk, SOCK_DONE)) {
822 /* This occurs when user tries to read
823 * from never connected socket.
824 */
825 len = -ENOTCONN;
826 break;
827 }
828 len = 0;
829 break;
830 }
831
832 if (!timeo) {
833 len = -EAGAIN;
834 break;
835 }
836
837 if (signal_pending(current)) {
838 len = sock_intr_errno(timeo);
839 break;
840 }
841
842 sk_wait_data(sk, &timeo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700843 continue;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700844 found_ok_skb:
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300845 if (len > skb->len)
846 len = skb->len;
847 else if (len < skb->len)
848 msg->msg_flags |= MSG_TRUNC;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700849
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300850 if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len)) {
851 /* Exception. Bailout! */
852 len = -EFAULT;
853 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700854 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700855 found_fin_ok:
856 if (!(flags & MSG_PEEK))
Chris Leech624d1162006-05-23 18:01:28 -0700857 sk_eat_skb(sk, skb, 0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700858 break;
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300859 } while (1);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700860out:
861 release_sock(sk);
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300862 return len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700863}
864
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800865EXPORT_SYMBOL_GPL(dccp_recvmsg);
866
867int inet_dccp_listen(struct socket *sock, int backlog)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700868{
869 struct sock *sk = sock->sk;
870 unsigned char old_state;
871 int err;
872
873 lock_sock(sk);
874
875 err = -EINVAL;
876 if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP)
877 goto out;
878
879 old_state = sk->sk_state;
880 if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN)))
881 goto out;
882
883 /* Really, if the socket is already in listen state
884 * we can only allow the backlog to be adjusted.
885 */
886 if (old_state != DCCP_LISTEN) {
887 /*
888 * FIXME: here it probably should be sk->sk_prot->listen_start
889 * see tcp_listen_start
890 */
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800891 err = dccp_listen_start(sk, backlog);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700892 if (err)
893 goto out;
894 }
895 sk->sk_max_ack_backlog = backlog;
896 err = 0;
897
898out:
899 release_sock(sk);
900 return err;
901}
902
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800903EXPORT_SYMBOL_GPL(inet_dccp_listen);
904
Gerrit Renker0c869622007-11-28 11:59:48 -0200905static void dccp_terminate_connection(struct sock *sk)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700906{
Gerrit Renker0c869622007-11-28 11:59:48 -0200907 u8 next_state = DCCP_CLOSED;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700908
Gerrit Renker0c869622007-11-28 11:59:48 -0200909 switch (sk->sk_state) {
910 case DCCP_PASSIVE_CLOSE:
911 case DCCP_PASSIVE_CLOSEREQ:
912 dccp_finish_passive_close(sk);
913 break;
914 case DCCP_PARTOPEN:
915 dccp_pr_debug("Stop PARTOPEN timer (%p)\n", sk);
916 inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);
917 /* fall through */
918 case DCCP_OPEN:
919 dccp_send_close(sk, 1);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700920
Gerrit Renker0c869622007-11-28 11:59:48 -0200921 if (dccp_sk(sk)->dccps_role == DCCP_ROLE_SERVER)
922 next_state = DCCP_ACTIVE_CLOSEREQ;
923 else
924 next_state = DCCP_CLOSING;
925 /* fall through */
926 default:
927 dccp_set_state(sk, next_state);
928 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700929}
930
931void dccp_close(struct sock *sk, long timeout)
932{
Ian McDonald97e58482006-08-26 19:16:45 -0700933 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700934 struct sk_buff *skb;
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800935 u32 data_was_unread = 0;
Herbert Xu134af342006-05-05 17:09:13 -0700936 int state;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700937
938 lock_sock(sk);
939
940 sk->sk_shutdown = SHUTDOWN_MASK;
941
942 if (sk->sk_state == DCCP_LISTEN) {
943 dccp_set_state(sk, DCCP_CLOSED);
944
945 /* Special case. */
946 inet_csk_listen_stop(sk);
947
948 goto adjudge_to_death;
949 }
950
Ian McDonald97e58482006-08-26 19:16:45 -0700951 sk_stop_timer(sk, &dp->dccps_xmit_timer);
952
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700953 /*
954 * We need to flush the recv. buffs. We do this only on the
955 * descriptor close, not protocol-sourced closes, because the
956 *reader process may not have drained the data yet!
957 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700958 while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800959 data_was_unread += skb->len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700960 __kfree_skb(skb);
961 }
962
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800963 if (data_was_unread) {
964 /* Unread data was tossed, send an appropriate Reset Code */
965 DCCP_WARN("DCCP: ABORT -- %u bytes unread\n", data_was_unread);
966 dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
967 dccp_set_state(sk, DCCP_CLOSED);
968 } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700969 /* Check zero linger _after_ checking for unread data. */
970 sk->sk_prot->disconnect(sk, 0);
Gerrit Renker0c869622007-11-28 11:59:48 -0200971 } else if (sk->sk_state != DCCP_CLOSED) {
972 dccp_terminate_connection(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700973 }
974
975 sk_stream_wait_close(sk, timeout);
976
977adjudge_to_death:
Herbert Xu134af342006-05-05 17:09:13 -0700978 state = sk->sk_state;
979 sock_hold(sk);
980 sock_orphan(sk);
981 atomic_inc(sk->sk_prot->orphan_count);
982
Arnaldo Carvalho de Melo7ad07e72005-08-23 21:50:06 -0700983 /*
984 * It is the last release_sock in its life. It will remove backlog.
985 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700986 release_sock(sk);
987 /*
988 * Now socket is owned by kernel and we acquire BH lock
989 * to finish close. No need to check for user refs.
990 */
991 local_bh_disable();
992 bh_lock_sock(sk);
993 BUG_TRAP(!sock_owned_by_user(sk));
994
Herbert Xu134af342006-05-05 17:09:13 -0700995 /* Have we already been destroyed by a softirq or backlog? */
996 if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
997 goto out;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700998
Arnaldo Carvalho de Melo7ad07e72005-08-23 21:50:06 -0700999 /*
1000 * The last release_sock may have processed the CLOSE or RESET
1001 * packet moving sock to CLOSED state, if not we have to fire
1002 * the CLOSE/CLOSEREQ retransmission timer, see "8.3. Termination"
1003 * in draft-ietf-dccp-spec-11. -acme
1004 */
1005 if (sk->sk_state == DCCP_CLOSING) {
1006 /* FIXME: should start at 2 * RTT */
1007 /* Timer for repeating the CLOSE/CLOSEREQ until an answer. */
1008 inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
1009 inet_csk(sk)->icsk_rto,
1010 DCCP_RTO_MAX);
1011#if 0
1012 /* Yeah, we should use sk->sk_prot->orphan_count, etc */
1013 dccp_set_state(sk, DCCP_CLOSED);
1014#endif
1015 }
1016
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001017 if (sk->sk_state == DCCP_CLOSED)
1018 inet_csk_destroy_sock(sk);
1019
1020 /* Otherwise, socket is reprieved until protocol close. */
1021
Herbert Xu134af342006-05-05 17:09:13 -07001022out:
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001023 bh_unlock_sock(sk);
1024 local_bh_enable();
1025 sock_put(sk);
1026}
1027
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001028EXPORT_SYMBOL_GPL(dccp_close);
1029
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001030void dccp_shutdown(struct sock *sk, int how)
1031{
Gerrit Renker8e8c71f2007-11-21 09:56:48 -02001032 dccp_pr_debug("called shutdown(%x)\n", how);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001033}
1034
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001035EXPORT_SYMBOL_GPL(dccp_shutdown);
1036
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001037static int __init dccp_mib_init(void)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001038{
1039 int rc = -ENOMEM;
1040
1041 dccp_statistics[0] = alloc_percpu(struct dccp_mib);
1042 if (dccp_statistics[0] == NULL)
1043 goto out;
1044
1045 dccp_statistics[1] = alloc_percpu(struct dccp_mib);
1046 if (dccp_statistics[1] == NULL)
1047 goto out_free_one;
1048
1049 rc = 0;
1050out:
1051 return rc;
1052out_free_one:
1053 free_percpu(dccp_statistics[0]);
1054 dccp_statistics[0] = NULL;
1055 goto out;
1056
1057}
1058
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001059static void dccp_mib_exit(void)
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001060{
1061 free_percpu(dccp_statistics[0]);
1062 free_percpu(dccp_statistics[1]);
1063 dccp_statistics[0] = dccp_statistics[1] = NULL;
1064}
1065
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001066static int thash_entries;
1067module_param(thash_entries, int, 0444);
1068MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
1069
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -03001070#ifdef CONFIG_IP_DCCP_DEBUG
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001071int dccp_debug;
Gerrit Renker042d18f2007-10-04 14:39:53 -07001072module_param(dccp_debug, bool, 0444);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001073MODULE_PARM_DESC(dccp_debug, "Enable debug messages");
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001074
1075EXPORT_SYMBOL_GPL(dccp_debug);
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -03001076#endif
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001077
1078static int __init dccp_init(void)
1079{
1080 unsigned long goal;
1081 int ehash_order, bhash_order, i;
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001082 int rc = -ENOBUFS;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001083
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001084 dccp_hashinfo.bind_bucket_cachep =
1085 kmem_cache_create("dccp_bind_bucket",
1086 sizeof(struct inet_bind_bucket), 0,
Paul Mundt20c2df82007-07-20 10:11:58 +09001087 SLAB_HWCACHE_ALIGN, NULL);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001088 if (!dccp_hashinfo.bind_bucket_cachep)
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001089 goto out;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001090
1091 /*
1092 * Size and allocate the main established and bind bucket
1093 * hash tables.
1094 *
1095 * The methodology is similar to that of the buffer cache.
1096 */
1097 if (num_physpages >= (128 * 1024))
1098 goal = num_physpages >> (21 - PAGE_SHIFT);
1099 else
1100 goal = num_physpages >> (23 - PAGE_SHIFT);
1101
1102 if (thash_entries)
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001103 goal = (thash_entries *
1104 sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001105 for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++)
1106 ;
1107 do {
1108 dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE /
1109 sizeof(struct inet_ehash_bucket);
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001110 while (dccp_hashinfo.ehash_size &
1111 (dccp_hashinfo.ehash_size - 1))
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001112 dccp_hashinfo.ehash_size--;
1113 dccp_hashinfo.ehash = (struct inet_ehash_bucket *)
1114 __get_free_pages(GFP_ATOMIC, ehash_order);
1115 } while (!dccp_hashinfo.ehash && --ehash_order > 0);
1116
1117 if (!dccp_hashinfo.ehash) {
Gerrit Renker59348b12006-11-20 18:39:23 -02001118 DCCP_CRIT("Failed to allocate DCCP established hash table");
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001119 goto out_free_bind_bucket_cachep;
1120 }
1121
Eric Dumazetdbca9b2752007-02-08 14:16:46 -08001122 for (i = 0; i < dccp_hashinfo.ehash_size; i++) {
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001123 INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain);
Eric Dumazetdbca9b2752007-02-08 14:16:46 -08001124 INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].twchain);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001125 }
1126
Eric Dumazet230140c2007-11-07 02:40:20 -08001127 if (inet_ehash_locks_alloc(&dccp_hashinfo))
1128 goto out_free_dccp_ehash;
1129
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001130 bhash_order = ehash_order;
1131
1132 do {
1133 dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE /
1134 sizeof(struct inet_bind_hashbucket);
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001135 if ((dccp_hashinfo.bhash_size > (64 * 1024)) &&
1136 bhash_order > 0)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001137 continue;
1138 dccp_hashinfo.bhash = (struct inet_bind_hashbucket *)
1139 __get_free_pages(GFP_ATOMIC, bhash_order);
1140 } while (!dccp_hashinfo.bhash && --bhash_order >= 0);
1141
1142 if (!dccp_hashinfo.bhash) {
Gerrit Renker59348b12006-11-20 18:39:23 -02001143 DCCP_CRIT("Failed to allocate DCCP bind hash table");
Eric Dumazet230140c2007-11-07 02:40:20 -08001144 goto out_free_dccp_locks;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001145 }
1146
1147 for (i = 0; i < dccp_hashinfo.bhash_size; i++) {
1148 spin_lock_init(&dccp_hashinfo.bhash[i].lock);
1149 INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
1150 }
1151
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001152 rc = dccp_mib_init();
Arnaldo Carvalho de Melofa23e2e2006-03-20 17:16:01 -08001153 if (rc)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001154 goto out_free_dccp_bhash;
1155
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001156 rc = dccp_ackvec_init();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001157 if (rc)
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001158 goto out_free_dccp_mib;
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001159
Arnaldo Carvalho de Meloe55d9122006-03-20 19:25:02 -08001160 rc = dccp_sysctl_init();
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001161 if (rc)
1162 goto out_ackvec_exit;
Gerrit Renker4c70f382007-09-25 22:40:13 -07001163
1164 dccp_timestamping_init();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001165out:
1166 return rc;
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001167out_ackvec_exit:
1168 dccp_ackvec_exit();
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001169out_free_dccp_mib:
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001170 dccp_mib_exit();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001171out_free_dccp_bhash:
1172 free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
1173 dccp_hashinfo.bhash = NULL;
Eric Dumazet230140c2007-11-07 02:40:20 -08001174out_free_dccp_locks:
1175 inet_ehash_locks_free(&dccp_hashinfo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001176out_free_dccp_ehash:
1177 free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order);
1178 dccp_hashinfo.ehash = NULL;
1179out_free_bind_bucket_cachep:
1180 kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
1181 dccp_hashinfo.bind_bucket_cachep = NULL;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001182 goto out;
1183}
1184
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001185static void __exit dccp_fini(void)
1186{
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001187 dccp_mib_exit();
Arnaldo Carvalho de Melo725ba8e2005-08-13 20:35:39 -03001188 free_pages((unsigned long)dccp_hashinfo.bhash,
1189 get_order(dccp_hashinfo.bhash_size *
1190 sizeof(struct inet_bind_hashbucket)));
1191 free_pages((unsigned long)dccp_hashinfo.ehash,
1192 get_order(dccp_hashinfo.ehash_size *
1193 sizeof(struct inet_ehash_bucket)));
Eric Dumazet230140c2007-11-07 02:40:20 -08001194 inet_ehash_locks_free(&dccp_hashinfo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001195 kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001196 dccp_ackvec_exit();
Arnaldo Carvalho de Meloe55d9122006-03-20 19:25:02 -08001197 dccp_sysctl_exit();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001198}
1199
1200module_init(dccp_init);
1201module_exit(dccp_fini);
1202
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001203MODULE_LICENSE("GPL");
1204MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>");
1205MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");