blob: 5dd85ec51bfef3552dc6bfd3e7697bd42ef99bd6 [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090023#include <linux/slab.h>
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070024#include <net/checksum.h>
25
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020026#include <net/inet_sock.h>
Eric Dumazet120e9da2017-08-16 07:03:15 -070027#include <net/inet_common.h>
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070028#include <net/sock.h>
29#include <net/xfrm.h>
30
Arnaldo Carvalho de Melo62731722007-10-23 20:23:30 -070031#include <asm/ioctls.h>
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070032#include <linux/spinlock.h>
33#include <linux/timer.h>
34#include <linux/delay.h>
35#include <linux/poll.h>
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070036
37#include "ccid.h"
38#include "dccp.h"
Andrea Bittauafe00252006-03-20 17:43:56 -080039#include "feat.h"
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070040
Masami Hiramatsuee549be2017-12-29 11:47:55 +090041#define CREATE_TRACE_POINTS
42#include "trace.h"
43
Eric Dumazetba899662005-08-26 12:05:31 -070044DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070045
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -080046EXPORT_SYMBOL_GPL(dccp_statistics);
47
Eric Dumazetdd24c002008-11-25 21:17:14 -080048struct percpu_counter dccp_orphan_count;
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -080049EXPORT_SYMBOL_GPL(dccp_orphan_count);
50
Eric Dumazet5caea4e2008-11-20 00:40:07 -080051struct inet_hashinfo dccp_hashinfo;
Arnaldo Carvalho de Melo075ae862006-03-20 21:24:19 -080052EXPORT_SYMBOL_GPL(dccp_hashinfo);
53
Ian McDonaldb1308dc2006-11-20 18:30:17 -020054/* the maximum queue length for tx in packets. 0 is no limit */
55int sysctl_dccp_tx_qlen __read_mostly = 5;
56
stephen hemminger1f4f0f62010-10-05 04:24:09 +000057#ifdef CONFIG_IP_DCCP_DEBUG
58static const char *dccp_state_name(const int state)
59{
60 static const char *const dccp_state_names[] = {
61 [DCCP_OPEN] = "OPEN",
62 [DCCP_REQUESTING] = "REQUESTING",
63 [DCCP_PARTOPEN] = "PARTOPEN",
64 [DCCP_LISTEN] = "LISTEN",
65 [DCCP_RESPOND] = "RESPOND",
66 [DCCP_CLOSING] = "CLOSING",
67 [DCCP_ACTIVE_CLOSEREQ] = "CLOSEREQ",
68 [DCCP_PASSIVE_CLOSE] = "PASSIVE_CLOSE",
69 [DCCP_PASSIVE_CLOSEREQ] = "PASSIVE_CLOSEREQ",
70 [DCCP_TIME_WAIT] = "TIME_WAIT",
71 [DCCP_CLOSED] = "CLOSED",
72 };
73
74 if (state >= DCCP_MAX_STATES)
75 return "INVALID STATE!";
76 else
77 return dccp_state_names[state];
78}
79#endif
80
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080081void dccp_set_state(struct sock *sk, const int state)
82{
83 const int oldstate = sk->sk_state;
84
Gerrit Renkerf11135a2007-11-28 11:34:53 -020085 dccp_pr_debug("%s(%p) %s --> %s\n", dccp_role(sk), sk,
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080086 dccp_state_name(oldstate), dccp_state_name(state));
87 WARN_ON(state == oldstate);
88
89 switch (state) {
90 case DCCP_OPEN:
91 if (oldstate != DCCP_OPEN)
92 DCCP_INC_STATS(DCCP_MIB_CURRESTAB);
Gerrit Renker6eb55d12008-12-08 01:15:26 -080093 /* Client retransmits all Confirm options until entering OPEN */
94 if (oldstate == DCCP_PARTOPEN)
95 dccp_feat_list_purge(&dccp_sk(sk)->dccps_featneg);
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -080096 break;
97
98 case DCCP_CLOSED:
Gerrit Renker0c869622007-11-28 11:59:48 -020099 if (oldstate == DCCP_OPEN || oldstate == DCCP_ACTIVE_CLOSEREQ ||
100 oldstate == DCCP_CLOSING)
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -0800101 DCCP_INC_STATS(DCCP_MIB_ESTABRESETS);
102
103 sk->sk_prot->unhash(sk);
104 if (inet_csk(sk)->icsk_bind_hash != NULL &&
105 !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))
Arnaldo Carvalho de Meloab1e0a12008-02-03 04:06:04 -0800106 inet_put_port(sk);
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -0800107 /* fall through */
108 default:
109 if (oldstate == DCCP_OPEN)
110 DCCP_DEC_STATS(DCCP_MIB_CURRESTAB);
111 }
112
113 /* Change state AFTER socket is unhashed to avoid closed
114 * socket sitting in hash tables.
115 */
Yafang Shaob0832e32017-12-20 11:12:53 +0800116 inet_sk_set_state(sk, state);
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -0800117}
118
119EXPORT_SYMBOL_GPL(dccp_set_state);
120
Gerrit Renker0c869622007-11-28 11:59:48 -0200121static void dccp_finish_passive_close(struct sock *sk)
122{
123 switch (sk->sk_state) {
124 case DCCP_PASSIVE_CLOSE:
125 /* Node (client or server) has received Close packet. */
126 dccp_send_reset(sk, DCCP_RESET_CODE_CLOSED);
127 dccp_set_state(sk, DCCP_CLOSED);
128 break;
129 case DCCP_PASSIVE_CLOSEREQ:
130 /*
131 * Client received CloseReq. We set the `active' flag so that
132 * dccp_send_close() retransmits the Close as per RFC 4340, 8.3.
133 */
134 dccp_send_close(sk, 1);
135 dccp_set_state(sk, DCCP_CLOSING);
136 }
137}
138
Arnaldo Carvalho de Meloc25a18b2006-03-20 21:58:56 -0800139void dccp_done(struct sock *sk)
140{
141 dccp_set_state(sk, DCCP_CLOSED);
142 dccp_clear_xmit_timers(sk);
143
144 sk->sk_shutdown = SHUTDOWN_MASK;
145
146 if (!sock_flag(sk, SOCK_DEAD))
147 sk->sk_state_change(sk);
148 else
149 inet_csk_destroy_sock(sk);
150}
151
152EXPORT_SYMBOL_GPL(dccp_done);
153
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700154const char *dccp_packet_name(const int type)
155{
Jan Engelhardt36cbd3d2009-08-05 10:42:58 -0700156 static const char *const dccp_packet_names[] = {
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700157 [DCCP_PKT_REQUEST] = "REQUEST",
158 [DCCP_PKT_RESPONSE] = "RESPONSE",
159 [DCCP_PKT_DATA] = "DATA",
160 [DCCP_PKT_ACK] = "ACK",
161 [DCCP_PKT_DATAACK] = "DATAACK",
162 [DCCP_PKT_CLOSEREQ] = "CLOSEREQ",
163 [DCCP_PKT_CLOSE] = "CLOSE",
164 [DCCP_PKT_RESET] = "RESET",
165 [DCCP_PKT_SYNC] = "SYNC",
166 [DCCP_PKT_SYNCACK] = "SYNCACK",
167 };
168
169 if (type >= DCCP_NR_PKT_TYPES)
170 return "INVALID";
171 else
172 return dccp_packet_names[type];
173}
174
175EXPORT_SYMBOL_GPL(dccp_packet_name);
176
Eric Dumazet120e9da2017-08-16 07:03:15 -0700177static void dccp_sk_destruct(struct sock *sk)
178{
179 struct dccp_sock *dp = dccp_sk(sk);
180
181 ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
182 dp->dccps_hc_tx_ccid = NULL;
183 inet_sock_destruct(sk);
184}
185
Arnaldo Carvalho de Melo72478872006-03-20 22:00:37 -0800186int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800187{
188 struct dccp_sock *dp = dccp_sk(sk);
189 struct inet_connection_sock *icsk = inet_csk(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800190
Arnaldo Carvalho de Meloe18d7a92007-11-24 21:42:53 -0200191 icsk->icsk_rto = DCCP_TIMEOUT_INIT;
192 icsk->icsk_syn_retries = sysctl_dccp_request_retries;
193 sk->sk_state = DCCP_CLOSED;
194 sk->sk_write_space = dccp_write_space;
Eric Dumazet120e9da2017-08-16 07:03:15 -0700195 sk->sk_destruct = dccp_sk_destruct;
Arnaldo Carvalho de Meloe18d7a92007-11-24 21:42:53 -0200196 icsk->icsk_sync_mss = dccp_sync_mss;
Gerrit Renker410e27a2008-09-09 13:27:22 +0200197 dp->dccps_mss_cache = 536;
Arnaldo Carvalho de Meloe18d7a92007-11-24 21:42:53 -0200198 dp->dccps_rate_last = jiffies;
199 dp->dccps_role = DCCP_ROLE_UNDEFINED;
200 dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT;
Tomasz Grobelny871a2c12010-12-04 13:38:01 +0100201 dp->dccps_tx_qlen = sysctl_dccp_tx_qlen;
Arnaldo Carvalho de Meloe18d7a92007-11-24 21:42:53 -0200202
203 dccp_init_xmit_timers(sk);
204
Gerrit Renkerac757732008-11-04 23:55:49 -0800205 INIT_LIST_HEAD(&dp->dccps_featneg);
Gerrit Renker6eb55d12008-12-08 01:15:26 -0800206 /* control socket doesn't need feat nego */
207 if (likely(ctl_sock_initialized))
208 return dccp_feat_init(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800209 return 0;
210}
211
212EXPORT_SYMBOL_GPL(dccp_init_sock);
213
Brian Haley7d06b2e2008-06-14 17:04:49 -0700214void dccp_destroy_sock(struct sock *sk)
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800215{
216 struct dccp_sock *dp = dccp_sk(sk);
217
Eric Dumazet7749d4f2017-08-14 14:10:25 -0700218 __skb_queue_purge(&sk->sk_write_queue);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800219 if (sk->sk_send_head != NULL) {
220 kfree_skb(sk->sk_send_head);
221 sk->sk_send_head = NULL;
222 }
223
224 /* Clean up a referenced DCCP bind bucket. */
225 if (inet_csk(sk)->icsk_bind_hash != NULL)
Arnaldo Carvalho de Meloab1e0a12008-02-03 04:06:04 -0800226 inet_put_port(sk);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800227
228 kfree(dp->dccps_service_list);
229 dp->dccps_service_list = NULL;
230
Gerrit Renker6fdd34d2008-12-08 01:19:06 -0800231 if (dp->dccps_hc_rx_ackvec != NULL) {
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800232 dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
233 dp->dccps_hc_rx_ackvec = NULL;
234 }
235 ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
Eric Dumazet120e9da2017-08-16 07:03:15 -0700236 dp->dccps_hc_rx_ccid = NULL;
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800237
238 /* clean up feature negotiation state */
Gerrit Renkerd99a7bd2008-11-04 23:56:30 -0800239 dccp_feat_list_purge(&dp->dccps_featneg);
Arnaldo Carvalho de Melo3e0fadc2006-03-20 21:23:15 -0800240}
241
242EXPORT_SYMBOL_GPL(dccp_destroy_sock);
243
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800244static inline int dccp_listen_start(struct sock *sk, int backlog)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700245{
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700246 struct dccp_sock *dp = dccp_sk(sk);
247
248 dp->dccps_role = DCCP_ROLE_LISTEN;
Gerrit Renker9eca0a42008-11-12 00:48:44 -0800249 /* do not start to listen if feature negotiation setup fails */
250 if (dccp_feat_finalise_settings(dp))
251 return -EPROTO;
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800252 return inet_csk_listen_start(sk, backlog);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700253}
254
Gerrit Renkerce865a62007-11-24 22:14:15 -0200255static inline int dccp_need_reset(int state)
256{
257 return state != DCCP_CLOSED && state != DCCP_LISTEN &&
258 state != DCCP_REQUESTING;
259}
260
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700261int dccp_disconnect(struct sock *sk, int flags)
262{
263 struct inet_connection_sock *icsk = inet_csk(sk);
264 struct inet_sock *inet = inet_sk(sk);
Mohamed Ghannam69c64862017-12-05 20:58:35 +0000265 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700266 const int old_state = sk->sk_state;
267
268 if (old_state != DCCP_CLOSED)
269 dccp_set_state(sk, DCCP_CLOSED);
270
Gerrit Renkerce865a62007-11-24 22:14:15 -0200271 /*
272 * This corresponds to the ABORT function of RFC793, sec. 3.8
273 * TCP uses a RST segment, DCCP a Reset packet with Code 2, "Aborted".
274 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700275 if (old_state == DCCP_LISTEN) {
276 inet_csk_listen_stop(sk);
Gerrit Renkerce865a62007-11-24 22:14:15 -0200277 } else if (dccp_need_reset(old_state)) {
278 dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
279 sk->sk_err = ECONNRESET;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700280 } else if (old_state == DCCP_REQUESTING)
281 sk->sk_err = ECONNRESET;
282
283 dccp_clear_xmit_timers(sk);
Mohamed Ghannam69c64862017-12-05 20:58:35 +0000284 ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
Mohamed Ghannam69c64862017-12-05 20:58:35 +0000285 dp->dccps_hc_rx_ccid = NULL;
Gerrit Renker48816322008-08-23 13:28:27 +0200286
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700287 __skb_queue_purge(&sk->sk_receive_queue);
Gerrit Renker48816322008-08-23 13:28:27 +0200288 __skb_queue_purge(&sk->sk_write_queue);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700289 if (sk->sk_send_head != NULL) {
290 __kfree_skb(sk->sk_send_head);
291 sk->sk_send_head = NULL;
292 }
293
Eric Dumazetc720c7e82009-10-15 06:30:45 +0000294 inet->inet_dport = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700295
296 if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
297 inet_reset_saddr(sk);
298
299 sk->sk_shutdown = 0;
300 sock_reset_flag(sk, SOCK_DONE);
301
302 icsk->icsk_backoff = 0;
303 inet_csk_delack_init(sk);
304 __sk_dst_reset(sk);
305
Eric Dumazetc720c7e82009-10-15 06:30:45 +0000306 WARN_ON(inet->inet_num && !icsk->icsk_bind_hash);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700307
308 sk->sk_error_report(sk);
Hariprasad Kelam3285a9aa2019-05-12 16:09:49 +0530309 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700310}
311
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800312EXPORT_SYMBOL_GPL(dccp_disconnect);
313
Linus Torvaldsa11e1d42018-06-28 09:43:44 -0700314/*
315 * Wait for a DCCP event.
316 *
317 * Note that we don't need to lock the socket, as the upper poll layers
318 * take care of normal races (between the test and the event) and we don't
319 * go look at any of the socket buffers directly.
320 */
321__poll_t dccp_poll(struct file *file, struct socket *sock,
322 poll_table *wait)
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700323{
Al Viroade994f2017-07-03 00:01:49 -0400324 __poll_t mask;
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700325 struct sock *sk = sock->sk;
326
Karsten Graul89ab0662018-10-23 13:40:39 +0200327 sock_poll_wait(file, sock, wait);
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700328 if (sk->sk_state == DCCP_LISTEN)
329 return inet_csk_listen_poll(sk);
330
331 /* Socket is not locked. We are protected from async events
332 by poll logic and correct handling of state changes
333 made by another threads is impossible in any case.
334 */
335
336 mask = 0;
337 if (sk->sk_err)
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800338 mask = EPOLLERR;
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700339
340 if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED)
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800341 mask |= EPOLLHUP;
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700342 if (sk->sk_shutdown & RCV_SHUTDOWN)
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800343 mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP;
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700344
345 /* Connected? */
346 if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) {
347 if (atomic_read(&sk->sk_rmem_alloc) > 0)
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800348 mask |= EPOLLIN | EPOLLRDNORM;
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700349
350 if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {
Eric Dumazet64dc6132013-07-22 20:26:31 -0700351 if (sk_stream_is_writeable(sk)) {
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800352 mask |= EPOLLOUT | EPOLLWRNORM;
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700353 } else { /* send SIGIO later */
Eric Dumazet9cd3e072015-11-29 20:03:10 -0800354 sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700355 set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
356
357 /* Race breaker. If space is freed after
358 * wspace test but before the flags are set,
359 * IO signal will be lost.
360 */
Eric Dumazet64dc6132013-07-22 20:26:31 -0700361 if (sk_stream_is_writeable(sk))
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800362 mask |= EPOLLOUT | EPOLLWRNORM;
Arnaldo Carvalho de Melo331968b2005-08-23 21:54:23 -0700363 }
364 }
365 }
366 return mask;
367}
368
Linus Torvaldsa11e1d42018-06-28 09:43:44 -0700369EXPORT_SYMBOL_GPL(dccp_poll);
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800370
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700371int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
372{
Arnaldo Carvalho de Melo62731722007-10-23 20:23:30 -0700373 int rc = -ENOTCONN;
374
375 lock_sock(sk);
376
377 if (sk->sk_state == DCCP_LISTEN)
378 goto out;
379
380 switch (cmd) {
381 case SIOCINQ: {
382 struct sk_buff *skb;
383 unsigned long amount = 0;
384
385 skb = skb_peek(&sk->sk_receive_queue);
386 if (skb != NULL) {
387 /*
388 * We will only return the amount of this packet since
389 * that is all that will be read.
390 */
391 amount = skb->len;
392 }
393 rc = put_user(amount, (int __user *)arg);
394 }
395 break;
396 default:
397 rc = -ENOIOCTLCMD;
398 break;
399 }
400out:
401 release_sock(sk);
402 return rc;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700403}
404
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800405EXPORT_SYMBOL_GPL(dccp_ioctl);
406
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800407static int dccp_setsockopt_service(struct sock *sk, const __be32 service,
David S. Millerb7058842009-09-30 16:12:20 -0700408 char __user *optval, unsigned int optlen)
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700409{
410 struct dccp_sock *dp = dccp_sk(sk);
411 struct dccp_service_list *sl = NULL;
412
Arnaldo Carvalho de Melo8109b022006-12-10 16:01:18 -0200413 if (service == DCCP_SERVICE_INVALID_VALUE ||
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700414 optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32))
415 return -EINVAL;
416
417 if (optlen > sizeof(service)) {
418 sl = kmalloc(optlen, GFP_KERNEL);
419 if (sl == NULL)
420 return -ENOMEM;
421
422 sl->dccpsl_nr = optlen / sizeof(u32) - 1;
423 if (copy_from_user(sl->dccpsl_list,
424 optval + sizeof(service),
425 optlen - sizeof(service)) ||
426 dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) {
427 kfree(sl);
428 return -EFAULT;
429 }
430 }
431
432 lock_sock(sk);
433 dp->dccps_service = service;
434
Jesper Juhla51482b2005-11-08 09:41:34 -0800435 kfree(dp->dccps_service_list);
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700436
437 dp->dccps_service_list = sl;
438 release_sock(sk);
439 return 0;
440}
441
Gerrit Renker29450552008-11-16 22:53:48 -0800442static int dccp_setsockopt_cscov(struct sock *sk, int cscov, bool rx)
443{
444 u8 *list, len;
445 int i, rc;
446
447 if (cscov < 0 || cscov > 15)
448 return -EINVAL;
449 /*
450 * Populate a list of permissible values, in the range cscov...15. This
451 * is necessary since feature negotiation of single values only works if
452 * both sides incidentally choose the same value. Since the list starts
453 * lowest-value first, negotiation will pick the smallest shared value.
454 */
455 if (cscov == 0)
456 return 0;
457 len = 16 - cscov;
458
459 list = kmalloc(len, GFP_KERNEL);
460 if (list == NULL)
461 return -ENOBUFS;
462
463 for (i = 0; i < len; i++)
464 list[i] = cscov++;
465
466 rc = dccp_feat_register_sp(sk, DCCPF_MIN_CSUM_COVER, rx, list, len);
467
468 if (rc == 0) {
469 if (rx)
470 dccp_sk(sk)->dccps_pcrlen = cscov;
471 else
472 dccp_sk(sk)->dccps_pcslen = cscov;
473 }
474 kfree(list);
475 return rc;
476}
477
Gerrit Renkerb20a9c22008-11-23 16:02:31 -0800478static int dccp_setsockopt_ccid(struct sock *sk, int type,
David S. Millerb7058842009-09-30 16:12:20 -0700479 char __user *optval, unsigned int optlen)
Gerrit Renkerb20a9c22008-11-23 16:02:31 -0800480{
481 u8 *val;
482 int rc = 0;
483
484 if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS)
485 return -EINVAL;
486
Julia Lawall042604d2010-05-21 22:25:19 +0000487 val = memdup_user(optval, optlen);
488 if (IS_ERR(val))
489 return PTR_ERR(val);
Gerrit Renkerb20a9c22008-11-23 16:02:31 -0800490
491 lock_sock(sk);
492 if (type == DCCP_SOCKOPT_TX_CCID || type == DCCP_SOCKOPT_CCID)
493 rc = dccp_feat_register_sp(sk, DCCPF_CCID, 1, val, optlen);
494
495 if (!rc && (type == DCCP_SOCKOPT_RX_CCID || type == DCCP_SOCKOPT_CCID))
496 rc = dccp_feat_register_sp(sk, DCCPF_CCID, 0, val, optlen);
497 release_sock(sk);
498
499 kfree(val);
500 return rc;
501}
502
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800503static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
David S. Millerb7058842009-09-30 16:12:20 -0700504 char __user *optval, unsigned int optlen)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700505{
Gerrit Renker09dbc382006-11-14 12:57:34 -0200506 struct dccp_sock *dp = dccp_sk(sk);
507 int val, err = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700508
Gerrit Renker19102992008-11-16 22:56:55 -0800509 switch (optname) {
510 case DCCP_SOCKOPT_PACKET_SIZE:
511 DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
512 return 0;
513 case DCCP_SOCKOPT_CHANGE_L:
514 case DCCP_SOCKOPT_CHANGE_R:
515 DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n");
516 return 0;
Gerrit Renkerb20a9c22008-11-23 16:02:31 -0800517 case DCCP_SOCKOPT_CCID:
518 case DCCP_SOCKOPT_RX_CCID:
519 case DCCP_SOCKOPT_TX_CCID:
520 return dccp_setsockopt_ccid(sk, optname, optval, optlen);
Gerrit Renker19102992008-11-16 22:56:55 -0800521 }
522
523 if (optlen < (int)sizeof(int))
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300524 return -EINVAL;
525
526 if (get_user(val, (int __user *)optval))
527 return -EFAULT;
528
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700529 if (optname == DCCP_SOCKOPT_SERVICE)
530 return dccp_setsockopt_service(sk, val, optval, optlen);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300531
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700532 lock_sock(sk);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300533 switch (optname) {
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200534 case DCCP_SOCKOPT_SERVER_TIMEWAIT:
535 if (dp->dccps_role != DCCP_ROLE_SERVER)
536 err = -EOPNOTSUPP;
537 else
538 dp->dccps_server_timewait = (val != 0);
539 break;
Gerrit Renker29450552008-11-16 22:53:48 -0800540 case DCCP_SOCKOPT_SEND_CSCOV:
541 err = dccp_setsockopt_cscov(sk, val, false);
Tomasz Grobelnyd6da3512008-09-04 07:30:19 +0200542 break;
Gerrit Renker29450552008-11-16 22:53:48 -0800543 case DCCP_SOCKOPT_RECV_CSCOV:
544 err = dccp_setsockopt_cscov(sk, val, true);
Tomasz Grobelnyd6da3512008-09-04 07:30:19 +0200545 break;
Tomasz Grobelny871a2c12010-12-04 13:38:01 +0100546 case DCCP_SOCKOPT_QPOLICY_ID:
547 if (sk->sk_state != DCCP_CLOSED)
548 err = -EISCONN;
549 else if (val < 0 || val >= DCCPQ_POLICY_MAX)
550 err = -EINVAL;
551 else
552 dp->dccps_qpolicy = val;
553 break;
554 case DCCP_SOCKOPT_QPOLICY_TXQLEN:
555 if (val < 0)
556 err = -EINVAL;
557 else
558 dp->dccps_tx_qlen = val;
559 break;
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300560 default:
561 err = -ENOPROTOOPT;
562 break;
563 }
Gerrit Renker410e27a2008-09-09 13:27:22 +0200564 release_sock(sk);
Gerrit Renker19102992008-11-16 22:56:55 -0800565
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300566 return err;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700567}
568
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800569int dccp_setsockopt(struct sock *sk, int level, int optname,
David S. Millerb7058842009-09-30 16:12:20 -0700570 char __user *optval, unsigned int optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800571{
572 if (level != SOL_DCCP)
573 return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
574 optname, optval,
575 optlen);
576 return do_dccp_setsockopt(sk, level, optname, optval, optlen);
577}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800578
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800579EXPORT_SYMBOL_GPL(dccp_setsockopt);
580
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800581#ifdef CONFIG_COMPAT
582int compat_dccp_setsockopt(struct sock *sk, int level, int optname,
David S. Millerb7058842009-09-30 16:12:20 -0700583 char __user *optval, unsigned int optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800584{
Arnaldo Carvalho de Melodec73ff2006-03-20 22:46:16 -0800585 if (level != SOL_DCCP)
586 return inet_csk_compat_setsockopt(sk, level, optname,
587 optval, optlen);
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800588 return do_dccp_setsockopt(sk, level, optname, optval, optlen);
589}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800590
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800591EXPORT_SYMBOL_GPL(compat_dccp_setsockopt);
592#endif
593
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700594static int dccp_getsockopt_service(struct sock *sk, int len,
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800595 __be32 __user *optval,
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700596 int __user *optlen)
597{
598 const struct dccp_sock *dp = dccp_sk(sk);
599 const struct dccp_service_list *sl;
600 int err = -ENOENT, slen = 0, total_len = sizeof(u32);
601
602 lock_sock(sk);
Arnaldo Carvalho de Melo67e6b622005-09-16 16:58:40 -0700603 if ((sl = dp->dccps_service_list) != NULL) {
604 slen = sl->dccpsl_nr * sizeof(u32);
605 total_len += slen;
606 }
607
608 err = -EINVAL;
609 if (total_len > len)
610 goto out;
611
612 err = 0;
613 if (put_user(total_len, optlen) ||
614 put_user(dp->dccps_service, optval) ||
615 (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen)))
616 err = -EFAULT;
617out:
618 release_sock(sk);
619 return err;
620}
621
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800622static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -0300623 char __user *optval, int __user *optlen)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700624{
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300625 struct dccp_sock *dp;
626 int val, len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700627
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300628 if (get_user(len, optlen))
629 return -EFAULT;
630
Arnaldo Carvalho de Melo39ebc022007-03-28 11:54:32 -0700631 if (len < (int)sizeof(int))
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300632 return -EINVAL;
633
634 dp = dccp_sk(sk);
635
636 switch (optname) {
637 case DCCP_SOCKOPT_PACKET_SIZE:
Gerrit Renker5aed3242006-11-28 19:33:36 -0200638 DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
Arnaldo Carvalho de Melo841bac12006-11-28 19:42:03 -0200639 return 0;
Arnaldo Carvalho de Melo88f964d2005-09-18 00:19:32 -0700640 case DCCP_SOCKOPT_SERVICE:
641 return dccp_getsockopt_service(sk, len,
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800642 (__be32 __user *)optval, optlen);
Gerrit Renker7c559a92007-10-04 14:39:22 -0700643 case DCCP_SOCKOPT_GET_CUR_MPS:
644 val = dp->dccps_mss_cache;
Gerrit Renker7c559a92007-10-04 14:39:22 -0700645 break;
Gerrit Renkerd90ebcb2008-11-12 00:47:26 -0800646 case DCCP_SOCKOPT_AVAILABLE_CCIDS:
647 return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen);
Gerrit Renker71c262a2008-11-23 16:04:59 -0800648 case DCCP_SOCKOPT_TX_CCID:
649 val = ccid_get_current_tx_ccid(dp);
650 if (val < 0)
651 return -ENOPROTOOPT;
652 break;
653 case DCCP_SOCKOPT_RX_CCID:
654 val = ccid_get_current_rx_ccid(dp);
655 if (val < 0)
656 return -ENOPROTOOPT;
657 break;
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200658 case DCCP_SOCKOPT_SERVER_TIMEWAIT:
659 val = dp->dccps_server_timewait;
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200660 break;
Gerrit Renker6f4e5ff2006-11-10 17:43:06 -0200661 case DCCP_SOCKOPT_SEND_CSCOV:
662 val = dp->dccps_pcslen;
663 break;
664 case DCCP_SOCKOPT_RECV_CSCOV:
665 val = dp->dccps_pcrlen;
666 break;
Tomasz Grobelny871a2c12010-12-04 13:38:01 +0100667 case DCCP_SOCKOPT_QPOLICY_ID:
668 val = dp->dccps_qpolicy;
669 break;
670 case DCCP_SOCKOPT_QPOLICY_TXQLEN:
671 val = dp->dccps_tx_qlen;
672 break;
Arnaldo Carvalho de Melo88f964d2005-09-18 00:19:32 -0700673 case 128 ... 191:
674 return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
675 len, (u32 __user *)optval, optlen);
676 case 192 ... 255:
677 return ccid_hc_tx_getsockopt(dp->dccps_hc_tx_ccid, sk, optname,
678 len, (u32 __user *)optval, optlen);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300679 default:
680 return -ENOPROTOOPT;
681 }
682
Gerrit Renker79133502007-12-13 12:27:14 -0200683 len = sizeof(val);
Arnaldo Carvalho de Meloa84ffe42005-08-28 04:51:32 -0300684 if (put_user(len, optlen) || copy_to_user(optval, &val, len))
685 return -EFAULT;
686
687 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700688}
689
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800690int dccp_getsockopt(struct sock *sk, int level, int optname,
691 char __user *optval, int __user *optlen)
692{
693 if (level != SOL_DCCP)
694 return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
695 optname, optval,
696 optlen);
697 return do_dccp_getsockopt(sk, level, optname, optval, optlen);
698}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800699
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800700EXPORT_SYMBOL_GPL(dccp_getsockopt);
701
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800702#ifdef CONFIG_COMPAT
703int compat_dccp_getsockopt(struct sock *sk, int level, int optname,
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800704 char __user *optval, int __user *optlen)
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800705{
Arnaldo Carvalho de Melodec73ff2006-03-20 22:46:16 -0800706 if (level != SOL_DCCP)
707 return inet_csk_compat_getsockopt(sk, level, optname,
708 optval, optlen);
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800709 return do_dccp_getsockopt(sk, level, optname, optval, optlen);
710}
Arnaldo Carvalho de Melo543d9cf2006-03-20 22:48:35 -0800711
Dmitry Mishin3fdadf72006-03-20 22:45:21 -0800712EXPORT_SYMBOL_GPL(compat_dccp_getsockopt);
713#endif
714
Tomasz Grobelny871a2c12010-12-04 13:38:01 +0100715static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb)
716{
Gu Zhengf95b4142014-12-11 11:22:04 +0800717 struct cmsghdr *cmsg;
Tomasz Grobelny871a2c12010-12-04 13:38:01 +0100718
719 /*
720 * Assign an (opaque) qpolicy priority value to skb->priority.
721 *
722 * We are overloading this skb field for use with the qpolicy subystem.
723 * The skb->priority is normally used for the SO_PRIORITY option, which
724 * is initialised from sk_priority. Since the assignment of sk_priority
725 * to skb->priority happens later (on layer 3), we overload this field
726 * for use with queueing priorities as long as the skb is on layer 4.
727 * The default priority value (if nothing is set) is 0.
728 */
729 skb->priority = 0;
730
Gu Zhengf95b4142014-12-11 11:22:04 +0800731 for_each_cmsghdr(cmsg, msg) {
Tomasz Grobelny871a2c12010-12-04 13:38:01 +0100732 if (!CMSG_OK(msg, cmsg))
733 return -EINVAL;
734
735 if (cmsg->cmsg_level != SOL_DCCP)
736 continue;
737
Tomasz Grobelny04910262010-12-04 13:39:13 +0100738 if (cmsg->cmsg_type <= DCCP_SCM_QPOLICY_MAX &&
739 !dccp_qpolicy_param_ok(skb->sk, cmsg->cmsg_type))
740 return -EINVAL;
741
Tomasz Grobelny871a2c12010-12-04 13:38:01 +0100742 switch (cmsg->cmsg_type) {
743 case DCCP_SCM_PRIORITY:
744 if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u32)))
745 return -EINVAL;
746 skb->priority = *(__u32 *)CMSG_DATA(cmsg);
747 break;
748 default:
749 return -EINVAL;
750 }
751 }
752 return 0;
753}
754
Ying Xue1b784142015-03-02 15:37:48 +0800755int dccp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700756{
757 const struct dccp_sock *dp = dccp_sk(sk);
758 const int flags = msg->msg_flags;
759 const int noblock = flags & MSG_DONTWAIT;
760 struct sk_buff *skb;
761 int rc, size;
762 long timeo;
763
Masami Hiramatsuee549be2017-12-29 11:47:55 +0900764 trace_dccp_probe(sk, len);
765
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700766 if (len > dp->dccps_mss_cache)
767 return -EMSGSIZE;
768
769 lock_sock(sk);
Ian McDonaldb1308dc2006-11-20 18:30:17 -0200770
Tomasz Grobelny871a2c12010-12-04 13:38:01 +0100771 if (dccp_qpolicy_full(sk)) {
Ian McDonaldb1308dc2006-11-20 18:30:17 -0200772 rc = -EAGAIN;
773 goto out_release;
774 }
775
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700776 timeo = sock_sndtimeo(sk, noblock);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700777
778 /*
779 * We have to use sk_stream_wait_connect here to set sk_write_pending,
780 * so that the trick in dccp_rcv_request_sent_state_process.
781 */
782 /* Wait for a connection to finish. */
Gerrit Renkercecd8d02007-09-26 19:36:08 -0300783 if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN))
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700784 if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0)
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700785 goto out_release;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700786
787 size = sk->sk_prot->max_header + len;
788 release_sock(sk);
789 skb = sock_alloc_send_skb(sk, size, noblock, &rc);
790 lock_sock(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700791 if (skb == NULL)
792 goto out_release;
793
Alexey Kodanev67f93df2018-03-06 22:57:01 +0300794 if (sk->sk_state == DCCP_CLOSED) {
795 rc = -ENOTCONN;
796 goto out_discard;
797 }
798
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700799 skb_reserve(skb, sk->sk_prot->max_header);
Al Viro6ce8e9c2014-04-06 21:25:44 -0400800 rc = memcpy_from_msg(skb_put(skb, len), msg, len);
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700801 if (rc != 0)
802 goto out_discard;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700803
Tomasz Grobelny871a2c12010-12-04 13:38:01 +0100804 rc = dccp_msghdr_parse(msg, skb);
805 if (rc != 0)
806 goto out_discard;
807
808 dccp_qpolicy_push(sk, skb);
Gerrit Renkerb1fcf552010-10-27 19:16:27 +0000809 /*
810 * The xmit_timer is set if the TX CCID is rate-based and will expire
811 * when congestion control permits to release further packets into the
812 * network. Window-based CCIDs do not use this timer.
813 */
814 if (!timer_pending(&dp->dccps_xmit_timer))
815 dccp_write_xmit(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700816out_release:
817 release_sock(sk);
818 return rc ? : len;
Arnaldo Carvalho de Melo27258ee2005-08-09 20:30:56 -0700819out_discard:
820 kfree_skb(skb);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700821 goto out_release;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700822}
823
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800824EXPORT_SYMBOL_GPL(dccp_sendmsg);
825
Ying Xue1b784142015-03-02 15:37:48 +0800826int dccp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
827 int flags, int *addr_len)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700828{
829 const struct dccp_hdr *dh;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700830 long timeo;
831
832 lock_sock(sk);
833
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300834 if (sk->sk_state == DCCP_LISTEN) {
835 len = -ENOTCONN;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700836 goto out;
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300837 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700838
839 timeo = sock_rcvtimeo(sk, nonblock);
840
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700841 do {
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300842 struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700843
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300844 if (skb == NULL)
845 goto verify_sock_status;
846
847 dh = dccp_hdr(skb);
848
Gerrit Renker0c869622007-11-28 11:59:48 -0200849 switch (dh->dccph_type) {
850 case DCCP_PKT_DATA:
851 case DCCP_PKT_DATAACK:
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300852 goto found_ok_skb;
853
Gerrit Renker0c869622007-11-28 11:59:48 -0200854 case DCCP_PKT_CLOSE:
855 case DCCP_PKT_CLOSEREQ:
856 if (!(flags & MSG_PEEK))
857 dccp_finish_passive_close(sk);
858 /* fall through */
859 case DCCP_PKT_RESET:
860 dccp_pr_debug("found fin (%s) ok!\n",
861 dccp_packet_name(dh->dccph_type));
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300862 len = 0;
863 goto found_fin_ok;
Gerrit Renker0c869622007-11-28 11:59:48 -0200864 default:
865 dccp_pr_debug("packet_type=%s\n",
866 dccp_packet_name(dh->dccph_type));
Dan Williams7bced392013-12-30 12:37:29 -0800867 sk_eat_skb(sk, skb);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700868 }
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300869verify_sock_status:
870 if (sock_flag(sk, SOCK_DONE)) {
871 len = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700872 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700873 }
874
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300875 if (sk->sk_err) {
876 len = sock_error(sk);
877 break;
878 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700879
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300880 if (sk->sk_shutdown & RCV_SHUTDOWN) {
881 len = 0;
882 break;
883 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700884
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300885 if (sk->sk_state == DCCP_CLOSED) {
886 if (!sock_flag(sk, SOCK_DONE)) {
887 /* This occurs when user tries to read
888 * from never connected socket.
889 */
890 len = -ENOTCONN;
891 break;
892 }
893 len = 0;
894 break;
895 }
896
897 if (!timeo) {
898 len = -EAGAIN;
899 break;
900 }
901
902 if (signal_pending(current)) {
903 len = sock_intr_errno(timeo);
904 break;
905 }
906
Sabrina Dubrocadfbafc92015-07-24 18:19:25 +0200907 sk_wait_data(sk, &timeo, NULL);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700908 continue;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700909 found_ok_skb:
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300910 if (len > skb->len)
911 len = skb->len;
912 else if (len < skb->len)
913 msg->msg_flags |= MSG_TRUNC;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700914
David S. Miller51f3d022014-11-05 16:46:40 -0500915 if (skb_copy_datagram_msg(skb, 0, msg, len)) {
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300916 /* Exception. Bailout! */
917 len = -EFAULT;
918 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700919 }
Gerrit Renker55d95592010-02-10 20:26:18 +0000920 if (flags & MSG_TRUNC)
921 len = skb->len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700922 found_fin_ok:
923 if (!(flags & MSG_PEEK))
Dan Williams7bced392013-12-30 12:37:29 -0800924 sk_eat_skb(sk, skb);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700925 break;
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300926 } while (1);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700927out:
928 release_sock(sk);
Arnaldo Carvalho de Melo531669a2005-08-13 20:35:17 -0300929 return len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700930}
931
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800932EXPORT_SYMBOL_GPL(dccp_recvmsg);
933
934int inet_dccp_listen(struct socket *sock, int backlog)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700935{
936 struct sock *sk = sock->sk;
937 unsigned char old_state;
938 int err;
939
940 lock_sock(sk);
941
942 err = -EINVAL;
943 if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP)
944 goto out;
945
946 old_state = sk->sk_state;
947 if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN)))
948 goto out;
949
Yafang Shao1295e2c2018-11-07 19:20:16 +0800950 sk->sk_max_ack_backlog = backlog;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700951 /* Really, if the socket is already in listen state
952 * we can only allow the backlog to be adjusted.
953 */
954 if (old_state != DCCP_LISTEN) {
955 /*
956 * FIXME: here it probably should be sk->sk_prot->listen_start
957 * see tcp_listen_start
958 */
Eric Dumazet72a3eff2006-11-16 02:30:37 -0800959 err = dccp_listen_start(sk, backlog);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700960 if (err)
961 goto out;
962 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700963 err = 0;
964
965out:
966 release_sock(sk);
967 return err;
968}
969
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -0800970EXPORT_SYMBOL_GPL(inet_dccp_listen);
971
Gerrit Renker0c869622007-11-28 11:59:48 -0200972static void dccp_terminate_connection(struct sock *sk)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700973{
Gerrit Renker0c869622007-11-28 11:59:48 -0200974 u8 next_state = DCCP_CLOSED;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700975
Gerrit Renker0c869622007-11-28 11:59:48 -0200976 switch (sk->sk_state) {
977 case DCCP_PASSIVE_CLOSE:
978 case DCCP_PASSIVE_CLOSEREQ:
979 dccp_finish_passive_close(sk);
980 break;
981 case DCCP_PARTOPEN:
982 dccp_pr_debug("Stop PARTOPEN timer (%p)\n", sk);
983 inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);
984 /* fall through */
985 case DCCP_OPEN:
986 dccp_send_close(sk, 1);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700987
Gerrit Renkerb8599d22007-12-13 12:25:01 -0200988 if (dccp_sk(sk)->dccps_role == DCCP_ROLE_SERVER &&
989 !dccp_sk(sk)->dccps_server_timewait)
Gerrit Renker0c869622007-11-28 11:59:48 -0200990 next_state = DCCP_ACTIVE_CLOSEREQ;
991 else
992 next_state = DCCP_CLOSING;
993 /* fall through */
994 default:
995 dccp_set_state(sk, next_state);
996 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700997}
998
999void dccp_close(struct sock *sk, long timeout)
1000{
Ian McDonald97e5848d2006-08-26 19:16:45 -07001001 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001002 struct sk_buff *skb;
Gerrit Renkerd83bd952007-12-16 16:06:03 -08001003 u32 data_was_unread = 0;
Herbert Xu134af342006-05-05 17:09:13 -07001004 int state;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001005
1006 lock_sock(sk);
1007
1008 sk->sk_shutdown = SHUTDOWN_MASK;
1009
1010 if (sk->sk_state == DCCP_LISTEN) {
1011 dccp_set_state(sk, DCCP_CLOSED);
1012
1013 /* Special case. */
1014 inet_csk_listen_stop(sk);
1015
1016 goto adjudge_to_death;
1017 }
1018
Ian McDonald97e5848d2006-08-26 19:16:45 -07001019 sk_stop_timer(sk, &dp->dccps_xmit_timer);
1020
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001021 /*
1022 * We need to flush the recv. buffs. We do this only on the
1023 * descriptor close, not protocol-sourced closes, because the
1024 *reader process may not have drained the data yet!
1025 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001026 while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
Gerrit Renkerd83bd952007-12-16 16:06:03 -08001027 data_was_unread += skb->len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001028 __kfree_skb(skb);
1029 }
1030
Eric Dumazet346da622016-11-02 18:04:24 -07001031 /* If socket has been already reset kill it. */
1032 if (sk->sk_state == DCCP_CLOSED)
1033 goto adjudge_to_death;
1034
Gerrit Renkerd83bd952007-12-16 16:06:03 -08001035 if (data_was_unread) {
1036 /* Unread data was tossed, send an appropriate Reset Code */
Gerrit Renker2f34b322010-10-11 20:44:42 +02001037 DCCP_WARN("ABORT with %u bytes unread\n", data_was_unread);
Gerrit Renkerd83bd952007-12-16 16:06:03 -08001038 dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
1039 dccp_set_state(sk, DCCP_CLOSED);
1040 } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001041 /* Check zero linger _after_ checking for unread data. */
1042 sk->sk_prot->disconnect(sk, 0);
Gerrit Renker0c869622007-11-28 11:59:48 -02001043 } else if (sk->sk_state != DCCP_CLOSED) {
Gerrit Renkerb1fcf552010-10-27 19:16:27 +00001044 /*
1045 * Normal connection termination. May need to wait if there are
1046 * still packets in the TX queue that are delayed by the CCID.
1047 */
1048 dccp_flush_write_queue(sk, &timeout);
Gerrit Renker0c869622007-11-28 11:59:48 -02001049 dccp_terminate_connection(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001050 }
1051
Gerrit Renkerb1fcf552010-10-27 19:16:27 +00001052 /*
1053 * Flush write queue. This may be necessary in several cases:
1054 * - we have been closed by the peer but still have application data;
1055 * - abortive termination (unread data or zero linger time),
1056 * - normal termination but queue could not be flushed within time limit
1057 */
1058 __skb_queue_purge(&sk->sk_write_queue);
1059
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001060 sk_stream_wait_close(sk, timeout);
1061
1062adjudge_to_death:
Herbert Xu134af342006-05-05 17:09:13 -07001063 state = sk->sk_state;
1064 sock_hold(sk);
1065 sock_orphan(sk);
Herbert Xu134af342006-05-05 17:09:13 -07001066
Arnaldo Carvalho de Melo7ad07e72005-08-23 21:50:06 -07001067 /*
1068 * It is the last release_sock in its life. It will remove backlog.
1069 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001070 release_sock(sk);
1071 /*
1072 * Now socket is owned by kernel and we acquire BH lock
1073 * to finish close. No need to check for user refs.
1074 */
1075 local_bh_disable();
1076 bh_lock_sock(sk);
Ilpo Järvinen547b7922008-07-25 21:43:18 -07001077 WARN_ON(sock_owned_by_user(sk));
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001078
Herbert Xueb4dea52008-12-29 23:04:08 -08001079 percpu_counter_inc(sk->sk_prot->orphan_count);
1080
Herbert Xu134af342006-05-05 17:09:13 -07001081 /* Have we already been destroyed by a softirq or backlog? */
1082 if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
1083 goto out;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001084
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001085 if (sk->sk_state == DCCP_CLOSED)
1086 inet_csk_destroy_sock(sk);
1087
1088 /* Otherwise, socket is reprieved until protocol close. */
1089
Herbert Xu134af342006-05-05 17:09:13 -07001090out:
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001091 bh_unlock_sock(sk);
1092 local_bh_enable();
1093 sock_put(sk);
1094}
1095
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001096EXPORT_SYMBOL_GPL(dccp_close);
1097
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001098void dccp_shutdown(struct sock *sk, int how)
1099{
Gerrit Renker8e8c71f2007-11-21 09:56:48 -02001100 dccp_pr_debug("called shutdown(%x)\n", how);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001101}
1102
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001103EXPORT_SYMBOL_GPL(dccp_shutdown);
1104
Fabian Frederick0c5b8a42014-10-01 06:48:03 +02001105static inline int __init dccp_mib_init(void)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001106{
WANG Cong698365f2014-05-05 15:55:55 -07001107 dccp_statistics = alloc_percpu(struct dccp_mib);
1108 if (!dccp_statistics)
1109 return -ENOMEM;
1110 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001111}
1112
YOSHIFUJI Hideaki24e8b7e2008-04-10 03:48:43 -07001113static inline void dccp_mib_exit(void)
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001114{
WANG Cong698365f2014-05-05 15:55:55 -07001115 free_percpu(dccp_statistics);
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001116}
1117
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001118static int thash_entries;
1119module_param(thash_entries, int, 0444);
1120MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
1121
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -03001122#ifdef CONFIG_IP_DCCP_DEBUG
Rusty Russelleb939922011-12-19 14:08:01 +00001123bool dccp_debug;
Gerrit Renker43264992008-08-23 13:28:27 +02001124module_param(dccp_debug, bool, 0644);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001125MODULE_PARM_DESC(dccp_debug, "Enable debug messages");
Arnaldo Carvalho de Melof21e68c2005-12-13 23:24:16 -08001126
1127EXPORT_SYMBOL_GPL(dccp_debug);
Arnaldo Carvalho de Meloa1d3a352005-08-13 22:42:25 -03001128#endif
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001129
1130static int __init dccp_init(void)
1131{
1132 unsigned long goal;
Arun KSca79b0c2018-12-28 00:34:29 -08001133 unsigned long nr_pages = totalram_pages();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001134 int ehash_order, bhash_order, i;
Eric Dumazetdd24c002008-11-25 21:17:14 -08001135 int rc;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001136
Patrick McHardy028b0272008-04-12 18:35:41 -07001137 BUILD_BUG_ON(sizeof(struct dccp_skb_cb) >
1138 FIELD_SIZEOF(struct sk_buff, cb));
Tejun Heo908c7f12014-09-08 09:51:29 +09001139 rc = percpu_counter_init(&dccp_orphan_count, 0, GFP_KERNEL);
Eric Dumazetdd24c002008-11-25 21:17:14 -08001140 if (rc)
Gerrit Renkerd14a0eb2010-03-14 20:13:19 +00001141 goto out_fail;
Eric Dumazet5caea4e2008-11-20 00:40:07 -08001142 inet_hashinfo_init(&dccp_hashinfo);
Peter Oskolkovc92c81d2018-12-24 12:57:17 -08001143 rc = inet_hashinfo2_init_mod(&dccp_hashinfo);
1144 if (rc)
1145 goto out_fail;
1146 rc = -ENOBUFS;
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001147 dccp_hashinfo.bind_bucket_cachep =
1148 kmem_cache_create("dccp_bind_bucket",
1149 sizeof(struct inet_bind_bucket), 0,
Paul Mundt20c2df82007-07-20 10:11:58 +09001150 SLAB_HWCACHE_ALIGN, NULL);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001151 if (!dccp_hashinfo.bind_bucket_cachep)
Eric Dumazetdd24c002008-11-25 21:17:14 -08001152 goto out_free_percpu;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001153
1154 /*
1155 * Size and allocate the main established and bind bucket
1156 * hash tables.
1157 *
1158 * The methodology is similar to that of the buffer cache.
1159 */
Arun KS3d6357d2018-12-28 00:34:20 -08001160 if (nr_pages >= (128 * 1024))
1161 goal = nr_pages >> (21 - PAGE_SHIFT);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001162 else
Arun KS3d6357d2018-12-28 00:34:20 -08001163 goal = nr_pages >> (23 - PAGE_SHIFT);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001164
1165 if (thash_entries)
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001166 goal = (thash_entries *
1167 sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001168 for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++)
1169 ;
1170 do {
Eric Dumazetf373b532009-10-09 00:16:19 +00001171 unsigned long hash_size = (1UL << ehash_order) * PAGE_SIZE /
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001172 sizeof(struct inet_ehash_bucket);
Eric Dumazetf373b532009-10-09 00:16:19 +00001173
1174 while (hash_size & (hash_size - 1))
1175 hash_size--;
1176 dccp_hashinfo.ehash_mask = hash_size - 1;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001177 dccp_hashinfo.ehash = (struct inet_ehash_bucket *)
Mel Gorman1c29b3f2009-07-29 15:04:10 -07001178 __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, ehash_order);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001179 } while (!dccp_hashinfo.ehash && --ehash_order > 0);
1180
1181 if (!dccp_hashinfo.ehash) {
Gerrit Renker59348b12006-11-20 18:39:23 -02001182 DCCP_CRIT("Failed to allocate DCCP established hash table");
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001183 goto out_free_bind_bucket_cachep;
1184 }
1185
Eric Dumazet05dbc7b2013-10-03 00:22:02 -07001186 for (i = 0; i <= dccp_hashinfo.ehash_mask; i++)
Eric Dumazet3ab5aee2008-11-16 19:40:17 -08001187 INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].chain, i);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001188
Eric Dumazet230140c2007-11-07 02:40:20 -08001189 if (inet_ehash_locks_alloc(&dccp_hashinfo))
1190 goto out_free_dccp_ehash;
1191
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001192 bhash_order = ehash_order;
1193
1194 do {
1195 dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE /
1196 sizeof(struct inet_bind_hashbucket);
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -03001197 if ((dccp_hashinfo.bhash_size > (64 * 1024)) &&
1198 bhash_order > 0)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001199 continue;
1200 dccp_hashinfo.bhash = (struct inet_bind_hashbucket *)
Mel Gorman1c29b3f2009-07-29 15:04:10 -07001201 __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, bhash_order);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001202 } while (!dccp_hashinfo.bhash && --bhash_order >= 0);
1203
1204 if (!dccp_hashinfo.bhash) {
Gerrit Renker59348b12006-11-20 18:39:23 -02001205 DCCP_CRIT("Failed to allocate DCCP bind hash table");
Eric Dumazet230140c2007-11-07 02:40:20 -08001206 goto out_free_dccp_locks;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001207 }
1208
1209 for (i = 0; i < dccp_hashinfo.bhash_size; i++) {
1210 spin_lock_init(&dccp_hashinfo.bhash[i].lock);
1211 INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
1212 }
1213
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001214 rc = dccp_mib_init();
Arnaldo Carvalho de Melofa23e2e2006-03-20 17:16:01 -08001215 if (rc)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001216 goto out_free_dccp_bhash;
1217
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001218 rc = dccp_ackvec_init();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001219 if (rc)
Arnaldo Carvalho de Melob61fafc42006-03-20 21:25:11 -08001220 goto out_free_dccp_mib;
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001221
Arnaldo Carvalho de Meloe55d9122006-03-20 19:25:02 -08001222 rc = dccp_sysctl_init();
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001223 if (rc)
1224 goto out_ackvec_exit;
Gerrit Renker4c70f382007-09-25 22:40:13 -07001225
Gerrit Renkerddebc972009-01-04 21:42:53 -08001226 rc = ccid_initialize_builtins();
1227 if (rc)
1228 goto out_sysctl_exit;
1229
Gerrit Renker4c70f382007-09-25 22:40:13 -07001230 dccp_timestamping_init();
Gerrit Renkerd14a0eb2010-03-14 20:13:19 +00001231
1232 return 0;
1233
Gerrit Renkerddebc972009-01-04 21:42:53 -08001234out_sysctl_exit:
1235 dccp_sysctl_exit();
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001236out_ackvec_exit:
1237 dccp_ackvec_exit();
Arnaldo Carvalho de Melob61fafc42006-03-20 21:25:11 -08001238out_free_dccp_mib:
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001239 dccp_mib_exit();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001240out_free_dccp_bhash:
1241 free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
Eric Dumazet230140c2007-11-07 02:40:20 -08001242out_free_dccp_locks:
1243 inet_ehash_locks_free(&dccp_hashinfo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001244out_free_dccp_ehash:
1245 free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001246out_free_bind_bucket_cachep:
1247 kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
Eric Dumazetdd24c002008-11-25 21:17:14 -08001248out_free_percpu:
1249 percpu_counter_destroy(&dccp_orphan_count);
Gerrit Renkerd14a0eb2010-03-14 20:13:19 +00001250out_fail:
1251 dccp_hashinfo.bhash = NULL;
1252 dccp_hashinfo.ehash = NULL;
1253 dccp_hashinfo.bind_bucket_cachep = NULL;
1254 return rc;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001255}
1256
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001257static void __exit dccp_fini(void)
1258{
Gerrit Renkerddebc972009-01-04 21:42:53 -08001259 ccid_cleanup_builtins();
Arnaldo Carvalho de Melo46f09ff2006-03-20 21:24:42 -08001260 dccp_mib_exit();
Arnaldo Carvalho de Melo725ba8e2005-08-13 20:35:39 -03001261 free_pages((unsigned long)dccp_hashinfo.bhash,
1262 get_order(dccp_hashinfo.bhash_size *
1263 sizeof(struct inet_bind_hashbucket)));
1264 free_pages((unsigned long)dccp_hashinfo.ehash,
Eric Dumazetf373b532009-10-09 00:16:19 +00001265 get_order((dccp_hashinfo.ehash_mask + 1) *
Arnaldo Carvalho de Melo725ba8e2005-08-13 20:35:39 -03001266 sizeof(struct inet_ehash_bucket)));
Eric Dumazet230140c2007-11-07 02:40:20 -08001267 inet_ehash_locks_free(&dccp_hashinfo);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001268 kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
Arnaldo Carvalho de Melo9b07ef52006-03-20 17:16:17 -08001269 dccp_ackvec_exit();
Arnaldo Carvalho de Meloe55d9122006-03-20 19:25:02 -08001270 dccp_sysctl_exit();
Wei Yongjun476181c2009-08-04 21:44:39 +00001271 percpu_counter_destroy(&dccp_orphan_count);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001272}
1273
1274module_init(dccp_init);
1275module_exit(dccp_fini);
1276
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001277MODULE_LICENSE("GPL");
1278MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>");
1279MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");