blob: db225f93cd5a6e9b1f11c14773ca7ac691d3e21c [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 <linux/spinlock.h>
31#include <linux/timer.h>
32#include <linux/delay.h>
33#include <linux/poll.h>
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070034
35#include "ccid.h"
36#include "dccp.h"
Andrea Bittauafe00252006-03-20 17:43:56 -080037#include "feat.h"
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070038
Eric Dumazetba899662005-08-26 12:05:31 -070039DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070040
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -080041EXPORT_SYMBOL_GPL(dccp_statistics);
42
Eric Dumazetdd24c002008-11-25 21:17:14 -080043struct percpu_counter dccp_orphan_count;
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -080044EXPORT_SYMBOL_GPL(dccp_orphan_count);
45
Eric Dumazet5caea4e2008-11-20 00:40:07 -080046struct inet_hashinfo dccp_hashinfo;
Arnaldo Carvalho de Melo075ae862006-03-20 21:24:19 -080047EXPORT_SYMBOL_GPL(dccp_hashinfo);
48
Ian McDonaldb1308dc2006-11-20 18:30:17 -020049/* the maximum queue length for tx in packets. 0 is no limit */
50int sysctl_dccp_tx_qlen __read_mostly = 5;
51
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080052void dccp_set_state(struct sock *sk, const int state)
53{
54 const int oldstate = sk->sk_state;
55
Gerrit Renkerf11135a2007-11-28 11:34:53 -020056 dccp_pr_debug("%s(%p) %s --> %s\n", dccp_role(sk), sk,
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080057 dccp_state_name(oldstate), dccp_state_name(state));
58 WARN_ON(state == oldstate);
59
60 switch (state) {
61 case DCCP_OPEN:
62 if (oldstate != DCCP_OPEN)
63 DCCP_INC_STATS(DCCP_MIB_CURRESTAB);
64 break;
65
66 case DCCP_CLOSED:
Gerrit Renker0c869622007-11-28 11:59:48 -020067 if (oldstate == DCCP_OPEN || oldstate == DCCP_ACTIVE_CLOSEREQ ||
68 oldstate == DCCP_CLOSING)
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080069 DCCP_INC_STATS(DCCP_MIB_ESTABRESETS);
70
71 sk->sk_prot->unhash(sk);
72 if (inet_csk(sk)->icsk_bind_hash != NULL &&
73 !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))
Arnaldo Carvalho de Meloab1e0a12008-02-03 04:06:04 -080074 inet_put_port(sk);
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080075 /* fall through */
76 default:
77 if (oldstate == DCCP_OPEN)
78 DCCP_DEC_STATS(DCCP_MIB_CURRESTAB);
79 }
80
81 /* Change state AFTER socket is unhashed to avoid closed
82 * socket sitting in hash tables.
83 */
84 sk->sk_state = state;
85}
86
87EXPORT_SYMBOL_GPL(dccp_set_state);
88
Gerrit Renker0c869622007-11-28 11:59:48 -020089static void dccp_finish_passive_close(struct sock *sk)
90{
91 switch (sk->sk_state) {
92 case DCCP_PASSIVE_CLOSE:
93 /* Node (client or server) has received Close packet. */
94 dccp_send_reset(sk, DCCP_RESET_CODE_CLOSED);
95 dccp_set_state(sk, DCCP_CLOSED);
96 break;
97 case DCCP_PASSIVE_CLOSEREQ:
98 /*
99 * Client received CloseReq. We set the `active' flag so that
100 * dccp_send_close() retransmits the Close as per RFC 4340, 8.3.
101 */
102 dccp_send_close(sk, 1);
103 dccp_set_state(sk, DCCP_CLOSING);
104 }
105}
106
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -0800107void dccp_done(struct sock *sk)
108{
109 dccp_set_state(sk, DCCP_CLOSED);
110 dccp_clear_xmit_timers(sk);
111
112 sk->sk_shutdown = SHUTDOWN_MASK;
113
114 if (!sock_flag(sk, SOCK_DEAD))
115 sk->sk_state_change(sk);
116 else
117 inet_csk_destroy_sock(sk);
118}
119
120EXPORT_SYMBOL_GPL(dccp_done);
121
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700122const char *dccp_packet_name(const int type)
123{
124 static const char *dccp_packet_names[] = {
125 [DCCP_PKT_REQUEST] = "REQUEST",
126 [DCCP_PKT_RESPONSE] = "RESPONSE",
127 [DCCP_PKT_DATA] = "DATA",
128 [DCCP_PKT_ACK] = "ACK",
129 [DCCP_PKT_DATAACK] = "DATAACK",
130 [DCCP_PKT_CLOSEREQ] = "CLOSEREQ",
131 [DCCP_PKT_CLOSE] = "CLOSE",
132 [DCCP_PKT_RESET] = "RESET",
133 [DCCP_PKT_SYNC] = "SYNC",
134 [DCCP_PKT_SYNCACK] = "SYNCACK",
135 };
136
137 if (type >= DCCP_NR_PKT_TYPES)
138 return "INVALID";
139 else
140 return dccp_packet_names[type];
141}
142
143EXPORT_SYMBOL_GPL(dccp_packet_name);
144
145const char *dccp_state_name(const int state)
146{
147 static char *dccp_state_names[] = {
Gerrit Renkerf11135a2007-11-28 11:34:53 -0200148 [DCCP_OPEN] = "OPEN",
149 [DCCP_REQUESTING] = "REQUESTING",
150 [DCCP_PARTOPEN] = "PARTOPEN",
151 [DCCP_LISTEN] = "LISTEN",
152 [DCCP_RESPOND] = "RESPOND",
153 [DCCP_CLOSING] = "CLOSING",
154 [DCCP_ACTIVE_CLOSEREQ] = "CLOSEREQ",
155 [DCCP_PASSIVE_CLOSE] = "PASSIVE_CLOSE",
156 [DCCP_PASSIVE_CLOSEREQ] = "PASSIVE_CLOSEREQ",
157 [DCCP_TIME_WAIT] = "TIME_WAIT",
158 [DCCP_CLOSED] = "CLOSED",
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700159 };
160
161 if (state >= DCCP_MAX_STATES)
162 return "INVALID STATE!";
163 else
164 return dccp_state_names[state];
165}
166
167EXPORT_SYMBOL_GPL(dccp_state_name);
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);
Gerrit Renker410e27a2008-09-09 13:27:22 +0200172 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
Gerrit Renker410e27a2008-09-09 13:27:22 +0200175 dccp_minisock_init(&dp->dccps_minisock);
176
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;
Gerrit Renker410e27a2008-09-09 13:27:22 +0200182 dp->dccps_mss_cache = 536;
Arnaldo Carvalho de Meloe18d7a92007-11-24 21:42:53 -0200183 dp->dccps_rate_last = jiffies;
184 dp->dccps_role = DCCP_ROLE_UNDEFINED;
185 dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT;
Gerrit Renker410e27a2008-09-09 13:27:22 +0200186 dp->dccps_l_ack_ratio = dp->dccps_r_ack_ratio = 1;
Arnaldo Carvalho de Meloe18d7a92007-11-24 21:42:53 -0200187
188 dccp_init_xmit_timers(sk);
189
Gerrit Renkerac757732008-11-04 23:55:49 -0800190 INIT_LIST_HEAD(&dp->dccps_featneg);
Gerrit Renker410e27a2008-09-09 13:27:22 +0200191 /*
192 * FIXME: We're hardcoding the CCID, and doing this at this point makes
193 * the listening (master) sock get CCID control blocks, which is not
194 * necessary, but for now, to not mess with the test userspace apps,
195 * lets leave it here, later the real solution is to do this in a
196 * setsockopt(CCIDs-I-want/accept). -acme
197 */
198 if (likely(ctl_sock_initialized)) {
Gerrit Renkere8ef9672008-11-12 00:43:40 -0800199 int rc = dccp_feat_init(sk);
Gerrit Renker410e27a2008-09-09 13:27:22 +0200200
201 if (rc)
202 return rc;
203
204 if (dmsk->dccpms_send_ack_vector) {
205 dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL);
206 if (dp->dccps_hc_rx_ackvec == NULL)
207 return -ENOMEM;
208 }
209 dp->dccps_hc_rx_ccid = ccid_hc_rx_new(dmsk->dccpms_rx_ccid,
210 sk, GFP_KERNEL);
211 dp->dccps_hc_tx_ccid = ccid_hc_tx_new(dmsk->dccpms_tx_ccid,
212 sk, GFP_KERNEL);
213 if (unlikely(dp->dccps_hc_rx_ccid == NULL ||
214 dp->dccps_hc_tx_ccid == NULL)) {
215 ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
216 ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
217 if (dmsk->dccpms_send_ack_vector) {
218 dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
219 dp->dccps_hc_rx_ackvec = NULL;
220 }
221 dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
222 return -ENOMEM;
223 }
224 } else {
225 /* control socket doesn't need feat nego */
226 INIT_LIST_HEAD(&dmsk->dccpms_pending);
227 INIT_LIST_HEAD(&dmsk->dccpms_conf);
228 }
229
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800230 return 0;
231}
232
233EXPORT_SYMBOL_GPL(dccp_init_sock);
234
Brian Haley7d06b2e2008-06-14 17:04:49 -0700235void dccp_destroy_sock(struct sock *sk)
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800236{
237 struct dccp_sock *dp = dccp_sk(sk);
Gerrit Renker410e27a2008-09-09 13:27:22 +0200238 struct dccp_minisock *dmsk = dccp_msk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800239
240 /*
241 * DCCP doesn't use sk_write_queue, just sk_send_head
242 * for retransmissions
243 */
244 if (sk->sk_send_head != NULL) {
245 kfree_skb(sk->sk_send_head);
246 sk->sk_send_head = NULL;
247 }
248
249 /* Clean up a referenced DCCP bind bucket. */
250 if (inet_csk(sk)->icsk_bind_hash != NULL)
Arnaldo Carvalho de Meloab1e0a12008-02-03 04:06:04 -0800251 inet_put_port(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800252
253 kfree(dp->dccps_service_list);
254 dp->dccps_service_list = NULL;
255
Gerrit Renker410e27a2008-09-09 13:27:22 +0200256 if (dmsk->dccpms_send_ack_vector) {
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800257 dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
258 dp->dccps_hc_rx_ackvec = NULL;
259 }
260 ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
261 ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
262 dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
263
264 /* clean up feature negotiation state */
Gerrit Renkerd99a7bd2008-11-04 23:56:30 -0800265 dccp_feat_list_purge(&dp->dccps_featneg);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800266}
267
268EXPORT_SYMBOL_GPL(dccp_destroy_sock);
269
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800270static inline int dccp_listen_start(struct sock *sk, int backlog)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700271{
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700272 struct dccp_sock *dp = dccp_sk(sk);
273
274 dp->dccps_role = DCCP_ROLE_LISTEN;
Gerrit Renker9eca0a42008-11-12 00:48:44 -0800275 /* do not start to listen if feature negotiation setup fails */
276 if (dccp_feat_finalise_settings(dp))
277 return -EPROTO;
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800278 return inet_csk_listen_start(sk, backlog);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700279}
280
Gerrit Renkerce865a62007-11-24 22:14:15 -0200281static inline int dccp_need_reset(int state)
282{
283 return state != DCCP_CLOSED && state != DCCP_LISTEN &&
284 state != DCCP_REQUESTING;
285}
286
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700287int dccp_disconnect(struct sock *sk, int flags)
288{
289 struct inet_connection_sock *icsk = inet_csk(sk);
290 struct inet_sock *inet = inet_sk(sk);
291 int err = 0;
292 const int old_state = sk->sk_state;
293
294 if (old_state != DCCP_CLOSED)
295 dccp_set_state(sk, DCCP_CLOSED);
296
Gerrit Renkerce865a62007-11-24 22:14:15 -0200297 /*
298 * This corresponds to the ABORT function of RFC793, sec. 3.8
299 * TCP uses a RST segment, DCCP a Reset packet with Code 2, "Aborted".
300 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700301 if (old_state == DCCP_LISTEN) {
302 inet_csk_listen_stop(sk);
Gerrit Renkerce865a62007-11-24 22:14:15 -0200303 } else if (dccp_need_reset(old_state)) {
304 dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
305 sk->sk_err = ECONNRESET;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700306 } else if (old_state == DCCP_REQUESTING)
307 sk->sk_err = ECONNRESET;
308
309 dccp_clear_xmit_timers(sk);
Gerrit Renker48816322008-08-23 13:28:27 +0200310
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700311 __skb_queue_purge(&sk->sk_receive_queue);
Gerrit Renker48816322008-08-23 13:28:27 +0200312 __skb_queue_purge(&sk->sk_write_queue);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700313 if (sk->sk_send_head != NULL) {
314 __kfree_skb(sk->sk_send_head);
315 sk->sk_send_head = NULL;
316 }
317
318 inet->dport = 0;
319
320 if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
321 inet_reset_saddr(sk);
322
323 sk->sk_shutdown = 0;
324 sock_reset_flag(sk, SOCK_DONE);
325
326 icsk->icsk_backoff = 0;
327 inet_csk_delack_init(sk);
328 __sk_dst_reset(sk);
329
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700330 WARN_ON(inet->num && !icsk->icsk_bind_hash);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700331
332 sk->sk_error_report(sk);
333 return err;
334}
335
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800336EXPORT_SYMBOL_GPL(dccp_disconnect);
337
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700338/*
339 * Wait for a DCCP event.
340 *
341 * Note that we don't need to lock the socket, as the upper poll layers
342 * take care of normal races (between the test and the event) and we don't
343 * go look at any of the socket buffers directly.
344 */
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800345unsigned int dccp_poll(struct file *file, struct socket *sock,
346 poll_table *wait)
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700347{
348 unsigned int mask;
349 struct sock *sk = sock->sk;
350
351 poll_wait(file, sk->sk_sleep, wait);
352 if (sk->sk_state == DCCP_LISTEN)
353 return inet_csk_listen_poll(sk);
354
355 /* Socket is not locked. We are protected from async events
356 by poll logic and correct handling of state changes
357 made by another threads is impossible in any case.
358 */
359
360 mask = 0;
361 if (sk->sk_err)
362 mask = POLLERR;
363
364 if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED)
365 mask |= POLLHUP;
366 if (sk->sk_shutdown & RCV_SHUTDOWN)
Davide Libenzif348d702006-03-25 03:07:39 -0800367 mask |= POLLIN | POLLRDNORM | POLLRDHUP;
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700368
369 /* Connected? */
370 if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) {
371 if (atomic_read(&sk->sk_rmem_alloc) > 0)
372 mask |= POLLIN | POLLRDNORM;
373
374 if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {
375 if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {
376 mask |= POLLOUT | POLLWRNORM;
377 } else { /* send SIGIO later */
378 set_bit(SOCK_ASYNC_NOSPACE,
379 &sk->sk_socket->flags);
380 set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
381
382 /* Race breaker. If space is freed after
383 * wspace test but before the flags are set,
384 * IO signal will be lost.
385 */
386 if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk))
387 mask |= POLLOUT | POLLWRNORM;
388 }
389 }
390 }
391 return mask;
392}
393
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800394EXPORT_SYMBOL_GPL(dccp_poll);
395
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700396int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
397{
Arnaldo Carvalho de Melo62731722007-10-23 20:23:30 -0700398 int rc = -ENOTCONN;
399
400 lock_sock(sk);
401
402 if (sk->sk_state == DCCP_LISTEN)
403 goto out;
404
405 switch (cmd) {
406 case SIOCINQ: {
407 struct sk_buff *skb;
408 unsigned long amount = 0;
409
410 skb = skb_peek(&sk->sk_receive_queue);
411 if (skb != NULL) {
412 /*
413 * We will only return the amount of this packet since
414 * that is all that will be read.
415 */
416 amount = skb->len;
417 }
418 rc = put_user(amount, (int __user *)arg);
419 }
420 break;
421 default:
422 rc = -ENOIOCTLCMD;
423 break;
424 }
425out:
426 release_sock(sk);
427 return rc;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700428}
429
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800430EXPORT_SYMBOL_GPL(dccp_ioctl);
431
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800432static int dccp_setsockopt_service(struct sock *sk, const __be32 service,
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700433 char __user *optval, int optlen)
434{
435 struct dccp_sock *dp = dccp_sk(sk);
436 struct dccp_service_list *sl = NULL;
437
Arnaldo Carvalho de Melo8109b022006-12-10 16:01:18 -0200438 if (service == DCCP_SERVICE_INVALID_VALUE ||
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700439 optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32))
440 return -EINVAL;
441
442 if (optlen > sizeof(service)) {
443 sl = kmalloc(optlen, GFP_KERNEL);
444 if (sl == NULL)
445 return -ENOMEM;
446
447 sl->dccpsl_nr = optlen / sizeof(u32) - 1;
448 if (copy_from_user(sl->dccpsl_list,
449 optval + sizeof(service),
450 optlen - sizeof(service)) ||
451 dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) {
452 kfree(sl);
453 return -EFAULT;
454 }
455 }
456
457 lock_sock(sk);
458 dp->dccps_service = service;
459
Jesper Juhla51482b2005-11-08 09:41:34 -0800460 kfree(dp->dccps_service_list);
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700461
462 dp->dccps_service_list = sl;
463 release_sock(sk);
464 return 0;
465}
466
Gerrit Renker29450552008-11-16 22:53:48 -0800467static int dccp_setsockopt_cscov(struct sock *sk, int cscov, bool rx)
468{
469 u8 *list, len;
470 int i, rc;
471
472 if (cscov < 0 || cscov > 15)
473 return -EINVAL;
474 /*
475 * Populate a list of permissible values, in the range cscov...15. This
476 * is necessary since feature negotiation of single values only works if
477 * both sides incidentally choose the same value. Since the list starts
478 * lowest-value first, negotiation will pick the smallest shared value.
479 */
480 if (cscov == 0)
481 return 0;
482 len = 16 - cscov;
483
484 list = kmalloc(len, GFP_KERNEL);
485 if (list == NULL)
486 return -ENOBUFS;
487
488 for (i = 0; i < len; i++)
489 list[i] = cscov++;
490
491 rc = dccp_feat_register_sp(sk, DCCPF_MIN_CSUM_COVER, rx, list, len);
492
493 if (rc == 0) {
494 if (rx)
495 dccp_sk(sk)->dccps_pcrlen = cscov;
496 else
497 dccp_sk(sk)->dccps_pcslen = cscov;
498 }
499 kfree(list);
500 return rc;
501}
502
Gerrit Renkerb20a9c22008-11-23 16:02:31 -0800503static int dccp_setsockopt_ccid(struct sock *sk, int type,
504 char __user *optval, int optlen)
505{
506 u8 *val;
507 int rc = 0;
508
509 if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS)
510 return -EINVAL;
511
512 val = kmalloc(optlen, GFP_KERNEL);
513 if (val == NULL)
514 return -ENOMEM;
515
516 if (copy_from_user(val, optval, optlen)) {
517 kfree(val);
518 return -EFAULT;
519 }
520
521 lock_sock(sk);
522 if (type == DCCP_SOCKOPT_TX_CCID || type == DCCP_SOCKOPT_CCID)
523 rc = dccp_feat_register_sp(sk, DCCPF_CCID, 1, val, optlen);
524
525 if (!rc && (type == DCCP_SOCKOPT_RX_CCID || type == DCCP_SOCKOPT_CCID))
526 rc = dccp_feat_register_sp(sk, DCCPF_CCID, 0, val, optlen);
527 release_sock(sk);
528
529 kfree(val);
530 return rc;
531}
532
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800533static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
534 char __user *optval, int optlen)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700535{
Gerrit Renker09dbc382006-11-14 12:57:34 -0200536 struct dccp_sock *dp = dccp_sk(sk);
537 int val, err = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700538
Gerrit Renker19102992008-11-16 22:56:55 -0800539 switch (optname) {
540 case DCCP_SOCKOPT_PACKET_SIZE:
541 DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
542 return 0;
543 case DCCP_SOCKOPT_CHANGE_L:
544 case DCCP_SOCKOPT_CHANGE_R:
545 DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n");
546 return 0;
Gerrit Renkerb20a9c22008-11-23 16:02:31 -0800547 case DCCP_SOCKOPT_CCID:
548 case DCCP_SOCKOPT_RX_CCID:
549 case DCCP_SOCKOPT_TX_CCID:
550 return dccp_setsockopt_ccid(sk, optname, optval, optlen);
Gerrit Renker19102992008-11-16 22:56:55 -0800551 }
552
553 if (optlen < (int)sizeof(int))
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300554 return -EINVAL;
555
556 if (get_user(val, (int __user *)optval))
557 return -EFAULT;
558
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700559 if (optname == DCCP_SOCKOPT_SERVICE)
560 return dccp_setsockopt_service(sk, val, optval, optlen);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300561
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700562 lock_sock(sk);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300563 switch (optname) {
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200564 case DCCP_SOCKOPT_SERVER_TIMEWAIT:
565 if (dp->dccps_role != DCCP_ROLE_SERVER)
566 err = -EOPNOTSUPP;
567 else
568 dp->dccps_server_timewait = (val != 0);
569 break;
Gerrit Renker29450552008-11-16 22:53:48 -0800570 case DCCP_SOCKOPT_SEND_CSCOV:
571 err = dccp_setsockopt_cscov(sk, val, false);
Tomasz Grobelnyd6da3512008-09-04 07:30:19 +0200572 break;
Gerrit Renker29450552008-11-16 22:53:48 -0800573 case DCCP_SOCKOPT_RECV_CSCOV:
574 err = dccp_setsockopt_cscov(sk, val, true);
Tomasz Grobelnyd6da3512008-09-04 07:30:19 +0200575 break;
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300576 default:
577 err = -ENOPROTOOPT;
578 break;
579 }
Gerrit Renker410e27a2008-09-09 13:27:22 +0200580 release_sock(sk);
Gerrit Renker19102992008-11-16 22:56:55 -0800581
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300582 return err;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700583}
584
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800585int dccp_setsockopt(struct sock *sk, int level, int optname,
586 char __user *optval, int optlen)
587{
588 if (level != SOL_DCCP)
589 return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
590 optname, optval,
591 optlen);
592 return do_dccp_setsockopt(sk, level, optname, optval, optlen);
593}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800594
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800595EXPORT_SYMBOL_GPL(dccp_setsockopt);
596
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800597#ifdef CONFIG_COMPAT
598int compat_dccp_setsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800599 char __user *optval, int optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800600{
Arnaldo Carvalho de Melodec73ff2006-03-20 22:46:16 -0800601 if (level != SOL_DCCP)
602 return inet_csk_compat_setsockopt(sk, level, optname,
603 optval, optlen);
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800604 return do_dccp_setsockopt(sk, level, optname, optval, optlen);
605}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800606
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800607EXPORT_SYMBOL_GPL(compat_dccp_setsockopt);
608#endif
609
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700610static int dccp_getsockopt_service(struct sock *sk, int len,
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800611 __be32 __user *optval,
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700612 int __user *optlen)
613{
614 const struct dccp_sock *dp = dccp_sk(sk);
615 const struct dccp_service_list *sl;
616 int err = -ENOENT, slen = 0, total_len = sizeof(u32);
617
618 lock_sock(sk);
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700619 if ((sl = dp->dccps_service_list) != NULL) {
620 slen = sl->dccpsl_nr * sizeof(u32);
621 total_len += slen;
622 }
623
624 err = -EINVAL;
625 if (total_len > len)
626 goto out;
627
628 err = 0;
629 if (put_user(total_len, optlen) ||
630 put_user(dp->dccps_service, optval) ||
631 (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen)))
632 err = -EFAULT;
633out:
634 release_sock(sk);
635 return err;
636}
637
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800638static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -0300639 char __user *optval, int __user *optlen)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700640{
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300641 struct dccp_sock *dp;
642 int val, len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700643
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300644 if (get_user(len, optlen))
645 return -EFAULT;
646
Arnaldo Carvalho de Melo39ebc022007-03-28 11:54:32 -0700647 if (len < (int)sizeof(int))
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300648 return -EINVAL;
649
650 dp = dccp_sk(sk);
651
652 switch (optname) {
653 case DCCP_SOCKOPT_PACKET_SIZE:
Gerrit Renker5aed3242006-11-28 19:33:36 -0200654 DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
Arnaldo Carvalho de Melo841bac12006-11-28 19:42:03 -0200655 return 0;
Arnaldo Carvalho de Melo88f964d2005-09-18 00:19:32 -0700656 case DCCP_SOCKOPT_SERVICE:
657 return dccp_getsockopt_service(sk, len,
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800658 (__be32 __user *)optval, optlen);
Gerrit Renker7c559a92007-10-04 14:39:22 -0700659 case DCCP_SOCKOPT_GET_CUR_MPS:
660 val = dp->dccps_mss_cache;
Gerrit Renker7c559a92007-10-04 14:39:22 -0700661 break;
Gerrit Renkerd90ebcb2008-11-12 00:47:26 -0800662 case DCCP_SOCKOPT_AVAILABLE_CCIDS:
663 return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen);
Gerrit Renker71c262a2008-11-23 16:04:59 -0800664 case DCCP_SOCKOPT_TX_CCID:
665 val = ccid_get_current_tx_ccid(dp);
666 if (val < 0)
667 return -ENOPROTOOPT;
668 break;
669 case DCCP_SOCKOPT_RX_CCID:
670 val = ccid_get_current_rx_ccid(dp);
671 if (val < 0)
672 return -ENOPROTOOPT;
673 break;
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200674 case DCCP_SOCKOPT_SERVER_TIMEWAIT:
675 val = dp->dccps_server_timewait;
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200676 break;
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200677 case DCCP_SOCKOPT_SEND_CSCOV:
678 val = dp->dccps_pcslen;
679 break;
680 case DCCP_SOCKOPT_RECV_CSCOV:
681 val = dp->dccps_pcrlen;
682 break;
Arnaldo Carvalho de Melo88f964d2005-09-18 00:19:32 -0700683 case 128 ... 191:
684 return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
685 len, (u32 __user *)optval, optlen);
686 case 192 ... 255:
687 return ccid_hc_tx_getsockopt(dp->dccps_hc_tx_ccid, sk, optname,
688 len, (u32 __user *)optval, optlen);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300689 default:
690 return -ENOPROTOOPT;
691 }
692
Gerrit Renker79133502007-12-13 12:27:14 -0200693 len = sizeof(val);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300694 if (put_user(len, optlen) || copy_to_user(optval, &val, len))
695 return -EFAULT;
696
697 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700698}
699
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800700int dccp_getsockopt(struct sock *sk, int level, int optname,
701 char __user *optval, int __user *optlen)
702{
703 if (level != SOL_DCCP)
704 return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
705 optname, optval,
706 optlen);
707 return do_dccp_getsockopt(sk, level, optname, optval, optlen);
708}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800709
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800710EXPORT_SYMBOL_GPL(dccp_getsockopt);
711
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800712#ifdef CONFIG_COMPAT
713int compat_dccp_getsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800714 char __user *optval, int __user *optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800715{
Arnaldo Carvalho de Melodec73ff2006-03-20 22:46:16 -0800716 if (level != SOL_DCCP)
717 return inet_csk_compat_getsockopt(sk, level, optname,
718 optval, optlen);
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800719 return do_dccp_getsockopt(sk, level, optname, optval, optlen);
720}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800721
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800722EXPORT_SYMBOL_GPL(compat_dccp_getsockopt);
723#endif
724
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700725int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
726 size_t len)
727{
728 const struct dccp_sock *dp = dccp_sk(sk);
729 const int flags = msg->msg_flags;
730 const int noblock = flags & MSG_DONTWAIT;
731 struct sk_buff *skb;
732 int rc, size;
733 long timeo;
734
735 if (len > dp->dccps_mss_cache)
736 return -EMSGSIZE;
737
738 lock_sock(sk);
Ian McDonaldb1308dc2006-11-20 18:30:17 -0200739
Gerrit Renker410e27a2008-09-09 13:27:22 +0200740 if (sysctl_dccp_tx_qlen &&
741 (sk->sk_write_queue.qlen >= sysctl_dccp_tx_qlen)) {
Ian McDonaldb1308dc2006-11-20 18:30:17 -0200742 rc = -EAGAIN;
743 goto out_release;
744 }
745
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700746 timeo = sock_sndtimeo(sk, noblock);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700747
748 /*
749 * We have to use sk_stream_wait_connect here to set sk_write_pending,
750 * so that the trick in dccp_rcv_request_sent_state_process.
751 */
752 /* Wait for a connection to finish. */
Gerrit Renkercecd8d02007-09-26 19:36:08 -0300753 if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN))
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700754 if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0)
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700755 goto out_release;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700756
757 size = sk->sk_prot->max_header + len;
758 release_sock(sk);
759 skb = sock_alloc_send_skb(sk, size, noblock, &rc);
760 lock_sock(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700761 if (skb == NULL)
762 goto out_release;
763
764 skb_reserve(skb, sk->sk_prot->max_header);
765 rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700766 if (rc != 0)
767 goto out_discard;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700768
Gerrit Renker410e27a2008-09-09 13:27:22 +0200769 skb_queue_tail(&sk->sk_write_queue, skb);
770 dccp_write_xmit(sk,0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700771out_release:
772 release_sock(sk);
773 return rc ? : len;
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700774out_discard:
775 kfree_skb(skb);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700776 goto out_release;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700777}
778
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800779EXPORT_SYMBOL_GPL(dccp_sendmsg);
780
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700781int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
782 size_t len, int nonblock, int flags, int *addr_len)
783{
784 const struct dccp_hdr *dh;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700785 long timeo;
786
787 lock_sock(sk);
788
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300789 if (sk->sk_state == DCCP_LISTEN) {
790 len = -ENOTCONN;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700791 goto out;
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300792 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700793
794 timeo = sock_rcvtimeo(sk, nonblock);
795
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700796 do {
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300797 struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700798
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300799 if (skb == NULL)
800 goto verify_sock_status;
801
802 dh = dccp_hdr(skb);
803
Gerrit Renker0c869622007-11-28 11:59:48 -0200804 switch (dh->dccph_type) {
805 case DCCP_PKT_DATA:
806 case DCCP_PKT_DATAACK:
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300807 goto found_ok_skb;
808
Gerrit Renker0c869622007-11-28 11:59:48 -0200809 case DCCP_PKT_CLOSE:
810 case DCCP_PKT_CLOSEREQ:
811 if (!(flags & MSG_PEEK))
812 dccp_finish_passive_close(sk);
813 /* fall through */
814 case DCCP_PKT_RESET:
815 dccp_pr_debug("found fin (%s) ok!\n",
816 dccp_packet_name(dh->dccph_type));
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300817 len = 0;
818 goto found_fin_ok;
Gerrit Renker0c869622007-11-28 11:59:48 -0200819 default:
820 dccp_pr_debug("packet_type=%s\n",
821 dccp_packet_name(dh->dccph_type));
822 sk_eat_skb(sk, skb, 0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700823 }
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300824verify_sock_status:
825 if (sock_flag(sk, SOCK_DONE)) {
826 len = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700827 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700828 }
829
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300830 if (sk->sk_err) {
831 len = sock_error(sk);
832 break;
833 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700834
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300835 if (sk->sk_shutdown & RCV_SHUTDOWN) {
836 len = 0;
837 break;
838 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700839
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300840 if (sk->sk_state == DCCP_CLOSED) {
841 if (!sock_flag(sk, SOCK_DONE)) {
842 /* This occurs when user tries to read
843 * from never connected socket.
844 */
845 len = -ENOTCONN;
846 break;
847 }
848 len = 0;
849 break;
850 }
851
852 if (!timeo) {
853 len = -EAGAIN;
854 break;
855 }
856
857 if (signal_pending(current)) {
858 len = sock_intr_errno(timeo);
859 break;
860 }
861
862 sk_wait_data(sk, &timeo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700863 continue;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700864 found_ok_skb:
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300865 if (len > skb->len)
866 len = skb->len;
867 else if (len < skb->len)
868 msg->msg_flags |= MSG_TRUNC;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700869
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300870 if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len)) {
871 /* Exception. Bailout! */
872 len = -EFAULT;
873 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700874 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700875 found_fin_ok:
876 if (!(flags & MSG_PEEK))
Chris Leech624d1162006-05-23 18:01:28 -0700877 sk_eat_skb(sk, skb, 0);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700878 break;
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300879 } while (1);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700880out:
881 release_sock(sk);
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300882 return len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700883}
884
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800885EXPORT_SYMBOL_GPL(dccp_recvmsg);
886
887int inet_dccp_listen(struct socket *sock, int backlog)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700888{
889 struct sock *sk = sock->sk;
890 unsigned char old_state;
891 int err;
892
893 lock_sock(sk);
894
895 err = -EINVAL;
896 if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP)
897 goto out;
898
899 old_state = sk->sk_state;
900 if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN)))
901 goto out;
902
903 /* Really, if the socket is already in listen state
904 * we can only allow the backlog to be adjusted.
905 */
906 if (old_state != DCCP_LISTEN) {
907 /*
908 * FIXME: here it probably should be sk->sk_prot->listen_start
909 * see tcp_listen_start
910 */
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800911 err = dccp_listen_start(sk, backlog);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700912 if (err)
913 goto out;
914 }
915 sk->sk_max_ack_backlog = backlog;
916 err = 0;
917
918out:
919 release_sock(sk);
920 return err;
921}
922
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800923EXPORT_SYMBOL_GPL(inet_dccp_listen);
924
Gerrit Renker0c869622007-11-28 11:59:48 -0200925static void dccp_terminate_connection(struct sock *sk)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700926{
Gerrit Renker0c869622007-11-28 11:59:48 -0200927 u8 next_state = DCCP_CLOSED;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700928
Gerrit Renker0c869622007-11-28 11:59:48 -0200929 switch (sk->sk_state) {
930 case DCCP_PASSIVE_CLOSE:
931 case DCCP_PASSIVE_CLOSEREQ:
932 dccp_finish_passive_close(sk);
933 break;
934 case DCCP_PARTOPEN:
935 dccp_pr_debug("Stop PARTOPEN timer (%p)\n", sk);
936 inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);
937 /* fall through */
938 case DCCP_OPEN:
939 dccp_send_close(sk, 1);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700940
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200941 if (dccp_sk(sk)->dccps_role == DCCP_ROLE_SERVER &&
942 !dccp_sk(sk)->dccps_server_timewait)
Gerrit Renker0c869622007-11-28 11:59:48 -0200943 next_state = DCCP_ACTIVE_CLOSEREQ;
944 else
945 next_state = DCCP_CLOSING;
946 /* fall through */
947 default:
948 dccp_set_state(sk, next_state);
949 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700950}
951
952void dccp_close(struct sock *sk, long timeout)
953{
Ian McDonald97e58482006-08-26 19:16:45 -0700954 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700955 struct sk_buff *skb;
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800956 u32 data_was_unread = 0;
Herbert Xu134af342006-05-05 17:09:13 -0700957 int state;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700958
959 lock_sock(sk);
960
961 sk->sk_shutdown = SHUTDOWN_MASK;
962
963 if (sk->sk_state == DCCP_LISTEN) {
964 dccp_set_state(sk, DCCP_CLOSED);
965
966 /* Special case. */
967 inet_csk_listen_stop(sk);
968
969 goto adjudge_to_death;
970 }
971
Ian McDonald97e58482006-08-26 19:16:45 -0700972 sk_stop_timer(sk, &dp->dccps_xmit_timer);
973
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700974 /*
975 * We need to flush the recv. buffs. We do this only on the
976 * descriptor close, not protocol-sourced closes, because the
977 *reader process may not have drained the data yet!
978 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700979 while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800980 data_was_unread += skb->len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700981 __kfree_skb(skb);
982 }
983
Gerrit Renkerd83bd952007-12-16 16:06:03 -0800984 if (data_was_unread) {
985 /* Unread data was tossed, send an appropriate Reset Code */
986 DCCP_WARN("DCCP: ABORT -- %u bytes unread\n", data_was_unread);
987 dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
988 dccp_set_state(sk, DCCP_CLOSED);
989 } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700990 /* Check zero linger _after_ checking for unread data. */
991 sk->sk_prot->disconnect(sk, 0);
Gerrit Renker0c869622007-11-28 11:59:48 -0200992 } else if (sk->sk_state != DCCP_CLOSED) {
993 dccp_terminate_connection(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700994 }
995
996 sk_stream_wait_close(sk, timeout);
997
998adjudge_to_death:
Herbert Xu134af342006-05-05 17:09:13 -0700999 state = sk->sk_state;
1000 sock_hold(sk);
1001 sock_orphan(sk);
Eric Dumazetdd24c002008-11-25 21:17:14 -08001002 percpu_counter_inc(sk->sk_prot->orphan_count);
Herbert Xu134af342006-05-05 17:09:13 -07001003
Arnaldo Carvalho de Melo7ad07e72005-08-23 21:50:06 -07001004 /*
1005 * It is the last release_sock in its life. It will remove backlog.
1006 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001007 release_sock(sk);
1008 /*
1009 * Now socket is owned by kernel and we acquire BH lock
1010 * to finish close. No need to check for user refs.
1011 */
1012 local_bh_disable();
1013 bh_lock_sock(sk);
Ilpo Järvinen547b7922008-07-25 21:43:18 -07001014 WARN_ON(sock_owned_by_user(sk));
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001015
Herbert Xu134af342006-05-05 17:09:13 -07001016 /* Have we already been destroyed by a softirq or backlog? */
1017 if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
1018 goto out;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001019
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001020 if (sk->sk_state == DCCP_CLOSED)
1021 inet_csk_destroy_sock(sk);
1022
1023 /* Otherwise, socket is reprieved until protocol close. */
1024
Herbert Xu134af342006-05-05 17:09:13 -07001025out:
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001026 bh_unlock_sock(sk);
1027 local_bh_enable();
1028 sock_put(sk);
1029}
1030
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001031EXPORT_SYMBOL_GPL(dccp_close);
1032
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001033void dccp_shutdown(struct sock *sk, int how)
1034{
Gerrit Renker8e8c71f2007-11-21 09:56:48 -02001035 dccp_pr_debug("called shutdown(%x)\n", how);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001036}
1037
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001038EXPORT_SYMBOL_GPL(dccp_shutdown);
1039
YOSHIFUJI Hideaki24e8b7e2008-04-10 03:48:43 -07001040static inline int dccp_mib_init(void)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001041{
YOSHIFUJI Hideaki24e8b7e2008-04-10 03:48:43 -07001042 return snmp_mib_init((void**)dccp_statistics, sizeof(struct dccp_mib));
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001043}
1044
YOSHIFUJI Hideaki24e8b7e2008-04-10 03:48:43 -07001045static inline void dccp_mib_exit(void)
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001046{
YOSHIFUJI Hideaki24e8b7e2008-04-10 03:48:43 -07001047 snmp_mib_free((void**)dccp_statistics);
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001048}
1049
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001050static int thash_entries;
1051module_param(thash_entries, int, 0444);
1052MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
1053
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -03001054#ifdef CONFIG_IP_DCCP_DEBUG
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001055int dccp_debug;
Gerrit Renker43264992008-08-23 13:28:27 +02001056module_param(dccp_debug, bool, 0644);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001057MODULE_PARM_DESC(dccp_debug, "Enable debug messages");
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001058
1059EXPORT_SYMBOL_GPL(dccp_debug);
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -03001060#endif
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001061
1062static int __init dccp_init(void)
1063{
1064 unsigned long goal;
1065 int ehash_order, bhash_order, i;
Eric Dumazetdd24c002008-11-25 21:17:14 -08001066 int rc;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001067
Patrick McHardy028b0272008-04-12 18:35:41 -07001068 BUILD_BUG_ON(sizeof(struct dccp_skb_cb) >
1069 FIELD_SIZEOF(struct sk_buff, cb));
Eric Dumazetdd24c002008-11-25 21:17:14 -08001070 rc = percpu_counter_init(&dccp_orphan_count, 0);
1071 if (rc)
1072 goto out;
1073 rc = -ENOBUFS;
Eric Dumazet5caea4e2008-11-20 00:40:07 -08001074 inet_hashinfo_init(&dccp_hashinfo);
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001075 dccp_hashinfo.bind_bucket_cachep =
1076 kmem_cache_create("dccp_bind_bucket",
1077 sizeof(struct inet_bind_bucket), 0,
Paul Mundt20c2df82007-07-20 10:11:58 +09001078 SLAB_HWCACHE_ALIGN, NULL);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001079 if (!dccp_hashinfo.bind_bucket_cachep)
Eric Dumazetdd24c002008-11-25 21:17:14 -08001080 goto out_free_percpu;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001081
1082 /*
1083 * Size and allocate the main established and bind bucket
1084 * hash tables.
1085 *
1086 * The methodology is similar to that of the buffer cache.
1087 */
1088 if (num_physpages >= (128 * 1024))
1089 goal = num_physpages >> (21 - PAGE_SHIFT);
1090 else
1091 goal = num_physpages >> (23 - PAGE_SHIFT);
1092
1093 if (thash_entries)
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001094 goal = (thash_entries *
1095 sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001096 for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++)
1097 ;
1098 do {
1099 dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE /
1100 sizeof(struct inet_ehash_bucket);
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001101 while (dccp_hashinfo.ehash_size &
1102 (dccp_hashinfo.ehash_size - 1))
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001103 dccp_hashinfo.ehash_size--;
1104 dccp_hashinfo.ehash = (struct inet_ehash_bucket *)
1105 __get_free_pages(GFP_ATOMIC, ehash_order);
1106 } while (!dccp_hashinfo.ehash && --ehash_order > 0);
1107
1108 if (!dccp_hashinfo.ehash) {
Gerrit Renker59348b12006-11-20 18:39:23 -02001109 DCCP_CRIT("Failed to allocate DCCP established hash table");
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001110 goto out_free_bind_bucket_cachep;
1111 }
1112
Eric Dumazetdbca9b2752007-02-08 14:16:46 -08001113 for (i = 0; i < dccp_hashinfo.ehash_size; i++) {
Eric Dumazet3ab5aee2008-11-16 19:40:17 -08001114 INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].chain, i);
1115 INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].twchain, i);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001116 }
1117
Eric Dumazet230140c2007-11-07 02:40:20 -08001118 if (inet_ehash_locks_alloc(&dccp_hashinfo))
1119 goto out_free_dccp_ehash;
1120
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001121 bhash_order = ehash_order;
1122
1123 do {
1124 dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE /
1125 sizeof(struct inet_bind_hashbucket);
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001126 if ((dccp_hashinfo.bhash_size > (64 * 1024)) &&
1127 bhash_order > 0)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001128 continue;
1129 dccp_hashinfo.bhash = (struct inet_bind_hashbucket *)
1130 __get_free_pages(GFP_ATOMIC, bhash_order);
1131 } while (!dccp_hashinfo.bhash && --bhash_order >= 0);
1132
1133 if (!dccp_hashinfo.bhash) {
Gerrit Renker59348b12006-11-20 18:39:23 -02001134 DCCP_CRIT("Failed to allocate DCCP bind hash table");
Eric Dumazet230140c2007-11-07 02:40:20 -08001135 goto out_free_dccp_locks;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001136 }
1137
1138 for (i = 0; i < dccp_hashinfo.bhash_size; i++) {
1139 spin_lock_init(&dccp_hashinfo.bhash[i].lock);
1140 INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
1141 }
1142
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001143 rc = dccp_mib_init();
Arnaldo Carvalho de Melofa23e2e2006-03-20 17:16:01 -08001144 if (rc)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001145 goto out_free_dccp_bhash;
1146
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001147 rc = dccp_ackvec_init();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001148 if (rc)
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001149 goto out_free_dccp_mib;
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001150
Arnaldo Carvalho de Meloe55d9122006-03-20 19:25:02 -08001151 rc = dccp_sysctl_init();
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001152 if (rc)
1153 goto out_ackvec_exit;
Gerrit Renker4c70f382007-09-25 22:40:13 -07001154
1155 dccp_timestamping_init();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001156out:
1157 return rc;
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001158out_ackvec_exit:
1159 dccp_ackvec_exit();
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -08001160out_free_dccp_mib:
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001161 dccp_mib_exit();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001162out_free_dccp_bhash:
1163 free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
1164 dccp_hashinfo.bhash = NULL;
Eric Dumazet230140c2007-11-07 02:40:20 -08001165out_free_dccp_locks:
1166 inet_ehash_locks_free(&dccp_hashinfo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001167out_free_dccp_ehash:
1168 free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order);
1169 dccp_hashinfo.ehash = NULL;
1170out_free_bind_bucket_cachep:
1171 kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
1172 dccp_hashinfo.bind_bucket_cachep = NULL;
Eric Dumazetdd24c002008-11-25 21:17:14 -08001173out_free_percpu:
1174 percpu_counter_destroy(&dccp_orphan_count);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001175 goto out;
1176}
1177
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001178static void __exit dccp_fini(void)
1179{
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001180 dccp_mib_exit();
Arnaldo Carvalho de Melo725ba8e2005-08-13 20:35:39 -03001181 free_pages((unsigned long)dccp_hashinfo.bhash,
1182 get_order(dccp_hashinfo.bhash_size *
1183 sizeof(struct inet_bind_hashbucket)));
1184 free_pages((unsigned long)dccp_hashinfo.ehash,
1185 get_order(dccp_hashinfo.ehash_size *
1186 sizeof(struct inet_ehash_bucket)));
Eric Dumazet230140c2007-11-07 02:40:20 -08001187 inet_ehash_locks_free(&dccp_hashinfo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001188 kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001189 dccp_ackvec_exit();
Arnaldo Carvalho de Meloe55d9122006-03-20 19:25:02 -08001190 dccp_sysctl_exit();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001191}
1192
1193module_init(dccp_init);
1194module_exit(dccp_fini);
1195
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001196MODULE_LICENSE("GPL");
1197MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>");
1198MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");