xref: /openbmc/linux/net/dccp/proto.c (revision a1d3a355)
17c657876SArnaldo Carvalho de Melo /*
27c657876SArnaldo Carvalho de Melo  *  net/dccp/proto.c
37c657876SArnaldo Carvalho de Melo  *
47c657876SArnaldo Carvalho de Melo  *  An implementation of the DCCP protocol
57c657876SArnaldo Carvalho de Melo  *  Arnaldo Carvalho de Melo <acme@conectiva.com.br>
67c657876SArnaldo Carvalho de Melo  *
77c657876SArnaldo Carvalho de Melo  *	This program is free software; you can redistribute it and/or modify it
87c657876SArnaldo Carvalho de Melo  *	under the terms of the GNU General Public License version 2 as
97c657876SArnaldo Carvalho de Melo  *	published by the Free Software Foundation.
107c657876SArnaldo Carvalho de Melo  */
117c657876SArnaldo Carvalho de Melo 
127c657876SArnaldo Carvalho de Melo #include <linux/config.h>
137c657876SArnaldo Carvalho de Melo #include <linux/dccp.h>
147c657876SArnaldo Carvalho de Melo #include <linux/module.h>
157c657876SArnaldo Carvalho de Melo #include <linux/types.h>
167c657876SArnaldo Carvalho de Melo #include <linux/sched.h>
177c657876SArnaldo Carvalho de Melo #include <linux/kernel.h>
187c657876SArnaldo Carvalho de Melo #include <linux/skbuff.h>
197c657876SArnaldo Carvalho de Melo #include <linux/netdevice.h>
207c657876SArnaldo Carvalho de Melo #include <linux/in.h>
217c657876SArnaldo Carvalho de Melo #include <linux/if_arp.h>
227c657876SArnaldo Carvalho de Melo #include <linux/init.h>
237c657876SArnaldo Carvalho de Melo #include <linux/random.h>
247c657876SArnaldo Carvalho de Melo #include <net/checksum.h>
257c657876SArnaldo Carvalho de Melo 
267c657876SArnaldo Carvalho de Melo #include <net/inet_common.h>
277c657876SArnaldo Carvalho de Melo #include <net/ip.h>
287c657876SArnaldo Carvalho de Melo #include <net/protocol.h>
297c657876SArnaldo Carvalho de Melo #include <net/sock.h>
307c657876SArnaldo Carvalho de Melo #include <net/xfrm.h>
317c657876SArnaldo Carvalho de Melo 
327c657876SArnaldo Carvalho de Melo #include <asm/semaphore.h>
337c657876SArnaldo Carvalho de Melo #include <linux/spinlock.h>
347c657876SArnaldo Carvalho de Melo #include <linux/timer.h>
357c657876SArnaldo Carvalho de Melo #include <linux/delay.h>
367c657876SArnaldo Carvalho de Melo #include <linux/poll.h>
377c657876SArnaldo Carvalho de Melo #include <linux/dccp.h>
387c657876SArnaldo Carvalho de Melo 
397c657876SArnaldo Carvalho de Melo #include "ccid.h"
407c657876SArnaldo Carvalho de Melo #include "dccp.h"
417c657876SArnaldo Carvalho de Melo 
427c657876SArnaldo Carvalho de Melo DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics);
437c657876SArnaldo Carvalho de Melo 
447c657876SArnaldo Carvalho de Melo atomic_t dccp_orphan_count = ATOMIC_INIT(0);
457c657876SArnaldo Carvalho de Melo 
467c657876SArnaldo Carvalho de Melo static struct net_protocol dccp_protocol = {
477c657876SArnaldo Carvalho de Melo 	.handler	= dccp_v4_rcv,
487c657876SArnaldo Carvalho de Melo 	.err_handler	= dccp_v4_err,
497c657876SArnaldo Carvalho de Melo };
507c657876SArnaldo Carvalho de Melo 
517c657876SArnaldo Carvalho de Melo const char *dccp_packet_name(const int type)
527c657876SArnaldo Carvalho de Melo {
537c657876SArnaldo Carvalho de Melo 	static const char *dccp_packet_names[] = {
547c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_REQUEST]  = "REQUEST",
557c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_RESPONSE] = "RESPONSE",
567c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_DATA]	    = "DATA",
577c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_ACK]	    = "ACK",
587c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_DATAACK]  = "DATAACK",
597c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_CLOSEREQ] = "CLOSEREQ",
607c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_CLOSE]    = "CLOSE",
617c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_RESET]    = "RESET",
627c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_SYNC]	    = "SYNC",
637c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_SYNCACK]  = "SYNCACK",
647c657876SArnaldo Carvalho de Melo 	};
657c657876SArnaldo Carvalho de Melo 
667c657876SArnaldo Carvalho de Melo 	if (type >= DCCP_NR_PKT_TYPES)
677c657876SArnaldo Carvalho de Melo 		return "INVALID";
687c657876SArnaldo Carvalho de Melo 	else
697c657876SArnaldo Carvalho de Melo 		return dccp_packet_names[type];
707c657876SArnaldo Carvalho de Melo }
717c657876SArnaldo Carvalho de Melo 
727c657876SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_packet_name);
737c657876SArnaldo Carvalho de Melo 
747c657876SArnaldo Carvalho de Melo const char *dccp_state_name(const int state)
757c657876SArnaldo Carvalho de Melo {
767c657876SArnaldo Carvalho de Melo 	static char *dccp_state_names[] = {
777c657876SArnaldo Carvalho de Melo 	[DCCP_OPEN]	  = "OPEN",
787c657876SArnaldo Carvalho de Melo 	[DCCP_REQUESTING] = "REQUESTING",
797c657876SArnaldo Carvalho de Melo 	[DCCP_PARTOPEN]	  = "PARTOPEN",
807c657876SArnaldo Carvalho de Melo 	[DCCP_LISTEN]	  = "LISTEN",
817c657876SArnaldo Carvalho de Melo 	[DCCP_RESPOND]	  = "RESPOND",
827c657876SArnaldo Carvalho de Melo 	[DCCP_CLOSING]	  = "CLOSING",
837c657876SArnaldo Carvalho de Melo 	[DCCP_TIME_WAIT]  = "TIME_WAIT",
847c657876SArnaldo Carvalho de Melo 	[DCCP_CLOSED]	  = "CLOSED",
857c657876SArnaldo Carvalho de Melo 	};
867c657876SArnaldo Carvalho de Melo 
877c657876SArnaldo Carvalho de Melo 	if (state >= DCCP_MAX_STATES)
887c657876SArnaldo Carvalho de Melo 		return "INVALID STATE!";
897c657876SArnaldo Carvalho de Melo 	else
907c657876SArnaldo Carvalho de Melo 		return dccp_state_names[state];
917c657876SArnaldo Carvalho de Melo }
927c657876SArnaldo Carvalho de Melo 
937c657876SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_state_name);
947c657876SArnaldo Carvalho de Melo 
957c657876SArnaldo Carvalho de Melo static inline int dccp_listen_start(struct sock *sk)
967c657876SArnaldo Carvalho de Melo {
977c657876SArnaldo Carvalho de Melo 	dccp_sk(sk)->dccps_role = DCCP_ROLE_LISTEN;
987c657876SArnaldo Carvalho de Melo 	return inet_csk_listen_start(sk, TCP_SYNQ_HSIZE);
997c657876SArnaldo Carvalho de Melo }
1007c657876SArnaldo Carvalho de Melo 
1017c657876SArnaldo Carvalho de Melo int dccp_disconnect(struct sock *sk, int flags)
1027c657876SArnaldo Carvalho de Melo {
1037c657876SArnaldo Carvalho de Melo 	struct inet_connection_sock *icsk = inet_csk(sk);
1047c657876SArnaldo Carvalho de Melo 	struct inet_sock *inet = inet_sk(sk);
1057c657876SArnaldo Carvalho de Melo 	int err = 0;
1067c657876SArnaldo Carvalho de Melo 	const int old_state = sk->sk_state;
1077c657876SArnaldo Carvalho de Melo 
1087c657876SArnaldo Carvalho de Melo 	if (old_state != DCCP_CLOSED)
1097c657876SArnaldo Carvalho de Melo 		dccp_set_state(sk, DCCP_CLOSED);
1107c657876SArnaldo Carvalho de Melo 
1117c657876SArnaldo Carvalho de Melo 	/* ABORT function of RFC793 */
1127c657876SArnaldo Carvalho de Melo 	if (old_state == DCCP_LISTEN) {
1137c657876SArnaldo Carvalho de Melo 		inet_csk_listen_stop(sk);
1147c657876SArnaldo Carvalho de Melo 	/* FIXME: do the active reset thing */
1157c657876SArnaldo Carvalho de Melo 	} else if (old_state == DCCP_REQUESTING)
1167c657876SArnaldo Carvalho de Melo 		sk->sk_err = ECONNRESET;
1177c657876SArnaldo Carvalho de Melo 
1187c657876SArnaldo Carvalho de Melo 	dccp_clear_xmit_timers(sk);
1197c657876SArnaldo Carvalho de Melo 	__skb_queue_purge(&sk->sk_receive_queue);
1207c657876SArnaldo Carvalho de Melo 	if (sk->sk_send_head != NULL) {
1217c657876SArnaldo Carvalho de Melo 		__kfree_skb(sk->sk_send_head);
1227c657876SArnaldo Carvalho de Melo 		sk->sk_send_head = NULL;
1237c657876SArnaldo Carvalho de Melo 	}
1247c657876SArnaldo Carvalho de Melo 
1257c657876SArnaldo Carvalho de Melo 	inet->dport = 0;
1267c657876SArnaldo Carvalho de Melo 
1277c657876SArnaldo Carvalho de Melo 	if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
1287c657876SArnaldo Carvalho de Melo 		inet_reset_saddr(sk);
1297c657876SArnaldo Carvalho de Melo 
1307c657876SArnaldo Carvalho de Melo 	sk->sk_shutdown = 0;
1317c657876SArnaldo Carvalho de Melo 	sock_reset_flag(sk, SOCK_DONE);
1327c657876SArnaldo Carvalho de Melo 
1337c657876SArnaldo Carvalho de Melo 	icsk->icsk_backoff = 0;
1347c657876SArnaldo Carvalho de Melo 	inet_csk_delack_init(sk);
1357c657876SArnaldo Carvalho de Melo 	__sk_dst_reset(sk);
1367c657876SArnaldo Carvalho de Melo 
1377c657876SArnaldo Carvalho de Melo 	BUG_TRAP(!inet->num || icsk->icsk_bind_hash);
1387c657876SArnaldo Carvalho de Melo 
1397c657876SArnaldo Carvalho de Melo 	sk->sk_error_report(sk);
1407c657876SArnaldo Carvalho de Melo 	return err;
1417c657876SArnaldo Carvalho de Melo }
1427c657876SArnaldo Carvalho de Melo 
1437c657876SArnaldo Carvalho de Melo int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
1447c657876SArnaldo Carvalho de Melo {
1457c657876SArnaldo Carvalho de Melo 	dccp_pr_debug("entry\n");
1467c657876SArnaldo Carvalho de Melo 	return -ENOIOCTLCMD;
1477c657876SArnaldo Carvalho de Melo }
1487c657876SArnaldo Carvalho de Melo 
1497c657876SArnaldo Carvalho de Melo int dccp_setsockopt(struct sock *sk, int level, int optname,
150a1d3a355SArnaldo Carvalho de Melo 		    char __user *optval, int optlen)
1517c657876SArnaldo Carvalho de Melo {
1527c657876SArnaldo Carvalho de Melo 	dccp_pr_debug("entry\n");
1537c657876SArnaldo Carvalho de Melo 
1547c657876SArnaldo Carvalho de Melo 	if (level != SOL_DCCP)
1557c657876SArnaldo Carvalho de Melo 		return ip_setsockopt(sk, level, optname, optval, optlen);
1567c657876SArnaldo Carvalho de Melo 
1577c657876SArnaldo Carvalho de Melo 	return -EOPNOTSUPP;
1587c657876SArnaldo Carvalho de Melo }
1597c657876SArnaldo Carvalho de Melo 
1607c657876SArnaldo Carvalho de Melo int dccp_getsockopt(struct sock *sk, int level, int optname,
161a1d3a355SArnaldo Carvalho de Melo 		    char __user *optval, int __user *optlen)
1627c657876SArnaldo Carvalho de Melo {
1637c657876SArnaldo Carvalho de Melo 	dccp_pr_debug("entry\n");
1647c657876SArnaldo Carvalho de Melo 
1657c657876SArnaldo Carvalho de Melo 	if (level != SOL_DCCP)
1667c657876SArnaldo Carvalho de Melo 		return ip_getsockopt(sk, level, optname, optval, optlen);
1677c657876SArnaldo Carvalho de Melo 
1687c657876SArnaldo Carvalho de Melo 	return -EOPNOTSUPP;
1697c657876SArnaldo Carvalho de Melo }
1707c657876SArnaldo Carvalho de Melo 
1717c657876SArnaldo Carvalho de Melo int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
1727c657876SArnaldo Carvalho de Melo 		 size_t len)
1737c657876SArnaldo Carvalho de Melo {
1747c657876SArnaldo Carvalho de Melo 	const struct dccp_sock *dp = dccp_sk(sk);
1757c657876SArnaldo Carvalho de Melo 	const int flags = msg->msg_flags;
1767c657876SArnaldo Carvalho de Melo 	const int noblock = flags & MSG_DONTWAIT;
1777c657876SArnaldo Carvalho de Melo 	struct sk_buff *skb;
1787c657876SArnaldo Carvalho de Melo 	int rc, size;
1797c657876SArnaldo Carvalho de Melo 	long timeo;
1807c657876SArnaldo Carvalho de Melo 
1817c657876SArnaldo Carvalho de Melo 	if (len > dp->dccps_mss_cache)
1827c657876SArnaldo Carvalho de Melo 		return -EMSGSIZE;
1837c657876SArnaldo Carvalho de Melo 
1847c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
18527258ee5SArnaldo Carvalho de Melo 	timeo = sock_sndtimeo(sk, noblock);
1867c657876SArnaldo Carvalho de Melo 
1877c657876SArnaldo Carvalho de Melo 	/*
1887c657876SArnaldo Carvalho de Melo 	 * We have to use sk_stream_wait_connect here to set sk_write_pending,
1897c657876SArnaldo Carvalho de Melo 	 * so that the trick in dccp_rcv_request_sent_state_process.
1907c657876SArnaldo Carvalho de Melo 	 */
1917c657876SArnaldo Carvalho de Melo 	/* Wait for a connection to finish. */
1927c657876SArnaldo Carvalho de Melo 	if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN | DCCPF_CLOSING))
1937c657876SArnaldo Carvalho de Melo 		if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0)
19427258ee5SArnaldo Carvalho de Melo 			goto out_release;
1957c657876SArnaldo Carvalho de Melo 
1967c657876SArnaldo Carvalho de Melo 	size = sk->sk_prot->max_header + len;
1977c657876SArnaldo Carvalho de Melo 	release_sock(sk);
1987c657876SArnaldo Carvalho de Melo 	skb = sock_alloc_send_skb(sk, size, noblock, &rc);
1997c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
2007c657876SArnaldo Carvalho de Melo 	if (skb == NULL)
2017c657876SArnaldo Carvalho de Melo 		goto out_release;
2027c657876SArnaldo Carvalho de Melo 
2037c657876SArnaldo Carvalho de Melo 	skb_reserve(skb, sk->sk_prot->max_header);
2047c657876SArnaldo Carvalho de Melo 	rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
20527258ee5SArnaldo Carvalho de Melo 	if (rc != 0)
20627258ee5SArnaldo Carvalho de Melo 		goto out_discard;
2077c657876SArnaldo Carvalho de Melo 
20827258ee5SArnaldo Carvalho de Melo 	rc = dccp_write_xmit(sk, skb, len);
2097c657876SArnaldo Carvalho de Melo out_release:
2107c657876SArnaldo Carvalho de Melo 	release_sock(sk);
2117c657876SArnaldo Carvalho de Melo 	return rc ? : len;
21227258ee5SArnaldo Carvalho de Melo out_discard:
21327258ee5SArnaldo Carvalho de Melo 	kfree_skb(skb);
2147c657876SArnaldo Carvalho de Melo 	goto out_release;
2157c657876SArnaldo Carvalho de Melo }
2167c657876SArnaldo Carvalho de Melo 
2177c657876SArnaldo Carvalho de Melo int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
2187c657876SArnaldo Carvalho de Melo 		 size_t len, int nonblock, int flags, int *addr_len)
2197c657876SArnaldo Carvalho de Melo {
2207c657876SArnaldo Carvalho de Melo 	const struct dccp_hdr *dh;
2217c657876SArnaldo Carvalho de Melo 	long timeo;
2227c657876SArnaldo Carvalho de Melo 
2237c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
2247c657876SArnaldo Carvalho de Melo 
225531669a0SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_LISTEN) {
226531669a0SArnaldo Carvalho de Melo 		len = -ENOTCONN;
2277c657876SArnaldo Carvalho de Melo 		goto out;
228531669a0SArnaldo Carvalho de Melo 	}
2297c657876SArnaldo Carvalho de Melo 
2307c657876SArnaldo Carvalho de Melo 	timeo = sock_rcvtimeo(sk, nonblock);
2317c657876SArnaldo Carvalho de Melo 
2327c657876SArnaldo Carvalho de Melo 	do {
233531669a0SArnaldo Carvalho de Melo 		struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
2347c657876SArnaldo Carvalho de Melo 
235531669a0SArnaldo Carvalho de Melo 		if (skb == NULL)
236531669a0SArnaldo Carvalho de Melo 			goto verify_sock_status;
2377c657876SArnaldo Carvalho de Melo 
2387c657876SArnaldo Carvalho de Melo 		dh = dccp_hdr(skb);
2397c657876SArnaldo Carvalho de Melo 
2407c657876SArnaldo Carvalho de Melo 		if (dh->dccph_type == DCCP_PKT_DATA ||
2417c657876SArnaldo Carvalho de Melo 		    dh->dccph_type == DCCP_PKT_DATAACK)
2427c657876SArnaldo Carvalho de Melo 			goto found_ok_skb;
2437c657876SArnaldo Carvalho de Melo 
2447c657876SArnaldo Carvalho de Melo 		if (dh->dccph_type == DCCP_PKT_RESET ||
2457c657876SArnaldo Carvalho de Melo 		    dh->dccph_type == DCCP_PKT_CLOSE) {
2467c657876SArnaldo Carvalho de Melo 			dccp_pr_debug("found fin ok!\n");
247531669a0SArnaldo Carvalho de Melo 			len = 0;
2487c657876SArnaldo Carvalho de Melo 			goto found_fin_ok;
2497c657876SArnaldo Carvalho de Melo 		}
2507690af3fSArnaldo Carvalho de Melo 		dccp_pr_debug("packet_type=%s\n",
2517690af3fSArnaldo Carvalho de Melo 			      dccp_packet_name(dh->dccph_type));
252531669a0SArnaldo Carvalho de Melo 		sk_eat_skb(sk, skb);
253531669a0SArnaldo Carvalho de Melo verify_sock_status:
254531669a0SArnaldo Carvalho de Melo 		if (sock_flag(sk, SOCK_DONE)) {
255531669a0SArnaldo Carvalho de Melo 			len = 0;
2567c657876SArnaldo Carvalho de Melo 			break;
2577c657876SArnaldo Carvalho de Melo 		}
2587c657876SArnaldo Carvalho de Melo 
259531669a0SArnaldo Carvalho de Melo 		if (sk->sk_err) {
260531669a0SArnaldo Carvalho de Melo 			len = sock_error(sk);
2617c657876SArnaldo Carvalho de Melo 			break;
262531669a0SArnaldo Carvalho de Melo 		}
263531669a0SArnaldo Carvalho de Melo 
264531669a0SArnaldo Carvalho de Melo 		if (sk->sk_shutdown & RCV_SHUTDOWN) {
265531669a0SArnaldo Carvalho de Melo 			len = 0;
266531669a0SArnaldo Carvalho de Melo 			break;
267531669a0SArnaldo Carvalho de Melo 		}
2687c657876SArnaldo Carvalho de Melo 
2697c657876SArnaldo Carvalho de Melo 		if (sk->sk_state == DCCP_CLOSED) {
2707c657876SArnaldo Carvalho de Melo 			if (!sock_flag(sk, SOCK_DONE)) {
2717c657876SArnaldo Carvalho de Melo 				/* This occurs when user tries to read
2727c657876SArnaldo Carvalho de Melo 				 * from never connected socket.
2737c657876SArnaldo Carvalho de Melo 				 */
274531669a0SArnaldo Carvalho de Melo 				len = -ENOTCONN;
2757c657876SArnaldo Carvalho de Melo 				break;
2767c657876SArnaldo Carvalho de Melo 			}
277531669a0SArnaldo Carvalho de Melo 			len = 0;
2787c657876SArnaldo Carvalho de Melo 			break;
2797c657876SArnaldo Carvalho de Melo 		}
2807c657876SArnaldo Carvalho de Melo 
2817c657876SArnaldo Carvalho de Melo 		if (!timeo) {
282531669a0SArnaldo Carvalho de Melo 			len = -EAGAIN;
2837c657876SArnaldo Carvalho de Melo 			break;
2847c657876SArnaldo Carvalho de Melo 		}
2857c657876SArnaldo Carvalho de Melo 
2867c657876SArnaldo Carvalho de Melo 		if (signal_pending(current)) {
287531669a0SArnaldo Carvalho de Melo 			len = sock_intr_errno(timeo);
2887c657876SArnaldo Carvalho de Melo 			break;
2897c657876SArnaldo Carvalho de Melo 		}
2907c657876SArnaldo Carvalho de Melo 
2917c657876SArnaldo Carvalho de Melo 		sk_wait_data(sk, &timeo);
2927c657876SArnaldo Carvalho de Melo 		continue;
2937c657876SArnaldo Carvalho de Melo 	found_ok_skb:
294531669a0SArnaldo Carvalho de Melo 		if (len > skb->len)
295531669a0SArnaldo Carvalho de Melo 			len = skb->len;
296531669a0SArnaldo Carvalho de Melo 		else if (len < skb->len)
297531669a0SArnaldo Carvalho de Melo 			msg->msg_flags |= MSG_TRUNC;
2987c657876SArnaldo Carvalho de Melo 
299531669a0SArnaldo Carvalho de Melo 		if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len)) {
3007c657876SArnaldo Carvalho de Melo 			/* Exception. Bailout! */
301531669a0SArnaldo Carvalho de Melo 			len = -EFAULT;
3027c657876SArnaldo Carvalho de Melo 			break;
3037c657876SArnaldo Carvalho de Melo 		}
3047c657876SArnaldo Carvalho de Melo 	found_fin_ok:
3057c657876SArnaldo Carvalho de Melo 		if (!(flags & MSG_PEEK))
3067c657876SArnaldo Carvalho de Melo 			sk_eat_skb(sk, skb);
3077c657876SArnaldo Carvalho de Melo 		break;
308531669a0SArnaldo Carvalho de Melo 	} while (1);
3097c657876SArnaldo Carvalho de Melo out:
3107c657876SArnaldo Carvalho de Melo 	release_sock(sk);
311531669a0SArnaldo Carvalho de Melo 	return len;
3127c657876SArnaldo Carvalho de Melo }
3137c657876SArnaldo Carvalho de Melo 
3147c657876SArnaldo Carvalho de Melo static int inet_dccp_listen(struct socket *sock, int backlog)
3157c657876SArnaldo Carvalho de Melo {
3167c657876SArnaldo Carvalho de Melo 	struct sock *sk = sock->sk;
3177c657876SArnaldo Carvalho de Melo 	unsigned char old_state;
3187c657876SArnaldo Carvalho de Melo 	int err;
3197c657876SArnaldo Carvalho de Melo 
3207c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
3217c657876SArnaldo Carvalho de Melo 
3227c657876SArnaldo Carvalho de Melo 	err = -EINVAL;
3237c657876SArnaldo Carvalho de Melo 	if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP)
3247c657876SArnaldo Carvalho de Melo 		goto out;
3257c657876SArnaldo Carvalho de Melo 
3267c657876SArnaldo Carvalho de Melo 	old_state = sk->sk_state;
3277c657876SArnaldo Carvalho de Melo 	if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN)))
3287c657876SArnaldo Carvalho de Melo 		goto out;
3297c657876SArnaldo Carvalho de Melo 
3307c657876SArnaldo Carvalho de Melo 	/* Really, if the socket is already in listen state
3317c657876SArnaldo Carvalho de Melo 	 * we can only allow the backlog to be adjusted.
3327c657876SArnaldo Carvalho de Melo 	 */
3337c657876SArnaldo Carvalho de Melo 	if (old_state != DCCP_LISTEN) {
3347c657876SArnaldo Carvalho de Melo 		/*
3357c657876SArnaldo Carvalho de Melo 		 * FIXME: here it probably should be sk->sk_prot->listen_start
3367c657876SArnaldo Carvalho de Melo 		 * see tcp_listen_start
3377c657876SArnaldo Carvalho de Melo 		 */
3387c657876SArnaldo Carvalho de Melo 		err = dccp_listen_start(sk);
3397c657876SArnaldo Carvalho de Melo 		if (err)
3407c657876SArnaldo Carvalho de Melo 			goto out;
3417c657876SArnaldo Carvalho de Melo 	}
3427c657876SArnaldo Carvalho de Melo 	sk->sk_max_ack_backlog = backlog;
3437c657876SArnaldo Carvalho de Melo 	err = 0;
3447c657876SArnaldo Carvalho de Melo 
3457c657876SArnaldo Carvalho de Melo out:
3467c657876SArnaldo Carvalho de Melo 	release_sock(sk);
3477c657876SArnaldo Carvalho de Melo 	return err;
3487c657876SArnaldo Carvalho de Melo }
3497c657876SArnaldo Carvalho de Melo 
3507c657876SArnaldo Carvalho de Melo static const unsigned char dccp_new_state[] = {
3517c657876SArnaldo Carvalho de Melo 	/* current state:   new state:      action:	*/
3527c657876SArnaldo Carvalho de Melo 	[0]		  = DCCP_CLOSED,
3537c657876SArnaldo Carvalho de Melo 	[DCCP_OPEN] 	  = DCCP_CLOSING | DCCP_ACTION_FIN,
3547c657876SArnaldo Carvalho de Melo 	[DCCP_REQUESTING] = DCCP_CLOSED,
3557c657876SArnaldo Carvalho de Melo 	[DCCP_PARTOPEN]	  = DCCP_CLOSING | DCCP_ACTION_FIN,
3567c657876SArnaldo Carvalho de Melo 	[DCCP_LISTEN]	  = DCCP_CLOSED,
3577c657876SArnaldo Carvalho de Melo 	[DCCP_RESPOND]	  = DCCP_CLOSED,
3587c657876SArnaldo Carvalho de Melo 	[DCCP_CLOSING]	  = DCCP_CLOSED,
3597c657876SArnaldo Carvalho de Melo 	[DCCP_TIME_WAIT]  = DCCP_CLOSED,
3607c657876SArnaldo Carvalho de Melo 	[DCCP_CLOSED]	  = DCCP_CLOSED,
3617c657876SArnaldo Carvalho de Melo };
3627c657876SArnaldo Carvalho de Melo 
3637c657876SArnaldo Carvalho de Melo static int dccp_close_state(struct sock *sk)
3647c657876SArnaldo Carvalho de Melo {
3657c657876SArnaldo Carvalho de Melo 	const int next = dccp_new_state[sk->sk_state];
3667c657876SArnaldo Carvalho de Melo 	const int ns = next & DCCP_STATE_MASK;
3677c657876SArnaldo Carvalho de Melo 
3687c657876SArnaldo Carvalho de Melo 	if (ns != sk->sk_state)
3697c657876SArnaldo Carvalho de Melo 		dccp_set_state(sk, ns);
3707c657876SArnaldo Carvalho de Melo 
3717c657876SArnaldo Carvalho de Melo 	return next & DCCP_ACTION_FIN;
3727c657876SArnaldo Carvalho de Melo }
3737c657876SArnaldo Carvalho de Melo 
3747c657876SArnaldo Carvalho de Melo void dccp_close(struct sock *sk, long timeout)
3757c657876SArnaldo Carvalho de Melo {
3767c657876SArnaldo Carvalho de Melo 	struct sk_buff *skb;
3777c657876SArnaldo Carvalho de Melo 
3787c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
3797c657876SArnaldo Carvalho de Melo 
3807c657876SArnaldo Carvalho de Melo 	sk->sk_shutdown = SHUTDOWN_MASK;
3817c657876SArnaldo Carvalho de Melo 
3827c657876SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_LISTEN) {
3837c657876SArnaldo Carvalho de Melo 		dccp_set_state(sk, DCCP_CLOSED);
3847c657876SArnaldo Carvalho de Melo 
3857c657876SArnaldo Carvalho de Melo 		/* Special case. */
3867c657876SArnaldo Carvalho de Melo 		inet_csk_listen_stop(sk);
3877c657876SArnaldo Carvalho de Melo 
3887c657876SArnaldo Carvalho de Melo 		goto adjudge_to_death;
3897c657876SArnaldo Carvalho de Melo 	}
3907c657876SArnaldo Carvalho de Melo 
3917c657876SArnaldo Carvalho de Melo 	/*
3927c657876SArnaldo Carvalho de Melo 	 * We need to flush the recv. buffs.  We do this only on the
3937c657876SArnaldo Carvalho de Melo 	 * descriptor close, not protocol-sourced closes, because the
3947c657876SArnaldo Carvalho de Melo 	  *reader process may not have drained the data yet!
3957c657876SArnaldo Carvalho de Melo 	 */
3967c657876SArnaldo Carvalho de Melo 	/* FIXME: check for unread data */
3977c657876SArnaldo Carvalho de Melo 	while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
3987c657876SArnaldo Carvalho de Melo 		__kfree_skb(skb);
3997c657876SArnaldo Carvalho de Melo 	}
4007c657876SArnaldo Carvalho de Melo 
4017c657876SArnaldo Carvalho de Melo 	if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
4027c657876SArnaldo Carvalho de Melo 		/* Check zero linger _after_ checking for unread data. */
4037c657876SArnaldo Carvalho de Melo 		sk->sk_prot->disconnect(sk, 0);
4047c657876SArnaldo Carvalho de Melo 	} else if (dccp_close_state(sk)) {
4057c657876SArnaldo Carvalho de Melo 		dccp_send_close(sk);
4067c657876SArnaldo Carvalho de Melo 	}
4077c657876SArnaldo Carvalho de Melo 
4087c657876SArnaldo Carvalho de Melo 	sk_stream_wait_close(sk, timeout);
4097c657876SArnaldo Carvalho de Melo 
4107c657876SArnaldo Carvalho de Melo adjudge_to_death:
4117c657876SArnaldo Carvalho de Melo 	release_sock(sk);
4127c657876SArnaldo Carvalho de Melo 	/*
4137c657876SArnaldo Carvalho de Melo 	 * Now socket is owned by kernel and we acquire BH lock
4147c657876SArnaldo Carvalho de Melo 	 * to finish close. No need to check for user refs.
4157c657876SArnaldo Carvalho de Melo 	 */
4167c657876SArnaldo Carvalho de Melo 	local_bh_disable();
4177c657876SArnaldo Carvalho de Melo 	bh_lock_sock(sk);
4187c657876SArnaldo Carvalho de Melo 	BUG_TRAP(!sock_owned_by_user(sk));
4197c657876SArnaldo Carvalho de Melo 
4207c657876SArnaldo Carvalho de Melo 	sock_hold(sk);
4217c657876SArnaldo Carvalho de Melo 	sock_orphan(sk);
4227c657876SArnaldo Carvalho de Melo 
4237c657876SArnaldo Carvalho de Melo 	if (sk->sk_state != DCCP_CLOSED)
4247c657876SArnaldo Carvalho de Melo 		dccp_set_state(sk, DCCP_CLOSED);
4257c657876SArnaldo Carvalho de Melo 
4267c657876SArnaldo Carvalho de Melo 	atomic_inc(&dccp_orphan_count);
4277c657876SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_CLOSED)
4287c657876SArnaldo Carvalho de Melo 		inet_csk_destroy_sock(sk);
4297c657876SArnaldo Carvalho de Melo 
4307c657876SArnaldo Carvalho de Melo 	/* Otherwise, socket is reprieved until protocol close. */
4317c657876SArnaldo Carvalho de Melo 
4327c657876SArnaldo Carvalho de Melo 	bh_unlock_sock(sk);
4337c657876SArnaldo Carvalho de Melo 	local_bh_enable();
4347c657876SArnaldo Carvalho de Melo 	sock_put(sk);
4357c657876SArnaldo Carvalho de Melo }
4367c657876SArnaldo Carvalho de Melo 
4377c657876SArnaldo Carvalho de Melo void dccp_shutdown(struct sock *sk, int how)
4387c657876SArnaldo Carvalho de Melo {
4397c657876SArnaldo Carvalho de Melo 	dccp_pr_debug("entry\n");
4407c657876SArnaldo Carvalho de Melo }
4417c657876SArnaldo Carvalho de Melo 
442a1d3a355SArnaldo Carvalho de Melo static struct proto_ops inet_dccp_ops = {
4437c657876SArnaldo Carvalho de Melo 	.family		= PF_INET,
4447c657876SArnaldo Carvalho de Melo 	.owner		= THIS_MODULE,
4457c657876SArnaldo Carvalho de Melo 	.release	= inet_release,
4467c657876SArnaldo Carvalho de Melo 	.bind		= inet_bind,
4477c657876SArnaldo Carvalho de Melo 	.connect	= inet_stream_connect,
4487c657876SArnaldo Carvalho de Melo 	.socketpair	= sock_no_socketpair,
4497c657876SArnaldo Carvalho de Melo 	.accept		= inet_accept,
4507c657876SArnaldo Carvalho de Melo 	.getname	= inet_getname,
4517c657876SArnaldo Carvalho de Melo 	.poll		= sock_no_poll,
4527c657876SArnaldo Carvalho de Melo 	.ioctl		= inet_ioctl,
4537690af3fSArnaldo Carvalho de Melo 	/* FIXME: work on inet_listen to rename it to sock_common_listen */
4547690af3fSArnaldo Carvalho de Melo 	.listen		= inet_dccp_listen,
4557c657876SArnaldo Carvalho de Melo 	.shutdown	= inet_shutdown,
4567c657876SArnaldo Carvalho de Melo 	.setsockopt	= sock_common_setsockopt,
4577c657876SArnaldo Carvalho de Melo 	.getsockopt	= sock_common_getsockopt,
4587c657876SArnaldo Carvalho de Melo 	.sendmsg	= inet_sendmsg,
4597c657876SArnaldo Carvalho de Melo 	.recvmsg	= sock_common_recvmsg,
4607c657876SArnaldo Carvalho de Melo 	.mmap		= sock_no_mmap,
4617c657876SArnaldo Carvalho de Melo 	.sendpage	= sock_no_sendpage,
4627c657876SArnaldo Carvalho de Melo };
4637c657876SArnaldo Carvalho de Melo 
4647c657876SArnaldo Carvalho de Melo extern struct net_proto_family inet_family_ops;
4657c657876SArnaldo Carvalho de Melo 
4667c657876SArnaldo Carvalho de Melo static struct inet_protosw dccp_v4_protosw = {
4677c657876SArnaldo Carvalho de Melo 	.type		= SOCK_DCCP,
4687c657876SArnaldo Carvalho de Melo 	.protocol	= IPPROTO_DCCP,
4697c657876SArnaldo Carvalho de Melo 	.prot		= &dccp_v4_prot,
4707c657876SArnaldo Carvalho de Melo 	.ops		= &inet_dccp_ops,
4717c657876SArnaldo Carvalho de Melo 	.capability	= -1,
4727c657876SArnaldo Carvalho de Melo 	.no_check	= 0,
4737c657876SArnaldo Carvalho de Melo 	.flags		= 0,
4747c657876SArnaldo Carvalho de Melo };
4757c657876SArnaldo Carvalho de Melo 
4767c657876SArnaldo Carvalho de Melo /*
4777c657876SArnaldo Carvalho de Melo  * This is the global socket data structure used for responding to
4787c657876SArnaldo Carvalho de Melo  * the Out-of-the-blue (OOTB) packets. A control sock will be created
4797c657876SArnaldo Carvalho de Melo  * for this socket at the initialization time.
4807c657876SArnaldo Carvalho de Melo  */
4817c657876SArnaldo Carvalho de Melo struct socket *dccp_ctl_socket;
4827c657876SArnaldo Carvalho de Melo 
4837c657876SArnaldo Carvalho de Melo static char dccp_ctl_socket_err_msg[] __initdata =
4847c657876SArnaldo Carvalho de Melo 	KERN_ERR "DCCP: Failed to create the control socket.\n";
4857c657876SArnaldo Carvalho de Melo 
4867c657876SArnaldo Carvalho de Melo static int __init dccp_ctl_sock_init(void)
4877c657876SArnaldo Carvalho de Melo {
4887c657876SArnaldo Carvalho de Melo 	int rc = sock_create_kern(PF_INET, SOCK_DCCP, IPPROTO_DCCP,
4897c657876SArnaldo Carvalho de Melo 				  &dccp_ctl_socket);
4907c657876SArnaldo Carvalho de Melo 	if (rc < 0)
4917c657876SArnaldo Carvalho de Melo 		printk(dccp_ctl_socket_err_msg);
4927c657876SArnaldo Carvalho de Melo 	else {
4937c657876SArnaldo Carvalho de Melo 		dccp_ctl_socket->sk->sk_allocation = GFP_ATOMIC;
4947c657876SArnaldo Carvalho de Melo 		inet_sk(dccp_ctl_socket->sk)->uc_ttl = -1;
4957c657876SArnaldo Carvalho de Melo 
4967c657876SArnaldo Carvalho de Melo 		/* Unhash it so that IP input processing does not even
4977c657876SArnaldo Carvalho de Melo 		 * see it, we do not wish this socket to see incoming
4987c657876SArnaldo Carvalho de Melo 		 * packets.
4997c657876SArnaldo Carvalho de Melo 		 */
5007c657876SArnaldo Carvalho de Melo 		dccp_ctl_socket->sk->sk_prot->unhash(dccp_ctl_socket->sk);
5017c657876SArnaldo Carvalho de Melo 	}
5027c657876SArnaldo Carvalho de Melo 
5037c657876SArnaldo Carvalho de Melo 	return rc;
5047c657876SArnaldo Carvalho de Melo }
5057c657876SArnaldo Carvalho de Melo 
506725ba8eeSArnaldo Carvalho de Melo #ifdef CONFIG_IP_DCCP_UNLOAD_HACK
507725ba8eeSArnaldo Carvalho de Melo void dccp_ctl_sock_exit(void)
5087c657876SArnaldo Carvalho de Melo {
5097c657876SArnaldo Carvalho de Melo 	if (dccp_ctl_socket != NULL)
5107c657876SArnaldo Carvalho de Melo 		sock_release(dccp_ctl_socket);
5117c657876SArnaldo Carvalho de Melo }
5127c657876SArnaldo Carvalho de Melo 
513725ba8eeSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_ctl_sock_exit);
514725ba8eeSArnaldo Carvalho de Melo #endif
515725ba8eeSArnaldo Carvalho de Melo 
5167c657876SArnaldo Carvalho de Melo static int __init init_dccp_v4_mibs(void)
5177c657876SArnaldo Carvalho de Melo {
5187c657876SArnaldo Carvalho de Melo 	int rc = -ENOMEM;
5197c657876SArnaldo Carvalho de Melo 
5207c657876SArnaldo Carvalho de Melo 	dccp_statistics[0] = alloc_percpu(struct dccp_mib);
5217c657876SArnaldo Carvalho de Melo 	if (dccp_statistics[0] == NULL)
5227c657876SArnaldo Carvalho de Melo 		goto out;
5237c657876SArnaldo Carvalho de Melo 
5247c657876SArnaldo Carvalho de Melo 	dccp_statistics[1] = alloc_percpu(struct dccp_mib);
5257c657876SArnaldo Carvalho de Melo 	if (dccp_statistics[1] == NULL)
5267c657876SArnaldo Carvalho de Melo 		goto out_free_one;
5277c657876SArnaldo Carvalho de Melo 
5287c657876SArnaldo Carvalho de Melo 	rc = 0;
5297c657876SArnaldo Carvalho de Melo out:
5307c657876SArnaldo Carvalho de Melo 	return rc;
5317c657876SArnaldo Carvalho de Melo out_free_one:
5327c657876SArnaldo Carvalho de Melo 	free_percpu(dccp_statistics[0]);
5337c657876SArnaldo Carvalho de Melo 	dccp_statistics[0] = NULL;
5347c657876SArnaldo Carvalho de Melo 	goto out;
5357c657876SArnaldo Carvalho de Melo 
5367c657876SArnaldo Carvalho de Melo }
5377c657876SArnaldo Carvalho de Melo 
5387c657876SArnaldo Carvalho de Melo static int thash_entries;
5397c657876SArnaldo Carvalho de Melo module_param(thash_entries, int, 0444);
5407c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
5417c657876SArnaldo Carvalho de Melo 
542a1d3a355SArnaldo Carvalho de Melo #ifdef CONFIG_IP_DCCP_DEBUG
5437c657876SArnaldo Carvalho de Melo int dccp_debug;
5447c657876SArnaldo Carvalho de Melo module_param(dccp_debug, int, 0444);
5457c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(dccp_debug, "Enable debug messages");
546a1d3a355SArnaldo Carvalho de Melo #endif
5477c657876SArnaldo Carvalho de Melo 
5487c657876SArnaldo Carvalho de Melo static int __init dccp_init(void)
5497c657876SArnaldo Carvalho de Melo {
5507c657876SArnaldo Carvalho de Melo 	unsigned long goal;
5517c657876SArnaldo Carvalho de Melo 	int ehash_order, bhash_order, i;
5527c657876SArnaldo Carvalho de Melo 	int rc = proto_register(&dccp_v4_prot, 1);
5537c657876SArnaldo Carvalho de Melo 
5547c657876SArnaldo Carvalho de Melo 	if (rc)
5557c657876SArnaldo Carvalho de Melo 		goto out;
5567c657876SArnaldo Carvalho de Melo 
5577690af3fSArnaldo Carvalho de Melo 	dccp_hashinfo.bind_bucket_cachep =
5587690af3fSArnaldo Carvalho de Melo 		kmem_cache_create("dccp_bind_bucket",
5597690af3fSArnaldo Carvalho de Melo 				  sizeof(struct inet_bind_bucket), 0,
5607690af3fSArnaldo Carvalho de Melo 				  SLAB_HWCACHE_ALIGN, NULL, NULL);
5617c657876SArnaldo Carvalho de Melo 	if (!dccp_hashinfo.bind_bucket_cachep)
5627c657876SArnaldo Carvalho de Melo 		goto out_proto_unregister;
5637c657876SArnaldo Carvalho de Melo 
5647c657876SArnaldo Carvalho de Melo 	/*
5657c657876SArnaldo Carvalho de Melo 	 * Size and allocate the main established and bind bucket
5667c657876SArnaldo Carvalho de Melo 	 * hash tables.
5677c657876SArnaldo Carvalho de Melo 	 *
5687c657876SArnaldo Carvalho de Melo 	 * The methodology is similar to that of the buffer cache.
5697c657876SArnaldo Carvalho de Melo 	 */
5707c657876SArnaldo Carvalho de Melo 	if (num_physpages >= (128 * 1024))
5717c657876SArnaldo Carvalho de Melo 		goal = num_physpages >> (21 - PAGE_SHIFT);
5727c657876SArnaldo Carvalho de Melo 	else
5737c657876SArnaldo Carvalho de Melo 		goal = num_physpages >> (23 - PAGE_SHIFT);
5747c657876SArnaldo Carvalho de Melo 
5757c657876SArnaldo Carvalho de Melo 	if (thash_entries)
5767690af3fSArnaldo Carvalho de Melo 		goal = (thash_entries *
5777690af3fSArnaldo Carvalho de Melo 			sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT;
5787c657876SArnaldo Carvalho de Melo 	for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++)
5797c657876SArnaldo Carvalho de Melo 		;
5807c657876SArnaldo Carvalho de Melo 	do {
5817c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE /
5827c657876SArnaldo Carvalho de Melo 					sizeof(struct inet_ehash_bucket);
5837c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.ehash_size >>= 1;
5847690af3fSArnaldo Carvalho de Melo 		while (dccp_hashinfo.ehash_size &
5857690af3fSArnaldo Carvalho de Melo 		       (dccp_hashinfo.ehash_size - 1))
5867c657876SArnaldo Carvalho de Melo 			dccp_hashinfo.ehash_size--;
5877c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.ehash = (struct inet_ehash_bucket *)
5887c657876SArnaldo Carvalho de Melo 			__get_free_pages(GFP_ATOMIC, ehash_order);
5897c657876SArnaldo Carvalho de Melo 	} while (!dccp_hashinfo.ehash && --ehash_order > 0);
5907c657876SArnaldo Carvalho de Melo 
5917c657876SArnaldo Carvalho de Melo 	if (!dccp_hashinfo.ehash) {
5927c657876SArnaldo Carvalho de Melo 		printk(KERN_CRIT "Failed to allocate DCCP "
5937c657876SArnaldo Carvalho de Melo 				 "established hash table\n");
5947c657876SArnaldo Carvalho de Melo 		goto out_free_bind_bucket_cachep;
5957c657876SArnaldo Carvalho de Melo 	}
5967c657876SArnaldo Carvalho de Melo 
5977c657876SArnaldo Carvalho de Melo 	for (i = 0; i < (dccp_hashinfo.ehash_size << 1); i++) {
5987c657876SArnaldo Carvalho de Melo 		rwlock_init(&dccp_hashinfo.ehash[i].lock);
5997c657876SArnaldo Carvalho de Melo 		INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain);
6007c657876SArnaldo Carvalho de Melo 	}
6017c657876SArnaldo Carvalho de Melo 
6027c657876SArnaldo Carvalho de Melo 	bhash_order = ehash_order;
6037c657876SArnaldo Carvalho de Melo 
6047c657876SArnaldo Carvalho de Melo 	do {
6057c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE /
6067c657876SArnaldo Carvalho de Melo 					sizeof(struct inet_bind_hashbucket);
6077690af3fSArnaldo Carvalho de Melo 		if ((dccp_hashinfo.bhash_size > (64 * 1024)) &&
6087690af3fSArnaldo Carvalho de Melo 		    bhash_order > 0)
6097c657876SArnaldo Carvalho de Melo 			continue;
6107c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.bhash = (struct inet_bind_hashbucket *)
6117c657876SArnaldo Carvalho de Melo 			__get_free_pages(GFP_ATOMIC, bhash_order);
6127c657876SArnaldo Carvalho de Melo 	} while (!dccp_hashinfo.bhash && --bhash_order >= 0);
6137c657876SArnaldo Carvalho de Melo 
6147c657876SArnaldo Carvalho de Melo 	if (!dccp_hashinfo.bhash) {
6157c657876SArnaldo Carvalho de Melo 		printk(KERN_CRIT "Failed to allocate DCCP bind hash table\n");
6167c657876SArnaldo Carvalho de Melo 		goto out_free_dccp_ehash;
6177c657876SArnaldo Carvalho de Melo 	}
6187c657876SArnaldo Carvalho de Melo 
6197c657876SArnaldo Carvalho de Melo 	for (i = 0; i < dccp_hashinfo.bhash_size; i++) {
6207c657876SArnaldo Carvalho de Melo 		spin_lock_init(&dccp_hashinfo.bhash[i].lock);
6217c657876SArnaldo Carvalho de Melo 		INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
6227c657876SArnaldo Carvalho de Melo 	}
6237c657876SArnaldo Carvalho de Melo 
6247c657876SArnaldo Carvalho de Melo 	if (init_dccp_v4_mibs())
6257c657876SArnaldo Carvalho de Melo 		goto out_free_dccp_bhash;
6267c657876SArnaldo Carvalho de Melo 
6277c657876SArnaldo Carvalho de Melo 	rc = -EAGAIN;
6287c657876SArnaldo Carvalho de Melo 	if (inet_add_protocol(&dccp_protocol, IPPROTO_DCCP))
6297c657876SArnaldo Carvalho de Melo 		goto out_free_dccp_v4_mibs;
6307c657876SArnaldo Carvalho de Melo 
6317c657876SArnaldo Carvalho de Melo 	inet_register_protosw(&dccp_v4_protosw);
6327c657876SArnaldo Carvalho de Melo 
6337c657876SArnaldo Carvalho de Melo 	rc = dccp_ctl_sock_init();
6347c657876SArnaldo Carvalho de Melo 	if (rc)
6357c657876SArnaldo Carvalho de Melo 		goto out_unregister_protosw;
6367c657876SArnaldo Carvalho de Melo out:
6377c657876SArnaldo Carvalho de Melo 	return rc;
6387c657876SArnaldo Carvalho de Melo out_unregister_protosw:
6397c657876SArnaldo Carvalho de Melo 	inet_unregister_protosw(&dccp_v4_protosw);
6407c657876SArnaldo Carvalho de Melo 	inet_del_protocol(&dccp_protocol, IPPROTO_DCCP);
6417c657876SArnaldo Carvalho de Melo out_free_dccp_v4_mibs:
6427c657876SArnaldo Carvalho de Melo 	free_percpu(dccp_statistics[0]);
6437c657876SArnaldo Carvalho de Melo 	free_percpu(dccp_statistics[1]);
6447c657876SArnaldo Carvalho de Melo 	dccp_statistics[0] = dccp_statistics[1] = NULL;
6457c657876SArnaldo Carvalho de Melo out_free_dccp_bhash:
6467c657876SArnaldo Carvalho de Melo 	free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
6477c657876SArnaldo Carvalho de Melo 	dccp_hashinfo.bhash = NULL;
6487c657876SArnaldo Carvalho de Melo out_free_dccp_ehash:
6497c657876SArnaldo Carvalho de Melo 	free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order);
6507c657876SArnaldo Carvalho de Melo 	dccp_hashinfo.ehash = NULL;
6517c657876SArnaldo Carvalho de Melo out_free_bind_bucket_cachep:
6527c657876SArnaldo Carvalho de Melo 	kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
6537c657876SArnaldo Carvalho de Melo 	dccp_hashinfo.bind_bucket_cachep = NULL;
6547c657876SArnaldo Carvalho de Melo out_proto_unregister:
6557c657876SArnaldo Carvalho de Melo 	proto_unregister(&dccp_v4_prot);
6567c657876SArnaldo Carvalho de Melo 	goto out;
6577c657876SArnaldo Carvalho de Melo }
6587c657876SArnaldo Carvalho de Melo 
6597c657876SArnaldo Carvalho de Melo static const char dccp_del_proto_err_msg[] __exitdata =
6607c657876SArnaldo Carvalho de Melo 	KERN_ERR "can't remove dccp net_protocol\n";
6617c657876SArnaldo Carvalho de Melo 
6627c657876SArnaldo Carvalho de Melo static void __exit dccp_fini(void)
6637c657876SArnaldo Carvalho de Melo {
6647c657876SArnaldo Carvalho de Melo 	inet_unregister_protosw(&dccp_v4_protosw);
6657c657876SArnaldo Carvalho de Melo 
6667c657876SArnaldo Carvalho de Melo 	if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0)
6677c657876SArnaldo Carvalho de Melo 		printk(dccp_del_proto_err_msg);
6687c657876SArnaldo Carvalho de Melo 
669725ba8eeSArnaldo Carvalho de Melo 	free_percpu(dccp_statistics[0]);
670725ba8eeSArnaldo Carvalho de Melo 	free_percpu(dccp_statistics[1]);
671725ba8eeSArnaldo Carvalho de Melo 	free_pages((unsigned long)dccp_hashinfo.bhash,
672725ba8eeSArnaldo Carvalho de Melo 		   get_order(dccp_hashinfo.bhash_size *
673725ba8eeSArnaldo Carvalho de Melo 			     sizeof(struct inet_bind_hashbucket)));
674725ba8eeSArnaldo Carvalho de Melo 	free_pages((unsigned long)dccp_hashinfo.ehash,
675725ba8eeSArnaldo Carvalho de Melo 		   get_order(dccp_hashinfo.ehash_size *
676725ba8eeSArnaldo Carvalho de Melo 			     sizeof(struct inet_ehash_bucket)));
6777c657876SArnaldo Carvalho de Melo 	kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
678725ba8eeSArnaldo Carvalho de Melo 	proto_unregister(&dccp_v4_prot);
6797c657876SArnaldo Carvalho de Melo }
6807c657876SArnaldo Carvalho de Melo 
6817c657876SArnaldo Carvalho de Melo module_init(dccp_init);
6827c657876SArnaldo Carvalho de Melo module_exit(dccp_fini);
6837c657876SArnaldo Carvalho de Melo 
684bb97d31fSArnaldo Carvalho de Melo /*
685bb97d31fSArnaldo Carvalho de Melo  * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33)
686bb97d31fSArnaldo Carvalho de Melo  * values directly, Also cover the case where the protocol is not specified,
687bb97d31fSArnaldo Carvalho de Melo  * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP
688bb97d31fSArnaldo Carvalho de Melo  */
689bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-33-type-6");
690bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-0-type-6");
6917c657876SArnaldo Carvalho de Melo MODULE_LICENSE("GPL");
6927c657876SArnaldo Carvalho de Melo MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>");
6937c657876SArnaldo Carvalho de Melo MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");
694