blob: 73006b74767854bcb7b00bbcb9b8498ce69fa3bb [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
63 dccp_pr_debug("%s(%p) %-10.10s -> %s\n",
64 dccp_role(sk), sk,
65 dccp_state_name(oldstate), dccp_state_name(state));
66 WARN_ON(state == oldstate);
67
68 switch (state) {
69 case DCCP_OPEN:
70 if (oldstate != DCCP_OPEN)
71 DCCP_INC_STATS(DCCP_MIB_CURRESTAB);
72 break;
73
74 case DCCP_CLOSED:
75 if (oldstate == DCCP_CLOSING || oldstate == DCCP_OPEN)
76 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
96void dccp_done(struct sock *sk)
97{
98 dccp_set_state(sk, DCCP_CLOSED);
99 dccp_clear_xmit_timers(sk);
100
101 sk->sk_shutdown = SHUTDOWN_MASK;
102
103 if (!sock_flag(sk, SOCK_DEAD))
104 sk->sk_state_change(sk);
105 else
106 inet_csk_destroy_sock(sk);
107}
108
109EXPORT_SYMBOL_GPL(dccp_done);
110
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700111const char *dccp_packet_name(const int type)
112{
113 static const char *dccp_packet_names[] = {
114 [DCCP_PKT_REQUEST] = "REQUEST",
115 [DCCP_PKT_RESPONSE] = "RESPONSE",
116 [DCCP_PKT_DATA] = "DATA",
117 [DCCP_PKT_ACK] = "ACK",
118 [DCCP_PKT_DATAACK] = "DATAACK",
119 [DCCP_PKT_CLOSEREQ] = "CLOSEREQ",
120 [DCCP_PKT_CLOSE] = "CLOSE",
121 [DCCP_PKT_RESET] = "RESET",
122 [DCCP_PKT_SYNC] = "SYNC",
123 [DCCP_PKT_SYNCACK] = "SYNCACK",
124 };
125
126 if (type >= DCCP_NR_PKT_TYPES)
127 return "INVALID";
128 else
129 return dccp_packet_names[type];
130}
131
132EXPORT_SYMBOL_GPL(dccp_packet_name);
133
134const char *dccp_state_name(const int state)
135{
136 static char *dccp_state_names[] = {
137 [DCCP_OPEN] = "OPEN",
138 [DCCP_REQUESTING] = "REQUESTING",
139 [DCCP_PARTOPEN] = "PARTOPEN",
140 [DCCP_LISTEN] = "LISTEN",
141 [DCCP_RESPOND] = "RESPOND",
142 [DCCP_CLOSING] = "CLOSING",
143 [DCCP_TIME_WAIT] = "TIME_WAIT",
144 [DCCP_CLOSED] = "CLOSED",
145 };
146
147 if (state >= DCCP_MAX_STATES)
148 return "INVALID STATE!";
149 else
150 return dccp_state_names[state];
151}
152
153EXPORT_SYMBOL_GPL(dccp_state_name);
154
Arnaldo Carvalho de Meloc985ed72006-03-20 21:23:39 -0800155void dccp_hash(struct sock *sk)
156{
157 inet_hash(&dccp_hashinfo, sk);
158}
159
160EXPORT_SYMBOL_GPL(dccp_hash);
161
162void dccp_unhash(struct sock *sk)
163{
164 inet_unhash(&dccp_hashinfo, sk);
165}
166
167EXPORT_SYMBOL_GPL(dccp_unhash);
168
Arnaldo Carvalho de Melo72478872006-03-20 22:00:37 -0800169int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800170{
171 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800172 struct dccp_minisock *dmsk = dccp_msk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800173 struct inet_connection_sock *icsk = inet_csk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800174
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800175 dccp_minisock_init(&dp->dccps_minisock);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800176
Arnaldo Carvalho de Meloe18d7a92007-11-24 21:42:53 -0200177 icsk->icsk_rto = DCCP_TIMEOUT_INIT;
178 icsk->icsk_syn_retries = sysctl_dccp_request_retries;
179 sk->sk_state = DCCP_CLOSED;
180 sk->sk_write_space = dccp_write_space;
181 icsk->icsk_sync_mss = dccp_sync_mss;
182 dp->dccps_mss_cache = 536;
183 dp->dccps_rate_last = jiffies;
184 dp->dccps_role = DCCP_ROLE_UNDEFINED;
185 dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT;
186 dp->dccps_l_ack_ratio = dp->dccps_r_ack_ratio = 1;
187
188 dccp_init_xmit_timers(sk);
189
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800190 /*
191 * FIXME: We're hardcoding the CCID, and doing this at this point makes
192 * the listening (master) sock get CCID control blocks, which is not
193 * necessary, but for now, to not mess with the test userspace apps,
194 * lets leave it here, later the real solution is to do this in a
195 * setsockopt(CCIDs-I-want/accept). -acme
196 */
Arnaldo Carvalho de Melo72478872006-03-20 22:00:37 -0800197 if (likely(ctl_sock_initialized)) {
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800198 int rc = dccp_feat_init(dmsk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800199
200 if (rc)
201 return rc;
202
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800203 if (dmsk->dccpms_send_ack_vector) {
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800204 dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL);
205 if (dp->dccps_hc_rx_ackvec == NULL)
206 return -ENOMEM;
207 }
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800208 dp->dccps_hc_rx_ccid = ccid_hc_rx_new(dmsk->dccpms_rx_ccid,
209 sk, GFP_KERNEL);
210 dp->dccps_hc_tx_ccid = ccid_hc_tx_new(dmsk->dccpms_tx_ccid,
211 sk, GFP_KERNEL);
Arnaldo Carvalho de Melo8109b022006-12-10 16:01:18 -0200212 if (unlikely(dp->dccps_hc_rx_ccid == NULL ||
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800213 dp->dccps_hc_tx_ccid == NULL)) {
214 ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
215 ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800216 if (dmsk->dccpms_send_ack_vector) {
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800217 dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
218 dp->dccps_hc_rx_ackvec = NULL;
219 }
220 dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
221 return -ENOMEM;
222 }
223 } else {
224 /* control socket doesn't need feat nego */
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800225 INIT_LIST_HEAD(&dmsk->dccpms_pending);
226 INIT_LIST_HEAD(&dmsk->dccpms_conf);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800227 }
228
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800229 return 0;
230}
231
232EXPORT_SYMBOL_GPL(dccp_init_sock);
233
234int dccp_destroy_sock(struct sock *sk)
235{
236 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800237 struct dccp_minisock *dmsk = dccp_msk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800238
239 /*
240 * DCCP doesn't use sk_write_queue, just sk_send_head
241 * for retransmissions
242 */
243 if (sk->sk_send_head != NULL) {
244 kfree_skb(sk->sk_send_head);
245 sk->sk_send_head = NULL;
246 }
247
248 /* Clean up a referenced DCCP bind bucket. */
249 if (inet_csk(sk)->icsk_bind_hash != NULL)
250 inet_put_port(&dccp_hashinfo, sk);
251
252 kfree(dp->dccps_service_list);
253 dp->dccps_service_list = NULL;
254
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800255 if (dmsk->dccpms_send_ack_vector) {
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800256 dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
257 dp->dccps_hc_rx_ackvec = NULL;
258 }
259 ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
260 ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
261 dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
262
263 /* clean up feature negotiation state */
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800264 dccp_feat_clean(dmsk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800265
266 return 0;
267}
268
269EXPORT_SYMBOL_GPL(dccp_destroy_sock);
270
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800271static inline int dccp_listen_start(struct sock *sk, int backlog)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700272{
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700273 struct dccp_sock *dp = dccp_sk(sk);
274
275 dp->dccps_role = DCCP_ROLE_LISTEN;
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800276 return inet_csk_listen_start(sk, backlog);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700277}
278
Gerrit Renkerce865a62007-11-24 22:14:15 -0200279static inline int dccp_need_reset(int state)
280{
281 return state != DCCP_CLOSED && state != DCCP_LISTEN &&
282 state != DCCP_REQUESTING;
283}
284
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700285int dccp_disconnect(struct sock *sk, int flags)
286{
287 struct inet_connection_sock *icsk = inet_csk(sk);
288 struct inet_sock *inet = inet_sk(sk);
289 int err = 0;
290 const int old_state = sk->sk_state;
291
292 if (old_state != DCCP_CLOSED)
293 dccp_set_state(sk, DCCP_CLOSED);
294
Gerrit Renkerce865a62007-11-24 22:14:15 -0200295 /*
296 * This corresponds to the ABORT function of RFC793, sec. 3.8
297 * TCP uses a RST segment, DCCP a Reset packet with Code 2, "Aborted".
298 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700299 if (old_state == DCCP_LISTEN) {
300 inet_csk_listen_stop(sk);
Gerrit Renkerce865a62007-11-24 22:14:15 -0200301 } else if (dccp_need_reset(old_state)) {
302 dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
303 sk->sk_err = ECONNRESET;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700304 } else if (old_state == DCCP_REQUESTING)
305 sk->sk_err = ECONNRESET;
306
307 dccp_clear_xmit_timers(sk);
308 __skb_queue_purge(&sk->sk_receive_queue);
309 if (sk->sk_send_head != NULL) {
310 __kfree_skb(sk->sk_send_head);
311 sk->sk_send_head = NULL;
312 }
313
314 inet->dport = 0;
315
316 if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
317 inet_reset_saddr(sk);
318
319 sk->sk_shutdown = 0;
320 sock_reset_flag(sk, SOCK_DONE);
321
322 icsk->icsk_backoff = 0;
323 inet_csk_delack_init(sk);
324 __sk_dst_reset(sk);
325
326 BUG_TRAP(!inet->num || icsk->icsk_bind_hash);
327
328 sk->sk_error_report(sk);
329 return err;
330}
331
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800332EXPORT_SYMBOL_GPL(dccp_disconnect);
333
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700334/*
335 * Wait for a DCCP event.
336 *
337 * Note that we don't need to lock the socket, as the upper poll layers
338 * take care of normal races (between the test and the event) and we don't
339 * go look at any of the socket buffers directly.
340 */
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800341unsigned int dccp_poll(struct file *file, struct socket *sock,
342 poll_table *wait)
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700343{
344 unsigned int mask;
345 struct sock *sk = sock->sk;
346
347 poll_wait(file, sk->sk_sleep, wait);
348 if (sk->sk_state == DCCP_LISTEN)
349 return inet_csk_listen_poll(sk);
350
351 /* Socket is not locked. We are protected from async events
352 by poll logic and correct handling of state changes
353 made by another threads is impossible in any case.
354 */
355
356 mask = 0;
357 if (sk->sk_err)
358 mask = POLLERR;
359
360 if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED)
361 mask |= POLLHUP;
362 if (sk->sk_shutdown & RCV_SHUTDOWN)
Davide Libenzif348d702006-03-25 03:07:39 -0800363 mask |= POLLIN | POLLRDNORM | POLLRDHUP;
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700364
365 /* Connected? */
366 if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) {
367 if (atomic_read(&sk->sk_rmem_alloc) > 0)
368 mask |= POLLIN | POLLRDNORM;
369
370 if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {
371 if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {
372 mask |= POLLOUT | POLLWRNORM;
373 } else { /* send SIGIO later */
374 set_bit(SOCK_ASYNC_NOSPACE,
375 &sk->sk_socket->flags);
376 set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
377
378 /* Race breaker. If space is freed after
379 * wspace test but before the flags are set,
380 * IO signal will be lost.
381 */
382 if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk))
383 mask |= POLLOUT | POLLWRNORM;
384 }
385 }
386 }
387 return mask;
388}
389
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800390EXPORT_SYMBOL_GPL(dccp_poll);
391
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700392int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
393{
Arnaldo Carvalho de Melo62731722007-10-23 20:23:30 -0700394 int rc = -ENOTCONN;
395
396 lock_sock(sk);
397
398 if (sk->sk_state == DCCP_LISTEN)
399 goto out;
400
401 switch (cmd) {
402 case SIOCINQ: {
403 struct sk_buff *skb;
404 unsigned long amount = 0;
405
406 skb = skb_peek(&sk->sk_receive_queue);
407 if (skb != NULL) {
408 /*
409 * We will only return the amount of this packet since
410 * that is all that will be read.
411 */
412 amount = skb->len;
413 }
414 rc = put_user(amount, (int __user *)arg);
415 }
416 break;
417 default:
418 rc = -ENOIOCTLCMD;
419 break;
420 }
421out:
422 release_sock(sk);
423 return rc;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700424}
425
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800426EXPORT_SYMBOL_GPL(dccp_ioctl);
427
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800428static int dccp_setsockopt_service(struct sock *sk, const __be32 service,
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700429 char __user *optval, int optlen)
430{
431 struct dccp_sock *dp = dccp_sk(sk);
432 struct dccp_service_list *sl = NULL;
433
Arnaldo Carvalho de Melo8109b022006-12-10 16:01:18 -0200434 if (service == DCCP_SERVICE_INVALID_VALUE ||
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700435 optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32))
436 return -EINVAL;
437
438 if (optlen > sizeof(service)) {
439 sl = kmalloc(optlen, GFP_KERNEL);
440 if (sl == NULL)
441 return -ENOMEM;
442
443 sl->dccpsl_nr = optlen / sizeof(u32) - 1;
444 if (copy_from_user(sl->dccpsl_list,
445 optval + sizeof(service),
446 optlen - sizeof(service)) ||
447 dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) {
448 kfree(sl);
449 return -EFAULT;
450 }
451 }
452
453 lock_sock(sk);
454 dp->dccps_service = service;
455
Jesper Juhla51482b2005-11-08 09:41:34 -0800456 kfree(dp->dccps_service_list);
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700457
458 dp->dccps_service_list = sl;
459 release_sock(sk);
460 return 0;
461}
462
Andrea Bittauafe00252006-03-20 17:43:56 -0800463/* byte 1 is feature. the rest is the preference list */
464static int dccp_setsockopt_change(struct sock *sk, int type,
465 struct dccp_so_feat __user *optval)
466{
467 struct dccp_so_feat opt;
468 u8 *val;
469 int rc;
470
471 if (copy_from_user(&opt, optval, sizeof(opt)))
472 return -EFAULT;
473
474 val = kmalloc(opt.dccpsf_len, GFP_KERNEL);
475 if (!val)
476 return -ENOMEM;
477
478 if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) {
479 rc = -EFAULT;
480 goto out_free_val;
481 }
482
Arnaldo Carvalho de Melo8ca0d172006-03-20 22:51:53 -0800483 rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat,
484 val, opt.dccpsf_len, GFP_KERNEL);
Andrea Bittauafe00252006-03-20 17:43:56 -0800485 if (rc)
486 goto out_free_val;
487
488out:
489 return rc;
490
491out_free_val:
492 kfree(val);
493 goto out;
494}
495
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800496static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
497 char __user *optval, int optlen)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700498{
Gerrit Renker09dbc382006-11-14 12:57:34 -0200499 struct dccp_sock *dp = dccp_sk(sk);
500 int val, err = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700501
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300502 if (optlen < sizeof(int))
503 return -EINVAL;
504
505 if (get_user(val, (int __user *)optval))
506 return -EFAULT;
507
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700508 if (optname == DCCP_SOCKOPT_SERVICE)
509 return dccp_setsockopt_service(sk, val, optval, optlen);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300510
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700511 lock_sock(sk);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300512 switch (optname) {
513 case DCCP_SOCKOPT_PACKET_SIZE:
Gerrit Renker5aed3242006-11-28 19:33:36 -0200514 DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
Arnaldo Carvalho de Melo841bac12006-11-28 19:42:03 -0200515 err = 0;
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300516 break;
Andrea Bittauafe00252006-03-20 17:43:56 -0800517 case DCCP_SOCKOPT_CHANGE_L:
518 if (optlen != sizeof(struct dccp_so_feat))
519 err = -EINVAL;
520 else
521 err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L,
YOSHIFUJI Hideakic9eaf172007-02-09 23:24:38 +0900522 (struct dccp_so_feat __user *)
Andrea Bittauafe00252006-03-20 17:43:56 -0800523 optval);
524 break;
Andrea Bittauafe00252006-03-20 17:43:56 -0800525 case DCCP_SOCKOPT_CHANGE_R:
526 if (optlen != sizeof(struct dccp_so_feat))
527 err = -EINVAL;
528 else
529 err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R,
Alan Cox9faefb62006-07-10 14:24:23 -0700530 (struct dccp_so_feat __user *)
Andrea Bittauafe00252006-03-20 17:43:56 -0800531 optval);
532 break;
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200533 case DCCP_SOCKOPT_SEND_CSCOV: /* sender side, RFC 4340, sec. 9.2 */
534 if (val < 0 || val > 15)
535 err = -EINVAL;
536 else
537 dp->dccps_pcslen = val;
538 break;
539 case DCCP_SOCKOPT_RECV_CSCOV: /* receiver side, RFC 4340 sec. 9.2.1 */
540 if (val < 0 || val > 15)
541 err = -EINVAL;
542 else {
543 dp->dccps_pcrlen = val;
544 /* FIXME: add feature negotiation,
545 * ChangeL(MinimumChecksumCoverage, val) */
546 }
547 break;
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300548 default:
549 err = -ENOPROTOOPT;
550 break;
551 }
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200552
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300553 release_sock(sk);
554 return err;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700555}
556
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800557int dccp_setsockopt(struct sock *sk, int level, int optname,
558 char __user *optval, int optlen)
559{
560 if (level != SOL_DCCP)
561 return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
562 optname, optval,
563 optlen);
564 return do_dccp_setsockopt(sk, level, optname, optval, optlen);
565}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800566
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800567EXPORT_SYMBOL_GPL(dccp_setsockopt);
568
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800569#ifdef CONFIG_COMPAT
570int compat_dccp_setsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800571 char __user *optval, int optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800572{
Arnaldo Carvalho de Melodec73ff2006-03-20 22:46:16 -0800573 if (level != SOL_DCCP)
574 return inet_csk_compat_setsockopt(sk, level, optname,
575 optval, optlen);
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800576 return do_dccp_setsockopt(sk, level, optname, optval, optlen);
577}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800578
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800579EXPORT_SYMBOL_GPL(compat_dccp_setsockopt);
580#endif
581
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700582static int dccp_getsockopt_service(struct sock *sk, int len,
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800583 __be32 __user *optval,
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700584 int __user *optlen)
585{
586 const struct dccp_sock *dp = dccp_sk(sk);
587 const struct dccp_service_list *sl;
588 int err = -ENOENT, slen = 0, total_len = sizeof(u32);
589
590 lock_sock(sk);
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700591 if ((sl = dp->dccps_service_list) != NULL) {
592 slen = sl->dccpsl_nr * sizeof(u32);
593 total_len += slen;
594 }
595
596 err = -EINVAL;
597 if (total_len > len)
598 goto out;
599
600 err = 0;
601 if (put_user(total_len, optlen) ||
602 put_user(dp->dccps_service, optval) ||
603 (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen)))
604 err = -EFAULT;
605out:
606 release_sock(sk);
607 return err;
608}
609
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800610static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -0300611 char __user *optval, int __user *optlen)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700612{
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300613 struct dccp_sock *dp;
614 int val, len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700615
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300616 if (get_user(len, optlen))
617 return -EFAULT;
618
Arnaldo Carvalho de Melo39ebc022007-03-28 11:54:32 -0700619 if (len < (int)sizeof(int))
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300620 return -EINVAL;
621
622 dp = dccp_sk(sk);
623
624 switch (optname) {
625 case DCCP_SOCKOPT_PACKET_SIZE:
Gerrit Renker5aed3242006-11-28 19:33:36 -0200626 DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
Arnaldo Carvalho de Melo841bac12006-11-28 19:42:03 -0200627 return 0;
Arnaldo Carvalho de Melo88f964d2005-09-18 00:19:32 -0700628 case DCCP_SOCKOPT_SERVICE:
629 return dccp_getsockopt_service(sk, len,
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800630 (__be32 __user *)optval, optlen);
Gerrit Renker7c559a92007-10-04 14:39:22 -0700631 case DCCP_SOCKOPT_GET_CUR_MPS:
632 val = dp->dccps_mss_cache;
633 len = sizeof(val);
634 break;
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200635 case DCCP_SOCKOPT_SEND_CSCOV:
636 val = dp->dccps_pcslen;
Arnaldo Carvalho de Melo39ebc022007-03-28 11:54:32 -0700637 len = sizeof(val);
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200638 break;
639 case DCCP_SOCKOPT_RECV_CSCOV:
640 val = dp->dccps_pcrlen;
Arnaldo Carvalho de Melo39ebc022007-03-28 11:54:32 -0700641 len = sizeof(val);
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200642 break;
Arnaldo Carvalho de Melo88f964d2005-09-18 00:19:32 -0700643 case 128 ... 191:
644 return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
645 len, (u32 __user *)optval, optlen);
646 case 192 ... 255:
647 return ccid_hc_tx_getsockopt(dp->dccps_hc_tx_ccid, sk, optname,
648 len, (u32 __user *)optval, optlen);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300649 default:
650 return -ENOPROTOOPT;
651 }
652
653 if (put_user(len, optlen) || copy_to_user(optval, &val, len))
654 return -EFAULT;
655
656 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700657}
658
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800659int dccp_getsockopt(struct sock *sk, int level, int optname,
660 char __user *optval, int __user *optlen)
661{
662 if (level != SOL_DCCP)
663 return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
664 optname, optval,
665 optlen);
666 return do_dccp_getsockopt(sk, level, optname, optval, optlen);
667}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800668
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800669EXPORT_SYMBOL_GPL(dccp_getsockopt);
670
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800671#ifdef CONFIG_COMPAT
672int compat_dccp_getsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800673 char __user *optval, int __user *optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800674{
Arnaldo Carvalho de Melodec73ff2006-03-20 22:46:16 -0800675 if (level != SOL_DCCP)
676 return inet_csk_compat_getsockopt(sk, level, optname,
677 optval, optlen);
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800678 return do_dccp_getsockopt(sk, level, optname, optval, optlen);
679}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800680
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800681EXPORT_SYMBOL_GPL(compat_dccp_getsockopt);
682#endif
683
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700684int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
685 size_t len)
686{
687 const struct dccp_sock *dp = dccp_sk(sk);
688 const int flags = msg->msg_flags;
689 const int noblock = flags & MSG_DONTWAIT;
690 struct sk_buff *skb;
691 int rc, size;
692 long timeo;
693
694 if (len > dp->dccps_mss_cache)
695 return -EMSGSIZE;
696
697 lock_sock(sk);
Ian McDonaldb1308dc2006-11-20 18:30:17 -0200698
699 if (sysctl_dccp_tx_qlen &&
700 (sk->sk_write_queue.qlen >= sysctl_dccp_tx_qlen)) {
701 rc = -EAGAIN;
702 goto out_release;
703 }
704
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700705 timeo = sock_sndtimeo(sk, noblock);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700706
707 /*
708 * We have to use sk_stream_wait_connect here to set sk_write_pending,
709 * so that the trick in dccp_rcv_request_sent_state_process.
710 */
711 /* Wait for a connection to finish. */
Gerrit Renkercecd8d02007-09-26 19:36:08 -0300712 if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN))
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700713 if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0)
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700714 goto out_release;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700715
716 size = sk->sk_prot->max_header + len;
717 release_sock(sk);
718 skb = sock_alloc_send_skb(sk, size, noblock, &rc);
719 lock_sock(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700720 if (skb == NULL)
721 goto out_release;
722
723 skb_reserve(skb, sk->sk_prot->max_header);
724 rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700725 if (rc != 0)
726 goto out_discard;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700727
Ian McDonald97e58482006-08-26 19:16:45 -0700728 skb_queue_tail(&sk->sk_write_queue, skb);
729 dccp_write_xmit(sk,0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700730out_release:
731 release_sock(sk);
732 return rc ? : len;
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700733out_discard:
734 kfree_skb(skb);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700735 goto out_release;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700736}
737
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800738EXPORT_SYMBOL_GPL(dccp_sendmsg);
739
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700740int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
741 size_t len, int nonblock, int flags, int *addr_len)
742{
743 const struct dccp_hdr *dh;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700744 long timeo;
745
746 lock_sock(sk);
747
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300748 if (sk->sk_state == DCCP_LISTEN) {
749 len = -ENOTCONN;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700750 goto out;
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300751 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700752
753 timeo = sock_rcvtimeo(sk, nonblock);
754
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700755 do {
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300756 struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700757
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300758 if (skb == NULL)
759 goto verify_sock_status;
760
761 dh = dccp_hdr(skb);
762
763 if (dh->dccph_type == DCCP_PKT_DATA ||
764 dh->dccph_type == DCCP_PKT_DATAACK)
765 goto found_ok_skb;
766
767 if (dh->dccph_type == DCCP_PKT_RESET ||
768 dh->dccph_type == DCCP_PKT_CLOSE) {
769 dccp_pr_debug("found fin ok!\n");
770 len = 0;
771 goto found_fin_ok;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700772 }
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300773 dccp_pr_debug("packet_type=%s\n",
774 dccp_packet_name(dh->dccph_type));
Chris Leech624d1162006-05-23 18:01:28 -0700775 sk_eat_skb(sk, skb, 0);
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300776verify_sock_status:
777 if (sock_flag(sk, SOCK_DONE)) {
778 len = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700779 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700780 }
781
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300782 if (sk->sk_err) {
783 len = sock_error(sk);
784 break;
785 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700786
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300787 if (sk->sk_shutdown & RCV_SHUTDOWN) {
788 len = 0;
789 break;
790 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700791
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300792 if (sk->sk_state == DCCP_CLOSED) {
793 if (!sock_flag(sk, SOCK_DONE)) {
794 /* This occurs when user tries to read
795 * from never connected socket.
796 */
797 len = -ENOTCONN;
798 break;
799 }
800 len = 0;
801 break;
802 }
803
804 if (!timeo) {
805 len = -EAGAIN;
806 break;
807 }
808
809 if (signal_pending(current)) {
810 len = sock_intr_errno(timeo);
811 break;
812 }
813
814 sk_wait_data(sk, &timeo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700815 continue;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700816 found_ok_skb:
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300817 if (len > skb->len)
818 len = skb->len;
819 else if (len < skb->len)
820 msg->msg_flags |= MSG_TRUNC;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700821
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300822 if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len)) {
823 /* Exception. Bailout! */
824 len = -EFAULT;
825 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700826 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700827 found_fin_ok:
828 if (!(flags & MSG_PEEK))
Chris Leech624d1162006-05-23 18:01:28 -0700829 sk_eat_skb(sk, skb, 0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700830 break;
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300831 } while (1);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700832out:
833 release_sock(sk);
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300834 return len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700835}
836
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800837EXPORT_SYMBOL_GPL(dccp_recvmsg);
838
839int inet_dccp_listen(struct socket *sock, int backlog)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700840{
841 struct sock *sk = sock->sk;
842 unsigned char old_state;
843 int err;
844
845 lock_sock(sk);
846
847 err = -EINVAL;
848 if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP)
849 goto out;
850
851 old_state = sk->sk_state;
852 if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN)))
853 goto out;
854
855 /* Really, if the socket is already in listen state
856 * we can only allow the backlog to be adjusted.
857 */
858 if (old_state != DCCP_LISTEN) {
859 /*
860 * FIXME: here it probably should be sk->sk_prot->listen_start
861 * see tcp_listen_start
862 */
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800863 err = dccp_listen_start(sk, backlog);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700864 if (err)
865 goto out;
866 }
867 sk->sk_max_ack_backlog = backlog;
868 err = 0;
869
870out:
871 release_sock(sk);
872 return err;
873}
874
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800875EXPORT_SYMBOL_GPL(inet_dccp_listen);
876
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700877static const unsigned char dccp_new_state[] = {
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -0300878 /* current state: new state: action: */
879 [0] = DCCP_CLOSED,
Arnaldo Carvalho de Melo8109b022006-12-10 16:01:18 -0200880 [DCCP_OPEN] = DCCP_CLOSING | DCCP_ACTION_FIN,
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -0300881 [DCCP_REQUESTING] = DCCP_CLOSED,
882 [DCCP_PARTOPEN] = DCCP_CLOSING | DCCP_ACTION_FIN,
883 [DCCP_LISTEN] = DCCP_CLOSED,
884 [DCCP_RESPOND] = DCCP_CLOSED,
885 [DCCP_CLOSING] = DCCP_CLOSED,
886 [DCCP_TIME_WAIT] = DCCP_CLOSED,
887 [DCCP_CLOSED] = DCCP_CLOSED,
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700888};
889
890static int dccp_close_state(struct sock *sk)
891{
892 const int next = dccp_new_state[sk->sk_state];
893 const int ns = next & DCCP_STATE_MASK;
894
895 if (ns != sk->sk_state)
896 dccp_set_state(sk, ns);
897
898 return next & DCCP_ACTION_FIN;
899}
900
901void dccp_close(struct sock *sk, long timeout)
902{
Ian McDonald97e58482006-08-26 19:16:45 -0700903 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700904 struct sk_buff *skb;
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800905 u32 data_was_unread = 0;
Herbert Xu134af342006-05-05 17:09:13 -0700906 int state;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700907
908 lock_sock(sk);
909
910 sk->sk_shutdown = SHUTDOWN_MASK;
911
912 if (sk->sk_state == DCCP_LISTEN) {
913 dccp_set_state(sk, DCCP_CLOSED);
914
915 /* Special case. */
916 inet_csk_listen_stop(sk);
917
918 goto adjudge_to_death;
919 }
920
Ian McDonald97e58482006-08-26 19:16:45 -0700921 sk_stop_timer(sk, &dp->dccps_xmit_timer);
922
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700923 /*
924 * We need to flush the recv. buffs. We do this only on the
925 * descriptor close, not protocol-sourced closes, because the
926 *reader process may not have drained the data yet!
927 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700928 while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800929 data_was_unread += skb->len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700930 __kfree_skb(skb);
931 }
932
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800933 if (data_was_unread) {
934 /* Unread data was tossed, send an appropriate Reset Code */
935 DCCP_WARN("DCCP: ABORT -- %u bytes unread\n", data_was_unread);
936 dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
937 dccp_set_state(sk, DCCP_CLOSED);
938 } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700939 /* Check zero linger _after_ checking for unread data. */
940 sk->sk_prot->disconnect(sk, 0);
941 } else if (dccp_close_state(sk)) {
Arnaldo Carvalho de Melo7ad07e72005-08-23 21:50:06 -0700942 dccp_send_close(sk, 1);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700943 }
944
945 sk_stream_wait_close(sk, timeout);
946
947adjudge_to_death:
Herbert Xu134af342006-05-05 17:09:13 -0700948 state = sk->sk_state;
949 sock_hold(sk);
950 sock_orphan(sk);
951 atomic_inc(sk->sk_prot->orphan_count);
952
Arnaldo Carvalho de Melo7ad07e72005-08-23 21:50:06 -0700953 /*
954 * It is the last release_sock in its life. It will remove backlog.
955 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700956 release_sock(sk);
957 /*
958 * Now socket is owned by kernel and we acquire BH lock
959 * to finish close. No need to check for user refs.
960 */
961 local_bh_disable();
962 bh_lock_sock(sk);
963 BUG_TRAP(!sock_owned_by_user(sk));
964
Herbert Xu134af342006-05-05 17:09:13 -0700965 /* Have we already been destroyed by a softirq or backlog? */
966 if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
967 goto out;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700968
Arnaldo Carvalho de Melo7ad07e72005-08-23 21:50:06 -0700969 /*
970 * The last release_sock may have processed the CLOSE or RESET
971 * packet moving sock to CLOSED state, if not we have to fire
972 * the CLOSE/CLOSEREQ retransmission timer, see "8.3. Termination"
973 * in draft-ietf-dccp-spec-11. -acme
974 */
975 if (sk->sk_state == DCCP_CLOSING) {
976 /* FIXME: should start at 2 * RTT */
977 /* Timer for repeating the CLOSE/CLOSEREQ until an answer. */
978 inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
979 inet_csk(sk)->icsk_rto,
980 DCCP_RTO_MAX);
981#if 0
982 /* Yeah, we should use sk->sk_prot->orphan_count, etc */
983 dccp_set_state(sk, DCCP_CLOSED);
984#endif
985 }
986
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700987 if (sk->sk_state == DCCP_CLOSED)
988 inet_csk_destroy_sock(sk);
989
990 /* Otherwise, socket is reprieved until protocol close. */
991
Herbert Xu134af342006-05-05 17:09:13 -0700992out:
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700993 bh_unlock_sock(sk);
994 local_bh_enable();
995 sock_put(sk);
996}
997
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800998EXPORT_SYMBOL_GPL(dccp_close);
999
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001000void dccp_shutdown(struct sock *sk, int how)
1001{
Gerrit Renker8e8c71f2007-11-21 09:56:48 -02001002 dccp_pr_debug("called shutdown(%x)\n", how);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001003}
1004
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001005EXPORT_SYMBOL_GPL(dccp_shutdown);
1006
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001007static int __init dccp_mib_init(void)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001008{
1009 int rc = -ENOMEM;
1010
1011 dccp_statistics[0] = alloc_percpu(struct dccp_mib);
1012 if (dccp_statistics[0] == NULL)
1013 goto out;
1014
1015 dccp_statistics[1] = alloc_percpu(struct dccp_mib);
1016 if (dccp_statistics[1] == NULL)
1017 goto out_free_one;
1018
1019 rc = 0;
1020out:
1021 return rc;
1022out_free_one:
1023 free_percpu(dccp_statistics[0]);
1024 dccp_statistics[0] = NULL;
1025 goto out;
1026
1027}
1028
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001029static void dccp_mib_exit(void)
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001030{
1031 free_percpu(dccp_statistics[0]);
1032 free_percpu(dccp_statistics[1]);
1033 dccp_statistics[0] = dccp_statistics[1] = NULL;
1034}
1035
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001036static int thash_entries;
1037module_param(thash_entries, int, 0444);
1038MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
1039
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -03001040#ifdef CONFIG_IP_DCCP_DEBUG
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001041int dccp_debug;
Gerrit Renker042d18f2007-10-04 14:39:53 -07001042module_param(dccp_debug, bool, 0444);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001043MODULE_PARM_DESC(dccp_debug, "Enable debug messages");
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001044
1045EXPORT_SYMBOL_GPL(dccp_debug);
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -03001046#endif
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001047
1048static int __init dccp_init(void)
1049{
1050 unsigned long goal;
1051 int ehash_order, bhash_order, i;
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001052 int rc = -ENOBUFS;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001053
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001054 dccp_hashinfo.bind_bucket_cachep =
1055 kmem_cache_create("dccp_bind_bucket",
1056 sizeof(struct inet_bind_bucket), 0,
Paul Mundt20c2df82007-07-20 10:11:58 +09001057 SLAB_HWCACHE_ALIGN, NULL);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001058 if (!dccp_hashinfo.bind_bucket_cachep)
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001059 goto out;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001060
1061 /*
1062 * Size and allocate the main established and bind bucket
1063 * hash tables.
1064 *
1065 * The methodology is similar to that of the buffer cache.
1066 */
1067 if (num_physpages >= (128 * 1024))
1068 goal = num_physpages >> (21 - PAGE_SHIFT);
1069 else
1070 goal = num_physpages >> (23 - PAGE_SHIFT);
1071
1072 if (thash_entries)
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001073 goal = (thash_entries *
1074 sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001075 for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++)
1076 ;
1077 do {
1078 dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE /
1079 sizeof(struct inet_ehash_bucket);
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001080 while (dccp_hashinfo.ehash_size &
1081 (dccp_hashinfo.ehash_size - 1))
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001082 dccp_hashinfo.ehash_size--;
1083 dccp_hashinfo.ehash = (struct inet_ehash_bucket *)
1084 __get_free_pages(GFP_ATOMIC, ehash_order);
1085 } while (!dccp_hashinfo.ehash && --ehash_order > 0);
1086
1087 if (!dccp_hashinfo.ehash) {
Gerrit Renker59348b12006-11-20 18:39:23 -02001088 DCCP_CRIT("Failed to allocate DCCP established hash table");
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001089 goto out_free_bind_bucket_cachep;
1090 }
1091
Eric Dumazetdbca9b2752007-02-08 14:16:46 -08001092 for (i = 0; i < dccp_hashinfo.ehash_size; i++) {
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001093 INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain);
Eric Dumazetdbca9b2752007-02-08 14:16:46 -08001094 INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].twchain);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001095 }
1096
Eric Dumazet230140c2007-11-07 02:40:20 -08001097 if (inet_ehash_locks_alloc(&dccp_hashinfo))
1098 goto out_free_dccp_ehash;
1099
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001100 bhash_order = ehash_order;
1101
1102 do {
1103 dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE /
1104 sizeof(struct inet_bind_hashbucket);
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001105 if ((dccp_hashinfo.bhash_size > (64 * 1024)) &&
1106 bhash_order > 0)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001107 continue;
1108 dccp_hashinfo.bhash = (struct inet_bind_hashbucket *)
1109 __get_free_pages(GFP_ATOMIC, bhash_order);
1110 } while (!dccp_hashinfo.bhash && --bhash_order >= 0);
1111
1112 if (!dccp_hashinfo.bhash) {
Gerrit Renker59348b12006-11-20 18:39:23 -02001113 DCCP_CRIT("Failed to allocate DCCP bind hash table");
Eric Dumazet230140c2007-11-07 02:40:20 -08001114 goto out_free_dccp_locks;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001115 }
1116
1117 for (i = 0; i < dccp_hashinfo.bhash_size; i++) {
1118 spin_lock_init(&dccp_hashinfo.bhash[i].lock);
1119 INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
1120 }
1121
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001122 rc = dccp_mib_init();
Arnaldo Carvalho de Melofa23e2e2006-03-20 17:16:01 -08001123 if (rc)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001124 goto out_free_dccp_bhash;
1125
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001126 rc = dccp_ackvec_init();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001127 if (rc)
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001128 goto out_free_dccp_mib;
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001129
Arnaldo Carvalho de Meloe55d9122006-03-20 19:25:02 -08001130 rc = dccp_sysctl_init();
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001131 if (rc)
1132 goto out_ackvec_exit;
Gerrit Renker4c70f382007-09-25 22:40:13 -07001133
1134 dccp_timestamping_init();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001135out:
1136 return rc;
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001137out_ackvec_exit:
1138 dccp_ackvec_exit();
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001139out_free_dccp_mib:
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001140 dccp_mib_exit();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001141out_free_dccp_bhash:
1142 free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
1143 dccp_hashinfo.bhash = NULL;
Eric Dumazet230140c2007-11-07 02:40:20 -08001144out_free_dccp_locks:
1145 inet_ehash_locks_free(&dccp_hashinfo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001146out_free_dccp_ehash:
1147 free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order);
1148 dccp_hashinfo.ehash = NULL;
1149out_free_bind_bucket_cachep:
1150 kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
1151 dccp_hashinfo.bind_bucket_cachep = NULL;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001152 goto out;
1153}
1154
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001155static void __exit dccp_fini(void)
1156{
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001157 dccp_mib_exit();
Arnaldo Carvalho de Melo725ba8e2005-08-13 20:35:39 -03001158 free_pages((unsigned long)dccp_hashinfo.bhash,
1159 get_order(dccp_hashinfo.bhash_size *
1160 sizeof(struct inet_bind_hashbucket)));
1161 free_pages((unsigned long)dccp_hashinfo.ehash,
1162 get_order(dccp_hashinfo.ehash_size *
1163 sizeof(struct inet_ehash_bucket)));
Eric Dumazet230140c2007-11-07 02:40:20 -08001164 inet_ehash_locks_free(&dccp_hashinfo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001165 kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001166 dccp_ackvec_exit();
Arnaldo Carvalho de Meloe55d9122006-03-20 19:25:02 -08001167 dccp_sysctl_exit();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001168}
1169
1170module_init(dccp_init);
1171module_exit(dccp_fini);
1172
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001173MODULE_LICENSE("GPL");
1174MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>");
1175MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");